Bug 722595 - Add memory reporters for misc things hanging off JS objects. r=bhackett.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 30 Jan 2012 18:12:03 -0800
changeset 89106 fff20ca000849ae216c04b8d069fce0d264539b6
parent 89105 cc977bb3b1d9b90e3291158daa95d0a5ee368b66
child 89107 86f0cf0ca4dccbd7fbe446cbd21d94310d9b82e7
push idunknown
push userunknown
push dateunknown
reviewersbhackett
bugs722595
milestone13.0a1
Bug 722595 - Add memory reporters for misc things hanging off JS objects. r=bhackett.
js/public/MemoryMetrics.h
js/src/MemoryMetrics.cpp
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/vm/ArgumentsObject-inl.h
js/src/vm/ArgumentsObject.h
js/src/vm/RegExpStatics-inl.h
js/src/vm/RegExpStatics.cpp
js/src/vm/RegExpStatics.h
js/xpconnect/src/XPCJSRuntime.cpp
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -97,16 +97,17 @@ struct CompartmentStats
     size_t gcHeapShapesDict;
     size_t gcHeapShapesBase;
     size_t gcHeapScripts;
     size_t gcHeapTypeObjects;
     size_t gcHeapXML;
 
     size_t objectSlots;
     size_t objectElements;
+    size_t objectMisc;
     size_t stringChars;
     size_t shapesExtraTreeTables;
     size_t shapesExtraDictTables;
     size_t shapesExtraTreeShapeKids;
     size_t shapesCompartmentTables;
     size_t scriptData;
 
 #ifdef JS_METHODJIT
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -121,20 +121,22 @@ CellCallback(JSContext *cx, void *vdata,
     case JSTRACE_OBJECT:
     {
         JSObject *obj = static_cast<JSObject *>(thing);
         if (obj->isFunction()) {
             cStats->gcHeapObjectsFunction += thingSize;
         } else {
             cStats->gcHeapObjectsNonFunction += thingSize;
         }
-        size_t slotsSize, elementsSize;
-        obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize, &elementsSize);
+        size_t slotsSize, elementsSize, miscSize;
+        obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize,
+                                 &elementsSize, &miscSize);
         cStats->objectSlots += slotsSize;
         cStats->objectElements += elementsSize;
+        cStats->objectMisc += miscSize;
         break;
     }
     case JSTRACE_STRING:
     {
         JSString *str = static_cast<JSString *>(thing);
         cStats->gcHeapStrings += thingSize;
         cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf);
         break;
