只需看看今天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文档就清楚了。

參考资料

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

Scripting Java #3:Groovy与invokedynamic的更多相关文章

  1. Java与groovy混编 —— 一种兼顾接口清晰和实现敏捷的开发方式

    有大量平均水平左右的"工人"可被选择.参与进来 -- 这意味着好招人 有成熟的.大量的程序库可供选择 -- 这意味着大多数项目都是既有程序库的拼装,标准化程度高而定制化场景少 开发 ...

  2. Groovy小结:java调用Groovy方法并传递参数

    Groovy小结:java调用Groovy方法并传递参数 @(JAVA总结) 1. 场景描述 在网上查了资料发现,java有三种方式调用groovy脚本.但是真正在实际的服务器环境中,嵌入groovy ...

  3. 使用 Java 执行 groovy 脚本或方法

    1. 引入依赖 <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groo ...

  4. Java执行groovy脚本的两种方式

    记录Java执行groovy脚本的两种方式,简单粗暴: 一种是通过脚本引擎ScriptEngine提供的eval(String)方法执行脚本内容:一种是执行groovy脚本: 二者都通过Invocab ...

  5. Java和Groovy脚本互相调用实例

    本实例是GODU动态脚本的一个技术简化版,演示了java调groovy,groovy又调java的运行过程. 测试用例: package com.boco.godu.integration; impo ...

  6. java 和groovy的混合使用

    在应用中,我们可以在一个Java类.一个Groovy类或者一个Groovy脚本中实现某个特定功能.之后可以在Java类.Groovy类或Groovy脚本中调用该功能. 在groovy 使用groovy ...

  7. 工作随笔——Java调用Groovy类的方法、传递参数和获取返回值

    接触Groovy也快一年了,一直在尝试怎么将Groovy引用到日常工作中来.最近在做一个功能的时候,花了点时间重新看了下Java怎么调用Groovy的方法.传递参数和获取返回值. 示例Groovy代码 ...

  8. 研究dotnet动态语言IronPython(对应Java的Groovy)

    Java的标配动态语言Groovy,两者搭配可以说是完美!大规模运用的项目,如:Jenkins,通过动态语言可以弥补先天系统缺陷的bug,再者就是加强自己的业务逻辑等. 那么换过dotnet上,对应的 ...

  9. Java 调用 groovy 脚本文件,groovy 访问 MongoDB

    groovy 访问 MongoDB 示例: shell.groovy package db import com.gmongo.GMongoClient import com.mongodb.Basi ...

随机推荐

  1. 14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器

    14.4.4 Configuring the Memory Allocator for InnoDB InnoDB 配置内存分配器 当InnoDB 被开发, 内分配齐 提供了与操作系统和运行库往往缺乏 ...

  2. Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

    设备控制软件编程涉及到的基本通信方式主要有TCP/IP与串口,用到的数据通信协议有Fins与ModBus. 更高级别的通信如.net中的Remoting与WCF在进行C/S架构软件开发时会采用. 本篇 ...

  3. boost 的函数式编程库 Phoenix入门学习

    这篇文章是我学习boost phoenix的总结. 序言 Phoenix是一个C++的函数式编程(function programming)库.Phoenix的函数式编程是构建在函数对象上的.因此,了 ...

  4. Win8下在Vmware11中安装使用苹果系统OS X 10.10

    原文:Win8下在Vmware11中安装使用苹果系统OS X 10.10   近来因为需要做 iOS 的项目,所以需要多花一些时间看看敲敲代码.因为自己手头上并没有 Mac(过年为了闲的时候能玩玩游戏 ...

  5. ArcGIS多面体(multipatch)解析——引

    多面体(multipatch)结构在ArcGIS数据结构中是与点.线.面平行的一种数据结构,对于ArcGIS三维来说是一个很核心的结构,有了它,ArcGIS平台才可以灵活的描述规则和不规则的三维实体. ...

  6. syslog-ng 3.4.3 发布,强烈建议升级 - 开源中国社区

    syslog-ng 3.4.3 发布,强烈建议升级 - 开源中国社区 syslog-ng 3.4.3 发布,强烈建议升级    oschina 发布于: 2013年08月16日 (2评)    分享到 ...

  7. Android.mk编译.apk .so .jar .a第三方.apk .so .jar .a的方法

    一.编译一个简单的APK LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java s ...

  8. hdu 1251 统计难题 (map水过)

    # include <stdio.h> # include <algorithm> # include <string> # include <map> ...

  9. hdu1114(完全背包)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1114 分析:很裸的一道完全背包题,只是这里求装满背包后使得价值最少,只需初始化数组dp为inf:dp[ ...

  10. 【Python】Coding the Matrix:Week 5 Perspective Lab

    这个Lab的内容光是说明就有7页之巨,我反复看了很久才看懂一点点,Lab主要完成的是从不同坐标系表示之间变换的方法. 原始的图片,从Camera basis的表示转换成WhiteBoard basis ...