Skip to content

Commit 2bd918c

Browse files
committed
Added promise all settled constructor
1 parent 803d179 commit 2bd918c

File tree

1 file changed

+253
-4
lines changed

1 file changed

+253
-4
lines changed

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

Lines changed: 253 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::{
2424
promise_objects::{
2525
promise_abstract_operations::{
2626
promise_all_record::PromiseAllRecord,
27+
promise_all_settled_record::PromiseAllSettledRecord,
2728
promise_capability_records::if_abrupt_reject_promise_m,
2829
promise_reaction_records::PromiseReactionHandler,
2930
},
@@ -350,11 +351,115 @@ impl PromiseConstructor {
350351
/// > constructor.
351352
fn all_settled<'gc>(
352353
agent: &mut Agent,
353-
_this_value: Value,
354-
_arguments: ArgumentsList,
355-
gc: GcScope<'gc, '_>,
354+
this_value: Value,
355+
arguments: ArgumentsList,
356+
mut gc: GcScope<'gc, '_>,
356357
) -> JsResult<'gc, Value<'gc>> {
357-
Err(agent.todo("Promise.allSettled", gc.into_nogc()))
358+
let this_value = this_value.bind(gc.nogc());
359+
let arguments = arguments.bind(gc.nogc());
360+
let iterable = arguments.get(0).scope(agent, gc.nogc());
361+
362+
// 1. Let C be the this value.
363+
if this_value
364+
!= agent
365+
.current_realm_record()
366+
.intrinsics()
367+
.promise()
368+
.into_value()
369+
{
370+
return Err(throw_promise_subclassing_not_supported(
371+
agent,
372+
gc.into_nogc(),
373+
));
374+
}
375+
376+
// 2. Let promiseCapability be ? NewPromiseCapability(C).
377+
let Some(constructor) = is_constructor(agent, this_value) else {
378+
return Err(agent.throw_exception_with_static_message(
379+
ExceptionType::TypeError,
380+
"Expected the this value to be a constructor.",
381+
gc.into_nogc(),
382+
));
383+
};
384+
let constructor = constructor.scope(agent, gc.nogc());
385+
let promise_capability = PromiseCapability::new(agent, gc.nogc());
386+
let promise = promise_capability.promise().scope(agent, gc.nogc());
387+
388+
// 3. Let promiseResolve be Completion(GetPromiseResolve(C)).
389+
let promise_resolve = get_promise_resolve(agent, constructor.get(agent), gc.reborrow())
390+
.unbind()
391+
.bind(gc.nogc());
392+
393+
// 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).
394+
let promise_capability = PromiseCapability {
395+
promise: promise.get(agent).bind(gc.nogc()),
396+
must_be_unresolved: true,
397+
};
398+
let promise_resolve =
399+
if_abrupt_reject_promise_m!(agent, promise_resolve, promise_capability, gc);
400+
let promise_resolve = promise_resolve.scope(agent, gc.nogc());
401+
402+
// 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)).
403+
let iterator_record = get_iterator(agent, iterable.get(agent), false, gc.reborrow())
404+
.unbind()
405+
.bind(gc.nogc());
406+
407+
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
408+
let promise_capability = PromiseCapability {
409+
promise: promise.get(agent).bind(gc.nogc()),
410+
must_be_unresolved: true,
411+
};
412+
let MaybeInvalidIteratorRecord {
413+
iterator,
414+
next_method,
415+
} = if_abrupt_reject_promise_m!(agent, iterator_record, promise_capability, gc);
416+
417+
let iterator = iterator.scope(agent, gc.nogc());
418+
419+
// 7. Let result be Completion(PerformPromiseAll(iteratorRecord, C, promiseCapability, promiseResolve)).
420+
let mut iterator_done = false;
421+
let result = perform_promise_all_settled(
422+
agent,
423+
iterator.clone(),
424+
next_method.unbind(),
425+
constructor,
426+
promise_capability.unbind(),
427+
promise_resolve,
428+
&mut iterator_done,
429+
gc.reborrow(),
430+
)
431+
.unbind()
432+
.bind(gc.nogc());
433+
434+
// 8. If result is an abrupt completion, then
435+
let result = match result {
436+
Err(mut result) => {
437+
// a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)).
438+
if !iterator_done {
439+
result = iterator_close_with_error(
440+
agent,
441+
iterator.get(agent),
442+
result.unbind(),
443+
gc.reborrow(),
444+
)
445+
.unbind()
446+
.bind(gc.nogc());
447+
}
448+
449+
// b. IfAbruptRejectPromise(result, promiseCapability).
450+
let promise_capability = PromiseCapability {
451+
promise: promise.get(agent).bind(gc.nogc()),
452+
must_be_unresolved: true,
453+
};
454+
// a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
455+
promise_capability.reject(agent, result.value().unbind(), gc.nogc());
456+
// b. Return capability.[[Promise]].
457+
promise_capability.promise()
458+
}
459+
Ok(result) => result,
460+
};
461+
// 9. Return ! result.
462+
Ok(result.into_value().unbind())
358463
}
359464

360465
/// ### [27.2.4.3 Promise.any ( iterable )](https://tc39.es/ecma262/#sec-promise.any)
@@ -801,6 +906,150 @@ fn perform_promise_all<'gc>(
801906
}
802907
}
803908

