Java InvokeDynamic (part 1)
Tags: java, invokedynamic, methodhandle
Published: 2020-03-30 17:44:03

---
title: Java InvokeDynamic (part 1)
published: true
description: เข้าใจ InvokeDynamic ไปจนถึง Lambda
tags: java,invokedynamic,methodhandle
---

`invokedynamic` เป็น instruction ที่ถูกอิมพลีเมนต์เพิ่มเข้ามาใน java 7 (JVM) โดยจุดประสงค์หลักจะทำมารองรับกับผู้ทำเฟรมเวิร์คหรือภาษา dynamic บน JVM เช่น JRuby,Groovy (เราเลยไม่ค่อยได้ใช้เท่าไร)ซึ่งแต่ก่อนนี้จะต้องใช้ java Reflection ในการ implement method call ซึ่งจะทำให้ได้ performace ที่ต่ำ เนื่องจากว่า JVM จะ optimize ลำบาก

*คอมไพเลอร์ javac version 7 จะยังไม่มีการ compile ที่ให้ผลลัพธ์ที่เป็น `invokedynamic` ออกมา ภาษาจาวาเองเริ่มนำ invokedynamic มาใช้ในเวอร์ชัน 8*


>**Background**
JVM  จะมี instruction ที่ใช้ในการเรียก method ที่แตกต่างกันดังนี้
1. invokespecial  สำหรับเรียก instance private method หรือ constructor (non-virtual)
2. invokestatic สำหรับเรียก static method
3. invokevirtual สำหรับเรียก instance method ที่เป็น virtual พวก protected,public,default visibility
4. invokeinterface สำหรับเรียก interface virtual method 

 >สาเหตที่มี invokevirtual และ invokeinterface เพราะว่า invokervirtual คอมไพล์เลอร์จะรู้ตำเหน่งของ method ที่จะเรียกใน vitual table ได้ในตอนคอมไพล์ ส่วน invokeinterface เราจะ assume index ใน virtual table(vtable) ในตอนคอมไพล์ไม่ได้ 

>ปล. ผมเข้าใจว่า virtual เป็น term ที่ใช้ใน c++ เป็นภาษาแรกนะ แต่ไม่ชัวร์ครับ


เพื่อให้ invokedynamic สามารถทำงานได้  java bytecode จำเป็นจะต้องมี runtime information อื่นๆเพิ่มเข้ามาใน class file ในส่วนที่เป็น constant pool ดังนี้
1. **CONSTANT_MethodHandle_info** เป็น data structure ที่เอาไว้ resolve reference ไปยัง invokespectal,invokestatic,invokevirtual,invokeintervace ในภาษาจาวาจะ represent โดย `java.lang.invoke.MethodHandle`
2. **CONSTANT_MethodType_info** เอาไว้เป็น type descriptor ของ method (parameter และ return type) ในภาษาจาวาจะ represent โดย `java.lang.invoke.MethodType`
3. **CONSTANT_InvokeDynamic_info** เอาไว้ resolve reference ไปยัง bootstrap method และ optional argument *ยังไม่ต้องสนใจในตอนนี้*

สำหรับในบทความนี้เราจะมาเขียนจาวาให้ทำการเรียก method String.concat(String) เพื่อจำลองให้เห็นว่า `CONSTANT_MethodHandle` และ `CONSTANT_MethodType` มีบทบาทยังไงใน constant pool โดยใช้  java api

อ่านคำอธิบายจาก comment เอานะครับ

สำหรับใครที่ยังอ่าน bytecode ไม่ออก ผมมีบทความเก่าอยู่ ลองอ่านดูนะครับ
[JVM 1](https://medium.com/@pramothsuwanpech/%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99-java-virtual-machine-jvm-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-typescript-part-1-61375d51a1b3)
[JVM 2](https://medium.com/@pramothsuwanpech/%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99-java-virtual-machine-jvm-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-typescript-part-2-a52b98669297)
[JVM 3](https://medium.com/@pramothsuwanpech/%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99-jvm-%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B9%86%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-typescript-part-3-for-loop-234e0572784b)
 


```java
package th.co.geniustree.indy;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;

public class MethodHandleTest {

    public static void main(String[] args) throws Throwable {
        //Lookup เป็น Factory สำหรับเอาไว้สร้าง MethodHandle
        final MethodHandles.Lookup lookup = MethodHandles.lookup();

        // เราจะอธิบายว่าเราต้องการ method ที่มี return type เป็นอะไร และ param เป็นอะไร
        //ในที่นี้เราจะเรียก String.concat(String)
        //กรณีที่ compiler เป็นคนจัดการให้ ตรงนีจะเป็น CONSTANT_MethodType
        //CONSTANT_MethodType --> (Ljava/lang/String;)Ljava/lang/String;
        //โดย L className ; เป็น field descriptor L หมายถึง reference type ใดๆ ส่วน className จะแทน . ด้วย /
        MethodType returnStringAndStringParamMethosType = MethodType.methodType(String.class, String.class);


        // สร้าง MethodHandle เพื่อห่อหุ่มเอา invokevirtual  java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String; เอาไว้สำหรับ invoke
        //ขั้นตอนนี้จะทำการ access checking ด้วยว่ามีสิทธิเข้าถึงไหม ซึ่งจะต่างจาก Reflection ตรงที่ reflection จะ check ทุกครั้งที่ invoke method ดังนั้นตรงนี้ MethodHandle จะเร็วกว่า
        // เพราะโดยปกติแล้ว lookup.findXxx จะทำครั้งเดียวแล้วเก็บ method handle instance เอาไว้ invoke()
        MethodHandle concatMethodHandle = lookup.findVirtual(String.class, "concat",returnStringAndStringParamMethosType);

        //ถ้าเป็น bytecode ก็จะเทียบเท่ากับ
        //ldc "Hello"  --> push "Hello" reference to operand stack
        //ldc "World"  --> push "World" reference to operand stack
        //invokevirtual  java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
        final String result = (String) concatMethodHandle.invokeExact("Hello", "World");
        System.out.println(result); // print "HelloWorld"
    }
}

```