Bug 739512: Patch 9: move JSScript::debug into a table. r=jorendorff.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 10 Apr 2012 18:03:01 -0700
changeset 94722 cdd78230db65003364a15ee800a9d801ba32ede8
parent 94721 6cb5a238e20d79a6a72ebe201d52120413e87ba2
child 94723 a54c9b8aa9e48ae80921c77b7c3d661497496193
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs739512
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 739512: Patch 9: move JSScript::debug into a table. r=jorendorff.
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -88,17 +88,18 @@ JSCompartment::JSCompartment(JSRuntime *
     emptyTypeObject(NULL),
     gcMallocAndFreeBytes(0),
     gcTriggerMallocAndFreeBytes(0),
     gcMallocBytes(0),
     debugModeBits(rt->debugMode ? DebugFromC : 0),
     mathCache(NULL),
     watchpointMap(NULL),
     scriptCountsMap(NULL),
-    sourceMapMap(NULL)
+    sourceMapMap(NULL),
+    debugScriptMap(NULL)
 {
     PodArrayZero(evalCache);
     setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9);
 }
 
 JSCompartment::~JSCompartment()
 {
     /*
@@ -110,16 +111,17 @@ JSCompartment::~JSCompartment()
 #ifdef JS_METHODJIT
     Foreground::delete_(jaegerCompartment_);
 #endif
 
     Foreground::delete_(mathCache);
     Foreground::delete_(watchpointMap);
     Foreground::delete_(scriptCountsMap);
     Foreground::delete_(sourceMapMap);
+    Foreground::delete_(debugScriptMap);
 
 #ifdef DEBUG
     for (size_t i = 0; i < ArrayLength(evalCache); ++i)
         JS_ASSERT(!evalCache[i]);
 #endif
 }
 
 bool
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -462,16 +462,18 @@ struct JSCompartment
     void sweepBreakpoints(js::FreeOp *fop);
 
   public:
     js::WatchpointMap *watchpointMap;
 
     js::ScriptCountsMap *scriptCountsMap;
 
     js::SourceMapMap *sourceMapMap;
+
+    js::DebugScriptMap *debugScriptMap;
 };
 
 #define JS_PROPERTY_TREE(cx)    ((cx)->compartment->propertyTree)
 
 namespace js {
 static inline MathCache *
 GetMathCache(JSContext *cx)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1516,29 +1516,17 @@ JSScript::finalize(FreeOp *fop)
         types->destroy();
 
 #ifdef JS_METHODJIT
     mjit::ReleaseScriptCode(fop, this);
 #endif
 
     destroyScriptCounts(fop);
     destroySourceMap(fop);
-
-    if (debug) {
-        jsbytecode *end = code + length;
-        for (jsbytecode *pc = code; pc < end; pc++) {
-            if (BreakpointSite *site = getBreakpointSite(pc)) {
-                /* Breakpoints are swept before finalization. */
-                JS_ASSERT(site->firstBreakpoint() == NULL);
-                site->clearTrap(fop, NULL, NULL);
-                JS_ASSERT(getBreakpointSite(pc) == NULL);
-            }
-        }
-        fop->free_(debug);
-    }
+    destroyDebugScript(fop);
 
     JS_POISON(data, 0xdb, computedSizeOfData());
     fop->free_(data);
 }
 
 namespace js {
 
 static const uint32_t GSN_CACHE_THRESHOLD = 100;
@@ -1765,27 +1753,88 @@ js::CloneScript(JSContext *cx, JSScript 
 
     JSScript *newScript;
     if (!XDRScript(&decoder, &newScript, NULL))
         return NULL;
 
     return newScript;
 }
 
+DebugScript *
+JSScript::debugScript()
+{
+    JS_ASSERT(hasDebugScript);
+    DebugScriptMap *map = compartment()->debugScriptMap;
+    JS_ASSERT(map);
+    DebugScriptMap::Ptr p = map->lookup(this);
+    JS_ASSERT(p);
+    return p->value;
+}
+
+DebugScript *
+JSScript::releaseDebugScript()
+{
+    JS_ASSERT(hasDebugScript);
+    DebugScriptMap *map = compartment()->debugScriptMap;
+    JS_ASSERT(map);
+    DebugScriptMap::Ptr p = map->lookup(this);
+    JS_ASSERT(p);
+    DebugScript *debug = p->value;
+    map->remove(p);
+    hasDebugScript = false;
+    return debug;
+}
+
+void
+JSScript::destroyDebugScript(FreeOp *fop)
+{
+    if (hasDebugScript) {
+        jsbytecode *end = code + length;
+        for (jsbytecode *pc = code; pc < end; pc++) {
+            if (BreakpointSite *site = getBreakpointSite(pc)) {
+                /* Breakpoints are swept before finalization. */
+                JS_ASSERT(site->firstBreakpoint() == NULL);
+                site->clearTrap(fop, NULL, NULL);
+                JS_ASSERT(getBreakpointSite(pc) == NULL);
+            }
+        }
+        fop->free_(releaseDebugScript());
+    }
+}
+
 bool
-JSScript::ensureHasDebug(JSContext *cx)
+JSScript::ensureHasDebugScript(JSContext *cx)
 {
-    if (debug)
+    if (hasDebugScript)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length * sizeof(BreakpointSite*);
-    debug = (DebugScript *) cx->calloc_(nbytes);
+    DebugScript *debug = (DebugScript *) cx->calloc_(nbytes);
     if (!debug)
         return false;
 
+    /* Create compartment's debugScriptMap if necessary. */
+    DebugScriptMap *map = compartment()->debugScriptMap;
+    if (!map) {
+        map = cx->new_<DebugScriptMap>();
+        if (!map || !map->init()) {
+            cx->free_(debug);
+            cx->delete_(map);
+            return false;
+        }
+        compartment()->debugScriptMap = map;
+    }
+
+    if (!map->putNew(this, debug)) {
+        cx->free_(debug);
+        cx->delete_(map);
+        return false;
+    }
+    hasDebugScript = true; // safe to set this;  we can't fail after this point
+
     /*
      * Ensure that any Interpret() instances running on this script have
      * interrupts enabled. The interrupts must stay enabled until the
      * debug state is destroyed.
      */
     InterpreterFrames *frames;
     for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
         frames->enableInterruptsIfRunning(this);
@@ -1802,68 +1851,70 @@ JSScript::recompileForStepMode(FreeOp *f
         mjit::ReleaseScriptCode(fop, this);
     }
 #endif
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
 {
-    JS_ASSERT(debug);
+    JS_ASSERT(hasDebugScript);
 
+    DebugScript *debug = debugScript();
     uint32_t prior = debug->stepMode;
     debug->stepMode = newValue;
 
     if (!prior != !newValue) {
         /* Step mode has been enabled or disabled. Alert the methodjit. */
         recompileForStepMode(cx->runtime->defaultFreeOp());
 
-        if (!stepModeEnabled() && !debug->numSites) {
-            cx->free_(debug);
-            debug = NULL;
-        }
+        if (!stepModeEnabled() && !debug->numSites)
+            cx->free_(releaseDebugScript());
     }
 
     return true;
 }
 
 bool
 JSScript::setStepModeFlag(JSContext *cx, bool step)
 {
-    if (!ensureHasDebug(cx))
+    if (!ensureHasDebugScript(cx))
         return false;
 
-    return tryNewStepMode(cx, (debug->stepMode & stepCountMask) | (step ? stepFlagMask : 0));
+    return tryNewStepMode(cx, (debugScript()->stepMode & stepCountMask) |
+                               (step ? stepFlagMask : 0));
 }
 
 bool
 JSScript::changeStepModeCount(JSContext *cx, int delta)
 {
-    if (!ensureHasDebug(cx))
+    if (!ensureHasDebugScript(cx))
         return false;
 
     assertSameCompartment(cx, this);
     JS_ASSERT_IF(delta > 0, cx->compartment->debugMode());
 
+    DebugScript *debug = debugScript();
     uint32_t count = debug->stepMode & stepCountMask;
     JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
     return tryNewStepMode(cx,
                           (debug->stepMode & stepFlagMask) |
                           ((count + delta) & stepCountMask));
 }
 
 BreakpointSite *
 JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
                                     GlobalObject *scriptGlobal)
 {
     JS_ASSERT(size_t(pc - code) < length);
 
-    if (!ensureHasDebug(cx))
+    if (!ensureHasDebugScript(cx))
         return NULL;
 
+    DebugScript *debug = debugScript();
     BreakpointSite *&site = debug->breakpoints[pc - code];
 
     if (!site) {
         site = cx->runtime->new_<BreakpointSite>(this, pc);
         if (!site) {
             js_ReportOutOfMemory(cx);
             return NULL;
         }
@@ -1878,26 +1929,25 @@ JSScript::getOrCreateBreakpointSite(JSCo
     return site;
 }
 
 void
 JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc)
 {
     JS_ASSERT(unsigned(pc - code) < length);
 
+    DebugScript *debug = debugScript();
     BreakpointSite *&site = debug->breakpoints[pc - code];
     JS_ASSERT(site);
 
     fop->delete_(site);
     site = NULL;
 
-    if (--debug->numSites == 0 && !stepModeEnabled()) {
-        fop->free_(debug);
-        debug = NULL;
-    }
+    if (--debug->numSites == 0 && !stepModeEnabled())
+        fop->free_(releaseDebugScript());
 }
 
 void
 JSScript::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *handler)
 {
     if (!hasAnyBreakpointsOrStepMode())
         return;
 
@@ -1965,17 +2015,17 @@ JSScript::markChildren(JSTracer *trc)
 
     bindings.trace(trc);
 
     if (types)
         types->trace(trc);
 
     if (hasAnyBreakpointsOrStepMode()) {
         for (unsigned i = 0; i < length; i++) {
-            BreakpointSite *site = debug->breakpoints[i];
+            BreakpointSite *site = debugScript()->breakpoints[i];
             if (site && site->trapHandler)
                 MarkValue(trc, &site->trapClosure, "trap closure");
         }
     }
 }
 
 void
 JSScript::setArgumentsHasLocalBinding(uint16_t slot)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -342,16 +342,21 @@ class DebugScript
 
     /*
      * Array with all breakpoints installed at opcodes in the script, indexed
      * by the offset of the opcode into the script.
      */
     BreakpointSite  *breakpoints[1];
 };
 
