commit 3352c88229b2c228aa6421bddd3f97f1fa8f3fe0
parent 38ef8117f2f149c75b9dc5e5d67027f71c0499bd
Author: agoloman <agoloman@mozilla.com>
Date: Wed, 15 Oct 2025 16:28:49 +0300
Revert "Bug 1991081 - Part 7: Rewrite EncodeBytecodeAndSave with LoadedScript. r=nbp" for causing failures @LoadedScript.h.
This reverts commit b10b7c8addb4abc437e33a381de9711980ca107f.
Revert "Bug 1991081 - Part 6: Remove testing-only events from EncodeBytecodeAndSave. r=nbp"
This reverts commit caf275708ea66789a24905cc587652efa4e947f3.
Revert "Bug 1991081 - Part 5: Use single field for SRI and optional bytecode, for bytecode case and for cached stencil case. r=nbp"
This reverts commit 86631a6e10a29b4c8f5922ea4ecc3fdb5a0cad94.
Revert "Bug 1991081 - Part 4: Use LoadedScript for logging in EncodeBytecodeAndSave. r=nbp"
This reverts commit 0f9d7f11e80987412f41d7e71c1390644acab33e.
Revert "Bug 1991081 - Part 3: Store nsICacheInfoChannel to LoadedScript also in text and bytecode variants. r=nbp"
This reverts commit 370f5bcd48a457bdab97a9af9d7895f4473544f3.
Revert "Bug 1991081 - Part 2: Store stencil to LoadedScript also in text and bytecode variants. r=nbp"
This reverts commit 7eaf4f425f15e3fec6e81e2041d78ed77ebc3600.
Revert "Bug 1991081 - Part 1: Rename Stencil variant to CachedStencil in LoadedScript. r=nbp"
This reverts commit a7a6ce8f6f42bf7d5c746a5c57ac4d9fca3e4644.
Diffstat:
9 files changed, 224 insertions(+), 210 deletions(-)
diff --git a/dom/script/ModuleLoader.cpp b/dom/script/ModuleLoader.cpp
@@ -95,7 +95,7 @@ bool ModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, nsresult* aRvOut) {
}
nsresult ModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
GetScriptLoader()->EmulateNetworkEvents(aRequest);
SetModuleFetchStarted(aRequest);
return aRequest->OnFetchComplete(NS_OK);
@@ -235,7 +235,7 @@ nsresult ModuleLoader::CompileJavaScriptModule(
JS::MutableHandle<JSObject*> aModuleOut) {
GetScriptLoader()->CalculateCacheFlag(aRequest);
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
JS::InstantiateOptions instantiateOptions(aOptions);
RefPtr<JS::Stencil> stencil = aRequest->GetStencil();
aModuleOut.set(
@@ -262,8 +262,6 @@ nsresult ModuleLoader::CompileJavaScriptModule(
return NS_ERROR_FAILURE;
}
- aRequest->SetStencil(stencil);
-
JS::InstantiateOptions instantiateOptions(aOptions);
aModuleOut.set(JS::InstantiateModuleStencil(aCx, instantiateOptions,
stencil, &storage));
@@ -280,7 +278,7 @@ nsresult ModuleLoader::CompileJavaScriptModule(
MOZ_ASSERT(!alreadyStarted);
}
- GetScriptLoader()->TryCacheRequest(aRequest);
+ GetScriptLoader()->TryCacheRequest(aRequest, stencil);
return NS_OK;
}
@@ -313,8 +311,6 @@ nsresult ModuleLoader::CompileJavaScriptModule(
return NS_ERROR_FAILURE;
}
- aRequest->SetStencil(stencil);
-
JS::InstantiateOptions instantiateOptions(aOptions);
aModuleOut.set(
JS::InstantiateModuleStencil(aCx, instantiateOptions, stencil));
@@ -331,7 +327,7 @@ nsresult ModuleLoader::CompileJavaScriptModule(
MOZ_ASSERT(!alreadyStarted);
}
- GetScriptLoader()->TryCacheRequest(aRequest);
+ GetScriptLoader()->TryCacheRequest(aRequest, stencil);
return NS_OK;
}
diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp
@@ -469,9 +469,9 @@ ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
// later save the bytecode on the cache entry.
if (NS_SUCCEEDED(rv) && mRequest->IsSource() &&
StaticPrefs::dom_script_loader_bytecode_cache_enabled()) {
- mRequest->getLoadedScript()->mCacheInfo = do_QueryInterface(channelRequest);
+ mRequest->mCacheInfo = do_QueryInterface(channelRequest);
LOG(("ScriptLoadRequest (%p): nsICacheInfoChannel = %p", mRequest.get(),
- mRequest->getLoadedScript()->mCacheInfo.get()));
+ mRequest->mCacheInfo.get()));
}
// we have to mediate and use mRequest.
@@ -480,7 +480,7 @@ ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
// In case of failure, clear the mCacheInfoChannel to avoid keeping it alive.
if (NS_FAILED(rv)) {
- mRequest->getLoadedScript()->DropDiskCacheReference();
+ mRequest->DropDiskCacheReference();
}
return rv;
diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
@@ -636,7 +636,7 @@ static nsSecurityFlags CORSModeToSecurityFlags(CORSMode aCORSMode) {
nsresult ScriptLoader::StartClassicLoad(
ScriptLoadRequest* aRequest,
const Maybe<nsAutoString>& aCharsetForPreload) {
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
EmulateNetworkEvents(aRequest);
return NS_OK;
}
@@ -725,7 +725,7 @@ void ScriptLoader::PrepareCacheInfoChannel(nsIChannel* aChannel,
ScriptLoadRequest* aRequest) {
// To avoid decoding issues, the build-id is part of the bytecode MIME type
// constant.
- aRequest->getLoadedScript()->DropDiskCacheReference();
+ aRequest->DropDiskCacheReference();
nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(aChannel));
if (cic && StaticPrefs::dom_script_loader_bytecode_cache_enabled()) {
MOZ_ASSERT(!IsWebExtensionRequest(aRequest),
@@ -1153,21 +1153,11 @@ void ScriptLoader::TryUseCache(ScriptLoadRequest* aRequest,
ScriptLoadRequestType aRequestType) {
if (aRequestType == ScriptLoadRequestType::Inline) {
aRequest->NoCacheEntryFound();
- LOG(
- ("ScriptLoader (%p): Created LoadedScript (%p) for "
- "ScriptLoadRequest(%p) %s.",
- this, aRequest->getLoadedScript(), aRequest,
- aRequest->mURI->GetSpecOrDefault().get()));
return;
}
if (!mCache) {
aRequest->NoCacheEntryFound();
- LOG(
- ("ScriptLoader (%p): Created LoadedScript (%p) for "
- "ScriptLoadRequest(%p) %s.",
- this, aRequest->getLoadedScript(), aRequest,
- aRequest->mURI->GetSpecOrDefault().get()));
return;
}
@@ -1175,11 +1165,6 @@ void ScriptLoader::TryUseCache(ScriptLoadRequest* aRequest,
auto cacheResult = mCache->Lookup(*this, key, /* aSyncLoad = */ true);
if (cacheResult.mState != CachedSubResourceState::Complete) {
aRequest->NoCacheEntryFound();
- LOG(
- ("ScriptLoader (%p): Created LoadedScript (%p) for "
- "ScriptLoadRequest(%p) %s.",
- this, aRequest->getLoadedScript(), aRequest,
- aRequest->mURI->GetSpecOrDefault().get()));
return;
}
@@ -1188,11 +1173,6 @@ void ScriptLoader::TryUseCache(ScriptLoadRequest* aRequest,
// LookupPreloadRequest call.
if (NS_FAILED(CheckContentPolicy(aElement, aNonce, aRequest))) {
aRequest->NoCacheEntryFound();
- LOG(
- ("ScriptLoader (%p): Created LoadedScript (%p) for "
- "ScriptLoadRequest(%p) %s.",
- this, aRequest->getLoadedScript(), aRequest,
- aRequest->mURI->GetSpecOrDefault().get()));
return;
}
}
@@ -1200,10 +1180,7 @@ void ScriptLoader::TryUseCache(ScriptLoadRequest* aRequest,
aRequest->mNetworkMetadata = cacheResult.mNetworkMetadata;
aRequest->CacheEntryFound(cacheResult.mCompleteValue);
- LOG(
- ("ScriptLoader (%p): Found in-memory cache LoadedScript (%p) for "
- "ScriptLoadRequest(%p) %s.",
- this, aRequest->getLoadedScript(), aRequest,
+ LOG(("ScriptLoader (%p): Found in-memory cache for %s.", this,
aRequest->mURI->GetSpecOrDefault().get()));
if (cacheResult.mCompleteValue->mFetchCount < UINT8_MAX) {
@@ -1212,8 +1189,23 @@ void ScriptLoader::TryUseCache(ScriptLoadRequest* aRequest,
return;
}
+void ScriptLoader::StoreCacheInfo(LoadedScript* aLoadedScript,
+ ScriptLoadRequest* aRequest) {
+ MOZ_ASSERT(aRequest->mCacheInfo);
+ MOZ_ASSERT(!aRequest->SRIAndBytecode().empty());
+ MOZ_ASSERT(aRequest->SRIAndBytecode().length() == aRequest->GetSRILength());
+ MOZ_ASSERT(!aLoadedScript->mCacheInfo);
+ MOZ_ASSERT(aLoadedScript->mSRI.empty());
+
+ if (!aLoadedScript->mSRI.appendAll(aRequest->SRIAndBytecode())) {
+ return;
+ }
+
+ aLoadedScript->mCacheInfo = aRequest->mCacheInfo;
+}
+
void ScriptLoader::EmulateNetworkEvents(ScriptLoadRequest* aRequest) {
- MOZ_ASSERT(aRequest->IsCachedStencil());
+ MOZ_ASSERT(aRequest->IsStencil());
MOZ_ASSERT(aRequest->mNetworkMetadata);
nsIScriptElement* element = aRequest->GetScriptLoadContext()->mScriptElement;
@@ -1438,7 +1430,7 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
return block;
}
- if (request->IsCachedStencil()) {
+ if (request->IsStencil()) {
// https://html.spec.whatwg.org/#prepare-the-script-element
//
// Step 33. If el's type is "classic" and el has a src attribute, or el's
@@ -1889,7 +1881,7 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
- if (!aRequest->IsCachedStencil() &&
+ if (!aRequest->IsStencil() &&
!aRequest->GetScriptLoadContext()->mCompileOrDecodeTask &&
!aRequest->GetScriptLoadContext()->CompileStarted()) {
bool couldCompile = false;
@@ -2749,13 +2741,28 @@ void ScriptLoader::CalculateCacheFlag(ScriptLoadRequest* aRequest) {
// We need the nsICacheInfoChannel to exist to be able to open the alternate
// data output stream.
- if (!aRequest->getLoadedScript()->HasDiskCacheReference()) {
- LOG(
- ("ScriptLoadRequest (%p): Bytecode-cache: Skip disk: "
- "!LoadedScript::HasDiskCacheReference",
- aRequest));
- aRequest->MarkSkippedDiskCaching();
- return;
+ if (aRequest->IsStencil()) {
+ // For in-memory cache, the pointer is cached in the LoadedScript,
+ // if the cache had never been saved.
+ if (!aRequest->getLoadedScript()->mCacheInfo) {
+ LOG(
+ ("ScriptLoadRequest (%p): Bytecode-cache: Skip disk: "
+ "!LoadedScript::mCacheInfo",
+ aRequest));
+ aRequest->MarkSkippedDiskCaching();
+ return;
+ }
+ } else {
+ // This pointer would only be non-null if the bytecode was
+ // activated at the time the channel got created in StartLoad.
+ if (!aRequest->HasDiskCacheReference()) {
+ LOG(
+ ("ScriptLoadRequest (%p): Bytecode-cache: Skip disk: "
+ "!HasDiskCacheReference",
+ aRequest));
+ aRequest->MarkSkippedDiskCaching();
+ return;
+ }
}
// Look at the preference to know which strategy (parameters) should be used
@@ -2813,7 +2820,7 @@ void ScriptLoader::CalculateCacheFlag(ScriptLoadRequest* aRequest) {
if (hasSourceLengthMin) {
size_t sourceLength;
size_t minLength;
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
sourceLength = JS::GetScriptSourceLength(aRequest->GetStencil());
} else {
MOZ_ASSERT(aRequest->IsTextSource());
@@ -2835,12 +2842,11 @@ void ScriptLoader::CalculateCacheFlag(ScriptLoadRequest* aRequest) {
// are going to be dropped soon.
if (hasFetchCountMin) {
uint32_t fetchCount = 0;
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
fetchCount = aRequest->mLoadedScript->mFetchCount;
} else {
if (NS_FAILED(
- aRequest->getLoadedScript()->mCacheInfo->GetCacheTokenFetchCount(
- &fetchCount))) {
+ aRequest->mCacheInfo->GetCacheTokenFetchCount(&fetchCount))) {
LOG(
("ScriptLoadRequest (%p): Bytecode-cache: Skip disk: Cannot get "
"fetchCount.",
@@ -3059,6 +3065,7 @@ static void InstantiateStencil(
void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
JSContext* aCx, JS::CompileOptions& aCompileOptions,
ScriptLoadRequest* aRequest, JS::MutableHandle<JSScript*> aScript,
+ RefPtr<JS::Stencil>& aStencilOut,
JS::Handle<JS::Value> aDebuggerPrivateValue,
JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv) {
nsAutoCString profilerLabelString;
@@ -3079,8 +3086,7 @@ void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
aRv.NoteJSContextException(aCx);
return;
}
-
- aRequest->SetStencil(stencil);
+ aStencilOut = stencil.get();
InstantiateStencil(aCx, aCompileOptions, stencil, aScript,
aDebuggerPrivateValue, aDebuggerIntroductionScript,
@@ -3093,10 +3099,9 @@ void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
RefPtr<JS::Stencil> stencil;
Decode(aCx, aCompileOptions, aRequest->Bytecode(), stencil, aRv);
+ aStencilOut = stencil.get();
if (stencil) {
- aRequest->SetStencil(stencil);
-
InstantiateStencil(aCx, aCompileOptions, stencil, aScript,
aDebuggerPrivateValue, aDebuggerIntroductionScript,
aRv);
@@ -3105,7 +3110,7 @@ void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
// We do not expect to be saving anything when we already have some
// bytecode.
- MOZ_ASSERT(!aRequest->getLoadedScript()->HasDiskCacheReference());
+ MOZ_ASSERT(!aRequest->HasDiskCacheReference());
return;
}
@@ -3130,8 +3135,7 @@ void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
aRv.NoteJSContextException(aCx);
return;
}
-
- aRequest->SetStencil(stencil);
+ aStencilOut = stencil.get();
InstantiateStencil(aCx, aCompileOptions, stencil, aScript,
aDebuggerPrivateValue, aDebuggerIntroductionScript, aRv,
@@ -3159,10 +3163,9 @@ void ScriptLoader::InstantiateClassicScriptFromMaybeEncodedSource(
MOZ_ASSERT(!maybeSource.empty());
maybeSource.mapNonEmpty(compile);
+ aStencilOut = stencil.get();
if (stencil) {
- aRequest->SetStencil(stencil);
-
InstantiateStencil(aCx, aCompileOptions, stencil, aScript,
aDebuggerPrivateValue, aDebuggerIntroductionScript,
erv, /* aStorage = */ nullptr,
@@ -3211,7 +3214,7 @@ void ScriptLoader::InstantiateClassicScriptFromAny(
ScriptLoadRequest* aRequest, JS::MutableHandle<JSScript*> aScript,
JS::Handle<JS::Value> aDebuggerPrivateValue,
JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv) {
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
RefPtr<JS::Stencil> stencil = aRequest->GetStencil();
InstantiateClassicScriptFromCachedStencil(
aCx, aCompileOptions, aRequest, stencil, aScript, aDebuggerPrivateValue,
@@ -3219,14 +3222,15 @@ void ScriptLoader::InstantiateClassicScriptFromAny(
return;
}
+ RefPtr<JS::Stencil> stencil;
InstantiateClassicScriptFromMaybeEncodedSource(
- aCx, aCompileOptions, aRequest, aScript, aDebuggerPrivateValue,
+ aCx, aCompileOptions, aRequest, aScript, stencil, aDebuggerPrivateValue,
aDebuggerIntroductionScript, aRv);
if (aRv.Failed()) {
return;
}
- TryCacheRequest(aRequest);
+ TryCacheRequest(aRequest, stencil);
}
ScriptLoader::CacheBehavior ScriptLoader::GetCacheBehavior(
@@ -3272,29 +3276,30 @@ ScriptLoader::CacheBehavior ScriptLoader::GetCacheBehavior(
return CacheBehavior::Insert;
}
-void ScriptLoader::TryCacheRequest(ScriptLoadRequest* aRequest) {
- MOZ_ASSERT(aRequest->HasStencil());
+void ScriptLoader::TryCacheRequest(ScriptLoadRequest* aRequest,
+ RefPtr<JS::Stencil>& aStencil) {
CacheBehavior cacheBehavior = GetCacheBehavior(aRequest);
if (cacheBehavior == CacheBehavior::DoNothing) {
- if (!aRequest->PassedConditionForDiskCache()) {
- aRequest->ClearStencil();
- }
return;
}
MOZ_ASSERT(mCache);
+ MOZ_ASSERT(aStencil);
- if (!JS::IsStencilCacheable(aRequest->GetStencil())) {
+ if (!JS::IsStencilCacheable(aStencil)) {
// If the stencil is not compatible with the cache (e.g. contains asm.js),
// this should also evict any the existing cache if any.
cacheBehavior = CacheBehavior::Evict;
}
- aRequest->ConvertToCachedStencil();
+ aRequest->SetStencil(aStencil.forget());
if (cacheBehavior == CacheBehavior::Insert) {
auto loadData = MakeRefPtr<ScriptLoadData>(this, aRequest);
+ if (aRequest->HasDiskCacheReference()) {
+ StoreCacheInfo(aRequest->getLoadedScript(), aRequest);
+ }
mCache->Insert(*loadData);
LOG(("ScriptLoader (%p): Inserting in-memory cache for %s.", this,
aRequest->mURI->GetSpecOrDefault().get()));
@@ -3304,10 +3309,6 @@ void ScriptLoader::TryCacheRequest(ScriptLoadRequest* aRequest) {
mCache->Evict(key);
LOG(("ScriptLoader (%p): Evicting in-memory cache for %s.", this,
aRequest->mURI->GetSpecOrDefault().get()));
-
- if (!aRequest->PassedConditionForDiskCache()) {
- aRequest->ClearStencil();
- }
}
}
@@ -3319,15 +3320,6 @@ nsCString& ScriptLoader::BytecodeMimeTypeFor(ScriptLoadRequest* aRequest) {
return nsContentUtils::JSScriptBytecodeMimeType();
}
-/* static */
-nsCString& ScriptLoader::BytecodeMimeTypeFor(
- JS::loader::LoadedScript* aLoadedScript) {
- if (aLoadedScript->IsModuleScript()) {
- return nsContentUtils::JSModuleBytecodeMimeType();
- }
- return nsContentUtils::JSScriptBytecodeMimeType();
-}
-
void ScriptLoader::MaybePrepareForCacheBeforeExecute(
ScriptLoadRequest* aRequest, JS::Handle<JSScript*> aScript) {
if (!aRequest->PassedConditionForEitherCache()) {
@@ -3351,7 +3343,7 @@ nsresult ScriptLoader::MaybePrepareForCacheAfterExecute(
// NOTE: This assertion will fail once we start encoding more data after the
// first encode.
MOZ_ASSERT_IF(
- !aRequest->IsCachedStencil(),
+ !aRequest->IsStencil(),
aRequest->GetSRILength() == aRequest->SRIAndBytecode().length());
RegisterForCache(aRequest);
@@ -3361,7 +3353,7 @@ nsresult ScriptLoader::MaybePrepareForCacheAfterExecute(
LOG(("ScriptLoadRequest (%p): Bytecode-cache: disabled (rv = %X)", aRequest,
unsigned(aRv)));
TRACE_FOR_TEST_NONE(aRequest, "scriptloader_no_encode");
- aRequest->getLoadedScript()->DropDiskCacheReference();
+ aRequest->DropDiskCacheReference();
return aRv;
}
@@ -3446,7 +3438,7 @@ nsresult ScriptLoader::EvaluateScript(nsIGlobalObject* aGlobalObject,
}
#endif
- if (aRequest->IsCachedStencil()) {
+ if (aRequest->IsStencil()) {
#ifdef DEBUG
// A request with cache might not have mBaseURL.
if (aRequest->mBaseURL) {
@@ -3549,7 +3541,7 @@ LoadedScript* ScriptLoader::GetActiveScript(JSContext* aCx) {
void ScriptLoader::RegisterForCache(ScriptLoadRequest* aRequest) {
MOZ_ASSERT(aRequest->IsMarkedForEitherCache());
MOZ_ASSERT_IF(aRequest->IsMarkedForDiskCache(),
- aRequest->getLoadedScript()->HasDiskCacheReference());
+ aRequest->HasDiskCacheReference());
MOZ_DIAGNOSTIC_ASSERT(!aRequest->isInList());
mCachingQueue.AppendElement(aRequest);
}
@@ -3644,23 +3636,65 @@ void ScriptLoader::UpdateCache() {
MOZ_ASSERT(!IsWebExtensionRequest(request),
"Bytecode for web extension content scrips is not cached");
- FinishCollectingDelazifications(aes.cx(), request);
+ RefPtr<JS::Stencil> stencil;
+ stencil = FinishCollectingDelazifications(aes.cx(), request);
+ if (mCache) {
+ if (stencil) {
+ MOZ_ASSERT_IF(request->IsStencil(), stencil == request->GetStencil());
+
+ // The bytecode encoding is performed only when there was no
+ // bytecode stored in the necko cache.
+ //
+ // TODO: Move this to SharedScriptCache.
+
+ if (request->IsMarkedForDiskCache()) {
+ if (request->HasDiskCacheReference()) {
+ // The nsICacheInfoChannel is stored when the this request
+ // receives a source text (See ScriptLoadHandler::OnStreamComplete),
+ // and also the SRI is calculated only in that case.
+ MOZ_ASSERT(request->SRIAndBytecode().length() ==
+ request->GetSRILength());
+
+ EncodeBytecodeAndSave(aes.cx(), request, request->mCacheInfo,
+ BytecodeMimeTypeFor(request),
+ request->SRIAndBytecode(), stencil);
+
+ request->DropBytecode();
+ } else if (request->getLoadedScript()->mCacheInfo) {
+ // The nsICacheInfoChannel is stored when the cached request
+ // received a source text (See ScriptLoadHandler::OnStreamComplete),
+ // and also the SRI is calculated and stored into the cache only in
+ // that case.
+ EncodeBytecodeAndSave(aes.cx(), request,
+ request->getLoadedScript()->mCacheInfo,
+ BytecodeMimeTypeFor(request),
+ request->getLoadedScript()->mSRI, stencil);
+
+ // The cached nsICacheInfoChannel can be used only once.
+ // We don't overwrite the bytecode cache.
+ request->getLoadedScript()->mCacheInfo = nullptr;
+ request->getLoadedScript()->mSRI.clear();
+ }
+ }
+ }
+ request->DropDiskCacheReference();
+ } else {
+ if (stencil) {
+ MOZ_ASSERT(request->SRIAndBytecode().length() ==
+ request->GetSRILength());
- // The bytecode encoding is performed only when there was no
- // bytecode stored in the necko cache.
- //
- // TODO: Move this to SharedScriptCache.
- if (request->IsMarkedForDiskCache() &&
- request->getLoadedScript()->HasDiskCacheReference()) {
- EncodeBytecodeAndSave(aes.cx(), request->getLoadedScript());
- }
+ EncodeBytecodeAndSave(aes.cx(), request, request->mCacheInfo,
+ BytecodeMimeTypeFor(request),
+ request->SRIAndBytecode(), stencil);
- request->DropBytecode();
- request->getLoadedScript()->DropDiskCacheReference();
+ request->DropBytecode();
+ }
+ request->DropDiskCacheReference();
+ }
}
}
-void ScriptLoader::FinishCollectingDelazifications(
+already_AddRefed<JS::Stencil> ScriptLoader::FinishCollectingDelazifications(
JSContext* aCx, ScriptLoadRequest* aRequest) {
RefPtr<JS::Stencil> stencil;
bool result;
@@ -3678,34 +3712,39 @@ void ScriptLoader::FinishCollectingDelazifications(
}
if (!result) {
JS_ClearPendingException(aCx);
- return;
+ return nullptr;
}
- MOZ_ASSERT(stencil == aRequest->GetStencil());
+ return stencil.forget();
}
void ScriptLoader::EncodeBytecodeAndSave(
- JSContext* aCx, JS::loader::LoadedScript* aLoadedScript) {
- MOZ_ASSERT(aLoadedScript->HasDiskCacheReference());
- MOZ_ASSERT(aLoadedScript->HasStencil());
+ JSContext* aCx, ScriptLoadRequest* aRequest,
+ nsCOMPtr<nsICacheInfoChannel>& aCacheInfo, nsCString& aMimeType,
+ const JS::TranscodeBuffer& aSRI, JS::Stencil* aStencil) {
+ MOZ_ASSERT(aCacheInfo);
- size_t SRILength = aLoadedScript->SRIAndBytecode().length();
+ using namespace mozilla::Telemetry;
+ nsresult rv = NS_OK;
+ auto bytecodeFailed = mozilla::MakeScopeExit(
+ [&]() { TRACE_FOR_TEST_NONE(aRequest, "scriptloader_bytecode_failed"); });
+
+ size_t SRILength = aSRI.length();
MOZ_ASSERT(JS::IsTranscodingBytecodeOffsetAligned(SRILength));
JS::TranscodeBuffer SRIAndBytecode;
- if (!SRIAndBytecode.appendAll(aLoadedScript->SRIAndBytecode())) {
- LOG(("LoadedScript (%p): Cannot allocate buffer", aLoadedScript));
+ if (!SRIAndBytecode.appendAll(aSRI)) {
+ LOG(("ScriptLoadRequest (%p): Cannot allocate buffer", aRequest));
return;
}
- JS::TranscodeResult result =
- JS::EncodeStencil(aCx, aLoadedScript->GetStencil(), SRIAndBytecode);
+ JS::TranscodeResult result = JS::EncodeStencil(aCx, aStencil, SRIAndBytecode);
if (result != JS::TranscodeResult::Ok) {
// Encoding can be aborted for non-supported syntax (e.g. asm.js), or
// any other internal error.
// We don't care the error and just give up encoding.
JS_ClearPendingException(aCx);
- LOG(("LoadedScript (%p): Cannot serialize bytecode", aLoadedScript));
+ LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", aRequest));
return;
}
@@ -3717,9 +3756,9 @@ void ScriptLoader::EncodeBytecodeAndSave(
if (compressedBytecode.length() >= UINT32_MAX) {
LOG(
- ("LoadedScript (%p): Bytecode cache is too large to be decoded "
+ ("ScriptLoadRequest (%p): Bytecode cache is too large to be decoded "
"correctly.",
- aLoadedScript));
+ aRequest));
return;
}
@@ -3727,36 +3766,38 @@ void ScriptLoader::EncodeBytecodeAndSave(
// might fail if the stream is already open by another request, in which
// case, we just ignore the current one.
nsCOMPtr<nsIAsyncOutputStream> output;
- nsresult rv = aLoadedScript->mCacheInfo->OpenAlternativeOutputStream(
- BytecodeMimeTypeFor(aLoadedScript),
- static_cast<int64_t>(compressedBytecode.length()),
+ rv = aCacheInfo->OpenAlternativeOutputStream(
+ aMimeType, static_cast<int64_t>(compressedBytecode.length()),
getter_AddRefs(output));
if (NS_FAILED(rv)) {
LOG(
- ("LoadedScript (%p): Cannot open bytecode cache (rv = %X, output "
+ ("ScriptLoadRequest (%p): Cannot open bytecode cache (rv = %X, output "
"= %p)",
- aLoadedScript, unsigned(rv), output.get()));
+ aRequest, unsigned(rv), output.get()));
return;
}
MOZ_ASSERT(output);
auto closeOutStream = mozilla::MakeScopeExit([&]() {
rv = output->CloseWithStatus(rv);
- LOG(("LoadedScript (%p): Closing (rv = %X)", aLoadedScript, unsigned(rv)));
+ LOG(("ScriptLoadRequest (%p): Closing (rv = %X)", aRequest, unsigned(rv)));
});
uint32_t n;
rv = output->Write(reinterpret_cast<char*>(compressedBytecode.begin()),
compressedBytecode.length(), &n);
LOG(
- ("LoadedScript (%p): Write bytecode cache (rv = %X, length = %u, "
+ ("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, "
"written = %u)",
- aLoadedScript, unsigned(rv), unsigned(compressedBytecode.length()), n));
+ aRequest, unsigned(rv), unsigned(compressedBytecode.length()), n));
if (NS_FAILED(rv)) {
return;
}
MOZ_RELEASE_ASSERT(compressedBytecode.length() == n);
+
+ bytecodeFailed.release();
+ TRACE_FOR_TEST_NONE(aRequest, "scriptloader_bytecode_saved");
}
void ScriptLoader::GiveUpCaching() {
@@ -3797,7 +3838,7 @@ void ScriptLoader::GiveUpCaching() {
}
}
- request->getLoadedScript()->DropDiskCacheReference();
+ request->DropDiskCacheReference();
}
while (!mCacheableDependencyModules.isEmpty()) {
diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h
@@ -526,7 +526,8 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
CacheBehavior GetCacheBehavior(ScriptLoadRequest* aRequest);
- void TryCacheRequest(ScriptLoadRequest* aRequest);
+ void TryCacheRequest(ScriptLoadRequest* aRequest,
+ RefPtr<JS::Stencil>& aStencil);
JS::loader::ScriptLoadRequest* LookupPreloadRequest(
nsIScriptElement* aElement, JS::loader::ScriptKind aScriptKind,
@@ -694,6 +695,7 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
void InstantiateClassicScriptFromMaybeEncodedSource(
JSContext* aCx, JS::CompileOptions& aCompileOptions,
ScriptLoadRequest* aRequest, JS::MutableHandle<JSScript*> aScript,
+ RefPtr<JS::Stencil>& aStencilOut,
JS::Handle<JS::Value> aDebuggerPrivateValue,
JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv);
@@ -707,8 +709,6 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
JS::Handle<JSScript*> aDebuggerIntroductionScript, ErrorResult& aRv);
static nsCString& BytecodeMimeTypeFor(ScriptLoadRequest* aRequest);
- static nsCString& BytecodeMimeTypeFor(
- JS::loader::LoadedScript* aLoadedScript);
// Decide whether to encode bytecode for given script load request,
// and store the script into the request if necessary.
@@ -774,14 +774,20 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
/**
* Finish collecting the delazifications and return the stencil.
*/
- void FinishCollectingDelazifications(JSContext* aCx,
- ScriptLoadRequest* aRequest);
+ already_AddRefed<JS::Stencil> FinishCollectingDelazifications(
+ JSContext* aCx, ScriptLoadRequest* aRequest);
/**
* Encode the stencils and save the bytecode to the necko cache.
*/
- void EncodeBytecodeAndSave(JSContext* aCx,
- JS::loader::LoadedScript* aLoadedScript);
+ void EncodeBytecodeAndSave(JSContext* aCx, ScriptLoadRequest* aRequest,
+ nsCOMPtr<nsICacheInfoChannel>& aCacheInfo,
+ nsCString& aMimeType,
+ const JS::TranscodeBuffer& aSRI,
+ JS::Stencil* aStencil);
+
+ void StoreCacheInfo(JS::loader::LoadedScript* aLoadedScript,
+ ScriptLoadRequest* aRequest);
/**
* Stop collecting delazifications for all requests.
diff --git a/js/loader/LoadedScript.cpp b/js/loader/LoadedScript.cpp
@@ -49,7 +49,7 @@ LoadedScript::LoadedScript(ScriptKind aKind,
}
LoadedScript::LoadedScript(const LoadedScript& aOther)
- : mDataType(DataType::eCachedStencil),
+ : mDataType(DataType::eStencil),
mKind(aOther.mKind),
mReferrerPolicy(aOther.mReferrerPolicy),
mBytecodeOffset(0),
@@ -62,10 +62,10 @@ LoadedScript::LoadedScript(const LoadedScript& aOther)
MOZ_ASSERT(mURI);
// NOTE: This is only for the stencil case.
// The script text and the bytecode are not reflected.
- MOZ_DIAGNOSTIC_ASSERT(aOther.mDataType == DataType::eCachedStencil);
+ MOZ_DIAGNOSTIC_ASSERT(aOther.mDataType == DataType::eStencil);
MOZ_DIAGNOSTIC_ASSERT(mStencil);
MOZ_ASSERT(!mScriptData);
- MOZ_ASSERT(mSRIAndBytecode.empty());
+ MOZ_ASSERT(mScriptBytecode.empty());
}
LoadedScript::~LoadedScript() {
@@ -118,7 +118,7 @@ size_t LoadedScript::SizeOfIncludingThis(
}
}
- bytes += mSRIAndBytecode.sizeOfExcludingThis(aMallocSizeOf);
+ bytes += mScriptBytecode.sizeOfExcludingThis(aMallocSizeOf);
// NOTE: Stencil is reported by SpiderMonkey.
return bytes;
@@ -262,7 +262,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript, LoadedScript)
tmp->UnlinkModuleRecord();
tmp->mParseError.setUndefined();
tmp->mErrorToRethrow.setUndefined();
- tmp->DropDiskCacheReference();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript, LoadedScript)
@@ -292,13 +291,13 @@ ModuleScript::ModuleScript(const LoadedScript& aOther) : LoadedScript(aOther) {
already_AddRefed<ModuleScript> ModuleScript::FromCache(
const LoadedScript& aScript) {
MOZ_DIAGNOSTIC_ASSERT(aScript.IsModuleScript());
- MOZ_DIAGNOSTIC_ASSERT(aScript.IsCachedStencil());
+ MOZ_DIAGNOSTIC_ASSERT(aScript.IsStencil());
return mozilla::MakeRefPtr<ModuleScript>(aScript).forget();
}
already_AddRefed<LoadedScript> ModuleScript::ToCache() {
- MOZ_DIAGNOSTIC_ASSERT(IsCachedStencil());
+ MOZ_DIAGNOSTIC_ASSERT(IsStencil());
MOZ_DIAGNOSTIC_ASSERT(!HasParseError());
MOZ_DIAGNOSTIC_ASSERT(!HasErrorToRethrow());
diff --git a/js/loader/LoadedScript.h b/js/loader/LoadedScript.h
@@ -117,26 +117,7 @@ class LoadedScript : public nsIMemoryReporter {
// Type of data this instance holds, which is either provided by the nsChannel
// or retrieved from the cache.
- enum class DataType : uint8_t {
- // This script haven't yet received the data.
- eUnknown,
-
- // This script is received as a plain text from the channel.
- // mScriptData holds the text source, and mStencil holds the compiled
- // stencil.
- // mSRIAndBytecode holds the SRI.
- eTextSource,
-
- // This script is received as a bytecode from the channel.
- // mSRIAndBytecode holds the SRI and the bytecode, and mStencil holds the
- // decoded stencil.
- eBytecode,
-
- // This script is cached from the previous load.
- // mStencil holds the cached stencil, and mSRIAndBytecode holds the SRI.
- // mScriptData is unused.
- eCachedStencil
- };
+ enum class DataType : uint8_t { eUnknown, eTextSource, eBytecode, eStencil };
// Use a vector backed by the JS allocator for script text so that contents
// can be transferred in constant time to the JS engine, not copied in linear
@@ -151,7 +132,7 @@ class LoadedScript : public nsIMemoryReporter {
bool IsTextSource() const { return mDataType == DataType::eTextSource; }
bool IsSource() const { return IsTextSource(); }
bool IsBytecode() const { return mDataType == DataType::eBytecode; }
- bool IsCachedStencil() const { return mDataType == DataType::eCachedStencil; }
+ bool IsStencil() const { return mDataType == DataType::eStencil; }
void SetUnknownDataType() {
mDataType = DataType::eUnknown;
@@ -169,10 +150,10 @@ class LoadedScript : public nsIMemoryReporter {
mDataType = DataType::eBytecode;
}
- void ConvertToCachedStencil() {
- MOZ_ASSERT(HasStencil());
+ void SetStencil(already_AddRefed<Stencil> aStencil) {
SetUnknownDataType();
- mDataType = DataType::eCachedStencil;
+ mDataType = DataType::eStencil;
+ mStencil = aStencil;
}
bool IsUTF16Text() const {
@@ -223,7 +204,7 @@ class LoadedScript : public nsIMemoryReporter {
}
bool CanHaveBytecode() const {
- return IsBytecode() || IsSource() || IsCachedStencil();
+ return IsBytecode() || IsSource() || IsStencil();
}
TranscodeBuffer& SRIAndBytecode() {
@@ -231,11 +212,11 @@ class LoadedScript : public nsIMemoryReporter {
// as we want to be able to save the bytecode content when we are loading
// from source.
MOZ_ASSERT(CanHaveBytecode());
- return mSRIAndBytecode;
+ return mScriptBytecode;
}
TranscodeRange Bytecode() const {
MOZ_ASSERT(IsBytecode());
- const auto& bytecode = mSRIAndBytecode;
+ const auto& bytecode = mScriptBytecode;
auto offset = mBytecodeOffset;
return TranscodeRange(bytecode.begin() + offset,
bytecode.length() - offset);
@@ -252,37 +233,18 @@ class LoadedScript : public nsIMemoryReporter {
void DropBytecode() {
MOZ_ASSERT(CanHaveBytecode());
- mSRIAndBytecode.clearAndFree();
+ mScriptBytecode.clearAndFree();
}
- bool HasStencil() const { return mStencil; }
-
Stencil* GetStencil() const {
- MOZ_ASSERT(!IsUnknownDataType());
- MOZ_ASSERT(HasStencil());
+ MOZ_ASSERT(IsStencil());
return mStencil;
}
- void SetStencil(Stencil* aStencil) {
- MOZ_ASSERT(aStencil);
- MOZ_ASSERT(!HasStencil());
- mStencil = aStencil;
- }
-
- void ClearStencil() { mStencil = nullptr; }
-
- // Check the reference to the cache info channel, which is used by the disk
- // cache.
- bool HasDiskCacheReference() const { return !!mCacheInfo; }
-
- // Drop the reference to the cache info channel.
- void DropDiskCacheReference() { mCacheInfo = nullptr; }
-
public:
// Fields.
- // Determine whether the mScriptData or mSRIAndBytecode is used.
- // See DataType description for more info.
+ // Determine whether the mScriptData or mScriptBytecode is used.
DataType mDataType;
// The consumer-defined number of times that this loaded script is used.
@@ -298,7 +260,7 @@ class LoadedScript : public nsIMemoryReporter {
mozilla::dom::ReferrerPolicy mReferrerPolicy;
public:
- // Offset of the bytecode in mSRIAndBytecode.
+ // Offset of the bytecode in mScriptBytecode.
uint32_t mBytecodeOffset;
private:
@@ -316,23 +278,18 @@ class LoadedScript : public nsIMemoryReporter {
// since mScriptData is cleared when the source is passed to the JS engine.
size_t mReceivedScriptTextLength;
- // Holds either of the following for non-inline scripts:
- // * The SRI serialized hash and the paddings, which is calculated when
- // receiving the source text
- // * The SRI, padding, and the script bytecode, which is received
- // from necko. The data is laid out according to ScriptBytecodeDataLayout
- // or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
- TranscodeBuffer mSRIAndBytecode;
+ // Holds the SRI serialized hash and the script bytecode for non-inline
+ // scripts. The data is laid out according to ScriptBytecodeDataLayout
+ // or, if compression is enabled, ScriptBytecodeCompressedDataLayout.
+ TranscodeBuffer mScriptBytecode;
- // Holds the stencil for the script. This field is used in all DataType.
RefPtr<Stencil> mStencil;
// The cache info channel used when saving the bytecode to the necko cache.
- //
- // This field is populated if the cache is enabled and this is either
- // IsTextSource() or IsCachedStencil(), and it's cleared after saving the
- // bytecode (Thus, used only once).
nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
+
+ // The SRI data and the padding, used when saving the bytecode.
+ JS::TranscodeBuffer mSRI;
};
// Provide accessors for any classes `Derived` which is providing the
@@ -365,7 +322,7 @@ class LoadedScriptDelegate {
bool IsTextSource() const { return GetLoadedScript()->IsTextSource(); }
bool IsSource() const { return GetLoadedScript()->IsSource(); }
bool IsBytecode() const { return GetLoadedScript()->IsBytecode(); }
- bool IsCachedStencil() const { return GetLoadedScript()->IsCachedStencil(); }
+ bool IsStencil() const { return GetLoadedScript()->IsStencil(); }
void SetUnknownDataType() { GetLoadedScript()->SetUnknownDataType(); }
@@ -375,7 +332,9 @@ class LoadedScriptDelegate {
void SetBytecode() { GetLoadedScript()->SetBytecode(); }
- void ConvertToCachedStencil() { GetLoadedScript()->ConvertToCachedStencil(); }
+ void SetStencil(already_AddRefed<Stencil> aStencil) {
+ GetLoadedScript()->SetStencil(std::move(aStencil));
+ }
bool IsUTF16Text() const { return GetLoadedScript()->IsUTF16Text(); }
bool IsUTF8Text() const { return GetLoadedScript()->IsUTF8Text(); }
@@ -426,12 +385,7 @@ class LoadedScriptDelegate {
void DropBytecode() { GetLoadedScript()->DropBytecode(); }
- bool HasStencil() const { return GetLoadedScript()->HasStencil(); }
Stencil* GetStencil() const { return GetLoadedScript()->GetStencil(); }
- void SetStencil(Stencil* aStencil) {
- GetLoadedScript()->SetStencil(aStencil);
- }
- void ClearStencil() { GetLoadedScript()->ClearStencil(); }
};
class ClassicScript final : public LoadedScript {
diff --git a/js/loader/ModuleLoaderBase.cpp b/js/loader/ModuleLoaderBase.cpp
@@ -543,11 +543,11 @@ nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
// NOTE: The LoadedScript::mDataType field used by the IsStencil call can be
// modified asynchronously after the StartFetch call.
// In order to avoid the race condition, cache the value here.
- bool isCachedStencil = aRequest->IsCachedStencil();
+ bool isStencil = aRequest->IsStencil();
- MOZ_ASSERT_IF(isCachedStencil, aRestart == RestartRequest::No);
+ MOZ_ASSERT_IF(isStencil, aRestart == RestartRequest::No);
- if (!isCachedStencil) {
+ if (!isStencil) {
aRequest->SetUnknownDataType();
}
@@ -582,7 +582,7 @@ nsresult ModuleLoaderBase::StartOrRestartModuleLoad(ModuleLoadRequest* aRequest,
rv = StartFetch(aRequest);
NS_ENSURE_SUCCESS(rv, rv);
- if (isCachedStencil) {
+ if (isStencil) {
MOZ_ASSERT(
IsModuleFetched(ModuleMapKey(aRequest->mURI, aRequest->mModuleType)));
return NS_OK;
diff --git a/js/loader/ScriptLoadRequest.cpp b/js/loader/ScriptLoadRequest.cpp
@@ -20,6 +20,7 @@
#include "ModuleLoadRequest.h"
#include "nsContentUtils.h"
+#include "nsICacheInfoChannel.h"
#include "nsIClassOfService.h"
#include "nsISupportsPriority.h"
@@ -74,12 +75,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mOriginPrincipal, mBaseURL,
- mLoadedScript, mLoadContext)
+ mLoadedScript, mCacheInfo, mLoadContext)
tmp->mScriptForCache = nullptr;
+ tmp->DropDiskCacheReference();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mLoadContext, mLoadedScript)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mCacheInfo, mLoadContext,
+ mLoadedScript)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest)
@@ -122,6 +125,8 @@ void ScriptLoadRequest::Cancel() {
}
}
+void ScriptLoadRequest::DropDiskCacheReference() { mCacheInfo = nullptr; }
+
bool ScriptLoadRequest::HasScriptLoadContext() const {
return HasLoadContext() && mLoadContext->IsWindowContext();
}
diff --git a/js/loader/ScriptLoadRequest.h b/js/loader/ScriptLoadRequest.h
@@ -29,6 +29,8 @@
#include "ScriptKind.h"
#include "ScriptFetchOptions.h"
+class nsICacheInfoChannel;
+
namespace mozilla::dom {
class ScriptLoadContext;
@@ -257,6 +259,13 @@ class ScriptLoadRequest : public nsISupports,
mozilla::CORSMode CORSMode() const { return mFetchOptions->mCORSMode; }
+ // Check the reference to the cache info channel, which is used by the disk
+ // cache.
+ bool HasDiskCacheReference() const { return !!mCacheInfo; }
+
+ // Drop the reference to the cache info channel.
+ void DropDiskCacheReference();
+
bool HasLoadContext() const { return mLoadContext; }
bool HasScriptLoadContext() const;
bool HasWorkerLoadContext() const;
@@ -362,6 +371,10 @@ class ScriptLoadRequest : public nsISupports,
// NOTE: This field is not used for ModuleLoadRequest.
JS::Heap<JSScript*> mScriptForCache;
+ // Holds the Cache information, which is used to register the bytecode
+ // on the cache entry, such that we can load it the next time.
+ nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
+
// LoadContext for augmenting the load depending on the loading
// context (DOM, Worker, etc.)
RefPtr<LoadContextBase> mLoadContext;