Skip to content

Commit 6271720

Browse files
committed
WIP Peform trait
1 parent cec10f5 commit 6271720

File tree

5 files changed

+254
-27
lines changed

5 files changed

+254
-27
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;
56
pub(crate) mod promise_all_record;
67
pub(crate) mod promise_all_settled_record;
78
pub mod promise_capability_records;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use crate::{
2+
ecmascript::{
3+
builtins::{
4+
Array,
5+
promise_objects::promise_abstract_operations::promise_reaction_records::PromiseReactionHandler,
6+
},
7+
execution::Agent,
8+
types::Value,
9+
},
10+
engine::{
11+
context::{Bindable, GcScope, NoGcScope, bindable_handle},
12+
rootable::Rootable,
13+
},
14+
};
15+
16+
pub(crate) trait PromiseAllReactionHandler<'a>: Rootable + Bindable {
17+
fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a>;
18+
fn increase_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>);
19+
fn decrease_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>);
20+
fn get_remaining_elements_count(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> u32;
21+
fn on_promise_fulfilled(
22+
self,
23+
agent: &mut Agent,
24+
index: u32,
25+
value: Value<'a>,
26+
gc: GcScope<'a, '_>,
27+
);
28+
fn on_promise_rejected(
29+
self,
30+
agent: &mut Agent,
31+
index: u32,
32+
value: Value<'a>,
33+
gc: GcScope<'a, '_>,
34+
);
35+
fn to_reaction_handler(self, index: u32, gc: NoGcScope<'a, '_>) -> PromiseReactionHandler<'a>;
36+
}

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

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
use crate::{
66
ecmascript::{
77
builtins::{
8-
Array, promise::Promise,
9-
promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability,
8+
Array,
9+
promise::Promise,
10+
promise_objects::promise_abstract_operations::{
11+
promise_all::PromiseAllReactionHandler,
12+
promise_capability_records::PromiseCapability,
13+
promise_reaction_records::PromiseReactionHandler,
14+
},
1015
},
1116
execution::Agent,
1217
types::{IntoValue, Value},
@@ -32,7 +37,38 @@ pub struct PromiseAllRecord<'a> {
3237
pub struct PromiseAll<'a>(BaseIndex<'a, PromiseAllRecord<'static>>);
3338

3439
impl<'a> PromiseAll<'a> {
35-
pub(crate) fn on_promise_fulfilled(
40+
// pub(crate) fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> {
41+
// let data = self.get(agent);
42+
// data.result_array.bind(gc).unbind()
43+
// }
44+
45+
pub(crate) const fn get_index(self) -> usize {
46+
self.0.into_index()
47+
}
48+
49+
pub fn get(self, agent: &Agent) -> &PromiseAllRecord<'a> {
50+
agent
51+
.heap
52+
.promise_all_records
53+
.get(self.get_index())
54+
.expect("PromiseAllRecord not found")
55+
}
56+
57+
pub fn get_mut(self, agent: &mut Agent) -> &mut PromiseAllRecord<'static> {
58+
agent
59+
.heap
60+
.promise_all_records
61+
.get_mut(self.get_index())
62+
.expect("PromiseAllRecord not found")
63+
}
64+
65+
pub(crate) const fn _def() -> Self {
66+
Self(BaseIndex::from_u32_index(0))
67+
}
68+
}
69+
70+
impl<'a> PromiseAllReactionHandler<'a> for PromiseAll<'a> {
71+
fn on_promise_fulfilled(
3672
self,
3773
agent: &mut Agent,
3874
index: u32,
@@ -61,47 +97,49 @@ impl<'a> PromiseAll<'a> {
6197
}
6298
}
6399

64-
pub(crate) fn on_promise_rejected(
100+
fn on_promise_rejected(
65101
self,
66102
agent: &mut Agent,
103+
_index: u32,
67104
value: Value<'a>,
68-
gc: NoGcScope<'a, '_>,
105+
gc: GcScope<'a, '_>,
69106
) {
70-
let value = value.bind(gc);
71-
let promise_all = self.bind(gc);
107+
let value = value.bind(gc.nogc());
108+
let promise_all = self.bind(gc.nogc());
72109
let data = promise_all.get_mut(agent);
73110

74111
let capability = PromiseCapability::from_promise(data.promise, true);
75-
capability.reject(agent, value.unbind(), gc);
112+
capability.reject(agent, value.unbind(), gc.nogc());
76113
}
77114

78-
pub(crate) fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> {
115+
fn get_result_array(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> Array<'a> {
79116
let data = self.get(agent);
80117
data.result_array.bind(gc).unbind()
81118
}
82119

83-
pub(crate) const fn get_index(self) -> usize {
84-
self.0.into_index()
120+
fn increase_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>) {
121+
let promise_all = self.bind(gc);
122+
let data = promise_all.get_mut(agent);
123+
data.remaining_elements_count = data.remaining_elements_count.saturating_add(1);
85124
}
86125

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")
126+
fn decrease_remaining_elements_count(self, agent: &mut Agent, gc: NoGcScope<'a, '_>) {
127+
let promise_all = self.bind(gc);
128+
let data = promise_all.get_mut(agent);
129+
data.remaining_elements_count = data.remaining_elements_count.saturating_sub(1);
93130
}
94131

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")
132+
fn get_remaining_elements_count(self, agent: &Agent, gc: NoGcScope<'a, '_>) -> u32 {
133+
let promise_all = self.bind(gc);
134+
promise_all.get(agent).remaining_elements_count
101135
}
102136

103-
pub(crate) const fn _def() -> Self {
104-
Self(BaseIndex::from_u32_index(0))
137+
fn to_reaction_handler(self, index: u32, gc: NoGcScope<'a, '_>) -> PromiseReactionHandler<'a> {
138+
let promise_all = self.bind(gc);
139+
PromiseReactionHandler::PromiseAll {
140+
index,
141+
promise_all: promise_all.unbind(),
142+
}
105143
}
106144
}
107145

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use crate::{
1212
},
1313
operations_on_objects::{call_function, get_function_realm},
1414
},
15-
builtins::{ArgumentsList, promise::Promise},
15+
builtins::{
16+
ArgumentsList, promise::Promise,
17+
promise_objects::promise_abstract_operations::promise_all::PromiseAllReactionHandler,
18+
},
1619
execution::{
1720
Agent, JsResult,
1821
agent::{InnerJob, Job, JsError},
@@ -296,7 +299,12 @@ impl PromiseReactionJob {
296299
);
297300
}
298301
PromiseReactionType::Reject => {
299-
promise_all.on_promise_rejected(agent, argument.unbind(), gc.nogc());
302+
promise_all.on_promise_rejected(
303+
agent,
304+
index,
305+
argument.unbind(),
306+
gc.reborrow(),
307+
);
300308
}
301309
}
302310
return Ok(());

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

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use crate::{
2323
},
2424
promise_objects::{
2525
promise_abstract_operations::{
26+
promise_all::{PromiseAllReactionHandler, PromiseType},
2627
promise_all_record::PromiseAllRecord,
2728
promise_all_settled_record::PromiseAllSettledRecord,
2829
promise_capability_records::if_abrupt_reject_promise_m,
@@ -1058,6 +1059,149 @@ fn perform_promise_all_settled<'gc>(
10581059
}
10591060
}
10601061

