escargot/src/builtins/BuiltinPromise.cpp
2023-08-18 18:36:46 +09:00

836 lines
47 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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) 2017-present Samsung Electronics Co., Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "Escargot.h"
#include "runtime/GlobalObject.h"
#include "runtime/Context.h"
#include "runtime/VMInstance.h"
#include "runtime/PromiseObject.h"
#include "runtime/ArrayObject.h"
#include "runtime/JobQueue.h"
#include "runtime/SandBox.h"
#include "runtime/NativeFunctionObject.h"
#include "runtime/IteratorObject.h"
#include "runtime/ExtendedNativeFunctionObject.h"
namespace Escargot {
// $25.4.3 Promise(executor)
static Value builtinPromiseConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
auto strings = &state.context()->staticStrings();
if (!newTarget.hasValue()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString, "%s: Promise constructor should be called with new Promise()");
}
Value executor = argv[0];
if (!executor.isCallable()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString, "%s: Promise executor is not a function object");
}
// Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »).
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
return constructorRealm->globalObject()->promisePrototype();
});
RETURN_VALUE_IF_PENDING_EXCEPTION
PromiseObject* promise = new PromiseObject(state, proto);
PromiseReaction::Capability capability = promise->createResolvingFunctions(state);
// PromiseHook for Promise initialization case
if (UNLIKELY(state.context()->vmInstance()->isPromiseHookRegistered())) {
// Note) To pass parent promise, we allocate the second argument (argv[1]) for the parent promise
// otherwise, the second argument would be ignored
state.context()->vmInstance()->triggerPromiseHook(state, VMInstance::PromiseHookType::Init, promise, (argc > 1) ? argv[1] : Value());
}
{
Value arguments[] = { capability.m_resolveFunction, capability.m_rejectFunction };
Object::call(state, executor, Value(), 2, arguments);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
Object::call(state, capability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
}
return promise;
}
// https://tc39.es/ecma262/#sec-getpromiseresolve
static Value getPromiseResolve(ExecutionState& state, Object* promiseConstructor)
{
// Assert: IsConstructor(promiseConstructor) is true.
// Let promiseResolve be ? Get(promiseConstructor, "resolve").
auto promiseResolve = promiseConstructor->get(state, state.context()->staticStrings().resolve).value(state, promiseConstructor);
RETURN_VALUE_IF_PENDING_EXCEPTION
// If IsCallable(promiseResolve) is false, throw a TypeError exception.
if (!promiseResolve.isCallable()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "Promise resolve is not callable");
}
// Return promiseResolve.
return promiseResolve;
}
static Value builtinPromiseAll(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// https://tc39.es/ecma262/#sec-performpromiseall
auto strings = &state.context()->staticStrings();
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
if (!thisValue.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, strings->all.string(), ErrorObject::Messages::GlobalObject_ThisNotObject);
}
Object* C = thisValue.asObject();
// Let promiseCapability be NewPromiseCapability(C).
PromiseReaction::Capability promiseCapability = PromiseObject::newPromiseCapability(state, C);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let promiseResolve be GetPromiseResolve(C).
Value promiseResolve = getPromiseResolve(state, C);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let iteratorRecord be GetIterator(iterable).
// IfAbruptRejectPromise(iteratorRecord, promiseCapability).
IteratorRecord* iteratorRecord = IteratorObject::getIterator(state, argv[0]);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let result be PerformPromiseAll(iteratorRecord, C, promiseCapability).
Value result;
// Let values be a new empty List.
ValueVector* values = new ValueVector();
// Let remainingElementsCount be a new Record { [[value]]: 1 }.
size_t* remainingElementsCount = new (PointerFreeGC) size_t(1);
// Let index be 0.
int64_t index = 0;
// Repeat
while (true) {
// Let next be IteratorStep(iteratorRecord).
Optional<Object*> next = IteratorObject::iteratorStep(state, iteratorRecord);
if (UNLIKELY(state.hasPendingException())) {
// If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// ReturnIfAbrupt(next).
goto IfAbrupt;
}
// If next is false,
if (!next.hasValue()) {
// set iteratorRecord.[[done]] to true.
iteratorRecord->m_done = true;
// Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] 1.
*remainingElementsCount = *remainingElementsCount - 1;
// If remainingElementsCount.[[value]] is 0,
if (*remainingElementsCount == 0) {
// Let valuesArray be CreateArrayFromList(values).
// Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
Value argv = Object::createArrayFromList(state, *values);
Value resolveResult = Object::call(state, promiseCapability.m_resolveFunction, Value(), 1, &argv);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
}
// Return resultCapability.[[Promise]].
result = promiseCapability.m_promise;
break;
}
// Let nextValue be IteratorValue(next).
Value nextValue = IteratorObject::iteratorValue(state, next.value());
if (UNLIKELY(state.hasPendingException())) {
// If next is an abrupt completion, set iteratorRecord.[[done]] to true.
iteratorRecord->m_done = true;
// ReturnIfAbrupt(nextValue).
goto IfAbrupt;
}
// Append undefined to values.
values->pushBack(Value());
// Let nextPromise be Invoke(constructor, "resolve", « nextValue »).
Value nextPromise = Object::call(state, promiseResolve, C, 1, &nextValue);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
// Let resolveElement be a new built-in function object as defined in Promise.all Resolve Element Functions.
ExtendedNativeFunctionObject* resolveElement = new ExtendedNativeFunctionObjectImpl<6>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseAllResolveElementFunction, 1, NativeFunctionInfo::Strict));
// Set the [[AlreadyCalled]] internal slot of resolveElement to a new Record {[[value]]: false }.
// Set the [[Index]] internal slot of resolveElement to index.
// Set the [[Values]] internal slot of resolveElement to values.
// Set the [[Capabilities]] internal slot of resolveElement to resultCapability.
// Set the [[RemainingElements]] internal slot of resolveElement to remainingElementsCount.
bool* alreadyCalled = new (PointerFreeGC) bool(false);
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::AlreadyCalled, alreadyCalled);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Index, Value(index));
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::Values, values);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Resolve, promiseCapability.m_resolveFunction);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Reject, promiseCapability.m_rejectFunction);
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::RemainingElements, remainingElementsCount);
// Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] + 1.
*remainingElementsCount = *remainingElementsCount + 1;
// Perform ? Invoke(nextPromise, "then", « resolveElement, resultCapability.[[Reject]] »).
Object* nextPromiseObject = nextPromise.toObject(state);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
Value argv[] = { Value(resolveElement), Value(promiseCapability.m_rejectFunction) };
Value callee = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
Object::call(state, callee, nextPromiseObject, 2, argv);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
// Increase index by 1.
index++;
}
// Return Completion(result).
return result;
IfAbrupt:
ASSERT(state.hasPendingException());
Value exceptionValue = state.detachPendingException();
// If result is an abrupt completion,
// If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
// IfAbruptRejectPromise(result, promiseCapability).
if (!iteratorRecord->m_done) {
result = IteratorObject::iteratorClose(state, iteratorRecord, exceptionValue, true);
if (UNLIKELY(state.hasPendingException())) {
exceptionValue = state.detachPendingException();
}
}
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &exceptionValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
static Value builtinPromiseRace(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// https://tc39.es/ecma262/#sec-performpromiserace
auto strings = &state.context()->staticStrings();
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
if (!thisValue.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, strings->race.string(), ErrorObject::Messages::GlobalObject_ThisNotObject);
}
Object* C = thisValue.asObject();
// Let promiseCapability be NewPromiseCapability(C).
// ReturnIfAbrupt(promiseCapability).
PromiseReaction::Capability promiseCapability = PromiseObject::newPromiseCapability(state, C);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let promiseResolve be GetPromiseResolve(C).
Value resolve = getPromiseResolve(state, C);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let iteratorRecord be GetIterator(iterable).
// IfAbruptRejectPromise(iteratorRecord, promiseCapability).
IteratorRecord* record = IteratorObject::getIterator(state, argv[0]);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Let rejectResult be Call(capability.[[Reject]], undefined, «value.[[value]]»).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let result be PerformPromiseRace(iteratorRecord, C, promiseCapability).
Value result;
// Repeat
while (true) {
// Let next be IteratorStep(iteratorRecord).
Optional<Object*> next = IteratorObject::iteratorStep(state, record);
if (UNLIKELY(state.hasPendingException())) {
// If next is an abrupt completion, set iteratorRecord.[[done]] to true.
// ReturnIfAbrupt(next).
record->m_done = true;
goto IfAbrupt;
}
// If next is false, then
if (!next.hasValue()) {
// Set iteratorRecord.[[done]] to true.
record->m_done = true;
// Return resultCapability.[[Promise]].
result = promiseCapability.m_promise;
break;
}
// Let nextValue be IteratorValue(next).
Value nextValue = IteratorObject::iteratorValue(state, next.value());
if (UNLIKELY(state.hasPendingException())) {
// If next is an abrupt completion, set iteratorRecord.[[done]] to true.
record->m_done = true;
// ReturnIfAbrupt(next).
goto IfAbrupt;
}
// Let nextPromise be Invoke(C, "resolve", «nextValue»).
Value nextPromise = Object::call(state, resolve, C, 1, &nextValue);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
// Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], resultCapability.[[Reject]] »).
Object* nextPromiseObject = nextPromise.toObject(state);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
Value argv[] = { Value(promiseCapability.m_resolveFunction), Value(promiseCapability.m_rejectFunction) };
Value callee = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
Object::call(state, callee, nextPromiseObject, 2, argv);
if (UNLIKELY(state.hasPendingException()))
goto IfAbrupt;
}
// Return Completion(result).
return result;
IfAbrupt:
ASSERT(state.hasPendingException());
Value exceptionValue = state.detachPendingException();
// If result is an abrupt completion, then
// If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
// IfAbruptRejectPromise(result, promiseCapability).
if (!record->m_done) {
result = IteratorObject::iteratorClose(state, record, exceptionValue, true);
if (UNLIKELY(state.hasPendingException())) {
exceptionValue = state.detachPendingException();
}
}
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &exceptionValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
static Value builtinPromiseReject(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
auto strings = &state.context()->staticStrings();
Object* thisObject = thisValue.toObject(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
PromiseReaction::Capability capability = PromiseObject::newPromiseCapability(state, thisObject);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value arguments[] = { argv[0] };
Object::call(state, capability.m_rejectFunction, Value(), 1, arguments);
RETURN_VALUE_IF_PENDING_EXCEPTION
return capability.m_promise;
}
// http://www.ecma-international.org/ecma-262/10.0/#sec-promise.resolve
static Value builtinPromiseResolve(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let C be the this value.
const Value& C = thisValue;
// If Type(C) is not Object, throw a TypeError exception.
if (!C.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, state.context()->staticStrings().Promise.string(), false, state.context()->staticStrings().resolve.string(), "%s: PromiseResolve called on non-object");
}
// Return ? PromiseResolve(C, x).
return PromiseObject::promiseResolve(state, C.asObject(), argv[0]);
}
static Value builtinPromiseCatch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
auto strings = &state.context()->staticStrings();
Object* thisObject = thisValue.toObject(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value onRejected = argv[0];
Value then = thisObject->get(state, strings->then).value(state, thisObject);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value arguments[] = { Value(), onRejected };
return Object::call(state, then, thisObject, 2, arguments);
}
static Value builtinPromiseFinally(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// https://www.ecma-international.org/ecma-262/10.0/#sec-promise.prototype.finally
auto strings = &state.context()->staticStrings();
if (!thisValue.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, strings->finally.string(), "%s: not a Promise object");
}
Object* thisObject = thisValue.asObject();
Value C = thisObject->speciesConstructor(state, state.context()->globalObject()->promise());
RETURN_VALUE_IF_PENDING_EXCEPTION
Value onFinally = argv[0];
Value arguments[] = { onFinally, onFinally };
if (onFinally.isCallable()) {
ExtendedNativeFunctionObject* thenFinally = new ExtendedNativeFunctionObjectImpl<2>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseThenFinally, 1, NativeFunctionInfo::Strict));
thenFinally->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Constructor, C);
thenFinally->setInternalSlot(PromiseObject::BuiltinFunctionSlot::OnFinally, onFinally);
ExtendedNativeFunctionObject* catchFinally = new ExtendedNativeFunctionObjectImpl<2>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseCatchFinally, 1, NativeFunctionInfo::Strict));
catchFinally->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Constructor, C);
catchFinally->setInternalSlot(PromiseObject::BuiltinFunctionSlot::OnFinally, onFinally);
arguments[0] = thenFinally;
arguments[1] = catchFinally;
}
Value then = thisObject->get(state, strings->then).value(state, thisObject);
RETURN_VALUE_IF_PENDING_EXCEPTION
return Object::call(state, then, thisObject, 2, arguments);
}
static Value builtinPromiseThen(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
auto strings = &state.context()->staticStrings();
if (!thisValue.isObject() || !thisValue.asObject()->isPromiseObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, strings->then.string(), "%s: not a Promise object");
}
Value C = thisValue.asObject()->speciesConstructor(state, state.context()->globalObject()->promise());
RETURN_VALUE_IF_PENDING_EXCEPTION
PromiseReaction::Capability promiseCapability = PromiseObject::newPromiseCapability(state, C.asObject(), thisValue.asObject()->asPromiseObject());
RETURN_VALUE_IF_PENDING_EXCEPTION
return thisValue.asObject()->asPromiseObject()->then(state, argv[0], argv[1], promiseCapability).value();
}
// https://tc39.es/ecma262/#sec-performpromiseallsettled
static Value performPromiseAllSettled(ExecutionState& state, IteratorRecord* iteratorRecord, Object* constructor, PromiseReaction::Capability& resultCapability, Value promiseResolve)
{
// Assert: ! IsConstructor(constructor) is true.
// Assert: resultCapability is a PromiseCapability Record.
// Let values be a new empty List.
ValueVector* values = new ValueVector();
// Let remainingElementsCount be a new Record { [[Value]]: 1 }.
size_t* remainingElementsCount = new (PointerFreeGC) size_t(1);
// Let index be 0.
size_t index = 0;
// Repeat,
while (true) {
// Let next be IteratorStep(iteratorRecord).
Optional<Object*> next = IteratorObject::iteratorStep(state, iteratorRecord);
if (UNLIKELY(state.hasPendingException())) {
// If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// ReturnIfAbrupt(next).
return Value(Value::Exception);
}
// If next is false, then
if (!next.hasValue()) {
// Set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
*remainingElementsCount = *remainingElementsCount - 1;
// If remainingElementsCount.[[Value]] is 0, then
if (*remainingElementsCount == 0) {
// Let valuesArray be ! CreateArrayFromList(values).
Value valuesArray = ArrayObject::createArrayFromList(state, *values);
// Perform ? Call(resultCapability.[[Resolve]], undefined, « valuesArray »).
Object::call(state, resultCapability.m_resolveFunction, Value(), 1, &valuesArray);
RETURN_VALUE_IF_PENDING_EXCEPTION
}
// Return resultCapability.[[Promise]].
return resultCapability.m_promise;
}
// Let nextValue be IteratorValue(next).
Value nextValue = IteratorObject::iteratorValue(state, next.value());
if (UNLIKELY(state.hasPendingException())) {
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// ReturnIfAbrupt(nextValue).
return Value(Value::Exception);
}
// Append undefined to values.
values->pushBack(Value());
// Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
Value nextValueArgv = nextValue;
Value nextPromise = Object::call(state, promiseResolve, constructor, 1, &nextValueArgv);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let steps be the algorithm steps defined in Promise.allSettled Resolve Element Functions.
// Let resolveElement be ! CreateBuiltinFunction(steps, « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
// Let alreadyCalled be the Record { [[Value]]: false }.
bool* alreadyCalled = new (PointerFreeGC) bool(false);
// Set resolveElement.[[AlreadyCalled]] to alreadyCalled.
// Set resolveElement.[[Index]] to index.
// Set resolveElement.[[Values]] to values.
// Set resolveElement.[[Capability]] to resultCapability.
// Set resolveElement.[[RemainingElements]] to remainingElementsCount.
auto resolveElement = new ExtendedNativeFunctionObjectImpl<6>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseAllSettledResolveElementFunction, 1, NativeFunctionInfo::Strict));
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::AlreadyCalled, alreadyCalled);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Index, Value(index));
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::Values, values);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Resolve, resultCapability.m_resolveFunction);
resolveElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Reject, resultCapability.m_rejectFunction);
resolveElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::RemainingElements, remainingElementsCount);
// Let rejectSteps be the algorithm steps defined in Promise.allSettled Reject Element Functions.
// Let rejectElement be ! CreateBuiltinFunction(rejectSteps, « [[AlreadyCalled]], [[Index]], [[Values]], [[Capability]], [[RemainingElements]] »).
// Set rejectElement.[[AlreadyCalled]] to alreadyCalled.
// Set rejectElement.[[Index]] to index.
// Set rejectElement.[[Values]] to values.
// Set rejectElement.[[Capability]] to resultCapability.
// Set rejectElement.[[RemainingElements]] to remainingElementsCount.
auto rejectElement = new ExtendedNativeFunctionObjectImpl<6>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseAllSettledRejectElementFunction, 1, NativeFunctionInfo::Strict));
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::AlreadyCalled, alreadyCalled);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Index, Value(index));
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::Values, values);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Resolve, resultCapability.m_resolveFunction);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Reject, resultCapability.m_rejectFunction);
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::RemainingElements, remainingElementsCount);
// Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
*remainingElementsCount = *remainingElementsCount + 1;
// Perform ? Invoke(nextPromise, "then", « resolveElement, rejectElement »).
Value argv[2] = { resolveElement, rejectElement };
Value callee = Object::getMethod(state, nextPromise, state.context()->staticStrings().then);
RETURN_VALUE_IF_PENDING_EXCEPTION
Object::call(state, callee, nextPromise, 2, argv);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Set index to index + 1.
index++;
}
return Value();
}
// https://tc39.es/ecma262/#sec-promise.allsettled
static Value builtinPromiseAllSettled(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
if (!thisValue.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, "this value of allSettled is not Object");
}
Object* C = thisValue.asObject();
// Let promiseCapability be ? NewPromiseCapability(C).
auto promiseCapability = PromiseObject::newPromiseCapability(state, C);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let promiseResolve be GetPromiseResolve(C).
Value promiseResolve = getPromiseResolve(state, C);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let iteratorRecord be GetIterator(iterable).
// IfAbruptRejectPromise(iteratorRecord, promiseCapability).
IteratorRecord* iteratorRecord = IteratorObject::getIterator(state, argv[0]);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let result be PerformPromiseAllSettled(iteratorRecord, C, promiseCapability).
Value result = performPromiseAllSettled(state, iteratorRecord, C, promiseCapability, promiseResolve);
if (UNLIKELY(state.hasPendingException())) {
Value exceptionValue = state.detachPendingException();
// If result is an abrupt completion,
// If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
if (!iteratorRecord->m_done) {
result = IteratorObject::iteratorClose(state, iteratorRecord, exceptionValue, true);
if (UNLIKELY(state.hasPendingException())) {
exceptionValue = state.detachPendingException();
} else {
return result;
}
}
// IfAbruptRejectPromise(result, promiseCapability).
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &exceptionValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Return Completion(result).
return result;
}
// https://tc39.es/ecma262/#sec-performpromiseany
static Value performPromiseAny(ExecutionState& state, IteratorRecord* iteratorRecord, Object* constructor, const PromiseReaction::Capability& resultCapability, const Value& promiseResolve)
{
const StaticStrings* strings = &state.context()->staticStrings();
// Assert: ! IsConstructor(constructor) is true.
// Assert: ! IsCallable(promiseResolve) is true.
// Let errors be a new empty List.
ValueVector* errors = new ValueVector();
// Let remainingElementsCount be the Record { [[Value]]: 1 }.
size_t* remainingElementsCount = new (PointerFreeGC) size_t(1);
// Let index be 0.
int64_t index = 0;
// Repeat,
while (true) {
// Let next be IteratorStep(iteratorRecord).
// If next is an abrupt completion, set iteratorRecord.[[Done]] to true.
// ReturnIfAbrupt(next).
Optional<Object*> next = IteratorObject::iteratorStep(state, iteratorRecord);
if (UNLIKELY(state.hasPendingException())) {
iteratorRecord->m_done = true;
return Value(Value::Exception);
}
// If next is false, then
if (!next.hasValue()) {
// Set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1.
*remainingElementsCount = *remainingElementsCount - 1;
// If remainingElementsCount.[[Value]] is 0, then
if (*remainingElementsCount == 0) {
// Let error be a newly created AggregateError object.
ErrorObject* error = ErrorObject::createBuiltinError(state, ErrorCode::AggregateError, "Got AggregateError on processing Promise.any");
// Perform ! DefinePropertyOrThrow(error, "errors", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: ! CreateArrayFromList(errors) }).
error->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, String::fromASCII("errors")),
ObjectPropertyDescriptor(Object::createArrayFromList(state, *errors),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent | ObjectPropertyDescriptor::WritablePresent)));
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return ThrowCompletion(error).
THROW_EXCEPTION_RETURN_VALUE(state, error);
}
// Return resultCapability.[[Promise]].
return resultCapability.m_promise;
}
// Let nextValue be IteratorValue(next).
Value nextValue = IteratorObject::iteratorValue(state, next.value());
if (UNLIKELY(state.hasPendingException())) {
// If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true.
iteratorRecord->m_done = true;
// ReturnIfAbrupt(nextValue).
return Value(Value::Exception);
}
// Append undefined to errors.
errors->pushBack(Value());
// Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »).
Value argv = nextValue;
Value nextPromise = Object::call(state, promiseResolve, constructor, 1, &argv);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let steps be the algorithm steps defined in Promise.any Reject Element Functions.
// Let rejectElement be ! CreateBuiltinFunction(steps, « [[AlreadyCalled]], [[Index]], [[Errors]], [[Capability]], [[RemainingElements]] »).
ExtendedNativeFunctionObject* rejectElement = new ExtendedNativeFunctionObjectImpl<6>(state, NativeFunctionInfo(AtomicString(), PromiseObject::promiseAnyRejectElementFunction, 1, NativeFunctionInfo::Strict));
// Set rejectElement.[[AlreadyCalled]] to false.
// Set rejectElement.[[Index]] to index.
// Set rejectElement.[[Errors]] to errors.
// Set rejectElement.[[Capability]] to resultCapability.
// Set rejectElement.[[RemainingElements]] to remainingElementsCount.
bool* alreadyCalled = new (PointerFreeGC) bool(false);
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::AlreadyCalled, alreadyCalled);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Index, Value(index));
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::Errors, errors);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Resolve, resultCapability.m_resolveFunction);
rejectElement->setInternalSlot(PromiseObject::BuiltinFunctionSlot::Reject, resultCapability.m_rejectFunction);
rejectElement->setInternalSlotAsPointer(PromiseObject::BuiltinFunctionSlot::RemainingElements, remainingElementsCount);
// Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] + 1.
*remainingElementsCount = *remainingElementsCount + 1;
// Perform ? Invoke(nextPromise, "then", « resultCapability.[[Resolve]], rejectElement »).
Object* nextPromiseObject = nextPromise.toObject(state);
RETURN_VALUE_IF_PENDING_EXCEPTION
Value argv2[] = { Value(resultCapability.m_resolveFunction), Value(rejectElement) };
Value callee = nextPromiseObject->get(state, strings->then).value(state, nextPromiseObject);
RETURN_VALUE_IF_PENDING_EXCEPTION
Object::call(state, callee, nextPromiseObject, 2, argv2);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Set index to index + 1.
index++;
}
ASSERT_NOT_REACHED();
}
// https://tc39.es/ecma262/#sec-promise.any
static Value builtinPromiseAny(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
const StaticStrings* strings = &state.context()->staticStrings();
// Let C be the this value.
// If Type(C) is not Object, throw a TypeError exception.
if (!thisValue.isObject()) {
THROW_BUILTIN_ERROR_RETURN_VALUE(state, ErrorCode::TypeError, strings->Promise.string(), false, strings->all.string(), ErrorObject::Messages::GlobalObject_ThisNotObject);
}
Object* C = thisValue.asObject();
// Let promiseCapability be NewPromiseCapability(C).
PromiseReaction::Capability promiseCapability = PromiseObject::newPromiseCapability(state, C);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Let promiseResolve be GetPromiseResolve(C).
// IfAbruptRejectPromise(promiseResolve, promiseCapability).
Value promiseResolve = getPromiseResolve(state, C);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let iteratorRecord be GetIterator(iterable).
// IfAbruptRejectPromise(iteratorRecord, promiseCapability).
const Value& iterable = argv[0];
IteratorRecord* iteratorRecord = IteratorObject::getIterator(state, iterable);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If value is an abrupt completion,
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
// Return capability.[[Promise]].
return promiseCapability.m_promise;
}
// Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability, promiseResolve).
Value result = performPromiseAny(state, iteratorRecord, C, promiseCapability, promiseResolve);
if (UNLIKELY(state.hasPendingException())) {
Value thrownValue = state.detachPendingException();
// If result is an abrupt completion, then
// If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result).
if (!iteratorRecord->m_done) {
IteratorObject::iteratorClose(state, iteratorRecord, thrownValue, true);
if (UNLIKELY(state.hasPendingException())) {
thrownValue = state.detachPendingException();
}
}
// IfAbruptRejectPromise(result, promiseCapability).
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
RETURN_VALUE_IF_PENDING_EXCEPTION
return promiseCapability.m_promise;
}
// Return Completion(result).
return result;
}
void GlobalObject::initializePromise(ExecutionState& state)
{
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
ASSERT(self->isGlobalObject());
return self->asGlobalObject()->promise();
},
nullptr);
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Promise), nativeData, Value(Value::EmptyValue));
}
void GlobalObject::installPromise(ExecutionState& state)
{
const StaticStrings* strings = &state.context()->staticStrings();
m_promise = new NativeFunctionObject(state, NativeFunctionInfo(strings->Promise, builtinPromiseConstructor, 1), NativeFunctionObject::__ForBuiltinConstructor__);
m_promise->setGlobalIntrinsicObject(state);
{
JSGetterSetter gs(
new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().getSymbolSpecies, builtinSpeciesGetter, 0, NativeFunctionInfo::Strict)), Value(Value::EmptyValue));
ObjectPropertyDescriptor desc(gs, ObjectPropertyDescriptor::ConfigurablePresent);
m_promise->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().species), desc);
}
m_promisePrototype = new PrototypeObject(state);
m_promisePrototype->setGlobalIntrinsicObject(state, true);
m_promisePrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->constructor), ObjectPropertyDescriptor(m_promise, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_promisePrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag),
ObjectPropertyDescriptor(Value(state.context()->staticStrings().Promise.string()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.4.1 Promise.all(iterable);
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->all),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->all, builtinPromiseAll, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.4.3 Promise.race(iterable)
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->race),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->race, builtinPromiseRace, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.4.4 Promise.reject(r)
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->reject),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->reject, builtinPromiseReject, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.4.5 Promise.resolve(r)
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->resolve),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->resolve, builtinPromiseResolve, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.5.1 Promise.prototype.catch(onRejected)
m_promisePrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->stringCatch),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->stringCatch, builtinPromiseCatch, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)
m_promisePrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->then),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->then, builtinPromiseThen, 2, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// $25.6.5.3 Promise.prototype.finally ( onFinally )
m_promisePrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->finally),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->finally, builtinPromiseFinally, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
m_promise->setFunctionPrototype(state, m_promisePrototype);
// Promise.allSettled ( iterable )
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->allSettled),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->allSettled, builtinPromiseAllSettled, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
// Promise.any ( iterable )
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->any),
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->any, builtinPromiseAny, 1, NativeFunctionInfo::Strict)),
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
redefineOwnProperty(state, ObjectPropertyName(strings->Promise),
ObjectPropertyDescriptor(m_promise, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
}
} // namespace Escargot