IJS: Interactive JS Shell
click this and start slide
ところで
IRB, iPython 使ったことありますか?
V8のDeveloper Toolsのコンソールは, そのone lineでJSとしてvalid出なければならない.
たとえば
function AngelBeats() { console.log("天使ちゃんマジ天使"); }
と書ききらなければいけない.
これを
function AngelBeats(){ console.log("天使ちゃんマジ天使"); }
と3行に分けて書きたい.
テーマ
叶えたい夢 遠いけどJSとならできるよ 無敵の勇気でSTRIKE JAVASCRIPTERS 2 - JITの魔法 -
IJS, Interactive JS Shell
実装: 復帰可能SyntaxError
復帰可能Syntax Errorとは(自分で勝手に名づけている)
ほしいtokenが来ずに, EOS(End Of String)が来てしまったとき.
つまりTokenStreamが打ち切られてしまったことによるSyntax Error
これはつまり, 後でもうちょっとTokenを足せばSyntax Validになりえることを示す.
復帰可能Syntax Errorかどうかを見るには, Syntax Errorになったときにcurrent tokenがEOSかどうかを見ればいい.
Parserがあるのでちょっとみるだけ…
しかし
世の中には復帰不可能なErrorが存在.
code name : FATAL KO
Throw Statement
ECMA-262 section 7.9: Automatic Semicolon Insertion
ThrowStatement: throw [no LineTerminator here] Expression;
と指定されている.
つまり, one linerで, throw
これだけ弾けば(FATAL KOかどうかを出す例外で判別すれば), IJSのParse部分は完成です.
評価部分
IJS, 評価は同一のContextで行わないといけない
だめな例
function evaluate(text) { eval(text); }
関数終了と同時にevalの評価scopeが吹っ飛ぶ.
ゴミな例
loop.ijsScript = null; function loop() { while (true) { if (loop.ijsScript) { eval(loop.ijsScript); loop.ijsScript = null; } } }
これは余りにゴミ過ぎる
で
そもそもEvalの評価コンテキストって何?
Entering Eval Code
ECMA-262 5th section 10.4.2
Execution ContextsのEntering Eval Code
Set the LexicalEnvironment to the same value as the LexicalEnvironment of the calling execution context.
Set the VariableEnvironment to the same value as the VariableEnvironment of the calling execution context.
LexicalEnvとVariableEnvは呼び出し側実行Contextと同一.
Lexical/Variable Environmentとは
簡単に言うと, scope chain先頭がLexicalEnv, 変数の実体化が行われるのがVariableEnv
これに現在のthisの値を含めた3つをあわせたものをContextとする.
変数解決
section 10.3.1 に変数の解決(Identifier Resolution)
具体的な内容はsec 10.2.2.1の
GetIdentifierReference(lex, name, strict)
LexicalEnvironmentが渡される.
lexがなければReferenceのundefinedが結び付けられたもの, lexが存在すれば検索し, HasBindingがtrueであればReference, falseならばouter lexに再帰的に解決を実行.
変数の実体化
section 10.5 に変数の実体化(Declaration Binding Instantiation)
ここで使われるのはVariableEnvironment
VariableEnvにBindingが追加されていく(VariableStatementやFunctionDeclarationなど)
var文実行時じゃなく, CodeにEnteringしているときに行われます.
この挙動により, 清潔で安全なJS Quizが10生まれます.
1 quiz for 10 quiz.
実は
ほとんどすべての場合, LexicalEnvとVariableEnvはまったく同じ.
- Global Code
- 両方Global Object
- sec 10.4.1.1
- Function Code
- NewDeclarationEnvironmentで作られたlocal Env (3rd的名称でいくと, ActivationObject)
- sec 10.4.3
- Eval Code
- 呼び出し側のLexicalEnvとVariableEnvをそのまま使う
- sec 10.4.2
これを引っ掻き回すのがWithStatement
section 12.10 The with Statement
WithStatement: with (Expression) Statement
Expressionの評価結果は, 呼び出し側LexicalEnvironmentの先頭に追加される.
そしてStatement評価後, LexicalEnvironmentはもとの値に戻される.
大事なのは, 変更されるのはLexicalEnvironmentだけだということ!
この刹那, LexicalEnvironmentとVariableEnvironmentは違うものをさす.
with + eval
ただでさえ非推奨のwithがevalという翼を持って邪神になろうとしているな
example
function test() { with (window) { eval("var i = 20;"); } }
evalの評価時, LexicalEnvはwindow, VariableEnvはtestのlocal Env.
evalは呼び出し側のLexicalEnvとVariableEnvをそのまま使って変数の実体化をする.
変数iが実体化されるのはwindowではなく, 関数testのlocal Env.
つまり同じContextを保持したままのEvalはできない. 残念….?
まだまだ
あきらめない!!
ECMA-262 5thからの変更.
Entering Eval Codeの規律は, 実はDirect Callの場合のみ適用される.
Eval Call
- ECMA-262 sec 15.1.2.1.1, Direct Call to Eval
- eval(“var i = 20”); など
- これはさっきの規律に従う
- section 10.4.2, Annex E 10.4.2, indirect call to eval function
- Indirect Call to Evalでは常にLexical/VariableがGlobal Contextとなる.
Indirect Eval Call例
Indirect Eval Callとはこのようなもの.
var global = eval; (function() { global("var i = 20;"); })();
これは使える!!
ECMA-262 3rdの場合はすべて先ほどのDirect Call to Eval扱いでした.
Indirect Eval Call
indirect call to evalを実装しているEngineなら以下の表現でGlobal Contextで評価することができる.
var i = 20; (function() { var i = 10; var globalEval = eval; console.assert(eval("i") === 10); // pass console.assert(globalEval("i") === 20); // pass })();
indirect callの場合, Contextは常にGlobalとなる. (Lexical / VariableがGlobal Object)
これはFirefox, Operaで有効.
ちなみに
V8は…
console.assert(eval("i") === 10); // failure console.assert(globalEval("i") === 20); // pass
常にGlobal Context. えっ, なにそれこわい…
まとめ
Firefox, Opera
var globalEval = eval;
で以降globalEvalを使えばGlobal Contextで評価できる.
V8
evalで常にGlobal評価.
ECMA262 5th最高や! withなんて最初からいらんかったんや!!
終わり
ありがとうございました