Skip to content

Commit cf2e8ea

Browse files
komygaapoalas
andauthored
feat(ecmascript): Implement Promise.all (#797)
Co-authored-by: Aapo Alasuutari <[email protected]>
1 parent 81774d3 commit cf2e8ea

File tree

21 files changed

+632
-267
lines changed

21 files changed

+632
-267
lines changed

nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
pub(crate) mod promise_all_record;
56
pub mod promise_capability_records;
67
pub(crate) mod promise_finally_functions;
78
pub(crate) mod promise_jobs;
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
use crate::{
6+
ecmascript::{
7+
builtins::{
8+
Array, promise::Promise,
9+
promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability,
10+
},
11+
execution::Agent,
12+
types::{IntoValue, Value},
13+
},
14+
engine::{
15+
context::{Bindable, GcScope, NoGcScope, bindable_handle},
16+
rootable::{HeapRootData, HeapRootRef, Rootable},
17+
},
18+
heap::{
19+
CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, indexes::BaseIndex,
20+
},
21+
};
22+
23+
#[derive(Debug, Clone, Copy)]
24+
pub struct PromiseAllRecord<'a> {
25+
pub(crate) remaining_elements_count: u32,
26+
pub(crate) result_array: Array<'a>,
27+
pub(crate) promise: Promise<'a>,
28+
}
29+
30+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
31+
#[repr(transparent)]
32+
pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>);
33+
34+
impl<'a> PromiseAll<'a> {
35+
pub(crate) fn on_promise_fulfilled(
36+
self,
37+
agent: &mut Agent,
38+
index: u32,
39+
value: Value<'a>,
40+
gc: GcScope<'a, '_>,
41+
) {
42+
let promise_all = self.bind(gc.nogc());
43+
let value = value.bind(gc.nogc());
44+
45+
let result_array = promise_all.get_result_array(agent, gc.nogc());
46+
47+
let elements = result_array.as_mut_slice(agent);
48+
elements[index as usize] = Some(value.unbind());
49+
50+
let data = promise_all.get_mut(agent);
51+
52+
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
53+
data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1);
54+
55+
//ii. If remainingElementsCount.[[Value]] = 0, then
56+
if data.remaining_elements_count == 0 {
57+
// 1. Let valuesArray be CreateArrayFromList(values).
58+
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
59+
let capability = PromiseCapability::from_promise(data.promise, true);
60+
capability.resolve(agent, result_array.into_value().unbind(), gc);
61+
}
62+
}
63+
64+
pub(crate) fn on_promise_rejected(
65+
self,
66+
agent: &mut Agent,
67+
value: Value<'a>,
68+
gc: NoGcScope<'a, '_>,
69+
) {
70+
let value = value.bind(gc);
71+
let promise_all = self.bind(gc);
72+
let data = promise_all.get_mut(agent);
73+
74+
let capability = PromiseCapability::from_promise(data.promise, true);
75+
capability.reject(agent, value.unbind(), gc);
76+
}
77+
78+
pub(crate) fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> {
79+
let data = self.get(agent);
80+
data.result_array.bind(gc).unbind()
81+
}
82+
83+
pub(crate) const fn get_index(self) -> usize {
84+
self.0.into_index()
85+
}
86+
87+
pub fn get(self, agent: &Agent) -> &PromiseAllRecord<'a> {
88+
agent
89+
.heap
90+
.promise_all_records
91+
.get(self.get_index())
92+
.expect("PromiseAllRecord not found")
93+
}
94+
95+
pub fn get_mut(self, agent: &mut Agent) -> &mut PromiseAllRecord<'static> {
96+
agent
97+
.heap
98+
.promise_all_records
99+
.get_mut(self.get_index())
100+
.expect("PromiseAllRecord not found")
101+
}
102+
103+
pub(crate) const fn _def() -> Self {
104+
Self(BaseIndex::from_u32_index(0))
105+
}
106+
}
107+
108+
impl AsRef<[PromiseAllRecord<'static>]> for Agent {
109+
fn as_ref(&self) -> &[PromiseAllRecord<'static>] {
110+
&self.heap.promise_all_records
111+
}
112+
}
113+
114+
impl AsMut<[PromiseAllRecord<'static>]> for Agent {
115+
fn as_mut(&mut self) -> &mut [PromiseAllRecord<'static>] {
116+
&mut self.heap.promise_all_records
117+
}
118+
}
119+
120+
impl HeapMarkAndSweep for PromiseAllRecord<'static> {
121+
fn mark_values(&self, queues: &mut WorkQueues) {
122+
let Self {
123+
remaining_elements_count: _,
124+
result_array,
125+
promise,
126+
} = self;
127+
result_array.mark_values(queues);
128+
promise.mark_values(queues);
129+
}
130+
131+
fn sweep_values(&mut self, compactions: &CompactionLists) {
132+
let Self {
133+
remaining_elements_count: _,
134+
result_array,
135+
promise,
136+
} = self;
137+
result_array.sweep_values(compactions);
138+
promise.sweep_values(compactions);
139+
}
140+
}
141+
142+
impl Rootable for PromiseAll<'_> {
143+
type RootRepr = HeapRootRef;
144+
145+
fn to_root_repr(value: Self) -> Result<Self::RootRepr, HeapRootData> {
146+
Err(HeapRootData::PromiseAll(value.unbind()))
147+
}
148+
149+
fn from_root_repr(value: &Self::RootRepr) -> Result<Self, HeapRootRef> {
150+
Err(*value)
151+
}
152+
153+
fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr {
154+
heap_ref
155+
}
156+
157+
fn from_heap_data(heap_data: HeapRootData) -> Option<Self> {
158+
match heap_data {
159+
HeapRootData::PromiseAll(object) => Some(object),
160+
_ => None,
161+
}
162+
}
163+
}
164+
165+
impl HeapMarkAndSweep for PromiseAll<'static> {
166+
fn mark_values(&self, queues: &mut WorkQueues) {
167+
queues.promise_all_records.push(*self);
168+
}
169+
170+
fn sweep_values(&mut self, compactions: &CompactionLists) {
171+
compactions.promise_all_records.shift_index(&mut self.0);
172+
}
173+
}
174+
175+
bindable_handle!(PromiseAllRecord);
176+
bindable_handle!(PromiseAll);
177+
178+
impl<'a> CreateHeapData<PromiseAllRecord<'a>, PromiseAll<'a>> for Heap {
179+
fn create(&mut self, data: PromiseAllRecord<'a>) -> PromiseAll<'a> {
180+
self.promise_all_records.push(data.unbind());
181+
self.alloc_counter += core::mem::size_of::<PromiseAllRecord<'static>>();
182+
PromiseAll(BaseIndex::last_t(&self.promise_all_records))
183+
}
184+
}

nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_jobs.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl PromiseReactionJob {
145145
let Self { reaction, argument } = self;
146146
let reaction = reaction.take(agent).bind(gc.nogc());
147147
let argument = argument.take(agent).bind(gc.nogc());
148-
// The following are substeps of point 1 in NewPromiseReactionJob.
148+
149149
let (handler_result, promise_capability) = match agent[reaction].handler {
150150
PromiseReactionHandler::Empty => {
151151
let capability = agent[reaction].capability.clone().unwrap().bind(gc.nogc());
@@ -278,15 +278,28 @@ impl PromiseReactionJob {
278278
);
279279
return Ok(());
280280
}
281+
PromiseReactionType::Reject => (
282+
Err(JsError::new(argument)),
283+
PromiseCapability::from_promise(promise, true),
284+
),
285+
}
286+
}
287+
PromiseReactionHandler::PromiseAll { promise_all, index } => {
288+
let reaction_type = agent[reaction].reaction_type;
289+
match reaction_type {
290+
PromiseReactionType::Fulfill => {
291+
promise_all.on_promise_fulfilled(
292+
agent,
293+
index,
294+
argument.unbind(),
295+
gc.reborrow(),
296+
);
297+
}
281298
PromiseReactionType::Reject => {
282-
// a. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »).
283-
// b. Return unused.
284-
(
285-
Err(JsError::new(argument)),
286-
PromiseCapability::from_promise(promise, true),
287-
)
299+
promise_all.on_promise_rejected(agent, argument.unbind(), gc.nogc());
288300
}
289301
}
302+
return Ok(());
290303
}
291304
};
292305

@@ -348,7 +361,8 @@ pub(crate) fn new_promise_reaction_job(
348361
| PromiseReactionHandler::AsyncFromSyncIteratorClose(_)
349362
| PromiseReactionHandler::AsyncModule(_)
350363
| PromiseReactionHandler::DynamicImport { .. }
351-
| PromiseReactionHandler::DynamicImportEvaluate { .. } => None,
364+
| PromiseReactionHandler::DynamicImportEvaluate { .. }
365+
| PromiseReactionHandler::PromiseAll { .. } => None,
352366
};
353367

354368
// 4. Return the Record { [[Job]]: job, [[Realm]]: handlerRealm }.

nova_vm/src/ecmascript/builtins/control_abstraction_objects/promise_objects/promise_abstract_operations/promise_reaction_records.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::{
1010
async_generator_objects::AsyncGenerator,
1111
control_abstraction_objects::async_function_objects::await_reaction::AwaitReaction,
1212
promise::Promise,
13+
promise_objects::promise_abstract_operations::promise_all_record::PromiseAll,
1314
},
1415
execution::Agent,
1516
scripts_and_modules::module::module_semantics::{
@@ -68,8 +69,13 @@ pub(crate) enum PromiseReactionHandler<'a> {
6869
promise: Promise<'a>,
6970
module: AbstractModule<'a>,
7071
},
72+
PromiseAll {
73+
index: u32,
74+
promise_all: PromiseAll<'a>,
75+
},
7176
Empty,
7277
}
78+
bindable_handle!(PromiseReactionHandler);
7379

7480
impl HeapMarkAndSweep for PromiseReactionHandler<'static> {
7581
fn mark_values(&self, queues: &mut WorkQueues) {
@@ -85,6 +91,10 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> {
8591
promise.mark_values(queues);
8692
module.mark_values(queues);
8793
}
94+
Self::PromiseAll {
95+
index: _,
96+
promise_all,
97+
} => promise_all.mark_values(queues),
8898
Self::Empty => {}
8999
}
90100
}
@@ -104,6 +114,12 @@ impl HeapMarkAndSweep for PromiseReactionHandler<'static> {
104114
promise.sweep_values(compactions);
105115
module.sweep_values(compactions);
106116
}
117+
Self::PromiseAll {
118+
index: _,
119+
promise_all,
120+
} => {
121+
promise_all.sweep_values(compactions);
122+
}
107123
Self::Empty => {}
108124
}
109125
}

0 commit comments

Comments
 (0)