Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java から Go のメソッドを呼んでみる! JEP 419​ Foreign Function & Memory API で一緒に遊んでみた​

Java から Go のメソッドを呼んでみる! JEP 419​ Foreign Function & Memory API で一緒に遊んでみた​

2022年8月26日のJJUGナイトセミナー「おうちで!ビール片手にLT大会!」の資料です。

参考: JEP 419: Foreign Function & Memory API (Second Incubator)
ソースコード: https://github.com/YujiSoftware/duke-gopher

YujiSoftware

August 27, 2022
Tweet

More Decks by YujiSoftware

Other Decks in Technology

Transcript

  1. JEP 419
    Foreign Function &
    Memory API で
    一緒に遊んでみた
    @YujiSoftware
    https://yuji.software/

    View Slide

  2. ある日のこと

    View Slide

  3. Javaのマスコットキャラクター Duke は、窓から外を眺めていました。
    すると、そこに一匹のマスコットキャラクターが通りかかりました。

    View Slide

  4. View Slide

  5. ひょこ !

    View Slide

  6. Go言語のマスコットキャラクター、Gopher 君です。

    View Slide

  7. Gopher 君は、Duke を見かけるとそっとその手を差し出してきました。

    View Slide

  8. talk(message string) string

    その手には、ある関数がエクスポートされていました。
    そこで、Duke はその関数を呼び出してみることにしました。
    しかし、一つ困った問題がありました。

    View Slide

  9. Duke の家(JVM)
    とても頑丈

    View Slide

  10. JVM から外に出るのは大変
    Java のヘッダファイルを使って
    C言語でコードを記述してコンパイル
    JNIEXPORT void JNICALL
    Java_com_example_TestClass_printString
    (JNIEnv *env, jobject obj, jstring javaString)
    {
    const char *nativeString = (*env)->GetStringUTFChars(env,
    javaString, NULL);
    printf("%s¥n", nativeString);
    (*env)->ReleaseStringUTFChars(env, javaString,
    nativeString);
    }
    ​Java で native コードを呼び出すように記述して、コン
    パイル
    package com.example;
    public class TestClass {
    static {
    System.loadLibrary("myjni");
    }
    public static native printString(String
    s);
    public static void main(String[] args) {
    JNI

    View Slide

  11. JEP 419
    Foreign Function & Memory API
    JEP 419
    (仮設)

    View Slide

  12. サンプルコード
    JEP 419
    (仮設)
    private static void talk(String message) throws Throwable {
    CLinker linker = CLinker.systemCLinker();
    SymbolLookup lookup = SymbolLookup.loaderLookup();
    MethodHandle recv = linker.downcallHandle(
    lookup.lookup("talk").get(),
    FunctionDescriptor.of(GoString.LAYOUT, GoString.LAYOUT));
    try (ResourceScope scope = ResourceScope.newConfinedScope()) {
    MemorySegment address = (MemorySegment) recv.invoke(scope,
    GoString.allocate(message, scope));
    System.out.println(GoString.get(address));
    }
    }
    • Java のコードを書くだけ!
    (抜粋)

    View Slide

  13. コンパイル
    > javac Main.java
    Main.java:1: エラー: パッケージjdk.incubator.foreignは表示不可です
    import jdk.incubator.foreign.*;
    ^
    (パッケージjdk.incubator.foreignはモジュールjdk.incubator.foreignで
    宣言されていますが、モジュール・グラフにありません)
    Main.java:2: エラー: パッケージjdk.incubator.foreignは表示不可です
    import static jdk.incubator.foreign.ValueLayout.*;
    ^
    (略)

    View Slide

  14. コンパイル
    > javac Main.java
    Main.java:1: エラー: パッケージjdk.incubator.foreignは表示不可です
    import jdk.incubator.foreign.*;
    ^
    (パッケージjdk.incubator.foreignはモジュールjdk.incubator.foreignで
    宣言されていますが、モジュール・グラフにありません)
    Main.java:2: エラー: パッケージjdk.incubator.foreignは表示不可です
    import static jdk.incubator.foreign.ValueLayout.*;
    ^
    (略)

    View Slide

  15. 改めてコンパイル
    > javac --enable-preview --source 19 Main.java
    Main.java:1: エラー: パッケージjdk.incubator.foreignは表示不可です
    import jdk.incubator.foreign.*;
    ^
    (パッケージjdk.incubator.foreignはモジュールjdk.incubator.foreignで
    宣言されていますが、モジュール・グラフにありません)
    Main.java:2: エラー: パッケージjdk.incubator.foreignは表示不可です
    import static jdk.incubator.foreign.ValueLayout.*;
    ^
    (略)

    View Slide

  16. 改めてコンパイル
    > javac --enable-preview --source 19 Main.java
    Main.java:1: エラー: パッケージjdk.incubator.foreignは表示不可です
    import jdk.incubator.foreign.*;
    ^
    (パッケージjdk.incubator.foreignはモジュールjdk.incubator.foreignで
    宣言されていますが、モジュール・グラフにありません)
    Main.java:2: エラー: パッケージjdk.incubator.foreignは表示不可です
    import static jdk.incubator.foreign.ValueLayout.*;
    ^
    (略)

    View Slide

  17. --add-modules が必要!
    • まだ仮設のAPI(incubator module)
    • 本体とは分離されている
    • 使用するには、明示的な指定が必要
    > javac --add-modules=jdk.incubator.foreign Main.java
    警告:実験的なモジュールを使用しています:jdk.incubator.foreign
    警告1個
    → クラスファイルができた!

    View Slide

  18. 実行

    View Slide

  19. 実行
    talk("Hello Gopher! (from Duke)")

    View Slide

  20. 実行
    talk("Hello Gopher! (from Duke)")
    fatal error:
    out of memory allocating heap arena
    !?

    View Slide

  21. 定義を間違えていた!
    • Go のヘッダー
    • Java のコード
    typedef struct { const char *p; ptrdiff_t n; } _GoString_;
    private static final OfAddress POINTER = ADDRESS.withName("p");
    private static final OfLong NUMBER = JAVA_LONG.withName("n");
    private static final GroupLayout LAYOUT =
    MemoryLayout.structLayout(NUMBER, POINTER);

    View Slide

  22. 再実行
    talk("Hello Gopher! (from Duke)")

    View Slide

  23. 再実行
    talk("Hello Gopher! (from Duke)")
    "Hello Gopher! (from Duke)"

    View Slide

  24. 再実行
    talk("Hello Gopher! (from Duke)")
    return "Hello Duke! (from Gopher)"

    View Slide

  25. 再実行
    talk("Hello Gopher! (from Duke)")
    return "Hello Duke! (from Gopher)"
    # EXCEPTION_ACCESS_VIOLATION
    (0xc0000005) at pc=0x00007ff87c79cc69, pid=15380, tid=18228
    !?

    View Slide

  26. 処理を間違えていた!
    • ポインターの扱いが違う
    • 範囲外までアクセスしてしまった
    int capacity = (int) (long) N.get(memory);
    MemoryAddress ptr = (MemoryAddress) P.get(memory);
    byte[] b = new byte[capacity];
    for (int i = 0; i < capacity; i++) {
    b[i] = ptr
    .get(ADDRESS, i).get(JAVA_BYTE, i);
    ↑ これがいらない
    }
    return new String(b, StandardCharsets.UTF_8);

    View Slide

  27. 再々実行
    talk("Hello Gopher! (from Duke)")
    return "Hello Duke! (from Gopher)"

    View Slide

  28. 再々実行
    talk("Hello Gopher! (from Duke)")
    return "Hello Duke! (from Gopher)"
    "Hello Duke! (from Gopher)"

    View Slide

  29. 再々実行
    正常終了!

    View Slide

  30. めでたし めでたし
    関数呼び出しに成功したDuke とGopher
    は、お互いにいろいろなメッセージを
    やり取りして遊びましたとさ

    View Slide

  31. まとめ
    みんなも JEP419: Foreign Function & Memory API で、
    いろんな言語と遊んでみてね!
    https://github.com/YujiSoftware/duke-gopher

    View Slide

  32. JEP 419
    Foreign Function &
    Memory API で
    一緒に遊んでみた
    @YujiSoftware
    https://yuji.software/

    View Slide

  33. 使用素材について
    • Gopher Stickers (tenntenn)
    • https://github.com/tenntenn/gopher-stickers
    • Creative Commons 3.0 Attributions license
    • OpenJDK Duke Project
    • https://wiki.openjdk.org/display/duke/Gallery
    • BSD License
    • Redistribution of Duke images
    • https://yuji.software/Duke/
    • BSD License

    View Slide