Home About
javascript, rhino, groovy

Rhino を使って Java から JavaScript を実行する

今更な話題ですが 将来 nashorn (JEP 335: Deprecate the Nashorn JavaScript Engine ) が廃止になるらしい.
nashorn の代わりに Rhino で実行する方法を調査したのでメモします.

実行する javascript から Java の自前オブジェクトを使いたい

MDN web docs Tutorial: Embedding Rhino を読めばだいたいわかります。

Java側でJavaScriptで使うことを想定して用意したオブジェクトは JavaScript host objects とよばれているようです.

ここでは JavaScript から使う Pikachu オブジェクトを Java側で用意する例を考えます. pikachu.say すると pika と発言するようにしましょう.

Pikachu クラス

Java 側( コードは Groovy ) で用意します.

public class Pikachu extends ScriptableObject {
    @Override
    String getClassName() { return 'Pikachu' }

    Pikachu(){}

    @JSFunction
    String sayHello(int i){ return 'pika ' * i }
}

これを使うコード:

def cx = Context.enter()
def scope = cx.initStandardObjects()
ScriptableObject.defineClass(scope, Pikachu.class)

で用意しておき:

def result = cx.evaluateString(scope, 'new Pikachu().sayHello(1);', "<cmd>", 1, null)

で JavaScript new Pickachu().sayHello(1); を評価します. result には pika が入ります.

まとめ

コード全体では以下のようになります.

pikachu.groovy

@Grab(group='org.mozilla', module='rhino', version='1.7.12')
import org.mozilla.javascript.Context
import org.mozilla.javascript.ScriptableObject
import org.mozilla.javascript.annotations.JSFunction

public class Pikachu extends ScriptableObject {
    @Override
    String getClassName() { return 'Pikachu' }

    Pikachu(){}

    @JSFunction
    String sayHello(int i){ return 'pika ' * i }
}

def cx = Context.enter()

def scope = cx.initStandardObjects()
ScriptableObject.defineClass(scope, Pikachu.class)

def script0 = 'new Pikachu().sayHello(3);'
def result0 = cx.evaluateString(scope, script0, "<cmd>", 1, null)
println result0

Context.exit()

groovy pikachu して実行:

$ groovy pikachu
pika pika pika

になります.

あらかじめ、Pikachu クラスのインスタンス pikachu を グローバルスコープに存在させておくようにするには、Java側で以下の記述を追加しておけばOKです.

def pikachu = cx.newObject(scope, "Pikachu")
scope.put("pikachu", scope, pikachu)

これなら javascript 側は:

pikachu.sayHello(3);

だけになります。

see also : Rhino で実行する javascript をコンパイルしてから使う