+typedef HashMap<JSScript *,
+                DebugScript *,
+                DefaultHasher<JSScript *>,
+                SystemAllocPolicy> DebugScriptMap;
+
 } /* namespace js */
 
 static const uint32_t JS_SCRIPT_COOKIE = 0xc00cee;
 
 struct JSScript : public js::gc::Cell
 {
   private:
     static const uint32_t stepFlagMask = 0x80000000U;
@@ -444,17 +449,16 @@ struct JSScript : public js::gc::Cell
 
   public:
 #ifdef JS_METHODJIT
     JITScriptHandle jitHandleNormal; // extra JIT info for normal scripts
     JITScriptHandle jitHandleCtor;   // extra JIT info for constructors
 #endif
 
   private:
-    js::DebugScript     *debug;
     js::HeapPtrFunction function_;
 
     size_t          useCount;   /* Number of times the script has been called
                                  * or has had backedges taken. Reset if the
                                  * script's JIT code is forcibly discarded. */
 
     // 32-bit fields.
 
@@ -471,16 +475,21 @@ struct JSScript : public js::gc::Cell
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
     // printing analysis information.
     uint32_t        id_;
   private:
     uint32_t        idpad;
 #endif
 
+#if JS_BITS_PER_WORD == 32
+  private:
+    uint32_t        pad32;
+#endif
+
     // 16-bit fields.
 
   private:
     uint16_t        version;    /* JS version under which script was compiled */
 
   public:
     uint16_t        nfixed;     /* number of slots besides stack operands in
                                    slot array */
@@ -539,16 +548,18 @@ struct JSScript : public js::gc::Cell
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
     bool            callDestroyHook:1;/* need to call destroy hook */
     bool            isGenerator:1;    /* is a generator */
     bool            hasScriptCounts:1;/* script has an entry in
                                          JSCompartment::scriptCountsMap */
     bool            hasSourceMap:1;   /* script has an entry in
                                          JSCompartment::sourceMapMap */
+    bool            hasDebugScript:1; /* script has an entry in
+                                         JSCompartment::debugScriptMap */
 
   private:
     /* See comments below. */
     bool            argsHasLocalBinding_:1;
     bool            needsArgsAnalysis_:1;
     bool            needsArgsObj_:1;
 
     //
@@ -829,26 +840,29 @@ struct JSScript : public js::gc::Cell
      * Recompile with or without single-stepping support, as directed
      * by stepModeEnabled().
      */
     void recompileForStepMode(js::FreeOp *fop);
 
     /* Attempt to change this->stepMode to |newValue|. */
     bool tryNewStepMode(JSContext *cx, uint32_t newValue);
 
-    bool ensureHasDebug(JSContext *cx);
+    bool ensureHasDebugScript(JSContext *cx);
+    js::DebugScript *debugScript();
+    js::DebugScript *releaseDebugScript();
+    void destroyDebugScript(js::FreeOp *fop);
 
   public:
     bool hasBreakpointsAt(jsbytecode *pc) { return !!getBreakpointSite(pc); }
-    bool hasAnyBreakpointsOrStepMode() { return !!debug; }
+    bool hasAnyBreakpointsOrStepMode() { return hasDebugScript; }
 
     js::BreakpointSite *getBreakpointSite(jsbytecode *pc)
     {
         JS_ASSERT(size_t(pc - code) < length);
-        return debug ? debug->breakpoints[pc - code] : NULL;
+        return hasDebugScript ? debugScript()->breakpoints[pc - code] : NULL;
     }
 
     js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
                                                   js::GlobalObject *scriptGlobal);
 
     void destroyBreakpointSite(js::FreeOp *fop, jsbytecode *pc);
 
     void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler);
