После нескольких тестов я обнаружил, что моя реализация не может справиться с рекурсией. Хотя после того, как я провел несколько тестов в Firefox, я обнаружил, что это может быть более распространенным, чем я думал. Я считаю, что основная проблема заключается в том, что моя реализация требует 3 вызова для вызова функции. Первый вызов выполняется для метода с именем Call
, который гарантирует, что вызов выполняется для вызываемого объекта, и получает значение любых аргументов, являющихся ссылками. Второй вызов выполняется для метода с именем 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;
}