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