diff --git a/Directory.Packages.props b/Directory.Packages.props index 917e1a124..912e7c868 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,6 +25,8 @@ + + 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/Engine.Async.cs b/Jint/Engine.Async.cs new file mode 100644 index 000000000..310b85d6c --- /dev/null +++ b/Jint/Engine.Async.cs @@ -0,0 +1,194 @@ +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; + +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 ValueTask 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 ValueTask 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 ValueTask 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 ValueTask 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 ValueTask 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 callable.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); + } + + /// + /// 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(); + + 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/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 b738a4afd..0470433f7 100644 --- a/Jint/Jint.csproj +++ b/Jint/Jint.csproj @@ -16,6 +16,7 @@ README.md $(NoWarn);1591 + $(NoWarn);NU1507 true @@ -33,12 +34,14 @@ - + + + diff --git a/Jint/JsValueExtensions.cs b/Jint/JsValueExtensions.cs index 62112eed3..38b1506a4 100644 --- a/Jint/JsValueExtensions.cs +++ b/Jint/JsValueExtensions.cs @@ -649,29 +649,48 @@ public static JsValue UnwrapIfPromise(this JsValue value) { if (value is JsPromise promise) { - var engine = promise.Engine; - var completedEvent = promise.CompletedEvent; - engine.RunAvailableContinuations(); - completedEvent.Wait(); - 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 UnwrapIfPromiseAsync(value).Preserve().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 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; + } + } + [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowWrongTypeException(JsValue value, string expectedType) { diff --git a/Jint/Native/Function/BindFunction.cs b/Jint/Native/Function/BindFunction.cs index 6aea0a576..9874a1cb7 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; } + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return new ValueTask(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..199638f5a --- /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 ValueTask CallAsync(JsValue thisObject, JsValue[] arguments) => new ValueTask(Call(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 9c07c9891..22159fc67 100644 --- a/Jint/Native/Function/ScriptFunction.cs +++ b/Jint/Native/Function/ScriptFunction.cs @@ -57,6 +57,14 @@ internal ScriptFunction( /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist /// protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) + { + return CallAsync(thisObject, arguments).Preserve().GetAwaiter().GetResult(); + } + + /// + /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist + /// + protected internal override async ValueTask CallAsync(JsValue thisObject, JsValue[] arguments) { var strict = _functionDefinition!.Strict || _thisMode == FunctionThisMode.Strict; using (new StrictModeScope(strict, true)) @@ -75,7 +83,7 @@ protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments // actual call var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine); - var result = _functionDefinition.EvaluateBody(context, this, arguments); + var result = await _functionDefinition.EvaluateBodyAsync(context, this, arguments).ConfigureAwait(false); if (result.Type == CompletionType.Throw) { diff --git a/Jint/Native/ICallable.cs b/Jint/Native/ICallable.cs index e69d11109..16691f2df 100644 --- a/Jint/Native/ICallable.cs +++ b/Jint/Native/ICallable.cs @@ -3,4 +3,7 @@ namespace Jint.Native; internal interface ICallable { JsValue Call(JsValue thisObject, params JsValue[] arguments); + //Task CallAsync(JsValue thisObject, params JsValue[] arguments); + + ValueTask 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..c930ff30d 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); } + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return new ValueTask(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..e094ae751 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,9 @@ public JsValue Call(JsValue thisObject, params JsValue[] arguments) return Undefined; } + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return new ValueTask(this.Call(thisObject, arguments)); + } } } diff --git a/Jint/Runtime/Interop/NamespaceReference.cs b/Jint/Runtime/Interop/NamespaceReference.cs index 96da128bc..0734254bd 100644 --- a/Jint/Runtime/Interop/NamespaceReference.cs +++ b/Jint/Runtime/Interop/NamespaceReference.cs @@ -73,6 +73,11 @@ JsValue ICallable.Call(JsValue thisObject, JsValue[] arguments) } } + ValueTask ICallable.CallAsync(JsValue thisObject, JsValue[] arguments) + { + return new ValueTask(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..4aca74050 100644 --- a/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs +++ b/Jint/Runtime/Interpreter/Expressions/ExpressionCache.cs @@ -59,33 +59,45 @@ internal void Initialize(EvaluationContext context, ReadOnlySpan arg public JsValue[] ArgumentListEvaluation(EvaluationContext context, out bool rented) { - rented = false; + var (jsValueArray, isRented) = ArgumentListEvaluationAsync(context).Preserve().GetAwaiter().GetResult(); + rented = isRented; + return jsValueArray; + } + + public async ValueTask<(JsValue[], bool rented)> ArgumentListEvaluationAsync(EvaluationContext context) + { + var rented = false; if (_fullyCached) { - return Unsafe.As(_expressions); + return (Unsafe.As(_expressions), rented); } if (HasSpreads) { var args = new List(_expressions.Length); - BuildArgumentsWithSpreads(context, args); - return args.ToArray(); + await BuildArgumentsWithSpreadsAsync(context, args).ConfigureAwait(false); + return (args.ToArray(), rented); } var arguments = context.Engine._jsValueArrayPool.RentArray(_expressions.Length); rented = true; - BuildArguments(context, arguments); + await BuildArgumentsAsync(context, arguments).ConfigureAwait(false); - return arguments; + return (arguments, rented); } internal void BuildArguments(EvaluationContext context, JsValue[] targetArray) + { + BuildArgumentsAsync(context, targetArray).Preserve().GetAwaiter().GetResult(); + } + + internal async ValueTask BuildArgumentsAsync(EvaluationContext context, JsValue[] targetArray) { var expressions = _expressions; for (uint i = 0; i < (uint) expressions.Length; i++) { - targetArray[i] = GetValue(context, expressions[i])!; + targetArray[i] = await GetValueAsync(context, expressions[i])!.ConfigureAwait(false); } } @@ -96,11 +108,14 @@ public JsValue GetValue(EvaluationContext context, int index) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static JsValue GetValue(EvaluationContext context, object? value) + private static JsValue GetValue(EvaluationContext context, object? value) => GetValueAsync(context, value).Preserve().GetAwaiter().GetResult(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static async ValueTask GetValueAsync(EvaluationContext context, object? value) { return value switch { - JintExpression expression => expression.GetValue(context).Clone(), + JintExpression expression => (await expression.GetValueAsync(context).ConfigureAwait(false)).Clone(), _ => (JsValue) value!, }; } @@ -140,7 +155,9 @@ internal JsValue[] DefaultSuperCallArgumentListEvaluation(EvaluationContext cont return args.ToArray(); } - internal void BuildArgumentsWithSpreads(EvaluationContext context, List target) + internal void BuildArgumentsWithSpreads(EvaluationContext context, List target) => BuildArgumentsWithSpreadsAsync(context, target).Preserve().GetAwaiter().GetResult(); + + internal async ValueTask BuildArgumentsWithSpreadsAsync(EvaluationContext context, List target) { foreach (var expression in _expressions) { @@ -165,7 +182,7 @@ internal void BuildArgumentsWithSpreads(EvaluationContext context, List } else { - target.Add(GetValue(context, expression)!); + target.Add(await GetValueAsync(context, expression)!.ConfigureAwait(false)); } } } diff --git a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs index e85f6ad3e..0e5d83417 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAssignmentExpression.cs @@ -424,6 +424,12 @@ private JsValue SetValue(EvaluationContext context) } internal static object? AssignToIdentifier( + EvaluationContext context, + JintIdentifierExpression left, + JintExpression right, + bool hasEvalOrArguments) => AssignToIdentifierAsync(context, left, right, hasEvalOrArguments).Preserve().GetAwaiter().GetResult(); + + internal static async ValueTask AssignToIdentifierAsync( EvaluationContext context, JintIdentifierExpression left, JintExpression right, @@ -443,7 +449,7 @@ private JsValue SetValue(EvaluationContext context) ExceptionHelper.ThrowSyntaxError(engine.Realm, "Invalid assignment target"); } - var completion = right.GetValue(context); + var completion = await right.GetValueAsync(context).ConfigureAwait(false); if (context.IsAbrupt()) { return completion; diff --git a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs index 77622abb5..44f4f4b8b 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs @@ -13,6 +13,11 @@ public JintAwaitExpression(AwaitExpression expression) : base(expression) } protected override object EvaluateInternal(EvaluationContext context) + { + return EvaluateInternalAsync(context).Preserve().GetAwaiter().GetResult(); + } + + protected override async ValueTask EvaluateInternalAsync(EvaluationContext context) { if (!_initialized) { @@ -25,7 +30,7 @@ protected override object EvaluateInternal(EvaluationContext context) try { - var value = _awaitExpression.GetValue(context); + var value = await _awaitExpression.GetValueAsync(context).ConfigureAwait(false); if (value is not JsPromise) { @@ -34,7 +39,7 @@ protected override object EvaluateInternal(EvaluationContext context) value = promiseInstance; } - return value.UnwrapIfPromise(); + return await value.UnwrapIfPromiseAsync().ConfigureAwait(false); } catch (PromiseRejectedException e) { 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..81f2a7fbe 100644 --- a/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs +++ b/Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs @@ -26,7 +26,9 @@ private void Initialize(EvaluationContext context) _calleeExpression = Build(expression.Callee); } - protected override object EvaluateInternal(EvaluationContext context) + protected override object EvaluateInternal(EvaluationContext context) => EvaluateInternalAsync(context).Preserve().GetAwaiter().GetResult(); + + protected override async ValueTask EvaluateInternalAsync(EvaluationContext context) { if (!_initialized) { @@ -46,7 +48,7 @@ protected override object EvaluateInternal(EvaluationContext context) // https://tc39.es/ecma262/#sec-function-calls - var reference = _calleeExpression.Evaluate(context); + var reference = await _calleeExpression.EvaluateAsync(context).ConfigureAwait(false); if (ReferenceEquals(reference, JsValue.Undefined)) { @@ -104,7 +106,7 @@ protected override object EvaluateInternal(EvaluationContext context) thisObject = JsValue.Undefined; } - var arguments = this._arguments.ArgumentListEvaluation(context, out var rented); + var (arguments, rented) = await this._arguments.ArgumentListEvaluationAsync(context).ConfigureAwait(false); if (!func.IsObject() && !engine._referenceResolver.TryGetCallable(engine, reference, out func)) { @@ -139,7 +141,7 @@ protected override object EvaluateInternal(EvaluationContext context) try { - result = functionInstance.Call(thisObject, arguments); + result = await callable.CallAsync(thisObject, arguments).ConfigureAwait(false); } finally { @@ -152,7 +154,7 @@ protected override object EvaluateInternal(EvaluationContext context) } else { - result = callable.Call(thisObject, arguments); + result = await callable.CallAsync(thisObject, arguments).ConfigureAwait(false); } if (rented) diff --git a/Jint/Runtime/Interpreter/Expressions/JintExpression.cs b/Jint/Runtime/Interpreter/Expressions/JintExpression.cs index 3a7477bda..1864fca41 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; @@ -21,9 +20,17 @@ protected JintExpression(Expression expression) /// /// /// - public virtual JsValue GetValue(EvaluationContext context) + public virtual JsValue GetValue(EvaluationContext context) => GetValueAsync(context).Preserve().GetAwaiter().GetResult(); + + /// + /// Resolves the underlying value for this expression. + /// By default uses the Engine for resolving. + /// + /// + /// + public virtual async ValueTask GetValueAsync(EvaluationContext context) { - var result = Evaluate(context); + var result = await EvaluateAsync(context).ConfigureAwait(false); if (result is not Reference reference) { return (JsValue) result; @@ -33,12 +40,15 @@ public virtual JsValue GetValue(EvaluationContext context) } [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public object Evaluate(EvaluationContext context) + public object Evaluate(EvaluationContext context) => EvaluateAsync(context).Preserve().GetAwaiter().GetResult(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async ValueTask EvaluateAsync(EvaluationContext context) { var oldSyntaxElement = context.LastSyntaxElement; context.PrepareFor(_expression); - var result = EvaluateInternal(context); + var result = await EvaluateInternalAsync(context).ConfigureAwait(false); context.LastSyntaxElement = oldSyntaxElement; @@ -53,6 +63,8 @@ internal object EvaluateWithoutNodeTracking(EvaluationContext context) protected abstract object EvaluateInternal(EvaluationContext 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 08b1bf896..f3412f953 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; @@ -34,7 +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) + 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 ValueTask EvaluateBodyAsync(EvaluationContext context, Function functionObject, JsValue[] argumentsList) { Completion result; JsArguments? argumentsInstance = null; @@ -49,13 +54,13 @@ 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 => + await AsyncFunctionStart(context, promiseCapability, context => { context.Engine.FunctionDeclarationInstantiation(function, jsValues); context.RunBeforeExecuteStatementChecks(Function.Body); var jsValue = _bodyExpression.GetValue(context).Clone(); - return 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); } else @@ -80,11 +85,11 @@ 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 => + await AsyncFunctionStart(context, promiseCapability, async context => { context.Engine.FunctionDeclarationInstantiation(function, arguments); - return _bodyStatementList.Execute(context); - }); + return await _bodyStatementList.ExecuteAsync(context).ConfigureAwait(false); + }).ConfigureAwait(false); result = new Completion(CompletionType.Return, promiseCapability.PromiseInstance, Function.Body); } else @@ -103,21 +108,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 ValueTask 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 ValueTask 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 +130,7 @@ private static void AsyncBlockStart( Completion result; try { - result = asyncBody(context); + result = await asyncBody(context).ConfigureAwait(false); } catch (JavaScriptException e) { @@ -168,7 +173,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 +421,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..f96f04f96 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; @@ -59,7 +58,10 @@ private void Initialize(EvaluationContext context) [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public Completion Execute(EvaluationContext context) + public Completion Execute(EvaluationContext context) => ExecuteAsync(context).Preserve().GetAwaiter().GetResult(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async ValueTask ExecuteAsync(EvaluationContext context) { if (!_initialized) { @@ -84,11 +86,11 @@ public Completion Execute(EvaluationContext context) { for (; i < (uint) temp.Length; i++) { - ref readonly var pair = ref temp[i]; + var pair = temp[i]; if (pair.Value is null) { - c = pair.Statement.Execute(context); + c = await pair.Statement.ExecuteAsync(context).ConfigureAwait(false); if (context.Engine._error is not null) { c = HandleError(context.Engine, pair.Statement); @@ -139,6 +141,7 @@ public Completion Execute(EvaluationContext context) 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..05de9a83b 100644 --- a/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintExpressionStatement.cs @@ -19,11 +19,13 @@ protected override void Initialize(EvaluationContext context) _identifierExpression = _expression as JintIdentifierExpression; } - protected override Completion ExecuteInternal(EvaluationContext context) + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); + + protected override async ValueTask ExecuteInternalAsync(EvaluationContext context) { var value = _identifierExpression is not null - ? _identifierExpression.GetValue(context) - : _expression.GetValue(context); + ? 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..35bd0bbad 100644 --- a/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintReturnStatement.cs @@ -21,9 +21,11 @@ protected override void Initialize(EvaluationContext context) : null; } - protected override Completion ExecuteInternal(EvaluationContext context) + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); + + protected override async ValueTask ExecuteInternalAsync(EvaluationContext context) { - var value = _argument is not null ? _argument.GetValue(context).Clone() : JsValue.Undefined; + 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..23dcdf740 100644 --- a/Jint/Runtime/Interpreter/Statements/JintStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintStatement.cs @@ -25,7 +25,10 @@ protected JintStatement(Statement statement) } [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] - public Completion Execute(EvaluationContext context) + public Completion Execute(EvaluationContext context) => ExecuteAsync(context).Preserve().GetAwaiter().GetResult(); + + [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)] + public async ValueTask ExecuteAsync(EvaluationContext context) { if (_statement.Type != NodeType.BlockStatement) { @@ -39,9 +42,11 @@ public Completion Execute(EvaluationContext context) _initialized = true; } - return ExecuteInternal(context); + return await ExecuteInternalAsync(context).ConfigureAwait(false); } + protected virtual ValueTask ExecuteInternalAsync(EvaluationContext context) => new ValueTask(ExecuteInternal(context)); + protected abstract Completion ExecuteInternal(EvaluationContext context); public ref readonly SourceLocation Location => ref _statement.LocationRef; @@ -108,4 +113,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..074a3db5b 100644 --- a/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs +++ b/Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs @@ -58,18 +58,20 @@ protected override void Initialize(EvaluationContext context) } } - protected override Completion ExecuteInternal(EvaluationContext context) + protected override Completion ExecuteInternal(EvaluationContext context) => ExecuteInternalAsync(context).Preserve().GetAwaiter().GetResult(); + + protected override async ValueTask ExecuteInternalAsync(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 lhs = (Reference) (await declaration.Left.EvaluateAsync(context).ConfigureAwait(false)); var value = JsValue.Undefined; if (declaration.Init != null) { - value = declaration.Init.GetValue(context).Clone(); + value = (await declaration.Init.GetValueAsync(context).ConfigureAwait(false)).Clone(); if (declaration.Init._expression.IsFunctionDefinition()) { ((Function) value).SetFunctionName(lhs.ReferencedName); @@ -87,7 +89,7 @@ protected override Completion ExecuteInternal(EvaluationContext context) ? engine.ExecutionContext.LexicalEnvironment : null; - var value = declaration.Init.GetValue(context); + var value = await declaration.Init.GetValueAsync(context).ConfigureAwait(false); DestructuringPatternAssignmentExpression.ProcessPatterns( context, @@ -97,17 +99,17 @@ protected override Completion ExecuteInternal(EvaluationContext context) checkPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var); } else if (declaration.LeftIdentifierExpression == null - || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier( + || (await JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifierAsync( context, declaration.LeftIdentifierExpression, declaration.Init, - declaration.EvalOrArguments) is null) + declaration.EvalOrArguments).ConfigureAwait(false)) is null) { // slow path - var lhs = (Reference) declaration.Left!.Evaluate(context); + var lhs = (Reference) (await declaration.Left!.EvaluateAsync(context).ConfigureAwait(false)); lhs.AssertValid(engine.Realm); - var value = declaration.Init.GetValue(context).Clone(); + var value = (await declaration.Init.GetValueAsync(context).ConfigureAwait(false)).Clone(); if (declaration.Init._expression.IsFunctionDefinition()) { @@ -122,4 +124,4 @@ protected override Completion ExecuteInternal(EvaluationContext context) return Completion.Empty(); } -} \ No newline at end of file +}