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)); }