diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 3e7cbad34a8d..ddf2b1ab831c 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -41,6 +41,7 @@ #include "jscompartment.h" #include "jsfriendapi.h" #include "jswrapper.h" +#include "jsweakmap.h" #include "jsobjinlines.h" @@ -223,6 +224,12 @@ JS_GetCustomIteratorCount(JSContext *cx) return sCustomIteratorCount; } +void +js::TraceWeakMaps(WeakMapTracer *trc) +{ + WeakMapBase::traceAllMappings(trc); +} + JS_FRIEND_API(void) JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index ef12a9ab8990..d36cd8cf589c 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -205,6 +205,30 @@ JS_FRIEND_API(JSBool) obj_defineSetter(JSContext *cx, uintN argc, js::Value *vp) extern JS_FRIEND_API(bool) CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname); +struct WeakMapTracer; + +/* + * Weak map tracer callback, called once for every binding of every + * weak map that was live at the time of the last garbage collection. + * + * m will be NULL if the weak map is not contained in a JS Object. + */ +typedef void +(* WeakMapTraceCallback)(WeakMapTracer *trc, JSObject *m, + void *k, JSGCTraceKind kkind, + void *v, JSGCTraceKind vkind); + +struct WeakMapTracer { + JSContext *context; + WeakMapTraceCallback callback; + + WeakMapTracer(JSContext *cx, WeakMapTraceCallback cb) + : context(cx), callback(cb) {} +}; + +extern JS_FRIEND_API(void) +TraceWeakMaps(WeakMapTracer *trc); + /* * Shadow declarations of JS internal structures, for access by inline access * functions below. Do not use these structures in any other way. When adding diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 37b17010212c..17479af3905e 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2673,6 +2673,9 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind) rt->gcIsNeeded = false; rt->gcTriggerCompartment = NULL; + /* Reset weak map list. */ + rt->gcWeakMapList = NULL; + /* Reset malloc counter. */ rt->resetGCMallocBytes(); @@ -2935,7 +2938,6 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) rt->gcRegenShapes = false; rt->setGCLastBytes(rt->gcBytes, gckind); rt->gcCurrentCompartment = NULL; - rt->gcWeakMapList = NULL; for (CompartmentsIter c(rt); !c.done(); c.next()) c->setGCLastBytes(c->gcBytes, gckind); diff --git a/js/src/jsweakmap.cpp b/js/src/jsweakmap.cpp index ac1faa2b8996..1120cfcedf89 100644 --- a/js/src/jsweakmap.cpp +++ b/js/src/jsweakmap.cpp @@ -78,6 +78,14 @@ WeakMapBase::sweepAll(JSTracer *tracer) m->sweep(tracer); } +void +WeakMapBase::traceAllMappings(WeakMapTracer *tracer) +{ + JSRuntime *rt = tracer->context->runtime; + for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) + m->traceMappings(tracer); +} + } /* namespace js */ typedef WeakMap, HeapValue> ObjectValueMap; @@ -215,7 +223,7 @@ WeakMap_set(JSContext *cx, uintN argc, Value *vp) ObjectValueMap *map = GetObjectMap(obj); if (!map) { - map = cx->new_(cx); + map = cx->new_(cx, obj); if (!map->init()) { cx->delete_(map); goto out_of_memory; diff --git a/js/src/jsweakmap.h b/js/src/jsweakmap.h index 0ddaf0fe8726..0db34782d1d4 100644 --- a/js/src/jsweakmap.h +++ b/js/src/jsweakmap.h @@ -43,6 +43,7 @@ #define jsweakmap_h___ #include "jsapi.h" +#include "jsfriendapi.h" #include "jscntxt.h" #include "jsobj.h" #include "jsgcmark.h" @@ -101,11 +102,15 @@ namespace js { // provides default types for WeakMap's MarkPolicy template parameter. template class DefaultMarkPolicy; +// A policy template holding default tracing algorithms for common type combinations. This +// provides default types for WeakMap's TracePolicy template parameter. +template class DefaultTracePolicy; + // Common base class for all WeakMap specializations. The collector uses this to call // their markIteratively and sweep methods. class WeakMapBase { public: - WeakMapBase() : next(NULL) { } + WeakMapBase(JSObject *memOf) : memberOf(memOf), next(NULL) { } virtual ~WeakMapBase() { } void trace(JSTracer *tracer) { @@ -121,8 +126,8 @@ class WeakMapBase { } 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. + // such as the cycle collector must use their own means for cycle detection. + // So here we do a conservative approximation: pretend all keys are live. if (tracer->eagerlyTraceWeakMaps) nonMarkingTrace(tracer); } @@ -140,12 +145,19 @@ class WeakMapBase { // garbage collection. static void sweepAll(JSTracer *tracer); + // Trace all delayed weak map bindings. Used by the cycle collector. + static void traceAllMappings(WeakMapTracer *tracer); + protected: // Instance member functions called by the above. Instantiations of WeakMap override // these with definitions appropriate for their Key and Value types. virtual void nonMarkingTrace(JSTracer *tracer) = 0; virtual bool markIteratively(JSTracer *tracer) = 0; virtual void sweep(JSTracer *tracer) = 0; + virtual void traceMappings(WeakMapTracer *tracer) = 0; + + // Object that this weak map is part of, if any. + JSObject *memberOf; private: // Link in a list of WeakMaps to mark iteratively and sweep in this garbage @@ -156,7 +168,8 @@ class WeakMapBase { template , class KeyMarkPolicy = DefaultMarkPolicy, - class ValueMarkPolicy = DefaultMarkPolicy > + class ValueMarkPolicy = DefaultMarkPolicy, + class TracePolicy = DefaultTracePolicy > class WeakMap : public HashMap, public WeakMapBase { private: typedef HashMap Base; @@ -165,8 +178,8 @@ class WeakMap : public HashMap, publ public: typedef typename Base::Range Range; - explicit WeakMap(JSRuntime *rt) : Base(rt) { } - explicit WeakMap(JSContext *cx) : Base(cx) { } + explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { } + explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { } // Use with caution, as result can be affected by garbage collection. Range nondeterministicAll() { @@ -191,9 +204,8 @@ class WeakMap : public HashMap, publ if (kp.isMarked(k)) { markedAny |= vp.mark(v); } else if (kp.overrideKeyMarking(k)) { - // We always mark wrapped natives. This will cause leaks, but WeakMap+CC - // integration is currently busted anyways. When WeakMap+CC integration is - // fixed in Bug 668855, XPC wrapped natives should only be marked during + // We always mark wrapped natives. This will cause leaks. Bug 680937 + // will fix this so XPC wrapped natives are only marked during // non-BLACK marking (ie grey marking). kp.mark(k); vp.mark(v); @@ -225,6 +237,13 @@ class WeakMap : public HashMap, publ } #endif } + + // mapObj can be NULL, which means that the map is not part of a JSObject. + void traceMappings(WeakMapTracer *tracer) { + TracePolicy t(tracer); + for (Range r = Base::all(); !r.empty(); r.popFront()) + t.traceMapping(memberOf, r.front().key, r.front().value); + } }; template <> @@ -289,6 +308,42 @@ class DefaultMarkPolicy { bool overrideKeyMarking(const HeapPtrScript &k) { return false; } }; +// Default trace policies + +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtr &k, HeapValue &v) { + if (v.isMarkable()) + tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind()); + } +}; + +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtrObject &k, const HeapPtrObject &v) { + tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT); + } +}; + +template <> +class DefaultTracePolicy { + private: + WeakMapTracer *tracer; + public: + DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { } + void traceMapping(JSObject *m, const HeapPtrScript &k, const HeapPtrObject &v) { + tracer->callback(tracer, m, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT); + } +}; + } extern JSObject *