mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			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
 |