JavaFX 8のWebViewのJavaScriptエンジンは何?

RhinoとNashonでV8ベンチマークをとってみるとこんな感じ。Windows 8.1 / AMD A6-3500 2.1GHz / Java 8で計測。Java7のRhinoとJava8のNashornのベンチを取ってみるではJavaの実行環境付属のエンジンを使っているが、ここでは2014/9/13あたりに取得したソースをビルドしている。

>java -version
java version "1.8.0_11"
Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

>java -Djava.ext.dirs= -jar ..\..\nashorn\dist\nashorn.jar run.js
Richards: 257
DeltaBlue: 77.1
Crypto: 279
RayTrace: 245
EarleyBoyer: 451
RegExp: 165
Splay: 568
NavierStokes: 521
----
Score (version 7): 272

>java -Djava.ext.dirs= -jar ..\..\rhino\build\rhino1_7R5pre\js.jar run.js
Richards: 190
DeltaBlue: 234
Crypto: 222
RayTrace: 387
EarleyBoyer: 411
RegExp: 127
Splay: 230
NavierStokes: 265
----
Score (version 7): 243

次に、同マシン上のHyper-V環境でのFedora 20でV8をビルドして実行。

$ ../out/x64.release/shell run.js
Richards: 12010
DeltaBlue: 12642
Crypto: 9885
RayTrace: 15978
EarleyBoyer: 12220
RegExp: 1767
Splay: 4580
NavierStokes: 9598
----
Score (version 7): 8311

Windows 8.1のブラウザでrun.htmlで実行してみた。

Chrome 37

Score: 11222
Richards: 14594
DeltaBlue: 18599
Crypto: 12070
RayTrace: 23828
EarleyBoyer: 18346
RegExp: 1957
Splay: 8540
NavierStokes: 10505

Firefox 32

Score: 7549
Richards: 12066
DeltaBlue: 4470
Crypto: 11168
RayTrace: 11118
EarleyBoyer: 14621
RegExp: 1416
Splay: 6334
NavierStokes: 12008

IE 11

Score: 818

Richards: 1183
DeltaBlue: 302
Crypto: 874
RayTrace: 537
EarleyBoyer: 2066
RegExp: 1447
Splay: 1903
NavierStokes: 210

Hyper-V上のFedora 20のGNOME Epiphany 3.10.3でrun.htmlで実行してみた。GNOME EpiphanyはWebKit付属のJavaScriptCoreを使っている。

Score: 5351
Richards: 6005
DeltaBlue: 5098
Crypto: 5487
RayTrace: 8945
EarleyBoyer: 9958
RegExp: 1267
Splay: 5574
NavierStokes: 6356

次にJavaFX 8に付属するWebViewだが、GNOME Epiphanyより大分遅い。

Score: 2012
Richards: 1541
DeltaBlue: 1311
Crypto: 2389
RayTrace: 3120
EarleyBoyer: 3007
RegExp: 1261
Splay: 2849
NavierStokes: 1649

JavaFXのJavaScriptエンジンを推定するためにテストを書いてみた。 このテストはJavaScriptのビルトインオブジェクトがサポートしているプロパティをダンプする。実装状況はエンジンによって違うのでエンジンの種類を特定することができる。列の一番左はコンストラクターとプロトタイプのプロパティの数を表示している。ブラウザ無しの場合はJSの部分だけ抜き出して実行している。Objectの部分だけをブラウザごとに調べてみた。JavaFX 8はJavaScriptCoreっぽい。

V8 (Chrome)

Object: 24, 12
IonMonkey (Firefox)

Object: 20, 14
Chakra (IE 11)

undefined: 18, 12
JavaScriptCore (GNOME Epiphany)

Object: 16, 12
Rhino

Object: 18, 12
JDK 7 jrunscript

Object: 18, 12
Nashorn

Object: 18, 7
JDK 8 jjs

Object: 18, 7
V8 コマンドライン

Object: 25, 12 (ChromeよりgetOwnPropertySymbolsが追加されている)
??? (JavaFX 8 WebView)

Object: 16, 12

Nashornでホストオブジェクトを追加するには