@@ -257,17 +259,18 @@ CollectRuntimeStats(JSRuntime *rt, Runti
                       cStats.gcHeapTypeObjects +
                       cStats.gcHeapXML;
 
         rtStats->gcHeapChunkDirtyUnused -= used;
         rtStats->gcHeapArenaUnused += cStats.gcHeapArenaUnused;
         rtStats->totalObjects += cStats.gcHeapObjectsNonFunction +
                                  cStats.gcHeapObjectsFunction +
                                  cStats.objectSlots +
-                                 cStats.objectElements;
+                                 cStats.objectElements +
+                                 cStats.objectMisc;
         rtStats->totalShapes  += cStats.gcHeapShapesTree +
                                  cStats.gcHeapShapesDict +
                                  cStats.gcHeapShapesBase +
                                  cStats.shapesExtraTreeTables +
                                  cStats.shapesExtraDictTables +
                                  cStats.shapesCompartmentTables;
         rtStats->totalScripts += cStats.gcHeapScripts +
                                  cStats.scriptData;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -973,17 +973,17 @@ JS_SetDebugErrorHook(JSRuntime *rt, JSDe
     return JS_TRUE;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(size_t)
 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
 {
-    return obj->computedSizeOfIncludingThis();
+    return obj->computedSizeOfThisSlotsElements();
 }
 
 static size_t
 GetAtomTotalSize(JSContext *cx, JSAtom *atom)
 {
     size_t nbytes;
 
     nbytes = sizeof(JSAtom *) + sizeof(JSDHashEntryStub);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1464,16 +1464,24 @@ fun_trace(JSTracer *trc, JSObject *obj)
 
 static void
 fun_finalize(JSContext *cx, JSObject *obj)
 {
     if (obj->toFunction()->isFlatClosure())
         obj->toFunction()->finalizeUpvars();
 }
 
+size_t
+JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const
+{
+    return (isFlatClosure() && hasFlatClosureUpvars()) ?
+           mallocSizeOf(getFlatClosureUpvars()) :
+           0;
+}
+
 /*
  * Reserve two slots in all function objects for XPConnect.  Note that this
  * does not bloat every instance, only those on which reserved slots are set,
  * and those on which ad-hoc properties are defined.
  */
 JS_FRIEND_DATA(Class) js::FunctionClass = {
     js_Function_str,
     JSCLASS_NEW_RESOLVE |
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -285,16 +285,22 @@ struct JSFunction : public JSObject
     /*
      * Method name imputed from property uniquely assigned to or initialized,
      * where the function does not need to be cloned to carry a scope chain or
      * flattened upvars. This is set on both the original and cloned function.
      */
     inline JSAtom *methodAtom() const;
     inline void setMethodAtom(JSAtom *atom);
 
+    /*
+     * Measures things hanging off this JSFunction that are counted by the
+     * |miscSize| argument in JSObject::sizeOfExcludingThis().
+     */
+    size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
+
   private:
     /* 
      * These member functions are inherited from JSObject, but should never be applied to
      * a value statically known to be a JSFunction.
      */
     inline JSFunction *toFunction() MOZ_DELETE;
     inline const JSFunction *toFunction() const MOZ_DELETE;
 };
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4114,17 +4114,17 @@ JSObject::growSlots(JSContext *cx, uint3
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
      */
     JS_ASSERT(newCount < NELEMENTS_LIMIT);
 
-    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfIncludingThis() : 0;
+    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
     size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value);
 
     /*
      * If we are allocating slots for an object whose type is always created
      * by calling 'new' on a particular script, bump the GC kind for that
      * type to give these objects a larger number of fixed slots when future
      * objects are constructed.
      */
@@ -4183,17 +4183,17 @@ JSObject::shrinkSlots(JSContext *cx, uin
      * Refuse to shrink slots for call objects. This only happens in a very
      * obscure situation (deleting names introduced by a direct 'eval') and
      * allowing the slots pointer to change may require updating pointers in
      * the function's active args/vars information.
      */
     if (isCall())
         return;
 
-    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfIncludingThis() : 0;
+    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
     size_t newSize = oldSize - (oldCount - newCount) * sizeof(Value);
 
     if (newCount == 0) {
         cx->free_(slots);
         slots = NULL;
         if (Probes::objectResizeActive())
             Probes::resizeObject(cx, this, oldSize, newSize);
         return;
@@ -4229,17 +4229,17 @@ JSObject::growElements(JSContext *cx, ui
      * O(N), with a higher constant factor, and we waste less space.
      */
     static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
     static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
 
     uint32_t oldcap = getDenseArrayCapacity();
     JS_ASSERT(oldcap <= newcap);
 
-    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfIncludingThis() : 0;
+    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
 
     uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
                       ? oldcap * 2
                       : oldcap + (oldcap >> 3);
 
     uint32_t actualCapacity = JS_MAX(newcap, nextsize);
     if (actualCapacity >= CAPACITY_CHUNK)
         actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
@@ -4272,30 +4272,30 @@ JSObject::growElements(JSContext *cx, ui
     }
 
     newheader->capacity = actualCapacity;
     elements = newheader->elements();
 
     Debug_SetValueRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
 
     if (Probes::objectResizeActive())
-        Probes::resizeObject(cx, this, oldSize, computedSizeOfIncludingThis());
+        Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
 
     return true;
 }
 
 void
 JSObject::shrinkElements(JSContext *cx, uintN newcap)
 {
     JS_ASSERT(isDenseArray());
 
     uint32_t oldcap = getDenseArrayCapacity();
     JS_ASSERT(newcap <= oldcap);
 
-    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfIncludingThis() : 0;
+    size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
 
     /* Don't shrink elements below the minimum capacity. */
     if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
         return;
 
     newcap = Max(newcap, SLOT_CAPACITY_MIN);
 
     uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER;
@@ -4304,17 +4304,17 @@ JSObject::shrinkElements(JSContext *cx, 
         cx->realloc_(getElementsHeader(), newAllocated * sizeof(Value));
     if (!newheader)
         return;  /* Leave elements at its old size. */
 
     newheader->capacity = newcap;
     elements = newheader->elements();
 
     if (Probes::objectResizeActive())
-        Probes::resizeObject(cx, this, oldSize, computedSizeOfIncludingThis());
+        Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
 }
 
 #ifdef DEBUG
 bool
 JSObject::slotInRange(uintN slot, SentinelAllowed sentinel) const
 {
     size_t capacity = numFixedSlots() + numDynamicSlots();
     if (sentinel == SENTINEL_ALLOWED)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -358,16 +358,17 @@ extern Class GeneratorClass;
 extern Class IteratorClass;
 extern Class JSONClass;
 extern Class MathClass;
 extern Class NumberClass;
 extern Class NormalArgumentsObjectClass;
 extern Class ObjectClass;
 extern Class ProxyClass;
 extern Class RegExpClass;
+extern Class RegExpStaticsClass;
 extern Class SlowArrayClass;
 extern Class StopIterationClass;
 extern Class StringClass;
 extern Class StrictArgumentsObjectClass;
 extern Class WeakMapClass;
 extern Class WithClass;
 extern Class XMLFilterClass;
 
@@ -667,21 +668,21 @@ struct JSObject : js::gc::Cell
      */
     inline bool inDictionaryMode() const;
 
     inline uint32_t propertyCount() const;
 
     inline bool hasPropertyTable() const;
 
     inline size_t sizeOfThis() const;
-    inline size_t computedSizeOfIncludingThis() const;
+    inline size_t computedSizeOfThisSlotsElements() const;
 
-    /* mallocSizeOf can be NULL, in which case we compute the sizes analytically */
     inline void sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
-                                    size_t *slotsSize, size_t *elementsSize) const;
+                                    size_t *slotsSize, size_t *elementsSize,
+                                    size_t *miscSize) const;
 
     inline size_t numFixedSlots() const;
 
     static const uint32_t MAX_FIXED_SLOTS = 16;
 
   private:
     inline js::HeapValue* fixedSlots() const;
 
@@ -1411,16 +1412,17 @@ struct JSObject : js::gc::Cell
     inline bool isGlobal() const;
     inline bool isIterator() const;
     inline bool isNamespace() const;
     inline bool isObject() const;
     inline bool isQName() const;
     inline bool isPrimitive() const;
     inline bool isProxy() const;
     inline bool isRegExp() const;
+    inline bool isRegExpStatics() const;
     inline bool isScope() const;
     inline bool isScript() const;
     inline bool isSlowArray() const;
     inline bool isStopIteration() const;
     inline bool isWeakMap() const;
     inline bool isXML() const;
     inline bool isXMLId() const;
 
@@ -1443,16 +1445,17 @@ struct JSObject : js::gc::Cell
     inline bool isStrictArguments() const;
 
     /* Subtypes of Proxy. */
     inline bool isWrapper() const;
     inline bool isFunctionProxy() const;
     inline bool isCrossCompartmentWrapper() const;
 
     inline js::ArgumentsObject &asArguments();
+    inline const js::ArgumentsObject &asArguments() const;
     inline js::BlockObject &asBlock();
     inline js::BooleanObject &asBoolean();
     inline js::CallObject &asCall();
     inline js::ClonedBlockObject &asClonedBlock();
     inline js::DeclEnvObject &asDeclEnv();
     inline js::GlobalObject &asGlobal();
     inline js::NestedScopeObject &asNestedScope();
     inline js::NormalArgumentsObject &asNormalArguments();
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -61,26 +61,28 @@
 #include "jsstr.h"
 #include "jstypedarray.h"
 #include "jsxml.h"
 #include "jswrapper.h"
 
 #include "gc/Barrier.h"
 #include "js/TemplateLib.h"
 #include "vm/GlobalObject.h"
+#include "vm/RegExpStatics.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsgcinlines.h"
 #include "jsinferinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "gc/Barrier-inl.h"
 #include "vm/String-inl.h"
+#include "vm/RegExpStatics-inl.h"
 
 inline bool
 JSObject::hasPrivate() const
 {
     return getClass()->hasPrivate();
 }
 
 inline void *&
@@ -931,16 +933,17 @@ inline bool JSObject::isGenerator() cons
 inline bool JSObject::isIterator() const { return hasClass(&js::IteratorClass); }
 inline bool JSObject::isNamespace() const { return hasClass(&js::NamespaceClass); }
 inline bool JSObject::isNestedScope() const { return isBlock() || isWith(); }
 inline bool JSObject::isNormalArguments() const { return hasClass(&js::NormalArgumentsObjectClass); }
 inline bool JSObject::isNumber() const { return hasClass(&js::NumberClass); }
 inline bool JSObject::isObject() const { return hasClass(&js::ObjectClass); }
 inline bool JSObject::isPrimitive() const { return isNumber() || isString() || isBoolean(); }
 inline bool JSObject::isRegExp() const { return hasClass(&js::RegExpClass); }
+inline bool JSObject::isRegExpStatics() const { return hasClass(&js::RegExpStaticsClass); }
 inline bool JSObject::isScope() const { return isCall() || isDeclEnv() || isNestedScope(); }
 inline bool JSObject::isStaticBlock() const { return isBlock() && !getProto(); }
 inline bool JSObject::isStopIteration() const { return hasClass(&js::StopIterationClass); }
 inline bool JSObject::isStrictArguments() const { return hasClass(&js::StrictArgumentsObjectClass); }
 inline bool JSObject::isString() const { return hasClass(&js::StringClass); }
 inline bool JSObject::isWeakMap() const { return hasClass(&js::WeakMapClass); }
 inline bool JSObject::isWith() const { return hasClass(&js::WithClass); }
 inline bool JSObject::isXML() const { return hasClass(&js::XMLClass); }
@@ -1203,41 +1206,53 @@ JSObject::hasPropertyTable() const
 
 inline size_t
 JSObject::sizeOfThis() const
 {
     return arenaHeader()->getThingSize();
 }
 
 inline size_t
-JSObject::computedSizeOfIncludingThis() const
+JSObject::computedSizeOfThisSlotsElements() const
 {
-    size_t slotsSize, elementsSize;
-    sizeOfExcludingThis(NULL, &slotsSize, &elementsSize);
-    return sizeOfThis() + slotsSize + elementsSize;
+    size_t n = sizeOfThis();
+
+    if (hasDynamicSlots())
+        n += numDynamicSlots() * sizeof(js::Value);
+
+    if (hasDynamicElements())
+        n += (js::ObjectElements::VALUES_PER_HEADER + getElementsHeader()->capacity) *
+             sizeof(js::Value);
+
+    return n;
 }
 
 inline void
 JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf,
-                              size_t *slotsSize, size_t *elementsSize) const
+                              size_t *slotsSize, size_t *elementsSize,
+                              size_t *miscSize) const
 {
+    *slotsSize = 0;
     if (hasDynamicSlots()) {
-        size_t computedSize = numDynamicSlots() * sizeof(js::Value);
-        *slotsSize = mallocSizeOf ? mallocSizeOf(slots) : computedSize;
-    } else {
-        *slotsSize = 0;
+        *slotsSize += mallocSizeOf(slots);
     }
+
+    *elementsSize = 0;
     if (hasDynamicElements()) {
-        size_t computedSize =
-            (js::ObjectElements::VALUES_PER_HEADER +
-             getElementsHeader()->capacity) * sizeof(js::Value);
-        *elementsSize =
-            mallocSizeOf ? mallocSizeOf(getElementsHeader()) : computedSize;
-    } else {
-        *elementsSize = 0;
+        *elementsSize += mallocSizeOf(getElementsHeader());
+    }
+
+    /* Other things may be measured in the future if DMD indicates it is worthwhile. */
+    *miscSize = 0;
+    if (isFunction()) {
+        *miscSize += toFunction()->sizeOfMisc(mallocSizeOf);
+    } else if (isArguments()) {
+        *miscSize += asArguments().sizeOfMisc(mallocSizeOf);
+    } else if (isRegExpStatics()) {
+        *miscSize += js::SizeOfRegExpStaticsData(this, mallocSizeOf);
     }
 }
 
 inline JSBool
 JSObject::lookupGeneric(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp)
 {
     js::LookupGenericOp op = getOps()->lookupGeneric;
     return (op ? op : js_LookupProperty)(cx, this, id, objp, propp);
--- a/js/src/vm/ArgumentsObject-inl.h
+++ b/js/src/vm/ArgumentsObject-inl.h
@@ -116,16 +116,22 @@ ArgumentsObject::maybeStackFrame() const
 }
 
 inline void
 ArgumentsObject::setStackFrame(StackFrame *frame)
 {
     setFixedSlot(STACK_FRAME_SLOT, PrivateValue(frame));
 }
 
+inline size_t
+ArgumentsObject::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const
+{
+    return mallocSizeOf(data());
+}
+
 inline const js::Value &
 NormalArgumentsObject::callee() const
 {
     return data()->callee;
 }
 
 inline void
 NormalArgumentsObject::clearCallee()
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -208,16 +208,22 @@ class ArgumentsObject : public JSObject
 
     inline const js::Value &element(uint32_t i) const;
     inline const js::Value *elements() const;
     inline void setElement(uint32_t i, const js::Value &v);
 
     /* The stack frame for this ArgumentsObject, if the frame is still active. */
     inline js::StackFrame *maybeStackFrame() const;
     inline void setStackFrame(js::StackFrame *frame);
+
+    /*
+     * Measures things hanging off this ArgumentsObject that are counted by the
+     * |miscSize| argument in JSObject::sizeOfExcludingThis().
+     */
+    inline size_t sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const;
 };
 
 class NormalArgumentsObject : public ArgumentsObject
 {
     friend bool JSObject::isNormalArguments() const;
     friend struct EmptyShape; // for EmptyShape::getEmptyArgumentsShape
     friend ArgumentsObject *
     ArgumentsObject::create(JSContext *cx, uint32_t argc, JSObject &callee);
@@ -241,26 +247,33 @@ class StrictArgumentsObject : public Arg
 };
 
 } // namespace js
 
 js::NormalArgumentsObject &
 JSObject::asNormalArguments()
 {
     JS_ASSERT(isNormalArguments());
-    return *reinterpret_cast<js::NormalArgumentsObject *>(this);
+    return *static_cast<js::NormalArgumentsObject *>(this);
 }
 
 js::StrictArgumentsObject &
 JSObject::asStrictArguments()
 {
     JS_ASSERT(isStrictArguments());
-    return *reinterpret_cast<js::StrictArgumentsObject *>(this);
+    return *static_cast<js::StrictArgumentsObject *>(this);
 }
 
 js::ArgumentsObject &
 JSObject::asArguments()
 {
     JS_ASSERT(isArguments());
-    return *reinterpret_cast<js::ArgumentsObject *>(this);
+    return *static_cast<js::ArgumentsObject *>(this);
+}
+
+const js::ArgumentsObject &
+JSObject::asArguments() const
+{
+    JS_ASSERT(isArguments());
+    return *static_cast<const js::ArgumentsObject *>(this);
 }
 
 #endif /* ArgumentsObject_h___ */
--- a/js/src/vm/RegExpStatics-inl.h
+++ b/js/src/vm/RegExpStatics-inl.h
@@ -49,16 +49,22 @@ namespace js {
 
 inline js::RegExpStatics *
 js::GlobalObject::getRegExpStatics() const
 {
     JSObject &resObj = getSlot(REGEXP_STATICS).toObject();
     return static_cast<RegExpStatics *>(resObj.getPrivate());
 }
 
+inline size_t
+SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf)
+{
+    return mallocSizeOf(obj->getPrivate());
+}
+
 inline
 RegExpStatics::RegExpStatics()
   : bufferLink(NULL),
     copied(false)
 {
     clear();
 }
 
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -64,17 +64,17 @@ static void
 resc_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     JS_ASSERT(pdata);
     RegExpStatics *res = static_cast<RegExpStatics *>(pdata);
     res->mark(trc);
 }
 
