DEV Community

Pramoth Suwanpech
Pramoth Suwanpech

Posted on

Java InvokeDynamic (part 2 CallSite)

ในตอนนี้เราจะ เข้าไปใกล้ invokedynamic เพิ่มขึ้นอีกนิด
จาก part 1 เราได้เห็นวิธีการเรียก method ด้วย MethodHandle.invoke*() ไปแล้ว ในตอนนี้เราจะมาอธิบายการทำงานของ invokedynamic ในรูปแบบ java api และบทความถัดไปจะทดลองทำให้ดูในรูปแบบ bytecode โดยใช้ ASM

invokespecial,invokestatic จะ resolve target method โดยตรงจาก class
invokevirtual,invokeinterface จะ resolve target method จาก virtual method table

invoke** ด้านบนทั้งหมดจะมีกฏตายตัวในการ resove target method แต่สำหรับ invokedynamic จะ delegate การ resolve target method ไปให้เป็นหน้าที่ของ bootstrap method เพื่อเปิดโอกาสที่ผู้ที่อิมพรีเมนภาษาต่างๆสามารถ customize logic ในการ resolve method เองได้

bootstrap method จะ associate กับ invokedynamic instruction หรือพูดได้ว่า invokedynamic จะเรียก bootstrap method เพื่อให้มันรีเทิร์น java.lang.invoke.CallSite กลับมาให้ ซึ่งใน CallSite ก็จะมี target (CallSite.getTarget()) ที่เป็น MethodHandle ที่จะเป็น target method ที่เราต้องการให้เรียกอีกที
การเรียก bootstrap method สามารถคืนค่า MethodHandle เดิมเมือ่เรียกครั้งถัดไปหรืออาจจะไม่คืนค่าเดิมก้ได้ โดย CallSite จะมีสองแบบ constant และ mutable และเราสามารถสร้าง bootstrap method สำหรับแต่ละคำสั่ง invokedynamic หรือจะแชร์ bootstrap method กันก็ได้

invokedynamic จะมีรุปแบบดังนี้

invokedynamic boostrap-method-ref:[method-name:method-type]

โดย

  • boostrap-method-ref จะอ้างถึง BootstrapMethod attribute ใน constant pool
  • [method-name : method-type] จะอ้างถึง NameAndType_info ใน constant pool ซึ่ง method-name จะเป็นชื่อ target method และ method-type จะเป็น args และ return type

bootstrap method จะมีหลายรูปแบบ (สามารถอ่านเพิ่มเติมจาก JSR-292) การเรียก bootstrap method จะมีการระบุ parameter อย่างน้อย 3 ตัว ตามตัวอย่างนี้

CallSite bootstrap(Lookup caller, String name, MethodType type)

การทำงานของ invokedynamic จะทำงานหลักๆอยู่ 3 step สรุปโดยย่อจาก 6.5.invokedynamic

  1. prepare parameter Lookup,name,MethodType (name,MethodType จะ resove reference จาก constant pool แต่ Lookup ผมไม่ทราบ ไม่ได้มีอธิบายไว้ ถ้าดูจากโคีด มันจะใช้ Reflection.getCallerClass() ซึ่งเป็น native method)
  2. link คือขั้นตอนการเรียก bootstrap method
  3. invoke

เด๋วจะขอยกตัวอย่างการจำลองการ invokedynamic โดย java api เลยละกันครับ

public class MethodHandleTest2 {
    public static void main(String[] args) throws Throwable {
        // บรรทัดถัดไปคือ bytecode ของ invokedynamic นะครับ #1 #2 #3  จะถูกเก็บไว้ใน constant pool
        // invokedynamic #1:[#2:#3]

        // step 1 preapre อันนี้ทำโดย JVM
        final MethodHandles.Lookup callerLookup = MethodHandles.lookup();
        // #2
        String targetMethodName = "target";
        // #3
        MethodType methodType = MethodType.methodType(void.class, String.class);


        // step 2 link
        CallSite callSite = boostrapMethod(callerLookup, targetMethodName, methodType);


        // step 3 invoke
        // ในเคสของ ConstantCallSite dynamicInvoker() จะ delegate ไป getTarget()
        callSite.dynamicInvoker().invokeWithArguments("world"); // print "Hello world"

    }

    // #1
    private static CallSite boostrapMethod(MethodHandles.Lookup callerContextLookup, String name, MethodType methodType) throws NoSuchMethodException, IllegalAccessException {
        final MethodHandle methodHandle = callerContextLookup.findStatic(callerContextLookup.lookupClass(), name, methodType);
        CallSite callSite = new ConstantCallSite(methodHandle);
        return callSite;
    }

    //target method  ของเรา มี type description (Ljava/lang/String;)V
    private static void target(String msg) {
        System.out.println("Hello " + msg);
    }
}

reference:
Constant Pool
invokedynamic
JSR-292

Top comments (0)