1062+
#[allow(clippy::too_many_arguments)]
1063+
fn perform_promise_all_or_all_settled<'gc, T: PromiseAllReactionHandler<'gc>>(
1064+
agent: &mut Agent,
1065+
iterator: Scoped<Object>,
1066+
next_method: Option<Function>,
1067+
constructor: Scoped<Function>,
1068+
result_capability: PromiseCapability,
1069+
promise_resolve: Scoped<Function>,
1070+
iterator_done: &mut bool,
1071+
promise_all_handler: T,
1072+
mut gc: GcScope<'gc, '_>,
1073+
) -> JsResult<'gc, Promise<'gc>> {
1074+
let result_capability = result_capability.bind(gc.nogc());
1075+
let promise_all_handler = promise_all_handler.bind(gc.nogc());
1076+
1077+
let Some(next_method) = next_method else {
1078+
return Err(throw_not_callable(agent, gc.into_nogc()));
1079+
};
1080+
1081+
let next_method = next_method.scope(agent, gc.nogc());
1082+
1083+
// 1. Let values be a new empty List.
1084+
let capacity = match iterator.get(agent) {
1085+
Object::Array(array) => array.len(agent),
1086+
Object::Map(map) => agent[map].size(),
1087+
Object::Set(set) => agent[set].size(),
1088+
_ => 0,
1089+
};
1090+
1091+
let result_array = array_create(agent, 0, capacity as usize, None, gc.nogc())
1092+
.unbind()?
1093+
.bind(gc.nogc());
1094+
let result_array = result_array.scope(agent, gc.nogc());
1095+
1096+
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
1097+
let promise = result_capability.promise.scope(agent, gc.nogc());
1098+
// let promise_all_reference = agent
1099+
// .heap
1100+
// .create(PromiseAllRecord {
1101+
// remaining_elements_count: 1,
1102+
// result_array: result_array.get(agent),
1103+
// promise: promise.get(agent),
1104+
// })
1105+
// .scope(agent, gc.nogc());
1106+
1107+
// 3. Let index be 0.
1108+
let mut index = 0;
1109+
1110+
// 4. Repeat,
1111+
loop {
1112+
let iterator_record = IteratorRecord {
1113+
iterator: iterator.get(agent),
1114+
next_method: next_method.get(agent),
1115+
}
1116+
.bind(gc.nogc());
1117+
1118+
// a. Let next be ? IteratorStepValue(iteratorRecord).
1119+
let next = iterator_step_value(agent, iterator_record.unbind(), gc.reborrow())
1120+
.unbind()?
1121+
.bind(gc.nogc());
1122+
1123+
// b. If next is done, then
1124+
let Some(next) = next else {
1125+
*iterator_done = true;
1126+
// let promise_all = promise_all_reference.get(agent).bind(gc.nogc());
1127+
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
1128+
promise_all_handler.decrease_remaining_elements_count(agent, gc.nogc());
1129+
// ii. If remainingElementsCount.[[Value]] = 0, then
1130+
if promise_all_handler.get_remaining_elements_count(agent, gc.nogc()) == 0 {
1131+
// 1. Let valuesArray be CreateArrayFromList(values).
1132+
let values_array = result_array.get(agent).bind(gc.nogc());
1133+
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
1134+
let result_capability = PromiseCapability {
1135+
promise: promise.get(agent).bind(gc.nogc()),
1136+
must_be_unresolved: true,
1137+
};
1138+
result_capability.unbind().resolve(
1139+
agent,
1140+
values_array.into_value().unbind(),
1141+
gc.reborrow(),
1142+
);
1143+
}
1144+
1145+
// iii. Return resultCapability.[[Promise]].
1146+
return Ok(promise.get(agent));
1147+
};
1148+
1149+
// c. Append undefined to values.
1150+
let temp_array = result_array.get(agent).bind(gc.nogc());
1151+
if let Err(err) = temp_array.reserve(agent, 1) {
1152+
return Err(agent.throw_allocation_exception(err, gc.into_nogc()));
1153+
}
1154+
// SAFETY: reserve did not fail.
1155+
unsafe { temp_array.set_len(agent, index + 1) };
1156+
1157+
// d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
1158+
let call_result = call_function(
1159+
agent,
1160+
promise_resolve.get(agent),
1161+
constructor.get(agent).into_value(),
1162+
Some(ArgumentsList::from_mut_value(&mut next.unbind())),
1163+
gc.reborrow(),
1164+
)
1165+
.unbind()?
1166+
.bind(gc.nogc());
1167+
1168+
// Note: as we don't yet support Promise subclassing, if we see a
1169+
// non-Promise return we wrap it inside a resolved Promise to get
1170+
// then-chaining.
1171+
let next_promise = match call_result {
1172+
Value::Promise(next_promise) => next_promise,
1173+
_ => Promise::new_resolved(agent, call_result),
1174+
};
1175+
1176+
// e. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions.
1177+
// f. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions.
1178+
// g. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
1179+
// h. Set onFulfilled.[[AlreadyCalled]] to false.
1180+
// i. Set onFulfilled.[[Index]] to index.
1181+
// j. Set onFulfilled.[[Values]] to values.
1182+
let reaction = promise_all_handler
1183+
.to_reaction_handler(index, gc.nogc())
1184+
.bind(gc.nogc());
1185+
1186+
// k. Set onFulfilled.[[Capability]] to resultCapability.
1187+
// l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
1188+
// m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
1189+
promise_all_handler.increase_remaining_elements_count(agent, gc.nogc());
1190+
// n. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »).
1191+
inner_promise_then(
1192+
agent,
1193+
next_promise.unbind(),
1194+
reaction.unbind(),
1195+
reaction.unbind(),
1196+
None,
1197+
gc.nogc(),
1198+
);
1199+
1200+
// o. Set index to index + 1.
1201+
index += 1;
1202+
}
1203+
}
1204+
10611205
fn throw_promise_subclassing_not_supported<'a>(
10621206
agent: &mut Agent,
10631207
gc: NoGcScope<'a, '_>,

0 commit comments

Comments
 (0)