blob: e267159e755fdfe00414ca01bb21f9d0e9c170df [file]
/*
* Copyright (C) 2015-2021, 2026 Apple Inc. All rights reserved.
* Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
*
* 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. ``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
* 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 "JSModuleLoader.h"
#include "ProgramExecutable.h"
#include "BuiltinNames.h"
#include "Completion.h"
#include "GlobalObjectMethodTable.h"
#include "JSCInlines.h"
#include "JSModuleNamespaceObject.h"
#include "JSModuleRecord.h"
#include "JSPromise.h"
#include "JSSourceCode.h"
#include "JSWebAssembly.h"
#include "Microtask.h"
#include "ModuleAnalyzer.h"
#include "ModuleLoadingContext.h"
#include "ModuleRegistryEntry.h"
#include "Nodes.h"
#include "ObjectConstructor.h"
#include "Parser.h"
#include "ParserError.h"
#include "Symbol.h"
#include "SyntheticModuleRecord.h"
#include "TopExceptionScope.h"
#include "VMTrapsInlines.h"
#include <wtf/text/MakeString.h>
namespace JSC {
namespace JSModuleLoaderInternal {
static constexpr unsigned maximumResolutionFailures = 128;
}
static Identifier jsValueToSpecifier(JSGlobalObject* globalObject, JSValue value)
{
if (value.isSymbol())
return Identifier::fromUid(uncheckedDowncast<Symbol>(value)->privateName());
ASSERT(value.isString());
return asString(value)->toIdentifier(globalObject);
}
bool JSModuleLoader::isFetchError(JSGlobalObject* globalObject, ErrorInstance* error)
{
VM& vm = globalObject->vm();
return error->hasOwnProperty(globalObject, vm.propertyNames->builtinNames().moduleFetchFailureKindPrivateName());
}
// Web Platform Tests requires that import() promises reject with difference error object instances for fetch errors.
// It also requires that errors be cached. To satisfy both requirements, it's necessary to duplicate errors.
ErrorInstance* JSModuleLoader::duplicateTypeError(JSGlobalObject* globalObject, ErrorInstance* error)
{
ASSERT(error);
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* copy = uncheckedDowncast<ErrorInstance>(createTypeErrorCopy(globalObject, error));
RETURN_IF_EXCEPTION(scope, nullptr);
auto copyField = [&](const Identifier& name) -> bool {
if (JSValue value = error->getDirect(vm, name)) {
copy->putDirect(vm, name, value);
RETURN_IF_EXCEPTION(scope, false);
}
return true;
};
const BuiltinNames& names = vm.propertyNames->builtinNames();
if (!copyField(names.moduleFetchFailureKindPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleRecordPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleKeyPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleTypePrivateName()))
return nullptr;
if (!copyField(names.moduleFailureKindPrivateName()))
return nullptr;
return copy;
}
ErrorInstance* JSModuleLoader::duplicateError(JSGlobalObject* globalObject, ErrorInstance* error)
{
ASSERT(error);
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (error->errorType() == ErrorType::TypeError)
RELEASE_AND_RETURN(scope, JSModuleLoader::duplicateTypeError(globalObject, error));
String message = error->sanitizedMessageString(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
ErrorInstance* out = ErrorInstance::create(globalObject, WTF::move(message), error->errorType(), { }, { }, { });
RETURN_IF_EXCEPTION(scope, nullptr);
auto copyField = [&](const Identifier& name) -> bool {
if (JSValue value = error->getDirect(vm, name)) {
out->putDirect(vm, name, value);
RETURN_IF_EXCEPTION(scope, false);
}
return true;
};
const BuiltinNames& names = vm.propertyNames->builtinNames();
if (!copyField(names.moduleFetchFailureKindPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleRecordPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleKeyPrivateName()))
return nullptr;
if (!copyField(names.moduleFailureModuleTypePrivateName()))
return nullptr;
if (!copyField(names.moduleFailureKindPrivateName()))
return nullptr;
return out;
}
ErrorInstance* JSModuleLoader::maybeDuplicateFetchError(JSGlobalObject* globalObject, ErrorInstance* error)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (error->hasOwnProperty(globalObject, vm.propertyNames->builtinNames().moduleFetchFailureKindPrivateName())) {
error = JSModuleLoader::duplicateError(globalObject, error);
RETURN_IF_EXCEPTION(scope, nullptr);
}
RELEASE_AND_RETURN(scope, error);
}
JSModuleLoader::ModuleFailure::ModuleFailure(AbstractModuleRecord* source, ScriptFetchParameters::Type type, Kind kind)
: m_source(source)
, m_key(source->moduleKey())
, m_type(type)
, m_kind(kind)
{
}
JSModuleLoader::ModuleFailure::ModuleFailure(Identifier key, ScriptFetchParameters::Type type, Kind kind)
: m_source(nullptr)
, m_key(WTF::move(key))
, m_type(type)
, m_kind(kind)
{
}
bool JSModuleLoader::ModuleFailure::isEvaluationError(const Identifier& expectedSpecifier, ScriptFetchParameters::Type expectedType) const
{
return m_kind == ModuleFailure::Kind::Evaluation || (!m_key.isNull() && m_key != expectedSpecifier) || (m_type != ScriptFetchParameters::None && m_type != expectedType);
}
JSModuleLoader::ModuleFailure::operator bool() const
{
return m_source != nullptr || !m_key.isNull() || m_type != ScriptFetchParameters::Type::None || m_kind != ModuleFailure::Kind::Unknown;
}
JSModuleLoader::ModuleFailure JSModuleLoader::getErrorInfo(JSGlobalObject* globalObject, ErrorInstance* error)
{
VM& vm = globalObject->vm();
JSModuleLoader::ModuleFailure failure;
if (JSValue sourceValue = error->getDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleRecordPrivateName()))
failure.m_source = dynamicDowncast<AbstractModuleRecord>(sourceValue);
if (JSValue keyValue = error->getDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleKeyPrivateName()))
failure.m_key = jsValueToSpecifier(globalObject, keyValue);
if (JSValue typeValue = error->getDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleTypePrivateName()))
failure.m_type = static_cast<ScriptFetchParameters::Type>(typeValue.asInt32());
if (JSValue kindValue = error->getDirect(vm, vm.propertyNames->builtinNames().moduleFailureKindPrivateName()))
failure.m_kind = static_cast<JSModuleLoader::ModuleFailure::Kind>(kindValue.asInt32());
return failure;
}
void JSModuleLoader::attachErrorInfo(JSGlobalObject* globalObject, ErrorInstance* error, AbstractModuleRecord* source, const Identifier& key, ScriptFetchParameters::Type type, JSModuleLoader::ModuleFailure::Kind kind)
{
VM& vm = globalObject->vm();
if (source)
error->putDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleRecordPrivateName(), source);
if (!key.isNull())
error->putDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleKeyPrivateName(), identifierToJSValue(vm, key));
if (type != ScriptFetchParameters::Type::None)
error->putDirect(vm, vm.propertyNames->builtinNames().moduleFailureModuleTypePrivateName(), jsNumber(std::to_underlying(type)));
if (kind != JSModuleLoader::ModuleFailure::Kind::Unknown)
error->putDirect(vm, vm.propertyNames->builtinNames().moduleFailureKindPrivateName(), jsNumber(std::to_underlying(kind)));
}
bool JSModuleLoader::attachErrorInfo(JSGlobalObject* globalObject, Exception* exception, AbstractModuleRecord* source, const Identifier& key, ScriptFetchParameters::Type type, ModuleFailure::Kind kind)
{
if (auto* error = dynamicDowncast<ErrorInstance>(exception->value())) {
attachErrorInfo(globalObject, error, source, key, type, kind);
return true;
}
return false;
}
bool JSModuleLoader::attachErrorInfo(JSGlobalObject* globalObject, ThrowScope& scope, AbstractModuleRecord* source, const Identifier& key, ScriptFetchParameters::Type type, ModuleFailure::Kind kind)
{
if (Exception* exception = scope.exception())
return attachErrorInfo(globalObject, exception, source, key, type, kind);
return false;
}
const ClassInfo JSModuleLoader::s_info = { "ModuleLoader"_s, nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSModuleLoader) };
JSModuleLoader::JSModuleLoader(VM& vm, Structure* structure)
: JSCell(vm, structure)
{
}
void JSModuleLoader::destroy(JSCell* cell)
{
SUPPRESS_MEMORY_UNSAFE_CAST auto* thisObject = static_cast<JSModuleLoader*>(cell);
thisObject->JSModuleLoader::~JSModuleLoader();
}
void JSModuleLoader::finishCreation(JSGlobalObject*, VM& vm)
{
Base::finishCreation(vm);
ASSERT(inherits(info()));
}
template<typename Visitor>
void JSModuleLoader::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
JSModuleLoader* thisObject = uncheckedDowncast<JSModuleLoader>(cell);
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
Base::visitChildren(thisObject, visitor);
Locker locker { thisObject->cellLock() };
auto moduleMapValues = thisObject->m_moduleMap.values();
visitor.append(moduleMapValues.begin(), moduleMapValues.end());
auto resolutionFailuresValues = thisObject->m_resolutionFailures.values();
visitor.append(resolutionFailuresValues.begin(), resolutionFailuresValues.end());
for (auto& [key, loadedModule] : thisObject->m_loadedModules)
visitor.append(loadedModule.m_module);
}
DEFINE_VISIT_CHILDREN(JSModuleLoader);
// ------------------------------ Functions --------------------------------
static String printableModuleKey(JSGlobalObject* globalObject, JSValue key)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
if (key.isString() || key.isSymbol()) {
auto propertyName = key.toPropertyKey(globalObject);
scope.assertNoExceptionExceptTermination(); // This is OK since this function is just for debugging purpose.
return propertyName.impl();
}
return vm.propertyNames->emptyIdentifier.impl();
}
JSArray* JSModuleLoader::dependencyKeysIfEvaluated(JSGlobalObject* globalObject, const String& key)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
Identifier ident = Identifier::fromString(vm, key);
auto iter = m_moduleMap.find({ ident.impl(), ScriptFetchParameters::Type::JavaScript });
if (iter == m_moduleMap.end())
iter = m_moduleMap.find({ ident.impl(), ScriptFetchParameters::Type::WebAssembly });
if (iter == m_moduleMap.end())
RELEASE_AND_RETURN(scope, nullptr);
ModuleRegistryEntry* entry = iter->value.get();
AbstractModuleRecord* record = entry->record();
if (record == nullptr)
RELEASE_AND_RETURN(scope, nullptr);
if (auto status = entry->status(); status != ModuleRegistryEntry::Status::Fetched && status != ModuleRegistryEntry::Status::EvaluationFailed)
RELEASE_AND_RETURN(scope, nullptr);
if (auto* cyclic = dynamicDowncast<CyclicModuleRecord>(record); cyclic && cyclic->status() != CyclicModuleRecord::Status::Evaluated)
RELEASE_AND_RETURN(scope, nullptr);
const Vector<AbstractModuleRecord::ModuleRequest>& requests = record->requestedModules();
JSArray* array = constructEmptyArray(globalObject, nullptr, requests.size());
RETURN_IF_EXCEPTION(scope, nullptr);
for (unsigned index = 0; const AbstractModuleRecord::ModuleRequest& request : requests) {
Identifier resolved = resolve(globalObject, request.m_specifier, ident, nullptr, /* useImportMap */ true);
RETURN_IF_EXCEPTION(scope, nullptr);
array->putDirectIndex(globalObject, index++, identifierToJSValue(vm, resolved));
RETURN_IF_EXCEPTION(scope, nullptr);
}
RELEASE_AND_RETURN(scope, array);
}
void JSModuleLoader::provideFetch(JSGlobalObject* globalObject, const Identifier& key, ScriptFetchParameters::Type type, SourceCode&& sourceCode)
{
ModuleRegistryEntry* entry = ensureRegistered(globalObject, key, type);
if (entry->status() == ModuleRegistryEntry::Status::New)
entry->provideFetch(globalObject, WTF::move(sourceCode)); // can throw
}
void JSModuleLoader::provideFetch(JSGlobalObject* globalObject, const Identifier& key, ScriptFetchParameters::Type type, JSSourceCode* jsSourceCode)
{
ModuleRegistryEntry* entry = ensureRegistered(globalObject, key, type);
if (entry->status() == ModuleRegistryEntry::Status::New)
entry->provideFetch(globalObject, jsSourceCode); // can throw
}
JSPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, const Identifier& specifier, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher, bool evaluate, bool dynamic, bool useImportMap)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSPromise* promise = nullptr;
ScriptFetchParameters::Type type = parameters ? parameters->type() : ScriptFetchParameters::Type::JavaScript;
if (ModuleRegistryEntry* entry = getRegisteredMayBeNull(specifier, type)) {
JSValue error = entry->error(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
if (error)
return JSPromise::rejectedPromise(globalObject, error);
if (entry->status() != ModuleRegistryEntry::Status::New)
promise = entry->ensureFetchPromise(globalObject);
}
if (!promise) {
promise = fetch(globalObject, identifierToJSValue(vm, specifier), WTF::move(parameters), scriptFetcher);
RETURN_IF_EXCEPTION(scope, nullptr);
}
AbstractModuleRecord::ModuleRequest request { specifier, ScriptFetchParameters::create(type) };
auto* context = ModuleLoadingContext::create(vm, request, WTF::move(scriptFetcher), evaluate, dynamic, useImportMap);
JSPromise* intermediatePromise = JSPromise::create(vm, globalObject->promiseStructure());
intermediatePromise->markAsHandled();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadTopSettled, intermediatePromise, context);
JSPromise* resultPromise = JSPromise::create(vm, globalObject->promiseStructure());
resultPromise->markAsHandled();
intermediatePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadTopRejected, resultPromise, context);
return resultPromise;
}
JSPromise* JSModuleLoader::linkAndEvaluateModule(JSGlobalObject* globalObject, const Identifier& moduleKey, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
ScriptFetchParameters::Type type = parameters ? parameters->type() : ScriptFetchParameters::Type::JavaScript;
ModuleRegistryEntry* entry = ensureRegistered(globalObject, moduleKey, type);
RETURN_IF_EXCEPTION(scope, nullptr);
AbstractModuleRecord* record = entry->record();
ASSERT(record);
JSValue error = entry->error(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
if (error) {
scope.throwException(globalObject, error);
return nullptr;
}
record->link(globalObject, WTF::move(scriptFetcher));
if (Exception* exception = scope.exception()) {
attachErrorInfo(globalObject, scope, record, entry->key(), entry->moduleType(), ModuleFailure::Kind::Instantiation);
entry->setInstantiationError(globalObject, exception->value());
if (auto* cyclic = dynamicDowncast<CyclicModuleRecord>(record))
cyclic->setEvaluationError(vm, exception->value());
return nullptr;
}
JSPromise* promise = record->evaluate(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
error = entry->error(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
if (error) {
// At this point, the promise exists but won't be returned. However, it will be rejected later.
// To prevent an unhandled promise rejection error, we have to explicitly mark the promise as handled.
promise->markAsHandled();
scope.throwException(globalObject, error);
return nullptr;
}
return promise;
}
JSPromise* JSModuleLoader::requestImportModule(JSGlobalObject* globalObject, const Identifier& moduleName, const Identifier& referrer, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
Identifier resolved = resolve(globalObject, moduleName, referrer, scriptFetcher, /* useImportMap */ true);
RETURN_IF_EXCEPTION(scope, nullptr);
JSPromise* promise = loadModule(globalObject, resolved, WTF::move(parameters), WTF::move(scriptFetcher), /* evaluate */ true, /* dynamic */ true, /* useImportMap */ false);
RETURN_IF_EXCEPTION(scope, nullptr);
JSPromise* resultPromise = JSPromise::create(vm, globalObject->promiseStructure());
resultPromise->markAsHandled();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ImportModuleNamespace, resultPromise, jsUndefined());
return resultPromise;
}
JSPromise* JSModuleLoader::importModule(JSGlobalObject* globalObject, JSString* moduleName, JSValue parameters, const SourceOrigin& referrer)
{
dataLogLnIf(Options::dumpModuleLoadingState(), "Loader [import] ", printableModuleKey(globalObject, moduleName));
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto attributes = retrieveImportAttributesFromDynamicImportOptions(globalObject, parameters, { vm.propertyNames->type.impl() });
RETURN_IF_EXCEPTION(scope, nullptr);
auto type = retrieveTypeImportAttribute(globalObject, attributes);
RETURN_IF_EXCEPTION(scope, nullptr);
RefPtr<ScriptFetchParameters> fetchParams;
if (type)
fetchParams = ScriptFetchParameters::create(type.value());
if (globalObject->globalObjectMethodTable()->moduleLoaderImportModule)
RELEASE_AND_RETURN(scope, globalObject->globalObjectMethodTable()->moduleLoaderImportModule(globalObject, this, moduleName, WTF::move(fetchParams), referrer));
auto* promise = JSPromise::create(vm, globalObject->promiseStructure());
auto moduleNameString = moduleName->value(globalObject);
RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
scope.release();
promise->reject(vm, globalObject, createError(globalObject, makeString("Could not import the module '"_s, moduleNameString.data, "'."_s)));
return promise;
}
Identifier JSModuleLoader::resolve(JSGlobalObject* globalObject, JSValue name, JSValue referrer, RefPtr<ScriptFetcher> scriptFetcher, bool useImportMap)
{
dataLogLnIf(Options::dumpModuleLoadingState(), "Loader [resolve] ", printableModuleKey(globalObject, name));
if (globalObject->globalObjectMethodTable()->moduleLoaderResolve)
return globalObject->globalObjectMethodTable()->moduleLoaderResolve(globalObject, this, name, referrer, WTF::move(scriptFetcher), useImportMap);
return name.toPropertyKey(globalObject);
}
Identifier JSModuleLoader::resolve(JSGlobalObject* globalObject, const Identifier& name, const Identifier& referrer, RefPtr<ScriptFetcher> scriptFetcher, bool useImportMap)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSValue nameValue = name.isSymbol() || name.isNull() ? jsUndefined() : jsString(vm, name.string());
RETURN_IF_EXCEPTION(scope, { });
JSValue referrerValue = referrer.isSymbol() || referrer.isNull() ? jsUndefined() : jsString(vm, referrer.string());
RETURN_IF_EXCEPTION(scope, { });
RELEASE_AND_RETURN(scope, resolve(globalObject, nameValue, referrerValue, WTF::move(scriptFetcher), useImportMap));
}
JSPromise* JSModuleLoader::fetch(JSGlobalObject* globalObject, JSValue key, RefPtr<ScriptFetchParameters> parameters, RefPtr<ScriptFetcher> scriptFetcher)
{
dataLogLnIf(Options::dumpModuleLoadingState(), "Loader [fetch] ", printableModuleKey(globalObject, key));
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
if (globalObject->globalObjectMethodTable()->moduleLoaderFetch)
RELEASE_AND_RETURN(scope, globalObject->globalObjectMethodTable()->moduleLoaderFetch(globalObject, this, key, WTF::move(parameters), WTF::move(scriptFetcher)));
auto* promise = JSPromise::create(vm, globalObject->promiseStructure());
String moduleKey = key.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
scope.release();
promise->reject(vm, globalObject, createError(globalObject, makeString("Could not open the module '"_s, moduleKey, "'."_s)));
return promise;
}
JSObject* JSModuleLoader::createImportMetaProperties(JSGlobalObject* globalObject, JSValue key, JSModuleRecord* moduleRecord, RefPtr<ScriptFetcher> scriptFetcher)
{
if (globalObject->globalObjectMethodTable()->moduleLoaderCreateImportMetaProperties)
return globalObject->globalObjectMethodTable()->moduleLoaderCreateImportMetaProperties(globalObject, this, key, moduleRecord, WTF::move(scriptFetcher));
return constructEmptyObject(globalObject->vm(), globalObject->nullPrototypeObjectStructure());
}
JSValue JSModuleLoader::evaluate(JSGlobalObject* globalObject, JSValue key, JSValue moduleRecordValue, RefPtr<ScriptFetcher> scriptFetcher, JSValue sentValue, JSValue resumeMode)
{
dataLogLnIf(Options::dumpModuleLoadingState(), "Loader [evaluate] ", printableModuleKey(globalObject, key));
if (globalObject->globalObjectMethodTable()->moduleLoaderEvaluate)
return globalObject->globalObjectMethodTable()->moduleLoaderEvaluate(globalObject, this, key, moduleRecordValue, WTF::move(scriptFetcher), sentValue, resumeMode);
return evaluateNonVirtual(globalObject, key, moduleRecordValue, nullptr, sentValue, resumeMode);
}
JSValue JSModuleLoader::evaluateNonVirtual(JSGlobalObject* globalObject, JSValue, JSValue moduleRecordValue, RefPtr<ScriptFetcher>, JSValue sentValue, JSValue resumeMode)
{
if (auto* moduleRecord = dynamicDowncast<AbstractModuleRecord>(moduleRecordValue))
return moduleRecord->evaluate(globalObject, sentValue, resumeMode);
return jsUndefined();
}
JSModuleNamespaceObject* JSModuleLoader::getModuleNamespaceObject(JSGlobalObject* globalObject, JSValue moduleRecordValue)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto* moduleRecord = dynamicDowncast<AbstractModuleRecord>(moduleRecordValue);
if (!moduleRecord) {
throwTypeError(globalObject, scope);
return nullptr;
}
RELEASE_AND_RETURN(scope, moduleRecord->getModuleNamespace(globalObject));
}
AbstractModuleRecord* JSModuleLoader::getImportedModule(AbstractModuleRecord* referrer, const AbstractModuleRecord::ModuleRequest& request)
{
// GetImportedModule(referrer, request)
// https://tc39.es/ecma262/#sec-GetImportedModule
// 1. Let records be a List consisting of each LoadedModuleRequest Record r of referrer.[[LoadedModules]] such that ModuleRequestsEqual(r, request) is true.
auto iter = referrer->loadedModules().find(ModuleMapKey { request.m_specifier.impl(), request.type() });
// 2. Assert: records has exactly one element, since LoadRequestedModules has completed successfully on referrer prior to invoking this abstract operation.
ASSERT(iter != referrer->loadedModules().end());
// 3. Let record be the sole element of records.
// 4. Return record.[[Module]].
return iter->value.m_module.get();
}
AbstractModuleRecord* JSModuleLoader::maybeGetImportedModule(AbstractModuleRecord* referrer, const Identifier& moduleKey)
{
for (const auto& [key, loadedModuleRequest] : referrer->loadedModules()) {
if (loadedModuleRequest.m_specifier == moduleKey)
return loadedModuleRequest.m_module.get();
}
return nullptr;
}
JSPromise* JSModuleLoader::hostLoadImportedModule(JSGlobalObject* globalObject, const ModuleReferrer& referrer, const ModuleRequest& moduleRequest, JSCell* payload, RefPtr<ScriptFetcher> scriptFetcher, bool useImportMap)
{
// HostLoadImportedModule(referrer, moduleRequest, loadState, payload)
// https://html.spec.whatwg.org/multipage/webappapis.html#hostloadimportedmodule
ASSERT(isModuleLoaderHostDefinedPayload(payload));
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
Identifier referrerKey;
CyclicModuleRecord* record = referrer.getModule();
if (record)
referrerKey = record->moduleKey();
ModuleRegistryEntry* mapEntry = nullptr;
const Identifier& specifier = moduleRequest.m_specifier;
auto type = moduleRequest.type();
if (specifier.isSymbol())
mapEntry = getRegisteredMayBeNull(specifier, type);
ModuleMapKey moduleMapKey { specifier.impl(), type };
ResolutionMapKey resolutionKey { referrerKey.impl(), specifier.impl() };
if (auto error = m_resolutionFailures.get(resolutionKey)) {
JSValue errorValue = error.get();
ASSERT(errorValue);
JSPromise* promise = JSPromise::create(vm, globalObject->promiseStructure());
promise->reject(vm, globalObject, errorValue);
// 9.1. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to resolutionError.
// (Unused.)
// 9.2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(resolutionError)).
finishLoadingImportedModule(globalObject, referrer, moduleRequest, payload, Exception::create(vm, errorValue), scriptFetcher);
// 9.3. Return.
RELEASE_AND_RETURN(scope, promise);
}
Identifier resolved;
if (!mapEntry) {
// 8. Let url be the result of resolving a module specifier given referencingScript and moduleRequest.[[Specifier]], catching any exceptions. If they throw an exception, let resolutionError be the thrown exception.
resolved = resolve(globalObject, specifier, referrerKey, scriptFetcher, useImportMap);
// 9. If the previous step threw an exception, then:
if (Exception* resolutionError = scope.exception()) {
attachErrorInfo(globalObject, resolutionError, nullptr, specifier, moduleRequest.type(), ModuleFailure::Kind::Instantiation);
// Cache the resolution error so subsequent calls for the same specifier return the same error object.
JSValue errorValue = resolutionError->value();
addResolutionFailure(vm, resolutionKey, errorValue);
JSPromise* promise = JSPromise::create(vm, globalObject->promiseStructure());
promise->rejectWithCaughtException(globalObject, scope);
// 9.1. If loadState is not undefined and loadState.[[ErrorToRethrow]] is null, set loadState.[[ErrorToRethrow]] to resolutionError.
// (Unused.)
// 9.2. Perform FinishLoadingImportedModule(referrer, moduleRequest, payload, ThrowCompletion(resolutionError)).
finishLoadingImportedModule(globalObject, referrer, moduleRequest, payload, Exception::create(vm, errorValue), scriptFetcher);
// 9.3. Return.
RELEASE_AND_RETURN(scope, promise);
}
moduleMapKey.first = resolved.impl();
if (auto iter = m_moduleMap.find(moduleMapKey); iter != m_moduleMap.end())
mapEntry = iter->value.get();
} else
resolved = specifier;
if (mapEntry) {
ASSERT(mapEntry->status() != ModuleRegistryEntry::Status::New);
// Only fetching errors should be checked here, not instantiation or evaluation errors.
// This allows fetch errors to propagate first because they're required to have priority.
// To avoid a race condition, instantiation errors need to be checked later, not here.
if (JSValue fetchError = mapEntry->fetchError()) {
if (auto* errorInstance = dynamicDowncast<ErrorInstance>(fetchError)) {
fetchError = JSModuleLoader::duplicateError(globalObject, errorInstance);
RETURN_IF_EXCEPTION(scope, nullptr);
}
finishLoadingImportedModule(globalObject, referrer, moduleRequest, payload, Exception::create(vm, fetchError), scriptFetcher);
RETURN_IF_EXCEPTION(scope, nullptr);
return JSPromise::rejectedPromise(globalObject, fetchError);
}
JSPromise* promise = mapEntry->loadPromise();
if (promise) {
if (mapEntry->record()) {
finishLoadingImportedModule(globalObject, referrer, moduleRequest, payload, mapEntry->record(), scriptFetcher);
RETURN_IF_EXCEPTION(scope, nullptr);
} else {
auto* context = ModuleLoadingContext::create(vm, ModuleLoadingContext::Step::Cached, referrer, moduleRequest, payload, mapEntry, scriptFetcher);
JSPromise* resultPromise = JSPromise::create(vm, globalObject->promiseStructure());
resultPromise->markAsHandled();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadStep, resultPromise, context);
promise = resultPromise;
}
return promise;
}
} else {
mapEntry = ModuleRegistryEntry::create(vm, resolved, type, scriptFetcher);
Locker locker { cellLock() };
m_moduleMap.add(moduleMapKey, WriteBarrier<ModuleRegistryEntry>(vm, this, mapEntry));
}
if (mapEntry->status() == ModuleRegistryEntry::Status::New) {
JSPromise* promise = fetch(globalObject, identifierToJSValue(vm, resolved), moduleRequest.m_attributes, scriptFetcher);
RETURN_IF_EXCEPTION(scope, nullptr);
mapEntry->setStatus(ModuleRegistryEntry::Status::Fetching);
mapEntry->ensureFetchPromise(globalObject)->pipeFrom(vm, promise);
}
JSPromise* modulePromise = mapEntry->ensureModulePromise(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
auto* context = ModuleLoadingContext::create(vm, ModuleLoadingContext::Step::Main, referrer, moduleRequest, payload, mapEntry, scriptFetcher);
JSPromise* loadPromise = JSPromise::create(vm, globalObject->promiseStructure());
loadPromise->markAsHandled();
modulePromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadStep, loadPromise, context);
mapEntry->setLoadPromise(vm, loadPromise);
return loadPromise;
}
JSPromise* JSModuleLoader::loadModule(JSGlobalObject* globalObject, const ModuleReferrer& referrer, const ModuleRequest& moduleRequest, JSCell* payload, RefPtr<ScriptFetcher> scriptFetcher, bool evaluate, bool useImportMap)
{
ASSERT(isModuleLoaderHostDefinedPayload(payload));
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
JSPromise* promise = hostLoadImportedModule(globalObject, referrer, moduleRequest, payload, scriptFetcher, useImportMap);
RETURN_IF_EXCEPTION(scope, nullptr);
auto* context = ModuleLoadingContext::create(vm, moduleRequest, WTF::move(scriptFetcher), evaluate, /* dynamic */ false, useImportMap);
JSPromise* resultPromise = JSPromise::create(vm, globalObject->promiseStructure());
resultPromise->markAsHandled();
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadLinkEvaluateSettled, resultPromise, context);
resultPromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleLoadStoreError, jsUndefined(), context);
return resultPromise;
}
static void checkSafeToRecurse(JSGlobalObject* globalObject, ThrowScope& scope)
{
if (!globalObject->vm().isSafeToRecurse())
throwRangeError(globalObject, scope, "Maximum call stack size exceeded"_s);
}
void JSModuleLoader::innerModuleLoading(JSGlobalObject* globalObject, ModuleGraphLoadingState *state, AbstractModuleRecord* module)
{
// InnerModuleLoading(state, module)
// https://tc39.es/ecma262/#sec-InnerModuleLoading
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. Assert: state.[[IsLoading]] is true.
ASSERT(state->isLoading());
// 2. If module is a Cyclic Module Record, module.[[Status]] is NEW, and state.[[Visited]] does not contain module, then
if (auto* cyclic = dynamicDowncast<CyclicModuleRecord>(module); cyclic && cyclic->status() == CyclicModuleRecord::Status::New && !state->containsVisited(cyclic)) {
// 2.a. Append module to state.[[Visited]].
state->appendVisited(vm, cyclic);
// 2.b. Let requestedModulesCount be the number of elements in module.[[RequestedModules]].
size_t requestedModulesCount = module->requestedModules().size();
// 2.c. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] + requestedModulesCount.
state->setPendingModulesCount(state->pendingModulesCount() + requestedModulesCount);
// 2.d. For each ModuleRequest Record request of module.[[RequestedModules]], do
for (const AbstractModuleRecord::ModuleRequest& request : module->requestedModules()) {
// 2.d.i. If AllImportAttributesSupported(request.[[Attributes]]) is false, then
// 2.d.i.1. Let error be ThrowCompletion(a newly created SyntaxError object).
// 2.d.i.2. Perform ContinueModuleLoading(state, error).
// (Not possible.)
// 2.d.ii. Else if module.[[LoadedModules]] contains a LoadedModuleRequest Record record such that ModuleRequestsEqual(record, request) is true, then
if (auto iter = module->loadedModules().find(ModuleMapKey { request.m_specifier.impl(), request.type() }); iter != module->loadedModules().end()) {
checkSafeToRecurse(globalObject, scope);
RETURN_IF_EXCEPTION(scope, void());
// 2.d.ii.1. Perform InnerModuleLoading(state, record.[[Module]]).
innerModuleLoading(globalObject, state, iter->value.m_module.get());
RETURN_IF_EXCEPTION(scope, void());
// 2.d.iii. Else,
} else {
// 2.d.iii.1. Perform HostLoadImportedModule(module, request, state.[[HostDefined]], state).
JSPromise* promise = hostLoadImportedModule(globalObject, cyclic, request, state, state->scriptFetcher(), true);
RETURN_IF_EXCEPTION(scope, void());
promise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::ModuleGraphLoadingError, jsUndefined(), state);
// 2.d.iii.2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters the graph loading process through ContinueModuleLoading.
}
// 2.d.iv. If state.[[IsLoading]] is false, return UNUSED.
if (!state->isLoading())
RELEASE_AND_RETURN(scope, void());
}
}
// 3. Assert: state.[[PendingModulesCount]] ≥ 1.
ASSERT(state->pendingModulesCount() >= 1);
// 4. Set state.[[PendingModulesCount]] to state.[[PendingModulesCount]] - 1.
state->setPendingModulesCount(state->pendingModulesCount() - 1);
// 5. If state.[[PendingModulesCount]] = 0, then
if (!state->pendingModulesCount()) {
// 5.a. Set state.[[IsLoading]] to false.
state->setIsLoading(false);
// 5.b. For each Cyclic Module Record loaded of state.[[Visited]], do
state->iterateVisited([](CyclicModuleRecord* loaded) {
// 5.b.i. If loaded.[[Status]] is NEW, set loaded.[[Status]] to UNLINKED.
if (loaded->status() == CyclicModuleRecord::Status::New)
loaded->setStatus(CyclicModuleRecord::Status::Unlinked);
});
// 5.c. Perform ! Call(state.[[PromiseCapability]].[[Resolve]], undefined, « undefined »).
state->promise()->fulfill(vm, globalObject, module);
}
// 6. Return UNUSED.
scope.release();
}
void JSModuleLoader::finishLoadingImportedModule(JSGlobalObject* globalObject, const ModuleReferrer& referrer, const ModuleRequest& moduleRequest, JSCell* payload, ModuleCompletion result, RefPtr<ScriptFetcher> scriptFetcher)
{
// FinishLoadingImportedModule(referrer, moduleRequest, payload, result)
// https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
ASSERT(isModuleLoaderHostDefinedPayload(payload));
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. If result is a normal completion, then
if (auto* resultRecord = std::get_if<AbstractModuleRecord*>(&result)) {
JSCell* owner = nullptr;
auto& loadedModules = [&] -> ModuleMap<AbstractModuleRecord::LoadedModuleRequest> & {
if (CyclicModuleRecord* module = referrer.getModule()) {
owner = module;
return module->loadedModules();
}
ASSERT(referrer.isRealm());
owner = this;
return m_loadedModules;
}();
ASSERT(owner);
// 1.a. If referrer.[[LoadedModules]] contains a LoadedModuleRequest Record record such that ModuleRequestsEqual(record, moduleRequest) is true, then
if (auto iter = loadedModules.find(ModuleMapKey { moduleRequest.m_specifier.impl(), moduleRequest.type() }); iter != loadedModules.end()) {
// 1.a.i. Assert: record.[[Module]] and result.[[Value]] are the same Module Record.
ASSERT(iter->value.m_module.get() == *resultRecord);
// 1.b. Else,
} else {
// 1.b.i. Append the LoadedModuleRequest Record { [[Specifier]]: moduleRequest.[[Specifier]], [[Attributes]]: moduleRequest.[[Attributes]], [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]].
ModuleMapKey key { moduleRequest.m_specifier.impl(), moduleRequest.type() };
Locker locker { owner->cellLock() };
AbstractModuleRecord::LoadedModuleRequest value { vm, moduleRequest, *resultRecord, owner };
loadedModules.add(WTF::move(key), WTF::move(value));
}
}
// 2. If payload is a GraphLoadingState Record, then
if (auto* state = dynamicDowncast<ModuleGraphLoadingState>(payload)) {
// 2.a. Perform ContinueModuleLoading(payload, result).
continueModuleLoading(globalObject, state, result);
RETURN_IF_EXCEPTION(scope, void());
// 3. Else,
} else {
// 3.a. Perform ContinueDynamicImport(payload, result).
auto* dynamicPayload = uncheckedDowncast<ModuleLoaderPayload>(payload);
continueDynamicImport(globalObject, dynamicPayload->promise(), result, WTF::move(scriptFetcher));
RETURN_IF_EXCEPTION(scope, void());
}
// 4. Return UNUSED.
scope.release();
}
void JSModuleLoader::continueModuleLoading(JSGlobalObject* globalObject, ModuleGraphLoadingState *state, ModuleCompletion moduleCompletion)
{
// ContinueModuleLoading(state, moduleCompletion)
// https://tc39.es/ecma262/#sec-ContinueModuleLoading
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. If state.[[IsLoading]] is false, return UNUSED.
if (!state->isLoading())
RELEASE_AND_RETURN(scope, void());
// 2. If moduleCompletion is a normal completion, then
if (auto* module = std::get_if<AbstractModuleRecord*>(&moduleCompletion)) {
// 2.a. Perform InnerModuleLoading(state, moduleCompletion.[[Value]]).
innerModuleLoading(globalObject, state, *module);
RETURN_IF_EXCEPTION(scope, void());
// 3. Else,
} else {
// 3.a. Set state.[[IsLoading]] to false.
state->setIsLoading(false);
// 3.b. Perform ! Call(state.[[PromiseCapability]].[[Reject]], undefined, « moduleCompletion.[[Value]] »).
state->promise()->reject(vm, globalObject, std::get<Exception*>(moduleCompletion)->value());
}
// 4. Return UNUSED.
scope.release();
}
void JSModuleLoader::continueDynamicImport(JSGlobalObject* globalObject, JSPromise* promise, ModuleCompletion completion, RefPtr<ScriptFetcher> scriptFetcher)
{
// ContinueDynamicImport(promiseCapability, moduleCompletion)
// https://tc39.es/ecma262/#sec-ContinueDynamicImport
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. If moduleCompletion is an abrupt completion, then
if (Exception** exception = std::get_if<Exception*>(&completion)) {
// 1.a. Perform ! Call(promiseCapability.[[Reject]], undefined, « moduleCompletion.[[Value]] »).
promise->reject(vm, globalObject, (*exception)->value());
// 1.b. Return UNUSED.
scope.assertNoException();
return;
}
// 2. Let module be moduleCompletion.[[Value]].
auto* module = std::get<AbstractModuleRecord*>(completion);
// 3. Let loadPromise be module.LoadRequestedModules().
JSPromise* loadPromise = loadRequestedModules(globalObject, module, WTF::move(scriptFetcher));
RETURN_IF_EXCEPTION(scope, void());
// 4-8. Link and evaluate using microtask dispatch instead of closures.
loadPromise->performPromiseThenWithInternalMicrotask(vm, globalObject, InternalMicrotask::DynamicImportLoadSettled, promise, module);
// 9. Return UNUSED.
scope.release();
}
JSPromise* JSModuleLoader::loadRequestedModules(JSGlobalObject* globalObject, AbstractModuleRecord* module, RefPtr<ScriptFetcher> scriptFetcher)
{
// LoadRequestedModules([hostDefined])
// https://tc39.es/ecma262/#sec-LoadRequestedModules
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
// 1. If hostDefined is not present, let hostDefined be empty.
// 2. Let pc be ! NewPromiseCapability(%Promise%).
JSPromise* pc = JSPromise::create(vm, globalObject->promiseStructure()); // This will eventually be resolved with an AbstractModuleRecord*.
pc->markAsHandled();
// 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true, [[PendingModulesCount]]: 1, [[Visited]]: « », [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }.
auto state = ModuleGraphLoadingState::create(vm, pc, WTF::move(scriptFetcher));
RETURN_IF_EXCEPTION(scope, nullptr);
// 4. Perform InnerModuleLoading(state, module).
innerModuleLoading(globalObject, state, module);
RETURN_IF_EXCEPTION(scope, nullptr);
// 5. Return pc.[[Promise]].
return pc;
}
ModuleRegistryEntry* JSModuleLoader::ensureRegistered(JSGlobalObject* globalObject, const Identifier& key, ScriptFetchParameters::Type type)
{
VM& vm = globalObject->vm();
ModuleMapKey moduleMapKey { key.impl(), type };
if (auto iter = m_moduleMap.find(moduleMapKey); iter != m_moduleMap.end())
return iter->value.get();
ModuleRegistryEntry* entry = ModuleRegistryEntry::create(vm, key, type, nullptr);
Locker locker { cellLock() };
m_moduleMap.add(moduleMapKey, WriteBarrier<ModuleRegistryEntry>(vm, this, entry));
return entry;
}
ModuleRegistryEntry* JSModuleLoader::getRegisteredMayBeNull(const Identifier& key, ScriptFetchParameters::Type type)
{
if (auto iter = m_moduleMap.find({ key.impl(), type }); iter != m_moduleMap.end())
return iter->value.get();
return nullptr;
}
void JSModuleLoader::addResolutionFailure(VM& vm, const ResolutionMapKey& key, JSValue error)
{
Locker locker { cellLock() };
if (m_resolutionFailures.size() >= JSModuleLoaderInternal::maximumResolutionFailures)
m_resolutionFailures.remove(m_resolutionFailures.begin());
m_resolutionFailures.add(key, WriteBarrier<Unknown>(vm, this, error));
}
ProgramExecutable* JSModuleLoader::ModuleReferrer::getScript() const
{
auto option = std::get_if<ProgramExecutable*>(this);
return option ? *option : nullptr;
}
CyclicModuleRecord* JSModuleLoader::ModuleReferrer::getModule() const
{
auto option = std::get_if<CyclicModuleRecord*>(this);
return option ? *option : nullptr;
}
JSGlobalObject* JSModuleLoader::ModuleReferrer::getRealm() const
{
auto option = std::get_if<JSGlobalObject*>(this);
return option ? *option : nullptr;
}
bool JSModuleLoader::ModuleReferrer::isScript() const
{
return std::holds_alternative<ProgramExecutable*>(*this);
}
bool JSModuleLoader::ModuleReferrer::isModule() const
{
return std::holds_alternative<CyclicModuleRecord*>(*this);
}
bool JSModuleLoader::ModuleReferrer::isRealm() const
{
return std::holds_alternative<JSGlobalObject*>(*this);
}
JSValue JSModuleLoader::ModuleReferrer::toJSValue() const
{
if (auto executable = std::get_if<ProgramExecutable*>(this))
return *executable;
if (auto module = std::get_if<CyclicModuleRecord*>(this))
return *module;
ASSERT(isRealm());
return std::get<JSGlobalObject*>(*this);
}
JSPromise* JSModuleLoader::makeModule(JSGlobalObject* globalObject, const Identifier& moduleKey, JSSourceCode* jsSourceCode)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
const SourceCode& sourceCode = jsSourceCode->sourceCode();
JSPromise* promise = JSPromise::create(vm, globalObject->promiseStructure());
promise->markAsHandled();
#if ENABLE(WEBASSEMBLY)
if (sourceCode.provider()->sourceType() == SourceProviderSourceType::WebAssembly)
RELEASE_AND_RETURN(scope, uncheckedDowncast<JSPromise>(JSWebAssembly::instantiate(globalObject, promise, sourceCode.provider(), moduleKey, jsSourceCode)));
#endif
// https://tc39.es/proposal-json-modules/#sec-parse-json-module
if (sourceCode.provider()->sourceType() == SourceProviderSourceType::JSON) {
auto* moduleRecord = SyntheticModuleRecord::parseJSONModule(globalObject, moduleKey, SourceCode { sourceCode });
attachErrorInfo(globalObject, scope, moduleRecord, moduleKey, ScriptFetchParameters::JSON, ModuleFailure::Kind::Evaluation);
RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope));
scope.release();
promise->fulfill(vm, globalObject, moduleRecord);
return promise;
}
ParserError error;
std::unique_ptr<ModuleProgramNode> moduleProgramNode = parseRootNode<ModuleProgramNode>(
vm, sourceCode, ImplementationVisibility::Public, JSParserBuiltinMode::NotBuiltin,
StrictModeLexicallyScopedFeature, JSParserScriptMode::Module, SourceParseMode::ModuleAnalyzeMode, error);
if (error.isValid()) {
auto* errorInstance = uncheckedDowncast<ErrorInstance>(error.toErrorObject(globalObject, sourceCode));
attachErrorInfo(globalObject, errorInstance, nullptr, moduleKey, ScriptFetchParameters::JavaScript, ModuleFailure::Kind::Evaluation);
promise->reject(vm, globalObject, errorInstance);
RELEASE_AND_RETURN(scope, promise);
}
ASSERT(moduleProgramNode);
ModuleAnalyzer moduleAnalyzer(globalObject, moduleKey, sourceCode, moduleProgramNode->varDeclarations(), moduleProgramNode->lexicalVariables(), moduleProgramNode->features());
RETURN_IF_EXCEPTION(scope, nullptr);
auto result = moduleAnalyzer.analyze(*moduleProgramNode);
if (!result) {
auto [errorType, message] = WTF::move(result.error());
auto* errorInstance = uncheckedDowncast<ErrorInstance>(createError(globalObject, errorType, message));
attachErrorInfo(globalObject, errorInstance, nullptr, moduleKey, ScriptFetchParameters::JavaScript, ModuleFailure::Kind::Evaluation);
promise->reject(vm, globalObject, errorInstance);
RELEASE_AND_RETURN(scope, promise);
}
promise->fulfill(vm, globalObject, result.value());
RELEASE_AND_RETURN(scope, promise);
}
} // namespace JSC