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 //