blob: bb3b5151e7cad32f3ac80a254442de35776ae6b5 [file]
/*
* Copyright (C) 2013-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "JSMicrotask.h"
#include "AggregateError.h"
#include "BuiltinNames.h"
#include "Debugger.h"
#include "DeferTermination.h"
#include "FunctionCodeBlock.h"
#include "FunctionExecutable.h"
#include "GlobalObjectMethodTable.h"
#include "Interpreter.h"
#include "InterpreterInlines.h"
#include "IteratorOperations.h"
#include "MicrotaskCallInlines.h"
#include "JSArray.h"
#include "JSAsyncGenerator.h"
#include "JSFunction.h"
#include "JSGenerator.h"
#include "JSGlobalObject.h"
#include "JSObjectInlines.h"
#include "JSModuleLoader.h"
#include "JSModuleNamespaceObject.h"
#include "JSModuleRecord.h"
#include "JSPromise.h"
#include "JSPromiseCombinatorsContext.h"
#include "JSPromiseCombinatorsGlobalContext.h"
#include "JSPromiseConstructor.h"
#include "JSPromisePrototype.h"
#include "JSPromiseReaction.h"
#include "LLIntThunks.h"
#include "Microtask.h"
#include "ModuleGraphLoadingState.h"
#include "ModuleLoaderPayload.h"
#include "ModuleLoadingContext.h"
#include "ModuleRegistryEntry.h"
#include "ObjectConstructor.h"
#include "ScriptFetcher.h"
#include "ThrowScope.h"
#include "TopExceptionScope.h"
#include "VMTrapsInlines.h"
#include <wtf/NoTailCalls.h>
WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
namespace JSC {
static ALWAYS_INLINE JSCell* NODELETE dynamicCastToCell(JSValue value)
{
if (value.isCell())
return value.asCell();
return nullptr;
}
template<typename... Args> requires (std::is_convertible_v<Args, JSValue> && ...)
static JSValue callMicrotask(JSGlobalObject* globalObject, JSValue functionObject, JSValue thisValue, JSCell* context, ASCIILiteral message, MicrotaskCall* microtaskCall, Args... args)
{
NO_TAIL_CALLS();
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
static_assert(sizeof...(args) <= MicrotaskCall::maxCallArguments);
if (microtaskCall && microtaskCall->canUseCall(functionObject)) [[likely]] {
if (!vm.isSafeToRecurseSoft()) [[unlikely]]
return throwStackOverflowError(globalObject, scope);
auto* jsFunction = uncheckedDowncast<JSFunction>(functionObject.asCell());
if (auto result = microtaskCall->tryCallWithArguments(vm, jsFunction, thisValue, context, args...)) [[likely]] {
scope.release();
return result;
}
RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, scope.exception());
}
auto callData = JSC::getCallDataInline(functionObject);
if (callData.type == CallData::Type::None)
return throwTypeError(globalObject, scope, message);
ASSERT_WITH_MESSAGE(callData.type == CallData::Type::JS || callData.type == CallData::Type::Native, "Expected object to be callable but received %d", static_cast<int>(callData.type));
ASSERT_WITH_MESSAGE(!thisValue.isEmpty(), "Expected thisValue to be non-empty. Use jsUndefined() if you meant to use undefined.");
scope.assertNoException();
ASSERT(!vm.isCollectorBusyOnCurrentThread());
bool isJSCall = callData.type == CallData::Type::JS;
JSScope* functionScope = nullptr;
FunctionExecutable* functionExecutable = nullptr;
TaggedNativeFunction nativeFunction;
JSGlobalObject* calleeGlobalObject = nullptr;
RefPtr<JSC::JITCode> jitCode;
CodeBlock* newCodeBlock = nullptr;
if (isJSCall) {
functionScope = callData.js.scope;
functionExecutable = callData.js.functionExecutable;
if (!vm.isSafeToRecurseSoft()) [[unlikely]]
return throwStackOverflowError(functionScope->realm(), scope);
{
DeferTraps deferTraps(vm); // We can't jettison this code if we're about to run it.
// Compile the callee:
functionExecutable->prepareForExecution<FunctionExecutable>(vm, uncheckedDowncast<JSFunction>(functionObject.asCell()), functionScope, CodeSpecializationKind::CodeForCall, newCodeBlock);
RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, scope.exception());
ASSERT(newCodeBlock);
newCodeBlock->m_shouldAlwaysBeInlined = false;
}
if (microtaskCall) {
auto* jsFunction = uncheckedDowncast<JSFunction>(functionObject.asCell());
microtaskCall->initialize(vm, jsFunction);
RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, scope.exception());
if (auto result = microtaskCall->tryCallWithArguments(vm, jsFunction, thisValue, context, args...)) [[likely]] {
scope.release();
return result;
}
RETURN_IF_EXCEPTION_WITH_TRAPS_DEFERRED(scope, scope.exception());
}
#if (CPU(ARM64) || CPU(X86_64)) && CPU(ADDRESS64) && !ENABLE(C_LOOP)
if ((sizeof...(args) + 1) >= newCodeBlock->numParameters()) [[likely]] {
auto* entry = functionExecutable->generatedJITCodeAddressForCall();
auto* callee = asObject(functionObject.asCell());
if constexpr (!sizeof...(args))
return JSValue::decode(vmEntryToJavaScriptWith0Arguments(entry, &vm, newCodeBlock, callee, thisValue, context));
else if constexpr (sizeof...(args) == 1)
return JSValue::decode(vmEntryToJavaScriptWith1Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else if constexpr (sizeof...(args) == 2)
return JSValue::decode(vmEntryToJavaScriptWith2Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else if constexpr (sizeof...(args) == 3)
return JSValue::decode(vmEntryToJavaScriptWith3Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else if constexpr (sizeof...(args) == 4)
return JSValue::decode(vmEntryToJavaScriptWith4Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else if constexpr (sizeof...(args) == 5)
return JSValue::decode(vmEntryToJavaScriptWith5Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else if constexpr (sizeof...(args) == 6)
return JSValue::decode(vmEntryToJavaScriptWith6Arguments(entry, &vm, newCodeBlock, callee, thisValue, context, args...));
else
return { };
}
#endif
calleeGlobalObject = functionScope->realm();
{
AssertNoGC assertNoGC; // Ensure no GC happens. GC can replace CodeBlock in Executable.
jitCode = functionExecutable->generatedJITCodeForCall();
}
} else {
ASSERT(callData.type == CallData::Type::Native);
nativeFunction = callData.native.function;
calleeGlobalObject = asObject(functionObject)->realm();
if (!vm.isSafeToRecurseSoft()) [[unlikely]]
return throwStackOverflowError(calleeGlobalObject, scope);
}
// For native calls, fall back to the regular path
scope.release();
ProtoCallFrame protoCallFrame;
std::array<EncodedJSValue, sizeof...(args)> argArray = { { JSValue::encode(args)... } };
protoCallFrame.init(newCodeBlock, calleeGlobalObject, asObject(functionObject), thisValue, context, sizeof...(args) + 1, argArray.data());
if (isJSCall) {
ASSERT(jitCode == functionExecutable->generatedJITCodeForCall().ptr());
return JSValue::decode(vmEntryToJavaScript(jitCode->addressForCall(), &vm, &protoCallFrame));
}
#if ENABLE(WEBASSEMBLY)
if (callData.native.isWasm)
return JSValue::decode(vmEntryToWasm(uncheckedDowncast<WebAssemblyFunction>(functionObject)->jsToWasm(ArityCheckMode::MustCheckArity).taggedPtr(), &vm, &protoCallFrame));
#endif
return JSValue::decode(vmEntryToNative(nativeFunction.taggedPtr(), &vm, &protoCallFrame));
}
static void promiseResolveThenableJobFastSlow(JSGlobalObject* globalObject, JSPromise* promise, JSPromise* promiseToResolve)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
JSObject* constructor = promiseSpeciesConstructor(globalObject, promise);
if (scope.exception()) [[unlikely]]
return;
auto [resolve, reject] = promiseToResolve->createResolvingFunctions(vm, globalObject);
auto capability = JSPromise::createNewPromiseCapability(globalObject, constructor);
if (!scope.exception()) [[likely]] {
promise->performPromiseThen(vm, globalObject, resolve, reject, capability);
return;
}
JSValue error = scope.exception()->value();
if (!scope.clearExceptionExceptTermination()) [[unlikely]]
return;
MarkedArgumentBuffer arguments;
arguments.append(error);
ASSERT(!arguments.hasOverflowed());
auto callData = JSC::getCallDataInline(reject);
call(globalObject, reject, callData, jsUndefined(), arguments);
EXCEPTION_ASSERT(scope.exception() || true);
}
static void promiseResolveThenableJobWithInternalMicrotaskFastSlow(JSGlobalObject* globalObject, JSPromise* promise, InternalMicrotask task, JSValue context)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
JSObject* constructor = promiseSpeciesConstructor(globalObject, promise);
if (scope.exception()) [[unlikely]]
return;
auto [resolve, reject] = JSPromise::createResolvingFunctionsWithInternalMicrotask(vm, globalObject, task, context);
auto capability = JSPromise::createNewPromiseCapability(globalObject, constructor);
if (!scope.exception()) [[likely]] {
promise->performPromiseThen(vm, globalObject, resolve, reject, capability);
return;
}
JSValue error = scope.exception()->value();
if (!scope.clearExceptionExceptTermination()) [[unlikely]]
return;
MarkedArgumentBuffer arguments;
arguments.append(error);
ASSERT(!arguments.hasOverflowed());
auto callData = JSC::getCallDataInline(reject);
call(globalObject, reject, callData, jsUndefined(), arguments);
EXCEPTION_ASSERT(scope.exception() || true);
}
static void promiseResolveThenableJob(JSGlobalObject* globalObject, JSValue promise, JSValue then, JSValue resolve, JSValue reject)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
{
callMicrotask(globalObject, then, promise, dynamicCastToCell(then), "|then| is not a function"_s, nullptr, resolve, reject);
if (!scope.exception()) [[likely]]
return;
}
JSValue error = scope.exception()->value();
if (!scope.clearExceptionExceptTermination()) [[unlikely]]
return;
MarkedArgumentBuffer arguments;
arguments.append(error);
ASSERT(!arguments.hasOverflowed());
call(globalObject, reject, jsUndefined(), arguments, "|reject| is not a function"_s);
EXCEPTION_ASSERT(scope.exception() || true);
}
static void asyncFromSyncIteratorContinueOrDone(JSGlobalObject* globalObject, VM& vm, JSValue context, JSValue result, JSPromise::Status status, bool done)
{
auto scope = DECLARE_THROW_SCOPE(vm);
auto* contextObject = asObject(context);
JSValue promise = contextObject->getDirect(vm, vm.propertyNames->builtinNames().promisePrivateName());
ASSERT(promise.inherits<JSPromise>());
switch (status) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Rejected: {
JSValue syncIterator = contextObject->getDirect(vm, vm.propertyNames->builtinNames().syncIteratorPrivateName());
if (syncIterator.isObject()) {
JSValue returnMethod;
JSValue error;
{
auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
returnMethod = asObject(syncIterator)->get(globalObject, vm.propertyNames->returnKeyword);
if (catchScope.exception()) [[unlikely]] {
error = catchScope.exception()->value();
if (!catchScope.clearExceptionExceptTermination()) [[unlikely]] {
scope.release();
return;
}
}
}
if (error) [[unlikely]] {
uncheckedDowncast<JSPromise>(promise)->reject(vm, globalObject, error);
return;
}
if (returnMethod.isCallable()) {
callMicrotask(globalObject, returnMethod, syncIterator, dynamicCastToCell(returnMethod), "return is not a function"_s, nullptr);
if (scope.exception()) [[unlikely]]
return;
}
}
scope.release();
uncheckedDowncast<JSPromise>(promise)->reject(vm, globalObject, result);
break;
}
case JSPromise::Status::Fulfilled: {
auto* resultObject = createIteratorResultObject(globalObject, result, done);
scope.release();
uncheckedDowncast<JSPromise>(promise)->resolve(globalObject, vm, resultObject);
break;
}
}
}
static void promiseRaceResolveJob(JSGlobalObject* globalObject, VM& vm, JSPromise* promise, JSValue resolution, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
if (promise->status() != JSPromise::Status::Pending)
return;
switch (status) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Fulfilled: {
scope.release();
promise->resolve(globalObject, vm, resolution);
break;
}
case JSPromise::Status::Rejected: {
scope.release();
promise->reject(vm, globalObject, resolution);
break;
}
}
}
static void promiseAllResolveJob(JSGlobalObject* globalObject, VM& vm, JSPromise* promise, JSValue resolution, JSPromiseCombinatorsContext* context, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
auto* globalContext = context->globalContext();
switch (status) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Fulfilled: {
auto* values = uncheckedDowncast<JSArray>(globalContext->values());
uint64_t index = context->index();
values->putDirectIndex(globalObject, index, resolution);
RETURN_IF_EXCEPTION(scope, void());
uint64_t count = globalContext->remainingElementsCount().toIndex(globalObject, "count exceeds size"_s);
RETURN_IF_EXCEPTION(scope, void());
--count;
globalContext->setRemainingElementsCount(vm, jsNumber(count));
if (!count) {
scope.release();
promise->resolve(globalObject, vm, values);
}
break;
}
case JSPromise::Status::Rejected: {
scope.release();
promise->reject(vm, globalObject, resolution);
break;
}
}
}
static void promiseAllSettledResolveJob(JSGlobalObject* globalObject, VM& vm, JSPromise* promise, JSValue resolution, JSPromiseCombinatorsContext* context, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
auto* globalContext = context->globalContext();
auto* values = uncheckedDowncast<JSArray>(globalContext->values());
uint64_t index = context->index();
JSObject* resultObject = nullptr;
switch (status) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Fulfilled: {
resultObject = createPromiseAllSettledFulfilledResult(globalObject, resolution);
break;
}
case JSPromise::Status::Rejected: {
resultObject = createPromiseAllSettledRejectedResult(globalObject, resolution);
break;
}
}
values->putDirectIndex(globalObject, index, resultObject);
RETURN_IF_EXCEPTION(scope, void());
uint64_t count = globalContext->remainingElementsCount().toIndex(globalObject, "count exceeds size"_s);
RETURN_IF_EXCEPTION(scope, void());
--count;
globalContext->setRemainingElementsCount(vm, jsNumber(count));
if (!count) {
scope.release();
promise->resolve(globalObject, vm, values);
}
}
static void promiseAnyResolveJob(JSGlobalObject* globalObject, VM& vm, JSPromise* promise, JSValue resolution, JSPromiseCombinatorsContext* context, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
auto* globalContext = context->globalContext();
switch (status) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Fulfilled: {
scope.release();
promise->resolve(globalObject, vm, resolution);
break;
}
case JSPromise::Status::Rejected: {
auto* errors = uncheckedDowncast<JSArray>(globalContext->values());
uint64_t index = context->index();
errors->putDirectIndex(globalObject, index, resolution);
RETURN_IF_EXCEPTION(scope, void());
uint64_t count = globalContext->remainingElementsCount().toIndex(globalObject, "count exceeds size"_s);
RETURN_IF_EXCEPTION(scope, void());
--count;
globalContext->setRemainingElementsCount(vm, jsNumber(count));
if (!count) {
auto* aggregateError = createAggregateError(vm, globalObject->errorStructure(ErrorType::AggregateError), errors, String(), jsUndefined());
scope.release();
promise->reject(vm, globalObject, aggregateError);
}
break;
}
}
}
static bool NODELETE isSuspendYieldState(int32_t state)
{
return state > 0 && (state & JSAsyncGenerator::reasonMask) == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Yield);
}
static void asyncGeneratorResumeNext(JSGlobalObject*, JSAsyncGenerator*);
template<IterationStatus status>
static void asyncGeneratorReject(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue error)
{
VM& vm = globalObject->vm();
auto [value, resumeMode, promise] = generator->dequeue(vm);
ASSERT(promise);
promise->reject(vm, globalObject, error);
if constexpr (status == IterationStatus::Continue)
asyncGeneratorResumeNext(globalObject, generator);
}
template<IterationStatus status>
static void asyncGeneratorResolve(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue value, bool done)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto [itemValue, itemResumeMode, promise] = generator->dequeue(vm);
ASSERT(promise);
auto* iteratorResult = createIteratorResultObject(globalObject, value, done);
promise->resolve(globalObject, vm, iteratorResult);
RETURN_IF_EXCEPTION(scope, void());
if constexpr (status == IterationStatus::Continue)
RELEASE_AND_RETURN(scope, asyncGeneratorResumeNext(globalObject, generator));
}
template<IterationStatus status>
static bool asyncGeneratorBodyCall(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue resumeValue, int32_t resumeMode)
{
VM& vm = globalObject->vm();
int32_t state = generator->state();
if (resumeMode == static_cast<int32_t>(JSGenerator::ResumeMode::ReturnMode) && isSuspendYieldState(state)) {
state = (state & ~JSAsyncGenerator::reasonMask) | static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Await);
generator->setState(state);
JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, resumeValue, InternalMicrotask::AsyncGeneratorBodyCallReturn, generator);
return false;
}
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Executing));
JSValue generatorFunction = generator->next();
JSValue generatorThis = generator->thisValue();
JSValue generatorFrame = generator->frame();
JSValue value;
JSValue error;
{
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
value = callMicrotask(globalObject, generatorFunction, generatorThis, generator, "handler is not a function"_s, nullptr,
generator, jsNumber(state >> JSAsyncGenerator::reasonShift), resumeValue, jsNumber(resumeMode), generatorFrame);
if (scope.exception()) [[unlikely]] {
error = scope.exception()->value();
if (!scope.clearExceptionExceptTermination()) [[unlikely]]
return false;
}
}
if (error) [[unlikely]] {
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed));
asyncGeneratorReject<status>(globalObject, generator, error);
return true;
}
state = generator->state();
if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Executing)) {
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed));
state = static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed);
}
if (state > 0) {
if ((state & JSAsyncGenerator::reasonMask) == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Await)) {
JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, value, InternalMicrotask::AsyncGeneratorBodyCallNormal, generator);
return false;
}
state = (state & ~JSAsyncGenerator::reasonMask) | static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Await);
generator->setState(state);
JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, value, InternalMicrotask::AsyncGeneratorYieldAwaited, generator);
return false;
}
if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed)) {
asyncGeneratorResolve<status>(globalObject, generator, value, true);
return true;
}
return false;
}
static void asyncGeneratorResumeNext(JSGlobalObject* globalObject, JSAsyncGenerator* generator)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
while (true) {
int32_t state = generator->state();
ASSERT(state != static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Executing));
if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::AwaitingReturn))
return;
if (generator->isQueueEmpty())
return;
JSValue nextValue = generator->resumeValue();
int32_t resumeMode = generator->resumeMode();
if (resumeMode != static_cast<int32_t>(JSGenerator::ResumeMode::NormalMode)) {
if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Init)) {
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed));
state = static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed);
}
if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed)) {
if (resumeMode == static_cast<int32_t>(JSGenerator::ResumeMode::ReturnMode)) {
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::AwaitingReturn));
RELEASE_AND_RETURN(scope, JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, nextValue, InternalMicrotask::AsyncGeneratorResumeNext, generator));
}
ASSERT(resumeMode == static_cast<int32_t>(JSGenerator::ResumeMode::ThrowMode));
asyncGeneratorReject<IterationStatus::Done>(globalObject, generator, nextValue);
continue;
}
} else if (state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed)) {
asyncGeneratorResolve<IterationStatus::Done>(globalObject, generator, jsUndefined(), true);
RETURN_IF_EXCEPTION(scope, void());
continue;
}
ASSERT(state == static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Init) || isSuspendYieldState(state));
bool next = asyncGeneratorBodyCall<IterationStatus::Done>(globalObject, generator, nextValue, resumeMode);
RETURN_IF_EXCEPTION(scope, void());
if (!next)
return;
}
}
static void asyncGeneratorYieldAwaited(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue result, JSPromise::Status status)
{
switch (status) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Rejected:
asyncGeneratorBodyCall<IterationStatus::Continue>(globalObject, generator, result, static_cast<int32_t>(JSGenerator::ResumeMode::ThrowMode));
return;
case JSPromise::Status::Fulfilled: {
int32_t state = generator->state();
state = (state & ~JSAsyncGenerator::reasonMask) | static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorSuspendReason::Yield);
generator->setState(state);
asyncGeneratorResolve<IterationStatus::Continue>(globalObject, generator, result, false);
return;
}
}
}
static void asyncGeneratorBodyCallNormal(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue result, JSPromise::Status status)
{
switch (status) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Rejected:
asyncGeneratorBodyCall<IterationStatus::Continue>(globalObject, generator, result, static_cast<int32_t>(JSGenerator::ResumeMode::ThrowMode));
return;
case JSPromise::Status::Fulfilled:
asyncGeneratorBodyCall<IterationStatus::Continue>(globalObject, generator, result, static_cast<int32_t>(JSGenerator::ResumeMode::NormalMode));
return;
}
}
static void asyncGeneratorBodyCallReturn(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue result, JSPromise::Status status)
{
switch (status) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Rejected:
asyncGeneratorBodyCall<IterationStatus::Continue>(globalObject, generator, result, static_cast<int32_t>(JSGenerator::ResumeMode::ThrowMode));
return;
case JSPromise::Status::Fulfilled:
asyncGeneratorBodyCall<IterationStatus::Continue>(globalObject, generator, result, static_cast<int32_t>(JSGenerator::ResumeMode::ReturnMode));
return;
}
}
static void asyncGeneratorResumeNextReturn(JSGlobalObject* globalObject, JSAsyncGenerator* generator, JSValue result, JSPromise::Status status)
{
generator->setState(static_cast<int32_t>(JSAsyncGenerator::AsyncGeneratorState::Completed));
switch (status) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Rejected:
asyncGeneratorReject<IterationStatus::Continue>(globalObject, generator, result);
return;
case JSPromise::Status::Fulfilled:
asyncGeneratorResolve<IterationStatus::Continue>(globalObject, generator, result, true);
return;
}
}
static void promiseFinallyAwaitJob(JSGlobalObject* globalObject, VM& vm, JSValue settledValue, JSPromiseCombinatorsGlobalContext* context, JSPromise::Status status)
{
auto* resultPromise = uncheckedDowncast<JSPromise>(context->promise());
JSValue originalValue = context->values();
bool wasFulfilled = context->remainingElementsCount().asBoolean();
if (status == JSPromise::Status::Rejected) {
resultPromise->rejectPromise(vm, globalObject, settledValue);
return;
}
if (wasFulfilled)
resultPromise->resolvePromise(globalObject, vm, originalValue);
else
resultPromise->rejectPromise(vm, globalObject, originalValue);
}
static void promiseFinallyReactionJob(JSGlobalObject* globalObject, VM& vm, JSPromise* resultPromise, JSValue valueOrReason, JSPromiseCombinatorsGlobalContext* context, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue onFinally = context->values();
JSValue result;
JSValue error;
{
auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
result = callMicrotask(globalObject, onFinally, jsUndefined(), dynamicCastToCell(onFinally), "onFinally is not a function"_s, nullptr);
if (catchScope.exception()) {
error = catchScope.exception()->value();
if (!catchScope.clearExceptionExceptTermination()) [[unlikely]] {
scope.release();
return;
}
}
}
if (error) {
resultPromise->rejectPromise(vm, globalObject, error);
return;
}
context->setValues(vm, valueOrReason);
context->setRemainingElementsCount(vm, jsBoolean(status == JSPromise::Status::Fulfilled));
if (result.inherits<JSPromise>()) {
auto* promise = uncheckedDowncast<JSPromise>(result);
if (promise->isThenFastAndNonObservable()) {
scope.release();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::PromiseFinallyAwaitJob, resultPromise, context);
return;
}
}
if (!result.isObject()) {
scope.release();
promiseFinallyAwaitJob(globalObject, vm, result, context, JSPromise::Status::Fulfilled);
return;
}
auto* resolutionObject = asObject(result);
if (isDefinitelyNonThenable(resolutionObject, globalObject)) {
scope.release();
promiseFinallyAwaitJob(globalObject, vm, result, context, JSPromise::Status::Fulfilled);
return;
}
JSValue then;
{
auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
then = resolutionObject->get(globalObject, vm.propertyNames->then);
if (catchScope.exception()) [[unlikely]] {
error = catchScope.exception()->value();
if (!catchScope.clearExceptionExceptTermination()) [[unlikely]] {
scope.release();
return;
}
}
}
if (error) [[unlikely]] {
scope.release();
promiseFinallyAwaitJob(globalObject, vm, error, context, JSPromise::Status::Rejected);
return;
}
if (!then.isCallable()) [[likely]] {
scope.release();
promiseFinallyAwaitJob(globalObject, vm, result, context, JSPromise::Status::Fulfilled);
return;
}
auto [resolve, reject] = JSPromise::createResolvingFunctionsWithInternalMicrotask(vm, globalObject, InternalMicrotask::PromiseFinallyAwaitJob, context);
scope.release();
promiseResolveThenableJob(globalObject, resolutionObject, then, resolve, reject);
}
static void asyncModuleExecutionDone(JSGlobalObject* globalObject, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
scope.release();
auto* module = uncheckedDowncast<JSModuleRecord>(arguments[2]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled)
module->asyncExecutionFulfilled(globalObject);
else {
ASSERT(status == JSPromise::Status::Rejected);
module->asyncExecutionRejected(globalObject, arguments[1]);
}
}
static void asyncModuleExecutionResume(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
auto* module = uncheckedDowncast<JSModuleRecord>(arguments[2]);
JSValue resolution = arguments[1];
auto status = static_cast<JSPromise::Status>(payload);
auto* capability = module->asyncCapability();
JSValue resumeMode = jsNumber(status == JSPromise::Status::Fulfilled
? static_cast<int32_t>(JSGenerator::ResumeMode::NormalMode)
: static_cast<int32_t>(JSGenerator::ResumeMode::ThrowMode));
JSValue result = module->evaluate(globalObject, resolution, resumeMode);
if (scope.exception())
capability->rejectWithCaughtException(globalObject, scope);
else {
JSValue state = module->internalField(AbstractModuleRecord::Field::State).get();
if (!state.isNumber() || state.asNumber() == static_cast<int32_t>(JSGenerator::State::Executing))
capability->resolve(globalObject, vm, result);
else
JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, result, InternalMicrotask::AsyncModuleExecutionResume, module);
}
}
static void moduleRegistryFetchSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// arguments[0] = pre-created modulePromise
// arguments[1] = resolution (JSSourceCode*) or rejection (error)
// arguments[2] = ModuleRegistryEntry*
auto* entry = uncheckedDowncast<ModuleRegistryEntry>(arguments[2]);
auto* modulePromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
auto* jsSourceCode = downcast<JSSourceCode>(arguments[1]);
JSPromise* makeModulePromise = JSModuleLoader::makeModule(globalObject, entry->key(), jsSourceCode);
if (scope.exception()) {
modulePromise->rejectWithCaughtException(globalObject, scope);
return;
}
makeModulePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleRegistryModuleSettled, modulePromise, entry);
} else {
JSValue errorValue = arguments[1];
if (auto* error = dynamicDowncast<ErrorInstance>(errorValue))
JSModuleLoader::attachErrorInfo(globalObject, error, nullptr, entry->key(), entry->moduleType(), JSModuleLoader::ModuleFailure::Kind::Instantiation);
entry->setFetchError(globalObject, errorValue);
modulePromise->reject(vm, globalObject, errorValue);
}
}
static void moduleRegistryModuleSettled(JSGlobalObject* globalObject, VM& vm, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// arguments[0] = pre-created modulePromise
// arguments[1] = resolution (AbstractModuleRecord*) or rejection (error)
// arguments[2] = ModuleRegistryEntry*
auto* entry = uncheckedDowncast<ModuleRegistryEntry>(arguments[2]);
auto* modulePromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
auto* moduleRecord = downcast<AbstractModuleRecord>(arguments[1]);
entry->fetchComplete(globalObject, moduleRecord);
modulePromise->fulfill(vm, globalObject, moduleRecord);
} else {
JSValue errorValue = arguments[1];
entry->setEvaluationError(globalObject, errorValue);
modulePromise->reject(vm, globalObject, errorValue);
}
}
static void moduleGraphLoadingError(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// arguments[0] = unused (jsUndefined)
// arguments[1] = resolution value or error
// arguments[2] = ModuleGraphLoadingState*
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Rejected) {
auto* state = uncheckedDowncast<ModuleGraphLoadingState>(arguments[2]);
JSValue errorValue = arguments[1];
if (auto* error = dynamicDowncast<ErrorInstance>(errorValue)) {
errorValue = JSModuleLoader::maybeDuplicateFetchError(globalObject, error);
RETURN_IF_EXCEPTION(scope, void());
}
state->promise()->reject(vm, globalObject, errorValue);
}
}
static void moduleLoadStep(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// arguments[0] = pre-created loadPromise
// arguments[1] = resolution value or error
// arguments[2] = ModuleLoadingContext*
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
auto* loadPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
switch (context->step()) {
case ModuleLoadingContext::Step::Main: {
// modulePromise settled: on fulfillment, call loadRequestedModules and chain next step
if (status == JSPromise::Status::Fulfilled) {
auto* module = downcast<AbstractModuleRecord>(arguments[1]);
context->module(vm, module);
JSPromise* requestedPromise = globalObject->moduleLoader()->loadRequestedModules(globalObject, module, context->scriptFetcher());
if (scope.exception()) {
loadPromise->rejectWithCaughtException(globalObject, scope);
return;
}
context->setStep(ModuleLoadingContext::Step::Requested);
requestedPromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadStep, loadPromise, context);
} else
loadPromise->reject(vm, globalObject, arguments[1]);
return;
}
case ModuleLoadingContext::Step::Requested: {
// loadRequestedModules settled: on fulfillment, call finishLoading and update entry
if (status == JSPromise::Status::Fulfilled) {
auto* module = context->module();
globalObject->moduleLoader()->finishLoadingImportedModule(globalObject, context->referrer(), context->moduleRequest(), context->payload(), module, context->scriptFetcher());
if (scope.exception()) {
loadPromise->rejectWithCaughtException(globalObject, scope);
return;
}
// setEntryRecord logic
auto* entry = context->entry();
if (auto* cyclic = dynamicDowncast<CyclicModuleRecord>(module); cyclic && cyclic->status() != CyclicModuleRecord::Status::Unlinked) {
ASSERT(cyclic->status() != CyclicModuleRecord::Status::Linking);
loadPromise->fulfill(vm, globalObject, entry->record());
} else {
entry->setRecord(vm, module);
entry->setStatus(ModuleRegistryEntry::Status::Fetched);
loadPromise->fulfill(vm, globalObject, entry->record());
}
} else {
// onRejected logic: store evaluation error on entry
auto* entry = context->entry();
JSValue errorValue = arguments[1];
entry->setEvaluationError(globalObject, errorValue);
loadPromise->reject(vm, globalObject, errorValue);
}
return;
}
case ModuleLoadingContext::Step::Cached: {
// Cached loadPromise settled: on fulfillment, call finishLoading
if (status == JSPromise::Status::Fulfilled) {
auto* module = downcast<AbstractModuleRecord>(arguments[1]);
globalObject->moduleLoader()->finishLoadingImportedModule(globalObject, context->referrer(), context->moduleRequest(), context->payload(), module, context->scriptFetcher());
if (scope.exception()) {
loadPromise->rejectWithCaughtException(globalObject, scope);
return;
}
loadPromise->fulfill(vm, globalObject, module);
} else {
auto* entry = context->entry();
JSValue errorValue = arguments[1];
entry->setEvaluationError(globalObject, errorValue);
loadPromise->reject(vm, globalObject, errorValue);
}
return;
}
}
RELEASE_ASSERT_NOT_REACHED();
}
static void moduleLoadTopSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// loadModule first overload: fetch promise settled
// arguments[0] = pre-created intermediatePromise
// arguments[1] = resolution (JSSourceCode*) or error
// arguments[2] = ModuleLoadingContext*
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
auto* intermediatePromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
auto* jsSourceCode = downcast<JSSourceCode>(arguments[1]);
const Identifier& specifier = context->moduleRequest().m_specifier;
auto type = context->moduleRequest().type();
ScriptFetcher* scriptFetcher = context->scriptFetcher();
globalObject->moduleLoader()->provideFetch(globalObject, specifier, type, jsSourceCode);
if (scope.exception()) {
intermediatePromise->rejectWithCaughtException(globalObject, scope);
return;
}
JSPromise* statePromise = JSPromise::create(vm, globalObject->promiseStructure());
statePromise->markAsHandled();
AbstractModuleRecord::ModuleRequest request { specifier, ScriptFetchParameters::create(type) };
// combinedCell is the host-defined payload AND the AND-join state for loadPromise+statePromise.
// For dynamic import we wrap statePromise; for graph load we use the ModuleGraphLoadingState directly.
JSCell* combinedCell;
JSPromise* loadPromise;
if (context->dynamic()) {
combinedCell = ModuleLoaderPayload::create(vm, statePromise);
loadPromise = globalObject->moduleLoader()->loadModule(globalObject, globalObject, request, combinedCell, scriptFetcher, false, context->useImportMap());
} else {
combinedCell = ModuleGraphLoadingState::create(vm, statePromise, scriptFetcher);
loadPromise = globalObject->moduleLoader()->loadModule(globalObject, globalObject, request, combinedCell, scriptFetcher, context->evaluate(), context->useImportMap());
if (scope.exception()) {
intermediatePromise->rejectWithCaughtException(globalObject, scope);
return;
}
// Specifier transform: instead of creating a closure, use a microtask
JSPromise* transformedStatePromise = JSPromise::create(vm, globalObject->promiseStructure());
transformedStatePromise->markAsHandled();
statePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadSpecifierTransform, transformedStatePromise, context);
statePromise = transformedStatePromise;
}
if (scope.exception()) {
intermediatePromise->rejectWithCaughtException(globalObject, scope);
return;
}
JSPromise* combinedPromise = JSPromise::create(vm, globalObject->promiseStructure());
combinedPromise->markAsHandled();
loadPromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadCombinedLoadSettled, combinedPromise, combinedCell);
statePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadCombinedStateSettled, combinedPromise, combinedCell);
intermediatePromise->pipeFrom(vm, combinedPromise);
} else {
// onFetchRejected logic
const Identifier& specifier = context->moduleRequest().m_specifier;
auto type = context->moduleRequest().type();
ModuleRegistryEntry* entry = globalObject->moduleLoader()->ensureRegistered(globalObject, specifier, type);
if (scope.exception()) {
intermediatePromise->rejectWithCaughtException(globalObject, scope);
return;
}
JSValue errorValue = arguments[1];
if (auto* error = dynamicDowncast<ErrorInstance>(errorValue)) {
auto failure = JSModuleLoader::getErrorInfo(globalObject, error);
if (failure.isEvaluationError(specifier, type))
entry->setEvaluationError(globalObject, error);
else
entry->setFetchError(globalObject, error);
}
intermediatePromise->reject(vm, globalObject, errorValue);
}
}
static void moduleLoadTopRejected(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// loadModule first overload: onLoadRejected
// arguments[0] = pre-created resultPromise
// arguments[1] = resolution or error
// arguments[2] = ModuleLoadingContext*
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
auto* resultPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled)
resultPromise->fulfill(vm, globalObject, arguments[1]);
else {
const Identifier& specifier = context->moduleRequest().m_specifier;
auto type = context->moduleRequest().type();
ModuleRegistryEntry* entry = globalObject->moduleLoader()->ensureRegistered(globalObject, specifier, type);
if (scope.exception()) {
resultPromise->rejectWithCaughtException(globalObject, scope);
return;
}
if (JSValue fetchErrorValue = entry->fetchError()) {
if (ErrorInstance* fetchError = dynamicDowncast<ErrorInstance>(fetchErrorValue)) {
ErrorInstance* fetchErrorCopy = JSModuleLoader::maybeDuplicateFetchError(globalObject, fetchError);
if (scope.exception()) {
resultPromise->rejectWithCaughtException(globalObject, scope);
return;
}
resultPromise->reject(vm, globalObject, fetchErrorCopy);
} else
resultPromise->reject(vm, globalObject, fetchErrorValue);
return;
}
JSValue error = arguments[1];
entry->setEvaluationError(globalObject, error);
resultPromise->reject(vm, globalObject, error);
}
}
static void moduleLoadSpecifierTransform(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// Transforms resolution to specifier identifier
// arguments[0] = pre-created transformedStatePromise
// arguments[1] = resolution
// arguments[2] = ModuleLoadingContext*
auto* transformedPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
scope.release();
transformedPromise->fulfill(vm, globalObject, identifierToJSValue(vm, context->moduleRequest().m_specifier));
} else
transformedPromise->reject(vm, globalObject, arguments[1]);
}
static void moduleLoadCombinedLoadSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// Combined promise: load side settled
// arguments[0] = combinedPromise
// arguments[1] = resolution or error
// arguments[2] = ModuleGraphLoadingState* (graph load) or ModuleLoaderPayload* (dynamic import)
auto* combinedPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto* combinedCell = arguments[2].asCell();
ASSERT(isModuleLoaderHostDefinedPayload(combinedCell));
auto status = static_cast<JSPromise::Status>(payload);
scope.release();
bool fullySettled;
if (auto* state = dynamicDowncast<ModuleGraphLoadingState>(combinedCell))
fullySettled = state->decrementRemaining();
else
fullySettled = uncheckedDowncast<ModuleLoaderPayload>(combinedCell)->decrementRemaining();
switch (combinedPromise->status()) {
case JSPromise::Status::Pending: {
if (status == JSPromise::Status::Fulfilled) {
if (!fullySettled)
return;
JSValue fulfillmentValue;
if (auto* state = dynamicDowncast<ModuleGraphLoadingState>(combinedCell))
fulfillmentValue = state->fulfillment();
else
fulfillmentValue = uncheckedDowncast<ModuleLoaderPayload>(combinedCell)->fulfillment();
ASSERT(fulfillmentValue);
combinedPromise->fulfill(vm, globalObject, fulfillmentValue);
} else
combinedPromise->reject(vm, globalObject, arguments[1]);
return;
}
default:
return;
}
}
static void moduleLoadCombinedStateSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// Combined promise: state side settled
// arguments[0] = combinedPromise
// arguments[1] = resolution or error
// arguments[2] = ModuleGraphLoadingState* (graph load) or ModuleLoaderPayload* (dynamic import)
auto* combinedPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto* combinedCell = arguments[2].asCell();
ASSERT(isModuleLoaderHostDefinedPayload(combinedCell));
auto status = static_cast<JSPromise::Status>(payload);
scope.release();
bool fullySettled;
if (auto* state = dynamicDowncast<ModuleGraphLoadingState>(combinedCell)) {
fullySettled = state->decrementRemaining();
if (status == JSPromise::Status::Fulfilled && !fullySettled)
state->setFulfillment(vm, arguments[1]);
} else {
auto* p = uncheckedDowncast<ModuleLoaderPayload>(combinedCell);
fullySettled = p->decrementRemaining();
if (status == JSPromise::Status::Fulfilled && !fullySettled)
p->setFulfillment(vm, arguments[1]);
}
switch (combinedPromise->status()) {
case JSPromise::Status::Pending:
if (status == JSPromise::Status::Fulfilled) {
if (fullySettled)
combinedPromise->fulfill(vm, globalObject, arguments[1]);
} else
combinedPromise->reject(vm, globalObject, arguments[1]);
return;
default:
return;
}
}
static void moduleLoadLinkEvaluateSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// loadModule second overload: onFulfilled
// arguments[0] = pre-created resultPromise
// arguments[1] = resolution (AbstractModuleRecord*) or error
// arguments[2] = ModuleLoadingContext*
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
auto* resultPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
auto* record = downcast<AbstractModuleRecord>(arguments[1]);
if (context->evaluate()) {
record->link(globalObject, context->scriptFetcher());
JSModuleLoader::attachErrorInfo(globalObject, scope, record, record->moduleKey(), record->moduleType(), JSModuleLoader::ModuleFailure::Kind::Instantiation);
if (scope.exception()) {
resultPromise->rejectWithCaughtException(globalObject, scope);
return;
}
JSPromise* evaluatePromise = record->evaluate(globalObject);
JSModuleLoader::attachErrorInfo(globalObject, scope, record, record->moduleKey(), record->moduleType(), JSModuleLoader::ModuleFailure::Kind::Evaluation);
if (scope.exception()) {
resultPromise->rejectWithCaughtException(globalObject, scope);
return;
}
// Chain: when evaluation completes, resolve resultPromise with record
evaluatePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadReturnRecord, resultPromise, record);
} else
resultPromise->fulfill(vm, globalObject, identifierToJSValue(vm, record->moduleKey()));
} else
resultPromise->reject(vm, globalObject, arguments[1]);
}
static void moduleLoadReturnRecord(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// Resolves promise with the record after evaluation completes
// arguments[0] = resultPromise
// arguments[1] = resolution or error
// arguments[2] = AbstractModuleRecord*
auto* resultPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
scope.release();
if (status == JSPromise::Status::Fulfilled)
resultPromise->fulfill(vm, globalObject, arguments[2]);
else
resultPromise->reject(vm, globalObject, arguments[1]);
}
static void moduleLoadStoreError(JSGlobalObject* globalObject, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// loadModule second overload: fire-and-forget error categorization
// arguments[0] = unused
// arguments[1] = resolution or error
// arguments[2] = ModuleLoadingContext*
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Rejected) {
auto* context = uncheckedDowncast<ModuleLoadingContext>(arguments[2]);
JSValue errorValue = arguments[1];
const Identifier& specifier = context->moduleRequest().m_specifier;
auto type = context->moduleRequest().type();
ModuleRegistryEntry* entry = globalObject->moduleLoader()->ensureRegistered(globalObject, specifier, type);
RETURN_IF_EXCEPTION(scope, void());
if (auto* error = dynamicDowncast<ErrorInstance>(errorValue)) {
auto failure = JSModuleLoader::getErrorInfo(globalObject, error);
if (failure.isEvaluationError(specifier, type))
entry->setEvaluationError(globalObject, error);
else if (JSModuleLoader::isFetchError(globalObject, error))
entry->setFetchError(globalObject, error);
else
entry->setInstantiationError(globalObject, error);
} else
entry->setEvaluationError(globalObject, errorValue);
}
}
static void dynamicImportLoadSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// https://tc39.es/ecma262/#sec-ContinueDynamicImport
// Step-4 rejectedClosure or Step-6 linkAndEvaluateClosure
//
// continueDynamicImport: loadPromise settled
// arguments[0] = capabilityPromise
// arguments[1] = resolution or error
// arguments[2] = AbstractModuleRecord*
auto* capabilityPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto* module = uncheckedDowncast<AbstractModuleRecord>(arguments[2]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
// Step-6 linkAndEvaluateClosure
// 6.a. Let link be Completion(module.Link()).
module->link(globalObject, nullptr);
// 6.b. If link is an abrupt completion, then
if (Exception* exception = scope.exception()) [[unlikely]] {
// 6.b.i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
JSModuleLoader::attachErrorInfo(globalObject, exception, module, module->moduleKey(), module->moduleType(), JSModuleLoader::ModuleFailure::Kind::Instantiation);
capabilityPromise->rejectWithCaughtException(globalObject, scope);
return;
}
// 6.c. Let evaluatePromise be module.Evaluate().
JSPromise* evaluatePromise = module->evaluate(globalObject);
if (scope.exception()) [[unlikely]] {
capabilityPromise->rejectWithCaughtException(globalObject, scope);
return;
}
// 6.d-f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, onRejected).
evaluatePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::DynamicImportEvaluateSettled, capabilityPromise, module);
} else {
// Step-4 rejectedClosure
// 4.a. Perform ! Call(promiseCapability.[[Reject]], undefined, « reason »).
capabilityPromise->reject(vm, globalObject, arguments[1]);
}
}
static void dynamicImportEvaluateSettled(JSGlobalObject* globalObject, VM& vm, ThrowScope& scope, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// https://tc39.es/ecma262/#sec-ContinueDynamicImport
// Step-4 rejectedClosure or Step-6.c fulfilledClosure
//
// continueDynamicImport: evaluate settled
// arguments[0] = capabilityPromise
// arguments[1] = resolution or error
// arguments[2] = AbstractModuleRecord*
auto* capabilityPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto* module = uncheckedDowncast<AbstractModuleRecord>(arguments[2]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
// 6.d.i. Let namespace be GetModuleNamespace(module).
JSModuleNamespaceObject* moduleNamespace = module->getModuleNamespace(globalObject);
if (scope.exception()) [[unlikely]] {
capabilityPromise->rejectWithCaughtException(globalObject, scope);
return;
}
// 6.d.ii. Perform ! Call(promiseCapability.[[Resolve]], undefined, « namespace »).
// This step resolves the promiseCapability with the namespace. However,
// capabilityPromise here is the internal statePromise from moduleLoadTopSettled,
// not the user-visible import() promise. The actual spec-required resolve()
// happens in importModuleNamespace. Use fulfill here to avoid unnecessary
// thenable unwrapping on internal pipeline.
//
// FIXME: This is different from the spec while user-observable behavior is correctly
// aligned (as "resolve" will happen in resultPromise side from dynamic import).
// But ideally, this carried capabilityPromise should be the last user-observable
// promise and we should do "resolve" here. This requires some clean up.
capabilityPromise->fulfill(vm, globalObject, moduleNamespace);
} else
capabilityPromise->reject(vm, globalObject, arguments[1]);
}
static void importModuleNamespace(JSGlobalObject* globalObject, VM& vm, ThrowScope&, std::span<const JSValue, maxMicrotaskArguments> arguments, uint8_t payload)
{
// requestImportModule: namespace getter
// arguments[0] = resultPromise
// arguments[1] = module namespace (from dynamic import pipeline) or error
// arguments[2] = unused
auto* resultPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
if (status == JSPromise::Status::Fulfilled) {
// The value is a JSModuleNamespaceObject forwarded from the internal
// pipeline (dynamicImportEvaluateSettled → combinedPromise → here).
// resultPromise is the user-visible import() promise. Must use resolve() per spec:
// ContinueDynamicImport https://tc39.es/ecma262/#sec-ContinueDynamicImport
// Step 6.d.ii: Call(promiseCapability.[[Resolve]], undefined, « namespace »).
// A module namespace that exports "then" is a thenable per spec.
auto* moduleNamespace = downcast<JSModuleNamespaceObject>(arguments[1]);
resultPromise->resolve(globalObject, vm, moduleNamespace);
} else
resultPromise->reject(vm, globalObject, arguments[1]);
return;
}
static void promiseResolveWithoutHandlerJobSlow(JSGlobalObject* globalObject, VM& vm, JSValue capability, JSValue resolution, JSPromise::Status status)
{
auto scope = DECLARE_THROW_SCOPE(vm);
if (status == JSPromise::Status::Rejected) {
JSValue reject = capability.get(globalObject, vm.propertyNames->reject);
RETURN_IF_EXCEPTION(scope, void());
MarkedArgumentBuffer arguments;
arguments.append(resolution);
ASSERT(!arguments.hasOverflowed());
scope.release();
call(globalObject, reject, jsUndefined(), arguments, "reject is not a function"_s);
return;
}
JSValue resolve = capability.get(globalObject, vm.propertyNames->resolve);
RETURN_IF_EXCEPTION(scope, void());
MarkedArgumentBuffer arguments;
arguments.append(resolution);
ASSERT(!arguments.hasOverflowed());
scope.release();
call(globalObject, resolve, jsUndefined(), arguments, "resolve is not a function"_s);
}
static void promiseResolveWithoutHandlerJob(JSGlobalObject* globalObject, VM& vm, JSValue promiseOrCapability, JSValue resolution, JSPromise::Status status)
{
if (auto* promise = dynamicDowncast<JSPromise>(promiseOrCapability)) [[likely]] {
switch (status) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Fulfilled:
promise->resolvePromise(globalObject, vm, resolution);
break;
case JSPromise::Status::Rejected:
promise->rejectPromise(vm, globalObject, resolution);
break;
}
return;
}
promiseResolveWithoutHandlerJobSlow(globalObject, vm, promiseOrCapability, resolution, status);
}
void runInternalMicrotask(JSGlobalObject* globalObject, VM& vm, InternalMicrotask task, uint8_t payload, std::span<const JSValue, maxMicrotaskArguments> arguments, MicrotaskCall* microtaskCall)
{
auto scope = DECLARE_THROW_SCOPE(vm);
switch (task) {
case InternalMicrotask::None: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case InternalMicrotask::PromiseResolveThenableJobFast: {
auto* promise = uncheckedDowncast<JSPromise>(arguments[0]);
auto* promiseToResolve = uncheckedDowncast<JSPromise>(arguments[1]);
if (!promiseSpeciesWatchpointIsValid(vm, promise)) [[unlikely]]
RELEASE_AND_RETURN(scope, promiseResolveThenableJobFastSlow(globalObject, promise, promiseToResolve));
scope.release();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::PromiseResolveWithoutHandlerJob, promiseToResolve, jsUndefined());
return;
}
case InternalMicrotask::PromiseResolveThenableJobWithInternalMicrotaskFast: {
auto* promise = uncheckedDowncast<JSPromise>(arguments[0]);
JSValue context = arguments[1];
auto task = static_cast<InternalMicrotask>(payload);
if (!promiseSpeciesWatchpointIsValid(vm, promise)) [[unlikely]]
RELEASE_AND_RETURN(scope, promiseResolveThenableJobWithInternalMicrotaskFastSlow(globalObject, promise, task, context));
JSValue reactionsOrResult = promise->reactionsOrResult();
switch (promise->status()) {
case JSPromise::Status::Pending: {
auto* reaction = JSSlimPromiseReaction::create(vm, jsUndefined(), task, context, reactionsOrResult ? uncheckedDowncast<JSPromiseReaction>(reactionsOrResult) : nullptr);
promise->setReactionsOrResult(vm, reaction);
promise->markAsHandled();
break;
}
case JSPromise::Status::Rejected: {
if (!promise->isHandled())
globalObject->globalObjectMethodTable()->promiseRejectionTracker(globalObject, promise, JSPromiseRejectionOperation::Handle);
JSPromise::rejectWithInternalMicrotask(vm, globalObject, reactionsOrResult, task, context);
promise->markAsHandled();
break;
}
case JSPromise::Status::Fulfilled: {
JSPromise::fulfillWithInternalMicrotask(vm, globalObject, reactionsOrResult, task, context);
break;
}
}
return;
}
case InternalMicrotask::PromiseResolveThenableJob: {
JSValue promise = arguments[0];
JSValue then = arguments[1];
JSPromise* promiseToResolve = uncheckedDowncast<JSPromise>(arguments[2]);
auto [resolve, reject] = promiseToResolve->createResolvingFunctions(vm, globalObject);
RELEASE_AND_RETURN(scope, promiseResolveThenableJob(globalObject, promise, then, resolve, reject));
}
case InternalMicrotask::PromiseResolveThenableJobWithInternalMicrotask: {
auto task = static_cast<InternalMicrotask>(payload);
JSValue promise = arguments[0];
JSValue then = arguments[1];
JSValue context = arguments[2];
auto [resolve, reject] = JSPromise::createResolvingFunctionsWithInternalMicrotask(vm, globalObject, task, context);
RELEASE_AND_RETURN(scope, promiseResolveThenableJob(globalObject, promise, then, resolve, reject));
}
case InternalMicrotask::PromiseResolveWithoutHandlerJob: {
RELEASE_AND_RETURN(scope, promiseResolveWithoutHandlerJob(globalObject, vm, arguments[0], arguments[1], static_cast<JSPromise::Status>(payload)));
}
case InternalMicrotask::PromiseFulfillWithoutHandlerJob: {
auto* promise = uncheckedDowncast<JSPromise>(arguments[0]);
JSValue resolution = arguments[1];
switch (static_cast<JSPromise::Status>(payload)) {
case JSPromise::Status::Pending:
RELEASE_ASSERT_NOT_REACHED();
break;
case JSPromise::Status::Fulfilled:
scope.release();
promise->fulfillPromise(vm, globalObject, resolution);
break;
case JSPromise::Status::Rejected:
scope.release();
promise->rejectPromise(vm, globalObject, resolution);
break;
}
return;
}
case InternalMicrotask::PromiseRaceResolveJob:
RELEASE_AND_RETURN(scope, promiseRaceResolveJob(globalObject, vm, uncheckedDowncast<JSPromise>(arguments[0]), arguments[1], static_cast<JSPromise::Status>(payload)));
case InternalMicrotask::PromiseAllResolveJob:
RELEASE_AND_RETURN(scope, promiseAllResolveJob(globalObject, vm, uncheckedDowncast<JSPromise>(arguments[0]), arguments[1], uncheckedDowncast<JSPromiseCombinatorsContext>(arguments[2]), static_cast<JSPromise::Status>(payload)));
case InternalMicrotask::PromiseAllSettledResolveJob:
RELEASE_AND_RETURN(scope, promiseAllSettledResolveJob(globalObject, vm, uncheckedDowncast<JSPromise>(arguments[0]), arguments[1], uncheckedDowncast<JSPromiseCombinatorsContext>(arguments[2]), static_cast<JSPromise::Status>(payload)));
case InternalMicrotask::PromiseAnyResolveJob:
RELEASE_AND_RETURN(scope, promiseAnyResolveJob(globalObject, vm, uncheckedDowncast<JSPromise>(arguments[0]), arguments[1], uncheckedDowncast<JSPromiseCombinatorsContext>(arguments[2]), static_cast<JSPromise::Status>(payload)));
case InternalMicrotask::PromiseReactionJob: {
JSValue promiseOrCapability = arguments[0];
JSValue handler = arguments[1];
JSValue result;
JSValue error;
{
auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
result = callMicrotask(globalObject, handler, jsUndefined(), dynamicCastToCell(handler), "handler is not a function"_s, microtaskCall, arguments[2]);
if (catchScope.exception()) {
if (promiseOrCapability.isUndefinedOrNull()) {
scope.release();
return;
}
error = catchScope.exception()->value();
if (!catchScope.clearExceptionExceptTermination()) [[unlikely]] {
scope.release();
return;
}
}
if (promiseOrCapability.isUndefinedOrNull()) {
scope.release();
return;
}
ASSERT(result || error);
}
if (error) {
if (auto* promise = dynamicDowncast<JSPromise>(promiseOrCapability))
RELEASE_AND_RETURN(scope, promise->rejectPromise(vm, globalObject, error));
JSValue reject = promiseOrCapability.get(globalObject, vm.propertyNames->reject);
RETURN_IF_EXCEPTION(scope, void());
MarkedArgumentBuffer arguments;
arguments.append(error);
ASSERT(!arguments.hasOverflowed());
scope.release();
call(globalObject, reject, jsUndefined(), arguments, "reject is not a function"_s);
return;
}
if (auto* promise = dynamicDowncast<JSPromise>(promiseOrCapability))
RELEASE_AND_RETURN(scope, promise->resolvePromise(globalObject, vm, result));
JSValue resolve = promiseOrCapability.get(globalObject, vm.propertyNames->resolve);
RETURN_IF_EXCEPTION(scope, void());
MarkedArgumentBuffer arguments;
arguments.append(result);
ASSERT(!arguments.hasOverflowed());
scope.release();
call(globalObject, resolve, jsUndefined(), arguments, "resolve is not a function"_s);
return;
}
case InternalMicrotask::InvokeFunctionJob: {
JSValue handler = arguments[0];
scope.release();
callMicrotask(globalObject, handler, jsUndefined(), nullptr, "handler is not a function"_s, microtaskCall);
return;
}
case InternalMicrotask::AsyncFunctionResume: {
JSValue resolution = arguments[1];
auto* generator = uncheckedDowncast<JSGenerator>(arguments[2]);
JSGenerator::ResumeMode resumeMode = JSGenerator::ResumeMode::NormalMode;
switch (static_cast<JSPromise::Status>(payload)) {
case JSPromise::Status::Pending: {
RELEASE_ASSERT_NOT_REACHED();
break;
}
case JSPromise::Status::Rejected: {
resumeMode = JSGenerator::ResumeMode::ThrowMode;
break;
}
case JSPromise::Status::Fulfilled: {
resumeMode = JSGenerator::ResumeMode::NormalMode;
break;
}
}
int32_t state = generator->state();
generator->setState(static_cast<int32_t>(JSGenerator::State::Executing));
JSValue next = generator->next();
JSValue thisValue = generator->thisValue();
JSValue frame = generator->frame();
JSValue value;
JSValue error;
{
auto catchScope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
value = callMicrotask(globalObject, next, thisValue, generator, "handler is not a function"_s, microtaskCall,
generator, jsNumber(state), resolution, jsNumber(static_cast<int32_t>(resumeMode)), frame);
if (catchScope.exception()) {
error = catchScope.exception()->value();
if (!catchScope.clearExceptionExceptTermination()) [[unlikely]] {
scope.release();
return;
}
}
}
if (error) {
auto* promise = uncheckedDowncast<JSPromise>(generator->context());
scope.release();
promise->reject(vm, globalObject, error);
return;
}
if (generator->state() == static_cast<int32_t>(JSGenerator::State::Executing)) {
auto* promise = uncheckedDowncast<JSPromise>(generator->context());
scope.release();
promise->resolve(globalObject, vm, value);
return;
}
scope.release();
JSPromise::resolveWithInternalMicrotaskForAsyncAwait(globalObject, vm, value, InternalMicrotask::AsyncFunctionResume, generator);
return;
}
case InternalMicrotask::AsyncFromSyncIteratorContinue:
case InternalMicrotask::AsyncFromSyncIteratorDone:
RELEASE_AND_RETURN(scope, asyncFromSyncIteratorContinueOrDone(globalObject, vm, arguments[2], arguments[1], static_cast<JSPromise::Status>(payload), task == InternalMicrotask::AsyncFromSyncIteratorDone));
case InternalMicrotask::AsyncGeneratorYieldAwaited: {
RELEASE_AND_RETURN(scope, asyncGeneratorYieldAwaited(globalObject, uncheckedDowncast<JSAsyncGenerator>(arguments[2]), arguments[1], static_cast<JSPromise::Status>(payload)));
}
case InternalMicrotask::AsyncGeneratorBodyCallNormal: {
RELEASE_AND_RETURN(scope, asyncGeneratorBodyCallNormal(globalObject, uncheckedDowncast<JSAsyncGenerator>(arguments[2]), arguments[1], static_cast<JSPromise::Status>(payload)));
}
case InternalMicrotask::AsyncGeneratorBodyCallReturn: {
RELEASE_AND_RETURN(scope, asyncGeneratorBodyCallReturn(globalObject, uncheckedDowncast<JSAsyncGenerator>(arguments[2]), arguments[1], static_cast<JSPromise::Status>(payload)));
}
case InternalMicrotask::AsyncGeneratorResumeNext: {
RELEASE_AND_RETURN(scope, asyncGeneratorResumeNextReturn(globalObject, uncheckedDowncast<JSAsyncGenerator>(arguments[2]), arguments[1], static_cast<JSPromise::Status>(payload)));
}
case InternalMicrotask::PromiseFinallyReactionJob: {
// Phase 1: Original promise settled
// arguments[0] = resultPromise
// arguments[1] = value/reason from original promise
// arguments[2] = context (JSPromiseCombinatorsGlobalContext: promise=resultPromise, values=onFinally)
// payload = Fulfilled/Rejected status
scope.release();
promiseFinallyReactionJob(globalObject, vm,
uncheckedDowncast<JSPromise>(arguments[0]),
arguments[1],
uncheckedDowncast<JSPromiseCombinatorsGlobalContext>(arguments[2]),
static_cast<JSPromise::Status>(payload));
return;
}
case InternalMicrotask::PromiseFinallyAwaitJob: {
// Phase 2: onFinally's result settled
// arguments[0] = unused (we get resultPromise from context)
// arguments[1] = settled value from onFinally's result
// arguments[2] = context (JSPromiseCombinatorsGlobalContext: promise=resultPromise, values=originalValue, remainingElementsCount=wasFulfilled)
// payload = status of onFinally's result
scope.release();
promiseFinallyAwaitJob(globalObject, vm,
arguments[1],
uncheckedDowncast<JSPromiseCombinatorsGlobalContext>(arguments[2]),
static_cast<JSPromise::Status>(payload));
return;
}
case InternalMicrotask::AsyncModuleExecutionDone: {
asyncModuleExecutionDone(globalObject, scope, arguments, payload);
return;
}
case InternalMicrotask::AsyncModuleExecutionResume: {
asyncModuleExecutionResume(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleRegistryFetchSettled: {
moduleRegistryFetchSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleRegistryModuleSettled: {
moduleRegistryModuleSettled(globalObject, vm, arguments, payload);
return;
}
case InternalMicrotask::ModuleGraphLoadingError: {
moduleGraphLoadingError(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadStep: {
moduleLoadStep(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadTopSettled: {
moduleLoadTopSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadTopRejected: {
moduleLoadTopRejected(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadSpecifierTransform: {
moduleLoadSpecifierTransform(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadCombinedLoadSettled: {
moduleLoadCombinedLoadSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadCombinedStateSettled: {
moduleLoadCombinedStateSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadLinkEvaluateSettled: {
moduleLoadLinkEvaluateSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadReturnRecord: {
moduleLoadReturnRecord(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ModuleLoadReturnModuleKey: {
// loadAndEvaluateModule: extract module key from AbstractModuleRecord
// arguments[0] = resultPromise
// arguments[1] = resolution (AbstractModuleRecord*) or error
auto* resultPromise = uncheckedDowncast<JSPromise>(arguments[0]);
auto status = static_cast<JSPromise::Status>(payload);
scope.release();
if (status == JSPromise::Status::Fulfilled) {
auto* module = downcast<AbstractModuleRecord>(arguments[1]);
resultPromise->fulfillPromise(vm, globalObject, identifierToJSValue(vm, module->moduleKey()));
} else
resultPromise->rejectPromise(vm, globalObject, arguments[1]);
return;
}
case InternalMicrotask::ModuleLoadStoreError: {
moduleLoadStoreError(globalObject, scope, arguments, payload);
return;
}
case InternalMicrotask::DynamicImportLoadSettled: {
dynamicImportLoadSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::DynamicImportEvaluateSettled: {
dynamicImportEvaluateSettled(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::ImportModuleNamespace: {
importModuleNamespace(globalObject, vm, scope, arguments, payload);
return;
}
case InternalMicrotask::Opaque: {
RELEASE_ASSERT_NOT_REACHED();
return;
}
}
}
} // namespace JSC
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END