ScriptEngineの実装

GraalJSは、JavaScriptを実行するためのJSR-223準拠のjavax.script.ScriptEngine実装を提供します。この機能は、現在ScriptEngineに基づいている実装の移行を容易にするために、レガシーの理由で提供されていることに注意してください。多くの設定を直接制御し、GraalVMのより詳細なセキュリティ設定の恩恵を受けるには、org.graalvm.polyglot.Contextインターフェースを使用することを強くお勧めします。

注:JDK 21以降のGraalVMには、デフォルトでScriptEngineが含まれていません。これを使用していた場合は、スクリプトエンジンモジュールに明示的に依存するように設定を移行し、それをモジュールパスに追加する必要があります。

js-scriptengineモジュールを有効にするには、次のようにMaven依存関係として追加します。

<dependency>
    <groupId>org.graalvm.js</groupId>
    <artifactId>js-scriptengine</artifactId>
    <version>${graaljs.version}</version>
</dependency>
<dependency>
    <groupId>org.graalvm.polyglot</groupId>
    <artifactId>js</artifactId>
    <version>${graaljs.version}</version>
    <type>pom</type>
</dependency>

Mavenを使用していない場合は、--module-path=languages/js/graaljs-scriptengine.jarのように、js-scriptengine.jarファイルをモジュールパスに手動で追加する必要があります。場合によっては、ScriptEngineが見つかるように、コマンドラインに--add-modules org.graalvm.js.scriptengineを追加する必要がある場合もあります。GraalJSScriptEngineを直接使用する場合にのみ、org.graalvm.js.scriptengineモジュールへの明示的な依存関係が必要です(下記参照)。最後に、jlinkを使用して、GraalJSのScriptEngineを含むカスタムJavaランタイムイメージを生成することもできます。

例となるpom.xmlファイルは、GitHubのGraalJSリポジトリにあります。

推奨される使用方法 #

JavaScriptソースの不要な再コンパイルを避けるために、ScriptEngine.evalではなくCompiledScript.evalを使用することをお勧めします。これにより、対応するCompiledScriptオブジェクトがアクティブである限り、JITコンパイルされたコードがガベージコレクションされるのを防ぎます。

シングルスレッドの例

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
CompiledScript script = ((Compilable) engine).compile("console.log('hello world');");
script.eval();

マルチスレッドの例

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
CompiledScript script = ((Compilable) engine).compile("console.log('start');var start = Date.now(); while (Date.now()-start < 2000);console.log('end');");
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            // Create ScriptEngine for this thread (with a shared polyglot Engine)
            ScriptEngine engine = manager.getEngineByName("js");
            script.eval(engine.getContext());
        } catch (ScriptException scriptException) {
            scriptException.printStackTrace();
        }
    }
}).start();
script.eval();

Bindingsによるオプションの設定 #

ScriptEngineインターフェースは、オプションを設定するためのデフォルトの方法を提供しません。回避策として、GraalJSScriptEngineBindingsを介していくつかのContextオプションの設定をサポートします。これらのオプションは次のとおりです。

  • polyglot.js.allowHostAccess <boolean>
  • polyglot.js.allowNativeAccess <boolean>
  • polyglot.js.allowCreateThread <boolean>
  • polyglot.js.allowIO <boolean>
  • polyglot.js.allowHostClassLookup <booleanまたはPredicate<String>>
  • polyglot.js.allowHostClassLoading <boolean>
  • polyglot.js.allowAllAccess <boolean>
  • polyglot.js.nashorn-compat <boolean>
  • polyglot.js.ecmascript-version <String>

これらのオプションは、評価されたJavaScriptコードに適用されるサンドボックス化ルールを制御し、アプリケーションがNashorn互換モード(--js.nashorn-compat=true)で開始されていない限り、デフォルトでfalseに設定されます。

ScriptEngineを使用すると、実験的なオプションが許可されることに注意してください。これは、Bindingsを介して渡すことができる許可されたオプションの網羅的なリストです。GraalJSに追加のオプションを渡す必要がある場合は、以下に示すようにContextを手動で作成する必要があります。

Bindingsを介してオプションを設定するには、エンジンのスクリプトコンテキストが初期化されるに、Bindings.put(<option name>, true)を使用します。Bindings#get(String)への呼び出しでもコンテキストの初期化につながる可能性があることに注意してください。次のコードは、Bindingsを介してpolyglot.js.allowHostAccessを有効にする方法を示しています。

ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> true);
bindings.put("javaObj", new Object());
engine.eval("(javaObj instanceof Java.type('java.lang.Object'));"); // it will not work without allowHostAccess and allowHostClassLookup

この例は、たとえばbindings.put("polyglot.js.allowHostAccess", true);を呼び出す前に、ユーザーがengine.eval("var x = 1;")を呼び出すと機能しません。これは、evalへの呼び出しによってコンテキストの初期化が強制されるためです。

システムプロパティによるオプションの設定 #

JavaScriptエンジンへのオプションは、polyglot.をプレフィックスとして付けることで、JVMの開始前にシステムプロパティを介して設定できます。

java -Dpolyglot.js.ecmascript-version=2022 MyApplication

または、ScriptEngineを作成する前に、Javaアプリケーション内からプログラムでJavaScriptエンジンへのオプションを設定できます。ただし、これは、例で示されているBindingsを介して設定できるオプションではなく、JavaScriptエンジンに渡されるオプション(js.ecmascript-versionなど)にのみ機能します。もう1つの注意点として、これらのシステムプロパティは、同時に実行されるすべてのScriptEngineで共有されることです。

より柔軟性のあるContextの手動作成 #

Contextオプションは、Context.Builderのインスタンスを介して、GraalJSScriptEngineに直接渡すこともできます。

ScriptEngine engine = GraalJSScriptEngine.create(null,
        Context.newBuilder("js")
        .allowHostAccess(HostAccess.ALL)
        .allowHostClassLookup(s -> true)
        .option("js.ecmascript-version", "2022"));
engine.put("javaObj", new Object());
engine.eval("(javaObj instanceof Java.type('java.lang.Object'));");

これにより、GraalJSで使用可能なすべてのオプションを設定できます。ただし、GraalJSScriptEngineContextクラスなど、GraalJSへのハード依存が発生します。

サポートされているファイル拡張子 #

javax.script.ScriptEngineのGraalJS実装は、JavaScriptソースファイルにはjsファイル拡張子、ESモジュールにはmjs拡張子をサポートしています。

お問い合わせ