Truffle Interop 2.0
このドキュメントは、ゲスト言語およびツールの実装者を対象としています。先に進む前に、まずTruffleライブラリのチュートリアルをお読みになることをお薦めします。
使用する意義
Truffleバージョン1.0 RC15では、Truffleライブラリと呼ばれる新しいAPIが導入されました。Truffleライブラリを使用すると、プロファイリング/キャッシングをサポートする多相性を使用できます。Interop 2.0では、相互運用性プロトコルにTruffleライブラリを使用することが計画されています。現在の相互運用性APIは成熟しており、十分にテストされており、すでに言語およびツールで採用されています。
現在の相互運用性APIが変更され、Interop 2.0が導入された理由のリストを次に示します:
- フットプリント: 現在の相互運用性APIでは、送信されるすべてのメッセージは
CallTarget
を経由し、引数はObject[]
にボックス化されます。これにより、インタプリタ・コールの現在の相互運用性が非効率になり、追加のメモリーが必要になります。Truffleライブラリは、引数配列のボックス化またはコール・ターゲットを必要としない単純なノードおよび型が特殊化されたコール・シグネチャを使用します。 - キャッシュされていないディスパッチ: 一時ノードを割り当てずにスローパスから現在の相互運用性メッセージを実行する方法はありません。Truffleライブラリは、エクスポートされたすべてのメッセージのキャッシュされていないバージョンを自動的に生成します。これにより、一時データの構造を割り当てることなく、スローパス/ランタイムから相互運用性メッセージを使用できます。
- 複数のメッセージに対するディスパッチの再利用: 現在の相互運用性では、エクスポートされたメッセージへのディスパッチは、送信される各メッセージに対して繰り返されます。複数のメッセージを送信する必要があり、レシーバの型が多相になる場合は、不正なコードが生成されます。相互運用性ライブラリ・インスタンスは、入力値用に特殊化できます。これにより、ユーザーはディスパッチを1回実行すると、ディスパッチを繰り返さずに複数のメッセージを呼び出すことができます。これにより、多相の場合により効率的なコードが生成されます。
- デフォルト実装のサポート: 現在の相互運用性は、
TruffleObject
の実装にのみ使用できます。Truffleライブラリはどのレシーバの型にも使用できます。たとえば、プリミティブ数値でisExecutableメッセージを呼び出すことができ、これによりfalse
のみが返されます。 - エラーの可能性: レシーバの型の混在や間違った型チェックの実装など、Truffleライブラリが実行しないことで回避しようとするメッセージ解決に一般的な問題がいくつかありました。Truffleライブラリの新しいアサーション機能を使用すると、不変条件、事前条件および事後条件の検証を可能にするメッセージ固有のアサーションを指定できます。
- ドキュメントの冗長性: 現在の相互運用性は、
Message
定数およびForeignAccess
静的アクセサ・メソッドでメッセージをドキュメント化します。これにより、ほとんどの場合、冗長なドキュメントが作成されます。Truffleの相互運用性では、ライブラリ・クラスのインスタンス・メソッドであるドキュメントの場所は1つのみです。 - 一般性: Truffleライブラリは、メモリー消費およびインタプリタのパフォーマンスの点で十分に効率的であるため、言語表現の抽象化に使用できます。この問題のため、現在の相互運用性APIを現実的にはそのように使用できませんでした。
- プロトコルの問題への対処: 現在の相互運用性APIには、Interop 2.0で対処しようとする設計上の問題がいくつかあります(後述の説明を参照)。
互換性
Interop 1.0から2.0への変更は、互換性のある方法で行われました。したがって、古い相互運用性は引き続き機能し、採用は増分的に行うことができます。つまり、一方の言語が古い相互運用性APIを使用して引き続きコールし、もう一方の言語が新しい相互運用性APIをすでに採用している場合、互換性ブリッジがAPIをマップします。この仕組みに興味がある場合は、古い相互運用性に対する新しい相互運用性コールについてDefaultTruffleObjectExports
クラスを確認してください。新しい相互運用性に対する古い相互運用性コールについてはLegacyToLibraryNode
です。互換性ブリッジを使用すると、パフォーマンスが低下する可能性があることに注意してください。そのため、言語は可能なかぎり早く移行する必要があります。
相互運用性プロトコルの変更点
Interop 2.0には、多くのプロトコルの変更が伴います。この項では、これらの変更理由について説明します。詳細なリファレンス・ドキュメントは、『InteropLibrary Javadoc』を参照してください。ノート: 非推奨のすべてのAPIは、@deprecated
でタグ付けされたJavadoc内の移行パスを記述します。
IS_BOXEDおよびUNBOXを明示的な型に置換
IS_BOXED/UNBOXの設計には次のいくつかの問題があります:
- 値が特定の型(文字列など)であるかどうかを確認するには、まず値をボックス化解除する必要があります。ボックス化解除は、単に値の型をチェックする非効率なコードにつながるコストの高い操作になる可能性があります。
TruffleObject
を実装していない値には古いAPIを使用できません。したがって、プリミティブ数値の処理はTruffleObjectのケースと切り離す必要があり、既存のコードを再利用するにはUNBOX設計が必要でした。Truffleライブラリは、プリミティブ・レシーバの型をサポートしています。- UNBOXの設計は、返されるプリミティブ型の指定されたセットに依存します。言語はプリミティブ型を直接参照するため、この方法で追加の新しい相互運用性の型を導入することは困難です。
かわりに、次の新しいメッセージがInteropLibrary
に導入されました:
boolean isBoolean(Object)
boolean asBoolean(Object)
boolean isString(Object)
String asString(Object)
boolean isNumber(Object)
boolean fitsInByte(Object)
boolean fitsInShort(Object)
boolean fitsInInt(Object)
boolean fitsInLong(Object)
boolean fitsInFloat(Object)
boolean fitsInDouble(Object)
byte asByte(Object)
short asShort(Object)
int asInt(Object)
long asLong(Object)
float asFloat(Object)
double asDouble(Object)
InteropLibrary
は、レシーバの型Boolean
、Byte
、Short
、Integer
、Long
、Float
、Double
、Character
およびString
のデフォルトの実装を指定します。この設計は、Javaプリミティブ型が直接使用されなくなったため、大きい数値やカスタム文字列の抽象化などの新しい値をサポートするように拡張できます。相互運用性のプリミティブ型のセットは将来変更される可能性があるため、特殊化でプリミティブ型を直接使用することはお薦めしません。かわりに、常に相互運用性ライブラリを使用して特定の型をチェックします。たとえば、instanceof Integer
のかわりにfitsInInt
を使用します。
新しいメッセージを使用して、元のUNBOXメッセージを次のようにエミュレートできます:
@Specialization(limit="5")
Object doUnbox(Object value, @CachedLibrary("value") InteropLibrary interop) {
if (interop.isBoolean(value)) {
return interop.asBoolean(value);
} else if (interop.isString(value)) {
return interop.asString(value);
} else if (interop.isNumber(value)) {
if (interop.fitsInByte(value)) {
return interop.asByte(value);
} else if (interop.fitsInShort(value)) {
return interop.asShort(value);
} else if (interop.fitsInInt(value)) {
return interop.asInt(value);
} else if (interop.fitsInLong(value)) {
return interop.asLong(value);
} else if (interop.fitsInFloat(value)) {
return interop.asFloat(value);
} else if (interop.fitsInDouble(value)) {
return interop.asDouble(value);
}
}
throw UnsupportedMessageException.create();
}
ノート: このようなすべてのプリミティブ型をボックス化解除することはお薦めしません。かわりに、言語は実際に使用するプリミティブ型のみボックス化解除する必要があります。次のように、ボックス化解除操作が不要で、相互運用性ライブラリを直接使用して操作を実装することが理想的です:
@Specialization(guards = {
"leftValues.fitsInLong(l)",
"rightValues.fitsInLong(r)"}, limit="5")
long doAdd(Object l, Object r,
@CachedLibrary("l") InteropLibrary leftValues,
@CachedLibrary("r") InteropLibrary rightValues) {
return leftValues.asLong(l) + rightValues.asLong(r);
}
配列要素およびメンバー要素の明示的なネームスペース
汎用のREADおよびWRITEメッセージは、本来は主にJavaScriptのユースケースを考慮して設計されました。より多くの言語で相互運用性が採用されるにつれ、配列およびオブジェクト・メンバーに明示的なネームスペースが必要であることが明らかになりました。時間の経過とともに、READおよびWRITEの解釈が変更され、数値で使用する場合は配列アクセスを表し、文字列で使用する場合はオブジェクト・メンバー・アクセスを表すようになりました。HAS_SIZEメッセージは、値に追加の保証付き配列要素が含まれているかどうかとして再解釈されました。たとえば、その配列要素はインデックス0とサイズの間で反復可能でした。
言語間の相互運用性を向上させるには、明示的なハッシュ/マップ/ディクショナリ・エントリのネームスペースが必要です。当初は、このために汎用のREAD/WRITEネームスペースを再利用することを目的としていました。JavaScriptでは、ディクショナリとメンバーのネームスペースが同等であったため、これが可能でした。ただし、ほとんどの言語では、マップ・エントリがオブジェクト・メンバーと切り離されているため、キーがあいまいになります。ソース言語(プロトコル・インプリメンタ)では、この競合を解決する方法がわかりません。かわりに、ネームスペースを明示的に指定することで、ターゲット言語(プロトコル・コール元)があいまいさの解決方法を決定できます。たとえば、ディクショナリ要素とメンバー要素のどちらを優先するかを、ターゲット言語の操作で決定できるようになりました。
次の相互運用性メッセージが変更されました:
READ, WRITE, REMOVE, HAS_SIZE, GET_SIZE, HAS_KEYS, KEYS
InteropLibraryの個別のメンバーおよび配列のネームスペースを持つ更新されたプロトコルは次のようになります:
オブジェクト・ネームスペース:
hasMembers(Object)
getMembers(Object, boolean)
readMember(Object, String)
writeMember(Object, String, Object)
removeMember(Object, String)
invokeMember(Object, String, Object...)
配列ネームスペース:
hasArrayElements(Object)
readArrayElement(Object, long)
getArraySize(Object)
writeArrayElement(Object, long, Object)
removeArrayElement(Object, long)
配列アクセス・メッセージはUnknownIdentifierException
をスローしなくなり、かわりにInvalidArrayIndexException
をスローします。これは元の設計のバグであり、アクセスされた数値をUnknownIdentifierException
で識別子文字列に変換する必要がありました。
KeyInfoを個々のメッセージに置換
前の項では、KEY_INFOメッセージについては説明しませんでした。KEY_INFOメッセージは、メンバー要素または配列要素のすべてのプロパティを問い合せる場合に役立ちました。これは簡便な小規模のAPIでしたが、インプリメンタがすべてのキー情報プロパティを返す必要があったため、多くの場合非効率でした。同時に、コール元が実際にすべてのキー情報プロパティを必要とすることはめったにありませんでした。Interop 2.0では、KEY_INFOメッセージが削除されました。かわりに、この問題に対処するために、ネームスペースごとに明示的なメッセージを導入しました。
オブジェクト・ネームスペース:
isMemberReadable(Object, String)
isMemberModifiable(Object, String)
isMemberInsertable(Object, String)
isMemberRemovable(Object, String)
isMemberInvocable(Object, String)
isMemberInternal(Object, String)
isMemberWritable(Object, String)
isMemberExisting(Object, String)
hasMemberReadSideEffects(Object, String)
hasMemberWriteSideEffects(Object, String)
配列ネームスペース:
isArrayElementReadable(Object, long)
isArrayElementModifiable(Object, long)
isArrayElementInsertable(Object, long)
isArrayElementRemovable(Object, long)
isArrayElementWritable(Object, long)
isArrayElementExisting(Object, long)
ノート: 配列ネームスペースでは、読取りまたは書込みの副次効果の問合せはサポートされなくなりました。これらのメッセージは再導入される可能性がありますが、現時点ではユースケースはありませんでした。また、配列ネームスペースでは呼出しは許可されません。
TO_NATIVEの戻り型の削除
TO_NATIVEメッセージはInteropLibraryでtoNativeに名前が変更されましたが、値を返さなくなった点が異なります。ただし、レシーバでサポートされている場合は、ネイティブ遷移を副次効果として実行します。これにより、メッセージのコール元はコードを簡素化できます。別の値を返すためにtoNative
遷移が必要だったケースは見つかりませんでした。toNative
のデフォルトの動作は、値を返さないように変更されました。
微細な変更点
次のメッセージはほとんど変更されていません。NEW
メッセージは、isInstantiable
と一致するようにinstantiate
に名前が変更されました。
Message.IS_NULL -> InteropLibrary.isNull
Message.EXECUTE -> InteropLibrary.execute
Message.IS_INSTANTIABLE -> InteropLibrary.isInstantiable
Message.NEW -> InteropLibrary.instantiate
Message.IS_EXECUTABLE -> InteropLibrary.isExecutable
Message.EXECUTE -> InteropLibrary.execute
Message.IS_POINTER -> InteropLibrary.isPointer
Message.AS_POINTER -> InteropLibrary.asPointer
より強力なアサーション
移行の一環として、多くの新しいアサーションが導入されました。具体的な事前/事後条件および不変条件については、Javadocで説明しています。古い相互運用性ノードとは異なり、キャッシュされたライブラリは、ASTの一部として採用された場合にのみ使用できます。
未チェック/チェック済の例外なし
Interop 2.0では、InteropException.raise
は非推奨になりました。可能ですが、チェック済の例外を未チェックの例外として再スローすることはアンチパターンとみなされます。Truffleライブラリでは、ターゲット言語ノードがコール元のASTに直接挿入されるため、チェック済の例外をサポートしないCallTarget
は制限されなくなりました。Truffle DSLからのチェック済の例外の追加サポートとともに、呼出しメソッドを使用する必要がなくなりました。かわりに、すべての相互運用性例外タイプに新しいファクトリ作成メソッドが導入されました。
相互運用性例外は常に即座に捕捉され、再スローされないようにすることを目的としているため、効率を向上させるために相互運用性例外からスタック・トレースを削除することが計画されています。これは、互換性レイヤーを削除できるようになるまで延期されました。
移行
相互運用性にTruffleライブラリを使用する場合は、既存のほとんどの相互運用性APIを非推奨にする必要がありました。次のInterop 1.0とInterop 2.0の比較は、相互運用性の既存の使用を移行する場合に役立つように設計されています。
相互運用性メッセージを送信するファストパス
これは、操作ノードに埋め込まれた相互運用性メッセージを送信するファストパスの方法です。これは、相互運用性メッセージを送信する最も一般的な方法です。
Interop 1.0:
@ImportStatic({Message.class, ForeignAccess.class})
abstract static class ForeignExecuteNode extends Node {
abstract Object execute(Object function, Object[] arguments);
@Specialization(guards = "sendIsExecutable(isExecutableNode, function)")
Object doDefault(TruffleObject function, Object[] arguments,
@Cached("IS_EXECUTABLE.createNode()") Node isExecutableNode,
@Cached("EXECUTE.createNode()") Node executeNode) {
try {
return ForeignAccess.sendExecute(executeNode, function, arguments);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
// ... convert errors to guest language errors ...
}
}
}
Interop 2.0:
abstract static class ForeignExecuteNode extends Node {
abstract Object execute(Object function, Object[] arguments);
@Specialization(guards = "functions.isExecutable(function)", limit = "2")
Object doDefault(Object function, Object[] arguments,
@CachedLibrary("function") InteropLibrary functions) {
try {
return functions.execute(function, arguments);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
// ... convert errors to guest language errors ...
}
}
}
次の相違点に注意してください。
- メッセージを呼び出すには、
ForeignAccess
で静的メソッドをコールするかわりに、TruffleLibrary
でインスタンス・メソッドをコールします。 - 古い相互運用性では、操作ごとに1つのノードを作成する必要がありました。新しいバージョンでは、特殊化された相互運用性ライブラリが1つのみ作成されます。
- 古いAPIでは、
TruffleObject
のレシーバの型を特殊化する必要がありました。新しい相互運用性ライブラリは、任意の相互運用性値で呼び出すことができます。デフォルトでは、isExecutable
は、相互運用性ライブラリをエクスポートしない値に対してfalse
を返します。たとえば、ボックス化されたプリミティブ・レシーバ値を使用してライブラリをコールすることが有効になりました。 - 古い相互運用性で
@Cached
を使用するかわりに、新しい相互運用性では@CachedLibrary
を使用します。 - 新しい
@CachedLibrary
注釈は、ライブラリが特殊化する値を指定します。これにより、DSLはライブラリ・インスタンスをその値に特殊化できます。これにより、すべてのメッセージ呼出しについてレシーバ値に対するディスパッチを1回実行できます。古い相互運用性バージョンでは、ノードを値に特殊化できませんでした。したがって、相互運用性メッセージを送信するたびにディスパッチを繰り返す必要がありました。 - 特殊化されたライブラリ・インスタンスでは、特殊化メソッドに
limit
を指定する必要があります。この制限がオーバーフローした場合、プロファイリング/キャッシングを実行しない、キャッシュされていないバージョンのライブラリが使用されます。古い相互運用性APIでは、相互運用性ノードごとに定数の特殊化制限が8
であると想定されていました。 - 新しい相互運用性APIでは、かわりに
@CachedLibrary(limit="2")
を指定することで、ディスパッチされたバージョンのライブラリを使用できます。これにより、相互運用性ライブラリを任意の値で使用できますが、古い相互運用性APIと同様に、メッセージを呼び出すたびにインライン・キャッシュを複製するというデメリットがあります。したがって、可能なかぎり特殊化されたライブラリを使用することをお薦めします。
相互運用性メッセージを送信するスローパス
ノードのコンテキストを使用せずに、ランタイムから相互運用性メッセージをコールする必要がある場合があります:
Interop 1.0:
ForeignAccess.sendRead(Message.READ.createNode(), object, "property")
Interop 2.0:
InteropLibrary.getFactory().getUncached().read(object, "property");
次の相違点に注意してください。
- 古いインタフェースは、各呼出しにノードを割り当てました。
- 新しいライブラリは、各呼出しに割当てやボックス化を必要としない、キャッシュされていないバージョンのライブラリを使用します。
InteropLibrary.getFactory().getUncached(object)
を使用すると、キャッシュされていない特殊化されたバージョンのライブラリを参照できます。これを使用すると、キャッシュされていない複数の相互運用性メッセージを同じレシーバに送信する必要がある場合に、ルックアップのエクスポートの繰返しを回避できます。
相互運用性メッセージを送信するカスタム・ファストパス
Truffle DSLを使用できず、ノードを手動で書き込む必要がある場合があります。どちらのAPIでも、次のことが可能です:
Interop 1.0:
final class ForeignExecuteNode extends Node {
@Child private Node isExecutableNode = Message.IS_EXECUTABLE.createNode();
@Child private Node executeNode = Message.EXECUTE.createNode();
Object execute(Object function, Object[] arguments) {
if (function instanceof TruffleObject) {
TruffleObject tFunction = (TruffleObject) function;
if (ForeignAccess.sendIsExecutable(isExecutableNode, tFunction)) {
try {
return ForeignAccess.sendExecute(executeNode, tFunction, arguments);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
// TODO handle errors
}
}
}
// throw user error
}
}
Interop 2.0:
static final class ForeignExecuteNode extends Node {
@Child private InteropLibrary functions = InteropLibrary.getFactory().createDispatched(5);
Object execute(Object function, Object[] arguments) {
if (functions.isExecutable(function)) {
try {
return functions.execute(function, arguments);
} catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
// handle errors
return null;
}
}
// throw user error
}
}
次の相違点に注意してください。
- 新しい相互運用性では、
InteropLibrary.getFactory()
を介してアクセス可能なLibraryFactory<InteropLibrary>
を介してノードを作成します。古い相互運用性では、Message
インスタンスを介してディスパッチ・ノードを作成します。 - 新しい相互運用性ライブラリには、ディスパッチ制限を指定できます。古い相互運用性APIでは、常に定数制限が
8
であると想定されていました。 - 新しい相互運用性では、Truffleライブラリはどのレシーバの型でも使用できるため、
TruffleObject
型をチェックする必要はありません。関数以外の値の場合、isExecutable
はfalse
のみを返します。
相互運用性メッセージの実装/エクスポート
相互運用性ライブラリ・メッセージを実装/エクスポートするには、次の例を参照してください:
Interop 1.0:
@MessageResolution(receiverType = KeysArray.class)
final class KeysArray implements TruffleObject {
private final String[] keys;
KeysArray(String[] keys) {
this.keys = keys;
}
@Resolve(message = "HAS_SIZE")
abstract static class HasSize extends Node {
public Object access(KeysArray receiver) {
return true;
}
}
@Resolve(message = "GET_SIZE")
abstract static class GetSize extends Node {
public Object access(KeysArray receiver) {
return receiver.keys.length;
}
}
@Resolve(message = "READ")
abstract static class Read extends Node {
public Object access(KeysArray receiver, int index) {
try {
return receiver.keys[index];
} catch (IndexOutOfBoundsException e) {
CompilerDirectives.transferToInterpreter();
throw UnknownIdentifierException.raise(String.valueOf(index));
}
}
}
@Override
public ForeignAccess getForeignAccess() {
return KeysArrayForeign.ACCESS;
}
static boolean isInstance(TruffleObject array) {
return array instanceof KeysArray;
}
}
Interop 2.0:
@ExportLibrary(InteropLibrary.class)
final class KeysArray implements TruffleObject {
private final String[] keys;
KeysArray(String[] keys) {
this.keys = keys;
}
@ExportMessage
boolean hasArrayElements() {
return true;
}
@ExportMessage
boolean isArrayElementReadable(long index) {
return index >= 0 && index < keys.length;
}
@ExportMessage
long getArraySize() {
return keys.length;
}
@ExportMessage
Object readArrayElement(long index) throws InvalidArrayIndexException {
if (!isArrayElementReadable(index) {
throw InvalidArrayIndexException.create(index);
}
return keys[(int) index];
}
}
次の相違点に注意してください。
- @MessageResolutionのかわりに、@ExportLibraryを使用します。
- どちらのバージョンもTruffleObjectを実装する必要があります。新しい相互運用性APIには、互換性のためにTruffleObject型のみが必要です。
- @Resolveのかわりに、@ExportMessage注釈が使用されます。後者の注釈は、メソッド名からメッセージの名前を推測できます。メソッド名があいまいな場合(たとえば、複数のライブラリをエクスポートする場合)、名前とライブラリを明示的に指定できます。
- エクスポート/解決用のクラスを指定する必要はありません。ただし、エクスポートに複数の特殊化が必要な場合は、これを行うことができます。詳細は、Truffleライブラリのチュートリアルを参照してください。
- 例外は、チェック済の例外としてスローされるようになりました。
- getForeignAccess()を実装する必要がなくなりました。実装により、レシーバの型の実装が自動的に検出されます。
isInstance
を実装する必要がなくなりました。この実装はクラス・シグネチャから導出されるようになりました。レシーバの型がfinalとして宣言されている場合、チェックの効率が向上する可能性があることに注意してください。レシーバの型がfinal以外の場合は、エクスポートされるメソッドをfinal
として指定することをお薦めします。
DynamicObjectとの統合
古い相互運用性では、ObjectType.getForeignAccessFactory()
を介して外部アクセス・ファクトリを指定できました。このメソッドは非推奨になり、新しいメソッドObjectType.dispatch()
が導入されました。ディスパッチ・メソッドは、外部アクセス・ファクトリのかわりに、明示的なレシーバでInteropLibraryをエクスポートするクラスを返す必要があります:
Interop 1.0:
public final class SLObjectType extends ObjectType {
public static final ObjectType SINGLETON = new SLObjectType();
private SLObjectType() {
}
public static boolean isInstance(TruffleObject obj) {
return SLContext.isSLObject(obj);
}
@Override
public ForeignAccess getForeignAccessFactory(DynamicObject obj) {
return SLObjectMessageResolutionForeign.ACCESS;
}
}
@MessageResolution(receiverType = SLObjectType.class)
public class SLObjectMessageResolution {
@Resolve(message = "WRITE")
public abstract static class SLForeignWriteNode extends Node {...}
@Resolve(message = "READ")
public abstract static class SLForeignReadNode extends Node {...}
...
Interop 2.0:
@ExportLibrary(value = InteropLibrary.class, receiverType = DynamicObject.class)
public final class SLObjectType extends ObjectType {
public static final ObjectType SINGLETON = new SLObjectType();
private SLObjectType() {
}
@Override
public Class<?> dispatch() {
return SLObjectType.class;
}
@ExportMessage
static boolean hasMembers(DynamicObject receiver) {
return true;
}
@ExportMessage
static boolean removeMember(DynamicObject receiver, String member) throws UnknownIdentifierException {...}
// other exports omitted
}
次の相違点に注意してください。
- オブジェクト型はエクスポート・クラスとして再利用できます。
- isInstanceメソッドを指定する必要はなくなりました。
- 新しい相互運用性では、レシーバの型をDynamicObjectに指定する必要があります。
相互運用性の拡張
Truffleで実装された言語は相互運用性を拡張する必要はほとんどありませんが、独自の言語固有のプロトコルを拡張する必要がある場合があります:
Interop 1.0:
FooBar
という新しいKnownMessageサブクラスを追加します。- 新しいメソッド
sendFooBar
をForeignAccess
に追加します。 - 新しいメソッドを
ForeignAccess.Factory
:createFooBar
に追加します。 - 相互運用性注釈プロセッサを、
createFooBar
のコードを生成するように変更します。
Interop 2.0:
- 新しいメソッド
fooBar
をInteropLibrary
に追加します。その他すべては自動的に実行されます。