- 適用於 JDK 23 的 GraalVM (最新版)
- 適用於 JDK 24 的 GraalVM (搶先體驗版)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
從 Nashorn 遷移到 GraalJS 的指南
本指南作為先前以 Nashorn 引擎為目標的程式碼的遷移指南。有關支援的 Java 互通性功能的概述,請參閱Java 互通性指南。
Nashorn 引擎已在 JDK 11 中作為 JEP 335 的一部分被棄用,並已從 JDK15 中作為 JEP 372 的一部分移除。
GraalJS 可以作為先前在 Nashorn 引擎上執行的 JavaScript 程式碼的替代方案。GraalJS 提供了 Nashorn 先前提供的所有 JavaScript 功能。許多功能預設可用,有些功能需要在選項中設定,而其他功能則需要對原始碼進行微小的修改。
Nashorn 和 GraalJS 都支援類似的 Java 互通性語法和語意。一個顯著的差異是,GraalJS 採用預設安全方法,這表示某些預設在 Nashorn 上可用的功能需要明確啟用。這裡列出了與遷移相關的最重要差異。
預設可用的 Nashorn 功能(取決於安全性設定)
Java.type
,Java.typeName
Java.from
,Java.to
Java.extend
,Java.super
- Java 套件全域變數:
Packages
,java
,javafx
,javax
,com
,org
,edu
Nashorn 相容模式 #
GraalJS 提供 Nashorn 相容模式。只有在啟用 js.nashorn-compat
選項時,才能使用 Nashorn 相容性所需的部分功能。對於 GraalJS 不想預設公開的 Nashorn 特定擴充功能,情況就是如此。
請注意,您必須解除鎖定實驗性功能才能使用此選項。另請注意,在某些情況下,設定此選項會違反 GraalJS 的預設安全方法,例如,在傳統 ScriptEngine
上操作時。
當您使用 Nashorn 相容模式時,預設會將 ECMAScript 5 設定為相容性層級。您可以使用 js.ecmascript-version
選項指定不同的 ECMAScript 版本。請注意,這可能會與完全的 Nashorn 相容性衝突。在本節的結尾附近提供了一個如何設定選項的程式碼範例。
可以設定 js.nashorn-compat
選項
- 透過使用命令列選項
js --experimental-options --js.nashorn-compat=true
- 透過使用 Polyglot API
import org.graalvm.polyglot.Context; try (Context context = Context.newBuilder().allowExperimentalOptions(true).option("js.nashorn-compat", "true").build()) { context.eval("js", "print(__LINE__)"); }
- 在啟動 Java 應用程式時透過使用系統屬性(也請記得在應用程式中的
Context.Builder
上啟用allowExperimentalOptions
)java -Dpolyglot.js.nashorn-compat=true MyApplication
只有在 nashorn-compat
選項下才可用的功能包括
Java.isJavaFunction
,Java.isJavaMethod
,Java.isScriptObject
,Java.isScriptFunction
new Interface|AbstractClass(fn|obj)
JavaImporter
JSAdapter
- 字串值的
java.lang.String
方法 load("nashorn:parser.js")
,load("nashorn:mozilla_compat.js")
exit
,quit
可以採用類似的方式設定 js.ecmascript-version
選項。由於這是一個支援的選項,因此無需僅為設定 ecmascript-version
提供 experimental-options
選項
js --js.ecmascript-version=2020
Nashorn 語法擴充功能 #
可以使用 js.syntax-extensions
實驗性選項啟用Nashorn 語法擴充功能。它們也會在 Nashorn 相容模式 (js.nashorn-compat
) 中預設啟用。
GraalJS 與 Nashorn #
GraalJS 在某些方面與 Nashorn 不同,這些方面是刻意的設計決策。
預設安全 #
GraalJS 採用預設安全方法。除非嵌入器明確允許,否則 JavaScript 程式碼無法存取 Java 類別或存取檔案系統,以及其他限制。只有在相關安全性設定足夠寬容時,才能使用 GraalJS 的多個功能(包括 Nashorn 相容性功能)。
請確保您瞭解任何解除應用程式和主機系統安全預設限制的變更的安全性影響。
有關可用設定的完整列表,請參閱Context.Builder
。這些選項可以在使用 Polyglot API 建置內容時定義。
啟用 GraalJS 功能時經常需要的選項是
allowHostAccess()
:設定哪個公用類別的公用建構函式、方法或欄位可由來賓應用程式存取。使用HostAccess.EXPLICIT
或自訂HostAccess
原則來有選擇地啟用存取。設定為HostAccess.ALL
以允許無限制的存取。allowHostClassLookup()
:設定一個篩選器,用於指定來賓應用程式可以查找哪些 Java 主機類別。設定為 PredicateclassName -> true
以允許查找所有類別。allowIO()
:允許來賓語言在主機系統上執行無限制的 IO 操作,例如,從檔案系統load()
時需要此操作。設定為true
以啟用 IO。
如果您在傳統 ScriptEngine
上執行程式碼,請參閱透過 Bindings
設定選項,瞭解如何在那裡設定它們。
最後,請注意,當在 ScriptEngine
(但不是在 Context
上)執行程式碼時,nashorn-compat
模式會啟用相關選項,以便在該設定中提供與 Nashorn 更好的相容性。
啟動器名稱 js
#
GraalJS 隨附一個名為 js
的二進位啟動器。請注意,根據建置環境,GraalJS 可能仍然會隨附 Nashorn 及其 jjs
啟動器。
ScriptEngine 名稱 graal.js
#
GraalJS 隨附對 ScriptEngine
的支援。它以多個名稱註冊,包括「graal.js」、「JavaScript」和「js」。如果您需要完全的 Nashorn 相容性,請務必如上所述啟用 Nashorn 相容模式。根據建置設定,GraalJS 可能仍然會隨附 Nashorn 並透過 ScriptEngine
提供它。有關更多詳細資訊,請參閱ScriptEngine 實作。
ClassFilter
#
當使用多語言 Context
啟動時,GraalJS 提供類別篩選器。請參閱Context.Builder.hostClassFilter
。
完整限定名稱 #
GraalJS 需要使用 Java.type(typename)
。預設情況下,它不支援僅透過其完整限定類別名稱存取類別。Java.type
更清楚,並避免在 JavaScript 程式碼中意外使用 Java 類別。例如,請看這個模式
var bd = new java.math.BigDecimal('10');
它應該表示為
var BigDecimal = Java.type('java.math.BigDecimal');
var bd = new BigDecimal('10');
有損轉換 #
GraalJS 在呼叫 Java 方法時不允許參數的有損轉換。這可能會導致難以偵測的數值錯誤。
GraalJS 總是選擇具有可轉換為且不會造成損失的最窄可能引數類型的多載方法。如果沒有此類多載方法可用,GraalJS 會擲回 TypeError
,而不是有損轉換。一般來說,這會影響執行哪個多載方法。
自訂 targetTypeMapping
可以用來自訂行為。請參閱HostAccess.Builder#targetTypeMapping。
ScriptObjectMirror
物件 #
GraalJS 不提供 ScriptObjectMirror
類別的物件。相反,JavaScript 物件以實作 Java 的 Map
介面的物件的形式向 Java 程式碼公開。
可以透過將類型變更為介面 (Map
或 List
) 或提供類似功能的多語言 Value 類別來重寫引用 ScriptObjectMirror
實例的程式碼。
多執行緒 #
在 GraalVM 上執行 JavaScript 透過從 Java 程式碼建立多個 Context
物件來支援多執行緒。內容可以在執行緒之間共用,但每個內容一次都必須由一個執行緒存取。可以從 Java 應用程式建立多個 JavaScript 引擎,並且可以在多個執行緒上安全地並行執行
Context polyglot = Context.create();
Value array = polyglot.eval("js", "[1,2,42,4]");
GraalJS 不允許從 JavaScript 建立具有存取目前 Context
權限的執行緒。此外,GraalJS 不允許並行執行緒同時存取相同的 Context
。這可能會導致在未準備好進行多執行緒處理的語言中出現無法管理的同步問題,例如資料競爭。例如
new Thread(function() {
print('printed from another thread'); // throws Exception due to potential synchronization problems
}).start();
JavaScript 程式碼可以使用在 Java 中實作的 Runnable
建立和啟動執行緒。子執行緒可能無法存取父執行緒或任何其他多語言執行緒的 Context
。如果發生違規,則會擲回 IllegalStateException
。但是,子執行緒可能會建立新的 Context
實例。
new Thread(aJavaRunnable).start(); // allowed on GraalJS
透過適當的同步,可以在不同執行緒之間共用多個內容。使用來自多個執行緒的 JavaScript Context
的 Java 應用程式範例可以在此處找到。
僅在 Nashorn 相容模式下可用的擴充功能 #
以下在 Nashorn 中可用的 JavaScript 擴展功能,在 GraalJS 中預設為停用。它們在 Nashorn 相容模式下提供。強烈建議不要基於這些功能實作新的應用程式,而僅將其作為將現有應用程式遷移到 GraalVM 的一種手段。
字串 length
屬性 #
GraalJS 不會特別處理字串的 length 屬性。存取字串長度的標準方式是讀取 length
屬性。
myJavaString.length;
Nashorn 允許使用者將 length
作為屬性和函式存取。現有的函式呼叫 length()
應改用屬性存取來表示。在 Nashorn 相容模式下會模擬 Nashorn 的行為。
JavaScript 全域物件中的 Java 套件 #
GraalJS 需要使用 Java.type
而非完整限定名稱。在 Nashorn 相容模式下,以下 Java 套件會新增至 JavaScript 全域物件:java
、javafx
、javax
、com
、org
和 edu
。
JavaImporter #
JavaImporter
功能僅在 Nashorn 相容模式下可用。
JSAdapter #
不建議使用非標準的 JSAdapter
功能,而應使用等效的標準 Proxy
功能來取代。為了相容性,JSAdapter
仍然在 Nashorn 相容模式下可用。
Java.* 方法 #
Nashorn 在 Java
全域物件上提供的幾個方法僅在 Nashorn 相容模式下可用,或目前 GraalJS 不支援。在 Nashorn 相容模式下可用的有:Java.isJavaFunction
、Java.isJavaMethod
、Java.isScriptObject
和 Java.isScriptFunction
。Java.asJSONCompatible
目前不支援。
存取器 #
在 Nashorn 相容模式下,GraalJS 允許使用者僅使用名稱作為屬性來存取 getter 和 setter,同時省略 get
、set
或 is
。
var Date = Java.type('java.util.Date');
var date = new Date();
var myYear = date.year; // calls date.getYear()
date.year = myYear + 1; // calls date.setYear(myYear + 1);
GraalJS 模擬 Nashorn 在存取順序方面的行為。
- 在讀取操作的情況下,GraalJS 會先嘗試呼叫名稱為
get
且屬性名稱為駝峰式大小寫的 getter。如果不可用,則呼叫名稱為is
且屬性名稱為駝峰式大小寫的 getter。在第二種情況下,與 Nashorn 不同,即使結果值不是布林類型,也會傳回該值。只有當這兩種方法都不可用時,才會讀取屬性本身。 - 在寫入操作的情況下,GraalJS 會嘗試呼叫名稱為
set
且屬性名稱為駝峰式大小寫的 setter,並將該值作為引數提供給該函式。如果 setter 不可用,則會寫入屬性本身。
請注意,Nashorn(因此 GraalJS)明確區分屬性讀/寫和函式呼叫。當 Java 類別同時具有同名且公開可用的欄位和方法時,obj.property
將始終讀取該欄位(或如上所述的 getter),而 obj.property()
將始終呼叫各自的方法。
其他需要考量的方面 #
GraalJS 的功能 #
GraalJS 支援最新 ECMAScript 規格的功能和一些擴展。請參閱JavaScript 相容性。請注意,此範例會將物件新增至全域範圍,這可能會干擾不知道這些擴展的現有原始碼。
主控台輸出 #
GraalJS 提供與 Nashorn 相容的 print
內建函式。
請注意,GraalJS 還提供 console.log
函式。在純 JavaScript 模式下,它是 print
的別名,但在 Node 模式下執行時,會使用 Node.js 提供的實作。對於 Node 模式下的 console.log
,Java 物件的行為有所不同,因為 Node.js 不會對此類物件進行特殊處理。