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 90a9fc71e..5be2b8cef 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 6740edbbd..d8aa757a7 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 @@ -2,100 +2,686 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::engine::context::GcScope; +mod async_generator_abstract_operations; +mod async_generator_prototype; + +use std::{ + collections::VecDeque, + ops::{Index, IndexMut}, +}; + +use async_generator_abstract_operations::{ + async_generator_await_return_on_fulfilled, async_generator_await_return_on_rejected, + async_generator_yield, resume_handle_result, +}; +pub(crate) use async_generator_prototype::AsyncGeneratorPrototype; + use crate::{ ecmascript::{ - builders::ordinary_object_builder::OrdinaryObjectBuilder, - builtins::{ArgumentsList, Behaviour, Builtin}, - execution::{Agent, JsResult, RealmIdentifier}, - types::{IntoValue, String, Value, BUILTIN_STRING_MEMORY}, + builtins::control_abstraction_objects::{ + generator_objects::VmOrArguments, + promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, + }, + execution::{agent::JsError, Agent, ExecutionContext, ProtoIntrinsics}, + types::{ + InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, + }, + }, + engine::{ + context::{GcScope, NoGcScope}, + rootable::{HeapRootData, HeapRootRef, Rootable}, + Executable, ExecutionResult, Scoped, SuspendedVm, + }, + heap::{ + indexes::{AsyncGeneratorIndex, BaseIndex}, + CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, - heap::WellKnownSymbolIndexes, }; -pub(crate) struct AsyncGeneratorPrototype; - -struct AsyncGeneratorPrototypeNext; -impl Builtin for AsyncGeneratorPrototypeNext { - const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; - - const LENGTH: u8 = 1; - - const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::next); -} -struct AsyncGeneratorPrototypeReturn; -impl Builtin for AsyncGeneratorPrototypeReturn { - const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#return; - - const LENGTH: u8 = 1; - - const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::r#return); -} -struct AsyncGeneratorPrototypeThrow; -impl Builtin for AsyncGeneratorPrototypeThrow { - const NAME: String<'static> = BUILTIN_STRING_MEMORY.throw; - - const LENGTH: u8 = 1; - - const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::throw); -} - -impl AsyncGeneratorPrototype { - fn next( - _agent: &mut Agent, - _this_value: Value, - _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - todo!() - } - - fn r#return( - _agent: &mut Agent, - _this_value: Value, - _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - todo!() - } - - fn throw( - _agent: &mut Agent, - _this_value: Value, - _arguments: ArgumentsList, - _gc: GcScope, - ) -> JsResult { - todo!() - } - - pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { - let intrinsics = agent.get_realm(realm).intrinsics(); - let async_iterator_prototype = intrinsics.async_iterator_prototype(); - let async_generator_function_prototype = intrinsics.async_generator_function_prototype(); - let this = intrinsics.async_generator_prototype(); - - OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this) - .with_property_capacity(5) - .with_prototype(async_iterator_prototype) - .with_property(|builder| { - builder - .with_key(BUILTIN_STRING_MEMORY.constructor.into()) - .with_value_readonly(async_generator_function_prototype.into_value()) - .with_enumerable(false) - .with_configurable(true) - .build() - }) - .with_builtin_function_property::() - .with_builtin_function_property::() - .with_builtin_function_property::() - .with_property(|builder| { - builder - .with_key(WellKnownSymbolIndexes::ToStringTag.into()) - .with_value_readonly(BUILTIN_STRING_MEMORY.AsyncGenerator.into_value()) - .with_enumerable(false) - .with_configurable(true) - .build() - }) - .build(); +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>); + +impl AsyncGenerator<'_> { + /// Unbind this AsyncGenerator from its current lifetime. This is necessary to use + /// the AsyncGenerator as a parameter in a call that can perform garbage + /// collection. + pub fn unbind(self) -> AsyncGenerator<'static> { + unsafe { std::mem::transmute::>(self) } + } + + // Bind this AsyncGenerator to the garbage collection lifetime. This enables Rust's + // borrow checker to verify that your AsyncGenerators cannot not be invalidated by + // garbage collection being performed. + // + // This function is best called with the form + // ```rs + // let gen = gen.bind(&gc); + // ``` + // to make sure that the unbound AsyncGenerator cannot be used after binding. + pub const fn bind<'a>(self, _: NoGcScope<'a, '_>) -> AsyncGenerator<'a> { + unsafe { std::mem::transmute::>(self) } + } + + pub fn scope<'scope>( + self, + agent: &mut Agent, + gc: NoGcScope<'_, 'scope>, + ) -> Scoped<'scope, AsyncGenerator<'static>> { + Scoped::new(agent, self.unbind(), gc) + } + + pub(crate) const fn _def() -> Self { + Self(BaseIndex::from_u32_index(0)) + } + + pub(crate) const fn get_index(self) -> usize { + 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 { + agent[self] + .async_generator_state + .as_ref() + .unwrap() + .is_draining_queue() + } + + pub(crate) fn is_executing(self, agent: &Agent) -> bool { + agent[self] + .async_generator_state + .as_ref() + .unwrap() + .is_executing() + } + + pub(crate) fn is_suspended_start(self, agent: &Agent) -> bool { + agent[self] + .async_generator_state + .as_ref() + .unwrap() + .is_suspended_start() + } + + pub(crate) fn is_suspended_yield(self, agent: &Agent) -> bool { + agent[self] + .async_generator_state + .as_ref() + .unwrap() + .is_suspended_yield() + } + + pub(crate) fn is_completed(self, agent: &Agent) -> bool { + agent[self] + .async_generator_state + .as_ref() + .unwrap() + .is_completed() + } + + pub(crate) fn queue_is_empty(self, agent: &Agent) -> bool { + match agent[self].async_generator_state.as_ref().unwrap() { + AsyncGeneratorState::Awaiting { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } + | AsyncGeneratorState::Executing(queue) + | AsyncGeneratorState::DrainingQueue(queue) => queue.is_empty(), + AsyncGeneratorState::Completed => unreachable!(), + } + } + + pub(crate) fn peek_first(self, agent: &mut Agent) -> &AsyncGeneratorRequest { + match agent[self].async_generator_state.as_mut().unwrap() { + AsyncGeneratorState::Awaiting { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } + | AsyncGeneratorState::Executing(queue) + | AsyncGeneratorState::DrainingQueue(queue) => queue.front().unwrap(), + AsyncGeneratorState::Completed => unreachable!(), + } + } + + pub(crate) fn pop_first(self, agent: &mut Agent) -> AsyncGeneratorRequest { + match agent[self].async_generator_state.as_mut().unwrap() { + AsyncGeneratorState::Awaiting { queue, .. } + | AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } + | AsyncGeneratorState::Executing(queue) + | AsyncGeneratorState::DrainingQueue(queue) => queue.pop_front().unwrap(), + AsyncGeneratorState::Completed => unreachable!(), + } + } + + 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::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } + | AsyncGeneratorState::Executing(queue) + | AsyncGeneratorState::DrainingQueue(queue) => queue.push_back(request), + AsyncGeneratorState::Completed => unreachable!(), + } + } + + pub(crate) fn transition_to_draining_queue(self, agent: &mut Agent) { + let async_generator_state = &mut agent[self].async_generator_state; + let state = async_generator_state.take().unwrap(); + let queue = match state { + AsyncGeneratorState::SuspendedStart { queue, .. } + | AsyncGeneratorState::SuspendedYield { queue, .. } + | AsyncGeneratorState::Executing(queue) => queue, + _ => unreachable!(), + }; + async_generator_state.replace(AsyncGeneratorState::DrainingQueue(queue)); + } + + pub(crate) fn transition_to_awaiting( + self, + agent: &mut Agent, + vm: SuspendedVm, + kind: AsyncGeneratorAwaitKind, + 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::Awaiting { + queue, + 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, + 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::SuspendedYield { + queue, + vm, + execution_context, + }); + } + + pub(crate) fn resume_await( + self, + agent: &mut Agent, + reaction_type: PromiseReactionType, + value: Value, + mut gc: GcScope, + ) { + if self.is_draining_queue(agent) { + // We're coming here because return was called. + match reaction_type { + PromiseReactionType::Fulfill => { + // AsyncGeneratorAwaitReturn onFulfilled + async_generator_await_return_on_fulfilled(agent, self, value, gc); + } + PromiseReactionType::Reject => { + async_generator_await_return_on_rejected(agent, self, value, gc); + } + } + return; + } + let AsyncGeneratorState::Awaiting { + vm, + execution_context, + queue, + kind, + } = agent[self].async_generator_state.take().unwrap() + else { + unreachable!() + }; + agent.execution_context_stack.push(execution_context); + 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()) + } + PromiseReactionType::Reject => { + vm.resume_throw(agent, executable, value, gc.reborrow()) + } + } + } + AsyncGeneratorAwaitKind::Yield => { + // Await yield + 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(), + vm, + gc.reborrow(), + ); + return; + } + } + AsyncGeneratorAwaitKind::Return => { + // 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()) + // 4. Assert: awaited is a normal completion. + // 5. Return ReturnCompletion(awaited.[[Value]]). + ExecutionResult::Return(value) + } + } + }; + resume_handle_result(agent, execution_result, scoped_generator, gc); + } +} + +impl IntoValue for AsyncGenerator<'_> { + fn into_value(self) -> Value { + self.into() + } +} + +impl<'a> IntoObject<'a> for AsyncGenerator<'a> { + fn into_object(self) -> Object<'a> { + self.into() + } +} + +impl From> for Value { + fn from(val: AsyncGenerator) -> Self { + Value::AsyncGenerator(val.unbind()) + } +} + +impl<'a> From> for Object<'a> { + fn from(value: AsyncGenerator) -> Self { + Object::AsyncGenerator(value.unbind()) + } +} + +impl TryFrom for AsyncGenerator<'_> { + type Error = (); + + fn try_from(value: Value) -> Result { + if let Value::AsyncGenerator(value) = value { + Ok(value) + } else { + Err(()) + } + } +} + +impl<'a> TryFrom> for AsyncGenerator<'a> { + type Error = (); + + fn try_from(value: Object) -> Result { + if let Object::AsyncGenerator(value) = value { + Ok(value) + } else { + Err(()) + } + } +} + +impl<'a> InternalSlots<'a> for AsyncGenerator<'a> { + const DEFAULT_PROTOTYPE: ProtoIntrinsics = ProtoIntrinsics::AsyncGeneratorFunction; + + fn get_backing_object(self, agent: &Agent) -> Option> { + agent[self].object_index + } + + fn set_backing_object(self, agent: &mut Agent, backing_object: OrdinaryObject<'static>) { + assert!(agent[self] + .object_index + .replace(backing_object.unbind()) + .is_none()); + } +} + +impl<'a> InternalMethods<'a> for AsyncGenerator<'a> {} + +impl CreateHeapData> for Heap { + fn create(&mut self, data: AsyncGeneratorHeapData) -> AsyncGenerator<'static> { + self.async_generators.push(Some(data)); + AsyncGenerator(AsyncGeneratorIndex::last(&self.async_generators)) + } +} + +impl Index> for Agent { + type Output = AsyncGeneratorHeapData; + + fn index(&self, index: AsyncGenerator) -> &Self::Output { + &self.heap.async_generators[index] + } +} + +impl IndexMut> for Agent { + fn index_mut(&mut self, index: AsyncGenerator) -> &mut Self::Output { + &mut self.heap.async_generators[index] + } +} + +impl Index> for Vec> { + type Output = AsyncGeneratorHeapData; + + fn index(&self, index: AsyncGenerator) -> &Self::Output { + self.get(index.get_index()) + .expect("AsyncGenerator out of bounds") + .as_ref() + .expect("AsyncGenerator slot empty") + } +} + +impl IndexMut> for Vec> { + fn index_mut(&mut self, index: AsyncGenerator) -> &mut Self::Output { + self.get_mut(index.get_index()) + .expect("AsyncGenerator out of bounds") + .as_mut() + .expect("AsyncGenerator slot empty") + } +} + +#[derive(Debug, Default)] +pub struct AsyncGeneratorHeapData { + pub(crate) object_index: Option>, + pub(crate) async_generator_state: Option, + pub(crate) executable: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum AsyncGeneratorAwaitKind { + /// AsyncGenerator is currently executing an explicit await. + Await, + /// AsyncGenerator is currently executing a next(value)'s implicit await. + Yield, + /// AsyncGenerator is currently executing a return(value)'s implicit await. + Return, +} + +#[derive(Debug)] +pub(crate) enum AsyncGeneratorState { + SuspendedStart { + arguments: Box<[Value]>, + execution_context: ExecutionContext, + queue: VecDeque, + }, + SuspendedYield { + vm: SuspendedVm, + execution_context: ExecutionContext, + queue: VecDeque, + }, + Executing(VecDeque), + Awaiting { + vm: SuspendedVm, + execution_context: ExecutionContext, + queue: VecDeque, + kind: AsyncGeneratorAwaitKind, + }, + DrainingQueue(VecDeque), + Completed, +} + +impl AsyncGeneratorState { + pub(crate) fn is_completed(&self) -> bool { + matches!(self, Self::Completed) + } + + pub(crate) fn is_draining_queue(&self) -> bool { + matches!(self, AsyncGeneratorState::DrainingQueue(_)) + } + + pub(crate) fn is_executing(&self) -> bool { + matches!(self, AsyncGeneratorState::Executing(_)) + } + + pub(crate) fn is_suspended(&self) -> bool { + matches!( + self, + Self::SuspendedStart { .. } | Self::SuspendedYield { .. } + ) + } + + pub(crate) fn is_suspended_start(&self) -> bool { + matches!(self, AsyncGeneratorState::SuspendedStart { .. }) + } + + pub(crate) fn is_suspended_yield(&self) -> bool { + matches!(self, AsyncGeneratorState::SuspendedYield { .. }) + } + + pub(crate) fn is_active(&self) -> bool { + matches!( + self, + Self::Awaiting { .. } | Self::Executing { .. } | Self::DrainingQueue(_) + ) + } +} + +/// ## [27.6.3.1 AsyncGeneratorRequest Records](https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records) +/// +/// An AsyncGeneratorRequest is a Record value used to store information about +/// how an async generator should be resumed and contains capabilities for +/// fulfilling or rejecting the corresponding promise. +#[derive(Debug)] +pub(crate) struct AsyncGeneratorRequest { + /// \[\[Completion]] + pub(crate) completion: AsyncGeneratorRequestCompletion, + /// \[\[Capability]] + pub(crate) capability: PromiseCapability, +} + +#[derive(Debug, Clone, Copy)] +pub(crate) enum AsyncGeneratorRequestCompletion { + Ok(Value), + Err(JsError), + Return(Value), +} + +impl Rootable for AsyncGenerator<'_> { + type RootRepr = HeapRootRef; + + fn to_root_repr(value: Self) -> Result { + Err(HeapRootData::AsyncGenerator(value.unbind())) + } + + fn from_root_repr(value: &Self::RootRepr) -> Result { + Err(*value) + } + + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + heap_ref + } + + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::AsyncGenerator(object) => Some(object), + _ => None, + } + } +} + +impl HeapMarkAndSweep for AsyncGenerator<'static> { + fn mark_values(&self, queues: &mut WorkQueues) { + queues.async_generators.push(*self); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + compactions.async_generators.shift_index(&mut self.0); + } +} + +impl HeapMarkAndSweep for AsyncGeneratorRequest { + fn mark_values(&self, queues: &mut WorkQueues) { + let Self { + completion, + capability, + } = self; + match completion { + AsyncGeneratorRequestCompletion::Ok(value) + | AsyncGeneratorRequestCompletion::Return(value) => value.mark_values(queues), + AsyncGeneratorRequestCompletion::Err(err) => err.mark_values(queues), + } + capability.mark_values(queues); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + let Self { + completion, + capability, + } = self; + match completion { + AsyncGeneratorRequestCompletion::Ok(value) + | AsyncGeneratorRequestCompletion::Return(value) => value.sweep_values(compactions), + AsyncGeneratorRequestCompletion::Err(err) => err.sweep_values(compactions), + } + capability.sweep_values(compactions); + } +} + +impl HeapMarkAndSweep for AsyncGeneratorHeapData { + fn mark_values(&self, queues: &mut WorkQueues) { + 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::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); + } + } + AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => { + for req in queue { + req.mark_values(queues); + } + } + AsyncGeneratorState::Completed => {} + } + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + 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::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); + } + } + AsyncGeneratorState::Executing(queue) | AsyncGeneratorState::DrainingQueue(queue) => { + for req in queue { + req.sweep_values(compactions); + } + } + AsyncGeneratorState::Completed => {} + } } } 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 new file mode 100644 index 000000000..0f97574fb --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs @@ -0,0 +1,608 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use crate::{ + ecmascript::{ + abstract_operations::operations_on_iterator_objects::create_iter_result_object, + builtins::{ + promise::Promise, + promise_objects::{ + promise_abstract_operations::{ + promise_capability_records::PromiseCapability, + promise_reaction_records::PromiseReactionHandler, + }, + promise_prototype::inner_promise_then, + }, + ECMAScriptFunction, + }, + execution::{ + agent::{ExceptionType, JsError}, + Agent, JsResult, RealmIdentifier, + }, + types::Value, + }, + engine::{ + context::{GcScope, NoGcScope}, + unwrap_try, ExecutionResult, Scoped, SuspendedVm, Vm, + }, +}; + +use super::{ + AsyncGenerator, AsyncGeneratorAwaitKind, AsyncGeneratorRequest, + AsyncGeneratorRequestCompletion, AsyncGeneratorState, VmOrArguments, +}; + +/// ### [27.6.3.2 AsyncGeneratorStart ( generator, generatorBody )](https://tc39.es/ecma262/#sec-asyncgeneratorstart) +/// +/// Performs steps 4.f. through 4.l. +pub(crate) fn async_generator_start_result( + agent: &mut Agent, + generator: AsyncGenerator, + result: JsResult, + mut gc: GcScope, +) { + let generator = generator.bind(gc.nogc()); + let scoped_generator = generator.scope(agent, gc.nogc()); + // f. Remove acGenContext 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. + // g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue. + generator.transition_to_draining_queue(agent); + // h. If result is a normal completion, set result to + // NormalCompletion(undefined). + // i. If result is a return completion, set result to + // NormalCompletion(result.[[Value]]). + let result = result.unwrap_or_else(|e| e.value()); + // j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Ok(result), + true, + None, + gc.nogc(), + ); + // k. Perform AsyncGeneratorDrainQueue(acGenerator). + async_generator_drain_queue(agent, scoped_generator.get(agent), gc.reborrow()); + // l. Return undefined. +} + +/// ### [27.6.3.3 AsyncGeneratorValidate ( generator, generatorBrand )](https://tc39.es/ecma262/#sec-asyncgeneratorvalidate) +/// +/// The abstract operation AsyncGeneratorValidate takes arguments generator (an ECMAScript language value) and generatorBrand (a String or empty) and returns either a normal completion containing unused or a throw completion. It performs the following steps when called: +pub(super) fn async_generator_validate<'a>( + agent: &mut Agent, + generator: Value, + _generator_brand: (), + gc: NoGcScope<'a, '_>, +) -> JsResult> { + // 1. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]). + // 2. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorState]]). + // 3. Perform ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]). + // 4. If generator.[[GeneratorBrand]] is not generatorBrand, throw a TypeError exception. + // 5. Return unused. + if let Value::AsyncGenerator(generator) = generator { + Ok(generator) + } else { + Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Not an async generator object", + gc, + )) + } +} + +/// ### [27.6.3.4 AsyncGeneratorEnqueue ( generator, completion, promiseCapability )](https://tc39.es/ecma262/#sec-asyncgeneratorenqueue) +/// +/// The abstract operation AsyncGeneratorEnqueue takes arguments generator (an +/// AsyncGenerator), completion (a Completion Record), and promiseCapability +/// (a PromiseCapability Record) and returns unused. +pub(super) fn async_generator_enqueue( + agent: &mut Agent, + generator: AsyncGenerator, + completion: AsyncGeneratorRequestCompletion, + promise_capability: PromiseCapability, +) { + // 1. Let request be AsyncGeneratorRequest { [[Completion]]: completion, [[Capability]]: promiseCapability }. + let request = AsyncGeneratorRequest { + completion, + capability: promise_capability, + }; + // 2. Append request to generator.[[AsyncGeneratorQueue]]. + generator.append_to_queue(agent, request); + // 3. Return unused. +} + +/// ### [27.6.3.5 AsyncGeneratorCompleteStep ( generator, completion, done \[ , realm \] )](https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep) +/// +/// The abstract operation AsyncGeneratorCompleteStep takes arguments generator +/// (an AsyncGenerator), completion (a Completion Record), and done (a Boolean) +/// and optional argument realm (a Realm Record) and returns unused. +fn async_generator_complete_step( + agent: &mut Agent, + generator: AsyncGenerator, + completion: AsyncGeneratorRequestCompletion, + done: bool, + realm: Option, + gc: NoGcScope, +) { + // 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty. + assert!(!generator.queue_is_empty(agent)); + // 2. Let next be the first element of generator.[[AsyncGeneratorQueue]]. + // 3. Remove the first element from generator.[[AsyncGeneratorQueue]]. + let next = generator.pop_first(agent); + // 4. Let promiseCapability be next.[[Capability]]. + let promise_capability = next.capability; + // 5. Let value be completion.[[Value]]. + let value = match completion { + AsyncGeneratorRequestCompletion::Ok(value) => value, + // 6. If completion is a throw completion, then + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »). + AsyncGeneratorRequestCompletion::Err(err) => { + promise_capability.reject(agent, err.value()); + // 8. Return unused. + return; + } + // 7. Else, + // a. Assert: completion is a normal completion. + _ => unreachable!(), + }; + // b. If realm is present, then + let iterator_result = if let Some(realm) = realm { + // i. Let oldRealm be the running execution context's Realm. + let old_realm = agent.running_execution_context().realm; + let set_realm = realm != old_realm; + // ii. Set the running execution context's Realm to realm. + if set_realm { + agent.running_execution_context_mut().realm = realm; + } + // iii. Let iteratorResult be CreateIteratorResultObject(value, done). + let iterator_result = create_iter_result_object(agent, value, done, gc); + // iv. Set the running execution context's Realm to oldRealm. + if set_realm { + agent.running_execution_context_mut().realm = old_realm; + } + iterator_result + } else { + // c. Else, + // i. Let iteratorResult be CreateIteratorResultObject(value, done). + create_iter_result_object(agent, value, done, gc) + }; + // d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). + unwrap_try(promise_capability.try_resolve(agent, iterator_result.into_value(), gc)); + // 8. Return unused. +} + +/// ### [27.6.3.6 AsyncGeneratorResume ( generator, completion )](https://tc39.es/ecma262/#sec-asyncgeneratorresume) +/// +/// The abstract operation AsyncGeneratorResume takes arguments generator (an +/// AsyncGenerator) and completion (a Completion Record) and returns unused. +pub(super) fn async_generator_resume( + agent: &mut Agent, + generator: AsyncGenerator, + completion: AsyncGeneratorRequestCompletion, + mut gc: GcScope, +) { + let generator = generator.bind(gc.nogc()); + // 1. Assert: generator.[[AsyncGeneratorState]] is either suspended-start or suspended-yield. + // 2. Let genContext be generator.[[AsyncGeneratorContext]]. + // 5. Set generator.[[AsyncGeneratorState]] to executing. + 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. + // 6. Push genContext onto the execution context stack; genContext is now + // the running execution context. + agent.execution_context_stack.push(gen_context); + + let scoped_generator = generator.scope(agent, gc.nogc()); + + // 7. Resume the suspended evaluation of genContext using completion as the + // result of the operation that suspended it. Let result be the + // Completion Record returned by the resumed computation. + let execution_result = match vm_or_args { + VmOrArguments::Arguments(args) => { + Vm::execute(agent, executable, Some(&args), gc.reborrow()) + } + VmOrArguments::Vm(vm) => { + let AsyncGeneratorRequestCompletion::Ok(value) = completion else { + unreachable!() + }; + vm.resume(agent, executable, value, gc.reborrow()) + } + }; + // 8. Assert: result is never an abrupt completion. + // 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, scoped_generator, gc); + // 10. Return unused. +} + +pub(super) fn resume_handle_result( + agent: &mut Agent, + execution_result: ExecutionResult, + scoped_generator: Scoped<'_, AsyncGenerator>, + mut gc: GcScope, +) { + match execution_result { + ExecutionResult::Return(result) => { + // Function is done. + let _ = agent.execution_context_stack.pop().unwrap(); + let generator = scoped_generator.get(agent).bind(gc.nogc()); + // AsyncGeneratorStart step 4: + // g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue. + generator.transition_to_draining_queue(agent); + + // i. If result is a return completion, set result to NormalCompletion(result.[[Value]]). + // j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Ok(result), + true, + None, + gc.nogc(), + ); + // k. Perform AsyncGeneratorDrainQueue(acGenerator). + let generator = scoped_generator.get(agent).bind(gc.nogc()); + async_generator_drain_queue(agent, generator.unbind(), gc.reborrow()); + // l. Return undefined. + } + ExecutionResult::Throw(err) => { + // Function is done. + let _ = agent.execution_context_stack.pop().unwrap(); + let generator = scoped_generator.get(agent).bind(gc.nogc()); + // AsyncGeneratorStart step 4: + // g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue. + generator.transition_to_draining_queue(agent); + // j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true). + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Err(err), + true, + None, + gc.nogc(), + ); + // k. Perform AsyncGeneratorDrainQueue(acGenerator). + async_generator_drain_queue(agent, generator.unbind(), gc.reborrow()); + // l. Return undefined. + } + ExecutionResult::Yield { vm, yielded_value } => { + // 27.5.3.7 Yield ( value ) + // If generatorKind is async, return ? AsyncGeneratorYield(? Await(value)). + async_generator_perform_await( + agent, + scoped_generator, + vm, + yielded_value, + AsyncGeneratorAwaitKind::Yield, + gc, + ); + } + ExecutionResult::Await { vm, awaited_value } => { + async_generator_perform_await( + agent, + scoped_generator, + vm, + awaited_value, + AsyncGeneratorAwaitKind::Await, + gc, + ); + } + } +} + +fn async_generator_perform_await( + agent: &mut Agent, + scoped_generator: Scoped<'_, AsyncGenerator>, + vm: SuspendedVm, + awaited_value: Value, + kind: AsyncGeneratorAwaitKind, + gc: GcScope, +) { + // [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, 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); + + // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). + inner_promise_then(agent, promise, handler, handler, None); +} + +/// ### [27.6.3.7 AsyncGeneratorUnwrapYieldResumption ( resumptionValue )](https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption) +/// +/// The abstract operation AsyncGeneratorUnwrapYieldResumption takes argument +/// resumptionValue (a Completion Record) and returns either a normal +/// completion containing an ECMAScript language value or an abrupt completion. +fn async_generator_unwrap_yield_resumption( + agent: &mut Agent, + vm: SuspendedVm, + generator: Scoped, + 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, + 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, + value, + AsyncGeneratorAwaitKind::Return, + gc.reborrow(), + ); + return; + } + }; + resume_handle_result(agent, execution_result, generator, gc); +} + +/// ### [27.6.3.8 AsyncGeneratorYield ( value )](https://tc39.es/ecma262/#sec-asyncgeneratoryield) +/// +/// The abstract operation AsyncGeneratorYield takes argument value (an +/// ECMAScript language value) and returns either a normal completion +/// containing an ECMAScript language value or an abrupt completion. +pub(super) fn async_generator_yield( + agent: &mut Agent, + value: Value, + generator: Scoped<'_, AsyncGenerator>, + vm: SuspendedVm, + gc: GcScope, +) { + // 1. Let genContext be the running execution context. + let gen_context = agent.running_execution_context(); + // 2. Assert: genContext is the execution context of a generator. + // 3. Let generator be the value of the Generator component of genContext. + // 4. Assert: GetGeneratorKind() is async. + let generator_function = ECMAScriptFunction::try_from(gen_context.function.unwrap()).unwrap(); + let func_data = &agent[generator_function]; + assert!(func_data.ecmascript_function.is_async && func_data.ecmascript_function.is_generator); + // 5. Let completion be NormalCompletion(value). + let completion = AsyncGeneratorRequestCompletion::Ok(value); + // 6. Assert: The execution context stack has at least two elements. + assert!(agent.execution_context_stack.len() >= 2); + // 7. Let previousContext be the second to top element of the execution context stack. + let previous_context = &agent.execution_context_stack[agent.execution_context_stack.len() - 2]; + // 8. Let previousRealm be previousContext's Realm. + let previous_realm = previous_context.realm; + // 9. Perform AsyncGeneratorCompleteStep(generator, completion, false, previousRealm). + async_generator_complete_step( + agent, + generator.get(agent), + completion, + false, + Some(previous_realm), + gc.nogc(), + ); + // 10. Let queue be generator.[[AsyncGeneratorQueue]]. + // 11. If queue is not empty, then + if !generator.get(agent).queue_is_empty(agent) { + // a. NOTE: Execution continues without suspending the generator. + // b. Let toYield be the first element of queue. + let to_yield = generator.get(agent).peek_first(agent); + // 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, resumption_value, gc); + } else { + // 12. Else, + // a. Set generator.[[AsyncGeneratorState]] to suspended-yield. + let generator = generator.get(agent).bind(gc.nogc()); + let gen_context = agent.execution_context_stack.pop().unwrap(); + // 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, 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 + // which it is resumed. + // e. Assert: If control reaches here, then genContext is the running execution context again. + // f. Return ? AsyncGeneratorUnwrapYieldResumption(resumptionValue). + + // Note: Yield performs an implicit await on the value. + // [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 handler = PromiseReactionHandler::AsyncGenerator(generator.unbind()); + // 2. Let promise be ? PromiseResolve(%Promise%, value). + let promise = Promise::resolve(agent, value, gc); + + // 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected). + inner_promise_then(agent, promise, handler, handler, None); + } +} + +/// ### [27.6.3.9 AsyncGeneratorAwaitReturn ( generator )](https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn) +/// +/// The abstract operation AsyncGeneratorAwaitReturn takes argument generator +/// (an AsyncGenerator) and returns unused. +pub(super) fn async_generator_await_return( + agent: &mut Agent, + generator: AsyncGenerator, + mut gc: GcScope, +) { + let generator = generator.bind(gc.nogc()); + // 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue. + assert!(generator.is_draining_queue(agent)); + // 2. Let queue be generator.[[AsyncGeneratorQueue]]. + // 3. Assert: queue is not empty. + assert!(!generator.queue_is_empty(agent)); + // 4. Let next be the first element of queue. + let next = generator.peek_first(agent); + // 5. Let completion be Completion(next.[[Completion]]). + let completion = next.completion; + // 6. Assert: completion is a return completion. + let AsyncGeneratorRequestCompletion::Return(value) = completion else { + unreachable!() + }; + // 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%, completion.[[Value]])). + let generator = generator.scope(agent, gc.nogc()); + // 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, gc.reborrow()); + // 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(generator.get(agent)); + inner_promise_then(agent, promise, handler, handler, None); + // 16. Return unused. +} + +pub(crate) fn async_generator_await_return_on_fulfilled( + agent: &mut Agent, + generator: AsyncGenerator, + value: Value, + gc: GcScope, +) { + let generator = generator.bind(gc.nogc()); + let value = value.bind(gc.nogc()); + // 11. Let fulfilledClosure be a new Abstract Closure with parameters + // (value) that captures generator and performs the following steps + // when called: + // a. Assert: generator.[[AsyncGeneratorState]] is draining-queue. + assert!(generator.is_draining_queue(agent)); + // b. Let result be NormalCompletion(value). + // c. Perform AsyncGeneratorCompleteStep(generator, result, true). + let scoped_generator = generator.scope(agent, gc.nogc()); + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Ok(value), + true, + None, + gc.nogc(), + ); + // d. Perform AsyncGeneratorDrainQueue(generator). + async_generator_drain_queue(agent, scoped_generator.get(agent).unbind(), gc); + // e. Return undefined. +} + +pub(crate) fn async_generator_await_return_on_rejected( + agent: &mut Agent, + generator: AsyncGenerator, + value: Value, + gc: GcScope, +) { + let generator = generator.bind(gc.nogc()); + let value = value.bind(gc.nogc()); + // 13. Let rejectedClosure be a new Abstract Closure with parameters + // (reason) that captures generator and performs the following steps + // when called: + // a. Assert: generator.[[AsyncGeneratorState]] is draining-queue. + assert!(generator.is_draining_queue(agent)); + // b. Let result be ThrowCompletion(reason). + let scoped_generator = generator.scope(agent, gc.nogc()); + // c. Perform AsyncGeneratorCompleteStep(generator, result, true). + async_generator_complete_step( + agent, + generator.unbind(), + AsyncGeneratorRequestCompletion::Err(JsError::new(value)), + true, + None, + gc.nogc(), + ); + // d. Perform AsyncGeneratorDrainQueue(generator). + async_generator_drain_queue(agent, scoped_generator.get(agent).unbind(), gc); + // e. Return undefined. +} + +/// ### [27.6.3.10 AsyncGeneratorDrainQueue ( generator )](https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue) +/// +/// The abstract operation AsyncGeneratorDrainQueue takes argument generator +/// (an AsyncGenerator) and returns unused. It drains the generator's +/// AsyncGeneratorQueue until it encounters an AsyncGeneratorRequest which +/// holds a return completion. +fn async_generator_drain_queue(agent: &mut Agent, generator: AsyncGenerator, mut gc: GcScope) { + // Assert: generator.[[AsyncGeneratorState]] is draining-queue. + // 2. Let queue be generator.[[AsyncGeneratorQueue]]. + let Some(AsyncGeneratorState::DrainingQueue(queue)) = + &mut agent[generator].async_generator_state + else { + unreachable!() + }; + // 3. If queue is empty, then + if queue.is_empty() { + // a. Set generator.[[AsyncGeneratorState]] to completed. + agent[generator] + .async_generator_state + .replace(AsyncGeneratorState::Completed); + // b. Return unused. + return; + } + + // 4. Let done be false. + let mut done = false; + // 5. Repeat, while done is false, + while !done { + // a. Let next be the first element of queue. + let next = generator.peek_first(agent); + // b. Let completion be Completion(next.[[Completion]]). + let completion = next.completion; + // c. If completion is a return completion, then + if let AsyncGeneratorRequestCompletion::Return(_) = completion { + // i. Perform AsyncGeneratorAwaitReturn(generator). + async_generator_await_return(agent, generator, gc.reborrow()); + // ii. Set done to true. + done = true; + } else { + // d. Else, + // i. If completion is a normal completion, then + let completion = if let AsyncGeneratorRequestCompletion::Ok(_) = completion { + // 1. Set completion to NormalCompletion(undefined). + AsyncGeneratorRequestCompletion::Ok(Value::Undefined) + } else { + completion + }; + // ii. Perform AsyncGeneratorCompleteStep(generator, completion, true). + async_generator_complete_step(agent, generator, completion, true, None, gc.nogc()); + // iii. If queue is empty, then + let Some(AsyncGeneratorState::DrainingQueue(queue)) = + &mut agent[generator].async_generator_state + else { + unreachable!() + }; + if queue.is_empty() { + // 1. Set generator.[[AsyncGeneratorState]] to completed. + agent[generator] + .async_generator_state + .replace(AsyncGeneratorState::Completed); + // 2. Set done to true. + done = true + } + } + } + + // 6. Return unused. +} diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs new file mode 100644 index 000000000..bccb7ede9 --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs @@ -0,0 +1,245 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use crate::ecmascript::abstract_operations::operations_on_iterator_objects::create_iter_result_object; +use crate::ecmascript::builtins::async_generator_objects::AsyncGeneratorState; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_capability_records::{if_abrupt_reject_promise, PromiseCapability}; +use crate::ecmascript::execution::agent::JsError; +use crate::engine::context::GcScope; +use crate::{ + ecmascript::{ + builders::ordinary_object_builder::OrdinaryObjectBuilder, + builtins::{ArgumentsList, Behaviour, Builtin}, + execution::{Agent, JsResult, RealmIdentifier}, + types::{IntoValue, String, Value, BUILTIN_STRING_MEMORY}, + }, + heap::WellKnownSymbolIndexes, +}; + +use super::async_generator_abstract_operations::{ + async_generator_await_return, async_generator_enqueue, async_generator_resume, + async_generator_validate, +}; +use super::AsyncGeneratorRequestCompletion; + +pub(crate) struct AsyncGeneratorPrototype; + +struct AsyncGeneratorPrototypeNext; +impl Builtin for AsyncGeneratorPrototypeNext { + const NAME: String<'static> = BUILTIN_STRING_MEMORY.next; + + const LENGTH: u8 = 1; + + const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::next); +} +struct AsyncGeneratorPrototypeReturn; +impl Builtin for AsyncGeneratorPrototypeReturn { + const NAME: String<'static> = BUILTIN_STRING_MEMORY.r#return; + + const LENGTH: u8 = 1; + + const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::r#return); +} +struct AsyncGeneratorPrototypeThrow; +impl Builtin for AsyncGeneratorPrototypeThrow { + const NAME: String<'static> = BUILTIN_STRING_MEMORY.throw; + + const LENGTH: u8 = 1; + + const BEHAVIOUR: Behaviour = Behaviour::Regular(AsyncGeneratorPrototype::throw); +} + +impl AsyncGeneratorPrototype { + /// ### [27.6.1.2 %AsyncGeneratorPrototype%.next ( value )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next) + fn next( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + gc: GcScope, + ) -> JsResult { + let value = arguments.get(0); + // 1. Let generator be the this value. + let generator = this_value; + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new(agent); + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + let result = async_generator_validate(agent, generator, (), gc.nogc()); + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + Ok(g) => g, + Err(p) => { + return Ok(p.into_value()); + } + }; + // 5. Let state be generator.[[AsyncGeneratorState]]. + let state = agent[generator].async_generator_state.as_ref().unwrap(); + // 6. If state is completed, then + if state.is_completed() { + // a. Let iteratorResult be CreateIteratorResultObject(undefined, true). + let iterator_result = + create_iter_result_object(agent, Value::Undefined, true, gc.nogc()); + // b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »). + promise_capability.resolve(agent, iterator_result.into_value(), gc); + // c. Return promiseCapability.[[Promise]]. + return Ok(promise_capability.promise().into_value()); + } + let state_is_suspended = state.is_suspended(); + let state_is_executing_or_draining = state.is_active(); + // 7. Let completion be NormalCompletion(value). + let completion = AsyncGeneratorRequestCompletion::Ok(value); + // 8. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + async_generator_enqueue(agent, generator, completion, promise_capability); + // 9. If state is either suspended-start or suspended-yield, then + if state_is_suspended { + // a. Perform AsyncGeneratorResume(generator, completion). + async_generator_resume(agent, generator.unbind(), completion, gc); + } else { + // 10. Else, + // a. Assert: state is either executing or draining-queue. + assert!(state_is_executing_or_draining); + } + // 11. Return promiseCapability.[[Promise]]. + Ok(promise_capability.promise().into_value()) + } + + /// ### [27.6.1.3 %AsyncGeneratorPrototype%.return ( value )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return) + fn r#return( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + mut gc: GcScope, + ) -> JsResult { + let value = arguments.get(0).bind(gc.nogc()); + // 1. Let generator be the this value. + let generator = this_value.bind(gc.nogc()); + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new(agent); + let promise = promise_capability.promise().bind(gc.nogc()); + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + let result = async_generator_validate(agent, generator, (), gc.nogc()); + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + Ok(g) => g, + Err(p) => { + return Ok(p.into_value()); + } + }; + // 5. Let completion be ReturnCompletion(value). + let completion = AsyncGeneratorRequestCompletion::Return(value); + // 6. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + async_generator_enqueue(agent, generator, completion, promise_capability); + // 7. Let state be generator.[[AsyncGeneratorState]]. + if generator.is_suspended_start(agent) || generator.is_completed(agent) { + // 8. If state is either suspended-start or completed, then + let promise = promise.scope(agent, gc.nogc()); + // a. Set generator.[[AsyncGeneratorState]] to draining-queue. + generator.transition_to_draining_queue(agent); + // b. Perform AsyncGeneratorAwaitReturn(generator). + async_generator_await_return(agent, generator.unbind(), gc.reborrow()); + // 11. Return promiseCapability.[[Promise]]. + Ok(promise.get(agent).into_value()) + } else if generator.is_suspended_yield(agent) { + // 9. Else if state is suspended-yield, then + let promise = promise.scope(agent, gc.nogc()); + // a. Perform AsyncGeneratorResume(generator, completion). + async_generator_resume(agent, generator.unbind(), completion, gc.reborrow()); + // 11. Return promiseCapability.[[Promise]]. + Ok(promise.get(agent).into_value()) + } else { + // 10. Else, + // a. Assert: state is either executing or draining-queue. + assert!(generator.is_draining_queue(agent) || generator.is_executing(agent)); + // 11. Return promiseCapability.[[Promise]]. + Ok(promise.into_value()) + } + } + + /// ### [27.6.1.4 %AsyncGeneratorPrototype%.throw ( exception )](https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw) + fn throw( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + mut gc: GcScope, + ) -> JsResult { + let exception = arguments.get(0).bind(gc.nogc()); + // 1. Let generator be the this value. + let generator = this_value.bind(gc.nogc()); + // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%). + let promise_capability = PromiseCapability::new(agent); + let mut promise = promise_capability.promise().bind(gc.nogc()); + // 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)). + let result = async_generator_validate(agent, generator, (), gc.nogc()); + // 4. IfAbruptRejectPromise(result, promiseCapability). + let generator = match if_abrupt_reject_promise(agent, result, promise_capability) { + Ok(g) => g, + Err(p) => { + return Ok(p.into_value()); + } + }; + // 5. Let state be generator.[[AsyncGeneratorState]]. + // 6. If state is suspended-start, then + let mut completed = false; + if generator.is_suspended_start(agent) { + // a. Set generator.[[AsyncGeneratorState]] to completed. + agent[generator].async_generator_state = Some(AsyncGeneratorState::Completed); + // b. Set state to completed. + completed = true; + } + // 7. If state is completed, then + if completed || generator.is_completed(agent) { + // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « exception »). + promise_capability.reject(agent, exception); + // b. Return promiseCapability.[[Promise]]. + return Ok(promise.into_value()); + } + // 8. Let completion be ThrowCompletion(exception). + let completion = AsyncGeneratorRequestCompletion::Err(JsError::new(exception)); + // 9. Perform AsyncGeneratorEnqueue(generator, completion, promiseCapability). + async_generator_enqueue(agent, generator, completion, promise_capability); + // 10. If state is suspended-yield, then + if generator.is_suspended_yield(agent) { + // a. Perform AsyncGeneratorResume(generator, completion). + let scoped_promise = promise.scope(agent, gc.nogc()); + async_generator_resume(agent, generator.unbind(), completion, gc.reborrow()); + promise = scoped_promise.get(agent).bind(gc.nogc()); + } else { + // 11. Else, + // a. Assert: state is either executing or draining-queue. + assert!(generator.is_executing(agent) || generator.is_draining_queue(agent)); + } + // 12. Return promiseCapability.[[Promise]]. + Ok(promise.into_value()) + } + + pub(crate) fn create_intrinsic(agent: &mut Agent, realm: RealmIdentifier) { + let intrinsics = agent.get_realm(realm).intrinsics(); + let async_iterator_prototype = intrinsics.async_iterator_prototype(); + let async_generator_function_prototype = intrinsics.async_generator_function_prototype(); + let this = intrinsics.async_generator_prototype(); + + OrdinaryObjectBuilder::new_intrinsic_object(agent, realm, this) + .with_property_capacity(5) + .with_prototype(async_iterator_prototype) + .with_property(|builder| { + builder + .with_key(BUILTIN_STRING_MEMORY.constructor.into()) + .with_value_readonly(async_generator_function_prototype.into_value()) + .with_enumerable(false) + .with_configurable(true) + .build() + }) + .with_builtin_function_property::() + .with_builtin_function_property::() + .with_builtin_function_property::() + .with_property(|builder| { + builder + .with_key(WellKnownSymbolIndexes::ToStringTag.into()) + .with_value_readonly(BUILTIN_STRING_MEMORY.AsyncGenerator.into_value()) + .with_enumerable(false) + .with_configurable(true) + .build() + }) + .build(); + } +} diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs index 4a4833ac2..7f78019da 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs @@ -98,11 +98,11 @@ impl Generator<'_> { }; // 7. Set generator.[[GeneratorState]] to executing. - let Some(GeneratorState::Suspended { + let Some(GeneratorState::Suspended(SuspendedGeneratorState { vm_or_args, executable, execution_context, - }) = agent[generator] + })) = agent[generator] .generator_state .replace(GeneratorState::Executing) else { @@ -177,11 +177,12 @@ impl Generator<'_> { // GeneratorYield: // 3. Let generator be the value of the Generator component of genContext. // 5. Set generator.[[GeneratorState]] to suspended-yield. - agent[generator].generator_state = Some(GeneratorState::Suspended { - vm_or_args: VmOrArguments::Vm(vm), - executable, - execution_context, - }); + agent[generator].generator_state = + Some(GeneratorState::Suspended(SuspendedGeneratorState { + vm_or_args: VmOrArguments::Vm(vm), + executable, + execution_context, + })); // 8. Resume callerContext passing NormalCompletion(iterNextObj). ... // NOTE: `callerContext` here is the `GeneratorResume` execution context. Ok(create_iter_result_object( @@ -205,10 +206,10 @@ impl Generator<'_> { ) -> JsResult> { // 1. Let state be ? GeneratorValidate(generator, generatorBrand). match agent[self].generator_state.as_ref().unwrap() { - GeneratorState::Suspended { + GeneratorState::Suspended(SuspendedGeneratorState { vm_or_args: VmOrArguments::Arguments(_), .. - } => { + }) => { // 2. If state is suspended-start, then // a. Set generator.[[GeneratorState]] to completed. // b. NOTE: Once a generator enters the completed state it never leaves it and its @@ -239,11 +240,11 @@ impl Generator<'_> { }; // 8. Set generator.[[GeneratorState]] to executing. - let Some(GeneratorState::Suspended { + let Some(GeneratorState::Suspended(SuspendedGeneratorState { vm_or_args: VmOrArguments::Vm(vm), executable, execution_context, - }) = agent[self] + })) = agent[self] .generator_state .replace(GeneratorState::Executing) else { @@ -287,11 +288,12 @@ impl Generator<'_> { Err(err) } ExecutionResult::Yield { vm, yielded_value } => { - agent[self].generator_state = Some(GeneratorState::Suspended { - vm_or_args: VmOrArguments::Vm(vm), - executable, - execution_context, - }); + agent[self].generator_state = + Some(GeneratorState::Suspended(SuspendedGeneratorState { + vm_or_args: VmOrArguments::Vm(vm), + executable, + execution_context, + })); Ok(create_iter_result_object( agent, yielded_value, @@ -433,18 +435,51 @@ pub(crate) enum VmOrArguments { Arguments(Box<[Value]>), } +#[derive(Debug)] +pub(crate) struct SuspendedGeneratorState { + pub(crate) vm_or_args: VmOrArguments, + pub(crate) executable: Executable, + pub(crate) execution_context: ExecutionContext, +} + #[derive(Debug)] pub(crate) enum GeneratorState { // SUSPENDED-START has `vm_or_args` set to Arguments, SUSPENDED-YIELD has it set to Vm. - Suspended { - vm_or_args: VmOrArguments, - executable: Executable, - execution_context: ExecutionContext, - }, + Suspended(SuspendedGeneratorState), Executing, Completed, } +impl HeapMarkAndSweep for SuspendedGeneratorState { + fn mark_values(&self, queues: &mut WorkQueues) { + let Self { + vm_or_args, + executable, + execution_context, + } = self; + match vm_or_args { + VmOrArguments::Vm(vm) => vm.mark_values(queues), + VmOrArguments::Arguments(args) => args.as_ref().mark_values(queues), + } + executable.mark_values(queues); + execution_context.mark_values(queues); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + let Self { + vm_or_args, + executable, + execution_context, + } = self; + match vm_or_args { + VmOrArguments::Vm(vm) => vm.sweep_values(compactions), + VmOrArguments::Arguments(args) => args.as_ref().sweep_values(compactions), + } + executable.sweep_values(compactions); + execution_context.sweep_values(compactions); + } +} + impl HeapMarkAndSweep for GeneratorHeapData { fn mark_values(&self, queues: &mut WorkQueues) { let Self { @@ -452,18 +487,8 @@ impl HeapMarkAndSweep for GeneratorHeapData { generator_state, } = self; object_index.mark_values(queues); - if let Some(GeneratorState::Suspended { - vm_or_args, - executable, - execution_context, - }) = generator_state - { - match vm_or_args { - VmOrArguments::Vm(vm) => vm.mark_values(queues), - VmOrArguments::Arguments(args) => args.as_ref().mark_values(queues), - } - executable.mark_values(queues); - execution_context.mark_values(queues); + if let Some(GeneratorState::Suspended(state)) = generator_state { + state.mark_values(queues); } } @@ -473,18 +498,8 @@ impl HeapMarkAndSweep for GeneratorHeapData { generator_state, } = self; object_index.sweep_values(compactions); - if let Some(GeneratorState::Suspended { - vm_or_args, - executable, - execution_context, - }) = generator_state - { - match vm_or_args { - VmOrArguments::Vm(vm) => vm.sweep_values(compactions), - VmOrArguments::Arguments(args) => args.as_ref().sweep_values(compactions), - } - executable.sweep_values(compactions); - execution_context.sweep_values(compactions); + if let Some(GeneratorState::Suspended(state)) = generator_state { + state.sweep_values(compactions); } } } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs index ee86bfedd..e6921b9ec 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_capability_records.rs @@ -4,7 +4,9 @@ //! ## [27.2.1.1 PromiseCapability Records]() -use crate::engine::context::GcScope; +use crate::ecmascript::abstract_operations::operations_on_objects::try_get; +use crate::engine::context::{GcScope, NoGcScope}; +use crate::engine::TryResult; use crate::{ ecmascript::{ abstract_operations::operations_on_objects::get, @@ -13,7 +15,7 @@ use crate::{ Promise, }, execution::{ - agent::{ExceptionType, JsError, PromiseRejectionTrackerOperation}, + agent::{ExceptionType, PromiseRejectionTrackerOperation}, Agent, JsResult, }, types::{Function, IntoValue, Object, Value, BUILTIN_STRING_MEMORY}, @@ -211,6 +213,71 @@ impl PromiseCapability { // 16. Return undefined. } + /// [27.2.1.3.2 Promise Resolve Functions](https://tc39.es/ecma262/#sec-promise-resolve-functions) + pub fn try_resolve(self, agent: &mut Agent, resolution: Value, gc: NoGcScope) -> TryResult<()> { + // 1. Let F be the active function object. + // 2. Assert: F has a [[Promise]] internal slot whose value is an Object. + // 3. Let promise be F.[[Promise]]. + // 4. Let alreadyResolved be F.[[AlreadyResolved]]. + // 5. If alreadyResolved.[[Value]] is true, return undefined. + if self.is_already_resolved(agent) { + return TryResult::Continue(()); + } + // 6. Set alreadyResolved.[[Value]] to true. + match &mut agent[self.promise].promise_state { + PromiseState::Pending { is_resolved, .. } => *is_resolved = true, + _ => unreachable!(), + }; + + // 7. If SameValue(resolution, promise) is true, then + if resolution == self.promise.into_value() { + // a. Let selfResolutionError be a newly created TypeError object. + // b. Perform RejectPromise(promise, selfResolutionError). + let exception = agent.create_exception_with_static_message( + ExceptionType::TypeError, + "Tried to resolve a promise with itself.", + gc, + ); + self.internal_reject(agent, exception); + // c. Return undefined. + return TryResult::Continue(()); + } + + // 8. If resolution is not an Object, then + let Ok(resolution) = Object::try_from(resolution) else { + // a. Perform FulfillPromise(promise, resolution). + self.internal_fulfill(agent, resolution); + // b. Return undefined. + return TryResult::Continue(()); + }; + + // 9. Let then be Completion(Get(resolution, "then")). + // 10. If then is an abrupt completion, then + // a. Perform RejectPromise(promise, then.[[Value]]). + // b. Return undefined. + // 11. Let thenAction be then.[[Value]]. + let then_action = try_get(agent, resolution, BUILTIN_STRING_MEMORY.then.into(), gc)?; + + // 12. If IsCallable(thenAction) is false, then + // TODO: Callable proxies + let Ok(then_action) = Function::try_from(then_action) else { + // a. Perform FulfillPromise(promise, resolution). + self.internal_fulfill(agent, resolution.into_value()); + // b. Return undefined. + return TryResult::Continue(()); + }; + + // 13. Let thenJobCallback be HostMakeJobCallback(thenAction). + // TODO: Add the HostMakeJobCallback host hook. Leaving it for later, since in + // implementations other than browsers, [[HostDefine]] must be EMPTY. + // 14. Let job be NewPromiseResolveThenableJob(promise, resolution, thenJobCallback). + let job = new_promise_resolve_thenable_job(agent, self.promise, resolution, then_action); + // 15. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]). + agent.host_hooks.enqueue_promise_job(job); + // 16. Return undefined. + TryResult::Continue(()) + } + /// [27.2.1.3.1 Promise Reject Functions](https://tc39.es/ecma262/#sec-promise-reject-functions) pub fn reject(self, agent: &mut Agent, reason: Value) { // 1. Let F be the active function object. @@ -274,12 +341,12 @@ pub(crate) fn if_abrupt_reject_promise( agent: &mut Agent, value: JsResult, capability: PromiseCapability, -) -> JsResult { +) -> Result { value.map_err(|err| { capability.reject(agent, err.value()); // Note: We return an error here so that caller gets to call this // function with the ? operator - JsError::new(capability.promise().into_value()) + capability.promise() }) } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs index e75b3fe68..6ff504d5b 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs @@ -147,10 +147,15 @@ impl PromiseReactionJob { let reaction_type = agent[reaction].reaction_type; await_reaction.resume(agent, reaction_type, argument, gc.reborrow()); // [27.7.5.3 Await ( value )](https://tc39.es/ecma262/#await) - // 3. f. Return undefined. // 5. f. Return undefined. Ok(Value::Undefined) } + PromiseReactionHandler::AsyncGenerator(async_generator) => { + assert!(agent[reaction].capability.is_none()); + let reaction_type = agent[reaction].reaction_type; + async_generator.resume_await(agent, reaction_type, argument, gc.reborrow()); + Ok(Value::Undefined) + } }; // f. If promiseCapability is undefined, then @@ -205,7 +210,7 @@ pub(crate) fn new_promise_reaction_job( .realm, ), // 2. Let handlerRealm be null. - PromiseReactionHandler::Empty => None, + PromiseReactionHandler::AsyncGenerator(_) | PromiseReactionHandler::Empty => None, }; // 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }. diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs index b79df66f1..75c43be10 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs @@ -6,8 +6,12 @@ use std::ops::{Index, IndexMut}; use crate::{ ecmascript::{ - builtins::control_abstraction_objects::async_function_objects::await_reaction::AwaitReactionIdentifier, - execution::Agent, types::Function, + builtins::{ + async_generator_objects::AsyncGenerator, + control_abstraction_objects::async_function_objects::await_reaction::AwaitReactionIdentifier, + }, + execution::Agent, + types::Function, }, engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{indexes::BaseIndex, CreateHeapData, Heap, HeapMarkAndSweep}, @@ -21,7 +25,7 @@ use super::promise_capability_records::PromiseCapability; /// /// The \[\[Type\]\] is used when \[\[Handler\]\] is empty to allow for /// behaviour specific to the settlement type. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum PromiseReactionType { Fulfill, Reject, @@ -39,6 +43,7 @@ pub(crate) enum PromiseReactionType { pub(crate) enum PromiseReactionHandler { JobCallback(Function<'static>), Await(AwaitReactionIdentifier), + AsyncGenerator(AsyncGenerator<'static>), Empty, } diff --git a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs index 4d8dd7886..87fdf80b7 100644 --- a/nova_vm/src/ecmascript/builtins/ecmascript_function.rs +++ b/nova_vm/src/ecmascript/builtins/ecmascript_function.rs @@ -11,14 +11,6 @@ use oxc_ast::ast::{FormalParameters, FunctionBody}; use oxc_ecmascript::IsSimpleParameterList; use oxc_span::Span; -use crate::{ - ecmascript::types::{function_try_get, function_try_has_property, function_try_set}, - engine::{ - context::{GcScope, NoGcScope}, - rootable::{HeapRootData, HeapRootRef, Rootable}, - Scoped, TryResult, - }, -}; use crate::{ ecmascript::{ abstract_operations::type_conversion::to_object, @@ -45,11 +37,23 @@ use crate::{ BUILTIN_STRING_MEMORY, }, }, + engine::Executable, heap::{ indexes::ECMAScriptFunctionIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, }; +use crate::{ + ecmascript::{ + syntax_directed_operations::function_definitions::evaluate_async_generator_body, + types::{function_try_get, function_try_has_property, function_try_set}, + }, + engine::{ + context::{GcScope, NoGcScope}, + rootable::{HeapRootData, HeapRootRef, Rootable}, + Scoped, TryResult, + }, +}; use super::{ ordinary::{ordinary_create_from_constructor, ordinary_object_create_with_intrinsics}, @@ -320,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. @@ -813,7 +821,7 @@ pub(crate) fn evaluate_body( } // AsyncGeneratorBody : FunctionBody // 1. Return ? EvaluateAsyncGeneratorBody of AsyncGeneratorBody with arguments functionObject and argumentsList. - _ => todo!(), + _ => evaluate_async_generator_body(agent, function_object.unbind(), arguments_list, gc), } // Initializer : diff --git a/nova_vm/src/ecmascript/builtins/ordinary.rs b/nova_vm/src/ecmascript/builtins/ordinary.rs index 69cac9bae..3468a07cc 100644 --- a/nova_vm/src/ecmascript/builtins/ordinary.rs +++ b/nova_vm/src/ecmascript/builtins/ordinary.rs @@ -38,6 +38,7 @@ use super::regexp::RegExpHeapData; #[cfg(feature = "shared-array-buffer")] use super::shared_array_buffer::data::SharedArrayBufferHeapData; use super::{ + async_generator_objects::AsyncGeneratorHeapData, control_abstraction_objects::generator_objects::GeneratorHeapData, error::ErrorHeapData, finalization_registry::data::FinalizationRegistryHeapData, indexed_collections::array_objects::array_iterator_objects::array_iterator::ArrayIteratorHeapData, @@ -1216,6 +1217,10 @@ pub(crate) fn ordinary_object_create_with_intrinsics<'a>( )) .into_object(), ProtoIntrinsics::AsyncFunction => todo!(), + ProtoIntrinsics::AsyncGenerator => agent + .heap + .create(AsyncGeneratorHeapData::default()) + .into_object(), ProtoIntrinsics::AsyncGeneratorFunction => todo!(), #[cfg(feature = "array-buffer")] ProtoIntrinsics::BigInt64Array => agent @@ -1397,6 +1402,7 @@ pub(crate) fn get_prototype_from_constructor<'a>( #[cfg(feature = "array-buffer")] ProtoIntrinsics::ArrayBuffer => Some(intrinsics.array_buffer().into_function()), ProtoIntrinsics::AsyncFunction => Some(intrinsics.async_function().into_function()), + ProtoIntrinsics::AsyncGenerator => None, ProtoIntrinsics::AsyncGeneratorFunction => { Some(intrinsics.async_generator_function().into_function()) } diff --git a/nova_vm/src/ecmascript/execution/agent.rs b/nova_vm/src/ecmascript/execution/agent.rs index c1594f109..6c01afefe 100644 --- a/nova_vm/src/ecmascript/execution/agent.rs +++ b/nova_vm/src/ecmascript/execution/agent.rs @@ -18,7 +18,7 @@ use crate::{ builtins::{control_abstraction_objects::promise_objects::promise_abstract_operations::promise_jobs::{PromiseReactionJob, PromiseResolveThenableJob}, error::ErrorHeapData, promise::Promise}, scripts_and_modules::ScriptOrModule, types::{Function, IntoValue, Object, Reference, String, Symbol, Value}, - }, engine::{context::{GcScope, NoGcScope}, rootable::HeapRootData, TryResult, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, PrimitiveHeapIndexable}, Heap + }, engine::{context::{GcScope, NoGcScope}, rootable::HeapRootData, TryResult, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, HeapMarkAndSweep, PrimitiveHeapIndexable}, Heap }; use std::{any::Any, cell::RefCell, ptr::NonNull}; @@ -31,6 +31,7 @@ pub struct Options { pub type JsResult = std::result::Result; #[derive(Debug, Default, Clone, Copy)] +#[repr(transparent)] pub struct JsError(Value); impl JsError { @@ -47,6 +48,16 @@ impl JsError { } } +impl HeapMarkAndSweep for JsError { + fn mark_values(&self, queues: &mut crate::heap::WorkQueues) { + self.0.mark_values(queues); + } + + fn sweep_values(&mut self, compactions: &crate::heap::CompactionLists) { + self.0.sweep_values(compactions); + } +} + // #[derive(Debug)] // pub struct PreAllocated; @@ -520,6 +531,7 @@ pub(crate) fn resolve_binding<'a, 'b>( env: Option, gc: GcScope<'a, 'b>, ) -> JsResult> { + let name = name.bind(gc.nogc()); let env = env.unwrap_or_else(|| { // 1. If env is not present or env is undefined, then // a. Set env to the running execution context's LexicalEnvironment. @@ -542,7 +554,7 @@ pub(crate) fn resolve_binding<'a, 'b>( .is_strict_mode; // 4. Return ? GetIdentifierReference(env, name, strict). - get_identifier_reference(agent, Some(env), name, strict, gc) + get_identifier_reference(agent, Some(env), name.unbind(), strict, gc) } #[derive(Debug, Clone, Copy)] diff --git a/nova_vm/src/ecmascript/execution/environments.rs b/nova_vm/src/ecmascript/execution/environments.rs index d5b87e938..19503dc9f 100644 --- a/nova_vm/src/ecmascript/execution/environments.rs +++ b/nova_vm/src/ecmascript/execution/environments.rs @@ -73,9 +73,15 @@ macro_rules! create_environment_index { /// plus one. This allows us to not use an empty value in storage for /// the zero index while still saving room for a [`None`] value when /// stored in an [`Option`]. - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct $index(NonZeroU32, PhantomData<$name>); + impl std::fmt::Debug for $index { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "$index({:?})", self.0) + } + } + impl $index { /// Creates a new index from a u32. /// @@ -192,7 +198,7 @@ impl ModuleEnvironmentIndex { /// Environment Record, and Global Environment Record. Function Environment /// Records and Module Environment Records are subclasses of Declarative /// Environment Record. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub(crate) enum EnvironmentIndex { // Leave 0 for None option @@ -203,6 +209,18 @@ pub(crate) enum EnvironmentIndex { Object(ObjectEnvironmentIndex), } +impl std::fmt::Debug for EnvironmentIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EnvironmentIndex::Declarative(d) => write!(f, "DeclarativeEnvironment({:?})", d.0), + EnvironmentIndex::Function(d) => write!(f, "FunctionEnvironment({:?})", d.0), + EnvironmentIndex::Global(d) => write!(f, "GlobalEnvironment({:?})", d.0), + EnvironmentIndex::Object(d) => write!(f, "ObjectEnvironment({:?})", d.0), + // EnvironmentIndex::Module(d) => {} + } + } +} + impl EnvironmentIndex { pub(crate) fn get_outer_env(self, agent: &Agent) -> OuterEnv { match self { diff --git a/nova_vm/src/ecmascript/execution/realm/intrinsics.rs b/nova_vm/src/ecmascript/execution/realm/intrinsics.rs index 505eb5895..ff3056f43 100644 --- a/nova_vm/src/ecmascript/execution/realm/intrinsics.rs +++ b/nova_vm/src/ecmascript/execution/realm/intrinsics.rs @@ -169,6 +169,7 @@ pub enum ProtoIntrinsics { ArrayBuffer, ArrayIterator, AsyncFunction, + AsyncGenerator, AsyncGeneratorFunction, BigInt, #[cfg(feature = "array-buffer")] @@ -396,6 +397,7 @@ impl Intrinsics { ProtoIntrinsics::UriError => self.uri_error_prototype().into(), ProtoIntrinsics::AggregateError => self.aggregate_error_prototype().into(), ProtoIntrinsics::AsyncFunction => self.async_function_prototype().into(), + ProtoIntrinsics::AsyncGenerator => self.async_generator_prototype().into(), ProtoIntrinsics::AsyncGeneratorFunction => { self.async_generator_function_prototype().into() } 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 1a3929235..236cda17c 100644 --- a/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs +++ b/nova_vm/src/ecmascript/syntax_directed_operations/function_definitions.rs @@ -2,7 +2,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use std::collections::VecDeque; + use crate::ecmascript::abstract_operations::operations_on_objects::try_define_property_or_throw; +use crate::ecmascript::builtins::async_generator_objects::AsyncGeneratorState; +use crate::ecmascript::builtins::generator_objects::SuspendedGeneratorState; use crate::engine::context::{GcScope, NoGcScope}; use crate::engine::unwrap_try; use crate::{ @@ -414,12 +418,64 @@ pub(crate) fn evaluate_generator_body( // SAFETY: We're alive so SourceCode must be too. let data = CompileFunctionBodyData::new(agent, scoped_function_object.get(agent)); let executable = Executable::compile_function_body(agent, data, gc); - agent[generator].generator_state = Some(GeneratorState::Suspended { + agent[generator].generator_state = Some(GeneratorState::Suspended(SuspendedGeneratorState { vm_or_args: VmOrArguments::Arguments(arguments_list.0.into()), executable, execution_context: agent.running_execution_context().clone(), - }); + })); // 5. Return Completion Record { [[Type]]: return, [[Value]]: G, [[Target]]: empty }. Ok(generator.into_value()) } + +/// ### [15.6.2 Runtime Semantics: EvaluateAsyncGeneratorBody](https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody) +/// +/// The syntax-directed operation EvaluateAsyncGeneratorBody takes arguments +/// functionObject (an ECMAScript function object) and argumentsList (a List of +/// ECMAScript language values) and returns a throw completion or a return +/// completion. +pub(crate) fn evaluate_async_generator_body( + agent: &mut Agent, + function_object: ECMAScriptFunction, + arguments_list: ArgumentsList, + mut gc: GcScope<'_, '_>, +) -> JsResult { + let function_object = function_object.bind(gc.nogc()); + // 1. Perform ? FunctionDeclarationInstantiation(functionObject, argumentsList). + + let scoped_function_object = function_object.scope(agent, gc.nogc()); + // 2. Let generator be ? OrdinaryCreateFromConstructor(functionObject, + // "%AsyncGeneratorPrototype%", « [[AsyncGeneratorState]], + // [[AsyncGeneratorContext]], [[AsyncGeneratorQueue]], + // [[GeneratorBrand]] »). + let generator = ordinary_create_from_constructor( + agent, + function_object.into_function().unbind(), + ProtoIntrinsics::AsyncGenerator, + gc.reborrow(), + )?; + let Object::AsyncGenerator(generator) = generator else { + unreachable!() + }; + + // 3. Set generator.[[GeneratorBrand]] to empty. + // 4. Set generator.[[AsyncGeneratorState]] to suspended-start. + // 5. Perform AsyncGeneratorStart(generator, FunctionBody). + let function_object = scoped_function_object.get(agent).bind(gc.nogc()); + let executable = if let Some(exe) = agent[function_object].compiled_bytecode { + exe + } else { + let data = CompileFunctionBodyData::new(agent, function_object); + let exe = Executable::compile_function_body(agent, data, gc.nogc()); + agent[function_object].compiled_bytecode = Some(exe); + exe + }; + 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). + Ok(generator.into_value()) +} diff --git a/nova_vm/src/ecmascript/types/language.rs b/nova_vm/src/ecmascript/types/language.rs index 25cd484db..47181c1a0 100644 --- a/nova_vm/src/ecmascript/types/language.rs +++ b/nova_vm/src/ecmascript/types/language.rs @@ -47,7 +47,7 @@ pub(crate) use value::REGEXP_DISCRIMINANT; pub(crate) use value::SHARED_ARRAY_BUFFER_DISCRIMINANT; pub(crate) use value::{ ARGUMENTS_DISCRIMINANT, ARRAY_DISCRIMINANT, ARRAY_ITERATOR_DISCRIMINANT, - ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_ITERATOR_DISCRIMINANT, BIGINT_DISCRIMINANT, + ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_GENERATOR_DISCRIMINANT, BIGINT_DISCRIMINANT, BOOLEAN_DISCRIMINANT, BOUND_FUNCTION_DISCRIMINANT, BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, BUILTIN_FUNCTION_DISCRIMINANT, BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, BUILTIN_PROMISE_COLLECTOR_FUNCTION_DISCRIMINANT, diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 2a4dd086c..36e54150c 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -30,7 +30,7 @@ use super::value::{WEAK_MAP_DISCRIMINANT, WEAK_REF_DISCRIMINANT, WEAK_SET_DISCRI use super::{ value::{ ARGUMENTS_DISCRIMINANT, ARRAY_DISCRIMINANT, ARRAY_ITERATOR_DISCRIMINANT, - ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_ITERATOR_DISCRIMINANT, + ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_GENERATOR_DISCRIMINANT, BOUND_FUNCTION_DISCRIMINANT, BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, BUILTIN_FUNCTION_DISCRIMINANT, BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, BUILTIN_PROMISE_COLLECTOR_FUNCTION_DISCRIMINANT, @@ -66,6 +66,7 @@ use crate::{ use crate::{ ecmascript::{ builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, control_abstraction_objects::{ generator_objects::Generator, @@ -86,11 +87,7 @@ use crate::{ execution::{Agent, JsResult}, types::PropertyDescriptor, }, - engine::{ - context::GcScope, - rootable::{HeapRootData, HeapRootRef, Rootable}, - TryResult, - }, + engine::{context::GcScope, rootable::HeapRootData, TryResult}, heap::{ indexes::ObjectIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, }, @@ -171,7 +168,7 @@ pub enum Object<'a> { #[cfg(feature = "array-buffer")] Float64Array(TypedArrayIndex<'a>) = FLOAT_64_ARRAY_DISCRIMINANT, AsyncFromSyncIterator = ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, - AsyncIterator = ASYNC_ITERATOR_DISCRIMINANT, + AsyncGenerator(AsyncGenerator<'static>) = ASYNC_GENERATOR_DISCRIMINANT, Iterator = ITERATOR_DISCRIMINANT, ArrayIterator(ArrayIterator<'a>) = ARRAY_ITERATOR_DISCRIMINANT, #[cfg(feature = "set")] @@ -250,7 +247,7 @@ impl IntoValue for Object<'_> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => Value::Float64Array(data.unbind()), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => Value::AsyncGenerator(data), Object::Iterator => todo!(), Object::ArrayIterator(data) => Value::ArrayIterator(data.unbind()), #[cfg(feature = "set")] @@ -473,7 +470,7 @@ impl From> for Value { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => Value::Float64Array(data.unbind()), Object::AsyncFromSyncIterator => Value::AsyncFromSyncIterator, - Object::AsyncIterator => Value::AsyncIterator, + Object::AsyncGenerator(data) => Value::AsyncGenerator(data), Object::Iterator => Value::Iterator, Object::ArrayIterator(data) => Value::ArrayIterator(data.unbind()), #[cfg(feature = "set")] @@ -561,7 +558,7 @@ impl TryFrom for Object<'_> { #[cfg(feature = "array-buffer")] Value::Float64Array(data) => Ok(Object::Float64Array(data)), Value::AsyncFromSyncIterator => Ok(Object::AsyncFromSyncIterator), - Value::AsyncIterator => Ok(Object::AsyncIterator), + Value::AsyncGenerator(data) => Ok(Object::AsyncGenerator(data)), Value::Iterator => Ok(Object::Iterator), Value::ArrayIterator(data) => Ok(Object::ArrayIterator(data)), #[cfg(feature = "set")] @@ -674,7 +671,7 @@ impl Hash for Object<'_> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => data.into_index().hash(state), Object::AsyncFromSyncIterator => {} - Object::AsyncIterator => {} + Object::AsyncGenerator(data) => data.get_index().hash(state), Object::Iterator => {} Object::ArrayIterator(data) => data.get_index().hash(state), #[cfg(feature = "set")] @@ -766,7 +763,7 @@ impl<'a> InternalSlots<'a> for Object<'a> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => TypedArray::Float64Array(data).internal_extensible(agent), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_extensible(agent), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_extensible(agent), #[cfg(feature = "set")] @@ -862,7 +859,7 @@ impl<'a> InternalSlots<'a> for Object<'a> { TypedArray::Float64Array(data).internal_set_extensible(agent, value) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_set_extensible(agent, value), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_set_extensible(agent, value), #[cfg(feature = "set")] @@ -940,7 +937,7 @@ impl<'a> InternalSlots<'a> for Object<'a> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => TypedArray::Float64Array(data).internal_prototype(agent), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_prototype(agent), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_prototype(agent), #[cfg(feature = "set")] @@ -1038,7 +1035,7 @@ impl<'a> InternalSlots<'a> for Object<'a> { TypedArray::Float64Array(data).internal_set_prototype(agent, prototype) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_set_prototype(agent, prototype), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_set_prototype(agent, prototype), #[cfg(feature = "set")] @@ -1136,7 +1133,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_get_prototype_of(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_get_prototype_of(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_get_prototype_of(agent, gc), #[cfg(feature = "set")] @@ -1236,7 +1233,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_get_prototype_of(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_get_prototype_of(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_get_prototype_of(agent, gc), #[cfg(feature = "set")] @@ -1339,7 +1336,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_set_prototype_of(agent, prototype, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_set_prototype_of(agent, prototype, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_set_prototype_of(agent, prototype, gc), #[cfg(feature = "set")] @@ -1446,7 +1443,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_set_prototype_of(agent, prototype, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_set_prototype_of(agent, prototype, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_set_prototype_of(agent, prototype, gc), #[cfg(feature = "set")] @@ -1528,7 +1525,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_is_extensible(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_is_extensible(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_is_extensible(agent, gc), #[cfg(feature = "set")] @@ -1622,7 +1619,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_is_extensible(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_is_extensible(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_is_extensible(agent, gc), #[cfg(feature = "set")] @@ -1716,7 +1713,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_prevent_extensions(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_prevent_extensions(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_prevent_extensions(agent, gc), #[cfg(feature = "set")] @@ -1812,7 +1809,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_prevent_extensions(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_prevent_extensions(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_prevent_extensions(agent, gc), #[cfg(feature = "set")] @@ -1917,7 +1914,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_get_own_property(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_get_own_property(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_get_own_property(agent, property_key, gc), #[cfg(feature = "set")] @@ -2029,7 +2026,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_get_own_property(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_get_own_property(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_get_own_property(agent, property_key, gc), #[cfg(feature = "set")] @@ -2200,7 +2197,9 @@ impl<'a> InternalMethods<'a> for Object<'a> { gc, ), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => { + data.try_define_own_property(agent, property_key, property_descriptor, gc) + } Object::Iterator => todo!(), Object::ArrayIterator(data) => { data.try_define_own_property(agent, property_key, property_descriptor, gc) @@ -2363,7 +2362,9 @@ impl<'a> InternalMethods<'a> for Object<'a> { Object::Float64Array(data) => TypedArray::Float64Array(data) .internal_define_own_property(agent, property_key, property_descriptor, gc), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => { + data.internal_define_own_property(agent, property_key, property_descriptor, gc) + } Object::Iterator => todo!(), Object::ArrayIterator(data) => { data.internal_define_own_property(agent, property_key, property_descriptor, gc) @@ -2478,7 +2479,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_has_property(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_has_property(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_has_property(agent, property_key, gc), #[cfg(feature = "set")] @@ -2583,7 +2584,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_has_property(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_has_property(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_has_property(agent, property_key, gc), #[cfg(feature = "set")] @@ -2687,7 +2688,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_get(agent, property_key, receiver, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_get(agent, property_key, receiver, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_get(agent, property_key, receiver, gc), #[cfg(feature = "set")] @@ -2795,7 +2796,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_get(agent, property_key, receiver, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_get(agent, property_key, receiver, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_get(agent, property_key, receiver, gc), #[cfg(feature = "set")] @@ -2910,7 +2911,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_set(agent, property_key, value, receiver, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_set(agent, property_key, value, receiver, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_set(agent, property_key, value, receiver, gc), #[cfg(feature = "set")] @@ -3049,7 +3050,9 @@ impl<'a> InternalMethods<'a> for Object<'a> { gc, ), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => { + data.internal_set(agent, property_key, value, receiver, gc) + } Object::Iterator => todo!(), Object::ArrayIterator(data) => { data.internal_set(agent, property_key, value, receiver, gc) @@ -3158,7 +3161,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_delete(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_delete(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_delete(agent, property_key, gc), #[cfg(feature = "set")] @@ -3261,7 +3264,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_delete(agent, property_key, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_delete(agent, property_key, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_delete(agent, property_key, gc), #[cfg(feature = "set")] @@ -3357,7 +3360,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).try_own_property_keys(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.try_own_property_keys(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.try_own_property_keys(agent, gc), #[cfg(feature = "set")] @@ -3457,7 +3460,7 @@ impl<'a> InternalMethods<'a> for Object<'a> { TypedArray::Float64Array(data).internal_own_property_keys(agent, gc) } Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.internal_own_property_keys(agent, gc), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.internal_own_property_keys(agent, gc), #[cfg(feature = "set")] @@ -3574,7 +3577,7 @@ impl HeapMarkAndSweep for Object<'static> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => data.mark_values(queues), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.mark_values(queues), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.mark_values(queues), #[cfg(feature = "set")] @@ -3646,7 +3649,7 @@ impl HeapMarkAndSweep for Object<'static> { #[cfg(feature = "array-buffer")] Object::Float64Array(data) => data.sweep_values(compactions), Object::AsyncFromSyncIterator => todo!(), - Object::AsyncIterator => todo!(), + Object::AsyncGenerator(data) => data.sweep_values(compactions), Object::Iterator => todo!(), Object::ArrayIterator(data) => data.sweep_values(compactions), #[cfg(feature = "set")] @@ -3666,25 +3669,14 @@ impl CreateHeapData> for Heap { } } -impl Rootable for OrdinaryObject<'static> { - type RootRepr = HeapRootRef; - - fn to_root_repr(value: Self) -> Result { - Err(HeapRootData::Object(value)) - } - - fn from_root_repr(value: &Self::RootRepr) -> Result { - Err(*value) - } - - fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { - heap_ref - } +impl TryFrom for OrdinaryObject<'static> { + type Error = (); - fn from_heap_data(heap_data: HeapRootData) -> Option { - match heap_data { - HeapRootData::Object(object) => Some(object), - _ => None, + fn try_from(value: HeapRootData) -> Result { + if let HeapRootData::Object(value) = value { + Ok(value) + } else { + Err(()) } } } @@ -3694,10 +3686,10 @@ impl TryFrom for Object<'_> { fn try_from(value: HeapRootData) -> Result { match value { - HeapRootData::String(_) => Err(()), - HeapRootData::Symbol(_) => Err(()), - HeapRootData::Number(_) => Err(()), - HeapRootData::BigInt(_) => Err(()), + HeapRootData::String(_) + | HeapRootData::Symbol(_) + | HeapRootData::Number(_) + | HeapRootData::BigInt(_) => Err(()), HeapRootData::Object(ordinary_object) => Ok(Self::Object(ordinary_object)), HeapRootData::BoundFunction(bound_function) => Ok(Self::BoundFunction(bound_function)), HeapRootData::BuiltinFunction(builtin_function) => { @@ -3774,7 +3766,7 @@ impl TryFrom for Object<'_> { #[cfg(feature = "array-buffer")] HeapRootData::Float64Array(base_index) => Ok(Self::Float64Array(base_index)), HeapRootData::AsyncFromSyncIterator => Ok(Self::AsyncFromSyncIterator), - HeapRootData::AsyncIterator => Ok(Self::AsyncIterator), + HeapRootData::AsyncGenerator(gen) => Ok(Self::AsyncGenerator(gen)), HeapRootData::Iterator => Ok(Self::Iterator), HeapRootData::ArrayIterator(array_iterator) => Ok(Self::ArrayIterator(array_iterator)), #[cfg(feature = "set")] @@ -3786,8 +3778,6 @@ impl TryFrom for Object<'_> { Ok(Self::EmbedderObject(embedder_object)) } HeapRootData::PromiseReaction(_) => Err(()), - // Note: Do not use _ => Err(()) to make sure any added - // HeapRootData Value variants cause compile errors if not handled. } } } diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index 767c653c8..4854a5765 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -33,6 +33,7 @@ use crate::{ try_to_string, }, builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, control_abstraction_objects::{ generator_objects::Generator, @@ -206,7 +207,7 @@ pub enum Value { // Iterator objects // TODO: Figure out if these are needed at all. AsyncFromSyncIterator, - AsyncIterator, + AsyncGenerator(AsyncGenerator<'static>), Iterator, ArrayIterator(ArrayIterator<'static>), #[cfg(feature = "set")] @@ -341,7 +342,8 @@ pub(crate) const FLOAT_64_ARRAY_DISCRIMINANT: u8 = value_discriminant(Value::Float64Array(TypedArrayIndex::from_u32_index(0))); pub(crate) const ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT: u8 = value_discriminant(Value::AsyncFromSyncIterator); -pub(crate) const ASYNC_ITERATOR_DISCRIMINANT: u8 = value_discriminant(Value::AsyncIterator); +pub(crate) const ASYNC_GENERATOR_DISCRIMINANT: u8 = + value_discriminant(Value::AsyncGenerator(AsyncGenerator::_def())); pub(crate) const ITERATOR_DISCRIMINANT: u8 = value_discriminant(Value::Iterator); pub(crate) const ARRAY_ITERATOR_DISCRIMINANT: u8 = value_discriminant(Value::ArrayIterator(ArrayIterator::_def())); @@ -819,7 +821,10 @@ impl Value { data.into_index().hash(hasher); } Value::AsyncFromSyncIterator => todo!(), - Value::AsyncIterator => todo!(), + Value::AsyncGenerator(data) => { + discriminant.hash(hasher); + data.get_index().hash(hasher); + } Value::Iterator => todo!(), Value::ArrayIterator(data) => { discriminant.hash(hasher); @@ -1042,7 +1047,10 @@ impl Value { data.into_index().hash(hasher); } Value::AsyncFromSyncIterator => todo!(), - Value::AsyncIterator => todo!(), + Value::AsyncGenerator(data) => { + discriminant.hash(hasher); + data.get_index().hash(hasher); + } Value::Iterator => todo!(), Value::ArrayIterator(data) => { discriminant.hash(hasher); @@ -1251,7 +1259,7 @@ impl Rootable for Value { #[cfg(feature = "array-buffer")] Self::Float64Array(base_index) => Err(HeapRootData::Float64Array(base_index)), Self::AsyncFromSyncIterator => Err(HeapRootData::AsyncFromSyncIterator), - Self::AsyncIterator => Err(HeapRootData::AsyncIterator), + Self::AsyncGenerator(gen) => Err(HeapRootData::AsyncGenerator(gen)), Self::Iterator => Err(HeapRootData::Iterator), Self::ArrayIterator(array_iterator) => Err(HeapRootData::ArrayIterator(array_iterator)), #[cfg(feature = "set")] @@ -1369,7 +1377,7 @@ impl Rootable for Value { #[cfg(feature = "array-buffer")] HeapRootData::Float64Array(base_index) => Some(Self::Float64Array(base_index)), HeapRootData::AsyncFromSyncIterator => Some(Self::AsyncFromSyncIterator), - HeapRootData::AsyncIterator => Some(Self::AsyncIterator), + HeapRootData::AsyncGenerator(gen) => Some(Self::AsyncGenerator(gen)), HeapRootData::Iterator => Some(Self::Iterator), HeapRootData::ArrayIterator(array_iterator) => { Some(Self::ArrayIterator(array_iterator)) @@ -1476,7 +1484,7 @@ impl HeapMarkAndSweep for Value { Value::BuiltinPromiseCollectorFunction => todo!(), Value::BuiltinProxyRevokerFunction => todo!(), Value::AsyncFromSyncIterator => todo!(), - Value::AsyncIterator => todo!(), + Value::AsyncGenerator(data) => data.mark_values(queues), Value::Iterator => todo!(), Value::ArrayIterator(data) => data.mark_values(queues), #[cfg(feature = "set")] @@ -1561,7 +1569,7 @@ impl HeapMarkAndSweep for Value { Value::BuiltinPromiseCollectorFunction => todo!(), Value::BuiltinProxyRevokerFunction => todo!(), Value::AsyncFromSyncIterator => todo!(), - Value::AsyncIterator => todo!(), + Value::AsyncGenerator(data) => data.sweep_values(compactions), Value::Iterator => todo!(), Value::ArrayIterator(data) => data.sweep_values(compactions), #[cfg(feature = "set")] diff --git a/nova_vm/src/engine/bytecode/vm.rs b/nova_vm/src/engine/bytecode/vm.rs index 43b4ef3d0..a45e403a7 100644 --- a/nova_vm/src/engine/bytecode/vm.rs +++ b/nova_vm/src/engine/bytecode/vm.rs @@ -72,6 +72,7 @@ struct EmptyParametersList(ast::FormalParameters<'static>); unsafe impl Send for EmptyParametersList {} unsafe impl Sync for EmptyParametersList {} +#[derive(Debug)] pub(crate) enum ExecutionResult { Return(Value), Throw(JsError), @@ -2272,7 +2273,7 @@ fn typeof_operator(_: &mut Agent, val: Value) -> String { Value::Map(_) | Value::Promise(_) | Value::AsyncFromSyncIterator | - Value::AsyncIterator | + Value::AsyncGenerator(_) | Value::Iterator | Value::ArrayIterator(_) | Value::MapIterator(_) | diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs index 6b1da3fd3..e811c0260 100644 --- a/nova_vm/src/engine/rootable.rs +++ b/nova_vm/src/engine/rootable.rs @@ -47,6 +47,7 @@ use crate::heap::indexes::TypedArrayIndex; use crate::{ ecmascript::{ builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, embedder_object::EmbedderObject, error::Error, @@ -68,7 +69,7 @@ use crate::{ types::{ bigint::HeapBigInt, HeapNumber, HeapString, IntoObject, Object, OrdinaryObject, Symbol, ARGUMENTS_DISCRIMINANT, ARRAY_DISCRIMINANT, ARRAY_ITERATOR_DISCRIMINANT, - ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_ITERATOR_DISCRIMINANT, + ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_GENERATOR_DISCRIMINANT, BIGINT_DISCRIMINANT, BOUND_FUNCTION_DISCRIMINANT, BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, BUILTIN_FUNCTION_DISCRIMINANT, BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, @@ -101,6 +102,7 @@ mod private { use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set::WeakSet}; use crate::ecmascript::{ builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, embedder_object::EmbedderObject, error::Error, @@ -131,6 +133,7 @@ mod private { #[cfg(feature = "array-buffer")] impl RootableSealed for ArrayBuffer<'_> {} impl RootableSealed for ArrayIterator<'_> {} + impl RootableSealed for AsyncGenerator<'_> {} impl RootableSealed for BigInt<'_> {} impl RootableSealed for BoundFunction<'_> {} impl RootableSealed for BuiltinConstructorFunction<'_> {} @@ -313,7 +316,7 @@ pub enum HeapRootData { #[cfg(feature = "array-buffer")] Float64Array(TypedArrayIndex<'static>) = FLOAT_64_ARRAY_DISCRIMINANT, AsyncFromSyncIterator = ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, - AsyncIterator = ASYNC_ITERATOR_DISCRIMINANT, + AsyncGenerator(AsyncGenerator<'static>) = ASYNC_GENERATOR_DISCRIMINANT, Iterator = ITERATOR_DISCRIMINANT, ArrayIterator(ArrayIterator<'static>) = ARRAY_ITERATOR_DISCRIMINANT, #[cfg(feature = "set")] @@ -385,7 +388,7 @@ impl From> for HeapRootData { Object::Float32Array(base_index) => Self::Float32Array(base_index), Object::Float64Array(base_index) => Self::Float64Array(base_index), Object::AsyncFromSyncIterator => Self::AsyncFromSyncIterator, - Object::AsyncIterator => Self::AsyncIterator, + Object::AsyncGenerator(gen) => Self::AsyncGenerator(gen), Object::Iterator => Self::Iterator, Object::ArrayIterator(array_iterator) => Self::ArrayIterator(array_iterator), #[cfg(feature = "set")] @@ -510,7 +513,7 @@ impl HeapMarkAndSweep for HeapRootData { #[cfg(feature = "array-buffer")] HeapRootData::Float64Array(base_index) => base_index.mark_values(queues), HeapRootData::AsyncFromSyncIterator => todo!(), - HeapRootData::AsyncIterator => todo!(), + HeapRootData::AsyncGenerator(gen) => gen.mark_values(queues), HeapRootData::Iterator => todo!(), HeapRootData::ArrayIterator(array_iterator) => array_iterator.mark_values(queues), #[cfg(feature = "set")] @@ -601,7 +604,7 @@ impl HeapMarkAndSweep for HeapRootData { #[cfg(feature = "array-buffer")] HeapRootData::Float64Array(base_index) => base_index.sweep_values(compactions), HeapRootData::AsyncFromSyncIterator => todo!(), - HeapRootData::AsyncIterator => todo!(), + HeapRootData::AsyncGenerator(gen) => gen.sweep_values(compactions), HeapRootData::Iterator => todo!(), HeapRootData::ArrayIterator(array_iterator) => array_iterator.sweep_values(compactions), #[cfg(feature = "set")] diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 0c90c4c3b..6db0695bb 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -54,6 +54,7 @@ use crate::{ ecmascript::{ builtins::{ array_buffer::DetachKey, + async_generator_objects::AsyncGeneratorHeapData, control_abstraction_objects::{ async_function_objects::await_reaction::AwaitReaction, generator_objects::GeneratorHeapData, @@ -101,6 +102,7 @@ pub struct Heap { pub array_buffer_detach_keys: AHashMap, DetachKey>, pub arrays: Vec>, pub array_iterators: Vec>, + pub async_generators: Vec>, pub(crate) await_reactions: Vec>, pub bigints: Vec>, pub bound_functions: Vec>, @@ -207,6 +209,7 @@ impl Heap { array_buffer_detach_keys: AHashMap::with_capacity(0), arrays: Vec::with_capacity(1024), array_iterators: Vec::with_capacity(256), + async_generators: Vec::with_capacity(0), await_reactions: Vec::with_capacity(1024), bigints: Vec::with_capacity(1024), bound_functions: Vec::with_capacity(256), diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index dedc1befe..2a84b6dba 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -28,6 +28,7 @@ use crate::ecmascript::builtins::{ use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set::WeakSet}; use crate::ecmascript::{ builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, control_abstraction_objects::{ async_function_objects::await_reaction::AwaitReactionIdentifier, @@ -67,6 +68,7 @@ pub struct HeapBits { pub array_buffers: Box<[bool]>, pub arrays: Box<[bool]>, pub array_iterators: Box<[bool]>, + pub async_generators: Box<[bool]>, pub await_reactions: Box<[bool]>, pub bigints: Box<[bool]>, pub bound_functions: Box<[bool]>, @@ -133,6 +135,7 @@ pub(crate) struct WorkQueues { pub array_buffers: Vec>, pub arrays: Vec>, pub array_iterators: Vec>, + pub async_generators: Vec>, pub await_reactions: Vec, pub bigints: Vec>, pub bound_functions: Vec>, @@ -199,6 +202,7 @@ impl HeapBits { let array_buffers = vec![false; heap.array_buffers.len()]; let arrays = vec![false; heap.arrays.len()]; let array_iterators = vec![false; heap.array_iterators.len()]; + let async_generators = vec![false; heap.async_generators.len()]; let await_reactions = vec![false; heap.await_reactions.len()]; let bigints = vec![false; heap.bigints.len()]; let bound_functions = vec![false; heap.bound_functions.len()]; @@ -262,6 +266,7 @@ impl HeapBits { array_buffers: array_buffers.into_boxed_slice(), arrays: arrays.into_boxed_slice(), array_iterators: array_iterators.into_boxed_slice(), + async_generators: async_generators.into_boxed_slice(), await_reactions: await_reactions.into_boxed_slice(), bigints: bigints.into_boxed_slice(), bound_functions: bound_functions.into_boxed_slice(), @@ -331,6 +336,7 @@ impl WorkQueues { array_buffers: Vec::with_capacity(heap.array_buffers.len() / 4), arrays: Vec::with_capacity(heap.arrays.len() / 4), array_iterators: Vec::with_capacity(heap.array_iterators.len() / 4), + async_generators: Vec::with_capacity(heap.async_generators.len() / 4), await_reactions: Vec::with_capacity(heap.await_reactions.len() / 4), bigints: Vec::with_capacity(heap.bigints.len() / 4), bound_functions: Vec::with_capacity(heap.bound_functions.len() / 4), @@ -423,6 +429,7 @@ impl WorkQueues { array_buffers, arrays, array_iterators, + async_generators, await_reactions, bigints, bound_functions, @@ -508,6 +515,7 @@ impl WorkQueues { array_buffers.is_empty() && arrays.is_empty() && array_iterators.is_empty() + && async_generators.is_empty() && await_reactions.is_empty() && bigints.is_empty() && bound_functions.is_empty() @@ -716,6 +724,7 @@ pub(crate) struct CompactionLists { pub array_buffers: CompactionList, pub arrays: CompactionList, pub array_iterators: CompactionList, + pub async_generators: CompactionList, pub await_reactions: CompactionList, pub bigints: CompactionList, pub bound_functions: CompactionList, @@ -809,6 +818,7 @@ impl CompactionLists { #[cfg(feature = "array-buffer")] array_buffers: CompactionList::from_mark_bits(&bits.array_buffers), array_iterators: CompactionList::from_mark_bits(&bits.array_iterators), + async_generators: CompactionList::from_mark_bits(&bits.async_generators), await_reactions: CompactionList::from_mark_bits(&bits.await_reactions), bigints: CompactionList::from_mark_bits(&bits.bigints), bound_functions: CompactionList::from_mark_bits(&bits.bound_functions), diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 1a72874e3..00192a9b6 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -34,6 +34,7 @@ use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set use crate::{ ecmascript::{ builtins::{ + async_generator_objects::AsyncGenerator, bound_function::BoundFunction, control_abstraction_objects::{ async_function_objects::await_reaction::AwaitReactionIdentifier, @@ -143,6 +144,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option], g array_buffer_detach_keys: _, arrays, array_iterators, + async_generators, await_reactions, bigints, bound_functions, @@ -361,6 +363,20 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option], g array_iterators.get(index).mark_values(&mut queues); } }); + let mut async_generator_marks: Box<[AsyncGenerator]> = + queues.async_generators.drain(..).collect(); + async_generator_marks.sort(); + async_generator_marks.iter().for_each(|&idx| { + let index = idx.get_index(); + if let Some(marked) = bits.async_generators.get_mut(index) { + if *marked { + // Already marked, ignore + return; + } + *marked = true; + async_generators.get(index).mark_values(&mut queues); + } + }); let mut await_reaction_marks: Box<[AwaitReactionIdentifier]> = queues.await_reactions.drain(..).collect(); await_reaction_marks.sort(); @@ -1031,6 +1047,7 @@ fn sweep( array_buffer_detach_keys, arrays, array_iterators, + async_generators, await_reactions, bigints, bound_functions, @@ -1246,6 +1263,11 @@ fn sweep( sweep_heap_vector_values(array_iterators, &compactions, &bits.array_iterators); }); } + if !async_generators.is_empty() { + s.spawn(|| { + sweep_heap_vector_values(async_generators, &compactions, &bits.async_generators); + }); + } if !await_reactions.is_empty() { s.spawn(|| { sweep_heap_vector_values(await_reactions, &compactions, &bits.await_reactions); diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index 71efa4d03..198660806 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -25,6 +25,7 @@ use crate::ecmascript::builtins::{ use crate::{ ecmascript::{ builtins::{ + async_generator_objects::AsyncGeneratorHeapData, control_abstraction_objects::generator_objects::GeneratorHeapData, embedder_object::data::EmbedderObjectHeapData, error::ErrorHeapData, finalization_registry::data::FinalizationRegistryHeapData, @@ -187,6 +188,7 @@ impl<'a, T> Default for BaseIndex<'a, T> { pub type ArrayBufferIndex<'a> = BaseIndex<'a, ArrayBufferHeapData>; pub type ArrayIndex<'a> = BaseIndex<'a, ArrayHeapData>; pub type ArrayIteratorIndex<'a> = BaseIndex<'a, ArrayIteratorHeapData>; +pub type AsyncGeneratorIndex<'a> = BaseIndex<'a, AsyncGeneratorHeapData>; pub type BigIntIndex<'a> = BaseIndex<'a, BigIntHeapData>; pub type BoundFunctionIndex<'a> = BaseIndex<'a, BoundFunctionHeapData>; pub type BuiltinFunctionIndex<'a> = BaseIndex<'a, BuiltinFunctionHeapData>; diff --git a/tests/expectations.json b/tests/expectations.json index 68933066c..a3395fe7f 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -559,17 +559,10 @@ "built-ins/AsyncGeneratorFunction/prototype/constructor.js": "FAIL", "built-ins/AsyncGeneratorFunction/prototype/prototype.js": "FAIL", "built-ins/AsyncGeneratorPrototype/Symbol.toStringTag.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/next/iterator-result-prototype.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/next/request-queue-await-order.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/next/request-queue-order-state-executing.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/request-queue-order.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/request-queue-promise-resolve-order.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/next/return-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/this-val-not-async-generator.js": "CRASH", "built-ins/AsyncGeneratorPrototype/next/this-val-not-object.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/return/iterator-result-prototype.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/return/request-queue-order-state-executing.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/return/return-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-state-completed.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js": "CRASH", @@ -583,8 +576,6 @@ "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/this-val-not-async-generator.js": "CRASH", "built-ins/AsyncGeneratorPrototype/return/this-val-not-object.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/throw/request-queue-order-state-executing.js": "CRASH", - "built-ins/AsyncGeneratorPrototype/throw/return-rejected-promise.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/this-val-not-async-generator.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/this-val-not-object.js": "CRASH", "built-ins/AsyncGeneratorPrototype/throw/throw-state-completed.js": "CRASH", @@ -11404,11 +11395,6 @@ "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-single-args.js": "CRASH", "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-spread-operator.js": "CRASH", "language/arguments-object/async-gen-named-func-expr-args-trailing-comma-undefined.js": "CRASH", - "language/arguments-object/cls-decl-async-gen-func-args-trailing-comma-multiple.js": "CRASH", - "language/arguments-object/cls-decl-async-gen-func-args-trailing-comma-null.js": "CRASH", - "language/arguments-object/cls-decl-async-gen-func-args-trailing-comma-single-args.js": "CRASH", - "language/arguments-object/cls-decl-async-gen-func-args-trailing-comma-spread-operator.js": "CRASH", - "language/arguments-object/cls-decl-async-gen-func-args-trailing-comma-undefined.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-multiple.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-null.js": "CRASH", "language/arguments-object/cls-decl-async-gen-meth-args-trailing-comma-single-args.js": "CRASH", @@ -12586,7 +12572,6 @@ "language/expressions/async-generator/yield-star-sync-throw.js": "CRASH", "language/expressions/async-generator/yield-thenable-create-resolving-functions-reject.js": "CRASH", "language/expressions/async-generator/yield-thenable-create-resolving-functions-resolve.js": "CRASH", - "language/expressions/await/async-generator-interleaved.js": "CRASH", "language/expressions/await/await-BindingIdentifier-in-global.js": "FAIL", "language/expressions/await/await-in-function.js": "FAIL", "language/expressions/await/await-in-generator.js": "FAIL", @@ -16758,206 +16743,70 @@ "language/statements/async-function/unscopables-with-in-nested-fn.js": "FAIL", "language/statements/async-function/unscopables-with.js": "FAIL", "language/statements/async-generator/dflt-params-abrupt.js": "CRASH", - "language/statements/async-generator/dflt-params-arg-val-not-undefined.js": "CRASH", - "language/statements/async-generator/dflt-params-arg-val-undefined.js": "CRASH", "language/statements/async-generator/dflt-params-ref-later.js": "CRASH", - "language/statements/async-generator/dflt-params-ref-prior.js": "CRASH", "language/statements/async-generator/dflt-params-ref-self.js": "CRASH", - "language/statements/async-generator/dflt-params-trailing-comma.js": "CRASH", - "language/statements/async-generator/dstr/ary-init-iter-close.js": "CRASH", "language/statements/async-generator/dstr/ary-init-iter-get-err-array-prototype.js": "CRASH", "language/statements/async-generator/dstr/ary-init-iter-get-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-init-iter-no-close.js": "CRASH", - "language/statements/async-generator/dstr/ary-name-iter-val.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-undef.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-done.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-obj-id.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-null.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elision-exhausted.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-elision-step-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-elision.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-empty.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-ary-elem.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-ary-elision.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-ary-empty.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-ary-rest.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-id-direct.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-id-elision.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-id-exhausted.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/statements/async-generator/dstr/ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-id.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-obj-id.js": "CRASH", - "language/statements/async-generator/dstr/ary-ptrn-rest-obj-prop-id.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-init-iter-close.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-init-iter-get-err-array-prototype.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-init-iter-get-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-init-iter-no-close.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-name-iter-val.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-elem-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-elem-iter.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-elision-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-elision-iter.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-empty-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-empty-iter.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-rest-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-rest-iter.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-ary-val-null.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-exhausted.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-arrow.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-class.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-cover.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-fn.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-fn-name-gen.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-hole.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-throws.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-undef.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-complete.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-done.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-step-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val-array-prototype.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-id-iter-val.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-id-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-id.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-prop-id-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-prop-id.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-null.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elem-obj-val-undef.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elision-exhausted.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-elision-step-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-elision.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-empty.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-ary-elem.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-ary-elision.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-ary-empty.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-ary-rest.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-direct.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-elision-next-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-elision.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-exhausted.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-step-err.js": "CRASH", "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id-iter-val-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-id.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-obj-id.js": "CRASH", - "language/statements/async-generator/dstr/dflt-ary-ptrn-rest-obj-prop-id.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-init-null.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-init-undefined.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-empty.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-id-get-value-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-throws.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-id-trailing-comma.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-list-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-ary.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-eval-err.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-throws.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-init.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-id.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-init.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-null.js": "CRASH", "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-prop-obj.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-rest-getter.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/statements/async-generator/dstr/dflt-obj-ptrn-rest-val-obj.js": "CRASH", "language/statements/async-generator/dstr/obj-init-null.js": "CRASH", "language/statements/async-generator/dstr/obj-init-undefined.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-empty.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-id-get-value-err.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-fn-name-arrow.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-fn-name-class.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-fn-name-cover.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-fn-name-fn.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-fn-name-gen.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-id-init-throws.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-id-trailing-comma.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-list-err.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-ary-init.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-ary-trailing-comma.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-ary-value-null.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-ary.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-eval-err.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-id-get-value-err.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-id-init-skipped.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-id-init-throws.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-id-init-unresolvable.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-id-init.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-id-trailing-comma.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-id.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-obj-init.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-null.js": "CRASH", "language/statements/async-generator/dstr/obj-ptrn-prop-obj-value-undef.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-prop-obj.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-rest-getter.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-rest-skip-non-enumerable.js": "CRASH", - "language/statements/async-generator/dstr/obj-ptrn-rest-val-obj.js": "CRASH", "language/statements/async-generator/eval-var-scope-syntax-err.js": "CRASH", - "language/statements/async-generator/forbidden-ext/b1/async-gen-func-decl-forbidden-ext-direct-access-prop-arguments.js": "CRASH", - "language/statements/async-generator/forbidden-ext/b1/async-gen-func-decl-forbidden-ext-direct-access-prop-caller.js": "CRASH", - "language/statements/async-generator/forbidden-ext/b2/async-gen-func-decl-forbidden-ext-indirect-access-own-prop-caller-get.js": "CRASH", - "language/statements/async-generator/forbidden-ext/b2/async-gen-func-decl-forbidden-ext-indirect-access-own-prop-caller-value.js": "CRASH", - "language/statements/async-generator/forbidden-ext/b2/async-gen-func-decl-forbidden-ext-indirect-access-prop-caller.js": "CRASH", "language/statements/async-generator/generator-created-after-decl-inst.js": "CRASH", - "language/statements/async-generator/params-trailing-comma-multiple.js": "CRASH", - "language/statements/async-generator/params-trailing-comma-single.js": "CRASH", - "language/statements/async-generator/return-undefined-implicit-and-explicit.js": "CRASH", + "language/statements/async-generator/return-undefined-implicit-and-explicit.js": "FAIL", "language/statements/async-generator/unscopables-with-in-nested-fn.js": "FAIL", "language/statements/async-generator/unscopables-with.js": "FAIL", - "language/statements/async-generator/yield-identifier-non-strict.js": "CRASH", "language/statements/async-generator/yield-identifier-spread-non-strict.js": "CRASH", - "language/statements/async-generator/yield-promise-reject-next-catch.js": "CRASH", "language/statements/async-generator/yield-promise-reject-next-for-await-of-async-iterator.js": "CRASH", "language/statements/async-generator/yield-promise-reject-next-for-await-of-sync-iterator.js": "CRASH", "language/statements/async-generator/yield-promise-reject-next-yield-star-async-iterator.js": "CRASH", "language/statements/async-generator/yield-promise-reject-next-yield-star-sync-iterator.js": "CRASH", - "language/statements/async-generator/yield-promise-reject-next.js": "CRASH", "language/statements/async-generator/yield-return-then-getter-ticks.js": "CRASH", "language/statements/async-generator/yield-spread-arr-multiple.js": "CRASH", "language/statements/async-generator/yield-spread-arr-single.js": "CRASH", @@ -21963,7 +21812,6 @@ "staging/sm/Array/unscopables.js": "CRASH", "staging/sm/Array/values.js": "FAIL", "staging/sm/ArrayBuffer/slice-species.js": "CRASH", - "staging/sm/AsyncGenerators/async-generator-declaration-in-modules.js": "CRASH", "staging/sm/Atomics/cross-compartment.js": "FAIL", "staging/sm/Atomics/detached-buffers.js": "CRASH", "staging/sm/BigInt/Number-conversion-rounding.js": "CRASH", diff --git a/tests/metrics.json b/tests/metrics.json index ea72b7b41..11d2eb233 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { - "crash": 14931, - "fail": 7665, - "pass": 24138, + "crash": 13215, + "fail": 9229, + "pass": 24290, "skip": 55, "timeout": 12, "unresolved": 0