tor-browser

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

commit 82cca9c5e0be46b196ce7dbca1bf8ac4a8efa08a
parent b9c23ddfed2cb96cf71d7603d73bbf109d4dd7de
Author: Cristina Horotan <chorotan@mozilla.com>
Date:   Wed,  1 Oct 2025 14:01:29 +0300

Revert "Bug 1991380, Bug 1991358 - Remove MarkedForCache state from the caching plan. r=nbp" for causing build bustages

This reverts commit fbc2c401214040726ea8d3979e585af16a01713a.

Revert "Bug 1991358 - Part 3: Remove ScriptLoadRequest::MarkScriptForCache and ModuleLoadRequest::MarkModuleForCache. r=nbp"

This reverts commit ebec92d70eba098284ff4e8effd8ab4e20404bc2.

Revert "Bug 1991358 - Part 2: Remove ScriptLoadRequest::mScriptForCache. r=nbp"

This reverts commit f9e1c5eb8c9a4c082cd5ba7b2f673c50b13c75e4.

Revert "Bug 1991358 - Part 1: Stop performing JS::FinishCollectingDelazifications/JS::AbortCollectingDelazifications. r=nbp"

This reverts commit 7fa3a4ebaf7e4ee800d55b74ead6913e389e333b.

Diffstat:
Mdom/script/ScriptLoader.cpp | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mdom/script/ScriptLoader.h | 16++++++++++++++++
Mjs/loader/ModuleLoadRequest.cpp | 8--------
Mjs/loader/ModuleLoadRequest.h | 9+++++++--
Mjs/loader/ModuleLoaderBase.cpp | 4+++-
Mjs/loader/ScriptLoadRequest.cpp | 25+++++++++++++++++++++++--
Mjs/loader/ScriptLoadRequest.h | 45++++++++++++++++++++++++++++++++++++++++++---
7 files changed, 191 insertions(+), 27 deletions(-)

diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp @@ -23,7 +23,7 @@ #include "js/Transcoding.h" // JS::TranscodeRange, JS::TranscodeResult, JS::IsTranscodeFailureResult #include "js/Utility.h" #include "js/experimental/CompileScript.h" // JS::FrontendContext, JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::ThreadStackQuotaForSize, JS::CompilationStorage, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::DecodeStencil, JS::PrepareForInstantiate -#include "js/experimental/JSStencil.h" // JS::Stencil, JS::InstantiationStorage, JS::StartCollectingDelazifications, JS::IsStencilCacheable +#include "js/experimental/JSStencil.h" // JS::Stencil, JS::InstantiationStorage, JS::StartCollectingDelazifications, JS::FinishCollectingDelazifications, JS::AbortCollectingDelazifications, JS::IsStencilCacheable #include "js/loader/LoadedScript.h" #include "js/loader/ModuleLoadRequest.h" #include "js/loader/ModuleLoaderBase.h" @@ -3321,9 +3321,18 @@ nsCString& ScriptLoader::BytecodeMimeTypeFor( return nsContentUtils::JSScriptBytecodeMimeType(); } +void ScriptLoader::MaybePrepareForCacheBeforeExecute( + ScriptLoadRequest* aRequest, JS::Handle<JSScript*> aScript) { + if (!aRequest->PassedConditionForEitherCache()) { + return; + } + + aRequest->MarkScriptForCache(aScript); +} + nsresult ScriptLoader::MaybePrepareForCacheAfterExecute( ScriptLoadRequest* aRequest, nsresult aRv) { - if (aRequest->PassedConditionForEitherCache()) { + if (aRequest->IsMarkedForEitherCache()) { TRACE_FOR_TEST(aRequest, "scriptloader_encode"); // Bytecode-encoding branch is used for 2 purposes right now: // * If the request is stencil, reflect delazifications to cached stencil @@ -3350,6 +3359,30 @@ nsresult ScriptLoader::MaybePrepareForCacheAfterExecute( return aRv; } +void ScriptLoader::MaybePrepareModuleForCacheBeforeExecute( + JSContext* aCx, ModuleLoadRequest* aRequest) { + if (aRequest->IsMarkedForEitherCache()) { + // This module is imported multiple times, and already marked. + return; + } + + if (aRequest->PassedConditionForEitherCache()) { + aRequest->MarkModuleForCache(); + } + + for (auto* r = mCacheableDependencyModules.getFirst(); r; r = r->getNext()) { + auto* dep = r->AsModuleRequest(); + MOZ_ASSERT(dep->PassedConditionForEitherCache()); + + if (dep->GetRootModule() != aRequest) { + continue; + } + MOZ_ASSERT(!dep->IsMarkedForEitherCache()); + + dep->MarkModuleForCache(); + } +} + nsresult ScriptLoader::MaybePrepareModuleForCacheAfterExecute( ModuleLoadRequest* aRequest, nsresult aRv) { MOZ_ASSERT(aRequest->IsTopLevel()); @@ -3465,13 +3498,17 @@ nsresult ScriptLoader::EvaluateScript(nsIGlobalObject* aGlobalObject, classicScriptValue, introductionScript, erv); if (!erv.Failed()) { - LOG(("ScriptLoadRequest (%p): Evaluate Script", aRequest)); - AUTO_PROFILER_MARKER_TEXT("ScriptExecution", JS, - MarkerInnerWindowIdFromJSContext(cx), - profilerLabelString); + MaybePrepareForCacheBeforeExecute(aRequest, script); - MOZ_ASSERT(options.noScriptRval); - ExecuteCompiledScript(cx, classicScript, script, erv); + { + LOG(("ScriptLoadRequest (%p): Evaluate Script", aRequest)); + AUTO_PROFILER_MARKER_TEXT("ScriptExecution", JS, + MarkerInnerWindowIdFromJSContext(cx), + profilerLabelString); + + MOZ_ASSERT(options.noScriptRval); + ExecuteCompiledScript(cx, classicScript, script, erv); + } } rv = EvaluationExceptionToNSResult(erv); @@ -3503,8 +3540,8 @@ LoadedScript* ScriptLoader::GetActiveScript(JSContext* aCx) { } void ScriptLoader::RegisterForCache(ScriptLoadRequest* aRequest) { - MOZ_ASSERT(aRequest->PassedConditionForEitherCache()); - MOZ_ASSERT_IF(aRequest->PassedConditionForDiskCache(), + MOZ_ASSERT(aRequest->IsMarkedForEitherCache()); + MOZ_ASSERT_IF(aRequest->IsMarkedForDiskCache(), aRequest->getLoadedScript()->HasDiskCacheReference()); MOZ_DIAGNOSTIC_ASSERT(!aRequest->isInList()); mCachingQueue.AppendElement(aRequest); @@ -3600,11 +3637,13 @@ void ScriptLoader::UpdateCache() { MOZ_ASSERT(!IsWebExtensionRequest(request), "Bytecode for web extension content scrips is not cached"); + FinishCollectingDelazifications(aes.cx(), request); + // The bytecode encoding is performed only when there was no // bytecode stored in the necko cache. // // TODO: Move this to SharedScriptCache. - if (request->PassedConditionForDiskCache() && + if (request->IsMarkedForDiskCache() && request->getLoadedScript()->HasDiskCacheReference()) { EncodeBytecodeAndSave(aes.cx(), request->getLoadedScript()); } @@ -3614,6 +3653,29 @@ void ScriptLoader::UpdateCache() { } } +void ScriptLoader::FinishCollectingDelazifications( + JSContext* aCx, ScriptLoadRequest* aRequest) { + RefPtr<JS::Stencil> stencil; + bool result; + if (aRequest->IsModuleRequest()) { + aRequest->mScriptForCache = nullptr; + ModuleScript* moduleScript = aRequest->AsModuleRequest()->mModuleScript; + JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord()); + result = JS::FinishCollectingDelazifications(aCx, module, + getter_AddRefs(stencil)); + } else { + JS::Rooted<JSScript*> script(aCx, aRequest->mScriptForCache); + aRequest->mScriptForCache = nullptr; + result = JS::FinishCollectingDelazifications(aCx, script, + getter_AddRefs(stencil)); + } + if (!result) { + JS_ClearPendingException(aCx); + return; + } + MOZ_ASSERT(stencil == aRequest->GetStencil()); +} + void ScriptLoader::EncodeBytecodeAndSave( JSContext* aCx, JS::loader::LoadedScript* aLoadedScript) { MOZ_ASSERT(aLoadedScript->HasDiskCacheReference()); @@ -3695,12 +3757,39 @@ void ScriptLoader::GiveUpCaching() { // to avoid queuing more scripts. mGiveUpCaching = true; + // Ideally we prefer to properly end the incremental encoder, such that we + // would not keep a large buffer around. If we cannot, we fallback on the + // removal of all request from the current list and these large buffers would + // be removed at the same time as the source object. + nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject(); + AutoAllowLegacyScriptExecution exemption; + Maybe<AutoEntryScript> aes; + + if (globalObject) { + nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext(); + if (context) { + aes.emplace(globalObject, "give-up bytecode encoding", true); + } + } + while (!mCachingQueue.isEmpty()) { RefPtr<ScriptLoadRequest> request = mCachingQueue.StealFirst(); LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", request.get())); TRACE_FOR_TEST_NONE(request, "scriptloader_bytecode_failed"); MOZ_ASSERT(!IsWebExtensionRequest(request)); + if (aes.isSome()) { + if (request->IsModuleRequest()) { + ModuleScript* moduleScript = request->AsModuleRequest()->mModuleScript; + JS::Rooted<JSObject*> module(aes->cx(), moduleScript->ModuleRecord()); + JS::AbortCollectingDelazifications(module); + } else { + JS::Rooted<JSScript*> script(aes->cx(), request->mScriptForCache); + request->mScriptForCache = nullptr; + JS::AbortCollectingDelazifications(script); + } + } + request->getLoadedScript()->DropDiskCacheReference(); } diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h @@ -710,6 +710,13 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface { 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. + // + // This method must be called before executing the script. + void MaybePrepareForCacheBeforeExecute(ScriptLoadRequest* aRequest, + JS::Handle<JSScript*> aScript); + // Queue the script load request for caching if we decided to cache it, or // cleanup the script load request fields otherwise. // @@ -717,6 +724,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface { nsresult MaybePrepareForCacheAfterExecute(ScriptLoadRequest* aRequest, nsresult aRv); + void MaybePrepareModuleForCacheBeforeExecute( + JSContext* aCx, ModuleLoadRequest* aRequest) override; + // Queue the top-level module load request for caching if we decided to cache // it, or cleanup the module load request fields otherwise. // @@ -762,6 +772,12 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface { void UpdateCache(); /** + * Finish collecting the delazifications and return the stencil. + */ + void FinishCollectingDelazifications(JSContext* aCx, + ScriptLoadRequest* aRequest); + + /** * Encode the stencils and save the bytecode to the necko cache. */ void EncodeBytecodeAndSave(JSContext* aCx, diff --git a/js/loader/ModuleLoadRequest.cpp b/js/loader/ModuleLoadRequest.cpp @@ -61,14 +61,6 @@ ModuleLoadRequest::ModuleLoadRequest( MOZ_ASSERT(mLoader); } -ModuleLoadRequest::~ModuleLoadRequest() { - MOZ_ASSERT(!mReferrerScript); - MOZ_ASSERT(!mModuleRequestObj); - MOZ_ASSERT(mPayload.isUndefined()); - - DropJSObjects(this); -} - nsIGlobalObject* ModuleLoadRequest::GetGlobalObject() { return mLoader->GetGlobalObject(); } diff --git a/js/loader/ModuleLoadRequest.h b/js/loader/ModuleLoadRequest.h @@ -11,7 +11,6 @@ #include "ScriptLoadRequest.h" #include "ModuleLoaderBase.h" #include "mozilla/Assertions.h" -#include "mozilla/HoldDropJSObjects.h" #include "js/RootingAPI.h" #include "js/Value.h" #include "nsURIHashKey.h" @@ -28,7 +27,11 @@ class ModuleLoaderBase; // multiple imports of the same module. class ModuleLoadRequest final : public ScriptLoadRequest { - ~ModuleLoadRequest(); + ~ModuleLoadRequest() { + MOZ_ASSERT(!mReferrerScript); + MOZ_ASSERT(!mModuleRequestObj); + MOZ_ASSERT(mPayload.isUndefined()); + } ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete; ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete; @@ -85,6 +88,8 @@ class ModuleLoadRequest final : public ScriptLoadRequest { return mRootModule; } + void MarkModuleForCache() { MarkForCache(); } + // Convenience methods to call into the module loader for this request. void CancelDynamicImport(nsresult aResult) { diff --git a/js/loader/ModuleLoaderBase.cpp b/js/loader/ModuleLoaderBase.cpp @@ -1585,6 +1585,9 @@ nsresult ModuleLoaderBase::EvaluateModuleInContext( Rooted<Value> rval(aCx); + // TODO: Bug 1973321: Prepare Bytecode encoding for dynamic import + mLoader->MaybePrepareModuleForCacheBeforeExecute(aCx, aRequest); + bool ok = ModuleEvaluate(aCx, module, &rval); // ModuleEvaluate will usually set a pending exception if it returns false, @@ -1612,7 +1615,6 @@ nsresult ModuleLoaderBase::EvaluateModuleInContext( LOG(("ScriptLoadRequest (%p): evaluation failed on throw", aRequest)); } - // TODO: Bug 1973321: Prepare Bytecode encoding for dynamic import rv = mLoader->MaybePrepareModuleForCacheAfterExecute(aRequest, NS_OK); mLoader->MaybeUpdateCache(); diff --git a/js/loader/ScriptLoadRequest.cpp b/js/loader/ScriptLoadRequest.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/ScriptLoadContext.h" #include "mozilla/dom/WorkerLoadContext.h" #include "mozilla/dom/ScriptSettings.h" +#include "mozilla/HoldDropJSObjects.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/Unused.h" #include "mozilla/Utf8.h" // mozilla::Utf8Unit @@ -69,10 +70,20 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest) -NS_IMPL_CYCLE_COLLECTION(ScriptLoadRequest, mFetchOptions, mOriginPrincipal, - mBaseURL, mLoadedScript, mLoadContext) +NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mOriginPrincipal, mBaseURL, + mLoadedScript, mLoadContext) + tmp->mScriptForCache = nullptr; +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_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptForCache) NS_IMPL_CYCLE_COLLECTION_TRACE_END ScriptLoadRequest::ScriptLoadRequest( @@ -97,6 +108,8 @@ ScriptLoadRequest::ScriptLoadRequest( } } +ScriptLoadRequest::~ScriptLoadRequest() { DropJSObjects(this); } + void ScriptLoadRequest::SetReady() { MOZ_ASSERT(!IsFinished()); mState = State::Ready; @@ -230,6 +243,14 @@ void ScriptLoadRequest::SetPendingFetchingError() { mState = State::PendingFetchingError; } +void ScriptLoadRequest::MarkScriptForCache(JSScript* aScript) { + MOZ_ASSERT(!IsModuleRequest()); + MOZ_ASSERT(!mScriptForCache); + MarkForCache(); + mScriptForCache = aScript; + HoldJSObjects(this); +} + static bool IsInternalURIScheme(nsIURI* uri) { return uri->SchemeIs("moz-extension") || uri->SchemeIs("resource") || uri->SchemeIs("moz-src") || uri->SchemeIs("chrome"); diff --git a/js/loader/ScriptLoadRequest.h b/js/loader/ScriptLoadRequest.h @@ -87,7 +87,7 @@ class ScriptLoadRequest : public nsISupports, friend class ScriptLoadRequestList; protected: - virtual ~ScriptLoadRequest() {} + virtual ~ScriptLoadRequest(); public: using SRIMetadata = mozilla::dom::SRIMetadata; @@ -187,11 +187,13 @@ class ScriptLoadRequest : public nsISupports, void SetPendingFetchingError(); bool PassedConditionForDiskCache() const { - return mDiskCachingPlan == CachingPlan::PassedCondition; + return mDiskCachingPlan == CachingPlan::PassedCondition || + mDiskCachingPlan == CachingPlan::MarkedForCache; } bool PassedConditionForMemoryCache() const { - return mMemoryCachingPlan == CachingPlan::PassedCondition; + return mMemoryCachingPlan == CachingPlan::PassedCondition || + mMemoryCachingPlan == CachingPlan::MarkedForCache; } bool PassedConditionForEitherCache() const { @@ -225,7 +227,34 @@ class ScriptLoadRequest : public nsISupports, mMemoryCachingPlan = CachingPlan::PassedCondition; } + bool IsMarkedForDiskCache() const { + return mDiskCachingPlan == CachingPlan::MarkedForCache; + } + + bool IsMarkedForMemoryCache() const { + return mMemoryCachingPlan == CachingPlan::MarkedForCache; + } + + bool IsMarkedForEitherCache() const { + return IsMarkedForDiskCache() || IsMarkedForMemoryCache(); + } + + protected: + void MarkForCache() { + MOZ_ASSERT(mDiskCachingPlan == CachingPlan::PassedCondition || + mMemoryCachingPlan == CachingPlan::PassedCondition); + + if (mDiskCachingPlan == CachingPlan::PassedCondition) { + mDiskCachingPlan = CachingPlan::MarkedForCache; + } + if (mMemoryCachingPlan == CachingPlan::PassedCondition) { + mMemoryCachingPlan = CachingPlan::MarkedForCache; + } + } + public: + void MarkScriptForCache(JSScript* aScript); + mozilla::CORSMode CORSMode() const { return mFetchOptions->mCORSMode; } bool HasLoadContext() const { return mLoadContext; } @@ -272,6 +301,10 @@ class ScriptLoadRequest : public nsISupports, // This fits the condition for the caching (e.g. file size, fetch count). PassedCondition, + + // This is marked for encoding, with setting sufficient input, + // e.g. mScriptForCache for script. + MarkedForCache, }; CachingPlan mDiskCachingPlan = CachingPlan::Uninitialized; CachingPlan mMemoryCachingPlan = CachingPlan::Uninitialized; @@ -323,6 +356,12 @@ class ScriptLoadRequest : public nsISupports, // would share the same loaded script. RefPtr<LoadedScript> mLoadedScript; + // Holds the top-level JSScript that corresponds to the current source, once + // it is parsed, and marked to be saved in the bytecode cache. + // + // NOTE: This field is not used for ModuleLoadRequest. + JS::Heap<JSScript*> mScriptForCache; + // LoadContext for augmenting the load depending on the loading // context (DOM, Worker, etc.) RefPtr<LoadContextBase> mLoadContext;