I am using BCEL to transform method byte code to achieve method interceptor with anonymous inner class style, while intercepting the method, I need to process some annotations on the intercepted method. I use BCEL to intercept method access other than java reflection.
Right now my code can work well with method that do not have primitive types. Since I don't know how to use Class.getDeclaredMethod with primitive argument types list, due to getDeclaredMethod accept methodName and Class[] array as arguments.
So the 1st question is how to do this.
And then I found in JDK7, I can directly obtain a MethodHandle reference through CONSTANT_MethodHandle with ldc_w byte code in java class file. Just like using ldc to reference a Java Class, if I can reference java.lang.reflection.Method with ldc_w directly , then I will save my time to do reflection and won't be bothered by the primitive types mentioned above in the 1st question. I tried but I failed to do this.
So the 2nd question is can I use ldc_w to reference a java.lang.reflection.Method?
And the 3rd question is can I covert MethodHandle to java.lang.reflection.Method or the Annotations on the corresponding method?
Thank you Holger, I am almost totally clear by your answer, but the following problem. I might misunderstanding your answer, right now I got exception during runtime:
Exception in thread "main" java.lang.NoClassDefFoundError: long
at net.madz.lifecycle.demo.standalone.ServiceOrder.allocateResources(ServiceOrder.java)
at net.madz.lifecycle.demo.standalone.Main2.main(Main2.java:18)
Caused by: java.lang.ClassNotFoundException: long
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
Code is as following //2.5 assign value for each element in array
// Step 2. final InterceptContext<Void> context = new
// InterceptContext<Void>(getClass(), this, "allocateResources",
// new Class[] { Long.class, Long.class, Long.class });
// 2.1 getClass()
ilist.append(ifact.createNew(InterceptContext.class.getName()));
ilist.append(InstructionFactory.DUP);
ilist.append(new LDC(cgen.getConstantPool().lookupClass(interceptingClass)));
// 2.2 load this
ilist.append(InstructionFactory.createLoad(new ObjectType(interceptingClass), 0));// this
// 2.3 load intercepting method
int methodNameIndex = cgen.getConstantPool().lookupString(interceptingMethod);
if ( -1 >= methodNameIndex ) {
methodNameIndex = cgen.getConstantPool().addString(interceptingMethod);
}
ilist.append(new LDC(methodNameIndex));// methodName
// 2.4 calculate argument size and allocate an array with same size
ilist.append(new ICONST(types.length));
ilist.append(ifact.createNewArray(new ObjectType("java.lang.Class"), (short) 1));
// 2.5 assign value for each element in array
for ( int i = 0; i < types.length; i++ ) {
ilist.append(InstructionFactory.DUP);
ilist.append(new ICONST(i));
String className = convertType2ClassName(types[i]);
int argumentClassIndex = cgen.getConstantPool().lookupClass(className); // ?
if ( -1 >= argumentClassIndex ) {
argumentClassIndex = cgen.getConstantPool().addClass(className);
}
if ( types[i].getSize() > 4 ) {
ilist.append(new LDC_W(argumentClassIndex));
} else {
ilist.append(new LDC(argumentClassIndex));
}
ilist.append(InstructionConstants.AASTORE);
}
// 2.6 new InterceptContext<Void>(...
final Type[] interceptor_method_arg_types = new Type[4];
interceptor_method_arg_types[0] = new ObjectType("java.lang.Class");
interceptor_method_arg_types[1] = new ObjectType("java.lang.Object");
interceptor_method_arg_types[2] = new ObjectType("java.lang.String");
interceptor_method_arg_types[3] = new ArrayType("java.lang.Class", 1);
ilist.append(ifact.createInvoke(InterceptContext.class.getName(), "<init>", Type.VOID,
interceptor_method_arg_types, Constants.INVOKESPECIAL));
And the convertType2ClassName is as following:
private static String convertType2ClassName(Type type) {
if ( Type.BOOLEAN.equals(type) ) {
return boolean.class.getName();
} else if ( Type.BYTE.equals(type) ) {
return byte.class.getName();
} else if ( Type.CHAR.equals(type) ) {
return char.class.getName();
} else if ( Type.DOUBLE.equals(type) ) {
return double.class.getName();
} else if ( Type.FLOAT.equals(type) ) {
return float.class.getName();
} else if ( Type.INT.equals(type) ) {
return int.class.getName();
} else if ( Type.LONG.equals(type) ) {
return long.class.getName();
} else if ( Type.SHORT.equals(type) ) {
return short.class.getName();
} else if ( type instanceof ObjectType ) {
String signature = type.getSignature();
if ( signature.startsWith("L") ) {
signature = signature.substring(1);
}
int leftArrow = signature.indexOf("<");
if ( -1 < leftArrow ) {
signature = signature.substring(0, leftArrow);
}
if ( signature.endsWith(";") ) {
signature = signature.substring(0, signature.length() - 1);
}
return signature;
} else if ( type instanceof ArrayType ) {
//unsupport for now
}
//wrong return
return type.getSignature();
}