写点什么

JS 引擎 (2):Java 平台上 JavaScript 引擎—Rhino/Nashorn 概述

作者:zhoulujun
  • 2023-04-09
    广东
  • 本文字数:2682 字

    阅读完需:约 9 分钟

可以后端开发的 javascript 引擎有

  • Chrome V8 基于 C++

  • java 的 Rhino 引擎(JDK6 被植入),Java8 被替换为 Nashorn

Rhino 和 Nashorn 都是用 Java 实现的 JavaScript 引擎。它们自身都是普通的 Java 程序,运行在 JVM 上

Rhino简介

Rhino [ˈraɪnəʊ]是一种使用 Java 语言编写的 JavaScript 的开源实现,原先由 Mozilla 开发。

Rhino 是一种动态类型的、基于对象的脚本语言,它可以简单地访问各种 Java 类库。

当时 Netscape 想用纯 Java 来实现新版浏览器,自然需要一个 Java 版的 JavaScript 引擎实现;另外也希望能在服务器端把 JavaScript 当作 Java 应用里的脚本语言使用。于是 Rhino 就诞生了。

官网自带Rhino历史,跟Wikipedia上的Rhino词条基本上一样,有兴趣的话可以去看看。

Parser是从 SpiderMonkey 移植过来的。自然也是手写的纯递归下降式。JavaScript 对象的接口是 org.mozilla.javascript.Scriptable。主要实现类是 IdScriptableObject、ScriptableObject。用 Object[]来存字段,挺高效的。

