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/
  2. ある日のこと

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

  4. None
  5. ひょこ !

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

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

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

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

  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
  11. JEP 419 Foreign Function & Memory API JEP 419 (仮設)

  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 のコードを書くだけ! (抜粋)
  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.*; ^ (略)
  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.*; ^ (略)
  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.*; ^ (略)
  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.*; ^ (略)
  17. --add-modules が必要! • まだ仮設のAPI(incubator module) • 本体とは分離されている • 使用するには、明示的な指定が必要 >

    javac --add-modules=jdk.incubator.foreign Main.java 警告:実験的なモジュールを使用しています:jdk.incubator.foreign 警告1個 → クラスファイルができた!
  18. 実行

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

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

    allocating heap arena !?
  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); 逆
  22. 再実行 talk("Hello Gopher! (from Duke)")

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

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

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

    # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007ff87c79cc69, pid=15380, tid=18228 !?
  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);
  27. 再々実行 talk("Hello Gopher! (from Duke)") return "Hello Duke! (from Gopher)"

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

    "Hello Duke! (from Gopher)"
  29. 再々実行 正常終了!

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

  31. まとめ みんなも JEP419: Foreign Function & Memory API で、 いろんな言語と遊んでみてね!

    https://github.com/YujiSoftware/duke-gopher
  32. JEP 419 Foreign Function & Memory API で 一緒に遊んでみた @YujiSoftware

    https://yuji.software/
  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