- GraalVM for JDK 23 (最新)
- GraalVM for JDK 24 (抢先体验)
- GraalVM for JDK 21
- GraalVM for JDK 17
- 存档
- 开发版本
脚本引擎实现
GraalJS 提供了一个符合 JSR-223 的 javax.script.ScriptEngine
实现,用于运行 JavaScript。请注意,此功能出于遗留原因提供,以便为当前基于 ScriptEngine
的实现提供更轻松的迁移。我们强烈建议用户使用 org.graalvm.polyglot.Context
接口直接控制许多设置,并从 GraalVM 中更细粒度的安全设置中获益。
注意:从 GraalVM for JDK 21 开始,GraalVM 默认情况下不再包含
ScriptEngine
。如果您依赖它,则需要将您的设置迁移为显式依赖脚本引擎模块,并将它添加到模块路径中。
要启用 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(<option name>, 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 实现支持 js 文件扩展名用于 JavaScript 源文件,以及 mjs 扩展名用于 ES 模块。