- 适用于 JDK 24 的 GraalVM(最新)
- 适用于 JDK 25 的 GraalVM(早期访问)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 存档
- 开发构建
从 Nashorn 迁移到 GraalJS 指南
本指南旨在作为以前针对 Nashorn 引擎的代码的迁移指南。有关支持的 Java 互操作性功能的概述,请参阅Java 互操作性指南。
Nashorn 引擎已在 JDK 11 中作为 JEP 335 的一部分弃用,并已在 JDK 15 中作为 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 语法扩展 #
Nashorn 语法扩展可以使用 js.syntax-extensions
实验性选项启用。它们在 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
设置选项,了解如何在其中设置它们。
最后,请注意,nashorn-compat
模式在 ScriptEngine
上执行代码时(但不在 Context
上)启用相关选项,以便在该设置中提供更好的 Nashorn 兼容性。
启动器名称 js
#
GraalJS 附带一个名为 js
的二进制启动器。请注意,根据构建环境的不同,GraalJS 可能仍然附带 Nashorn 及其 jjs
启动器。
ScriptEngine 名称 graal.js
#
GraalJS 附带 ScriptEngine
支持。它以多个名称注册,包括“graal.js”、“JavaScript”和“js”。如果您需要完整的 Nashorn 兼容性,请务必如上所述激活 Nashorn 兼容模式。根据构建设置,GraalJS 可能仍然附带 Nashorn 并通过 ScriptEngine
提供它。有关更多详细信息,请参阅ScriptEngine 实现。
ClassFilter
#
GraalJS 在启动多语言 Context
时提供一个类过滤器。请参阅 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 代码。
引用 ScriptObjectMirror
实例的代码可以通过将类型更改为接口(Map
或 List
)或多语言 Value 类来重写,该类提供类似的功能。
多线程 #
在 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 的手段。
String 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 不会对这些对象进行特殊处理。