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/. */
6
7 #include "jsweakmap.h"
8
9 #include <string.h>
10
11 #include "jsapi.h"
12 #include "jscntxt.h"
13 #include "jsfriendapi.h"
14 #include "jsobj.h"
15 #include "jswrapper.h"
16
17 #include "js/GCAPI.h"
18 #include "vm/GlobalObject.h"
19
20 #include "jsobjinlines.h"
21
22 using namespace js;
23 using namespace js::gc;
24
WeakMapBase(JSObject * memOf,Zone * zone)25 WeakMapBase::WeakMapBase(JSObject* memOf, Zone* zone)
26 : memberOf(memOf),
27 zone(zone),
28 marked(false)
29 {
30 MOZ_ASSERT_IF(memberOf, memberOf->compartment()->zone() == zone);
31 }
32
~WeakMapBase()33 WeakMapBase::~WeakMapBase()
34 {
35 MOZ_ASSERT(CurrentThreadIsGCSweeping());
36 }
37
38 void
unmarkZone(JS::Zone * zone)39 WeakMapBase::unmarkZone(JS::Zone* zone)
40 {
41 for (WeakMapBase* m : zone->gcWeakMapList)
42 m->marked = false;
43 }
44
45 void
markAll(JS::Zone * zone,JSTracer * tracer)46 WeakMapBase::markAll(JS::Zone* zone, JSTracer* tracer)
47 {
48 MOZ_ASSERT(tracer->weakMapAction() != DoNotTraceWeakMaps);
49 for (WeakMapBase* m : zone->gcWeakMapList) {
50 m->trace(tracer);
51 if (m->memberOf)
52 TraceEdge(tracer, &m->memberOf, "memberOf");
53 }
54 }
55
56 bool
markZoneIteratively(JS::Zone * zone,JSTracer * tracer)57 WeakMapBase::markZoneIteratively(JS::Zone* zone, JSTracer* tracer)
58 {
59 bool markedAny = false;
60 for (WeakMapBase* m : zone->gcWeakMapList) {
61 if (m->marked && m->traceEntries(tracer))
62 markedAny = true;
63 }
64 return markedAny;
65 }
66
67 bool
findInterZoneEdges(JS::Zone * zone)68 WeakMapBase::findInterZoneEdges(JS::Zone* zone)
69 {
70 for (WeakMapBase* m : zone->gcWeakMapList) {
71 if (!m->findZoneEdges())
72 return false;
73 }
74 return true;
75 }
76
77 void
sweepZone(JS::Zone * zone)78 WeakMapBase::sweepZone(JS::Zone* zone)
79 {
80 for (WeakMapBase* m = zone->gcWeakMapList.getFirst(); m; ) {
81 WeakMapBase* next = m->getNext();
82 if (m->marked) {
83 m->sweep();
84 } else {
85 /* Destroy the hash map now to catch any use after this point. */
86 m->finish();
87 m->removeFrom(zone->gcWeakMapList);
88 }
89 m = next;
90 }
91
92 #ifdef DEBUG
93 for (WeakMapBase* m : zone->gcWeakMapList) {
94 MOZ_ASSERT(m->isInList() && m->marked);
95 }
96 #endif
97 }
98
99 void
traceAllMappings(WeakMapTracer * tracer)100 WeakMapBase::traceAllMappings(WeakMapTracer* tracer)
101 {
102 JSRuntime* rt = tracer->runtime;
103 for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
104 for (WeakMapBase* m : zone->gcWeakMapList) {
105 // The WeakMapTracer callback is not allowed to GC.
106 JS::AutoSuppressGCAnalysis nogc;
107 m->traceMappings(tracer);
108 }
109 }
110 }
111
112 bool
saveZoneMarkedWeakMaps(JS::Zone * zone,WeakMapSet & markedWeakMaps)113 WeakMapBase::saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps)
114 {
115 for (WeakMapBase* m : zone->gcWeakMapList) {
116 if (m->marked && !markedWeakMaps.put(m))
117 return false;
118 }
119 return true;
120 }
121
122 void
restoreMarkedWeakMaps(WeakMapSet & markedWeakMaps)123 WeakMapBase::restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps)
124 {
125 for (WeakMapSet::Range r = markedWeakMaps.all(); !r.empty(); r.popFront()) {
126 WeakMapBase* map = r.front();
127 MOZ_ASSERT(map->zone->isGCMarking());
128 MOZ_ASSERT(!map->marked);
129 map->marked = true;
130 }
131 }
132
133 bool
findZoneEdges()134 ObjectValueMap::findZoneEdges()
135 {
136 /*
137 * For unmarked weakmap keys with delegates in a different zone, add a zone
138 * edge to ensure that the delegate zone finishes marking before the key
139 * zone.
140 */
141 JS::AutoSuppressGCAnalysis nogc;
142 for (Range r = all(); !r.empty(); r.popFront()) {
143 JSObject* key = r.front().key();
144 if (key->asTenured().isMarked(BLACK) && !key->asTenured().isMarked(GRAY))
145 continue;
146 JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp;
147 if (!op)
148 continue;
149 JSObject* delegate = op(key);
150 if (!delegate)
151 continue;
152 Zone* delegateZone = delegate->zone();
153 if (delegateZone == zone || !delegateZone->isGCMarking())
154 continue;
155 if (!delegateZone->gcZoneGroupEdges.put(key->zone()))
156 return false;
157 }
158 return true;
159 }
160
ObjectWeakMap(JSContext * cx)161 ObjectWeakMap::ObjectWeakMap(JSContext* cx)
162 : map(cx, nullptr)
163 {}
164
165 bool
init()166 ObjectWeakMap::init()
167 {
168 return map.init();
169 }
170
171 JSObject*
lookup(const JSObject * obj)172 ObjectWeakMap::lookup(const JSObject* obj)
173 {
174 MOZ_ASSERT(map.initialized());
175 if (ObjectValueMap::Ptr p = map.lookup(const_cast<JSObject*>(obj)))
176 return &p->value().toObject();
177 return nullptr;
178 }
179
180 bool
add(JSContext * cx,JSObject * obj,JSObject * target)181 ObjectWeakMap::add(JSContext* cx, JSObject* obj, JSObject* target)
182 {
183 MOZ_ASSERT(obj && target);
184 MOZ_ASSERT(map.initialized());
185
186 MOZ_ASSERT(!map.has(obj));
187 if (!map.put(obj, ObjectValue(*target))) {
188 ReportOutOfMemory(cx);
189 return false;
190 }
191
192 return true;
193 }
194
195 void
clear()196 ObjectWeakMap::clear()
197 {
198 MOZ_ASSERT(map.initialized());
199 map.clear();
200 }
201
202 void
trace(JSTracer * trc)203 ObjectWeakMap::trace(JSTracer* trc)
204 {
205 MOZ_ASSERT(map.initialized());
206 map.trace(trc);
207 }
208
209 size_t
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)210 ObjectWeakMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
211 {
212 MOZ_ASSERT(map.initialized());
213 return map.sizeOfExcludingThis(mallocSizeOf);
214 }
215
216 #ifdef JSGC_HASH_TABLE_CHECKS
217 void
checkAfterMovingGC()218 ObjectWeakMap::checkAfterMovingGC()
219 {
220 MOZ_ASSERT(map.initialized());
221 for (ObjectValueMap::Range r = map.all(); !r.empty(); r.popFront()) {
222 CheckGCThingAfterMovingGC(r.front().key().get());
223 CheckGCThingAfterMovingGC(&r.front().value().toObject());
224 }
225 }
226 #endif // JSGC_HASH_TABLE_CHECKS
227