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

zhoulujun發表於2023-04-09

可以後端開發的 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

相關文章