signature, exceptions });
LOGGER.debug("Processing method: {} with descriptor {}", name, desc);
// identify the target method parameters and return type
Method currentTransformMethod = new Method(name, desc);
Type[] targetMethodParameters = currentTransformMethod.getArgumentTypes();
Type returnType = currentTransformMethod.getReturnType();
// we create a static field for each method we encounter with a name
// like method_parm1_parm2...
StringBuilder methodStaticFieldNameBuilder = new StringBuilder(name);
// for each a parameter get the name and add it to the field removing
// the dots first
for (Type t : targetMethodParameters) {
methodStaticFieldNameBuilder.append("_");
methodStaticFieldNameBuilder.append(t.getClassName().replaceAll("\\[\\]", "Array")
.replaceAll("\\.", ""));
}
String methodStaticFieldName = methodStaticFieldNameBuilder.toString();
// add a private static field for the method
cv.visitField(ACC_PRIVATE | ACC_STATIC, methodStaticFieldName, METHOD_TYPE.getDescriptor(),
null, null);
// visit the method using the class writer, delegated through the method
// visitor and generator
// modify the method access so that any native methods aren't
// described as native
// since they won't be native in proxy form
// also stop methods being marked synchronized on the proxy as they will
// be sync
// on the real object
int newAccess = access & (~ACC_NATIVE) & (~ACC_SYNCHRONIZED);
MethodVisitor mv = cv.visitMethod(newAccess, name, desc, signature, exceptions);
// use a GeneratorAdapter to build the invoke call directly in byte code
GeneratorAdapter methodAdapter = new GeneratorAdapter(mv, newAccess, name, desc);
/*
* Stage 1 creates the bytecode for adding the reflected method of the
* superclass to a static field in the subclass: private static Method
* methodName_parm1_parm2... = null; static{ methodName_parm1_parm2... =
* superClass.getDeclaredMethod(methodName,new Class[]{method args}; }
*
* Stage 2 is to call the ih.invoke(this,methodName_parm1_parm2,args) in
* the new subclass methods Stage 3 is to cast the return value to the
* correct type
*/
/*
* Stage 1 use superClass.getMethod(methodName,new Class[]{method args}
* from the Class object on the stack
*/
// load the static superclass Class onto the stack
staticAdapter.getStatic(newClassType, currentClassFieldName, CLASS_TYPE);
// push the method name string arg onto the stack
staticAdapter.push(name);
// create an array of the method parm class[] arg
staticAdapter.push(targetMethodParameters.length);
staticAdapter.newArray(CLASS_TYPE);
int index = 0;
for (Type t : targetMethodParameters) {
staticAdapter.dup();
staticAdapter.push(index);
switch (t.getSort())
{
case Type.BOOLEAN:
staticAdapter.getStatic(Type.getType(java.lang.Boolean.class), "TYPE", CLASS_TYPE);
break;
case Type.BYTE:
staticAdapter.getStatic(Type.getType(java.lang.Byte.class), "TYPE", CLASS_TYPE);
break;
case Type.CHAR:
staticAdapter.getStatic(Type.getType(java.lang.Character.class), "TYPE", CLASS_TYPE);
break;
case Type.DOUBLE:
staticAdapter.getStatic(Type.getType(java.lang.Double.class), "TYPE", CLASS_TYPE);
break;
case Type.FLOAT:
staticAdapter.getStatic(Type.getType(java.lang.Float.class), "TYPE", CLASS_TYPE);
break;
case Type.INT:
staticAdapter.getStatic(Type.getType(java.lang.Integer.class), "TYPE", CLASS_TYPE);
break;
case Type.LONG:
staticAdapter.getStatic(Type.getType(java.lang.Long.class), "TYPE", CLASS_TYPE);
break;
case Type.SHORT:
staticAdapter.getStatic(Type.getType(java.lang.Short.class), "TYPE", CLASS_TYPE);
break;
default:
case Type.OBJECT:
staticAdapter.push(t);
break;
}
staticAdapter.arrayStore(CLASS_TYPE);
index++;
}
// invoke the getMethod
staticAdapter.invokeVirtual(CLASS_TYPE, new Method("getDeclaredMethod", METHOD_TYPE,
new Type[] { STRING_TYPE, Type.getType(java.lang.Class[].class) }));
// store the reflected method in the static field
staticAdapter.putStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
/*
* Stage 2 call the ih.invoke(this,supermethod,parms)
*/
// load this to get the ih field
methodAdapter.loadThis();
// load the invocation handler from the field (the location of the
// InvocationHandler.invoke)
methodAdapter.getField(newClassType, IH_FIELD, IH_TYPE);
// loadThis (the first arg of the InvocationHandler.invoke)
methodAdapter.loadThis();
// load the method to invoke (the second arg of the
// InvocationHandler.invoke)
methodAdapter.getStatic(newClassType, methodStaticFieldName, METHOD_TYPE);
// load all the method arguments onto the stack as an object array (the
// third arg of the InvocationHandler.invoke)
methodAdapter.loadArgArray();
// generate the invoke method
Method invocationHandlerInvokeMethod = new Method("invoke", OBJECT_TYPE, new Type[] {
OBJECT_TYPE, METHOD_TYPE, Type.getType(java.lang.Object[].class) });
// call the invoke method of the invocation handler
methodAdapter.invokeInterface(IH_TYPE, invocationHandlerInvokeMethod);
/*