- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
ScriptEngine 实现
GraalJS 提供了一个符合 JSR-223 规范的 javax.script.ScriptEngine
实现,用于运行 JavaScript。请注意,此功能是出于兼容性考虑而提供的,以便基于 ScriptEngine
的现有实现能够更轻松地进行迁移。我们强烈建议用户使用 org.graalvm.polyglot.Context
接口直接控制多项设置,并受益于 GraalVM 中更精细的安全设置。
注意:自 GraalVM for JDK 21 起,GraalVM 默认不再包含
ScriptEngine
。如果您依赖此功能,则需要迁移您的设置,以显式依赖脚本引擎模块并将其添加到 module path(模块路径)中。
要启用 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 运行时映像。
示例 pom.xml 文件可在 GitHub 上的 GraalJS 仓库中找到。
使用建议 #
为避免 JavaScript 源文件不必要的重新编译,建议使用 CompiledScript.eval
而非 ScriptEngine.eval
。只要相应的 CompiledScript
对象存在,这就能防止 JIT 编译的代码被垃圾回收。
单线程示例
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 or 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(
。请注意,即使调用 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
选项也可以通过 Context.Builder
实例直接传递给 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
类。
支持的文件扩展名 #
GraalJS 的 javax.script.ScriptEngine
实现支持 JavaScript 源文件的 *js* 文件扩展名,以及 ES 模块的 *mjs* 扩展名。