909+
/// ### [27.2.4.2.1 PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, promiseResolve )](https://tc39.es/ecma262/#sec-performpromiseallsettled)
910+
#[allow(clippy::too_many_arguments)]
911+
fn perform_promise_all_settled<'gc>(
912+
agent: &mut Agent,
913+
iterator: Scoped<Object>,
914+
next_method: Option<Function>,
915+
constructor: Scoped<Function>,
916+
result_capability: PromiseCapability,
917+
promise_resolve: Scoped<Function>,
918+
iterator_done: &mut bool,
919+
mut gc: GcScope<'gc, '_>,
920+
) -> JsResult<'gc, Promise<'gc>> {
921+
let result_capability = result_capability.bind(gc.nogc());
922+
923+
let Some(next_method) = next_method else {
924+
return Err(throw_not_callable(agent, gc.into_nogc()));
925+
};
926+
927+
let next_method = next_method.scope(agent, gc.nogc());
928+
929+
// 1. Let values be a new empty List.
930+
let capacity = match iterator.get(agent) {
931+
Object::Array(array) => array.len(agent),
932+
Object::Map(map) => agent[map].size(),
933+
Object::Set(set) => agent[set].size(),
934+
_ => 0,
935+
};
936+
937+
let result_array = array_create(agent, 0, capacity as usize, None, gc.nogc())
938+
.unbind()?
939+
.bind(gc.nogc());
940+
let result_array = result_array.scope(agent, gc.nogc());
941+
942+
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
943+
let promise = result_capability.promise.scope(agent, gc.nogc());
944+
let promise_all_settled_reference = agent
945+
.heap
946+
.create(PromiseAllSettledRecord {
947+
remaining_elements_count: 1,
948+
result_array: result_array.get(agent),
949+
promise: promise.get(agent),
950+
})
951+
.scope(agent, gc.nogc());
952+
953+
// 3. Let index be 0.
954+
let mut index = 0;
955+
956+
// 4. Repeat,
957+
loop {
958+
let iterator_record = IteratorRecord {
959+
iterator: iterator.get(agent),
960+
next_method: next_method.get(agent),
961+
}
962+
.bind(gc.nogc());
963+
964+
// a. Let next be ? IteratorStepValue(iteratorRecord).
965+
let next = iterator_step_value(agent, iterator_record.unbind(), gc.reborrow())
966+
.unbind()?
967+
.bind(gc.nogc());
968+
969+
// b. If next is done, then
970+
let Some(next) = next else {
971+
*iterator_done = true;
972+
let promise_all = promise_all_settled_reference.get(agent).bind(gc.nogc());
973+
let data = promise_all.get_mut(agent);
974+
// i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
975+
data.remaining_elements_count -= 1;
976+
// ii. If remainingElementsCount.[[Value]] = 0, then
977+
if data.remaining_elements_count == 0 {
978+
// 1. Let valuesArray be CreateArrayFromList(values).
979+
let values_array = result_array.get(agent).bind(gc.nogc());
980+
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
981+
let result_capability = PromiseCapability {
982+
promise: promise.get(agent).bind(gc.nogc()),
983+
must_be_unresolved: true,
984+
};
985+
result_capability.unbind().resolve(
986+
agent,
987+
values_array.into_value().unbind(),
988+
gc.reborrow(),
989+
);
990+
}
991+
992+
// iii. Return resultCapability.[[Promise]].
993+
return Ok(promise.get(agent));
994+
};
995+
996+
// c. Append undefined to values.
997+
let temp_array = result_array.get(agent).bind(gc.nogc());
998+
if let Err(err) = temp_array.reserve(agent, 1) {
999+
return Err(agent.throw_allocation_exception(err, gc.into_nogc()));
1000+
}
1001+
// SAFETY: reserve did not fail.
1002+
unsafe { temp_array.set_len(agent, index + 1) };
1003+
1004+
// d. Let nextPromise be ? Call(promiseResolve, constructor, « next »).
1005+
let call_result = call_function(
1006+
agent,
1007+
promise_resolve.get(agent),
1008+
constructor.get(agent).into_value(),
1009+
Some(ArgumentsList::from_mut_value(&mut next.unbind())),
1010+
gc.reborrow(),
1011+
)
1012+
.unbind()?
1013+
.bind(gc.nogc());
1014+
1015+
// Note: as we don't yet support Promise subclassing, if we see a
1016+
// non-Promise return we wrap it inside a resolved Promise to get
1017+
// then-chaining.
1018+
let next_promise = match call_result {
1019+
Value::Promise(next_promise) => next_promise,
1020+
_ => Promise::new_resolved(agent, call_result),
1021+
};
1022+
1023+
// e. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions.
1024+
// f. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions.
1025+
// g. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
1026+
// h. Set onFulfilled.[[AlreadyCalled]] to false.
1027+
// i. Set onFulfilled.[[Index]] to index.
1028+
// j. Set onFulfilled.[[Values]] to values.
1029+
let promise_all_settled = promise_all_settled_reference.get(agent).bind(gc.nogc());
1030+
let reaction = PromiseReactionHandler::PromiseAllSettled {
1031+
index,
1032+
promise_all_settled,
1033+
};
1034+
// k. Set onFulfilled.[[Capability]] to resultCapability.
1035+
// l. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
1036+
// m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
1037+
promise_all_settled.get_mut(agent).remaining_elements_count += 1;
1038+
// n. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »).
1039+
inner_promise_then(
1040+
agent,
1041+
next_promise.unbind(),
1042+
reaction.unbind(),
1043+
reaction.unbind(),
1044+
None,
1045+
gc.nogc(),
1046+
);
1047+
1048+
// o. Set index to index + 1.
1049+
index += 1;
1050+
}
1051+
}
1052+
8041053
fn throw_promise_subclassing_not_supported<'a>(
8051054
agent: &mut Agent,
8061055
gc: NoGcScope<'a, '_>,

0 commit comments

Comments
 (0)