Skip to content

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 方法。

执行代码需要几个库的支持。

主要的运行时库包括直接支持执行代码所需的方法,例如 toStringtoNumber 所需的类型转换例程,以及像 allocateArray 这样的分配例程。

链接器库包括使用 java.lang.invoke API(JSR 292)辅助绑定 invokedynamic 调用的方法。绑定过程非常复杂,要搜索正确的方法,定义保护以确保调用站点仍然是正确的,并且如果有任何变化需要不同的正确方法,则重新链接。链接器还管理调用历史记录,以便在连续的重新链接上加快查找速度。

JavaScript 对象库包括对所有标准 JavaScript 对象的支持,例如 ObjectFunctionNumberStringDateArrayRegExp 等。它还包括用于字符串操作、复杂数学、应用函数等的函数。

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 测试。

功能 / 单元测试

现有的测试包括:

这些测试涵盖了大部分功能领域,但这些测试必须适应 Nashorn 基于 TestNG 的测试套件。由于 Nashorn 符合 ECMAScript-262 第 5.1 版并具有少数扩展功能,因此这些测试可能并非全部适用。特别是,mozilla_js_tests 包含许多仅适用于 Mozilla 的特定功能,而 Nashorn 将不支持这些功能。因此,需要对这些测试进行一些子集和适应工作,以在 Nashorn 的测试框架中使用这些测试。

现有的 javax.script API 和 jrunscript 命令行工具的单元测试可以同样用于对 Nashorn 进行测试。

Nashorn 仓库中的其他单元 / 回归测试

这些是 Nashorn 开发人员在实现功能和修复错误过程中开发的测试。

性能测试

已经进行了一些适应工作,以在 Nashorn 的测试框架下运行这些测试。

注意:这些性能测试不包括浏览器 API 访问(DOM/CSS 等)。性能测试套件(如 Dromaeo)提供了这些测试,但它们超出了 Nashorn 目前的范围。

安全测试

Nashorn 编译 JavaScript 源代码并生成 Java 类。这些类由特殊的类加载器加载。Nashorn 允许从 JavaScript 调用 Java。必须注意确保这些生成的类遵循 Java 安全模型。需要进行测试,以确保在安全管理器下,Nashorn 也是安全的。对于由 Nashorn 生成的脚本类,不应该有任何不可用于编译的 Java 类文件的额外权限。

影响

  • 兼容性:Nashorn 不会实现 JDK 中当前 Rhino 引擎超出 ECMAScript-262 的任何功能。
  • 安全性:需要进行测试。
  • 性能 / 可伸缩性:需要进行测试。
  • I18n/L10n:是的
  • 可移植性:否;Nashorn 不包含任何本地代码。
  • 文档:是的