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