diff --git a/Jint/Engine.cs b/Jint/Engine.cs index c72d290335..d4839854b0 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -1134,7 +1134,7 @@ private void GlobalDeclarationInstantiation( // A https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation DeclarativeEnvironment lexEnv; - if (!strict) + if (configuration.NeedsEvalContext || _isDebugMode) { lexEnv = JintEnvironment.NewDeclarativeEnvironment(this, varEnv); // NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index ebea2dcba0..c41e859a15 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using Jint.Native; using Jint.Native.Function; using Jint.Native.Generator; @@ -168,7 +167,7 @@ private Completion EvaluateGeneratorBody( var G = engine.Realm.Intrinsics.Function.OrdinaryCreateFromConstructor( functionObject, static intrinsics => intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject, - static (Engine engine , Realm _, object? _) => new GeneratorInstance(engine)); + static (Engine engine, Realm _, object? _) => new GeneratorInstance(engine)); _bodyStatementList ??= new JintStatementList(Function); _bodyStatementList.Reset(); @@ -199,6 +198,7 @@ internal sealed class State public DeclarationCache? LexicalDeclarations; public HashSet? ParameterBindings; public List? VarsToInitialize; + public bool NeedsEvalContext; internal readonly record struct VariableValuePair(Key Name, JsValue? InitialValue); } @@ -233,8 +233,6 @@ internal static State BuildState(IFunction function) state.FunctionsToInitialize = functionsToInitialize; - const string ParameterNameArguments = "arguments"; - state.ArgumentsObjectNeeded = true; var thisMode = strict ? FunctionThisMode.Strict : FunctionThisMode.Global; if (function.Type == NodeType.ArrowFunctionExpression) @@ -242,18 +240,13 @@ internal static State BuildState(IFunction function) thisMode = FunctionThisMode.Lexical; } - if (thisMode == FunctionThisMode.Lexical) - { - state.ArgumentsObjectNeeded = false; - } - else if (hasArguments) + if (thisMode == FunctionThisMode.Lexical || hasArguments) { state.ArgumentsObjectNeeded = false; } else if (!state.HasParameterExpressions) { - if (state.FunctionNames.Contains(ParameterNameArguments) - || lexicalNames?.Contains(ParameterNameArguments) == true) + if (state.FunctionNames.Contains(KnownKeys.Arguments) || lexicalNames?.Contains(KnownKeys.Arguments.Name) == true) { state.ArgumentsObjectNeeded = false; } @@ -265,6 +258,13 @@ internal static State BuildState(IFunction function) state.ArgumentsObjectNeeded = ArgumentsUsageAstVisitor.HasArgumentsReference(function); } + state.NeedsEvalContext = !strict; + if (state.NeedsEvalContext) + { + // yet another extra check + state.NeedsEvalContext = EvalContextAstVisitor.HasEvalOrDebugger(function); + } + var parameterBindings = new HashSet(state.ParameterNames); if (state.ArgumentsObjectNeeded) { @@ -502,4 +502,46 @@ private static bool HasArgumentsReference(Node node) return false; } } + + private static class EvalContextAstVisitor + { + public static bool HasEvalOrDebugger(IFunction function) + { + if (HasEvalOrDebugger(function.Body)) + { + return true; + } + + return false; + } + + private static bool HasEvalOrDebugger(Node node) + { + foreach (var childNode in node.ChildNodes) + { + var childType = childNode.Type; + if (childType == NodeType.DebuggerStatement) + { + return true; + } + + if (childType == NodeType.CallExpression) + { + if (((CallExpression) childNode).Callee is Identifier identifier && identifier.Name.Equals("eval", StringComparison.Ordinal)) + { + return true; + } + } + else if (childType != NodeType.FunctionDeclaration && !childNode.ChildNodes.IsEmpty()) + { + if (HasEvalOrDebugger(childNode)) + { + return true; + } + } + } + + return false; + } + } }