Nashorn removal: GraalVM to the rescue!

原文はこちら。
The original entry was written by Christian Wirth (Manager, Oracle Labs / working on the Graal/JavaScript and TruffleRuby implementations).
https://medium.com/graalvm/nashorn-removal-graalvm-to-the-rescue-d4da3605b6cb

OpenJDKのJavaScriptエンジンであるNashornはJEP 335によりJDK 11で廃止対象になり、先頃JEP 372で将来のJDKで削除されることになりました。そのため、Nashornを現在お使いであれば、サポートされなくなるまでの時間が刻々と過ぎていきます。しかし解決策はあります。それは、GraalVMの新しいJavaScriptエンジンです。

JEP 335: Deprecate the Nashorn JavaScript Engine
https://openjdk.java.net/jeps/335
JEP 372: Remove the Nashorn JavaScript Engine
https://openjdk.java.net/jeps/372
GraalVM
https://www.graalvm.org/

GraalVMのJavaScriptには最新かつ互換性があり、高性能なJavaScriptエンジンが含まれています。Nashornとは異なり、このエンジンは最新のECMAScript仕様互換です(現時点ではECMAScript 2019)。

さらに、GraalVM JavaScriptは、Nashorn(およびそれ以前のRhino)と同様、Javaコードとの相互運用性があり、Nashornが導入したほとんどの拡張 (extension) を提供します。場合によっては、コードにいくつかの変更が必要になることがありますが、通常はGraalVM JavaScriptで動作するはずですし、(Nashornに比べて)パフォーマンスがよく、よいツールを備えており、その他の追加言語との相互運用性があるという、追加の利点があります。最後に、GraalVMはNode.jsアプリケーションを実行できるため、アプリケーションのまったく新しい可能性にアクセスできるようになります。

GraalVM JavaScriptはGraalVMから実行できますし、Maven CentralからダウンロードしてGraaVM Compilerを有効化した後にJDK 11以後で実行できます。詳細は後で説明します。

Why you should use GraalVM JavaScript to replace Nashorn

NashornからGraalVMに切り替える決断をする前に、以下のような疑問が浮かぶかもしれません。

  • GraalVMには互換性があるのか?
  • GraalVMは高速なのか?
  • メンテナンスおよびサポートされるのか?

いずれの答えもYESです。その理由は以下の通りです。

Yes, GraalVM JavaScript is compatible with ECMAScript

GraalVM JavaScriptは完全互換のJavaScript/ECMAScriptエンジンです。これは確認済みです。テストスィートでOfficial ECMAScript Conformance Test Suite(test262)のようなテストスィートでチェックされ、外部機関、例えばKangaxの互換性テーブルで検証されています。

Official ECMAScript Conformance Test Suite
https://github.com/tc39/test262
ECMAScript 6 Compatibility Table
https://kangax.github.io/compat-table/es6/

仕様の進化を厳密に追跡し、仕様の成熟に合わせて新しい機能を実装します。2020年3月の時点で、GraalVM JavaScriptは最新のECMAScript 2019仕様と互換性があり、来るECMAScript 2020仕様(現在 –js.ecmascript-version=2020 フラグで隠されています)で期待されるすべての機能を既にサポートしています。

Relevant engines from the ES6 section of the Kangax compatibility table.

Yes, GraalVM JavaScript is compatible with Nashorn’s extensions

NashornはECMAScript仕様を超える拡張が付属しています。これにはJavaとの相互運用性と、主としてJavaとの相互運用に関連する非標準のビルドインオブジェクトや関数が含まれています。GraalVM JavaScriptはほとんどの拡張をサポートします。一部はフラグの背後に隠れています。Nashorn互換のコードのほとんどはそのままGraalVMで動作しますが、一部は少々変更が必要な場合もあります。これは特にセキュリティ関連の機能にあてはまります。これはGraalVMがsecurity by defaultのアプローチを取っており、安全性の低いレガシーな機能を有効化するために追加のフラグが必要だからです。詳細は後述しますが、Nashorn migration guideでもご覧頂けます。移行例は、サーバーサイドレンダリングされたReactアプリをNashornからGraaVMに移動した以前の記事に記載されています。移行例には、サーバーサイドレンダリングするReactアプリケーションをNashornからGraalVMに移行した例があります。

