1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
14 #include "gc/Marking.h"
15 #include "gc/StoreBuffer.h"
16 #include "js/HashTable.h"
20 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
21 // a key is collected, the table entry disappears, dropping its reference to the value.
25 // A WeakMap entry is collected if and only if either the WeakMap or the entry's key
26 // is collected. If an entry is not collected, it remains in the WeakMap and it has a
27 // strong reference to the value.
29 // You must call this table's 'trace' method when the object of which it is a part is
30 // reached by the garbage collection tracer. Once a table is known to be live, the
31 // implementation takes care of the iterative marking needed for weak tables and removing
32 // table entries when collection is complete.
34 // The value for the next pointer for maps not in the map list.
35 static WeakMapBase
* const WeakMapNotInList
= reinterpret_cast<WeakMapBase
*>(1);
37 typedef HashSet
<WeakMapBase
*, DefaultHasher
<WeakMapBase
*>, SystemAllocPolicy
> WeakMapSet
;
39 // Common base class for all WeakMap specializations. The collector uses this to call
40 // their markIteratively and sweep methods.
43 WeakMapBase(JSObject
* memOf
, JSCompartment
* c
);
44 virtual ~WeakMapBase();
46 void trace(JSTracer
* tracer
);
48 // Garbage collector entry points.
50 // Unmark all weak maps in a compartment.
51 static void unmarkCompartment(JSCompartment
* c
);
53 // Mark all the weakmaps in a compartment.
54 static void markAll(JSCompartment
* c
, JSTracer
* tracer
);
56 // Check all weak maps in a compartment that have been marked as live in this garbage
57 // collection, and mark the values of all entries that have become strong references
58 // to them. Return true if we marked any new values, indicating that we need to make
59 // another pass. In other words, mark my marked maps' marked members' mid-collection.
60 static bool markCompartmentIteratively(JSCompartment
* c
, JSTracer
* tracer
);
62 // Add zone edges for weakmaps with key delegates in a different zone.
63 static bool findZoneEdgesForCompartment(JSCompartment
* c
);
65 // Sweep the weak maps in a compartment, removing dead weak maps and removing
66 // entries of live weak maps whose keys are dead.
67 static void sweepCompartment(JSCompartment
* c
);
69 // Trace all delayed weak map bindings. Used by the cycle collector.
70 static void traceAllMappings(WeakMapTracer
* tracer
);
72 bool isInList() { return next
!= WeakMapNotInList
; }
74 // Save information about which weak maps are marked for a compartment.
75 static bool saveCompartmentMarkedWeakMaps(JSCompartment
* c
, WeakMapSet
& markedWeakMaps
);
77 // Restore information about which weak maps are marked for many compartments.
78 static void restoreCompartmentMarkedWeakMaps(WeakMapSet
& markedWeakMaps
);
80 // Remove a weakmap from its compartment's weakmaps list.
81 static void removeWeakMapFromList(WeakMapBase
* weakmap
);
84 // Instance member functions called by the above. Instantiations of WeakMap override
85 // these with definitions appropriate for their Key and Value types.
86 virtual void nonMarkingTraceKeys(JSTracer
* tracer
) = 0;
87 virtual void nonMarkingTraceValues(JSTracer
* tracer
) = 0;
88 virtual bool markIteratively(JSTracer
* tracer
) = 0;
89 virtual bool findZoneEdges() = 0;
90 virtual void sweep() = 0;
91 virtual void traceMappings(WeakMapTracer
* tracer
) = 0;
92 virtual void finish() = 0;
94 // Object that this weak map is part of, if any.
95 RelocatablePtrObject memberOf
;
97 // Compartment that this weak map is part of.
98 JSCompartment
* compartment
;
100 // Link in a list of all WeakMaps in a compartment, headed by
101 // JSCompartment::gcWeakMapList. The last element of the list has nullptr as
102 // its next. Maps not in the list have WeakMapNotInList as their next.
105 // Whether this object has been traced during garbage collection.
109 template <class Key
, class Value
,
110 class HashPolicy
= DefaultHasher
<Key
> >
111 class WeakMap
: public HashMap
<Key
, Value
, HashPolicy
, RuntimeAllocPolicy
>, public WeakMapBase
114 typedef HashMap
<Key
, Value
, HashPolicy
, RuntimeAllocPolicy
> Base
;
115 typedef typename
Base::Enum Enum
;
116 typedef typename
Base::Lookup Lookup
;
117 typedef typename
Base::Range Range
;
118 typedef typename
Base::Ptr Ptr
;
119 typedef typename
Base::AddPtr AddPtr
;
121 explicit WeakMap(JSContext
* cx
, JSObject
* memOf
= nullptr)
122 : Base(cx
->runtime()), WeakMapBase(memOf
, cx
->compartment()) { }
124 bool init(uint32_t len
= 16) {
125 if (!Base::init(len
))
127 next
= compartment
->gcWeakMapList
;
128 compartment
->gcWeakMapList
= this;
129 marked
= JS::IsIncrementalGCInProgress(compartment
->runtimeFromMainThread());
133 // Overwritten to add a read barrier to prevent an incorrectly gray value
134 // from escaping the weak map. See the comment before UnmarkGrayChildren in
136 Ptr
lookup(const Lookup
& l
) const {
137 Ptr p
= Base::lookup(l
);
139 exposeGCThingToActiveJS(p
->value());
143 AddPtr
lookupForAdd(const Lookup
& l
) const {
144 AddPtr p
= Base::lookupForAdd(l
);
146 exposeGCThingToActiveJS(p
->value());
150 Ptr
lookupWithDefault(const Key
& k
, const Value
& defaultValue
) {
151 Ptr p
= Base::lookupWithDefault(k
, defaultValue
);
153 exposeGCThingToActiveJS(p
->value());
158 void exposeGCThingToActiveJS(const JS::Value
& v
) const { JS::ExposeValueToActiveJS(v
); }
159 void exposeGCThingToActiveJS(JSObject
* obj
) const { JS::ExposeObjectToActiveJS(obj
); }
161 bool markValue(JSTracer
* trc
, Value
* x
) {
164 gc::Mark(trc
, x
, "WeakMap entry value");
165 MOZ_ASSERT(gc::IsMarked(x
));
169 void nonMarkingTraceKeys(JSTracer
* trc
) {
170 for (Enum
e(*this); !e
.empty(); e
.popFront()) {
171 Key
key(e
.front().key());
172 gc::Mark(trc
, &key
, "WeakMap entry key");
173 if (key
!= e
.front().key())
178 void nonMarkingTraceValues(JSTracer
* trc
) {
179 for (Range r
= Base::all(); !r
.empty(); r
.popFront())
180 gc::Mark(trc
, &r
.front().value(), "WeakMap entry value");
183 bool keyNeedsMark(JSObject
* key
) {
184 if (JSWeakmapKeyDelegateOp op
= key
->getClass()->ext
.weakmapKeyDelegateOp
) {
185 JSObject
* delegate
= op(key
);
187 * Check if the delegate is marked with any color to properly handle
188 * gray marking when the key's delegate is black and the map is
191 return delegate
&& gc::IsObjectMarked(&delegate
);
196 bool keyNeedsMark(gc::Cell
* cell
) {
200 bool markIteratively(JSTracer
* trc
) {
201 bool markedAny
= false;
202 for (Enum
e(*this); !e
.empty(); e
.popFront()) {
203 /* If the entry is live, ensure its key and value are marked. */
204 Key
key(e
.front().key());
205 if (gc::IsMarked(const_cast<Key
*>(&key
))) {
206 if (markValue(trc
, &e
.front().value()))
208 if (e
.front().key() != key
)
210 } else if (keyNeedsMark(key
)) {
211 gc::Mark(trc
, &e
.front().value(), "WeakMap entry value");
212 gc::Mark(trc
, &key
, "proxy-preserved WeakMap entry key");
213 if (e
.front().key() != key
)
217 key
.unsafeSet(nullptr);
222 bool findZoneEdges() {
223 // This is overridden by ObjectValueMap.
228 /* Remove all entries whose keys remain unmarked. */
229 for (Enum
e(*this); !e
.empty(); e
.popFront()) {
230 Key
k(e
.front().key());
231 if (gc::IsAboutToBeFinalized(&k
))
233 else if (k
!= e
.front().key())
237 * Once we've swept, all remaining edges should stay within the
238 * known-live part of the graph.
240 assertEntriesNotAboutToBeFinalized();
247 /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
248 void traceMappings(WeakMapTracer
* tracer
) {
249 for (Range r
= Base::all(); !r
.empty(); r
.popFront()) {
250 gc::Cell
* key
= gc::ToMarkable(r
.front().key());
251 gc::Cell
* value
= gc::ToMarkable(r
.front().value());
253 tracer
->callback(tracer
, memberOf
,
254 JS::GCCellPtr(r
.front().key()),
255 JS::GCCellPtr(r
.front().value()));
260 /* Rekey an entry when moved, ensuring we do not trigger barriers. */
261 void entryMoved(Enum
& eArg
, const Key
& k
) {
262 typedef typename HashMap
<typename Unbarriered
<Key
>::type
,
263 typename Unbarriered
<Value
>::type
,
264 typename Unbarriered
<HashPolicy
>::type
,
265 RuntimeAllocPolicy
>::Enum UnbarrieredEnum
;
266 UnbarrieredEnum
& e
= reinterpret_cast<UnbarrieredEnum
&>(eArg
);
267 e
.rekeyFront(reinterpret_cast<const typename Unbarriered
<Key
>::type
&>(k
));
271 void assertEntriesNotAboutToBeFinalized() {
273 for (Range r
= Base::all(); !r
.empty(); r
.popFront()) {
274 Key
k(r
.front().key());
275 MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k
));
276 MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r
.front().value()));
277 MOZ_ASSERT(k
== r
.front().key());
284 * At times, you will need to ignore barriers when accessing WeakMap entries.
285 * Localize the templatized casting craziness here.
287 template <class Key
, class Value
>
288 static inline gc::HashKeyRef
<HashMap
<Key
, Value
, DefaultHasher
<Key
>, RuntimeAllocPolicy
>, Key
>
289 UnbarrieredRef(WeakMap
<PreBarriered
<Key
>, RelocatablePtr
<Value
>>* map
, Key key
)
292 * Some compilers complain about instantiating the WeakMap class for
293 * unbarriered type arguments, so we cast to a HashMap instead. Because of
294 * WeakMap's multiple inheritance, we need to do this in two stages, first
295 * to the HashMap base class and then to the unbarriered version.
298 typedef typename WeakMap
<PreBarriered
<Key
>, RelocatablePtr
<Value
>>::Base BaseMap
;
299 auto baseMap
= static_cast<BaseMap
*>(map
);
300 typedef HashMap
<Key
, Value
, DefaultHasher
<Key
>, RuntimeAllocPolicy
> UnbarrieredMap
;
301 typedef gc::HashKeyRef
<UnbarrieredMap
, Key
> UnbarrieredKeyRef
;
302 return UnbarrieredKeyRef(reinterpret_cast<UnbarrieredMap
*>(baseMap
), key
);
305 /* WeakMap methods exposed so they can be installed in the self-hosting global. */
308 InitBareWeakMapCtor(JSContext
* cx
, js::HandleObject obj
);
311 WeakMap_has(JSContext
* cx
, unsigned argc
, Value
* vp
);
314 WeakMap_get(JSContext
* cx
, unsigned argc
, Value
* vp
);
317 WeakMap_set(JSContext
* cx
, unsigned argc
, Value
* vp
);
320 WeakMap_delete(JSContext
* cx
, unsigned argc
, Value
* vp
);
323 WeakMap_clear(JSContext
* cx
, unsigned argc
, Value
* vp
);
328 js_InitWeakMapClass(JSContext
* cx
, js::HandleObject obj
);
330 #endif /* jsweakmap_h */