Bug 1800641 - Move some ScriptLoadRequest fields into LoadedScript. r=yulia

We are looking into caching loaded script in memory. To do so we need something
to cache.

At the moment, the `ScriptLoadRequest` structure hold all the fields which are
loaded, and used before executing JavaScript code. Then, the `ScriptLoadRequest`
is not guaranteed to out-live the first execution.

Therefore, we have to move fields out of the `ScriptLoadRequest` such that they
can later be used by any caching mechanism. The `LoadedScript` is the closest
existing structure which exists which fit the description.

This patch moves fields out of the ScriptLoadRequest into the `LoadedScript`,
which already has a `LoadedScript` field.

The `LoadedScript` field is initialized sooner, when the `ScriptLoadRequest` is
created, to be subsituted later by a real cache implementation. At the moment
the function `ScriptLoadRequest::NoCacheEntryFound` is used as a placeholder to
change the state of the `ScriptLoadRequest` from `CheckingCache` to `Fetching`.
Existing initializations are replaced by assertions to fail in debug build if
the current patch does not reproduce the expected state properly.

The `LoadedScript` get fields such as the source text, the text length, the
bytecode buffer (which also contains SRI), and the offset at which the bytecode
starts within the bytecode buffer. As these fields are no longer reachable by
name, multiple accessors are added to work-around the issue. Using this as an
opportunity to add extra assertions as part of these accessors.

A new class named `LoadedScriptDelegate` is added to re-add, by inheritance, all
the accessors which used to be part of `ScriptLoadRequest` as methods which are
delegating to the field which is holding the `LoadedScript`. This class is using
templates to avoid virtual inheritance which might hinder inlining, especially
since `ScriptLoadRequest` cannot be made final, as `ModuleLoadRequest` extends
it.

The `ScriptFetchOptions` structure is moved to its own file to solve C++ include
issues.

Differential Revision: https://phabricator.services.mozilla.com/D163615
This commit is contained in:
Nicolas B. Pierron
2023-12-25 09:46:54 +00:00
parent 6886ed0953
commit 7ad7f045d4
20 changed files with 620 additions and 362 deletions

View File

