Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Jint/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
64 changes: 53 additions & 11 deletions Jint/Runtime/Interpreter/JintFunctionDefinition.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Jint.Native;
using Jint.Native.Function;
using Jint.Native.Generator;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -199,6 +198,7 @@ internal sealed class State
public DeclarationCache? LexicalDeclarations;
public HashSet<Key>? ParameterBindings;
public List<VariableValuePair>? VarsToInitialize;
public bool NeedsEvalContext;

internal readonly record struct VariableValuePair(Key Name, JsValue? InitialValue);
}
Expand Down Expand Up @@ -233,27 +233,20 @@ 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)
{
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;
}
Expand All @@ -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<Key>(state.ParameterNames);
if (state.ArgumentsObjectNeeded)
{
Expand Down Expand Up @@ -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;
}
}
}