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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user