Bug 1155455 - Relax assertion to take account of breakpoints added during incremental sweeping r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Tue, 21 Apr 2015 16:08:36 +0100
changeset 240242 dbe8f15eefa17925d7f0098bfb7c6c0ed7120bc6
parent 240241 ed00b18082845f28908c006c7b611a1fe2f793c4
child 240243 295890a566bdc852904f71755e4a0d1282df6500
push id28627
push userkwierso@gmail.com
push dateTue, 21 Apr 2015 22:25:44 +0000
treeherdermozilla-central@fcbc5a4f5bb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1155455
milestone40.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 1155455 - Relax assertion to take account of breakpoints added during incremental sweeping r=terrence
js/src/gc/Zone.cpp
js/src/jit-test/tests/gc/bug-1155455.js
js/src/vm/Debugger.h
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -128,33 +128,40 @@ Zone::sweepBreakpoints(FreeOp* fop)
     /*
      * Sweep all compartments in a zone at the same time, since there is no way
      * to iterate over the scripts belonging to a single compartment in a zone.
      */
 
     MOZ_ASSERT(isGCSweepingOrCompacting());
     for (ZoneCellIterUnderGC i(this, AllocKind::SCRIPT); !i.done(); i.next()) {
         JSScript* script = i.get<JSScript>();
-        MOZ_ASSERT_IF(isGCSweeping(), script->zone()->isGCSweeping());
         if (!script->hasAnyBreakpointsOrStepMode())
             continue;
 
         bool scriptGone = IsAboutToBeFinalizedUnbarriered(&script);
         MOZ_ASSERT(script == i.get<JSScript>());
         for (unsigned i = 0; i < script->length(); i++) {
             BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
             if (!site)
                 continue;
 
             Breakpoint* nextbp;
             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
                 HeapPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
+
+                // If we are sweeping, then we expect the script and the
+                // debugger object to be swept in the same zone group, except if
+                // the breakpoint was added after we computed the zone
+                // groups. In this case both script and debugger object must be
+                // live.
                 MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
-                              dbgobj->zone()->isGCSweeping());
+                              dbgobj->zone()->isGCSweeping() ||
+                              (!scriptGone && dbgobj->asTenured().isMarked()));
+
                 bool dying = scriptGone || IsAboutToBeFinalized(&dbgobj);
                 MOZ_ASSERT_IF(!dying, !IsAboutToBeFinalized(&bp->getHandlerRef()));
                 if (dying)
                     bp->destroy(fop);
             }
         }
     }
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1155455.js
@@ -0,0 +1,17 @@
+// |jit-test| error: TypeError
+if (!("gczeal" in this))
+    quit();
+var g = newGlobal();
+gczeal(10, 2)
+var dbg = Debugger(g);
+dbg.onDebuggerStatement = function (frame1) {
+    function hit(frame2)
+      hit[0] = "mutated";
+    var s = frame1.script;
+    var offs = s.getLineOffsets(g.line0 + 2);
+    for (var i = 0; i < offs.length; i++)
+      s.setBreakpoint(offs[i], {hit: hit});
+    return;
+};
+var lfGlobal = newGlobal();
+g.eval("var line0 = Error().lineNumber;\n debugger;\nx = 1;\n");
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -875,17 +875,17 @@ class BreakpointSite {
  *      ==> retain the breakpoint and the handler object is live
  *
  * Debugger::markAllIteratively implements these two rules. It uses
  * Debugger::hasAnyLiveHooks to check for rule 1.
  *
  * Nothing else causes a breakpoint to be retained, so if its script or
  * debugger is collected, the breakpoint is destroyed during GC sweep phase,
  * even if the debugger compartment isn't being GC'd. This is implemented in
- * JSCompartment::sweepBreakpoints.
+ * Zone::sweepBreakpoints.
  */
 class Breakpoint {
     friend struct ::JSCompartment;
     friend class Debugger;
 
   public:
     Debugger * const debugger;
     BreakpointSite * const site;