改訂:JSR-223を使ってはいけない

一番重要な欠陥を説明するのを忘れていたのでこれを機に書き直し。

The ScriptEngineManager provides a method to return a list of all these factories as well as utility methods which look up factories on the basis of language name, file extension and mime type.

とありますが実際にはlanguage name──言語名に基づいてファクトリを検索するメソッドは持っていません。実際に持っているgetEngineByNameメソッドはScriptEngineFactory#getNamesの返す値に基いてファクトリを検索します。しかも、shortName引数の説明にはThe short name of the ScriptEngine implementation.と書かれていますがScriptEngineFactory#getNamesメソッドはエンジンの実装名を返すメソッドではありません。このメソッドはエンジンを識別するための短い名前を返すメソッドです。このメソッドは詳細な名前を返すメソッド群と対をなす物でそのなかにgetEngineNameというメソッドがあり、これが実装名を返す正しいメソッドです。

そして、getEngineByNameメソッドはその名が示す通りエンジンの実装名を利用してエンジン実装を取得するメソッドでもあります。しかし、ここに問題がああります。JSR-223の利用者はSPIの実装者ではないため実装名は知り得ません。

実際にgetEngineByNameメソッドを利用してエンジン実装を取得するには実装名がわからないのでAPIの仕様通りにやると以下のように行わなければいけません。

ScriptEngineFactory spi = ...;

String name = spi.getParameter(ScriptEngine.NAME);
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName(name);

もう気付いたと思いますが特定のScriptEngineFactory実装を取得する術はJSR-223の利用者は持ち合わせてはいません。つまりこのメソッドはSPIの実装者か実装名を知っている者のみが利用します。例えばScriptEngineManager#registerEngineNameを利用してエンジンをオーバーライドしなければならない場合です。実際のシナリオに当てはめるとjsのエンジンにnashornが登録されていることを知っているがrhinoを使いたい場合、rhino用のJSR-223 SPIを用意してregisterEngineNameメソッドを利用してnashornをrhinoに置き換えるときです。

というわけで本当に必要な物が一番初めの引用に書かれているように「言語名に基づいてファクトリを検索するメソッド」であることはここまでの説明で明白になりました。しかし、やはり説明したように「言語名に基づいてファクトリを検索するメソッド」は存在しません。さらに getNamesメソッドが返す文字列リストと自分の期待する文字列がマッチすることは期待できないのでScriptEngineManager#getEngineByExtensionかScriptEngineManager#getEngineByMimeTypeを使うかgetEngineFactories()して直接それぞれのScriptEngineFactoryのメソッドを利用するのが安全でしょう。

JSR-223の利用者は実装の詳細は知り得ません。知らずしてスクリプトを呼び出す事こそがこのフレームワークの目的です──and provides a framework for their use in Java applications.(それらを Java アプリケーションで使用するためのフレームワークを提供します。)──This API is intended for use by application programmers who wish to execute programs written in scripting languages in their Java applications.(この API は、スクリプト言語で記述されたプログラムを Java アプリケーション内で実行するアプリケーションプログラマを対象としています。)──とあるように。

つまり、JSR-223の利用者はそれがnashornであるかrhinoであるか、はたまたJRubyやJythonであるかは重要ではなくesやjsやrubyやPythonが利用したいのです(利用しなければならないのです)。実装に依存するわけにはいけません、またはしてはいけません。なぜならスクリプトエンジンの開発者ではないので詳細は知らないし、java8のnashornの例のようにいつSPI実装が変更されるかも知る由もありません。なのでmanager.getEngineByName("Rhino")などとやってしまうと簡単に動かなくなってしまう可能性があるのです。


ここまでで、複数の登場人物が出てきたのがわかると思います。JSR-223の利用者であるアプリケーションプログラマ、スクリプトエンジンの実装者、そして忘れてはならないのがJSR-223 SPIの実装者です。スクリプトエンジンの実装者とJSR-223 SPIの実装者が同一人物だと思うこともあるかもしれませんがそれは間違いです。当然、スクリプトエンジンはJSR-223がなくても利用できるのでJSR-223には依存していません。しかし、JSR-223の利用者は違います。スクリプトエンジンとJSR-223 SPI実装が揃っていなければJSR-223を利用できません。それぞれ目的とやるべきことが違うということに注目してみましょう。

