いくつかの再送テストの後、私の実装ではあまり多くの再帰を処理できないことがわかりました。Firefox でいくつかのテストを実行した後、これは当初考えていたよりも一般的である可能性があることがわかりました。基本的な問題は、私の実装では、関数呼び出しを行うために 3 つの呼び出しが必要であることだと思います。最初の呼び出しは、名前付きのメソッドに対してCall
行われ、呼び出しが呼び出し可能なオブジェクトに対して行われていることを確認し、参照であるすべての引数の値を取得します。2 番目の呼び出しはCall
、ICallable
インターフェース。このメソッドは、新しい実行コンテキストを作成し、ラムダ式が作成されていない場合はビルドします。最後の呼び出しは、関数オブジェクトがカプセル化するラムダに対して行われます。関数呼び出しを行うのは明らかにかなり重いですが、この実装を使用する場合、少し調整するだけで再帰を実行可能なツールにできると確信しています。
public static object Call(ExecutionContext context, object value, object[] args)
{
var func = Reference.GetValue(value) as ICallable;
if (func == null)
{
throw new TypeException();
}
if (args != null && args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
args[i] = Reference.GetValue(args[i]);
}
}
var reference = value as Reference;
if (reference != null)
{
if (reference.IsProperty)
{
return func.Call(reference.Value, args);
}
else
{
return func.Call(((EnviromentRecord)reference.Value).ImplicitThisValue(), args);
}
}
return func.Call(Undefined.Value, args);
}
public object Call(object thisObject, object[] arguments)
{
var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
var variableEnviroment = Scope.NewDeclarativeEnviroment();
var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
Engine.EnterContext(newContext);
var result = Function.Value(newContext, arguments);
Engine.LeaveContext();
return result;
}