Migration Guide from Nashorn to GraalVM JavaScript
https://github.com/graalvm/graaljs/blob/master/docs/user/NashornMigrationGuide.md
Improve React.js Server-Side Rendering by 150% with GraalVM
https://medium.com/graalvm/improve-react-js-server-side-rendering-by-150-with-graalvm-58a06ccb45df
https://logico-jp.io/2020/02/13/improve-react-js-server-side-rendering-by-150-with-graalvm/

Yes, GraalVM JavaScript is as fast as Nashorn–up to 6x faster!

GraalVM JavaScriptは、アプリケーションに最高のパフォーマンスをもたらすために開発されました。GraalVM JavaScriptは、他のJavaScriptエンジンとパフォーマンスを競います。これにはブラウザでのJavaScript実行も含みます。私たちは以前、GraalVM JavaScriptのベンチマーク結果を発表しましたが、Octane JavaScriptベンチマークスイートで測定したピークパフォーマンスにおいて、GraalVM JavaScriptはNashornの約4倍(Community Edition)または6倍(Enterprise Edition)高速でした。GraalVMチームは、サポートされているすべての言語のパフォーマンスを重視しており、リリースのたびにJavaScriptエンジンのパフォーマンスが向上しています。

Oracle GraalVM announces support for Nashorn migration
https://medium.com/graalvm/oracle-graalvm-announces-support-for-nashorn-migration-c04810d75c1f

Yes, GraalVM is a supported product

GraalVMには2種類あり、一つが完全なオープンソースであるCommunity Editionであり、もう一つはOracle GraalVM Enterprise EditionというOracle製品です。

GraalVM
https://graalvm.org
GraalVM (in oracle.com)
https://www.oracle.com/graalvm

Community Editionはオープンソースとして利用できます。このCommunity Editionは、メンテナンスおよび高頻度なアップデートをするコミュニティに支えられています。

Enterprise Editionは完全にサポートされるOracle製品です。利用者は、24時間365日サポートやセキュリティアップデートといった、Oracleによるメンテナンスサービスを得られるでしょう。

Yes, GraalVM is compatible with Node.js!

Nashornとは異なり、GraalVM JavaScriptはNode.jsアプリケーションを実行できます。素のままで、GraalVMは、オリジナルのNode.jsプラットフォーム用に書かれたほとんど全てのアプリケーションを実行できます。npmモジュール100,000個以上に対してテストすることで検証しています。GraalVMはJavaScriptベースのモジュールだけでなく、(C言語ベースの)ネイティブモジュールも実行できます。GraalVMが現時点で簡単にサポートできない、独自のV8内部に依存しているモジュールはほんのわずかです。通常のWebアプリケーションはGraalVM上で完璧に動作し、言語の相互運用性からツールのサポートまで、Node.jsエコシステムの中でGraalVMの他のすべての利点を利用することができます。

以下はちょっとしたNode.jsアプリケーションです。この中ではnpmパッケージを利用し、Javaコードも利用できます。

const http = require("http");
const span = require("ansispan");
require("colors");
var sys = Java.type('java.lang.System');
var javaVersion = sys.getProperties().getProperty("java.version");
http.createServer(function (request, response) {
    response.writeHead(200, {"Content-Type": "text/html"});
    response.end(span(("GraalVM with Java "+javaVersion).green));
    response.end(span("Hello GraalVM!".green));
}).listen(8000, function() {
    console.log("Graal.js server at http://127.0.0.1:8000/".red);
});

上記アプリケーションを実行するには、上記コードをファイル(例えばindex.js)に保存して実行します。

> npm install http ansispan colors
> node index.js
Graal.js server at http://127.0.0.1:8000/

How to use GraalVM to replace Nashorn

GraalVM JavaScript実行の好ましい方法は、GraalVMインストールから直接実行することです。GraalVMは既存のJDK/JREを置き換えることができるため、やるべきことはGraalVMをインストールし、別のJDKの前に環境変数$PATHに記載してPathを通すことだけです。全てGraalVMダウンロードメディアにパッケージされているので、このセットアップ方法であれば、最高のパフォーマンスとツールとの最上の統合を保証します。何かの理由で既存のJDKをGraalVMに置き換えることができない場合(そんな理由はないと信じていますが)、Mavenでダウンロードし、既存のJDK上でGraalVM JavaScriptを実行する方法を以下で説明します。

GraalVMではgraalvm/binにjsバイナリが入っています。これを使って直接JavaScriptのコードを実行します。

js myPlainJavaScriptApp.js

