зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1269451 - Make the UncompressedSourceCache use the shared, immutable strings infrastructure; r=jimb
This commit is contained in:
Родитель
c4d029f247
Коммит
7b5fd55b61
|
@ -1437,12 +1437,11 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
|
||||||
MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
|
MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
|
||||||
|
|
||||||
// Parse and compile the script from source.
|
// Parse and compile the script from source.
|
||||||
UncompressedSourceCache::AutoHoldEntry holder;
|
auto text = lazy->scriptSource()->sourceText(cx);
|
||||||
const char16_t* chars = lazy->scriptSource()->chars(cx, holder);
|
if (!text)
|
||||||
if (!chars)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char16_t* lazyStart = chars + lazy->begin();
|
const char16_t* lazyStart = text->chars() + lazy->begin();
|
||||||
size_t lazyLength = lazy->end() - lazy->begin();
|
size_t lazyLength = lazy->end() - lazy->begin();
|
||||||
|
|
||||||
if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) {
|
if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) {
|
||||||
|
|
|
@ -1812,73 +1812,19 @@ JSScript::sourceData(JSContext* cx)
|
||||||
return scriptSource()->substring(cx, sourceStart(), sourceEnd());
|
return scriptSource()->substring(cx, sourceStart(), sourceEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
|
mozilla::Maybe<SharedImmutableTwoByteString>
|
||||||
: cache_(nullptr), source_(nullptr)
|
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_)
|
if (!map_)
|
||||||
return nullptr;
|
return mozilla::Nothing();
|
||||||
if (Map::Ptr p = map_->lookup(ss)) {
|
if (Map::Ptr p = map_->lookup(ss))
|
||||||
holdEntry(holder, ss);
|
return mozilla::Some(p->value().clone());
|
||||||
return p->value().get();
|
return mozilla::Nothing();
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
UncompressedSourceCache::put(ScriptSource* ss, UniqueTwoByteChars str, AutoHoldEntry& holder)
|
UncompressedSourceCache::put(ScriptSource* ss, SharedImmutableTwoByteString&& str)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!holder_);
|
|
||||||
|
|
||||||
if (!map_) {
|
if (!map_) {
|
||||||
UniquePtr<Map> map = MakeUnique<Map>();
|
UniquePtr<Map> map = MakeUnique<Map>();
|
||||||
if (!map || !map->init())
|
if (!map || !map->init())
|
||||||
|
@ -1887,11 +1833,7 @@ UncompressedSourceCache::put(ScriptSource* ss, UniqueTwoByteChars str, AutoHoldE
|
||||||
map_ = Move(map);
|
map_ = Move(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!map_->put(ss, Move(str)))
|
return map_->put(ss, Move(str));
|
||||||
return false;
|
|
||||||
|
|
||||||
holdEntry(holder, ss);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1900,13 +1842,6 @@ UncompressedSourceCache::purge()
|
||||||
if (!map_)
|
if (!map_)
|
||||||
return;
|
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();
|
map_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1914,45 +1849,39 @@ size_t
|
||||||
UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
|
||||||
{
|
{
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
if (map_ && !map_->empty()) {
|
if (map_ && !map_->empty())
|
||||||
n += map_->sizeOfIncludingThis(mallocSizeOf);
|
n += map_->sizeOfIncludingThis(mallocSizeOf);
|
||||||
for (Map::Range r = map_->all(); !r.empty(); r.popFront())
|
|
||||||
n += mallocSizeOf(r.front().value().get());
|
|
||||||
}
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char16_t*
|
mozilla::Maybe<SharedImmutableTwoByteString>
|
||||||
ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder)
|
ScriptSource::sourceText(JSContext* cx)
|
||||||
{
|
{
|
||||||
struct CharsMatcher
|
struct SourceTextMatcher
|
||||||
{
|
{
|
||||||
using ReturnType = const char16_t*;
|
using ReturnType = mozilla::Maybe<SharedImmutableTwoByteString>;
|
||||||
|
|
||||||
JSContext* cx;
|
JSContext* cx;
|
||||||
ScriptSource& ss;
|
ScriptSource& ss;
|
||||||
UncompressedSourceCache::AutoHoldEntry& holder;
|
|
||||||
|
|
||||||
explicit CharsMatcher(JSContext* cx, ScriptSource& ss,
|
explicit SourceTextMatcher(JSContext* cx, ScriptSource& ss)
|
||||||
UncompressedSourceCache::AutoHoldEntry& holder)
|
|
||||||
: cx(cx)
|
: cx(cx)
|
||||||
, ss(ss)
|
, ss(ss)
|
||||||
, holder(holder)
|
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
ReturnType match(Uncompressed& u) {
|
ReturnType match(Uncompressed& u) {
|
||||||
return u.string.chars();
|
return mozilla::Some(u.string.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnType match(Compressed& c) {
|
ReturnType match(Compressed& c) {
|
||||||
if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss, holder))
|
if (auto decompressed = cx->runtime()->uncompressedSourceCache.lookup(&ss))
|
||||||
return decompressed;
|
return mozilla::Some(mozilla::Move(*decompressed));
|
||||||
|
|
||||||
const size_t lengthWithNull = ss.length() + 1;
|
const size_t lengthWithNull = ss.length() + 1;
|
||||||
UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
|
UniqueTwoByteChars decompressed(js_pod_malloc<char16_t>(lengthWithNull));
|
||||||
if (!decompressed) {
|
if (!decompressed) {
|
||||||
JS_ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return mozilla::Nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DecompressString((const unsigned char*) ss.compressedData(),
|
if (!DecompressString((const unsigned char*) ss.compressedData(),
|
||||||
|
@ -1960,43 +1889,44 @@ ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holde
|
||||||
reinterpret_cast<unsigned char*>(decompressed.get()),
|
reinterpret_cast<unsigned char*>(decompressed.get()),
|
||||||
lengthWithNull * sizeof(char16_t)))
|
lengthWithNull * sizeof(char16_t)))
|
||||||
{
|
{
|
||||||
JS_ReportOutOfMemory(cx);
|
ReportOutOfMemory(cx);
|
||||||
return nullptr;
|
return mozilla::Nothing();
|
||||||
}
|
}
|
||||||
|
|
||||||
decompressed[ss.length()] = 0;
|
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
|
// Decompressing a huge script is expensive. With lazy parsing and
|
||||||
// relazification, this can happen repeatedly, so conservatively go
|
// relazification, this can happen repeatedly, so conservatively go
|
||||||
// back to storing the data uncompressed to avoid wasting too much
|
// back to storing the data uncompressed to avoid wasting too much
|
||||||
// time yo-yoing back and forth between compressed and uncompressed.
|
// time yo-yoing back and forth between compressed and uncompressed.
|
||||||
const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
|
const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
|
||||||
if (lengthWithNull > HUGE_SCRIPT) {
|
if (lengthWithNull > HUGE_SCRIPT) {
|
||||||
auto& strings = cx->runtime()->sharedImmutableStrings();
|
ss.data = SourceType(Uncompressed(deduped->clone()));
|
||||||
auto str = strings.getOrCreate(mozilla::Move(decompressed), ss.length());
|
return mozilla::Some(mozilla::Move(*deduped));
|
||||||
if (!str) {
|
|
||||||
JS_ReportOutOfMemory(cx);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
ss.data = SourceType(Uncompressed(mozilla::Move(*str)));
|
|
||||||
return ss.uncompressedChars();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnType ret = decompressed.get();
|
if (!cx->runtime()->uncompressedSourceCache.put(&ss, deduped->clone())) {
|
||||||
if (!cx->runtime()->uncompressedSourceCache.put(&ss, Move(decompressed), holder)) {
|
ReportOutOfMemory(cx);
|
||||||
JS_ReportOutOfMemory(cx);
|
return mozilla::Nothing();
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
|
return mozilla::Some(mozilla::Move(*deduped));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnType match(Missing&) {
|
ReturnType match(Missing&) {
|
||||||
MOZ_CRASH("ScriptSource::chars() on ScriptSource with SourceType = Missing");
|
MOZ_CRASH("ScriptSource::sourceText() on ScriptSource with SourceType = Missing");
|
||||||
return nullptr;
|
return mozilla::Nothing();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CharsMatcher cm(cx, *this, holder);
|
SourceTextMatcher cm(cx, *this);
|
||||||
return data.match(cm);
|
return data.match(cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2004,22 +1934,20 @@ JSFlatString*
|
||||||
ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop)
|
ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(start <= stop);
|
MOZ_ASSERT(start <= stop);
|
||||||
UncompressedSourceCache::AutoHoldEntry holder;
|
auto text = sourceText(cx);
|
||||||
const char16_t* chars = this->chars(cx, holder);
|
if (!text)
|
||||||
if (!chars)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return NewStringCopyN<CanGC>(cx, chars + start, stop - start);
|
return NewStringCopyN<CanGC>(cx, text->chars() + start, stop - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSFlatString*
|
JSFlatString*
|
||||||
ScriptSource::substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop)
|
ScriptSource::substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(start <= stop);
|
MOZ_ASSERT(start <= stop);
|
||||||
UncompressedSourceCache::AutoHoldEntry holder;
|
auto text = sourceText(cx);
|
||||||
const char16_t* chars = this->chars(cx, holder);
|
if (!text)
|
||||||
if (!chars)
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return NewStringCopyNDontDeflate<CanGC>(cx, chars + start, stop - start);
|
return NewStringCopyNDontDeflate<CanGC>(cx, text->chars() + start, stop - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_MUST_USE bool
|
MOZ_MUST_USE bool
|
||||||
|
@ -4468,19 +4396,17 @@ LazyScriptHashPolicy::match(JSScript* script, const Lookup& lookup)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UncompressedSourceCache::AutoHoldEntry holder;
|
auto scriptText = script->scriptSource()->sourceText(cx);
|
||||||
|
if (!scriptText)
|
||||||
const char16_t* scriptChars = script->scriptSource()->chars(cx, holder);
|
|
||||||
if (!scriptChars)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const char16_t* lazyChars = lazy->scriptSource()->chars(cx, holder);
|
auto lazyText = lazy->scriptSource()->sourceText(cx);
|
||||||
if (!lazyChars)
|
if (!lazyText)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
size_t begin = script->sourceStart();
|
size_t begin = script->sourceStart();
|
||||||
size_t length = script->sourceEnd() - begin;
|
size_t length = script->sourceEnd() - begin;
|
||||||
return !memcmp(scriptChars + begin, lazyChars + begin, length);
|
return !memcmp(scriptText->chars() + begin, lazyText->chars() + begin, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
#define jsscript_h
|
#define jsscript_h
|
||||||
|
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/MemoryReporting.h"
|
#include "mozilla/MemoryReporting.h"
|
||||||
#include "mozilla/PodOperations.h"
|
#include "mozilla/PodOperations.h"
|
||||||
#include "mozilla/Variant.h"
|
#include "mozilla/Variant.h"
|
||||||
|
@ -576,45 +578,20 @@ class ScriptSource;
|
||||||
|
|
||||||
class UncompressedSourceCache
|
class UncompressedSourceCache
|
||||||
{
|
{
|
||||||
typedef HashMap<ScriptSource*,
|
using Map = HashMap<ScriptSource*,
|
||||||
UniqueTwoByteChars,
|
SharedImmutableTwoByteString,
|
||||||
DefaultHasher<ScriptSource*>,
|
DefaultHasher<ScriptSource*>,
|
||||||
SystemAllocPolicy> Map;
|
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> map_;
|
UniquePtr<Map> map_;
|
||||||
AutoHoldEntry* holder_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UncompressedSourceCache() : holder_(nullptr) {}
|
UncompressedSourceCache() { }
|
||||||
|
|
||||||
const char16_t* lookup(ScriptSource* ss, AutoHoldEntry& asp);
|
|
||||||
bool put(ScriptSource* ss, UniqueTwoByteChars chars, AutoHoldEntry& asp);
|
|
||||||
|
|
||||||
|
MOZ_MUST_USE mozilla::Maybe<SharedImmutableTwoByteString> lookup(ScriptSource* ss);
|
||||||
|
bool put(ScriptSource* ss, SharedImmutableTwoByteString&& chars);
|
||||||
void purge();
|
void purge();
|
||||||
|
|
||||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||||
|
|
||||||
private:
|
|
||||||
void holdEntry(AutoHoldEntry& holder, ScriptSource* ss);
|
|
||||||
void releaseEntry(AutoHoldEntry& holder);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ScriptSource
|
class ScriptSource
|
||||||
|
@ -764,7 +741,11 @@ class ScriptSource
|
||||||
MOZ_ASSERT(hasSourceData());
|
MOZ_ASSERT(hasSourceData());
|
||||||
return argumentsNotIncluded_;
|
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<SharedImmutableTwoByteString> sourceText(JSContext* cx);
|
||||||
|
|
||||||
JSFlatString* substring(JSContext* cx, uint32_t start, uint32_t stop);
|
JSFlatString* substring(JSContext* cx, uint32_t start, uint32_t stop);
|
||||||
JSFlatString* substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop);
|
JSFlatString* substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop);
|
||||||
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче