diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs index 929805ba8..ee2f0b2d6 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_function_objects/await_reaction.rs @@ -4,8 +4,8 @@ use crate::{ ecmascript::{ - Agent, ECMAScriptFunction, ExecutionContext, Promise, PromiseCapability, - PromiseReactionHandler, PromiseReactionType, SourceTextModule, Value, inner_promise_then, + Agent, ECMAScriptFunction, ExecutionContext, PromiseCapability, PromiseReactionHandler, + PromiseReactionType, SourceTextModule, Value, inner_promise_then, }, engine::{ Bindable, Executable, ExecutionResult, GcScope, Scopable, SuspendedVm, bindable_handle, @@ -105,21 +105,20 @@ impl AwaitReaction<'_> { .clone() .reject(agent, err.value().unbind(), gc.nogc()); } - ExecutionResult::Await { vm, awaited_value } => { + ExecutionResult::Await { vm, promise } => { + let promise = promise.unbind(); + let gc = gc.into_nogc(); + let promise = promise.bind(gc); // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) // 8. Remove asyncContext from the execution context stack and // restore the execution context that is at the top of the // execution context stack as the running execution context. let execution_context = agent.pop_execution_context().unwrap(); - let data = reaction.get(agent).bind(gc.nogc()).get_mut(agent); + let data = reaction.get(agent).bind(gc).get_mut(agent); data.vm = Some(vm); data.execution_context = Some(execution_context); // 2. Let promise be ? PromiseResolve(%Promise%, value). - let promise = - Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()).unbind(); - let gc = gc.into_nogc(); - let promise = promise.bind(gc); // SAFETY: not shared. let reaction = unsafe { reaction.take(agent) }.bind(gc); diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs index 67ee8e102..1ae5e1aa3 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs @@ -232,14 +232,14 @@ pub(super) fn resume_handle_result( // NOTE: Await is performed in the bytecode. async_generator_yield(agent, yielded_value, scoped_generator, vm, gc); } - ExecutionResult::Await { vm, awaited_value } => { + ExecutionResult::Await { vm, promise } => { async_generator_perform_await( agent, scoped_generator, vm, - awaited_value, + promise, AsyncGeneratorAwaitKind::Await, - gc, + gc.into_nogc(), ); } } @@ -249,25 +249,22 @@ fn async_generator_perform_await( agent: &mut Agent, scoped_generator: Scoped, vm: SuspendedVm, - awaited_value: Value, + promise: Promise, kind: AsyncGeneratorAwaitKind, - mut gc: GcScope, + gc: NoGcScope, ) { // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) let execution_context = agent.pop_execution_context().unwrap(); - let generator = scoped_generator.get(agent).bind(gc.nogc()); + let generator = scoped_generator.get(agent).bind(gc); generator.transition_to_awaiting(agent, vm, kind, execution_context); // 8. Remove asyncContext from the execution context stack and // restore the execution context that is at the top of the // execution context stack as the running execution context. let handler = PromiseReactionHandler::AsyncGenerator(generator.unbind()); // 2. Let promise be ? PromiseResolve(%Promise%, value). - let promise = Promise::resolve(agent, awaited_value, gc.reborrow()) - .unbind() - .bind(gc.nogc()); - + // Performed by caller. // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). - inner_promise_then(agent, promise, handler, handler, None, gc.nogc()); + inner_promise_then(agent, promise, handler, handler, None, gc); } /// ### [27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue )](https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption) @@ -405,24 +402,44 @@ pub(crate) fn async_generator_await_return( unreachable!() }; // 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%, completion.[[Value]])). - // 8. If promiseCompletion is an abrupt completion, then - // a. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true). - // b. Perform AsyncGeneratorDrainQueue(generator). - // c. Return unused. - // 9. Assert: promiseCompletion is a normal completion. - // 10. Let promise be promiseCompletion.[[Value]]. - let promise = Promise::resolve(agent, value.unbind(), gc.reborrow()) + let promise_completion = Promise::resolve(agent, value.unbind(), gc.reborrow()) .unbind() .bind(gc.nogc()); - // 11. ... onFulfilled ... - // 12. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »). - // 13. ... onRejected ... - // 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »). - // 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected). - let handler = - PromiseReactionHandler::AsyncGenerator(scoped_generator.get(agent).bind(gc.nogc())); - inner_promise_then(agent, promise, handler, handler, None, gc.nogc()); - // 16. Return unused. + let promise_completion = promise_completion.bind(gc.nogc()); + match promise_completion { + // 8. If promiseCompletion is an abrupt completion, then + Err(promise_completion) => { + let generator = scoped_generator.get(agent).bind(gc.nogc()); + // a. Perform AsyncGeneratorCompleteStep(generator, promiseCompletion, true). + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Err(promise_completion.unbind()), + true, + None, + gc.nogc(), + ); + // b. Perform AsyncGeneratorDrainQueue(generator). + async_generator_drain_queue(agent, scoped_generator, gc); + // c. Return unused. + } + // 9. Assert: promiseCompletion is a normal completion. + Ok(promise) => { + let promise = promise.unbind(); + let gc = gc.into_nogc(); + let promise = promise.bind(gc); + let generator = unsafe { scoped_generator.take(agent) }.bind(gc); + // 10. Let promise be promiseCompletion.[[Value]]. + // 11. ... onFulfilled ... + // 12. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »). + // 13. ... onRejected ... + // 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »). + let handler = PromiseReactionHandler::AsyncGenerator(generator); + // 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected). + inner_promise_then(agent, promise, handler, handler, None, gc); + // 16. Return unused. + } + }; } pub(crate) fn async_generator_await_return_on_fulfilled( diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_objects.rs index d87e1c646..69de7c73f 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/iteration/async_from_sync_iterator_objects.rs @@ -12,8 +12,8 @@ use crate::{ Agent, ArgumentsList, BUILTIN_STRING_MEMORY, ExceptionType, IteratorRecord, MaybeInvalidIteratorRecord, Object, Promise, PromiseCapability, PromiseReactionHandler, Value, call_function, create_iter_result_object, get_object_method, - if_abrupt_reject_promise_m, inner_promise_then, iterator_close_with_value, - iterator_complete, iterator_next, iterator_value, unwrap_try, + if_abrupt_reject_promise_m, inner_promise_then, iterator_close_with_error, + iterator_close_with_value, iterator_complete, iterator_next, iterator_value, unwrap_try, }, engine::{Bindable, GcScope, Scopable, VmIteratorRecord}, }; @@ -375,7 +375,7 @@ pub(crate) fn async_from_sync_iterator_continuation<'a>( .unbind() .bind(gc.nogc()); // 3. IfAbruptRejectPromise(done, promiseCapability). - let promise_capability = PromiseCapability { + let mut promise_capability = PromiseCapability { promise: scoped_promise.get(agent).bind(gc.nogc()), must_be_unresolved, }; @@ -387,18 +387,37 @@ pub(crate) fn async_from_sync_iterator_continuation<'a>( .unbind() .bind(gc.nogc()); // 5. IfAbruptRejectPromise(value, promiseCapability). - let promise_capability = PromiseCapability { + promise_capability = PromiseCapability { promise: scoped_promise.get(agent).bind(gc.nogc()), must_be_unresolved, }; let value = if_abrupt_reject_promise_m!(agent, value, promise_capability, gc); // 6. Let valueWrapper be Completion(PromiseResolve(%Promise%, value)). - let value_wrapper = Promise::resolve(agent, value.unbind(), gc.reborrow()) + let mut value_wrapper = Promise::resolve(agent, value.unbind(), gc.reborrow()) .unbind() .bind(gc.nogc()); - // 7. If valueWrapper is an abrupt completion, done is false, and closeOnRejection is true, then - // a. Set valueWrapper to Completion(IteratorClose(syncIteratorRecord, valueWrapper)). + // 7. If valueWrapper is an abrupt completion, done is false, and + // closeOnRejection is true, then + if let Err(err) = value_wrapper + && !done + && close_on_rejection + { + // a. Set valueWrapper to Completion(IteratorClose(syncIteratorRecord, valueWrapper)). + value_wrapper = Err(iterator_close_with_error( + agent, + sync_iterator.get(agent), + err.unbind(), + gc.reborrow(), + ) + .unbind() + .bind(gc.nogc())); + } + promise_capability = PromiseCapability { + promise: scoped_promise.get(agent).bind(gc.nogc()), + must_be_unresolved, + }; // 8. IfAbruptRejectPromise(valueWrapper, promiseCapability). + let value_wrapper = if_abrupt_reject_promise_m!(agent, value_wrapper, promise_capability, gc); // 9. Let unwrap be a new Abstract Closure with parameters (v) that // captures done and performs the following steps when called: // a. Return CreateIteratorResultObject(v, done). @@ -414,10 +433,13 @@ pub(crate) fn async_from_sync_iterator_continuation<'a>( PromiseReactionHandler::Empty } else { // 13. Else, - // a. Let closeIterator be a new Abstract Closure with parameters (error) that captures syncIteratorRecord and performs the following steps when called: + // a. Let closeIterator be a new Abstract Closure with parameters + // (error) that captures syncIteratorRecord and performs the + // following steps when called: // i. Return ? IteratorClose(syncIteratorRecord, ThrowCompletion(error)). // b. Let onRejected be CreateBuiltinFunction(closeIterator, 1, "", « »). - // c. NOTE: onRejected is used to close the Iterator when the "value" property of an IteratorResult object it yields is a rejected promise. + // c. NOTE: onRejected is used to close the Iterator when the "value" + // property of an IteratorResult object it yields is a rejected promise. PromiseReactionHandler::AsyncFromSyncIteratorClose( unsafe { sync_iterator.take(agent) }.bind(gc.nogc()), ) diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_finally_functions.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_finally_functions.rs index 7bae755f3..7110856c6 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_finally_functions.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_finally_functions.rs @@ -133,7 +133,7 @@ impl<'a> FunctionInternalProperties<'a> for BuiltinPromiseFinallyFunction<'a> { let _c = unsafe { c.take(agent) }.bind(gc.nogc()); // ii. Let p be ? PromiseResolve(C, result). let p = Promise::resolve(agent, result.unbind(), gc.reborrow()) - .unbind() + .unbind()? .bind(gc.nogc()); // SAFETY: not shared. let value = unsafe { value.take(agent) }.bind(gc.nogc()); @@ -175,10 +175,12 @@ impl<'a> FunctionInternalProperties<'a> for BuiltinPromiseFinallyFunction<'a> { let _c = unsafe { c.take(agent) }.bind(gc.nogc()); // ii. Let p be ? PromiseResolve(C, result). let p = Promise::resolve(agent, result.unbind(), gc.reborrow()) - .unbind() + .unbind()? .bind(gc.nogc()); let reason = unsafe { reason.take(agent) }.bind(gc.nogc()); - // iii. Let throwReason be a new Abstract Closure with no parameters that captures reason and performs the following steps when called: + // iii. Let throwReason be a new Abstract Closure with no + // parameters that captures reason and performs the following + // steps when called: // iv. Let thrower be CreateBuiltinFunction(throwReason, 0, "", « »). let thrower = agent.heap.create(PromiseFinallyFunctionHeapData { backing_object: None, diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs index 399dea2f1..efee5ce7c 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs @@ -516,7 +516,7 @@ impl PromiseConstructor { } // 3. Return ? PromiseResolve(C, x). - Ok(Promise::resolve(agent, arguments.get(0), gc).into()) + Promise::resolve(agent, arguments.get(0), gc).map(Value::from) } /// ### [27.2.4.8 Promise.try ( callback, ...args )](https://tc39.es/ecma262/#sec-promise.try) @@ -573,7 +573,7 @@ impl PromiseConstructor { // 6. Else, Ok(result) => { // a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »). - Promise::resolve(agent, result.unbind(), gc) + Promise::resolve(agent, result.unbind(), gc)? } }; // 7. Return promiseCapability.[[Promise]]. diff --git a/nova_vm/src/ecmascript/builtins/promise.rs b/nova_vm/src/ecmascript/builtins/promise.rs index 2a7388b1d..b4bea3735 100644 --- a/nova_vm/src/ecmascript/builtins/promise.rs +++ b/nova_vm/src/ecmascript/builtins/promise.rs @@ -8,8 +8,8 @@ pub(crate) use data::*; use crate::{ ecmascript::{ - Agent, InternalMethods, InternalSlots, JsError, JsResult, OrdinaryObject, - PromiseCapability, ProtoIntrinsics, Value, object_handle, + Agent, BUILTIN_STRING_MEMORY, InternalMethods, InternalSlots, JsError, JsResult, + OrdinaryObject, PromiseCapability, ProtoIntrinsics, Value, get, object_handle, }, engine::{Bindable, GcScope, NoGcScope, Scopable}, heap::{ @@ -99,23 +99,48 @@ impl<'a> Promise<'a> { } ///### [27.2.4.7.1 PromiseResolve ( C, x )](https://tc39.es/ecma262/#sec-promise-resolve) - pub(crate) fn resolve(agent: &mut Agent, x: Value, mut gc: GcScope<'a, '_>) -> Self { + pub(crate) fn resolve( + agent: &mut Agent, + x: Value, + mut gc: GcScope<'a, '_>, + ) -> JsResult<'a, Self> { + let x = x.bind(gc.nogc()); // 1. If IsPromise(x) is true, then - if let Value::Promise(promise) = x { + let x = if let Value::Promise(x) = x { + let scoped_x = x.scope(agent, gc.nogc()); // a. Let xConstructor be ? Get(x, "constructor"). + let x_constructor = match get( + agent, + x.unbind(), + BUILTIN_STRING_MEMORY.constructor.into(), + gc.reborrow(), + ) + .unbind() + .bind(gc.nogc()) + { + Ok(v) => v, + Err(err) => return Err(err.unbind()), + }; + // SAFETY: not shared. + let x = unsafe { scoped_x.take(agent) }.bind(gc.nogc()); // b. If SameValue(xConstructor, C) is true, return x. - // NOTE: Ignoring subclasses. - promise.unbind() + if x_constructor == agent.current_realm_record().intrinsics().promise().into() { + return Ok(x.unbind().bind(gc.into_nogc())); + } + x.into() } else { - // 2. Let promiseCapability be ? NewPromiseCapability(C). - let promise_capability = PromiseCapability::new(agent, gc.nogc()); - let promise = promise_capability.promise().scope(agent, gc.nogc()); - // 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). - promise_capability.unbind().resolve(agent, x, gc.reborrow()); - // 4. Return promiseCapability.[[Promise]]. - // SAFETY: Not shared. - unsafe { promise.take(agent) } - } + x + }; + // 2. Let promiseCapability be ? NewPromiseCapability(C). + let promise_capability = PromiseCapability::new(agent, gc.nogc()); + let promise = promise_capability.promise().scope(agent, gc.nogc()); + // 3. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »). + promise_capability + .unbind() + .resolve(agent, x.unbind(), gc.reborrow()); + // 4. Return promiseCapability.[[Promise]]. + // SAFETY: Not shared. + Ok(unsafe { promise.take(agent).bind(gc.into_nogc()) }) } } diff --git a/nova_vm/src/ecmascript/scripts_and_modules/module/module_semantics/source_text_module_records.rs b/nova_vm/src/ecmascript/scripts_and_modules/module/module_semantics/source_text_module_records.rs index 53f973e82..c12f2dca1 100644 --- a/nova_vm/src/ecmascript/scripts_and_modules/module/module_semantics/source_text_module_records.rs +++ b/nova_vm/src/ecmascript/scripts_and_modules/module/module_semantics/source_text_module_records.rs @@ -1572,7 +1572,10 @@ fn async_module_start( // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). promise_capability.reject(agent, err.value(), gc.nogc()); } - ExecutionResult::Await { vm, awaited_value } => { + ExecutionResult::Await { + vm, + promise: resolve_promise, + } => { let async_context = agent.pop_execution_context().unwrap(); // SAFETY: not shared. let (bytecode, promise, module) = unsafe { @@ -1596,9 +1599,6 @@ fn async_module_start( // `handler` corresponds to the `fulfilledClosure` and `rejectedClosure` functions, // which resume execution of the function. // 2. Let promise be ? PromiseResolve(%Promise%, value). - let resolve_promise = Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()) - .unbind() - .bind(gc.nogc()); module.set_executable(agent, bytecode); diff --git a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs index ee297b608..0184efc92 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs @@ -278,46 +278,45 @@ pub(crate) fn evaluate_async_function_body<'a>( }; let exe = exe.scope(agent, gc.nogc()); - // AsyncFunctionStart will run the function until it returns, throws or - // gets suspended with an await. - match Vm::execute( + let result = Vm::execute( agent, exe, Some(arguments_list.unbind().as_mut_slice()), gc.reborrow(), - ) { + ) + .unbind(); + let gc = gc.into_nogc(); + let result = result.bind(gc); + // SAFETY: not shared. + let promise = unsafe { promise.take(agent) }.bind(gc); + // AsyncFunctionStart will run the function until it returns, throws or gets + // suspended with an await. + match result { ExecutionResult::Return(result) => { - let result = result.unbind().bind(gc.nogc()); - let promise = promise.get(agent).bind(gc.nogc()); let promise_capability = PromiseCapability::from_promise(promise, must_be_unresolved); // [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart) // 2. e. If result is a normal completion, then // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « undefined »). // f. Else if result is a return completion, then // i. Perform ! Call(promiseCapability.[[Resolve]], undefined, « result.[[Value]] »). - promise_capability - .unbind() - .resolve(agent, result.unbind(), gc.reborrow()); + unwrap_try(promise_capability.try_resolve(agent, result, gc)); } ExecutionResult::Throw(err) => { - let err = err.unbind().bind(gc.nogc()); - let promise = promise.get(agent).bind(gc.nogc()); let promise_capability = PromiseCapability::from_promise(promise, must_be_unresolved); // [27.7.5.2 AsyncBlockStart ( promiseCapability, asyncBody, asyncContext )](https://tc39.es/ecma262/#sec-asyncblockstart) // 2. g. i. Assert: result is a throw completion. // ii. Perform ! Call(promiseCapability.[[Reject]], undefined, « result.[[Value]] »). - promise_capability.reject(agent, err.value(), gc.nogc()); + promise_capability.reject(agent, err.value(), gc); } - ExecutionResult::Await { vm, awaited_value } => { + ExecutionResult::Await { + vm, + promise: resolve_promise, + } => { // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) // `handler` corresponds to the `fulfilledClosure` and `rejectedClosure` functions, // which resume execution of the function. // 2. Let promise be ? PromiseResolve(%Promise%, value). - let resolve_promise = Promise::resolve(agent, awaited_value.unbind(), gc.reborrow()) - .unbind() - .bind(gc.nogc()); - let promise = promise.get(agent).bind(gc.nogc()); let promise_capability = PromiseCapability::from_promise(promise, must_be_unresolved); // NOTE: the execution context has to be cloned because it will be popped when we @@ -331,21 +330,14 @@ pub(crate) fn evaluate_async_function_body<'a>( })); // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). - inner_promise_then( - agent, - resolve_promise.unbind(), - handler, - handler, - None, - gc.nogc(), - ); + inner_promise_then(agent, resolve_promise, handler, handler, None, gc); } ExecutionResult::Yield { .. } => unreachable!(), } //} // 5. Return Completion Record { [[Type]]: return, [[Value]]: promiseCapability.[[Promise]], [[Target]]: empty }. - promise.get(agent).bind(gc.into_nogc()) + promise } /// ### [15.5.2 Runtime Semantics: EvaluateGeneratorBody](https://tc39.es/ecma262/#sec-runtime-semantics-evaluategeneratorbody) diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 0e8adaf7d..1f5f54658 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -13,9 +13,9 @@ use wtf8::Wtf8Buf; use crate::{ ecmascript::{ Agent, ArgumentsList, BUILTIN_STRING_MEMORY, BigInt, Environment, ExceptionType, JsError, - JsResult, Number, Object, Primitive, Reference, ScopedArgumentsList, String, Value, - call_function, get_method, is_callable, ordinary_has_instance, to_boolean, to_numeric, - to_numeric_primitive, to_primitive, to_property_key, to_string_primitive, + JsResult, Number, Object, Primitive, Promise, Reference, ScopedArgumentsList, String, + Value, call_function, get_method, is_callable, ordinary_has_instance, to_boolean, + to_numeric, to_numeric_primitive, to_primitive, to_property_key, to_string_primitive, try_get_object_method, try_result_into_option_js, }, engine::{ @@ -34,7 +34,7 @@ pub(crate) enum ExecutionResult<'a> { Throw(JsError<'a>), Await { vm: SuspendedVm, - awaited_value: Value<'a>, + promise: Promise<'a>, }, Yield { vm: SuspendedVm, @@ -365,10 +365,12 @@ impl Vm { if agent.options.print_internals { Self::print_awaiting(); } - let awaited_value = self.result.take().unwrap(); + let Value::Promise(promise) = self.result.take().unwrap() else { + unreachable!() + }; Some(ExecutionResult::Await { vm: core::mem::take(self).suspend(), - awaited_value, + promise, }) } Err(err) => { @@ -571,7 +573,10 @@ impl Vm { unreachable!("hot instruction not handled before execute_cold_instruction") } Instruction::Return => return Ok(ContinuationKind::Return), - Instruction::Await => return Ok(ContinuationKind::Await), + Instruction::Await => { + execute_await_promise_resolve(agent, vm, gc)?; + return Ok(ContinuationKind::Await); + } Instruction::Yield => return Ok(ContinuationKind::Yield), Instruction::IsStrictlyEqual => execute_is_strictly_equal(agent, vm, gc.into_nogc()), Instruction::IsNullOrUndefined => vm.execute_is_null_or_undefined(), diff --git a/nova_vm/src/engine/bytecode/vm/execute_instructions.rs b/nova_vm/src/engine/bytecode/vm/execute_instructions.rs index 325ad4384..df9b663c5 100644 --- a/nova_vm/src/engine/bytecode/vm/execute_instructions.rs +++ b/nova_vm/src/engine/bytecode/vm/execute_instructions.rs @@ -10,11 +10,11 @@ use crate::{ Agent, ArgumentsList, Array, BUILTIN_STRING_MEMORY, BigInt, BuiltinConstructorArgs, ConstructorStatus, Environment, ExceptionType, Function, FunctionAstRef, InternalMethods, InternalSlots, JsResult, Number, Numeric, Object, OrdinaryFunctionCreateParams, - OrdinaryObject, Primitive, PrivateMethod, PropertyDescriptor, PropertyKey, PropertyKeySet, - PropertyLookupCache, ProtoIntrinsics, Reference, ScriptOrModule, SetFunctionNamePrefix, - SetResult, String, TryError, TryGetValueContinue, TryHasResult, TryResult, Value, - array_create, call, call_function, call_proxy_set, construct, copy_data_properties, - copy_data_properties_into_object, create_builtin_constructor, + OrdinaryObject, Primitive, PrivateMethod, Promise, PropertyDescriptor, PropertyKey, + PropertyKeySet, PropertyLookupCache, ProtoIntrinsics, Reference, ScriptOrModule, + SetFunctionNamePrefix, SetResult, String, TryError, TryGetValueContinue, TryHasResult, + TryResult, Value, array_create, call, call_function, call_proxy_set, construct, + copy_data_properties, copy_data_properties_into_object, create_builtin_constructor, create_data_property_or_throw, create_unmapped_arguments_object, define_property_or_throw, evaluate_import_call, get_this_environment, get_this_value, get_value, has_property, is_constructor, is_less_than, is_loosely_equal, is_private_reference, @@ -53,6 +53,25 @@ use super::{ throw_error_in_target_not_object, typeof_operator, verify_is_object, with_vm_gc, }; +// Perform step 2 of `Await( value )` in the VM context. +pub(super) fn execute_await_promise_resolve<'gc>( + agent: &mut Agent, + vm: &mut Vm, + gc: GcScope<'gc, '_>, +) -> JsResult<'gc, ()> { + let value = vm.result.take().unwrap(); + // 1. Let asyncContext be the running execution context. + // 2. Let promise be ? PromiseResolve(%Promise%, value). + let promise = with_vm_gc( + agent, + vm, + |agent, gc| Promise::resolve(agent, value, gc), + gc, + )?; + vm.result = Some(promise.unbind().into()); + Ok(()) +} + pub(super) fn execute_array_create<'gc>( agent: &mut Agent, vm: &mut Vm, diff --git a/tests/expectations.json b/tests/expectations.json index 40e13f944..9fde238da 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -218,17 +218,11 @@ "built-ins/AsyncDisposableStack/prototype/use/this-not-object-throws.js": "FAIL", "built-ins/AsyncDisposableStack/prototype/use/throws-if-value-not-object.js": "FAIL", "built-ins/AsyncDisposableStack/undefined-newtarget-throws.js": "FAIL", - "built-ins/AsyncFromSyncIteratorPrototype/next/iterator-result-poisoned-wrapper.js": "FAIL", - "built-ins/AsyncFromSyncIteratorPrototype/next/next-result-poisoned-wrapper.js": "FAIL", - "built-ins/AsyncFromSyncIteratorPrototype/throw/throw-result-poisoned-wrapper.js": "FAIL", "built-ins/AsyncFunction/proto-from-ctor-realm.js": "FAIL", "built-ins/AsyncGeneratorFunction/instance-prototype.js": "FAIL", "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm-prototype.js": "FAIL", "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm.js": "FAIL", "built-ins/AsyncGeneratorFunction/prototype/constructor.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/invokes-return.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/is-function.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/length.js": "FAIL", @@ -1054,7 +1048,6 @@ "built-ins/Promise/reject/ctx-ctor.js": "FAIL", "built-ins/Promise/reject/ctx-non-ctor.js": "FAIL", "built-ins/Promise/reject/ctx-non-object.js": "FAIL", - "built-ins/Promise/resolve/arg-uniq-ctor.js": "FAIL", "built-ins/Promise/resolve/capability-executor-called-twice.js": "FAIL", "built-ins/Promise/resolve/capability-executor-not-callable.js": "FAIL", "built-ins/Promise/resolve/capability-invocation-error.js": "FAIL", @@ -6377,9 +6370,6 @@ "language/statements/const/cptn-value.js": "FAIL", "language/statements/const/static-init-await-binding-valid.js": "FAIL", "language/statements/do-while/tco-body.js": "FAIL", - "language/statements/for-await-of/async-from-sync-iterator-continuation-abrupt-completion-get-constructor.js": "FAIL", - "language/statements/for-await-of/ticks-with-async-iter-resolved-promise-and-constructor-lookup-two.js": "FAIL", - "language/statements/for-await-of/ticks-with-sync-iter-resolved-promise-and-constructor-lookup.js": "FAIL", "language/statements/for-of/arguments-mapped-aliasing.js": "FAIL", "language/statements/for/tco-const-body.js": "FAIL", "language/statements/for/tco-let-body.js": "FAIL", diff --git a/tests/metrics.json b/tests/metrics.json index 753cb3d11..d6b67a54f 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { "crash": 52, - "fail": 6941, - "pass": 40359, + "fail": 6931, + "pass": 40369, "skip": 3326, "timeout": 18, "unresolved": 37