只需看看今天Groovy语言实现机制。在此之前,是第一个推倒与语言在实现上面的一些差异。
静态类型 vs. 动态类型
看以下这个简单的栗子。
def addtwo(a, b) { return a + b;}
静态类型语言与动态类型语言对于上面这个简单的加法实现全然不同。静态类型语言。比如Java。语言的编译器在编译时就已经进行类型检查,所以能够将+
运算符编译成特定的指令。语言的runtime系统能够直接执行该指令。比如javac会将两个int
类型的+
运算编译成iadd
指令,执行时由JVM直接执行iadd
指令。
而对于动态类型语言,因为须要到执行时才干确定变量的类型,因此运算符的详细实现也须要到执行时才干确定。a + b
会被编译成相似(+ a b)
这个方案调用(Lisp风格^_^)。+
仅仅是个方法名。语言的runtime系统须要依据方法名(+
)和參数类型(a
和b
的类型)来确定这个加法运算的详细实现。
The challenge of compiling dynamically typed languages is how to implement a runtime system that can choose the most appropriate implementation of a method or function — after the program has been compiled.
总而言之,言而总之。静态类型语言苦了编译器爽了执行时。动态类型语言爽了编译器苦了执行时。
既然是这样,那以下我们就来看看Groovy的编译器()是怎么苦了Groovy的runtime系统的。
invokedynamic之前
使用groovyc编译上面的栗子。得到class文件。javap看下字节码,
> groovyc demo.groovy> javap -v -p demo
Classfile /C:/Users/tongyuan.zbs/demo.class Last modified 2015-3-7; size 2287 bytes MD5 checksum ee25ddebc1ef5ab750baebf75f8031b6 Compiled from "demo.groovy"public class demo extends groovy.lang.Script SourceFile: "demo.groovy" minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Utf8 demo #2 = Class #1 // demo #3 = Utf8 groovy/lang/Script #4 = Class #3 // groovy/lang/Script #5 = Utf8 demo.groovy #6 = Utf8 $staticClassInfo #7 = Utf8 Lorg/codehaus/groovy/reflection/ClassInfo; #8 = Utf8 __$stMC #9 = Utf8 Z #10 = Utf8#11 = Utf8 ()V #12 = NameAndType #10:#11 // " ":()V #13 = Methodref #4.#12 // groovy/lang/Script." ":()V #14 = Utf8 $getCallSiteArray #15 = Utf8 ()[Lorg/codehaus/groovy/runtime/callsite/CallSite; #16 = NameAndType #14:#15 // $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; #17 = Methodref #2.#16 // demo.$getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; #18 = Utf8 this #19 = Utf8 Ldemo; #20 = Utf8 (Lgroovy/lang/Binding;)V #21 = NameAndType #10:#20 // " ":(Lgroovy/lang/Binding;)V #22 = Methodref #4.#21 // groovy/lang/Script." ":(Lgroovy/lang/Binding;)V #23 = Utf8 context #24 = Utf8 Lgroovy/lang/Binding; #25 = Utf8 main #26 = Utf8 ([Ljava/lang/String;)V #27 = Integer 0 #28 = Utf8 org/codehaus/groovy/runtime/InvokerHelper #29 = Class #28 // org/codehaus/groovy/runtime/InvokerHelper #30 = Utf8 org/codehaus/groovy/runtime/callsite/CallSite #31 = Class #30 // org/codehaus/groovy/runtime/callsite/CallSite #32 = Utf8 call #33 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #34 = NameAndType #32:#33 // call:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #35 = InterfaceMethodref #31.#34 // org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #36 = Utf8 args #37 = Utf8 [Ljava/lang/String; #38 = Utf8 run #39 = Utf8 ()Ljava/lang/Object; #40 = Utf8 addtwo #41 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #42 = Integer 1 #43 = NameAndType #32:#41 // call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #44 = InterfaceMethodref #31.#43 // org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #45 = Utf8 a #46 = Utf8 Ljava/lang/Object; #47 = Utf8 b #48 = Utf8 $getStaticMetaClass #49 = Utf8 ()Lgroovy/lang/MetaClass; #50 = Utf8 java/lang/Object #51 = Class #50 // java/lang/Object #52 = Utf8 getClass #53 = Utf8 ()Ljava/lang/Class; #54 = NameAndType #52:#53 // getClass:()Ljava/lang/Class; #55 = Methodref #51.#54 // java/lang/Object.getClass:()Ljava/lang/Class; #56 = Utf8 org/codehaus/groovy/runtime/ScriptBytecodeAdapter #57 = Class #56 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter #58 = Utf8 initMetaClass #59 = Utf8 (Ljava/lang/Object;)Lgroovy/lang/MetaClass; #60 = NameAndType #58:#59 // initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; #61 = Methodref #57.#60 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; #62 = NameAndType #6:#7 // $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; #63 = Fieldref #2.#62 // demo.$staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; #64 = Utf8 org/codehaus/groovy/reflection/ClassInfo #65 = Class #64 // org/codehaus/groovy/reflection/ClassInfo #66 = Utf8 getClassInfo #67 = Utf8 (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #68 = NameAndType #66:#67 // getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #69 = Methodref #65.#68 // org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #70 = Utf8 getMetaClass #71 = NameAndType #70:#49 // getMetaClass:()Lgroovy/lang/MetaClass; #72 = Methodref #65.#71 // org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass; #73 = Utf8 $callSiteArray #74 = Utf8 Ljava/lang/ref/SoftReference; #75 = Utf8 $createCallSiteArray_1 #76 = Utf8 runScript #77 = String #76 // runScript #78 = Utf8 plus #79 = String #78 // plus #80 = Utf8 $createCallSiteArray #81 = Utf8 ()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray; #82 = Integer 2 #83 = Utf8 java/lang/String #84 = Class #83 // java/lang/String #85 = NameAndType #75:#26 // $createCallSiteArray_1:([Ljava/lang/String;)V #86 = Methodref #2.#85 // demo.$createCallSiteArray_1:([Ljava/lang/String;)V #87 = Utf8 org/codehaus/groovy/runtime/callsite/CallSiteArray #88 = Class #87 // org/codehaus/groovy/runtime/callsite/CallSiteArray #89 = Utf8 (Ljava/lang/Class;[Ljava/lang/String;)V #90 = NameAndType #10:#89 // " ":(Ljava/lang/Class;[Ljava/lang/String;)V #91 = Methodref #88.#90 // org/codehaus/groovy/runtime/callsite/CallSiteArray." ":(Ljava/lang/Class;[Ljava/lang/String;)V #92 = NameAndType #73:#74 // $callSiteArray:Ljava/lang/ref/SoftReference; #93 = Fieldref #2.#92 // demo.$callSiteArray:Ljava/lang/ref/SoftReference; #94 = Utf8 java/lang/ref/SoftReference #95 = Class #94 // java/lang/ref/SoftReference #96 = Utf8 get #97 = NameAndType #96:#39 // get:()Ljava/lang/Object; #98 = Methodref #95.#97 // java/lang/ref/SoftReference.get:()Ljava/lang/Object; #99 = NameAndType #80:#81 // $createCallSiteArray:()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray; #100 = Methodref #2.#99 // demo.$createCallSiteArray:()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray; #101 = Utf8 (Ljava/lang/Object;)V #102 = NameAndType #10:#101 // " ":(Ljava/lang/Object;)V #103 = Methodref #95.#102 // java/lang/ref/SoftReference." ":(Ljava/lang/Object;)V #104 = Utf8 array #105 = Utf8 [Lorg/codehaus/groovy/runtime/callsite/CallSite; #106 = NameAndType #104:#105 // array:[Lorg/codehaus/groovy/runtime/callsite/CallSite; #107 = Fieldref #88.#106 // org/codehaus/groovy/runtime/callsite/CallSiteArray.array:[Lorg/codehaus/groovy/runtime/callsite/CallSite; #108 = Utf8 Code #109 = Utf8 LocalVariableTable #110 = Utf8 LineNumberTable #111 = Utf8 SourceFile{ private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo; flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC public static transient boolean __$stMC; flags: ACC_PUBLIC, ACC_STATIC, ACC_TRANSIENT, ACC_SYNTHETIC private static java.lang.ref.SoftReference $callSiteArray; flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC public demo(); flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=1 0: aload_0 1: invokespecial #13 // Method groovy/lang/Script." ":()V 4: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; 7: astore_1 8: return LocalVariableTable: Start Length Slot Name Signature 4 4 0 this Ldemo; public demo(groovy.lang.Binding); flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=2 0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; 3: astore_2 4: aload_0 5: aload_1 6: invokespecial #22 // Method groovy/lang/Script." ":(Lgroovy/lang/Binding;)V 9: return LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Ldemo; 0 9 1 context Lgroovy/lang/Binding; public static void main(java.lang.String...); flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS Code: stack=4, locals=2, args_size=1 0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; 3: astore_1 4: aload_1 5: ldc #27 // int 0 7: aaload 8: ldc #29 // class org/codehaus/groovy/runtime/InvokerHelper 10: ldc #2 // class demo 12: aload_0 13: invokeinterface #35, 4 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 18: pop 19: return LocalVariableTable: Start Length Slot Name Signature 0 19 0 args [Ljava/lang/String; public java.lang.Object run(); flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=1 0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; 3: astore_1 4: aconst_null 5: areturn 6: aconst_null 7: areturn LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Ldemo; public java.lang.Object addtwo(java.lang.Object, java.lang.Object); flags: ACC_PUBLIC Code: stack=3, locals=4, args_size=3 0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite; 3: astore_3 4: aload_3 5: ldc #42 // int 1 7: aaload 8: aload_1 9: aload_2 10: invokeinterface #44, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.call:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 15: areturn 16: aconst_null 17: areturn LocalVariableTable: Start Length Slot Name Signature 0 16 0 this Ldemo; 0 16 1 a Ljava/lang/Object; 0 16 2 b Ljava/lang/Object; LineNumberTable: line 2: 4 protected groovy.lang.MetaClass $getStaticMetaClass(); flags: ACC_PROTECTED, ACC_SYNTHETIC Code: stack=2, locals=2, args_size=1 0: aload_0 1: invokevirtual #55 // Method java/lang/Object.getClass:()Ljava/lang/Class; 4: ldc #2 // class demo 6: if_acmpeq 14 9: aload_0 10: invokestatic #61 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; 13: areturn 14: getstatic #63 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; 17: astore_1 18: aload_1 19: ifnonnull 34 22: aload_0 23: invokevirtual #55 // Method java/lang/Object.getClass:()Ljava/lang/Class; 26: invokestatic #69 // Method org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; 29: dup 30: astore_1 31: putstatic #63 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; 34: aload_1 35: invokevirtual #72 // Method org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass; 38: areturn private static void $createCallSiteArray_1(java.lang.String[]); flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: ldc #27 // int 0 3: ldc #77 // String runScript 5: aastore 6: aload_0 7: ldc #42 // int 1 9: ldc #79 // String plus 11: aastore 12: return private static org.codehaus.groovy.runtime.callsite.CallSiteArray $createCallSiteArray(); flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=4, locals=1, args_size=0 0: ldc #82 // int 2 2: anewarray #84 // class java/lang/String 5: astore_0 6: aload_0 7: invokestatic #86 // Method $createCallSiteArray_1:([Ljava/lang/String;)V 10: new #88 // class org/codehaus/groovy/runtime/callsite/CallSiteArray 13: dup 14: ldc #2 // class demo 16: aload_0 17: invokespecial #91 // Method org/codehaus/groovy/runtime/callsite/CallSiteArray." ":(Ljava/lang/Class;[Ljava/lang/String;)V 20: areturn private static org.codehaus.groovy.runtime.callsite.CallSite[] $getCallSiteArray(); flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=3, locals=1, args_size=0 0: getstatic #93 // Field $callSiteArray:Ljava/lang/ref/SoftReference; 3: ifnull 20 6: getstatic #93 // Field $callSiteArray:Ljava/lang/ref/SoftReference; 9: invokevirtual #98 // Method java/lang/ref/SoftReference.get:()Ljava/lang/Object; 12: checkcast #88 // class org/codehaus/groovy/runtime/callsite/CallSiteArray 15: dup 16: astore_0 17: ifnonnull 35 20: invokestatic #100 // Method $createCallSiteArray:()Lorg/codehaus/groovy/runtime/callsite/CallSiteArray; 23: astore_0 24: new #95 // class java/lang/ref/SoftReference 27: dup 28: aload_0 29: invokespecial #103 // Method java/lang/ref/SoftReference." ":(Ljava/lang/Object;)V 32: putstatic #93 // Field $callSiteArray:Ljava/lang/ref/SoftReference; 35: aload_0 36: getfield #107 // Field org/codehaus/groovy/runtime/callsite/CallSiteArray.array:[Lorg/codehaus/groovy/runtime/callsite/CallSite; 39: areturn }
人肉反编译,大概是这个样子来使用Groovy的runtime的,
private static void addtwo(Object o1, Object o2) throws Throwable { String[] names = new String[]{ "plus"}; CallSiteArray callSiteArray = new CallSiteArray(Main.class, names); CallSite callSite = callSiteArray.array[0]; System.out.println(callSite.call(o1, o2)); }
plus
就是我们上面说到的方法名+
。
写个栗子跑跑看,
public static void main(String[] args) throws Throwable { addtwo(7, 7); addtwo("hello,", "world"); addtwo(new Receiver(), new Parameter()); }
public class Receiver implements GroovyInterceptable { @Override public Object invokeMethod(String name, Object args) { System.out.println("methodName->" + name); System.out.println("args->" + args); if(args instanceof Object[]) { Object[] params = (Object[])args; System.out.println("params->"); for (Object param : params) { System.out.println(param); } } return "Receiver#invokeMethod"; } ...}
输出例如以下,Receiver
的输出跟Groovy的有关,这个以后再说。
14hello,worldmethodName->plusargs->[Ljava.lang.Object;@6b573f80params->me.kisimple.just4fun.Parameter@2d0a238eReceiver#invokeMethod
Groovy的runtime应该也是个不小的坑。以后再研究。以下来看下invokedynamic
。
使用invokedynamic
从上面的栗子能够看到,groovyc须要生成非常多runtime相关的字节码,为了使动态类型语言在Java平台上更easy实现,JavaSE 7引入了invokedynamic
指令。
简单来讲,执行时虚拟机在执行invokedynamic
指令时会执行用户自己定义的bootstrap方法,用户能够在bootstrap方法中给出调用点的详细实现,这样就能达到执行时才确定详细实现的目的了。invokedynamic
与的详细内容參,以下我们来看下Groovy是怎样使用invokedynamic
的。
依据更换一下jar包,编译时加上--indy
选项。得到的字节码例如以下,
Classfile /home/blues/Projects/groovy-core/demo.class Last modified Mar 8, 2015; size 1839 bytes MD5 checksum 5bbf49b81b00dece4523fbf55f8e7266 Compiled from "demo.groovy"public class demo extends groovy.lang.Script BootstrapMethods: 0: #31 invokestatic org/codehaus/groovy/vmplugin/v7/IndyInterface.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; Method arguments: #33 runScript #34 0 1: #31 invokestatic org/codehaus/groovy/vmplugin/v7/IndyInterface.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; Method arguments: #48 plus #34 0 SourceFile: "demo.groovy" minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPERConstant pool: #1 = Utf8 demo #2 = Class #1 // demo #3 = Utf8 groovy/lang/Script #4 = Class #3 // groovy/lang/Script #5 = Utf8 demo.groovy #6 = Utf8 $staticClassInfo #7 = Utf8 Lorg/codehaus/groovy/reflection/ClassInfo; #8 = Utf8 __$stMC #9 = Utf8 Z #10 = Utf8#11 = Utf8 ()V #12 = NameAndType #10:#11 // " ":()V #13 = Methodref #4.#12 // groovy/lang/Script." ":()V #14 = Utf8 this #15 = Utf8 Ldemo; #16 = Utf8 (Lgroovy/lang/Binding;)V #17 = NameAndType #10:#16 // " ":(Lgroovy/lang/Binding;)V #18 = Methodref #4.#17 // groovy/lang/Script." ":(Lgroovy/lang/Binding;)V #19 = Utf8 context #20 = Utf8 Lgroovy/lang/Binding; #21 = Utf8 main #22 = Utf8 ([Ljava/lang/String;)V #23 = Utf8 org/codehaus/groovy/runtime/InvokerHelper #24 = Class #23 // org/codehaus/groovy/runtime/InvokerHelper #25 = Utf8 org/codehaus/groovy/vmplugin/v7/IndyInterface #26 = Class #25 // org/codehaus/groovy/vmplugin/v7/IndyInterface #27 = Utf8 bootstrap #28 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; #29 = NameAndType #27:#28 // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; #30 = Methodref #26.#29 // org/codehaus/groovy/vmplugin/v7/IndyInterface.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; #31 = MethodHandle #6:#30 // invokestatic org/codehaus/groovy/vmplugin/v7/IndyInterface.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; #32 = Utf8 runScript #33 = String #32 // runScript #34 = Integer 0 #35 = Utf8 invoke #36 = Utf8 (Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/String;)Ljava/lang/Object; #37 = NameAndType #35:#36 // invoke:(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/String;)Ljava/lang/Object; #38 = InvokeDynamic #0:#37 // #0:invoke:(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/String;)Ljava/lang/Object; #39 = Utf8 args #40 = Utf8 [Ljava/lang/String; #41 = Utf8 run #42 = Utf8 ()Ljava/lang/Object; #43 = Utf8 java/lang/Throwable #44 = Class #43 // java/lang/Throwable #45 = Utf8 addtwo #46 = Utf8 (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #47 = Utf8 plus #48 = String #47 // plus #49 = NameAndType #35:#46 // invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #50 = InvokeDynamic #1:#49 // #1:invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; #51 = Utf8 a #52 = Utf8 Ljava/lang/Object; #53 = Utf8 b #54 = Utf8 $getStaticMetaClass #55 = Utf8 ()Lgroovy/lang/MetaClass; #56 = Utf8 java/lang/Object #57 = Class #56 // java/lang/Object #58 = Utf8 getClass #59 = Utf8 ()Ljava/lang/Class; #60 = NameAndType #58:#59 // getClass:()Ljava/lang/Class; #61 = Methodref #57.#60 // java/lang/Object.getClass:()Ljava/lang/Class; #62 = Utf8 org/codehaus/groovy/runtime/ScriptBytecodeAdapter #63 = Class #62 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter #64 = Utf8 initMetaClass #65 = Utf8 (Ljava/lang/Object;)Lgroovy/lang/MetaClass; #66 = NameAndType #64:#65 // initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; #67 = Methodref #63.#66 // org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; #68 = NameAndType #6:#7 // $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; #69 = Fieldref #2.#68 // demo.$staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; #70 = Utf8 org/codehaus/groovy/reflection/ClassInfo #71 = Class #70 // org/codehaus/groovy/reflection/ClassInfo #72 = Utf8 getClassInfo #73 = Utf8 (Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #74 = NameAndType #72:#73 // getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #75 = Methodref #71.#74 // org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; #76 = Utf8 getMetaClass #77 = NameAndType #76:#55 // getMetaClass:()Lgroovy/lang/MetaClass; #78 = Methodref #71.#77 // org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass; #79 = Utf8 Code #80 = Utf8 LocalVariableTable #81 = Utf8 StackMapTable #82 = Utf8 LineNumberTable #83 = Utf8 BootstrapMethods #84 = Utf8 SourceFile{ private static org.codehaus.groovy.reflection.ClassInfo $staticClassInfo; flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC public static transient boolean __$stMC; flags: ACC_PUBLIC, ACC_STATIC, ACC_TRANSIENT, ACC_SYNTHETIC public demo(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #13 // Method groovy/lang/Script." ":()V 4: return LocalVariableTable: Start Length Slot Name Signature 4 0 0 this Ldemo; public demo(groovy.lang.Binding); flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: invokespecial #18 // Method groovy/lang/Script." ":(Lgroovy/lang/Binding;)V 5: return LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Ldemo; 0 5 1 context Lgroovy/lang/Binding; public static void main(java.lang.String...); flags: ACC_PUBLIC, ACC_STATIC, ACC_VARARGS Code: stack=3, locals=1, args_size=1 0: ldc #24 // class org/codehaus/groovy/runtime/InvokerHelper 2: ldc #2 // class demo 4: aload_0 5: invokedynamic #38, 0 // InvokeDynamic #0:invoke:(Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/String;)Ljava/lang/Object; 10: pop 11: return LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; public java.lang.Object run(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn 2: nop 3: athrow LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Ldemo; StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 2 locals = [] stack = [ class java/lang/Throwable ] public java.lang.Object addtwo(java.lang.Object, java.lang.Object); flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=3 0: aload_1 1: aload_2 2: invokedynamic #50, 0 // InvokeDynamic #1:invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; 7: areturn 8: nop 9: athrow LocalVariableTable: Start Length Slot Name Signature 0 8 0 this Ldemo; 0 8 1 a Ljava/lang/Object; 0 8 2 b Ljava/lang/Object; LineNumberTable: line 2: 0 StackMapTable: number_of_entries = 1 frame_type = 255 /* full_frame */ offset_delta = 8 locals = [] stack = [ class java/lang/Throwable ] protected groovy.lang.MetaClass $getStaticMetaClass(); flags: ACC_PROTECTED, ACC_SYNTHETIC Code: stack=2, locals=2, args_size=1 0: aload_0 1: invokevirtual #61 // Method java/lang/Object.getClass:()Ljava/lang/Class; 4: ldc #2 // class demo 6: if_acmpeq 14 9: aload_0 10: invokestatic #67 // Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.initMetaClass:(Ljava/lang/Object;)Lgroovy/lang/MetaClass; 13: areturn 14: getstatic #69 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; 17: astore_1 18: aload_1 19: ifnonnull 34 22: aload_0 23: invokevirtual #61 // Method java/lang/Object.getClass:()Ljava/lang/Class; 26: invokestatic #75 // Method org/codehaus/groovy/reflection/ClassInfo.getClassInfo:(Ljava/lang/Class;)Lorg/codehaus/groovy/reflection/ClassInfo; 29: dup 30: astore_1 31: putstatic #69 // Field $staticClassInfo:Lorg/codehaus/groovy/reflection/ClassInfo; 34: aload_1 35: invokevirtual #78 // Method org/codehaus/groovy/reflection/ClassInfo.getMetaClass:()Lgroovy/lang/MetaClass; 38: areturn StackMapTable: number_of_entries = 2 frame_type = 14 /* same */ frame_type = 252 /* append */ offset_delta = 19 locals = [ class org/codehaus/groovy/reflection/ClassInfo ]}
能够看到生成的字节码确实比不使用invokedynamic
要少得多。
跟上面一样,人肉反编译,invokedynamic
相关的runtime大概是这么来使用的。
private static void addtwo(Object o1, Object o2) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(Object.class, Object.class, Object.class); java.lang.invoke.CallSite callSite = IndyInterface.bootstrap(lookup, "invoke", mt, "plus", 0); MethodHandle mh = callSite.getTarget(); System.out.println(mh.invoke(o1, o2)); }
有一点值得说明的是,通过字节码能够看到,除了bootstrap方法默认的三个參数。groovyc还多生成了两个參数。
2: invokedynamic #50, 0 // InvokeDynamic #1:invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
1: #31 invokestatic org/codehaus/groovy/vmplugin/v7/IndyInterface.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;I)Ljava/lang/invoke/CallSite; Method arguments: #48 plus #34 0
也就是说给bootstrap方法传递的methodName
是invoke
,methodType
是(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object
,而我们关心的方法名。也就是plus
。是作为额外的參数传进去的。所以我们上面的栗子中对bootstrap方法的调用才会是IndyInterface.bootstrap(lookup, "invoke", mt, "plus", 0)
这个样子的。假设是要实现减法。则能够这么写IndyInterface.bootstrap(lookup, "invoke", mt, "minus", 0)
。也就是通过额外的參数来进行方法的分发。至于为什么要这么来实现。须要后面再研究下Groovy的runtime看看了。
简单实现
Groovy的runtime是比較复杂的,以下我们用相关的API实现一个简单一点的,仅仅能进行整数的加法与字符串的加法。
public class IntegerOps { public static Integer adder(Integer x, Integer y) { return x + y; }}
public class StringOps { public static String adder(String x, String y) { return x + y; }}
public class BootstrapMethod { public static CallSite link(MethodHandles.Lookup callerClass, String dynMethodName, MethodType dynMethodType) throws Throwable { if("adder".equals(dynMethodName)) { MethodHandle mh; Class receiverType = dynMethodType.parameterType(0); if(receiverType.equals(Integer.class)) { mh = callerClass.findStatic( IntegerOps.class, "adder", MethodType.methodType(Integer.class, Integer.class, Integer.class)); } else if(receiverType.equals(String.class)) { mh = callerClass.findStatic( StringOps.class, "adder", MethodType.methodType(String.class, String.class, String.class)); } else { return null; } return new ConstantCallSite(mh); } return null; }}
private static void addtwo(Object o1, Object o2) throws Throwable { MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodType mt = MethodType.methodType(o1.getClass(), o1.getClass(), o2.getClass()); java.lang.invoke.CallSite callSite = BootstrapMethod.link(lookup, "adder", mt); MethodHandle mh = callSite.getTarget(); System.out.println(mh.invoke(o1, o2)); }
有两点须要说明。首先是MethodType
,栗子的bootstrap方法须要依据MethodType
来进行方法分发(这里我们仅仅依据第一个參数的类型来推断)。究竟如今是要进行整数的加法,还是要进行字符串的加法。
但因为这个MethodType
是invokedynamic
指令的參数,因此个人认为这样设计事实上是有问题的,所以groovyc也才会直接传的(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object
。
另外一点是MethodHandle
的问题。当我们执行addtwo(7, 7L)
也就是第二个參数传的是个long
型就跪了。java.lang.ClassCastException: Cannot cast java.lang.Long to java.lang.Integer
,须要转换一下。这里就不赘述了,想了解的同学看下API文档就清楚了。
參考资料
版权声明:本文博主原创文章。博客,未经同意不得转载。