-static Class regexp_statics_class = {
+Class js::RegExpStaticsClass = {
     "RegExpStatics",
     JSCLASS_HAS_PRIVATE,
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
@@ -87,17 +87,17 @@ static Class regexp_statics_class = {
     NULL,                    /* xdrObject   */
     NULL,                    /* hasInstance */
     resc_trace
 };
 
 JSObject *
 RegExpStatics::create(JSContext *cx, GlobalObject *parent)
 {
-    JSObject *obj = NewObjectWithGivenProto(cx, &regexp_statics_class, NULL, parent);
+    JSObject *obj = NewObjectWithGivenProto(cx, &RegExpStaticsClass, NULL, parent);
     if (!obj)
         return NULL;
     RegExpStatics *res = cx->new_<RegExpStatics>();
     if (!res)
         return NULL;
     obj->setPrivate(static_cast<void *>(res));
     return obj;
 }
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -256,11 +256,13 @@ class PreserveRegExpStatics
 
     bool init(JSContext *cx) {
         return original->save(cx, &buffer);
     }
 
     inline ~PreserveRegExpStatics();
 };
 
+size_t SizeOfRegExpStaticsData(const JSObject *obj, JSMallocSizeOfFun mallocSizeOf);
+
 } /* namespace js */
 
 #endif
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1486,33 +1486,40 @@ ReportCompartmentStats(const JS::Compart
     ReportGCHeapBytes0(MakeMemoryReporterPath(pathPrefix, cStats,
                                               "gc-heap/xml"),
                        &gcTotal, cStats.gcHeapXML,
                        "Memory on the compartment's garbage-collected JavaScript heap that holds "
                        "E4X XML objects.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, cStats,
-                                              "object-slots"),
+                                              "objects/slots"),
                        nsIMemoryReporter::KIND_HEAP, cStats.objectSlots,
                        "Memory allocated for the compartment's non-fixed object slot arrays, "
                        "which are used to represent object properties.  Some objects also "
                        "contain a fixed number of slots which are stored on the compartment's "
                        "JavaScript heap; those slots are not counted here, but in "
                        "'gc-heap/objects' instead.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, cStats,
-                                              "object-elements"),
+                                              "objects/elements"),
                        nsIMemoryReporter::KIND_HEAP, cStats.objectElements,
                        "Memory allocated for the compartment's object element arrays, "
                        "which are used to represent indexed object properties.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, cStats,
+                                              "objects/misc"),
+                       nsIMemoryReporter::KIND_HEAP, cStats.objectMisc,
+                       "Memory allocated for various small, miscellaneous "
+                       "structures that hang off certain kinds of objects.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, cStats,
                                               "string-chars"),
                        nsIMemoryReporter::KIND_HEAP, cStats.stringChars,
                        "Memory allocated to hold the compartment's string characters.  Sometimes "
                        "more memory is allocated than necessary, to simplify string "
                        "concatenation.  Each string also includes a header which is stored on the "
                        "compartment's JavaScript heap;  that header is not counted here, but in "
                        "'gc-heap/strings' instead.",
                        callback, closure);