JSR-223の利用者はエンドユーザーにアプリケーション内でスクリプトを実行させる手段を提供します。SPIの実装者はJSR-223の利用者がスクリプトエンジンを実装を気にせず利用できる手段を提供します。しかし、スクリプトエンジンの実装者はJSR-223に限りません。すべてのjavaプログラマにスクリプトエンジンを提供します。

さらにjavadocではThe scripting language programs are usually provided by the end-users of the applications.(通常、スクリプト言語のプログラムは、アプリケーションのエンドユーザーによって提供されます。 )と続くように、JSR-223の利用者はエンドユーザーが利用するホスト・オブジェクトを提供しなくてはならないかもしれませんが、これは当然JSR-223の利用者であるアプリケーションプログラマが行います。なぜなら、ホスト・オブジェクトはドメインに特化しているので何が必要かはアプリケーションプログラマにしか解からないからです。SPI、スクリプトエンジン両実装者には出来ません。しかし、JSR-223の利用者にもそれは不可能です。

JSR-223はアプリケーションプログラマがスクリプトエンジンに対してホストオブジェクトを登録する術を提供していません。Bindingsがあるじゃないかと思ったかと思いますが話はそんなに単純ではありません。BindingsはStringキーとObject値のマップとしか定義されていないのでそれ以外は不明です。もし、javaの値をマッピングするとどうなりますか? それは正しくスクリプトで扱えますか? 詳しい人はJava Plug-inへ移行した新しいLiveConnectの出番だと思うでしょう。しかし、JSR-223がそれを利用するとは仕様に書かれていません。新しいLiveConnectはjava6の途中で登場したので無理もありません。よってJSR-223仕様で決められたjavaとスクリプトをシームレスに行き来する値を用意する術はありません。それにスクリプトエンジンが扱える値をスクリプトエンジン実装にアクセスせずにどうやって実装しますか? 結局、実装詳細を知りエンジン実装にアクセスする必要があります。

さて、実装詳細を知り、スクリプトエンジンにアクセスし、ホスト・オブジェクトをアプリケーションプログラマが実装するところまではなんとかこぎ着けたとします。ではそれをスクリプトエンジンにどうやって登録しますか? アプリケーションプログラマはJSR-223を仲介してスクリプトエンジンを利用しています。まだ、Bindingsでは不十分という問題は解決していません。実際この問題に対する明確な答えをJSR-223は用意していません。少なくともスクリプトがjavaの値を扱えるようにするにはjavaの値を扱えるようにするおまじないでラップする必要があります。共通のおまじないがありますか? そのおまじないは実際に使われていますか? それらは仕様において保証されていますか? 全ての答えはNOです。

良い知らせと悪い知らせがあります。jsには限らない新しいLiveConnectというおまじないは用意されています。しかし、実際に使われているのはjsでのみです。新しいLiveConnectの仕様上はjsに限らないJVM言語全てで利用可能ですが実際にはmozilla固有の実装であったLiveConnectをjava Plug-in側で再実装しmozillaブラウザ以外でも利用できるようにしただけです。しかも、netscapeのLiveConnectとjavaのLiveConnectは別の仕様です。詳しくは仕様書を読んでももらうとしてこのことは一旦忘れてください。解決しなければならないことはまだ解決していません。

じつはBindingsではダメな理由は2つあります。ホストオブジェクトを登録することと、javaの値をスクリプトで扱えるようにする事は厳密には違います。ホスト・オブジェクトをホスト言語でスクリプトエンジンのAPIを使いスクリプトエンジンの作法で実装されたならその値はスクリプトの値です。スクリプトから正しく扱えますし、ホスト・オブジェクトを実装するにはスクリプトエンジンにアクセスしなければならないという事実を知ったのでホスト言語からもホスト言語の型を持った値として扱えます。

