Bumping manifests a=b2g-bump
[gecko.git] / js / src / jsweakmap.h
blobe0795291e6fc420d5b41c67049d652809b07862b
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/. */
7 #ifndef jsweakmap_h
8 #define jsweakmap_h
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
12 #include "jsobj.h"
14 #include "gc/Marking.h"
15 #include "gc/StoreBuffer.h"
16 #include "js/HashTable.h"
18 namespace js {
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.
23 // More precisely:
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.
41 class WeakMapBase {
42 public:
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);
83 protected:
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.
103 WeakMapBase* next;
105 // Whether this object has been traced during garbage collection.
106 bool marked;
109 template <class Key, class Value,
110 class HashPolicy = DefaultHasher<Key> >
111 class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
113 public:
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))
126 return false;
127 next = compartment->gcWeakMapList;
128 compartment->gcWeakMapList = this;
129 marked = JS::IsIncrementalGCInProgress(compartment->runtimeFromMainThread());
130 return true;
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
135 // gc/Marking.cpp
136 Ptr lookup(const Lookup& l) const {
137 Ptr p = Base::lookup(l);
138 if (p)
139 exposeGCThingToActiveJS(p->value());
140 return p;
143 AddPtr lookupForAdd(const Lookup& l) const {
144 AddPtr p = Base::lookupForAdd(l);
145 if (p)
146 exposeGCThingToActiveJS(p->value());
147 return p;
150 Ptr lookupWithDefault(const Key& k, const Value& defaultValue) {
151 Ptr p = Base::lookupWithDefault(k, defaultValue);
152 if (p)
153 exposeGCThingToActiveJS(p->value());
154 return p;
157 private:
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) {
162 if (gc::IsMarked(x))
163 return false;
164 gc::Mark(trc, x, "WeakMap entry value");
165 MOZ_ASSERT(gc::IsMarked(x));
166 return true;
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())
174 entryMoved(e, 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
189 * gray.
191 return delegate && gc::IsObjectMarked(&delegate);
193 return false;
196 bool keyNeedsMark(gc::Cell* cell) {
197 return false;
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()))
207 markedAny = true;
208 if (e.front().key() != key)
209 entryMoved(e, 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)
214 entryMoved(e, key);
215 markedAny = true;
217 key.unsafeSet(nullptr);
219 return markedAny;
222 bool findZoneEdges() {
223 // This is overridden by ObjectValueMap.
224 return true;
227 void sweep() {
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))
232 e.removeFront();
233 else if (k != e.front().key())
234 entryMoved(e, k);
237 * Once we've swept, all remaining edges should stay within the
238 * known-live part of the graph.
240 assertEntriesNotAboutToBeFinalized();
243 void finish() {
244 Base::finish();
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());
252 if (key && 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));
270 protected:
271 void assertEntriesNotAboutToBeFinalized() {
272 #if DEBUG
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());
279 #endif
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. */
307 extern JSObject*
308 InitBareWeakMapCtor(JSContext* cx, js::HandleObject obj);
310 extern bool
311 WeakMap_has(JSContext* cx, unsigned argc, Value* vp);
313 extern bool
314 WeakMap_get(JSContext* cx, unsigned argc, Value* vp);
316 extern bool
317 WeakMap_set(JSContext* cx, unsigned argc, Value* vp);
319 extern bool
320 WeakMap_delete(JSContext* cx, unsigned argc, Value* vp);
322 extern bool
323 WeakMap_clear(JSContext* cx, unsigned argc, Value* vp);
325 } /* namespace js */
327 extern JSObject*
328 js_InitWeakMapClass(JSContext* cx, js::HandleObject obj);
330 #endif /* jsweakmap_h */