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