Javaとの相互運用性を確保したい場合、jvmフラグで完全なJVM上での実行を保証します。このフラグがない場合、コードはネイティブモードで実行されます。このとき、JavaコードはGraalVMのnative-imageツールを使って事前(ahead-of-time)コンパイルされます。このモードでは、Javaへのアクセスはまだ可能ではありますが、動的クラスローディングのような機能は制限されます。

js --jvm myAppWithJavaInterop.js

同様に、GraalVMでサポートされている他のプログラミング言語との相互運用性を有効にしたい場合は、polyglotフラグを使用して有効にします。まず graalvm/bin/gu ツールを使用して、Python、Ruby、Rなどの追加言語をインストールする必要があります。

js --polyglot myAppWithPolyglotInterop.js

Use GraalVM’s polyglot Contexts for interoperability with other languages

GraalVMは、異なるプログラミング言語(Java、JavaScript、Ruby、Python、R、LLVM、WebAssemblyなど)間での相互作用を可能にする高度なpolyglot機能を搭載しています。このケースを完全にサポートするために、既存のJSR 223 Scripting APIに代わるContext APIを提供します。このContext APIは、多様なプログラミング言語からのコードやデータの完全な交換を可能にするだけでなく、異なる言語のコンテキストがアクセス可能なものを制御できるようにします。

Context (GraalVM Truffle Java API Reference)
https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/Context.html
The Java Scripting API
https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide/api.html

Context APIを使うコードの記述はScripting APIのユースケースに類似しており、多くの場合、Scripting APIの実装からContextへの移行は簡単です。

ScriptEngineを使うかわりに

ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("nashorn");
    Object result = engine.eval("40+2");
    assert result.equals(42);

Contextを使うGraalVMでコードを書きます。

Context context = Context.create();
    Value result = context.eval("js", "40+2");
    assert result.asInt() == 42;

このちょっとした変更で、アプリケーションがGraalVMのPolyglotな言語相互運用性を完全に活用できるようになるかもしれません。

Use Scripting API to be backwards compatible with existing code

もちろん、GraalVM JavaScriptは、Scripting APIに対して書かれたコードと互換性があります。最終的にContext APIに移行することには、(他の言語との相互運用性、より細かいセキュリティ、より優れたツールなどといった)いくつかの利点がありますが、最初のステップとしては、私たちの互換性レイヤーを使用すればアプリケーションにとって十分かもしれません。

Context APIの拡張セキュリティ機能のため、 JavaScriptコードからJavaクラスにアクセスできるようにするには、 ScriptEngineに追加のオプションを設定しなければならないかもしれません。詳細は以下のドキュメントを参照ください。

ScriptEngine Implementation
https://github.com/graalvm/graaljs/blob/master/docs/user/ScriptEngine.md

Use the Nashorn compatibility mode for legacy code

GraalVM JavaScriptはデフォルトでECMAScript(2019年、現在)対応のJavaScriptエンジンです。これは、ECMAScript 5.1との互換性がほとんどで、いくつかの限定的なECMAScript 6の機能を持っていたNashornに比べて大きく飛躍しています。

しかし、NashornはECMAScript仕様に対する独自の拡張機能を備えており、いくつかはRhinoの頃からすでに知られていました。GraalVM JavaScriptは、Java.type、Java.from、Java.extendなど、これらの拡張機能のほとんどをサポートしています。いくつかの拡張機能は、特にECMAScriptの機能と競合する場合や、使用時に無視される場合には、nashorn-compatフラグを付けなければ利用できません(対象はJava.isJavaFunction、JSAdapter、JavaImporter など)。

【注意】nashorn-compat フラグの前に experimental-options フラグを付ける必要があります。

js --experimental-options --nashorn-compat myNashornCompatApp.js

本番環境では、Nashorn互換モードを使わずに動作するようアプリケーションを移行することを強くお勧めします(したがって、experimental-optionsフラグは必要ありません)。これにより、GraalVMと進化するJavaScript/ECMAScriptエコシステムとの長期的な互換性が保証されます。しかし、互換性モードを使用することで、GraalVM への移行の最初の一歩を踏み出すことができます

Migration guide from Nashorn to GraalVM JavaScript

デフォルトで利用可能な機能と Nashorn 互換モードで利用可能な機能について説明しているより詳細な移行ガイドがあります。同じフォルダには、Java との相互運用性や JavaScript/ECMAScript/エンジンの互換性などに関するGraalVM JavaScript の追加ドキュメントがあります。

