js/src/jsweakmap.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef jsweakmap_h
michael@0 8 #define jsweakmap_h
michael@0 9
michael@0 10 #include "jscompartment.h"
michael@0 11 #include "jsfriendapi.h"
michael@0 12 #include "jsobj.h"
michael@0 13
michael@0 14 #include "gc/Marking.h"
michael@0 15 #include "js/HashTable.h"
michael@0 16
michael@0 17 namespace js {
michael@0 18
michael@0 19 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
michael@0 20 // a key is collected, the table entry disappears, dropping its reference to the value.
michael@0 21 //
michael@0 22 // More precisely:
michael@0 23 //
michael@0 24 // A WeakMap entry is collected if and only if either the WeakMap or the entry's key
michael@0 25 // is collected. If an entry is not collected, it remains in the WeakMap and it has a
michael@0 26 // strong reference to the value.
michael@0 27 //
michael@0 28 // You must call this table's 'trace' method when the object of which it is a part is
michael@0 29 // reached by the garbage collection tracer. Once a table is known to be live, the
michael@0 30 // implementation takes care of the iterative marking needed for weak tables and removing
michael@0 31 // table entries when collection is complete.
michael@0 32
michael@0 33 // The value for the next pointer for maps not in the map list.
michael@0 34 static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
michael@0 35
michael@0 36 typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
michael@0 37
michael@0 38 // Common base class for all WeakMap specializations. The collector uses this to call
michael@0 39 // their markIteratively and sweep methods.
michael@0 40 class WeakMapBase {
michael@0 41 public:
michael@0 42 WeakMapBase(JSObject *memOf, JSCompartment *c);
michael@0 43 virtual ~WeakMapBase();
michael@0 44
michael@0 45 void trace(JSTracer *tracer) {
michael@0 46 if (IS_GC_MARKING_TRACER(tracer)) {
michael@0 47 // We don't do anything with a WeakMap at trace time. Rather, we wait until as
michael@0 48 // many keys as possible have been marked, and add ourselves to the list of
michael@0 49 // known-live WeakMaps to be scanned in the iterative marking phase, by
michael@0 50 // markAllIteratively.
michael@0 51 JS_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
michael@0 52
michael@0 53 // Add ourselves to the list if we are not already in the list. We can already
michael@0 54 // be in the list if the weak map is marked more than once due delayed marking.
michael@0 55 if (next == WeakMapNotInList) {
michael@0 56 next = compartment->gcWeakMapList;
michael@0 57 compartment->gcWeakMapList = this;
michael@0 58 }
michael@0 59 } else {
michael@0 60 // If we're not actually doing garbage collection, the keys won't be marked
michael@0 61 // nicely as needed by the true ephemeral marking algorithm --- custom tracers
michael@0 62 // such as the cycle collector must use their own means for cycle detection.
michael@0 63 // So here we do a conservative approximation: pretend all keys are live.
michael@0 64 if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
michael@0 65 return;
michael@0 66
michael@0 67 nonMarkingTraceValues(tracer);
michael@0 68 if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
michael@0 69 nonMarkingTraceKeys(tracer);
michael@0 70 }
michael@0 71 }
michael@0 72
michael@0 73 // Garbage collector entry points.
michael@0 74
michael@0 75 // Check all weak maps in a compartment that have been marked as live in this garbage
michael@0 76 // collection, and mark the values of all entries that have become strong references
michael@0 77 // to them. Return true if we marked any new values, indicating that we need to make
michael@0 78 // another pass. In other words, mark my marked maps' marked members' mid-collection.
michael@0 79 static bool markCompartmentIteratively(JSCompartment *c, JSTracer *tracer);
michael@0 80
michael@0 81 // Remove entries whose keys are dead from all weak maps in a compartment marked as
michael@0 82 // live in this garbage collection.
michael@0 83 static void sweepCompartment(JSCompartment *c);
michael@0 84
michael@0 85 // Trace all delayed weak map bindings. Used by the cycle collector.
michael@0 86 static void traceAllMappings(WeakMapTracer *tracer);
michael@0 87
michael@0 88 bool isInList() { return next != WeakMapNotInList; }
michael@0 89 void check() { JS_ASSERT(!isInList()); }
michael@0 90
michael@0 91 // Remove everything from the weak map list for a compartment.
michael@0 92 static void resetCompartmentWeakMapList(JSCompartment *c);
michael@0 93
michael@0 94 // Save the live weak map list for a compartment, appending the data to a vector.
michael@0 95 static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
michael@0 96
michael@0 97 // Restore live weak map lists for multiple compartments from a vector.
michael@0 98 static void restoreCompartmentWeakMapLists(WeakMapVector &vector);
michael@0 99
michael@0 100 // Remove a weakmap from the live weakmaps list
michael@0 101 static void removeWeakMapFromList(WeakMapBase *weakmap);
michael@0 102
michael@0 103 protected:
michael@0 104 // Instance member functions called by the above. Instantiations of WeakMap override
michael@0 105 // these with definitions appropriate for their Key and Value types.
michael@0 106 virtual void nonMarkingTraceKeys(JSTracer *tracer) = 0;
michael@0 107 virtual void nonMarkingTraceValues(JSTracer *tracer) = 0;
michael@0 108 virtual bool markIteratively(JSTracer *tracer) = 0;
michael@0 109 virtual void sweep() = 0;
michael@0 110 virtual void traceMappings(WeakMapTracer *tracer) = 0;
michael@0 111
michael@0 112 // Object that this weak map is part of, if any.
michael@0 113 JSObject *memberOf;
michael@0 114
michael@0 115 // Compartment that this weak map is part of.
michael@0 116 JSCompartment *compartment;
michael@0 117
michael@0 118 private:
michael@0 119 // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
michael@0 120 // collection, headed by JSCompartment::gcWeakMapList. The last element of
michael@0 121 // the list has nullptr as its next. Maps not in the list have
michael@0 122 // WeakMapNotInList as their next. We must distinguish these cases to
michael@0 123 // avoid creating infinite lists when a weak map gets traced twice due to
michael@0 124 // delayed marking.
michael@0 125 WeakMapBase *next;
michael@0 126 };
michael@0 127
michael@0 128 template <class Key, class Value,
michael@0 129 class HashPolicy = DefaultHasher<Key> >
michael@0 130 class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
michael@0 131 {
michael@0 132 public:
michael@0 133 typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
michael@0 134 typedef typename Base::Enum Enum;
michael@0 135 typedef typename Base::Lookup Lookup;
michael@0 136 typedef typename Base::Range Range;
michael@0 137
michael@0 138 explicit WeakMap(JSContext *cx, JSObject *memOf = nullptr)
michael@0 139 : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()) { }
michael@0 140
michael@0 141 private:
michael@0 142 bool markValue(JSTracer *trc, Value *x) {
michael@0 143 if (gc::IsMarked(x))
michael@0 144 return false;
michael@0 145 gc::Mark(trc, x, "WeakMap entry value");
michael@0 146 JS_ASSERT(gc::IsMarked(x));
michael@0 147 return true;
michael@0 148 }
michael@0 149
michael@0 150 void nonMarkingTraceKeys(JSTracer *trc) {
michael@0 151 for (Enum e(*this); !e.empty(); e.popFront()) {
michael@0 152 Key key(e.front().key());
michael@0 153 gc::Mark(trc, &key, "WeakMap entry key");
michael@0 154 if (key != e.front().key())
michael@0 155 entryMoved(e, key);
michael@0 156 }
michael@0 157 }
michael@0 158
michael@0 159 void nonMarkingTraceValues(JSTracer *trc) {
michael@0 160 for (Range r = Base::all(); !r.empty(); r.popFront())
michael@0 161 gc::Mark(trc, &r.front().value(), "WeakMap entry value");
michael@0 162 }
michael@0 163
michael@0 164 bool keyNeedsMark(JSObject *key) {
michael@0 165 if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
michael@0 166 JSObject *delegate = op(key);
michael@0 167 /*
michael@0 168 * Check if the delegate is marked with any color to properly handle
michael@0 169 * gray marking when the key's delegate is black and the map is
michael@0 170 * gray.
michael@0 171 */
michael@0 172 return delegate && gc::IsObjectMarked(&delegate);
michael@0 173 }
michael@0 174 return false;
michael@0 175 }
michael@0 176
michael@0 177 bool keyNeedsMark(gc::Cell *cell) {
michael@0 178 return false;
michael@0 179 }
michael@0 180
michael@0 181 bool markIteratively(JSTracer *trc) {
michael@0 182 bool markedAny = false;
michael@0 183 for (Enum e(*this); !e.empty(); e.popFront()) {
michael@0 184 /* If the entry is live, ensure its key and value are marked. */
michael@0 185 Key key(e.front().key());
michael@0 186 if (gc::IsMarked(const_cast<Key *>(&key))) {
michael@0 187 if (markValue(trc, &e.front().value()))
michael@0 188 markedAny = true;
michael@0 189 if (e.front().key() != key)
michael@0 190 entryMoved(e, key);
michael@0 191 } else if (keyNeedsMark(key)) {
michael@0 192 gc::Mark(trc, &e.front().value(), "WeakMap entry value");
michael@0 193 gc::Mark(trc, &key, "proxy-preserved WeakMap entry key");
michael@0 194 if (e.front().key() != key)
michael@0 195 entryMoved(e, key);
michael@0 196 markedAny = true;
michael@0 197 }
michael@0 198 key.unsafeSet(nullptr);
michael@0 199 }
michael@0 200 return markedAny;
michael@0 201 }
michael@0 202
michael@0 203 void sweep() {
michael@0 204 /* Remove all entries whose keys remain unmarked. */
michael@0 205 for (Enum e(*this); !e.empty(); e.popFront()) {
michael@0 206 Key k(e.front().key());
michael@0 207 if (gc::IsAboutToBeFinalized(&k))
michael@0 208 e.removeFront();
michael@0 209 else if (k != e.front().key())
michael@0 210 entryMoved(e, k);
michael@0 211 }
michael@0 212 /*
michael@0 213 * Once we've swept, all remaining edges should stay within the
michael@0 214 * known-live part of the graph.
michael@0 215 */
michael@0 216 assertEntriesNotAboutToBeFinalized();
michael@0 217 }
michael@0 218
michael@0 219 /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
michael@0 220 void traceMappings(WeakMapTracer *tracer) {
michael@0 221 for (Range r = Base::all(); !r.empty(); r.popFront()) {
michael@0 222 gc::Cell *key = gc::ToMarkable(r.front().key());
michael@0 223 gc::Cell *value = gc::ToMarkable(r.front().value());
michael@0 224 if (key && value) {
michael@0 225 tracer->callback(tracer, memberOf,
michael@0 226 key, gc::TraceKind(r.front().key()),
michael@0 227 value, gc::TraceKind(r.front().value()));
michael@0 228 }
michael@0 229 }
michael@0 230 }
michael@0 231
michael@0 232 /* Rekey an entry when moved, ensuring we do not trigger barriers. */
michael@0 233 void entryMoved(Enum &eArg, const Key &k) {
michael@0 234 typedef typename HashMap<typename Unbarriered<Key>::type,
michael@0 235 typename Unbarriered<Value>::type,
michael@0 236 typename Unbarriered<HashPolicy>::type,
michael@0 237 RuntimeAllocPolicy>::Enum UnbarrieredEnum;
michael@0 238 UnbarrieredEnum &e = reinterpret_cast<UnbarrieredEnum &>(eArg);
michael@0 239 e.rekeyFront(reinterpret_cast<const typename Unbarriered<Key>::type &>(k));
michael@0 240 }
michael@0 241
michael@0 242 protected:
michael@0 243 void assertEntriesNotAboutToBeFinalized() {
michael@0 244 #if DEBUG
michael@0 245 for (Range r = Base::all(); !r.empty(); r.popFront()) {
michael@0 246 Key k(r.front().key());
michael@0 247 JS_ASSERT(!gc::IsAboutToBeFinalized(&k));
michael@0 248 JS_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
michael@0 249 JS_ASSERT(k == r.front().key());
michael@0 250 }
michael@0 251 #endif
michael@0 252 }
michael@0 253 };
michael@0 254
michael@0 255 } /* namespace js */
michael@0 256
michael@0 257 extern JSObject *
michael@0 258 js_InitWeakMapClass(JSContext *cx, js::HandleObject obj);
michael@0 259
michael@0 260 #endif /* jsweakmap_h */

mercurial