Truffle言語との相互運用性

Java on Truffleを使用すると、他のTruffle言語(Truffleフレームワークでインタプリタが実装される言語)をインタフェースして、ポリグロット・プログラム(複数の言語で記述されたプログラム)を作成できます。

このガイドでは、外部言語で記述されたコードをロードする方法、言語間でオブジェクトをエクスポートおよびインポートする方法、外部言語からJava on Truffleオブジェクトを使用する方法、Java on Truffleから外部オブジェクトを使用する方法およびホストJavaへの埋込み方法について説明します。

混乱を避けるために、ホストゲストという用語を使用して、Javaが実行される異なるレイヤーを区別します。Java on Truffleは、ゲスト・レイヤーを指します。

ポリグロット・オプションはjava -truffleランチャに渡します。ネイティブ構成を使用する場合、他の言語にアクセスするには、--polyglotフラグを使用することが必要になります。

Java on Truffleで使用される外部オブジェクトはゲストJava型に存在する必要があります。この型が外部オブジェクトにどのようにアタッチされるかは、実装の詳細になります。

ポリグロット

Java on Truffleは、polyglot.jarとして記述されているゲストJavaポリグロットAPIを提供します。このJARはゲストJavaコンテキストで自動的に注入されますが、--java.Polyglot=falseを使用して除外できます。

Polyglotクラスをインポートして、他のゲスト言語と対話できます:

// guest java
import com.oracle.truffle.espresso.polyglot.Polyglot;

int two = Polyglot.eval(int.class, "js", "1+1");

オブジェクトが外部かどうかを判別できます:

// guest java
Object foreign = Polyglot.eval("js", "[2, 0, 2, 1]");
Object local = new int[]{2, 0, 2, 1};
System.out.println(Polyglot.isForeignObject(foreign)); // prints true
System.out.println(Polyglot.isForeignObject(local));   // prints false

外部オブジェクトをゲストJava型にキャストできます:

// guest java
Object foreignArray = Polyglot.eval("js", "['a string', 42, 3.14159, null]");
Object[] objects = Polyglot.cast(Object[].class, foreignArray);

assert objects.length == 4;
String elem0 = Polyglot.cast(String.class, objects[0]);   // eager conversion
Integer elem1 = Polyglot.cast(Integer.class, objects[1]); // preserves identity
int elem1_ = Polyglot.cast(int.class, objects[1]);        // eager conversion

double elem2 = Polyglot.cast(double.class, objects[2]);   // eager conversion
Object elem3 = objects[3];
assert elem3 == null;

Polyglot.cast(targetClass, obj)メソッドは、targetClass.cast(obj)などの拡張Javaキャストです:

Polyglot.castは、次に概要を示すように、一般的な相互運用の種類からJava型への自然なマッピングをサポートします:

相互運用の種類 使用可能な型 アイデンティティの保持
isBoolean Boolean/boolean はい*(ボックス化型)
fitsInByte Byte/byte はい*(ボックス化型)
fitsInShort Short/short はい*(ボックス化型)
fitsInInt Integer/int はい*(ボックス化型)
fitsInLong Long/long はい*(ボックス化型)
fitsInFloat Float/float はい*(ボックス化型)
fitsInDouble Double/double はい*(ボックス化型)
isString & 1-character Character/char はい*(ボックス化型)
isString String いいえ(即時変換)
isException & Polyglot.isForeignObject ForeignException はい
hasArrayElements Object[] はい
isNull * はい
* Object はい

ポリグロット・バインディングにアクセスできます:

// guest java
Object foreignObject = Polyglot.importObject("foreign_object");

// Also typed imports
String userName = Polyglot.importObject("user_name", String.class);
int year = Polyglot.importObject("year", int.class);