RhinoではScriptableObjectを継承することで簡単にプロパティの拡張が可能なホストオブジェクトを実装できた。Nashornではそのような公開インターフェイスは用意されていないが、jdk.nashorn.internal.objects.ScriptObjectを継承し、nasgenというツールを使うことで内部的には実現できている。

Nashornのホストオブジェクト実装方法はJavaScriptで擬似的にクラスを作るのに近い。JavaScriptでクラスを実現するには一般には以下のように行う。

function Greeting() {}
Greeting.prototype.sayHello = function (name) { return 'Hello, ' + name; };
var greeting = new Greeting();

JavaScriptでクラスのインスタンスを生成するには、インスタンスであるオブジェクト自身のほかにコンストラクターとなる関数オブジェクトとプロトタイプオブジェクトの2つのオブジェクトがかかわってくる。

コンストラクターとなる関数オブジェクトはただの関数オブジェクトで、利用者がどう使うかの違いでしかない。関数オブジェクトをコンストラクターとして使うにはnew演算子を使う。関数オブジェクトはprototypeプロパティを持っていて、初期値は空のオブジェクトが設定されている。new演算子を使ってオブジェクトを生成すると、生成したオブジェクトのプロトタイプ内部プロパティがコンストラクターのprototypeプロパティの値に設定される。プロトタイプ内部プロパティとprototypeプロパティは違うものなので、greeting.prototypeundefinedだ。プロトタイプ内部プロパティを取得するにはObject.getPrototypeOf(greeting)を使う。

greeting.prototype // -> undefined
Object.getPrototypeOf(greeting) // -> { sayHello: function () { } }

プロトタイプ内部プロパティはオブジェクトのひな形として機能する。greeting自身にはsayHelloプロパティを設定してはいないが、greeting.sayHelloはプロトタイプ内部プロパティはオブジェクトのsayHelloプロパティの値を返してくれる。

Nashornでホストオブジェクトを実装するには、コンストラクター、プロトタイプ、オブジェクトの実装の3つを用意してやればよい。NashornでもRhinoと同様にアノテーションを使って実装する。ホストオブジェクトのタイプごとに1つのJavaクラスを作ってやればよいのだが、コンストラクターやプロトタイプを自分で扱うため面倒になる。

試しに実装してみたホストオブジェクトの実装はこんな感じだ、Nashornではホストオブジェクトを生成するための公開インターフェイスは用意されておらず、実装はNashorn自身のパッケージであるjdk.nashorn.internal.objectsを使わなければならない。

package jdk.nashorn.internal.objects;

import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;

@ScriptClass("Greeting")
public class NativeGreeting extends ScriptObject {

    // initialized by nasgen
    private static PropertyMap $nasgenmap$;

    static PropertyMap getInitialMap() {
        return $nasgenmap$;
    }

    @Constructor
    public static Object constructor(final boolean isNew, final Object self, final Object... args) {
        return new NativeGreeting(Global.instance());
    }

    NativeGreeting(final Global global) {
        super(global.getGreetingPrototype(), global.getGreetingMap());
    }

    public String sayHello(String name) {
        return "Hello, " + name;
    }

    @Function(attributes = Attribute.NOT_ENUMERABLE)
    public static Object sayHello(final Object self, final Object name) {
        return ((NativeGreeting) self).sayHello((String) name);
    }
}

Nashornのビルド時には、Nativeの実装がコンパイルされた後でnasgenツールがjdk/nashorn/internal/objects/NativeGreeting.classを読み込み、
jdk/nashorn/internal/objects/NativeGreeting$Constructor.classjdk/nashorn/internal/objects/NativeGreeting$Prototype.classの2つのクラスファイルを自動生成する。

jdk.nashorn.internal.objects.GlobalクラスはJavaScriptのグローバルオブジェクトを実装するJavaクラスで、グローバルオブジェクトの初期時に、initConstructor()メソッドを使って、jdk.nashorn.internal.objects.NativeGreeting$Constructorクラスからコンストラクターの関数オブジェクトを生成する。

        this.builtinGreeting  = (ScriptFunction)initConstructor("Greeting");

