g00416891 db76d5d1e2 Remove redundant maybe_unused
Signed-off-by: g00416891 <guobingbing3@huawei.com>
Change-Id: Id437f8983ec76aab4ac72c161c5d818dd36b0896
2023-01-04 11:06:58 +08:00

1096 lines
60 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2021 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/ecma_runtime_call_info.h"
#include "ecmascript/ecma_vm.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_function.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_iterator.h"
#include "ecmascript/js_promise.h"
#include "ecmascript/js_tagged_number.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/mem/assert_scope.h"
#include "ecmascript/object_factory.h"
namespace panda::ecmascript::builtins {
using BuiltinsPromiseJob = builtins::BuiltinsPromiseJob;
// 25.4.3.1 Promise ( executor )
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();
// 1. If NewTarget is undefined, throw a TypeError exception.
JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv);
if (newTarget->IsUndefined()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: NewTarget is undefined", JSTaggedValue::Exception());
}
// 2. If IsCallable(executor) is false, throw a TypeError exception.
JSHandle<JSTaggedValue> executor = BuiltinsBase::GetCallArg(argv, 0);
if (!executor->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "PromiseConstructor: executor is not callable", JSTaggedValue::Exception());
}
// 3. Let promise be OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%",
// «[[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]]» ).
// 4. ReturnIfAbrupt(promise).
JSHandle<JSTaggedValue> constructor = GetConstructor(argv);
JSHandle<JSPromise> instancePromise =
JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), newTarget));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Set promise's [[PromiseState]] internal slot to "pending".
// 6. Set promise's [[PromiseFulfillReactions]] internal slot to a new empty List.
// 7. Set promise's [[PromiseRejectReactions]] internal slot to a new empty List.
// 8. Let resolvingFunctions be CreateResolvingFunctions(promise).
JSHandle<ResolvingFunctionsRecord> resolvingFunction = JSPromise::CreateResolvingFunctions(thread, instancePromise);
// 9. Let completion be Call(executor, undefined, «resolvingFunctions.[[Resolve]], resolvingFunctions.[[reject]])
auto resolveFunc = resolvingFunction->GetResolveFunction();
auto rejectFunc = resolvingFunction->GetRejectFunction();
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
const int32_t argsLength = 2; // 2: «resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]]»
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);
// 10. If completion is an abrupt completion, then
// a. Let status be Call(resolvingFunctions.[[Reject]], undefined, «completion.[[value]]»).
// b. ReturnIfAbrupt(status).
if (thread->HasPendingException()) {
completionValue = JSPromise::IfThrowGetThrowValue(thread);
thread->ClearException();
JSHandle<JSTaggedValue> reject(thread, resolvingFunction->GetRejectFunction());
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);
}
// 11. Return promise.
return instancePromise.GetTaggedValue();
}
// 25.4.4.1 Promise.all ( iterable )
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();
// 1. Let C be the this value.
JSHandle<JSTaggedValue> ctor = GetThis(argv);
// 2. If Type(C) is not Object, throw a TypeError exception.
if (!ctor->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Promise ALL: this value is not object", JSTaggedValue::Exception());
}
// 3. Let S be Get(C, @@species).
// 4. ReturnIfAbrupt(S).
JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
JSHandle<JSTaggedValue> sctor = JSObject::GetProperty(thread, ctor, speciesSymbol).GetValue();
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, sctor.GetTaggedValue());
// 5. If S is neither undefined nor null, let C be S.
if (!sctor->IsUndefined() && !sctor->IsNull()) {
ctor = sctor;
}
// 6. Let promiseCapability be NewPromiseCapability(C).
JSHandle<PromiseCapability> capa = JSPromise::NewPromiseCapability(thread, ctor);
// 7. ReturnIfAbrupt(promiseCapability).
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, capa.GetTaggedValue());
// 8. Let iterator be GetIterator(iterable).
JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, GetCallArg(argv, 0));
// 9. IfAbruptRejectPromise(iterator, promiseCapability).
if (thread->HasPendingException()) {
itor = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, itor, capa);
// 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
bool done = false;
JSHandle<PromiseIteratorRecord> itRecord = factory->NewPromiseIteratorRecord(itor, done);
// 11. Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
JSHandle<CompletionRecord> result = PerformPromiseAll(thread, itRecord, ctor, capa);
// 12. If result is an abrupt completion,
if (result->IsThrow()) {
thread->ClearException();
// a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator, result).
// b. IfAbruptRejectPromise(result, promiseCapability).
if (!itRecord->GetDone()) {
JSHandle<JSTaggedValue> closeVal =
JSIterator::IteratorClose(thread, itor, JSHandle<JSTaggedValue>::Cast(result));
if (closeVal.GetTaggedValue().IsRecord()) {
result = JSHandle<CompletionRecord>::Cast(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
return result->GetValue();
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, capa);
return result->GetValue();
}
// 13. Return Completion(result).
return result->GetValue();
}
// 25.4.4.3 Promise.race ( iterable )
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();
// 1. Let C be the this value.
// 2. If Type(C) is not Object, throw a TypeError exception.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Race: this value is not object", JSTaggedValue::Exception());
}
// 3. Let S be Get(C, @@species).
// 4. ReturnIfAbrupt(S).
// 5. If S is neither undefined nor null, let C be S.
JSHandle<JSTaggedValue> speciesSymbol = env->GetSpeciesSymbol();
JSHandle<JSTaggedValue> speciesConstructor = JSObject::GetProperty(thread, thisValue, speciesSymbol).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (!(speciesConstructor->IsUndefined() || speciesConstructor->IsNull())) {
thisValue = speciesConstructor;
}
// 6. Let promiseCapability be NewPromiseCapability(C).
// 7. ReturnIfAbrupt(promiseCapability).
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 8. Let iterator be GetIterator(iterable).
// 9. IfAbruptRejectPromise(iterator, 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);
// 10. Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
bool done = false;
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, done);
// 11. Let result be PerformPromiseRace(iteratorRecord, promiseCapability, C).
// 12. If result is an abrupt completion, then
// a. If iteratorRecord.[[done]] is false, let result be IteratorClose(iterator,result).
// b. IfAbruptRejectPromise(result, promiseCapability).
// 13. Return Completion(result).
JSHandle<CompletionRecord> result = PerformPromiseRace(thread, iteratorRecord, promiseCapability, thisValue);
if (result->IsThrow()) {
thread->ClearException();
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> value =
JSIterator::IteratorClose(thread, iterator, JSHandle<JSTaggedValue>::Cast(result));
if (value.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(value);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
return result->GetValue();
}
// 25.4.4.5 Promise.resolve ( x )
JSTaggedValue BuiltinsPromise::Resolve(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
BUILTINS_API_TRACE(argv->GetThread(), Promise, Resolve);
JSThread *thread = argv->GetThread();
[[maybe_unused]] EcmaHandleScope handleScope(thread);
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// 1. Let C be the this value.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
// 2. If Type(C) is not Object, throw a TypeError exception.
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Resolve: this value is not object", JSTaggedValue::Exception());
}
// 3. If IsPromise(x) is true,
// a. Let xConstructor be Get(x, "constructor").
// b. ReturnIfAbrupt(xConstructor).
// c. If SameValue(xConstructor, C) is true, return x.
JSHandle<JSTaggedValue> xValue = BuiltinsBase::GetCallArg(argv, 0);
if (xValue->IsJSPromise()) {
JSHandle<JSTaggedValue> ctorKey(globalConst->GetHandledConstructorString());
JSHandle<JSTaggedValue> ctorValue = JSObject::GetProperty(thread, xValue, ctorKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
if (JSTaggedValue::SameValue(ctorValue.GetTaggedValue(), thisValue.GetTaggedValue())) {
JSHandle<JSObject> value = JSHandle<JSObject>::Cast(xValue);
return value.GetTaggedValue();
}
}
// 4. Let promiseCapability be NewPromiseCapability(C).
// 5. ReturnIfAbrupt(promiseCapability).
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 6. Let resolveResult be Call(promiseCapability.[[Resolve]], undefined, «x»).
// 7. ReturnIfAbrupt(resolveResult).
JSHandle<JSTaggedValue> resolve(thread, promiseCapability->GetResolve());
JSHandle<JSTaggedValue> undefined = globalConst->GetHandledUndefined();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, resolve, undefined, undefined, 1);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(xValue.GetTaggedValue());
JSFunction::Call(info);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 8. Return promiseCapability.[[Promise]].
JSHandle<JSObject> promise(thread, promiseCapability->GetPromise());
return promise.GetTaggedValue();
}
// 25.4.4.4 Promise.reject ( r )
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();
// 1. Let C be the this value.
// 2. If Type(C) is not Object, throw a TypeError exception.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
if (!thisValue->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
}
// 3. Let promiseCapability be NewPromiseCapability(C).
// 4. ReturnIfAbrupt(promiseCapability).
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 5. Let rejectResult be Call(promiseCapability.[[Reject]], undefined, «r»).
// 6. ReturnIfAbrupt(rejectResult).
JSHandle<JSTaggedValue> reason = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> reject(thread, promiseCapability->GetReject());
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);
// 7. Return promiseCapability.[[Promise]].
JSHandle<JSObject> promise(thread, promiseCapability->GetPromise());
return promise.GetTaggedValue();
}
// 25.4.4.6 get Promise [ @@species ]
JSTaggedValue BuiltinsPromise::GetSpecies(EcmaRuntimeCallInfo *argv)
{
ASSERT(argv);
return JSTaggedValue(GetThis(argv).GetTaggedValue());
}
// 25.4.5.1 Promise.prototype.catch ( onRejected )
JSTaggedValue BuiltinsPromise::Catch(EcmaRuntimeCallInfo *argv)
{
// 1. Let promise be the this value.
// 2. Return Invoke(promise, "then", «undefined, onRejected»).
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); // 2: «undefined, onRejected»
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
info->SetCallArg(undefined.GetTaggedValue(), reject.GetTaggedValue());
return JSFunction::Invoke(info, thenKey);
}
// 25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected )
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();
// 1. Let promise be the this value.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
// 2. If IsPromise(promise) is false, throw a TypeError exception.
if (!thisValue->IsJSPromise()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Then: thisValue is not promise!", JSTaggedValue::Exception());
}
// 3. Let C be SpeciesConstructor(promise, %Promise%).
// 4. ReturnIfAbrupt(C).
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);
// 5. Let resultCapability be NewPromiseCapability(C).
// 6. ReturnIfAbrupt(resultCapability).
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);
// 7. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
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();
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());
TaggedQueue *newQueue =
TaggedQueue::Push(thread, fulfillReactions, JSHandle<JSTaggedValue>::Cast(fulfillReaction));
promise->SetPromiseFulfillReactions(thread, JSTaggedValue(newQueue));
JSHandle<TaggedQueue> rejectReactions(thread, promise->GetPromiseRejectReactions());
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); // 2: 2 means two args stored in array
argv->Set(thread, 0, fulfillReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult());
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); // 2: 2 means two args stored in array
argv->Set(thread, 0, rejectReaction.GetTaggedValue());
argv->Set(thread, 1, promise->GetPromiseResult());
// When a handler is added to a rejected promise for the first time, it is called with its operation
// argument set to "handle".
if (!promise->GetPromiseIsHandled()) {
JSHandle<JSTaggedValue> reason(thread, JSTaggedValue::Null());
thread->GetEcmaVM()->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();
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAll(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &itRecord,
const JSHandle<JSTaggedValue> &ctor,
const JSHandle<PromiseCapability> &capa)
{
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
// 1. Assert: constructor is a constructor function.
ASSERT_PRINT(ctor->IsConstructor(), "PerformPromiseAll is not constructor");
// 2. Assert: resultCapability is a PromiseCapability record. (not need)
// 3. Let values be a new empty List.
JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
values->SetValue(thread, emptyArray);
// 4. Let remainingElementsCount be a new Record { [[value]]: 1 }.
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
// 5. Let index be 0.
uint32_t index = 0;
// 6. Repeat
JSHandle<JSTaggedValue> itor(thread, itRecord->GetIterator());
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
while (true) {
// a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
// b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
if (thread->HasPendingException()) {
itRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
// c. ReturnIfAbrupt(next).
RETURN_COMPLETION_IF_ABRUPT(thread, next);
// d. If next is false,
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
if (next->IsFalse()) {
// i. Set iteratorRecord.[[done]] to true.
itRecord->SetDone(true);
// ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] 1.
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue()));
// iii. If remainingElementsCount.[[value]] is 0,
if (remainCnt->GetValue().IsZero()) {
// 1. Let valuesArray be CreateArrayFromList(values).
JSHandle<JSArray> jsArrayValues =
JSArray::CreateArrayFromList(thread, JSHandle<TaggedArray>(thread, values->GetValue()));
// 2. Let resolveResult be Call(resultCapability.[[Resolve]], undefined, «valuesArray»).
JSHandle<JSTaggedValue> resCapaFunc(thread, capa->GetResolve());
EcmaRuntimeCallInfo *info =
EcmaInterpreter::NewRuntimeCallInfo(thread, resCapaFunc, undefined, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, next);
info->SetCallArg(jsArrayValues.GetTaggedValue());
JSTaggedValue resolveRes = JSFunction::Call(info);
// 3. ReturnIfAbrupt(resolveResult)
JSHandle<JSTaggedValue> resolveAbrupt(thread, resolveRes);
RETURN_COMPLETION_IF_ABRUPT(thread, resolveAbrupt);
}
// iv. Return resultCapability.[[Promise]].
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, JSHandle<JSTaggedValue>(thread, capa->GetPromise()));
return resRecord;
}
// e. Let nextValue be IteratorValue(next).
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
// f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
if (thread->HasPendingException()) {
itRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
// g. ReturnIfAbrupt(nextValue).
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
// h. Append undefined to values.
JSHandle<TaggedArray> valuesArray =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, values->GetValue()));
valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
valuesArray->Set(thread, index, JSTaggedValue::Undefined());
values->SetValue(thread, valuesArray);
// i. Let nextPromise be Invoke(constructor, "resolve", «nextValue»).
JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, ctor, undefined, 1);
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
info->SetCallArg(nextVal.GetTaggedValue());
JSTaggedValue taggedNextPromise = JSFunction::Invoke(info, resolveKey);
// j. ReturnIfAbrupt(nextPromise).
JSHandle<JSTaggedValue> nextPromise(thread, taggedNextPromise);
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
// k. Let resolveElement be a new built-in function object as defined in Promise.all
// Resolve Element Functions.
JSHandle<JSPromiseAllResolveElementFunction> resoleveElement = factory->NewJSPromiseAllResolveElementFunction();
// l. Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }.
JSHandle<PromiseRecord> falseRecord = factory->NewPromiseRecord();
falseRecord->SetValue(thread, JSTaggedValue::False());
resoleveElement->SetAlreadyCalled(thread, falseRecord);
// m. Set the [[Index]] internal slot of resolveElement to index.
resoleveElement->SetIndex(thread, JSTaggedValue(index));
// n. Set the [[Values]] internal slot of resolveElement to values.
resoleveElement->SetValues(thread, values);
// o. Set the [[Capabilities]] internal slot of resolveElement to resultCapability.
resoleveElement->SetCapabilities(thread, capa);
// p. Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount.
resoleveElement->SetRemainingElements(thread, remainCnt);
// q. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1.
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue()));
// r. Let result be Invoke(nextPromise, "then", «resolveElement, resultCapability.[[Reject]]»).
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
EcmaRuntimeCallInfo *runtimeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise,
undefined, 2); // 2: «resolveElement, resultCapability.[[Reject]]»
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
runtimeInfo->SetCallArg(resoleveElement.GetTaggedValue(), capa->GetReject());
JSTaggedValue taggedResult = JSFunction::Invoke(runtimeInfo, thenKey);
JSHandle<JSTaggedValue> result(thread, taggedResult);
// s. ReturnIfAbrupt(result).
RETURN_COMPLETION_IF_ABRUPT(thread, result);
// t. Set index to index + 1.
++index;
}
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseRace(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<PromiseCapability> &capability,
const JSHandle<JSTaggedValue> &constructor)
{
// 1. Repeat
// a. Let next be IteratorStep(iteratorRecord.[[iterator]]).
// b. If next is an abrupt completion, set iteratorRecord.[[done]] to true.
// c. ReturnIfAbrupt(next).
// d. If next is false, then
// i. Set iteratorRecord.[[done]] to true.
// ii. Return promiseCapability.[[Promise]].
// e. Let nextValue be IteratorValue(next).
// f. If nextValue is an abrupt completion, set iteratorRecord.[[done]] to true.
// g. ReturnIfAbrupt(nextValue).
// h. Let nextPromise be Invoke(C, "resolve", «nextValue»).
// i. ReturnIfAbrupt(nextPromise).
// j. Let result be Invoke(nextPromise, "then", «promiseCapability.[[Resolve]], promiseCapability.[[Reject]]»).
// k. ReturnIfAbrupt(result).
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> iterator(thread, iteratorRecord->GetIterator());
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());
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); // 2: two args
RETURN_COMPLETION_IF_ABRUPT(thread, nextPromise);
runtimeInfo->SetCallArg(capability->GetResolve(), capability->GetReject());
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)
{
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
// 1. Let promiseResolve be ? Get(promiseConstructor, "resolve").
JSHandle<JSTaggedValue> resolveKey = globalConst->GetHandledPromiseResolveString();
JSHandle<JSTaggedValue> promiseResolve = JSObject::GetProperty(thread, promiseConstructor, resolveKey).GetValue();
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
// 2. If IsCallable(promiseResolve) is false, throw a TypeError exception.
if (!promiseResolve->IsCallable()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "promiseResolve is not callable", JSTaggedValue::Exception());
}
// 3. Return promiseResolve.
return promiseResolve.GetTaggedValue();
}
// 27.2.4.3 Promise.any ( iterable )
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();
// 1. Let C be the this value.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
// 2. Let promiseCapability be ? NewPromiseCapability(C).
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
// 3. Let promiseResolve be GetPromiseResolve(C).
JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
// 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).
if (thread->HasPendingException()) {
promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
// 5. Let iteratorRecord be GetIterator(iterable).
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
if (thread->HasPendingException()) {
iterator = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
// Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
// 7. Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability, promiseResolve).
JSHandle<CompletionRecord> result = PerformPromiseAny(thread, iteratorRecord, thisValue,
promiseCapability, promiseResolve);
// 8. If result is an abrupt completion, then
if (result->IsThrow()) {
thread->ClearException();
// a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
// b. IfAbruptRejectPromise(result, promiseCapability).
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
if (closeVal.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
// 9. Return ? result.
return result->GetValue();
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAny(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iteratorRecord,
const JSHandle<JSTaggedValue> &constructor,
const JSHandle<PromiseCapability> &resultCapability,
const JSHandle<JSTaggedValue> &promiseResolve)
{
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
// 1. Let errors be a new empty List.
JSHandle<PromiseRecord> errors = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
errors->SetValue(thread, emptyArray);
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
// 3. Let index be 0.
uint32_t index = 0;
// 4. Repeat,
JSHandle<JSTaggedValue> iter(thread, iteratorRecord->GetIterator());
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
while (true) {
// a. Let next be IteratorStep(iteratorRecord).
next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
// c. ReturnIfAbrupt(next).
RETURN_COMPLETION_IF_ABRUPT(thread, next);
// d. If next is false, then
if (next->IsFalse()) {
// i. Set iteratorRecord.[[Done]] to true.
iteratorRecord->SetDone(true);
// ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue()));
// iii. If remainingElementsCount.[[Value]] is 0, then
if (remainCnt->GetValue().IsZero()) {
// 1. Let error be a newly created AggregateError object.
JSHandle<JSObject> error = factory->NewJSAggregateError();
// 2. Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true,
// [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errors) }).
JSHandle<JSTaggedValue> errorsKey(thread, globalConst->GetErrorsString());
JSHandle<TaggedArray> errorsArray =
JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread, errors->GetValue()));
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);
// 3. Return ThrowCompletion(error).
JSHandle<JSTaggedValue> errorCompletion(
factory->NewCompletionRecord(CompletionRecordType::THROW, errorTagged));
JSHandle<CompletionRecord> errorResult = JSHandle<CompletionRecord>::Cast(errorCompletion);
return errorResult;
}
// iv. Return resultCapability.[[Promise]].
JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapability->GetPromise());
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, resultCapabilityHandle);
return resRecord;
}
// e. Let nextValue be IteratorValue(next).
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (thread->HasPendingException()) {
iteratorRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
// g. ReturnIfAbrupt(nextValue).
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
// h. Append undefined to errors.
JSHandle<JSTaggedValue> errorsHandle(thread, errors->GetValue());
JSHandle<TaggedArray> errorsArray = JSHandle<TaggedArray>::Cast(errorsHandle);
errorsArray = TaggedArray::SetCapacity(thread, errorsArray, index + 1);
errorsArray->Set(thread, index, JSTaggedValue::Undefined());
errors->SetValue(thread, errorsArray);
// i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
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;
}
// j. Let stepsRejected be the algorithm steps defined in Promise.any Reject Element Functions.
// k. Let lengthRejected be the number of non-optional parameters of the function definition in
// Promise.any Reject Element Functions.
// l. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "", « [[AlreadyCalled]],
// [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »).
JSHandle<JSPromiseAnyRejectElementFunction> onRejected = factory->NewJSPromiseAnyRejectElementFunction();
// m. Set onRejected.[[AlreadyCalled]] to false.
onRejected->SetAlreadyCalled(thread, JSTaggedValue::False());
// n. Set onRejected.[[Index]] to index.
onRejected->SetIndex(index);
// o. Set onRejected.[[Errors]] to errors.
onRejected->SetErrors(thread, errors);
// p. Set onRejected.[[Capability]] to resultCapability.
onRejected->SetCapability(thread, resultCapability);
// q. Set onRejected.[[RemainingElements]] to remainingElementsCount.
onRejected->SetRemainingElements(thread, remainCnt);
// r. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue()));
// s. Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], onRejected »).
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapability->GetResolve());
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2); // 2: two args
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;
}
// t. Set index to index + 1.
++index;
}
}
// 25.6.4.2 Promise.allSettled ( iterable )
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();
// 1. Let C be the this value.
JSHandle<JSTaggedValue> thisValue = GetThis(argv);
// 2. Let promiseCapability be ? NewPromiseCapability(C).
JSHandle<PromiseCapability> promiseCapability = JSPromise::NewPromiseCapability(thread, thisValue);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, promiseCapability.GetTaggedValue());
// 3. Let promiseResolve be Completion(GetPromiseResolve(C)).
JSHandle<JSTaggedValue> promiseResolve(thread, BuiltinsPromise::GetPromiseResolve(thread, thisValue));
// 4. IfAbruptRejectPromise(promiseResolve, promiseCapability).
if (thread->HasPendingException()) {
promiseResolve = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, promiseResolve, promiseCapability);
// 5. Let iteratorRecord be Completion(GetIterator(iterable)).
JSHandle<JSTaggedValue> iterable = GetCallArg(argv, 0);
JSHandle<JSTaggedValue> iterator = JSIterator::GetIterator(thread, iterable);
// 6. IfAbruptRejectPromise(iteratorRecord, promiseCapability).
if (thread->HasPendingException()) {
iterator = JSPromise::IfThrowGetThrowValue(thread);
}
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, iterator, promiseCapability);
// Let iteratorRecord be Record {[[iterator]]: iterator, [[done]]: false}.
JSHandle<PromiseIteratorRecord> iteratorRecord = factory->NewPromiseIteratorRecord(iterator, false);
// 7. Let result be PerformPromiseAllSettled(iteratorRecord, C, promiseCapability).
JSHandle<CompletionRecord> result = PerformPromiseAllSettled(thread, iteratorRecord, thisValue,
promiseCapability, promiseResolve);
// 8. If result is an abrupt completion, then
if (result->IsThrow()) {
thread->ClearException();
// a. If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
if (!iteratorRecord->GetDone()) {
JSHandle<JSTaggedValue> resultHandle = JSHandle<JSTaggedValue>::Cast(result);
JSHandle<JSTaggedValue> closeVal = JSIterator::IteratorClose(thread, iterator, resultHandle);
if (closeVal.GetTaggedValue().IsCompletionRecord()) {
result = JSHandle<CompletionRecord>(closeVal);
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
}
// b. IfAbruptRejectPromise(result, promiseCapability).
RETURN_REJECT_PROMISE_IF_ABRUPT(thread, result, promiseCapability);
return result->GetValue();
}
// 7.Return Completion(result).
return result->GetValue();
}
JSHandle<CompletionRecord> BuiltinsPromise::PerformPromiseAllSettled(JSThread *thread,
const JSHandle<PromiseIteratorRecord> &iterRecord,
const JSHandle<JSTaggedValue> &constructor,
const JSHandle<PromiseCapability> &resultCapa,
const JSHandle<JSTaggedValue> &promiseResolve)
{
auto ecmaVm = thread->GetEcmaVM();
const GlobalEnvConstants *globalConst = thread->GlobalConstants();
ObjectFactory *factory = ecmaVm->GetFactory();
// 1. Let values be a new empty List.
JSHandle<PromiseRecord> values = factory->NewPromiseRecord();
JSHandle<TaggedArray> emptyArray = factory->EmptyArray();
values->SetValue(thread, emptyArray);
// 2. Let remainingElementsCount be the Record { [[Value]]: 1 }.
JSHandle<PromiseRecord> remainCnt = factory->NewPromiseRecord();
remainCnt->SetValue(thread, JSTaggedNumber(1));
// 3. Let index be 0.
uint32_t index = 0;
// 4. Repeat,
JSHandle<JSTaggedValue> iter(thread, iterRecord->GetIterator());
JSMutableHandle<JSTaggedValue> next(thread, globalConst->GetUndefined());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
while (true) {
// a. Let next be IteratorStep(iteratorRecord).
next.Update(JSIterator::IteratorStep(thread, iter).GetTaggedValue());
// b. If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (thread->HasPendingException()) {
iterRecord->SetDone(true);
next.Update(JSPromise::IfThrowGetThrowValue(thread).GetTaggedValue());
}
// c. ReturnIfAbrupt(next).
RETURN_COMPLETION_IF_ABRUPT(thread, next);
// d. If next is false, then
if (next->IsFalse()) {
// i. Set iteratorRecord.[[Done]] to true.
iterRecord->SetDone(true);
// ii. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
remainCnt->SetValue(thread, --JSTaggedNumber(remainCnt->GetValue()));
// iii. If remainingElementsCount.[[Value]] is 0, then
if (remainCnt->GetValue().IsZero()) {
// 1. Let valuesArray be ! CreateArrayFromList(values).
JSHandle<TaggedArray> taggedValues(thread, values->GetValue());
JSHandle<JSArray> jsArrayValues = JSArray::CreateArrayFromList(thread, taggedValues);
// 2. Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
JSHandle<JSTaggedValue> resCapaFunc(thread, resultCapa->GetResolve());
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;
}
}
// iv. Return resultCapability.[[Promise]].
JSHandle<JSTaggedValue> resultCapabilityHandle(thread, resultCapa->GetPromise());
JSHandle<CompletionRecord> resRecord = factory->NewCompletionRecord(
CompletionRecordType::NORMAL, resultCapabilityHandle);
return resRecord;
}
// e. Let nextValue be IteratorValue(next).
JSHandle<JSTaggedValue> nextVal = JSIterator::IteratorValue(thread, next);
// f. If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
if (thread->HasPendingException()) {
iterRecord->SetDone(true);
nextVal = JSHandle<JSTaggedValue>(thread, thread->GetException());
}
// g. ReturnIfAbrupt(nextValue).
RETURN_COMPLETION_IF_ABRUPT(thread, nextVal);
// h. Append undefined to values.
JSHandle<JSTaggedValue> valuesHandle(thread, values->GetValue());
JSHandle<TaggedArray> valuesArray = JSHandle<TaggedArray>::Cast(valuesHandle);
valuesArray = TaggedArray::SetCapacity(thread, valuesArray, index + 1);
valuesArray->Set(thread, index, JSTaggedValue::Undefined());
values->SetValue(thread, valuesArray);
// i. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
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;
}
// j. Let stepsFulfilled be the algorithm steps defined in Promise.allSettled Resolve Element Functions.
// k. Let lengthFulfilled be the number of non-optional parameters of the function definition in
// Promise.allSettled Resolve Element Functions.
// l. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled, lengthFulfilled, "",
// « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
JSHandle<JSPromiseAllSettledElementFunction> onFulfilled =
factory->NewJSPromiseAllSettledResolveElementFunction();
// m. Let alreadyCalled be the Record { [[Value]]: false }.
JSHandle<PromiseRecord> alreadyCalled = factory->NewPromiseRecord();
alreadyCalled->SetValue(thread, JSTaggedValue::False());
// n. Set onFulfilled.[[AlreadyCalled]] to alreadyCalled.
onFulfilled->SetAlreadyCalled(thread, alreadyCalled);
// o. Set onFulfilled.[[Index]] to index.
onFulfilled->SetIndex(index);
// p. Set onFulfilled.[[Values]] to values.
onFulfilled->SetValues(thread, values);
// q. Set onFulfilled.[[Capability]] to resultCapability.
onFulfilled->SetCapability(thread, resultCapa);
// r. Set onFulfilled.[[RemainingElements]] to remainingElementsCount.
onFulfilled->SetRemainingElements(thread, remainCnt);
// s. Let stepsRejected be the algorithm steps defined in Promise.allSettled Reject Element Functions.
// t. Let lengthRejected be the number of non-optional parameters of the function definition in
// Promise.allSettled Reject Element Functions.
// u. Let onRejected be CreateBuiltinFunction(stepsRejected, lengthRejected, "",
// « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
JSHandle<JSPromiseAllSettledElementFunction> onRejected =
factory->NewJSPromiseAllSettledRejectElementFunction();
// v. Set onRejected.[[AlreadyCalled]] to alreadyCalled.
onRejected->SetAlreadyCalled(thread, alreadyCalled);
// w. Set onRejected.[[Index]] to index.
onRejected->SetIndex(index);
// x. Set onRejected.[[Values]] to values.
onRejected->SetValues(thread, values);
// y. Set onRejected.[[Capability]] to resultCapability.
onRejected->SetCapability(thread, resultCapa);
// z. Set onRejected.[[RemainingElements]] to remainingElementsCount.
onRejected->SetRemainingElements(thread, remainCnt);
// aa. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
remainCnt->SetValue(thread, ++JSTaggedNumber(remainCnt->GetValue()));
// ab. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »).
JSHandle<JSTaggedValue> thenKey = globalConst->GetHandledPromiseThenString();
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, nextPromise, undefined, 2); // 2: two args
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;
}
// ac. Set index to index + 1.
++index;
}
}
// 27.2.5.3 Promise.prototype.finally ( onFinally )
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();
// 1. Let promise be the this value.
// 2. If Type(promise) is not Object, throw a TypeError exception.
JSHandle<JSTaggedValue> promise = GetThis(argv);
if (!promise->IsECMAObject()) {
THROW_TYPE_ERROR_AND_RETURN(thread, "Reject: this value is not object", JSTaggedValue::Exception());
}
// 3. Let C be SpeciesConstructor(promise, %Promise%).
// 4. Assert: IsConstructor(C) is true.
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);
ASSERT_PRINT(constructor->IsConstructor(), "constructor is not constructor");
JSHandle<JSTaggedValue> thenFinally;
JSHandle<JSTaggedValue> catchFinally;
// 5. If IsCallable(onFinally) is false, then
if (!onFinally->IsCallable()) {
// a. Let thenFinally be onFinally.
// b. Let catchFinally be onFinally.
thenFinally = onFinally;
catchFinally = onFinally;
// 6. Else,
} else {
// a. Let stepsThenFinally be the algorithm steps defined in Then Finally Functions.
// b. Let thenFinally be CreateBuiltinFunction(stepsThenFinally, « [[Constructor]], [[OnFinally]] »).
JSHandle<JSPromiseFinallyFunction> thenFinallyFun =
factory->NewJSPromiseThenFinallyFunction();
// c. Set thenFinally.[[Constructor]] to C.
// d. Set thenFinally.[[OnFinally]] to onFinally.
thenFinallyFun->SetConstructor(thread, constructor);
thenFinallyFun->SetOnFinally(thread, onFinally);
thenFinally = JSHandle<JSTaggedValue>(thenFinallyFun);
// e. Let stepsCatchFinally be the algorithm steps defined in Catch Finally Functions.
// f. Let catchFinally be CreateBuiltinFunction(stepsCatchFinally, « [[Constructor]], [[OnFinally]] »).
JSHandle<JSPromiseFinallyFunction> catchFinallyFun =
factory->NewJSPromiseCatchFinallyFunction();
// g. Set catchFinally.[[Constructor]] to C.
// h. Set catchFinally.[[OnFinally]] to onFinally.
catchFinallyFun->SetConstructor(thread, constructor);
catchFinallyFun->SetOnFinally(thread, onFinally);
catchFinally = JSHandle<JSTaggedValue>(catchFinallyFun);
}
// 7. return invoke(promise, "then", <<thenFinally, catchFinally>>)
JSHandle<JSTaggedValue> thenKey(globalConst->GetHandledPromiseThenString());
JSHandle<JSTaggedValue> undefined(globalConst->GetHandledUndefined());
EcmaRuntimeCallInfo *invokeInfo =
EcmaInterpreter::NewRuntimeCallInfo(thread, undefined, promise, undefined, 2); // 2: two args
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
invokeInfo->SetCallArg(thenFinally.GetTaggedValue(), catchFinally.GetTaggedValue());
return JSFunction::Invoke(invokeInfo, thenKey);
}
} // namespace panda::ecmascript::builtins