Migration Guide from Nashorn to GraalVM JavaScript
https://github.com/graalvm/graaljs/blob/master/docs/user/NashornMigrationGuide.md
Java Interoperability
https://github.com/graalvm/graaljs/blob/master/docs/user/JavaInterop.md
JavaScript Compatibility
https://github.com/graalvm/graaljs/blob/master/docs/user/JavaScriptCompatibility.md

How to run GraalVM JavaScript on a stock JDK

上記では、GraalVMインストールからGraalVM JavaScriptを実行する方法を説明しました。ごく稀に、既存の JDK を GraalVM で置き換えることができない場合があります。これは推奨されない構成方法ではありますが、以下の方法でもGraalVM の JavaScript エンジンを実行できます。

GraalVM JavaScriptは、Javaで完全に実装されたJavaScriptエンジンです。そのため、原則としてJavaと互換性のある(JDK 8以降の)JVM上で実行できます。しかし、これでは、JavaScriptアプリケーションをJavaScriptインタープリタで実行するだけになり、結果的に最適ではないパフォーマンスになってしまいます。GraalVM JavaScriptは、GraalVMコンパイラを使用して、実行するJavaScriptアプリケーションを最適化します。この方法をPartial Evaluation、部分評価と呼んでいます。このようにして、JavaScriptアプリケーションは、実行時に効果的にネイティブアプリケーションにコンパイルされます。GraalVMコンパイラが利用できない場合でも、JavaScriptアプリケーションを実行することができますが、コードは解釈されるだけで(コンパイルされません)、そのため、パフォーマンスはGraalVM上よりも低くなります。

GraalVM JavaScriptとGraalVMコンパイラの両方とも、Mavenからダウンロードできますので、これらを使って、既存のJDKがこれらのモジュールを使用するように設定できます。その結果、任意のJava互換JVM上でGraalVM JavaScriptを良好なパフォーマンスで実行できます。

Group: GraalVM JS
https://mvnrepository.com/artifact/org.graalvm.js
Group: GraalVM Compiler
https://mvnrepository.com/artifact/org.graalvm.compiler

このプロセスについては、以前のブログエントリで紹介しましたが、セットアップ方法を紹介するアプリケーションのサンプルをGitHub上で共有しています(graal-js-jdk11-maven-demo)。注意すべき重要なポイントは、GraalVM JavaScriptが必要とするJARをダウンロードするだけでなく、JavaScriptの部分にGraalVM最適化コンパイラを使用するようにJVMを設定する必要があるということです。

GraalVM’s JavaScript engine on JDK11 with high performance
https://medium.com/graalvm/graalvms-javascript-engine-on-jdk11-with-high-performance-3e79f968a819
Running GraalJS on stock JDK11
https://github.com/graalvm/graal-js-jdk11-maven-demo

Moving beyond Nashorn

おめでとうございます – あなたのアプリケーションがGraalVM上で動作するようになりました。次は何でしょうか?GraalVMには、あなたのアプリケーションが恩恵を受けることができる多くの機能があります。

  • GraalVMのnative-imageツール
    Javaアプリケーションを事前にコンパイルでき、起動時間の短縮ならびに生成されたバイナリの利用リソースを削減できます。
  • GraalVMのpolyglot言語統合
    アプリケーションがLLVMやPython、R、Rubyなどといった言語で記述されたアプリケーションと相互運用できます。
  • GraalVMの提供する共通ツールプラットフォーム
    言語非依存の分析やデバッグ、パフォーマンスチューニングなどが可能です。
  • Node.js互換のGraalVM JavaScript
    任意のNode.jsアプリケーションを実行でき、そのアプリケーションに相互運用性(および他の多くのGraalVM機能)を追加できます。

GraalVMでやるべきことトップ10を解説したブログ記事があります。

Top 10 Things To Do With GraalVM
https://medium.com/graalvm/graalvm-ten-things-12d9111f307d
https://logico-jp.io/2019/07/08/top-10-things-to-do-with-graalvm/

Conclusion

GraalVMのJavaScriptは既存のNashorn統合に役立ちます。ちょっとの手順でアプリケーションはGraalVM上で実行でき、最新のJavaScript開発との互換性が向上し、パフォーマンスが向上します。

GraalVMへの移行の成功話を楽しみにしています。期待通りに動かない場合には是非ご連絡ください。

GraalVM Community
https://www.graalvm.org/community/

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中