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..781732cb2 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,7 +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; pub(crate) mod promise_all_record; +pub(crate) mod promise_all_settled_record; pub mod promise_capability_records; pub(crate) mod promise_finally_functions; pub(crate) mod promise_jobs; diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all.rs new file mode 100644 index 000000000..aee5f7083 --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all.rs @@ -0,0 +1,36 @@ +use crate::{ + ecmascript::{ + builtins::{ + Array, + promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler, + }, + execution::Agent, + types::Value, + }, + engine::{ + context::{Bindable, GcScope, NoGcScope, bindable_handle}, + rootable::Rootable, + }, +}; + +pub(crate) trait PromiseAllReactionHandler<'a>: Rootable + Bindable { + fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a>; + fn increase_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>); + fn decrease_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>); + fn get_remaining_elements_count(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> u32; + fn on_promise_fulfilled( + self, + agent: &mut Agent, + index: u32, + value: Value<'a>, + gc: GcScope<'a, '_>, + ); + fn on_promise_rejected( + self, + agent: &mut Agent, + index: u32, + value: Value<'a>, + gc: GcScope<'a, '_>, + ); + fn to_reaction_handler(self, index: u32, gc: NoGcScope<'a, '_>) -> PromiseReactionHandler<'a>; +} 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 index 9e8229254..3d0a89f68 100644 --- 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 @@ -5,8 +5,13 @@ use crate::{ ecmascript::{ builtins::{ - Array, promise::Promise, - promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, + Array, + promise::Promise, + promise_objects::promise_abstract_operations::{ + promise_all::PromiseAllReactionHandler, + promise_capability_records::PromiseCapability, + promise_reaction_records::PromiseReactionHandler, + }, }, execution::Agent, types::{IntoValue, Value}, @@ -32,7 +37,38 @@ pub struct PromiseAllRecord<'a> { pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>); impl<'a> PromiseAll<'a> { - pub(crate) fn on_promise_fulfilled( + // 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<'a> PromiseAllReactionHandler<'a> for PromiseAll<'a> { + fn on_promise_fulfilled( self, agent: &mut Agent, index: u32, @@ -61,47 +97,49 @@ impl<'a> PromiseAll<'a> { } } - pub(crate) fn on_promise_rejected( + fn on_promise_rejected( self, agent: &mut Agent, + _index: u32, value: Value<'a>, - gc: NoGcScope<'a, '_>, + gc: GcScope<'a, '_>, ) { - let value = value.bind(gc); - let promise_all = self.bind(gc); + let value = value.bind(gc.nogc()); + let promise_all = self.bind(gc.nogc()); let data = promise_all.get_mut(agent); let capability = PromiseCapability::from_promise(data.promise, true); - capability.reject(agent, value.unbind(), gc); + capability.reject(agent, value.unbind(), gc.nogc()); } - pub(crate) fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> { + 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() + fn increase_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>) { + let promise_all = self.bind(gc); + let data = promise_all.get_mut(agent); + data.remaining_elements_count = data.remaining_elements_count.saturating_add(1); } - pub fn get(self, agent: &Agent) -> &PromiseAllRecord<'a> { - agent - .heap - .promise_all_records - .get(self.get_index()) - .expect("PromiseAllRecord not found") + fn decrease_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>) { + let promise_all = self.bind(gc); + let data = promise_all.get_mut(agent); + data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1); } - 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") + fn get_remaining_elements_count(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> u32 { + let promise_all = self.bind(gc); + promise_all.get(agent).remaining_elements_count } - pub(crate) const fn _def() -> Self { - Self(BaseIndex::from_u32_index(0)) + fn to_reaction_handler(self, index: u32, gc: NoGcScope<'a, '_>) -> PromiseReactionHandler<'a> { + let promise_all = self.bind(gc); + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all.unbind(), + } } } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_settled_record.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_settled_record.rs new file mode 100644 index 000000000..f3703bc36 --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_settled_record.rs @@ -0,0 +1,246 @@ +// 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::{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 struct PromiseAllSettledRecord<'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 PromiseAllSettled<'a>(BaseIndex<'a, PromiseAllSettledRecord<'static>>); + +impl<'a> PromiseAllSettled<'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 obj be OrdinaryObjectCreate(%Object.prototype%). + // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled"). + // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x). + let obj = 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.nogc()); + + let elements = result_array.as_mut_slice(agent); + elements[index as usize] = Some(obj.unbind().into_value()); + + let data = promise_all.get_mut(agent); + + // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1); + + // 14. If remainingElementsCount.[[Value]] = 0, then + if data.remaining_elements_count == 0 { + // a. Let valuesArray be CreateArrayFromList(values). + // b. Return ? Call(promiseCapability.[[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, + 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 obj be OrdinaryObjectCreate(%Object.prototype%). + // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected"). + // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x). + let obj = 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.nogc()); + + let elements = result_array.as_mut_slice(agent); + elements[index as usize] = Some(obj.unbind().into_value()); + + let data = promise_all.get_mut(agent); + + // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1); + + // 14. If remainingElementsCount.[[Value]] = 0, then + if data.remaining_elements_count == 0 { + // a. Let valuesArray be CreateArrayFromList(values). + // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »). + let capability = PromiseCapability::from_promise(data.promise, true); + capability.resolve(agent, result_array.into_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) -> &PromiseAllSettledRecord<'a> { + agent + .heap + .promise_all_settled_records + .get(self.get_index()) + .expect("PromiseAllSettledRecord not found") + } + + pub fn get_mut(self, agent: &mut Agent) -> &mut PromiseAllSettledRecord<'static> { + agent + .heap + .promise_all_settled_records + .get_mut(self.get_index()) + .expect("PromiseAllSettledRecord not found") + } + + pub(crate) const fn _def() -> Self { + Self(BaseIndex::from_u32_index(0)) + } +} + +impl AsRef<[PromiseAllSettledRecord<'static>]> for Agent { + fn as_ref(&self) -> &[PromiseAllSettledRecord<'static>] { + &self.heap.promise_all_settled_records + } +} + +impl AsMut<[PromiseAllSettledRecord<'static>]> for Agent { + fn as_mut(&mut self) -> &mut [PromiseAllSettledRecord<'static>] { + &mut self.heap.promise_all_settled_records + } +} + +impl HeapMarkAndSweep for PromiseAllSettledRecord<'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 PromiseAllSettled<'_> { + type RootRepr = HeapRootRef; + + fn to_root_repr(value: Self) -> Result { + Err(HeapRootData::PromiseAllSettled(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::PromiseAllSettled(object) => Some(object), + _ => None, + } + } +} + +impl HeapMarkAndSweep for PromiseAllSettled<'static> { + fn mark_values(&self, queues: &mut WorkQueues) { + queues.promise_all_settled_records.push(*self); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + compactions + .promise_all_settled_records + .shift_index(&mut self.0); + } +} + +bindable_handle!(PromiseAllSettledRecord); +bindable_handle!(PromiseAllSettled); + +impl<'a> CreateHeapData, PromiseAllSettled<'a>> for Heap { + fn create(&mut self, data: PromiseAllSettledRecord<'a>) -> PromiseAllSettled<'a> { + self.promise_all_settled_records.push(data.unbind()); + self.alloc_counter += core::mem::size_of::>(); + PromiseAllSettled(BaseIndex::last_t(&self.promise_all_settled_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..817e19f51 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 @@ -12,7 +12,10 @@ use crate::{ }, operations_on_objects::{call_function, get_function_realm}, }, - builtins::{ArgumentsList, promise::Promise}, + builtins::{ + ArgumentsList, promise::Promise, + promise_objects::promise_abstract_operations::promise_all::PromiseAllReactionHandler, + }, execution::{ Agent, JsResult, agent::{InnerJob, Job, JsError}, @@ -296,7 +299,37 @@ impl PromiseReactionJob { ); } PromiseReactionType::Reject => { - promise_all.on_promise_rejected(agent, argument.unbind(), gc.nogc()); + promise_all.on_promise_rejected( + agent, + index, + argument.unbind(), + gc.reborrow(), + ); + } + } + return Ok(()); + } + PromiseReactionHandler::PromiseAllSettled { + promise_all_settled, + index, + } => { + let reaction_type = agent[reaction].reaction_type; + match reaction_type { + PromiseReactionType::Fulfill => { + promise_all_settled.on_promise_fulfilled( + agent, + index, + argument.unbind(), + gc.reborrow(), + ); + } + PromiseReactionType::Reject => { + promise_all_settled.on_promise_rejected( + agent, + index, + argument.unbind(), + gc.reborrow(), + ); } } return Ok(()); @@ -363,6 +396,7 @@ pub(crate) fn new_promise_reaction_job( | PromiseReactionHandler::DynamicImport { .. } | PromiseReactionHandler::DynamicImportEvaluate { .. } | PromiseReactionHandler::PromiseAll { .. } => None, + PromiseReactionHandler::PromiseAllSettled { .. } => 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..264e0b470 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,9 @@ 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_all_record::PromiseAll, promise_all_settled_record::PromiseAllSettled, + }, }, execution::Agent, scripts_and_modules::module::module_semantics::{ @@ -73,6 +75,10 @@ pub(crate) enum PromiseReactionHandler<'a> { index: u32, promise_all: PromiseAll<'a>, }, + PromiseAllSettled { + index: u32, + promise_all_settled: PromiseAllSettled<'a>, + }, Empty, } bindable_handle!(PromiseReactionHandler); @@ -95,6 +101,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { index: _, promise_all, } => promise_all.mark_values(queues), + Self::PromiseAllSettled { + index: _, + promise_all_settled, + } => promise_all_settled.mark_values(queues), Self::Empty => {} } } @@ -120,6 +130,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { } => { promise_all.sweep_values(compactions); } + Self::PromiseAllSettled { + index: _, + promise_all_settled, + } => promise_all_settled.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..9e3de1543 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,7 +23,9 @@ use crate::{ }, promise_objects::{ promise_abstract_operations::{ + promise_all::{PromiseAllReactionHandler, PromiseType}, promise_all_record::PromiseAllRecord, + promise_all_settled_record::PromiseAllSettledRecord, promise_capability_records::if_abrupt_reject_promise_m, promise_reaction_records::PromiseReactionHandler, }, @@ -350,11 +352,113 @@ impl PromiseConstructor { /// > constructor. fn all_settled<'gc>( agent: &mut Agent, - _this_value: Value, - _arguments: ArgumentsList, - gc: GcScope<'gc, '_>, + this_value: Value, + arguments: ArgumentsList, + mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Err(agent.todo("Promise.allSettled", gc.into_nogc())) + 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(PerformPromiseAllSettled(iteratorRecord, C, promiseCapability, promiseResolve)). + let mut iterator_done = false; + let result = perform_promise_all_settled( + agent, + iterator.clone(), + next_method.unbind(), + constructor, + promise_capability.unbind(), + promise_resolve, + &mut iterator_done, + 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, + }; + promise_capability.reject(agent, result.value().unbind(), gc.nogc()); + promise_capability.promise() + } + Ok(result) => result, + }; + // 9. Return ! result. + Ok(result.into_value().unbind()) } /// ### [27.2.4.3 Promise.any ( iterable )](https://tc39.es/ecma262/#sec-promise.any) @@ -801,6 +905,303 @@ fn perform_promise_all<'gc>( } } +/// ### [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_settled<'gc>( + agent: &mut Agent, + iterator: Scoped, + next_method: Option, + constructor: Scoped, + result_capability: PromiseCapability, + promise_resolve: Scoped, + iterator_done: &mut bool, + mut gc: GcScope<'gc, '_>, +) -> JsResult<'gc, Promise<'gc>> { + let result_capability = result_capability.bind(gc.nogc()); + + let Some(next_method) = next_method else { + return Err(throw_not_callable(agent, gc.into_nogc())); + }; + + let next_method = next_method.scope(agent, gc.nogc()); + + // 1. Let values be a new empty List. + let capacity = match iterator.get(agent) { + Object::Array(array) => array.len(agent), + Object::Map(map) => agent[map].size(), + Object::Set(set) => agent[set].size(), + _ => 0, + }; + + let result_array = array_create(agent, 0, capacity as usize, None, gc.nogc()) + .unbind()? + .bind(gc.nogc()); + let result_array = result_array.scope(agent, gc.nogc()); + + // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. + let promise = result_capability.promise.scope(agent, gc.nogc()); + let promise_all_settled_reference = agent + .heap + .create(PromiseAllSettledRecord { + remaining_elements_count: 1, + result_array: result_array.get(agent), + promise: promise.get(agent), + }) + .scope(agent, gc.nogc()); + + // 3. Let index be 0. + let mut index = 0; + + // 4. Repeat, + loop { + let iterator_record = IteratorRecord { + iterator: iterator.get(agent), + next_method: next_method.get(agent), + } + .bind(gc.nogc()); + + // a. Let next be ? IteratorStepValue(iteratorRecord). + let next = iterator_step_value(agent, iterator_record.unbind(), gc.reborrow()) + .unbind()? + .bind(gc.nogc()); + + // b. If next is done, then + let Some(next) = next else { + *iterator_done = true; + let promise_all = promise_all_settled_reference.get(agent).bind(gc.nogc()); + let data = promise_all.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). + let values_array = result_array.get(agent).bind(gc.nogc()); + // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »). + let result_capability = PromiseCapability { + promise: promise.get(agent).bind(gc.nogc()), + must_be_unresolved: true, + }; + result_capability.unbind().resolve( + agent, + values_array.into_value().unbind(), + gc.reborrow(), + ); + } + + // iii. Return resultCapability.[[Promise]]. + return Ok(promise.get(agent)); + }; + + // c. Append undefined to values. + let temp_array = result_array.get(agent).bind(gc.nogc()); + if let Err(err) = temp_array.reserve(agent, 1) { + return Err(agent.throw_allocation_exception(err, gc.into_nogc())); + } + // SAFETY: reserve did not fail. + unsafe { temp_array.set_len(agent, index + 1) }; + + // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »). + let call_result = call_function( + agent, + promise_resolve.get(agent), + constructor.get(agent).into_value(), + Some(ArgumentsList::from_mut_value(&mut next.unbind())), + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + + // Note: as we don't yet support Promise subclassing, if we see a + // non-Promise return we wrap it inside a resolved Promise to get + // then-chaining. + let next_promise = match call_result { + Value::Promise(next_promise) => next_promise, + _ => Promise::new_resolved(agent, call_result), + }; + + // e. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions. + // f. Let lengthFulfilled be the number of non-optional parameters of the function definition in Promise.allSettled Resolve Element Functions. + // g. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). + // h. Let alreadyCalled be the Record { [[Value]]: false }. + // i. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled. + // j. Set onFulfilled.[[Index]] to index. + // k. Set onFulfilled.[[Values]] to values. + // l. Set onFulfilled.[[Capability]] to resultCapability. + // m. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. + // n. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions. + // o. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.allSettled Reject Element Functions. + // p. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). + // q. Set onRejected.[[AlreadyCalled]] to alreadyCalled. + // r. Set onRejected.[[Index]] to index. + // s. Set onRejected.[[Values]] to values. + // t. Set onRejected.[[Capability]] to resultCapability. + // u. Set onRejected.[[RemainingElements]] to remainingElementsCount. + let promise_all_settled = promise_all_settled_reference.get(agent).bind(gc.nogc()); + let reaction = PromiseReactionHandler::PromiseAllSettled { + index, + promise_all_settled, + }; + + // v. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. + promise_all_settled.get_mut(agent).remaining_elements_count += 1; + // w. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). + inner_promise_then( + agent, + next_promise.unbind(), + reaction.unbind(), + reaction.unbind(), + None, + gc.nogc(), + ); + + // x. Set index to index + 1. + index += 1; + } +} + +#[allow(clippy::too_many_arguments)] +fn perform_promise_all_or_all_settled<'gc, T: PromiseAllReactionHandler<'gc>>( + agent: &mut Agent, + iterator: Scoped, + next_method: Option, + constructor: Scoped, + result_capability: PromiseCapability, + promise_resolve: Scoped, + iterator_done: &mut bool, + promise_all_handler: T, + mut gc: GcScope<'gc, '_>, +) -> JsResult<'gc, Promise<'gc>> { + let result_capability = result_capability.bind(gc.nogc()); + let promise_all_handler = promise_all_handler.bind(gc.nogc()); + + let Some(next_method) = next_method else { + return Err(throw_not_callable(agent, gc.into_nogc())); + }; + + let next_method = next_method.scope(agent, gc.nogc()); + + // 1. Let values be a new empty List. + let capacity = match iterator.get(agent) { + Object::Array(array) => array.len(agent), + Object::Map(map) => agent[map].size(), + Object::Set(set) => agent[set].size(), + _ => 0, + }; + + let result_array = array_create(agent, 0, capacity as usize, None, gc.nogc()) + .unbind()? + .bind(gc.nogc()); + let result_array = result_array.scope(agent, gc.nogc()); + + // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. + let promise = result_capability.promise.scope(agent, gc.nogc()); + // let promise_all_reference = agent + // .heap + // .create(PromiseAllRecord { + // remaining_elements_count: 1, + // result_array: result_array.get(agent), + // promise: promise.get(agent), + // }) + // .scope(agent, gc.nogc()); + + // 3. Let index be 0. + let mut index = 0; + + // 4. Repeat, + loop { + let iterator_record = IteratorRecord { + iterator: iterator.get(agent), + next_method: next_method.get(agent), + } + .bind(gc.nogc()); + + // a. Let next be ? IteratorStepValue(iteratorRecord). + let next = iterator_step_value(agent, iterator_record.unbind(), gc.reborrow()) + .unbind()? + .bind(gc.nogc()); + + // 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()); + // i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + promise_all_handler.decrease_remaining_elements_count(agent, gc.nogc()); + // ii. If remainingElementsCount.[[Value]] = 0, then + if promise_all_handler.get_remaining_elements_count(agent, gc.nogc()) == 0 { + // 1. Let valuesArray be CreateArrayFromList(values). + let values_array = result_array.get(agent).bind(gc.nogc()); + // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »). + let result_capability = PromiseCapability { + promise: promise.get(agent).bind(gc.nogc()), + must_be_unresolved: true, + }; + result_capability.unbind().resolve( + agent, + values_array.into_value().unbind(), + gc.reborrow(), + ); + } + + // iii. Return resultCapability.[[Promise]]. + return Ok(promise.get(agent)); + }; + + // c. Append undefined to values. + let temp_array = result_array.get(agent).bind(gc.nogc()); + if let Err(err) = temp_array.reserve(agent, 1) { + return Err(agent.throw_allocation_exception(err, gc.into_nogc())); + } + // SAFETY: reserve did not fail. + unsafe { temp_array.set_len(agent, index + 1) }; + + // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »). + let call_result = call_function( + agent, + promise_resolve.get(agent), + constructor.get(agent).into_value(), + Some(ArgumentsList::from_mut_value(&mut next.unbind())), + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + + // Note: as we don't yet support Promise subclassing, if we see a + // non-Promise return we wrap it inside a resolved Promise to get + // then-chaining. + let next_promise = match call_result { + Value::Promise(next_promise) => next_promise, + _ => Promise::new_resolved(agent, call_result), + }; + + // e. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions. + // f. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions. + // g. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). + // h. Set onFulfilled.[[AlreadyCalled]] to false. + // i. Set onFulfilled.[[Index]] to index. + // j. Set onFulfilled.[[Values]] to values. + let reaction = promise_all_handler + .to_reaction_handler(index, gc.nogc()) + .bind(gc.nogc()); + + // k. Set onFulfilled.[[Capability]] to resultCapability. + // l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. + // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. + promise_all_handler.increase_remaining_elements_count(agent, gc.nogc()); + // n. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). + inner_promise_then( + agent, + next_promise.unbind(), + reaction.unbind(), + reaction.unbind(), + None, + gc.nogc(), + ); + + // o. Set index to index + 1. + index += 1; + } +} + fn throw_promise_subclassing_not_supported<'a>( agent: &mut Agent, gc: NoGcScope<'a, '_>, diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 399f094b6..05091bf9a 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -5017,6 +5017,7 @@ impl TryFrom for Object<'_> { HeapRootData::AwaitReaction(_) | HeapRootData::PromiseReaction(_) | HeapRootData::PromiseAll(_) + | HeapRootData::PromiseAllSettled(_) | 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 b74b3157b..9c47ab39a 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -1517,6 +1517,7 @@ impl Rootable for Value<'_> { | HeapRootData::AwaitReaction(_) | HeapRootData::PromiseReaction(_) | HeapRootData::PromiseAll(_) + | HeapRootData::PromiseAllSettled(_) | HeapRootData::DeclarativeEnvironment(_) | HeapRootData::FunctionEnvironment(_) | HeapRootData::GlobalEnvironment(_) diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs index c215d7aa6..1f16f1c5f 100644 --- a/nova_vm/src/engine/rootable.rs +++ b/nova_vm/src/engine/rootable.rs @@ -70,7 +70,7 @@ use crate::{ primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, + promise_all_record::PromiseAll, promise_all_settled_record::PromiseAllSettled, promise_finally_functions::BuiltinPromiseFinallyFunction, promise_reaction_records::PromiseReaction, promise_resolving_functions::BuiltinPromiseResolvingFunction, @@ -148,7 +148,7 @@ pub mod private { primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, + promise_all_record::PromiseAll, promise_all_settled_record::PromiseAllSettled, promise_finally_functions::BuiltinPromiseFinallyFunction, promise_reaction_records::PromiseReaction, promise_resolving_functions::BuiltinPromiseResolvingFunction, @@ -215,6 +215,7 @@ pub mod private { impl RootableSealed for Promise<'_> {} impl RootableSealed for PromiseReaction<'_> {} impl RootableSealed for PromiseAll<'_> {} + impl RootableSealed for PromiseAllSettled<'_> {} impl RootableSealed for PropertyKey<'_> {} impl RootableSealed for Proxy<'_> {} impl RootableSealed for Realm<'_> {} @@ -557,6 +558,7 @@ pub enum HeapRootData { AwaitReaction(AwaitReaction<'static>), PromiseReaction(PromiseReaction<'static>), PromiseAll(PromiseAll<'static>), + PromiseAllSettled(PromiseAllSettled<'static>), Realm(Realm<'static>), Script(Script<'static>), SourceTextModule(SourceTextModule<'static>), @@ -792,6 +794,9 @@ impl HeapMarkAndSweep for HeapRootData { HeapRootData::AwaitReaction(await_reaction) => await_reaction.mark_values(queues), HeapRootData::PromiseReaction(promise_reaction) => promise_reaction.mark_values(queues), HeapRootData::PromiseAll(promise_all) => promise_all.mark_values(queues), + HeapRootData::PromiseAllSettled(promise_all_settled) => { + promise_all_settled.mark_values(queues) + } HeapRootData::Realm(realm) => realm.mark_values(queues), HeapRootData::Script(script) => script.mark_values(queues), HeapRootData::SourceCode(source_code) => source_code.mark_values(queues), @@ -922,6 +927,9 @@ impl HeapMarkAndSweep for HeapRootData { promise_reaction.sweep_values(compactions) } HeapRootData::PromiseAll(promise_all) => promise_all.sweep_values(compactions), + HeapRootData::PromiseAllSettled(promise_all_settled) => { + promise_all_settled.sweep_values(compactions) + } HeapRootData::Realm(realm) => realm.sweep_values(compactions), HeapRootData::Script(script) => script.sweep_values(compactions), HeapRootData::SourceCode(source_code) => source_code.sweep_values(compactions), diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index faab8d75f..f843aab3d 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -79,8 +79,11 @@ 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_all_record::PromiseAllRecord, + promise_all_settled_record::PromiseAllSettledRecord, + promise_finally_functions::PromiseFinallyFunctionHeapData, + }, proxy::data::ProxyHeapData, text_processing::string_objects::string_iterator_objects::StringIteratorHeapData, }, @@ -175,6 +178,7 @@ pub(crate) struct Heap { pub(crate) proxies: Vec>>, pub(crate) realms: Vec>>, pub(crate) promise_all_records: Vec>, + pub(crate) promise_all_settled_records: Vec>, #[cfg(feature = "regexp")] pub(crate) regexps: Vec>>, #[cfg(feature = "regexp")] @@ -328,6 +332,7 @@ impl Heap { promise_finally_functions: Vec::with_capacity(0), promises: Vec::with_capacity(0), promise_all_records: Vec::with_capacity(0), + promise_all_settled_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 2c5c6f381..bb6847974 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -56,8 +56,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_all_record::PromiseAll, promise_all_settled_record::PromiseAllSettled, + promise_finally_functions::BuiltinPromiseFinallyFunction, + }, proxy::Proxy, text_processing::string_objects::string_iterator_objects::StringIterator, }, @@ -142,6 +144,7 @@ pub struct HeapBits { pub promise_finally_functions: Box<[bool]>, pub promises: Box<[bool]>, pub promise_all_records: Box<[bool]>, + pub promise_all_settled_records: Box<[bool]>, pub proxies: Box<[bool]>, pub realms: Box<[bool]>, #[cfg(feature = "regexp")] @@ -234,6 +237,7 @@ pub(crate) struct WorkQueues { pub promise_resolving_functions: Vec>, pub promise_finally_functions: Vec>, pub promise_all_records: Vec>, + pub promise_all_settled_records: Vec>, pub proxies: Vec>, pub realms: Vec>, #[cfg(feature = "regexp")] @@ -326,6 +330,7 @@ impl HeapBits { 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_all_settled_records = vec![false; heap.promise_all_settled_records.len()]; let proxies = vec![false; heap.proxies.len()]; let realms = vec![false; heap.realms.len()]; #[cfg(feature = "regexp")] @@ -415,6 +420,7 @@ impl HeapBits { private_environments: private_environments.into_boxed_slice(), promises: promises.into_boxed_slice(), promise_all_records: promise_all_records.into_boxed_slice(), + promise_all_settled_records: promise_all_settled_records.into_boxed_slice(), proxies: proxies.into_boxed_slice(), realms: realms.into_boxed_slice(), #[cfg(feature = "regexp")] @@ -512,6 +518,9 @@ 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_all_settled_records: Vec::with_capacity( + heap.promise_all_settled_records.len() / 4, + ), proxies: Vec::with_capacity(heap.proxies.len() / 4), realms: Vec::with_capacity(heap.realms.len() / 4), #[cfg(feature = "regexp")] @@ -607,6 +616,7 @@ impl WorkQueues { promise_resolving_functions, promise_finally_functions, promise_all_records, + promise_all_settled_records, proxies, realms, #[cfg(feature = "regexp")] @@ -717,6 +727,7 @@ impl WorkQueues { && promise_resolving_functions.is_empty() && promise_finally_functions.is_empty() && promise_all_records.is_empty() + && promise_all_settled_records.is_empty() && promises.is_empty() && proxies.is_empty() && realms.is_empty() @@ -1072,6 +1083,7 @@ pub(crate) struct CompactionLists { pub promise_finally_functions: CompactionList, pub promises: CompactionList, pub promise_all_records: CompactionList, + pub promise_all_settled_records: CompactionList, pub proxies: CompactionList, pub realms: CompactionList, #[cfg(feature = "regexp")] @@ -1180,6 +1192,9 @@ impl CompactionLists { ), promises: CompactionList::from_mark_bits(&bits.promises), promise_all_records: CompactionList::from_mark_bits(&bits.promise_all_records), + promise_all_settled_records: CompactionList::from_mark_bits( + &bits.promise_all_settled_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 c5cd0bb8e..8873a6e65 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -62,7 +62,7 @@ use crate::{ primitive_objects::PrimitiveObject, promise::Promise, promise_objects::promise_abstract_operations::{ - promise_all_record::PromiseAll, + promise_all_record::PromiseAll, promise_all_settled_record::PromiseAllSettled, promise_finally_functions::BuiltinPromiseFinallyFunction, }, proxy::Proxy, @@ -156,6 +156,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option>], gc promise_finally_functions, promises, promise_all_records, + promise_all_settled_records, proxies, realms, #[cfg(feature = "regexp")] @@ -674,6 +675,22 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option>], gc promise_all_records.get(index).mark_values(&mut queues); } }); + let mut promise_all_settled_record_marks: Box<[PromiseAllSettled]> = + queues.promise_all_settled_records.drain(..).collect(); + promise_all_settled_record_marks.sort(); + promise_all_settled_record_marks.iter().for_each(|&idx| { + let index = idx.get_index(); + if let Some(marked) = bits.promise_all_settled_records.get_mut(index) { + if *marked { + // Already marked, ignore + return; + } + *marked = true; + promise_all_settled_records + .get(index) + .mark_values(&mut queues); + } + }); let mut promise_resolving_function_marks: Box<[BuiltinPromiseResolvingFunction]> = queues.promise_resolving_functions.drain(..).collect(); promise_resolving_function_marks.sort(); @@ -1458,6 +1475,7 @@ fn sweep( promise_finally_functions, promises, promise_all_records, + promise_all_settled_records, proxies, realms, #[cfg(feature = "regexp")] @@ -1969,6 +1987,15 @@ fn sweep( ); }); } + if !promise_all_settled_records.is_empty() { + s.spawn(|| { + sweep_heap_vector_values( + promise_all_settled_records, + &compactions, + &bits.promise_all_settled_records, + ); + }); + } if !proxies.is_empty() { s.spawn(|| { sweep_heap_vector_values(proxies, &compactions, &bits.proxies);