• 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 #include "ecmascript/builtins/builtins_promise_handler.h"
18 #include "ecmascript/builtins/builtins_promise_job.h"
19 #include "ecmascript/ecma_runtime_call_info.h"
20 #include "ecmascript/ecma_vm.h"
21 #include "ecmascript/global_env.h"
22 #include "ecmascript/internal_call_params.h"
23 #include "ecmascript/jobs/micro_job_queue.h"
24 #include "ecmascript/js_array.h"
25 #include "ecmascript/js_function.h"
26 #include "ecmascript/js_handle.h"
27 #include "ecmascript/js_iterator.h"
28 #include "ecmascript/js_promise.h"
29 #include "ecmascript/js_tagged_number.h"
30 #include "ecmascript/js_tagged_value-inl.h"
31 #include "ecmascript/js_thread.h"
32 #include "ecmascript/mem/assert_scope-inl.h"
33 #include "ecmascript/object_factory.h"
34 
35 namespace panda::ecmascript::builtins {
36 using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob;
37 // 25.4.3.1 Promise ( executor )
PromiseConstructor(EcmaRuntimeCallInfo * argv)38 JSTaggedValue BuiltinsPromise::PromiseConstructor([[maybe_unused]] EcmaRuntimeCallInfo *argv)
39 {
40     ASSERT(argv);
41     BUILTINS_API_TRACE(argv->GetThread(), Promise, Constructor);
42     JSThread *thread = argv->GetThread();
43     [[maybe_unused]] EcmaHandleScope handleScope(thread);
44     EcmaVM *ecmaVm = thread->GetEcmaVM();
45     ObjectFactory *factory = ecmaVm->GetFactory();
46     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
47     // 1. If NewTarget is undefined, throw a TypeError exception.
48     JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
49     if (newTarget->IsUndefined()) {
50         THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception());
51     }
52     // 2. If IsCallable(executor) is false, throw a TypeError exception.
53     JSHandle<JSTaggedValue> executor = BuiltinsBase::GetCallArg(argv, 0);
54     if (!executor->IsCallable()) {
55         THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception());
56     }
57 
58     // 3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%",
59     // «[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ).
60     // 4. ReturnIfAbrupt(promise).
61     JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
62     JSHandle<JSPromise> instancePromise =
63         JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget));
64     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
65 
66     // 5. Set promise's [[PromiseState]] internal slot to "pending".
67     // 6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List.
68     // 7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List.
69     // 8. Let resolvingFunctions be CreateResolvingFunctions(promise).
70     JSHandle<ResolvingFunctionsRecord> resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise);
71     // 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]])
72     auto resolveFunc = resolvingFunction->GetResolveFunction();
73     auto rejectFunc = resolvingFunction->GetRejectFunction();
74     InternalCallParams *arguments = thread->GetInternalCallParams();
75     arguments->MakeArgv(resolveFunc, rejectFunc);
76     JSHandle<JSTaggedValue> thisValue = globalConst->GetHandledUndefined();
77     JSTaggedValue taggedValue = JSFunction::Call(thread, executor, thisValue, 2, arguments->GetArgv());  // 2: two args
78     JSHandle<JSTaggedValue> completionValue(thread, taggedValue);
79 
80     // 10. If completion is an abrupt completion, then
81     // a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»).
82     // b. ReturnIfAbrupt(status).
83     if (thread->HasPendingException()) {
84         completionValue = JSPromise::IfThrowGetThrowValue(thread);
85         thread->ClearException();
86         JSHandle<JSTaggedValue> reject(thread, resolvingFunction->GetRejectFunction());
87         arguments->MakeArgv(completionValue);
88         JSFunction::Call(thread, reject, thisValue, 1, arguments->GetArgv());
89         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
90     }
91 
92     // 11. Return promise.
93     return instancePromise.GetTaggedValue();
94 }
95 
96 // 25.4.4.1 Promise.all ( iterable )
All(EcmaRuntimeCallInfo * argv)97 JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv)
98 {
99     ASSERT(argv);
100     BUILTINS_API_TRACE(argv->GetThread(), Promise, All);
101     JSThread *thread = argv->GetThread();
102     [[maybe_unused]] EcmaHandleScope handleScope(thread);
103     EcmaVM *ecmaVm = thread->GetEcmaVM();
104     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
105     ObjectFactory *factory = ecmaVm->GetFactory();
106 
107     // 1. Let C be the this value.
108     JSHandle<JSTaggedValue> ctor = GetThis(argv);
109     // 2. If Type(C) is not Object, throw a TypeError exception.
110     if (!ctor->IsECMAObject()) {
111         THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception());
112     }
113     // 3. Let S be Get(C, @@species).
114     // 4. ReturnIfAbrupt(S).
115     JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
116     JSHandle<JSTaggedValue> sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue();
117     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue());
118 
119     // 5. If S is neither undefined nor null, let C be S.
120     if (!sctor->IsUndefined() && !sctor->IsNull()) {
121         ctor = sctor;
122     }
123     // 6. Let promiseCapability be NewPromiseCapability(C).
124     JSHandle<PromiseCapability> capa = JSPromise::NewPromiseCapability(thread, ctor);
125     // 7. ReturnIfAbrupt(promiseCapability).
126     RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue());
127     // 8. Let iterator be GetIterator(iterable).
128     JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0));
129     // 9. IfAbruptRejectPromise(iterator, promiseCapability).
130     if (thread->HasPendingException()) {
131         itor = JSPromise::IfThrowGetThrowValue(thread);
132     }
133     RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa);
134 
135     // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
136     bool done = false;
137     JSHandle<PromiseIteratorRecord> itRecord = factory->NewPromiseIteratorRecord(itor, done);
138     // 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
139     JSHandle<CompletionRecord> result = PerformPromiseAll(thread, itRecord, ctor, capa);
140     // 12. If result is an abrupt completion,
141     if (result->IsThrow()) {
142         thread->ClearException();
143         // a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator, result).
144         // b. IfAbruptRejectPromise(result, promiseCapability).
145         if (!itRecord->GetDone()) {
146             JSHandle<JSTaggedValue> closeVal =
147                 JSIterator::IteratorClose(thread, itor, JSHandle<JSTaggedValue>::Cast(result));
148             if (closeVal.GetTaggedValue().IsRecord()) {
149                 result = JSHandle<CompletionRecord>::Cast(closeVal);
150                 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
151                 return result->GetValue();
152             }
153         }
154         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
155         return result->GetValue();
156     }
157     // 13. Return Completion(result).
158     return result->GetValue();
159 }
160 
161 // 25.4.4.3 Promise.race ( iterable )
Race(EcmaRuntimeCallInfo * argv)162 JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv)
163 {
164     ASSERT(argv);
165     BUILTINS_API_TRACE(argv->GetThread(), Promise, Race);
166     JSThread *thread = argv->GetThread();
167     [[maybe_unused]] EcmaHandleScope handleScope(thread);
168     EcmaVM *ecmaVm = thread->GetEcmaVM();
169     ObjectFactory *factory = ecmaVm->GetFactory();
170     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
171     // 1. Let C be the this value.
172     // 2. If Type(C) is not Object, throw a TypeError exception.
173     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
174     if (!thisValue->IsECMAObject()) {
175         THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception());
176     }
177     // 3. Let S be Get(C, @@species).
178     // 4. ReturnIfAbrupt(S).
179     // 5. If S is neither undefined nor null, let C be S.
180     JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
181     JSHandle<JSTaggedValue> speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue();
182     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
183     if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) {
184         thisValue = speciesConstructor;
185     }
186 
187     // 6. Let promiseCapability be NewPromiseCapability(C).
188     // 7. ReturnIfAbrupt(promiseCapability).
189     JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
190     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
191 
192     // 8. Let iterator be GetIterator(iterable).
193     // 9. IfAbruptRejectPromise(iterator, promiseCapability).
194     JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
195     JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
196     if (thread->HasPendingException()) {
197         iterator = JSPromise::IfThrowGetThrowValue(thread);
198     }
199     RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
200 
201     // 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
202     bool done = false;
203     JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done);
204 
205     // 11. Let result be PerformPromiseRace(iteratorRecord, promiseCapability, C).
206     // 12. If result is an abrupt completion, then
207     //     a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator,result).
208     //     b. IfAbruptRejectPromise(result, promiseCapability).
209     // 13. Return Completion(result).
210     JSHandle<CompletionRecord> result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue);
211     if (result->IsThrow()) {
212         thread->ClearException();
213         if (!iteratorRecord->GetDone()) {
214             JSHandle<JSTaggedValue> value =
215                 JSIterator::IteratorClose(thread, iterator, JSHandle<JSTaggedValue>::Cast(result));
216             if (value.GetTaggedValue().IsCompletionRecord()) {
217                 result = JSHandle<CompletionRecord>(value);
218                 RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
219                 return result->GetValue();
220             }
221         }
222         RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
223         return result->GetValue();
224     }
225     return result->GetValue();
226 }
227 
228 // 25.4.4.5 Promise.resolve ( x )
Resolve(EcmaRuntimeCallInfo * argv)229 JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv)
230 {
231     ASSERT(argv);
232     BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve);
233     JSThread *thread = argv->GetThread();
234     [[maybe_unused]] EcmaHandleScope handleScope(thread);
235 
236     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
237     // 1. Let C be the this value.
238     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
239     // 2. If Type(C) is not Object, throw a TypeError exception.
240     if (!thisValue->IsECMAObject()) {
241         THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception());
242     }
243     // 3. If IsPromise(x) is true,
244     //     a. Let xConstructor be Get(x, "constructor").
245     //     b. ReturnIfAbrupt(xConstructor).
246     //     c. If SameValue(xConstructor, C) is true, return x.
247     JSHandle<JSTaggedValue> xValue = BuiltinsBase::GetCallArg(argv, 0);
248     if (xValue->IsJSPromise()) {
249         JSHandle<JSTaggedValue> ctorKey(globalConst->GetHandledConstructorString());
250         JSHandle<JSTaggedValue> ctorValue = JSObject::GetProperty(thread, xValue, ctorKey).GetValue();
251         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
252         if (JSTaggedValue::SameValue(ctorValue.GetTaggedValue(), thisValue.GetTaggedValue())) {
253             JSHandle<JSObject> value = JSHandle<JSObject>::Cast(xValue);
254             return value.GetTaggedValue();
255         }
256     }
257     // 4. Let promiseCapability be NewPromiseCapability(C).
258     // 5. ReturnIfAbrupt(promiseCapability).
259     JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
260     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
261 
262     // 6. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»).
263     // 7. ReturnIfAbrupt(resolveResult).
264     JSHandle<JSTaggedValue> resolve(thread, promiseCapability->GetResolve());
265     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
266     InternalCallParams *arguments = thread->GetInternalCallParams();
267     arguments->MakeArgv(xValue);
268     JSFunction::Call(thread, resolve, undefined, 1, arguments->GetArgv());
269     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
270 
271     // 8. Return promiseCapability.[[Promise]].
272     JSHandle<JSObject> promise(thread, promiseCapability->GetPromise());
273     return promise.GetTaggedValue();
274 }
275 
276 // 25.4.4.4 Promise.reject ( r )
Reject(EcmaRuntimeCallInfo * argv)277 JSTaggedValue BuiltinsPromise::Reject(EcmaRuntimeCallInfo *argv)
278 {
279     ASSERT(argv);
280     BUILTINS_API_TRACE(argv->GetThread(), Promise, Reject);
281     JSThread *thread = argv->GetThread();
282     [[maybe_unused]] EcmaHandleScope handleScope(thread);
283     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
284 
285     // 1. Let C be the this value.
286     // 2. If Type(C) is not Object, throw a TypeError exception.
287     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
288     if (!thisValue->IsECMAObject()) {
289         THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
290     }
291 
292     // 3. Let promiseCapability be NewPromiseCapability(C).
293     // 4. ReturnIfAbrupt(promiseCapability).
294     JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
295     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
296 
297     // 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»).
298     // 6. ReturnIfAbrupt(rejectResult).
299     JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
300     JSHandle<JSTaggedValue> reject(thread, promiseCapability->GetReject());
301     JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
302     InternalCallParams *arguments = thread->GetInternalCallParams();
303     arguments->MakeArgv(reason);
304     JSFunction::Call(thread, reject, undefined, 1, arguments->GetArgv());
305     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
306 
307     // 7. Return promiseCapability.[[Promise]].
308     JSHandle<JSObject> promise(thread, promiseCapability->GetPromise());
309     return promise.GetTaggedValue();
310 }
311 
312 // 25.4.4.6 get Promise [ @@species ]
GetSpecies(EcmaRuntimeCallInfo * argv)313 JSTaggedValue BuiltinsPromise::GetSpecies([[maybe_unused]] EcmaRuntimeCallInfo *argv)
314 {
315     ASSERT(argv);
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 
333     InternalCallParams *arguments = thread->GetInternalCallParams();
334     arguments->MakeArgv(globalConst->GetHandledUndefined(), reject);
335     return JSFunction::Invoke(thread, promise, thenKey, 2, arguments->GetArgv());  // 2: two args
336 }
337 
338 // 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected )
Then(EcmaRuntimeCallInfo * argv)339 JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv)
340 {
341     ASSERT(argv);
342     BUILTINS_API_TRACE(argv->GetThread(), Promise, Then);
343     JSThread *thread = argv->GetThread();
344     [[maybe_unused]] EcmaHandleScope handleScope(thread);
345     auto ecmaVm = thread->GetEcmaVM();
346     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
347 
348     // 1. Let promise be the this value.
349     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
350     // 2. If IsPromise(promise) is false, throw a TypeError exception.
351     if (!thisValue->IsJSPromise()) {
352         THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception());
353     }
354     // 3. Let C be SpeciesConstructor(promise, %Promise%).
355     // 4. ReturnIfAbrupt(C).
356     JSHandle<JSObject> promise = JSHandle<JSObject>::Cast(thisValue);
357     JSHandle<JSTaggedValue> defaultFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
358     JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc);
359     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
360 
361     // 5. Let resultCapability be NewPromiseCapability(C).
362     // 6. ReturnIfAbrupt(resultCapability).
363     JSHandle<PromiseCapability> resultCapability = JSPromise::NewPromiseCapability(thread, constructor);
364     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
365 
366     JSHandle<JSTaggedValue> onFulfilled = BuiltinsBase::GetCallArg(argv, 0);
367     JSHandle<JSTaggedValue> onRejected = BuiltinsBase::GetCallArg(argv, 1);
368 
369     // 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
370     return PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise), onFulfilled, onRejected, resultCapability);
371 }
372 
PerformPromiseThen(JSThread * thread,const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & onFulfilled,const JSHandle<JSTaggedValue> & onRejected,const JSHandle<PromiseCapability> & capability)373 JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle<JSPromise> &promise,
374                                                   const JSHandle<JSTaggedValue> &onFulfilled,
375                                                   const JSHandle<JSTaggedValue> &onRejected,
376                                                   const JSHandle<PromiseCapability> &capability)
377 {
378     auto ecmaVm = thread->GetEcmaVM();
379     JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
380     JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
381     ObjectFactory *factory = ecmaVm->GetFactory();
382     JSMutableHandle<JSTaggedValue> fulfilled(thread, onFulfilled.GetTaggedValue());
383     auto globalConst = thread->GlobalConstants();
384     if (!fulfilled->IsCallable()) {
385         fulfilled.Update(globalConst->GetIdentityString());
386     }
387     JSMutableHandle<JSTaggedValue> rejected(thread, onRejected.GetTaggedValue());
388     if (!rejected->IsCallable()) {
389         rejected.Update(globalConst->GetThrowerString());
390     }
391     JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
392     fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
393     fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue());
394 
395     JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
396     rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
397     rejectReaction->SetHandler(thread, rejected.GetTaggedValue());
398 
399     PromiseState state = promise->GetPromiseState();
400     if (state == PromiseState::PENDING) {
401         JSHandle<TaggedQueue> fulfillReactions(thread, promise->GetPromiseFulfillReactions());
402         TaggedQueue *newQueue =
403             TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction));
404         promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue));
405 
406         JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions());
407         newQueue = TaggedQueue::Push(thread, rejectReactions, JSHandle<JSTaggedValue>::Cast(rejectReaction));
408         promise->SetPromiseRejectReactions(thread, JSTaggedValue(newQueue));
409     } else if (state == PromiseState::FULFILLED) {
410         JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);  // 2: 2 means two args stored in array
411         argv->Set(thread, 0, fulfillReaction.GetTaggedValue());
412         argv->Set(thread, 1, promise->GetPromiseResult());
413 
414         JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
415         job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
416     } else if (state == PromiseState::REJECTED) {
417         JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);  // 2: 2 means two args stored in array
418         argv->Set(thread, 0, rejectReaction.GetTaggedValue());
419         argv->Set(thread, 1, promise->GetPromiseResult());
420         // When a handler is added to a rejected promise for the first time, it is called with its operation
421         // argument set to "handle".
422         if (!promise->GetPromiseIsHandled()) {
423             JSHandle<JSTaggedValue> reason(thread, JSTaggedValue::Null());
424             thread->GetEcmaVM()->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::HANDLE);
425         }
426         JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
427         job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
428     }
429     promise->SetPromiseIsHandled(true);
430     return capability->GetPromise();
431 }
432 
PerformPromiseAll(JSThread * thread,const JSHandle<PromiseIteratorRecord> & itRecord,const JSHandle<JSTaggedValue> & ctor,const JSHandle<PromiseCapability> & capa)433 JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAll(JSThread *thread,
434                                                               const JSHandle<PromiseIteratorRecord> &itRecord,
435                                                               const JSHandle<JSTaggedValue> &ctor,
436                                                               const JSHandle<PromiseCapability> &capa)
437 {
438     auto ecmaVm = thread->GetEcmaVM();
439     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
440     ObjectFactory *factory = ecmaVm->GetFactory();
441     // 1. Assert: constructor is a constructor function.
442     ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor");
443     // 2. Assert: resultCapability is a PromiseCapability record. (not need)
444     // 3. Let values be a new empty List.
445     JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
446     JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
447     values->SetValue(thread, emptyArray);
448     // 4. Let remainingElementsCount be a new Record { [[value]]: 1 }.
449     JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
450     remainCnt->SetValue(thread, JSTaggedNumber(1));
451     // 5. Let index be 0.
452     uint32_t index = 0;
453     // 6. Repeat
454     JSHandle<JSTaggedValue> itor(thread, itRecord->GetIterator());
455     JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
456     while (true) {
457         // a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
458         next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
459         // b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
460         if (thread->HasPendingException()) {
461             itRecord->SetDone(true);
462             next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
463         }
464         // c. ReturnIfAbrupt(next).
465         RETURN_COMPLETION_IF_ABRUPT(thread, next);
466         // d. If next is false,
467         if (next->IsFalse()) {
468             // i. Set iteratorRecord.[[done]] to true.
469             itRecord->SetDone(true);
470             // ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1.
471             remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue()));
472             // iii. If remainingElementsCount.[[value]] is 0,
473             if (remainCnt->GetValue().IsZero()) {
474                 // 1. Let valuesArray be CreateArrayFromList(values).
475                 JSHandle<JSArray> jsArrayValues =
476                     JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, values->GetValue()));
477                 // 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»).
478                 JSHandle<JSTaggedValue> resCapaFunc(thread, capa->GetResolve());
479                 InternalCallParams *arguments = thread->GetInternalCallParams();
480                 arguments->MakeArgv(jsArrayValues.GetTaggedValue());
481                 JSTaggedValue resolveRes =
482                     JSFunction::Call(thread, resCapaFunc, globalConst->GetHandledUndefined(), 1, arguments->GetArgv());
483                 // 3. ReturnIfAbrupt(resolveResult)
484                 JSHandle<JSTaggedValue> resolveAbrupt(thread, resolveRes);
485                 RETURN_COMPLETION_IF_ABRUPT(thread, resolveAbrupt);
486             }
487             // iv. Return resultCapability.[[Promise]].
488             JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
489                 CompletionRecordType::NORMAL, JSHandle<JSTaggedValue>(thread, capa->GetPromise()));
490             return resRecord;
491         }
492         // e. Let nextValue be IteratorValue(next).
493         JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
494         // f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
495         if (thread->HasPendingException()) {
496             itRecord->SetDone(true);
497             nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
498         }
499 
500         // g. ReturnIfAbrupt(nextValue).
501         RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
502         // h. Append undefined to values.
503         JSHandle<TaggedArray> valuesArray =
504             JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue()));
505         valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
506         valuesArray->Set(thread, index, JSTaggedValue::Undefined());
507         values->SetValue(thread, valuesArray);
508         // i. Let nextPromise be Invoke(constructor, "resolve", «‍nextValue»).
509         JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
510         InternalCallParams *arguments = thread->GetInternalCallParams();
511         arguments->MakeArgv(nextVal);
512         JSTaggedValue taggedNextPromise = JSFunction::Invoke(thread, ctor, resolveKey, 1, arguments->GetArgv());
513         // j. ReturnIfAbrupt(nextPromise).
514         JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
515         RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
516         // k. Let resolveElement be a new built-in function object as defined in Promise.all
517         //    Resolve Element Functions.
518         JSHandle<JSPromiseAllResolveElementFunction> resoleveElement = factory->NewJSPromiseAllResolveElementFunction(
519             reinterpret_cast<void *>(BuiltinsPromiseHandler::ResolveElementFunction));
520         // l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }.
521         JSHandle<PromiseRecord> falseRecord = factory->NewPromiseRecord();
522         falseRecord->SetValue(thread, JSTaggedValue::False());
523         resoleveElement->SetAlreadyCalled(thread, falseRecord);
524         // m. Set the [[Index]] internal slot of resolveElement to index.
525         resoleveElement->SetIndex(thread, JSTaggedValue(index));
526         // n. Set the [[Values]] internal slot of resolveElement to values.
527         resoleveElement->SetValues(thread, values);
528         // o. Set the [[Capabilities]] internal slot of resolveElement to resultCapability.
529         resoleveElement->SetCapabilities(thread, capa);
530         // p. Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount.
531         resoleveElement->SetRemainingElements(thread, remainCnt);
532         // q. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1.
533         remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue()));
534         // r. Let result be Invoke(nextPromise, "then", «‍resolveElement, resultCapability.[[Reject]]»).
535         JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
536         arguments->MakeArgv(resoleveElement.GetTaggedValue(), capa->GetReject());
537         JSTaggedValue taggedResult =
538             JSFunction::Invoke(thread, nextPromise, thenKey, 2, arguments->GetArgv());  // 2: two args
539 
540         JSHandle<JSTaggedValue> result(thread, taggedResult);
541         // s. ReturnIfAbrupt(result).
542         RETURN_COMPLETION_IF_ABRUPT(thread, result);
543         // t. Set index to index + 1.
544         ++index;
545     }
546 }
547 
PerformPromiseRace(JSThread * thread,const JSHandle<PromiseIteratorRecord> & iteratorRecord,const JSHandle<PromiseCapability> & capability,const JSHandle<JSTaggedValue> & constructor)548 JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseRace(JSThread *thread,
549                                                                const JSHandle<PromiseIteratorRecord> &iteratorRecord,
550                                                                const JSHandle<PromiseCapability> &capability,
551                                                                const JSHandle<JSTaggedValue> &constructor)
552 {
553     // 1. Repeat
554     //    a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
555     //    b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
556     //    c. ReturnIfAbrupt(next).
557     //    d. If next is false, then
558     //       i. Set iteratorRecord.[[done]] to true.
559     //       ii. Return promiseCapability.[[Promise]].
560     //    e. Let nextValue be IteratorValue(next).
561     //    f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
562     //    g. ReturnIfAbrupt(nextValue).
563     //    h. Let nextPromise be Invoke(C, "resolve", «nextValue»).
564     //    i. ReturnIfAbrupt(nextPromise).
565     //    j. Let result be Invoke(nextPromise, "then", «promiseCapability.[[Resolve]], promiseCapability.[[Reject]]»).
566     //    k. ReturnIfAbrupt(result).
567     auto ecmaVm = thread->GetEcmaVM();
568     const GlobalEnvConstants *globalConst = thread->GlobalConstants();
569     ObjectFactory *factory = ecmaVm->GetFactory();
570     JSHandle<JSTaggedValue> iterator(thread, iteratorRecord->GetIterator());
571     JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
572     while (true) {
573         next.Update(JSIterator::IteratorStep(thread, iterator).GetTaggedValue());
574         if (thread->HasPendingException()) {
575             iteratorRecord->SetDone(true);
576             next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
577         }
578         RETURN_COMPLETION_IF_ABRUPT(thread, next);
579         if (next->IsFalse()) {
580             iteratorRecord->SetDone(true);
581             JSHandle<JSTaggedValue> promise(thread, capability->GetPromise());
582             JSHandle<CompletionRecord> completionRecord =
583                 factory->NewCompletionRecord(CompletionRecordType::NORMAL, promise);
584             return completionRecord;
585         }
586         JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
587         if (thread->HasPendingException()) {
588             iteratorRecord->SetDone(true);
589             nextValue = JSPromise::IfThrowGetThrowValue(thread);
590         }
591         RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
592         JSHandle<JSTaggedValue> resolveStr = globalConst->GetHandledPromiseResolveString();
593 
594         InternalCallParams *arguments = thread->GetInternalCallParams();
595         arguments->MakeArgv(nextValue);
596         JSTaggedValue result = JSFunction::Invoke(thread, constructor, resolveStr, 1, arguments->GetArgv());
597 
598         JSHandle<JSTaggedValue> nextPromise(thread, result);
599         if (thread->HasPendingException()) {
600             nextPromise = JSPromise::IfThrowGetThrowValue(thread);
601         }
602         RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
603 
604         JSHandle<JSTaggedValue> thenStr = globalConst->GetHandledPromiseThenString();
605         arguments->MakeArgv(capability->GetResolve(), capability->GetReject());
606         result = JSFunction::Invoke(thread, nextPromise, thenStr, 2, arguments->GetArgv());  // 2: two args
607 
608         JSHandle<JSTaggedValue> handleResult(thread, result);
609         if (thread->HasPendingException()) {
610             handleResult = JSPromise::IfThrowGetThrowValue(thread);
611         }
612         RETURN_COMPLETION_IF_ABRUPT(thread, handleResult);
613     }
614 }
615 }  // namespace panda::ecmascript::builtins
616