IdScriptableObject {  Object[] valueArray;  short[] attributeArray;  // ...}
复制代码

Rhino 可以通过参数从 11 个预设的优化基本中选择一个使用。只从 JVM 以上的层面看,Rhino 既可以工作于纯解释模式(-1),也可以工作于纯编译模式(0-9)。这11个级别分别是

Rhino 优化级别

当优化级别为-1 时,Rhino 使用一个用 Java 写的字节码解释器来解释执行 JavaScript。

此时,Rhino 的工作流程简单说是:( [ ... ]表示数据实体,( ... )表示 Rhino 处理数据的程序)

[ JavaScript 源码 ] -> ( 语法分析器 Parser ) -> [ 抽象语法树(AST) ast ] -> ( Rhino 内部表现形式生成器 IRFactory ) -> [ Rhino 内部表现形式 ScriptNode ] -> ( Rhino 字节码生成器 CodeGenerator ) -> [ Rhino 字节码 Icode ] -> ( Rhino 解释器 Interpreter ) -> [ 运行结果 ]

这里说的 Rhino 字节码是 Rhino 内部用来表示 JavaScript 程序语义的一套字节码,跟 JVM 所支持的 Java 字节码没关系。      当优化级别为 0~9 时,Rhino 使用一个用 Java 写的编译器将 JavaScript 编译为 Java 字节码;生成出来的 Java 字节码交由 JVM 直接执行。至于底下的 JVM 是解释执行 Java 字节码,还是将 Java 字节码编译为机器码再执行,Rhino 并不关心。此时,Rhino 的工作流程简单说是:

[ JavaScript 源码 ] -> ( 语法分析器 Parser ) -> [ 抽象语法树(AST) ast ] -> ( Rhino 内部表现形式生成器 IRFactory ) -> [ Rhino 内部表现形式 ScriptNode ] -> ( 可选优化 Optimizer ) -> ( Java 字节码生成器 Codegen ) -> [ Java Class 文件(包含 Java 字节码) ] -> JVM 加载和执行生成的字节码 -> [ 运行结果 ]

只从 JVM 以上的层面看,Nashorn 是一种单层的纯编译型 JavaScript 实现。所有 JavaScript 代码在首次实际执行前都会被编译为 Java 字节码交由 JVM 执行。

这种以编译的方式模式执行 JavaScript,跟一个 Java 源码编译器(例如 javac)把 Java 源码编译为 Class 文件然后交由 JVM 执行,过程是类似的。只不过 Rhino 做的优化不够多而且 JavaScript 的语义也远比 Java 动态,所以此时 Rhino 上运行 JavaScript 的性能仍然无法跟 Java 的性能比。     

JDK6 JDK7  Rhino 区别

顺带一提,Sun/Oracle JDK6 / OpenJDK6 中自带的 Rhino 是经过裁剪的,去掉了 Mozilla Rhino 中的部分功能。其中一个被去掉的功能就是 Rhino 的编译模式。这意味着 JDK6 自带的 Rhino 只能用解释模式运行。

而 Oracle JDK7 / OpenJDK7 放宽了这一限制,当有 SecurityManager 时只能用解释模式,否则可以配置"rhino.opt.level"系统属性来设置 Rhino 的优化级别;默认仍然是用解释模式(优化级别默认为-1)。

Nashorn 起初是 Oracle 内部一个实验项目,用于验证 JSR 292 功能的完整性、可用性、易用性。后来得到了内部的关注,决定将其产品化,作为默认的 JavaScript 实现替换掉从 JDK6 开始包含在 JDK 之中的 Rhino。

Nashorn

Nashorn(读作 Naz-horn[naːsˌɔn])是 Oracle 全新开发的 JavaScript 实现。高度兼容 ECMAScript 5 标准,并尽可能兼容 Rhino。它使用 Java 语言实现,运行在 JVM 上,借助 JDK7 开始包含的JSR 292(invokedynamic)新功能达到较高的性能,同时保持代码的相对整洁

在 2012 年底 Nashorn 就已经达到可以完全通过test262测试套件的兼容性,就这点说它甚至比 SpiderMonkey、V8 更佳兼容于标准。

Nashorn 是一个纯编译的 JavaScript 引擎。它没有用 Java 实现的 JavaScript 解释器,而只有把 JavaScript 编译为 Java 字节码再交由 JVM 执行这一种流程,跟 Rhino 的编译流程类似

Nashorn 还在快速开发中,日新月异,所以它的工作流程在不断变化。简单来说,Nashorn 的编译入口可以从 Context.compile()开始看:

[ JavaScript 源码 ] -> ( 语法分析器 Parser ) -> [ 抽象语法树(AST) ir ] -> ( 编译优化 Compiler ) -> [ 优化后的 AST + Java Class 文件(包含 Java 字节码) ] -> JVM 加载和执行生成的字节码 -> [ 运行结果 ]

只从 JVM 以上的层面看,Nashorn 是一种单层的纯编译型 JavaScript 实现。所有 JavaScript 代码在首次实际执行前都会被编译为 Java 字节码交由 JVM 执行。

(当然 JVM 自身可能是混合执行模式的,例如 HotSpot VM 与 J9 VM。所以 Nashorn 在实际运行中可能需要一定预热才会达到最高速度)

Nashorn 不但可以执行 JavaScript,还可以当作库为其它工具提供一些基础服务。例如说它现在为NetBeans IDE中的JavaScript编辑器提供语法高亮支持和调试支持

从 Oracle JDK 8 build 82 开始,Nashorn 已经作为 JDK8 的一部分包含在安装包中。安装后可以在 JDK 安装目录的 jre/lib/ext/nashorn.jar 找到 Nashorn 的实现。

直接使用 Java 类的实例来容纳 JavaScript 对象的字段,在对象内嵌入字段而不放在 spill array 里的好处是:

  1. 对象更加紧凑,数据离得更近,局部性更好

  2. 数组访问有边界检查,而对象字段访问则没有,后者效率更高


参考内容:

Rhino 和 Nashorn 到底怎么运行? - RednaxelaFX 的回答 - 知乎 https://www.zhihu.com/question/27631001/answer/37407481

各 JavaScript 引擎的简介,及相关资料/博客收集帖 https://hllvm-group.iteye.com/group/topic/37596

转载本站文章《JS引擎(2):Java平台上JavaScript引擎—Rhino/Nashorn概述》,请注明出处:https://www.zhoulujun.cn/html/webfront/browser/webkit/2020_0718_8520.html

用户头像

zhoulujun

关注

还未添加个人签名 2021-06-25 加入

15年草根站长,尽在:zhoulujun.cn

评论

发布
暂无评论
JS引擎(2):Java平台上JavaScript引擎—Rhino/Nashorn概述_JavaScript引擎_zhoulujun_InfoQ写作社区