このコンストラクターはjdk.nashorn.internal.objects.NativeGreeting$Prototypeで実装されるプロトタイプを持っていて、以下のように取得できる。

    ScriptObject getGreetingPrototype() {
        return ScriptFunction.getPrototype(builtinGreeting);
    }

あとはthis.builtinGreetingをJavaScriptから見えるようにすればJavaScriptでGreetingオブジェクトを生成できる。

print(Greeting);
var greeting = new Greeting();
print(greeting);
print(greeting.sayHello('Alice'));

以下、今回使ったパッチ

diff -r 18edd7a1b166 src/jdk/nashorn/internal/objects/Global.java
--- a/src/jdk/nashorn/internal/objects/Global.java	Wed Dec 11 18:09:34 2013 +0100
+++ b/src/jdk/nashorn/internal/objects/Global.java	Sun Sep 14 08:07:01 2014 +0900
@@ -313,6 +313,9 @@
     @Property(name = "__LINE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
     public Object __LINE__;
 
+    @Property(name = "Greeting", attributes = Attribute.NOT_ENUMERABLE)
+    public volatile Object greeting;
+
     /** Used as Date.prototype's default value */
     public NativeDate   DEFAULT_DATE;
 
@@ -364,6 +367,7 @@
     private ScriptObject   builtinUint32Array;
     private ScriptObject   builtinFloat32Array;
     private ScriptObject   builtinFloat64Array;
+    private ScriptObject   builtinGreeting;
 
     /*
      * ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
@@ -399,6 +403,7 @@
     private PropertyMap    anonymousFunctionMap;
     private PropertyMap    strictFunctionMap;
     private PropertyMap    boundFunctionMap;
+    private PropertyMap    nativeGreetingMap;
 
     // Flag to indicate that a split method issued a return statement
     private int splitState = -1;
@@ -999,6 +1004,10 @@
         return ScriptFunction.getPrototype(builtinFloat64Array);
     }
 
+    ScriptObject getGreetingPrototype() {
+        return ScriptFunction.getPrototype(builtinGreeting);
+    }
+
     // Builtin PropertyMap accessors
     PropertyMap getAccessorPropertyDescriptorMap() {
         return accessorPropertyDescriptorMap;
@@ -1124,6 +1133,10 @@
         return typeErrorThrower;
     }
 
+    PropertyMap getGreetingMap() {
+        return nativeGreetingMap;
+    }
+
     /**
      * Called from compiled script code to test if builtin has been overridden
      *
@@ -1669,6 +1682,8 @@
         this.builtinNumber    = (ScriptFunction)initConstructor("Number");
         this.builtinRegExp    = (ScriptFunction)initConstructor("RegExp");
         this.builtinString    = (ScriptFunction)initConstructor("String");
+        this.builtinGreeting  = (ScriptFunction)initConstructor("Greeting");
+	System.out.println(this.builtinGreeting);
 
         // initialize String.prototype.length to 0
         // add String.prototype.length
@@ -1883,6 +1898,7 @@
         this.uint32Array       = this.builtinUint32Array;
         this.float32Array      = this.builtinFloat32Array;
         this.float64Array      = this.builtinFloat64Array;
+        this.greeting          = this.builtinGreeting;
     }
 
     private void initDebug() {
@@ -1975,6 +1991,7 @@
         this.anonymousFunctionMap = ScriptFunctionImpl.getInitialAnonymousMap().duplicate();
         this.strictFunctionMap = ScriptFunctionImpl.getInitialStrictMap().duplicate();
         this.boundFunctionMap = ScriptFunctionImpl.getInitialBoundMap().duplicate();
+        this.nativeGreetingMap = NativeGreeting.getInitialMap().duplicate();
 
         // java
         if (! env._no_java) {

参考資料

MozillaのRhinoを使ってみた

以前使ってみたNashornは割とJavaのスクリプト言語としてお手軽に使うことを重視しているといった印象を受けたが、Rhinoに関してはいろいろ細かいところに手が届きそうだ。Nashornではそのままではちゃんと実装できそうではなかったホストオブジェクトも実装できるようだ。ホストオブジェクトはブラウザでいう、windowdocumentなどのオブジェクトのことでJavaScript単体では実現できないような機能を提供する。document.titleを変更するとページのタイトルが変更できるがこれはホストオブジェクトがtitleプロパティとページのタイトルを結びつける機能を提供しているのであって、素のJavaScriptのオブジェクト(ネイティブオブジェクト)のプロパティとは違うものだ。

JavaScriptのユニークなのはホストオブジェクトもネイティブオブジェクトのようにプロパティを追加できることだ。ブラウザで以下のようなコードを実行するとちゃんとmypropが変更できている。

document.myprop = "Hi";
alert(document.myprop);

NashornでもRhinoでもJavaのオブジェクトを渡すとホストオブジェクトのようにJavaで実装’された機能をJavaScriptから呼び出すことができる。以下のようなコードで実験してみた。RhinoはJDK 7に付属するものではなく https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino から取得したものを使っている。最終更新日は2013/2/24で大分前に更新が止まっているようだ。JDK 7のものではパッケージ名がsun.org.mozilla.javascriptになっているのに対し、MDNのものはorg.mozilla.javascriptになっている。

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

public class Greeting1 {
	private String hello;
	
	public Greeting1(String hello) {
		this.hello = hello;
	}
	
	public String sayHello(String name) {
		return hello + ", " + name + "!";
	}
	
	public static void main(String[] args) {
		Context cx = Context.enter();
		Scriptable scope = cx.initStandardObjects();
		Greeting1 greeting = new Greeting1("Hello");
		ScriptableObject.putProperty(scope, "greeting", greeting);
		Object result = cx.evaluateString(scope, args[0], "<cmd>", 1, null);
		System.out.println(Context.toString(result));
		Context.exit();
	}
}

以下をコマンドラインから実行すると、JavaScriptからJavaのコードが呼び出せるのがわかる。

CMD> java -cp js.jar;. Greeting1 "greeting.sayHello('Alice')"
Hello, Alice!

ところが、greeting.fooを変更しようとすると例外が発生してしまう。

CMD> java -cp js.jar;. Greeting1 "greeting.foo=123"
Exception in thread "main" org.mozilla.javascript.EvaluatorException: Java class
 "Greeting1" has no public instance field or method named "foo". (<cmd>#1)

Rhinoでプロパティを追加できるホストオブジェクトを作るにはorg.mozilla.javascript.ScriptableObjectを継承したオブジェクトを作る必要がある。

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;

public class Greeting2 extends ScriptableObject {
	private String hello;
	private String secret;
	
	public Greeting2() {
	}

	@JSConstructor
	public Greeting2(String hello, String secret) {
		this.hello = hello;
		this.secret = secret;
	}
	
	@Override
	public String getClassName() {
		return "Greeting";
	}

	@JSFunction
	public String sayHello(String name) {
		return hello + ", " + name + "!";
	}

	public String saySecret(String name) {
		return secret + ", " + name + "!";
	}
	
	public static void main(String[] args) throws Exception {
		Context cx = Context.enter();
		Scriptable scope = cx.initStandardObjects();
		ScriptableObject.defineClass(scope, Greeting2.class);
		Object[] initArgs = { "Hello", "Get away" };
		Scriptable greeting = cx.newObject(scope, "Greeting", initArgs);
		ScriptableObject.putProperty(scope, "greeting", greeting);
		Object result = cx.evaluateString(scope, args[0], "<cmd>", 1, null);
		System.out.println(Context.toString(result));
		Context.exit();
	}
}

以下をコマンドラインから実行すると、JavaScriptからプロパティの変更ができることがわかる。

CMD> java -cp js.jar;. Greeting2 "greeting.sayHello('Bob')"
Hello, Bob!
CMD> java -cp js.jar;. Greeting2 "greeting.foo=123; greeting.foo+456"
579

JavaScriptから見える関数にはアノテーションで指定する。sayHello()には@JSFunctionがついているのでJavaScriptから見えるがsaySecret()がついていないので触ることができない。

CMD> java -cp js.jar;. Greeting2 "greeting.saySecret('Bob')"
Exception in thread "main" org.mozilla.javascript.EcmaError: TypeError: Cannot find function saySecret in object [object Greeting]. (<cmd>#1)

もちろん、getClass()とかもできない。

CMD> java -cp js.jar;. Greeting2 "greeting.getClass()"
Exception in thread "main" org.mozilla.javascript.EcmaError: TypeError: Cannot find function saySecret in object [object Greeting]. (<cmd>#1)
CMD> java -cp js.jar;. Greeting2 "Greeting.class"
undefined

JavaScriptオブジェクトを渡すとStringへ変換されるようだ。

CMD> java -cp js.jar;. Greeting2 "greeting.sayHello({ toString: function () { return 'Bob'; } })"
Hello, Bob!

JavaScriptオブジェクトをそのまま受け取るには引数の型にorg.mozilla.javascript.ScriptableObjectを指定する。

import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.annotations.JSConstructor;
import org.mozilla.javascript.annotations.JSFunction;

public class Greeting3 extends ScriptableObject {
	private String hello;
	private String secret;
	
	@JSConstructor
	public Greeting3() {
	}
	
	@Override
	public String getClassName() {
		return "Greeting";
	}

	@JSFunction
	public String sayObject(Object obj) {
		return obj.getClass().toString();
	}

	@JSFunction
	public String sayScriptableObject(ScriptableObject obj) {
		return obj.getClass().toString();
	}

	public static void main(String[] args) throws Exception {
		Context cx = Context.enter();
		Scriptable scope = cx.initStandardObjects();
		ScriptableObject.defineClass(scope, Greeting3.class);
		Scriptable greeting = cx.newObject(scope, "Greeting");
		ScriptableObject.putProperty(scope, "greeting", greeting);
		Object result = cx.evaluateString(scope, args[0], "<cmd>", 1, null);
		System.out.println(Context.toString(result));
		Context.exit();
	}
}

引数の型がObjectの場合はできるだけJavaの対応するクラスへ変換されるが、org.mozilla.javascript.ScriptableObjectの場合にはRhinoの内部オブジェクトが渡される。

CMD> java -cp js.jar;. Greeting3 "greeting.sayObject(123)"
class java.lang.Integer
CMD> java -cp js.jar;. Greeting3 "greeting.sayObject('Bob')"
class java.lang.String
CMD> java -cp js.jar;. Greeting3 "greeting.sayObject(new Date())"
class org.mozilla.javascript.NativeDate
CMD> java -cp js.jar;. Greeting3 "greeting.sayScriptableObject(123)"
class org.mozilla.javascript.NativeNumber
CMD> java -cp js.jar;. Greeting3 "greeting.sayScriptableObject('Bob')"
class org.mozilla.javascript.NativeString
CMD> java -cp js.jar;. Greeting3 "greeting.sayScriptableObject(new Date())"
class org.mozilla.javascript.NativeDate
CMD> java -cp js.jar;. Greeting3 "greeting.sayScriptableObject({})"
class org.mozilla.javascript.NativeObject

参考資料

JDK8のJavaScript実装Nashornを使ってみた

NashormというのはJava 8で搭載されたJavaScriptの実装で、JavaScriptのコードをインタープリターとして実行できる。JDKにJavaScriptが搭載されたのはJava 8が初めてではなくJava 6の時点ではRhinoがすでに搭載されていたのでこの点では新しくない。RhinoもNashormもAPIはJSR-223規格に従っていて、エンジンの動作に差異があるが、基本、同じように使える。NashormがRhinoに対するアドバンテージは、Java 8の新機能の型無し言語を効率的に実行できるinvokedynamicというVM命令を使っていることだ。Nashormは、ブラウザに搭載されているJavaScriptエンジンと比べるとJava言語との相互運用性を意識していて、JavaScriptからJavaのクラスを継承したり、Javaのクラスを使ったりすることができる。コンテキストも複数持つことができてコンテキストごとにグローバルオブジェクトを区別できる。Nashornはドイツ語、Rhinoは英語で、それぞれ動物のサイのこと。

Nashornはjavax.scriptingパッケージから使える。使うのは簡単で、ScriptEngineManagerからScriptEngineを生成して、eval()メソッドを呼べばいい。

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
...
@Test
public void test1() {
	try {
		ScriptEngineManager manager = new ScriptEngineManager();
		ScriptEngine engine = manager.getEngineByName("nashorn");
		engine.eval("(function (x) { print(x); })('Hello World!')");
	} catch (ScriptException e) {
		e.printStackTrace();
	}
}

JavaのクラスはJavaScriptでECMA-262でいうホストオブジェクトとしてそのまま使うことができる。

public class Later {
	int incr = 60 * 1000;
	public Date later(Date date) {
		return new Date(date.getTime() + incr);
	}
}
...
engine.put("foo", new Later());
Date date = (Date) engine.eval("var now=new java.util.Date();print(now);foo.later(new java.util.Date());");
System.out.println(date.toString());

ホストオブジェクトは通常、プロパティの追加ができる。ウェブブラウザのHTMLBodyElementインターフェイスはfooを実装しないが以下を実行することでfooを書き換えられる。

document.body.foo = "bar";
alert(document.body.foo);

NashornではJavaのオブジェクトに対してプロパティの追加はできない。以下のコードはundefinedと表示する。Nashornを使ってウェブブラウザを実装したくなった場合には注意が必要だ。

engine.eval("var now=new java.util.Date();now.foo='bar';print(now.foo);");

JavaScriptのオブジェクトはJavaからはjdk.nashorn.api.scripting.ScriptObjectMirrorクラスのオブジェクトとして見える。ScriptObjectMirrorjavax.script.ScriptBingingsを実装しているので、そのメソッドを使ってJavaScriptのオブジェクトを操作できる。jdk.nashorn.api.scriptingパッケージはNashornのある程度安定したAPIとして用意されている。

import jdk.nashorn.api.scripting.ScriptObjectMirror;

ScriptObjectMirror jsobj = (ScriptObjectMirror) engine.eval("({foo:'bar'})");
System.out.println(jsobj.get("foo")); // Bingings.get()
System.out.println(jsobj.getMember("foo")); // JSObject.getMember()

JavaのオブジェクトをJavaScriptに渡すと、通常、JavaのメソッドやフィールドがそのままJavaScriptからプロパティとして見えるが、jdk.nashorn.api.scripting.JSObjectを実装したオブジェクトの場合は特別で、JSObjectのメソッド経由でアクセスされるようになる。AbstractJSObjectはJSObjectを実装しているがただの空の実装なので通常のJavaScriptオブジェクトのようなハッシュテーブルの機能を持たせる(extensible)にはScriptObjectMirrorが必要だ。ただしScriptObjectMirrorはファイナルクラスなので継承はできない。ScriptUtils.wrap()ScriptObjectMirror.wrap()でJavaのオブジェクトをラップできるかと思ったがJavaのオブジェクトはそのまま、同じオブジェクトを返してくるようだ。

public class FooObject extends AbstractJSObject {

	@Override
	public Object getMember(String name) {
		if (name.equals("foo"))
			return "bar";
		return super.getMember(name);
	}

	@Override
	public Object getSlot(int index) {
		if (index >= 0 && index < 10)
			return index * 2 + 3;
		return super.getSlot(index);
	}

	@Override
	public boolean hasMember(String name) {
		if (name.equals("foo"))
			return true;
		return super.hasMember(name);
	}

	@Override
	public boolean hasSlot(int slot) {
		if (slot >= 0 && slot < 10)
			return true;
		return super.hasSlot(slot);
	}
}
...
engine.put("x", new FooObject());
engine.eval("print([x.foo, x.bar, x[2], x[3]].join())");

Nashornはjavax.script経由で使うと、Javaとの連携機能がいろいろ有効になるが、そのような機能が必要なければjdk.nashorn.api.scripting.NashornScriptEngineFactoryを直接使う必要がある。

NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
String[] engineArgs = new String[] {
	"-strict",
	"--no-java",
	"--no-syntax-extensions",
	"--global-per-engine"
};
ScriptEngine engine = factory.getScriptEngine(engineArgs);

参考