- 適用於 JDK 23 的 GraalVM (最新)
- 適用於 JDK 24 的 GraalVM (搶先體驗)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發組建
與 Truffle 語言的互通性
Espresso 可讓您與其他「Truffle」語言(以 Truffle 框架實作解譯器的語言)介接,以建立多語言程式 - 以多種語言撰寫的程式。
本指南說明如何載入以其他語言撰寫的程式碼、如何在語言之間匯出和匯入物件、如何從外部語言使用 Espresso 物件、如何從 Espresso 使用外部物件,以及如何嵌入在 Java 應用程式中。
為避免混淆,我們使用主機和客體等術語來區分執行 Java 的不同層。Espresso 指的是客體層。
您將多語言選項傳遞至 java -truffle
啟動器。如果您使用原生組態,則需要使用 --polyglot
旗標才能存取其他語言。
外部物件在流入 Espresso 時,必須「棲息」於客體 Java 類型中。此類型如何附加至外部物件屬於實作細節。
多語言 #
Espresso 提供客體 Java 多語言 API,其說明於 polyglot.jar
中。這個 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)
方法是擴增的 Java 轉換,例如 targetClass.cast(obj)
- 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/char | 是* (盒裝類型) |
isString | String | 否 (急切轉換) |
isException & Polyglot.isForeignObject | ForeignException | 是 |
hasArrayElements | Object[] | 是 |
isNull | * | 是 |
* | Object | 是 |
您可以存取多語言繫結
// guest java
Object foreignObject = Polyglot.importObject("foreign_object");
// typed imports
String userName = Polyglot.importObject("user_name", String.class);
int year = Polyglot.importObject("year", int.class);
// exports
Polyglot.exportObject("data", new double[]{56.77, 59.23, 55.67, 57.50, 64.44, 61.37);
Polyglot.exportObject("message", "Hello, Espresso!");
互通協定 #
Espresso 提供明確的客體 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 #
Espresso 是透過 多語言 API 嵌入,這是 GraalVM 的一部分。
// 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.Properties.property.name
設定為所需的值來新增 Java 屬性 (在此情況下,這會設定property.name
)。 java.Properties.java.class.path
可用於設定 Truffle 環境中 Java 的類別路徑。java.Properties.java.library.path
可用於設定 Truffle 環境中 Java 的原生程式庫路徑。- 您可以將
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.PolyglotTypeConverters
以宣告將中繼限定名稱對應至類型轉換器類別的類型轉換函式。請參閱以下專用章節以取得更多詳細資料。 - 您可以將
java.PolyglotInterfaceMappings
設定為以分號分隔的 1:1 介面類型對應清單,以自動為實作清單中宣告的介面的主機物件建構客體 Proxy。請參閱以下專用章節以取得更多詳細資料。
*Espresso 不支援評估 Java 來源 (.eval
)。
在 Java 中,方法可以多載,例如,多種方法可以共用相同的名稱,但簽名不同。為了消除歧義,Espresso 允許以 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 vice versa.
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"));
使用類型轉換器將主機物件轉換為客體類型 #
Espresso 內建支援將主機物件宣告轉換為正確客體類型的物件。這會透過如上所述的環境建立器選項來完成。主要概念是允許物件從主機透明地流入客體,而無需在主機物件進入嵌入式 Espresso 環境時執行客體類型檢查。具體而言,可以設定下列選項來控制嵌入式環境的類型轉換
java.PolyglotTypeConverters
此選項優先於 java.PolyglotInterfaceMappings
,因此如果定義了專用的類型轉換器函式,則不會產生其他自動介面對應 Proxy Espresso。
注意:宣告的類型轉換器必須實作 com.oracle.truffle.espresso.polyglot
套件中 polyglor.jar
內的 GuestTypeConversion
介面。
package com.oracle.truffle.espresso.polyglot;
public interface GuestTypeConversion<T> {
T toGuest(Object polyglotInstance);
}
針對宣告的每個類型轉換器,使用一個選項呼叫,如下所示
// host java
Context polyglot = Context.newBuilder().allowAllAccess(true).
option("java.PolyglotTypeConverters.java.math.BigDecimal", "guest.context.path.BigDecimalConverter").
build();
...
// guest java
package guest.context.path;
import com.oracle.truffle.espresso.polyglot.GuestTypeConversion;
import com.oracle.truffle.espresso.polyglot.Interop;
import com.oracle.truffle.espresso.polyglot.InteropException;
import java.math.BigDecimal;
public class BigDecimalConverter implements GuestTypeConversion<BigDecimal> {
@Override
@SuppressWarnings("unchecked")
public BigDecimal toGuest(Object polyglotInstance) {
try {
return new BigDecimal(Interop.asString(Interop.invokeMember(polyglotInstance, "toString")));
} catch (InteropException e) {
throw new ClassCastException("polyglot instance cannot be cast to java.math.BigDecimal");
}
}
}
選項的 java.math.Bigdecimal
部分會宣告進入 Espresso 的主機物件的完整限定中繼名稱。
java.PolyglotInterfaceMappings
如果沒有專用的 java.PolyglotTypeConverters
用於流入嵌入式 Espresso 環境的主機物件,則會啟動自動介面類型對應。java.PolyglotInterfaceMappings
讓主機和嵌入式環境之間能夠無縫地共用介面類型。
下列範例說明如何使用此選項,以允許透過介面將常見的 JDK 集合類型傳遞至嵌入式 Espresso 環境
// host java
builder.option("java.PolyglotInterfaceMappings", getInterfaceMappings());
private static String getInterfaceMappings(){
return "java.lang.Iterable;"+
"java.util.Collection;"+
"java.util.List;"+
"java.util.Set;"+
"java.util.Map;"+
"java.util.Iterator;"+
"java.util.Spliterator;";
}
多執行緒 #
Espresso 設計為多執行緒語言,且許多生態系統都預期可以使用執行緒。這可能與不支援執行緒的其他 Truffle 語言不相容,因此您可以使用選項 --java.MultiThreaded=false
停用建立多個執行緒。
啟用此選項時,不會執行終結器,也不會執行 ReferenceQueue
通知機制。這兩種功能都需要啟動新的執行緒。請注意,弱可到達物件的垃圾回收不會受到影響。
反之,可以透過僅在單執行緒環境中可用的特殊命令,手動觸發參考處理。
// Host Java
// Will trigger Reference processing and run finalizers
polyglot.eval("java", "<ProcessReferences>");
請注意,此命令可能會觸發任意清除器和終結器程式碼。因此,理想情況下,應盡可能在堆疊上減少客體 Java 框架的情況下執行此命令。