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