// And exports
Polyglot.exportObject("data", new double[]{56.77, 59.23, 55.67, 57.50, 64.44, 61.37);
Polyglot.exportObject("message", "Hello, Espresso!");

相互運用プロトコル

Java on Truffleは、相互運用プロトコルにアクセスするための明示的なゲストAPIを提供します。これには、相互運用プロトコル・メッセージを模倣するメソッドが含まれます。このAPIは、ゲストJavaオブジェクトでも使用できます。

// guest java
import com.oracle.truffle.espresso.polyglot.Interop;

Object foreignArray = Polyglot.eval("js", "[2, 0, 2, 1]");
System.out.println(Interop.hasArrayElements(foreignArray)); // prints true
System.out.println(Interop.getArraySize(foreignArray));     // prints 4

Object elem0 = Interop.readArrayElement(foreignArray, 0);
System.out.println(Interop.fitsInInt(elem0)); // prints true
System.out.println(Interop.asInt(elem0));     // prints 2

ホストJavaへの埋込み

Java on Truffleは、GraalVMの一部であるポリグロットAPIを介して埋め込まれます。

// host java
import org.graalvm.polyglot.*;

class Embedding {
    public static void main(String[] args) {
        Context polyglot = Context.newBuilder().allowAllAccess(true).build();

        // Class loading is exposed through language bindings, with class
        // names using the same format as Class#forName(String).
        Value intArray = polyglot.getBindings("java").getMember("[I");
        Value objectArray = polyglot.getBindings("java").getMember("[Ljava.lang.Object;")

        Value java_lang_Math = polyglot.getBindings("java").getMember("java.lang.Math");
        double sqrt2 = java_lang_Math.invokeMember("sqrt", 2).asDouble();
        double pi = java_lang_Math.getMember("PI").asDouble();
        System.out.println(sqrt2);
        System.out.println(pi);
    }
}

contextBuilder.option(key, value)を使用して、数多くの有用なコンテキスト・オプションを設定できます:

*Java on Truffleは、Javaソースの評価(.eval)をサポートしていません。

Javaでは、メソッドをオーバーロードでき、たとえば複数のメソッドが異なるシグネチャで同じ名前を共有できます。あいまいさを解消するために、Java on TruffleではmethodName/methodDescriptor形式でメソッド・ディスクリプタを指定できます:

// host java
Value java_lang_String = polyglot.getBindings("java").getMember("java.lang.String");
// String#valueOf(int)
String valueOf = String.format("%s/%s", "valueOf", "(I)Ljava/lang/String;");
Value fortyTwo = java_lang_String.invokeMember(valueOf, 42);
assert "42".equals(fortyTwo.asString());

Class<?>インスタンスと静的クラス・アクセサ(Klass):

静的クラス・アクセサを使用すると、(public)静的フィールドにアクセスし、(public)静的メソッドをコールできます。

// Class loading through language bindings return the static class accessor.
Value java_lang_Number = polyglot.getBindings("java").getMember("java.lang.Number");
Value java_lang_Class = polyglot.getBindings("java").getMember("java.lang.Class");

// Class#forName(String) returns the Class<Integer> instance.
Value integer_class = java_lang_Class.invokeMember("forName", "java.lang.Integer");

// Static class accessor to Class<?> instance and viceversa.
assert integer_class.equals(java_lang_Integer.getMember("class"));
assert java_lang_Integer.equals(integer_class.getMember("static"));

// Get Integer super class.
assert java_lang_Number.equals(java_lang_Integer.getMember("super"));

マルチスレッド

Java on Truffleはマルチスレッド言語として設計され、エコシステムの多くが、スレッドを使用できることを想定しています。このことは、スレッドをサポートしていない他のTruffle言語と互換性がない可能性があるため、オプション--java.MultiThreaded=falseを使用して複数のスレッドの作成を無効にできます。

このオプションを有効にすると、ファイナライザは実行されず、ReferenceQueue通知メカニズムも実行されません。これらの機能には、どちらも新しいスレッドの起動が必要になります。到達可能性が低いオブジェクトのガベージ・コレクションは引き続き影響を受けないことに注意してください。

かわりに、参照処理は特殊なコマンドを使用して手動でトリガーでき、シングルスレッド環境でのみ使用できます。

// Host Java
// Will trigger Reference processing and run finalizers
polyglot.eval("java", "<ProcessReferences>");

このコマンドは、任意のクリーナおよびファイナライザ・コードをトリガーする可能性があります。そのため、これは、スタック上でできるだけ少ないゲストjavaフレームで実行することが理想的です。