diff --git a/nova_vm/src/builtin_strings b/nova_vm/src/builtin_strings index e6c73e32e..e9f44e7b9 100644 --- a/nova_vm/src/builtin_strings +++ b/nova_vm/src/builtin_strings @@ -150,6 +150,7 @@ fromCharCode fromCodePoint fromEntries #[cfg(feature = "math")]fround +fulfilled function Function Generator @@ -320,6 +321,7 @@ race #[cfg(feature = "math")]random RangeError raw +reason reduce reduceRight ReferenceError @@ -328,6 +330,7 @@ Reflect #[cfg(feature = "regexp")]RegExp String Iterator register reject +rejected repeat replace replaceAll @@ -389,6 +392,7 @@ split #[cfg(feature = "math")]SQRT1_2 #[cfg(feature = "math")]SQRT2 startsWith +status #[cfg(feature = "regexp")]sticky #[cfg(feature = "atomics")]store #[cfg(feature = "annex-b-string")]strike diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations.rs index e9c1c978b..812a219b2 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations.rs @@ -2,9 +2,9 @@ // 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/. -pub(crate) mod promise_all_record; pub mod promise_capability_records; pub(crate) mod promise_finally_functions; +pub(crate) mod promise_group_record; pub(crate) mod promise_jobs; pub(crate) mod promise_reaction_records; pub(crate) mod promise_resolving_functions; diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_record.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_record.rs deleted file mode 100644 index 9e8229254..000000000 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_record.rs +++ /dev/null @@ -1,184 +0,0 @@ -// 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::{ - builtins::{ - Array, promise::Promise, - promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, - }, - execution::Agent, - types::{IntoValue, Value}, - }, - engine::{ - context::{Bindable, GcScope, NoGcScope, bindable_handle}, - rootable::{HeapRootData, HeapRootRef, Rootable}, - }, - heap::{ - CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, - }, -}; - -#[derive(Debug, Clone, Copy)] -pub struct PromiseAllRecord<'a> { - pub(crate) remaining_elements_count: u32, - pub(crate) result_array: Array<'a>, - pub(crate) promise: Promise<'a>, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>); - -impl<'a> PromiseAll<'a> { - pub(crate) fn on_promise_fulfilled( - self, - agent: &mut Agent, - index: u32, - value: Value<'a>, - gc: GcScope<'a, '_>, - ) { - let promise_all = self.bind(gc.nogc()); - let value = value.bind(gc.nogc()); - - let result_array = promise_all.get_result_array(agent, gc.nogc()); - - let elements = result_array.as_mut_slice(agent); - elements[index as usize] = Some(value.unbind()); - - let data = promise_all.get_mut(agent); - - // i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. - data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1); - - //ii. If remainingElementsCount.[[Value]] = 0, then - if data.remaining_elements_count == 0 { - // 1. Let valuesArray be CreateArrayFromList(values). - // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »). - let capability = PromiseCapability::from_promise(data.promise, true); - capability.resolve(agent, result_array.into_value().unbind(), gc); - } - } - - pub(crate) fn on_promise_rejected( - self, - agent: &mut Agent, - value: Value<'a>, - gc: NoGcScope<'a, '_>, - ) { - let value = value.bind(gc); - let promise_all = self.bind(gc); - let data = promise_all.get_mut(agent); - - let capability = PromiseCapability::from_promise(data.promise, true); - capability.reject(agent, value.unbind(), gc); - } - - pub(crate) fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> { - let data = self.get(agent); - data.result_array.bind(gc).unbind() - } - - pub(crate) const fn get_index(self) -> usize { - self.0.into_index() - } - - pub fn get(self, agent: &Agent) -> &PromiseAllRecord<'a> { - agent - .heap - .promise_all_records - .get(self.get_index()) - .expect("PromiseAllRecord not found") - } - - pub fn get_mut(self, agent: &mut Agent) -> &mut PromiseAllRecord<'static> { - agent - .heap - .promise_all_records - .get_mut(self.get_index()) - .expect("PromiseAllRecord not found") - } - - pub(crate) const fn _def() -> Self { - Self(BaseIndex::from_u32_index(0)) - } -} - -impl AsRef<[PromiseAllRecord<'static>]> for Agent { - fn as_ref(&self) -> &[PromiseAllRecord<'static>] { - &self.heap.promise_all_records - } -} - -impl AsMut<[PromiseAllRecord<'static>]> for Agent { - fn as_mut(&mut self) -> &mut [PromiseAllRecord<'static>] { - &mut self.heap.promise_all_records - } -} - -impl HeapMarkAndSweep for PromiseAllRecord<'static> { - fn mark_values(&self, queues: &mut WorkQueues) { - let Self { - remaining_elements_count: _, - result_array, - promise, - } = self; - result_array.mark_values(queues); - promise.mark_values(queues); - } - - fn sweep_values(&mut self, compactions: &CompactionLists) { - let Self { - remaining_elements_count: _, - result_array, - promise, - } = self; - result_array.sweep_values(compactions); - promise.sweep_values(compactions); - } -} - -impl Rootable for PromiseAll<'_> { - type RootRepr = HeapRootRef; - - fn to_root_repr(value: Self) -> Result { - Err(HeapRootData::PromiseAll(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::PromiseAll(object) => Some(object), - _ => None, - } - } -} - -impl HeapMarkAndSweep for PromiseAll<'static> { - fn mark_values(&self, queues: &mut WorkQueues) { - queues.promise_all_records.push(*self); - } - - fn sweep_values(&mut self, compactions: &CompactionLists) { - compactions.promise_all_records.shift_index(&mut self.0); - } -} - -bindable_handle!(PromiseAllRecord); -bindable_handle!(PromiseAll); - -impl<'a> CreateHeapData, PromiseAll<'a>> for Heap { - fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAll<'a> { - self.promise_all_records.push(data.unbind()); - self.alloc_counter += core::mem::size_of::>(); - PromiseAll(BaseIndex::last_t(&self.promise_all_records)) - } -} diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_group_record.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_group_record.rs new file mode 100644 index 000000000..ffd079016 --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_group_record.rs @@ -0,0 +1,270 @@ +use crate::{ + ecmascript::{ + builtins::{ + Array, + promise::Promise, + promise_objects::promise_abstract_operations::{ + promise_capability_records::PromiseCapability, + promise_reaction_records::PromiseReactionType, + }, + }, + execution::Agent, + types::{BUILTIN_STRING_MEMORY, IntoValue, OrdinaryObject, Value}, + }, + engine::{ + context::{Bindable, GcScope, NoGcScope, bindable_handle}, + rootable::{HeapRootData, HeapRootRef, Rootable}, + }, + heap::{ + CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, ObjectEntry, WorkQueues, + indexes::BaseIndex, + }, +}; + +#[derive(Debug, Clone, Copy)] +pub enum PromiseGroupType { + PromiseAll, + PromiseAllSettled, +} + +#[derive(Debug, Clone, Copy)] +pub struct PromiseGroupRecord<'a> { + pub(crate) promise_group_type: PromiseGroupType, + pub(crate) remaining_elements_count: u32, + pub(crate) result_array: Array<'a>, + pub(crate) promise: Promise<'a>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct PromiseGroup<'a>(BaseIndex<'a, PromiseGroupRecord<'static>>); + +impl<'a> PromiseGroupRecord<'static> { + fn take_result_and_promise(&mut self) -> (Array<'a>, Option>) { + self.remaining_elements_count = self.remaining_elements_count.saturating_sub(1); + + if self.remaining_elements_count > 0 { + (self.result_array, None) + } else { + (self.result_array, Some(self.promise)) + } + } +} + +impl<'a> PromiseGroup<'a> { + pub(crate) fn settle( + self, + agent: &mut Agent, + reaction_type: PromiseReactionType, + index: u32, + value: Value<'a>, + mut gc: GcScope<'a, '_>, + ) { + let value = value.bind(gc.nogc()); + let record = self.get(agent); + + match record.promise_group_type { + PromiseGroupType::PromiseAll => match reaction_type { + PromiseReactionType::Fulfill => { + self.fulfill(agent, index, value.unbind(), gc.reborrow()); + } + PromiseReactionType::Reject => { + self.reject(agent, value.unbind(), gc.nogc()); + } + }, + PromiseGroupType::PromiseAllSettled => { + let obj = self + .to_all_settled_obj(agent, reaction_type, value.unbind(), gc.nogc()) + .bind(gc.nogc()); + self.fulfill(agent, index, obj.unbind(), gc.reborrow()); + } + } + } + + pub(crate) fn fulfill( + self, + agent: &mut Agent, + index: u32, + value: Value<'a>, + mut gc: GcScope<'a, '_>, + ) { + let promise_group = self.bind(gc.nogc()); + let value = value.bind(gc.nogc()); + + let promise_group_record = promise_group.get_mut(agent); + let (result_array, promise_to_resolve) = promise_group_record.take_result_and_promise(); + + let elements = result_array.as_mut_slice(agent); + elements[index as usize] = Some(value.unbind()); + + if let Some(promise_to_resolve) = promise_to_resolve { + let capability = PromiseCapability::from_promise(promise_to_resolve, true); + capability.resolve(agent, result_array.into_value().unbind(), gc.reborrow()); + } + } + + pub(crate) fn reject(self, agent: &mut Agent, value: Value<'a>, gc: NoGcScope<'a, '_>) { + let value = value.bind(gc); + let promise_group = self.bind(gc); + let data = promise_group.get_mut(agent); + + let capability = PromiseCapability::from_promise(data.promise, true); + capability.reject(agent, value.unbind(), gc); + } + + fn to_all_settled_obj( + self, + agent: &mut Agent, + reaction_type: PromiseReactionType, + value: Value<'a>, + gc: NoGcScope<'a, '_>, + ) -> Value<'a> { + let value = value.bind(gc); + + let obj = match reaction_type { + PromiseReactionType::Fulfill => OrdinaryObject::create_object( + agent, + Some( + agent + .current_realm_record() + .intrinsics() + .object_prototype() + .into(), + ), + &[ + ObjectEntry::new_data_entry( + BUILTIN_STRING_MEMORY.status.into(), + BUILTIN_STRING_MEMORY.fulfilled.into(), + ), + ObjectEntry::new_data_entry(BUILTIN_STRING_MEMORY.value.into(), value.unbind()), + ], + ) + .bind(gc), + PromiseReactionType::Reject => OrdinaryObject::create_object( + agent, + Some( + agent + .current_realm_record() + .intrinsics() + .object_prototype() + .into(), + ), + &[ + ObjectEntry::new_data_entry( + BUILTIN_STRING_MEMORY.status.into(), + BUILTIN_STRING_MEMORY.rejected.into(), + ), + ObjectEntry::new_data_entry( + BUILTIN_STRING_MEMORY.reason.into(), + value.unbind(), + ), + ], + ) + .bind(gc), + }; + + obj.into_value().unbind() + } + + pub(crate) const fn get_index(self) -> usize { + self.0.into_index() + } + + pub fn get(self, agent: &Agent) -> &PromiseGroupRecord<'a> { + agent + .heap + .promise_group_records + .get(self.get_index()) + .expect("PromiseGroupRecord not found") + } + + pub fn get_mut(self, agent: &mut Agent) -> &mut PromiseGroupRecord<'static> { + agent + .heap + .promise_group_records + .get_mut(self.get_index()) + .expect("PromiseGroupRecord not found") + } + + pub(crate) const _DEF: Self = { Self(BaseIndex::from_u32_index(0)) }; +} + +impl AsRef<[PromiseGroupRecord<'static>]> for Agent { + fn as_ref(&self) -> &[PromiseGroupRecord<'static>] { + &self.heap.promise_group_records + } +} + +impl AsMut<[PromiseGroupRecord<'static>]> for Agent { + fn as_mut(&mut self) -> &mut [PromiseGroupRecord<'static>] { + &mut self.heap.promise_group_records + } +} + +impl HeapMarkAndSweep for PromiseGroupRecord<'static> { + fn mark_values(&self, queues: &mut WorkQueues) { + let Self { + promise_group_type: _, + remaining_elements_count: _, + result_array, + promise, + } = self; + result_array.mark_values(queues); + promise.mark_values(queues); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + let Self { + promise_group_type: _, + remaining_elements_count: _, + result_array, + promise, + } = self; + result_array.sweep_values(compactions); + promise.sweep_values(compactions); + } +} + +impl Rootable for PromiseGroup<'_> { + type RootRepr = HeapRootRef; + + fn to_root_repr(value: Self) -> Result { + Err(HeapRootData::PromiseGroup(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::PromiseGroup(object) => Some(object), + _ => None, + } + } +} + +impl HeapMarkAndSweep for PromiseGroup<'static> { + fn mark_values(&self, queues: &mut WorkQueues) { + queues.promise_group_records.push(*self); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + compactions.promise_group_records.shift_index(&mut self.0); + } +} + +bindable_handle!(PromiseGroupRecord); +bindable_handle!(PromiseGroup); + +impl<'a> CreateHeapData, PromiseGroup<'a>> for Heap { + fn create(&mut self, data: PromiseGroupRecord<'a>) -> PromiseGroup<'a> { + self.promise_group_records.push(data.unbind()); + self.alloc_counter += core::mem::size_of::>(); + PromiseGroup(BaseIndex::last_t(&self.promise_group_records)) + } +} 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 e82c63270..d8aa5dcd9 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 @@ -284,21 +284,18 @@ impl PromiseReactionJob { ), } } - PromiseReactionHandler::PromiseAll { promise_all, index } => { + PromiseReactionHandler::PromiseGroup { + promise_group, + index, + } => { let reaction_type = agent[reaction].reaction_type; - match reaction_type { - PromiseReactionType::Fulfill => { - promise_all.on_promise_fulfilled( - agent, - index, - argument.unbind(), - gc.reborrow(), - ); - } - PromiseReactionType::Reject => { - promise_all.on_promise_rejected(agent, argument.unbind(), gc.nogc()); - } - } + promise_group.settle( + agent, + reaction_type, + index, + argument.unbind(), + gc.reborrow(), + ); return Ok(()); } }; @@ -362,7 +359,7 @@ pub(crate) fn new_promise_reaction_job( | PromiseReactionHandler::AsyncModule(_) | PromiseReactionHandler::DynamicImport { .. } | PromiseReactionHandler::DynamicImportEvaluate { .. } - | PromiseReactionHandler::PromiseAll { .. } => None, + | PromiseReactionHandler::PromiseGroup { .. } => 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 c7270f0ec..00170e114 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 @@ -10,7 +10,7 @@ use crate::{ async_generator_objects::AsyncGenerator, control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction, promise::Promise, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAll, + promise_objects::promise_abstract_operations::promise_group_record::PromiseGroup, }, execution::Agent, scripts_and_modules::module::module_semantics::{ @@ -69,9 +69,9 @@ pub(crate) enum PromiseReactionHandler<'a> { promise: Promise<'a>, module: AbstractModule<'a>, }, - PromiseAll { + PromiseGroup { index: u32, - promise_all: PromiseAll<'a>, + promise_group: PromiseGroup<'a>, }, Empty, } @@ -91,10 +91,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { promise.mark_values(queues); module.mark_values(queues); } - Self::PromiseAll { + Self::PromiseGroup { index: _, - promise_all, - } => promise_all.mark_values(queues), + promise_group, + } => promise_group.mark_values(queues), Self::Empty => {} } } @@ -114,12 +114,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { promise.sweep_values(compactions); module.sweep_values(compactions); } - Self::PromiseAll { + Self::PromiseGroup { index: _, - promise_all, - } => { - promise_all.sweep_values(compactions); - } + promise_group, + } => promise_group.sweep_values(compactions), Self::Empty => {} } } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs index 009480096..9c645757c 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_constructor.rs @@ -23,8 +23,8 @@ use crate::{ }, promise_objects::{ promise_abstract_operations::{ - promise_all_record::PromiseAllRecord, promise_capability_records::if_abrupt_reject_promise_m, + promise_group_record::{PromiseGroupRecord, PromiseGroupType}, promise_reaction_records::PromiseReactionHandler, }, promise_prototype::inner_promise_then, @@ -234,113 +234,15 @@ impl PromiseConstructor { agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope<'gc, '_>, + gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - let this_value = this_value.bind(gc.nogc()); - let arguments = arguments.bind(gc.nogc()); - let iterable = arguments.get(0).scope(agent, gc.nogc()); - - // 1. Let C be the this value. - if this_value - != agent - .current_realm_record() - .intrinsics() - .promise() - .into_value() - { - return Err(throw_promise_subclassing_not_supported( - agent, - gc.into_nogc(), - )); - } - - // 2. Let promiseCapability be ? NewPromiseCapability(C). - let Some(constructor) = is_constructor(agent, this_value) else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Expected the this value to be a constructor.", - gc.into_nogc(), - )); - }; - let constructor = constructor.scope(agent, gc.nogc()); - let promise_capability = PromiseCapability::new(agent, gc.nogc()); - let promise = promise_capability.promise().scope(agent, gc.nogc()); - - // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). - let promise_resolve = get_promise_resolve(agent, constructor.get(agent), gc.reborrow()) - .unbind() - .bind(gc.nogc()); - - // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). - let promise_capability = PromiseCapability { - promise: promise.get(agent).bind(gc.nogc()), - must_be_unresolved: true, - }; - let promise_resolve = - if_abrupt_reject_promise_m!(agent, promise_resolve, promise_capability, gc); - let promise_resolve = promise_resolve.scope(agent, gc.nogc()); - - // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). - let iterator_record = get_iterator(agent, iterable.get(agent), false, gc.reborrow()) - .unbind() - .bind(gc.nogc()); - - // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). - let promise_capability = PromiseCapability { - promise: promise.get(agent).bind(gc.nogc()), - must_be_unresolved: true, - }; - let MaybeInvalidIteratorRecord { - iterator, - next_method, - } = if_abrupt_reject_promise_m!(agent, iterator_record, promise_capability, gc); - - let iterator = iterator.scope(agent, gc.nogc()); - - // 7. Let result be Completion(PerformPromiseAll(iteratorRecord, C, promiseCapability, promiseResolve)). - let mut iterator_done = false; - let result = perform_promise_all( + promise_group( agent, - iterator.clone(), - next_method.unbind(), - constructor, - promise_capability.unbind(), - promise_resolve, - &mut iterator_done, - gc.reborrow(), + this_value, + arguments, + PromiseGroupType::PromiseAll, + gc, ) - .unbind() - .bind(gc.nogc()); - - // 8. If result is an abrupt completion, then - let result = match result { - Err(mut result) => { - // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). - if !iterator_done { - result = iterator_close_with_error( - agent, - iterator.get(agent), - result.unbind(), - gc.reborrow(), - ) - .unbind() - .bind(gc.nogc()); - } - - // b. IfAbruptRejectPromise(result, promiseCapability). - let promise_capability = PromiseCapability { - promise: promise.get(agent).bind(gc.nogc()), - must_be_unresolved: true, - }; - // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). - promise_capability.reject(agent, result.value().unbind(), gc.nogc()); - // b. Return capability.[[Promise]]. - promise_capability.promise() - } - Ok(result) => result, - }; - // 9. Return ! result. - Ok(result.into_value().unbind()) } /// ### [27.2.4.2 Promise.allSettled ( iterable )](https://tc39.es/ecma262/#sec-promise.allsettled) @@ -350,11 +252,17 @@ impl PromiseConstructor { /// > constructor. fn all_settled<'gc>( agent: &mut Agent, - _this_value: Value, - _arguments: ArgumentsList, + this_value: Value, + arguments: ArgumentsList, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Err(agent.todo("Promise.allSettled", gc.into_nogc())) + promise_group( + agent, + this_value, + arguments, + PromiseGroupType::PromiseAllSettled, + gc, + ) } /// ### [27.2.4.3 Promise.any ( iterable )](https://tc39.es/ecma262/#sec-promise.any) @@ -660,9 +568,125 @@ fn get_promise_resolve<'gc>( Ok(promise_resolve.unbind()) } +fn promise_group<'gc>( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + promise_group_type: PromiseGroupType, + mut gc: GcScope<'gc, '_>, +) -> JsResult<'gc, Value<'gc>> { + let this_value = this_value.bind(gc.nogc()); + let arguments = arguments.bind(gc.nogc()); + let iterable = arguments.get(0).scope(agent, gc.nogc()); + + // 1. Let C be the this value. + if this_value + != agent + .current_realm_record() + .intrinsics() + .promise() + .into_value() + { + return Err(throw_promise_subclassing_not_supported( + agent, + gc.into_nogc(), + )); + } + + // 2. Let promiseCapability be ? NewPromiseCapability(C). + let Some(constructor) = is_constructor(agent, this_value) else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Expected the this value to be a constructor.", + gc.into_nogc(), + )); + }; + let constructor = constructor.scope(agent, gc.nogc()); + let promise_capability = PromiseCapability::new(agent, gc.nogc()); + let promise = promise_capability.promise().scope(agent, gc.nogc()); + + // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). + let promise_resolve = get_promise_resolve(agent, constructor.get(agent), gc.reborrow()) + .unbind() + .bind(gc.nogc()); + + // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). + let promise_capability = PromiseCapability { + promise: promise.get(agent).bind(gc.nogc()), + must_be_unresolved: true, + }; + let promise_resolve = + if_abrupt_reject_promise_m!(agent, promise_resolve, promise_capability, gc); + let promise_resolve = promise_resolve.scope(agent, gc.nogc()); + + // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). + let iterator_record = get_iterator(agent, iterable.get(agent), false, gc.reborrow()) + .unbind() + .bind(gc.nogc()); + + // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). + let promise_capability = PromiseCapability { + promise: promise.get(agent).bind(gc.nogc()), + must_be_unresolved: true, + }; + let MaybeInvalidIteratorRecord { + iterator, + next_method, + } = if_abrupt_reject_promise_m!(agent, iterator_record, promise_capability, gc); + + let iterator = iterator.scope(agent, gc.nogc()); + + // 7. Let result be Completion(PerformPromiseAll(iteratorRecord, C, promiseCapability, promiseResolve)). + let mut iterator_done = false; + let result = perform_promise_group( + agent, + iterator.clone(), + next_method.unbind(), + constructor, + promise_capability.unbind(), + promise_resolve, + &mut iterator_done, + promise_group_type, + gc.reborrow(), + ) + .unbind() + .bind(gc.nogc()); + + // 8. If result is an abrupt completion, then + let result = match result { + Err(mut result) => { + // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). + if !iterator_done { + result = iterator_close_with_error( + agent, + iterator.get(agent), + result.unbind(), + gc.reborrow(), + ) + .unbind() + .bind(gc.nogc()); + } + + // b. IfAbruptRejectPromise(result, promiseCapability). + let promise_capability = PromiseCapability { + promise: promise.get(agent).bind(gc.nogc()), + must_be_unresolved: true, + }; + // a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »). + promise_capability.reject(agent, result.value().unbind(), gc.nogc()); + // b. Return capability.[[Promise]]. + promise_capability.promise() + } + Ok(result) => result, + }; + // 9. Return ! result. + Ok(result.into_value().unbind()) +} + /// ### [27.2.4.1.2 PerformPromiseAll ( iteratorRecord, constructor, resultCapability, promiseResolve )](https://tc39.es/ecma262/#sec-performpromiseall) +/// ### [27.2.4.2.1 PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, promiseResolve )](https://tc39.es/ecma262/#sec-performpromiseallsettled) #[allow(clippy::too_many_arguments)] -fn perform_promise_all<'gc>( +fn perform_promise_group<'gc>( agent: &mut Agent, iterator: Scoped, next_method: Option, @@ -670,6 +694,7 @@ fn perform_promise_all<'gc>( result_capability: PromiseCapability, promise_resolve: Scoped, iterator_done: &mut bool, + promise_group_type: PromiseGroupType, mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Promise<'gc>> { let result_capability = result_capability.bind(gc.nogc()); @@ -695,9 +720,10 @@ fn perform_promise_all<'gc>( // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. let promise = result_capability.promise.scope(agent, gc.nogc()); - let promise_all_reference = agent + let promise_group_reference = agent .heap - .create(PromiseAllRecord { + .create(PromiseGroupRecord { + promise_group_type, remaining_elements_count: 1, result_array: result_array.get(agent), promise: promise.get(agent), @@ -723,10 +749,12 @@ fn perform_promise_all<'gc>( // b. If next is done, then let Some(next) = next else { *iterator_done = true; - let promise_all = promise_all_reference.get(agent).bind(gc.nogc()); - let data = promise_all.get_mut(agent); + let promise_group = promise_group_reference.get(agent).bind(gc.nogc()); + let data = promise_group.get_mut(agent); + // i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. data.remaining_elements_count -= 1; + // ii. If remainingElementsCount.[[Value]] = 0, then if data.remaining_elements_count == 0 { // 1. Let valuesArray be CreateArrayFromList(values). @@ -780,12 +808,16 @@ fn perform_promise_all<'gc>( // h. Set onFulfilled.[[AlreadyCalled]] to false. // i. Set onFulfilled.[[Index]] to index. // j. Set onFulfilled.[[Values]] to values. - let promise_all = promise_all_reference.get(agent).bind(gc.nogc()); - let reaction = PromiseReactionHandler::PromiseAll { index, promise_all }; + let promise_group = promise_group_reference.get(agent).bind(gc.nogc()); + promise_group.get_mut(agent).remaining_elements_count += 1; + let reaction = PromiseReactionHandler::PromiseGroup { + index, + promise_group, + }; + // k. Set onFulfilled.[[Capability]] to resultCapability. // l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. - promise_all.get_mut(agent).remaining_elements_count += 1; // n. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). inner_promise_then( agent, diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index eab226908..8884e6cc8 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -1758,7 +1758,7 @@ impl TryFrom for Object<'_> { } HeapRootData::AwaitReaction(_) | HeapRootData::PromiseReaction(_) - | HeapRootData::PromiseAll(_) + | HeapRootData::PromiseGroup(_) | HeapRootData::Executable(_) | HeapRootData::Realm(_) | HeapRootData::Script(_) diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index d9c23833f..9cc35f91c 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -1248,7 +1248,7 @@ impl Rootable for Value<'_> { | HeapRootData::SourceTextModule(_) | HeapRootData::AwaitReaction(_) | HeapRootData::PromiseReaction(_) - | HeapRootData::PromiseAll(_) + | HeapRootData::PromiseGroup(_) | HeapRootData::DeclarativeEnvironment(_) | HeapRootData::FunctionEnvironment(_) | HeapRootData::GlobalEnvironment(_) diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs index bfafe0c72..7d09f7501 100644 --- a/nova_vm/src/engine/rootable.rs +++ b/nova_vm/src/engine/rootable.rs @@ -97,9 +97,8 @@ use crate::{ primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, promise_finally_functions::BuiltinPromiseFinallyFunction, - promise_reaction_records::PromiseReaction, + promise_group_record::PromiseGroup, promise_reaction_records::PromiseReaction, promise_resolving_functions::BuiltinPromiseResolvingFunction, }, proxy::Proxy, @@ -184,9 +183,8 @@ pub mod private { primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, promise_finally_functions::BuiltinPromiseFinallyFunction, - promise_reaction_records::PromiseReaction, + promise_group_record::PromiseGroup, promise_reaction_records::PromiseReaction, promise_resolving_functions::BuiltinPromiseResolvingFunction, }, proxy::Proxy, @@ -247,7 +245,7 @@ pub mod private { impl RootableSealed for PrimitiveObject<'_> {} impl RootableSealed for Promise<'_> {} impl RootableSealed for PromiseReaction<'_> {} - impl RootableSealed for PromiseAll<'_> {} + impl RootableSealed for PromiseGroup<'_> {} impl RootableSealed for PropertyKey<'_> {} impl RootableSealed for Proxy<'_> {} impl RootableSealed for Realm<'_> {} @@ -638,7 +636,7 @@ pub enum HeapRootData { Executable(Executable<'static>), AwaitReaction(AwaitReaction<'static>), PromiseReaction(PromiseReaction<'static>), - PromiseAll(PromiseAll<'static>), + PromiseGroup(PromiseGroup<'static>), Realm(Realm<'static>), Script(Script<'static>), SourceTextModule(SourceTextModule<'static>), @@ -925,7 +923,7 @@ impl HeapMarkAndSweep for HeapRootData { Self::Executable(exe) => exe.mark_values(queues), Self::AwaitReaction(await_reaction) => await_reaction.mark_values(queues), Self::PromiseReaction(promise_reaction) => promise_reaction.mark_values(queues), - Self::PromiseAll(promise_all) => promise_all.mark_values(queues), + Self::PromiseGroup(promise_group) => promise_group.mark_values(queues), Self::Realm(realm) => realm.mark_values(queues), Self::Script(script) => script.mark_values(queues), Self::SourceCode(source_code) => source_code.mark_values(queues), @@ -1076,7 +1074,7 @@ impl HeapMarkAndSweep for HeapRootData { Self::Executable(exe) => exe.sweep_values(compactions), Self::AwaitReaction(await_reaction) => await_reaction.sweep_values(compactions), Self::PromiseReaction(promise_reaction) => promise_reaction.sweep_values(compactions), - Self::PromiseAll(promise_all) => promise_all.sweep_values(compactions), + Self::PromiseGroup(promise_group) => promise_group.sweep_values(compactions), Self::Realm(realm) => realm.sweep_values(compactions), Self::Script(script) => script.sweep_values(compactions), Self::SourceCode(source_code) => source_code.sweep_values(compactions), diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index fb575d1fc..5cf804f52 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -80,8 +80,10 @@ use crate::{ }, primitive_objects::PrimitiveObjectHeapData, promise::data::PromiseHeapData, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, - promise_objects::promise_abstract_operations::promise_finally_functions::PromiseFinallyFunctionHeapData, + promise_objects::promise_abstract_operations::{ + promise_finally_functions::PromiseFinallyFunctionHeapData, + promise_group_record::PromiseGroupRecord, + }, proxy::data::ProxyHeapData, text_processing::string_objects::string_iterator_objects::StringIteratorHeapData, typed_array::{SharedVoidArray, VoidArray, data::SharedTypedArrayRecord}, @@ -168,7 +170,7 @@ pub(crate) struct Heap { pub(crate) promises: Vec>>, pub(crate) proxies: Vec>>, pub(crate) realms: Vec>>, - pub(crate) promise_all_records: Vec>, + pub(crate) promise_group_records: Vec>, #[cfg(feature = "regexp")] pub(crate) regexps: Vec>>, #[cfg(feature = "regexp")] @@ -335,7 +337,7 @@ impl Heap { promise_resolving_functions: Vec::with_capacity(0), promise_finally_functions: Vec::with_capacity(0), promises: Vec::with_capacity(0), - promise_all_records: Vec::with_capacity(0), + promise_group_records: Vec::with_capacity(0), proxies: Vec::with_capacity(0), realms: Vec::with_capacity(1), #[cfg(feature = "regexp")] diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index 6d0391e6f..50c70e229 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -55,8 +55,10 @@ use crate::ecmascript::{ ordinary::{caches::PropertyLookupCache, shape::ObjectShape}, primitive_objects::PrimitiveObject, promise::Promise, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAll, - promise_objects::promise_abstract_operations::promise_finally_functions::BuiltinPromiseFinallyFunction, + promise_objects::promise_abstract_operations::{ + promise_finally_functions::BuiltinPromiseFinallyFunction, + promise_group_record::PromiseGroup, + }, proxy::Proxy, text_processing::string_objects::string_iterator_objects::StringIterator, typed_array::{SharedVoidArray, VoidArray}, @@ -141,7 +143,7 @@ pub struct HeapBits { pub promise_resolving_functions: Box<[bool]>, pub promise_finally_functions: Box<[bool]>, pub promises: Box<[bool]>, - pub promise_all_records: Box<[bool]>, + pub promise_group_records: Box<[bool]>, pub proxies: Box<[bool]>, pub realms: Box<[bool]>, #[cfg(feature = "regexp")] @@ -237,7 +239,7 @@ pub(crate) struct WorkQueues { pub promise_reaction_records: Vec>, pub promise_resolving_functions: Vec>, pub promise_finally_functions: Vec>, - pub promise_all_records: Vec>, + pub promise_group_records: Vec>, pub proxies: Vec>, pub realms: Vec>, #[cfg(feature = "regexp")] @@ -333,7 +335,7 @@ impl HeapBits { let promise_finally_functions = vec![false; heap.promise_finally_functions.len()]; let private_environments = vec![false; heap.environments.private.len()]; let promises = vec![false; heap.promises.len()]; - let promise_all_records = vec![false; heap.promise_all_records.len()]; + let promise_group_records = vec![false; heap.promise_group_records.len()]; let proxies = vec![false; heap.proxies.len()]; let realms = vec![false; heap.realms.len()]; #[cfg(feature = "regexp")] @@ -426,7 +428,7 @@ impl HeapBits { promise_finally_functions: promise_finally_functions.into_boxed_slice(), private_environments: private_environments.into_boxed_slice(), promises: promises.into_boxed_slice(), - promise_all_records: promise_all_records.into_boxed_slice(), + promise_group_records: promise_group_records.into_boxed_slice(), proxies: proxies.into_boxed_slice(), realms: realms.into_boxed_slice(), #[cfg(feature = "regexp")] @@ -527,7 +529,7 @@ impl WorkQueues { ), promise_finally_functions: Vec::with_capacity(heap.promise_finally_functions.len() / 4), promises: Vec::with_capacity(heap.promises.len() / 4), - promise_all_records: Vec::with_capacity(heap.promise_all_records.len() / 4), + promise_group_records: Vec::with_capacity(heap.promise_group_records.len() / 4), proxies: Vec::with_capacity(heap.proxies.len() / 4), realms: Vec::with_capacity(heap.realms.len() / 4), #[cfg(feature = "regexp")] @@ -626,7 +628,7 @@ impl WorkQueues { promise_reaction_records, promise_resolving_functions, promise_finally_functions, - promise_all_records, + promise_group_records, proxies, realms, #[cfg(feature = "regexp")] @@ -744,7 +746,7 @@ impl WorkQueues { && promise_reaction_records.is_empty() && promise_resolving_functions.is_empty() && promise_finally_functions.is_empty() - && promise_all_records.is_empty() + && promise_group_records.is_empty() && promises.is_empty() && proxies.is_empty() && realms.is_empty() @@ -1101,7 +1103,7 @@ pub(crate) struct CompactionLists { pub promise_resolving_functions: CompactionList, pub promise_finally_functions: CompactionList, pub promises: CompactionList, - pub promise_all_records: CompactionList, + pub promise_group_records: CompactionList, pub proxies: CompactionList, pub realms: CompactionList, #[cfg(feature = "regexp")] @@ -1213,7 +1215,7 @@ impl CompactionLists { &bits.promise_finally_functions, ), promises: CompactionList::from_mark_bits(&bits.promises), - promise_all_records: CompactionList::from_mark_bits(&bits.promise_all_records), + promise_group_records: CompactionList::from_mark_bits(&bits.promise_group_records), #[cfg(feature = "regexp")] regexps: CompactionList::from_mark_bits(&bits.regexps), #[cfg(feature = "regexp")] diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index cf8597ccb..ecb1ea690 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -63,8 +63,8 @@ use crate::{ primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, promise_finally_functions::BuiltinPromiseFinallyFunction, + promise_group_record::PromiseGroup, }, proxy::Proxy, text_processing::string_objects::string_iterator_objects::StringIterator, @@ -151,7 +151,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option>], gc promise_resolving_functions, promise_finally_functions, promises, - promise_all_records, + promise_group_records, proxies, realms, #[cfg(feature = "regexp")] @@ -677,18 +677,18 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option>], gc promise_reaction_records.get(index).mark_values(&mut queues); } }); - let mut promise_all_record_marks: Box<[PromiseAll]> = - queues.promise_all_records.drain(..).collect(); - promise_all_record_marks.sort(); - promise_all_record_marks.iter().for_each(|&idx| { + let mut promise_group_record_marks: Box<[PromiseGroup]> = + queues.promise_group_records.drain(..).collect(); + promise_group_record_marks.sort(); + promise_group_record_marks.iter().for_each(|&idx| { let index = idx.get_index(); - if let Some(marked) = bits.promise_all_records.get_mut(index) { + if let Some(marked) = bits.promise_group_records.get_mut(index) { if *marked { // Already marked, ignore return; } *marked = true; - promise_all_records.get(index).mark_values(&mut queues); + promise_group_records.get(index).mark_values(&mut queues); } }); let mut promise_resolving_function_marks: Box<[BuiltinPromiseResolvingFunction]> = @@ -1501,7 +1501,7 @@ fn sweep( promise_resolving_functions, promise_finally_functions, promises, - promise_all_records, + promise_group_records, proxies, realms, #[cfg(feature = "regexp")] @@ -2033,12 +2033,12 @@ fn sweep( sweep_heap_vector_values(promises, &compactions, &bits.promises); }); } - if !promise_all_records.is_empty() { + if !promise_group_records.is_empty() { s.spawn(|| { sweep_heap_vector_values( - promise_all_records, + promise_group_records, &compactions, - &bits.promise_all_records, + &bits.promise_group_records, ); }); }