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 90a9fc71..5be2b8ce 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 @@ -70,10 +70,19 @@ impl AwaitReactionIdentifier { // 5. d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it. let vm = agent[self].vm.take().unwrap(); let async_function = agent[self].async_function.unwrap(); - let executable = agent[async_function].compiled_bytecode.unwrap(); let execution_result = match reaction_type { - PromiseReactionType::Fulfill => vm.resume(agent, executable, value, gc.reborrow()), - PromiseReactionType::Reject => vm.resume_throw(agent, executable, value, gc.reborrow()), + PromiseReactionType::Fulfill => vm.resume( + agent, + async_function.get_executable(agent), + value, + gc.reborrow(), + ), + PromiseReactionType::Reject => vm.resume_throw( + agent, + async_function.get_executable(agent), + value, + gc.reborrow(), + ), }; match execution_result { diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs index 92c0dd33..c4fe089c 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects.rs @@ -38,10 +38,7 @@ use crate::{ }, }; -use super::{ - generator_objects::SuspendedGeneratorState, - promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionType, -}; +use super::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionType; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct AsyncGenerator<'a>(pub(crate) AsyncGeneratorIndex<'a>); @@ -83,6 +80,10 @@ impl AsyncGenerator<'_> { self.0.into_index() } + pub(crate) fn get_executable(self, agent: &Agent) -> Executable { + agent[self].executable.unwrap() + } + pub(crate) fn is_draining_queue(self, agent: &Agent) -> bool { matches!( agent[self].async_generator_state.as_ref().unwrap(), @@ -100,26 +101,14 @@ impl AsyncGenerator<'_> { pub(crate) fn is_suspended_start(self, agent: &Agent) -> bool { matches!( &agent[self].async_generator_state, - Some(AsyncGeneratorState::Suspended { - state: SuspendedGeneratorState { - vm_or_args: VmOrArguments::Arguments(_), - .. - }, - .. - }) + Some(AsyncGeneratorState::SuspendedStart { .. }) ) } pub(crate) fn is_suspended_yield(self, agent: &Agent) -> bool { matches!( &agent[self].async_generator_state, - Some(AsyncGeneratorState::Suspended { - state: SuspendedGeneratorState { - vm_or_args: VmOrArguments::Vm(_), - .. - }, - .. - }) + Some(AsyncGeneratorState::SuspendedYield { .. }) ) } @@ -133,7 +122,8 @@ impl AsyncGenerator<'_> { pub(crate) fn queue_is_empty(self, agent: &Agent) -> bool { match agent[self].async_generator_state.as_ref().unwrap() { AsyncGeneratorState::Awaiting { queue, .. } - | AsyncGeneratorState::Suspended { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } | AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => queue.is_empty(), AsyncGeneratorState::Completed => unreachable!(), @@ -143,7 +133,8 @@ impl AsyncGenerator<'_> { pub(crate) fn peek_first(self, agent: &mut Agent) -> &AsyncGeneratorRequest { match agent[self].async_generator_state.as_mut().unwrap() { AsyncGeneratorState::Awaiting { queue, .. } - | AsyncGeneratorState::Suspended { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } | AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => queue.front().unwrap(), AsyncGeneratorState::Completed => unreachable!(), @@ -153,7 +144,8 @@ impl AsyncGenerator<'_> { pub(crate) fn pop_first(self, agent: &mut Agent) -> AsyncGeneratorRequest { match agent[self].async_generator_state.as_mut().unwrap() { AsyncGeneratorState::Awaiting { queue, .. } - | AsyncGeneratorState::Suspended { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } | AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => queue.pop_front().unwrap(), AsyncGeneratorState::Completed => unreachable!(), @@ -163,7 +155,8 @@ impl AsyncGenerator<'_> { pub(crate) fn append_to_queue(self, agent: &mut Agent, request: AsyncGeneratorRequest) { match agent[self].async_generator_state.as_mut().unwrap() { AsyncGeneratorState::Awaiting { queue, .. } - | AsyncGeneratorState::Suspended { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } | AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => queue.push_back(request), AsyncGeneratorState::Completed => unreachable!(), @@ -174,7 +167,8 @@ impl AsyncGenerator<'_> { let async_generator_state = &mut agent[self].async_generator_state; let state = async_generator_state.take().unwrap(); let queue = match state { - AsyncGeneratorState::Suspended { queue, .. } + AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } | AsyncGeneratorState::Executing(queue) => queue, _ => unreachable!(), }; @@ -185,7 +179,6 @@ impl AsyncGenerator<'_> { self, agent: &mut Agent, vm: SuspendedVm, - executable: Executable, kind: AsyncGeneratorAwaitKind, execution_context: ExecutionContext, ) { @@ -195,33 +188,52 @@ impl AsyncGenerator<'_> { }; async_generator_state.replace(AsyncGeneratorState::Awaiting { queue, - state: SuspendedGeneratorState { - vm_or_args: VmOrArguments::Vm(vm), - executable, - execution_context, - }, + vm, + execution_context, kind, }); } + pub(crate) fn transition_to_execution( + self, + agent: &mut Agent, + ) -> (VmOrArguments, ExecutionContext, Executable) { + let async_generator_state = &mut agent[self].async_generator_state; + let (vm_or_args, execution_context, queue) = match async_generator_state.take().unwrap() { + AsyncGeneratorState::SuspendedStart { + arguments, + execution_context, + queue, + } => ( + VmOrArguments::Arguments(arguments), + execution_context, + queue, + ), + AsyncGeneratorState::SuspendedYield { + vm, + execution_context, + queue, + } => (VmOrArguments::Vm(vm), execution_context, queue), + _ => unreachable!(), + }; + async_generator_state.replace(AsyncGeneratorState::Executing(queue)); + (vm_or_args, execution_context, self.get_executable(agent)) + } + pub(crate) fn transition_to_suspended( self, agent: &mut Agent, vm: SuspendedVm, - executable: Executable, execution_context: ExecutionContext, ) { let async_generator_state = &mut agent[self].async_generator_state; let AsyncGeneratorState::Executing(queue) = async_generator_state.take().unwrap() else { unreachable!() }; - async_generator_state.replace(AsyncGeneratorState::Suspended { + async_generator_state.replace(AsyncGeneratorState::SuspendedYield { queue, - state: SuspendedGeneratorState { - vm_or_args: VmOrArguments::Vm(vm), - executable, - execution_context, - }, + vm, + execution_context, }); } @@ -245,25 +257,22 @@ impl AsyncGenerator<'_> { } return; } - let AsyncGeneratorState::Awaiting { state, queue, kind } = - agent[self].async_generator_state.take().unwrap() + let AsyncGeneratorState::Awaiting { + vm, + execution_context, + queue, + kind, + } = agent[self].async_generator_state.take().unwrap() else { unreachable!() }; - let SuspendedGeneratorState { - vm_or_args, - executable, - execution_context, - } = state; agent.execution_context_stack.push(execution_context); - let VmOrArguments::Vm(vm) = vm_or_args else { - unreachable!() - }; agent[self].async_generator_state = Some(AsyncGeneratorState::Executing(queue)); let scoped_generator = self.scope(agent, gc.nogc()); let execution_result = match kind { AsyncGeneratorAwaitKind::Await => { // Await only. + let executable = agent[self].executable.unwrap(); match reaction_type { PromiseReactionType::Fulfill => { vm.resume(agent, executable, value, gc.reborrow()) @@ -278,13 +287,13 @@ impl AsyncGenerator<'_> { if reaction_type == PromiseReactionType::Reject { // ? Yield ( ? Await ( Value ) ), so Yield doesn't get // performed at all and value is just thrown. + let executable = agent[self].executable.unwrap(); vm.resume_throw(agent, executable, value, gc.reborrow()) } else { async_generator_yield( agent, value, scoped_generator.clone(), - executable, vm, gc.reborrow(), ); @@ -295,6 +304,7 @@ impl AsyncGenerator<'_> { // 27.6.3.7 AsyncGeneratorUnwrapYieldResumption // 3. If awaited is a throw completion, return ? awaited. if reaction_type == PromiseReactionType::Reject { + let executable = agent[self].executable.unwrap(); vm.resume_throw(agent, executable, value, gc.reborrow()) } else { // TODO: vm.resume_return(agent, executable, value, gc.reborrow()) @@ -304,7 +314,7 @@ impl AsyncGenerator<'_> { } } }; - resume_handle_result(agent, execution_result, executable, scoped_generator, gc); + resume_handle_result(agent, execution_result, scoped_generator, gc); } } @@ -418,6 +428,7 @@ impl IndexMut> for Vec> { pub struct AsyncGeneratorHeapData { pub(crate) object_index: Option>, pub(crate) async_generator_state: Option, + pub(crate) executable: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -432,16 +443,20 @@ pub(crate) enum AsyncGeneratorAwaitKind { #[derive(Debug)] pub(crate) enum AsyncGeneratorState { - // SUSPENDED-START has `vm_or_args` set to Arguments, SUSPENDED-YIELD has it set to Vm. - Suspended { - state: SuspendedGeneratorState, + SuspendedStart { + arguments: Box<[Value]>, + execution_context: ExecutionContext, + queue: VecDeque, + }, + SuspendedYield { + vm: SuspendedVm, + execution_context: ExecutionContext, queue: VecDeque, }, Executing(VecDeque), Awaiting { - // TODO: Can never contain Arguments to the VmOrArgs is useless. - // Just put the SuspendedVm in there. - state: SuspendedGeneratorState, + vm: SuspendedVm, + execution_context: ExecutionContext, queue: VecDeque, kind: AsyncGeneratorAwaitKind, }, @@ -455,7 +470,10 @@ impl AsyncGeneratorState { } pub(crate) fn is_suspended(&self) -> bool { - matches!(self, Self::Suspended { .. }) + matches!( + self, + Self::SuspendedStart { .. } | Self::SuspendedYield { .. } + ) } pub(crate) fn is_active(&self) -> bool { @@ -556,15 +574,38 @@ impl HeapMarkAndSweep for AsyncGeneratorHeapData { let Self { object_index, async_generator_state: generator_state, + executable, } = self; object_index.mark_values(queues); + executable.mark_values(queues); let Some(generator_state) = generator_state else { return; }; match generator_state { - AsyncGeneratorState::Awaiting { state, queue, .. } - | AsyncGeneratorState::Suspended { state, queue } => { - state.mark_values(queues); + AsyncGeneratorState::SuspendedStart { + arguments, + execution_context, + queue, + } => { + arguments.mark_values(queues); + execution_context.mark_values(queues); + for req in queue { + req.mark_values(queues); + } + } + AsyncGeneratorState::Awaiting { + vm, + execution_context, + queue, + .. + } + | AsyncGeneratorState::SuspendedYield { + vm, + execution_context, + queue, + } => { + vm.mark_values(queues); + execution_context.mark_values(queues); for req in queue { req.mark_values(queues); } @@ -582,15 +623,38 @@ impl HeapMarkAndSweep for AsyncGeneratorHeapData { let Self { object_index, async_generator_state: generator_state, + executable, } = self; object_index.sweep_values(compactions); + executable.sweep_values(compactions); let Some(generator_state) = generator_state else { return; }; match generator_state { - AsyncGeneratorState::Awaiting { state, queue, .. } - | AsyncGeneratorState::Suspended { state, queue } => { - state.sweep_values(compactions); + AsyncGeneratorState::SuspendedStart { + arguments, + execution_context, + queue, + } => { + arguments.sweep_values(compactions); + execution_context.sweep_values(compactions); + for req in queue { + req.sweep_values(compactions); + } + } + AsyncGeneratorState::Awaiting { + vm, + queue, + execution_context, + .. + } + | AsyncGeneratorState::SuspendedYield { + vm, + execution_context, + queue, + } => { + vm.sweep_values(compactions); + execution_context.sweep_values(compactions); for req in queue { req.sweep_values(compactions); } 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 c37c0ffd..0f97574f 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 @@ -24,13 +24,13 @@ use crate::{ }, engine::{ context::{GcScope, NoGcScope}, - unwrap_try, Executable, ExecutionResult, Scoped, SuspendedVm, Vm, + unwrap_try, ExecutionResult, Scoped, SuspendedVm, Vm, }, }; use super::{ AsyncGenerator, AsyncGeneratorAwaitKind, AsyncGeneratorRequest, - AsyncGeneratorRequestCompletion, AsyncGeneratorState, SuspendedGeneratorState, VmOrArguments, + AsyncGeneratorRequestCompletion, AsyncGeneratorState, VmOrArguments, }; /// ### [27.6.3.2 AsyncGeneratorStart ( generator, generatorBody )](https://tc39.es/ecma262/#sec-asyncgeneratorstart) @@ -186,22 +186,10 @@ pub(super) fn async_generator_resume( ) { let generator = generator.bind(gc.nogc()); // 1. Assert: generator.[[AsyncGeneratorState]] is either suspended-start or suspended-yield. - let async_generator_state = &mut agent[generator].async_generator_state; - let AsyncGeneratorState::Suspended { - state: - SuspendedGeneratorState { - vm_or_args, - executable, - // 2. Let genContext be generator.[[AsyncGeneratorContext]]. - execution_context: gen_context, - }, - queue, - } = async_generator_state.take().unwrap() - else { - unreachable!() - }; + // 2. Let genContext be generator.[[AsyncGeneratorContext]]. // 5. Set generator.[[AsyncGeneratorState]] to executing. - async_generator_state.replace(AsyncGeneratorState::Executing(queue)); + assert!(generator.is_suspended_start(agent) || generator.is_suspended_yield(agent)); + let (vm_or_args, gen_context, executable) = generator.transition_to_execution(agent); // 3. Let callerContext be the running execution context. // 4. Suspend callerContext. @@ -229,14 +217,13 @@ pub(super) fn async_generator_resume( // 9. Assert: When we return here, genContext has already been removed from // the execution context stack and callerContext is the currently // running execution context. - resume_handle_result(agent, execution_result, executable, scoped_generator, gc); + resume_handle_result(agent, execution_result, scoped_generator, gc); // 10. Return unused. } pub(super) fn resume_handle_result( agent: &mut Agent, execution_result: ExecutionResult, - executable: Executable, scoped_generator: Scoped<'_, AsyncGenerator>, mut gc: GcScope, ) { @@ -291,7 +278,6 @@ pub(super) fn resume_handle_result( agent, scoped_generator, vm, - executable, yielded_value, AsyncGeneratorAwaitKind::Yield, gc, @@ -302,7 +288,6 @@ pub(super) fn resume_handle_result( agent, scoped_generator, vm, - executable, awaited_value, AsyncGeneratorAwaitKind::Await, gc, @@ -315,7 +300,6 @@ fn async_generator_perform_await( agent: &mut Agent, scoped_generator: Scoped<'_, AsyncGenerator>, vm: SuspendedVm, - executable: Executable, awaited_value: Value, kind: AsyncGeneratorAwaitKind, gc: GcScope, @@ -323,7 +307,7 @@ fn async_generator_perform_await( // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) let execution_context = agent.execution_context_stack.pop().unwrap(); let generator = scoped_generator.get(agent).bind(gc.nogc()); - generator.transition_to_awaiting(agent, vm, executable, kind, execution_context); + 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. @@ -344,23 +328,29 @@ fn async_generator_unwrap_yield_resumption( agent: &mut Agent, vm: SuspendedVm, generator: Scoped, - executable: Executable, resumption_value: AsyncGeneratorRequestCompletion, mut gc: GcScope, ) { // 1. If resumptionValue is not a return completion, return ? resumptionValue. let execution_result = match resumption_value { - AsyncGeneratorRequestCompletion::Ok(v) => vm.resume(agent, executable, v, gc.reborrow()), - AsyncGeneratorRequestCompletion::Err(e) => { - vm.resume_throw(agent, executable, e.value(), gc.reborrow()) - } + AsyncGeneratorRequestCompletion::Ok(v) => vm.resume( + agent, + generator.get(agent).get_executable(agent), + v, + gc.reborrow(), + ), + AsyncGeneratorRequestCompletion::Err(e) => vm.resume_throw( + agent, + generator.get(agent).get_executable(agent), + e.value(), + gc.reborrow(), + ), AsyncGeneratorRequestCompletion::Return(value) => { // 2. Let awaited be Completion(Await(resumptionValue.[[Value]])). async_generator_perform_await( agent, generator, vm, - executable, value, AsyncGeneratorAwaitKind::Return, gc.reborrow(), @@ -368,7 +358,7 @@ fn async_generator_unwrap_yield_resumption( return; } }; - resume_handle_result(agent, execution_result, executable, generator, gc); + resume_handle_result(agent, execution_result, generator, gc); } /// ### [27.6.3.8 AsyncGeneratorYield ( value )](https://tc39.es/ecma262/#sec-asyncgeneratoryield) @@ -380,7 +370,6 @@ pub(super) fn async_generator_yield( agent: &mut Agent, value: Value, generator: Scoped<'_, AsyncGenerator>, - executable: Executable, vm: SuspendedVm, gc: GcScope, ) { @@ -418,14 +407,7 @@ pub(super) fn async_generator_yield( // c. Let resumptionValue be Completion(toYield.[[Completion]]). let resumption_value = to_yield.completion; // d. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue). - async_generator_unwrap_yield_resumption( - agent, - vm, - generator, - executable, - resumption_value, - gc, - ); + async_generator_unwrap_yield_resumption(agent, vm, generator, resumption_value, gc); } else { // 12. Else, // a. Set generator.[[AsyncGeneratorState]] to suspended-yield. @@ -434,7 +416,7 @@ pub(super) fn async_generator_yield( // b. Remove genContext 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. - generator.transition_to_suspended(agent, vm, executable, gen_context); + generator.transition_to_suspended(agent, vm, gen_context); // c. Let callerContext be the running execution context. // d. Resume callerContext passing undefined. If genContext is ever // resumed again, let resumptionValue be the Completion Record with diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index eb7b8f0c..87fdf80b 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -37,6 +37,7 @@ use crate::{ BUILTIN_STRING_MEMORY, }, }, + engine::Executable, heap::{ indexes::ECMAScriptFunctionIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -323,6 +324,10 @@ impl<'a> ECMAScriptFunction<'a> { self.0.into_index() } + pub(crate) fn get_executable(self, agent: &Agent) -> Executable { + agent[self].compiled_bytecode.unwrap() + } + pub fn is_constructor(self, agent: &Agent) -> bool { // An ECMAScript function has the [[Construct]] slot if its constructor // status is something other than non-constructor. 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 fd8393c8..236cda17 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs @@ -470,12 +470,10 @@ pub(crate) fn evaluate_async_generator_body( agent[function_object].compiled_bytecode = Some(exe); exe }; - agent[generator].async_generator_state = Some(AsyncGeneratorState::Suspended { - state: SuspendedGeneratorState { - vm_or_args: VmOrArguments::Arguments(arguments_list.0.into()), - executable, - execution_context: agent.running_execution_context().clone(), - }, + agent[generator].executable = Some(executable); + agent[generator].async_generator_state = Some(AsyncGeneratorState::SuspendedStart { + arguments: arguments_list.0.into(), + execution_context: agent.running_execution_context().clone(), queue: VecDeque::new(), }); // 6. Return ReturnCompletion(generator).