jvm中方法调用指令有:
invokeinterface:调用接口方法;
invokespecial:专门用来调用父类方法、私有方法和初始化方法;
invokestatic:调用静态方法;
invokevirtual:调用对象的一般方法。
这四个指令所对应的类、调用的方法在编译时几乎是固定的:invokestatic所对应的类为静态方法所在的类,方法为静态方法本身;invokespecial所对应的类为当前对象,方法是固定的;invokeinterface和invokevirtual所对应的类也为当前对象,方法可以因为继承和实现进行选择,但也仅限于整个继承体系中选择(比如:一个类的equals方法有重写,则调用当前对象的equals方法,否则就调用Object的equals方法,选择的余地不大)。
在java7 JVM中增加了一个新的指令invokedynamic,用于支持动态语言,即允许方法调用可以在运行时指定类和方法,不必在编译的时候确定。字节码中每条invokedynamic指令出现的位置称为一个动态调用点,invokedynamic指令后面会跟一个指向常量池的调用点限定符,这个限定符会被解析为一个动态调用点。解析和调用过程如下:
1、根据invokedynamic指令后面的限定符#n,找到调用点限定符在常量池中的位置,调用点限定符的符号引用为CONSTANT_InvokeDynamic_info结构:
CONSTANT_InvokeDynamic_info{ u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }
2、通过CONSTANT_InvokeDynamic_info结构,找到引导方法,引导方法返回值必须是java.lang.invoke.CallSite类型
3、调用引导方法。和调用普通方法一样
动态调用点限定符的符号引用解析时出现了异常、或者引导方法执行出现异常、或者引导方法的返回值不匹配、MethodHandle方法描述不一致等都会抛出BootstrapMethodError异常。
package invokedynamic.demo; import java.lang.invoke.*; public class Bootstrap { private static void hello() { System.out.println("Hello!"); } public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { MethodHandles.Lookup lookup = MethodHandles.lookup(); Class thisClass = lookup.lookupClass(); MethodHandle mh = lookup.findStatic(thisClass, "hello", MethodType.methodType(void.class)); return new ConstantCallSite(mh.asType(type)); } }
public byte[] generate(String dynamicInvokeClassName, String dynamicLinkageClassName, String bootstrapMethodName, String methodDescriptor) throws Exception { ClassWriter cw = new ClassWriter(0); MethodVisitor mv; cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, dynamicInvokeClassName, null, "java/lang/Object", null); { mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class); Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, dynamicLinkageClassName, bootstrapMethodName, mt.toMethodDescriptorString()); mv.visitInvokeDynamicInsn("dynamicInvoke", methodDescriptor, bootstrap); mv.visitInsn(RETURN); mv.visitMaxs(0, 1); mv.visitEnd(); } cw.visitEnd(); return cw.toByteArray(); }
public static void main(String[] args) throws IOException, Exception { String dynamicClass = "invokedynamic/demo/DynamicInvoker"; FileOutputStream fos = new FileOutputStream(new File("d:/code/java7/out/production/java7/" + dynamicClass + ".class")); fos.write(new DynamicInvokerGenerator().generate(dynamicClass, "invokedynamic/demo/Bootstrap", "bootstrap", "()V")); }
public class invokedynamic.demo.DynamicInvoker BootstrapMethods: 0: #13 invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Utf8 invokedynamic/demo/DynamicInvoker #2 = Class #1 // invokedynamic/demo/DynamicInvoker #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 main #6 = Utf8 ([Ljava/lang/String;)V #7 = Utf8 invokedynamic/demo/Bootstrap #8 = Class #7 // invokedynamic/demo/Bootstrap #9 = Utf8 bootstrap #10 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #11 = NameAndType #9:#10 // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #12 = Methodref #8.#11 // invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; #13 = MethodHandle #6:#12 // invokestatic invokedynamic/demo/Bootstrap.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/ #14 = Utf8 dynamicInvoke #15 = Utf8 ()V #16 = NameAndType #14:#15 // dynamicInvoke:()V #17 = InvokeDynamic #0:#16 // #0:dynamicInvoke:()V #18 = Utf8 Code #19 = Utf8 BootstrapMethods { public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=0, locals=1, args_size=1 0: invokedynamic #17, 0 // InvokeDynamic #0:dynamicInvoke:()V 5: return }第33行生成了ynvokedynamic指定,动态调用点在常量池#17,也就是25行,第3行是对引导方法的描述。
D:\code\java7\out\production\java7>java invokedynamic.demo.DynamicInvoker Hello!