diff --git a/Jint.Tests/Runtime/Debugger/EvaluateTests.cs b/Jint.Tests/Runtime/Debugger/EvaluateTests.cs index f3ecff43b0..d7f6f9b131 100644 --- a/Jint.Tests/Runtime/Debugger/EvaluateTests.cs +++ b/Jint.Tests/Runtime/Debugger/EvaluateTests.cs @@ -105,16 +105,7 @@ function test(x) var frameAfter = engine.CallStack.Stack[0]; // Stack frames and some of their properties are structs - can't check reference equality // Besides, even if we could, it would be no guarantee. Neither is the following, but it'll do for now. - Assert.Equal(frameBefore.CallingExecutionContext.Function, - frameAfter.CallingExecutionContext.Function); - Assert.Equal(frameBefore.CallingExecutionContext.LexicalEnvironment, - frameAfter.CallingExecutionContext.LexicalEnvironment); - Assert.Equal(frameBefore.CallingExecutionContext.PrivateEnvironment, - frameAfter.CallingExecutionContext.PrivateEnvironment); - Assert.Equal(frameBefore.CallingExecutionContext.VariableEnvironment, - frameAfter.CallingExecutionContext.VariableEnvironment); - Assert.Equal(frameBefore.CallingExecutionContext.Realm, frameAfter.CallingExecutionContext.Realm); - + Assert.Equal(frameBefore.CallingExecutionContext.LexicalEnvironment, frameAfter.CallingExecutionContext.LexicalEnvironment); Assert.Equal(frameBefore.Arguments, frameAfter.Arguments); Assert.Equal(frameBefore.Expression, frameAfter.Expression); Assert.Equal(frameBefore.Location, frameAfter.Location); diff --git a/Jint/Engine.cs b/Jint/Engine.cs index b06caea570..e685825f33 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -171,7 +171,7 @@ internal ExecutionContext EnterExecutionContext( return context; } - internal ExecutionContext EnterExecutionContext(ExecutionContext context) + internal ExecutionContext EnterExecutionContext(in ExecutionContext context) { _executionContexts.Push(context); return context; @@ -1291,6 +1291,7 @@ JsValue Callback() return ExecuteWithConstraints(Options.Strict, Callback); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal JsValue Call(ICallable callable, JsValue thisObject, JsValue[] arguments, JintExpression? expression) { if (callable is FunctionInstance functionInstance) @@ -1354,14 +1355,12 @@ internal JsValue Call( JsValue[] arguments, JintExpression? expression) { - var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext); - var recursionDepth = CallStack.Push(callStackElement); + var recursionDepth = CallStack.Push(functionInstance, expression, ExecutionContext); if (recursionDepth > Options.Constraints.MaxRecursionDepth) { - // pop the current element as it was never reached - CallStack.Pop(); - ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack, callStackElement.ToString()); + // automatically pops the current element as it was never reached + ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack); } JsValue result; @@ -1387,14 +1386,12 @@ private ObjectInstance Construct( JsValue newTarget, JintExpression? expression) { - var callStackElement = new CallStackElement(functionInstance, expression, ExecutionContext); - var recursionDepth = CallStack.Push(callStackElement); + var recursionDepth = CallStack.Push(functionInstance, expression, ExecutionContext); if (recursionDepth > Options.Constraints.MaxRecursionDepth) { - // pop the current element as it was never reached - CallStack.Pop(); - ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack, callStackElement.ToString()); + // automatically pops the current element as it was never reached + ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack); } ObjectInstance result; diff --git a/Jint/Native/JsBigInt.cs b/Jint/Native/JsBigInt.cs index 2f4be77503..85ef4e295b 100644 --- a/Jint/Native/JsBigInt.cs +++ b/Jint/Native/JsBigInt.cs @@ -113,7 +113,7 @@ public bool Equals(JsBigInt? other) return false; } - return ReferenceEquals(this, other) || _value.Equals(other._value); + return ReferenceEquals(this, other) || _value == other._value; } public override int GetHashCode() diff --git a/Jint/Runtime/CallStack/CallStackElement.cs b/Jint/Runtime/CallStack/CallStackElement.cs index 7e252006b8..03049f2fa3 100644 --- a/Jint/Runtime/CallStack/CallStackElement.cs +++ b/Jint/Runtime/CallStack/CallStackElement.cs @@ -1,17 +1,16 @@ using Esprima; using Esprima.Ast; using Jint.Native.Function; -using Jint.Runtime.Environments; using Jint.Runtime.Interpreter.Expressions; namespace Jint.Runtime.CallStack { - internal readonly struct CallStackElement + internal readonly struct CallStackElement : IEquatable { public CallStackElement( FunctionInstance function, JintExpression? expression, - ExecutionContext callingExecutionContext) + in CallStackExecutionContext callingExecutionContext) { Function = function; Expression = expression; @@ -20,7 +19,7 @@ public CallStackElement( public readonly FunctionInstance Function; public readonly JintExpression? Expression; - public readonly ExecutionContext CallingExecutionContext; + public readonly CallStackExecutionContext CallingExecutionContext; public Location Location { @@ -52,5 +51,23 @@ public override string ToString() return name ?? "(anonymous)"; } + + public bool Equals(CallStackElement other) + { + return Function.Equals(other.Function) && Equals(Expression, other.Expression); + } + + public override bool Equals(object? obj) + { + return obj is CallStackElement other && Equals(other); + } + + public override int GetHashCode() + { + unchecked + { + return (Function.GetHashCode() * 397) ^ (Expression != null ? Expression.GetHashCode() : 0); + } + } } } diff --git a/Jint/Runtime/CallStack/JintCallStack.cs b/Jint/Runtime/CallStack/JintCallStack.cs index a37a1961b5..6b3e5fb2a7 100644 --- a/Jint/Runtime/CallStack/JintCallStack.cs +++ b/Jint/Runtime/CallStack/JintCallStack.cs @@ -3,10 +3,42 @@ using Esprima; using Esprima.Ast; using Jint.Collections; +using Jint.Native.Function; using Jint.Pooling; +using Jint.Runtime.Environments; +using Jint.Runtime.Interpreter.Expressions; namespace Jint.Runtime.CallStack { + // smaller version with only required info + internal readonly record struct CallStackExecutionContext + { + public CallStackExecutionContext(in ExecutionContext context) + { + LexicalEnvironment = context.LexicalEnvironment; + } + + internal readonly EnvironmentRecord LexicalEnvironment; + + internal EnvironmentRecord GetThisEnvironment() + { + var lex = LexicalEnvironment; + while (true) + { + if (lex != null) + { + if (lex.HasThisBinding()) + { + return lex; + + } + + lex = lex._outerEnv; + } + } + } + } + internal sealed class JintCallStack { private readonly RefStack _stack = new(); @@ -23,8 +55,9 @@ public JintCallStack(bool trackRecursionDepth) } } - public int Push(in CallStackElement item) + public int Push(FunctionInstance functionInstance, JintExpression? expression, in ExecutionContext executionContext) { + var item = new CallStackElement(functionInstance, expression, new CallStackExecutionContext(executionContext)); _stack.Push(item); if (_statistics is not null) { @@ -172,5 +205,6 @@ private static string GetPropertyKey(Node expression) return "?"; } + } } diff --git a/Jint/Runtime/Debugger/CallFrame.cs b/Jint/Runtime/Debugger/CallFrame.cs index 320e70736c..fb1a099446 100644 --- a/Jint/Runtime/Debugger/CallFrame.cs +++ b/Jint/Runtime/Debugger/CallFrame.cs @@ -8,11 +8,15 @@ namespace Jint.Runtime.Debugger { public sealed class CallFrame { - private readonly ExecutionContext _context; + private readonly CallStackExecutionContext _context; private readonly CallStackElement? _element; private readonly Lazy _scopeChain; - internal CallFrame(CallStackElement? element, ExecutionContext context, Location location, JsValue? returnValue) + internal CallFrame( + CallStackElement? element, + in CallStackExecutionContext context, + Location location, + JsValue? returnValue) { _element = element; _context = context; diff --git a/Jint/Runtime/Debugger/DebugCallStack.cs b/Jint/Runtime/Debugger/DebugCallStack.cs index 5bad0b9e0f..3db38f83bc 100644 --- a/Jint/Runtime/Debugger/DebugCallStack.cs +++ b/Jint/Runtime/Debugger/DebugCallStack.cs @@ -12,7 +12,7 @@ public sealed class DebugCallStack : IReadOnlyList internal DebugCallStack(Engine engine, Location location, JintCallStack callStack, JsValue? returnValue) { _stack = new List(callStack.Count + 1); - var executionContext = engine.ExecutionContext; + var executionContext = new CallStackExecutionContext(engine.ExecutionContext); foreach (var element in callStack.Stack) { _stack.Add(new CallFrame(element, executionContext, location, returnValue)); diff --git a/Jint/Runtime/ExceptionHelper.cs b/Jint/Runtime/ExceptionHelper.cs index 66a78a0b07..e55dd980d0 100644 --- a/Jint/Runtime/ExceptionHelper.cs +++ b/Jint/Runtime/ExceptionHelper.cs @@ -152,9 +152,9 @@ public static void ThrowJavaScriptException(ErrorConstructor errorConstructor, s } [DoesNotReturn] - public static void ThrowRecursionDepthOverflowException(JintCallStack currentStack, - string currentExpressionReference) + public static void ThrowRecursionDepthOverflowException(JintCallStack currentStack) { + var currentExpressionReference = currentStack.Pop().ToString(); throw new RecursionDepthOverflowException(currentStack, currentExpressionReference); }