博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Scripting Java #3:Groovy与invokedynamic
阅读量:5291 次
发布时间:2019-06-14

本文共 29453 字,大约阅读时间需要 98 分钟。

只需看看今天Groovy语言实现机制。在此之前,是第一个推倒与语言在实现上面的一些差异。

静态类型 vs. 动态类型

看以下这个简单的栗子。

def addtwo(a, b) {    return a + b;}

静态类型语言与动态类型语言对于上面这个简单的加法实现全然不同。静态类型语言。比如Java。语言的编译器在编译时就已经进行类型检查,所以能够将+运算符编译成特定的指令。语言的runtime系统能够直接执行该指令。比如javac会将两个int类型的+运算编译成iadd指令,执行时由JVM直接执行iadd指令。

而对于动态类型语言,因为须要到执行时才干确定变量的类型,因此运算符的详细实现也须要到执行时才干确定。a + b会被编译成相似(+ a b)这个方案调用(Lisp风格^_^)。+仅仅是个方法名。语言的runtime系统须要依据方法名(+)和參数类型(ab的类型)来确定这个加法运算的详细实现。

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方法传递的methodNameinvokemethodType(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来进行方法分发(这里我们仅仅依据第一个參数的类型来推断)。究竟如今是要进行整数的加法,还是要进行字符串的加法。

但因为这个MethodTypeinvokedynamic指令的參数,因此个人认为这样设计事实上是有问题的,所以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文档就清楚了。

參考资料

版权声明:本文博主原创文章。博客,未经同意不得转载。

转载于:https://www.cnblogs.com/mfrbuaa/p/4908670.html

你可能感兴趣的文章
springCloud的zuul基于config和github实现热更新
查看>>
python学习笔记(pip下载安装)
查看>>
CSS
查看>>
shell 管道和tee使用时获取前面命令返回值
查看>>
[LeetCode] 55. Jump Game_ Medium tag: Dynamic Programming
查看>>
[Cypress] Stub a Post Request for Successful Form Submission with Cypress
查看>>
[TypeScript] Understanding Generics with RxJS
查看>>
一、基础篇--1.3进程和线程-基本概念
查看>>
Linux kernel ‘ioapic_read_indirect’函数拒绝服务漏洞
查看>>
WordPress GRAND FlAGallery插件“s”跨站脚本漏洞
查看>>
zoj3690 Choosing number
查看>>
阳宇宸:网站开发的常用语言
查看>>
CF 600 E 启发式合并
查看>>
保险配置
查看>>
【高并发解决方案】2、集群概述
查看>>
Mysql-SqlServer区别
查看>>
Windows Phone锁屏背景相关代码
查看>>
Linux - mkdir -p a/b/c
查看>>
Maven安装详细图文教程
查看>>
eclipse中启动tomcat报错 java.lang.ClassNotFoundException
查看>>