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:
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;