From 25194992a236a2d9dcea89234fe5e39240942df4 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 22 Jul 2025 07:57:38 -0300 Subject: [PATCH 01/46] Implement Promise.all --- .../promise_objects/promise_constructor.rs | 136 +++++++++++++++++- 1 file changed, 132 insertions(+), 4 deletions(-) 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 985ff4afd..fdf9862e2 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 @@ -2,6 +2,14 @@ // 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_objects::create_list_from_array_like; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; +use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; +use crate::ecmascript::builtins::Array; +use crate::ecmascript::execution::agent::JsError; +use crate::ecmascript::types::HeapNumber; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; +use crate::engine::rootable::Scopable; use crate::{ ecmascript::{ abstract_operations::{ @@ -212,11 +220,131 @@ impl PromiseConstructor { fn all<'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.all", gc.into_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(), + )); + } + + // Get the first argument from the ArgumentsList + let first_arg = arguments.get(0).bind(gc.nogc()); + + // Try to convert to Array to extract promises + let Ok(promise_array) = Array::try_from(first_arg.unbind()) else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Expected an array of promises", + gc.into_nogc(), + )); + }; + + // Get the first promise from the array + let array_slice = promise_array.as_slice(agent); + if array_slice.is_empty() { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Array is empty", + gc.into_nogc(), + )); + } + + let Some(first_element) = array_slice[0] else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "First element is None", + gc.into_nogc(), + )); + }; + + // Convert Value to Promise + let Value::Promise(promise_to_await) = first_element.unbind() else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "First element is not a Promise", + gc.into_nogc(), + )); + }; + + // Check if the promise is already settled + if let Some(result) = promise_to_await.try_get_result(agent, gc.nogc()) { + // Promise is already settled, return its result + match result { + Ok(value) => { + // Create a resolved promise with the value + let result_capability = PromiseCapability::new(agent, gc.nogc()); + let result_promise = result_capability.promise().scope(agent, gc.nogc()); + result_capability + .unbind() + .resolve(agent, value.unbind(), gc.reborrow()); + return Ok(result_promise.get(agent).into_value()); + } + Err(error) => { + // Create a rejected promise with the error + return Ok(Promise::new_rejected( + agent, + error.value().unbind(), + gc.into_nogc(), + ) + .into_value()); + } + } + } + + // Promise is pending, we need to wait for it using await reaction + // Create a promise capability for our result + let result_capability = PromiseCapability::new(agent, gc.nogc()); + let result_promise = result_capability.promise().scope(agent, gc.nogc()); + + // For await reactions, we typically need an async executable and execution context + // Since we're in Promise.all (not an async function), we'll use a simpler approach + // and use regular promise reactions instead of await reactions + + // Use inner_promise_then to wait for the promise + // For simplicity, we'll use Empty handlers which will pass through the value + let fulfill_handler = PromiseReactionHandler::Empty; + let reject_handler = PromiseReactionHandler::Empty; + + // Note: For a real await reaction, you would need: + // 1. An AwaitReactionRecord with execution context and VM state + // 2. A suspended VM that can be resumed + // 3. An async executable (async function or module) + + // Here's how you would create an await reaction (commented out as it requires more setup): + /* + let await_reaction_record = AwaitReactionRecord { + vm: Some(suspended_vm), // Would need a suspended VM + async_executable: Some(AsyncExecutable::AsyncFunction(async_function)), // Would need async function + execution_context: Some(execution_context), // Would need execution context + return_promise_capability: result_capability.clone(), + }; + let await_reaction = agent.heap.create(await_reaction_record); + let await_handler = PromiseReactionHandler::Await(await_reaction); + */ + + // For now, let's use a simpler approach that demonstrates the concept + inner_promise_then( + agent, + promise_to_await, + fulfill_handler, + reject_handler, + Some(result_capability), + gc.nogc(), + ); + + // Return the result promise + Ok(result_promise.get(agent).into_value()) } fn all_settled<'gc>( From 3dba3e7b926b72cba5f7f6c08d87dbed4a23f762 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 22 Jul 2025 07:58:41 -0300 Subject: [PATCH 02/46] Remove unecessary code and comments --- .../promise_objects/promise_constructor.rs | 54 ------------------- 1 file changed, 54 deletions(-) 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 fdf9862e2..6fba83244 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 @@ -238,10 +238,8 @@ impl PromiseConstructor { )); } - // Get the first argument from the ArgumentsList let first_arg = arguments.get(0).bind(gc.nogc()); - // Try to convert to Array to extract promises let Ok(promise_array) = Array::try_from(first_arg.unbind()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -250,7 +248,6 @@ impl PromiseConstructor { )); }; - // Get the first promise from the array let array_slice = promise_array.as_slice(agent); if array_slice.is_empty() { return Err(agent.throw_exception_with_static_message( @@ -268,7 +265,6 @@ impl PromiseConstructor { )); }; - // Convert Value to Promise let Value::Promise(promise_to_await) = first_element.unbind() else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, @@ -277,62 +273,12 @@ impl PromiseConstructor { )); }; - // Check if the promise is already settled - if let Some(result) = promise_to_await.try_get_result(agent, gc.nogc()) { - // Promise is already settled, return its result - match result { - Ok(value) => { - // Create a resolved promise with the value - let result_capability = PromiseCapability::new(agent, gc.nogc()); - let result_promise = result_capability.promise().scope(agent, gc.nogc()); - result_capability - .unbind() - .resolve(agent, value.unbind(), gc.reborrow()); - return Ok(result_promise.get(agent).into_value()); - } - Err(error) => { - // Create a rejected promise with the error - return Ok(Promise::new_rejected( - agent, - error.value().unbind(), - gc.into_nogc(), - ) - .into_value()); - } - } - } - - // Promise is pending, we need to wait for it using await reaction - // Create a promise capability for our result let result_capability = PromiseCapability::new(agent, gc.nogc()); let result_promise = result_capability.promise().scope(agent, gc.nogc()); - // For await reactions, we typically need an async executable and execution context - // Since we're in Promise.all (not an async function), we'll use a simpler approach - // and use regular promise reactions instead of await reactions - - // Use inner_promise_then to wait for the promise - // For simplicity, we'll use Empty handlers which will pass through the value let fulfill_handler = PromiseReactionHandler::Empty; let reject_handler = PromiseReactionHandler::Empty; - // Note: For a real await reaction, you would need: - // 1. An AwaitReactionRecord with execution context and VM state - // 2. A suspended VM that can be resumed - // 3. An async executable (async function or module) - - // Here's how you would create an await reaction (commented out as it requires more setup): - /* - let await_reaction_record = AwaitReactionRecord { - vm: Some(suspended_vm), // Would need a suspended VM - async_executable: Some(AsyncExecutable::AsyncFunction(async_function)), // Would need async function - execution_context: Some(execution_context), // Would need execution context - return_promise_capability: result_capability.clone(), - }; - let await_reaction = agent.heap.create(await_reaction_record); - let await_handler = PromiseReactionHandler::Await(await_reaction); - */ - // For now, let's use a simpler approach that demonstrates the concept inner_promise_then( agent, From 3699e5da5e1f930895b0dc264dc4d16b7bb66892 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 28 Jul 2025 20:41:08 -0300 Subject: [PATCH 03/46] Fix build --- .../promise_objects/promise_constructor.rs | 5 ----- 1 file changed, 5 deletions(-) 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 6fba83244..d63ace792 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 @@ -2,14 +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/. -use crate::ecmascript::abstract_operations::operations_on_objects::create_list_from_array_like; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; use crate::ecmascript::builtins::Array; -use crate::ecmascript::execution::agent::JsError; -use crate::ecmascript::types::HeapNumber; -use crate::engine::context::{Bindable, GcScope, NoGcScope}; -use crate::engine::rootable::Scopable; use crate::{ ecmascript::{ abstract_operations::{ From e90ffb0d827f1b21a89f6cb39440a08f2d9fd28d Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 29 Jul 2025 12:02:30 -0300 Subject: [PATCH 04/46] Added closure callback --- .../promise_objects/promise_constructor.rs | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) 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 d63ace792..8a3069361 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 @@ -4,7 +4,8 @@ use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; -use crate::ecmascript::builtins::Array; +use crate::ecmascript::builtins::{create_builtin_function, Array, BuiltinFunctionArgs}; +use crate::ecmascript::fundamental_objects::function_objects::function_constructor::create_dynamic_function; use crate::{ ecmascript::{ abstract_operations::{ @@ -271,10 +272,30 @@ impl PromiseConstructor { let result_capability = PromiseCapability::new(agent, gc.nogc()); let result_promise = result_capability.promise().scope(agent, gc.nogc()); - let fulfill_handler = PromiseReactionHandler::Empty; + let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( + &'a mut Agent, + Value<'b>, + ArgumentsList<'c, 'd>, + GcScope<'_gc, 'e>, + ) -> Result< + Value<'_gc>, + JsError<'_gc>, + > = |_agent, _this_value, arguments, _gc| { + let result_value = arguments.get(0); + eprintln!("Promise fulfilled with result: {:?}", result_value); + Ok(result_value.unbind()) + }; + + let result_callback = create_builtin_function( + agent, + Behaviour::Regular(result_callback_closure), + BuiltinFunctionArgs::new(0, "Promise.all callback"), + gc.nogc(), + ); + + let fulfill_handler = PromiseReactionHandler::JobCallback(result_callback.into()); let reject_handler = PromiseReactionHandler::Empty; - // For now, let's use a simpler approach that demonstrates the concept inner_promise_then( agent, promise_to_await, @@ -284,7 +305,6 @@ impl PromiseConstructor { gc.nogc(), ); - // Return the result promise Ok(result_promise.get(agent).into_value()) } From 8f93139eb0425b64cffa6410aea4bcf71e70ff13 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 31 Jul 2025 10:12:17 -0300 Subject: [PATCH 05/46] Add PromiseAll reaction handler --- .../promise_jobs.rs | 20 +++++++++++++++- .../promise_reaction_records.rs | 23 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) 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 409e95553..18f84045a 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 @@ -288,6 +288,23 @@ impl PromiseReactionJob { } } } + PromiseReactionHandler::PromiseAll { + result_promise: _, + remaining_unresolved_promise_count: _, + result_array: _, + } => { + let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); + match agent[reaction].reaction_type { + PromiseReactionType::Fulfill => { + // d.i.1. Let handlerResult be NormalCompletion(argument). + (Ok(argument), capability) + } + PromiseReactionType::Reject => { + // d.ii.1. Let handlerResult be ThrowCompletion(argument). + (Err(JsError::new(argument)), capability) + } + } + } }; // f. If promiseCapability is undefined, then @@ -348,7 +365,8 @@ pub(crate) fn new_promise_reaction_job( | PromiseReactionHandler::AsyncFromSyncIteratorClose(_) | PromiseReactionHandler::AsyncModule(_) | PromiseReactionHandler::DynamicImport { .. } - | PromiseReactionHandler::DynamicImportEvaluate { .. } => None, + | PromiseReactionHandler::DynamicImportEvaluate { .. } + | PromiseReactionHandler::PromiseAll { .. } => 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 74d2a19e6..6caf2b9dc 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 @@ -7,7 +7,7 @@ use core::ops::{Index, IndexMut}; use crate::{ ecmascript::{ builtins::{ - async_generator_objects::AsyncGenerator, + Array, async_generator_objects::AsyncGenerator, control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction, promise::Promise, }, @@ -68,6 +68,11 @@ pub(crate) enum PromiseReactionHandler<'a> { promise: Promise<'a>, module: AbstractModule<'a>, }, + PromiseAll { + result_promise: Promise<'a>, + remaining_unresolved_promise_count: usize, + result_array: Array<'a>, + }, Empty, } @@ -85,6 +90,14 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { promise.mark_values(queues); module.mark_values(queues); } + Self::PromiseAll { + result_promise: promise, + remaining_unresolved_promise_count: _, + result_array, + } => { + promise.mark_values(queues); + result_array.mark_values(queues); + } Self::Empty => {} } } @@ -104,6 +117,14 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { promise.sweep_values(compactions); module.sweep_values(compactions); } + Self::PromiseAll { + result_promise: promise, + remaining_unresolved_promise_count: _, + result_array, + } => { + promise.sweep_values(compactions); + result_array.sweep_values(compactions); + } Self::Empty => {} } } From 54881929a63f4b8383751711c51e356e783d4d3f Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 31 Jul 2025 10:53:23 -0300 Subject: [PATCH 06/46] Decrease promise count --- .../promise_jobs.rs | 53 +++++++++++++------ .../promise_reaction_records.rs | 2 +- 2 files changed, 38 insertions(+), 17 deletions(-) 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 18f84045a..0c57fa3e1 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 @@ -146,6 +146,41 @@ impl PromiseReactionJob { let reaction = reaction.take(agent).bind(gc.nogc()); let argument = argument.take(agent).bind(gc.nogc()); // The following are substeps of point 1 in NewPromiseReactionJob. + // Handle PromiseAll case separately to avoid borrowing conflicts + if matches!( + agent[reaction].handler, + PromiseReactionHandler::PromiseAll { .. } + ) { + let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); + let reaction_type = agent[reaction].reaction_type; + + if let PromiseReactionHandler::PromiseAll { + remaining_unresolved_promise_count, + .. + } = &mut agent[reaction].handler + { + let (handler_result, promise_capability) = match reaction_type { + PromiseReactionType::Fulfill => { + *remaining_unresolved_promise_count -= 1; + // TODO: handler promise finish + (Ok(argument), capability) + } + PromiseReactionType::Reject => { + // TODO: handle rejections + (Err(JsError::new(argument)), capability) + } + }; + + match handler_result { + Err(err) => promise_capability.reject(agent, err.value(), gc.nogc()), + Ok(value) => promise_capability + .unbind() + .resolve(agent, value.unbind(), gc), + }; + return Ok(()); + } + } + let (handler_result, promise_capability) = match agent[reaction].handler { PromiseReactionHandler::Empty => { let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); @@ -288,22 +323,8 @@ impl PromiseReactionJob { } } } - PromiseReactionHandler::PromiseAll { - result_promise: _, - remaining_unresolved_promise_count: _, - result_array: _, - } => { - let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); - match agent[reaction].reaction_type { - PromiseReactionType::Fulfill => { - // d.i.1. Let handlerResult be NormalCompletion(argument). - (Ok(argument), capability) - } - PromiseReactionType::Reject => { - // d.ii.1. Let handlerResult be ThrowCompletion(argument). - (Err(JsError::new(argument)), capability) - } - } + PromiseReactionHandler::PromiseAll { .. } => { + unreachable!("PromiseAll case is handled separately above") } }; 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 6caf2b9dc..47bdde191 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 @@ -70,7 +70,7 @@ pub(crate) enum PromiseReactionHandler<'a> { }, PromiseAll { result_promise: Promise<'a>, - remaining_unresolved_promise_count: usize, + remaining_unresolved_promise_count: u32, result_array: Array<'a>, }, Empty, From 4d8bdc2ea440543f9aa60c78111abf6ac3fd1a4f Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 4 Aug 2025 07:53:48 -0300 Subject: [PATCH 07/46] Add Promise All Heap Data --- .../promise_abstract_operations.rs | 1 + .../promise_all_record.rs | 180 ++++++++++++++++++ nova_vm/src/heap.rs | 3 + nova_vm/src/heap/heap_bits.rs | 9 + 4 files changed, 193 insertions(+) create mode 100644 nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_record.rs 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 23f08d039..e9c1c978b 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,6 +2,7 @@ // 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_jobs; 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 new file mode 100644 index 000000000..aae6f1691 --- /dev/null +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_all_record.rs @@ -0,0 +1,180 @@ +// 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 core::ops::{Index, IndexMut}; + +use crate::{ + ecmascript::{ + builtins::{ + Array, + promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, + }, + execution::Agent, + types::Value, + }, + engine::context::{Bindable, NoGcScope}, + heap::{ + CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, + }, +}; + +#[derive(Debug, Clone, Copy)] +pub struct PromiseAllRecord<'a> { + promise_capability: &'a PromiseCapability<'a>, + remaining_unresolved_promise_count: u32, + result_array: Array<'a>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct PromiseAllRecordHeapData<'a>(BaseIndex<'a, PromiseAllRecord<'a>>); + +impl<'a> PromiseAllRecord<'a> { + pub(crate) fn new( + agent: &mut Agent, + promise_capability: &'a PromiseCapability<'a>, + num_promises: u32, + gc: NoGcScope<'a, '_>, + ) -> Self { + let undefined_values = vec![Value::Undefined; num_promises as usize]; + let result_array = Array::from_slice(agent, &undefined_values, gc); + Self { + promise_capability, + remaining_unresolved_promise_count: num_promises, + result_array, + } + } + + pub(crate) fn on_promise_fufilled( + &mut self, + agent: &mut Agent, + index: u32, + value: Value<'a>, + gc: NoGcScope<'a, '_>, + ) { + // Update the result array at the specified index by reconstructing it + let array_slice = self.result_array.as_slice(agent); + let new_values: Vec = array_slice + .iter() + .enumerate() + .map(|(i, opt_val)| { + if i == index as usize { + value // Use the new value at this index + } else { + opt_val.unwrap_or(Value::Undefined) // Keep existing or default + } + }) + .collect(); + + self.result_array = Array::from_slice(agent, &new_values, gc); + + self.remaining_unresolved_promise_count -= 1; + if self.remaining_unresolved_promise_count == 0 { + eprintln!( + "All promises fulfilled, should resolve main promise: {:#?}", + self.result_array + ); + // self.promise_capability + // .resolve(agent, self.result_array, gc.into_nogc()); + } + } +} + +impl PromiseAllRecordHeapData<'_> { + pub(crate) const fn get_index(self) -> usize { + self.0.into_index() + } +} + +impl Index> for Agent { + type Output = PromiseAllRecord<'static>; + + fn index(&self, index: PromiseAllRecordHeapData) -> &Self::Output { + &self.heap.promise_all_records[index] + } +} + +impl IndexMut> for Agent { + fn index_mut(&mut self, index: PromiseAllRecordHeapData) -> &mut Self::Output { + &mut self.heap.promise_all_records[index] + } +} + +impl Index> for Vec>> { + type Output = PromiseAllRecord<'static>; + + fn index(&self, index: PromiseAllRecordHeapData) -> &Self::Output { + self.get(index.get_index()) + .expect("PromiseAllRecord out of bounds") + .as_ref() + .expect("PromiseAllRecord slot empty") + } +} + +impl IndexMut> for Vec>> { + fn index_mut(&mut self, index: PromiseAllRecordHeapData) -> &mut Self::Output { + self.get_mut(index.get_index()) + .expect("PromiseAllRecord out of bounds") + .as_mut() + .expect("PromiseAllRecord slot empty") + } +} + +impl HeapMarkAndSweep for PromiseAllRecord<'static> { + fn mark_values(&self, queues: &mut WorkQueues) { + self.result_array.mark_values(queues); + self.promise_capability.mark_values(queues); + } + + fn sweep_values(&mut self, compactions: &CompactionLists) { + self.result_array.sweep_values(compactions); + self.promise_capability.sweep_values(compactions); + } +} + +unsafe impl Bindable for PromiseAllRecord<'_> { + type Of<'a> = PromiseAllRecord<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + +impl HeapMarkAndSweep for PromiseAllRecordHeapData<'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); + } +} + +unsafe impl Bindable for PromiseAllRecordHeapData<'_> { + type Of<'a> = PromiseAllRecordHeapData<'a>; + + #[inline(always)] + fn unbind(self) -> Self::Of<'static> { + unsafe { core::mem::transmute::>(self) } + } + + #[inline(always)] + fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { + unsafe { core::mem::transmute::>(self) } + } +} + +impl<'a> CreateHeapData, PromiseAllRecordHeapData<'a>> for Heap { + fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAllRecordHeapData<'a> { + self.promise_all_records.push(Some(data.unbind())); + self.alloc_counter += core::mem::size_of::>>(); + PromiseAllRecordHeapData(BaseIndex::last(&self.promise_all_records)) + } +} diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 0b58458b7..8a7ba2a49 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -80,6 +80,7 @@ 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, proxy::data::ProxyHeapData, text_processing::{ @@ -173,6 +174,7 @@ pub struct Heap { pub promise_resolving_functions: Vec>>, pub promise_finally_functions: Vec>, pub promises: Vec>>, + pub promise_all_records: Vec>>, pub proxys: Vec>>, pub realms: Vec>>, #[cfg(feature = "regexp")] @@ -321,6 +323,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), proxys: 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 00819fcf0..fa437b196 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -50,6 +50,7 @@ use crate::ecmascript::{ ordinary::{caches::PropertyLookupCache, shape::ObjectShape}, primitive_objects::PrimitiveObject, promise::Promise, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, promise_objects::promise_abstract_operations::promise_finally_functions::BuiltinPromiseFinallyFunction, proxy::Proxy, text_processing::{ @@ -131,6 +132,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 proxys: Box<[bool]>, pub realms: Box<[bool]>, #[cfg(feature = "regexp")] @@ -216,6 +218,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 proxys: Vec>, pub realms: Vec>, #[cfg(feature = "regexp")] @@ -301,6 +304,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 proxys = vec![false; heap.proxys.len()]; let realms = vec![false; heap.realms.len()]; #[cfg(feature = "regexp")] @@ -383,6 +387,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(), proxys: proxys.into_boxed_slice(), realms: realms.into_boxed_slice(), #[cfg(feature = "regexp")] @@ -473,6 +478,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), proxys: Vec::with_capacity(heap.proxys.len() / 4), realms: Vec::with_capacity(heap.realms.len() / 4), #[cfg(feature = "regexp")] @@ -561,6 +567,7 @@ impl WorkQueues { promise_reaction_records, promise_resolving_functions, promise_finally_functions, + promise_all_records, proxys, realms, #[cfg(feature = "regexp")] @@ -1010,6 +1017,7 @@ pub(crate) struct CompactionLists { pub promise_resolving_functions: CompactionList, pub promise_finally_functions: CompactionList, pub promises: CompactionList, + pub promise_all_records: CompactionList, pub proxys: CompactionList, pub realms: CompactionList, #[cfg(feature = "regexp")] @@ -1111,6 +1119,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), #[cfg(feature = "regexp")] regexps: CompactionList::from_mark_bits(&bits.regexps), #[cfg(feature = "regexp")] From 7d5530cb33223f1ff2e861a9ee731304879c2bdf Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Wed, 6 Aug 2025 07:25:31 -0300 Subject: [PATCH 08/46] Update reactions --- .../promise_jobs.rs | 61 ++++++--------- .../promise_reaction_records.rs | 24 +++--- .../promise_objects/promise_constructor.rs | 77 ++++++++++++------- 3 files changed, 82 insertions(+), 80 deletions(-) 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 0c57fa3e1..dda7e19bd 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 @@ -4,6 +4,19 @@ //! ## [27.2.2 Promise Jobs](https://tc39.es/ecma262/#sec-promise-jobs) +use crate::ecmascript::abstract_operations::operations_on_iterator_objects::{ + create_iter_result_object, iterator_close_with_error, +}; +use crate::ecmascript::builtins::Array; +use crate::ecmascript::scripts_and_modules::module::module_semantics::cyclic_module_records::{ + async_module_execution_fulfilled, async_module_execution_rejected, +}; +use crate::ecmascript::scripts_and_modules::module::{ + import_get_module_namespace, link_and_evaluate, +}; +use crate::engine::Global; +use crate::engine::context::{Bindable, GcScope, NoGcScope}; +use crate::engine::rootable::Scopable; use crate::{ ecmascript::{ abstract_operations::{ @@ -145,41 +158,6 @@ impl PromiseReactionJob { let Self { reaction, argument } = self; let reaction = reaction.take(agent).bind(gc.nogc()); let argument = argument.take(agent).bind(gc.nogc()); - // The following are substeps of point 1 in NewPromiseReactionJob. - // Handle PromiseAll case separately to avoid borrowing conflicts - if matches!( - agent[reaction].handler, - PromiseReactionHandler::PromiseAll { .. } - ) { - let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); - let reaction_type = agent[reaction].reaction_type; - - if let PromiseReactionHandler::PromiseAll { - remaining_unresolved_promise_count, - .. - } = &mut agent[reaction].handler - { - let (handler_result, promise_capability) = match reaction_type { - PromiseReactionType::Fulfill => { - *remaining_unresolved_promise_count -= 1; - // TODO: handler promise finish - (Ok(argument), capability) - } - PromiseReactionType::Reject => { - // TODO: handle rejections - (Err(JsError::new(argument)), capability) - } - }; - - match handler_result { - Err(err) => promise_capability.reject(agent, err.value(), gc.nogc()), - Ok(value) => promise_capability - .unbind() - .resolve(agent, value.unbind(), gc), - }; - return Ok(()); - } - } let (handler_result, promise_capability) = match agent[reaction].handler { PromiseReactionHandler::Empty => { @@ -323,8 +301,17 @@ impl PromiseReactionJob { } } } - PromiseReactionHandler::PromiseAll { .. } => { - unreachable!("PromiseAll case is handled separately above") + PromiseReactionHandler::PromiseAll { promise_all, index } => { + let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); + match agent[reaction].reaction_type { + PromiseReactionType::Fulfill => { + let mut promise_all_record = agent.heap.promise_all_records[promise_all]; + promise_all_record.on_promise_fufilled(agent, index, argument, gc.nogc()); + + (Ok(argument), capability) + } + PromiseReactionType::Reject => (Err(JsError::new(argument)), capability), + } } }; 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 47bdde191..484ca06d2 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,6 +10,7 @@ use crate::{ Array, async_generator_objects::AsyncGenerator, control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction, promise::Promise, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, }, execution::Agent, scripts_and_modules::module::module_semantics::{ @@ -69,9 +70,8 @@ pub(crate) enum PromiseReactionHandler<'a> { module: AbstractModule<'a>, }, PromiseAll { - result_promise: Promise<'a>, - remaining_unresolved_promise_count: u32, - result_array: Array<'a>, + index: u32, + promise_all: PromiseAllRecordHeapData<'a>, }, Empty, } @@ -91,13 +91,9 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { module.mark_values(queues); } Self::PromiseAll { - result_promise: promise, - remaining_unresolved_promise_count: _, - result_array, - } => { - promise.mark_values(queues); - result_array.mark_values(queues); - } + index: _, + promise_all, + } => promise_all.mark_values(queues), Self::Empty => {} } } @@ -118,12 +114,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> { module.sweep_values(compactions); } Self::PromiseAll { - result_promise: promise, - remaining_unresolved_promise_count: _, - result_array, + index: _, + promise_all, } => { - promise.sweep_values(compactions); - result_array.sweep_values(compactions); + promise_all.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 8a3069361..0dcd7bf8a 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 @@ -2,6 +2,8 @@ // 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_objects::create_array_from_list; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; use crate::ecmascript::builtins::{create_builtin_function, Array, BuiltinFunctionArgs}; @@ -270,42 +272,61 @@ impl PromiseConstructor { }; let result_capability = PromiseCapability::new(agent, gc.nogc()); - let result_promise = result_capability.promise().scope(agent, gc.nogc()); - - let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( - &'a mut Agent, - Value<'b>, - ArgumentsList<'c, 'd>, - GcScope<'_gc, 'e>, - ) -> Result< - Value<'_gc>, - JsError<'_gc>, - > = |_agent, _this_value, arguments, _gc| { - let result_value = arguments.get(0); - eprintln!("Promise fulfilled with result: {:?}", result_value); - Ok(result_value.unbind()) + let result_promise = result_capability.promise(); + + // let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( + // &'a mut Agent, + // Value<'b>, + // ArgumentsList<'c, 'd>, + // GcScope<'_gc, 'e>, + // ) -> Result< + // Value<'_gc>, + // JsError<'_gc>, + // > = |_agent, _this_value, arguments, _gc| { + // let result_value = arguments.get(0); + // eprintln!("Promise fulfilled with result: {:?}", result_value); + // Ok(result_value.unbind()) + // }; + + // let result_callback = create_builtin_function( + // agent, + // Behaviour::Regular(result_callback_closure), + // BuiltinFunctionArgs::new(0, "Promise.all callback"), + // gc.nogc(), + // ); + + // let fulfill_handler = PromiseReactionHandler::JobCallback(result_callback.into()); + // let reject_handler = PromiseReactionHandler::Empty; + + // inner_promise_then( + // agent, + // promise_to_await, + // fulfill_handler, + // reject_handler, + // Some(result_capability), + // gc.nogc(), + // ); + + let args: [Value<'gc>; 1] = [Value::Undefined; 1]; + let result_array = Array::from_slice(agent, &args, gc.nogc()); + let promise_all_record = + PromiseAllRecord::new(agent, &result_capability.unbind(), 1, gc.nogc()); + + let promise_all_handler = PromiseReactionHandler::PromiseAll { + index: 0, + promise_all: promise_all_record, }; - let result_callback = create_builtin_function( - agent, - Behaviour::Regular(result_callback_closure), - BuiltinFunctionArgs::new(0, "Promise.all callback"), - gc.nogc(), - ); - - let fulfill_handler = PromiseReactionHandler::JobCallback(result_callback.into()); - let reject_handler = PromiseReactionHandler::Empty; - inner_promise_then( agent, promise_to_await, - fulfill_handler, - reject_handler, - Some(result_capability), + promise_all_handler, + PromiseReactionHandler::Empty, + None, gc.nogc(), ); - Ok(result_promise.get(agent).into_value()) + Ok(result_promise.unbind().into_value()) } fn all_settled<'gc>( From 24ab2a7e9034d9c6ec577d88dbefcac5f36c2bd4 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Wed, 6 Aug 2025 07:27:29 -0300 Subject: [PATCH 09/46] Fix merge conflicts --- .../promise_abstract_operations/promise_jobs.rs | 13 ------------- 1 file changed, 13 deletions(-) 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 dda7e19bd..5f904efbb 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 @@ -4,19 +4,6 @@ //! ## [27.2.2 Promise Jobs](https://tc39.es/ecma262/#sec-promise-jobs) -use crate::ecmascript::abstract_operations::operations_on_iterator_objects::{ - create_iter_result_object, iterator_close_with_error, -}; -use crate::ecmascript::builtins::Array; -use crate::ecmascript::scripts_and_modules::module::module_semantics::cyclic_module_records::{ - async_module_execution_fulfilled, async_module_execution_rejected, -}; -use crate::ecmascript::scripts_and_modules::module::{ - import_get_module_namespace, link_and_evaluate, -}; -use crate::engine::Global; -use crate::engine::context::{Bindable, GcScope, NoGcScope}; -use crate::engine::rootable::Scopable; use crate::{ ecmascript::{ abstract_operations::{ From e2e26568657f56979fa5d6e868638f76b733d646 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 8 Aug 2025 09:22:03 -0300 Subject: [PATCH 10/46] Fix naming --- .../promise_all_record.rs | 54 +++++++++---------- .../promise_reaction_records.rs | 4 +- .../promise_objects/promise_constructor.rs | 9 ++-- nova_vm/src/heap/heap_bits.rs | 3 +- nova_vm/src/heap/heap_gc.rs | 11 ++++ 5 files changed, 48 insertions(+), 33 deletions(-) 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 aae6f1691..376dd0e22 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 @@ -20,17 +20,17 @@ use crate::{ }; #[derive(Debug, Clone, Copy)] -pub struct PromiseAllRecord<'a> { - promise_capability: &'a PromiseCapability<'a>, - remaining_unresolved_promise_count: u32, - result_array: Array<'a>, +pub struct PromiseAllRecordHeapData<'a> { + pub promise_capability: &'a PromiseCapability<'a>, + pub remaining_unresolved_promise_count: u32, + pub result_array: Array<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct PromiseAllRecordHeapData<'a>(BaseIndex<'a, PromiseAllRecord<'a>>); +pub struct PromiseAllRecord<'a>(pub(crate) BaseIndex<'a, PromiseAllRecordHeapData<'a>>); -impl<'a> PromiseAllRecord<'a> { +impl<'a> PromiseAllRecordHeapData<'a> { pub(crate) fn new( agent: &mut Agent, promise_capability: &'a PromiseCapability<'a>, @@ -81,30 +81,30 @@ impl<'a> PromiseAllRecord<'a> { } } -impl PromiseAllRecordHeapData<'_> { +impl PromiseAllRecord<'_> { pub(crate) const fn get_index(self) -> usize { self.0.into_index() } } -impl Index> for Agent { - type Output = PromiseAllRecord<'static>; +impl Index> for Agent { + type Output = PromiseAllRecordHeapData<'static>; - fn index(&self, index: PromiseAllRecordHeapData) -> &Self::Output { + fn index(&self, index: PromiseAllRecord) -> &Self::Output { &self.heap.promise_all_records[index] } } -impl IndexMut> for Agent { - fn index_mut(&mut self, index: PromiseAllRecordHeapData) -> &mut Self::Output { +impl IndexMut> for Agent { + fn index_mut(&mut self, index: PromiseAllRecord) -> &mut Self::Output { &mut self.heap.promise_all_records[index] } } -impl Index> for Vec>> { - type Output = PromiseAllRecord<'static>; +impl Index> for Vec>> { + type Output = PromiseAllRecordHeapData<'static>; - fn index(&self, index: PromiseAllRecordHeapData) -> &Self::Output { + fn index(&self, index: PromiseAllRecord) -> &Self::Output { self.get(index.get_index()) .expect("PromiseAllRecord out of bounds") .as_ref() @@ -112,8 +112,8 @@ impl Index> for Vec> for Vec>> { - fn index_mut(&mut self, index: PromiseAllRecordHeapData) -> &mut Self::Output { +impl IndexMut> for Vec>> { + fn index_mut(&mut self, index: PromiseAllRecord) -> &mut Self::Output { self.get_mut(index.get_index()) .expect("PromiseAllRecord out of bounds") .as_mut() @@ -121,7 +121,7 @@ impl IndexMut> for Vec { +impl HeapMarkAndSweep for PromiseAllRecordHeapData<'static> { fn mark_values(&self, queues: &mut WorkQueues) { self.result_array.mark_values(queues); self.promise_capability.mark_values(queues); @@ -133,8 +133,8 @@ impl HeapMarkAndSweep for PromiseAllRecord<'static> { } } -unsafe impl Bindable for PromiseAllRecord<'_> { - type Of<'a> = PromiseAllRecord<'a>; +unsafe impl Bindable for PromiseAllRecordHeapData<'_> { + type Of<'a> = PromiseAllRecordHeapData<'a>; #[inline(always)] fn unbind(self) -> Self::Of<'static> { @@ -147,7 +147,7 @@ unsafe impl Bindable for PromiseAllRecord<'_> { } } -impl HeapMarkAndSweep for PromiseAllRecordHeapData<'static> { +impl HeapMarkAndSweep for PromiseAllRecord<'static> { fn mark_values(&self, queues: &mut WorkQueues) { queues.promise_all_records.push(*self); } @@ -157,8 +157,8 @@ impl HeapMarkAndSweep for PromiseAllRecordHeapData<'static> { } } -unsafe impl Bindable for PromiseAllRecordHeapData<'_> { - type Of<'a> = PromiseAllRecordHeapData<'a>; +unsafe impl Bindable for PromiseAllRecord<'_> { + type Of<'a> = PromiseAllRecord<'a>; #[inline(always)] fn unbind(self) -> Self::Of<'static> { @@ -171,10 +171,10 @@ unsafe impl Bindable for PromiseAllRecordHeapData<'_> { } } -impl<'a> CreateHeapData, PromiseAllRecordHeapData<'a>> for Heap { - fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAllRecordHeapData<'a> { +impl<'a> CreateHeapData, PromiseAllRecord<'a>> for Heap { + fn create(&mut self, data: PromiseAllRecordHeapData<'a>) -> PromiseAllRecord<'a> { self.promise_all_records.push(Some(data.unbind())); - self.alloc_counter += core::mem::size_of::>>(); - PromiseAllRecordHeapData(BaseIndex::last(&self.promise_all_records)) + self.alloc_counter += core::mem::size_of::>>(); + PromiseAllRecord(BaseIndex::last(&self.promise_all_records)) } } 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 484ca06d2..d522cb827 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::{ Array, async_generator_objects::AsyncGenerator, control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction, promise::Promise, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, }, execution::Agent, scripts_and_modules::module::module_semantics::{ @@ -71,7 +71,7 @@ pub(crate) enum PromiseReactionHandler<'a> { }, PromiseAll { index: u32, - promise_all: PromiseAllRecordHeapData<'a>, + promise_all: PromiseAllRecord<'a>, }, 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 0dcd7bf8a..91155b783 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 @@ -3,7 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::ecmascript::abstract_operations::operations_on_objects::create_array_from_list; -use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::{PromiseAllRecordHeapData, PromiseAllRecord}; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; use crate::ecmascript::builtins::{create_builtin_function, Array, BuiltinFunctionArgs}; @@ -309,8 +309,11 @@ impl PromiseConstructor { let args: [Value<'gc>; 1] = [Value::Undefined; 1]; let result_array = Array::from_slice(agent, &args, gc.nogc()); - let promise_all_record = - PromiseAllRecord::new(agent, &result_capability.unbind(), 1, gc.nogc()); + let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { + promise_capability: &result_capability, + remaining_unresolved_promise_count: 1, + result_array, + }); let promise_all_handler = PromiseReactionHandler::PromiseAll { index: 0, diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index fa437b196..4466c2e8b 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -50,6 +50,7 @@ use crate::ecmascript::{ ordinary::{caches::PropertyLookupCache, shape::ObjectShape}, primitive_objects::PrimitiveObject, promise::Promise, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, promise_objects::promise_abstract_operations::promise_finally_functions::BuiltinPromiseFinallyFunction, proxy::Proxy, @@ -218,7 +219,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_all_records: Vec>, pub proxys: Vec>, pub realms: Vec>, #[cfg(feature = "regexp")] diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index f4ad04a24..1391f291e 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -164,6 +164,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option>], gc promise_resolving_functions, promise_finally_functions, promises, + promise_all_records, proxys, realms, #[cfg(feature = "regexp")] @@ -1331,6 +1332,7 @@ fn sweep( promise_resolving_functions, promise_finally_functions, promises, + promise_all_records, proxys, realms, #[cfg(feature = "regexp")] @@ -1771,6 +1773,15 @@ fn sweep( sweep_heap_vector_values(promises, &compactions, &bits.promises); }); } + if !promise_all_records.is_empty() { + s.spawn(|| { + sweep_heap_vector_values( + promise_all_records, + &compactions, + &bits.promise_all_records, + ); + }); + } if !proxys.is_empty() { s.spawn(|| { sweep_heap_vector_values(proxys, &compactions, &bits.proxys); From 106d50366eee10b617e2d2dfeff67a042163dffa Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 8 Aug 2025 09:34:36 -0300 Subject: [PATCH 11/46] Implemented heap data --- .../promise_all_record.rs | 15 ++------------- .../promise_objects/promise_constructor.rs | 3 +-- nova_vm/src/heap.rs | 3 ++- 3 files changed, 5 insertions(+), 16 deletions(-) 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 376dd0e22..bbb6862b6 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 @@ -7,7 +7,7 @@ use core::ops::{Index, IndexMut}; use crate::{ ecmascript::{ builtins::{ - Array, + Array, promise::Promise, promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, }, execution::Agent, @@ -21,7 +21,6 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct PromiseAllRecordHeapData<'a> { - pub promise_capability: &'a PromiseCapability<'a>, pub remaining_unresolved_promise_count: u32, pub result_array: Array<'a>, } @@ -31,16 +30,10 @@ pub struct PromiseAllRecordHeapData<'a> { pub struct PromiseAllRecord<'a>(pub(crate) BaseIndex<'a, PromiseAllRecordHeapData<'a>>); impl<'a> PromiseAllRecordHeapData<'a> { - pub(crate) fn new( - agent: &mut Agent, - promise_capability: &'a PromiseCapability<'a>, - num_promises: u32, - gc: NoGcScope<'a, '_>, - ) -> Self { + pub(crate) fn new(agent: &mut Agent, num_promises: u32, gc: NoGcScope<'a, '_>) -> Self { let undefined_values = vec![Value::Undefined; num_promises as usize]; let result_array = Array::from_slice(agent, &undefined_values, gc); Self { - promise_capability, remaining_unresolved_promise_count: num_promises, result_array, } @@ -75,8 +68,6 @@ impl<'a> PromiseAllRecordHeapData<'a> { "All promises fulfilled, should resolve main promise: {:#?}", self.result_array ); - // self.promise_capability - // .resolve(agent, self.result_array, gc.into_nogc()); } } } @@ -124,12 +115,10 @@ impl IndexMut> for Vec { fn mark_values(&self, queues: &mut WorkQueues) { self.result_array.mark_values(queues); - self.promise_capability.mark_values(queues); } fn sweep_values(&mut self, compactions: &CompactionLists) { self.result_array.sweep_values(compactions); - self.promise_capability.sweep_values(compactions); } } 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 91155b783..d5fe59bde 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 @@ -310,7 +310,6 @@ impl PromiseConstructor { let args: [Value<'gc>; 1] = [Value::Undefined; 1]; let result_array = Array::from_slice(agent, &args, gc.nogc()); let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { - promise_capability: &result_capability, remaining_unresolved_promise_count: 1, result_array, }); @@ -325,7 +324,7 @@ impl PromiseConstructor { promise_to_await, promise_all_handler, PromiseReactionHandler::Empty, - None, + Some(result_capability), gc.nogc(), ); diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 8a7ba2a49..1f7d29e67 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -81,6 +81,7 @@ use crate::{ primitive_objects::PrimitiveObjectHeapData, promise::data::PromiseHeapData, promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, promise_objects::promise_abstract_operations::promise_finally_functions::PromiseFinallyFunctionHeapData, proxy::data::ProxyHeapData, text_processing::{ @@ -174,7 +175,7 @@ pub struct Heap { pub promise_resolving_functions: Vec>>, pub promise_finally_functions: Vec>, pub promises: Vec>>, - pub promise_all_records: Vec>>, + pub promise_all_records: Vec>>, pub proxys: Vec>>, pub realms: Vec>>, #[cfg(feature = "regexp")] From be860774f69fef6e2ecd7625d45e802af4a4c9af Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 8 Aug 2025 10:20:50 -0300 Subject: [PATCH 12/46] Minor changes --- .../promise_all_record.rs | 13 ++---- .../promise_objects/promise_constructor.rs | 45 +++++++++++++++---- 2 files changed, 40 insertions(+), 18 deletions(-) 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 bbb6862b6..47dcb155c 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 @@ -30,15 +30,6 @@ pub struct PromiseAllRecordHeapData<'a> { pub struct PromiseAllRecord<'a>(pub(crate) BaseIndex<'a, PromiseAllRecordHeapData<'a>>); impl<'a> PromiseAllRecordHeapData<'a> { - pub(crate) fn new(agent: &mut Agent, num_promises: u32, gc: NoGcScope<'a, '_>) -> Self { - let undefined_values = vec![Value::Undefined; num_promises as usize]; - let result_array = Array::from_slice(agent, &undefined_values, gc); - Self { - remaining_unresolved_promise_count: num_promises, - result_array, - } - } - pub(crate) fn on_promise_fufilled( &mut self, agent: &mut Agent, @@ -60,6 +51,8 @@ impl<'a> PromiseAllRecordHeapData<'a> { }) .collect(); + eprintln!("New values: {:#?}", new_values); + self.result_array = Array::from_slice(agent, &new_values, gc); self.remaining_unresolved_promise_count -= 1; @@ -68,6 +61,8 @@ impl<'a> PromiseAllRecordHeapData<'a> { "All promises fulfilled, should resolve main promise: {:#?}", self.result_array ); + // self.promise_capability + // .resolve(agent, self.result_array, gc.into_nogc()); } } } 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 d5fe59bde..37ab352ca 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 @@ -271,7 +271,24 @@ impl PromiseConstructor { )); }; + let Some(second_element) = array_slice[1] else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Second element is None", + gc.into_nogc(), + )); + }; + let Value::Promise(second_promise) = second_element.unbind() else { + return Err(agent.throw_exception_with_static_message( + ExceptionType::TypeError, + "Second element is not a Promise", + gc.into_nogc(), + )); + }; + let result_capability = PromiseCapability::new(agent, gc.nogc()); + + let second_capability = PromiseCapability::new(agent, gc.nogc()); let result_promise = result_capability.promise(); // let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( @@ -307,27 +324,37 @@ impl PromiseConstructor { // gc.nogc(), // ); - let args: [Value<'gc>; 1] = [Value::Undefined; 1]; - let result_array = Array::from_slice(agent, &args, gc.nogc()); + let undefined_values = vec![Value::Undefined; 2]; + let result_array = Array::from_slice(agent, &undefined_values, gc.nogc()); let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { - remaining_unresolved_promise_count: 1, + remaining_unresolved_promise_count: 2, result_array, }); - let promise_all_handler = PromiseReactionHandler::PromiseAll { - index: 0, - promise_all: promise_all_record, - }; - inner_promise_then( agent, promise_to_await, - promise_all_handler, + PromiseReactionHandler::PromiseAll { + index: 0, + promise_all: promise_all_record, + }, PromiseReactionHandler::Empty, Some(result_capability), gc.nogc(), ); + inner_promise_then( + agent, + second_promise, + PromiseReactionHandler::PromiseAll { + index: 1, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(second_capability), + gc.nogc(), + ); + Ok(result_promise.unbind().into_value()) } From cab7195a1bc1c2a476ebc7ee4788372d76dae3aa Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 8 Aug 2025 17:09:48 -0300 Subject: [PATCH 13/46] Mutate heap data --- .../promise_all_record.rs | 9 +++++++- .../promise_jobs.rs | 23 +++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) 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 47dcb155c..cc4357b33 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 @@ -13,7 +13,7 @@ use crate::{ execution::Agent, types::Value, }, - engine::context::{Bindable, NoGcScope}, + engine::context::{Bindable, GcScope, NoGcScope}, heap::{ CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, }, @@ -37,6 +37,7 @@ impl<'a> PromiseAllRecordHeapData<'a> { value: Value<'a>, gc: NoGcScope<'a, '_>, ) { + eprintln!("Self memory address: {:p}", &self); // Update the result array at the specified index by reconstructing it let array_slice = self.result_array.as_slice(agent); let new_values: Vec = array_slice @@ -51,9 +52,15 @@ impl<'a> PromiseAllRecordHeapData<'a> { }) .collect(); + eprintln!("Index: {:#?}", index); eprintln!("New values: {:#?}", new_values); + eprintln!( + "Remaining unresolved promise count: {:#?}", + self.remaining_unresolved_promise_count + ); self.result_array = Array::from_slice(agent, &new_values, gc); + eprintln!("Result array memory address: {:p}", &self.result_array); self.remaining_unresolved_promise_count -= 1; if self.remaining_unresolved_promise_count == 0 { 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 5f904efbb..6d475f150 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 @@ -292,8 +292,27 @@ impl PromiseReactionJob { let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); match agent[reaction].reaction_type { PromiseReactionType::Fulfill => { - let mut promise_all_record = agent.heap.promise_all_records[promise_all]; - promise_all_record.on_promise_fufilled(agent, index, argument, gc.nogc()); + // Take out the record to drop the vec borrow before we use `agent`/`gc` + let rec = { + let slot = agent + .heap + .promise_all_records + .get_mut(promise_all.get_index()) + .expect("PromiseAllRecord out of bounds"); + slot.take().expect("PromiseAllRecord slot empty") + }; + + // Bind to current scope and mutate + let mut rec_bound = rec.unbind().bind(gc.nogc()); + rec_bound.on_promise_fufilled(agent, index, argument.unbind(), gc.nogc()); + + // Write back with 'static lifetime + agent + .heap + .promise_all_records + .get_mut(promise_all.get_index()) + .unwrap() + .replace(rec_bound.unbind()); (Ok(argument), capability) } From 960b9ed7dce19e5e3d85ebd05ae2ad5b4b13474e Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 11 Aug 2025 09:59:55 -0300 Subject: [PATCH 14/46] Fix warns --- .../promise_all_record.rs | 34 ++++++++----------- .../promise_jobs.rs | 7 +++- .../promise_reaction_records.rs | 2 +- .../promise_objects/promise_constructor.rs | 14 ++++---- 4 files changed, 29 insertions(+), 28 deletions(-) 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 cc4357b33..4d9f9b950 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 @@ -11,7 +11,7 @@ use crate::{ promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability, }, execution::Agent, - types::Value, + types::{IntoValue, Value}, }, engine::context::{Bindable, GcScope, NoGcScope}, heap::{ @@ -23,6 +23,7 @@ use crate::{ pub struct PromiseAllRecordHeapData<'a> { pub remaining_unresolved_promise_count: u32, pub result_array: Array<'a>, + pub promise: Promise<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -35,12 +36,11 @@ impl<'a> PromiseAllRecordHeapData<'a> { agent: &mut Agent, index: u32, value: Value<'a>, - gc: NoGcScope<'a, '_>, + mut gc: GcScope<'a, '_>, ) { - eprintln!("Self memory address: {:p}", &self); - // Update the result array at the specified index by reconstructing it + value.bind(gc.nogc()); let array_slice = self.result_array.as_slice(agent); - let new_values: Vec = array_slice + array_slice .iter() .enumerate() .map(|(i, opt_val)| { @@ -50,26 +50,20 @@ impl<'a> PromiseAllRecordHeapData<'a> { opt_val.unwrap_or(Value::Undefined) // Keep existing or default } }) - .collect(); + .collect::>(); - eprintln!("Index: {:#?}", index); - eprintln!("New values: {:#?}", new_values); - eprintln!( - "Remaining unresolved promise count: {:#?}", - self.remaining_unresolved_promise_count - ); - - self.result_array = Array::from_slice(agent, &new_values, gc); - eprintln!("Result array memory address: {:p}", &self.result_array); + // Update in place to avoid consuming the GC scope + let elements = self.result_array.as_mut_slice(agent); + elements[index as usize] = Some(value.unbind()); self.remaining_unresolved_promise_count -= 1; if self.remaining_unresolved_promise_count == 0 { - eprintln!( - "All promises fulfilled, should resolve main promise: {:#?}", - self.result_array + let capability = PromiseCapability::from_promise(self.promise, true); + capability.resolve( + agent, + self.result_array.unbind().into_value(), + gc.reborrow(), ); - // self.promise_capability - // .resolve(agent, self.result_array, gc.into_nogc()); } } } 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 6d475f150..7eab20a9f 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 @@ -304,7 +304,12 @@ impl PromiseReactionJob { // Bind to current scope and mutate let mut rec_bound = rec.unbind().bind(gc.nogc()); - rec_bound.on_promise_fufilled(agent, index, argument.unbind(), gc.nogc()); + rec_bound.on_promise_fufilled( + agent, + index, + argument.clone(), + gc.reborrow(), + ); // Write back with 'static lifetime agent 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 d522cb827..9006c2a7a 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 @@ -7,7 +7,7 @@ use core::ops::{Index, IndexMut}; use crate::{ ecmascript::{ builtins::{ - Array, async_generator_objects::AsyncGenerator, + async_generator_objects::AsyncGenerator, control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction, promise::Promise, promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, 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 37ab352ca..7579ec098 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 @@ -2,12 +2,10 @@ // 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_objects::create_array_from_list; -use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::{PromiseAllRecordHeapData, PromiseAllRecord}; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; -use crate::ecmascript::builtins::{create_builtin_function, Array, BuiltinFunctionArgs}; -use crate::ecmascript::fundamental_objects::function_objects::function_constructor::create_dynamic_function; +use crate::ecmascript::builtins::Array; use crate::{ ecmascript::{ abstract_operations::{ @@ -287,9 +285,12 @@ impl PromiseConstructor { }; let result_capability = PromiseCapability::new(agent, gc.nogc()); - let second_capability = PromiseCapability::new(agent, gc.nogc()); - let result_promise = result_capability.promise(); + + let result_promise = PromiseCapability::new(agent, gc.nogc()) + .promise() + .unbind() + .bind(gc.nogc()); // let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( // &'a mut Agent, @@ -329,6 +330,7 @@ impl PromiseConstructor { let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { remaining_unresolved_promise_count: 2, result_array, + promise: result_promise, }); inner_promise_then( From ddb27f492ec560af3029e9ce559dda441a176b45 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 12 Aug 2025 10:26:29 -0300 Subject: [PATCH 15/46] Updated GC --- .../promise_all_record.rs | 2 +- .../promise_abstract_operations/promise_jobs.rs | 2 +- .../promise_objects/promise_constructor.rs | 17 +++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) 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 4d9f9b950..5011b7f11 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 @@ -58,7 +58,7 @@ impl<'a> PromiseAllRecordHeapData<'a> { self.remaining_unresolved_promise_count -= 1; if self.remaining_unresolved_promise_count == 0 { - let capability = PromiseCapability::from_promise(self.promise, true); + let capability = PromiseCapability::from_promise(self.promise.unbind(), true); capability.resolve( agent, self.result_array.unbind().into_value(), 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 7eab20a9f..1fa48d203 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 @@ -307,7 +307,7 @@ impl PromiseReactionJob { rec_bound.on_promise_fufilled( agent, index, - argument.clone(), + argument.clone().unbind(), gc.reborrow(), ); 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 7579ec098..b66dd4ea0 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 @@ -325,35 +325,36 @@ impl PromiseConstructor { // gc.nogc(), // ); - let undefined_values = vec![Value::Undefined; 2]; - let result_array = Array::from_slice(agent, &undefined_values, gc.nogc()); + let undefined_values = (vec![Value::Undefined.bind(gc.nogc()); 2]).bind(gc.nogc()); + let result_array = + Array::from_slice(agent, &undefined_values.unbind(), gc.nogc()).bind(gc.nogc()); let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { remaining_unresolved_promise_count: 2, - result_array, - promise: result_promise, + result_array: result_array.unbind(), + promise: result_promise.unbind(), }); inner_promise_then( agent, - promise_to_await, + promise_to_await.unbind(), PromiseReactionHandler::PromiseAll { index: 0, promise_all: promise_all_record, }, PromiseReactionHandler::Empty, - Some(result_capability), + Some(result_capability.unbind()), gc.nogc(), ); inner_promise_then( agent, - second_promise, + second_promise.unbind(), PromiseReactionHandler::PromiseAll { index: 1, promise_all: promise_all_record, }, PromiseReactionHandler::Empty, - Some(second_capability), + Some(second_capability.unbind()), gc.nogc(), ); From 80a5ba235119d7293dd30e88c6e01af1e4577900 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 12 Aug 2025 10:40:10 -0300 Subject: [PATCH 16/46] Remove useless code in `on_promise_fulfilled` --- .../promise_all_record.rs | 31 ++++++------------- .../promise_jobs.rs | 3 +- 2 files changed, 11 insertions(+), 23 deletions(-) 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 5011b7f11..bd099449a 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 @@ -36,34 +36,21 @@ impl<'a> PromiseAllRecordHeapData<'a> { agent: &mut Agent, index: u32, value: Value<'a>, - mut gc: GcScope<'a, '_>, + gc: NoGcScope<'a, '_>, ) { - value.bind(gc.nogc()); - let array_slice = self.result_array.as_slice(agent); - array_slice - .iter() - .enumerate() - .map(|(i, opt_val)| { - if i == index as usize { - value // Use the new value at this index - } else { - opt_val.unwrap_or(Value::Undefined) // Keep existing or default - } - }) - .collect::>(); - - // Update in place to avoid consuming the GC scope + value.bind(gc); let elements = self.result_array.as_mut_slice(agent); elements[index as usize] = Some(value.unbind()); self.remaining_unresolved_promise_count -= 1; if self.remaining_unresolved_promise_count == 0 { - let capability = PromiseCapability::from_promise(self.promise.unbind(), true); - capability.resolve( - agent, - self.result_array.unbind().into_value(), - gc.reborrow(), - ); + eprintln!("Promise fulfilled: {:#?}", elements); + // let capability = PromiseCapability::from_promise(self.promise.unbind(), true); + // capability.resolve( + // agent, + // self.result_array.unbind().into_value(), + // gc.reborrow(), + // ); } } } 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 1fa48d203..3e9867eb1 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 @@ -308,7 +308,8 @@ impl PromiseReactionJob { agent, index, argument.clone().unbind(), - gc.reborrow(), + // gc.reborrow(), + gc.nogc(), ); // Write back with 'static lifetime From 33924a6cdeb6b858a247114fc6cd179eddd23148 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 12 Aug 2025 10:40:49 -0300 Subject: [PATCH 17/46] Re-add gc.reborrow() --- .../promise_all_record.rs | 16 ++++++++-------- .../promise_abstract_operations/promise_jobs.rs | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) 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 bd099449a..142dc8bbe 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 @@ -36,21 +36,21 @@ impl<'a> PromiseAllRecordHeapData<'a> { agent: &mut Agent, index: u32, value: Value<'a>, - gc: NoGcScope<'a, '_>, + mut gc: GcScope<'a, '_>, ) { - value.bind(gc); + value.bind(gc.nogc()); let elements = self.result_array.as_mut_slice(agent); elements[index as usize] = Some(value.unbind()); self.remaining_unresolved_promise_count -= 1; if self.remaining_unresolved_promise_count == 0 { eprintln!("Promise fulfilled: {:#?}", elements); - // let capability = PromiseCapability::from_promise(self.promise.unbind(), true); - // capability.resolve( - // agent, - // self.result_array.unbind().into_value(), - // gc.reborrow(), - // ); + let capability = PromiseCapability::from_promise(self.promise.unbind(), true); + capability.resolve( + agent, + self.result_array.unbind().into_value(), + gc.reborrow(), + ); } } } 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 3e9867eb1..1fa48d203 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 @@ -308,8 +308,7 @@ impl PromiseReactionJob { agent, index, argument.clone().unbind(), - // gc.reborrow(), - gc.nogc(), + gc.reborrow(), ); // Write back with 'static lifetime From 12598994c96ce4ae1c6e8d66c341ac1ba4c049f4 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 14 Aug 2025 07:25:33 -0300 Subject: [PATCH 18/46] unbind fix --- .../promise_abstract_operations/promise_all_record.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 142dc8bbe..d15774818 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 @@ -46,11 +46,7 @@ impl<'a> PromiseAllRecordHeapData<'a> { if self.remaining_unresolved_promise_count == 0 { eprintln!("Promise fulfilled: {:#?}", elements); let capability = PromiseCapability::from_promise(self.promise.unbind(), true); - capability.resolve( - agent, - self.result_array.unbind().into_value(), - gc.reborrow(), - ); + capability.resolve(agent, self.result_array.into_value().unbind(), gc); } } } From 50a3b8481a0c945643ee4b134d6b1818b5279f81 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 14 Aug 2025 09:11:01 -0300 Subject: [PATCH 19/46] Rename structs --- .../promise_all_record.rs | 48 +++++++++---------- .../promise_reaction_records.rs | 4 +- .../promise_objects/promise_constructor.rs | 4 +- nova_vm/src/heap.rs | 3 +- nova_vm/src/heap/heap_bits.rs | 5 +- 5 files changed, 31 insertions(+), 33 deletions(-) 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 d15774818..7f4719187 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 @@ -20,7 +20,7 @@ use crate::{ }; #[derive(Debug, Clone, Copy)] -pub struct PromiseAllRecordHeapData<'a> { +pub struct PromiseAllRecord<'a> { pub remaining_unresolved_promise_count: u32, pub result_array: Array<'a>, pub promise: Promise<'a>, @@ -28,9 +28,9 @@ pub struct PromiseAllRecordHeapData<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct PromiseAllRecord<'a>(pub(crate) BaseIndex<'a, PromiseAllRecordHeapData<'a>>); +pub struct PromiseAll<'a>(pub(crate) BaseIndex<'a, PromiseAllRecord<'a>>); -impl<'a> PromiseAllRecordHeapData<'a> { +impl<'a> PromiseAllRecord<'a> { pub(crate) fn on_promise_fufilled( &mut self, agent: &mut Agent, @@ -51,30 +51,30 @@ impl<'a> PromiseAllRecordHeapData<'a> { } } -impl PromiseAllRecord<'_> { +impl PromiseAll<'_> { pub(crate) const fn get_index(self) -> usize { self.0.into_index() } } -impl Index> for Agent { - type Output = PromiseAllRecordHeapData<'static>; +impl Index> for Agent { + type Output = PromiseAllRecord<'static>; - fn index(&self, index: PromiseAllRecord) -> &Self::Output { + fn index(&self, index: PromiseAll) -> &Self::Output { &self.heap.promise_all_records[index] } } -impl IndexMut> for Agent { - fn index_mut(&mut self, index: PromiseAllRecord) -> &mut Self::Output { +impl IndexMut> for Agent { + fn index_mut(&mut self, index: PromiseAll) -> &mut Self::Output { &mut self.heap.promise_all_records[index] } } -impl Index> for Vec>> { - type Output = PromiseAllRecordHeapData<'static>; +impl Index> for Vec>> { + type Output = PromiseAllRecord<'static>; - fn index(&self, index: PromiseAllRecord) -> &Self::Output { + fn index(&self, index: PromiseAll) -> &Self::Output { self.get(index.get_index()) .expect("PromiseAllRecord out of bounds") .as_ref() @@ -82,8 +82,8 @@ impl Index> for Vec> for Vec>> { - fn index_mut(&mut self, index: PromiseAllRecord) -> &mut Self::Output { +impl IndexMut> for Vec>> { + fn index_mut(&mut self, index: PromiseAll) -> &mut Self::Output { self.get_mut(index.get_index()) .expect("PromiseAllRecord out of bounds") .as_mut() @@ -91,7 +91,7 @@ impl IndexMut> for Vec { +impl HeapMarkAndSweep for PromiseAllRecord<'static> { fn mark_values(&self, queues: &mut WorkQueues) { self.result_array.mark_values(queues); } @@ -101,8 +101,8 @@ impl HeapMarkAndSweep for PromiseAllRecordHeapData<'static> { } } -unsafe impl Bindable for PromiseAllRecordHeapData<'_> { - type Of<'a> = PromiseAllRecordHeapData<'a>; +unsafe impl Bindable for PromiseAllRecord<'_> { + type Of<'a> = PromiseAllRecord<'a>; #[inline(always)] fn unbind(self) -> Self::Of<'static> { @@ -115,7 +115,7 @@ unsafe impl Bindable for PromiseAllRecordHeapData<'_> { } } -impl HeapMarkAndSweep for PromiseAllRecord<'static> { +impl HeapMarkAndSweep for PromiseAll<'static> { fn mark_values(&self, queues: &mut WorkQueues) { queues.promise_all_records.push(*self); } @@ -125,8 +125,8 @@ impl HeapMarkAndSweep for PromiseAllRecord<'static> { } } -unsafe impl Bindable for PromiseAllRecord<'_> { - type Of<'a> = PromiseAllRecord<'a>; +unsafe impl Bindable for PromiseAll<'_> { + type Of<'a> = PromiseAll<'a>; #[inline(always)] fn unbind(self) -> Self::Of<'static> { @@ -139,10 +139,10 @@ unsafe impl Bindable for PromiseAllRecord<'_> { } } -impl<'a> CreateHeapData, PromiseAllRecord<'a>> for Heap { - fn create(&mut self, data: PromiseAllRecordHeapData<'a>) -> PromiseAllRecord<'a> { +impl<'a> CreateHeapData, PromiseAll<'a>> for Heap { + fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAll<'a> { self.promise_all_records.push(Some(data.unbind())); - self.alloc_counter += core::mem::size_of::>>(); - PromiseAllRecord(BaseIndex::last(&self.promise_all_records)) + self.alloc_counter += core::mem::size_of::>>(); + PromiseAll(BaseIndex::last(&self.promise_all_records)) } } 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 9006c2a7a..4e9aaa937 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::PromiseAllRecord, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAll, }, execution::Agent, scripts_and_modules::module::module_semantics::{ @@ -71,7 +71,7 @@ pub(crate) enum PromiseReactionHandler<'a> { }, PromiseAll { index: u32, - promise_all: PromiseAllRecord<'a>, + promise_all: PromiseAll<'a>, }, 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 b66dd4ea0..244a782dd 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 @@ -2,7 +2,7 @@ // 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::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData; +use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord; use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; use crate::ecmascript::builtins::Array; @@ -328,7 +328,7 @@ impl PromiseConstructor { let undefined_values = (vec![Value::Undefined.bind(gc.nogc()); 2]).bind(gc.nogc()); let result_array = Array::from_slice(agent, &undefined_values.unbind(), gc.nogc()).bind(gc.nogc()); - let promise_all_record = agent.heap.create(PromiseAllRecordHeapData { + let promise_all_record = agent.heap.create(PromiseAllRecord { remaining_unresolved_promise_count: 2, result_array: result_array.unbind(), promise: result_promise.unbind(), diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 1f7d29e67..8a7ba2a49 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -81,7 +81,6 @@ use crate::{ primitive_objects::PrimitiveObjectHeapData, promise::data::PromiseHeapData, promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, promise_objects::promise_abstract_operations::promise_finally_functions::PromiseFinallyFunctionHeapData, proxy::data::ProxyHeapData, text_processing::{ @@ -175,7 +174,7 @@ pub struct Heap { pub promise_resolving_functions: Vec>>, pub promise_finally_functions: Vec>, pub promises: Vec>>, - pub promise_all_records: Vec>>, + pub promise_all_records: Vec>>, pub proxys: Vec>>, pub realms: Vec>>, #[cfg(feature = "regexp")] diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index 4466c2e8b..3a66436a9 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -50,8 +50,7 @@ use crate::ecmascript::{ ordinary::{caches::PropertyLookupCache, shape::ObjectShape}, primitive_objects::PrimitiveObject, promise::Promise, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord, - promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecordHeapData, + promise_objects::promise_abstract_operations::promise_all_record::PromiseAll, promise_objects::promise_abstract_operations::promise_finally_functions::BuiltinPromiseFinallyFunction, proxy::Proxy, text_processing::{ @@ -219,7 +218,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_all_records: Vec>, pub proxys: Vec>, pub realms: Vec>, #[cfg(feature = "regexp")] From 4adfb073b1fb8cc622c7149872b31b65b1add3ed Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 14 Aug 2025 09:15:47 -0300 Subject: [PATCH 20/46] Fix mark and sweep --- .../promise_all_record.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 7f4719187..151d90cd2 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 @@ -93,11 +93,23 @@ impl IndexMut> for Vec>> { impl HeapMarkAndSweep for PromiseAllRecord<'static> { fn mark_values(&self, queues: &mut WorkQueues) { - self.result_array.mark_values(queues); + let Self { + remaining_unresolved_promise_count: _, + result_array, + promise, + } = self; + result_array.mark_values(queues); + promise.mark_values(queues); } fn sweep_values(&mut self, compactions: &CompactionLists) { - self.result_array.sweep_values(compactions); + let Self { + remaining_unresolved_promise_count: _, + result_array, + promise, + } = self; + result_array.sweep_values(compactions); + promise.sweep_values(compactions); } } From 8d7c640e6d960f201da01f846745bd2d0e71aa87 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 07:44:14 -0300 Subject: [PATCH 21/46] Replace index for get --- .../promise_all_record.rs | 42 ++++--------------- nova_vm/src/heap.rs | 2 +- 2 files changed, 10 insertions(+), 34 deletions(-) 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 151d90cd2..4cb5f5142 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 @@ -2,8 +2,6 @@ // 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 core::ops::{Index, IndexMut}; - use crate::{ ecmascript::{ builtins::{ @@ -57,37 +55,15 @@ impl PromiseAll<'_> { } } -impl Index> for Agent { - type Output = PromiseAllRecord<'static>; - - fn index(&self, index: PromiseAll) -> &Self::Output { - &self.heap.promise_all_records[index] - } -} - -impl IndexMut> for Agent { - fn index_mut(&mut self, index: PromiseAll) -> &mut Self::Output { - &mut self.heap.promise_all_records[index] - } -} - -impl Index> for Vec>> { - type Output = PromiseAllRecord<'static>; - - fn index(&self, index: PromiseAll) -> &Self::Output { - self.get(index.get_index()) - .expect("PromiseAllRecord out of bounds") - .as_ref() - .expect("PromiseAllRecord slot empty") +impl AsRef<[PromiseAllRecord<'static>]> for Agent { + fn as_ref(&self) -> &[PromiseAllRecord<'static>] { + &self.heap.promise_all_records } } -impl IndexMut> for Vec>> { - fn index_mut(&mut self, index: PromiseAll) -> &mut Self::Output { - self.get_mut(index.get_index()) - .expect("PromiseAllRecord out of bounds") - .as_mut() - .expect("PromiseAllRecord slot empty") +impl AsMut<[PromiseAllRecord<'static>]> for Agent { + fn as_mut(&mut self) -> &mut [PromiseAllRecord<'static>] { + &mut self.heap.promise_all_records } } @@ -153,8 +129,8 @@ unsafe impl Bindable for PromiseAll<'_> { impl<'a> CreateHeapData, PromiseAll<'a>> for Heap { fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAll<'a> { - self.promise_all_records.push(Some(data.unbind())); - self.alloc_counter += core::mem::size_of::>>(); - PromiseAll(BaseIndex::last(&self.promise_all_records)) + 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/heap.rs b/nova_vm/src/heap.rs index 8a7ba2a49..dafc87815 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -174,7 +174,7 @@ pub struct Heap { pub promise_resolving_functions: Vec>>, pub promise_finally_functions: Vec>, pub promises: Vec>>, - pub promise_all_records: Vec>>, + pub promise_all_records: Vec>, pub proxys: Vec>>, pub realms: Vec>>, #[cfg(feature = "regexp")] From d8ecceaa858dd2eca1eb96cc21bd3f9d24b0439c Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 08:57:08 -0300 Subject: [PATCH 22/46] Implemented on promise fulfilled --- .../promise_all_record.rs | 47 ++++++++++++++----- .../promise_jobs.rs | 28 ++--------- 2 files changed, 40 insertions(+), 35 deletions(-) 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 4cb5f5142..275dc32a9 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 @@ -11,12 +11,15 @@ use crate::{ execution::Agent, types::{IntoValue, Value}, }, - engine::context::{Bindable, GcScope, NoGcScope}, + engine::context::{Bindable, GcScope, NoGcScope, bindable_handle}, heap::{ CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, }, }; +pub(crate) type BuiltinPromiseAllRecordIndex<'a> = BaseIndex<'a, PromiseAllRecord<'static>>; +bindable_handle!(BuiltinPromiseAllRecordIndex); + #[derive(Debug, Clone, Copy)] pub struct PromiseAllRecord<'a> { pub remaining_unresolved_promise_count: u32, @@ -26,9 +29,9 @@ pub struct PromiseAllRecord<'a> { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct PromiseAll<'a>(pub(crate) BaseIndex<'a, PromiseAllRecord<'a>>); +pub struct PromiseAll<'a>(pub(crate) BuiltinPromiseAllRecordIndex<'a>); -impl<'a> PromiseAllRecord<'a> { +impl<'a> PromiseAll<'a> { pub(crate) fn on_promise_fufilled( &mut self, agent: &mut Agent, @@ -36,23 +39,43 @@ impl<'a> PromiseAllRecord<'a> { value: Value<'a>, mut gc: GcScope<'a, '_>, ) { - value.bind(gc.nogc()); - let elements = self.result_array.as_mut_slice(agent); + let promise_all_index = self.bind(gc.nogc()); + let value = value.bind(gc.nogc()); + let data = promise_all_index.get_mut(agent); // splitting heap borrow + let result_array = data.result_array.bind(gc.nogc()); // note: this copies the handle from the heap to stack; on stack we must always bind to make sure we don't use the handle after GC. + let elements = result_array.as_mut_slice(...array_heap...); // we want to split the heap borrow so we can get the elements slice as mutable at the same time as we get data as mutable. ArrayHeap is the thing you want to pass here here, IIRC. elements[index as usize] = Some(value.unbind()); - self.remaining_unresolved_promise_count -= 1; - if self.remaining_unresolved_promise_count == 0 { - eprintln!("Promise fulfilled: {:#?}", elements); - let capability = PromiseCapability::from_promise(self.promise.unbind(), true); - capability.resolve(agent, self.result_array.into_value().unbind(), gc); + data.remaining_unresolved_promise_count -= 1; + if data.remaining_unresolved_promise_count == 0 { + let capability = PromiseCapability::from_promise(data.promise, false); + capability.resolve(agent, result_array.into_value().unbind(), gc); } } -} -impl PromiseAll<'_> { pub(crate) const fn get_index(self) -> usize { self.0.into_index() } + + fn get(self, agent: &Agent) -> &PromiseAllRecord<'_> { + agent + .heap + .promise_all_records + .get(self.get_index()) + .expect("PromiseAllRecord not found") + } + + 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 { 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 1fa48d203..239c0533b 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 @@ -288,37 +288,19 @@ impl PromiseReactionJob { } } } - PromiseReactionHandler::PromiseAll { promise_all, index } => { + PromiseReactionHandler::PromiseAll { + mut promise_all, + index, + } => { let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); match agent[reaction].reaction_type { PromiseReactionType::Fulfill => { - // Take out the record to drop the vec borrow before we use `agent`/`gc` - let rec = { - let slot = agent - .heap - .promise_all_records - .get_mut(promise_all.get_index()) - .expect("PromiseAllRecord out of bounds"); - slot.take().expect("PromiseAllRecord slot empty") - }; - - // Bind to current scope and mutate - let mut rec_bound = rec.unbind().bind(gc.nogc()); - rec_bound.on_promise_fufilled( + promise_all.on_promise_fufilled( agent, index, argument.clone().unbind(), gc.reborrow(), ); - - // Write back with 'static lifetime - agent - .heap - .promise_all_records - .get_mut(promise_all.get_index()) - .unwrap() - .replace(rec_bound.unbind()); - (Ok(argument), capability) } PromiseReactionType::Reject => (Err(JsError::new(argument)), capability), From fe562a193c19f989ce207c172a1f264b206ad28f Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 09:07:30 -0300 Subject: [PATCH 23/46] Fix on promise fulfilled --- .../promise_all_record.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) 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 275dc32a9..fa3d26b9c 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 @@ -37,15 +37,20 @@ impl<'a> PromiseAll<'a> { agent: &mut Agent, index: u32, value: Value<'a>, - mut gc: GcScope<'a, '_>, + gc: GcScope<'a, '_>, ) { let promise_all_index = self.bind(gc.nogc()); let value = value.bind(gc.nogc()); - let data = promise_all_index.get_mut(agent); // splitting heap borrow - let result_array = data.result_array.bind(gc.nogc()); // note: this copies the handle from the heap to stack; on stack we must always bind to make sure we don't use the handle after GC. - let elements = result_array.as_mut_slice(...array_heap...); // we want to split the heap borrow so we can get the elements slice as mutable at the same time as we get data as mutable. ArrayHeap is the thing you want to pass here here, IIRC. + + let result_array = { + let data = promise_all_index.get_mut(agent); + data.result_array.bind(gc.nogc()) + }; + + let elements = result_array.as_mut_slice(agent); elements[index as usize] = Some(value.unbind()); + let data = promise_all_index.get_mut(agent); data.remaining_unresolved_promise_count -= 1; if data.remaining_unresolved_promise_count == 0 { let capability = PromiseCapability::from_promise(data.promise, false); From 0dfa8935e97bdc42bcb97e558350c73365ffe5e9 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 09:18:21 -0300 Subject: [PATCH 24/46] Fix promise jobs --- .../promise_jobs.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) 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 239c0533b..2d39f91fe 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 @@ -292,18 +292,17 @@ impl PromiseReactionJob { mut promise_all, index, } => { - let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc()); - match agent[reaction].reaction_type { + let reaction_type = agent[reaction].reaction_type; + let capability = agent[reaction].capability.clone().unwrap(); + match reaction_type { PromiseReactionType::Fulfill => { - promise_all.on_promise_fufilled( - agent, - index, - argument.clone().unbind(), - gc.reborrow(), - ); - (Ok(argument), capability) + let arg_unbound = argument.unbind(); + promise_all.on_promise_fufilled(agent, index, arg_unbound, gc.reborrow()); + (Ok(arg_unbound.bind(gc.nogc())), capability.bind(gc.nogc())) + } + PromiseReactionType::Reject => { + (Err(JsError::new(argument)), capability.bind(gc.nogc())) } - PromiseReactionType::Reject => (Err(JsError::new(argument)), capability), } } }; From 79a2fa0afaadf5499f222456f77da6b3f4fb14bc Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 09:25:31 -0300 Subject: [PATCH 25/46] Fix GC --- nova_vm/src/heap/heap_gc.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 1391f291e..f85c9eb25 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -57,7 +57,10 @@ use crate::{ ordinary::{caches::PropertyLookupCache, shape::ObjectShape}, primitive_objects::PrimitiveObject, promise::Promise, - promise_objects::promise_abstract_operations::promise_finally_functions::BuiltinPromiseFinallyFunction, + promise_objects::promise_abstract_operations::{ + promise_all_record::PromiseAll, + promise_finally_functions::BuiltinPromiseFinallyFunction, + }, proxy::Proxy, text_processing::{ regexp_objects::regexp_string_iterator_objects::RegExpStringIterator, @@ -661,6 +664,20 @@ 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 index = idx.get_index(); + if let Some(marked) = bits.promise_all_records.get_mut(index) { + if *marked { + // Already marked, ignore + return; + } + *marked = true; + promise_all_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(); From fc72d59b2a6171ed057d230c60015052418649ef Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 09:30:03 -0300 Subject: [PATCH 26/46] Combine imports --- .../promise_objects/promise_constructor.rs | 48 ++++--------------- 1 file changed, 9 insertions(+), 39 deletions(-) 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 244a782dd..3148611f8 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 @@ -2,10 +2,6 @@ // 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::promise_objects::promise_abstract_operations::promise_all_record::PromiseAllRecord; -use crate::ecmascript::builtins::promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler; -use crate::ecmascript::builtins::promise_objects::promise_prototype::inner_promise_then; -use crate::ecmascript::builtins::Array; use crate::{ ecmascript::{ abstract_operations::{ @@ -14,12 +10,19 @@ use crate::{ }, builders::builtin_function_builder::BuiltinFunctionBuilder, builtins::{ - ArgumentsList, Behaviour, Builtin, BuiltinGetter, BuiltinIntrinsicConstructor, + ArgumentsList, Array, Behaviour, Builtin, BuiltinGetter, BuiltinIntrinsicConstructor, ordinary::ordinary_create_from_constructor, promise::{ Promise, data::{PromiseHeapData, PromiseState}, }, + promise_objects::{ + promise_abstract_operations::{ + promise_all_record::PromiseAllRecord, + promise_reaction_records::PromiseReactionHandler, + }, + promise_prototype::inner_promise_then, + }, }, execution::{ Agent, JsResult, ProtoIntrinsics, Realm, @@ -218,7 +221,7 @@ impl PromiseConstructor { agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - mut gc: GcScope<'gc, '_>, + gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { // 1. Let C be the this value. if this_value @@ -292,39 +295,6 @@ impl PromiseConstructor { .unbind() .bind(gc.nogc()); - // let result_callback_closure: for<'a, 'b, 'c, 'd, 'e, '_gc> fn( - // &'a mut Agent, - // Value<'b>, - // ArgumentsList<'c, 'd>, - // GcScope<'_gc, 'e>, - // ) -> Result< - // Value<'_gc>, - // JsError<'_gc>, - // > = |_agent, _this_value, arguments, _gc| { - // let result_value = arguments.get(0); - // eprintln!("Promise fulfilled with result: {:?}", result_value); - // Ok(result_value.unbind()) - // }; - - // let result_callback = create_builtin_function( - // agent, - // Behaviour::Regular(result_callback_closure), - // BuiltinFunctionArgs::new(0, "Promise.all callback"), - // gc.nogc(), - // ); - - // let fulfill_handler = PromiseReactionHandler::JobCallback(result_callback.into()); - // let reject_handler = PromiseReactionHandler::Empty; - - // inner_promise_then( - // agent, - // promise_to_await, - // fulfill_handler, - // reject_handler, - // Some(result_capability), - // gc.nogc(), - // ); - let undefined_values = (vec![Value::Undefined.bind(gc.nogc()); 2]).bind(gc.nogc()); let result_array = Array::from_slice(agent, &undefined_values.unbind(), gc.nogc()).bind(gc.nogc()); From 49f9aedeb2149cf344ec1176ba4627f25471bb2b Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 19 Aug 2025 09:31:22 -0300 Subject: [PATCH 27/46] Fix heap bits --- nova_vm/src/heap/heap_bits.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index 3a66436a9..302c84cfb 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -669,6 +669,7 @@ impl WorkQueues { && promise_reaction_records.is_empty() && promise_resolving_functions.is_empty() && promise_finally_functions.is_empty() + && promise_all_records.is_empty() && promises.is_empty() && proxys.is_empty() && realms.is_empty() From b5c11894ff6a3e8df34302a23f79bf436235454e Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 20:58:00 -0300 Subject: [PATCH 28/46] Fix nitpick --- .../promise_all_record.rs | 19 ++++++++----------- .../promise_jobs.rs | 5 +---- 2 files changed, 9 insertions(+), 15 deletions(-) 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 fa3d26b9c..294b70487 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 @@ -11,46 +11,43 @@ use crate::{ execution::Agent, types::{IntoValue, Value}, }, - engine::context::{Bindable, GcScope, NoGcScope, bindable_handle}, + engine::context::{Bindable, GcScope, NoGcScope}, heap::{ CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, }, }; -pub(crate) type BuiltinPromiseAllRecordIndex<'a> = BaseIndex<'a, PromiseAllRecord<'static>>; -bindable_handle!(BuiltinPromiseAllRecordIndex); - #[derive(Debug, Clone, Copy)] pub struct PromiseAllRecord<'a> { - pub remaining_unresolved_promise_count: u32, + pub(crate) remaining_unresolved_promise_count: u32, pub result_array: Array<'a>, pub promise: Promise<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] -pub struct PromiseAll<'a>(pub(crate) BuiltinPromiseAllRecordIndex<'a>); +pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>); impl<'a> PromiseAll<'a> { pub(crate) fn on_promise_fufilled( - &mut self, + self, agent: &mut Agent, index: u32, value: Value<'a>, gc: GcScope<'a, '_>, ) { - let promise_all_index = self.bind(gc.nogc()); + let promise_all = self.bind(gc.nogc()); let value = value.bind(gc.nogc()); let result_array = { - let data = promise_all_index.get_mut(agent); + let data = promise_all.get_mut(agent); data.result_array.bind(gc.nogc()) }; let elements = result_array.as_mut_slice(agent); elements[index as usize] = Some(value.unbind()); - let data = promise_all_index.get_mut(agent); + let data = promise_all.get_mut(agent); data.remaining_unresolved_promise_count -= 1; if data.remaining_unresolved_promise_count == 0 { let capability = PromiseCapability::from_promise(data.promise, false); @@ -62,7 +59,7 @@ impl<'a> PromiseAll<'a> { self.0.into_index() } - fn get(self, agent: &Agent) -> &PromiseAllRecord<'_> { + fn get(self, agent: &Agent) -> &PromiseAllRecord<'a> { agent .heap .promise_all_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 2d39f91fe..2fb8f7cc3 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 @@ -288,10 +288,7 @@ impl PromiseReactionJob { } } } - PromiseReactionHandler::PromiseAll { - mut promise_all, - index, - } => { + PromiseReactionHandler::PromiseAll { promise_all, index } => { let reaction_type = agent[reaction].reaction_type; let capability = agent[reaction].capability.clone().unwrap(); match reaction_type { From 3282bd7df170c1484fed2851ddc0ccb34623b4a3 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 21:02:38 -0300 Subject: [PATCH 29/46] Added result_array helper --- .../promise_abstract_operations/promise_all_record.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 294b70487..3e2ab3dad 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 @@ -39,10 +39,7 @@ impl<'a> PromiseAll<'a> { let promise_all = self.bind(gc.nogc()); let value = value.bind(gc.nogc()); - let result_array = { - let data = promise_all.get_mut(agent); - data.result_array.bind(gc.nogc()) - }; + let result_array = self.get_result_array(agent, gc.nogc()); let elements = result_array.as_mut_slice(agent); elements[index as usize] = Some(value.unbind()); @@ -55,6 +52,11 @@ impl<'a> PromiseAll<'a> { } } + 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() } From 1a2fc7aca82ff793b6d96367948d14abcbc96db4 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 21:14:13 -0300 Subject: [PATCH 30/46] Use bindable_handle --- .../promise_all_record.rs | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) 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 3e2ab3dad..b57962d06 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 @@ -11,7 +11,7 @@ use crate::{ execution::Agent, types::{IntoValue, Value}, }, - engine::context::{Bindable, GcScope, NoGcScope}, + engine::context::{Bindable, GcScope, NoGcScope, bindable_handle}, heap::{ CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex, }, @@ -116,20 +116,6 @@ impl HeapMarkAndSweep for PromiseAllRecord<'static> { } } -unsafe impl Bindable for PromiseAllRecord<'_> { - type Of<'a> = PromiseAllRecord<'a>; - - #[inline(always)] - fn unbind(self) -> Self::Of<'static> { - unsafe { core::mem::transmute::>(self) } - } - - #[inline(always)] - fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { - unsafe { core::mem::transmute::>(self) } - } -} - impl HeapMarkAndSweep for PromiseAll<'static> { fn mark_values(&self, queues: &mut WorkQueues) { queues.promise_all_records.push(*self); @@ -140,19 +126,8 @@ impl HeapMarkAndSweep for PromiseAll<'static> { } } -unsafe impl Bindable for PromiseAll<'_> { - type Of<'a> = PromiseAll<'a>; - - #[inline(always)] - fn unbind(self) -> Self::Of<'static> { - unsafe { core::mem::transmute::>(self) } - } - - #[inline(always)] - fn bind<'a>(self, _gc: NoGcScope<'a, '_>) -> Self::Of<'a> { - unsafe { core::mem::transmute::>(self) } - } -} +bindable_handle!(PromiseAllRecord); +bindable_handle!(PromiseAll); impl<'a> CreateHeapData, PromiseAll<'a>> for Heap { fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAll<'a> { From 785c68c8608090968ffa4e7e29655629a6eebd13 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 21:52:23 -0300 Subject: [PATCH 31/46] Remove debug code from PromiseConstructor --- .../promise_objects/promise_constructor.rs | 101 +++++------------- 1 file changed, 29 insertions(+), 72 deletions(-) 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 3148611f8..a3ecab382 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 @@ -238,8 +238,7 @@ impl PromiseConstructor { } let first_arg = arguments.get(0).bind(gc.nogc()); - - let Ok(promise_array) = Array::try_from(first_arg.unbind()) else { + let Ok(promise_array) = Array::try_from(first_arg.unbind()).bind(gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Expected an array of promises", @@ -247,86 +246,44 @@ impl PromiseConstructor { )); }; - let array_slice = promise_array.as_slice(agent); - if array_slice.is_empty() { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Array is empty", - gc.into_nogc(), - )); - } - - let Some(first_element) = array_slice[0] else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "First element is None", - gc.into_nogc(), - )); - }; - - let Value::Promise(promise_to_await) = first_element.unbind() else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "First element is not a Promise", - gc.into_nogc(), - )); - }; - - let Some(second_element) = array_slice[1] else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Second element is None", - gc.into_nogc(), - )); - }; - let Value::Promise(second_promise) = second_element.unbind() else { - return Err(agent.throw_exception_with_static_message( - ExceptionType::TypeError, - "Second element is not a Promise", - gc.into_nogc(), - )); - }; - + let array_len = promise_array.len(agent); let result_capability = PromiseCapability::new(agent, gc.nogc()); - let second_capability = PromiseCapability::new(agent, gc.nogc()); + let result_promise = result_capability.promise().bind(gc.nogc()); - let result_promise = PromiseCapability::new(agent, gc.nogc()) - .promise() - .unbind() - .bind(gc.nogc()); - - let undefined_values = (vec![Value::Undefined.bind(gc.nogc()); 2]).bind(gc.nogc()); + let undefined_values = + (vec![Value::Undefined.bind(gc.nogc()); array_len as usize]).bind(gc.nogc()); let result_array = Array::from_slice(agent, &undefined_values.unbind(), gc.nogc()).bind(gc.nogc()); + let promise_all_record = agent.heap.create(PromiseAllRecord { - remaining_unresolved_promise_count: 2, + remaining_unresolved_promise_count: array_len, result_array: result_array.unbind(), promise: result_promise.unbind(), }); - inner_promise_then( - agent, - promise_to_await.unbind(), - PromiseReactionHandler::PromiseAll { - index: 0, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(result_capability.unbind()), - gc.nogc(), - ); + for index in 0..array_len { + let element = { + let promise_array_slice = promise_array.as_slice(agent); + promise_array_slice[index as usize] + }; - inner_promise_then( - agent, - second_promise.unbind(), - PromiseReactionHandler::PromiseAll { - index: 1, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(second_capability.unbind()), - gc.nogc(), - ); + let capability = PromiseCapability::new(agent, gc.nogc()); + let Some(Value::Promise(promise)) = element.unbind() else { + continue; + }; + + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index: index as u32, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); + } Ok(result_promise.unbind().into_value()) } From 2c4c9e5f05450e9c68d372d35452b7d627e92484 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 21:56:35 -0300 Subject: [PATCH 32/46] Minor improvements --- .../promise_objects/promise_constructor.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) 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 a3ecab382..80c7eb667 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 @@ -262,13 +262,11 @@ impl PromiseConstructor { }); for index in 0..array_len { - let element = { - let promise_array_slice = promise_array.as_slice(agent); - promise_array_slice[index as usize] - }; + let storage = promise_array.get_storage(agent); + let element = storage.values[index as usize].bind(gc.nogc()); - let capability = PromiseCapability::new(agent, gc.nogc()); - let Some(Value::Promise(promise)) = element.unbind() else { + let capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); + let Some(Value::Promise(promise)) = element else { continue; }; From 0976b821131f302d2b14fa3ad8982c869cbdd47f Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 21:58:12 -0300 Subject: [PATCH 33/46] Fix typo --- .../promise_abstract_operations/promise_all_record.rs | 2 +- .../promise_objects/promise_abstract_operations/promise_jobs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 b57962d06..133f15074 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 @@ -29,7 +29,7 @@ pub struct PromiseAllRecord<'a> { pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>); impl<'a> PromiseAll<'a> { - pub(crate) fn on_promise_fufilled( + pub(crate) fn on_promise_fulfilled( self, agent: &mut Agent, index: u32, 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 2fb8f7cc3..169be38c7 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 @@ -294,7 +294,7 @@ impl PromiseReactionJob { match reaction_type { PromiseReactionType::Fulfill => { let arg_unbound = argument.unbind(); - promise_all.on_promise_fufilled(agent, index, arg_unbound, gc.reborrow()); + promise_all.on_promise_fulfilled(agent, index, arg_unbound, gc.reborrow()); (Ok(arg_unbound.bind(gc.nogc())), capability.bind(gc.nogc())) } PromiseReactionType::Reject => { From c26cefe23ff3570759d4f4f3e94092b075369127 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 21 Aug 2025 22:01:07 -0300 Subject: [PATCH 34/46] Fix warn --- .../promise_objects/promise_constructor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 80c7eb667..ced4218cd 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 @@ -274,7 +274,7 @@ impl PromiseConstructor { agent, promise.unbind(), PromiseReactionHandler::PromiseAll { - index: index as u32, + index, promise_all: promise_all_record, }, PromiseReactionHandler::Empty, From 977ee2cf7d75e85348b7501c7c52aeae3cf38764 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 22 Aug 2025 07:35:46 -0300 Subject: [PATCH 35/46] Updated If --- .../promise_objects/promise_constructor.rs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) 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 ced4218cd..58b34904b 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 @@ -265,22 +265,20 @@ impl PromiseConstructor { let storage = promise_array.get_storage(agent); let element = storage.values[index as usize].bind(gc.nogc()); - let capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); - let Some(Value::Promise(promise)) = element else { - continue; + if let Some(Value::Promise(promise)) = element { + let capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); }; - - inner_promise_then( - agent, - promise.unbind(), - PromiseReactionHandler::PromiseAll { - index, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(capability.unbind()), - gc.nogc(), - ); } Ok(result_promise.unbind().into_value()) From 302c45999becc10c6056527fe886026483174b26 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 25 Aug 2025 08:01:00 -0300 Subject: [PATCH 36/46] Updated metrics --- tests/expectations.json | 207 +++++----------------------------------- tests/metrics.json | 10 +- 2 files changed, 31 insertions(+), 186 deletions(-) diff --git a/tests/expectations.json b/tests/expectations.json index 679a306b4..072479c03 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -238,17 +238,9 @@ "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm-prototype.js": "FAIL", "built-ins/AsyncGeneratorFunction/proto-from-ctor-realm.js": "FAIL", "built-ins/AsyncGeneratorFunction/prototype/constructor.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/next/request-queue-order.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/next/request-queue-promise-resolve-order.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/next/this-val-not-async-generator.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/next/this-val-not-object.js": "FAIL", "built-ins/AsyncGeneratorPrototype/return/return-state-completed-broken-promise.js": "FAIL", "built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js": "FAIL", "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-broken-promise-try-catch.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/return/this-val-not-async-generator.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/return/this-val-not-object.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/throw/this-val-not-async-generator.js": "FAIL", - "built-ins/AsyncGeneratorPrototype/throw/this-val-not-object.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/invokes-return.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/is-function.js": "FAIL", "built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/length.js": "FAIL", @@ -1247,7 +1239,7 @@ "built-ins/Number/prototype/toExponential/return-values.js": "FAIL", "built-ins/Number/prototype/toExponential/tointeger-fractiondigits.js": "FAIL", "built-ins/Number/prototype/toExponential/undefined-fractiondigits.js": "FAIL", - "built-ins/Object/defineProperty/15.2.3.6-4-116.js": "FAIL", + "built-ins/Object/defineProperty/15.2.3.6-4-116.js": "TIMEOUT", "built-ins/Object/defineProperty/15.2.3.6-4-292-1.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-293-2.js": "FAIL", "built-ins/Object/defineProperty/15.2.3.6-4-293-3.js": "FAIL", @@ -1332,21 +1324,17 @@ "built-ins/Object/seal/seal-finalizationregistry.js": "FAIL", "built-ins/Object/seal/seal-sharedarraybuffer.js": "FAIL", "built-ins/Object/seal/seal-weakmap.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A2.1_T1.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A2.2_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A2.3_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A2.3_T2.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A2.3_T3.js": "FAIL", + "built-ins/Promise/all/S25.4.4.1_A2.3_T1.js": "UNRESOLVED", + "built-ins/Promise/all/S25.4.4.1_A2.3_T2.js": "UNRESOLVED", + "built-ins/Promise/all/S25.4.4.1_A2.3_T3.js": "UNRESOLVED", "built-ins/Promise/all/S25.4.4.1_A3.1_T1.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A3.1_T2.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A3.1_T3.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A4.1_T1.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A5.1_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A7.1_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A7.2_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A8.1_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A8.2_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A8.2_T2.js": "FAIL", + "built-ins/Promise/all/S25.4.4.1_A8.2_T1.js": "UNRESOLVED", + "built-ins/Promise/all/S25.4.4.1_A8.2_T2.js": "UNRESOLVED", "built-ins/Promise/all/call-resolve-element-after-return.js": "FAIL", "built-ins/Promise/all/call-resolve-element-items.js": "FAIL", "built-ins/Promise/all/call-resolve-element.js": "FAIL", @@ -1358,22 +1346,22 @@ "built-ins/Promise/all/ctx-ctor.js": "FAIL", "built-ins/Promise/all/ctx-non-ctor.js": "FAIL", "built-ins/Promise/all/ctx-non-object.js": "FAIL", - "built-ins/Promise/all/does-not-invoke-array-setters.js": "FAIL", + "built-ins/Promise/all/does-not-invoke-array-setters.js": "UNRESOLVED", "built-ins/Promise/all/invoke-resolve-error-close.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-error-reject.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-get-error-reject.js": "FAIL", + "built-ins/Promise/all/invoke-resolve-error-reject.js": "UNRESOLVED", + "built-ins/Promise/all/invoke-resolve-get-error-reject.js": "UNRESOLVED", "built-ins/Promise/all/invoke-resolve-get-error.js": "FAIL", "built-ins/Promise/all/invoke-resolve-get-once-multiple-calls.js": "FAIL", "built-ins/Promise/all/invoke-resolve-get-once-no-calls.js": "FAIL", "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-custom.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-promise.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-on-values-every-iteration-of-promise.js": "FAIL", + "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-promise.js": "UNRESOLVED", + "built-ins/Promise/all/invoke-resolve-on-values-every-iteration-of-promise.js": "UNRESOLVED", "built-ins/Promise/all/invoke-resolve-return.js": "FAIL", "built-ins/Promise/all/invoke-resolve.js": "FAIL", "built-ins/Promise/all/invoke-then-error-close.js": "FAIL", - "built-ins/Promise/all/invoke-then-error-reject.js": "FAIL", + "built-ins/Promise/all/invoke-then-error-reject.js": "UNRESOLVED", "built-ins/Promise/all/invoke-then-get-error-close.js": "FAIL", - "built-ins/Promise/all/invoke-then-get-error-reject.js": "FAIL", + "built-ins/Promise/all/invoke-then-get-error-reject.js": "UNRESOLVED", "built-ins/Promise/all/invoke-then.js": "FAIL", "built-ins/Promise/all/iter-arg-is-false-reject.js": "FAIL", "built-ins/Promise/all/iter-arg-is-null-reject.js": "FAIL", @@ -1401,10 +1389,10 @@ "built-ins/Promise/all/iter-step-err-no-close.js": "FAIL", "built-ins/Promise/all/iter-step-err-reject.js": "FAIL", "built-ins/Promise/all/new-resolve-function.js": "FAIL", - "built-ins/Promise/all/reject-deferred.js": "FAIL", - "built-ins/Promise/all/reject-ignored-deferred.js": "FAIL", - "built-ins/Promise/all/reject-ignored-immed.js": "FAIL", - "built-ins/Promise/all/reject-immed.js": "FAIL", + "built-ins/Promise/all/reject-deferred.js": "UNRESOLVED", + "built-ins/Promise/all/reject-ignored-deferred.js": "UNRESOLVED", + "built-ins/Promise/all/reject-ignored-immed.js": "UNRESOLVED", + "built-ins/Promise/all/reject-immed.js": "UNRESOLVED", "built-ins/Promise/all/resolve-before-loop-exit-from-same.js": "FAIL", "built-ins/Promise/all/resolve-before-loop-exit.js": "FAIL", "built-ins/Promise/all/resolve-element-function-extensible.js": "FAIL", @@ -1414,13 +1402,13 @@ "built-ins/Promise/all/resolve-element-function-property-order.js": "FAIL", "built-ins/Promise/all/resolve-element-function-prototype.js": "FAIL", "built-ins/Promise/all/resolve-from-same-thenable.js": "FAIL", - "built-ins/Promise/all/resolve-ignores-late-rejection-deferred.js": "FAIL", - "built-ins/Promise/all/resolve-ignores-late-rejection.js": "FAIL", + "built-ins/Promise/all/resolve-ignores-late-rejection-deferred.js": "UNRESOLVED", + "built-ins/Promise/all/resolve-ignores-late-rejection.js": "UNRESOLVED", "built-ins/Promise/all/resolve-non-callable.js": "FAIL", - "built-ins/Promise/all/resolve-non-thenable.js": "FAIL", - "built-ins/Promise/all/resolve-not-callable-reject-with-typeerror.js": "FAIL", - "built-ins/Promise/all/resolve-poisoned-then.js": "FAIL", - "built-ins/Promise/all/resolve-thenable.js": "FAIL", + "built-ins/Promise/all/resolve-non-thenable.js": "UNRESOLVED", + "built-ins/Promise/all/resolve-not-callable-reject-with-typeerror.js": "UNRESOLVED", + "built-ins/Promise/all/resolve-poisoned-then.js": "UNRESOLVED", + "built-ins/Promise/all/resolve-thenable.js": "UNRESOLVED", "built-ins/Promise/all/resolve-throws-iterator-return-is-not-callable.js": "FAIL", "built-ins/Promise/all/resolve-throws-iterator-return-null-or-undefined.js": "FAIL", "built-ins/Promise/all/same-reject-function.js": "FAIL", @@ -1592,7 +1580,7 @@ "built-ins/Promise/any/reject-element-function-property-order.js": "FAIL", "built-ins/Promise/any/reject-element-function-prototype.js": "FAIL", "built-ins/Promise/any/reject-from-same-thenable.js": "FAIL", - "built-ins/Promise/any/reject-ignored-deferred.js": "FAIL", + "built-ins/Promise/any/reject-ignored-deferred.js": "UNRESOLVED", "built-ins/Promise/any/reject-ignored-immed.js": "FAIL", "built-ins/Promise/any/reject-immed.js": "FAIL", "built-ins/Promise/any/resolve-before-loop-exit-from-same.js": "FAIL", @@ -6997,30 +6985,6 @@ "language/expressions/class/decorator/syntax/valid/decorator-call-expr-identifier-reference.js": "FAIL", "language/expressions/class/decorator/syntax/valid/decorator-member-expr-identifier-reference.js": "FAIL", "language/expressions/class/decorator/syntax/valid/decorator-parenthesized-expr-identifier-reference.js": "FAIL", - "language/expressions/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-async-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/after-same-line-static-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/expressions/class/elements/arrow-body-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/expressions/class/elements/arrow-body-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/expressions/class/elements/arrow-body-direct-eval-err-contains-arguments.js": "FAIL", @@ -7040,14 +7004,6 @@ "language/expressions/class/elements/evaluation-error/computed-name-valueof-err.js": "FAIL", "language/expressions/class/elements/fields-multiple-definitions-static-private-methods-proxy.js": "FAIL", "language/expressions/class/elements/fields-run-once-on-double-super.js": "FAIL", - "language/expressions/class/elements/multiple-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/multiple-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/multiple-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/multiple-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/multiple-stacked-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", "language/expressions/class/elements/nested-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/expressions/class/elements/nested-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/expressions/class/elements/nested-direct-eval-err-contains-arguments.js": "FAIL", @@ -7056,18 +7012,6 @@ "language/expressions/class/elements/nested-private-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/expressions/class/elements/nested-private-direct-eval-err-contains-arguments.js": "FAIL", "language/expressions/class/elements/nested-private-direct-eval-err-contains-newtarget.js": "FAIL", - "language/expressions/class/elements/new-no-sc-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-no-sc-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/new-no-sc-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-no-sc-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/new-sc-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-sc-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/new-sc-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-sc-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/new-sc-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/new-sc-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/expressions/class/elements/private-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/expressions/class/elements/private-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/expressions/class/elements/private-direct-eval-err-contains-arguments.js": "FAIL", @@ -7075,34 +7019,10 @@ "language/expressions/class/elements/private-fields-proxy-default-handler-throws.js": "FAIL", "language/expressions/class/elements/private-getter-is-not-a-own-property.js": "FAIL", "language/expressions/class/elements/private-setter-is-not-a-own-property.js": "FAIL", - "language/expressions/class/elements/regular-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/regular-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/regular-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/regular-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-async-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-async-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-async-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-async-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-async-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-async-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/same-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/same-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/expressions/class/elements/static-private-fields-proxy-default-handler-throws.js": "FAIL", "language/expressions/class/elements/static-private-methods-proxy-default-handler-throws.js": "FAIL", "language/expressions/class/elements/super-access-from-arrow-func-on-field.js": "CRASH", "language/expressions/class/elements/syntax/valid/grammar-field-accessor.js": "CRASH", - "language/expressions/class/elements/wrapped-in-sc-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/wrapped-in-sc-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/expressions/class/elements/wrapped-in-sc-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/expressions/class/elements/wrapped-in-sc-rs-static-async-method-privatename-identifier.js": "FAIL", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-realm-function-ctor.js": "FAIL", "language/expressions/class/private-getter-brand-check-multiple-evaluations-of-class-realm.js": "FAIL", "language/expressions/class/private-method-brand-check-multiple-evaluations-of-class-realm-function-ctor.js": "FAIL", @@ -7171,7 +7091,7 @@ "language/expressions/delete/11.4.1-4.a-6.js": "FAIL", "language/expressions/delete/super-property-uninitialized-this.js": "FAIL", "language/expressions/division/S11.5.2_A4_T10.js": "FAIL", - "language/expressions/does-not-equals/bigint-and-number-extremes.js": "CRASH", + "language/expressions/does-not-equals/bigint-and-number-extremes.js": "FAIL", "language/expressions/dynamic-import/assignment-expression/await-identifier.js": "FAIL", "language/expressions/dynamic-import/catch/nested-arrow-import-catch-eval-script-code-target.js": "FAIL", "language/expressions/dynamic-import/catch/nested-arrow-import-catch-import-source-source-text-module.js": "UNRESOLVED", @@ -7219,9 +7139,6 @@ "language/expressions/dynamic-import/catch/top-level-import-catch-eval-script-code-target.js": "FAIL", "language/expressions/dynamic-import/catch/top-level-import-catch-import-source-source-text-module.js": "UNRESOLVED", "language/expressions/dynamic-import/catch/top-level-import-catch-import-source-specifier-tostring.js": "UNRESOLVED", - "language/expressions/dynamic-import/eval-rqstd-once.js": "FAIL", - "language/expressions/dynamic-import/eval-self-once-module.js": "FAIL", - "language/expressions/dynamic-import/eval-self-once-script.js": "FAIL", "language/expressions/dynamic-import/for-await-resolution-and-error-agen-yield.js": "CRASH", "language/expressions/dynamic-import/import-attributes/2nd-param-await-expr.js": "CRASH", "language/expressions/dynamic-import/import-attributes/2nd-param-await-ident.js": "CRASH", @@ -7243,9 +7160,6 @@ "language/expressions/dynamic-import/import-defer/sync-dependency-of-deferred-async-module/main.js": "FAIL", "language/expressions/dynamic-import/import-defer/sync/main.js": "FAIL", "language/expressions/dynamic-import/import-errored-module.js": "CRASH", - "language/expressions/dynamic-import/reuse-namespace-object-from-import.js": "FAIL", - "language/expressions/dynamic-import/reuse-namespace-object-from-script.js": "FAIL", - "language/expressions/dynamic-import/reuse-namespace-object.js": "FAIL", "language/expressions/dynamic-import/syntax/valid/nested-arrow-assignment-expression-import-source-script-code-valid.js": "FAIL", "language/expressions/dynamic-import/syntax/valid/nested-arrow-assignment-expression-script-code-valid.js": "FAIL", "language/expressions/dynamic-import/syntax/valid/nested-arrow-import-source-script-code-valid.js": "FAIL", @@ -7322,7 +7236,7 @@ "language/expressions/dynamic-import/usage/nested-while-import-then-eval-script-code-host-resolves-module-code.js": "FAIL", "language/expressions/dynamic-import/usage/syntax-nested-block-labeled-eval-script-code-host-resolves-module-code.js": "FAIL", "language/expressions/dynamic-import/usage/top-level-import-then-eval-script-code-host-resolves-module-code.js": "FAIL", - "language/expressions/equals/bigint-and-number-extremes.js": "CRASH", + "language/expressions/equals/bigint-and-number-extremes.js": "FAIL", "language/expressions/exponentiation/applying-the-exp-operator_A6.js": "FAIL", "language/expressions/function/static-init-await-binding.js": "FAIL", "language/expressions/function/static-init-await-reference.js": "FAIL", @@ -7694,7 +7608,6 @@ "language/module-code/instn-resolve-order-src.js": "FAIL", "language/module-code/instn-star-err-not-found.js": "FAIL", "language/module-code/top-level-await/await-dynamic-import-rejection.js": "FAIL", - "language/module-code/top-level-await/dynamic-import-of-waiting-module.js": "FAIL", "language/module-code/top-level-await/dynamic-import-rejection.js": "FAIL", "language/module-code/top-level-await/module-import-rejection-body.js": "FAIL", "language/module-code/top-level-await/module-import-rejection-tick.js": "FAIL", @@ -7723,30 +7636,6 @@ "language/statements/class/definition/constructor-strict-by-default.js": "FAIL", "language/statements/class/definition/fn-name-accessor-get.js": "FAIL", "language/statements/class/definition/fn-name-accessor-set.js": "FAIL", - "language/statements/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-async-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/after-same-line-static-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/after-same-line-static-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/statements/class/elements/arrow-body-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/statements/class/elements/arrow-body-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/statements/class/elements/arrow-body-direct-eval-err-contains-arguments.js": "FAIL", @@ -7765,14 +7654,6 @@ "language/statements/class/elements/evaluation-error/computed-name-toprimitive-returns-nonobject.js": "FAIL", "language/statements/class/elements/evaluation-error/computed-name-tostring-err.js": "FAIL", "language/statements/class/elements/evaluation-error/computed-name-valueof-err.js": "FAIL", - "language/statements/class/elements/multiple-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/multiple-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/multiple-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/multiple-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/multiple-stacked-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/multiple-stacked-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/multiple-stacked-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/multiple-stacked-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", "language/statements/class/elements/nested-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/statements/class/elements/nested-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/statements/class/elements/nested-direct-eval-err-contains-arguments.js": "FAIL", @@ -7781,18 +7662,6 @@ "language/statements/class/elements/nested-private-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/statements/class/elements/nested-private-direct-eval-err-contains-arguments.js": "FAIL", "language/statements/class/elements/nested-private-direct-eval-err-contains-newtarget.js": "FAIL", - "language/statements/class/elements/new-no-sc-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-no-sc-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/new-no-sc-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-no-sc-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-sc-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/new-sc-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-sc-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/new-sc-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-sc-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/new-sc-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/new-sc-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/statements/class/elements/private-derived-cls-direct-eval-contains-superproperty-1.js": "FAIL", "language/statements/class/elements/private-derived-cls-direct-eval-contains-superproperty-2.js": "FAIL", "language/statements/class/elements/private-direct-eval-err-contains-arguments.js": "FAIL", @@ -7816,33 +7685,9 @@ "language/statements/class/elements/privategetter-on-proxy.js": "FAIL", "language/statements/class/elements/privatemethods-on-proxy.js": "FAIL", "language/statements/class/elements/public-class-field-initialization-is-visible-to-proxy.js": "FAIL", - "language/statements/class/elements/regular-definitions-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/regular-definitions-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/regular-definitions-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/regular-definitions-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-async-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-async-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-async-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-async-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-async-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-async-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-async-method-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-gen-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-gen-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-gen-rs-static-async-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-method-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-method-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/same-line-method-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/same-line-method-rs-static-async-method-privatename-identifier.js": "FAIL", "language/statements/class/elements/static-private-fields-proxy-default-handler-throws.js": "FAIL", "language/statements/class/elements/super-access-from-arrow-func-on-field.js": "CRASH", "language/statements/class/elements/syntax/valid/grammar-field-accessor.js": "CRASH", - "language/statements/class/elements/wrapped-in-sc-rs-static-async-generator-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/wrapped-in-sc-rs-static-async-generator-method-privatename-identifier.js": "FAIL", - "language/statements/class/elements/wrapped-in-sc-rs-static-async-method-privatename-identifier-alt.js": "FAIL", - "language/statements/class/elements/wrapped-in-sc-rs-static-async-method-privatename-identifier.js": "FAIL", "language/statements/class/static-init-await-binding-valid.js": "FAIL", "language/statements/class/static-init-super-property.js": "CRASH", "language/statements/class/subclass-builtins/subclass-ArrayBuffer.js": "FAIL", diff --git a/tests/metrics.json b/tests/metrics.json index de31ad5b9..41082a4b3 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,11 +1,11 @@ { "results": { - "crash": 105, - "fail": 8528, - "pass": 38612, + "crash": 103, + "fail": 8351, + "pass": 38767, "skip": 3310, - "timeout": 9, - "unresolved": 30 + "timeout": 10, + "unresolved": 53 }, "total": 50594 } \ No newline at end of file From 3fe4d5cd8f8280203f401a05ad8ad2ae23785afe Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 26 Aug 2025 21:39:19 -0300 Subject: [PATCH 37/46] Minor fixes --- .../promise_abstract_operations/promise_all_record.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 133f15074..f5ffb9e9c 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 @@ -20,8 +20,8 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub struct PromiseAllRecord<'a> { pub(crate) remaining_unresolved_promise_count: u32, - pub result_array: Array<'a>, - pub promise: Promise<'a>, + pub(crate) result_array: Array<'a>, + pub(crate) promise: Promise<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -45,7 +45,8 @@ impl<'a> PromiseAll<'a> { elements[index as usize] = Some(value.unbind()); let data = promise_all.get_mut(agent); - data.remaining_unresolved_promise_count -= 1; + data.remaining_unresolved_promise_count = + data.remaining_unresolved_promise_count.saturating_sub(1); if data.remaining_unresolved_promise_count == 0 { let capability = PromiseCapability::from_promise(data.promise, false); capability.resolve(agent, result_array.into_value().unbind(), gc); From aad261a3223fa0ab69794420ce6c9746b68324fb Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 28 Aug 2025 21:10:15 -0300 Subject: [PATCH 38/46] Create array initialized as None --- .../promise_objects/promise_constructor.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 58b34904b..4cff15ffc 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 @@ -11,6 +11,7 @@ use crate::{ builders::builtin_function_builder::BuiltinFunctionBuilder, builtins::{ ArgumentsList, Array, Behaviour, Builtin, BuiltinGetter, BuiltinIntrinsicConstructor, + array_create, ordinary::ordinary_create_from_constructor, promise::{ Promise, @@ -250,10 +251,15 @@ impl PromiseConstructor { let result_capability = PromiseCapability::new(agent, gc.nogc()); let result_promise = result_capability.promise().bind(gc.nogc()); - let undefined_values = - (vec![Value::Undefined.bind(gc.nogc()); array_len as usize]).bind(gc.nogc()); - let result_array = - Array::from_slice(agent, &undefined_values.unbind(), gc.nogc()).bind(gc.nogc()); + let result_array = array_create( + agent, + array_len as usize, + array_len as usize, + None, + gc.nogc(), + ) + .unbind()? + .bind(gc.nogc()); let promise_all_record = agent.heap.create(PromiseAllRecord { remaining_unresolved_promise_count: array_len, From fcb8e4756fb3d674d2effc78a183854ddaa280df Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 28 Aug 2025 21:51:46 -0300 Subject: [PATCH 39/46] Turn values into promises --- .../promise_objects/promise_constructor.rs | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) 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 4cff15ffc..6bc2dfbb6 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 @@ -222,7 +222,7 @@ impl PromiseConstructor { agent: &mut Agent, this_value: Value, arguments: ArgumentsList, - gc: GcScope<'gc, '_>, + mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { // 1. Let C be the this value. if this_value @@ -237,19 +237,22 @@ impl PromiseConstructor { gc.into_nogc(), )); } + let arguments = arguments.bind(gc.nogc()); - let first_arg = arguments.get(0).bind(gc.nogc()); - let Ok(promise_array) = Array::try_from(first_arg.unbind()).bind(gc.nogc()) else { + let first_arg = arguments.get(0).unbind().bind(gc.nogc()); + let Ok(promise_array) = Array::try_from(first_arg.unbind()).unbind().bind(gc.nogc()) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, - "Expected an array of promises", + "Expected an array as first argument", gc.into_nogc(), )); }; + let scoped_promise_array = promise_array.scope(agent, gc.nogc()); let array_len = promise_array.len(agent); - let result_capability = PromiseCapability::new(agent, gc.nogc()); - let result_promise = result_capability.promise().bind(gc.nogc()); + let result_capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); + let result_promise = result_capability.promise().unbind().bind(gc.nogc()); + let scoped_result_promise = result_promise.scope(agent, gc.nogc()); let result_array = array_create( agent, @@ -264,15 +267,32 @@ impl PromiseConstructor { let promise_all_record = agent.heap.create(PromiseAllRecord { remaining_unresolved_promise_count: array_len, result_array: result_array.unbind(), - promise: result_promise.unbind(), + promise: scoped_result_promise.get(agent), }); for index in 0..array_len { - let storage = promise_array.get_storage(agent); - let element = storage.values[index as usize].bind(gc.nogc()); + let element = (scoped_promise_array.get(agent).as_slice(agent)[index as usize]) + .unbind() + .bind(gc.nogc()); if let Some(Value::Promise(promise)) = element { - let capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); + let capability = PromiseCapability::new(agent, gc.nogc()); + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); + } else if let Some(value) = element { + let promise = Promise::resolve(agent, value.unbind(), gc.reborrow()) + .unbind() + .bind(gc.nogc()); + let capability = PromiseCapability::new(agent, gc.nogc()); inner_promise_then( agent, promise.unbind(), @@ -284,10 +304,10 @@ impl PromiseConstructor { Some(capability.unbind()), gc.nogc(), ); - }; + } } - Ok(result_promise.unbind().into_value()) + Ok(scoped_result_promise.get(agent).into_value().unbind()) } fn all_settled<'gc>( From 18a0f515d3c567694a039f5368feaf852158d8eb Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 28 Aug 2025 21:53:50 -0300 Subject: [PATCH 40/46] Replace `if` with `match` --- .../promise_objects/promise_constructor.rs | 64 ++++++++++--------- 1 file changed, 35 insertions(+), 29 deletions(-) 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 6bc2dfbb6..7263714ed 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 @@ -275,35 +275,41 @@ impl PromiseConstructor { .unbind() .bind(gc.nogc()); - if let Some(Value::Promise(promise)) = element { - let capability = PromiseCapability::new(agent, gc.nogc()); - inner_promise_then( - agent, - promise.unbind(), - PromiseReactionHandler::PromiseAll { - index, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(capability.unbind()), - gc.nogc(), - ); - } else if let Some(value) = element { - let promise = Promise::resolve(agent, value.unbind(), gc.reborrow()) - .unbind() - .bind(gc.nogc()); - let capability = PromiseCapability::new(agent, gc.nogc()); - inner_promise_then( - agent, - promise.unbind(), - PromiseReactionHandler::PromiseAll { - index, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(capability.unbind()), - gc.nogc(), - ); + match element { + Some(Value::Promise(promise)) => { + let capability = PromiseCapability::new(agent, gc.nogc()); + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); + } + Some(value) => { + let promise = Promise::resolve(agent, value.unbind(), gc.reborrow()) + .unbind() + .bind(gc.nogc()); + let capability = PromiseCapability::new(agent, gc.nogc()); + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); + } + None => { + unreachable!() + } } } From f7e966e2879b2e83da9beb4eb53498710f9d6304 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Thu, 28 Aug 2025 21:56:54 -0300 Subject: [PATCH 41/46] Improved code --- .../promise_objects/promise_constructor.rs | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) 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 7263714ed..9e87e4ba6 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 @@ -275,45 +275,31 @@ impl PromiseConstructor { .unbind() .bind(gc.nogc()); - match element { - Some(Value::Promise(promise)) => { - let capability = PromiseCapability::new(agent, gc.nogc()); - inner_promise_then( - agent, - promise.unbind(), - PromiseReactionHandler::PromiseAll { - index, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(capability.unbind()), - gc.nogc(), - ); - } - Some(value) => { - let promise = Promise::resolve(agent, value.unbind(), gc.reborrow()) - .unbind() - .bind(gc.nogc()); - let capability = PromiseCapability::new(agent, gc.nogc()); - inner_promise_then( - agent, - promise.unbind(), - PromiseReactionHandler::PromiseAll { - index, - promise_all: promise_all_record, - }, - PromiseReactionHandler::Empty, - Some(capability.unbind()), - gc.nogc(), - ); - } + let promise = match element { + Some(Value::Promise(promise)) => promise, + Some(value) => Promise::resolve(agent, value.unbind(), gc.reborrow()) + .unbind() + .bind(gc.nogc()), None => { unreachable!() } - } + }; + + let capability = PromiseCapability::new(agent, gc.nogc()); + inner_promise_then( + agent, + promise.unbind(), + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, + PromiseReactionHandler::Empty, + Some(capability.unbind()), + gc.nogc(), + ); } - Ok(scoped_result_promise.get(agent).into_value().unbind()) + Ok(scoped_result_promise.get(agent).into_value()) } fn all_settled<'gc>( From 7a77664b87d61b18ea8dbc21cbef2fc0ca4ea62d Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 1 Sep 2025 09:12:50 -0300 Subject: [PATCH 42/46] Handle errors --- .../promise_all_record.rs | 16 +++++++++- .../promise_jobs.rs | 29 +++++++++++-------- .../promise_objects/promise_constructor.rs | 5 +++- 3 files changed, 36 insertions(+), 14 deletions(-) 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 f5ffb9e9c..cbe96f4e8 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 @@ -48,11 +48,25 @@ impl<'a> PromiseAll<'a> { data.remaining_unresolved_promise_count = data.remaining_unresolved_promise_count.saturating_sub(1); if data.remaining_unresolved_promise_count == 0 { - let capability = PromiseCapability::from_promise(data.promise, false); + 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() 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 169be38c7..36c53a862 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 @@ -278,27 +278,32 @@ impl PromiseReactionJob { ); return Ok(()); } - PromiseReactionType::Reject => { - // a. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »). - // b. Return unused. - ( - Err(JsError::new(argument)), - PromiseCapability::from_promise(promise, true), - ) - } + PromiseReactionType::Reject => ( + Err(JsError::new(argument)), + PromiseCapability::from_promise(promise, true), + ), } } PromiseReactionHandler::PromiseAll { promise_all, index } => { let reaction_type = agent[reaction].reaction_type; let capability = agent[reaction].capability.clone().unwrap(); + let scoped_arg = argument.scope(agent, gc.nogc()); match reaction_type { PromiseReactionType::Fulfill => { - let arg_unbound = argument.unbind(); - promise_all.on_promise_fulfilled(agent, index, arg_unbound, gc.reborrow()); - (Ok(arg_unbound.bind(gc.nogc())), capability.bind(gc.nogc())) + promise_all.on_promise_fulfilled( + agent, + index, + scoped_arg.get(agent), + gc.reborrow(), + ); + (Ok(scoped_arg.get(agent)), capability.bind(gc.nogc())) } PromiseReactionType::Reject => { - (Err(JsError::new(argument)), capability.bind(gc.nogc())) + promise_all.on_promise_rejected(agent, scoped_arg.get(agent), gc.nogc()); + ( + Err(JsError::new(scoped_arg.get(agent))), + capability.bind(gc.nogc()), + ) } } } 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 9e87e4ba6..e1b1fec47 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 @@ -293,7 +293,10 @@ impl PromiseConstructor { index, promise_all: promise_all_record, }, - PromiseReactionHandler::Empty, + PromiseReactionHandler::PromiseAll { + index, + promise_all: promise_all_record, + }, Some(capability.unbind()), gc.nogc(), ); From 6eb1b159dc01da3a14e13eb89d5552b715156f7f Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 1 Sep 2025 09:16:00 -0300 Subject: [PATCH 43/46] Updated metrics --- tests/expectations.json | 19 ++++--------------- tests/metrics.json | 6 +++--- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/tests/expectations.json b/tests/expectations.json index 072479c03..0782c19e5 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -1333,8 +1333,6 @@ "built-ins/Promise/all/S25.4.4.1_A3.1_T3.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A4.1_T1.js": "FAIL", "built-ins/Promise/all/S25.4.4.1_A5.1_T1.js": "FAIL", - "built-ins/Promise/all/S25.4.4.1_A8.2_T1.js": "UNRESOLVED", - "built-ins/Promise/all/S25.4.4.1_A8.2_T2.js": "UNRESOLVED", "built-ins/Promise/all/call-resolve-element-after-return.js": "FAIL", "built-ins/Promise/all/call-resolve-element-items.js": "FAIL", "built-ins/Promise/all/call-resolve-element.js": "FAIL", @@ -1346,16 +1344,15 @@ "built-ins/Promise/all/ctx-ctor.js": "FAIL", "built-ins/Promise/all/ctx-non-ctor.js": "FAIL", "built-ins/Promise/all/ctx-non-object.js": "FAIL", - "built-ins/Promise/all/does-not-invoke-array-setters.js": "UNRESOLVED", "built-ins/Promise/all/invoke-resolve-error-close.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-error-reject.js": "UNRESOLVED", + "built-ins/Promise/all/invoke-resolve-error-reject.js": "FAIL", "built-ins/Promise/all/invoke-resolve-get-error-reject.js": "UNRESOLVED", "built-ins/Promise/all/invoke-resolve-get-error.js": "FAIL", "built-ins/Promise/all/invoke-resolve-get-once-multiple-calls.js": "FAIL", "built-ins/Promise/all/invoke-resolve-get-once-no-calls.js": "FAIL", "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-custom.js": "FAIL", - "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-promise.js": "UNRESOLVED", - "built-ins/Promise/all/invoke-resolve-on-values-every-iteration-of-promise.js": "UNRESOLVED", + "built-ins/Promise/all/invoke-resolve-on-promises-every-iteration-of-promise.js": "FAIL", + "built-ins/Promise/all/invoke-resolve-on-values-every-iteration-of-promise.js": "FAIL", "built-ins/Promise/all/invoke-resolve-return.js": "FAIL", "built-ins/Promise/all/invoke-resolve.js": "FAIL", "built-ins/Promise/all/invoke-then-error-close.js": "FAIL", @@ -1389,10 +1386,6 @@ "built-ins/Promise/all/iter-step-err-no-close.js": "FAIL", "built-ins/Promise/all/iter-step-err-reject.js": "FAIL", "built-ins/Promise/all/new-resolve-function.js": "FAIL", - "built-ins/Promise/all/reject-deferred.js": "UNRESOLVED", - "built-ins/Promise/all/reject-ignored-deferred.js": "UNRESOLVED", - "built-ins/Promise/all/reject-ignored-immed.js": "UNRESOLVED", - "built-ins/Promise/all/reject-immed.js": "UNRESOLVED", "built-ins/Promise/all/resolve-before-loop-exit-from-same.js": "FAIL", "built-ins/Promise/all/resolve-before-loop-exit.js": "FAIL", "built-ins/Promise/all/resolve-element-function-extensible.js": "FAIL", @@ -1402,11 +1395,8 @@ "built-ins/Promise/all/resolve-element-function-property-order.js": "FAIL", "built-ins/Promise/all/resolve-element-function-prototype.js": "FAIL", "built-ins/Promise/all/resolve-from-same-thenable.js": "FAIL", - "built-ins/Promise/all/resolve-ignores-late-rejection-deferred.js": "UNRESOLVED", - "built-ins/Promise/all/resolve-ignores-late-rejection.js": "UNRESOLVED", "built-ins/Promise/all/resolve-non-callable.js": "FAIL", - "built-ins/Promise/all/resolve-non-thenable.js": "UNRESOLVED", - "built-ins/Promise/all/resolve-not-callable-reject-with-typeerror.js": "UNRESOLVED", + "built-ins/Promise/all/resolve-not-callable-reject-with-typeerror.js": "FAIL", "built-ins/Promise/all/resolve-poisoned-then.js": "UNRESOLVED", "built-ins/Promise/all/resolve-thenable.js": "UNRESOLVED", "built-ins/Promise/all/resolve-throws-iterator-return-is-not-callable.js": "FAIL", @@ -1580,7 +1570,6 @@ "built-ins/Promise/any/reject-element-function-property-order.js": "FAIL", "built-ins/Promise/any/reject-element-function-prototype.js": "FAIL", "built-ins/Promise/any/reject-from-same-thenable.js": "FAIL", - "built-ins/Promise/any/reject-ignored-deferred.js": "UNRESOLVED", "built-ins/Promise/any/reject-ignored-immed.js": "FAIL", "built-ins/Promise/any/reject-immed.js": "FAIL", "built-ins/Promise/any/resolve-before-loop-exit-from-same.js": "FAIL", diff --git a/tests/metrics.json b/tests/metrics.json index 41082a4b3..a88c685e6 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,11 +1,11 @@ { "results": { "crash": 103, - "fail": 8351, - "pass": 38767, + "fail": 8355, + "pass": 38778, "skip": 3310, "timeout": 10, - "unresolved": 53 + "unresolved": 38 }, "total": 50594 } \ No newline at end of file From 95e3dad3681312c1d0e56e391463826ada964c29 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Mon, 1 Sep 2025 09:20:29 -0300 Subject: [PATCH 44/46] Updated expectations --- tests/expectations.json | 1 - tests/metrics.json | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/expectations.json b/tests/expectations.json index 0782c19e5..f22c3602a 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -8002,7 +8002,6 @@ "staging/sm/Atomics/cross-compartment.js": "FAIL", "staging/sm/Atomics/detached-buffers.js": "FAIL", "staging/sm/BigInt/Number-conversion-rounding.js": "FAIL", - "staging/sm/Date/dst-offset-caching-1-of-8.js": "TIMEOUT", "staging/sm/Date/dst-offset-caching-2-of-8.js": "TIMEOUT", "staging/sm/Date/dst-offset-caching-3-of-8.js": "TIMEOUT", "staging/sm/Date/dst-offset-caching-4-of-8.js": "TIMEOUT", diff --git a/tests/metrics.json b/tests/metrics.json index a88c685e6..6c63c73dd 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -2,9 +2,9 @@ "results": { "crash": 103, "fail": 8355, - "pass": 38778, + "pass": 38779, "skip": 3310, - "timeout": 10, + "timeout": 9, "unresolved": 38 }, "total": 50594 From c064ec165e46fcbe7a534b593b86e4a95cde3b78 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 2 Sep 2025 21:18:24 -0300 Subject: [PATCH 45/46] Solve nitpicks --- .../promise_objects/promise_constructor.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 e1b1fec47..8b1912c12 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 @@ -239,8 +239,8 @@ impl PromiseConstructor { } let arguments = arguments.bind(gc.nogc()); - let first_arg = arguments.get(0).unbind().bind(gc.nogc()); - let Ok(promise_array) = Array::try_from(first_arg.unbind()).unbind().bind(gc.nogc()) else { + let first_arg = arguments.get(0); + let Ok(promise_array) = Array::try_from(first_arg) else { return Err(agent.throw_exception_with_static_message( ExceptionType::TypeError, "Expected an array as first argument", @@ -250,8 +250,8 @@ impl PromiseConstructor { let scoped_promise_array = promise_array.scope(agent, gc.nogc()); let array_len = promise_array.len(agent); - let result_capability = PromiseCapability::new(agent, gc.nogc()).bind(gc.nogc()); - let result_promise = result_capability.promise().unbind().bind(gc.nogc()); + let result_capability = PromiseCapability::new(agent, gc.nogc()); + let result_promise = result_capability.promise(); let scoped_result_promise = result_promise.scope(agent, gc.nogc()); let result_array = array_create( @@ -271,9 +271,7 @@ impl PromiseConstructor { }); for index in 0..array_len { - let element = (scoped_promise_array.get(agent).as_slice(agent)[index as usize]) - .unbind() - .bind(gc.nogc()); + let element = scoped_promise_array.get(agent).as_slice(agent)[index as usize]; let promise = match element { Some(Value::Promise(promise)) => promise, From 7864cce584e556e9d2571d5d3d9c3e977bc50fb6 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Tue, 2 Sep 2025 21:21:06 -0300 Subject: [PATCH 46/46] Add comment --- .../promise_objects/promise_constructor.rs | 1 + 1 file changed, 1 insertion(+) 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 8b1912c12..8cc3573ac 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 @@ -218,6 +218,7 @@ impl PromiseConstructor { Ok(scoped_promise.get(agent).into_value()) } + /// ### [27.2.4.1 Promise.all ( iterable )](https://tc39.es/ecma262/#sec-promise.all) fn all<'gc>( agent: &mut Agent, this_value: Value,