Bug 693527 - tell the cycle collector about watchpoints. r=jorendorff
authorAndrew McCreight <amccreight@mozilla.com>
Fri, 27 Jan 2012 17:13:21 -0800
changeset 86853 9801e9475d3b6d12f992f10530d78a5bc84b5e48
parent 86852 9b81bf7d458c7c36b76d177f9c958e09139bf1b0
child 86854 602d30678cacd9ecdff8b7e04f69078badcbf723
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs693527
milestone12.0a1
Bug 693527 - tell the cycle collector about watchpoints. r=jorendorff
js/src/jsfriendapi.cpp
js/src/jswatchpoint.cpp
js/src/jswatchpoint.h
js/xpconnect/tests/chrome/Makefile.in
js/xpconnect/tests/chrome/test_watchpoints.xul
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -37,16 +37,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 #include "jsweakmap.h"
+#include "jswatchpoint.h"
 
 #include "mozilla/GuardObjects.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
@@ -369,16 +370,17 @@ JS_GetCustomIteratorCount(JSContext *cx)
 {
     return sCustomIteratorCount;
 }
 
 void
 js::TraceWeakMaps(WeakMapTracer *trc)
 {
     WeakMapBase::traceAllMappings(trc);
+    WatchpointMap::traceAll(trc);
 }
 
 JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback)
 {
     rt->telemetryCallback = callback;
 }
 
--- a/js/src/jswatchpoint.cpp
+++ b/js/src/jswatchpoint.cpp
@@ -256,8 +256,29 @@ WatchpointMap::sweep(JSContext *cx)
     for (Map::Enum r(map); !r.empty(); r.popFront()) {
         Map::Entry &e = r.front();
         if (IsAboutToBeFinalized(cx, e.key.object)) {
             JS_ASSERT(!e.value.held);
             r.removeFront();
         }
     }
 }
+
+void
+WatchpointMap::traceAll(WeakMapTracer *trc)
+{
+    JSRuntime *rt = trc->context->runtime;
+    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
+        if (WatchpointMap *wpmap = (*c)->watchpointMap)
+            wpmap->trace(trc);
+    }
+}
+
+void
+WatchpointMap::trace(WeakMapTracer *trc)
+{
+    for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
+        Map::Entry &e = r.front();
+        trc->callback(trc, NULL,
+                      e.key.object.get(), JSTRACE_OBJECT,
+                      e.value.closure.get(), JSTRACE_OBJECT);
+    }
+}
--- a/js/src/jswatchpoint.h
+++ b/js/src/jswatchpoint.h
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jswatchpoint_h___
 #define jswatchpoint_h___
 
 #include "jsalloc.h"
 #include "jsprvtd.h"
 #include "jsapi.h"
+#include "jsfriendapi.h"
 
 #include "gc/Barrier.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 struct WatchKey {
     WatchKey() {}
@@ -88,15 +89,18 @@ class WatchpointMap {
     bool triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
     static bool markAllIteratively(JSTracer *trc);
     bool markIteratively(JSTracer *trc);
     void markAll(JSTracer *trc);
     static void sweepAll(JSContext *cx);
     void sweep(JSContext *cx);
 
+    static void traceAll(WeakMapTracer *trc);
+    void trace(WeakMapTracer *trc);
+
   private:
     Map map;
 };
 
 }
 
 #endif /* jswatchpoint_h___ */
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -70,16 +70,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug679861.xul \
 		test_APIExposer.xul \
 		test_bug664689.xul \
 		test_precisegc.xul \
 		test_nodelists.xul \
 		test_getweakmapkeys.xul \
 		test_weakmaps.xul \
 		test_bug706301.xul \
+		test_watchpoints.xul \
 		$(NULL)
 
 # Disabled until this test gets updated to test the new proxy based
 # wrappers.
 #		test_wrappers-2.xul \
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_watchpoints.xul
@@ -0,0 +1,75 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=693527
+-->
+<window title="Mozilla Bug "
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id="
+     target="_blank">Mozilla Bug 693527</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 693527 **/
+
+  let Cu = Components.utils;
+  let Ci = Components.interfaces;
+
+  /* Create a weak reference, with a single-element weak map. */
+  let make_weak_ref = function (obj) {
+    let m = new WeakMap;
+    m.set(obj, {});
+    return m;
+  };
+
+  /* Check to see if a weak reference is dead. */
+  let weak_ref_dead = function (r) {
+    return Cu.nondeterministicGetWeakMapKeys(r).length == 0;
+  }
+
+
+  let make_cycle = function () {
+    var p = document.createElement("p");
+    p.children.x = p;
+    var f = function() { };
+    p.watch("y", f);
+    var d = document.createElement("div");
+    d.appendChild(p);
+    f.loop = d;
+    f.bar = {}; // observing f directly makes the leak go away even without the CC somehow
+    return make_weak_ref(f.bar);
+  };
+
+  var cycle_ref = make_cycle();
+
+
+  /* set up for running precise GC/CC then checking the results */
+
+  SimpleTest.waitForExplicitFinish();
+
+  Cu.schedulePreciseGC(function () {
+    window.QueryInterface(Ci.nsIInterfaceRequestor)
+         .getInterface(Ci.nsIDOMWindowUtils)
+         .cycleCollect();
+    window.QueryInterface(Ci.nsIInterfaceRequestor)
+         .getInterface(Ci.nsIDOMWindowUtils)
+         .garbageCollect();
+    window.QueryInterface(Ci.nsIInterfaceRequestor)
+         .getInterface(Ci.nsIDOMWindowUtils)
+         .garbageCollect();
+  
+    ok(weak_ref_dead(cycle_ref), "Garbage gray watchpoint cycle should be collected.");
+
+    SimpleTest.finish();
+  });
+
+  ]]>
+  </script>
+</window>