跳到主要内容

JEP 174:Nashorn JavaScript 引擎

QWen Max 中英对照

概述​

设计并实现一个新的轻量级、高性能的 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" 脚本、here 文档和编辑字符串中评估 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 代码进行大规模重写,我们选择从头开始。

推动 JVM 成为 Java 以外其他语言的可行平台,这也符合 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)虽然能提供良好的性能,但需要第二个 VM(更多的代码和内存),并且需要一个桥接 API 来与 Java 通信。

测试​

JCK​

现有的 javax.script JCK 测试应该已经足够。Nashorn 只是另一个可以通过 javax.script API 访问的脚本引擎。Nashorn 本身并未暴露任何新的 Java 标准 API,因此不需要额外的 JCK 测试。

功能/单元测试​

现有的测试包括:

这些测试覆盖了大部分功能区域,但这些测试必须适配到 Nashorn 的基于 TestNG 的测试套件中。并非所有这些测试都适用,因为 Nashorn 符合 ECMAScript-262 第 5.1 版标准,并且仅有少量扩展。特别是,mozilla_js_tests 涵盖了许多 Nashorn 不支持的 Mozilla 特定功能。因此,需要进行一些子集划分和适配工作,才能在 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 标准之外的功能。
  • 安全性:需要测试
  • 性能/可扩展性:需要测试
  • 国际化/本地化:是
  • 可移植性:否;Nashorn 不包含任何原生代码。
  • 文档:是