1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Mozilla Foundation
22 : * Portions created by the Initial Developer are Copyright (C) 2009
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : * Andreas Gal <gal@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #ifndef jsweakmap_h___
43 : #define jsweakmap_h___
44 :
45 : #include "jsapi.h"
46 : #include "jsfriendapi.h"
47 : #include "jscntxt.h"
48 : #include "jsobj.h"
49 : #include "jsgcmark.h"
50 :
51 : #include "js/HashTable.h"
52 :
53 : namespace js {
54 :
55 : // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
56 : // a key is collected, the table entry disappears, dropping its reference to the value.
57 : //
58 : // More precisely:
59 : //
60 : // A WeakMap entry is collected if and only if either the WeakMap or the entry's key
61 : // is collected. If an entry is not collected, it remains in the WeakMap and it has a
62 : // strong reference to the value.
63 : //
64 : // You must call this table's 'mark' method when the object of which it is a part is
65 : // reached by the garbage collection tracer. Once a table is known to be live, the
66 : // implementation takes care of the iterative marking needed for weak tables and removing
67 : // table entries when collection is complete.
68 : //
69 : // You may provide your own MarkPolicy class to specify how keys and values are marked; a
70 : // policy template provides default definitions for some common key/value type
71 : // combinations.
72 : //
73 : // Details:
74 : //
75 : // The interface is as for a js::HashMap, with the following additions:
76 : //
77 : // - You must call the WeakMap's 'trace' member function when you discover that the map is
78 : // part of a live object. (You'll typically call this from the containing type's 'trace'
79 : // function.)
80 : //
81 : // - There is no AllocPolicy parameter; these are used with our garbage collector, so
82 : // RuntimeAllocPolicy is hard-wired.
83 : //
84 : // - Optional fourth and fifth parameters are the MarkPolicies for the key and value type.
85 : // A MarkPolicy has the constructor:
86 : //
87 : // MarkPolicy(JSTracer *)
88 : //
89 : // and the following member functions:
90 : //
91 : // bool isMarked(const Type &x)
92 : // Return true if x has been marked as live by the garbage collector.
93 : //
94 : // bool mark(Type &x)
95 : // Return false if x is already marked. Otherwise, mark x and return true.
96 : //
97 : // If omitted, the MarkPolicy parameter defaults to js::DefaultMarkPolicy<Type>,
98 : // a policy template with the obvious definitions for some typical
99 : // SpiderMonkey type combinations.
100 :
101 : // A policy template holding default marking algorithms for common type combinations. This
102 : // provides default types for WeakMap's MarkPolicy template parameter.
103 : template <class Type> class DefaultMarkPolicy;
104 :
105 : // A policy template holding default tracing algorithms for common type combinations. This
106 : // provides default types for WeakMap's TracePolicy template parameter.
107 : template <class Key, class Value> class DefaultTracePolicy;
108 :
109 : // The value for the next pointer for maps not in the map list.
110 : static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
111 :
112 : typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
113 :
114 : // Common base class for all WeakMap specializations. The collector uses this to call
115 : // their markIteratively and sweep methods.
116 : class WeakMapBase {
117 : public:
118 12764 : WeakMapBase(JSObject *memOf) : memberOf(memOf), next(WeakMapNotInList) { }
119 12764 : virtual ~WeakMapBase() { }
120 :
121 20488 : void trace(JSTracer *tracer) {
122 20488 : if (IS_GC_MARKING_TRACER(tracer)) {
123 : // We don't do anything with a WeakMap at trace time. Rather, we wait until as
124 : // many keys as possible have been marked, and add ourselves to the list of
125 : // known-live WeakMaps to be scanned in the iterative marking phase, by
126 : // markAllIteratively.
127 8728 : JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
128 :
129 : // Add ourselves to the list if we are not already in the list. We can already
130 : // be in the list if the weak map is marked more than once due delayed marking.
131 8728 : if (next == WeakMapNotInList) {
132 8728 : JSRuntime *rt = tracer->runtime;
133 8728 : next = rt->gcWeakMapList;
134 8728 : rt->gcWeakMapList = this;
135 8728 : }
136 : } else {
137 : // If we're not actually doing garbage collection, the keys won't be marked
138 : // nicely as needed by the true ephemeral marking algorithm --- custom tracers
139 : // such as the cycle collector must use their own means for cycle detection.
140 : // So here we do a conservative approximation: pretend all keys are live.
141 11760 : if (tracer->eagerlyTraceWeakMaps)
142 11760 : nonMarkingTrace(tracer);
143 : }
144 20488 : }
145 :
146 : // Garbage collector entry points.
147 :
148 : // Check all weak maps that have been marked as live so far in this garbage
149 : // collection, and mark the values of all entries that have become strong references
150 : // to them. Return true if we marked any new values, indicating that we need to make
151 : // another pass. In other words, mark my marked maps' marked members' mid-collection.
152 : static bool markAllIteratively(JSTracer *tracer);
153 :
154 : // Remove entries whose keys are dead from all weak maps marked as live in this
155 : // garbage collection.
156 : static void sweepAll(JSTracer *tracer);
157 :
158 : // Trace all delayed weak map bindings. Used by the cycle collector.
159 : static void traceAllMappings(WeakMapTracer *tracer);
160 :
161 122 : void check() { JS_ASSERT(next == WeakMapNotInList); }
162 :
163 : // Remove everything from the live weak map list.
164 : static void resetWeakMapList(JSRuntime *rt);
165 :
166 : // Save and restore the live weak map list to a vector.
167 : static bool saveWeakMapList(JSRuntime *rt, WeakMapVector &vector);
168 : static void restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector);
169 :
170 : protected:
171 : // Instance member functions called by the above. Instantiations of WeakMap override
172 : // these with definitions appropriate for their Key and Value types.
173 : virtual void nonMarkingTrace(JSTracer *tracer) = 0;
174 : virtual bool markIteratively(JSTracer *tracer) = 0;
175 : virtual void sweep(JSTracer *tracer) = 0;
176 : virtual void traceMappings(WeakMapTracer *tracer) = 0;
177 :
178 : // Object that this weak map is part of, if any.
179 : JSObject *memberOf;
180 :
181 : private:
182 : // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
183 : // collection, headed by JSRuntime::gcWeakMapList. The last element of the list
184 : // has NULL as its next. Maps not in the list have WeakMapNotInList as their
185 : // next. We must distinguish these cases to avoid creating infinite lists
186 : // when a weak map gets traced twice due to delayed marking.
187 : WeakMapBase *next;
188 : };
189 :
190 : template <class Key, class Value,
191 : class HashPolicy = DefaultHasher<Key>,
192 : class KeyMarkPolicy = DefaultMarkPolicy<Key>,
193 : class ValueMarkPolicy = DefaultMarkPolicy<Value>,
194 : class TracePolicy = DefaultTracePolicy<Key, Value> >
195 12764 : class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase {
196 : private:
197 : typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
198 : typedef typename Base::Enum Enum;
199 :
200 : public:
201 : typedef typename Base::Range Range;
202 :
203 : explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { }
204 12764 : explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { }
205 :
206 : // Use with caution, as result can be affected by garbage collection.
207 0 : Range nondeterministicAll() {
208 0 : return Base::all();
209 : }
210 :
211 : private:
212 11760 : void nonMarkingTrace(JSTracer *trc) {
213 11760 : ValueMarkPolicy vp(trc);
214 11760 : for (Range r = Base::all(); !r.empty(); r.popFront())
215 0 : vp.mark(&r.front().value);
216 11760 : }
217 :
218 19772 : bool markIteratively(JSTracer *trc) {
219 19772 : KeyMarkPolicy kp(trc);
220 19772 : ValueMarkPolicy vp(trc);
221 19772 : bool markedAny = false;
222 30431 : for (Range r = Base::all(); !r.empty(); r.popFront()) {
223 10659 : const Key &k = r.front().key;
224 10659 : Value &v = r.front().value;
225 : /* If the entry is live, ensure its key and value are marked. */
226 10659 : if (kp.isMarked(k)) {
227 10216 : markedAny |= vp.mark(&v);
228 : }
229 10659 : JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v));
230 : }
231 19772 : return markedAny;
232 : }
233 :
234 8728 : void sweep(JSTracer *trc) {
235 8728 : KeyMarkPolicy kp(trc);
236 :
237 : /* Remove all entries whose keys remain unmarked. */
238 12784 : for (Enum e(*this); !e.empty(); e.popFront()) {
239 4056 : if (!kp.isMarked(e.front().key))
240 127 : e.removeFront();
241 : }
242 :
243 : #if DEBUG
244 8728 : ValueMarkPolicy vp(trc);
245 : /*
246 : * Once we've swept, all remaining edges should stay within the
247 : * known-live part of the graph.
248 : */
249 12657 : for (Range r = Base::all(); !r.empty(); r.popFront()) {
250 3929 : JS_ASSERT(kp.isMarked(r.front().key));
251 3929 : JS_ASSERT(vp.isMarked(r.front().value));
252 : }
253 : #endif
254 8728 : }
255 :
256 : // mapObj can be NULL, which means that the map is not part of a JSObject.
257 1 : void traceMappings(WeakMapTracer *tracer) {
258 1 : TracePolicy t(tracer);
259 1 : for (Range r = Base::all(); !r.empty(); r.popFront())
260 0 : t.traceMapping(memberOf, r.front().key, r.front().value);
261 1 : }
262 : };
263 :
264 : template <>
265 : class DefaultMarkPolicy<HeapValue> {
266 : private:
267 : JSTracer *tracer;
268 : public:
269 84 : DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
270 900 : bool isMarked(const HeapValue &x) {
271 900 : if (x.isMarkable())
272 0 : return !IsAboutToBeFinalized(x);
273 900 : return true;
274 : }
275 360 : bool mark(HeapValue *x) {
276 360 : if (isMarked(*x))
277 360 : return false;
278 0 : js::gc::MarkValue(tracer, x, "WeakMap entry");
279 0 : return true;
280 : }
281 : };
282 :
283 : template <>
284 : class DefaultMarkPolicy<HeapPtrObject> {
285 : private:
286 : JSTracer *tracer;
287 : public:
288 59204 : DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
289 45454 : bool isMarked(const HeapPtrObject &x) {
290 45454 : return !IsAboutToBeFinalized(x);
291 : }
292 9856 : bool mark(HeapPtrObject *x) {
293 9856 : if (isMarked(*x))
294 8293 : return false;
295 1563 : js::gc::MarkObject(tracer, x, "WeakMap entry");
296 1563 : return true;
297 : }
298 : };
299 :
300 : template <>
301 : class DefaultMarkPolicy<HeapPtrScript> {
302 : private:
303 : JSTracer *tracer;
304 : public:
305 9472 : DefaultMarkPolicy(JSTracer *t) : tracer(t) { }
306 7310 : bool isMarked(const HeapPtrScript &x) {
307 7310 : return !IsAboutToBeFinalized(x);
308 : }
309 : bool mark(HeapPtrScript *x) {
310 : if (isMarked(*x))
311 : return false;
312 : js::gc::MarkScript(tracer, x, "WeakMap entry");
313 : return true;
314 : }
315 : };
316 :
317 : // Default trace policies
318 :
319 : template <>
320 : class DefaultTracePolicy<HeapPtrObject, HeapValue> {
321 : private:
322 : WeakMapTracer *tracer;
323 : public:
324 1 : DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
325 0 : void traceMapping(JSObject *m, const HeapPtr<JSObject> &k, HeapValue &v) {
326 0 : if (v.isMarkable())
327 0 : tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind());
328 0 : }
329 : };
330 :
331 : template <>
332 : class DefaultTracePolicy<HeapPtrObject, HeapPtrObject> {
333 : private:
334 : WeakMapTracer *tracer;
335 : public:
336 0 : DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
337 0 : void traceMapping(JSObject *m, const HeapPtrObject &k, const HeapPtrObject &v) {
338 0 : tracer->callback(tracer, m, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT);
339 0 : }
340 : };
341 :
342 : template <>
343 : class DefaultTracePolicy<HeapPtrScript, HeapPtrObject> {
344 : private:
345 : WeakMapTracer *tracer;
346 : public:
347 0 : DefaultTracePolicy(WeakMapTracer *t) : tracer(t) { }
348 0 : void traceMapping(JSObject *m, const HeapPtrScript &k, const HeapPtrObject &v) {
349 0 : tracer->callback(tracer, m, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT);
350 0 : }
351 : };
352 :
353 : }
354 :
355 : extern JSObject *
356 : js_InitWeakMapClass(JSContext *cx, JSObject *obj);
357 :
358 : #endif
|