Bug 1020012 - Consolidate ScriptSources with the same source, r=luke.

This commit is contained in:
Brian Hackett 2014-06-21 10:39:04 -07:00
Родитель 1ad1ef7739
Коммит 3ba0ec8938
10 изменённых файлов: 171 добавлений и 38 удалений

Просмотреть файл

@ -351,7 +351,8 @@ struct RuntimeSizes
macro(_, _, temporary) \ macro(_, _, temporary) \
macro(_, _, interpreterStack) \ macro(_, _, interpreterStack) \
macro(_, _, mathCache) \ macro(_, _, mathCache) \
macro(_, _, sourceDataCache) \ macro(_, _, uncompressedSourceCache) \
macro(_, _, compressedSourceSet) \
macro(_, _, scriptData) \ macro(_, _, scriptData) \
RuntimeSizes() RuntimeSizes()

Просмотреть файл

@ -1285,7 +1285,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext *cx, HandleFuncti
JS_ASSERT(lazy->source()->hasSourceData()); JS_ASSERT(lazy->source()->hasSourceData());
// Parse and compile the script from source. // Parse and compile the script from source.
SourceDataCache::AutoHoldEntry holder; UncompressedSourceCache::AutoHoldEntry holder;
const jschar *chars = lazy->source()->chars(cx, holder); const jschar *chars = lazy->source()->chars(cx, holder);
if (!chars) if (!chars)
return false; return false;

Просмотреть файл

@ -2949,7 +2949,7 @@ PurgeRuntime(JSRuntime *rt)
rt->scopeCoordinateNameCache.purge(); rt->scopeCoordinateNameCache.purge();
rt->newObjectCache.purge(); rt->newObjectCache.purge();
rt->nativeIterCache.purge(); rt->nativeIterCache.purge();
rt->sourceDataCache.purge(); rt->uncompressedSourceCache.purge();
rt->evalCache.clear(); rt->evalCache.clear();
if (!rt->hasActiveCompilations()) if (!rt->hasActiveCompilations())

Просмотреть файл

