Question

Après quelques tests resent j'ai trouvé ma mise en œuvre ne peut pas gérer très récursivité. Bien après avoir couru quelques tests dans Firefox, je trouve que cela peut être plus commune que je pensais à l'origine. Je crois que le problème fondamental est que ma mise en œuvre nécessite 3 appels à faire un appel de fonction. Le premier appel est fait à une méthode nommée Call qui fait que l'appel est fait à un objet appelable et obtient la valeur des arguments qui sont des références. Le deuxième appel est effectué à une méthode nommée Call qui est définie dans l'interface ICallable. Cette méthode crée le nouveau contexte d'exécution et construit l'expression lambda si elle n'a pas été créée. Le dernier appel est fait au lambda que l'objet de la fonction encapsule. Il est clair que faire un appel de fonction est assez lourd mais je suis sûr qu'avec un peu de peaufinage je peux faire récursion un outil viable lors de l'utilisation de cette mise en œuvre.

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;
}
Était-ce utile?

La solution

Je ne peux pas croire à quel point facile cela était de faire fonctionner. En gros, dans mon compilateur je vérifie pour voir si la fonction renvoie le résultat de l'appel lui-même. Si oui, je reviens à la place des arguments qui sont passés. Ensuite, je prends simplement des valeurs de référence et re-Invoke le soutien lambda. Avec cela en place, j'ai pu faire des millions d'appels récursifs.

Je tiens à remercier DrJokepu pour inspirer cette solution.

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);
    var result = default(object);
    var callArgs = default(object[]);

    Engine.EnterContext(newContext);
    while (true)
    {
        result = Function.Value(newContext, arguments);
        callArgs = result as object[];
        if (callArgs == null)
        {
            break;
        }
        for (int i = 0; i < callArgs.Length; i++)
        {
            callArgs[i] = Reference.GetValue(callArgs[i]);
        }
        arguments = callArgs;
    }
    Engine.LeaveContext();

    return result;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top