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 : // The value for the next pointer for maps not in the map list.
102 : static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
103 :
104 : typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
105 :
106 : // Common base class for all WeakMap specializations. The collector uses this to call
107 : // their markIteratively and sweep methods.
108 : class WeakMapBase {
109 : public:
110 12831 : WeakMapBase(JSObject *memOf) : memberOf(memOf), next(WeakMapNotInList) { }
111 12831 : virtual ~WeakMapBase() { }
112 :
113 20490 : void trace(JSTracer *tracer) {
114 20490 : if (IS_GC_MARKING_TRACER(tracer)) {
115 : // We don't do anything with a WeakMap at trace time. Rather, we wait until as
116 : // many keys as possible have been marked, and add ourselves to the list of
117 : // known-live WeakMaps to be scanned in the iterative marking phase, by
118 : // markAllIteratively.
119 8730 : JS_ASSERT(!tracer->eagerlyTraceWeakMaps);
120 :
121 : // Add ourselves to the list if we are not already in the list. We can already
122 : // be in the list if the weak map is marked more than once due delayed marking.
123 8730 : if (next == WeakMapNotInList) {
124 8730 : JSRuntime *rt = tracer->runtime;
125 8730 : next = rt->gcWeakMapList;
126 8730 : rt->gcWeakMapList = this;
127 8730 : }
128 : } else {
129 : // If we're not actually doing garbage collection, the keys won't be marked
130 : // nicely as needed by the true ephemeral marking algorithm --- custom tracers
131 : // such as the cycle collector must use their own means for cycle detection.
132 : // So here we do a conservative approximation: pretend all keys are live.
133 11760 : if (tracer->eagerlyTraceWeakMaps)
134 11760 : nonMarkingTrace(tracer);
135 : }
136 20490 : }
137 :
138 : // Garbage collector entry points.
139 :
140 : // Check all weak maps that have been marked as live so far in this garbage
141 : // collection, and mark the values of all entries that have become strong references
142 : // to them. Return true if we marked any new values, indicating that we need to make
143 : // another pass. In other words, mark my marked maps' marked members' mid-collection.
144 : static bool markAllIteratively(JSTracer *tracer);
145 :
146 : // Remove entries whose keys are dead from all weak maps marked as live in this
147 : // garbage collection.
148 : static void sweepAll(JSTracer *tracer);
149 :
150 : // Trace all delayed weak map bindings. Used by the cycle collector.
151 : static void traceAllMappings(WeakMapTracer *tracer);
152 :
153 54 : void check() { JS_ASSERT(next == WeakMapNotInList); }
154 :
155 : // Remove everything from the live weak map list.
156 : static void resetWeakMapList(JSRuntime *rt);
157 :
158 : // Save and restore the live weak map list to a vector.
159 : static bool saveWeakMapList(JSRuntime *rt, WeakMapVector &vector);
160 : static void restoreWeakMapList(JSRuntime *rt, WeakMapVector &vector);
161 :
162 : protected:
163 : // Instance member functions called by the above. Instantiations of WeakMap override
164 : // these with definitions appropriate for their Key and Value types.
165 : virtual void nonMarkingTrace(JSTracer *tracer) = 0;
166 : virtual bool markIteratively(JSTracer *tracer) = 0;
167 : virtual void sweep(JSTracer *tracer) = 0;
168 : virtual void traceMappings(WeakMapTracer *tracer) = 0;
169 :
170 : // Object that this weak map is part of, if any.
171 : JSObject *memberOf;
172 :
173 : private:
174 : // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
175 : // collection, headed by JSRuntime::gcWeakMapList. The last element of the list
176 : // has NULL as its next. Maps not in the list have WeakMapNotInList as their
177 : // next. We must distinguish these cases to avoid creating infinite lists
178 : // when a weak map gets traced twice due to delayed marking.
179 : WeakMapBase *next;
180 : };
181 :
182 : template <class Key, class Value,
183 : class HashPolicy = DefaultHasher<Key> >
184 12831 : class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase {
185 : private:
186 : typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
187 : typedef typename Base::Enum Enum;
188 :
189 : public:
190 : typedef typename Base::Range Range;
191 :
192 : explicit WeakMap(JSRuntime *rt, JSObject *memOf=NULL) : Base(rt), WeakMapBase(memOf) { }
193 12831 : explicit WeakMap(JSContext *cx, JSObject *memOf=NULL) : Base(cx), WeakMapBase(memOf) { }
194 :
195 : /* Use with caution, as result can be affected by garbage collection. */
196 0 : Range nondeterministicAll() {
197 0 : return Base::all();
198 : }
199 :
200 : private:
201 900 : bool IsMarked(const HeapValue &x) {
202 900 : if (x.isMarkable())
203 0 : return !IsAboutToBeFinalized(x);
204 900 : return true;
205 : }
206 45452 : bool IsMarked(const HeapPtrObject &x) {
207 45452 : return !IsAboutToBeFinalized(x);
208 : }
209 7322 : bool IsMarked(const HeapPtrScript&x) {
210 7322 : return !IsAboutToBeFinalized(x);
211 : }
212 :
213 360 : bool Mark(JSTracer *trc, HeapValue *x) {
214 360 : if (IsMarked(*x))
215 360 : return false;
216 0 : js::gc::MarkValue(trc, x, "WeakMap entry value");
217 0 : return true;
218 : }
219 9861 : bool Mark(JSTracer *trc, HeapPtrObject *x) {
220 9861 : if (IsMarked(*x))
221 8320 : return false;
222 1541 : js::gc::MarkObject(trc, x, "WeakMap entry value");
223 1541 : return true;
224 : }
225 : bool Mark(JSTracer *trc, HeapPtrScript *x) {
226 : if (IsMarked(*x))
227 : return false;
228 : js::gc::MarkScript(trc, x, "WeakMap entry value");
229 : return true;
230 : }
231 :
232 11760 : void nonMarkingTrace(JSTracer *trc) {
233 11760 : for (Range r = Base::all(); !r.empty(); r.popFront())
234 0 : Mark(trc, &r.front().value);
235 11760 : }
236 :
237 19782 : bool markIteratively(JSTracer *trc) {
238 19782 : bool markedAny = false;
239 30442 : for (Range r = Base::all(); !r.empty(); r.popFront()) {
240 : /* If the entry is live, ensure its key and value are marked. */
241 10660 : if (IsMarked(r.front().key) && Mark(trc, &r.front().value))
242 1541 : markedAny = true;
243 10660 : JS_ASSERT_IF(IsMarked(r.front().key), IsMarked(r.front().value));
244 : }
245 19782 : return markedAny;
246 : }
247 :
248 8730 : void sweep(JSTracer *trc) {
249 : /* Remove all entries whose keys remain unmarked. */
250 12784 : for (Enum e(*this); !e.empty(); e.popFront()) {
251 4054 : if (!IsMarked(e.front().key))
252 125 : e.removeFront();
253 : }
254 :
255 : #if DEBUG
256 : /*
257 : * Once we've swept, all remaining edges should stay within the
258 : * known-live part of the graph.
259 : */
260 12659 : for (Range r = Base::all(); !r.empty(); r.popFront()) {
261 3929 : JS_ASSERT(IsMarked(r.front().key));
262 3929 : JS_ASSERT(IsMarked(r.front().value));
263 : }
264 : #endif
265 8730 : }
266 :
267 0 : void CallTracer(WeakMapTracer *trc, const HeapPtrObject &k, const HeapValue &v) {
268 0 : if (v.isMarkable())
269 0 : trc->callback(trc, memberOf, k.get(), JSTRACE_OBJECT, v.toGCThing(), v.gcKind());
270 0 : }
271 0 : void CallTracer(WeakMapTracer *trc, const HeapPtrObject &k, const HeapPtrObject &v) {
272 0 : trc->callback(trc, memberOf, k.get(), JSTRACE_OBJECT, v.get(), JSTRACE_OBJECT);
273 0 : }
274 0 : void CallTracer(WeakMapTracer *trc, const HeapPtrScript &k, const HeapPtrObject &v) {
275 0 : trc->callback(trc, memberOf, k.get(), JSTRACE_SCRIPT, v.get(), JSTRACE_OBJECT);
276 0 : }
277 :
278 : /* mapObj can be NULL, which means that the map is not part of a JSObject. */
279 0 : void traceMappings(WeakMapTracer *tracer) {
280 0 : for (Range r = Base::all(); !r.empty(); r.popFront())
281 0 : CallTracer(tracer, r.front().key, r.front().value);
282 0 : }
283 : };
284 :
285 : } /* namespace js */
286 :
287 : extern JSObject *
288 : js_InitWeakMapClass(JSContext *cx, JSObject *obj);
289 :
290 : #endif
|