zoukankan      html  css  js  c++  java
  • Scripting Java #3:Groovy与invokedynamic

    只需看看今天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的编译器(groovyc)是怎么苦了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_SUPER
    
    Constant 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               <init>
       #11 = Utf8               ()V
       #12 = NameAndType        #10:#11       //  "<init>":()V
       #13 = Methodref          #4.#12        //  groovy/lang/Script."<init>":()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       //  "<init>":(Lgroovy/lang/Binding;)V
       #22 = Methodref          #4.#21        //  groovy/lang/Script."<init>":(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       //  "<init>":(Ljava/lang/Class;[Ljava/lang/String;)V
       #91 = Methodref          #88.#90       //  org/codehaus/groovy/runtime/callsite/CallSiteArray."<init>":(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      //  "<init>":(Ljava/lang/Object;)V
      #103 = Methodref          #95.#102      //  java/lang/ref/SoftReference."<init>":(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."<init>":()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."<init>":(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."<init>":(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."<init>":(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的MOP有关,这个以后再说。

    14
    hello,world
    methodName->plus
    args->[Ljava.lang.Object;@6b573f80
    params->
    me.kisimple.just4fun.Parameter@2d0a238e
    Receiver#invokeMethod

    Groovy的runtime应该也是个不小的坑。以后再研究。以下来看下invokedynamic

    使用invokedynamic

    从上面的栗子能够看到,groovyc须要生成非常多runtime相关的字节码,为了使动态类型语言在Java平台上更easy实现,JavaSE 7引入了invokedynamic指令。

    简单来讲,执行时虚拟机在执行invokedynamic指令时会执行用户自己定义的bootstrap方法,用户能够在bootstrap方法中给出调用点的详细实现,这样就能达到执行时才确定详细实现的目的了。invokedynamicMethodHandle的详细内容參官方文档,以下我们来看下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_SUPER
    Constant 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               <init>
      #11 = Utf8               ()V
      #12 = NameAndType        #10:#11        //  "<init>":()V
      #13 = Methodref          #4.#12         //  groovy/lang/Script."<init>":()V
      #14 = Utf8               this
      #15 = Utf8               Ldemo;
      #16 = Utf8               (Lgroovy/lang/Binding;)V
      #17 = NameAndType        #10:#16        //  "<init>":(Lgroovy/lang/Binding;)V
      #18 = Methodref          #4.#17         //  groovy/lang/Script."<init>":(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."<init>":()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."<init>":(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,须要MethodHandle#asType转换一下。这里就不赘述了,想了解的同学看下API文档就清楚了。

    參考资料

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

  • 相关阅读:
    Privilege(特权)
    Access Control Model(访问控制模型)
    nrm 常用命令
    nrm安装完成运行报错 环境变量配置问题
    nrm : 无法加载文件 C:Program Files odejs rm.ps1,因为在此系统上禁止运行脚本。
    ts 实现简单的video播放器 源码: https://github.com/yuhualiang/miProjectTwo
    ts 弹窗组件
    替换字符串中图片的src
    03-05 变量声明
    03-04 变量声明
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4908670.html
Copyright © 2011-2022 走看看