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