瀏覽代碼

Avoid creating context for eval if eval is not present (#2059)

Marko Lahma 5 月之前
父節點
當前提交
1e90413e3d
共有 2 個文件被更改,包括 54 次插入12 次删除
  1. 1 1
      Jint/Engine.cs
  2. 53 11
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs

+ 1 - 1
Jint/Engine.cs

@@ -1134,7 +1134,7 @@ public sealed partial class Engine : IDisposable
         // 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

+ 53 - 11
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 @@ internal sealed class JintFunctionDefinition
         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 JintFunctionDefinition
         public DeclarationCache? LexicalDeclarations;
         public HashSet<Key>? ParameterBindings;
         public List<VariableValuePair>? VarsToInitialize;
+        public bool NeedsEvalContext;
 
         internal readonly record struct VariableValuePair(Key Name, JsValue? InitialValue);
     }
@@ -233,8 +233,6 @@ internal sealed class JintFunctionDefinition
 
         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 sealed class JintFunctionDefinition
             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 sealed class JintFunctionDefinition
             state.ArgumentsObjectNeeded = ArgumentsUsageAstVisitor.HasArgumentsReference(function);
         }
 
+        state.NeedsEvalContext = !strict;
+        if (state.NeedsEvalContext)
+        {
+            // yet another extra check
+            state.NeedsEvalContext = EvalContextAstVisitor.HasEvalOrDebugger(function);
+        }
+
         var parameterBindings = new HashSet<Key>(state.ParameterNames);
         if (state.ArgumentsObjectNeeded)
         {
@@ -502,4 +502,46 @@ internal sealed class JintFunctionDefinition
             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;
+        }
+    }
 }