@@ -514,8 +514,7 @@ void ScriptLoader::RunScriptWhenSafe(ScriptLoadRequest* aRequest) {
}
nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) {
MOZ_ASSERT(aRequest->IsBytecode());
aRequest->mScriptBytecode.clearAndFree();
aRequest->DropBytecode();
TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
"scriptloader_fallback");
@@ -1013,6 +1012,7 @@ already_AddRefed<ScriptLoadRequest> ScriptLoader::CreateLoadRequest(
new ScriptLoadRequest(aKind, aURI, aReferrerPolicy, fetchOptions,
aIntegrity, referrer, context);
aRequest->NoCacheEntryFound();
return aRequest.forget();
}
@@ -1369,7 +1369,7 @@ bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
request->GetScriptLoadContext()->mColumnNo =
aElement->GetScriptColumnNumber();
request->mFetchSourceOnly = true;
request->SetTextSource();
request->SetTextSource(request->mLoadContext.get());
TRACE_FOR_TEST_BOOL(request->GetScriptLoadContext()->GetScriptElement(),
"scriptloader_load_source");
CollectScriptTelemetry(request);
@@ -1761,10 +1761,9 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
} else {
MOZ_ASSERT(aRequest->IsBytecode());
size_t length =
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
JS::TranscodeRange bytecode = aRequest->Bytecode();
if (!StaticPrefs::javascript_options_parallel_parsing() ||
length < OffThreadMinimumBytecodeLength) {
bytecode.length() < OffThreadMinimumBytecodeLength) {
return NS_OK;
}
}
@@ -2019,11 +2018,9 @@ nsresult ScriptLoader::CreateOffThreadTask(
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
CompileOrDecodeTask** aCompileOrDecodeTask) {
if (aRequest->IsBytecode()) {
JS::TranscodeRange bytecode = aRequest->Bytecode();
JS::DecodeOptions decodeOptions(aOptions);
JS::TranscodeRange range(
aRequest->mScriptBytecode.begin() + aRequest->mBytecodeOffset,
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset);
RefPtr<ScriptDecodeTask> decodeTask = new ScriptDecodeTask(range);
RefPtr<ScriptDecodeTask> decodeTask = new ScriptDecodeTask(bytecode);
nsresult rv = decodeTask->Init(decodeOptions);
NS_ENSURE_SUCCESS(rv, rv);
decodeTask.forget(aCompileOrDecodeTask);
@@ -2031,7 +2028,8 @@ nsresult ScriptLoader::CreateOffThreadTask(
}
MaybeSourceText maybeSource;
nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource);
nsresult rv = aRequest->GetScriptSource(aCx, &maybeSource,
aRequest->mLoadContext.get());
NS_ENSURE_SUCCESS(rv, rv);
if (ShouldApplyDelazifyStrategy(aRequest)) {
@@ -2265,7 +2263,7 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
// We received bytecode as input, thus we were decoding, and we will not be
// encoding the bytecode once more. We can safely clear the content of this
// buffer.
aRequest->mScriptBytecode.clearAndFree();
aRequest->DropBytecode();
}
return rv;
@@ -2463,7 +2461,7 @@ bool ScriptLoader::ShouldCacheBytecode(ScriptLoadRequest* aRequest) {
size_t sourceLength;
size_t minLength;
MOZ_ASSERT(aRequest->IsTextSource());
sourceLength = aRequest->mScriptTextLength;
sourceLength = aRequest->ScriptTextLength();
minLength = sourceLengthMin;
if (sourceLength < minLength) {
LOG(("ScriptLoadRequest (%p): Bytecode-cache: Script is too small.",
@@ -2608,7 +2606,7 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
MarkerInnerWindowIdFromJSContext(aCx),
profilerLabelString);
rv = aExec.Decode(aRequest->mScriptBytecode, aRequest->mBytecodeOffset);
rv = aExec.Decode(aRequest->Bytecode());
}
// We do not expect to be saving anything when we already have some
@@ -2634,7 +2632,8 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
MOZ_ASSERT(aRequest->IsTextSource());
MaybeSourceText maybeSource;
rv = aRequest->GetScriptSource(aCx, &maybeSource);
rv = aRequest->GetScriptSource(aCx, &maybeSource,
aRequest->mLoadContext.get());
if (NS_SUCCEEDED(rv)) {
AUTO_PROFILER_MARKER_TEXT("ScriptCompileMainThread", JS,
MarkerInnerWindowIdFromJSContext(aCx),
@@ -2673,9 +2672,17 @@ nsresult ScriptLoader::MaybePrepareForBytecodeEncodingAfterExecute(
if (aRequest->IsMarkedForBytecodeEncoding()) {
TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
"scriptloader_encode");
// Check that the TranscodeBuffer which is going to receive the encoded
// bytecode only contains the SRI, and nothing more.
//
// NOTE: This assertion will fail once we start encoding more data after the
// first encode.
MOZ_ASSERT(aRequest->mBytecodeOffset == aRequest->mScriptBytecode.length());
#ifdef DEBUG
JS::TranscodeBuffer& bytecode = aRequest->SRIAndBytecode();
MOZ_ASSERT(aRequest->GetSRILength() == bytecode.length());
#endif
RegisterForBytecodeEncoding(aRequest);
MOZ_ASSERT(IsAlreadyHandledForBytecodeEncodingPreparation(aRequest));
@@ -2744,8 +2751,13 @@ nsresult ScriptLoader::EvaluateScript(nsIGlobalObject* aGlobalObject,
aRequest->GetScriptLoadContext()->GetProfilerLabel(profilerLabelString);
// Create a ClassicScript object and associate it with the JSScript.
RefPtr<ClassicScript> classicScript = new ClassicScript(
aRequest->ReferrerPolicy(), aRequest->mFetchOptions, aRequest->mBaseURL);
MOZ_ASSERT(aRequest->mLoadedScript->IsClassicScript());
MOZ_ASSERT(aRequest->mLoadedScript->GetFetchOptions() ==
aRequest->mFetchOptions);
MOZ_ASSERT(aRequest->mLoadedScript->GetURI() == aRequest->mURI);
aRequest->mLoadedScript->SetBaseURL(aRequest->mBaseURL);
RefPtr<ClassicScript> classicScript =
aRequest->mLoadedScript->AsClassicScript();
JS::Rooted<JS::Value> classicScriptValue(cx, JS::PrivateValue(classicScript));
JS::CompileOptions options(cx);
@@ -2929,7 +2941,7 @@ void ScriptLoader::EncodeBytecode() {
MOZ_ASSERT(!IsWebExtensionRequest(request),
"Bytecode for web extension content scrips is not cached");
EncodeRequestBytecode(aes.cx(), request);
request->mScriptBytecode.clearAndFree();
request->DropBytecode();
request->DropBytecodeCacheReferences();
}
}
@@ -2949,11 +2961,11 @@ void ScriptLoader::EncodeRequestBytecode(JSContext* aCx,
ModuleScript* moduleScript = aRequest->AsModuleRequest()->mModuleScript;
JS::Rooted<JSObject*> module(aCx, moduleScript->ModuleRecord());
result =
JS::FinishIncrementalEncoding(aCx, module, aRequest->mScriptBytecode);
JS::FinishIncrementalEncoding(aCx, module, aRequest->SRIAndBytecode());
} else {
JS::Rooted<JSScript*> script(aCx, aRequest->mScriptForBytecodeEncoding);
result =
JS::FinishIncrementalEncoding(aCx, script, aRequest->mScriptBytecode);
JS::FinishIncrementalEncoding(aCx, script, aRequest->SRIAndBytecode());
}
if (!result) {
// Encoding can be aborted for non-supported syntax (e.g. asm.js), or
@@ -2967,8 +2979,8 @@ void ScriptLoader::EncodeRequestBytecode(JSContext* aCx,
Vector<uint8_t> compressedBytecode;
// TODO probably need to move this to a helper thread
if (!ScriptBytecodeCompress(aRequest->mScriptBytecode,
aRequest->mBytecodeOffset, compressedBytecode)) {
if (!ScriptBytecodeCompress(aRequest->SRIAndBytecode(),
aRequest->GetSRILength(), compressedBytecode)) {
return;
}
@@ -3059,7 +3071,7 @@ void ScriptLoader::GiveUpBytecodeEncoding() {
}
}
request->mScriptBytecode.clearAndFree();
request->DropBytecode();
request->DropBytecodeCacheReferences();
}
}
@@ -3326,13 +3338,16 @@ nsresult ScriptLoader::OnStreamComplete(
if (aRequest->IsSource()) {
uint32_t sriLength = 0;
rv = SaveSRIHash(aRequest, aSRIDataVerifier, &sriLength);
MOZ_ASSERT_IF(NS_SUCCEEDED(rv),
aRequest->mScriptBytecode.length() == sriLength);
JS::TranscodeBuffer& bytecode = aRequest->SRIAndBytecode();
MOZ_ASSERT_IF(NS_SUCCEEDED(rv), bytecode.length() == sriLength);
aRequest->mBytecodeOffset = JS::AlignTranscodingBytecodeOffset(sriLength);
if (aRequest->mBytecodeOffset != sriLength) {
// We need extra padding after SRI hash.
if (!aRequest->mScriptBytecode.resize(aRequest->mBytecodeOffset)) {
// TODO: (Bug 1800896) This code should be moved into SaveSRIHash, and the
// SRI out-param can be removed.
aRequest->SetSRILength(sriLength);
if (aRequest->GetSRILength() != sriLength) {
// The bytecode is aligned in the bytecode buffer, and space might be
// reserved for padding after the SRI hash.
if (!bytecode.resize(aRequest->GetSRILength())) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
@@ -3401,44 +3416,45 @@ nsresult ScriptLoader::SaveSRIHash(ScriptLoadRequest* aRequest,
SRICheckDataVerifier* aSRIDataVerifier,
uint32_t* sriLength) const {
MOZ_ASSERT(aRequest->IsSource());
MOZ_ASSERT(aRequest->mScriptBytecode.empty());
JS::TranscodeBuffer& bytecode = aRequest->SRIAndBytecode();
MOZ_ASSERT(bytecode.empty());
uint32_t len;
// If the integrity metadata does not correspond to a valid hash function,
// IsComplete would be false.
if (!aRequest->mIntegrity.IsEmpty() && aSRIDataVerifier->IsComplete()) {
MOZ_ASSERT(aRequest->mScriptBytecode.length() == 0);
MOZ_ASSERT(bytecode.length() == 0);
// Encode the SRI computed hash.
len = aSRIDataVerifier->DataSummaryLength();
if (!aRequest->mScriptBytecode.resize(len)) {
if (!bytecode.resize(len)) {
return NS_ERROR_OUT_OF_MEMORY;
}
DebugOnly<nsresult> res = aSRIDataVerifier->ExportDataSummary(
len, aRequest->mScriptBytecode.begin());
DebugOnly<nsresult> res =
aSRIDataVerifier->ExportDataSummary(len, bytecode.begin());
MOZ_ASSERT(NS_SUCCEEDED(res));
} else {
MOZ_ASSERT(aRequest->mScriptBytecode.length() == 0);
MOZ_ASSERT(bytecode.length() == 0);
// Encode a dummy SRI hash.
len = SRICheckDataVerifier::EmptyDataSummaryLength();
if (!aRequest->mScriptBytecode.resize(len)) {
if (!bytecode.resize(len)) {
return NS_ERROR_OUT_OF_MEMORY;
}
DebugOnly<nsresult> res = SRICheckDataVerifier::ExportEmptyDataSummary(
len, aRequest->mScriptBytecode.begin());
DebugOnly<nsresult> res =
SRICheckDataVerifier::ExportEmptyDataSummary(len, bytecode.begin());
MOZ_ASSERT(NS_SUCCEEDED(res));
}
// Verify that the exported and predicted length correspond.
DebugOnly<uint32_t> srilen{};
MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(
len, aRequest->mScriptBytecode.begin(), &srilen)));
MOZ_ASSERT(NS_SUCCEEDED(
SRICheckDataVerifier::DataSummaryLength(len, bytecode.begin(), &srilen)));
MOZ_ASSERT(srilen == len);
*sriLength = len;