Twitterでのやり取りでGroovyのinvokedynamicの話題が出たので、まず簡単に試してみました。
まず以下のHelloWorld.groovyを作ります。
println 'Hello, world!'
普通にコンパイルする。
$ groovy -v Groovy Version: 2.4.0 JVM: 1.8.0_65 Vendor: Oracle Corporation OS: Mac OS X $ groovyc HelloWorld.groovy
javapする。
$ javap -v HelloWorld
...
public java.lang.Object run();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=1
0: invokestatic #17 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
3: astore_1
4: aload_1
5: ldc #40 // int 1
7: aaload
8: aload_0
9: ldc #42 // String Hello, world!
11: invokeinterface #46, 3 // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
16: areturn
17: aconst_null
18: areturn
LocalVariableTable:
Start Length Slot Name Signature
0 17 0 this LHelloWorld;
LineNumberTable:
line 1: 4
11でGroovy実装のCallSiteなのかな?を使っており、indyではありません。Groovyのcore以外のクラスでindyを使うためには、indyフラグをonにする必要があります。
$ groovyc -indy HelloWorld.groovy
はいjavap。
$ javap -v HelloWorld
...
public java.lang.Object run();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: ldc #44 // String Hello, world!
3: invokedynamic #50, 0 // InvokeDynamic #1:invoke:(LHelloWorld;Ljava/lang/String;)Ljava/lang/Object;
8: areturn
9: nop
10: athrow
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LHelloWorld;
LineNumberTable:
line 1: 0
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 9
locals = []
stack = [ class java/lang/Throwable ]
indyが使われるようになりました。 公式ドキュメントには、indyフラグのon/offでの挙動について記述があります。
For user compiled classes:
| indy flag | off | on |
|---|---|---|
| normal jar | call site caching | N/A |
| indy jar | call site caching | invokedynamic |
For core Groovy classes:
| indy flag | off | on |
|---|---|---|
| normal jar | call site caching | N/A |
| indy jar | invokedynamic | invokedynamic |
indyフラグをoffにしていても、Groovyコアのクラスはindy使います、ということです。
デコンパイラで見てみる
これらのclassファイルをデコンパイラで見てみましょう。 デコンパイラもいろんなものがありますが、ここではJDを使います。
indyフラグ off
import groovy.lang.Binding; import groovy.lang.Script; import org.codehaus.groovy.runtime.InvokerHelper; import org.codehaus.groovy.runtime.callsite.CallSite; public class HelloWorld extends Script { public HelloWorld() {} public HelloWorld(Binding context) { super(context); } public static void main(String... args) { CallSite[] arrayOfCallSite = $getCallSiteArray(); arrayOfCallSite[0].call(InvokerHelper.class, HelloWorld.class, args); } public Object run() { CallSite[] arrayOfCallSite = $getCallSiteArray();return arrayOfCallSite[1].callCurrent(this, "Hello, world!");return null; } }
デコンパイルできました。$getCallSiteArray()には触れない(触れられない)。
indyフラグ on
// INTERNAL ERROR //