From 242efe1128cfae823b2128f41a036b04088bdefe Mon Sep 17 00:00:00 2001 From: Kemal BAYTEKIN Date: Wed, 29 Jan 2025 16:20:45 +0300 Subject: [PATCH 1/4] $(NoWarn);NU1507 added to build --- Jint.AotExample/Jint.AotExample.csproj | 1 + Jint.Repl/Jint.Repl.csproj | 1 + Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj | 1 + Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj | 1 + Jint.Tests.Test262/Jint.Tests.Test262.csproj | 1 + Jint.Tests/Jint.Tests.csproj | 1 + Jint/Jint.csproj | 1 + 7 files changed, 7 insertions(+) diff --git a/Jint.AotExample/Jint.AotExample.csproj b/Jint.AotExample/Jint.AotExample.csproj index 9072d51b5..9d46980f5 100644 --- a/Jint.AotExample/Jint.AotExample.csproj +++ b/Jint.AotExample/Jint.AotExample.csproj @@ -6,6 +6,7 @@ enable false true + $(NoWarn);NU1507 diff --git a/Jint.Repl/Jint.Repl.csproj b/Jint.Repl/Jint.Repl.csproj index a5cec8738..e5b0992ff 100644 --- a/Jint.Repl/Jint.Repl.csproj +++ b/Jint.Repl/Jint.Repl.csproj @@ -4,6 +4,7 @@ Exe false true + $(NoWarn);NU1507 diff --git a/Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj b/Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj index c3c478749..4704422d9 100644 --- a/Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj +++ b/Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj @@ -4,6 +4,7 @@ net9.0 $(TargetFrameworks);net472 false + $(NoWarn);NU1507 diff --git a/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj b/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj index 7db90bf78..03558e448 100644 --- a/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj +++ b/Jint.Tests.PublicInterface/Jint.Tests.PublicInterface.csproj @@ -5,6 +5,7 @@ $(TargetFrameworks);net472 false 612 + $(NoWarn);NU1507 true diff --git a/Jint.Tests.Test262/Jint.Tests.Test262.csproj b/Jint.Tests.Test262/Jint.Tests.Test262.csproj index 494477487..2323f9d9d 100644 --- a/Jint.Tests.Test262/Jint.Tests.Test262.csproj +++ b/Jint.Tests.Test262/Jint.Tests.Test262.csproj @@ -6,6 +6,7 @@ true false $(NoWarn);CS8002 + $(NoWarn);NU1507 Generated diff --git a/Jint.Tests/Jint.Tests.csproj b/Jint.Tests/Jint.Tests.csproj index 387212c06..18706619c 100644 --- a/Jint.Tests/Jint.Tests.csproj +++ b/Jint.Tests/Jint.Tests.csproj @@ -7,6 +7,7 @@ true false $(NoWarn);612 + $(NoWarn);NU1507 diff --git a/Jint/Jint.csproj b/Jint/Jint.csproj index b738a4afd..35f7479be 100644 --- a/Jint/Jint.csproj +++ b/Jint/Jint.csproj @@ -16,6 +16,7 @@ README.md $(NoWarn);1591 + $(NoWarn);NU1507 true From a68829c31a980c138aca195168c399cc4c83faab Mon Sep 17 00:00:00 2001 From: Kemal BAYTEKIN Date: Wed, 29 Jan 2025 17:31:21 +0300 Subject: [PATCH 2/4] Engine InvokeAsync is implemented for task/valuetask structure --- Directory.Packages.props | 1 + Jint/Engine.Async.cs | 126 ++++++++++++++++ Jint/Jint.csproj | 3 +- Jint/JsValueExtensions.cs | 25 +++- Jint/Native/Function/BindFunction.cs | 5 + Jint/Native/Function/Function.Async.cs | 13 ++ Jint/Native/Function/ScriptFunction.cs | 54 +++++++ Jint/Native/ICallable.cs | 1 + Jint/Native/JsPromise.cs | 7 +- Jint/Native/JsProxy.cs | 5 + Jint/Native/Object/ObjectConstructor.cs | 10 +- Jint/Runtime/Interop/NamespaceReference.cs | 5 + .../Expressions/ExpressionCache.cs | 78 ++++++++++ .../Expressions/JintAssignmentExpression.cs | 40 +++++ .../Expressions/JintAwaitExpression.cs | 31 ++++ .../Expressions/JintBinaryExpression.cs | 8 +- .../Expressions/JintCallExpression.cs | 138 ++++++++++++++++++ .../Interpreter/Expressions/JintExpression.cs | 33 ++++- .../Interpreter/JintFunctionDefinition.cs | 97 ++++++++++-- Jint/Runtime/Interpreter/JintStatementList.cs | 83 ++++++++++- .../Statements/JintExpressionStatement.cs | 9 ++ .../Statements/JintReturnStatement.cs | 6 + .../Interpreter/Statements/JintStatement.cs | 22 ++- .../Statements/JintVariableDeclaration.cs | 67 ++++++++- 24 files changed, 834 insertions(+), 33 deletions(-) create mode 100644 Jint/Engine.Async.cs create mode 100644 Jint/Native/Function/Function.Async.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 917e1a124..37e06331e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,6 +25,7 @@ + diff --git a/Jint/Engine.Async.cs b/Jint/Engine.Async.cs new file mode 100644 index 000000000..e9a5488de --- /dev/null +++ b/Jint/Engine.Async.cs @@ -0,0 +1,126 @@ +using Jint.Native; +using Jint.Native.Function; +using Jint.Runtime; +using Jint.Runtime.Interpreter; + +namespace Jint; + +public partial class Engine +{ + /// + /// Invoke the current value as function. + /// + /// The name of the function to call. + /// The arguments of the function call. + /// The value returned by the function call. + public Task InvokeAsync(string propertyName, params object?[] arguments) + { + return InvokeAsync(propertyName, thisObj: null, arguments); + } + + /// + /// Invoke the current value as function. + /// + /// The name of the function to call. + /// The this value inside the function call. + /// The arguments of the function call. + /// The value returned by the function call. + public Task InvokeAsync(string propertyName, object? thisObj, object?[] arguments) + { + var value = GetValue(propertyName); + + return InvokeAsync(value, thisObj, arguments); + } + + /// + /// Invoke the current value as function. + /// + /// The function to call. + /// The arguments of the function call. + /// The value returned by the function call. + public Task InvokeAsync(JsValue value, params object?[] arguments) + { + return InvokeAsync(value, thisObj: null, arguments); + } + + /// + /// Invoke the current value as function. + /// + /// The function to call. + /// The this value inside the function call. + /// The arguments of the function call. + /// The value returned by the function call. + public async Task InvokeAsync(JsValue value, object? thisObj, object?[] arguments) + { + var callable = value as ICallable; + if (callable is null) + { + ExceptionHelper.ThrowJavaScriptException(Realm.Intrinsics.TypeError, "Can only invoke functions"); + } + + async Task DoInvokeAsync() + { + var items = _jsValueArrayPool.RentArray(arguments.Length); + for (var i = 0; i < arguments.Length; ++i) + { + items[i] = JsValue.FromObject(this, arguments[i]); + } + + // ensure logic is in sync between Call, Construct, engine.Invoke and JintCallExpression! + JsValue result; + var thisObject = JsValue.FromObject(this, thisObj); + if (callable is Function functionInstance) + { + var callStack = CallStack; + callStack.Push(functionInstance, expression: null, ExecutionContext); + try + { + result = await functionInstance.CallAsync(thisObject, items).ConfigureAwait(false); + } + finally + { + // if call stack was reset due to recursive call to engine or similar, we might not have it anymore + if (callStack.Count > 0) + { + callStack.Pop(); + } + } + } + else + { + result = await callable.CallAsync(thisObject, items).ConfigureAwait(false); + } + + _jsValueArrayPool.ReturnArray(items); + return result; + } + + return await ExecuteWithConstraintsAsync(Options.Strict, DoInvokeAsync).ConfigureAwait(false); + } + + internal async Task ExecuteWithConstraintsAsync(bool strict, Func> callback) + { + ResetConstraints(); + + var ownsContext = _activeEvaluationContext is null; + _activeEvaluationContext ??= new EvaluationContext(this); + + try + { + using (new StrictModeScope(strict)) + { + return await callback().ConfigureAwait(false); + } + } + finally + { + if (ownsContext) + { + _activeEvaluationContext = null!; + } + ResetConstraints(); + _agent.ClearKeptObjects(); + } + } + +} diff --git a/Jint/Jint.csproj b/Jint/Jint.csproj index 35f7479be..267cbac91 100644 --- a/Jint/Jint.csproj +++ b/Jint/Jint.csproj @@ -34,12 +34,13 @@ - + + diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs index 62112eed3..1be9cce2e 100644 --- a/Jint/JsValueExtensions.cs +++ b/Jint/JsValueExtensions.cs @@ -646,13 +646,34 @@ private static JsValue ThrowNotObject(JsValue value) /// value to unwrap /// inner value if Promise the value itself otherwise public static JsValue UnwrapIfPromise(this JsValue value) + { + if (value is JsPromise promise) + { + return UnwrapIfPromiseAsync(value).GetAwaiter().GetResult(); + } + + return value; + } + + /// + /// If the value is a Promise + /// 1. If "Fulfilled" returns the value it was fulfilled with + /// 2. If "Rejected" throws "PromiseRejectedException" with the rejection reason + /// 3. If "Pending" throws "InvalidOperationException". Should be called only in "Settled" state + /// Else + /// returns the value intact + /// + /// value to unwrap + /// inner value if Promise the value itself otherwise + public static async Task UnwrapIfPromiseAsync(this JsValue value) { if (value is JsPromise promise) { var engine = promise.Engine; - var completedEvent = promise.CompletedEvent; + var completedEventAsync = promise.CompletedEventAsync; engine.RunAvailableContinuations(); - completedEvent.Wait(); + await completedEventAsync.Task.ConfigureAwait(false); + switch (promise.State) { case PromiseState.Pending: diff --git a/Jint/Native/Function/BindFunction.cs b/Jint/Native/Function/BindFunction.cs index 6aea0a576..1b91bbd26 100644 --- a/Jint/Native/Function/BindFunction.cs +++ b/Jint/Native/Function/BindFunction.cs @@ -55,6 +55,11 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) return value; } + Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return Task.FromResult(this.Call(thisObject, arguments)); + } + ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget) { var target = BoundTargetFunction as IConstructor; diff --git a/Jint/Native/Function/Function.Async.cs b/Jint/Native/Function/Function.Async.cs new file mode 100644 index 000000000..6e51a58a2 --- /dev/null +++ b/Jint/Native/Function/Function.Async.cs @@ -0,0 +1,13 @@ +using System.Diagnostics; + +namespace Jint.Native.Function; + +[DebuggerDisplay("{ToString(),nq}")] +#pragma warning disable MA0049 +public abstract partial class Function +{ + protected internal virtual Task CallAsync(JsValue thisObject, JsValue[] arguments) => Task.FromResult(Call(thisObject, arguments)); + + Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) => CallAsync(thisObject, arguments); + +} diff --git a/Jint/Native/Function/ScriptFunction.cs b/Jint/Native/Function/ScriptFunction.cs index 9c07c9891..f669a6dc0 100644 --- a/Jint/Native/Function/ScriptFunction.cs +++ b/Jint/Native/Function/ScriptFunction.cs @@ -107,6 +107,60 @@ protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments } } + /// + /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist + /// + protected internal override async Task CallAsync(JsValue thisObject, JsValue[] arguments) + { + var strict = _functionDefinition!.Strict || _thisMode == FunctionThisMode.Strict; + using (new StrictModeScope(strict, true)) + { + try + { + ref readonly var calleeContext = ref PrepareForOrdinaryCall(Undefined); + + if (_isClassConstructor) + { + ExceptionHelper.ThrowTypeError(calleeContext.Realm, $"Class constructor {_functionDefinition.Name} cannot be invoked without 'new'"); + } + + OrdinaryCallBindThis(calleeContext, thisObject); + + // actual call + var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine); + + var result = await _functionDefinition.EvaluateBodyAsync(context, this, arguments).ConfigureAwait(false); + + if (result.Type == CompletionType.Throw) + { + ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result); + } + + // The DebugHandler needs the current execution context before the return for stepping through the return point + if (context.DebugMode) + { + // We don't have a statement, but we still need a Location for debuggers. DebugHandler will infer one from + // the function body: + _engine.Debugger.OnReturnPoint( + _functionDefinition.Function.Body, + result.Type == CompletionType.Normal ? Undefined : result.Value + ); + } + + if (result.Type == CompletionType.Return) + { + return result.Value; + } + } + finally + { + _engine.LeaveExecutionContext(); + } + + return Undefined; + } + } + internal override bool IsConstructor { get diff --git a/Jint/Native/ICallable.cs b/Jint/Native/ICallable.cs index e69d11109..48cb4d3f1 100644 --- a/Jint/Native/ICallable.cs +++ b/Jint/Native/ICallable.cs @@ -3,4 +3,5 @@ namespace Jint.Native; internal interface ICallable { JsValue Call(JsValue thisObject, params JsValue[] arguments); + Task CallAsync(JsValue thisObject, params JsValue[] arguments); } diff --git a/Jint/Native/JsPromise.cs b/Jint/Native/JsPromise.cs index d6f1473cb..e7d130147 100644 --- a/Jint/Native/JsPromise.cs +++ b/Jint/Native/JsPromise.cs @@ -1,4 +1,3 @@ -using System.Threading; using Jint.Native.Object; using Jint.Native.Promise; using Jint.Runtime; @@ -13,7 +12,7 @@ internal sealed class JsPromise : ObjectInstance // valid only in settled state (Fulfilled or Rejected) internal JsValue Value { get; private set; } = null!; - internal ManualResetEventSlim CompletedEvent { get; } = new(); + internal TaskCompletionSource CompletedEventAsync { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); internal List PromiseRejectReactions = new(); internal List PromiseFulfillReactions = new(); @@ -128,7 +127,7 @@ private JsValue RejectPromise(JsValue reason) var reactions = PromiseRejectReactions; PromiseRejectReactions = new List(); PromiseFulfillReactions.Clear(); - CompletedEvent.Set(); + CompletedEventAsync.TrySetResult(false); // Note that this part is skipped because there is no tracking yet // 7. If promise.[[PromiseIsHandled]] is false, perform HostPromiseRejectionTracker(promise, "reject"). @@ -148,7 +147,7 @@ private JsValue FulfillPromise(JsValue result) var reactions = PromiseFulfillReactions; PromiseFulfillReactions = new List(); PromiseRejectReactions.Clear(); - CompletedEvent.Set(); + CompletedEventAsync.TrySetResult(true); return PromiseOperations.TriggerPromiseReactions(_engine, reactions, result); } diff --git a/Jint/Native/JsProxy.cs b/Jint/Native/JsProxy.cs index 9446f31c8..f1d5aeadb 100644 --- a/Jint/Native/JsProxy.cs +++ b/Jint/Native/JsProxy.cs @@ -63,6 +63,11 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) return callable.Call(thisObject, arguments); } + Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return Task.FromResult(this.Call(thisObject, arguments)); + } + /// /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget /// diff --git a/Jint/Native/Object/ObjectConstructor.cs b/Jint/Native/Object/ObjectConstructor.cs index 92e5a2c53..6ed8ae35e 100644 --- a/Jint/Native/Object/ObjectConstructor.cs +++ b/Jint/Native/Object/ObjectConstructor.cs @@ -1,6 +1,5 @@ #pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of constructor methods return JsValue -using Jint.Collections; using Jint.Native.Iterator; using Jint.Runtime; using Jint.Runtime.Descriptors; @@ -139,7 +138,7 @@ protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments return Construct(arguments); } - if(arguments[0].IsNullOrUndefined()) + if (arguments[0].IsNullOrUndefined()) { return Construct(arguments); } @@ -187,7 +186,7 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget) internal ObjectInstance Construct(int propertyCount) { var obj = new JsObject(_engine); - obj.SetProperties(propertyCount > 0 ? new PropertyDictionary(propertyCount, checkExistingKeys: true) : null); + obj.SetProperties(propertyCount > 0 ? new PropertyDictionary(propertyCount, checkExistingKeys: true) : null); return obj; } @@ -565,5 +564,10 @@ public JsValue Call(JsValue thisObject, params JsValue[] arguments) return Undefined; } + + Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return Task.FromResult(this.Call(thisObject, arguments)); + } } } diff --git a/Jint/Runtime/Interop/NamespaceReference.cs b/Jint/Runtime/Interop/NamespaceReference.cs index 96da128bc..75dc5e0a3 100644 --- a/Jint/Runtime/Interop/NamespaceReference.cs +++ b/Jint/Runtime/Interop/NamespaceReference.cs @@ -73,6 +73,11 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) } } + Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return Task.FromResult(this.Call(thisObject, arguments)); + } + public override JsValue Get(JsValue property, JsValue receiver) { var newPath = string.IsNullOrEmpty(_path) diff --git a/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs b/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs index 6b854df18..6e6803664 100644 --- a/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs +++ b/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs @@ -80,6 +80,29 @@ public JsValue[] ArgumentListEvaluation(EvaluationContext context, out bool rent return arguments; } + public async Task<(JsValue[], bool rented)> ArgumentListEvaluationAsync(EvaluationContext context) + { + var rented = false; + if (_fullyCached) + { + return (Unsafe.As(_expressions), rented); + } + + if (HasSpreads) + { + var args = new List(_expressions.Length); + await BuildArgumentsWithSpreadsAsync(context, args).ConfigureAwait(false); + return (args.ToArray(), rented); + } + + var arguments = context.Engine._jsValueArrayPool.RentArray(_expressions.Length); + rented = true; + + await BuildArgumentsAsync(context, arguments).ConfigureAwait(false); + + return (arguments, rented); + } + internal void BuildArguments(EvaluationContext context, JsValue[] targetArray) { var expressions = _expressions; @@ -89,12 +112,27 @@ internal void BuildArguments(EvaluationContext context, JsValue[] targetArray) } } + internal async Task BuildArgumentsAsync(EvaluationContext context, JsValue[] targetArray) + { + var expressions = _expressions; + for (uint i = 0; i < (uint) expressions.Length; i++) + { + targetArray[i] = await GetValueAsync(context, expressions[i])!.ConfigureAwait(false); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public JsValue GetValue(EvaluationContext context, int index) { return GetValue(context, _expressions[index]); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Task GetValueAsync(EvaluationContext context, int index) + { + return GetValueAsync(context, _expressions[index]); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static JsValue GetValue(EvaluationContext context, object? value) { @@ -105,6 +143,16 @@ private static JsValue GetValue(EvaluationContext context, object? value) }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static async Task GetValueAsync(EvaluationContext context, object? value) + { + return value switch + { + JintExpression expression => (await expression.GetValueAsync(context).ConfigureAwait(false)).Clone(), + _ => (JsValue) value!, + }; + } + public bool IsAnonymousFunctionDefinition(int index) { var expressions = _expressions; @@ -170,6 +218,36 @@ internal void BuildArgumentsWithSpreads(EvaluationContext context, List } } + internal async Task BuildArgumentsWithSpreadsAsync(EvaluationContext context, List target) + { + foreach (var expression in _expressions) + { + if (expression is JintSpreadExpression jse) + { + jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator); + // optimize for array unless someone has touched the iterator + if (objectInstance is JsArray { HasOriginalIterator: true } ai) + { + var length = ai.GetLength(); + for (uint j = 0; j < length; ++j) + { + ai.TryGetValue(j, out var value); + target.Add(value); + } + } + else + { + var protocol = new ArraySpreadProtocol(context.Engine, target, iterator!); + protocol.Execute(); + } + } + else + { + target.Add(await GetValueAsync(context, expression)!.ConfigureAwait(false)); + } + } + } + private sealed class ArraySpreadProtocol : IteratorProtocol { private readonly List _instance; diff --git a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs index e85f6ad3e..a18698d24 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs @@ -462,5 +462,45 @@ private JsValue SetValue(EvaluationContext context) return null; } + + internal static async Task AssignToIdentifierAsync( + EvaluationContext context, + JintIdentifierExpression left, + JintExpression right, + bool hasEvalOrArguments) + { + var engine = context.Engine; + var env = engine.ExecutionContext.LexicalEnvironment; + var strict = StrictModeScope.IsStrictModeCode; + var identifier = left.Identifier; + if (JintEnvironment.TryGetIdentifierEnvironmentWithBinding( + env, + identifier, + out var environmentRecord)) + { + if (strict && hasEvalOrArguments && identifier.Key != KnownKeys.Eval) + { + ExceptionHelper.ThrowSyntaxError(engine.Realm, "Invalid assignment target"); + } + + var completion = await right.GetValueAsync(context).ConfigureAwait(false); + if (context.IsAbrupt()) + { + return completion; + } + + var rval = completion.Clone(); + + if (right._expression.IsFunctionDefinition()) + { + ((Function) rval).SetFunctionName(identifier.Value); + } + + environmentRecord.SetMutableBinding(identifier, rval, strict); + return rval; + } + + return null; + } } } diff --git a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs index 77622abb5..bd7b23544 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs @@ -42,4 +42,35 @@ protected override object EvaluateInternal(EvaluationContext context) return null; } } + + protected override async Task EvaluateInternalAsync(EvaluationContext context) + { + if (!_initialized) + { + _awaitExpression = Build(((AwaitExpression) _expression).Argument); + _initialized = true; + } + + var engine = context.Engine; + var asyncContext = engine.ExecutionContext; + + try + { + var value = await _awaitExpression.GetValueAsync(context).ConfigureAwait(false); + + if (value is not JsPromise) + { + var promiseInstance = new JsPromise(engine); + promiseInstance.Resolve(value); + value = promiseInstance; + } + + return await value.UnwrapIfPromiseAsync().ConfigureAwait(false); + } + catch (PromiseRejectedException e) + { + ExceptionHelper.ThrowJavaScriptException(engine, e.RejectedValue, _expression.Location); + return null; + } + } } diff --git a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs index ea8a50bf1..11b04c1b9 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs @@ -298,7 +298,7 @@ protected override object EvaluateInternal(EvaluationContext context) if (AreIntegerOperands(left, right)) { - return JsNumber.Create((long)left.AsInteger() + right.AsInteger()); + return JsNumber.Create((long) left.AsInteger() + right.AsInteger()); } var lprim = TypeConverter.ToPrimitive(left); @@ -308,7 +308,7 @@ protected override object EvaluateInternal(EvaluationContext context) { result = JsString.Create(TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim)); } - else if (AreNonBigIntOperands(left,right)) + else if (AreNonBigIntOperands(left, right)) { result = JsNumber.Create(TypeConverter.ToNumber(lprim) + TypeConverter.ToNumber(rprim)); } @@ -347,7 +347,7 @@ protected override object EvaluateInternal(EvaluationContext context) if (AreIntegerOperands(left, right)) { - number = JsNumber.Create((long)left.AsInteger() - right.AsInteger()); + number = JsNumber.Create((long) left.AsInteger() - right.AsInteger()); } else if (AreNonBigIntOperands(left, right)) { @@ -525,7 +525,7 @@ protected override object EvaluateInternal(EvaluationContext context) var right = TypeConverter.ToNumeric(rightReference); JsValue result; - if (AreNonBigIntOperands(left,right)) + if (AreNonBigIntOperands(left, right)) { // validation var baseNumber = (JsNumber) left; diff --git a/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs index 2c14eaf72..0e838417e 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs @@ -164,6 +164,144 @@ protected override object EvaluateInternal(EvaluationContext context) return result; } + protected override async Task EvaluateInternalAsync(EvaluationContext context) + { + if (!_initialized) + { + Initialize(context); + _initialized = true; + } + + if (!context.Engine._stackGuard.TryEnterOnCurrentStack()) + { + return StackGuard.RunOnEmptyStack(EvaluateInternal, context); + } + + if (_calleeExpression._expression.Type == NodeType.Super) + { + return SuperCall(context); + } + + // https://tc39.es/ecma262/#sec-function-calls + + var reference = await _calleeExpression.EvaluateAsync(context).ConfigureAwait(false); + + if (ReferenceEquals(reference, JsValue.Undefined)) + { + return JsValue.Undefined; + } + + var engine = context.Engine; + var func = engine.GetValue(reference, false); + + if (func.IsNullOrUndefined() && _expression.IsOptional()) + { + return JsValue.Undefined; + } + + var referenceRecord = reference as Reference; + if (ReferenceEquals(func, engine.Realm.Intrinsics.Eval) + && referenceRecord != null + && !referenceRecord.IsPropertyReference + && CommonProperties.Eval.Equals(referenceRecord.ReferencedName)) + { + return HandleEval(context, func, engine, referenceRecord); + } + + var thisCall = (CallExpression) _expression; + var tailCall = IsInTailPosition(thisCall); + + // https://tc39.es/ecma262/#sec-evaluatecall + + JsValue thisObject; + if (referenceRecord is not null) + { + if (referenceRecord.IsPropertyReference) + { + thisObject = referenceRecord.ThisValue; + } + else + { + var baseValue = referenceRecord.Base; + + // deviation from the spec to support null-propagation helper + if (baseValue.IsNullOrUndefined() + && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value)) + { + thisObject = value; + } + else + { + var refEnv = (Environment) baseValue; + thisObject = refEnv.WithBaseObject(); + } + } + } + else + { + thisObject = JsValue.Undefined; + } + + var (arguments, rented) = await this._arguments.ArgumentListEvaluationAsync(context).ConfigureAwait(false); + + if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func)) + { + ThrowMemberIsNotFunction(referenceRecord, reference, engine); + } + + var callable = func as ICallable; + if (callable is null) + { + ThrowReferenceNotFunction(referenceRecord, reference, engine); + } + + if (tailCall) + { + // TODO tail call + // PrepareForTailCall(); + } + + // ensure logic is in sync between Call, Construct and JintCallExpression! + + JsValue result; + if (callable is Function functionInstance) + { + var callStack = engine.CallStack; + var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext); + + if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth) + { + // automatically pops the current element as it was never reached + ExceptionHelper.ThrowRecursionDepthOverflowException(callStack); + } + + try + { + result = await functionInstance.CallAsync(thisObject, arguments).ConfigureAwait(false); + } + finally + { + // if call stack was reset due to recursive call to engine or similar, we might not have it anymore + if (callStack.Count > 0) + { + callStack.Pop(); + } + } + } + else + { + result = await callable.CallAsync(thisObject, arguments).ConfigureAwait(false); + } + + if (rented) + { + engine._jsValueArrayPool.ReturnArray(arguments); + } + + engine._referencePool.Return(referenceRecord); + return result; + } + [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowReferenceNotFunction(Reference? referenceRecord1, object reference, Engine engine) diff --git a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs index 3a7477bda..24aba5991 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs @@ -1,7 +1,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using Jint.Native; -using Jint.Native.Iterator; using Jint.Native.Number; namespace Jint.Runtime.Interpreter.Expressions; @@ -32,6 +31,23 @@ public virtual JsValue GetValue(EvaluationContext context) return context.Engine.GetValue(reference, returnReferenceToPool: true); } + /// + /// Resolves the underlying value for this expression. + /// By default uses the Engine for resolving. + /// + /// + /// + public virtual async Task GetValueAsync(EvaluationContext context) + { + var result = await EvaluateAsync(context).ConfigureAwait(false); + if (result is not Reference reference) + { + return (JsValue) result; + } + + return context.Engine.GetValue(reference, returnReferenceToPool: true); + } + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] public object Evaluate(EvaluationContext context) { @@ -45,6 +61,19 @@ public object Evaluate(EvaluationContext context) return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async Task EvaluateAsync(EvaluationContext context) + { + var oldSyntaxElement = context.LastSyntaxElement; + context.PrepareFor(_expression); + + var result = await EvaluateInternalAsync(context).ConfigureAwait(false); + + context.LastSyntaxElement = oldSyntaxElement; + + return result; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal object EvaluateWithoutNodeTracking(EvaluationContext context) { @@ -53,6 +82,8 @@ internal object EvaluateWithoutNodeTracking(EvaluationContext context) protected abstract object EvaluateInternal(EvaluationContext context); + protected virtual Task EvaluateInternalAsync(EvaluationContext context) => Task.FromResult(EvaluateInternal(context)); + /// /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher. /// diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 08b1bf896..4e093048e 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; @@ -49,13 +48,16 @@ internal Completion EvaluateBody(EvaluationContext context, Function functionObj var jsValues = argumentsList; var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); - AsyncFunctionStart(context, promiseCapability, context => + var t = AsyncFunctionStart(context, promiseCapability, context => { - context.Engine.FunctionDeclarationInstantiation(function, jsValues); + context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); context.RunBeforeExecuteStatementChecks(Function.Body); var jsValue = _bodyExpression.GetValue(context).Clone(); - return new Completion(CompletionType.Return, jsValue, _bodyExpression._expression); + return Task.FromResult(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); }); + + t.Wait(); + result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); } else @@ -80,11 +82,76 @@ internal Completion EvaluateBody(EvaluationContext context, Function functionObj var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); _bodyStatementList ??= new JintStatementList(Function); - AsyncFunctionStart(context, promiseCapability, context => + var t = AsyncFunctionStart(context, promiseCapability, context => { - context.Engine.FunctionDeclarationInstantiation(function, arguments); - return _bodyStatementList.Execute(context); + context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + return Task.FromResult(_bodyStatementList.Execute(context)); }); + + t.Wait(); + + result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); + } + else + { + // https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody + argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + _bodyStatementList ??= new JintStatementList(Function); + result = _bodyStatementList.Execute(context); + } + } + + argumentsInstance?.FunctionWasCalled(); + return result; + } + + /// + /// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody + /// + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + internal async Task EvaluateBodyAsync(EvaluationContext context, Function functionObject, JsValue[] argumentsList) + { + Completion result; + JsArguments? argumentsInstance = null; + if (Function.Expression) + { + // https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody + _bodyExpression ??= JintExpression.Build((Expression) Function.Body); + if (Function.Async) + { + var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); + await AsyncFunctionStart(context, promiseCapability, context => + { + context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + context.RunBeforeExecuteStatementChecks(Function.Body); + var jsValue = _bodyExpression.GetValue(context).Clone(); + return Task.FromResult(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); + }).ConfigureAwait(false); + result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); + } + else + { + argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + context.RunBeforeExecuteStatementChecks(Function.Body); + var jsValue = _bodyExpression.GetValue(context).Clone(); + result = new Completion(CompletionType.Return, jsValue, Function.Body); + } + } + else if (Function.Generator) + { + result = EvaluateGeneratorBody(context, functionObject, argumentsList); + } + else + { + if (Function.Async) + { + var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); + _bodyStatementList ??= new JintStatementList(Function); + await AsyncFunctionStart(context, promiseCapability, async context => + { + context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + return await _bodyStatementList.ExecuteAsync(context).ConfigureAwait(false); + }).ConfigureAwait(false); result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); } else @@ -103,21 +170,21 @@ internal Completion EvaluateBody(EvaluationContext context, Function functionObj /// /// https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start /// - private static void AsyncFunctionStart(EvaluationContext context, PromiseCapability promiseCapability, Func asyncFunctionBody) + private static async Task AsyncFunctionStart(EvaluationContext context, PromiseCapability promiseCapability, Func> asyncFunctionBody) { var runningContext = context.Engine.ExecutionContext; var asyncContext = runningContext; - AsyncBlockStart(context, promiseCapability, asyncFunctionBody, asyncContext); + await AsyncBlockStart(context, promiseCapability, asyncFunctionBody, asyncContext).ConfigureAwait(false); } /// /// https://tc39.es/ecma262/#sec-asyncblockstart /// - private static void AsyncBlockStart( + private static async Task AsyncBlockStart( EvaluationContext context, PromiseCapability promiseCapability, - Func asyncBody, - in ExecutionContext asyncContext) + Func> asyncBody, + ExecutionContext asyncContext) { var runningContext = context.Engine.ExecutionContext; // Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution contxt the following steps will be performed: @@ -125,7 +192,7 @@ private static void AsyncBlockStart( Completion result; try { - result = asyncBody(context); + result = await asyncBody(context).ConfigureAwait(false); } catch (JavaScriptException e) { @@ -168,7 +235,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(); @@ -416,7 +483,7 @@ private static void ProcessParameters( out bool hasArguments) { hasArguments = false; - state.IsSimpleParameterList = true; + state.IsSimpleParameterList = true; var countParameters = true; ref readonly var functionDeclarationParams = ref function.Params; diff --git a/Jint/Runtime/Interpreter/JintStatementList.cs b/Jint/Runtime/Interpreter/JintStatementList.cs index df88426bd..68900aa87 100644 --- a/Jint/Runtime/Interpreter/JintStatementList.cs +++ b/Jint/Runtime/Interpreter/JintStatementList.cs @@ -4,7 +4,6 @@ using Jint.Native.Error; using Jint.Runtime.Environments; using Jint.Runtime.Interpreter.Statements; -using Environment = Jint.Runtime.Environments.Environment; namespace Jint.Runtime.Interpreter; @@ -139,6 +138,88 @@ public Completion Execute(EvaluationContext context) return c.UpdateEmpty(lastValue).UpdateEmpty(JsValue.Undefined); } + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async Task ExecuteAsync(EvaluationContext context) + { + if (!_initialized) + { + Initialize(context); + _initialized = true; + } + + if (_statement is not null) + { + context.LastSyntaxElement = _statement; + context.RunBeforeExecuteStatementChecks(_statement); + } + + Completion c = Completion.Empty(); + Completion sl = c; + + // The value of a StatementList is the value of the last value-producing item in the StatementList + var lastValue = JsEmpty.Instance; + var i = _index; + var temp = _jintStatements!; + try + { + for (; i < (uint) temp.Length; i++) + { + var pair = temp[i]; + + if (pair.Value is null) + { + c = await pair.Statement.ExecuteAsync(context).ConfigureAwait(false); + if (context.Engine._error is not null) + { + c = HandleError(context.Engine, pair.Statement); + break; + } + } + else + { + c = new Completion(CompletionType.Return, pair.Value, pair.Statement._statement); + } + + if (_generator) + { + if (context.Engine.ExecutionContext.Suspended) + { + _index = i + 1; + c = new Completion(CompletionType.Return, c.Value, pair.Statement._statement); + break; + } + } + + if (c.Type != CompletionType.Normal) + { + return c.UpdateEmpty(sl.Value); + } + + sl = c; + if (!c.Value.IsEmpty) + { + lastValue = c.Value; + } + } + } + catch (Exception ex) + { + Reset(); + + if (ex is JintException) + { + c = HandleException(context, ex, temp[i].Statement); + } + else + { + throw; + } + } + + return c.UpdateEmpty(lastValue).UpdateEmpty(JsValue.Undefined); + } + + internal static Completion HandleException(EvaluationContext context, Exception exception, JintStatement? s) { return exception switch diff --git a/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs b/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs index 3a51caf9d..544d3a21f 100644 --- a/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs @@ -27,4 +27,13 @@ protected override Completion ExecuteInternal(EvaluationContext context) return new Completion(context.Completion, value, _statement); } + + protected override async Task ExecuteInternalAsync(EvaluationContext context) + { + var value = _identifierExpression is not null + ? await _identifierExpression.GetValueAsync(context).ConfigureAwait(false) + : await _expression.GetValueAsync(context).ConfigureAwait(false); + + return new Completion(context.Completion, value, _statement); + } } diff --git a/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs b/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs index c26679ae3..55df50636 100644 --- a/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs @@ -26,4 +26,10 @@ protected override Completion ExecuteInternal(EvaluationContext context) var value = _argument is not null ? _argument.GetValue(context).Clone() : JsValue.Undefined; return new Completion(CompletionType.Return, value, _statement); } + + protected override async Task ExecuteInternalAsync(EvaluationContext context) + { + var value = _argument is not null ? (await _argument.GetValueAsync(context).ConfigureAwait(false)).Clone() : JsValue.Undefined; + return new Completion(CompletionType.Return, value, _statement); + } } diff --git a/Jint/Runtime/Interpreter/Statements/JintStatement.cs b/Jint/Runtime/Interpreter/Statements/JintStatement.cs index 94684e362..d5ce5fdad 100644 --- a/Jint/Runtime/Interpreter/Statements/JintStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintStatement.cs @@ -42,6 +42,26 @@ public Completion Execute(EvaluationContext context) return ExecuteInternal(context); } + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async Task ExecuteAsync(EvaluationContext context) + { + if (_statement.Type != NodeType.BlockStatement) + { + context.PrepareFor(_statement); + context.RunBeforeExecuteStatementChecks(_statement); + } + + if (!_initialized) + { + Initialize(context); + _initialized = true; + } + + return await ExecuteInternalAsync(context).ConfigureAwait(false); + } + + protected virtual Task ExecuteInternalAsync(EvaluationContext context) => Task.FromResult(ExecuteInternal(context)); + protected abstract Completion ExecuteInternal(EvaluationContext context); public ref readonly SourceLocation Location => ref _statement.LocationRef; @@ -108,4 +128,4 @@ protected internal static JintStatement Build(Statement statement) return null; } -} \ No newline at end of file +} diff --git a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs index 9f81e3b85..bbaea3bb2 100644 --- a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs +++ b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs @@ -122,4 +122,69 @@ protected override Completion ExecuteInternal(EvaluationContext context) return Completion.Empty(); } -} \ No newline at end of file + + protected override async Task ExecuteInternalAsync(EvaluationContext context) + { + var engine = context.Engine; + foreach (var declaration in _declarations) + { + if (_statement.Kind != VariableDeclarationKind.Var && declaration.Left != null) + { + var lhs = (Reference) (await declaration.Left.EvaluateAsync(context).ConfigureAwait(false)); + var value = JsValue.Undefined; + if (declaration.Init != null) + { + value = (await declaration.Init.GetValueAsync(context).ConfigureAwait(false)).Clone(); + if (declaration.Init._expression.IsFunctionDefinition()) + { + ((Function) value).SetFunctionName(lhs.ReferencedName); + } + } + + lhs.InitializeReferencedBinding(value); + engine._referencePool.Return(lhs); + } + else if (declaration.Init != null) + { + if (declaration.LeftPattern != null) + { + var environment = _statement.Kind != VariableDeclarationKind.Var + ? engine.ExecutionContext.LexicalEnvironment + : null; + + var value = await declaration.Init.GetValueAsync(context).ConfigureAwait(false); + + DestructuringPatternAssignmentExpression.ProcessPatterns( + context, + declaration.LeftPattern, + value, + environment, + checkPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var); + } + else if (declaration.LeftIdentifierExpression == null + || (await JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifierAsync( + context, + declaration.LeftIdentifierExpression, + declaration.Init, + declaration.EvalOrArguments).ConfigureAwait(false)) is null) + { + // slow path + var lhs = (Reference) (await declaration.Left!.EvaluateAsync(context).ConfigureAwait(false)); + lhs.AssertValid(engine.Realm); + + var value = (await declaration.Init.GetValueAsync(context).ConfigureAwait(false)).Clone(); + + if (declaration.Init._expression.IsFunctionDefinition()) + { + ((Function) value).SetFunctionName(lhs.ReferencedName); + } + + engine.PutValue(lhs, value); + engine._referencePool.Return(lhs); + } + } + } + + return Completion.Empty(); + } +} From 9c6a20c6866ef45a3ad2f3b7125d7cbdd7a0bed8 Mon Sep 17 00:00:00 2001 From: Kemal BAYTEKIN Date: Mon, 3 Feb 2025 00:55:27 +0300 Subject: [PATCH 3/4] Returning Task is changed by ValueTask. Duplicate methods reduced to single. --- Directory.Packages.props | 1 + Jint/Engine.Async.cs | 82 +++++++++- Jint/Engine.cs | 98 +----------- Jint/Jint.csproj | 1 + Jint/JsValueExtensions.cs | 50 +++--- Jint/Native/Function/BindFunction.cs | 4 +- Jint/Native/Function/Function.Async.cs | 4 +- Jint/Native/Function/ScriptFunction.cs | 50 +----- Jint/Native/ICallable.cs | 4 +- Jint/Native/JsProxy.cs | 4 +- Jint/Native/Object/ObjectConstructor.cs | 5 +- Jint/Runtime/Interop/NamespaceReference.cs | 4 +- .../Expressions/ExpressionCache.cs | 81 ++-------- .../Expressions/JintAssignmentExpression.cs | 38 +---- .../Expressions/JintAwaitExpression.cs | 30 +--- .../Expressions/JintCallExpression.cs | 142 +----------------- .../Interpreter/Expressions/JintExpression.cs | 29 +--- .../Interpreter/JintFunctionDefinition.cs | 82 +--------- Jint/Runtime/Interpreter/JintStatementList.cs | 82 +--------- .../Statements/JintExpressionStatement.cs | 11 +- .../Statements/JintReturnStatement.cs | 8 +- .../Interpreter/Statements/JintStatement.cs | 21 +-- .../Statements/JintVariableDeclaration.cs | 67 +-------- 23 files changed, 160 insertions(+), 738 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 37e06331e..912e7c868 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,6 +25,7 @@ + diff --git a/Jint/Engine.Async.cs b/Jint/Engine.Async.cs index e9a5488de..310b85d6c 100644 --- a/Jint/Engine.Async.cs +++ b/Jint/Engine.Async.cs @@ -1,7 +1,9 @@ +using System.Runtime.CompilerServices; using Jint.Native; using Jint.Native.Function; using Jint.Runtime; using Jint.Runtime.Interpreter; +using Jint.Runtime.Interpreter.Expressions; namespace Jint; @@ -13,7 +15,7 @@ public partial class Engine /// The name of the function to call. /// The arguments of the function call. /// The value returned by the function call. - public Task InvokeAsync(string propertyName, params object?[] arguments) + public ValueTask InvokeAsync(string propertyName, params object?[] arguments) { return InvokeAsync(propertyName, thisObj: null, arguments); } @@ -25,7 +27,7 @@ public Task InvokeAsync(string propertyName, params object?[] arguments /// The this value inside the function call. /// The arguments of the function call. /// The value returned by the function call. - public Task InvokeAsync(string propertyName, object? thisObj, object?[] arguments) + public ValueTask InvokeAsync(string propertyName, object? thisObj, object?[] arguments) { var value = GetValue(propertyName); @@ -38,7 +40,7 @@ public Task InvokeAsync(string propertyName, object? thisObj, object?[] /// The function to call. /// The arguments of the function call. /// The value returned by the function call. - public Task InvokeAsync(JsValue value, params object?[] arguments) + public ValueTask InvokeAsync(JsValue value, params object?[] arguments) { return InvokeAsync(value, thisObj: null, arguments); } @@ -50,7 +52,7 @@ public Task InvokeAsync(JsValue value, params object?[] arguments) /// The this value inside the function call. /// The arguments of the function call. /// The value returned by the function call. - public async Task InvokeAsync(JsValue value, object? thisObj, object?[] arguments) + public async ValueTask InvokeAsync(JsValue value, object? thisObj, object?[] arguments) { var callable = value as ICallable; if (callable is null) @@ -58,7 +60,7 @@ public async Task InvokeAsync(JsValue value, object? thisObj, object?[] ExceptionHelper.ThrowJavaScriptException(Realm.Intrinsics.TypeError, "Can only invoke functions"); } - async Task DoInvokeAsync() + async ValueTask DoInvokeAsync() { var items = _jsValueArrayPool.RentArray(arguments.Length); for (var i = 0; i < arguments.Length; ++i) @@ -75,7 +77,7 @@ async Task DoInvokeAsync() callStack.Push(functionInstance, expression: null, ExecutionContext); try { - result = await functionInstance.CallAsync(thisObject, items).ConfigureAwait(false); + result = await callable.CallAsync(thisObject, items).ConfigureAwait(false); } finally { @@ -98,7 +100,73 @@ async Task DoInvokeAsync() return await ExecuteWithConstraintsAsync(Options.Strict, DoInvokeAsync).ConfigureAwait(false); } - internal async Task ExecuteWithConstraintsAsync(bool strict, Func> callback) + /// + /// Invokes the callable and returns the resulting object. + /// + /// The callable. + /// Value bound as this. + /// The arguments of the call. + /// The value returned by the call. + public ValueTask CallAsync(JsValue callable, JsValue thisObject, JsValue[] arguments) + { + ValueTask Callback() + { + if (!callable.IsCallable) + { + ExceptionHelper.ThrowArgumentException(callable + " is not callable"); + } + + return CallAsync((ICallable) callable, thisObject, arguments, null); + } + + return ExecuteWithConstraintsAsync(Options.Strict, Callback); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ValueTask CallAsync(ICallable callable, JsValue thisObject, JsValue[] arguments, JintExpression? expression) + { + if (callable is Function functionInstance) + { + return CallAsync(functionInstance, thisObject, arguments, expression); + } + + return callable.CallAsync(thisObject, arguments); + } + + internal async ValueTask CallAsync( + Function function, + JsValue thisObject, + JsValue[] arguments, + JintExpression? expression) + { + // ensure logic is in sync between Call, Construct, engine.Invoke and JintCallExpression! + + var recursionDepth = CallStack.Push(function, expression, ExecutionContext); + + if (recursionDepth > Options.Constraints.MaxRecursionDepth) + { + // automatically pops the current element as it was never reached + ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack); + } + + JsValue result; + try + { + result = await function.CallAsync(thisObject, arguments).ConfigureAwait(false); + } + finally + { + // if call stack was reset due to recursive call to engine or similar, we might not have it anymore + if (CallStack.Count > 0) + { + CallStack.Pop(); + } + } + + return result; + } + + internal async ValueTask ExecuteWithConstraintsAsync(bool strict, Func> callback) { ResetConstraints(); diff --git a/Jint/Engine.cs b/Jint/Engine.cs index 651ced817..1cb0f7818 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -1,5 +1,5 @@ -using System.Diagnostics; -using System.Collections.Concurrent; +using System.Collections.Concurrent; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Jint.Collections; @@ -759,50 +759,7 @@ public JsValue Invoke(JsValue value, params object?[] arguments) /// The value returned by the function call. public JsValue Invoke(JsValue value, object? thisObj, object?[] arguments) { - var callable = value as ICallable; - if (callable is null) - { - ExceptionHelper.ThrowJavaScriptException(Realm.Intrinsics.TypeError, "Can only invoke functions"); - } - - JsValue DoInvoke() - { - var items = _jsValueArrayPool.RentArray(arguments.Length); - for (var i = 0; i < arguments.Length; ++i) - { - items[i] = JsValue.FromObject(this, arguments[i]); - } - - // ensure logic is in sync between Call, Construct, engine.Invoke and JintCallExpression! - JsValue result; - var thisObject = JsValue.FromObject(this, thisObj); - if (callable is Function functionInstance) - { - var callStack = CallStack; - callStack.Push(functionInstance, expression: null, ExecutionContext); - try - { - result = functionInstance.Call(thisObject, items); - } - finally - { - // if call stack was reset due to recursive call to engine or similar, we might not have it anymore - if (callStack.Count > 0) - { - callStack.Pop(); - } - } - } - else - { - result = callable.Call(thisObject, items); - } - - _jsValueArrayPool.ReturnArray(items); - return result; - } - - return ExecuteWithConstraints(Options.Strict, DoInvoke); + return InvokeAsync(value, thisObj, arguments).Preserve().GetAwaiter().GetResult(); } internal T ExecuteWithConstraints(bool strict, Func callback) @@ -1448,28 +1405,7 @@ public JsValue Call(JsValue callable, params JsValue[] arguments) /// The value returned by the call. public JsValue Call(JsValue callable, JsValue thisObject, JsValue[] arguments) { - JsValue Callback() - { - if (!callable.IsCallable) - { - ExceptionHelper.ThrowArgumentException(callable + " is not callable"); - } - - return Call((ICallable) callable, thisObject, arguments, null); - } - - return ExecuteWithConstraints(Options.Strict, Callback); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal JsValue Call(ICallable callable, JsValue thisObject, JsValue[] arguments, JintExpression? expression) - { - if (callable is Function functionInstance) - { - return Call(functionInstance, thisObject, arguments, expression); - } - - return callable.Call(thisObject, arguments); + return CallAsync(callable, thisObject, arguments).Preserve().GetAwaiter().GetResult(); } /// @@ -1528,31 +1464,7 @@ internal JsValue Call( JsValue[] arguments, JintExpression? expression) { - // ensure logic is in sync between Call, Construct, engine.Invoke and JintCallExpression! - - var recursionDepth = CallStack.Push(function, expression, ExecutionContext); - - if (recursionDepth > Options.Constraints.MaxRecursionDepth) - { - // automatically pops the current element as it was never reached - ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack); - } - - JsValue result; - try - { - result = function.Call(thisObject, arguments); - } - finally - { - // if call stack was reset due to recursive call to engine or similar, we might not have it anymore - if (CallStack.Count > 0) - { - CallStack.Pop(); - } - } - - return result; + return CallAsync(function, thisObject, arguments, expression).Preserve().GetAwaiter().GetResult(); } private ObjectInstance Construct( diff --git a/Jint/Jint.csproj b/Jint/Jint.csproj index 267cbac91..0470433f7 100644 --- a/Jint/Jint.csproj +++ b/Jint/Jint.csproj @@ -40,6 +40,7 @@ + diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs index 1be9cce2e..38b1506a4 100644 --- a/Jint/JsValueExtensions.cs +++ b/Jint/JsValueExtensions.cs @@ -649,7 +649,7 @@ public static JsValue UnwrapIfPromise(this JsValue value) { if (value is JsPromise promise) { - return UnwrapIfPromiseAsync(value).GetAwaiter().GetResult(); + return UnwrapIfPromiseAsync(value).Preserve().GetAwaiter().GetResult(); } return value; @@ -665,32 +665,30 @@ public static JsValue UnwrapIfPromise(this JsValue value) /// /// value to unwrap /// inner value if Promise the value itself otherwise - public static async Task UnwrapIfPromiseAsync(this JsValue value) - { - if (value is JsPromise promise) - { - var engine = promise.Engine; - var completedEventAsync = promise.CompletedEventAsync; - engine.RunAvailableContinuations(); - await completedEventAsync.Task.ConfigureAwait(false); - - switch (promise.State) - { - case PromiseState.Pending: - ExceptionHelper.ThrowInvalidOperationException("'UnwrapIfPromise' called before Promise was settled"); - return null; - case PromiseState.Fulfilled: - return promise.Value; - case PromiseState.Rejected: - ExceptionHelper.ThrowPromiseRejectedException(promise.Value); - return null; - default: - ExceptionHelper.ThrowArgumentOutOfRangeException(); - return null; - } + public static async ValueTask UnwrapIfPromiseAsync(this JsValue value) + { + if (value is not JsPromise promise) + return value; + + var engine = promise.Engine; + var completedEventAsync = promise.CompletedEventAsync; + engine.RunAvailableContinuations(); + await completedEventAsync.Task.ConfigureAwait(false); + + switch (promise.State) + { + case PromiseState.Pending: + ExceptionHelper.ThrowInvalidOperationException("'UnwrapIfPromise' called before Promise was settled"); + return null; + case PromiseState.Fulfilled: + return promise.Value; + case PromiseState.Rejected: + ExceptionHelper.ThrowPromiseRejectedException(promise.Value); + return null; + default: + ExceptionHelper.ThrowArgumentOutOfRangeException(); + return null; } - - return value; } [MethodImpl(MethodImplOptions.NoInlining)] diff --git a/Jint/Native/Function/BindFunction.cs b/Jint/Native/Function/BindFunction.cs index 1b91bbd26..9874a1cb7 100644 --- a/Jint/Native/Function/BindFunction.cs +++ b/Jint/Native/Function/BindFunction.cs @@ -55,9 +55,9 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) return value; } - Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) { - return Task.FromResult(this.Call(thisObject, arguments)); + return new ValueTask(this.Call(thisObject, arguments)); } ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget) diff --git a/Jint/Native/Function/Function.Async.cs b/Jint/Native/Function/Function.Async.cs index 6e51a58a2..199638f5a 100644 --- a/Jint/Native/Function/Function.Async.cs +++ b/Jint/Native/Function/Function.Async.cs @@ -6,8 +6,8 @@ namespace Jint.Native.Function; #pragma warning disable MA0049 public abstract partial class Function { - protected internal virtual Task CallAsync(JsValue thisObject, JsValue[] arguments) => Task.FromResult(Call(thisObject, arguments)); + protected internal virtual ValueTask CallAsync(JsValue thisObject, JsValue[] arguments) => new ValueTask(Call(thisObject, arguments)); - Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) => CallAsync(thisObject, arguments); + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) => CallAsync(thisObject, arguments); } diff --git a/Jint/Native/Function/ScriptFunction.cs b/Jint/Native/Function/ScriptFunction.cs index f669a6dc0..22159fc67 100644 --- a/Jint/Native/Function/ScriptFunction.cs +++ b/Jint/Native/Function/ScriptFunction.cs @@ -58,59 +58,13 @@ internal ScriptFunction( /// protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) { - var strict = _functionDefinition!.Strict || _thisMode == FunctionThisMode.Strict; - using (new StrictModeScope(strict, true)) - { - try - { - ref readonly var calleeContext = ref PrepareForOrdinaryCall(Undefined); - - if (_isClassConstructor) - { - ExceptionHelper.ThrowTypeError(calleeContext.Realm, $"Class constructor {_functionDefinition.Name} cannot be invoked without 'new'"); - } - - OrdinaryCallBindThis(calleeContext, thisObject); - - // actual call - var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine); - - var result = _functionDefinition.EvaluateBody(context, this, arguments); - - if (result.Type == CompletionType.Throw) - { - ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result); - } - - // The DebugHandler needs the current execution context before the return for stepping through the return point - if (context.DebugMode) - { - // We don't have a statement, but we still need a Location for debuggers. DebugHandler will infer one from - // the function body: - _engine.Debugger.OnReturnPoint( - _functionDefinition.Function.Body, - result.Type == CompletionType.Normal ? Undefined : result.Value - ); - } - - if (result.Type == CompletionType.Return) - { - return result.Value; - } - } - finally - { - _engine.LeaveExecutionContext(); - } - - return Undefined; - } + return CallAsync(thisObject, arguments).Preserve().GetAwaiter().GetResult(); } /// /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist /// - protected internal override async Task CallAsync(JsValue thisObject, JsValue[] arguments) + protected internal override async ValueTask CallAsync(JsValue thisObject, JsValue[] arguments) { var strict = _functionDefinition!.Strict || _thisMode == FunctionThisMode.Strict; using (new StrictModeScope(strict, true)) diff --git a/Jint/Native/ICallable.cs b/Jint/Native/ICallable.cs index 48cb4d3f1..16691f2df 100644 --- a/Jint/Native/ICallable.cs +++ b/Jint/Native/ICallable.cs @@ -3,5 +3,7 @@ namespace Jint.Native; internal interface ICallable { JsValue Call(JsValue thisObject, params JsValue[] arguments); - Task CallAsync(JsValue thisObject, params JsValue[] arguments); + //Task CallAsync(JsValue thisObject, params JsValue[] arguments); + + ValueTask CallAsync(JsValue thisObject, params JsValue[] arguments); } diff --git a/Jint/Native/JsProxy.cs b/Jint/Native/JsProxy.cs index f1d5aeadb..c930ff30d 100644 --- a/Jint/Native/JsProxy.cs +++ b/Jint/Native/JsProxy.cs @@ -63,9 +63,9 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) return callable.Call(thisObject, arguments); } - Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) { - return Task.FromResult(this.Call(thisObject, arguments)); + return new ValueTask(this.Call(thisObject, arguments)); } /// diff --git a/Jint/Native/Object/ObjectConstructor.cs b/Jint/Native/Object/ObjectConstructor.cs index 6ed8ae35e..e094ae751 100644 --- a/Jint/Native/Object/ObjectConstructor.cs +++ b/Jint/Native/Object/ObjectConstructor.cs @@ -564,10 +564,9 @@ public JsValue Call(JsValue thisObject, params JsValue[] arguments) return Undefined; } - - Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) { - return Task.FromResult(this.Call(thisObject, arguments)); + return new ValueTask(this.Call(thisObject, arguments)); } } } diff --git a/Jint/Runtime/Interop/NamespaceReference.cs b/Jint/Runtime/Interop/NamespaceReference.cs index 75dc5e0a3..0734254bd 100644 --- a/Jint/Runtime/Interop/NamespaceReference.cs +++ b/Jint/Runtime/Interop/NamespaceReference.cs @@ -73,9 +73,9 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) } } - Task ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) { - return Task.FromResult(this.Call(thisObject, arguments)); + return new ValueTask(this.Call(thisObject, arguments)); } public override JsValue Get(JsValue property, JsValue receiver) diff --git a/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs b/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs index 6e6803664..4aca74050 100644 --- a/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs +++ b/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs @@ -59,28 +59,12 @@ internal void Initialize(EvaluationContext context, ReadOnlySpan arg public JsValue[] ArgumentListEvaluation(EvaluationContext context, out bool rented) { - rented = false; - if (_fullyCached) - { - return Unsafe.As(_expressions); - } - - if (HasSpreads) - { - var args = new List(_expressions.Length); - BuildArgumentsWithSpreads(context, args); - return args.ToArray(); - } - - var arguments = context.Engine._jsValueArrayPool.RentArray(_expressions.Length); - rented = true; - - BuildArguments(context, arguments); - - return arguments; + var (jsValueArray, isRented) = ArgumentListEvaluationAsync(context).Preserve().GetAwaiter().GetResult(); + rented = isRented; + return jsValueArray; } - public async Task<(JsValue[], bool rented)> ArgumentListEvaluationAsync(EvaluationContext context) + public async ValueTask<(JsValue[], bool rented)> ArgumentListEvaluationAsync(EvaluationContext context) { var rented = false; if (_fullyCached) @@ -105,14 +89,10 @@ public JsValue[] ArgumentListEvaluation(EvaluationContext context, out bool rent internal void BuildArguments(EvaluationContext context, JsValue[] targetArray) { - var expressions = _expressions; - for (uint i = 0; i < (uint) expressions.Length; i++) - { - targetArray[i] = GetValue(context, expressions[i])!; - } + BuildArgumentsAsync(context, targetArray).Preserve().GetAwaiter().GetResult(); } - internal async Task BuildArgumentsAsync(EvaluationContext context, JsValue[] targetArray) + internal async ValueTask BuildArgumentsAsync(EvaluationContext context, JsValue[] targetArray) { var expressions = _expressions; for (uint i = 0; i < (uint) expressions.Length; i++) @@ -128,23 +108,10 @@ public JsValue GetValue(EvaluationContext context, int index) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Task GetValueAsync(EvaluationContext context, int index) - { - return GetValueAsync(context, _expressions[index]); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static JsValue GetValue(EvaluationContext context, object? value) - { - return value switch - { - JintExpression expression => expression.GetValue(context).Clone(), - _ => (JsValue) value!, - }; - } + private static JsValue GetValue(EvaluationContext context, object? value) => GetValueAsync(context, value).Preserve().GetAwaiter().GetResult(); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static async Task GetValueAsync(EvaluationContext context, object? value) + private static async ValueTask GetValueAsync(EvaluationContext context, object? value) { return value switch { @@ -188,37 +155,9 @@ internal JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext cont return args.ToArray(); } - internal void BuildArgumentsWithSpreads(EvaluationContext context, List target) - { - foreach (var expression in _expressions) - { - if (expression is JintSpreadExpression jse) - { - jse.GetValueAndCheckIterator(context, out var objectInstance, out var iterator); - // optimize for array unless someone has touched the iterator - if (objectInstance is JsArray { HasOriginalIterator: true } ai) - { - var length = ai.GetLength(); - for (uint j = 0; j < length; ++j) - { - ai.TryGetValue(j, out var value); - target.Add(value); - } - } - else - { - var protocol = new ArraySpreadProtocol(context.Engine, target, iterator!); - protocol.Execute(); - } - } - else - { - target.Add(GetValue(context, expression)!); - } - } - } + internal void BuildArgumentsWithSpreads(EvaluationContext context, List target) => BuildArgumentsWithSpreadsAsync(context, target).Preserve().GetAwaiter().GetResult(); - internal async Task BuildArgumentsWithSpreadsAsync(EvaluationContext context, List target) + internal async ValueTask BuildArgumentsWithSpreadsAsync(EvaluationContext context, List target) { foreach (var expression in _expressions) { diff --git a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs index a18698d24..0e5d83417 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs @@ -427,43 +427,9 @@ private JsValue SetValue(EvaluationContext context) EvaluationContext context, JintIdentifierExpression left, JintExpression right, - bool hasEvalOrArguments) - { - var engine = context.Engine; - var env = engine.ExecutionContext.LexicalEnvironment; - var strict = StrictModeScope.IsStrictModeCode; - var identifier = left.Identifier; - if (JintEnvironment.TryGetIdentifierEnvironmentWithBinding( - env, - identifier, - out var environmentRecord)) - { - if (strict && hasEvalOrArguments && identifier.Key != KnownKeys.Eval) - { - ExceptionHelper.ThrowSyntaxError(engine.Realm, "Invalid assignment target"); - } - - var completion = right.GetValue(context); - if (context.IsAbrupt()) - { - return completion; - } - - var rval = completion.Clone(); - - if (right._expression.IsFunctionDefinition()) - { - ((Function) rval).SetFunctionName(identifier.Value); - } - - environmentRecord.SetMutableBinding(identifier, rval, strict); - return rval; - } - - return null; - } + bool hasEvalOrArguments) => AssignToIdentifierAsync(context, left, right, hasEvalOrArguments).Preserve().GetAwaiter().GetResult(); - internal static async Task AssignToIdentifierAsync( + internal static async ValueTask AssignToIdentifierAsync( EvaluationContext context, JintIdentifierExpression left, JintExpression right, diff --git a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs index bd7b23544..44f4f4b8b 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs @@ -14,36 +14,10 @@ public JintAwaitExpression(AwaitExpression expression) : base(expression) protected override object EvaluateInternal(EvaluationContext context) { - if (!_initialized) - { - _awaitExpression = Build(((AwaitExpression) _expression).Argument); - _initialized = true; - } - - var engine = context.Engine; - var asyncContext = engine.ExecutionContext; - - try - { - var value = _awaitExpression.GetValue(context); - - if (value is not JsPromise) - { - var promiseInstance = new JsPromise(engine); - promiseInstance.Resolve(value); - value = promiseInstance; - } - - return value.UnwrapIfPromise(); - } - catch (PromiseRejectedException e) - { - ExceptionHelper.ThrowJavaScriptException(engine, e.RejectedValue, _expression.Location); - return null; - } + return EvaluateInternalAsync(context).Preserve().GetAwaiter().GetResult(); } - protected override async Task EvaluateInternalAsync(EvaluationContext context) + protected override async ValueTask EvaluateInternalAsync(EvaluationContext context) { if (!_initialized) { diff --git a/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs index 0e838417e..81f2a7fbe 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs @@ -26,145 +26,9 @@ private void Initialize(EvaluationContext context) _calleeExpression = Build(expression.Callee); } - protected override object EvaluateInternal(EvaluationContext context) - { - if (!_initialized) - { - Initialize(context); - _initialized = true; - } - - if (!context.Engine._stackGuard.TryEnterOnCurrentStack()) - { - return StackGuard.RunOnEmptyStack(EvaluateInternal, context); - } - - if (_calleeExpression._expression.Type == NodeType.Super) - { - return SuperCall(context); - } - - // https://tc39.es/ecma262/#sec-function-calls - - var reference = _calleeExpression.Evaluate(context); - - if (ReferenceEquals(reference, JsValue.Undefined)) - { - return JsValue.Undefined; - } - - var engine = context.Engine; - var func = engine.GetValue(reference, false); - - if (func.IsNullOrUndefined() && _expression.IsOptional()) - { - return JsValue.Undefined; - } - - var referenceRecord = reference as Reference; - if (ReferenceEquals(func, engine.Realm.Intrinsics.Eval) - && referenceRecord != null - && !referenceRecord.IsPropertyReference - && CommonProperties.Eval.Equals(referenceRecord.ReferencedName)) - { - return HandleEval(context, func, engine, referenceRecord); - } - - var thisCall = (CallExpression) _expression; - var tailCall = IsInTailPosition(thisCall); - - // https://tc39.es/ecma262/#sec-evaluatecall - - JsValue thisObject; - if (referenceRecord is not null) - { - if (referenceRecord.IsPropertyReference) - { - thisObject = referenceRecord.ThisValue; - } - else - { - var baseValue = referenceRecord.Base; - - // deviation from the spec to support null-propagation helper - if (baseValue.IsNullOrUndefined() - && engine._referenceResolver.TryUnresolvableReference(engine, referenceRecord, out var value)) - { - thisObject = value; - } - else - { - var refEnv = (Environment) baseValue; - thisObject = refEnv.WithBaseObject(); - } - } - } - else - { - thisObject = JsValue.Undefined; - } - - var arguments = this._arguments.ArgumentListEvaluation(context, out var rented); - - if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func)) - { - ThrowMemberIsNotFunction(referenceRecord, reference, engine); - } - - var callable = func as ICallable; - if (callable is null) - { - ThrowReferenceNotFunction(referenceRecord, reference, engine); - } - - if (tailCall) - { - // TODO tail call - // PrepareForTailCall(); - } - - // ensure logic is in sync between Call, Construct and JintCallExpression! - - JsValue result; - if (callable is Function functionInstance) - { - var callStack = engine.CallStack; - var recursionDepth = callStack.Push(functionInstance, _calleeExpression, engine.ExecutionContext); - - if (recursionDepth > engine.Options.Constraints.MaxRecursionDepth) - { - // automatically pops the current element as it was never reached - ExceptionHelper.ThrowRecursionDepthOverflowException(callStack); - } - - try - { - result = functionInstance.Call(thisObject, arguments); - } - finally - { - // if call stack was reset due to recursive call to engine or similar, we might not have it anymore - if (callStack.Count > 0) - { - callStack.Pop(); - } - } - } - else - { - result = callable.Call(thisObject, arguments); - } - - if (rented) - { - engine._jsValueArrayPool.ReturnArray(arguments); - } - - engine._referencePool.Return(referenceRecord); - return result; - } + protected override object EvaluateInternal(EvaluationContext context) => EvaluateInternalAsync(context).Preserve().GetAwaiter().GetResult(); - protected override async Task EvaluateInternalAsync(EvaluationContext context) + protected override async ValueTask EvaluateInternalAsync(EvaluationContext context) { if (!_initialized) { @@ -277,7 +141,7 @@ protected override async Task EvaluateInternalAsync(EvaluationContext co try { - result = await functionInstance.CallAsync(thisObject, arguments).ConfigureAwait(false); + result = await callable.CallAsync(thisObject, arguments).ConfigureAwait(false); } finally { diff --git a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs index 24aba5991..1864fca41 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs @@ -20,16 +20,7 @@ protected JintExpression(Expression expression) /// /// /// - public virtual JsValue GetValue(EvaluationContext context) - { - var result = Evaluate(context); - if (result is not Reference reference) - { - return (JsValue) result; - } - - return context.Engine.GetValue(reference, returnReferenceToPool: true); - } + public virtual JsValue GetValue(EvaluationContext context) => GetValueAsync(context).Preserve().GetAwaiter().GetResult(); /// /// Resolves the underlying value for this expression. @@ -37,7 +28,7 @@ public virtual JsValue GetValue(EvaluationContext context) /// /// /// - public virtual async Task GetValueAsync(EvaluationContext context) + public virtual async ValueTask GetValueAsync(EvaluationContext context) { var result = await EvaluateAsync(context).ConfigureAwait(false); if (result is not Reference reference) @@ -49,20 +40,10 @@ public virtual async Task GetValueAsync(EvaluationContext context) } [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public object Evaluate(EvaluationContext context) - { - var oldSyntaxElement = context.LastSyntaxElement; - context.PrepareFor(_expression); - - var result = EvaluateInternal(context); - - context.LastSyntaxElement = oldSyntaxElement; - - return result; - } + public object Evaluate(EvaluationContext context) => EvaluateAsync(context).Preserve().GetAwaiter().GetResult(); [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public async Task EvaluateAsync(EvaluationContext context) + public async ValueTask EvaluateAsync(EvaluationContext context) { var oldSyntaxElement = context.LastSyntaxElement; context.PrepareFor(_expression); @@ -82,7 +63,7 @@ internal object EvaluateWithoutNodeTracking(EvaluationContext context) protected abstract object EvaluateInternal(EvaluationContext context); - protected virtual Task EvaluateInternalAsync(EvaluationContext context) => Task.FromResult(EvaluateInternal(context)); + protected virtual ValueTask EvaluateInternalAsync(EvaluationContext context) => new ValueTask(EvaluateInternal(context)); /// /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher. diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 4e093048e..2bf87a49d 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -33,83 +33,13 @@ public JintFunctionDefinition(IFunction function) /// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody /// [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - internal Completion EvaluateBody(EvaluationContext context, Function functionObject, JsValue[] argumentsList) - { - Completion result; - JsArguments? argumentsInstance = null; - if (Function.Expression) - { - // https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody - _bodyExpression ??= JintExpression.Build((Expression) Function.Body); - if (Function.Async) - { - // local copies to prevent capturing closure created on top of method - var function = functionObject; - var jsValues = argumentsList; - - var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); - var t = AsyncFunctionStart(context, promiseCapability, context => - { - context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); - context.RunBeforeExecuteStatementChecks(Function.Body); - var jsValue = _bodyExpression.GetValue(context).Clone(); - return Task.FromResult(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); - }); - - t.Wait(); - - result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); - } - else - { - argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); - context.RunBeforeExecuteStatementChecks(Function.Body); - var jsValue = _bodyExpression.GetValue(context).Clone(); - result = new Completion(CompletionType.Return, jsValue, Function.Body); - } - } - else if (Function.Generator) - { - result = EvaluateGeneratorBody(context, functionObject, argumentsList); - } - else - { - if (Function.Async) - { - // local copies to prevent capturing closure created on top of method - var function = functionObject; - var arguments = argumentsList; - - var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); - _bodyStatementList ??= new JintStatementList(Function); - var t = AsyncFunctionStart(context, promiseCapability, context => - { - context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); - return Task.FromResult(_bodyStatementList.Execute(context)); - }); - - t.Wait(); - - result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); - } - else - { - // https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody - argumentsInstance = context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); - _bodyStatementList ??= new JintStatementList(Function); - result = _bodyStatementList.Execute(context); - } - } - - argumentsInstance?.FunctionWasCalled(); - return result; - } + internal Completion EvaluateBody(EvaluationContext context, Function functionObject, JsValue[] argumentsList) => EvaluateBodyAsync(context, functionObject, argumentsList).Preserve().GetAwaiter().GetResult(); /// /// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody /// [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - internal async Task EvaluateBodyAsync(EvaluationContext context, Function functionObject, JsValue[] argumentsList) + internal async ValueTask EvaluateBodyAsync(EvaluationContext context, Function functionObject, JsValue[] argumentsList) { Completion result; JsArguments? argumentsInstance = null; @@ -125,7 +55,7 @@ await AsyncFunctionStart(context, promiseCapability, context => context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); context.RunBeforeExecuteStatementChecks(Function.Body); var jsValue = _bodyExpression.GetValue(context).Clone(); - return Task.FromResult(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); + return new ValueTask(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); }).ConfigureAwait(false); result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); } @@ -170,7 +100,7 @@ await AsyncFunctionStart(context, promiseCapability, async context => /// /// https://tc39.es/ecma262/#sec-async-functions-abstract-operations-async-function-start /// - private static async Task AsyncFunctionStart(EvaluationContext context, PromiseCapability promiseCapability, Func> asyncFunctionBody) + private static async ValueTask AsyncFunctionStart(EvaluationContext context, PromiseCapability promiseCapability, Func> asyncFunctionBody) { var runningContext = context.Engine.ExecutionContext; var asyncContext = runningContext; @@ -180,10 +110,10 @@ private static async Task AsyncFunctionStart(EvaluationContext context, PromiseC /// /// https://tc39.es/ecma262/#sec-asyncblockstart /// - private static async Task AsyncBlockStart( + private static async ValueTask AsyncBlockStart( EvaluationContext context, PromiseCapability promiseCapability, - Func> asyncBody, + Func> asyncBody, ExecutionContext asyncContext) { var runningContext = context.Engine.ExecutionContext; diff --git a/Jint/Runtime/Interpreter/JintStatementList.cs b/Jint/Runtime/Interpreter/JintStatementList.cs index 68900aa87..f96f04f96 100644 --- a/Jint/Runtime/Interpreter/JintStatementList.cs +++ b/Jint/Runtime/Interpreter/JintStatementList.cs @@ -58,88 +58,10 @@ private void Initialize(EvaluationContext context) [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public Completion Execute(EvaluationContext context) - { - if (!_initialized) - { - Initialize(context); - _initialized = true; - } - - if (_statement is not null) - { - context.LastSyntaxElement = _statement; - context.RunBeforeExecuteStatementChecks(_statement); - } - - Completion c = Completion.Empty(); - Completion sl = c; - - // The value of a StatementList is the value of the last value-producing item in the StatementList - var lastValue = JsEmpty.Instance; - var i = _index; - var temp = _jintStatements!; - try - { - for (; i < (uint) temp.Length; i++) - { - ref readonly var pair = ref temp[i]; - - if (pair.Value is null) - { - c = pair.Statement.Execute(context); - if (context.Engine._error is not null) - { - c = HandleError(context.Engine, pair.Statement); - break; - } - } - else - { - c = new Completion(CompletionType.Return, pair.Value, pair.Statement._statement); - } - - if (_generator) - { - if (context.Engine.ExecutionContext.Suspended) - { - _index = i + 1; - c = new Completion(CompletionType.Return, c.Value, pair.Statement._statement); - break; - } - } - - if (c.Type != CompletionType.Normal) - { - return c.UpdateEmpty(sl.Value); - } - - sl = c; - if (!c.Value.IsEmpty) - { - lastValue = c.Value; - } - } - } - catch (Exception ex) - { - Reset(); - - if (ex is JintException) - { - c = HandleException(context, ex, temp[i].Statement); - } - else - { - throw; - } - } - - return c.UpdateEmpty(lastValue).UpdateEmpty(JsValue.Undefined); - } + public Completion Execute(EvaluationContext context) => ExecuteAsync(context).Preserve().GetAwaiter().GetResult(); [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public async Task ExecuteAsync(EvaluationContext context) + public async ValueTask ExecuteAsync(EvaluationContext context) { if (!_initialized) { diff --git a/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs b/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs index 544d3a21f..05de9a83b 100644 --- a/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs @@ -19,16 +19,9 @@ protected override void Initialize(EvaluationContext context) _identifierExpression = _expression as JintIdentifierExpression; } - protected override Completion ExecuteInternal(EvaluationContext context) - { - var value = _identifierExpression is not null - ? _identifierExpression.GetValue(context) - : _expression.GetValue(context); - - return new Completion(context.Completion, value, _statement); - } + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); - protected override async Task ExecuteInternalAsync(EvaluationContext context) + protected override async ValueTask ExecuteInternalAsync(EvaluationContext context) { var value = _identifierExpression is not null ? await _identifierExpression.GetValueAsync(context).ConfigureAwait(false) diff --git a/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs b/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs index 55df50636..35bd0bbad 100644 --- a/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs @@ -21,13 +21,9 @@ protected override void Initialize(EvaluationContext context) : null; } - protected override Completion ExecuteInternal(EvaluationContext context) - { - var value = _argument is not null ? _argument.GetValue(context).Clone() : JsValue.Undefined; - return new Completion(CompletionType.Return, value, _statement); - } + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); - protected override async Task ExecuteInternalAsync(EvaluationContext context) + protected override async ValueTask ExecuteInternalAsync(EvaluationContext context) { var value = _argument is not null ? (await _argument.GetValueAsync(context).ConfigureAwait(false)).Clone() : JsValue.Undefined; return new Completion(CompletionType.Return, value, _statement); diff --git a/Jint/Runtime/Interpreter/Statements/JintStatement.cs b/Jint/Runtime/Interpreter/Statements/JintStatement.cs index d5ce5fdad..23dcdf740 100644 --- a/Jint/Runtime/Interpreter/Statements/JintStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintStatement.cs @@ -25,25 +25,10 @@ protected JintStatement(Statement statement) } [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public Completion Execute(EvaluationContext context) - { - if (_statement.Type != NodeType.BlockStatement) - { - context.PrepareFor(_statement); - context.RunBeforeExecuteStatementChecks(_statement); - } - - if (!_initialized) - { - Initialize(context); - _initialized = true; - } - - return ExecuteInternal(context); - } + public Completion Execute(EvaluationContext context) => ExecuteAsync(context).Preserve().GetAwaiter().GetResult(); [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public async Task ExecuteAsync(EvaluationContext context) + public async ValueTask ExecuteAsync(EvaluationContext context) { if (_statement.Type != NodeType.BlockStatement) { @@ -60,7 +45,7 @@ public async Task ExecuteAsync(EvaluationContext context) return await ExecuteInternalAsync(context).ConfigureAwait(false); } - protected virtual Task ExecuteInternalAsync(EvaluationContext context) => Task.FromResult(ExecuteInternal(context)); + protected virtual ValueTask ExecuteInternalAsync(EvaluationContext context) => new ValueTask(ExecuteInternal(context)); protected abstract Completion ExecuteInternal(EvaluationContext context); diff --git a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs index bbaea3bb2..074a3db5b 100644 --- a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs +++ b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs @@ -58,72 +58,9 @@ protected override void Initialize(EvaluationContext context) } } - protected override Completion ExecuteInternal(EvaluationContext context) - { - var engine = context.Engine; - foreach (var declaration in _declarations) - { - if (_statement.Kind != VariableDeclarationKind.Var && declaration.Left != null) - { - var lhs = (Reference) declaration.Left.Evaluate(context); - var value = JsValue.Undefined; - if (declaration.Init != null) - { - value = declaration.Init.GetValue(context).Clone(); - if (declaration.Init._expression.IsFunctionDefinition()) - { - ((Function) value).SetFunctionName(lhs.ReferencedName); - } - } - - lhs.InitializeReferencedBinding(value); - engine._referencePool.Return(lhs); - } - else if (declaration.Init != null) - { - if (declaration.LeftPattern != null) - { - var environment = _statement.Kind != VariableDeclarationKind.Var - ? engine.ExecutionContext.LexicalEnvironment - : null; - - var value = declaration.Init.GetValue(context); - - DestructuringPatternAssignmentExpression.ProcessPatterns( - context, - declaration.LeftPattern, - value, - environment, - checkPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var); - } - else if (declaration.LeftIdentifierExpression == null - || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier( - context, - declaration.LeftIdentifierExpression, - declaration.Init, - declaration.EvalOrArguments) is null) - { - // slow path - var lhs = (Reference) declaration.Left!.Evaluate(context); - lhs.AssertValid(engine.Realm); - - var value = declaration.Init.GetValue(context).Clone(); - - if (declaration.Init._expression.IsFunctionDefinition()) - { - ((Function) value).SetFunctionName(lhs.ReferencedName); - } - - engine.PutValue(lhs, value); - engine._referencePool.Return(lhs); - } - } - } - - return Completion.Empty(); - } + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); - protected override async Task ExecuteInternalAsync(EvaluationContext context) + protected override async ValueTask ExecuteInternalAsync(EvaluationContext context) { var engine = context.Engine; foreach (var declaration in _declarations) From 241620d36cad793f6fd6e41652288479bbebdb94 Mon Sep 17 00:00:00 2001 From: Kemal BAYTEKIN Date: Mon, 3 Feb 2025 09:36:46 +0300 Subject: [PATCH 4/4] local copies to prevent capturing closure created on top of method added --- Jint/Runtime/Interpreter/JintFunctionDefinition.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 2bf87a49d..f3412f953 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -49,10 +49,14 @@ internal async ValueTask EvaluateBodyAsync(EvaluationContext context _bodyExpression ??= JintExpression.Build((Expression) Function.Body); if (Function.Async) { + // local copies to prevent capturing closure created on top of method + var function = functionObject; + var jsValues = argumentsList; + var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); await AsyncFunctionStart(context, promiseCapability, context => { - context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + context.Engine.FunctionDeclarationInstantiation(function, jsValues); context.RunBeforeExecuteStatementChecks(Function.Body); var jsValue = _bodyExpression.GetValue(context).Clone(); return new ValueTask(new Completion(CompletionType.Return, jsValue, _bodyExpression._expression)); @@ -75,11 +79,15 @@ await AsyncFunctionStart(context, promiseCapability, context => { if (Function.Async) { + // local copies to prevent capturing closure created on top of method + var function = functionObject; + var arguments = argumentsList; + var promiseCapability = PromiseConstructor.NewPromiseCapability(context.Engine, context.Engine.Realm.Intrinsics.Promise); _bodyStatementList ??= new JintStatementList(Function); await AsyncFunctionStart(context, promiseCapability, async context => { - context.Engine.FunctionDeclarationInstantiation(functionObject, argumentsList); + context.Engine.FunctionDeclarationInstantiation(function, arguments); return await _bodyStatementList.ExecuteAsync(context).ConfigureAwait(false); }).ConfigureAwait(false); result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body);