Bug 1364974: Part 4 - Replace CachedScript::mStatus and related logic with original and final process sets. r?shu
The current logic is fairly hard to follow, and fails to account for changes
in the set of processes a script needs to be executed in when deciding whether
to write a new cache file. These changes simplify that logic considerably,
increase the chances that we'll correctly pre-decode a script that's needed in
a given process during startup.
MozReview-Commit-ID: BvqjKU8FDHW
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -521,43 +521,39 @@ ScriptPreloader::PrepareCacheWrite()
mChildCache->PrepareCacheWrite();
}
});
if (mDataPrepared) {
return;
}
- bool found = Find(IterHash(mScripts), [] (CachedScript* script) {
- return (script->mStatus == ScriptStatus::Restored ||
- !script->HasRange() || script->HasArray());
- });
+ bool found = false;
+ for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
+ // Don't write any scripts that are also in the child cache. They'll be
+ // loaded from the child cache in that case, so there's no need to write
+ // them twice.
+ CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
+ if (childScript && !childScript->mProcessTypes.isEmpty()) {
+ childScript->UpdateLoadTime(script->mLoadTime);
+ childScript->mProcessTypes += script->mProcessTypes;
+ script.Remove();
+ } else if (!(script->mProcessTypes == script->mOriginalProcessTypes)) {
+ found = true;
+ }
+ }
if (!found) {
mSaveComplete = true;
return;
}
AutoSafeJSAPI jsapi;
-
for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
- // Don't write any scripts that are also in the child cache. They'll be
- // loaded from the child cache in that case, so there's no need to write
- // them twice.
- CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
- if (childScript) {
- if (childScript->mStatus == ScriptStatus::Saved) {
- childScript->UpdateLoadTime(script->mLoadTime);
- childScript->mProcessTypes += script->mProcessTypes;
- } else {
- childScript = nullptr;
- }
- }
-
- if (childScript || (!script->mSize && !script->XDREncode(jsapi.cx()))) {
+ if (!script->mSize && !script->XDREncode(jsapi.cx())) {
script.Remove();
} else {
script->mSize = script->Range().length();
}
}
mDataPrepared = true;
}
@@ -684,49 +680,41 @@ ScriptPreloader::NoteScript(const nsCStr
// Don't bother caching files that belong to the mochitest harness.
NS_NAMED_LITERAL_CSTRING(mochikitPrefix, "chrome://mochikit/");
if (StringHead(url, mochikitPrefix.Length()) == mochikitPrefix) {
return;
}
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
- if (script->mStatus == ScriptStatus::Restored) {
- script->mStatus = ScriptStatus::Saved;
-
+ if (!script->mScript) {
MOZ_ASSERT(jsscript);
script->mScript = jsscript;
script->mReadyToExecute = true;
}
script->UpdateLoadTime(TimeStamp::Now());
script->mProcessTypes += CurrentProcessType();
}
void
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
ProcessType processType, nsTArray<uint8_t>&& xdrData,
TimeStamp loadTime)
{
auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, nullptr);
- if (script->mStatus == ScriptStatus::Restored) {
- script->mStatus = ScriptStatus::Saved;
+ if (!script->HasRange()) {
+ MOZ_ASSERT(!script->HasArray());
- script->mReadyToExecute = true;
- } else {
- if (!script->HasRange()) {
- MOZ_ASSERT(!script->HasArray());
+ script->mSize = xdrData.Length();
+ script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
- script->mSize = xdrData.Length();
- script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
-
- auto& data = script->Array();
- script->mXDRRange.emplace(data.Elements(), data.Length());
- }
+ auto& data = script->Array();
+ script->mXDRRange.emplace(data.Elements(), data.Length());
}
script->UpdateLoadTime(loadTime);
script->mProcessTypes += processType;
}
JSScript*
ScriptPreloader::GetCachedScript(JSContext* cx, const nsCString& path)
@@ -818,19 +806,20 @@ ScriptPreloader::OffThreadDecodeCallback
script->mToken = token;
script->mReadyToExecute = true;
mal.NotifyAll();
}
ScriptPreloader::CachedScript::CachedScript(ScriptPreloader& cache, InputBuffer& buf)
: mCache(cache)
- , mStatus(ScriptStatus::Restored)
{
Code(buf);
+ mOriginalProcessTypes = mProcessTypes;
+ mProcessTypes = {};
}
bool
ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
{
JSAutoCompartment ac(cx, mScript);
JS::RootedScript jsscript(cx, mScript);
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -141,25 +141,29 @@ private:
{
public:
CachedScript(CachedScript&&) = default;
CachedScript(ScriptPreloader& cache, const nsCString& url, const nsCString& cachePath, JSScript* script)
: mCache(cache)
, mURL(url)
, mCachePath(cachePath)
- , mStatus(ScriptStatus::Saved)
, mScript(script)
, mReadyToExecute(true)
{}
inline CachedScript(ScriptPreloader& cache, InputBuffer& buf);
~CachedScript() = default;
+ ScriptStatus Status() const
+ {
+ return mProcessTypes.isEmpty() ? ScriptStatus::Restored : ScriptStatus::Saved;
+ }
+
// For use with nsTArray::Sort.
//
// Orders scripts by:
//
// 1) Async-decoded scripts before sync-decoded scripts, since the
// former are needed immediately at startup, and should be stored
// contiguously.
// 2) Script load time, so that scripts which are needed earlier are
@@ -183,17 +187,17 @@ private:
};
struct StatusMatcher final : public Matcher<CachedScript*>
{
explicit StatusMatcher(ScriptStatus status) : mStatus(status) {}
virtual bool Matches(CachedScript* script)
{
- return script->mStatus == mStatus;
+ return script->Status() == mStatus;
}
const ScriptStatus mStatus;
};
void Cancel();
void FreeData()
@@ -287,18 +291,16 @@ private:
nsCString mCachePath;
// The offset of this script in the cache file, from the start of the XDR
// data block.
uint32_t mOffset = 0;
// The size of this script's encoded XDR data.
uint32_t mSize = 0;
- ScriptStatus mStatus;
-
TimeStamp mLoadTime{};
JS::Heap<JSScript*> mScript;
// True if this script is ready to be executed. This means that either the
// off-thread portion of an off-thread decode has finished, or the script
// is too small to be decoded off-thread, and may be immediately decoded
// whenever it is first executed.
@@ -306,16 +308,20 @@ private:
// The off-thread decode token for a completed off-thread decode, which
// has not yet been finalized on the main thread.
void* mToken = nullptr;
// The set of processes in which this script has been used.
EnumSet<ProcessType> mProcessTypes{};
+ // The set of processes which the script was loaded into during the
+ // last session, as read from the cache file.
+ EnumSet<ProcessType> mOriginalProcessTypes{};
+
// The read-only XDR data for this script, which was either read from an
// existing cache file, or generated by encoding a script which was
// compiled during this session.
Maybe<JS::TranscodeRange> mXDRRange;
// XDR data which was generated from a script compiled during this
// session, and will be written to the cache file.
MaybeOneOf<JS::TranscodeBuffer, nsTArray<uint8_t>> mXDRData;