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