- JDK 23用GraalVM(最新)
- JDK 24用GraalVM(早期アクセス)
- JDK 21用GraalVM
- JDK 17用GraalVM
- アーカイブ
- 開発ビルド
NashornからGraalJSへの移行ガイド
このガイドは、以前にNashornエンジンを対象としていたコードの移行ガイドとして機能します。サポートされているJava相互運用機能の概要については、Java相互運用性ガイドを参照してください。
Nashornエンジンは、JEP 335の一部としてJDK 11で非推奨となり、JEP 372の一部としてJDK15から削除されました。
GraalJSは、以前にNashornエンジンで実行されていたJavaScriptコードの代替として使用できます。GraalJSは、以前Nashornによって提供されていたJavaScriptのすべての機能を提供します。多くはデフォルトで利用可能ですが、一部はオプションの後ろにあり、他のものはソースコードにわずかな変更を加える必要があります。
NashornとGraalJSはどちらも、Java相互運用性に対して同様の構文とセマンティクスをサポートしています。注目すべき違いの1つは、GraalJSがデフォルトで安全なアプローチを取っていることです。つまり、Nashornでデフォルトで利用可能だった一部の機能は、明示的に有効にする必要があります。移行に関連する最も重要な違いを以下に示します。
デフォルトで利用可能なNashorn機能(セキュリティ設定に依存)
Java.type
,Java.typeName
Java.from
,Java.to
Java.extend
,Java.super
- Javaパッケージグローバル変数:
Packages
、java
、javafx
、javax
、com
、org
、edu
Nashorn互換モード #
GraalJSはNashorn互換モードを提供します。Nashorn互換に必要な機能の一部は、js.nashorn-compat
オプションが有効になっている場合にのみ利用できます。これは、GraalJSがデフォルトで公開したくないNashorn固有の拡張機能の場合です。
このオプションを使用するには、実験的な機能をアンロックする必要があることに注意してください。さらに、このオプションを設定すると、たとえばレガシーScriptEngine
で動作する場合など、GraalJSのデフォルトで安全なアプローチが一部の場合に損なわれることに注意してください。
Nashorn互換モードを使用する場合、デフォルトでは、ECMAScript 5が互換性レベルとして設定されます。js.ecmascript-version
オプションを使用して、別のECMAScriptバージョンを指定できます。これにより、Nashornとの完全な互換性が損なわれる可能性があることに注意してください。オプションを設定する方法のコード例は、このセクションの最後に示されています。
js.nashorn-compat
オプションは、次のように設定できます。
- コマンドラインオプションを使用する
js --experimental-options --js.nashorn-compat=true
- Polyglot APIを使用する
import org.graalvm.polyglot.Context; try (Context context = Context.newBuilder().allowExperimentalOptions(true).option("js.nashorn-compat", "true").build()) { context.eval("js", "print(__LINE__)"); }
- Javaアプリケーションの起動時にシステムプロパティを使用する(アプリケーションの
Context.Builder
でallowExperimentalOptions
を有効にすることも忘れないでください)java -Dpolyglot.js.nashorn-compat=true MyApplication
nashorn-compat
オプションでのみ利用可能な機能には、以下が含まれます。
Java.isJavaFunction
、Java.isJavaMethod
、Java.isScriptObject
、Java.isScriptFunction
new Interface|AbstractClass(fn|obj)
JavaImporter
JSAdapter
- 文字列値に対する
java.lang.String
メソッド load("nashorn:parser.js")
、load("nashorn:mozilla_compat.js")
exit
、quit
js.ecmascript-version
オプションは、同様の方法で設定できます。これはサポートされているオプションであるため、ecmascript-version
を設定するためだけにexperimental-options
オプションを提供する必要はありません。
js --js.ecmascript-version=2020
Nashorn構文拡張機能 #
Nashorn構文拡張機能は、js.syntax-extensions
実験的オプションを使用して有効にできます。これらは、Nashorn互換モード(js.nashorn-compat
)でもデフォルトで有効になっています。
GraalJSとNashornの比較 #
GraalJSは、意図的な設計上の決定であるいくつかの点でNashornとは異なります。
デフォルトで安全 #
GraalJSはデフォルトで安全なアプローチを採用しています。埋め込みによって明示的に許可されない限り、JavaScriptコードはJavaクラスにアクセスしたり、ファイルシステムにアクセスしたりすることはできません。Nashorn互換機能を含むGraalJSのいくつかの機能は、関連するセキュリティ設定が十分に許容範囲である場合にのみ使用できます。
セキュアなデフォルト制限をアプリケーションおよびホストシステムに解除する変更によるセキュリティへの影響を必ず理解してください。
利用可能な設定の完全なリストについては、Context.Builder
を参照してください。これらのオプションは、Polyglot APIでコンテキストを構築するときに定義できます。
GraalJSの機能を有効にするためによく必要なオプションは次のとおりです。
allowHostAccess()
:ゲストアプリケーションからアクセスできるパブリッククラスのパブリックコンストラクター、メソッド、またはフィールドを設定します。アクセスを選択的に有効にするには、HostAccess.EXPLICIT
またはカスタムHostAccess
ポリシーを使用します。制限のないアクセスを許可するには、HostAccess.ALL
に設定します。allowHostClassLookup()
:ゲストアプリケーションによってルックアップできるJavaホストクラスを指定するフィルターを設定します。すべてのクラスのルックアップを許可するには、述語className -> true
に設定します。allowIO()
:ゲスト言語がホストシステムで無制限のIO操作を実行できるようにします。たとえば、ファイルシステムからload()
するために必要です。IOを有効にするには、true
に設定します。
レガシーScriptEngine
でコードを実行する場合は、そこでの設定方法に関するBindings
を介したオプションの設定を参照してください。
最後に、nashorn-compat
モードでは、ScriptEngine
(Context
ではなく)でコードを実行するときに関連するオプションが有効になり、そのセットアップでNashornとの互換性が向上することに注意してください。
ランチャー名 js
#
GraalJSには、js
という名前のバイナリランチャーが付属しています。ビルド環境によっては、GraalJSは引き続きNashornとそのjjs
ランチャーを出荷する場合があることに注意してください。
ScriptEngine名 graal.js
#
GraalJSはScriptEngine
のサポートとともに提供されます。これは、「graal.js」、「JavaScript」、「js」を含むいくつかの名前で登録されます。完全なNashorn互換性が必要な場合は、上記のようにNashorn互換モードをアクティブにしてください。ビルドセットアップによっては、GraalJSは引き続きNashornを出荷し、ScriptEngine
経由で提供する場合があります。詳細については、ScriptEngineの実装を参照してください。
ClassFilter
#
GraalJSは、ポリグロットContext
で起動するときにクラスフィルターを提供します。Context.Builder.hostClassFilter
を参照してください。
完全修飾名 #
GraalJSでは、Java.type(typename)
を使用する必要があります。デフォルトでは、完全修飾クラス名だけでクラスにアクセスすることはサポートされていません。Java.type
はより明確になり、JavaScriptコードでのJavaクラスの誤った使用を回避できます。たとえば、このパターンを見てください
var bd = new java.math.BigDecimal('10');
次のように表現する必要があります
var BigDecimal = Java.type('java.math.BigDecimal');
var bd = new BigDecimal('10');
損失のある変換 #
GraalJSでは、Javaメソッドを呼び出すときに、引数の損失のある変換は許可されていません。これにより、検出が困難な数値のバグが発生する可能性があります。
GraalJSは常に、損失なしで変換できる引数型が最も狭いオーバーロードされたメソッドを選択します。そのようなオーバーロードされたメソッドが利用できない場合、GraalJSは損失のある変換の代わりにTypeError
をスローします。一般に、これはどのオーバーロードされたメソッドが実行されるかに影響します。
カスタムtargetTypeMapping
を使用して、動作をカスタマイズできます。HostAccess.Builder#targetTypeMappingを参照してください。
ScriptObjectMirror
オブジェクト #
GraalJSは、ScriptObjectMirror
クラスのオブジェクトを提供しません。代わりに、JavaScriptオブジェクトは、JavaのMap
インターフェイスを実装するオブジェクトとしてJavaコードに公開されます。
ScriptObjectMirror
インスタンスを参照するコードは、タイプをインターフェイス(Map
またはList
)または同様の機能を提供するポリグロットValueクラスのいずれかに変更することで書き換えることができます。
マルチスレッディング #
GraalVMでのJavaScriptの実行は、Javaコードから複数のContext
オブジェクトを作成することにより、マルチスレッドをサポートします。コンテキストはスレッド間で共有できますが、各コンテキストは一度に1つのスレッドによってアクセスされる必要があります。複数のJavaScriptエンジンをJavaアプリケーションから作成でき、複数のスレッドで並行して安全に実行できます。
Context polyglot = Context.create();
Value array = polyglot.eval("js", "[1,2,42,4]");
GraalJSでは、現在のContext
へのアクセスを許可するJavaScriptからのスレッドの作成は許可されていません。さらに、GraalJSでは、同時実行スレッドが同じContext
に同時にアクセスすることは許可されていません。これにより、マルチスレッドに対応していない言語で、データ競合などの管理不可能な同期の問題が発生する可能性があります。例えば
new Thread(function() {
print('printed from another thread'); // throws Exception due to potential synchronization problems
}).start();
JavaScriptコードは、Javaで実装されたRunnable
を使用してスレッドを作成および開始できます。子スレッドは、親スレッドまたは他のポリグロットスレッドのContext
にアクセスできません。違反した場合、IllegalStateException
がスローされます。ただし、子スレッドは新しいContext
インスタンスを作成できます。
new Thread(aJavaRunnable).start(); // allowed on GraalJS
適切な同期が行われている場合、複数のコンテキストを異なるスレッド間で共有できます。複数のスレッドからJavaScript Context
を使用するJavaアプリケーションの例は、こちらにあります。
Nashorn互換モードでのみ利用可能な拡張機能 #
Nashorn で利用可能だった以下の JavaScript 拡張機能は、デフォルトでは GraalJS では無効になっています。これらは Nashorn 互換モードで提供されます。これらの機能に基づいて新しいアプリケーションを実装することは強く推奨されず、既存のアプリケーションを GraalVM に移行する手段としてのみ使用してください。
String の length
プロパティ #
GraalJS は、String の length プロパティを特別に扱いません。String の長さにアクセスする正規の方法は、length
プロパティを読み取ることです。
myJavaString.length;
Nashorn では、ユーザーは length
にプロパティとしても関数としてもアクセスできます。既存の関数呼び出し length()
は、プロパティアクセスとして表現する必要があります。Nashorn の動作は、Nashorn 互換モードで模倣されています。
JavaScript グローバルオブジェクトの Java パッケージ #
GraalJS では、完全修飾名の代わりに Java.type
を使用する必要があります。Nashorn 互換モードでは、以下の Java パッケージが JavaScript グローバルオブジェクトに追加されます: java
, javafx
, javax
, com
, org
, および edu
。
JavaImporter #
JavaImporter
機能は、Nashorn 互換モードでのみ利用可能です。
JSAdapter #
非標準の JSAdapter
機能の使用は推奨されず、同等の標準の Proxy
機能に置き換える必要があります。互換性のために、JSAdapter
は Nashorn 互換モードで引き続き利用可能です。
Java.* メソッド #
Java
グローバルオブジェクトで Nashorn によって提供されるいくつかのメソッドは、Nashorn 互換モードでのみ利用可能であるか、現在 GraalJS でサポートされていません。Nashorn 互換モードで利用可能なのは、Java.isJavaFunction
, Java.isJavaMethod
, Java.isScriptObject
, および Java.isScriptFunction
です。Java.asJSONCompatible
は現在サポートされていません。
アクセサー #
Nashorn 互換モードでは、GraalJS では、ユーザーは get
、set
、または is
を省略して、プロパティとして名前を使用するだけでゲッターとセッターにアクセスできます。
var Date = Java.type('java.util.Date');
var date = new Date();
var myYear = date.year; // calls date.getYear()
date.year = myYear + 1; // calls date.setYear(myYear + 1);
GraalJS は、アクセスの順序に関して Nashorn の動作を模倣します。
- 読み取り操作の場合、GraalJS は最初に、
get
という名前とキャメルケースのプロパティ名を持つゲッターを呼び出そうとします。それが利用できない場合、is
という名前とキャメルケースのプロパティ名を持つゲッターが呼び出されます。2 番目のケースでは、Nashorn とは異なり、結果の値が boolean 型でなくても返されます。両方のメソッドが利用できない場合にのみ、プロパティ自体が読み取られます。 - 書き込み操作の場合、GraalJS は、
set
という名前とキャメルケースのプロパティ名を持つセッターを呼び出し、その関数に引数として値を提供しようとします。セッターが利用できない場合、プロパティ自体が書き込まれます。
Nashorn(したがって、GraalJS)は、プロパティの読み取り/書き込みと関数呼び出しを明確に区別していることに注意してください。Java クラスに、同じ名前のフィールドとメソッドの両方が公開されている場合、obj.property
は常にフィールド(または上記で説明したゲッター)を読み取り、obj.property()
は常にそれぞれのメソッドを呼び出します。
考慮すべき追加の側面 #
GraalJS の機能 #
GraalJS は、最新の ECMAScript 仕様の機能と、いくつかの拡張機能をサポートしています。詳しくは、JavaScript Compatibility を参照してください。この例では、これらの拡張機能を認識していない既存のソースコードに干渉する可能性のあるオブジェクトをグローバルスコープに追加することに注意してください。
コンソール出力 #
GraalJS は、Nashorn と互換性のある print
組み込み関数を提供します。
GraalJS は console.log
関数も提供することに注意してください。これは純粋な JavaScript モードでは print
のエイリアスですが、Node モードで実行する場合は Node.js によって提供される実装を使用します。Node.js は Java オブジェクトの特別な処理を実装していないため、Node モードでは console.log
の Java オブジェクトに関する動作が異なります。