- 適用於 JDK 23 的 GraalVM (最新)
- 適用於 JDK 24 的 GraalVM (搶先體驗)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
ScriptEngine 實作
GraalJS 提供符合 JSR-223 標準的 javax.script.ScriptEngine
實作,用於執行 JavaScript。請注意,提供此功能是為了舊版相容性,以便讓目前基於 ScriptEngine
的實作更容易遷移。我們強烈建議使用者使用 org.graalvm.polyglot.Context
介面來直接控制許多設定,並從 GraalVM 中更精細的安全設定中受益。
注意:從適用於 JDK 21 的 GraalVM 開始,GraalVM 預設不再包含
ScriptEngine
。如果您依賴該功能,則必須遷移您的設定,明確地依賴於 script engine 模組並將其新增至模組路徑。
若要啟用 js-scriptengine
模組,請將其作為 Maven 相依性新增,如下所示
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
<version>${graaljs.version}</version>
</dependency>
<dependency>
<groupId>org.graalvm.polyglot</groupId>
<artifactId>js</artifactId>
<version>${graaljs.version}</version>
<type>pom</type>
</dependency>
如果您未使用 Maven,則需要手動將 js-scriptengine.jar 檔案新增至模組路徑,例如,--module-path=languages/js/graaljs-scriptengine.jar
。在某些情況下,您可能還需要將 --add-modules org.graalvm.js.scriptengine
新增至命令列,以確保找到 ScriptEngine
。只有當您想要直接使用 GraalJSScriptEngine
時 (請參閱下方),才需要明確地依賴 org.graalvm.js.scriptengine
模組。最後,也可以使用 jlink
來產生包含 GraalJS 的 ScriptEngine
的自訂 Java 執行環境映像。
可以在 GitHub 上的 GraalJS 儲存庫中找到範例 pom.xml 檔案。
使用建議 #
為了避免不必要的 JavaScript 來源重新編譯,建議使用 CompiledScript.eval
而不是 ScriptEngine.eval
。這樣可以防止 JIT 編譯的程式碼在對應的 CompiledScript
物件存在時被垃圾回收。
單執行緒範例
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
CompiledScript script = ((Compilable) engine).compile("console.log('hello world');");
script.eval();
多執行緒範例
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
CompiledScript script = ((Compilable) engine).compile("console.log('start');var start = Date.now(); while (Date.now()-start < 2000);console.log('end');");
new Thread(new Runnable() {
@Override
public void run() {
try {
// Create ScriptEngine for this thread (with a shared polyglot Engine)
ScriptEngine engine = manager.getEngineByName("js");
script.eval(engine.getContext());
} catch (ScriptException scriptException) {
scriptException.printStackTrace();
}
}
}).start();
script.eval();
透過 Bindings
設定選項 #
ScriptEngine
介面沒有提供設定選項的預設方式。作為一種因應措施,GraalJSScriptEngine
支援透過 Bindings
設定一些 Context
選項。這些選項為
polyglot.js.allowHostAccess <boolean>
polyglot.js.allowNativeAccess <boolean>
polyglot.js.allowCreateThread <boolean>
polyglot.js.allowIO <boolean>
polyglot.js.allowHostClassLookup <boolean 或 Predicate<String>>
polyglot.js.allowHostClassLoading <boolean>
polyglot.js.allowAllAccess <boolean>
polyglot.js.nashorn-compat <boolean>
polyglot.js.ecmascript-version <String>
這些選項控制套用至已評估的 JavaScript 程式碼的沙箱規則,且預設設定為 false
,除非應用程式在 Nashorn 相容模式 (--js.nashorn-compat=true
) 下啟動。
請注意,使用 ScriptEngine
表示允許使用實驗性選項。這是透過 Bindings
傳遞的允許選項的完整清單;如果您需要將其他選項傳遞給 GraalJS,則需要手動建立 Context
,如下所示。
若要透過 Bindings
設定選項,請在引擎的指令碼內容初始化之前使用 Bindings.put(<選項名稱>, true)
。請注意,即使呼叫 Bindings#get(String)
也可能會導致內容初始化。下列程式碼顯示如何透過 Bindings
啟用 polyglot.js.allowHostAccess
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true);
bindings.put("javaObj", new Object());
engine.eval("(javaObj instanceof Java.type('java.lang.Object'));"); // it will not work without allowHostAccess and allowHostClassLookup
如果使用者在呼叫 bindings.put("polyglot.js.allowHostAccess", true);
之前,例如呼叫 engine.eval("var x = 1;")
,則此範例將不起作用,因為任何呼叫 eval
都會強制內容初始化。
透過系統屬性設定選項 #
在啟動 JVM 之前,可以透過在系統屬性前面加上 polyglot.
來設定 JavaScript 引擎的選項。
java -Dpolyglot.js.ecmascript-version=2022 MyApplication
或者,可以在建立 ScriptEngine
之前,從 Java 應用程式內以程式設計方式設定 JavaScript 引擎的選項。然而,這僅適用於傳遞至 JavaScript 引擎的選項 (例如 js.ecmascript-version
),而不適用於範例中提到的可以透過 Bindings
設定的選項。另一個注意事項是,這些系統屬性由所有並行執行的 ScriptEngine
共用。
手動建立 Context
以獲得更大的彈性 #
也可以透過 Context.Builder
的執行個體,將 Context
選項直接傳遞給 GraalJSScriptEngine
ScriptEngine engine = GraalJSScriptEngine.create(null,
Context.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.allowHostClassLookup(s -> true)
.option("js.ecmascript-version", "2022"));
engine.put("javaObj", new Object());
engine.eval("(javaObj instanceof Java.type('java.lang.Object'));");
這樣可以設定 GraalJS 中可用的所有選項。但缺點是會對 GraalJS 產生硬性相依性,例如 GraalJSScriptEngine
和 Context
類別。
支援的檔案副檔名 #
javax.script.ScriptEngine
的 GraalJS 實作支援 JavaScript 來源檔案的 js 檔案副檔名,以及 ES 模組的 mjs 副檔名。