オブジェクト内にカプセル化されたインタラクティブな read-eval-print-loop を (JavaScript で) 作成しました。しかし、私は最近、インタープリターに指定されたトップレベルの関数定義がインタープリターによって「記憶」されていないように見えることに気付きました。いくつかの診断作業の後、コアの問題を次のように減らしました。
var evaler = {
eval: function (str)
{
return eval(str);
},
};
eval("function t1() { return 1; }"); // GOOD
evaler.eval("function t2() { return 2; }"); // FAIL
この時点で、次の 2 つのステートメントが期待どおりに機能することを期待しています。
print(t1()); // => Results in 1 (This works)
print(t2()); // => Results in 2 (this fails with an error that t2 is undefined.)
t1
代わりに得られるのは、行の期待値であり、行はバインドされていないt2
エラーで失敗します。t2
IOW: このスクリプトを実行した後、 の定義があり、 の定義がt1
ありませんt2
。内部から eval を呼び出す行為はevaler
、グローバル定義が記録されないトップレベルの呼び出しとは十分に異なります。何が起こるかというと、への呼び出しが関数オブジェクトを
返すということです。そのため、それが定義され、アクセスできない他のバインディングのセットに格納されているとevaler.eval
推測しています。t2
( ではメンバーとして定義されていませんevaler
。)
これに対する簡単な修正はありますか?私はあらゆる種類の修正を試みましたが、機能するものに出くわしませんでした。(私が行ったことのほとんどは、 eval への呼び出しを無名関数に入れ、その呼び出し方法を変更したり、チェーン化したりすることに集中しています__parent__
。)
これを修正する方法について何か考えはありますか?
もう少し調べた結果が以下です。
tl;dr: インスタンスでメソッドを呼び出すと、Rhino は中間スコープをスコープ チェーンに追加します。t2
はこの中間スコープで定義されており、すぐに破棄されます。@マット:あなたの「ハッキーな」アプローチは、これを解決する最良の方法かもしれません。
私はまだ根本的な原因についていくつかの作業を行っていますが、jdb で質の高い時間を過ごしたおかげで、何が起こっているのかをより理解できるようになりました。すでに説明したように、関数ステートメント likefunction t1() { return 42; }
は 2 つのことを行います。
- 式で得られるように、関数オブジェクトの匿名インスタンスを作成します
function() { return 42; }
- その無名関数を現在の最上位のスコープに名前 でバインドします
t1
。
eval
私の最初の質問は、オブジェクトのメソッド内から呼び出したときに、これらの 2 番目のことが発生しない理由についてです。
Rhino でバインディングを実際に実行するコードは、関数内にあるようorg.mozilla.javascript.ScriptRuntime.initFunction
です。
if (type == FunctionNode.FUNCTION_STATEMENT) {
....
scope.put(name, scope, function);
上記のt1
場合、scope
私がトップレベルのスコープに設定したものです。これは、トップレベル関数を定義したい場所なので、これは予想される結果です:
main[1] print function.getFunctionName()
function.getFunctionName() = "t1"
main[1] print scope
scope = "com.me.testprogram.Main@24148662"
ただし、このt2
場合scope
は、まったく別のものです。
main[1] print function.getFunctionName()
function.getFunctionName() = "t2"
main[1] print scope
scope = "org.mozilla.javascript.NativeCall@23abcc03"
NativeCall
そして、これは私の予想されるトップレベルのスコープであるこれの親スコープです:
main[1] print scope.getParentScope()
scope.getParentScope() = "com.me.testprogram.Main@24148662"
これは、上でこれを書いたときに私が恐れていたことです。 ' のインスタンスであることが判明しましたNativeCall
...関数が作成され、 内の変数にt2
バインドされ、 への呼び出しが戻ると は消えます。t2
NativeCall
NativeCall
evaler.eval
そして、これは物事が少しあいまいになるところです...私が望むほど多くの分析を行っていませんが、私の現在の作業理論は、NativeCall
スコープが必要であるということです。(スタックフレームを少しバックアップすると、関数が「アクティブ化が必要」でゼロ以外の関数型を持つまでにスコープチェーンに追加されます。これらのことは単純な関数呼び出しにのみ当てはまると想定していますが、確かに知るのに十分な上流をたどっていない. 多分明日.)this
evaler
evaler.eval
NativeCall
Interpreter.initFrame