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