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