調査中なので推測や誤解があるものとして読んでください。
Rhinoで 1=1
を評価すると ParserException
が発生する
$ java -jar buildGradle/libs/rhino-1.7.14-SNAPSHOT.jar -version 200
Rhino 1.7.14-SNAPSHOT 2021 04 04
js> 1 = 1
js: line 1: Invalid assignment left-hand side.
js:
js: ^
Exception in thread "main" org.mozilla.javascript.Parser$ParserException
at org.mozilla.javascript.Parser.reportError(Parser.java:330)
at org.mozilla.javascript.Parser.reportError(Parser.java:312)
at org.mozilla.javascript.Parser.reportError(Parser.java:307)
at org.mozilla.javascript.IRFactory.createAssignment(IRFactory.java:2275)
at org.mozilla.javascript.IRFactory.transformAssignment(IRFactory.java:442)
at org.mozilla.javascript.IRFactory.transform(IRFactory.java:215)
at org.mozilla.javascript.IRFactory.transformExprStmt(IRFactory.java:547)
at org.mozilla.javascript.IRFactory.transform(IRFactory.java:212)
at org.mozilla.javascript.IRFactory.transformScript(IRFactory.java:1077)
at org.mozilla.javascript.IRFactory.transform(IRFactory.java:194)
at org.mozilla.javascript.IRFactory.transformTree(IRFactory.java:119)
at org.mozilla.javascript.Context.parse(Context.java:2606)
at org.mozilla.javascript.Context.compileImpl(Context.java:2540)
at org.mozilla.javascript.Context.compileString(Context.java:1544)
at org.mozilla.javascript.Context.compileString(Context.java:1533)
at org.mozilla.javascript.tools.shell.Main.processSource(Main.java:495)
at org.mozilla.javascript.tools.shell.Main.processFiles(Main.java:181)
at org.mozilla.javascript.tools.shell.Main$IProxy.run(Main.java:101)
at org.mozilla.javascript.Context.call(Context.java:586)
at org.mozilla.javascript.ContextFactory.call(ContextFactory.java:525)
at org.mozilla.javascript.tools.shell.Main.exec(Main.java:163)
at org.mozilla.javascript.tools.shell.Main.main(Main.java:138)
通常Rhinoで発生したJavaScriptエラーはErrorReporter
がハンドリングしてEvaluatorException
を投げる
EvaluatorException
は適切にハンドリングされるので、たとえばREPLが止まったりしない
$ java -jar buildGradle/libs/rhino-1.7.14-SNAPSHOT.jar -version 200
Rhino 1.7.14-SNAPSHOT 2021 04 04
js> #
js: "<stdin>", line 2: illegal character: #
js: #
js: ^
js: "<stdin>", line 2: Compilation produced 1 syntax errors.
js>
Parser$ParserException
はprivateクラスなため、これがRhinoの外に貫通するのは困る
https://github.com/mozilla/rhino/blob/af35d90e2022e82ca59bd58cef5b2f2824cb3a7c/src/org/mozilla/javascript/Parser.java#L162
IRFactory
が構文エラーを見つけてreportError
するとこうなる
Parser
内でreportError
した場合は適切にハンドリングされてEvaluatorException
へと変換される
なお、バージョン1.7R5でも再現しているため、かなり昔からの問題に見える
まずRhinoに渡したJavaScriptのコードはParser
で構文解析されてASTに変換される
次にASTがIRFactory
に渡され、ASTをソースコードに戻しながら再解析される
ソースコードに戻す役割はDecompiler
IRFactory
がParser
を継承しているため、どちらでなにをやっているのかがとてもわかりにくくなっている(継承によるコードの再利用問題)
たとえば({a})
の{a}
はオブジェクトリテラル(ObjectLiteral)だけど、({a} = { b: 1 })
だと{a}
は分割代入(DestructuringAssignment)の左辺値(LeftHandSideExpression)になる
=
が出てくるまで{a}
はどちらかわからない
var
がついていればわかるんだけど、ついてないばあいはグローバル変数になる仕様なので
エラーの数や箇所が変わる
${IRFactoryでSyntaxErrorになるコード}
${ParserでSyntaxErrorになるコード}
こんなコードがあったばあい、SyntaxErrorを全部見つけたいならエラーの数が2つになってほしいし、SyntaxErrorで早期脱出したいなら1行目のエラーを報告してほしい
2回解析だとエラーの数が1つになるし、早期脱出なら2行目のエラーが報告されてしまう
https://262.ecma-international.org/11.0/#sec-syntactic-grammar
"P is not covering an N"
多分これが2回解析する根拠になっている(違うかも)
SpiderMonkeyはこれ
https://searchfox.org/mozilla-central/source/js/src/frontend/Parser.cpp#11133
Esprimaだとこれ
https://github.com/jquery/esprima/blob/70c015998b5beb3b1f8c2277cd9288ad6917f378/src/parser.ts#L522
CoverInitializedName
とかisolateCoverGrammar
とか、coverなんとかってやつ
ちゃんと読んでないので、2回解析してる実装になっているかはあやしい
https://qiita.com/uhyo/items/c1574cdd11b1a28b85b7 によるとV8は2回解析になってるらしい
coverがこのあたりの挙動を把握するキーワードになるのはまちがいなさそう
ないと思う
Rhinoの分割代入はECMAScript6の仕様ではなく、JavaScript 1.8とか呼ばれるMozilla拡張の仕様で実装されている
だからECMA262の仕様と乖離した実装になったのではないかな
ほかにもたとえばlet
なんかも実装されてるように見えるけど、TDZがない
そう思う
そう思う
Rhinoや古いFirefoxのFunction#toString
にはASTからソースコードに戻している挙動が現れていて、以下の特徴がある
- コード内のコメントが消える
- コードのインデントなどが整形される
なのでテンプレートリテラルがなかった時代にFunction#toString
でコメントをヒアドキュメント代わりに使うtipsがあったんだけどFirefoxでは使えなかったりとか、Function#toString
をフォーマッタ代わりに使うtipsがあったりとかした
Function#toString
はもとのソースコードを切り取って返せばよくって、現代のJavaScriptエンジンだとだいたいそうなってたはず
わからん
問題を分割して段階的な移行ができればいいんだけど
大規模な変更をしたばあい、ECMA262準拠の部分はtest262があるからかんたんには壊れないはずだけど、Mozilla拡張とかE4Xとかを壊しそう(捨てたい)