tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mdom/script/ModuleLoader.cpp | 12++++--------
Mdom/script/ScriptLoadHandler.cpp | 6+++---
Mdom/script/ScriptLoader.cpp | 261++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mdom/script/ScriptLoader.h | 20+++++++++++++-------
Mjs/loader/LoadedScript.cpp | 13++++++-------
Mjs/loader/LoadedScript.h | 92++++++++++++++++++++-----------------------------------------------------------
Mjs/loader/ModuleLoaderBase.cpp | 8++++----
Mjs/loader/ScriptLoadRequest.cpp | 9+++++++--
Mjs/loader/ScriptLoadRequest.h | 13+++++++++++++
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;