* Copyright (c) 2021-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/builtins/builtins_promise.h"
#include "ecmascript/builtins/builtins_promise_handler.h"
#include "ecmascript/builtins/builtins_promise_job.h"
#include "ecmascript/debugger/js_debugger_manager.h"
#include "ecmascript/dfx/stackinfo/async_stack_trace.h"
#include "ecmascript/global_env.h"
#include "ecmascript/interpreter/interpreter.h"
#include "ecmascript/jobs/micro_job_queue.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_iterator.h"
namespace panda::ecmascript::builtins {
using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob;
JSTaggedValue BuiltinsPromise::PromiseConstructor(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Constructor);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> executor = BuiltinsBase::GetCallArg(argv, 0);
if (!executor->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSObject> newObject = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSPromise> instancePromise = JSHandle<JSPromise>::Cast(newObject);
JSHandle<ResolvingFunctionsRecord> resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise);
auto resolveFunc = resolvingFunction->GetResolveFunction(thread);
auto rejectFunc = resolvingFunction->GetRejectFunction(thread);
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
const uint32_t argsLength = 2;
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, executor, undefined, undefined, argsLength);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(resolveFunc, rejectFunc);
JSTaggedValue taggedValue = JSFunction::Call(info);
JSHandle<JSTaggedValue> completionValue(thread, taggedValue);
if (thread->HasPendingException()) {
completionValue = JSPromise::IfThrowGetThrowValue(thread);
thread->ClearExceptionAndExtraErrorMessage();
JSHandle<JSTaggedValue> reject(thread, resolvingFunction->GetRejectFunction(thread));
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
runtimeInfo->SetCallArg(completionValue.GetTaggedValue());
JSFunction::Call(runtimeInfo);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
return instancePromise.GetTaggedValue();
}
JSTaggedValue BuiltinsPromise::All(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, All);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
EcmaVM *ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> ctor = GetThis(argv);
if (!ctor->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> speciesSymbol = thread->GlobalConstants()->GetHandledSpeciesSymbol();
JSHandle<JSTaggedValue> sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue());
if (!sctor->IsUndefined() && !sctor->IsNull()) {
ctor = sctor;
}
JSHandle<PromiseCapability> capa = JSPromise::NewPromiseCapability(thread, ctor);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue());
JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0));
if (thread->HasPendingException()) {
itor = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa);
bool done = false;
JSHandle<PromiseIteratorRecord> itRecord = factory->NewPromiseIteratorRecord(itor, done);
JSTaggedValue resultValue = PerformPromiseAll(thread, itRecord, ctor, capa);
JSHandle<CompletionRecord> result = JSHandle<CompletionRecord>(thread, resultValue);
if (result->IsThrow()) {
thread->ClearExceptionAndExtraErrorMessage();
if (!itRecord->GetDone()) {
JSHandle<JSTaggedValue> closeVal =
JSIterator::IteratorClose(thread, itor, JSHandle<JSTaggedValue>::Cast(result));
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
if (closeVal.GetTaggedValue().IsRecord()) {
result = JSHandle<CompletionRecord>::Cast(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
return result->GetValue(thread);
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
return result->GetValue(thread);
}
return result->GetValue(thread);
}
JSTaggedValue BuiltinsPromise::Race(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Race);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
EcmaVM *ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> speciesSymbol = thread->GlobalConstants()->GetHandledSpeciesSymbol();
JSHandle<JSTaggedValue> speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) {
thisValue = speciesConstructor;
}
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
if (thread->HasPendingException()) {
iterator = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
bool done = false;
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done);
JSHandle<CompletionRecord> result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue);
if (result->IsThrow()) {
thread->ClearExceptionAndExtraErrorMessage();
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> value =
JSIterator::IteratorClose(thread, iterator, JSHandle<JSTaggedValue>::Cast(result));
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
if (value.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(value);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
return result->GetValue(thread);
}
JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> xValue = BuiltinsBase::GetCallArg(argv, 0);
return BuiltinsPromiseHandler::PromiseResolve(thread, thisValue, xValue).GetTaggedValue();
}
JSTaggedValue BuiltinsPromise::Reject(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Reject);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
}
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> reject(thread, promiseCapability->GetReject(thread));
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, reject, undefined, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(reason.GetTaggedValue());
JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSObject> promise(thread, promiseCapability->GetPromise(thread));
return promise.GetTaggedValue();
}
JSTaggedValue BuiltinsPromise::GetSpecies(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, GetSpecies);
return JSTaggedValue(GetThis(argv).GetTaggedValue());
}
JSTaggedValue BuiltinsPromise::Catch(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Catch);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> promise = GetThis(argv);
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
JSHandle<JSTaggedValue> reject = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(undefined.GetTaggedValue(), reject.GetTaggedValue());
return JSFunction::Invoke(info, thenKey);
}
#if ENABLE_LATEST_OPTIMIZATION
JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Then);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
auto ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsJSPromise()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception());
}
JSHandle<JSObject> promise = JSHandle<JSObject>::Cast(thisValue);
JSHandle<JSTaggedValue> defaultFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSMutableHandle<JSTaggedValue> promiseOrCapability(thread, thread->GlobalConstants()->GetHandledUndefined());
if (constructor == env->GetPromiseFunction()) {
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSPromise> jsPromise = factory->NewJSPromise();
promiseOrCapability.Update(jsPromise);
} else {
JSHandle<PromiseCapability> capability = JSPromise::NewPromiseCapability(thread, constructor);
promiseOrCapability.Update(capability);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
}
JSHandle<JSTaggedValue> onFulfilled = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSTaggedValue> onRejected = BuiltinsBase::GetCallArg(argv, 1);
if (ecmaVm->GetJsDebuggerManager()->IsAsyncStackTrace()) {
std::string description;
if (onRejected->IsUndefined()) {
description = "promise.then";
} else if (onFulfilled->IsUndefined()) {
description = "promise.catch";
} else {
description = "promise.finally";
}
if (promiseOrCapability->IsJSPromise()) {
ecmaVm->GetAsyncStackTrace()->InsertAsyncTaskStacks(JSHandle<JSPromise>::Cast(promiseOrCapability),
description);
} else {
auto capability = JSHandle<PromiseCapability>::Cast(promiseOrCapability);
ecmaVm->GetAsyncStackTrace()->InsertAsyncTaskStacks(
JSHandle<JSPromise>(thread, capability->GetPromise(thread)), description);
}
}
if (UNLIKELY(ecmaVm->IsEnableRuntimeAsyncStack())) {
if (promiseOrCapability->IsJSPromise()) {
ecmaVm->GetAsyncStackTraceManager()->SavePromiseNode(JSHandle<JSPromise>::Cast(promiseOrCapability));
} else {
auto capability = JSHandle<PromiseCapability>::Cast(promiseOrCapability);
ecmaVm->GetAsyncStackTraceManager()->SavePromiseNode(JSHandle<JSPromise>(thread,
capability->GetPromise(thread)));
}
}
return PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise), onFulfilled, onRejected,
promiseOrCapability);
}
JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle<JSPromise> &promise,
const JSHandle<JSTaggedValue> &onFulfilled,
const JSHandle<JSTaggedValue> &onRejected,
const JSHandle<JSTaggedValue> &promiseOrCapability)
{
auto ecmaVm = thread->GetEcmaVM();
BUILTINS_API_TRACE(thread, Promise, PerformPromiseThen);
JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSMutableHandle<JSTaggedValue> fulfilled(thread, onFulfilled.GetTaggedValue());
auto globalConst = thread->GlobalConstants();
if (!fulfilled->IsCallable()) {
fulfilled.Update(globalConst->GetIdentityString());
}
JSMutableHandle<JSTaggedValue> rejected(thread, onRejected.GetTaggedValue());
if (!rejected->IsCallable()) {
rejected.Update(globalConst->GetThrowerString());
}
JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
fulfillReaction->SetPromiseOrCapability(thread, promiseOrCapability.GetTaggedValue());
fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue());
JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
rejectReaction->SetPromiseOrCapability(thread, promiseOrCapability.GetTaggedValue());
rejectReaction->SetHandler(thread, rejected.GetTaggedValue());
PromiseState state = promise->GetPromiseState();
if (state == PromiseState::PENDING) {
JSHandle<TaggedQueue> fulfillReactions(thread, promise->GetPromiseFulfillReactions(thread));
TaggedQueue *newQueue =
TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction));
promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue));
JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions(thread));
newQueue = TaggedQueue::Push(thread, rejectReactions, JSHandle<JSTaggedValue>::Cast(rejectReaction));
promise->SetPromiseRejectReactions(thread, JSTaggedValue(newQueue));
} else if (state == PromiseState::FULFILLED) {
JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);
argv->Set(thread, 0, fulfillReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult(thread));
JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
} else if (state == PromiseState::REJECTED) {
JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);
argv->Set(thread, 0, rejectReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult(thread));
if (!promise->GetPromiseIsHandled()) {
JSHandle<JSTaggedValue> reason(thread, JSTaggedValue::Null());
ecmaVm->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::HANDLE);
}
JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
}
promise->SetPromiseIsHandled(true);
if (promiseOrCapability->IsUndefined()) {
return promiseOrCapability.GetTaggedValue();
}
if (promiseOrCapability->IsJSPromise()) {
return promiseOrCapability.GetTaggedValue();
}
return JSHandle<PromiseCapability>::Cast(promiseOrCapability)->GetPromise(thread);
}
#else
JSTaggedValue BuiltinsPromise::Then(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Then);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
auto ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsJSPromise()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception());
}
JSHandle<JSObject> promise = JSHandle<JSObject>::Cast(thisValue);
JSHandle<JSTaggedValue> defaultFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, promise, defaultFunc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<PromiseCapability> resultCapability = JSPromise::NewPromiseCapability(thread, constructor);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
JSHandle<JSTaggedValue> onFulfilled = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSTaggedValue> onRejected = BuiltinsBase::GetCallArg(argv, 1);
if (ecmaVm->GetJsDebuggerManager()->IsAsyncStackTrace()) {
std::string description;
if (onRejected->IsUndefined()) {
description = "promise.then";
} else if (onFulfilled->IsUndefined()) {
description = "promise.catch";
} else {
description = "promise.finally";
}
ecmaVm->GetAsyncStackTrace()->InsertAsyncTaskStacks(
JSHandle<JSPromise>(thread, resultCapability->GetPromise(thread)), description);
}
if (UNLIKELY(ecmaVm->IsEnableRuntimeAsyncStack())) {
ecmaVm->GetAsyncStackTraceManager()->SavePromiseNode(JSHandle<JSPromise>(thread,
resultCapability->GetPromise(thread)));
}
return PerformPromiseThen(thread, JSHandle<JSPromise>::Cast(promise), onFulfilled, onRejected, resultCapability);
}
JSTaggedValue BuiltinsPromise::PerformPromiseThen(JSThread *thread, const JSHandle<JSPromise> &promise,
const JSHandle<JSTaggedValue> &onFulfilled,
const JSHandle<JSTaggedValue> &onRejected,
const JSHandle<PromiseCapability> &capability)
{
auto ecmaVm = thread->GetEcmaVM();
BUILTINS_API_TRACE(thread, Promise, PerformPromiseThen);
JSHandle<job::MicroJobQueue> job = ecmaVm->GetMicroJobQueue();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSMutableHandle<JSTaggedValue> fulfilled(thread, onFulfilled.GetTaggedValue());
auto globalConst = thread->GlobalConstants();
if (!fulfilled->IsCallable()) {
fulfilled.Update(globalConst->GetIdentityString());
}
JSMutableHandle<JSTaggedValue> rejected(thread, onRejected.GetTaggedValue());
if (!rejected->IsCallable()) {
rejected.Update(globalConst->GetThrowerString());
}
JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
fulfillReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
fulfillReaction->SetHandler(thread, fulfilled.GetTaggedValue());
JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
rejectReaction->SetPromiseCapability(thread, capability.GetTaggedValue());
rejectReaction->SetHandler(thread, rejected.GetTaggedValue());
PromiseState state = promise->GetPromiseState();
if (state == PromiseState::PENDING) {
JSHandle<TaggedQueue> fulfillReactions(thread, promise->GetPromiseFulfillReactions(thread));
TaggedQueue *newQueue =
TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction));
promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue));
JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions(thread));
newQueue = TaggedQueue::Push(thread, rejectReactions, JSHandle<JSTaggedValue>::Cast(rejectReaction));
promise->SetPromiseRejectReactions(thread, JSTaggedValue(newQueue));
} else if (state == PromiseState::FULFILLED) {
JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);
argv->Set(thread, 0, fulfillReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult(thread));
JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
} else if (state == PromiseState::REJECTED) {
JSHandle<TaggedArray> argv = factory->NewTaggedArray(2);
argv->Set(thread, 0, rejectReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult(thread));
if (!promise->GetPromiseIsHandled()) {
JSHandle<JSTaggedValue> reason(thread, JSTaggedValue::Null());
ecmaVm->PromiseRejectionTracker(promise, reason, PromiseRejectionEvent::HANDLE);
}
JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
job::MicroJobQueue::EnqueueJob(thread, job, job::QueueType::QUEUE_PROMISE, promiseReactionsJob, argv);
}
promise->SetPromiseIsHandled(true);
return capability->GetPromise(thread);
}
#endif
JSTaggedValue BuiltinsPromise::PerformPromiseAll(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &itRecord,
const JSHandle<JSTaggedValue> &ctor,
const JSHandle<PromiseCapability> &capa)
{
auto ecmaVm = thread->GetEcmaVM();
BUILTINS_API_TRACE(thread, Promise, PerformPromiseAll);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor");
JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
values->SetValue(thread, emptyArray);
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
uint32_t index = 0;
JSHandle<JSTaggedValue> itor(thread, itRecord->GetIterator(thread));
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
while (true) {
[[maybe_unused]] EcmaHandleScope handleScope(thread);
next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
if (thread->HasPendingException()) {
itRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, next);
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
if (next->IsFalse()) {
itRecord->SetDone(true);
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
if (remainCnt->GetValue(thread).IsZero()) {
JSHandle<JSArray> jsArrayValues =
JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, values->GetValue(thread)));
JSHandle<JSTaggedValue> resCapaFunc(thread, capa->GetResolve(thread));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, resCapaFunc, undefined, undefined, 1);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, next);
info->SetCallArg(jsArrayValues.GetTaggedValue());
JSTaggedValue resolveRes = JSFunction::Call(info);
JSHandle<JSTaggedValue> resolveAbrupt(thread, resolveRes);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, resolveAbrupt);
}
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, JSHandle<JSTaggedValue>(thread, capa->GetPromise(thread)));
return resRecord.GetTaggedValue();
}
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
if (thread->HasPendingException()) {
itRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextVal);
JSHandle<TaggedArray> valuesArray =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue(thread)));
valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
valuesArray->Set(thread, index, JSTaggedValue::Undefined());
values->SetValue(thread, valuesArray);
JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, ctor, undefined, 1);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextVal);
info->SetCallArg(nextVal.GetTaggedValue());
JSTaggedValue taggedNextPromise = JSFunction::Invoke(info, resolveKey);
JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextPromise);
JSHandle<JSPromiseAllResolveElementFunction> resoleveElement = factory->NewJSPromiseAllResolveElementFunction();
JSHandle<PromiseRecord> falseRecord = factory->NewPromiseRecord();
falseRecord->SetValue(thread, JSTaggedValue::False());
resoleveElement->SetAlreadyCalled(thread, falseRecord);
resoleveElement->SetIndex(thread, JSTaggedValue(index));
resoleveElement->SetValues(thread, values);
resoleveElement->SetCapabilities(thread, capa);
resoleveElement->SetRemainingElements(thread, remainCnt);
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise,
undefined, 2);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, nextPromise);
runtimeInfo->SetCallArg(resoleveElement.GetTaggedValue(), capa->GetReject(thread));
JSTaggedValue taggedResult = JSFunction::Invoke(runtimeInfo, thenKey);
JSHandle<JSTaggedValue> result(thread, taggedResult);
RETURN_COMPLETION_VALUE_IF_ABRUPT(thread, result);
++index;
}
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseRace(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<PromiseCapability> &capability,
const JSHandle<JSTaggedValue> &constructor)
{
BUILTINS_API_TRACE(thread, Promise, PerformPromiseRace);
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> iterator(thread, iteratorRecord->GetIterator(thread));
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
while (true) {
next.Update(JSIterator::IteratorStep(thread, iterator).GetTaggedValue());
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
RETURN_COMPLETION_IF_ABRUPT(thread, next);
if (next->IsFalse()) {
iteratorRecord->SetDone(true);
JSHandle<JSTaggedValue> promise(thread, capability->GetPromise(thread));
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::NORMAL, promise);
return completionRecord;
}
JSHandle<JSTaggedValue> nextValue = JSIterator::IteratorValue(thread, next);
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
nextValue = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
JSHandle<JSTaggedValue> resolveStr = globalConst->GetHandledPromiseResolveString();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, constructor, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, nextValue);
info->SetCallArg(nextValue.GetTaggedValue());
JSTaggedValue result = JSFunction::Invoke(info, resolveStr);
JSHandle<JSTaggedValue> nextPromise(thread, result);
if (thread->HasPendingException()) {
nextPromise = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
JSHandle<JSTaggedValue> thenStr = globalConst->GetHandledPromiseThenString();
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2);
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
runtimeInfo->SetCallArg(capability->GetResolve(thread), capability->GetReject(thread));
result = JSFunction::Invoke(runtimeInfo, thenStr);
JSHandle<JSTaggedValue> handleResult(thread, result);
if (thread->HasPendingException()) {
handleResult = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_COMPLETION_IF_ABRUPT(thread, handleResult);
}
}
JSTaggedValue BuiltinsPromise::GetPromiseResolve(JSThread *thread, JSHandle<JSTaggedValue> promiseConstructor)
{
BUILTINS_API_TRACE(thread, Promise, GetPromiseResolve);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
JSHandle<JSTaggedValue> promiseResolve = JSObject::GetProperty(thread, promiseConstructor, resolveKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!promiseResolve->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "promiseResolve is not callable", JSTaggedValue::Exception());
}
return promiseResolve.GetTaggedValue();
}
JSTaggedValue BuiltinsPromise::Any(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Any);
JSThread *thread = argv->GetThread();
auto ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
if (thread->HasPendingException()) {
promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
if (thread->HasPendingException()) {
iterator = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
JSHandle<CompletionRecord> result = PerformPromiseAny(thread, iteratorRecord, thisValue,
promiseCapability, promiseResolve);
if (result->IsThrow()) {
thread->ClearExceptionAndExtraErrorMessage();
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
if (closeVal.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
return result->GetValue(thread);
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAny(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<JSTaggedValue> &constructor,
const JSHandle<PromiseCapability> &resultCapability,
const JSHandle<JSTaggedValue> &promiseResolve)
{
BUILTINS_API_TRACE(thread, Promise, PerformPromiseAny);
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<PromiseRecord> errors = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
errors->SetValue(thread, emptyArray);
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
uint32_t index = 0;
JSHandle<JSTaggedValue> iter(thread, iteratorRecord->GetIterator(thread));
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
while (true) {
next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
RETURN_COMPLETION_IF_ABRUPT(thread, next);
if (next->IsFalse()) {
iteratorRecord->SetDone(true);
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
if (remainCnt->GetValue(thread).IsZero()) {
JSHandle<JSObject> error = factory->NewJSAggregateError();
JSHandle<JSTaggedValue> errorsKey(thread, globalConst->GetErrorsString());
JSHandle<TaggedArray> errorsArray =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, errors->GetValue(thread)));
JSHandle<JSTaggedValue> errorsValue(JSArray::CreateArrayFromList(thread, errorsArray));
PropertyDescriptor msgDesc(thread, errorsValue, true, false, true);
JSHandle<JSTaggedValue> errorTagged = JSHandle<JSTaggedValue>::Cast(error);
JSTaggedValue::DefinePropertyOrThrow(thread, errorTagged, errorsKey, msgDesc);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(CompletionRecord, thread);
JSHandle<JSTaggedValue> errorCompletion(
factory->NewCompletionRecord(CompletionRecordType::THROW, errorTagged));
JSHandle<CompletionRecord> errorResult = JSHandle<CompletionRecord>::Cast(errorCompletion);
return errorResult;
}
JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapability->GetPromise(thread));
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, resultCapabilityHandle);
return resRecord;
}
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
JSHandle<JSTaggedValue> errorsHandle(thread, errors->GetValue(thread));
JSHandle<TaggedArray> errorsArray = JSHandle<TaggedArray>::Cast(errorsHandle);
errorsArray = TaggedArray::SetCapacity(thread, errorsArray, index + 1);
errorsArray->Set(thread, index, JSTaggedValue::Undefined());
errors->SetValue(thread, errorsArray);
EcmaRuntimeCallInfo *taggedInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, promiseResolve, constructor, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
taggedInfo->SetCallArg(nextVal.GetTaggedValue());
JSTaggedValue taggedNextPromise = JSFunction::Call(taggedInfo);
JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> promiseResult = JSPromise::IfThrowGetThrowValue(thread);
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::THROW, promiseResult);
return completionRecord;
}
JSHandle<JSPromiseAnyRejectElementFunction> onRejected = factory->NewJSPromiseAnyRejectElementFunction();
onRejected->SetAlreadyCalled(thread, JSTaggedValue::False());
onRejected->SetIndex(index);
onRejected->SetErrors(thread, errors);
onRejected->SetCapability(thread, resultCapability);
onRejected->SetRemainingElements(thread, remainCnt);
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapability->GetResolve(thread));
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2);
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
invokeInfo->SetCallArg(resCapaFunc.GetTaggedValue(), onRejected.GetTaggedValue());
JSFunction::Invoke(invokeInfo, thenKey);
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> taggedResult = JSPromise::IfThrowGetThrowValue(thread);
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::THROW, taggedResult);
return completionRecord;
}
++index;
}
}
JSTaggedValue BuiltinsPromise::AllSettled(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, AllSettled);
JSThread *thread = argv->GetThread();
auto ecmaVm = thread->GetEcmaVM();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
if (thread->HasPendingException()) {
promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
if (thread->HasPendingException()) {
iterator = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
JSHandle<CompletionRecord> result = PerformPromiseAllSettled(thread, iteratorRecord, thisValue,
promiseCapability, promiseResolve);
if (result->IsThrow()) {
thread->ClearExceptionAndExtraErrorMessage();
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
if (closeVal.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue(thread);
}
return result->GetValue(thread);
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAllSettled(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iterRecord,
const JSHandle<JSTaggedValue> &constructor,
const JSHandle<PromiseCapability> &resultCapa,
const JSHandle<JSTaggedValue> &promiseResolve)
{
BUILTINS_API_TRACE(thread, Promise, PerformPromiseAllSettled);
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
values->SetValue(thread, emptyArray);
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
uint32_t index = 0;
JSHandle<JSTaggedValue> iter(thread, iterRecord->GetIterator(thread));
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
while (true) {
next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
if (thread->HasPendingException()) {
iterRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
RETURN_COMPLETION_IF_ABRUPT(thread, next);
if (next->IsFalse()) {
iterRecord->SetDone(true);
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue(thread)));
if (remainCnt->GetValue(thread).IsZero()) {
JSHandle<TaggedArray> taggedValues(thread, values->GetValue(thread));
JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, taggedValues);
JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapa->GetResolve(thread));
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, resCapaFunc, undefined, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, next);
info->SetCallArg(jsArrayValues.GetTaggedValue());
JSFunction::Call(info);
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> throwValue = JSPromise::IfThrowGetThrowValue(thread);
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::THROW, throwValue);
return completionRecord;
}
}
JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapa->GetPromise(thread));
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, resultCapabilityHandle);
return resRecord;
}
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
if (thread->HasPendingException()) {
iterRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
JSHandle<JSTaggedValue> valuesHandle(thread, values->GetValue(thread));
JSHandle<TaggedArray> valuesArray = JSHandle<TaggedArray>::Cast(valuesHandle);
valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
valuesArray->Set(thread, index, JSTaggedValue::Undefined());
values->SetValue(thread, valuesArray);
EcmaRuntimeCallInfo *taggedInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, promiseResolve, constructor, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
taggedInfo->SetCallArg(nextVal.GetTaggedValue());
JSTaggedValue taggedNextPromise = JSFunction::Call(taggedInfo);
JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> promiseResult = JSPromise::IfThrowGetThrowValue(thread);
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::THROW, promiseResult);
return completionRecord;
}
JSHandle<JSPromiseAllSettledElementFunction> onFulfilled =
factory->NewJSPromiseAllSettledResolveElementFunction();
JSHandle<PromiseRecord> alreadyCalled = factory->NewPromiseRecord();
alreadyCalled->SetValue(thread, JSTaggedValue::False());
onFulfilled->SetAlreadyCalled(thread, alreadyCalled);
onFulfilled->SetIndex(index);
onFulfilled->SetValues(thread, values);
onFulfilled->SetCapability(thread, resultCapa);
onFulfilled->SetRemainingElements(thread, remainCnt);
JSHandle<JSPromiseAllSettledElementFunction> onRejected =
factory->NewJSPromiseAllSettledRejectElementFunction();
onRejected->SetAlreadyCalled(thread, alreadyCalled);
onRejected->SetIndex(index);
onRejected->SetValues(thread, values);
onRejected->SetCapability(thread, resultCapa);
onRejected->SetRemainingElements(thread, remainCnt);
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue(thread)));
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2);
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
invokeInfo->SetCallArg(onFulfilled.GetTaggedValue(), onRejected.GetTaggedValue());
JSFunction::Invoke(invokeInfo, thenKey);
if (thread->HasPendingException()) {
JSHandle<JSTaggedValue> taggedResult = JSPromise::IfThrowGetThrowValue(thread);
JSHandle<CompletionRecord> completionRecord =
factory->NewCompletionRecord(CompletionRecordType::THROW, taggedResult);
return completionRecord;
}
++index;
}
}
JSTaggedValue BuiltinsPromise::Finally(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Finally);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
auto ecmaVm = thread->GetEcmaVM();
JSHandle<GlobalEnv> env = ecmaVm->GetGlobalEnv();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> promise = GetThis(argv);
if (!promise->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
}
JSHandle<JSTaggedValue> onFinally = BuiltinsBase::GetCallArg(argv, 0);
JSHandle<JSObject> ctor = JSHandle<JSObject>::Cast(promise);
JSHandle<JSTaggedValue> promiseFunc = JSHandle<JSTaggedValue>::Cast(env->GetPromiseFunction());
JSHandle<JSTaggedValue> constructor = JSObject::SpeciesConstructor(thread, ctor, promiseFunc);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
ASSERT_PRINT(constructor->IsConstructor(), "constructor is not constructor");
JSHandle<JSTaggedValue> thenFinally;
JSHandle<JSTaggedValue> catchFinally;
if (!onFinally->IsCallable()) {
thenFinally = onFinally;
catchFinally = onFinally;
} else {
JSHandle<JSPromiseFinallyFunction> thenFinallyFun =
factory->NewJSPromiseThenFinallyFunction();
thenFinallyFun->SetConstructor(thread, constructor);
thenFinallyFun->SetOnFinally(thread, onFinally);
thenFinally = JSHandle<JSTaggedValue>(thenFinallyFun);
JSHandle<JSPromiseFinallyFunction> catchFinallyFun =
factory->NewJSPromiseCatchFinallyFunction();
catchFinallyFun->SetConstructor(thread, constructor);
catchFinallyFun->SetOnFinally(thread, onFinally);
catchFinally = JSHandle<JSTaggedValue>(catchFinallyFun);
}
JSHandle<JSTaggedValue> thenKey(globalConst->GetHandledPromiseThenString());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
invokeInfo->SetCallArg(thenFinally.GetTaggedValue(), catchFinally.GetTaggedValue());
return JSFunction::Invoke(invokeInfo, thenKey);
}
}