例えばjsのObjectはRhinoではScriptableの実装かScriptableObjectのサブクラスです。そこまで判っているのでjavaからはScriptableとしてアクセスできます。これは実装の話です。実装は値がjavaとjsを行き来する時にそれぞれの型へと変換する作業が必要です。jsのObjectはScriptableなので既に解決済みです。ではjsが扱えるようになったjavaのプリミティブ値やjavaのオブジェクトはどうでしょう? プリミティブはラッパー・クラスへ、StringはjsのプリミティブなStringはそのままjavaのStringなので気にする必要はありません。それ以外はLiveConnectによってJavaObjectというオブジェクトにラップされます。このオブジェクトは自分がjavaへ返されるときはjsの値(jsの配列やラップされたjavaのClassクラスなど)を変換またはアンラップします。変換規則はLiveConnectによって決まっていてこのオブジェクトはその変換処理を行うランタイムでもあります。

と──このようなことをスクリプトエンジンの中では行っているのですがJSR-223はそれを知りません。なのでこれらを行うAPIをスクリプトエンジンが公開していたとしてもJSR-223からは呼び出せません。Bindingsにputしたとき、これらの処理を行っているとは仕様に明記されていません。実際にはやっていますが(Bindings#put/getする時とは限りません)JSR-223利用者がどのような値をputするかが厳密にはわからないのでSPI実装者には正しく処理できるかもわかりません。JSR-223とスクリプトエンジンを橋渡しする時に通過する値ならそれはSPI実装者が担当します。ランタイム内部を行き来する値ならばスクリプトエンジン実装者の仕事です。

このようにJSR-223の仕様は単純ですがSPIの実装は複雑でその挙動が常に正しいかはSPI実装者には不明です。むしろ必要な物が足りないほど単純なので実装が複雑になるのです。もし、JSR-223がスクリプトエンジンのおまじないのためのラップ/アンラップAPIを呼び出せるならもう少し話は簡単になります。おまじないが必要かどうかはアプリケーションプログラマのみが正しく知っているのでスクリプトエンジンに値を公開する時には必要ならばラップし、スクリプトエンジンから受け取るときは値をアンラップしという処理をアプリケーションプログラマ側で行い、SPI側はそれがどんな値であるかにかかわらずスクリプトエンジンの環境に値を関連付ける必要があれば関連付け、アプリケーションに値を渡す必要があるなら渡すだけに専念できます。

というわけでJSR-223はスクリプトエンジンのおまじないのためのラップ/アンラップAPIを呼び出せる必要があるし、ホスト・オブジェクトを実装するにはアプリケーションプログラマがスクリプトエンジンにアクセスする必要があります。JSR-223を利用していてはそれらを満たせないわけです。かと言ってJSR-223がスクリプトエンジンのおまじないのためのラップ/アンラップAPIを呼び出せるだけでは不十分で、結局アプリケーションプログラマがドメイン特化したホスト・オブジェクトを用意するにはスクリプトエンジン実装にアクセスしているのでJSR-223を仲介する必要性がないのです。それどころかJSR-223では目的が果たせないのです。

とてもややこしい話かとは思いますが重要なのはJSR-223 SPIとスクリプトエンジンは別物でそれらの開発者も違うということです。そして何が自分に必要かはアプリケーションプログラマにしかわからないということです。

たとえばここのように一見会話が成立していないように見えますがスクリプトエンジンの開発者としての立場としての発言にはなにも落ち度がありません。JSR-223とスクリプトエンジンの間には関係性がありません。

rhinoをforkする身としてJSR-223のサポートを二度ほど考えましたが一度目はJSR-223は適切ではないと考えサポートしませんでした。二度目はこの投稿を見た時に再考したのですが結局、JSR-223を使っても目的を果たせないと結論付けました。よってRhino fork Projectで直接JSR-223のサポートが行われることはありません。orignal rhinoにしても昔からLiveConnectのJSObjectの方は未サポートだったので似たような状況です。また、スクリプトエンジンが直接JSR-223をサポートするかは個々に議論の余地があるでしょう。

ほかにも、JSR-223には問題があります。スクリプトからJavaへの通信を制御したい場合はどうしますか? また、javaのようにパーミッションを与えるポリシーベースでスクリプトを実行するにはどうしますか? これらセキュリティに関わることもJSR-223では出来ません。JSR-223はスクリプトエンジンを取得して実行することしか出来ません。その取得にも問題があり、実行するにもjavaの値をスクリプトにどうやって公開するかという問題がある事をこれまでに見てきました。

結論はこうです……「JSR-223 Scripting APIを使ってはならない」




──ただし、ただ呼び出すだけ、ドメインに特化しない、セキュリティを考慮する必要もない場合はその限りではありません。

さて、ただ呼び出すだけ、ドメインに特化しない、セキュリティを考慮する必要もない場合はどれだけあるのか? 本当にあるのか?


──あります!

例えばちょっとしたバッチ処理をスクリプトで書くとか。自分が使えるスクリプト言語の処理系ぐらい既に用意しているだろうって? その通りです。
例えばプロトタイプをささっと書いてみるとか……とjavadocにも書いてあるし。実際その程度しか使い道がないしそのような使い道のために導入されたものです。しかも、試験的なものです。Java スクリプトプログラマーズガイドを読んでみてください。ただし、アプリケーション拡張目的で使うには欠陥品です。それは無謀で間違った行為です。

Consoleオブジェクト仮実装

とりあえず端末用に仮実装して設計を考える。



rhino forkにconsole object

Consoleオブジェクトを実装しようかと考えているがいくつか問題と考えなければならないことがある。

まず動機から、将来的にデバッガを書き直すのでデバッグ用APIの整理をそろそろ始めたい。デバッガ使用中はおそらくConsoleオブジェクトを使用したいだろうと考えた。

しかし、この場合、Consoleオブジェクトの出力はデバッガの出力にパイプされるだろうがデバッガ使用中以外にもConsoleオブジェクトを使用したいことだってある。その場合、その出力は標準入出力かその他の出力になるのでConsoleオブジェクトの出力先について扱える必要がある。そうなると、ただの外部のホストオブジェクトとして実装するよりエンジン側でのサポートがあった方が良い。

ということでorg.mozilla.javascript.debugパッケージあたりにインターフェースを定義することにした──。

が……しかし、Consoleオブジェクトはそもそも標準化されていない。インターフェースと言ってもどんなメソッドを持っているか、そのメソッドの振る舞いなど結局のところブラウザの実装詳細にあたるので各種細かい仕様を勝手には決められない。これがまず問題点その一。これに関しては必要最低限のメソッドとその仕様を定義することにする。

つぎにconsole.logメソッドなどは書式付きだがそのフラグもブラウザの実装詳細が似通っているだけにすぎない。このフォーマッタは独自に実装するのかjavaのAPIを使うのかという問題がある。現実的にはjavaのAPIを使うわけだが例えばこのフォーマッタはSフラグを持っている。ではFormattableインターフェースを実装する必要があるのか──と、この二点が問題点その二だ。

Formattableインターフェースを実装しなければおそらく内部向けに再定義されているであろうtoStringメソッドを呼んでしまうので書式付きメッセージとして相応しくない。

最近のoriginalとforkの比較

久しぶりにoriginal rhinoとrhino forkのパフォーマンス比較を行ってみた。今回から比較対象をRhino1.7R5pre3とした。
最近のR5は多少最適化されているので以前のrhino forkとoriginal rhinoのパフォーマンスが1.3倍程度だったのが1.12倍程度に縮まっている。

ただrhino forkに最近入れた最適化は「そっちの方が早いのだがなぜ速くなるのか解らないし、パフォーマンスが安定しない。ただ、original rhinoよりは速い」という謎の変更を適応している。sunspiderはほとんど影響ないがOctaneがある程度受けている様でそれ以前が平均スコア930付近だったのが適応後は940~960にバラけてしまう。メモリ消費量も状況によって少し変動してしまうようになった。特にRichards,RegExp,SplayLatencyが影響を受ける。

そして、その件のコードは以下のもの──

original rhino


    public static Number wrapNumber(double x)
    {
        if (x != x) {
            return ScriptRuntime.NaNobj;
        }
        return new Double(x);
    }

rhino fork


    public static Double wrapNumber(double x) {
        //TODO optimization possibilities; auto-boxing vs hardwired Double constructor vs Double.valueOf().
        return x;
        // slower code
//        if (x != x) {
//            return ScriptRuntime.NAN_CLASS;
//        }
//        return new Double(x);
    }

両方共に以前はキャッシュした値を返そうと試みていたので条件分岐で遅くなっていた。両方そのことに気づいたわけだがやり方が違ったわけだ。
ScriptRuntime.NaNobjを返している部分はMS JVM互換コードなのであってもなくても今回の件に関係なく有るぶん遅いし無いぶん速い。rhino forkでは古すぎる互換コードは削除したのでそれで制約を受けることはないのでこの文は削除して問題ない。コメントにも書いてあるが違いはauto-boxingかnewするかの違いだ。しかし、auto-boxingの方が速い。どちらも最終的にはnewするはずなので同じオペコードを実行するはずだが違いが出ている。しかも、なぜか速くなるけど不安定にもなる。念のため言っておくとオブジェクトのallocateとコンストラクタの呼び出しそのものが遅いわけじゃない。そんなものの影響を受けていたのは大昔の話だ。今のCPUなら条件分岐するほうが遅いようなのでメモリを空ける目的以外でキャッシュする必要はない。以前のコードがキャッシュしていたのは確かに昔は遅かったからだが今では両方共キャッシュしていない。そんなわけで話を戻すが、なぜauto-boxingが速いのかわからない。auto-boxingもhardwired Double constructorもDouble.valueOf()も結局はallocateしたあとコンストラクタを呼び出すのでおなじになると思うのだが……ちなみに戻り値がDoubleになっているのはNumberである理由がとくに無かったから。

あとついでに言っておくとnashorn 1.8.0_05-b13は前がrhino forkとのOctaneの平均スコアの差が2倍程度だったのが2.5倍程度までスループットが増しているがメモリ消費量とsunspiderは相変わらず。そんなこんなでoriginal rhinoとrhino forkのベンチマーク結果を見ていく。

パス通っているのがjava8だったのでoriginal rhinoをjava8で実行してました(forkは関係ない)。やっぱりjava8はパフォーマンス悪いのでjava7で計測しなおしました。

original rhino


============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total: 918.1ms +- 1.7%
--------------------------------------------
3d: 89.1ms +- 7.9%
cube: 33.4ms +- 16.6%
morph: 28.9ms +- 19.6%
raytrace: 26.9ms +- 26.8%
access: 95.9ms +- 5.6%
binary-trees: 8.9ms +- 86.7%
fannkuch: 44.6ms +- 12.5%
nbody: 31.1ms +- 1.1%
nsieve: 11.3ms +- 63.3%
bitops: 138.1ms +- 3.9%
3bit-bits-in-byte: 13.3ms +- 41.0%
bits-in-byte: 33.6ms +- 16.4%
bitwise-and: 62.1ms +- 0.6%
nsieve-bits: 29.1ms +- 18.5%
controlflow: 6.6ms +- 115.6%
recursive: 6.6ms +- 115.6%
crypto: 35.7ms +- 20.0%
aes: 15.7ms +- 2.9%
md5: 17.7ms +- 30.7%
sha1: 2.3ms +- 245.0%
date: 75.7ms +- 7.4%
format-tofte: 29.0ms +- 19.8%
format-xparb: 46.7ms +- 1.0%
math: 113.9ms +- 6.2%
cordic: 40.3ms +- 19.3%
partial-sums: 53.4ms +- 14.5%
spectral-norm: 20.1ms +- 35.8%
regexp: 158.3ms +- 3.5%
dna: 158.3ms +- 3.5%
string: 204.9ms +- 4.8%
base64: 33.3ms +- 16.8%
fasta: 33.4ms +- 16.6%
tagcloud: 60.1ms +- 9.0%
unpack-code: 44.7ms +- 12.5%
validate-input: 33.3ms +- 16.8%

--------------------------------------------------------------------------------

Run 1 out of 10:
Richards: 867
DeltaBlue: 796
Crypto: 790
RayTrace: 1057
EarleyBoyer: 1293
RegExp: 266
Splay: 1114
SplayLatency: 592
NavierStokes: 1136
----
Score (version 9): 809

Run 2 out of 10:
Richards: 853
DeltaBlue: 815
Crypto: 798
RayTrace: 1108
EarleyBoyer: 1300
RegExp: 263
Splay: 1085
SplayLatency: 728
NavierStokes: 1162
----
Score (version 9): 833

Run 3 out of 10:
Richards: 849
DeltaBlue: 815
Crypto: 796
RayTrace: 1108
EarleyBoyer: 1302
RegExp: 265
Splay: 1214
SplayLatency: 730
NavierStokes: 1171
----
Score (version 9): 844

Run 4 out of 10:
Richards: 849
DeltaBlue: 815
Crypto: 796
RayTrace: 1116
EarleyBoyer: 1293
RegExp: 260
Splay: 1308
SplayLatency: 770
NavierStokes: 1145
----
Score (version 9): 853

Run 5 out of 10:
Richards: 832
DeltaBlue: 815
Crypto: 792
RayTrace: 1104
EarleyBoyer: 1291
RegExp: 263
Splay: 1350
SplayLatency: 792
NavierStokes: 1172
----
Score (version 9): 858

Run 6 out of 10:
Richards: 846
DeltaBlue: 822
Crypto: 778
RayTrace: 1095
EarleyBoyer: 1276
RegExp: 260
Splay: 1872
SplayLatency: 994
NavierStokes: 1144
----
Score (version 9): 908

Run 7 out of 10:
Richards: 836
DeltaBlue: 809
Crypto: 792
RayTrace: 1102
EarleyBoyer: 1309
RegExp: 263
Splay: 1925
SplayLatency: 977
NavierStokes: 1259
----
Score (version 9): 922

Run 8 out of 10:
Richards: 839
DeltaBlue: 822
Crypto: 786
RayTrace: 1095
EarleyBoyer: 1293
RegExp: 266
Splay: 1952
SplayLatency: 964
NavierStokes: 1234
----
Score (version 9): 920

Run 9 out of 10:
Richards: 849
DeltaBlue: 822
Crypto: 804
RayTrace: 1116
EarleyBoyer: 1323
RegExp: 267
Splay: 1954
SplayLatency: 1008
NavierStokes: 1216
----
Score (version 9): 932

Run 10 out of 10:
Richards: 856
DeltaBlue: 835
Crypto: 812
RayTrace: 1116
EarleyBoyer: 1328
RegExp: 268
Splay: 1954
SplayLatency: 1049
NavierStokes: 1189
----
Score (version 9): 938

--------------------------------------------------------------------------------

rhino fork


============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total: 834.1ms +- 2.0%
--------------------------------------------
3d: 82.4ms +- 8.5%
cube: 33.3ms +- 15.6%
morph: 22.3ms +- 33.9%
raytrace: 26.9ms +- 25.6%
access: 84.7ms +- 9.2%
binary-trees: 11.3ms +- 63.3%
fannkuch: 35.7ms +- 20.0%
nbody: 28.9ms +- 18.2%
nsieve: 8.9ms +- 86.7%
bitops: 109.4ms +- 0.5%
3bit-bits-in-byte: 13.6ms +- 40.9%
bits-in-byte: 31.0ms +- 0.0%
bitwise-and: 47.0ms +- 0.0%
nsieve-bits: 17.9ms +- 30.2%
controlflow: 6.9ms +- 111.5%
recursive: 6.9ms +- 111.5%
crypto: 33.1ms +- 15.8%
aes: 15.4ms +- 3.2%
md5: 13.4ms +- 41.0%
sha1: 4.3ms +- 158.1%
date: 71.4ms +- 15.7%
format-tofte: 42.6ms +- 16.5%
format-xparb: 28.9ms +- 18.2%
math: 93.7ms +- 0.5%
cordic: 24.6ms +- 30.2%
partial-sums: 42.3ms +- 16.9%
spectral-norm: 26.9ms +- 28.0%
regexp: 144.7ms +- 4.9%
dna: 144.7ms +- 4.9%
string: 207.7ms +- 8.5%
base64: 36.0ms +- 19.4%
fasta: 35.9ms +- 19.7%
tagcloud: 60.0ms +- 25.3%
unpack-code: 44.7ms +- 12.5%
validate-input: 31.1ms +- 1.1%

--------------------------------------------------------------------------------

Run 1 out of 10:
Richards: 877
DeltaBlue: 913
Crypto: 1125
RayTrace: 1115
EarleyBoyer: 1337
RegExp: 275
Splay: 901
SplayLatency: 452
NavierStokes: 1268
----
Score (version 9): 832

Run 2 out of 10:
Richards: 891
DeltaBlue: 965
Crypto: 1162
RayTrace: 1158
EarleyBoyer: 1343
RegExp: 277
Splay: 1039
SplayLatency: 545
NavierStokes: 1307
----
Score (version 9): 881

Run 3 out of 10:
Richards: 888
DeltaBlue: 965
Crypto: 1165
RayTrace: 1159
EarleyBoyer: 1348
RegExp: 276
Splay: 1073
SplayLatency: 615
NavierStokes: 1297
----
Score (version 9): 895

Run 4 out of 10:
Richards: 870
DeltaBlue: 965
Crypto: 1162
RayTrace: 1168
EarleyBoyer: 1351
RegExp: 277
Splay: 1161
SplayLatency: 633
NavierStokes: 1307
----
Score (version 9): 906

Run 5 out of 10:
Richards: 891
DeltaBlue: 972
Crypto: 1165
RayTrace: 1168
EarleyBoyer: 1353
RegExp: 277
Splay: 1870
SplayLatency: 821
NavierStokes: 1307
----
Score (version 9): 987

Run 6 out of 10:
Richards: 891
DeltaBlue: 965
Crypto: 1165
RayTrace: 1159
EarleyBoyer: 1353
RegExp: 279
Splay: 1872
SplayLatency: 820
NavierStokes: 1317
----
Score (version 9): 987

Run 7 out of 10:
Richards: 895
DeltaBlue: 978
Crypto: 1165
RayTrace: 1168
EarleyBoyer: 1358
RegExp: 278
Splay: 1870
SplayLatency: 866
NavierStokes: 1317
----
Score (version 9): 995

Run 8 out of 10:
Richards: 895
DeltaBlue: 972
Crypto: 1162
RayTrace: 1168
EarleyBoyer: 1358
RegExp: 276
Splay: 1864
SplayLatency: 873
NavierStokes: 1297
----
Score (version 9): 993

Run 9 out of 10:
Richards: 895
DeltaBlue: 972
Crypto: 1159
RayTrace: 1159
EarleyBoyer: 1361
RegExp: 277
Splay: 2218
SplayLatency: 867
NavierStokes: 1329
----
Score (version 9): 1013

Run 10 out of 10:
Richards: 895
DeltaBlue: 978
Crypto: 1168
RayTrace: 1158
EarleyBoyer: 1362
RegExp: 278
Splay: 1872
SplayLatency: 808
NavierStokes: 1307
----
Score (version 9): 987

どっちの結果もrhino forkの方が数値が100程良い。ただし、面倒なのでここには出してないが安定するまでのCPU負荷とメモリ消費量ではoriginal rhinoの方が優れている。RegExpに関してはoriginalの方が良いようにみえるが前述のように影響を受けやすいところで280前後から290前後を迷走するので平均的にはおそらくそんなに違いはないはず。

es4はじめました

どこかにdraft4落ちてないかな。draft2しか見当たらない。
それ以上にJVMが力不足な予感。強い静的型付け+クラス+ADT専用仮想マシンにes4の最適化は無理だと思う。StrongTalkVMにもどして!

ここでなにやらnashornに触れていたのでちょっとお話を──。

http://stormcat.hatenablog.com/entry/2014/05/22/142028

Octane2でのRhinoとNashornの比較──あ……ググっても私のブログしかhitしないんですがどこでしょうか。私、気になります!

Scripting

  • Here document
  • Back quote

scriptingってことはREPLに実装されたアレか。シェルスクリプト風コメントもあるよ。

JavaFXもNashornで書ける──rhinoも書けるし他のスクリプト言語でも書けるからそれらより楽に書けるよってアピールしたいんだと思いますよ。oracleはさ。実装読む限りはさ。

java.util.ArrayListとjava.util.Arraylistでの参照(例) Rhinoでの記法だと呼び出されて初めて例外がわかるためエラーがわかりにくかったが、Java.typeでの参照の場合宣言箇所での例外を検知できるようになった

rhinoの実装がどうなってるか知りたいだって?

    @Override
    public boolean has(String id, Scriptable start) {
        return true;
    }

    @Override
    public boolean has(int index, Scriptable start) {
        return false;
    }

    @Override
    public void put(String id, Scriptable start, Object value) {
        // Can't add properties to Java packages.  Sorry.
    }

    @Override
    public void put(int index, Scriptable start, Object value) {
        throw Context.reportRuntimeError0("msg.pkg.int");
    }

    @Override
    public Object get(String id, Scriptable start) {
        return getPkgProperty(id, start, true);
    }

    @Override
    public Object get(int index, Scriptable start) {
        return NOT_FOUND;
    }

Nice boat.


Rhinoではgetter/setterでのアクセスだったが、プロパティに直接アクセスできる──これはindy使って直接javaのフィールドにアクセスしてるって話じゃないのかな? 実装はそうなってるし、そうすればインライン展開できて速いし、java bean propertiesをjsのpropertiesとしてアクセスできるのは処理系固有の機能ではなくてLC3の仕様だからrhinoとnashorn以外でも出来るし「java bean propertiesにアクセスできるよ」なんて言い方してもoracleにはなんのメリットもないはずだけど……。
むしろ「java bean propertiesにアクセスできるよ」なんて言い方したらソースコード読んだらすぐに分かる「nashornなんてまともにLiveConnect実装してないじゃん」って突っ込まれて簡単にボロが出ちゃうし「そのせいでrhinoと置き換えたくせに互換性無いじゃん」って言われたら終わりだと思うんだけどなぁ。LiveConnectじゃなくてJavaオブジェクトに移行した技術的な話とか経緯は話さなかったんだろうか。そこらへんは元々rhinoにindy対応入れるはずだったのにコミッタみんなnashornに移っちゃった理由と関係するからnashornコミュニティには重要な話のはずなのに。


Lambda, SAM, Script関数の関係──これは良い壷だ、ぜひキシリア様に届けてくれよ。nashornで一番いい変更点だと思う。rhino forkにも実装したいんだけどrhinoはjava8以前で動いてるから対応できなくて残念。rhino forkのjava8対応はスキップしてjava9対応に入ろうと考えてるのでそのうち実装するんだけど他にやることが多いいのでしばらく出来ない。

ScriptContextはBindingに紐付いた複数のスコープをサポート──DynamicScopeですね。わかります! rhinoにもあります。元祖はspidermonkeyだったかな? ちょっと忘れたけど珍しい機能ではないです。rhinoはトップレベルスコープが別のスコープをシェアできるっていう実装だったけどnashornはどうなってるんだろう。それと、monkey-patchにintrinsic objectsが汚染される問題はどうしてるんだろう。

スコープを区切るためにJavaImporterをwithとともに利用──rhinoでLiveConnect利用するときの必須テクですね。なぜこれが必要かって話このブログでしたっけかな? まあいいや、良い機会なので説明します。


本質的にjavascriptというよりecma-262はGCフレンドリーではありません。GCと直接対話する手段もありませんし、var/letはDontDeleteなので削除できません。さらにecma-262ではundefinedと存在しない変数を区別できないのでスクリプトのユーザーから見れば変数がいつ削除されるかは実装依存です。ぶっちゃっけ実装もいつ削除していいか判らないんですが……トップレベルスコープで定義された変数はプログラムが終了するまで残り続けます。



var System = importClass(java.lang.System);

とかするとトップレベルを汚染するのでJavaImporterとwithを利用します。let文でもいいんですがインスタンス名を省略できるのでwithが使われます。

nashornのJavaImporterのインスタンスプロパティはDontDeleteではありませんがrhinoはDontDeleteだったりします。というかrhinoのLiveConnectの一部の実装詳細がいい加減なのでrhino forkではrewriteしています。今は動きませんが。

ツイート禁止な話数点──なんだろう。es6対応するよって話とかかな。anba氏もnashornに居るしね。