1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/builtins/builtins_promise_handler.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/interpreter/interpreter.h"
19 #include "ecmascript/jobs/micro_job_queue.h"
20 #include "ecmascript/js_array.h"
21 #include "ecmascript/js_async_function.h"
22 #include "ecmascript/js_promise.h"
23
24 namespace panda::ecmascript::builtins {
25 // es6 25.4.1.3.2 Promise Resolve Functions
Resolve(EcmaRuntimeCallInfo * argv)26 JSTaggedValue BuiltinsPromiseHandler::Resolve(EcmaRuntimeCallInfo *argv)
27 {
28 ASSERT(argv);
29 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Resolve);
30 JSThread *thread = argv->GetThread();
31 [[maybe_unused]] EcmaHandleScope handleScope(thread);
32
33 // 1. Assert: F has a [[Promise]] internal slot whose value is an Object.
34 JSHandle<JSPromiseReactionsFunction> resolve = JSHandle<JSPromiseReactionsFunction>::Cast(GetConstructor(argv));
35 ASSERT_PRINT(resolve->GetPromise(thread).IsECMAObject(), "Resolve: promise must be js object");
36
37 // 2. Let promise be the value of F's [[Promise]] internal slot.
38 // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot.
39 // 4. If alreadyResolved.[[value]] is true, return undefined.
40 // 5. Set alreadyResolved.[[value]] to true.
41 JSHandle<PromiseRecord> alreadyResolved(thread, resolve->GetAlreadyResolved(thread));
42 if (alreadyResolved->GetValue(thread).IsTrue()) {
43 return JSTaggedValue::Undefined();
44 }
45 alreadyResolved->SetValue(thread, JSTaggedValue::True());
46 JSHandle<JSPromise> resolvePromise(thread, resolve->GetPromise(thread));
47 JSHandle<JSTaggedValue> resolution = BuiltinsBase::GetCallArg(argv, 0);
48 return InnerResolve(thread, resolvePromise, resolution);
49 }
50
51 // es6 25.4.1.3.1 Promise Reject Functions
Reject(EcmaRuntimeCallInfo * argv)52 JSTaggedValue BuiltinsPromiseHandler::Reject(EcmaRuntimeCallInfo *argv)
53 {
54 ASSERT(argv);
55 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Reject);
56 JSThread *thread = argv->GetThread();
57 [[maybe_unused]] EcmaHandleScope handleScope(thread);
58
59 // 1. Assert: F has a [[Promise]] internal slot whose value is an Object.
60 JSHandle<JSPromiseReactionsFunction> reject = JSHandle<JSPromiseReactionsFunction>::Cast(GetConstructor(argv));
61 ASSERT_PRINT(reject->GetPromise(thread).IsECMAObject(), "Reject: promise must be js object");
62
63 // 2. Let promise be the value of F's [[Promise]] internal slot.
64 // 3. Let alreadyResolved be the value of F's [[AlreadyResolved]] internal slot.
65 // 4. If alreadyResolved.[[value]] is true, return undefined.
66 // 5. Set alreadyResolved.[[value]] to true.
67 JSHandle<JSPromise> rejectPromise(thread, reject->GetPromise(thread));
68 JSHandle<PromiseRecord> alreadyResolved(thread, reject->GetAlreadyResolved(thread));
69 if (alreadyResolved->GetValue(thread).IsTrue()) {
70 return JSTaggedValue::Undefined();
71 }
72 alreadyResolved->SetValue(thread, JSTaggedValue::True());
73
74 // 6. Return RejectPromise(promise, reason).
75 JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
76 JSHandle<JSTaggedValue> result(thread, JSPromise::RejectPromise(thread, rejectPromise, reason));
77 return result.GetTaggedValue();
78 }
79
80 // es6 25.4.1.5.1 GetCapabilitiesExecutor Functions
Executor(EcmaRuntimeCallInfo * argv)81 JSTaggedValue BuiltinsPromiseHandler::Executor(EcmaRuntimeCallInfo *argv)
82 {
83 ASSERT(argv);
84 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, Executor);
85 JSThread *thread = argv->GetThread();
86 [[maybe_unused]] EcmaHandleScope handleScope(thread);
87
88 // 1. Assert: F has a [[Capability]] internal slot whose value is a PromiseCapability Record.
89 JSHandle<JSPromiseExecutorFunction> executor = JSHandle<JSPromiseExecutorFunction>::Cast(GetConstructor(argv));
90 ASSERT_PRINT(executor->GetCapability(thread).IsRecord(),
91 "Executor: F has a [[Capability]] internal slot whose value is a PromiseCapability Record.");
92
93 // 2. Let promiseCapability be the value of F's [[Capability]] internal slot.
94 // 3. If promiseCapability.[[Resolve]] is not undefined, throw a TypeError exception.
95 JSHandle<PromiseCapability> promiseCapability(thread, executor->GetCapability(thread));
96 if (!promiseCapability->GetResolve(thread).IsUndefined()) {
97 THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: resolve should be undefine!", JSTaggedValue::Undefined());
98 }
99 // 4. If promiseCapability.[[Reject]] is not undefined, throw a TypeError exception.
100 if (!promiseCapability->GetReject(thread).IsUndefined()) {
101 THROW_TYPE_ERROR_AND_RETURN(thread, "Executor: reject should be undefine!", JSTaggedValue::Undefined());
102 }
103 // 5. Set promiseCapability.[[Resolve]] to resolve.
104 // 6. Set promiseCapability.[[Reject]] to reject.
105 JSHandle<JSTaggedValue> resolve = GetCallArg(argv, 0);
106 JSHandle<JSTaggedValue> reject = GetCallArg(argv, 1);
107 promiseCapability->SetResolve(thread, resolve);
108 promiseCapability->SetReject(thread, reject);
109 // 7. Return undefined.
110 return JSTaggedValue::Undefined();
111 }
112
113 // es6 25.4.4.1.2 Promise.all Resolve Element Functions
ResolveElementFunction(EcmaRuntimeCallInfo * argv)114 JSTaggedValue BuiltinsPromiseHandler::ResolveElementFunction(EcmaRuntimeCallInfo *argv)
115 {
116 ASSERT(argv);
117 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, ResolveElementFunction);
118 JSThread *thread = argv->GetThread();
119 [[maybe_unused]] EcmaHandleScope handleScope(thread);
120 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
121 JSHandle<JSPromiseAllResolveElementFunction> func =
122 JSHandle<JSPromiseAllResolveElementFunction>::Cast(GetConstructor(argv));
123 // 1. Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot.
124 JSHandle<PromiseRecord> alreadyCalled =
125 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, func->GetAlreadyCalled(thread)));
126 // 2. If alreadyCalled.[[value]] is true, return undefined.
127 if (alreadyCalled->GetValue(thread).IsTrue()) {
128 return JSTaggedValue::Undefined();
129 }
130 // 3. Set alreadyCalled.[[value]] to true.
131 alreadyCalled->SetValue(thread, JSTaggedValue::True());
132 // 4. Let index be the value of F's [[Index]] internal slot.
133 JSHandle<JSTaggedValue> index(thread, func->GetIndex(thread));
134 // 5. Let values be the value of F's [[Values]] internal slot.
135 JSHandle<PromiseRecord> values =
136 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, func->GetValues(thread)));
137 // 6. Let promiseCapability be the value of F's [[Capabilities]] internal slot.
138 JSHandle<PromiseCapability> capa =
139 JSHandle<PromiseCapability>::Cast(JSHandle<JSTaggedValue>(thread, func->GetCapabilities(thread)));
140 // 7. Let remainingElementsCount be the value of F's [[RemainingElements]] internal slot.
141 JSHandle<PromiseRecord> remainCnt =
142 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, func->GetRemainingElements(thread)));
143 // 8. Set values[index] to x.
144 JSHandle<TaggedArray> arrayValues =
145 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue(thread)));
146 arrayValues->Set(thread, JSTaggedValue::ToUint32(thread, index), GetCallArg(argv, 0).GetTaggedValue());
147 // 9. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] - 1.
148 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
149 // 10. If remainingElementsCount.[[value]] is 0,
150 if (remainCnt->GetValue(thread).IsZero()) {
151 // a. Let valuesArray be CreateArrayFromList(values).
152 JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues);
153 // b. Return Call(promiseCapability.[[Resolve]], undefined, «valuesArray»).
154 JSHandle<JSTaggedValue> capaResolve(thread, capa->GetResolve(thread));
155 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
156 EcmaRuntimeCallInfo *info =
157 EcmaInterpreter::NewRuntimeCallInfo(thread, capaResolve, undefined, undefined, 1);
158 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
159 info->SetCallArg(jsArrayValues.GetTaggedValue());
160 return JSFunction::Call(info);
161 }
162 // 11. Return undefined.
163 return JSTaggedValue::Undefined();
164 }
165
AsyncAwaitFulfilled(EcmaRuntimeCallInfo * argv)166 JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitFulfilled(EcmaRuntimeCallInfo *argv)
167 {
168 ASSERT(argv);
169 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, AsyncAwaitFulfilled);
170 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
171
172 JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
173 JSHandle<JSAsyncAwaitStatusFunction> func(GetConstructor(argv));
174 return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitFulfilled(argv->GetThread(), func, value).GetTaggedValue();
175 }
176
AsyncAwaitRejected(EcmaRuntimeCallInfo * argv)177 JSTaggedValue BuiltinsPromiseHandler::AsyncAwaitRejected(EcmaRuntimeCallInfo *argv)
178 {
179 ASSERT(argv);
180 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, AsyncAwaitRejected);
181 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
182
183 JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
184 JSHandle<JSAsyncAwaitStatusFunction> func(GetConstructor(argv));
185 return JSAsyncAwaitStatusFunction::AsyncFunctionAwaitRejected(argv->GetThread(), func, reason).GetTaggedValue();
186 }
187
valueThunkFunction(EcmaRuntimeCallInfo * argv)188 JSTaggedValue BuiltinsPromiseHandler::valueThunkFunction(EcmaRuntimeCallInfo *argv)
189 {
190 BUILTINS_API_TRACE(argv->GetThread(), PromiseHandler, valueThunkFunction);
191 JSHandle<JSPromiseValueThunkOrThrowerFunction> valueThunk =
192 JSHandle<JSPromiseValueThunkOrThrowerFunction>::Cast(GetConstructor(argv));
193 return valueThunk->GetResult(argv->GetThread());
194 }
195
throwerFunction(EcmaRuntimeCallInfo * argv)196 JSTaggedValue BuiltinsPromiseHandler::throwerFunction(EcmaRuntimeCallInfo *argv)
197 {
198 JSThread *thread = argv->GetThread();
199 BUILTINS_API_TRACE(thread, PromiseHandler, throwerFunction);
200 JSHandle<JSPromiseValueThunkOrThrowerFunction> thrower =
201 JSHandle<JSPromiseValueThunkOrThrowerFunction>::Cast(GetConstructor(argv));
202 JSTaggedValue undefined = thread->GlobalConstants()->GetUndefined();
203 THROW_NEW_ERROR_AND_RETURN_VALUE(thread, thrower->GetResult(thread), undefined);
204 }
205
ThenFinally(EcmaRuntimeCallInfo * argv)206 JSTaggedValue BuiltinsPromiseHandler::ThenFinally(EcmaRuntimeCallInfo *argv)
207 {
208 // 1. Let F be the active function object.
209 JSThread *thread = argv->GetThread();
210 BUILTINS_API_TRACE(thread, PromiseHandler, ThenFinally);
211 auto ecmaVm = thread->GetEcmaVM();
212 ObjectFactory *factory = ecmaVm->GetFactory();
213 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
214 JSHandle<JSPromiseFinallyFunction> thenFinally(GetConstructor(argv));
215 JSHandle<JSTaggedValue> value = BuiltinsBase::GetCallArg(argv, 0);
216 // 2. Let onFinally be F.[[OnFinally]].
217 // 3. Assert: IsCallable(onFinally) is true.
218 JSHandle<JSTaggedValue> onFinally(thread, thenFinally->GetOnFinally(thread));
219 ASSERT_PRINT(onFinally->IsCallable(), "onFinally is not callable");
220 // 4. Let result be ? Call(onFinally, undefined).
221 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
222 EcmaRuntimeCallInfo *taggedInfo =
223 EcmaInterpreter::NewRuntimeCallInfo(thread, onFinally, undefined, undefined, 0);
224 JSTaggedValue result = JSFunction::Call(taggedInfo);
225 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
226 JSHandle<JSTaggedValue> resultHandle(thread, result);
227 // 5. Let C be F.[[Constructor]].
228 // 6. Assert: IsConstructor(C) is true.
229 JSHandle<JSTaggedValue> thenFinallyConstructor(thread, thenFinally->GetConstructor(thread));
230 ASSERT_PRINT(thenFinallyConstructor->IsConstructor(), "thenFinallyConstructor is not constructor");
231 // 7. Let promise be ? PromiseResolve(C, result).
232 JSHandle<JSTaggedValue> promiseHandle =
233 BuiltinsPromiseHandler::PromiseResolve(thread, thenFinallyConstructor, resultHandle);
234 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
235 // 8. Let valueThunk be equivalent to a function that returns value.
236 JSHandle<JSPromiseValueThunkOrThrowerFunction> valueThunk =
237 factory->NewJSPromiseValueThunkFunction();
238 valueThunk->SetResult(thread, value);
239 JSHandle<JSTaggedValue> thenKey(globalConst->GetHandledPromiseThenString());
240 EcmaRuntimeCallInfo *invokeInfo =
241 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promiseHandle, undefined, 1);
242 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
243 invokeInfo->SetCallArg(valueThunk.GetTaggedValue());
244 // 9. Return ? Invoke(promise, "then", « valueThunk »).
245 return JSFunction::Invoke(invokeInfo, thenKey);
246 }
247
CatchFinally(EcmaRuntimeCallInfo * argv)248 JSTaggedValue BuiltinsPromiseHandler::CatchFinally(EcmaRuntimeCallInfo *argv)
249 {
250 // 1. Let F be the active function object.
251 JSThread *thread = argv->GetThread();
252 BUILTINS_API_TRACE(thread, PromiseHandler, CatchFinally);
253 auto ecmaVm = thread->GetEcmaVM();
254 ObjectFactory *factory = ecmaVm->GetFactory();
255 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
256 JSHandle<JSPromiseFinallyFunction> catchFinally(GetConstructor(argv));
257 // 2. Let onFinally be F.[[OnFinally]].
258 // 3. Assert: IsCallable(onFinally) is true.
259 JSHandle<JSTaggedValue> onFinally(thread, catchFinally->GetOnFinally(thread));
260 ASSERT_PRINT(onFinally->IsCallable(), "thenOnFinally is not callable");
261 // 4. Let result be ? Call(onFinally, undefined).
262 JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
263 EcmaRuntimeCallInfo *info =
264 EcmaInterpreter::NewRuntimeCallInfo(thread, onFinally, undefined, undefined, 0);
265 JSTaggedValue result = JSFunction::Call(info);
266 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
267 JSHandle<JSTaggedValue> resultHandle(thread, result);
268 // 5. Let C be F.[[Constructor]].
269 // 6. Assert: IsConstructor(C) is true.
270 JSHandle<JSTaggedValue> catchFinallyConstructor(thread, catchFinally->GetConstructor(thread));
271 ASSERT_PRINT(catchFinallyConstructor->IsConstructor(), "catchFinallyConstructor is not constructor");
272 // 7. Let promise be ? PromiseResolve(C, result).
273 JSHandle<JSTaggedValue> promiseHandle =
274 BuiltinsPromiseHandler::PromiseResolve(thread, catchFinallyConstructor, resultHandle);
275 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
276 // 8. Let thrower be equivalent to a function that throws reason.
277 JSHandle<JSTaggedValue> reason = BuiltinsBase::GetCallArg(argv, 0);
278 JSHandle<JSTaggedValue> thenKey(globalConst->GetHandledPromiseThenString());
279 JSHandle<JSPromiseValueThunkOrThrowerFunction> thrower =
280 factory->NewJSPromiseThrowerFunction();
281 thrower->SetResult(thread, reason);
282 EcmaRuntimeCallInfo *invokeInfo =
283 EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promiseHandle, undefined, 1);
284 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
285 invokeInfo->SetCallArg(thrower.GetTaggedValue());
286 // 9. Return ? Invoke(promise, "then", « thrower »).
287 return JSFunction::Invoke(invokeInfo, thenKey);
288 }
289
PromiseResolve(JSThread * thread,const JSHandle<JSTaggedValue> & constructor,const JSHandle<JSTaggedValue> & xValue)290 JSHandle<JSTaggedValue> BuiltinsPromiseHandler::PromiseResolve(JSThread *thread,
291 const JSHandle<JSTaggedValue> &constructor,
292 const JSHandle<JSTaggedValue> &xValue)
293 {
294 BUILTINS_API_TRACE(thread, PromiseHandler, PromiseResolve);
295 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
296 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
297 // 1. Assert: Type(C) is Object.
298 ASSERT_PRINT(constructor->IsECMAObject(), "PromiseResolve : is not callable");
299 // 2. If IsPromise(x) is true, then
300 if (xValue->IsJSPromise()) {
301 // a. Let xConstructor be ? Get(x, "constructor").
302 JSHandle<JSTaggedValue> ctorKey(globalConst->GetHandledConstructorString());
303 JSHandle<JSTaggedValue> ctorValue = JSObject::GetProperty(thread, xValue, ctorKey).GetValue();
304 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, ctorValue);
305 // b. If SameValue(xConstructor, C) is true, return x.
306 if (JSTaggedValue::SameValue(thread, ctorValue, constructor)) {
307 return xValue;
308 }
309 }
310 if (constructor == env->GetPromiseFunction()) {
311 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
312 JSHandle<JSPromise> promise = factory->NewJSPromise();
313 InnerResolve(thread, promise, xValue);
314 return JSHandle<JSTaggedValue>(thread, promise.GetTaggedValue());
315 }
316 // 3. Let promiseCapability be ? NewPromiseCapability(C).
317 // 4. ReturnIfAbrupt(promiseCapability)
318 JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, constructor);
319 RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
320 JSHandle<JSTaggedValue> promiseCapaHandle = JSHandle<JSTaggedValue>::Cast(promiseCapability);
321 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapaHandle);
322 // 6. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»).
323 // 7. ReturnIfAbrupt(resolveResult).
324 JSHandle<JSTaggedValue> resolve(thread, promiseCapability->GetResolve(thread));
325 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
326 EcmaRuntimeCallInfo *info =
327 EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
328 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapaHandle);
329 info->SetCallArg(xValue.GetTaggedValue());
330 JSTaggedValue resolveResult = JSFunction::Call(info);
331 JSHandle<JSTaggedValue> resolveResultHandle(thread, resolveResult);
332 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, resolveResultHandle);
333 // 8. Return promiseCapability.[[Promise]].
334 JSHandle<JSTaggedValue> promise(thread, promiseCapability->GetPromise(thread));
335 return promise;
336 }
337
AllSettledResolveElementFunction(EcmaRuntimeCallInfo * argv)338 JSTaggedValue BuiltinsPromiseHandler::AllSettledResolveElementFunction(EcmaRuntimeCallInfo *argv)
339 {
340 // 1. Let F be the active function object.
341 JSThread *thread = argv->GetThread();
342 BUILTINS_API_TRACE(thread, PromiseHandler, AllSettledResolveElementFunction);
343 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
344 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
345 JSHandle<JSPromiseAllSettledElementFunction> resolveElement =
346 JSHandle<JSPromiseAllSettledElementFunction>::Cast((GetConstructor(argv)));
347 // 2. Let alreadyCalled be F.[[AlreadyCalled]].
348 JSHandle<PromiseRecord> alreadyCalled =
349 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, resolveElement->GetAlreadyCalled(thread)));
350 // 3. If alreadyCalled.[[Value]] is true, return undefined.
351 if (alreadyCalled->GetValue(thread).IsTrue()) {
352 return JSTaggedValue::Undefined();
353 }
354 // 4. Set alreadyCalled.[[Value]] to true.
355 alreadyCalled->SetValue(thread, JSTaggedValue::True());
356 // 5. Let index be F.[[Index]].
357 uint32_t index = resolveElement->GetIndex();
358 // 6. Let values be F.[[Values]].
359 JSHandle<PromiseRecord> values =
360 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, resolveElement->GetValues(thread)));
361 // 7. Let promiseCapability be F.[[Capability]].
362 JSHandle<PromiseCapability> capa =
363 JSHandle<PromiseCapability>::Cast(JSHandle<JSTaggedValue>(thread, resolveElement->GetCapability(thread)));
364 // 8. Let remainingElementsCount be F.[[RemainingElements]].
365 JSHandle<PromiseRecord> remainCnt =
366 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, resolveElement->GetRemainingElements(thread)));
367 // 9. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
368 JSHandle<JSTaggedValue> proto = env->GetObjectFunctionPrototype();
369 JSHandle<JSObject> obj = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(proto);
370 // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "fulfilled").
371 JSHandle<JSTaggedValue> statusKey = globalConst->GetHandledPromiseStatusString();
372 JSHandle<JSTaggedValue> fulfilledKey = globalConst->GetHandledPromiseFulfilledString();
373 JSObject::CreateDataPropertyOrThrow(thread, obj, statusKey, fulfilledKey);
374 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
375 // 11. Perform ! CreateDataPropertyOrThrow(obj, "value", x).
376 JSHandle<JSTaggedValue> valueKey = globalConst->GetHandledValueString();
377 JSHandle<JSTaggedValue> xValue = GetCallArg(argv, 0);
378 JSObject::CreateDataPropertyOrThrow(thread, obj, valueKey, xValue);
379 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
380 // 12. Set values[index] to obj.
381 JSHandle<TaggedArray> arrayValues =
382 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue(thread)));
383 arrayValues->Set(thread, index, obj.GetTaggedValue());
384 // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
385 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
386 // 14. If remainingElementsCount.[[Value]] is 0, then
387 if (remainCnt->GetValue(thread).IsZero()) {
388 // a. Let valuesArray be CreateArrayFromList(values).
389 JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues);
390 // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).
391 JSHandle<JSTaggedValue> capaResolve(thread, capa->GetResolve(thread));
392 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
393 EcmaRuntimeCallInfo *info =
394 EcmaInterpreter::NewRuntimeCallInfo(thread, capaResolve, undefined, undefined, 1);
395 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
396 info->SetCallArg(jsArrayValues.GetTaggedValue());
397 return JSFunction::Call(info);
398 }
399 // 15. Return undefined.
400 return JSTaggedValue::Undefined();
401 }
402
AllSettledRejectElementFunction(EcmaRuntimeCallInfo * argv)403 JSTaggedValue BuiltinsPromiseHandler::AllSettledRejectElementFunction(EcmaRuntimeCallInfo *argv)
404 {
405 // 1. Let F be the active function object.
406 JSThread *thread = argv->GetThread();
407 BUILTINS_API_TRACE(thread, PromiseHandler, AllSettledRejectElementFunction);
408 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
409 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
410 JSHandle<JSPromiseAllSettledElementFunction> rejectElement =
411 JSHandle<JSPromiseAllSettledElementFunction>::Cast((GetConstructor(argv)));
412 // 2. Let alreadyCalled be F.[[AlreadyCalled]].
413 JSHandle<PromiseRecord> alreadyCalled =
414 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetAlreadyCalled(thread)));
415 // 3. If alreadyCalled.[[Value]] is true, return undefined.
416 if (alreadyCalled->GetValue(thread).IsTrue()) {
417 return JSTaggedValue::Undefined();
418 }
419 // 4. Set alreadyCalled.[[Value]] to true.
420 alreadyCalled->SetValue(thread, JSTaggedValue::True());
421 // 5. Let index be F.[[Index]].
422 uint32_t index = rejectElement->GetIndex();
423 // 6. Let values be F.[[Values]].
424 JSHandle<PromiseRecord> values =
425 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetValues(thread)));
426 // 7. Let promiseCapability be F.[[Capability]].
427 JSHandle<PromiseCapability> capa =
428 JSHandle<PromiseCapability>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetCapability(thread)));
429 // 8. Let remainingElementsCount be F.[[RemainingElements]].
430 JSHandle<PromiseRecord> remainCnt =
431 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetRemainingElements(thread)));
432 // 9. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
433 JSHandle<JSTaggedValue> proto = env->GetObjectFunctionPrototype();
434 JSHandle<JSObject> obj = thread->GetEcmaVM()->GetFactory()->OrdinaryNewJSObjectCreate(proto);
435 // 10. Perform ! CreateDataPropertyOrThrow(obj, "status", "rejected").
436 JSHandle<JSTaggedValue> statusKey = globalConst->GetHandledPromiseStatusString();
437 JSHandle<JSTaggedValue> rejectedKey = globalConst->GetHandledPromiseRejectedString();
438 JSObject::CreateDataPropertyOrThrow(thread, obj, statusKey, rejectedKey);
439 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
440 // 11. Perform ! CreateDataPropertyOrThrow(obj, "reason", x).
441 JSHandle<JSTaggedValue> xReason = GetCallArg(argv, 0);
442 JSHandle<JSTaggedValue> reasonKey = globalConst->GetHandledPromiseReasonString();
443 JSObject::CreateDataPropertyOrThrow(thread, obj, reasonKey, xReason);
444 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
445 // 12. Set values[index] to obj.
446 JSHandle<TaggedArray> arrayValues =
447 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue(thread)));
448 arrayValues->Set(thread, index, obj.GetTaggedValue());
449 // 13. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
450 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
451 // 14. If remainingElementsCount.[[Value]] is 0, then
452 if (remainCnt->GetValue(thread).IsZero()) {
453 // a. Let valuesArray be CreateArrayFromList(values).
454 JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, arrayValues);
455 // b. Return ? Call(promiseCapability.[[Resolve]], undefined, « valuesArray »).
456 JSHandle<JSTaggedValue> capaResolve(thread, capa->GetResolve(thread));
457 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
458 EcmaRuntimeCallInfo *info =
459 EcmaInterpreter::NewRuntimeCallInfo(thread, capaResolve, undefined, undefined, 1);
460 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
461 info->SetCallArg(jsArrayValues.GetTaggedValue());
462 return JSFunction::Call(info);
463 }
464 // 15. Return undefined.
465 return JSTaggedValue::Undefined();
466 }
467
AnyRejectElementFunction(EcmaRuntimeCallInfo * argv)468 JSTaggedValue BuiltinsPromiseHandler::AnyRejectElementFunction(EcmaRuntimeCallInfo *argv)
469 {
470 // 1. Let F be the active function object.
471 JSThread *thread = argv->GetThread();
472 BUILTINS_API_TRACE(thread, PromiseHandler, AnyRejectElementFunction);
473 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
474 const GlobalEnvConstants *globalConst = thread->GlobalConstants();
475 JSHandle<JSPromiseAnyRejectElementFunction> rejectElement =
476 JSHandle<JSPromiseAnyRejectElementFunction>::Cast((GetConstructor(argv)));
477 // 2. If F.[[AlreadyCalled]] is true, return undefined.
478 JSTaggedValue alreadyCalled = rejectElement->GetAlreadyCalled(thread);
479 if (alreadyCalled.IsTrue()) {
480 return JSTaggedValue::Undefined();
481 }
482 // 3. Set F.[[AlreadyCalled]] to true.
483 rejectElement->SetAlreadyCalled(thread, JSTaggedValue::True());
484 // 4. Let index be F.[[Index]].
485 uint32_t index = rejectElement->GetIndex();
486 // 5. Let errors be F.[[Errors]].
487 JSHandle<PromiseRecord> errors =
488 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetErrors(thread)));
489 // 6. Let promiseCapability be F.[[Capability]].
490 JSHandle<PromiseCapability> capa =
491 JSHandle<PromiseCapability>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetCapability(thread)));
492 // 7. Let remainingElementsCount be F.[[RemainingElements]].
493 JSHandle<PromiseRecord> remainCnt =
494 JSHandle<PromiseRecord>::Cast(JSHandle<JSTaggedValue>(thread, rejectElement->GetRemainingElements(thread)));
495 // 8. Set errors[index] to x.
496 JSHandle<JSTaggedValue> xValue = GetCallArg(argv, 0);
497 JSHandle<TaggedArray> errorsArray =
498 JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, errors->GetValue(thread)));
499 errorsArray->Set(thread, index, xValue.GetTaggedValue());
500 // 9. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
501 remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
502 // 10. If remainingElementsCount.[[Value]] is 0, then
503 if (remainCnt->GetValue(thread).IsZero()) {
504 // a. Let error be a newly created AggregateError object.
505 JSHandle<JSObject> error = factory->NewJSAggregateError();
506 // b. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true,
507 // [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errors) }).
508 JSHandle<JSTaggedValue> errorsKey(thread, globalConst->GetErrorsString());
509 JSHandle<JSTaggedValue> errorsValue(JSArray::CreateArrayFromList(thread, errorsArray));
510 PropertyDescriptor msgDesc(thread, errorsValue, true, false, true);
511 JSHandle<JSTaggedValue> errorTagged = JSHandle<JSTaggedValue>::Cast(error);
512 JSTaggedValue::DefinePropertyOrThrow(thread, errorTagged, errorsKey, msgDesc);
513 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
514 // c. Return ? Call(promiseCapability.[[Reject]], undefined, « error »).
515 JSHandle<JSTaggedValue> capaReject(thread, capa->GetReject(thread));
516 JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
517 EcmaRuntimeCallInfo *info =
518 EcmaInterpreter::NewRuntimeCallInfo(thread, capaReject, undefined, undefined, 1);
519 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
520 info->SetCallArg(error.GetTaggedValue());
521 return JSFunction::Call(info);
522 }
523 // 11. Return undefined.
524 return JSTaggedValue::Undefined();
525 }
526
InnerResolve(JSThread * thread,const JSHandle<JSPromise> & promise,const JSHandle<JSTaggedValue> & resolution)527 JSTaggedValue BuiltinsPromiseHandler::InnerResolve(JSThread *thread, const JSHandle<JSPromise> &promise,
528 const JSHandle<JSTaggedValue> &resolution)
529 {
530 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
531 auto ecmaVm = thread->GetEcmaVM();
532 JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
533 // 6. If SameValue(resolution, promise) is true, then
534 // a. Let selfResolutionError be a newly created TypeError object.
535 // b. Return RejectPromise(promise, selfResolutionError).
536 if (JSTaggedValue::SameValue(thread, resolution.GetTaggedValue(), promise.GetTaggedValue())) {
537 JSHandle<JSObject> resolutionError = factory->GetJSError(ErrorType::TYPE_ERROR,
538 "Resolve: The promise and resolution cannot be the same.", StackCheck::NO);
539 JSPromise::RejectPromise(thread, promise, JSHandle<JSTaggedValue>::Cast(resolutionError));
540 return JSTaggedValue::Undefined();
541 }
542 // 7. If Type(resolution) is not Object, then
543 // a. Return FulfillPromise(promise, resolution).
544 if (!resolution.GetTaggedValue().IsECMAObject()) {
545 JSPromise::FulfillPromise(thread, promise, resolution);
546 return JSTaggedValue::Undefined();
547 }
548 // 8. Let then be Get(resolution, "then").
549 // 9. If then is an abrupt completion, then
550 // a. Return RejectPromise(promise, then.[[value]]).
551 JSHandle<JSTaggedValue> thenKey(thread->GlobalConstants()->GetHandledPromiseThenString());
552 JSHandle<JSTaggedValue> thenValue = JSObject::GetProperty(thread, resolution, thenKey).GetValue();
553 if (thread->HasPendingException()) {
554 if (!thenValue->IsJSError()) {
555 thenValue = JSHandle<JSTaggedValue>(thread, thread->GetException());
556 }
557 thread->ClearException();
558 return JSPromise::RejectPromise(thread, promise, thenValue);
559 }
560 // 10. Let thenAction be then.[[value]].
561 // 11. If IsCallable(thenAction) is false, then
562 // a. Return FulfillPromise(promise, resolution).
563 if (!thenValue->IsCallable()) {
564 JSPromise::FulfillPromise(thread, promise, resolution);
565 return JSTaggedValue::Undefined();
566 }
567 // 12. Perform EnqueueJob ("PromiseJobs", PromiseResolveThenableJob, «promise, resolution, thenAction»)
568 JSHandle<TaggedArray> arguments = factory->NewTaggedArray(3); // 3: 3 means three args stored in array
569 arguments->Set(thread, 0, promise);
570 arguments->Set(thread, 1, resolution);
571 arguments->Set(thread, 2, thenValue); // 2: 2 means index of array is 2
572
573 JSHandle<JSFunction> promiseResolveThenableJob(env->GetPromiseResolveThenableJob());
574 JSHandle<job::MicroJobQueue> job = thread->GetEcmaVM()->GetMicroJobQueue();
575 job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseResolveThenableJob, arguments);
576 // 13. Return undefined.
577 return JSTaggedValue::Undefined();
578 }
579 } // namespace panda::ecmascript::builtins
580