@@ -867,20 +881,20 @@ struct JSScript : public js::gc::Cell
     /*
      * Increment or decrement the single-step count. If the count is non-zero or
      * the flag (set by setStepModeFlag) is set, then the script is in
      * single-step mode. (JSD uses an on/off-style interface; Debugger uses a
      * count-style interface.)
      */
     bool changeStepModeCount(JSContext *cx, int delta);
 
-    bool stepModeEnabled() { return debug && !!debug->stepMode; }
+    bool stepModeEnabled() { return hasDebugScript && !!debugScript()->stepMode; }
 
 #ifdef DEBUG
-    uint32_t stepModeCount() { return debug ? (debug->stepMode & stepCountMask) : 0; }
+    uint32_t stepModeCount() { return hasDebugScript ? (debugScript()->stepMode & stepCountMask) : 0; }
 #endif
 
     void finalize(js::FreeOp *fop);
 
     static inline void writeBarrierPre(JSScript *script);
     static inline void writeBarrierPost(JSScript *script, void *addr);
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
@@ -888,17 +902,17 @@ struct JSScript : public js::gc::Cell
     static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
                                                    JSPrincipals *originPrincipals) {
         return originPrincipals ? originPrincipals : principals;
     }
 
     void markChildren(JSTracer *trc);
 };
 
-/* If this fails, padding_ can be removed. */
+/* If this fails, add/remove padding within JSScript. */
 JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0);
 
 static JS_INLINE unsigned
 StackDepth(JSScript *script)
 {
     return script->nslots - script->nfixed;
 }