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系统须要依据方法名(+
)和參数类型(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的编译器(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方法中给出调用点的详细实现,这样就能达到执行时才确定详细实现的目的了。invokedynamic
与MethodHandle
的详细内容參官方文档,以下我们来看下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方法传递的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
,须要MethodHandle#asType
转换一下。这里就不赘述了,想了解的同学看下API文档就清楚了。
參考资料
- http://docs.oracle.com/javase/8/docs/technotes/guides/vm/multiple-language-support.html
- http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html
- http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.invokedynamic
- http://docs.groovy-lang.org/docs/groovy-2.4.0/pdf/wiki-snapshot.pdf
版权声明:本文博主原创文章。博客,未经同意不得转载。
Scripting Java #3:Groovy与invokedynamic的更多相关文章
- Java与groovy混编 —— 一种兼顾接口清晰和实现敏捷的开发方式
有大量平均水平左右的"工人"可被选择.参与进来 -- 这意味着好招人 有成熟的.大量的程序库可供选择 -- 这意味着大多数项目都是既有程序库的拼装,标准化程度高而定制化场景少 开发 ...
- Groovy小结:java调用Groovy方法并传递参数
Groovy小结:java调用Groovy方法并传递参数 @(JAVA总结) 1. 场景描述 在网上查了资料发现,java有三种方式调用groovy脚本.但是真正在实际的服务器环境中,嵌入groovy ...
- 使用 Java 执行 groovy 脚本或方法
1. 引入依赖 <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groo ...
- Java执行groovy脚本的两种方式
记录Java执行groovy脚本的两种方式,简单粗暴: 一种是通过脚本引擎ScriptEngine提供的eval(String)方法执行脚本内容:一种是执行groovy脚本: 二者都通过Invocab ...
- Java和Groovy脚本互相调用实例
本实例是GODU动态脚本的一个技术简化版,演示了java调groovy,groovy又调java的运行过程. 测试用例: package com.boco.godu.integration; impo ...
- java 和groovy的混合使用
在应用中,我们可以在一个Java类.一个Groovy类或者一个Groovy脚本中实现某个特定功能.之后可以在Java类.Groovy类或Groovy脚本中调用该功能. 在groovy 使用groovy ...
- 工作随笔——Java调用Groovy类的方法、传递参数和获取返回值
接触Groovy也快一年了,一直在尝试怎么将Groovy引用到日常工作中来.最近在做一个功能的时候,花了点时间重新看了下Java怎么调用Groovy的方法.传递参数和获取返回值. 示例Groovy代码 ...
- 研究dotnet动态语言IronPython(对应Java的Groovy)
Java的标配动态语言Groovy,两者搭配可以说是完美!大规模运用的项目,如:Jenkins,通过动态语言可以弥补先天系统缺陷的bug,再者就是加强自己的业务逻辑等. 那么换过dotnet上,对应的 ...
- Java 调用 groovy 脚本文件,groovy 访问 MongoDB
groovy 访问 MongoDB 示例: shell.groovy package db import com.gmongo.GMongoClient import com.mongodb.Basi ...
随机推荐
- Android---OpenGL ES之添加动作
本文译自:http://developer.android.com/training/graphics/opengl/motion.html 在屏幕上绘制对象是OpenGL的最基本功能,你可以使用其他 ...
- [Android学习笔记]页面布局
线性布局:LinearLayout 1.集成ViewGroup,故可容纳多个View 2.线性布局,可设置水平或者垂直方向 相对布局:RelativeLayout
- C++ 指针—02 指针与引用的对照
★同样点: ●都是地址的概念: 指针指向一块内存,它的内容是所指内存的地址:而引用则是某块内存的别名. ★不同点: ●指针是一个实体,而引用仅是个别名: ●引用仅仅能在定义时被初始化一次,之后不可变: ...
- 以正确的方式开源 Python 项目 - 技术翻译 - 开源中国社区
以正确的方式开源 Python 项目 - 技术翻译 - 开源中国社区 以正确的方式开源 Python 项目 英文原文:Open Sourcing a Python Project the Right ...
- 图像处理特征不变算子系列之Moravec算子(一)
论文转载请注明出处:http://blog.csdn.net/kezunhai 1977年,Moravec提出了兴趣点(Points of Interests)的概念,并应用于解决Stanford C ...
- PVPlayer的实现方式
关于opencore下多媒体播放,在mediaserver进程里面仅仅有一行代码: MediaPlayerService::instantiate(); 这行代码的作用是初始化一个MediaPlaye ...
- Eclipse 未开始 【Ubuntu】
/usr/lib/eclipse/configuration/1408532831122.log : !SESSION 2014-08-20 19:07:11.055 ---------------- ...
- 一个高速做git提交的脚本
用于高速将项目中的全部改变push到代码仓库.能够替代下面操作: git add . git commit -m "" git push 项目地址: https://github. ...
- CacheHelper工具类的使用
package com.bbcmart.util; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import ne ...
- GIS Tools for Hadoop 详细介绍
2013年Esri美国在开发者大会上演示了GIS数据结合Hadoop分析的一个示例,这个示例赢得了听众的阵阵掌声,我们也许对GIS不是很陌生,但是对Hadoop却不是很清楚,其实Esri是用Java开 ...