Esempio n. 1
0
        public static Completion RegExpAllocAndInitialize(string P, string F)
        {
            var         allowedFlags = "gimsuy";
            List <char> invalidFlags;

            if ((invalidFlags = F.Where(c => !allowedFlags.Contains(c, StringComparison.Ordinal)).ToList()).Any())
            {
                if (invalidFlags.Count == 1)
                {
                    return(Completion.ThrowTypeError($"'{invalidFlags[0]}' is not a valid RegExp flag."));
                }
                else
                {
                    return(Completion.ThrowTypeError($"These flags not valid RegExp flags: {{ {string.Join(", ", invalidFlags.Select(f => $"'{f}'"))} }}"));
                }
            }
            else if (F.Distinct().Count() != F.Length)
            {
                return(Completion.ThrowTypeError($"RegExp flags should not contain duplicates."));
            }

            var BMP = !F.Contains('u', StringComparison.Ordinal);

            var options = RegexOptions.ECMAScript;

            if (F.Contains('m', StringComparison.Ordinal))
            {
                options |= RegexOptions.Multiline;
            }
            if (F.Contains('s', StringComparison.Ordinal))
            {
                options |= RegexOptions.Singleline;
                options &= ~RegexOptions.ECMAScript;
            }
            if (F.Contains('i', StringComparison.Ordinal))
            {
                options |= RegexOptions.IgnoreCase;
            }

            if (options.HasFlag(RegexOptions.Multiline))
            {
                int index      = 0;
                var newPattern = P;
                while ((index = newPattern.IndexOf("$", index, StringComparison.Ordinal)) != -1)
                {
                    if (index > 0 && newPattern[index - 1] != '\\')
                    {
                        newPattern = newPattern.Substring(0, index) + @"\r?" + newPattern.Substring(index);
                        index     += 4;
                    }
                }

                P = newPattern;
            }
            Regex regex;

            try
            {
                regex = new Regex(P, options);
            }
            catch
            {
                return(Completion.ThrowSyntaxError("Invalid RegEx"));
            }

            var obj = new RegExpObject(P, F, regex);
            var set = obj.Set("lastIndex", new NumberValue(0), true);

            if (set.IsAbrupt())
            {
                return(set);
            }
            return(Completion.NormalCompletion(obj));
        }
        private static Completion EvalDeclarationInstantiation(ScriptStatementList body, LexicalEnvironment varEnv, LexicalEnvironment lexEnv, bool strict)
        {
            var varNames        = body.VarDeclaredNames();
            var varDeclarations = body.VarScopedDeclarations();
            var lexEnvRec       = lexEnv.EnvironmentRecord;
            var varEnvRec       = varEnv.EnvironmentRecord;

            if (!strict)
            {
                if (varEnvRec is GlobalEnvironmentRecord g)
                {
                    foreach (var name in varNames)
                    {
                        if (g.HasLexicalDeclaration(name))
                        {
                            return(Completion.ThrowSyntaxError("Spec 18.2.1.3 step 5ai1"));
                        }
                    }
                }
                LexicalEnvironment?thisLex = lexEnv;
                while (thisLex != varEnv)
                {
                    var thisEnvRec = thisLex.EnvironmentRecord;
                    if (!(thisEnvRec is ObjectEnvironmentRecord))
                    {
                        foreach (var name in varNames)
                        {
                            if (thisEnvRec.HasBinding(name).Other)
                            {
                                return(Completion.ThrowSyntaxError("Spec 18.2.1.3 step 5dii2ai"));
                            }
                        }
                    }
                    thisLex = thisLex.Outer;
                    if (thisLex == null)
                    {
                        throw new InvalidOperationException("thisLex and varEnv never matched");
                    }
                }
            }
            var functionsToInitialize = new List <FunctionDeclaration>();
            var declaredFunctionNames = new List <string>();

            foreach (var d in varDeclarations.Reverse())
            {
                if (!(d is VariableDeclaration) && !(d is ForBinding))
                {
                    if (!(d is FunctionDeclaration f))
                    {
                        throw new InvalidOperationException("Spec 18.2.1.3 step 8ai");
                    }
                    var fn = f.BoundNames()[0];
                    if (!declaredFunctionNames.Contains(fn))
                    {
                        if (varEnvRec is GlobalEnvironmentRecord g)
                        {
                            var fnDefinable = g.CanDeclareGlobalFunction(fn);
                            if (fnDefinable.IsAbrupt())
                            {
                                return(fnDefinable);
                            }
                            if (!fnDefinable.Other)
                            {
                                return(Completion.ThrowTypeError($"Function {fn} is not definable in global scope"));
                            }
                        }
                        declaredFunctionNames.Add(fn);
                        functionsToInitialize.Insert(0, f);
                    }
                }
            }
            var declaredVarNames = new List <string>();

            foreach (var d in varDeclarations)
            {
                IReadOnlyList <string>?boundNames = null;
                if (d is VariableDeclaration v)
                {
                    boundNames = v.BoundNames();
                }
                if (d is ForBinding f)
                {
                    boundNames = f.BoundNames();
                }
                if (boundNames != null)
                {
                    foreach (var vn in boundNames)
                    {
                        if (!declaredFunctionNames.Contains(vn))
                        {
                            if (varEnvRec is GlobalEnvironmentRecord g)
                            {
                                var fnDefinable = g.CanDeclareGlobalVar(vn);
                                if (fnDefinable.IsAbrupt())
                                {
                                    return(fnDefinable);
                                }
                                if (!fnDefinable.Other)
                                {
                                    return(Completion.ThrowTypeError($"Variable {vn} is not definable in global scope"));
                                }
                            }
                            if (!declaredVarNames.Contains(vn))
                            {
                                declaredVarNames.Add(vn);
                            }
                        }
                    }
                }
            }
            var lexDeclarations = body.LexicallyScopedDeclarations();

            foreach (var d in lexDeclarations)
            {
                foreach (var dn in d.BoundNames())
                {
                    Completion comp;
                    if (d.IsConstantDeclaration())
                    {
                        comp = lexEnvRec.CreateImmutableBinding(dn, true);
                    }
                    else
                    {
                        comp = lexEnvRec.CreateMutableBinding(dn, false);
                    }
                    if (comp.IsAbrupt())
                    {
                        return(comp);
                    }
                }
            }
            foreach (var f in functionsToInitialize)
            {
                var fn = f.BoundNames()[0];
                var fo = f.InstantiateFunctionObject(lexEnv);
                if (varEnvRec is GlobalEnvironmentRecord g)
                {
                    var comp = g.CreateGlobalFunctionBinding(fn, fo, true);
                    if (comp.IsAbrupt())
                    {
                        return(comp);
                    }
                }
                else
                {
                    var bindingExists = varEnvRec.HasBinding(fn);
                    if (!bindingExists.Other)
                    {
                        var status = varEnvRec.CreateMutableBinding(fn, true);
                        if (status.IsAbrupt())
                        {
                            throw new InvalidOperationException("Spec 18.2.1.3 Step 15dii2");
                        }
                        varEnvRec.InitializeBinding(fn, fo);
                    }
                    else
                    {
                        varEnvRec.SetMutableBinding(fn, fo, false);
                    }
                }
            }
            foreach (var vn in declaredVarNames)
            {
                if (varEnvRec is GlobalEnvironmentRecord g)
                {
                    var comp = g.CreateGlobalVarBinding(vn, true);
                    if (comp.IsAbrupt())
                    {
                        return(comp);
                    }
                }
                else
                {
                    var bindingExists = varEnvRec.HasBinding(vn);
                    if (!bindingExists.Other)
                    {
                        var status = varEnvRec.CreateMutableBinding(vn, true);
                        if (status.IsAbrupt())
                        {
                            throw new InvalidOperationException("Spec 18.2.1.3 Step 16bii2");
                        }
                        varEnvRec.InitializeBinding(vn, UndefinedValue.Instance);
                    }
                }
            }
            return(Completion.NormalCompletion());
        }
        public static Completion PerformEval(IValue xValue, Realm evalRealm, bool strictCaller, bool direct)
        {
            if (!direct && strictCaller)
            {
                throw new InvalidOperationException("Indirect evals cannot have strictCaller. Spec 18.2.1.1 step 1");
            }
            if (!(xValue is StringValue xString))
            {
                return(Completion.NormalCompletion(xValue));
            }
            var  x = xString.@string;
            var  thisEnvRec = Interpreter.Instance().GetThisEnvironment();
            bool inFunction, inMethod, inDerivedConstructor;

            if (thisEnvRec is FunctionEnvironmentRecord functionEnvironmentRecord)
            {
                var F = functionEnvironmentRecord.FunctionObject;
                inFunction           = true;
                inMethod             = functionEnvironmentRecord.HasSuperBinding();
                inDerivedConstructor = F.ConstructorKind == ConstructorKind.Derived;
            }
            else
            {
                inFunction = inMethod = inDerivedConstructor = false;
            }
            Script script;

            try
            {
                //TODO use the in variables to apply additional early errors
                // Spec 18.2.1.1 step 6
                script = new Parser.Parser(x)
                {
                    Strict = strictCaller
                }.ParseScript();
            }
            catch (Exception e)
            {
                return(Completion.ThrowSyntaxError(e.Message));
            }
            if (!script.scriptBody.Any())
            {
                return(Completion.NormalCompletion(UndefinedValue.Instance));
            }
            var strictEval = strictCaller || script.IsStrictMode;
            var ctx        = Interpreter.Instance().RunningExecutionContext();
            LexicalEnvironment lexEnv;
            LexicalEnvironment varEnv;

            if (direct)
            {
                lexEnv = ctx.LexicalEnvironment.NewDeclarativeEnvironment();
                varEnv = ctx.VariableEnvironment;
            }
            else
            {
                lexEnv = evalRealm.GlobalEnv.NewDeclarativeEnvironment();
                varEnv = evalRealm.GlobalEnv;
            }
            if (strictEval)
            {
                varEnv = lexEnv;
            }
            var evalCtx = new ExecutionContext(evalRealm)
            {
                VariableEnvironment = varEnv,
                LexicalEnvironment  = lexEnv
            };

            Interpreter.Instance().PushExecutionStack(evalCtx);
            var result = EvalDeclarationInstantiation(script.scriptBody, varEnv, lexEnv, strictEval);

            if (result.completionType == CompletionType.Normal)
            {
                result = script.scriptBody.Evaluate(Interpreter.Instance());
            }
            if (result.completionType == CompletionType.Normal && result.value == null)
            {
                result = Completion.NormalCompletion(UndefinedValue.Instance);
            }
            Interpreter.Instance().PopExecutionStack(evalCtx);
            return(result);
        }
        private Completion CreateDynamicFunction(Object?newTarget, FunctionKind kind, IReadOnlyList <IValue> arguments)
        {
            if (Interpreter.Instance().ExecutionContextStackSize() < 2)
            {
                throw new InvalidOperationException("Spec 19.2.1.1.1 step 1");
            }
            var callerContext = Interpreter.Instance().SecondExecutionContext();
            var callerRealm   = callerContext.Realm;
            var calleeRealm   = Interpreter.Instance().CurrentRealm();

            //TODO HostEnsureCanCompileStrings
            if (newTarget == null)
            {
                newTarget = this;
            }
            Func <Parser.Parser, AST.FunctionStatementList> goal;
            Func <Intrinsics, Object> fallbackProto;

            switch (kind)
            {
            case FunctionKind.Normal:
                goal          = (p) => p.ParseFunctionBody();
                fallbackProto = i => i.FunctionPrototype;
                break;

            default:
                throw new NotImplementedException("Async and/or generators");
            }
            var    argCount = arguments.Count;
            var    P        = "";
            string bodyText;

            if (argCount == 0)
            {
                bodyText = "";
            }
            else if (argCount == 1)
            {
                var bodyComp = arguments[0].ToJsString();
                if (bodyComp.IsAbrupt())
                {
                    return(bodyComp);
                }
                bodyText = (bodyComp.value as StringValue) !.@string;
            }
            else
            {
                var firstArg = arguments[0];
                var pComp    = firstArg.ToJsString();
                if (pComp.IsAbrupt())
                {
                    return(pComp);
                }
                P = (pComp.value as StringValue) !.@string;
                int k = 1;
                for (; k < argCount - 1; k++)
                {
                    var nextArg           = arguments[k];
                    var nextArgStringComp = nextArg.ToJsString();
                    if (nextArgStringComp.IsAbrupt())
                    {
                        return(nextArgStringComp);
                    }
                    var nextArgString = (nextArgStringComp.value as StringValue) !.@string;
                    P += "," + nextArgString;
                }
                var bodyComp = arguments[k].ToJsString();
                if (bodyComp.IsAbrupt())
                {
                    return(bodyComp);
                }
                bodyText = (bodyComp.value as StringValue) !.@string;
            }
            AST.FormalParameters?parameters = new AST.FormalParameters();
            try
            {
                if (!string.IsNullOrEmpty(P))
                {
                    parameters = new Parser.Parser(P).ParseFormalParameters();
                }
                if (parameters == null)
                {
                    throw new Parser.ParseFailureException($"parameters {P} could not be parsed.");
                }
            }
            catch (Parser.ParseFailureException e)
            {
                return(Completion.ThrowSyntaxError($"Failed to parse parameters \"{P}\".\n{e.Message}"));
            }
            AST.FunctionStatementList body;
            try
            {
                body = goal(new Parser.Parser(bodyText));
            }
            catch (Parser.ParseFailureException e)
            {
                return(Completion.ThrowSyntaxError($"Failed to parse body \"{bodyText}\".\n{e.Message}"));
            }
            //TODO detect strict mode: ContainsUseStrict
            bool strict = body.IsStrictMode;

            if (!parameters.IsSimpleParameterList() && strict)
            {
                return(Completion.ThrowSyntaxError($"parameters must be simple in strict mode. \"{P}\""));
            }
            //TODO implement tree walking for checking if parameters or body contains a SuperCall or SuperProperty
            //TODO generator yield, async await errors
            var protoComp = Utils.GetPrototypeFromConstructor(newTarget, fallbackProto);

            if (protoComp.IsAbrupt())
            {
                return(protoComp);
            }
            var proto  = protoComp.value;
            var F      = FunctionObject.FunctionAllocate(proto !, strict, kind);
            var realmF = F.Realm;
            var scope  = realmF.GlobalEnv;

            FunctionObject.FunctionInitialize(F, FunctionCreateKind.Normal, parameters, body, scope);
            //TODO generator, async generator
            if (kind == FunctionKind.Normal)
            {
                F.MakeConstructor();
            }
            F.SetFunctionName("anonymous");
            return(Completion.NormalCompletion(F));
        }