Bug 681104 - Add JSTracer flag to disable visiting WeakMap mappings. r=billm
authorAndrew McCreight <amccreight@mozilla.com>
Sun, 04 Sep 2011 11:25:49 -0700
changeset 77169 c64b8574e28235e03e8f15331f99ff894bb51b76
parent 77168 1db035c0e495182631bd2a2beca272372d46af85
child 77170 abfc871cc026560c62fdfaab92b8f31b1f92aee0
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersbillm
bugs681104
milestone9.0a1
Bug 681104 - Add JSTracer flag to disable visiting WeakMap mappings. r=billm
js/src/jsapi.h
js/src/jsgc.cpp
js/src/jsweakmap.h
js/src/xpconnect/src/nsXPConnect.cpp
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1654,26 +1654,31 @@ JSVAL_TRACE_KIND(jsval v)
  * to ensure the traversal of the full object graph via calling eventually
  * JS_TraceChildren on the passed thing. In this case the callback must be
  * prepared to deal with cycles in the traversal graph.
  *
  * kind argument is one of JSTRACE_OBJECT, JSTRACE_STRING or a tag denoting
  * internal implementation-specific traversal kind. In the latter case the only
  * operations on thing that the callback can do is to call JS_TraceChildren or
  * DEBUG-only JS_PrintTraceThingInfo.
+ *
+ * If eagerlyTraceWeakMaps is true, when we trace a WeakMap visit all
+ * of its mappings.  This should be used in cases where the tracer
+ * wants to use the existing liveness of entries.
  */
 typedef void
 (* JSTraceCallback)(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 struct JSTracer {
     JSContext           *context;
     JSTraceCallback     callback;
     JSTraceNamePrinter  debugPrinter;
     const void          *debugPrintArg;
     size_t              debugPrintIndex;
+    JSBool              eagerlyTraceWeakMaps;
 };
 
 /*
  * The method to call on each reference to a traceable thing stored in a
  * particular JSObject or other runtime structure. With DEBUG defined the
  * caller before calling JS_CallTracer must initialize JSTracer fields
  * describing the reference using the macros below.
  */
@@ -1763,16 +1768,17 @@ JS_CallTracer(JSTracer *trc, void *thing
  */
 # define JS_TRACER_INIT(trc, cx_, callback_)                                  \
     JS_BEGIN_MACRO                                                            \
         (trc)->context = (cx_);                                               \
         (trc)->callback = (callback_);                                        \
         (trc)->debugPrinter = NULL;                                           \
         (trc)->debugPrintArg = NULL;                                          \
         (trc)->debugPrintIndex = (size_t)-1;                                  \
+        (trc)->eagerlyTraceWeakMaps = JS_TRUE;                                \
     JS_END_MACRO
 
 extern JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, JSGCTraceKind kind);
 
 extern JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer *trc);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1550,16 +1550,22 @@ GCMarker::GCMarker(JSContext *cx)
     largeStack(cx->runtime->gcMarkStackLarges, sizeof(cx->runtime->gcMarkStackLarges))
 {
     JS_TRACER_INIT(this, cx, NULL);
     markLaterArenas = 0;
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     conservativeDumpFileName = getenv("JS_DUMP_CONSERVATIVE_GC_ROOTS");
     memset(&conservativeStats, 0, sizeof(conservativeStats));
 #endif
+
+    /*
+     * The GC is recomputing the liveness of WeakMap entries, so we
+     * delay visting entries.
+     */
+    eagerlyTraceWeakMaps = JS_FALSE;
 }
 
 GCMarker::~GCMarker()
 {
 #ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS
     dumpConservativeRoots();
 #endif
 }
@@ -1574,17 +1580,17 @@ GCMarker::delayMarkingChildren(const voi
         return;
     }
     aheader->setNextDelayedMarking(unmarkedArenaStackTop);
     unmarkedArenaStackTop = aheader->getArena();
     markLaterArenas++;
 }
 
 static void
-MarkDelayedChildren(JSTracer *trc, Arena *a)
+MarkDelayedChildren(GCMarker *trc, Arena *a)
 {
     AllocKind allocKind = a->aheader.getAllocKind();
     JSGCTraceKind traceKind = MapAllocToTraceKind(allocKind);
     size_t thingSize = Arena::thingSize(allocKind);
     uintptr_t end = a->thingsEnd();
     for (uintptr_t thing = a->thingsStart(allocKind); thing != end; thing += thingSize) {
         Cell *t = reinterpret_cast<Cell *>(thing);
         if (t->isMarked())
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -139,25 +139,27 @@ class WeakMapBase {
     WeakMapBase() : next(NULL) { }
 
     void trace(JSTracer *tracer) {
         if (IS_GC_MARKING_TRACER(tracer)) {
             // We don't do anything with a WeakMap at trace time. Rather, we wait until as
             // many keys as possible have been marked, and add ourselves to the list of
             // known-live WeakMaps to be scanned in the iterative marking phase, by
             // markAllIteratively.
+            JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
             JSRuntime *rt = tracer->context->runtime;
             next = rt->gcWeakMapList;
             rt->gcWeakMapList = this;
         } else {
             // If we're not actually doing garbage collection, the keys won't be marked
             // nicely as needed by the true ephemeral marking algorithm --- custom tracers
             // must use their own means for cycle detection. So here we do a conservative
             // approximation: pretend all keys are live.
-            nonMarkingTrace(tracer);
+            if (tracer->eagerlyTraceWeakMaps)
+                nonMarkingTrace(tracer);
         }
     }
 
     // Garbage collector entry points.
 
     // Check all weak maps that have been marked as live so far in this garbage
     // collection, and mark the values of all entries that have become strong references
     // to them. Return true if we marked any new values, indicating that we need to make
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -875,16 +875,20 @@ nsXPConnect::Traverse(void *p, nsCycleCo
     // if DEBUG_CC is not defined, else we do want to know about all JS objects
     // to get better graphs and explanations.
     if(!cb.WantAllTraces() && isMarked)
         return NS_OK;
 
     TraversalTracer trc(cb);
 
     JS_TRACER_INIT(&trc, cx, NoteJSChild);
+    // When WeakMaps are properly integrated with the cycle
+    // collector in Bug 668855, don't eagerly trace weak maps when
+    // building the cycle collector graph.
+    // trc.eagerlyTraceWeakMaps = JS_FALSE;
     JS_TraceChildren(&trc, p, traceKind);
 
     if(traceKind != JSTRACE_OBJECT || dontTraverse)
         return NS_OK;
 
     if(clazz == &XPC_WN_Tearoff_JSClass)
     {
         // A tearoff holds a strong reference to its native object