• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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