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キャストです:
- Javaキャストは成功します ⇒
Polyglot.castが成功します。 - Javaキャストは成功せず、
Polyglot.castは外部オブジェクトを再入力でき、たとえばIntegerにキャストするには、外部オブジェクトがfitsInIntである必要があります。 Polyglot.castが失敗した場合、Class#castと同様のClassCastExceptionをスローします。
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プロパティは、目的の値への設定
java.Properties.property.nameによって追加できます(この場合はproperty.nameを設定します)。 java.Properties.java.class.pathを使用すると、Java on Truffleコンテキストのクラスパスを設定できます。java.Properties.java.library.pathを使用すると、Java on Truffleコンテキストのネイティブ・ライブラリ・パスを設定できます。java.EnableAssertionsをtrueに設定すると、アサーションを有効にすることができます。java.EnableSystemAssertionsをtrueに設定すると、Java標準ライブラリでアサーションを有効にすることができます。java.Verifyをnone、removeまたはallに設定して、バイトコード検証が行われないか、ユーザー・コードでのみ行われるか、またはすべてのクラスで行われるかを制御できます。java.JDWPOptionsは、JDWPによるデバッグを設定して有効にするように設定できます。たとえば、transport=dt_socket,server=y,address=localhost:8000,suspend=yに設定できます。java.Polyglotをtrueまたはfalseに設定して、com.oracle.truffle.espresso.polyglotパッケージのポリグロット機能へのアクセスを許可または拒否できます。
*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通知メカニズムも実行されません。これらの機能には、どちらも新しいスレッドの起動が必要になります。到達可能性が低いオブジェクトのガベージ・コレクションは引き続き影響を受けないことに注意してください。