From 7b5fd55b61cfc6e5d3ee42993ce361cd075fcadc Mon Sep 17 00:00:00 2001 From: Nick Fitzgerald Date: Tue, 10 May 2016 15:45:01 -0700 Subject: [PATCH] Bug 1269451 - Make the UncompressedSourceCache use the shared, immutable strings infrastructure; r=jimb --- js/src/jsfun.cpp | 7 +- js/src/jsscript.cpp | 172 +++++++++++++------------------------------- js/src/jsscript.h | 47 ++++-------- 3 files changed, 66 insertions(+), 160 deletions(-) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index f56ef64c7c43..edf5d4650c78 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1437,12 +1437,11 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti MOZ_ASSERT(lazy->scriptSource()->hasSourceData()); // Parse and compile the script from source. - UncompressedSourceCache::AutoHoldEntry holder; - const char16_t* chars = lazy->scriptSource()->chars(cx, holder); - if (!chars) + auto text = lazy->scriptSource()->sourceText(cx); + if (!text) return false; - const char16_t* lazyStart = chars + lazy->begin(); + const char16_t* lazyStart = text->chars() + lazy->begin(); size_t lazyLength = lazy->end() - lazy->begin(); if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index b274e9f8b4b2..ec62710c357a 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1812,73 +1812,19 @@ JSScript::sourceData(JSContext* cx) return scriptSource()->substring(cx, sourceStart(), sourceEnd()); } -UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry() - : cache_(nullptr), source_(nullptr) +mozilla::Maybe +UncompressedSourceCache::lookup(ScriptSource* ss) { -} - -void -UncompressedSourceCache::AutoHoldEntry::holdEntry(UncompressedSourceCache* cache, ScriptSource* source) -{ - // Initialise the holder for a specific cache and script source. This will - // hold on to the cached source chars in the event that the cache is purged. - MOZ_ASSERT(!cache_ && !source_ && !charsToFree_); - cache_ = cache; - source_ = source; -} - -void -UncompressedSourceCache::AutoHoldEntry::deferDelete(UniqueTwoByteChars chars) -{ - // Take ownership of source chars now the cache is being purged. Remove our - // reference to the ScriptSource which might soon be destroyed. - MOZ_ASSERT(cache_ && source_ && !charsToFree_); - cache_ = nullptr; - source_ = nullptr; - charsToFree_ = Move(chars); -} - -UncompressedSourceCache::AutoHoldEntry::~AutoHoldEntry() -{ - if (cache_) { - MOZ_ASSERT(source_); - cache_->releaseEntry(*this); - } -} - -void -UncompressedSourceCache::holdEntry(AutoHoldEntry& holder, ScriptSource* ss) -{ - MOZ_ASSERT(!holder_); - holder.holdEntry(this, ss); - holder_ = &holder; -} - -void -UncompressedSourceCache::releaseEntry(AutoHoldEntry& holder) -{ - MOZ_ASSERT(holder_ == &holder); - holder_ = nullptr; -} - -const char16_t* -UncompressedSourceCache::lookup(ScriptSource* ss, AutoHoldEntry& holder) -{ - MOZ_ASSERT(!holder_); if (!map_) - return nullptr; - if (Map::Ptr p = map_->lookup(ss)) { - holdEntry(holder, ss); - return p->value().get(); - } - return nullptr; + return mozilla::Nothing(); + if (Map::Ptr p = map_->lookup(ss)) + return mozilla::Some(p->value().clone()); + return mozilla::Nothing(); } bool -UncompressedSourceCache::put(ScriptSource* ss, UniqueTwoByteChars str, AutoHoldEntry& holder) +UncompressedSourceCache::put(ScriptSource* ss, SharedImmutableTwoByteString&& str) { - MOZ_ASSERT(!holder_); - if (!map_) { UniquePtr map = MakeUnique(); if (!map || !map->init()) @@ -1887,11 +1833,7 @@ UncompressedSourceCache::put(ScriptSource* ss, UniqueTwoByteChars str, AutoHoldE map_ = Move(map); } - if (!map_->put(ss, Move(str))) - return false; - - holdEntry(holder, ss); - return true; + return map_->put(ss, Move(str)); } void @@ -1900,13 +1842,6 @@ UncompressedSourceCache::purge() if (!map_) return; - for (Map::Range r = map_->all(); !r.empty(); r.popFront()) { - if (holder_ && r.front().key() == holder_->source()) { - holder_->deferDelete(Move(r.front().value())); - holder_ = nullptr; - } - } - map_.reset(); } @@ -1914,45 +1849,39 @@ size_t UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) { size_t n = 0; - if (map_ && !map_->empty()) { + if (map_ && !map_->empty()) n += map_->sizeOfIncludingThis(mallocSizeOf); - for (Map::Range r = map_->all(); !r.empty(); r.popFront()) - n += mallocSizeOf(r.front().value().get()); - } return n; } -const char16_t* -ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder) +mozilla::Maybe +ScriptSource::sourceText(JSContext* cx) { - struct CharsMatcher + struct SourceTextMatcher { - using ReturnType = const char16_t*; + using ReturnType = mozilla::Maybe; JSContext* cx; ScriptSource& ss; - UncompressedSourceCache::AutoHoldEntry& holder; - explicit CharsMatcher(JSContext* cx, ScriptSource& ss, - UncompressedSourceCache::AutoHoldEntry& holder) + explicit SourceTextMatcher(JSContext* cx, ScriptSource& ss) : cx(cx) , ss(ss) - , holder(holder) { } ReturnType match(Uncompressed& u) { - return u.string.chars(); + return mozilla::Some(u.string.clone()); } ReturnType match(Compressed& c) { - if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss, holder)) - return decompressed; + if (auto decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss)) + return mozilla::Some(mozilla::Move(*decompressed)); const size_t lengthWithNull = ss.length() + 1; UniqueTwoByteChars decompressed(js_pod_malloc(lengthWithNull)); if (!decompressed) { - JS_ReportOutOfMemory(cx); - return nullptr; + ReportOutOfMemory(cx); + return mozilla::Nothing(); } if (!DecompressString((const unsigned char*) ss.compressedData(), @@ -1960,43 +1889,44 @@ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holde reinterpret_cast(decompressed.get()), lengthWithNull * sizeof(char16_t))) { - JS_ReportOutOfMemory(cx); - return nullptr; + ReportOutOfMemory(cx); + return mozilla::Nothing(); } decompressed[ss.length()] = 0; + auto& strings = cx->runtime()->sharedImmutableStrings(); + auto deduped = strings.getOrCreate(mozilla::Move(decompressed), ss.length()); + if (!deduped) { + ReportOutOfMemory(cx); + return mozilla::Nothing(); + } + // Decompressing a huge script is expensive. With lazy parsing and // relazification, this can happen repeatedly, so conservatively go // back to storing the data uncompressed to avoid wasting too much // time yo-yoing back and forth between compressed and uncompressed. const size_t HUGE_SCRIPT = 5 * 1024 * 1024; if (lengthWithNull > HUGE_SCRIPT) { - auto& strings = cx->runtime()->sharedImmutableStrings(); - auto str = strings.getOrCreate(mozilla::Move(decompressed), ss.length()); - if (!str) { - JS_ReportOutOfMemory(cx); - return nullptr; - } - ss.data = SourceType(Uncompressed(mozilla::Move(*str))); - return ss.uncompressedChars(); + ss.data = SourceType(Uncompressed(deduped->clone())); + return mozilla::Some(mozilla::Move(*deduped)); } - ReturnType ret = decompressed.get(); - if (!cx->runtime()->uncompressedSourceCache.put(&ss, Move(decompressed), holder)) { - JS_ReportOutOfMemory(cx); - return nullptr; + if (!cx->runtime()->uncompressedSourceCache.put(&ss, deduped->clone())) { + ReportOutOfMemory(cx); + return mozilla::Nothing(); } - return ret; + + return mozilla::Some(mozilla::Move(*deduped)); } ReturnType match(Missing&) { - MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing"); - return nullptr; + MOZ_CRASH("ScriptSource::sourceText() on ScriptSource with SourceType = Missing"); + return mozilla::Nothing(); } }; - CharsMatcher cm(cx, *this, holder); + SourceTextMatcher cm(cx, *this); return data.match(cm); } @@ -2004,22 +1934,20 @@ JSFlatString* ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop) { MOZ_ASSERT(start <= stop); - UncompressedSourceCache::AutoHoldEntry holder; - const char16_t* chars = this->chars(cx, holder); - if (!chars) + auto text = sourceText(cx); + if (!text) return nullptr; - return NewStringCopyN(cx, chars + start, stop - start); + return NewStringCopyN(cx, text->chars() + start, stop - start); } JSFlatString* ScriptSource::substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop) { MOZ_ASSERT(start <= stop); - UncompressedSourceCache::AutoHoldEntry holder; - const char16_t* chars = this->chars(cx, holder); - if (!chars) + auto text = sourceText(cx); + if (!text) return nullptr; - return NewStringCopyNDontDeflate(cx, chars + start, stop - start); + return NewStringCopyNDontDeflate(cx, text->chars() + start, stop - start); } MOZ_MUST_USE bool @@ -4468,19 +4396,17 @@ LazyScriptHashPolicy::match(JSScript* script, const Lookup& lookup) return false; } - UncompressedSourceCache::AutoHoldEntry holder; - - const char16_t* scriptChars = script->scriptSource()->chars(cx, holder); - if (!scriptChars) + auto scriptText = script->scriptSource()->sourceText(cx); + if (!scriptText) return false; - const char16_t* lazyChars = lazy->scriptSource()->chars(cx, holder); - if (!lazyChars) + auto lazyText = lazy->scriptSource()->sourceText(cx); + if (!lazyText) return false; size_t begin = script->sourceStart(); size_t length = script->sourceEnd() - begin; - return !memcmp(scriptChars + begin, lazyChars + begin, length); + return !memcmp(scriptText->chars() + begin, lazyText->chars() + begin, length); } void diff --git a/js/src/jsscript.h b/js/src/jsscript.h index e5dd7a82846e..76bdc5ba4109 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -10,6 +10,8 @@ #define jsscript_h #include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/Variant.h" @@ -576,45 +578,20 @@ class ScriptSource; class UncompressedSourceCache { - typedef HashMap, - SystemAllocPolicy> Map; + using Map = HashMap, + SystemAllocPolicy>; - public: - // Hold an entry in the source data cache and prevent it from being purged on GC. - class AutoHoldEntry - { - UncompressedSourceCache* cache_; - ScriptSource* source_; - UniqueTwoByteChars charsToFree_; - public: - explicit AutoHoldEntry(); - ~AutoHoldEntry(); - private: - void holdEntry(UncompressedSourceCache* cache, ScriptSource* source); - void deferDelete(UniqueTwoByteChars chars); - ScriptSource* source() const { return source_; } - friend class UncompressedSourceCache; - }; - - private: UniquePtr map_; - AutoHoldEntry* holder_; public: - UncompressedSourceCache() : holder_(nullptr) {} - - const char16_t* lookup(ScriptSource* ss, AutoHoldEntry& asp); - bool put(ScriptSource* ss, UniqueTwoByteChars chars, AutoHoldEntry& asp); + UncompressedSourceCache() { } + MOZ_MUST_USE mozilla::Maybe lookup(ScriptSource* ss); + bool put(ScriptSource* ss, SharedImmutableTwoByteString&& chars); void purge(); - size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); - - private: - void holdEntry(AutoHoldEntry& holder, ScriptSource* ss); - void releaseEntry(AutoHoldEntry& holder); }; class ScriptSource @@ -764,7 +741,11 @@ class ScriptSource MOZ_ASSERT(hasSourceData()); return argumentsNotIncluded_; } - const char16_t* chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& asp); + + // Get a handle on the underlying source text. Returns mozilla::Nothing on + // OOM failure. + MOZ_MUST_USE mozilla::Maybe sourceText(JSContext* cx); + JSFlatString* substring(JSContext* cx, uint32_t start, uint32_t stop); JSFlatString* substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop); void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,