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