14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/js_promise.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include "ecmascript/builtins/builtins_promise_handler.h"
194514f5e3Sopenharmony_ci#include "ecmascript/global_env.h"
204514f5e3Sopenharmony_ci#include "ecmascript/interpreter/interpreter.h"
214514f5e3Sopenharmony_ci#include "ecmascript/jobs/micro_job_queue.h"
224514f5e3Sopenharmony_ci
234514f5e3Sopenharmony_cinamespace panda::ecmascript {
244514f5e3Sopenharmony_ciusing BuiltinsPromiseHandler = builtins::BuiltinsPromiseHandler;
254514f5e3Sopenharmony_ci
264514f5e3Sopenharmony_ciJSHandle<ResolvingFunctionsRecord> JSPromise::CreateResolvingFunctions(JSThread *thread,
274514f5e3Sopenharmony_ci                                                                       const JSHandle<JSPromise> &promise)
284514f5e3Sopenharmony_ci{
294514f5e3Sopenharmony_ci    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
304514f5e3Sopenharmony_ci    // 1. Let alreadyResolved be a new Record { [[value]]: false }.
314514f5e3Sopenharmony_ci    JSHandle<PromiseRecord> record = factory->NewPromiseRecord();
324514f5e3Sopenharmony_ci    record->SetValue(thread, JSTaggedValue::False());
334514f5e3Sopenharmony_ci
344514f5e3Sopenharmony_ci    // 2. Let resolve be a new built-in function object as defined in Promise Resolve Functions (25.4.1.3.2).
354514f5e3Sopenharmony_ci    JSHandle<JSPromiseReactionsFunction> resolve = factory->CreateJSPromiseReactionsFunction(
364514f5e3Sopenharmony_ci        MethodIndex::BUILTINS_PROMISE_HANDLER_RESOLVE);
374514f5e3Sopenharmony_ci    // 3. Set the [[Promise]] internal slot of resolve to promise.
384514f5e3Sopenharmony_ci    resolve->SetPromise(thread, promise);
394514f5e3Sopenharmony_ci    // 4. Set the [[AlreadyResolved]] internal slot of resolve to alreadyResolved.
404514f5e3Sopenharmony_ci    resolve->SetAlreadyResolved(thread, record);
414514f5e3Sopenharmony_ci    // 5. Let reject be a new built-in function object as defined in Promise Reject Functions (25.4.1.3.1).
424514f5e3Sopenharmony_ci    JSHandle<JSPromiseReactionsFunction> reject = factory->CreateJSPromiseReactionsFunction(
434514f5e3Sopenharmony_ci        MethodIndex::BUILTINS_PROMISE_HANDLER_REJECT);
444514f5e3Sopenharmony_ci    // 6. Set the [[Promise]] internal slot of reject to promise.
454514f5e3Sopenharmony_ci    reject->SetPromise(thread, promise);
464514f5e3Sopenharmony_ci    // 7. Set the [[AlreadyResolved]] internal slot of reject to alreadyResolved.
474514f5e3Sopenharmony_ci    reject->SetAlreadyResolved(thread, record);
484514f5e3Sopenharmony_ci    // 8. Return a new Record { [[Resolve]]: resolve, [[Reject]]: reject }.
494514f5e3Sopenharmony_ci    JSHandle<ResolvingFunctionsRecord> reactions = factory->NewResolvingFunctionsRecord();
504514f5e3Sopenharmony_ci    reactions->SetResolveFunction(thread, resolve.GetTaggedValue());
514514f5e3Sopenharmony_ci    reactions->SetRejectFunction(thread, reject.GetTaggedValue());
524514f5e3Sopenharmony_ci    return reactions;
534514f5e3Sopenharmony_ci}
544514f5e3Sopenharmony_ci
554514f5e3Sopenharmony_ciJSTaggedValue JSPromise::FulfillPromise(JSThread *thread, const JSHandle<JSPromise> &promise,
564514f5e3Sopenharmony_ci                                        const JSHandle<JSTaggedValue> &value)
574514f5e3Sopenharmony_ci{
584514f5e3Sopenharmony_ci    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
594514f5e3Sopenharmony_ci    // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending".
604514f5e3Sopenharmony_ci    ASSERT_PRINT(promise->GetPromiseState() == PromiseState::PENDING, "FulfillPromise: state must be pending");
614514f5e3Sopenharmony_ci    // 2. Let reactions be the value of promise's [[PromiseFulfillReactions]] internal slot.
624514f5e3Sopenharmony_ci    JSHandle<TaggedQueue> reactions(thread, promise->GetPromiseFulfillReactions());
634514f5e3Sopenharmony_ci    // 3. Set the value of promise's [[PromiseResult]] internal slot to value.
644514f5e3Sopenharmony_ci    promise->SetPromiseResult(thread, value);
654514f5e3Sopenharmony_ci    // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined.
664514f5e3Sopenharmony_ci    promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined(), SKIP_BARRIER);
674514f5e3Sopenharmony_ci    // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined.
684514f5e3Sopenharmony_ci    promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined(), SKIP_BARRIER);
694514f5e3Sopenharmony_ci    // 6. Set the value of promise's [[PromiseState]] internal slot to "fulfilled".
704514f5e3Sopenharmony_ci    promise->SetPromiseState(PromiseState::FULFILLED);
714514f5e3Sopenharmony_ci    // 7. Return TriggerPromiseReactions(reactions, reason).
724514f5e3Sopenharmony_ci    return TriggerPromiseReactions(thread, reactions, value);
734514f5e3Sopenharmony_ci}
744514f5e3Sopenharmony_ci
754514f5e3Sopenharmony_ciJSHandle<PromiseCapability> JSPromise::NewPromiseCapability(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
764514f5e3Sopenharmony_ci{
774514f5e3Sopenharmony_ci    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
784514f5e3Sopenharmony_ci    // 1. If IsConstructor(C) is false, throw a TypeError exception.
794514f5e3Sopenharmony_ci    if (!obj->IsConstructor()) {
804514f5e3Sopenharmony_ci        THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: obj is not constructor!",
814514f5e3Sopenharmony_ci                                    factory->NewPromiseCapability());
824514f5e3Sopenharmony_ci    }
834514f5e3Sopenharmony_ci    // 2. NOTE C is assumed to be a constructor function that supports the parameter conventions of the Promise
844514f5e3Sopenharmony_ci    //    constructor (see 25.4.3.1).
854514f5e3Sopenharmony_ci    // 3. Let promiseCapability be a new PromiseCapability { [[Promise]]: undefined, [[Resolve]]: undefined,
864514f5e3Sopenharmony_ci    //    [[Reject]]: undefined }.
874514f5e3Sopenharmony_ci    JSHandle<PromiseCapability> promiseCapability = factory->NewPromiseCapability();
884514f5e3Sopenharmony_ci    // 4. Let executor be a new built-in function object as defined in GetCapabilitiesExecutor Functions
894514f5e3Sopenharmony_ci    //    (25.4.1.5.1).
904514f5e3Sopenharmony_ci    JSHandle<JSPromiseExecutorFunction> executor = factory->CreateJSPromiseExecutorFunction();
914514f5e3Sopenharmony_ci    // 5. Set the [[Capability]] internal slot of executor to promiseCapability.
924514f5e3Sopenharmony_ci    executor->SetCapability(thread, promiseCapability.GetTaggedValue());
934514f5e3Sopenharmony_ci    // 6. Let promise be Construct(C, «executor»).
944514f5e3Sopenharmony_ci    // 7. ReturnIfAbrupt(promise).
954514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
964514f5e3Sopenharmony_ci    EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, obj, undefined, undefined, 1);
974514f5e3Sopenharmony_ci    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability());
984514f5e3Sopenharmony_ci    info->SetCallArg(executor.GetTaggedValue());
994514f5e3Sopenharmony_ci    JSTaggedValue result = JSFunction::Construct(info);
1004514f5e3Sopenharmony_ci    JSHandle<JSPromise> promise(thread, result);
1014514f5e3Sopenharmony_ci    RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, factory->NewPromiseCapability());
1024514f5e3Sopenharmony_ci    // 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception.
1034514f5e3Sopenharmony_ci    if (!promiseCapability->GetResolve().IsCallable()) {
1044514f5e3Sopenharmony_ci        THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: resolve is not a callable function!",
1054514f5e3Sopenharmony_ci                                    factory->NewPromiseCapability());
1064514f5e3Sopenharmony_ci    }
1074514f5e3Sopenharmony_ci    // 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception.
1084514f5e3Sopenharmony_ci    if (!promiseCapability->GetReject().IsCallable()) {
1094514f5e3Sopenharmony_ci        THROW_TYPE_ERROR_AND_RETURN(thread, "NewPromiseCapability: reject is not a callable function!",
1104514f5e3Sopenharmony_ci                                    factory->NewPromiseCapability());
1114514f5e3Sopenharmony_ci    }
1124514f5e3Sopenharmony_ci    // 10. Set promiseCapability.[[Promise]] to promise.
1134514f5e3Sopenharmony_ci    promiseCapability->SetPromise(thread, promise);
1144514f5e3Sopenharmony_ci    // 11. Return promiseCapability.
1154514f5e3Sopenharmony_ci    return promiseCapability;
1164514f5e3Sopenharmony_ci}
1174514f5e3Sopenharmony_ci
1184514f5e3Sopenharmony_cibool JSPromise::IsPromise(const JSHandle<JSTaggedValue> &value)
1194514f5e3Sopenharmony_ci{
1204514f5e3Sopenharmony_ci    // 1. If Type(x) is not Object, return false.
1214514f5e3Sopenharmony_ci    if (!value->IsECMAObject()) {
1224514f5e3Sopenharmony_ci        return false;
1234514f5e3Sopenharmony_ci    }
1244514f5e3Sopenharmony_ci    // 2. If x does not have a [[PromiseState]] internal slot, return false.
1254514f5e3Sopenharmony_ci    if (!value->IsJSPromise()) {
1264514f5e3Sopenharmony_ci        return false;
1274514f5e3Sopenharmony_ci    }
1284514f5e3Sopenharmony_ci    // 3. Return true
1294514f5e3Sopenharmony_ci    return true;
1304514f5e3Sopenharmony_ci}
1314514f5e3Sopenharmony_ci
1324514f5e3Sopenharmony_ciJSTaggedValue JSPromise::RejectPromise(JSThread *thread, const JSHandle<JSPromise> &promise,
1334514f5e3Sopenharmony_ci                                       const JSHandle<JSTaggedValue> &reason)
1344514f5e3Sopenharmony_ci{
1354514f5e3Sopenharmony_ci    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1364514f5e3Sopenharmony_ci    // 1. Assert: the value of promise's [[PromiseState]] internal slot is "pending".
1374514f5e3Sopenharmony_ci    ASSERT_PRINT(promise->GetPromiseState() == PromiseState::PENDING, "RejectPromise: state must be pending");
1384514f5e3Sopenharmony_ci    // 2. Let reactions be the value of promise's [[PromiseRejectReactions]] internal slot.
1394514f5e3Sopenharmony_ci    JSHandle<TaggedQueue> reactions(thread, TaggedQueue::Cast(promise->GetPromiseRejectReactions().GetTaggedObject()));
1404514f5e3Sopenharmony_ci    // 3. Set the value of promise's [[PromiseResult]] internal slot to reason.
1414514f5e3Sopenharmony_ci    promise->SetPromiseResult(thread, reason);
1424514f5e3Sopenharmony_ci    // 4. Set the value of promise's [[PromiseFulfillReactions]] internal slot to undefined.
1434514f5e3Sopenharmony_ci    promise->SetPromiseFulfillReactions(thread, globalConst->GetHandledUndefined(), SKIP_BARRIER);
1444514f5e3Sopenharmony_ci    // 5. Set the value of promise's [[PromiseRejectReactions]] internal slot to undefined.
1454514f5e3Sopenharmony_ci    promise->SetPromiseRejectReactions(thread, globalConst->GetHandledUndefined(), SKIP_BARRIER);
1464514f5e3Sopenharmony_ci    // 6. Set the value of promise's [[PromiseState]] internal slot to "rejected".
1474514f5e3Sopenharmony_ci    promise->SetPromiseState(PromiseState::REJECTED);
1484514f5e3Sopenharmony_ci    // 7. When a promise is rejected without any handlers, it is called with its operation argument set to "reject".
1494514f5e3Sopenharmony_ci    if (!promise->GetPromiseIsHandled()) {
1504514f5e3Sopenharmony_ci        thread->GetCurrentEcmaContext()->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::REJECT);
1514514f5e3Sopenharmony_ci    }
1524514f5e3Sopenharmony_ci    // 8. Return TriggerPromiseReactions(reactions, reason).
1534514f5e3Sopenharmony_ci    return TriggerPromiseReactions(thread, reactions, reason);
1544514f5e3Sopenharmony_ci}
1554514f5e3Sopenharmony_ci
1564514f5e3Sopenharmony_ciJSTaggedValue JSPromise::TriggerPromiseReactions(JSThread *thread, const JSHandle<TaggedQueue> &reactions,
1574514f5e3Sopenharmony_ci                                                 const JSHandle<JSTaggedValue> &argument)
1584514f5e3Sopenharmony_ci{
1594514f5e3Sopenharmony_ci    // 1. Repeat for each reaction in reactions, in original insertion order
1604514f5e3Sopenharmony_ci    // a. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, «reaction, argument»).
1614514f5e3Sopenharmony_ci    JSHandle<job::MicroJobQueue> job = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
1624514f5e3Sopenharmony_ci    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
1634514f5e3Sopenharmony_ci    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
1644514f5e3Sopenharmony_ci    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
1654514f5e3Sopenharmony_ci    JSHandle<JSFunction> promiseReactionsJob(globalEnv->GetPromiseReactionJob());
1664514f5e3Sopenharmony_ci    JSMutableHandle<PromiseReaction> reaction(thread, JSTaggedValue::Undefined());
1674514f5e3Sopenharmony_ci    while (!reactions->Empty()) {
1684514f5e3Sopenharmony_ci        reaction.Update(reactions->Pop(thread));
1694514f5e3Sopenharmony_ci        JSHandle<TaggedArray> arguments = factory->NewTaggedArray(2);  // 2 means the length of new array
1704514f5e3Sopenharmony_ci        arguments->Set(thread, 0, reaction);
1714514f5e3Sopenharmony_ci        arguments->Set(thread, 1, argument);
1724514f5e3Sopenharmony_ci        job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments);
1734514f5e3Sopenharmony_ci    }
1744514f5e3Sopenharmony_ci    // 2. Return undefined.
1754514f5e3Sopenharmony_ci    return globalConst->GetUndefined();
1764514f5e3Sopenharmony_ci}
1774514f5e3Sopenharmony_ci
1784514f5e3Sopenharmony_ciJSHandle<JSTaggedValue> JSPromise::IfThrowGetThrowValue(JSThread *thread)
1794514f5e3Sopenharmony_ci{
1804514f5e3Sopenharmony_ci    return JSHandle<JSTaggedValue>(thread, thread->GetException());
1814514f5e3Sopenharmony_ci}
1824514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
183