@ -1369,13 +1369,13 @@ JSScript::sourceData(JSContext *cx)
return scriptSource()->substring(cx, sourceStart(), sourceEnd()); return scriptSource()->substring(cx, sourceStart(), sourceEnd());
} }
SourceDataCache::AutoHoldEntry::AutoHoldEntry() UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
: cache_(nullptr), source_(nullptr), charsToFree_(nullptr) : cache_(nullptr), source_(nullptr), charsToFree_(nullptr)
{ {
} }
void void
SourceDataCache::AutoHoldEntry::holdEntry(SourceDataCache *cache, ScriptSource *source) UncompressedSourceCache::AutoHoldEntry::holdEntry(UncompressedSourceCache *cache, ScriptSource *source)
{ {
// Initialise the holder for a specific cache and script source. This will // 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. // hold on to the cached source chars in the event that the cache is purged.
@ -1385,7 +1385,7 @@ SourceDataCache::AutoHoldEntry::holdEntry(SourceDataCache *cache, ScriptSource *
} }
void void
SourceDataCache::AutoHoldEntry::deferDelete(const jschar *chars) UncompressedSourceCache::AutoHoldEntry::deferDelete(const jschar *chars)
{ {
// Take ownership of source chars now the cache is being purged. Remove our // Take ownership of source chars now the cache is being purged. Remove our
// reference to the ScriptSource which might soon be destroyed. // reference to the ScriptSource which might soon be destroyed.
@ -1395,7 +1395,7 @@ SourceDataCache::AutoHoldEntry::deferDelete(const jschar *chars)
charsToFree_ = chars; charsToFree_ = chars;
} }
SourceDataCache::AutoHoldEntry::~AutoHoldEntry() UncompressedSourceCache::AutoHoldEntry::~AutoHoldEntry()
{ {
// The holder is going out of scope. If it has taken ownership of cached // The holder is going out of scope. If it has taken ownership of cached
// chars then delete them, otherwise unregister ourself with the cache. // chars then delete them, otherwise unregister ourself with the cache.
@ -1409,7 +1409,7 @@ SourceDataCache::AutoHoldEntry::~AutoHoldEntry()
} }
void void
SourceDataCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss) UncompressedSourceCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss)
{ {
JS_ASSERT(!holder_); JS_ASSERT(!holder_);
holder.holdEntry(this, ss); holder.holdEntry(this, ss);
@ -1417,14 +1417,14 @@ SourceDataCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss)
} }
void void
SourceDataCache::releaseEntry(AutoHoldEntry &holder) UncompressedSourceCache::releaseEntry(AutoHoldEntry &holder)
{ {
JS_ASSERT(holder_ == &holder); JS_ASSERT(holder_ == &holder);
holder_ = nullptr; holder_ = nullptr;
} }
const jschar * const jschar *
SourceDataCache::lookup(ScriptSource *ss, AutoHoldEntry &holder) UncompressedSourceCache::lookup(ScriptSource *ss, AutoHoldEntry &holder)
{ {
JS_ASSERT(!holder_); JS_ASSERT(!holder_);
if (!map_) if (!map_)
@ -1437,7 +1437,7 @@ SourceDataCache::lookup(ScriptSource *ss, AutoHoldEntry &holder)
} }
bool bool
SourceDataCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder) UncompressedSourceCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder)
{ {
JS_ASSERT(!holder_); JS_ASSERT(!holder_);
@ -1461,7 +1461,7 @@ SourceDataCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder)
} }
void void
SourceDataCache::purge() UncompressedSourceCache::purge()
{ {
if (!map_) if (!map_)
return; return;
@ -1481,7 +1481,7 @@ SourceDataCache::purge()
} }
size_t size_t
SourceDataCache::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()) {
@ -1495,7 +1495,7 @@ SourceDataCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
} }
const jschar * const jschar *
ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder) ScriptSource::chars(JSContext *cx, UncompressedSourceCache::AutoHoldEntry &holder)
{ {
switch (dataType) { switch (dataType) {
case DataUncompressed: case DataUncompressed:
@ -1503,7 +1503,7 @@ ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder)
case DataCompressed: { case DataCompressed: {
#ifdef USE_ZLIB #ifdef USE_ZLIB
if (const jschar *decompressed = cx->runtime()->sourceDataCache.lookup(this, holder)) if (const jschar *decompressed = cx->runtime()->uncompressedSourceCache.lookup(this, holder))
return decompressed; return decompressed;
const size_t nbytes = sizeof(jschar) * (length_ + 1); const size_t nbytes = sizeof(jschar) * (length_ + 1);
@ -1520,7 +1520,7 @@ ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder)
decompressed[length_] = 0; decompressed[length_] = 0;
if (!cx->runtime()->sourceDataCache.put(this, decompressed, holder)) { if (!cx->runtime()->uncompressedSourceCache.put(this, decompressed, holder)) {
JS_ReportOutOfMemory(cx); JS_ReportOutOfMemory(cx);
js_free(decompressed); js_free(decompressed);
return nullptr; return nullptr;
@ -1532,6 +1532,9 @@ ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder)
#endif #endif
} }
case DataParent:
return parent()->chars(cx, holder);
default: default:
MOZ_CRASH(); MOZ_CRASH();
} }
@ -1541,7 +1544,7 @@ JSFlatString *
ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop) ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
{ {
JS_ASSERT(start <= stop); JS_ASSERT(start <= stop);
SourceDataCache::AutoHoldEntry holder; UncompressedSourceCache::AutoHoldEntry holder;
const jschar *chars = this->chars(cx, holder); const jschar *chars = this->chars(cx, holder);
if (!chars) if (!chars)
return nullptr; return nullptr;
@ -1561,7 +1564,7 @@ ScriptSource::setSource(const jschar *chars, size_t length, bool ownsChars /* =
} }
void void
ScriptSource::setCompressedSource(void *raw, size_t nbytes) ScriptSource::setCompressedSource(JSRuntime *maybert, void *raw, size_t nbytes, HashNumber hash)
{ {
JS_ASSERT(dataType == DataMissing || dataType == DataUncompressed); JS_ASSERT(dataType == DataMissing || dataType == DataUncompressed);
if (dataType == DataUncompressed && ownsUncompressedChars()) if (dataType == DataUncompressed && ownsUncompressedChars())
@ -1570,6 +1573,33 @@ ScriptSource::setCompressedSource(void *raw, size_t nbytes)
dataType = DataCompressed; dataType = DataCompressed;
data.compressed.raw = raw; data.compressed.raw = raw;
data.compressed.nbytes = nbytes; data.compressed.nbytes = nbytes;
data.compressed.hash = hash;
if (maybert)
updateCompressedSourceSet(maybert);
}
void
ScriptSource::updateCompressedSourceSet(JSRuntime *rt)
{
JS_ASSERT(dataType == DataCompressed);
JS_ASSERT(!inCompressedSourceSet);
CompressedSourceSet::AddPtr p = rt->compressedSourceSet.lookupForAdd(this);
if (p) {
// There is another ScriptSource with the same compressed data.
// Mark that ScriptSource as the parent and use it for all attempts to
// get the source for this ScriptSource.
ScriptSource *parent = *p;
parent->incref();
js_free(compressedData());
dataType = DataParent;
data.parent = parent;
} else {
if (rt->compressedSourceSet.add(p, this))
inCompressedSourceSet = true;
}
} }
bool bool
@ -1688,6 +1718,7 @@ SourceCompressionTask::work()
} }
} }
compressedBytes = comp.outWritten(); compressedBytes = comp.outWritten();
compressedHash = CompressedSourceHasher::computeHash(compressed, compressedBytes);
#else #else
MOZ_CRASH(); MOZ_CRASH();
#endif #endif
@ -1701,6 +1732,8 @@ SourceCompressionTask::work()
ScriptSource::~ScriptSource() ScriptSource::~ScriptSource()
{ {
JS_ASSERT_IF(inCompressedSourceSet, dataType == DataCompressed);
switch (dataType) { switch (dataType) {
case DataUncompressed: case DataUncompressed:
if (ownsUncompressedChars()) if (ownsUncompressedChars())
@ -1708,9 +1741,21 @@ ScriptSource::~ScriptSource()
break; break;
case DataCompressed: case DataCompressed:
// Script source references are only manipulated on the main thread,
// except during off thread parsing when the source may be created
// and used exclusively by the thread doing the parse. In this case the
// ScriptSource might be destroyed while off the main thread, but it
// will not have been added to the runtime's compressed source set
// until the parse is finished on the main thread.
if (inCompressedSourceSet)
TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(this);
js_free(compressedData()); js_free(compressedData());
break; break;
case DataParent:
parent()->decref();
break;
default: default:
break; break;
} }
@ -1753,7 +1798,22 @@ ScriptSource::performXDR(XDRState<mode> *xdr)
if (!xdr->codeUint32(&length_)) if (!xdr->codeUint32(&length_))
return false; return false;
uint32_t compressedLength = (dataType == DataCompressed) ? compressedBytes() : 0; uint32_t compressedLength;
if (mode == XDR_ENCODE) {
switch (dataType) {
case DataUncompressed:
compressedLength = 0;
break;
case DataCompressed:
compressedLength = compressedBytes();
break;
case DataParent:
compressedLength = parent()->compressedBytes();
break;
default:
MOZ_CRASH();
}
}
if (!xdr->codeUint32(&compressedLength)) if (!xdr->codeUint32(&compressedLength))
return false; return false;
@ -1776,11 +1836,25 @@ ScriptSource::performXDR(XDRState<mode> *xdr)
} }
if (compressedLength) if (compressedLength)
setCompressedSource(p, compressedLength); setCompressedSource(xdr->cx()->runtime(), p, compressedLength,
CompressedSourceHasher::computeHash(p, compressedLength));
else else
setSource((const jschar *) p, length_); setSource((const jschar *) p, length_);
} else { } else {
void *p = compressedLength ? compressedData() : (void *) uncompressedChars(); void *p;
switch (dataType) {
case DataUncompressed:
p = (void *) uncompressedChars();
break;
case DataCompressed:
p = compressedData();
break;
case DataParent:
p = parent()->compressedData();
break;
default:
MOZ_CRASH();
}
if (!xdr->codeBytes(p, byteLen)) if (!xdr->codeBytes(p, byteLen))
return false; return false;
} }
@ -3874,7 +3948,7 @@ LazyScriptHashPolicy::match(JSScript *script, const Lookup &lookup)
return false; return false;
} }
SourceDataCache::AutoHoldEntry holder; UncompressedSourceCache::AutoHoldEntry holder;
const jschar *scriptChars = script->scriptSource()->chars(cx, holder); const jschar *scriptChars = script->scriptSource()->chars(cx, holder);
if (!scriptChars) if (!scriptChars)

Просмотреть файл

@ -339,7 +339,7 @@ typedef HashMap<JSScript *,
class ScriptSource; class ScriptSource;
class SourceDataCache class UncompressedSourceCache
{ {
typedef HashMap<ScriptSource *, typedef HashMap<ScriptSource *,
const jschar *, const jschar *,
@ -350,17 +350,17 @@ class SourceDataCache
// Hold an entry in the source data cache and prevent it from being purged on GC. // Hold an entry in the source data cache and prevent it from being purged on GC.
class AutoHoldEntry class AutoHoldEntry
{ {
SourceDataCache *cache_; UncompressedSourceCache *cache_;
ScriptSource *source_; ScriptSource *source_;
const jschar *charsToFree_; const jschar *charsToFree_;
public: public:
explicit AutoHoldEntry(); explicit AutoHoldEntry();
~AutoHoldEntry(); ~AutoHoldEntry();
private: private:
void holdEntry(SourceDataCache *cache, ScriptSource *source); void holdEntry(UncompressedSourceCache *cache, ScriptSource *source);
void deferDelete(const jschar *chars); void deferDelete(const jschar *chars);
ScriptSource *source() const { return source_; } ScriptSource *source() const { return source_; }
friend class SourceDataCache; friend class UncompressedSourceCache;
}; };
private: private:
@ -368,7 +368,7 @@ class SourceDataCache
AutoHoldEntry *holder_; AutoHoldEntry *holder_;
public: public:
SourceDataCache() : map_(nullptr), holder_(nullptr) {} UncompressedSourceCache() : map_(nullptr), holder_(nullptr) {}
const jschar *lookup(ScriptSource *ss, AutoHoldEntry &asp); const jschar *lookup(ScriptSource *ss, AutoHoldEntry &asp);
bool put(ScriptSource *ss, const jschar *chars, AutoHoldEntry &asp); bool put(ScriptSource *ss, const jschar *chars, AutoHoldEntry &asp);
@ -396,7 +396,8 @@ class ScriptSource
enum { enum {
DataMissing, DataMissing,
DataUncompressed, DataUncompressed,
DataCompressed DataCompressed,
DataParent
} dataType; } dataType;
union { union {
@ -408,7 +409,10 @@ class ScriptSource
struct { struct {
void *raw; void *raw;
size_t nbytes; size_t nbytes;
HashNumber hash;
} compressed; } compressed;
ScriptSource *parent;
} data; } data;
uint32_t length_; uint32_t length_;
@ -450,6 +454,9 @@ class ScriptSource
bool argumentsNotIncluded_:1; bool argumentsNotIncluded_:1;
bool hasIntroductionOffset_:1; bool hasIntroductionOffset_:1;
// Whether this is in the runtime's set of compressed ScriptSources.
bool inCompressedSourceSet:1;
public: public:
explicit ScriptSource() explicit ScriptSource()
: refs(0), : refs(0),
@ -464,7 +471,8 @@ class ScriptSource
introductionType_(nullptr), introductionType_(nullptr),
sourceRetrievable_(false), sourceRetrievable_(false),
argumentsNotIncluded_(false), argumentsNotIncluded_(false),
hasIntroductionOffset_(false) hasIntroductionOffset_(false),
inCompressedSourceSet(false)
{ {
} }
~ScriptSource(); ~ScriptSource();
@ -482,6 +490,7 @@ class ScriptSource
void setSourceRetrievable() { sourceRetrievable_ = true; } void setSourceRetrievable() { sourceRetrievable_ = true; }
bool sourceRetrievable() const { return sourceRetrievable_; } bool sourceRetrievable() const { return sourceRetrievable_; }
bool hasSourceData() const { return dataType != DataMissing; } bool hasSourceData() const { return dataType != DataMissing; }
bool hasCompressedSource() const { return dataType == DataCompressed; }
size_t length() const { size_t length() const {
JS_ASSERT(hasSourceData()); JS_ASSERT(hasSourceData());
return length_; return length_;
@ -490,7 +499,7 @@ class ScriptSource
JS_ASSERT(hasSourceData()); JS_ASSERT(hasSourceData());
return argumentsNotIncluded_; return argumentsNotIncluded_;
} }
const jschar *chars(JSContext *cx, SourceDataCache::AutoHoldEntry &asp); const jschar *chars(JSContext *cx, UncompressedSourceCache::AutoHoldEntry &asp);
JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop); JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
JS::ScriptSourceInfo *info) const; JS::ScriptSourceInfo *info) const;
@ -515,8 +524,19 @@ class ScriptSource
return data.compressed.nbytes; return data.compressed.nbytes;
} }
HashNumber compressedHash() const {
JS_ASSERT(dataType == DataCompressed);
return data.compressed.hash;
}
ScriptSource *parent() const {
JS_ASSERT(dataType == DataParent);
return data.parent;
}
void setSource(const jschar *chars, size_t length, bool ownsChars = true); void setSource(const jschar *chars, size_t length, bool ownsChars = true);
void setCompressedSource(void *raw, size_t nbytes); void setCompressedSource(JSRuntime *maybert, void *raw, size_t nbytes, HashNumber hash);
void updateCompressedSourceSet(JSRuntime *rt);
bool ensureOwnsSource(ExclusiveContext *cx); bool ensureOwnsSource(ExclusiveContext *cx);
// XDR handling // XDR handling
@ -581,6 +601,27 @@ class ScriptSourceHolder
} }
}; };
struct CompressedSourceHasher
{
typedef ScriptSource *Lookup;
static HashNumber computeHash(const void *data, size_t nbytes) {
return mozilla::HashBytes(data, nbytes);
}
static HashNumber hash(const ScriptSource *ss) {
return ss->compressedHash();
}
static bool match(const ScriptSource *a, const ScriptSource *b) {
return a->compressedBytes() == b->compressedBytes() &&
a->compressedHash() == b->compressedHash() &&
!memcmp(a->compressedData(), b->compressedData(), a->compressedBytes());
}
};
typedef HashSet<ScriptSource *, CompressedSourceHasher, SystemAllocPolicy> CompressedSourceSet;
class ScriptSourceObject : public JSObject class ScriptSourceObject : public JSObject
{ {
public: public:

Просмотреть файл

@ -702,6 +702,11 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void
// The NewScript hook needs to be called for all compiled scripts. // The NewScript hook needs to be called for all compiled scripts.
CallNewScriptHookForAllScripts(cx, script); CallNewScriptHookForAllScripts(cx, script);
// Update the compressed source table with the result. This is normally
// called by setCompressedSource when compilation occurs on the main thread.
if (script->scriptSource()->hasCompressedSource())
script->scriptSource()->updateCompressedSourceSet(rt);
} }
return script; return script;
@ -990,7 +995,8 @@ SourceCompressionTask::complete()
} }
if (result == Success) { if (result == Success) {
ss->setCompressedSource(compressed, compressedBytes); ss->setCompressedSource(cx->isJSContext() ? cx->asJSContext()->runtime() : nullptr,
compressed, compressedBytes, compressedHash);
// Update memory accounting. // Update memory accounting.
cx->updateMallocCounter(ss->computedSizeOfData()); cx->updateMallocCounter(ss->computedSizeOfData());

Просмотреть файл

@ -482,11 +482,12 @@ struct SourceCompressionTask
} result; } result;
void *compressed; void *compressed;
size_t compressedBytes; size_t compressedBytes;
HashNumber compressedHash;
public: public:
explicit SourceCompressionTask(ExclusiveContext *cx) explicit SourceCompressionTask(ExclusiveContext *cx)
: cx(cx), ss(nullptr), abort_(false), : cx(cx), ss(nullptr), abort_(false),
result(OOM), compressed(nullptr), compressedBytes(0) result(OOM), compressed(nullptr), compressedBytes(0), compressedHash(0)
{ {
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
helperThread = nullptr; helperThread = nullptr;

Просмотреть файл

@ -312,6 +312,9 @@ JSRuntime::init(uint32_t maxbytes)
if (!evalCache.init()) if (!evalCache.init())
return false; return false;
if (!compressedSourceSet.init())
return false;
/* The garbage collector depends on everything before this point being initialized. */ /* The garbage collector depends on everything before this point being initialized. */
gcInitialized = true; gcInitialized = true;
@ -511,7 +514,9 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
rtSizes->mathCache += mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0; rtSizes->mathCache += mathCache_ ? mathCache_->sizeOfIncludingThis(mallocSizeOf) : 0;
rtSizes->sourceDataCache += sourceDataCache.sizeOfExcludingThis(mallocSizeOf); rtSizes->uncompressedSourceCache += uncompressedSourceCache.sizeOfExcludingThis(mallocSizeOf);
rtSizes->compressedSourceSet += compressedSourceSet.sizeOfExcludingThis(mallocSizeOf);
rtSizes->scriptData += scriptDataTable().sizeOfExcludingThis(mallocSizeOf); rtSizes->scriptData += scriptDataTable().sizeOfExcludingThis(mallocSizeOf);
for (ScriptDataTable::Range r = scriptDataTable().all(); !r.empty(); r.popFront()) for (ScriptDataTable::Range r = scriptDataTable().all(); !r.empty(); r.popFront())

Просмотреть файл

@ -1091,10 +1091,11 @@ struct JSRuntime : public JS::shadow::Runtime,
js::ScopeCoordinateNameCache scopeCoordinateNameCache; js::ScopeCoordinateNameCache scopeCoordinateNameCache;
js::NewObjectCache newObjectCache; js::NewObjectCache newObjectCache;
js::NativeIterCache nativeIterCache; js::NativeIterCache nativeIterCache;
js::SourceDataCache sourceDataCache; js::UncompressedSourceCache uncompressedSourceCache;
js::EvalCache evalCache; js::EvalCache evalCache;
js::LazyScriptCache lazyScriptCache; js::LazyScriptCache lazyScriptCache;
js::CompressedSourceSet compressedSourceSet;
js::DateTimeInfo dateTimeInfo; js::DateTimeInfo dateTimeInfo;
// Pool of maps used during parse/emit. This may be modified by threads // Pool of maps used during parse/emit. This may be modified by threads

Просмотреть файл

@ -2374,9 +2374,13 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
KIND_HEAP, rtStats.runtime.mathCache, KIND_HEAP, rtStats.runtime.mathCache,
"The math cache."); "The math cache.");
RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/source-data-cache"), RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/uncompressed-source-cache"),
KIND_HEAP, rtStats.runtime.sourceDataCache, KIND_HEAP, rtStats.runtime.uncompressedSourceCache,
"The source data cache, which holds decompressed script source code."); "The uncompressed source code cache.");
RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/compressed-source-sets"),
KIND_HEAP, rtStats.runtime.compressedSourceSet,
"The table indexing compressed source code in the runtime.");
RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"), RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
KIND_HEAP, rtStats.runtime.scriptData, KIND_HEAP, rtStats.runtime.scriptData,