JEP 174: Nashorn JavaScript Engine | Nashorn JavaScript 引擎
摘要
设计和实现一个新的轻量级、高性能的 JavaScript 实现,并将其集成到 JDK 中。新的引擎将通过现有的 javax.script
API 提供给 Java 应用程序,并且还可以通过一个新的命令行工具进行更一般的访问。
目标
- Nashorn 将基于 ECMAScript-262 第 5.1 版语言规范,并且必须通过 ECMAScript-262 符合性测试。
- Nashorn 将支持
javax.script
(JSR 223)API。 - 支持从 JavaScript 调用 Java 代码以及从 Java 调用 JavaScript 代码。这包括直接映射到 JavaBeans。
- Nashorn 将定义一个新的命令行工具
jjs
,用于在 "shebang" 脚本、文档和编辑字符串中评估 JavaScript 代码。 - Nashorn 应用程序的性能和内存使用量应该明显优于 Rhino。
- Nashorn 不会暴露任何额外的安全风险。
- 提供的库应在本地化环境下正常工作。
- 错误消息和文档将国际化。
非目标
- Nashorn 仅支持 ECMAScript-262 第 5.1 版,不支持第 6 版的任何功能,也不支持其他 JavaScript 实现提供的非标准功能。
- Nashorn 不包括浏览器插件 API。
- Nashorn 不包括对 DOM/CSS 或任何相关库(如 jQuery、Prototype 或 Dojo)的支持。
- Nashorn 不包括直接调试支持。
动机
Rhino 的性能远远落后于其他 JavaScript 引擎。为了提高性能,现在必须对 Rhino 进行重写,用一个专为充分利用 JVM 而设计的代码生成器替换其解释器。与重写非常古老的 Rhino 代码相比,我们选择从头开始。
Java 社区有利于将 JVM 推广为除 Java 以外的其他语言的可行平台。这推进了技术的发展并吸引了新的开发人员。
描述
Nashorn 引擎对 JavaScript 源代码进行五个阶段的评估:词法分析器 -> 解析器 -> 代码生成 -> 加载 -> 运行时。
词法分析器将 Unicode 字符数组(源代码)翻译成词法单元或标记流,例如数字、字符串、标识符或特殊字符。
Nashorn 使用基于 ECMAScript 262 语言规范的递归下降解析器。解析器接收词法分析器产生的标记流,并收集与语言语法匹配的标记。当识别到语法单元时,解析器构建 JavaScript 代码的中间表示(AST/IR)。
代码生成器接收 AST/IR 并生成 JVM 字节码。当执行时,这些字节码实现了原始 JavaScript 源代码的语义。
代码生成分为两个步骤。第一步将 AST/IR 降低到更接近 JVM 指令集的形式。这个转换包括转换控制流,将表达式简化为基本操作,并简化调用表达式。这一步还负责定义用于管理数据使用、空间和类型的符号。
第二步通过 ASM 库将 AST/IR 转换成字节码。ASM 将实际字节码发出以形成一个脚本类。这个类被缓存在内存中,以供脚本加载器后续使用。
生成脚本类后,需要将其安装到 JVM 中。这是通过使用自定义安全类加载器的 defineClass 完成的。在生成的代码中遇到特殊的 Nashorn 对象类时,也使用该加载器来合成。一旦加载了脚本类,就会调用其中的 runMethod 方法。
执行代码需要几个库的支持。
主要的运行时库包括直接支持执行代码所需的方法,例如 toString
和 toNumber
所需的类型转换例程,以及像 allocateArray
这样的分配例程。
链接器库包括使用 java.lang.invoke
API(JSR 292)辅助绑定 invokedynamic
调用的方法。绑定过程非常复杂,要搜索正确的方法,定义保护以确保调用站点仍然是正确的,并且如果有任何变化需要不同的正确方法,则重新链接。链接器还管理调用历史记录,以便在连续的重新链接上加快查找速度。
JavaScript 对象库包括对所有标准 JavaScript 对象的支持,例如 Object
、Function
、Number
、String
、Date
、Array
、RegExp
等。它还包括用于字符串操作、复杂数学、应用函数等的函数。
Nashorn 使用 invokedynamic
来实现其所有调用。如果调用具有 Java 对象接收器,Nashorn 会尝试将调用绑定到适当的 Java 方法,而不是 JavaScript 函数。Nashorn 完全可以决定如何解析方法。例如,如果它不能在接收器中找到一个字段,它会寻找等效的 Java Bean 方法。结果对于从 JavaScript 到 Java 的调用是完全透明的。
Java 开发人员可以使用 javax.script
(JSR 223)API 来评估并回调 JavaScript 代码。
可替代方案
Rhino 有着悠久的历史,以及与之相匹配的源代码。大部分 Rhino 代码支持解释执行模型。为了获得显著的性能提升,执行模型需要利用 Hotspot 解释器 / 编译器。
其他 JavaScript 引擎,如 V8 和 Nitro,提供良好的性能,但需要第二个虚拟机(额外的代码和内存)和一个桥接 API 来与 Java 通信。
测试
JCK
现有的 javax.script JCK 测试应该足够。Nashorn 只是通过 javax.script
API 访问的另一个脚本引擎。Nashorn 本身不公开自己的新 Java 标准 API,因此不需要额外的 JCK 测试。
功能 / 单元测试
现有的测试包括:
- test262(ecmascript.org)
- ES5conform
这些测试涵盖了大部分功能领域,但这些测试必须适应 Nashorn 基于 TestNG 的测试套件。由于 Nashorn 符合 ECMAScript-262 第 5.1 版并具有少数扩展功能,因此这些测试可能并非全部适用。特别是,mozilla_js_tests 包含许多仅适用于 Mozilla 的特定功能,而 Nashorn 将不支持这些功能。因此,需要对这些测试进行一些子集和适应工作,以在 Nashorn 的测试框架中使用这些测试。
现有的 javax.script
API 和 jrunscript
命令行工具的单元测试可以同样用于对 Nashorn 进行测试。
Nashorn 仓库中的其他单元 / 回归测试
这些是 Nashorn 开发人员在实现功能和修复错误过程中开发的测试。
性能测试
- Sunspider JavaScript 核心引擎性能测试(webkit.org)
- v8 (Octane) 性能测试
已经进行了一些适应工作,以在 Nashorn 的测试框架下运行这些测试。
注意:这些性能测试不包括浏览器 API 访问(DOM/CSS 等)。性能测试套件(如 Dromaeo)提供了这些测试,但它们超出了 Nashorn 目前的范围。
安全测试
Nashorn 编译 JavaScript 源代码并生成 Java 类。这些类由特殊的类加载器加载。Nashorn 允许从 JavaScript 调用 Java。必须注意确保这些生成的类遵循 Java 安全模型。需要进行测试,以确保在安全管理器下,Nashorn 也是安全的。对于由 Nashorn 生成的脚本类,不应该有任何不可用于编译的 Java 类文件的额外权限。
影响
- 兼容性:Nashorn 不会实现 JDK 中当前 Rhino 引擎超出 ECMAScript-262 的任何功能。
- 安全性:需要进行测试。
- 性能 / 可伸缩性:需要进行测试。
- I18n/L10n:是的
- 可移植性:否;Nashorn 不包含任何本地代码。
- 文档:是的