/src/serenity/Userland/Libraries/LibJS/Runtime/PromiseConstructor.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021-2024, Linus Groh <linusg@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Function.h> |
8 | | #include <LibJS/Runtime/AbstractOperations.h> |
9 | | #include <LibJS/Runtime/AggregateError.h> |
10 | | #include <LibJS/Runtime/Array.h> |
11 | | #include <LibJS/Runtime/Error.h> |
12 | | #include <LibJS/Runtime/FunctionObject.h> |
13 | | #include <LibJS/Runtime/GlobalObject.h> |
14 | | #include <LibJS/Runtime/Iterator.h> |
15 | | #include <LibJS/Runtime/Promise.h> |
16 | | #include <LibJS/Runtime/PromiseCapability.h> |
17 | | #include <LibJS/Runtime/PromiseConstructor.h> |
18 | | #include <LibJS/Runtime/PromiseResolvingElementFunctions.h> |
19 | | |
20 | | namespace JS { |
21 | | |
22 | | JS_DEFINE_ALLOCATOR(PromiseConstructor); |
23 | | |
24 | | // 27.2.4.1.1 GetPromiseResolve ( promiseConstructor ), https://tc39.es/ecma262/#sec-getpromiseresolve |
25 | | static ThrowCompletionOr<Value> get_promise_resolve(VM& vm, Value constructor) |
26 | 0 | { |
27 | 0 | VERIFY(constructor.is_constructor()); |
28 | | |
29 | | // 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). |
30 | 0 | auto promise_resolve = TRY(constructor.get(vm, vm.names.resolve)); |
31 | | |
32 | | // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception. |
33 | 0 | if (!promise_resolve.is_function()) |
34 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAFunction, promise_resolve.to_string_without_side_effects()); |
35 | | |
36 | | // 3. Return promiseResolve. |
37 | 0 | return promise_resolve; |
38 | 0 | } |
39 | | |
40 | | using EndOfElementsCallback = Function<ThrowCompletionOr<Value>(PromiseValueList&)>; |
41 | | using InvokeElementFunctionCallback = Function<ThrowCompletionOr<Value>(PromiseValueList&, RemainingElements&, Value, size_t)>; |
42 | | |
43 | | static ThrowCompletionOr<Value> perform_promise_common(VM& vm, IteratorRecord& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve, EndOfElementsCallback end_of_list, InvokeElementFunctionCallback invoke_element_function) |
44 | 0 | { |
45 | 0 | VERIFY(constructor.is_constructor()); |
46 | 0 | VERIFY(promise_resolve.is_function()); |
47 | | |
48 | | // 1. Let values be a new empty List. |
49 | 0 | auto values = vm.heap().allocate_without_realm<PromiseValueList>(); |
50 | | |
51 | | // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }. |
52 | 0 | auto remaining_elements_count = vm.heap().allocate_without_realm<RemainingElements>(1); |
53 | | |
54 | | // 3. Let index be 0. |
55 | 0 | size_t index = 0; |
56 | | |
57 | | // 4. Repeat, |
58 | 0 | while (true) { |
59 | | // a. Let next be ? IteratorStepValue(iteratorRecord). |
60 | 0 | auto next = TRY(iterator_step_value(vm, iterator_record)); |
61 | | |
62 | | // b. If next is DONE, then |
63 | 0 | if (!next.has_value()) { |
64 | | // i. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. |
65 | | // ii. If remainingElementsCount.[[Value]] = 0, then |
66 | 0 | if (--remaining_elements_count->value == 0) { |
67 | | // 1-2. are handled in `end_of_list` |
68 | 0 | return TRY(end_of_list(*values)); |
69 | 0 | } |
70 | | |
71 | | // iii. Return resultCapability.[[Promise]]. |
72 | 0 | return result_capability.promise(); |
73 | 0 | } |
74 | | |
75 | | // c. Append undefined to values. |
76 | 0 | values->values().append(js_undefined()); |
77 | | |
78 | | // d. Let nextPromise be ? Call(promiseResolve, constructor, « next »). |
79 | 0 | auto next_promise = TRY(call(vm, promise_resolve.as_function(), constructor, next.release_value())); |
80 | | |
81 | | // e-l. are handled in `invoke_element_function` |
82 | | |
83 | | // m. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1. |
84 | 0 | ++remaining_elements_count->value; |
85 | | |
86 | | // n. Perform ? Invoke(nextPromise, "then", « ... »). |
87 | 0 | TRY(invoke_element_function(*values, *remaining_elements_count, next_promise, index)); |
88 | | |
89 | | // o. Set index to index + 1. |
90 | 0 | ++index; |
91 | 0 | } |
92 | 0 | } |
93 | | |
94 | | // 27.2.4.1.2 PerformPromiseAll ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseall |
95 | | static ThrowCompletionOr<Value> perform_promise_all(VM& vm, IteratorRecord& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve) |
96 | 0 | { |
97 | 0 | auto& realm = *vm.current_realm(); |
98 | |
|
99 | 0 | return perform_promise_common( |
100 | 0 | vm, iterator_record, constructor, result_capability, promise_resolve, |
101 | 0 | [&](PromiseValueList& values) -> ThrowCompletionOr<Value> { |
102 | | // 1. Let valuesArray be CreateArrayFromList(values). |
103 | 0 | auto values_array = Array::create_from(realm, values.values()); |
104 | | |
105 | | // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »). |
106 | 0 | TRY(call(vm, *result_capability.resolve(), js_undefined(), values_array)); |
107 | | |
108 | | // iv. Return resultCapability.[[Promise]]. |
109 | 0 | return result_capability.promise(); |
110 | 0 | }, |
111 | 0 | [&](PromiseValueList& values, RemainingElements& remaining_elements_count, Value next_promise, size_t index) { |
112 | | // j. Let steps be the algorithm steps defined in Promise.all Resolve Element Functions. |
113 | | // k. Let length be the number of non-optional parameters of the function definition in Promise.all Resolve Element Functions. |
114 | | // l. Let onFulfilled be CreateBuiltinFunction(steps, length, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). |
115 | | // m. Set onFulfilled.[[AlreadyCalled]] to false. |
116 | | // n. Set onFulfilled.[[Index]] to index. |
117 | | // o. Set onFulfilled.[[Values]] to values. |
118 | | // p. Set onFulfilled.[[Capability]] to resultCapability. |
119 | | // q. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. |
120 | 0 | auto on_fulfilled = PromiseAllResolveElementFunction::create(realm, index, values, result_capability, remaining_elements_count); |
121 | 0 | on_fulfilled->define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); |
122 | | |
123 | | // s. Perform ? Invoke(nextPromise, "then", « onFulfilled, resultCapability.[[Reject]] »). |
124 | 0 | return next_promise.invoke(vm, vm.names.then, on_fulfilled, result_capability.reject()); |
125 | 0 | }); |
126 | 0 | } |
127 | | |
128 | | // 27.2.4.2.1 PerformPromiseAllSettled ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseallsettled |
129 | | static ThrowCompletionOr<Value> perform_promise_all_settled(VM& vm, IteratorRecord& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve) |
130 | 0 | { |
131 | 0 | auto& realm = *vm.current_realm(); |
132 | |
|
133 | 0 | return perform_promise_common( |
134 | 0 | vm, iterator_record, constructor, result_capability, promise_resolve, |
135 | 0 | [&](PromiseValueList& values) -> ThrowCompletionOr<Value> { |
136 | 0 | auto values_array = Array::create_from(realm, values.values()); |
137 | |
|
138 | 0 | TRY(call(vm, *result_capability.resolve(), js_undefined(), values_array)); |
139 | |
|
140 | 0 | return result_capability.promise(); |
141 | 0 | }, |
142 | 0 | [&](PromiseValueList& values, RemainingElements& remaining_elements_count, Value next_promise, size_t index) { |
143 | | // j. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions. |
144 | | // k. Let lengthFulfilled be the number of non-optional parameters of the function definition in Promise.allSettled Resolve Element Functions. |
145 | | // l. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). |
146 | | // m. Let alreadyCalled be the Record { [[Value]]: false }. |
147 | | // n. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled. |
148 | | // o. Set onFulfilled.[[Index]] to index. |
149 | | // p. Set onFulfilled.[[Values]] to values. |
150 | | // q. Set onFulfilled.[[Capability]] to resultCapability. |
151 | | // r. Set onFulfilled.[[RemainingElements]] to remainingElementsCount. |
152 | 0 | auto on_fulfilled = PromiseAllSettledResolveElementFunction::create(realm, index, values, result_capability, remaining_elements_count); |
153 | 0 | on_fulfilled->define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); |
154 | | |
155 | | // s. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions. |
156 | | // t. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.allSettled Reject Element Functions. |
157 | | // u. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »). |
158 | | // v. Set onRejected.[[AlreadyCalled]] to alreadyCalled. |
159 | | // w. Set onRejected.[[Index]] to index. |
160 | | // x. Set onRejected.[[Values]] to values. |
161 | | // y. Set onRejected.[[Capability]] to resultCapability. |
162 | | // z. Set onRejected.[[RemainingElements]] to remainingElementsCount. |
163 | 0 | auto on_rejected = PromiseAllSettledRejectElementFunction::create(realm, index, values, result_capability, remaining_elements_count); |
164 | 0 | on_rejected->define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); |
165 | | |
166 | | // ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). |
167 | 0 | return next_promise.invoke(vm, vm.names.then, on_fulfilled, on_rejected); |
168 | 0 | }); |
169 | 0 | } |
170 | | |
171 | | // 27.2.4.3.1 PerformPromiseAny ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiseany |
172 | | static ThrowCompletionOr<Value> perform_promise_any(VM& vm, IteratorRecord& iterator_record, Value constructor, PromiseCapability& result_capability, Value promise_resolve) |
173 | 0 | { |
174 | 0 | auto& realm = *vm.current_realm(); |
175 | |
|
176 | 0 | return perform_promise_common( |
177 | 0 | vm, iterator_record, constructor, result_capability, promise_resolve, |
178 | 0 | [&](PromiseValueList& errors) -> ThrowCompletionOr<Value> { |
179 | | // 1. Let error be a newly created AggregateError object. |
180 | 0 | auto error = AggregateError::create(realm); |
181 | | |
182 | | // 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: CreateArrayFromList(errors) }). |
183 | 0 | auto errors_array = Array::create_from(realm, errors.values()); |
184 | 0 | MUST(error->define_property_or_throw(vm.names.errors, { .value = errors_array, .writable = true, .enumerable = false, .configurable = true })); |
185 | | |
186 | | // 3. Return ThrowCompletion(error). |
187 | 0 | return throw_completion(error); |
188 | 0 | }, |
189 | 0 | [&](PromiseValueList& errors, RemainingElements& remaining_elements_count, Value next_promise, size_t index) { |
190 | | // j. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions. |
191 | | // k. Let lengthRejected be the number of non-optional parameters of the function definition in Promise.any Reject Element Functions. |
192 | | // l. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »). |
193 | | // m. Set onRejected.[[AlreadyCalled]] to false. |
194 | | // n. Set onRejected.[[Index]] to index. |
195 | | // o. Set onRejected.[[Errors]] to errors. |
196 | | // p. Set onRejected.[[Capability]] to resultCapability. |
197 | | // q. Set onRejected.[[RemainingElements]] to remainingElementsCount. |
198 | 0 | auto on_rejected = PromiseAnyRejectElementFunction::create(realm, index, errors, result_capability, remaining_elements_count); |
199 | 0 | on_rejected->define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); |
200 | | |
201 | | // s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »). |
202 | 0 | return next_promise.invoke(vm, vm.names.then, result_capability.resolve(), on_rejected); |
203 | 0 | }); |
204 | 0 | } |
205 | | |
206 | | // 27.2.4.5.1 PerformPromiseRace ( iteratorRecord, constructor, resultCapability, promiseResolve ), https://tc39.es/ecma262/#sec-performpromiserace |
207 | | static ThrowCompletionOr<Value> perform_promise_race(VM& vm, IteratorRecord& iterator_record, Value constructor, PromiseCapability const& result_capability, Value promise_resolve) |
208 | 0 | { |
209 | 0 | return perform_promise_common( |
210 | 0 | vm, iterator_record, constructor, result_capability, promise_resolve, |
211 | 0 | [&](PromiseValueList&) -> ThrowCompletionOr<Value> { |
212 | | // ii. Return resultCapability.[[Promise]]. |
213 | 0 | return result_capability.promise(); |
214 | 0 | }, |
215 | 0 | [&](PromiseValueList&, RemainingElements&, Value next_promise, size_t) { |
216 | | // i. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »). |
217 | 0 | return next_promise.invoke(vm, vm.names.then, result_capability.resolve(), result_capability.reject()); |
218 | 0 | }); |
219 | 0 | } |
220 | | |
221 | | PromiseConstructor::PromiseConstructor(Realm& realm) |
222 | 0 | : NativeFunction(realm.vm().names.Promise.as_string(), realm.intrinsics().function_prototype()) |
223 | 0 | { |
224 | 0 | } |
225 | | |
226 | | void PromiseConstructor::initialize(Realm& realm) |
227 | 0 | { |
228 | 0 | auto& vm = this->vm(); |
229 | 0 | Base::initialize(realm); |
230 | | |
231 | | // 27.2.4.4 Promise.prototype, https://tc39.es/ecma262/#sec-promise.prototype |
232 | 0 | define_direct_property(vm.names.prototype, realm.intrinsics().promise_prototype(), 0); |
233 | |
|
234 | 0 | u8 attr = Attribute::Writable | Attribute::Configurable; |
235 | 0 | define_native_function(realm, vm.names.all, all, 1, attr); |
236 | 0 | define_native_function(realm, vm.names.allSettled, all_settled, 1, attr); |
237 | 0 | define_native_function(realm, vm.names.any, any, 1, attr); |
238 | 0 | define_native_function(realm, vm.names.race, race, 1, attr); |
239 | 0 | define_native_function(realm, vm.names.reject, reject, 1, attr); |
240 | 0 | define_native_function(realm, vm.names.resolve, resolve, 1, attr); |
241 | 0 | define_native_function(realm, vm.names.try_, try_, 1, attr); |
242 | 0 | define_native_function(realm, vm.names.withResolvers, with_resolvers, 0, attr); |
243 | |
|
244 | 0 | define_native_accessor(realm, vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable); |
245 | |
|
246 | 0 | define_direct_property(vm.names.length, Value(1), Attribute::Configurable); |
247 | 0 | } |
248 | | |
249 | | // 27.2.3.1 Promise ( executor ), https://tc39.es/ecma262/#sec-promise-executor |
250 | | ThrowCompletionOr<Value> PromiseConstructor::call() |
251 | 0 | { |
252 | 0 | auto& vm = this->vm(); |
253 | | |
254 | | // 1. If NewTarget is undefined, throw a TypeError exception. |
255 | 0 | return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, vm.names.Promise); |
256 | 0 | } |
257 | | |
258 | | // 27.2.3.1 Promise ( executor ), https://tc39.es/ecma262/#sec-promise-executor |
259 | | ThrowCompletionOr<NonnullGCPtr<Object>> PromiseConstructor::construct(FunctionObject& new_target) |
260 | 0 | { |
261 | 0 | auto& vm = this->vm(); |
262 | |
|
263 | 0 | auto executor = vm.argument(0); |
264 | | |
265 | | // 2. If IsCallable(executor) is false, throw a TypeError exception. |
266 | 0 | if (!executor.is_function()) |
267 | 0 | return vm.throw_completion<TypeError>(ErrorType::PromiseExecutorNotAFunction); |
268 | | |
269 | | // 3. Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%Promise.prototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »). |
270 | | // 4. Set promise.[[PromiseState]] to pending. |
271 | | // 5. Set promise.[[PromiseFulfillReactions]] to a new empty List. |
272 | | // 6. Set promise.[[PromiseRejectReactions]] to a new empty List. |
273 | | // 7. Set promise.[[PromiseIsHandled]] to false. |
274 | 0 | auto promise = TRY(ordinary_create_from_constructor<Promise>(vm, new_target, &Intrinsics::promise_prototype)); |
275 | | |
276 | | // 8. Let resolvingFunctions be CreateResolvingFunctions(promise). |
277 | 0 | auto [resolve_function, reject_function] = promise->create_resolving_functions(); |
278 | | |
279 | | // 9. Let completion be Completion(Call(executor, undefined, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »)). |
280 | 0 | auto completion = JS::call(vm, executor.as_function(), js_undefined(), resolve_function, reject_function); |
281 | | |
282 | | // 10. If completion is an abrupt completion, then |
283 | 0 | if (completion.is_error()) { |
284 | | // a. Perform ? Call(resolvingFunctions.[[Reject]], undefined, « completion.[[Value]] »). |
285 | 0 | TRY(JS::call(vm, *reject_function, js_undefined(), *completion.release_error().value())); |
286 | 0 | } |
287 | | |
288 | | // 11. Return promise. |
289 | 0 | return promise; |
290 | 0 | } |
291 | | |
292 | | // 27.2.4.1 Promise.all ( iterable ), https://tc39.es/ecma262/#sec-promise.all |
293 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::all) |
294 | 0 | { |
295 | | // 1. Let C be the this value. |
296 | 0 | auto constructor = TRY(vm.this_value().to_object(vm)); |
297 | | |
298 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
299 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
300 | | |
301 | | // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). |
302 | | // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). |
303 | 0 | auto promise_resolve = TRY_OR_REJECT(vm, promise_capability, get_promise_resolve(vm, constructor)); |
304 | | |
305 | | // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). |
306 | | // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). |
307 | 0 | auto iterator_record = TRY_OR_REJECT(vm, promise_capability, get_iterator(vm, vm.argument(0), IteratorHint::Sync)); |
308 | | |
309 | | // 7. Let result be Completion(PerformPromiseAll(iteratorRecord, C, promiseCapability, promiseResolve)). |
310 | 0 | auto result = perform_promise_all(vm, iterator_record, constructor, promise_capability, promise_resolve); |
311 | | |
312 | | // 8. If result is an abrupt completion, then |
313 | 0 | if (result.is_error()) { |
314 | | // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). |
315 | 0 | if (!iterator_record->done) |
316 | 0 | result = iterator_close(vm, iterator_record, result.release_error()); |
317 | | |
318 | | // b. IfAbruptRejectPromise(result, promiseCapability). |
319 | 0 | TRY_OR_REJECT(vm, promise_capability, result); |
320 | 0 | } |
321 | | |
322 | | // 9. Return ? result. |
323 | 0 | return result; |
324 | 0 | } |
325 | | |
326 | | // 27.2.4.2 Promise.allSettled ( iterable ), https://tc39.es/ecma262/#sec-promise.allsettled |
327 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::all_settled) |
328 | 0 | { |
329 | | // 1. Let C be the this value. |
330 | 0 | auto constructor = TRY(vm.this_value().to_object(vm)); |
331 | | |
332 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
333 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
334 | | |
335 | | // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). |
336 | | // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). |
337 | 0 | auto promise_resolve = TRY_OR_REJECT(vm, promise_capability, get_promise_resolve(vm, constructor)); |
338 | | |
339 | | // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). |
340 | | // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). |
341 | 0 | auto iterator_record = TRY_OR_REJECT(vm, promise_capability, get_iterator(vm, vm.argument(0), IteratorHint::Sync)); |
342 | | |
343 | | // 7. Let result be Completion(PerformPromiseAllSettled(iteratorRecord, C, promiseCapability, promiseResolve)). |
344 | 0 | auto result = perform_promise_all_settled(vm, iterator_record, constructor, promise_capability, promise_resolve); |
345 | | |
346 | | // 8. If result is an abrupt completion, then |
347 | 0 | if (result.is_error()) { |
348 | | // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). |
349 | 0 | if (!iterator_record->done) |
350 | 0 | result = iterator_close(vm, iterator_record, result.release_error()); |
351 | | |
352 | | // b. IfAbruptRejectPromise(result, promiseCapability). |
353 | 0 | TRY_OR_REJECT(vm, promise_capability, result); |
354 | 0 | } |
355 | | |
356 | | // 9. Return ? result. |
357 | 0 | return result; |
358 | 0 | } |
359 | | |
360 | | // 27.2.4.3 Promise.any ( iterable ), https://tc39.es/ecma262/#sec-promise.any |
361 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::any) |
362 | 0 | { |
363 | | // 1. Let C be the this value. |
364 | 0 | auto constructor = TRY(vm.this_value().to_object(vm)); |
365 | | |
366 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
367 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
368 | | |
369 | | // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). |
370 | | // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). |
371 | 0 | auto promise_resolve = TRY_OR_REJECT(vm, promise_capability, get_promise_resolve(vm, constructor)); |
372 | | |
373 | | // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). |
374 | | // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). |
375 | 0 | auto iterator_record = TRY_OR_REJECT(vm, promise_capability, get_iterator(vm, vm.argument(0), IteratorHint::Sync)); |
376 | | |
377 | | // 7. Let result be Completion(PerformPromiseAny(iteratorRecord, C, promiseCapability, promiseResolve)). |
378 | 0 | auto result = perform_promise_any(vm, iterator_record, constructor, promise_capability, promise_resolve); |
379 | | |
380 | | // 8. If result is an abrupt completion, then |
381 | 0 | if (result.is_error()) { |
382 | | // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). |
383 | 0 | if (!iterator_record->done) |
384 | 0 | result = iterator_close(vm, iterator_record, result.release_error()); |
385 | | |
386 | | // b. IfAbruptRejectPromise(result, promiseCapability). |
387 | 0 | TRY_OR_REJECT(vm, promise_capability, result); |
388 | 0 | } |
389 | | |
390 | | // 9. Return ? result. |
391 | 0 | return result; |
392 | 0 | } |
393 | | |
394 | | // 27.2.4.5 Promise.race ( iterable ), https://tc39.es/ecma262/#sec-promise.race |
395 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::race) |
396 | 0 | { |
397 | | // 1. Let C be the this value. |
398 | 0 | auto constructor = TRY(vm.this_value().to_object(vm)); |
399 | | |
400 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
401 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
402 | | |
403 | | // 3. Let promiseResolve be Completion(GetPromiseResolve(C)). |
404 | | // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). |
405 | 0 | auto promise_resolve = TRY_OR_REJECT(vm, promise_capability, get_promise_resolve(vm, constructor)); |
406 | | |
407 | | // 5. Let iteratorRecord be Completion(GetIterator(iterable, sync)). |
408 | | // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability). |
409 | 0 | auto iterator_record = TRY_OR_REJECT(vm, promise_capability, get_iterator(vm, vm.argument(0), IteratorHint::Sync)); |
410 | | |
411 | | // 7. Let result be Completion(PerformPromiseRace(iteratorRecord, C, promiseCapability, promiseResolve)). |
412 | 0 | auto result = perform_promise_race(vm, iterator_record, constructor, promise_capability, promise_resolve); |
413 | | |
414 | | // 8. If result is an abrupt completion, then |
415 | 0 | if (result.is_error()) { |
416 | | // a. If iteratorRecord.[[Done]] is false, set result to Completion(IteratorClose(iteratorRecord, result)). |
417 | 0 | if (!iterator_record->done) |
418 | 0 | result = iterator_close(vm, iterator_record, result.release_error()); |
419 | | |
420 | | // b. IfAbruptRejectPromise(result, promiseCapability). |
421 | 0 | TRY_OR_REJECT(vm, promise_capability, result); |
422 | 0 | } |
423 | | |
424 | | // 9. Return ? result. |
425 | 0 | return result; |
426 | 0 | } |
427 | | |
428 | | // 27.2.4.6 Promise.reject ( r ), https://tc39.es/ecma262/#sec-promise.reject |
429 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::reject) |
430 | 0 | { |
431 | 0 | auto reason = vm.argument(0); |
432 | | |
433 | | // 1. Let C be the this value. |
434 | 0 | auto constructor = TRY(vm.this_value().to_object(vm)); |
435 | | |
436 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
437 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
438 | | |
439 | | // 3. Perform ? Call(promiseCapability.[[Reject]], undefined, « r »). |
440 | 0 | [[maybe_unused]] auto result = TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), reason)); |
441 | | |
442 | | // 4. Return promiseCapability.[[Promise]]. |
443 | 0 | return promise_capability->promise(); |
444 | 0 | } |
445 | | |
446 | | // 27.2.4.7 Promise.resolve ( x ), https://tc39.es/ecma262/#sec-promise.resolve |
447 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::resolve) |
448 | 0 | { |
449 | 0 | auto value = vm.argument(0); |
450 | | |
451 | | // 1. Let C be the this value. |
452 | 0 | auto constructor = vm.this_value(); |
453 | | |
454 | | // 2. If Type(C) is not Object, throw a TypeError exception. |
455 | 0 | if (!constructor.is_object()) |
456 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, constructor.to_string_without_side_effects()); |
457 | | |
458 | | // 3. Return ? PromiseResolve(C, x). |
459 | 0 | return TRY(promise_resolve(vm, constructor.as_object(), value)); |
460 | 0 | } |
461 | | |
462 | | // 27.2.4.8 Promise.try ( callback, ...args ), https://tc39.es/ecma262/#sec-promise.try |
463 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::try_) |
464 | 0 | { |
465 | 0 | auto callback = vm.argument(0); |
466 | 0 | Span<Value> args; |
467 | 0 | if (vm.argument_count() > 1) { |
468 | 0 | args = vm.running_execution_context().arguments.span().slice(1, vm.argument_count() - 1); |
469 | 0 | } |
470 | | |
471 | | // 1. Let C be the this value. |
472 | 0 | auto constructor = vm.this_value(); |
473 | | |
474 | | // 2. If C is not an Object, throw a TypeError exception. |
475 | 0 | if (!constructor.is_object()) |
476 | 0 | return vm.throw_completion<TypeError>(ErrorType::NotAnObject, constructor.to_string_without_side_effects()); |
477 | | |
478 | | // 3. Let promiseCapability be ? NewPromiseCapability(C). |
479 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
480 | | |
481 | | // 4. Let status be Completion(Call(callback, undefined, args)). |
482 | 0 | auto status = JS::call(vm, callback, js_undefined(), args); |
483 | | |
484 | | // 5. If status is an abrupt completion, then |
485 | 0 | if (status.is_throw_completion()) { |
486 | | // a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »). |
487 | 0 | TRY(JS::call(vm, *promise_capability->reject(), js_undefined(), *status.throw_completion().value())); |
488 | 0 | } |
489 | | // 6. Else, |
490 | 0 | else { |
491 | | // a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »). |
492 | 0 | TRY(JS::call(vm, *promise_capability->resolve(), js_undefined(), status.value())); |
493 | 0 | } |
494 | | |
495 | | // 7. Return promiseCapability.[[Promise]]. |
496 | 0 | return promise_capability->promise(); |
497 | 0 | } |
498 | | |
499 | | // 27.2.4.9 Promise.withResolvers ( ), https://tc39.es/ecma262/#sec-promise.withResolvers |
500 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::with_resolvers) |
501 | 0 | { |
502 | 0 | auto& realm = *vm.current_realm(); |
503 | | |
504 | | // 1. Let C be the this value. |
505 | 0 | auto constructor = vm.this_value(); |
506 | | |
507 | | // 2. Let promiseCapability be ? NewPromiseCapability(C). |
508 | 0 | auto promise_capability = TRY(new_promise_capability(vm, constructor)); |
509 | | |
510 | | // 3. Let obj be OrdinaryObjectCreate(%Object.prototype%). |
511 | 0 | auto object = Object::create(realm, realm.intrinsics().object_prototype()); |
512 | | |
513 | | // 4. Perform ! CreateDataPropertyOrThrow(obj, "promise", promiseCapability.[[Promise]]). |
514 | 0 | MUST(object->create_data_property_or_throw(vm.names.promise, promise_capability->promise())); |
515 | | |
516 | | // 5. Perform ! CreateDataPropertyOrThrow(obj, "resolve", promiseCapability.[[Resolve]]). |
517 | 0 | MUST(object->create_data_property_or_throw(vm.names.resolve, promise_capability->resolve())); |
518 | | |
519 | | // 6. Perform ! CreateDataPropertyOrThrow(obj, "reject", promiseCapability.[[Reject]]). |
520 | 0 | MUST(object->create_data_property_or_throw(vm.names.reject, promise_capability->reject())); |
521 | | |
522 | | // 7. Return obj. |
523 | 0 | return object; |
524 | 0 | } |
525 | | |
526 | | // 27.2.4.10 get Promise [ @@species ], https://tc39.es/ecma262/#sec-get-promise-@@species |
527 | | JS_DEFINE_NATIVE_FUNCTION(PromiseConstructor::symbol_species_getter) |
528 | 0 | { |
529 | | // 1. Return the this value. |
530 | 0 | return vm.this_value(); |
531 | 0 | } |
532 | | |
533 | | } |