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