298 lines
8.4 KiB
C++
298 lines
8.4 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ModuleLoadRequest.h"
|
|
|
|
#include "mozilla/HoldDropJSObjects.h"
|
|
#include "mozilla/dom/ScriptLoadContext.h"
|
|
|
|
#include "LoadedScript.h"
|
|
#include "LoadContextBase.h"
|
|
#include "ModuleLoaderBase.h"
|
|
|
|
namespace JS::loader {
|
|
|
|
#undef LOG
|
|
#define LOG(args) \
|
|
MOZ_LOG(ModuleLoaderBase::gModuleLoaderBaseLog, mozilla::LogLevel::Debug, \
|
|
args)
|
|
|
|
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ModuleLoadRequest,
|
|
ScriptLoadRequest)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleLoadRequest)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleLoadRequest,
|
|
ScriptLoadRequest)
|
|
if (tmp->mWaitingParentRequest) {
|
|
tmp->mWaitingParentRequest->ChildModuleUnlinked();
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader, mRootModule, mModuleScript, mImports,
|
|
mWaitingParentRequest,
|
|
mDynamicReferencingScript)
|
|
tmp->ClearDynamicImport();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleLoadRequest,
|
|
ScriptLoadRequest)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader, mRootModule, mModuleScript,
|
|
mImports, mWaitingParentRequest,
|
|
mDynamicReferencingScript)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleLoadRequest,
|
|
ScriptLoadRequest)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicSpecifier)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicPromise)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
/* static */
|
|
VisitedURLSet* ModuleLoadRequest::NewVisitedSetForTopLevelImport(
|
|
nsIURI* aURI, JS::ModuleType aModuleType) {
|
|
auto set = new VisitedURLSet();
|
|
set->PutEntry(ModuleMapKey(aURI, aModuleType));
|
|
return set;
|
|
}
|
|
|
|
ModuleLoadRequest::ModuleLoadRequest(
|
|
nsIURI* aURI, JS::ModuleType aModuleType,
|
|
mozilla::dom::ReferrerPolicy aReferrerPolicy,
|
|
ScriptFetchOptions* aFetchOptions,
|
|
const mozilla::dom::SRIMetadata& aIntegrity, nsIURI* aReferrer,
|
|
LoadContextBase* aContext, Kind aKind, ModuleLoaderBase* aLoader,
|
|
VisitedURLSet* aVisitedSet, ModuleLoadRequest* aRootModule)
|
|
: ScriptLoadRequest(ScriptKind::eModule, aURI, aReferrerPolicy,
|
|
aFetchOptions, aIntegrity, aReferrer, aContext),
|
|
mIsTopLevel(aKind == Kind::TopLevel || aKind == Kind::DynamicImport),
|
|
mModuleType(aModuleType),
|
|
mIsDynamicImport(aKind == Kind::DynamicImport),
|
|
mLoader(aLoader),
|
|
mRootModule(aRootModule),
|
|
mVisitedSet(aVisitedSet) {
|
|
MOZ_ASSERT(mLoader);
|
|
}
|
|
|
|
nsIGlobalObject* ModuleLoadRequest::GetGlobalObject() {
|
|
return mLoader->GetGlobalObject();
|
|
}
|
|
|
|
bool ModuleLoadRequest::IsErrored() const {
|
|
return !mModuleScript || mModuleScript->HasParseError();
|
|
}
|
|
|
|
void ModuleLoadRequest::Cancel() {
|
|
if (IsCanceled()) {
|
|
AssertAllImportsCancelled();
|
|
return;
|
|
}
|
|
|
|
if (IsFinished()) {
|
|
return;
|
|
}
|
|
|
|
ScriptLoadRequest::Cancel();
|
|
|
|
mModuleScript = nullptr;
|
|
CancelImports();
|
|
|
|
if (mWaitingParentRequest) {
|
|
ChildLoadComplete(false);
|
|
}
|
|
}
|
|
|
|
void ModuleLoadRequest::SetReady() {
|
|
MOZ_ASSERT(!IsFinished());
|
|
|
|
// Mark a module as ready to execute. This means that this module and all it
|
|
// dependencies have had their source loaded, parsed as a module and the
|
|
// modules instantiated.
|
|
|
|
ScriptLoadRequest::SetReady();
|
|
|
|
if (mWaitingParentRequest) {
|
|
ChildLoadComplete(true);
|
|
}
|
|
}
|
|
|
|
void ModuleLoadRequest::ModuleLoaded() {
|
|
// A module that was found to be marked as fetching in the module map has now
|
|
// been loaded.
|
|
|
|
LOG(("ScriptLoadRequest (%p): Module loaded", this));
|
|
|
|
if (IsCanceled()) {
|
|
AssertAllImportsCancelled();
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(IsFetching() || IsPendingFetchingError());
|
|
|
|
mModuleScript = mLoader->GetFetchedModule(ModuleMapKey(mURI, mModuleType));
|
|
if (IsErrored()) {
|
|
ModuleErrored();
|
|
return;
|
|
}
|
|
|
|
mLoader->StartFetchingModuleDependencies(this);
|
|
}
|
|
|
|
void ModuleLoadRequest::LoadFailed() {
|
|
// We failed to load the source text or an error occurred unrelated to the
|
|
// content of the module (e.g. OOM).
|
|
|
|
LOG(("ScriptLoadRequest (%p): Module load failed", this));
|
|
|
|
if (IsCanceled()) {
|
|
AssertAllImportsCancelled();
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(IsFetching() || IsPendingFetchingError());
|
|
MOZ_ASSERT(!mModuleScript);
|
|
|
|
Cancel();
|
|
LoadFinished();
|
|
}
|
|
|
|
void ModuleLoadRequest::ModuleErrored() {
|
|
// Parse error, failure to resolve imported modules or error loading import.
|
|
|
|
LOG(("ScriptLoadRequest (%p): Module errored", this));
|
|
|
|
if (IsCanceled() || IsCancelingImports()) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(!IsFinished());
|
|
|
|
CheckModuleDependenciesLoaded();
|
|
MOZ_ASSERT(IsErrored());
|
|
|
|
CancelImports();
|
|
if (IsFinished()) {
|
|
// Cancelling an outstanding import will error this request.
|
|
return;
|
|
}
|
|
|
|
SetReady();
|
|
LoadFinished();
|
|
}
|
|
|
|
void ModuleLoadRequest::DependenciesLoaded() {
|
|
// The module and all of its dependencies have been successfully fetched and
|
|
// compiled.
|
|
|
|
LOG(("ScriptLoadRequest (%p): Module dependencies loaded", this));
|
|
|
|
if (IsCanceled()) {
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(IsLoadingImports());
|
|
MOZ_ASSERT(!IsErrored());
|
|
|
|
CheckModuleDependenciesLoaded();
|
|
AssertAllImportsFinished();
|
|
SetReady();
|
|
LoadFinished();
|
|
}
|
|
|
|
void ModuleLoadRequest::CheckModuleDependenciesLoaded() {
|
|
LOG(("ScriptLoadRequest (%p): Check dependencies loaded", this));
|
|
|
|
if (!mModuleScript || mModuleScript->HasParseError()) {
|
|
return;
|
|
}
|
|
|
|
for (const auto& childRequest : mImports) {
|
|
ModuleScript* childScript = childRequest->mModuleScript;
|
|
if (!childScript) {
|
|
mModuleScript = nullptr;
|
|
LOG(("ScriptLoadRequest (%p): %p failed (load error)", this,
|
|
childRequest.get()));
|
|
return;
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(mModuleScript->HadImportMap() ==
|
|
childScript->HadImportMap());
|
|
}
|
|
|
|
LOG(("ScriptLoadRequest (%p): all ok", this));
|
|
}
|
|
|
|
void ModuleLoadRequest::CancelImports() {
|
|
State origState = mState;
|
|
|
|
// To prevent reentering ModuleErrored() for this request via mImports[i]'s
|
|
// ChildLoadComplete().
|
|
mState = State::CancelingImports;
|
|
|
|
for (size_t i = 0; i < mImports.Length(); i++) {
|
|
if (mLoader->IsFetchingAndHasWaitingRequest(mImports[i])) {
|
|
LOG(("CancelImports import %p is fetching and has waiting\n",
|
|
mImports[i].get()));
|
|
continue;
|
|
}
|
|
mImports[i]->Cancel();
|
|
}
|
|
|
|
mState = origState;
|
|
}
|
|
|
|
void ModuleLoadRequest::LoadFinished() {
|
|
RefPtr<ModuleLoadRequest> request(this);
|
|
if (IsTopLevel() && IsDynamicImport()) {
|
|
mLoader->RemoveDynamicImport(request);
|
|
}
|
|
|
|
mLoader->OnModuleLoadComplete(request);
|
|
}
|
|
|
|
void ModuleLoadRequest::ChildModuleUnlinked() {
|
|
// This module was waiting for a child request, but the child reqeust
|
|
// got unlinked by CC and will never complete.
|
|
// It also means this module itself is also in the cycle, and will be
|
|
// unlinked or has already been unlinked, and will be collected.
|
|
// There's no need to normally finish the module request.
|
|
// Just reflect the awaiting imports count, so that the assertion in the
|
|
// destructor passes.
|
|
MOZ_ASSERT(mAwaitingImports > 0);
|
|
mAwaitingImports--;
|
|
}
|
|
|
|
void ModuleLoadRequest::SetDynamicImport(LoadedScript* aReferencingScript,
|
|
JS::Handle<JSString*> aSpecifier,
|
|
JS::Handle<JSObject*> aPromise) {
|
|
mDynamicReferencingScript = aReferencingScript;
|
|
mDynamicSpecifier = aSpecifier;
|
|
mDynamicPromise = aPromise;
|
|
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
|
|
void ModuleLoadRequest::ClearDynamicImport() {
|
|
mDynamicReferencingScript = nullptr;
|
|
mDynamicSpecifier = nullptr;
|
|
mDynamicPromise = nullptr;
|
|
}
|
|
|
|
inline void ModuleLoadRequest::AssertAllImportsFinished() const {
|
|
#ifdef DEBUG
|
|
for (const auto& request : mImports) {
|
|
MOZ_ASSERT(request->IsFinished());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void ModuleLoadRequest::AssertAllImportsCancelled() const {
|
|
#ifdef DEBUG
|
|
for (const auto& request : mImports) {
|
|
MOZ_ASSERT(request->IsCanceled());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} // namespace JS::loader
|