1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_promise.h"
17 #include "ecmascript/builtins/builtins_promise_handler.h"
18 #include "ecmascript/builtins/builtins_promise_job.h"
19 #include "ecmascript/debugger/js_debugger_manager.h"
20 #include "ecmascript/dfx/stackinfo/async_stack_trace.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/interpreter/interpreter.h"
23 #include "ecmascript/jobs/micro_job_queue.h"
24 #include "ecmascript/js_array.h"
25 #include "ecmascript/js_iterator.h"
26
27 namespace panda::ecmascript::builtins {
28 using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob;
29 // 25.4.3.1 Promise ( executor )
PromiseConstructor(EcmaRuntimeCallInfo * argv)30 JSTaggedValue BuiltinsPromise::PromiseConstructor(EcmaRuntimeCallInfo *argv)
31 {
32 ASSERT(argv);
33 BUILTINS_API_TRACE(argv->GetThread(), Promise, Constructor);
34 JSThread *thread = argv->GetThread();
35 [[maybe_unused]] EcmaHandleScope handleScope(thread);
36 EcmaVM *ecmaVm = thread->GetEcmaVM();
37 ObjectFactory *factory = ecmaVm->GetFactory();
38 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
39 // 1. If NewTarget is undefined, throw a TypeError exception.
40 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
41 if (newTarget->IsUndefined()) {
42 THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception());
43 }
44 // 2. If IsCallable(executor) is false, throw a TypeError exception.
45 JSHandle<JSTaggedValue> executor = BuiltinsBase::GetCallArg(argv, 0);
46 if (!executor->IsCallable()) {
47 THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception());
48 }
49
50 // 3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%",
51 // «[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ).
52 // 4. ReturnIfAbrupt(promise).
53 JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
54 JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
55 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
56 JSHandle<JSPromise> instancePromise = JSHandle<JSPromise>::Cast(newObject);
57
58 // 5. Set promise's [[PromiseState]] internal slot to "pending".
59 // 6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List.
60 // 7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List.
61 // 8. Let resolvingFunctions be CreateResolvingFunctions(promise).
62 JSHandle<ResolvingFunctionsRecord> resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise);
63 // 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]])
64 auto resolveFunc = resolvingFunction->GetResolveFunction(thread);
65 auto rejectFunc = resolvingFunction->GetRejectFunction(thread);
66 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
67 const uint32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»
68 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, executor, undefined, undefined, argsLength);
69 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
70 info->SetCallArg(resolveFunc, rejectFunc);
71 JSTaggedValue taggedValue = JSFunction::Call(info);
72 JSHandle<JSTaggedValue> completionValue(thread, taggedValue);
73
74 // 10. If completion is an abrupt completion, then
75 // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»).
76 // b. ReturnIfAbrupt(status).
77 if (thread->HasPendingException()) {
78 completionValue = JSPromise::IfThrowGetThrowValue(thread);
79 thread->ClearException();
80 JSHandle<JSTaggedValue> reject(thread, resolvingFunction->GetRejectFunction(thread));
81 EcmaRuntimeCallInfo *runtimeInfo =
82 EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
83 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
84 runtimeInfo->SetCallArg(completionValue.GetTaggedValue());
85 JSFunction::Call(runtimeInfo);
86 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
87 }
88
89 // 11. Return promise.
90 return instancePromise.GetTaggedValue();
91 }
92
93 // 25.4.4.1 Promise.all ( iterable )
All(EcmaRuntimeCallInfo * argv)94 JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv)
95 {
96 ASSERT(argv);
97 BUILTINS_API_TRACE(argv->GetThread(), Promise, All);
98 JSThread *thread = argv->GetThread();
99 [[maybe_unused]] EcmaHandleScope handleScope(thread);
100 EcmaVM *ecmaVm = thread->GetEcmaVM();
101 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
102 ObjectFactory *factory = ecmaVm->GetFactory();
103
104 // 1. Let C be the this value.
105 JSHandle<JSTaggedValue> ctor = GetThis(argv);
106 // 2. If Type(C) is not Object, throw a TypeError exception.
107 if (!ctor->IsECMAObject()) {
108 THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception());
109 }
110 // 3. Let S be Get(C, @@species).
111 // 4. ReturnIfAbrupt(S).
112 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
113 JSHandle<JSTaggedValue> sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue();
114 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue());
115
116 // 5. If S is neither undefined nor null, let C be S.
117 if (!sctor->IsUndefined() && !sctor->IsNull()) {
118 ctor = sctor;
119 }
120 // 6. Let promiseCapability be NewPromiseCapability(C).
121 JSHandle<PromiseCapability> capa = JSPromise::NewPromiseCapability(thread, ctor);
122 // 7. ReturnIfAbrupt(promiseCapability).
123 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue());
124 // 8. Let iterator be GetIterator(iterable).
125 JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0));
126 // 9. IfAbruptRejectPromise(iterator, promiseCapability).
127 if (thread->HasPendingException()) {
128 itor = JSPromise::IfThrowGetThrowValue(thread);
129 }
130 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa);
131
132 // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
133 bool done = false;
134 JSHandle<PromiseIteratorRecord> itRecord = factory->NewPromiseIteratorRecord(itor, done);
135 // 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
136 JSTaggedValue resultValue = PerformPromiseAll(thread, itRecord, ctor, capa);
137 JSHandle<CompletionRecord> result = JSHandle<CompletionRecord>(thread, resultValue);
138 // 12. If result is an abrupt completion,
139 if (result->IsThrow()) {
140 thread->ClearException();
141 // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator, result).
142 // b. IfAbruptRejectPromise(result, promiseCapability).
143 if (!itRecord->GetDone()) {
144 JSHandle<JSTaggedValue> closeVal =
145 JSIterator::IteratorClose(thread, itor, JSHandle<JSTaggedValue>::Cast(result));
146 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
147 if (closeVal.GetTaggedValue().IsRecord()) {
148 result = JSHandle<CompletionRecord>::Cast(closeVal);
149 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
150 return result->GetValue(thread);
151 }
152 }
153 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
154 return result->GetValue(thread);
155 }
156 // 13. Return Completion(result).
157 return result->GetValue(thread);
158 }
159
160 // 25.4.4.3 Promise.race ( iterable )
Race(EcmaRuntimeCallInfo * argv)161 JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv)
162 {
163 ASSERT(argv);
164 BUILTINS_API_TRACE(argv->GetThread(), Promise, Race);
165 JSThread *thread = argv->GetThread();
166 [[maybe_unused]] EcmaHandleScope handleScope(thread);
167 EcmaVM *ecmaVm = thread->GetEcmaVM();
168 ObjectFactory *factory = ecmaVm->GetFactory();
169 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
170 // 1. Let C be the this value.
171 // 2. If Type(C) is not Object, throw a TypeError exception.
172 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
173 if (!thisValue->IsECMAObject()) {
174 THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception());
175 }
176 // 3. Let S be Get(C, @@species).
177 // 4. ReturnIfAbrupt(S).
178 // 5. If S is neither undefined nor null, let C be S.
179 JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
180 JSHandle<JSTaggedValue> speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue();
181 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
182 if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) {
183 thisValue = speciesConstructor;
184 }
185
186 // 6. Let promiseCapability be NewPromiseCapability(C).
187 // 7. ReturnIfAbrupt(promiseCapability).
188 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
189 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
190
191 // 8. Let iterator be GetIterator(iterable).
192 // 9. IfAbruptRejectPromise(iterator, promiseCapability).
193 JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
194 JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
195 if (thread->HasPendingException()) {
196 iterator = JSPromise::IfThrowGetThrowValue(thread);
197 }
198 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
199
200 // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
201 bool done = false;
202 JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done);
203
204 // 11. Let result be PerformPromiseRace(iteratorRecord, promiseCapability, C).
205 // 12. If result is an abrupt completion, then
206 // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator,result).
207 // b. IfAbruptRejectPromise(result, promiseCapability).
208 // 13. Return Completion(result).
209 JSHandle<CompletionRecord> result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue);
210 if (result->IsThrow()) {
211 thread->ClearException();
212 if (!iteratorRecord->GetDone()) {
213 JSHandle<JSTaggedValue> value =
214 JSIterator::IteratorClose(thread, iterator, JSHandle<JSTaggedValue>::Cast(result));
215 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
216 if (value.GetTaggedValue().IsCompletionRecord()) {
217 result = JSHandle<CompletionRecord>(value);
218 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
219 return result->GetValue(thread);
220 }
221 }
222 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
223 return result->GetValue(thread);
224 }
225 return result->GetValue(thread);
226 }
227
228 // 25.4.4.5 Promise.resolve ( x )
Resolve(EcmaRuntimeCallInfo * argv)229 JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv)
230 {
231 ASSERT(argv);
232 BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve);
233 JSThread *thread = argv->GetThread();
234 [[maybe_unused]] EcmaHandleScope handleScope(thread);
235
236 // 1. Let C be the this value.
237 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
238 // 2. If Type(C) is not Object, throw a TypeError exception.
239 if (!thisValue->IsECMAObject()) {
240 THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception());
241 }
242 JSHandle<JSTaggedValue> xValue = BuiltinsBase::GetCallArg(argv, 0);
243 return BuiltinsPromiseHandler::PromiseResolve(thread, thisValue, xValue).GetTaggedValue();
244 }
245
246 // 25.4.4.4 Promise.reject ( r )
Reject(EcmaRuntimeCallInfo * argv)247 JSTaggedValue BuiltinsPromise::Reject(EcmaRuntimeCallInfo *argv)
248 {
249 ASSERT(argv);
250 BUILTINS_API_TRACE(argv->GetThread(), Promise, Reject);
251 JSThread *thread = argv->GetThread();
252 [[maybe_unused]] EcmaHandleScope handleScope(thread);
253 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
254
255 // 1. Let C be the this value.
256 // 2. If Type(C) is not Object, throw a TypeError exception.
257 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
258 if (!thisValue->IsECMAObject()) {
259 THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
260 }
261
262 // 3. Let promiseCapability be NewPromiseCapability(C).
263 // 4. ReturnIfAbrupt(promiseCapability).
264 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
265 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
266
267 // 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»).
268 // 6. ReturnIfAbrupt(rejectResult).
269 JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
270 JSHandle<JSTaggedValue> reject(thread, promiseCapability->GetReject(thread));
271 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
272 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
273 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
274 info->SetCallArg(reason.GetTaggedValue());
275 JSFunction::Call(info);
276 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
277
278 // 7. Return promiseCapability.[[Promise]].
279 JSHandle<JSObject> promise(thread, promiseCapability->GetPromise(thread));
280 return promise.GetTaggedValue();
281 }
282
283 // 25.4.4.6 get Promise [ @@species ]
GetSpecies(EcmaRuntimeCallInfo * argv)284 JSTaggedValue BuiltinsPromise::GetSpecies(EcmaRuntimeCallInfo *argv)
285 {
286 ASSERT(argv);
287 BUILTINS_API_TRACE(argv->GetThread(), Promise, GetSpecies);
288 return JSTaggedValue(GetThis(argv).GetTaggedValue());
289 }
290
291 // 25.4.5.1 Promise.prototype.catch ( onRejected )
Catch(EcmaRuntimeCallInfo * argv)292 JSTaggedValue BuiltinsPromise::Catch(EcmaRuntimeCallInfo *argv)
293 {
294 // 1. Let promise be the this value.
295 // 2. Return Invoke(promise, "then", «undefined, onRejected»).
296 ASSERT(argv);
297 BUILTINS_API_TRACE(argv->GetThread(), Promise, Catch);
298 JSThread *thread = argv->GetThread();
299 [[maybe_unused]] EcmaHandleScope handleScope(thread);
300 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
301 JSHandle<JSTaggedValue> promise = GetThis(argv);
302 JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
303 JSHandle<JSTaggedValue> reject = GetCallArg(argv, 0);
304 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
305 EcmaRuntimeCallInfo *info =
306 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: «undefined, onRejected»
307 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
308 info->SetCallArg(undefined.GetTaggedValue(), reject.GetTaggedValue());
309 return JSFunction::Invoke(info, thenKey);
310 }
311
312 // 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected )
Then(EcmaRuntimeCallInfo * argv)313 JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv)
314 {
315 ASSERT(argv);
316 BUILTINS_API_TRACE(argv->GetThread(), Promise, Then);
317 JSThread *thread = argv->GetThread();
318 [[maybe_unused]] EcmaHandleScope handleScope(thread);
319 auto ecmaVm = thread->GetEcmaVM();
320 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
321
322 // 1. Let promise be the this value.
323 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
324 // 2. If IsPromise(promise) is false, throw a TypeError exception.
325 if (!thisValue->IsJSPromise()) {
326 THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception());
327 }
328 // 3. Let C be SpeciesConstructor(promise, %Promise%).
329 // 4. ReturnIfAbrupt(C).
330 JSHandle<JSObject> promise = JSHandle<JSObject>::Cast(thisValue);
331 JSHandle<JSTaggedValue> defaultFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
332 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc);
333 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
334
335 // 5. Let resultCapability be NewPromiseCapability(C).
336 // 6. ReturnIfAbrupt(resultCapability).
337 JSHandle<PromiseCapability> resultCapability = JSPromise::NewPromiseCapability(thread, constructor);
338 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
339
340 JSHandle<JSTaggedValue> onFulfilled = BuiltinsBase::GetCallArg(argv, 0);
341 JSHandle<JSTaggedValue> onRejected = BuiltinsBase::GetCallArg(argv, 1);
342 if (ecmaVm->GetJsDebuggerManager()->IsAsyncStackTrace()) {
343 std::string description;
344 if (onRejected->IsUndefined()) {
345 description = "promise.then";
346 } else if (onFulfilled->IsUndefined()) {
347 description = "promise.catch";
348 } else {
349 description = "promise.finally";
350 }
351 ecmaVm->GetAsyncStackTrace()->InsertAsyncTaskStacks(
352 JSHandle<JSPromise>(thread, resultCapability->GetPromise(thread)), description);
353 }
354
355 // 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
356 return PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise), onFulfilled, onRejected, resultCapability);
357 }
358
PerformPromiseThen(JSThread * thread,const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & onFulfilled,const JSHandle<JSTaggedValue> & onRejected,const JSHandle<PromiseCapability> & capability)359 JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle<JSPromise> &promise,
360 const JSHandle<JSTaggedValue> &onFulfilled,
361 const JSHandle<JSTaggedValue> &onRejected,
362 const JSHandle<PromiseCapability> &capability)
363 {
364 auto ecmaVm = thread->GetEcmaVM();
365 BUILTINS_API_TRACE(thread, Promise, PerformPromiseThen);
366 JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
367 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
368 ObjectFactory *factory = ecmaVm->GetFactory();
369 JSMutableHandle<JSTaggedValue> fulfilled(thread, onFulfilled.GetTaggedValue());
370 auto globalConst = thread->GlobalConstants();
371 if (!fulfilled->IsCallable()) {
372 fulfilled.Update(globalConst->GetIdentityString());
373 }
374 JSMutableHandle<JSTaggedValue> rejected(thread, onRejected.GetTaggedValue());
375 if (!rejected->IsCallable()) {
376 rejected.Update(globalConst->GetThrowerString());
377 }
378 JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
379 fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
380 fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue());
381
382 JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
383 rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
384 rejectReaction->SetHandler(thread, rejected.GetTaggedValue());
385
386 PromiseState state = promise->GetPromiseState();
387 if (state == PromiseState::PENDING) {
388 JSHandle<TaggedQueue> fulfillReactions(thread, promise->GetPromiseFulfillReactions(thread));
389 TaggedQueue *newQueue =
390 TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction));
391 promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue));
392
393 JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions(thread));
394 newQueue = TaggedQueue::Push(thread, rejectReactions, JSHandle<JSTaggedValue>::Cast(rejectReaction));
395 promise->SetPromiseRejectReactions(thread, JSTaggedValue(newQueue));
396 } else if (state == PromiseState::FULFILLED) {
397 JSHandle<TaggedArray> argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array
398 argv->Set(thread, 0, fulfillReaction.GetTaggedValue());
399 argv->Set(thread, 1, promise->GetPromiseResult(thread));
400
401 JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
402 job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
403 } else if (state == PromiseState::REJECTED) {
404 JSHandle<TaggedArray> argv = factory->NewTaggedArray(2); // 2: 2 means two args stored in array
405 argv->Set(thread, 0, rejectReaction.GetTaggedValue());
406 argv->Set(thread, 1, promise->GetPromiseResult(thread));
407 // When a handler is added to a rejected promise for the first time, it is called with its operation
408 // argument set to "handle".
409 if (!promise->GetPromiseIsHandled()) {
410 JSHandle<JSTaggedValue> reason(thread, JSTaggedValue::Null());
411 ecmaVm->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::HANDLE);
412 }
413 JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
414 job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
415 }
416 promise->SetPromiseIsHandled(true);
417 return capability->GetPromise(thread);
418 }
419
PerformPromiseAll(JSThread * thread,const JSHandle<PromiseIteratorRecord> & itRecord,const JSHandle<JSTaggedValue> & ctor,const JSHandle<PromiseCapability> & capa)420 JSTaggedValue BuiltinsPromise::PerformPromiseAll(JSThread *thread,
421 const JSHandle<PromiseIteratorRecord> &itRecord,
422 const JSHandle<JSTaggedValue> &ctor,
423 const JSHandle<PromiseCapability> &capa)
424 {
425 auto ecmaVm = thread->GetEcmaVM();
426 BUILTINS_API_TRACE(thread, Promise, PerformPromiseAll);
427 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
428 ObjectFactory *factory = ecmaVm->GetFactory();
429 // 1. Assert: constructor is a constructor function.
430 ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor");
431 // 2. Assert: resultCapability is a PromiseCapability record. (not need)
432 // 3. Let values be a new empty List.
433 JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
434 JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
435 values->SetValue(thread, emptyArray);
436 // 4. Let remainingElementsCount be a new Record { [[value]]: 1 }.
437 JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
438 remainCnt->SetValue(thread, JSTaggedNumber(1));
439 // 5. Let index be 0.
440 uint32_t index = 0;
441 // 6. Repeat
442 JSHandle<JSTaggedValue> itor(thread, itRecord->GetIterator(thread));
443 JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
444 while (true) {
445 [[maybe_unused]] EcmaHandleScope handleScope(thread);
446 // a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
447 next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
448 // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
449 if (thread->HasPendingException()) {
450 itRecord->SetDone(true);
451 next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
452 }
453 // c. ReturnIfAbrupt(next).
454 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, next);
455 // d. If next is false,
456 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
457 if (next->IsFalse()) {
458 // i. Set iteratorRecord.[[done]] to true.
459 itRecord->SetDone(true);
460 // ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1.
461 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
462 // iii. If remainingElementsCount.[[value]] is 0,
463 if (remainCnt->GetValue(thread).IsZero()) {
464 // 1. Let valuesArray be CreateArrayFromList(values).
465 JSHandle<JSArray> jsArrayValues =
466 JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, values->GetValue(thread)));
467 // 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»).
468 JSHandle<JSTaggedValue> resCapaFunc(thread, capa->GetResolve(thread));
469 EcmaRuntimeCallInfo *info =
470 EcmaInterpreter::NewRuntimeCallInfo(thread, resCapaFunc, undefined, undefined, 1);
471 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, next);
472 info->SetCallArg(jsArrayValues.GetTaggedValue());
473 JSTaggedValue resolveRes = JSFunction::Call(info);
474 // 3. ReturnIfAbrupt(resolveResult)
475 JSHandle<JSTaggedValue> resolveAbrupt(thread, resolveRes);
476 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, resolveAbrupt);
477 }
478 // iv. Return resultCapability.[[Promise]].
479 JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
480 CompletionRecordType::NORMAL, JSHandle<JSTaggedValue>(thread, capa->GetPromise(thread)));
481 return resRecord.GetTaggedValue();
482 }
483 // e. Let nextValue be IteratorValue(next).
484 JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
485 // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
486 if (thread->HasPendingException()) {
487 itRecord->SetDone(true);
488 nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
489 }
490
491 // g. ReturnIfAbrupt(nextValue).
492 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextVal);
493 // h. Append undefined to values.
494 JSHandle<TaggedArray> valuesArray =
495 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue(thread)));
496 valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
497 valuesArray->Set(thread, index, JSTaggedValue::Undefined());
498 values->SetValue(thread, valuesArray);
499 // i. Let nextPromise be Invoke(constructor, "resolve", «nextValue»).
500 JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
501 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, ctor, undefined, 1);
502 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextVal);
503 info->SetCallArg(nextVal.GetTaggedValue());
504 JSTaggedValue taggedNextPromise = JSFunction::Invoke(info, resolveKey);
505 // j. ReturnIfAbrupt(nextPromise).
506 JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
507 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextPromise);
508 // k. Let resolveElement be a new built-in function object as defined in Promise.all
509 // Resolve Element Functions.
510 JSHandle<JSPromiseAllResolveElementFunction> resoleveElement = factory->NewJSPromiseAllResolveElementFunction();
511 // l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }.
512 JSHandle<PromiseRecord> falseRecord = factory->NewPromiseRecord();
513 falseRecord->SetValue(thread, JSTaggedValue::False());
514 resoleveElement->SetAlreadyCalled(thread, falseRecord);
515 // m. Set the [[Index]] internal slot of resolveElement to index.
516 resoleveElement->SetIndex(thread, JSTaggedValue(index));
517 // n. Set the [[Values]] internal slot of resolveElement to values.
518 resoleveElement->SetValues(thread, values);
519 // o. Set the [[Capabilities]] internal slot of resolveElement to resultCapability.
520 resoleveElement->SetCapabilities(thread, capa);
521 // p. Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount.
522 resoleveElement->SetRemainingElements(thread, remainCnt);
523 // q. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1.
524 remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
525 // r. Let result be Invoke(nextPromise, "then", «resolveElement, resultCapability.[[Reject]]»).
526 JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
527 EcmaRuntimeCallInfo *runtimeInfo =
528 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise,
529 undefined, 2); // 2: «resolveElement, resultCapability.[[Reject]]»
530 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextPromise);
531 runtimeInfo->SetCallArg(resoleveElement.GetTaggedValue(), capa->GetReject(thread));
532 JSTaggedValue taggedResult = JSFunction::Invoke(runtimeInfo, thenKey);
533 JSHandle<JSTaggedValue> result(thread, taggedResult);
534 // s. ReturnIfAbrupt(result).
535 RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, result);
536 // t. Set index to index + 1.
537 ++index;
538 }
539 }
540
PerformPromiseRace(JSThread * thread,const JSHandle<PromiseIteratorRecord> & iteratorRecord,const JSHandle<PromiseCapability> & capability,const JSHandle<JSTaggedValue> & constructor)541 JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseRace(JSThread *thread,
542 const JSHandle<PromiseIteratorRecord> &iteratorRecord,
543 const JSHandle<PromiseCapability> &capability,
544 const JSHandle<JSTaggedValue> &constructor)
545 {
546 // 1. Repeat
547 // a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
548 // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
549 // c. ReturnIfAbrupt(next).
550 // d. If next is false, then
551 // i. Set iteratorRecord.[[done]] to true.
552 // ii. Return promiseCapability.[[Promise]].
553 // e. Let nextValue be IteratorValue(next).
554 // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
555 // g. ReturnIfAbrupt(nextValue).
556 // h. Let nextPromise be Invoke(C, "resolve", «nextValue»).
557 // i. ReturnIfAbrupt(nextPromise).
558 // j. Let result be Invoke(nextPromise, "then", «promiseCapability.[[Resolve]], promiseCapability.[[Reject]]»).
559 // k. ReturnIfAbrupt(result).
560 BUILTINS_API_TRACE(thread, Promise, PerformPromiseRace);
561 auto ecmaVm = thread->GetEcmaVM();
562 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
563 ObjectFactory *factory = ecmaVm->GetFactory();
564 JSHandle<JSTaggedValue> iterator(thread, iteratorRecord->GetIterator(thread));
565 JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
566 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
567 while (true) {
568 next.Update(JSIterator::IteratorStep(thread, iterator).GetTaggedValue());
569 if (thread->HasPendingException()) {
570 iteratorRecord->SetDone(true);
571 next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
572 }
573 RETURN_COMPLETION_IF_ABRUPT(thread, next);
574 if (next->IsFalse()) {
575 iteratorRecord->SetDone(true);
576 JSHandle<JSTaggedValue> promise(thread, capability->GetPromise(thread));
577 JSHandle<CompletionRecord> completionRecord =
578 factory->NewCompletionRecord(CompletionRecordType::NORMAL, promise);
579 return completionRecord;
580 }
581 JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
582 if (thread->HasPendingException()) {
583 iteratorRecord->SetDone(true);
584 nextValue = JSPromise::IfThrowGetThrowValue(thread);
585 }
586 RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
587 JSHandle<JSTaggedValue> resolveStr = globalConst->GetHandledPromiseResolveString();
588
589 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, constructor, undefined, 1);
590 RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
591 info->SetCallArg(nextValue.GetTaggedValue());
592 JSTaggedValue result = JSFunction::Invoke(info, resolveStr);
593 JSHandle<JSTaggedValue> nextPromise(thread, result);
594 if (thread->HasPendingException()) {
595 nextPromise = JSPromise::IfThrowGetThrowValue(thread);
596 }
597 RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
598
599 JSHandle<JSTaggedValue> thenStr = globalConst->GetHandledPromiseThenString();
600
601 EcmaRuntimeCallInfo *runtimeInfo =
602 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2); // 2: two args
603 RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
604 runtimeInfo->SetCallArg(capability->GetResolve(thread), capability->GetReject(thread));
605 result = JSFunction::Invoke(runtimeInfo, thenStr);
606 JSHandle<JSTaggedValue> handleResult(thread, result);
607 if (thread->HasPendingException()) {
608 handleResult = JSPromise::IfThrowGetThrowValue(thread);
609 }
610 RETURN_COMPLETION_IF_ABRUPT(thread, handleResult);
611 }
612 }
613
GetPromiseResolve(JSThread * thread,JSHandle<JSTaggedValue> promiseConstructor)614 JSTaggedValue BuiltinsPromise::GetPromiseResolve(JSThread *thread, JSHandle<JSTaggedValue> promiseConstructor)
615 {
616 BUILTINS_API_TRACE(thread, Promise, GetPromiseResolve);
617 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
618 // 1. Let promiseResolve be ? Get(promiseConstructor, "resolve").
619 JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
620 JSHandle<JSTaggedValue> promiseResolve = JSObject::GetProperty(thread, promiseConstructor, resolveKey).GetValue();
621 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
622 // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception.
623 if (!promiseResolve->IsCallable()) {
624 THROW_TYPE_ERROR_AND_RETURN(thread, "promiseResolve is not callable", JSTaggedValue::Exception());
625 }
626 // 3. Return promiseResolve.
627 return promiseResolve.GetTaggedValue();
628 }
629
630 // 27.2.4.3 Promise.any ( iterable )
Any(EcmaRuntimeCallInfo * argv)631 JSTaggedValue BuiltinsPromise::Any(EcmaRuntimeCallInfo *argv)
632 {
633 ASSERT(argv);
634 BUILTINS_API_TRACE(argv->GetThread(), Promise, Any);
635 JSThread *thread = argv->GetThread();
636 auto ecmaVm = thread->GetEcmaVM();
637 ObjectFactory *factory = ecmaVm->GetFactory();
638 // 1. Let C be the this value.
639 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
640 // 2. Let promiseCapability be ? NewPromiseCapability(C).
641 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
642 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
643 // 3. Let promiseResolve be GetPromiseResolve(C).
644 JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
645 // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).
646 if (thread->HasPendingException()) {
647 promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
648 }
649 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
650 // 5. Let iteratorRecord be GetIterator(iterable).
651 JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
652 JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
653 // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
654 if (thread->HasPendingException()) {
655 iterator = JSPromise::IfThrowGetThrowValue(thread);
656 }
657 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
658 // Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
659 JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
660 // 7. Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability, promiseResolve).
661 JSHandle<CompletionRecord> result = PerformPromiseAny(thread, iteratorRecord, thisValue,
662 promiseCapability, promiseResolve);
663 // 8. If result is an abrupt completion, then
664 if (result->IsThrow()) {
665 thread->ClearException();
666 // a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
667 // b. IfAbruptRejectPromise(result, promiseCapability).
668 if (!iteratorRecord->GetDone()) {
669 JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
670 JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
671 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
672 if (closeVal.GetTaggedValue().IsCompletionRecord()) {
673 result = JSHandle<CompletionRecord>(closeVal);
674 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
675 return result->GetValue(thread);
676 }
677 }
678 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
679 return result->GetValue(thread);
680 }
681 // 9. Return ? result.
682 return result->GetValue(thread);
683 }
684
PerformPromiseAny(JSThread * thread,const JSHandle<PromiseIteratorRecord> & iteratorRecord,const JSHandle<JSTaggedValue> & constructor,const JSHandle<PromiseCapability> & resultCapability,const JSHandle<JSTaggedValue> & promiseResolve)685 JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAny(JSThread *thread,
686 const JSHandle<PromiseIteratorRecord> &iteratorRecord,
687 const JSHandle<JSTaggedValue> &constructor,
688 const JSHandle<PromiseCapability> &resultCapability,
689 const JSHandle<JSTaggedValue> &promiseResolve)
690 {
691 BUILTINS_API_TRACE(thread, Promise, PerformPromiseAny);
692 auto ecmaVm = thread->GetEcmaVM();
693 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
694 ObjectFactory *factory = ecmaVm->GetFactory();
695 // 1. Let errors be a new empty List.
696 JSHandle<PromiseRecord> errors = factory->NewPromiseRecord();
697 JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
698 errors->SetValue(thread, emptyArray);
699 // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
700 JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
701 remainCnt->SetValue(thread, JSTaggedNumber(1));
702 // 3. Let index be 0.
703 uint32_t index = 0;
704 // 4. Repeat,
705 JSHandle<JSTaggedValue> iter(thread, iteratorRecord->GetIterator(thread));
706 JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
707 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
708 while (true) {
709 // a. Let next be IteratorStep(iteratorRecord).
710 next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
711 // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
712 if (thread->HasPendingException()) {
713 iteratorRecord->SetDone(true);
714 next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
715 }
716 // c. ReturnIfAbrupt(next).
717 RETURN_COMPLETION_IF_ABRUPT(thread, next);
718 // d. If next is false, then
719 if (next->IsFalse()) {
720 // i. Set iteratorRecord.[[Done]] to true.
721 iteratorRecord->SetDone(true);
722 // ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
723 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
724 // iii. If remainingElementsCount.[[Value]] is 0, then
725 if (remainCnt->GetValue(thread).IsZero()) {
726 // 1. Let error be a newly created AggregateError object.
727 JSHandle<JSObject> error = factory->NewJSAggregateError();
728 // 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true,
729 // [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errors) }).
730 JSHandle<JSTaggedValue> errorsKey(thread, globalConst->GetErrorsString());
731 JSHandle<TaggedArray> errorsArray =
732 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, errors->GetValue(thread)));
733 JSHandle<JSTaggedValue> errorsValue(JSArray::CreateArrayFromList(thread, errorsArray));
734 PropertyDescriptor msgDesc(thread, errorsValue, true, false, true);
735 JSHandle<JSTaggedValue> errorTagged = JSHandle<JSTaggedValue>::Cast(error);
736 JSTaggedValue::DefinePropertyOrThrow(thread, errorTagged, errorsKey, msgDesc);
737 RETURN_HANDLE_IF_ABRUPT_COMPLETION(CompletionRecord, thread);
738 // 3. Return ThrowCompletion(error).
739 JSHandle<JSTaggedValue> errorCompletion(
740 factory->NewCompletionRecord(CompletionRecordType::THROW, errorTagged));
741 JSHandle<CompletionRecord> errorResult = JSHandle<CompletionRecord>::Cast(errorCompletion);
742 return errorResult;
743 }
744 // iv. Return resultCapability.[[Promise]].
745 JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapability->GetPromise(thread));
746 JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
747 CompletionRecordType::NORMAL, resultCapabilityHandle);
748 return resRecord;
749 }
750 // e. Let nextValue be IteratorValue(next).
751 JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
752 // f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
753 if (thread->HasPendingException()) {
754 iteratorRecord->SetDone(true);
755 nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
756 }
757 // g. ReturnIfAbrupt(nextValue).
758 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
759 // h. Append undefined to errors.
760 JSHandle<JSTaggedValue> errorsHandle(thread, errors->GetValue(thread));
761 JSHandle<TaggedArray> errorsArray = JSHandle<TaggedArray>::Cast(errorsHandle);
762 errorsArray = TaggedArray::SetCapacity(thread, errorsArray, index + 1);
763 errorsArray->Set(thread, index, JSTaggedValue::Undefined());
764 errors->SetValue(thread, errorsArray);
765 // i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
766 EcmaRuntimeCallInfo *taggedInfo =
767 EcmaInterpreter::NewRuntimeCallInfo(thread, promiseResolve, constructor, undefined, 1);
768 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
769 taggedInfo->SetCallArg(nextVal.GetTaggedValue());
770 JSTaggedValue taggedNextPromise = JSFunction::Call(taggedInfo);
771 JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
772 if (thread->HasPendingException()) {
773 JSHandle<JSTaggedValue> promiseResult = JSPromise::IfThrowGetThrowValue(thread);
774 JSHandle<CompletionRecord> completionRecord =
775 factory->NewCompletionRecord(CompletionRecordType::THROW, promiseResult);
776 return completionRecord;
777 }
778 // j. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions.
779 // k. Let lengthRejected be the number of non-optional parameters of the function definition in
780 // Promise.any Reject Element Functions.
781 // l. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]],
782 // [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »).
783 JSHandle<JSPromiseAnyRejectElementFunction> onRejected = factory->NewJSPromiseAnyRejectElementFunction();
784 // m. Set onRejected.[[AlreadyCalled]] to false.
785 onRejected->SetAlreadyCalled(thread, JSTaggedValue::False());
786 // n. Set onRejected.[[Index]] to index.
787 onRejected->SetIndex(index);
788 // o. Set onRejected.[[Errors]] to errors.
789 onRejected->SetErrors(thread, errors);
790 // p. Set onRejected.[[Capability]] to resultCapability.
791 onRejected->SetCapability(thread, resultCapability);
792 // q. Set onRejected.[[RemainingElements]] to remainingElementsCount.
793 onRejected->SetRemainingElements(thread, remainCnt);
794 // r. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
795 remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
796 // s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »).
797 JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
798 JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapability->GetResolve(thread));
799 EcmaRuntimeCallInfo *invokeInfo =
800 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2); // 2: two args
801 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
802 invokeInfo->SetCallArg(resCapaFunc.GetTaggedValue(), onRejected.GetTaggedValue());
803 JSFunction::Invoke(invokeInfo, thenKey);
804 if (thread->HasPendingException()) {
805 JSHandle<JSTaggedValue> taggedResult = JSPromise::IfThrowGetThrowValue(thread);
806 JSHandle<CompletionRecord> completionRecord =
807 factory->NewCompletionRecord(CompletionRecordType::THROW, taggedResult);
808 return completionRecord;
809 }
810 // t. Set index to index + 1.
811 ++index;
812 }
813 }
814
815 // 25.6.4.2 Promise.allSettled ( iterable )
AllSettled(EcmaRuntimeCallInfo * argv)816 JSTaggedValue BuiltinsPromise::AllSettled(EcmaRuntimeCallInfo *argv)
817 {
818 ASSERT(argv);
819 BUILTINS_API_TRACE(argv->GetThread(), Promise, AllSettled);
820 JSThread *thread = argv->GetThread();
821 auto ecmaVm = thread->GetEcmaVM();
822 ObjectFactory *factory = ecmaVm->GetFactory();
823 // 1. Let C be the this value.
824 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
825 // 2. Let promiseCapability be ? NewPromiseCapability(C).
826 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
827 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
828 // 3. Let promiseResolve be Completion(GetPromiseResolve(C)).
829 JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
830 // 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).
831 if (thread->HasPendingException()) {
832 promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
833 }
834 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
835 // 5. Let iteratorRecord be Completion(GetIterator(iterable)).
836 JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
837 JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
838 // 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
839 if (thread->HasPendingException()) {
840 iterator = JSPromise::IfThrowGetThrowValue(thread);
841 }
842 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
843 // Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
844 JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
845 // 7. Let result be PerformPromiseAllSettled(iteratorRecord, C, promiseCapability).
846 JSHandle<CompletionRecord> result = PerformPromiseAllSettled(thread, iteratorRecord, thisValue,
847 promiseCapability, promiseResolve);
848 // 8. If result is an abrupt completion, then
849 if (result->IsThrow()) {
850 thread->ClearException();
851 // a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
852 if (!iteratorRecord->GetDone()) {
853 JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
854 JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
855 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
856 if (closeVal.GetTaggedValue().IsCompletionRecord()) {
857 result = JSHandle<CompletionRecord>(closeVal);
858 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
859 return result->GetValue(thread);
860 }
861 }
862 // b. IfAbruptRejectPromise(result, promiseCapability).
863 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
864 return result->GetValue(thread);
865 }
866 // 7.Return Completion(result).
867 return result->GetValue(thread);
868 }
869
PerformPromiseAllSettled(JSThread * thread,const JSHandle<PromiseIteratorRecord> & iterRecord,const JSHandle<JSTaggedValue> & constructor,const JSHandle<PromiseCapability> & resultCapa,const JSHandle<JSTaggedValue> & promiseResolve)870 JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAllSettled(JSThread *thread,
871 const JSHandle<PromiseIteratorRecord> &iterRecord,
872 const JSHandle<JSTaggedValue> &constructor,
873 const JSHandle<PromiseCapability> &resultCapa,
874 const JSHandle<JSTaggedValue> &promiseResolve)
875 {
876 BUILTINS_API_TRACE(thread, Promise, PerformPromiseAllSettled);
877 auto ecmaVm = thread->GetEcmaVM();
878 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
879 ObjectFactory *factory = ecmaVm->GetFactory();
880 // 1. Let values be a new empty List.
881 JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
882 JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
883 values->SetValue(thread, emptyArray);
884 // 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
885 JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
886 remainCnt->SetValue(thread, JSTaggedNumber(1));
887 // 3. Let index be 0.
888 uint32_t index = 0;
889 // 4. Repeat,
890 JSHandle<JSTaggedValue> iter(thread, iterRecord->GetIterator(thread));
891 JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
892 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
893 while (true) {
894 // a. Let next be IteratorStep(iteratorRecord).
895 next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
896 // b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
897 if (thread->HasPendingException()) {
898 iterRecord->SetDone(true);
899 next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
900 }
901 // c. ReturnIfAbrupt(next).
902 RETURN_COMPLETION_IF_ABRUPT(thread, next);
903 // d. If next is false, then
904 if (next->IsFalse()) {
905 // i. Set iteratorRecord.[[Done]] to true.
906 iterRecord->SetDone(true);
907 // ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
908 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
909 // iii. If remainingElementsCount.[[Value]] is 0, then
910 if (remainCnt->GetValue(thread).IsZero()) {
911 // 1. Let valuesArray be ! CreateArrayFromList(values).
912 JSHandle<TaggedArray> taggedValues(thread, values->GetValue(thread));
913 JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, taggedValues);
914 // 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
915 JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapa->GetResolve(thread));
916 EcmaRuntimeCallInfo *info =
917 EcmaInterpreter::NewRuntimeCallInfo(thread, resCapaFunc, undefined, undefined, 1);
918 RETURN_COMPLETION_IF_ABRUPT(thread, next);
919 info->SetCallArg(jsArrayValues.GetTaggedValue());
920 JSFunction::Call(info);
921 if (thread->HasPendingException()) {
922 JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
923 JSHandle<CompletionRecord> completionRecord =
924 factory->NewCompletionRecord(CompletionRecordType::THROW, throwValue);
925 return completionRecord;
926 }
927 }
928 // iv. Return resultCapability.[[Promise]].
929 JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapa->GetPromise(thread));
930 JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
931 CompletionRecordType::NORMAL, resultCapabilityHandle);
932 return resRecord;
933 }
934 // e. Let nextValue be IteratorValue(next).
935 JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
936 // f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
937 if (thread->HasPendingException()) {
938 iterRecord->SetDone(true);
939 nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
940 }
941 // g. ReturnIfAbrupt(nextValue).
942 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
943 // h. Append undefined to values.
944 JSHandle<JSTaggedValue> valuesHandle(thread, values->GetValue(thread));
945 JSHandle<TaggedArray> valuesArray = JSHandle<TaggedArray>::Cast(valuesHandle);
946 valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
947 valuesArray->Set(thread, index, JSTaggedValue::Undefined());
948 values->SetValue(thread, valuesArray);
949 // i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
950 EcmaRuntimeCallInfo *taggedInfo =
951 EcmaInterpreter::NewRuntimeCallInfo(thread, promiseResolve, constructor, undefined, 1);
952 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
953 taggedInfo->SetCallArg(nextVal.GetTaggedValue());
954 JSTaggedValue taggedNextPromise = JSFunction::Call(taggedInfo);
955 JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
956 if (thread->HasPendingException()) {
957 JSHandle<JSTaggedValue> promiseResult = JSPromise::IfThrowGetThrowValue(thread);
958 JSHandle<CompletionRecord> completionRecord =
959 factory->NewCompletionRecord(CompletionRecordType::THROW, promiseResult);
960 return completionRecord;
961 }
962 // j. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions.
963 // k. Let lengthFulfilled be the number of non-optional parameters of the function definition in
964 // Promise.allSettled Resolve Element Functions.
965 // l. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "",
966 // « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
967 JSHandle<JSPromiseAllSettledElementFunction> onFulfilled =
968 factory->NewJSPromiseAllSettledResolveElementFunction();
969 // m. Let alreadyCalled be the Record { [[Value]]: false }.
970 JSHandle<PromiseRecord> alreadyCalled = factory->NewPromiseRecord();
971 alreadyCalled->SetValue(thread, JSTaggedValue::False());
972 // n. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled.
973 onFulfilled->SetAlreadyCalled(thread, alreadyCalled);
974 // o. Set onFulfilled.[[Index]] to index.
975 onFulfilled->SetIndex(index);
976 // p. Set onFulfilled.[[Values]] to values.
977 onFulfilled->SetValues(thread, values);
978 // q. Set onFulfilled.[[Capability]] to resultCapability.
979 onFulfilled->SetCapability(thread, resultCapa);
980 // r. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
981 onFulfilled->SetRemainingElements(thread, remainCnt);
982 // s. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions.
983 // t. Let lengthRejected be the number of non-optional parameters of the function definition in
984 // Promise.allSettled Reject Element Functions.
985 // u. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "",
986 // « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
987 JSHandle<JSPromiseAllSettledElementFunction> onRejected =
988 factory->NewJSPromiseAllSettledRejectElementFunction();
989 // v. Set onRejected.[[AlreadyCalled]] to alreadyCalled.
990 onRejected->SetAlreadyCalled(thread, alreadyCalled);
991 // w. Set onRejected.[[Index]] to index.
992 onRejected->SetIndex(index);
993 // x. Set onRejected.[[Values]] to values.
994 onRejected->SetValues(thread, values);
995 // y. Set onRejected.[[Capability]] to resultCapability.
996 onRejected->SetCapability(thread, resultCapa);
997 // z. Set onRejected.[[RemainingElements]] to remainingElementsCount.
998 onRejected->SetRemainingElements(thread, remainCnt);
999 // aa. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
1000 remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
1001 // ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »).
1002 JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
1003 EcmaRuntimeCallInfo *invokeInfo =
1004 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2); // 2: two args
1005 RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
1006 invokeInfo->SetCallArg(onFulfilled.GetTaggedValue(), onRejected.GetTaggedValue());
1007 JSFunction::Invoke(invokeInfo, thenKey);
1008 if (thread->HasPendingException()) {
1009 JSHandle<JSTaggedValue> taggedResult = JSPromise::IfThrowGetThrowValue(thread);
1010 JSHandle<CompletionRecord> completionRecord =
1011 factory->NewCompletionRecord(CompletionRecordType::THROW, taggedResult);
1012 return completionRecord;
1013 }
1014 // ac. Set index to index + 1.
1015 ++index;
1016 }
1017 }
1018
1019 // 27.2.5.3 Promise.prototype.finally ( onFinally )
Finally(EcmaRuntimeCallInfo * argv)1020 JSTaggedValue BuiltinsPromise::Finally(EcmaRuntimeCallInfo *argv)
1021 {
1022 ASSERT(argv);
1023 BUILTINS_API_TRACE(argv->GetThread(), Promise, Finally);
1024 JSThread *thread = argv->GetThread();
1025 [[maybe_unused]] EcmaHandleScope handleScope(thread);
1026 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1027 auto ecmaVm = thread->GetEcmaVM();
1028 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
1029 ObjectFactory *factory = ecmaVm->GetFactory();
1030 // 1. Let promise be the this value.
1031 // 2. If Type(promise) is not Object, throw a TypeError exception.
1032 JSHandle<JSTaggedValue> promise = GetThis(argv);
1033 if (!promise->IsECMAObject()) {
1034 THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
1035 }
1036 // 3. Let C be SpeciesConstructor(promise, %Promise%).
1037 // 4. Assert: IsConstructor(C) is true.
1038 JSHandle<JSTaggedValue> onFinally = BuiltinsBase::GetCallArg(argv, 0);
1039 JSHandle<JSObject> ctor = JSHandle<JSObject>::Cast(promise);
1040 JSHandle<JSTaggedValue> promiseFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
1041 JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, ctor, promiseFunc);
1042 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1043 ASSERT_PRINT(constructor->IsConstructor(), "constructor is not constructor");
1044 JSHandle<JSTaggedValue> thenFinally;
1045 JSHandle<JSTaggedValue> catchFinally;
1046 // 5. If IsCallable(onFinally) is false, then
1047 if (!onFinally->IsCallable()) {
1048 // a. Let thenFinally be onFinally.
1049 // b. Let catchFinally be onFinally.
1050 thenFinally = onFinally;
1051 catchFinally = onFinally;
1052 // 6. Else,
1053 } else {
1054 // a. Let stepsThenFinally be the algorithm steps defined in Then Finally Functions.
1055 // b. Let thenFinally be CreateBuiltinFunction(stepsThenFinally, « [[Constructor]], [[OnFinally]] »).
1056 JSHandle<JSPromiseFinallyFunction> thenFinallyFun =
1057 factory->NewJSPromiseThenFinallyFunction();
1058 // c. Set thenFinally.[[Constructor]] to C.
1059 // d. Set thenFinally.[[OnFinally]] to onFinally.
1060 thenFinallyFun->SetConstructor(thread, constructor);
1061 thenFinallyFun->SetOnFinally(thread, onFinally);
1062 thenFinally = JSHandle<JSTaggedValue>(thenFinallyFun);
1063 // e. Let stepsCatchFinally be the algorithm steps defined in Catch Finally Functions.
1064 // f. Let catchFinally be CreateBuiltinFunction(stepsCatchFinally, « [[Constructor]], [[OnFinally]] »).
1065 JSHandle<JSPromiseFinallyFunction> catchFinallyFun =
1066 factory->NewJSPromiseCatchFinallyFunction();
1067 // g. Set catchFinally.[[Constructor]] to C.
1068 // h. Set catchFinally.[[OnFinally]] to onFinally.
1069 catchFinallyFun->SetConstructor(thread, constructor);
1070 catchFinallyFun->SetOnFinally(thread, onFinally);
1071 catchFinally = JSHandle<JSTaggedValue>(catchFinallyFun);
1072 }
1073 // 7. return invoke(promise, "then", <<thenFinally, catchFinally>>)
1074 JSHandle<JSTaggedValue> thenKey(globalConst->GetHandledPromiseThenString());
1075 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
1076 EcmaRuntimeCallInfo *invokeInfo =
1077 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: two args
1078 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
1079 invokeInfo->SetCallArg(thenFinally.GetTaggedValue(), catchFinally.GetTaggedValue());
1080 return JSFunction::Invoke(invokeInfo, thenKey);
1081 }
1082 } // namespace panda::ecmascript::builtins
1083