js/src/jswatchpoint.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:a11cf69a4c55
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 "jswatchpoint.h"
8
9 #include "jsatom.h"
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
12
13 #include "gc/Marking.h"
14
15 #include "jsgcinlines.h"
16
17 using namespace js;
18 using namespace js::gc;
19
20 inline HashNumber
21 DefaultHasher<WatchKey>::hash(const Lookup &key)
22 {
23 return DefaultHasher<JSObject *>::hash(key.object.get()) ^ HashId(key.id.get());
24 }
25
26 namespace {
27
28 class AutoEntryHolder {
29 typedef WatchpointMap::Map Map;
30 Map &map;
31 Map::Ptr p;
32 uint32_t gen;
33 RootedObject obj;
34 RootedId id;
35
36 public:
37 AutoEntryHolder(JSContext *cx, Map &map, Map::Ptr p)
38 : map(map), p(p), gen(map.generation()), obj(cx, p->key().object), id(cx, p->key().id)
39 {
40 JS_ASSERT(!p->value().held);
41 p->value().held = true;
42 }
43
44 ~AutoEntryHolder() {
45 if (gen != map.generation())
46 p = map.lookup(WatchKey(obj, id));
47 if (p)
48 p->value().held = false;
49 }
50 };
51
52 } /* anonymous namespace */
53
54 bool
55 WatchpointMap::init()
56 {
57 return map.init();
58 }
59
60 bool
61 WatchpointMap::watch(JSContext *cx, HandleObject obj, HandleId id,
62 JSWatchPointHandler handler, HandleObject closure)
63 {
64 JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
65
66 if (!obj->setWatched(cx))
67 return false;
68
69 Watchpoint w(handler, closure, false);
70 if (!map.put(WatchKey(obj, id), w)) {
71 js_ReportOutOfMemory(cx);
72 return false;
73 }
74 /*
75 * For generational GC, we don't need to post-barrier writes to the
76 * hashtable here because we mark all watchpoints as part of root marking in
77 * markAll().
78 */
79 return true;
80 }
81
82 void
83 WatchpointMap::unwatch(JSObject *obj, jsid id,
84 JSWatchPointHandler *handlerp, JSObject **closurep)
85 {
86 if (Map::Ptr p = map.lookup(WatchKey(obj, id))) {
87 if (handlerp)
88 *handlerp = p->value().handler;
89 if (closurep) {
90 // Read barrier to prevent an incorrectly gray closure from escaping the
91 // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
92 JS::ExposeGCThingToActiveJS(p->value().closure, JSTRACE_OBJECT);
93 *closurep = p->value().closure;
94 }
95 map.remove(p);
96 }
97 }
98
99 void
100 WatchpointMap::unwatchObject(JSObject *obj)
101 {
102 for (Map::Enum e(map); !e.empty(); e.popFront()) {
103 Map::Entry &entry = e.front();
104 if (entry.key().object == obj)
105 e.removeFront();
106 }
107 }
108
109 void
110 WatchpointMap::clear()
111 {
112 map.clear();
113 }
114
115 bool
116 WatchpointMap::triggerWatchpoint(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
117 {
118 Map::Ptr p = map.lookup(WatchKey(obj, id));
119 if (!p || p->value().held)
120 return true;
121
122 AutoEntryHolder holder(cx, map, p);
123
124 /* Copy the entry, since GC would invalidate p. */
125 JSWatchPointHandler handler = p->value().handler;
126 RootedObject closure(cx, p->value().closure);
127
128 /* Determine the property's old value. */
129 Value old;
130 old.setUndefined();
131 if (obj->isNative()) {
132 if (Shape *shape = obj->nativeLookup(cx, id)) {
133 if (shape->hasSlot())
134 old = obj->nativeGetSlot(shape->slot());
135 }
136 }
137
138 // Read barrier to prevent an incorrectly gray closure from escaping the
139 // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
140 JS::ExposeGCThingToActiveJS(closure, JSTRACE_OBJECT);
141
142 /* Call the handler. */
143 return handler(cx, obj, id, old, vp.address(), closure);
144 }
145
146 bool
147 WatchpointMap::markCompartmentIteratively(JSCompartment *c, JSTracer *trc)
148 {
149 if (!c->watchpointMap)
150 return false;
151 return c->watchpointMap->markIteratively(trc);
152 }
153
154 bool
155 WatchpointMap::markIteratively(JSTracer *trc)
156 {
157 bool marked = false;
158 for (Map::Enum e(map); !e.empty(); e.popFront()) {
159 Map::Entry &entry = e.front();
160 JSObject *priorKeyObj = entry.key().object;
161 jsid priorKeyId(entry.key().id.get());
162 bool objectIsLive =
163 IsObjectMarked(const_cast<EncapsulatedPtrObject *>(&entry.key().object));
164 if (objectIsLive || entry.value().held) {
165 if (!objectIsLive) {
166 MarkObject(trc, const_cast<EncapsulatedPtrObject *>(&entry.key().object),
167 "held Watchpoint object");
168 marked = true;
169 }
170
171 JS_ASSERT(JSID_IS_STRING(priorKeyId) || JSID_IS_INT(priorKeyId));
172 MarkId(trc, const_cast<EncapsulatedId *>(&entry.key().id), "WatchKey::id");
173
174 if (entry.value().closure && !IsObjectMarked(&entry.value().closure)) {
175 MarkObject(trc, &entry.value().closure, "Watchpoint::closure");
176 marked = true;
177 }
178
179 /* We will sweep this entry in sweepAll if !objectIsLive. */
180 if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
181 e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
182 }
183 }
184 return marked;
185 }
186
187 void
188 WatchpointMap::markAll(JSTracer *trc)
189 {
190 for (Map::Enum e(map); !e.empty(); e.popFront()) {
191 Map::Entry &entry = e.front();
192 WatchKey key = entry.key();
193 WatchKey prior = key;
194 JS_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id));
195
196 MarkObject(trc, const_cast<EncapsulatedPtrObject *>(&key.object),
197 "held Watchpoint object");
198 MarkId(trc, const_cast<EncapsulatedId *>(&key.id), "WatchKey::id");
199 MarkObject(trc, &entry.value().closure, "Watchpoint::closure");
200
201 if (prior.object != key.object || prior.id != key.id)
202 e.rekeyFront(key);
203 }
204 }
205
206 void
207 WatchpointMap::sweepAll(JSRuntime *rt)
208 {
209 for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
210 if (WatchpointMap *wpmap = c->watchpointMap)
211 wpmap->sweep();
212 }
213 }
214
215 void
216 WatchpointMap::sweep()
217 {
218 for (Map::Enum e(map); !e.empty(); e.popFront()) {
219 Map::Entry &entry = e.front();
220 JSObject *obj(entry.key().object);
221 if (IsObjectAboutToBeFinalized(&obj)) {
222 JS_ASSERT(!entry.value().held);
223 e.removeFront();
224 } else if (obj != entry.key().object) {
225 e.rekeyFront(WatchKey(obj, entry.key().id));
226 }
227 }
228 }
229
230 void
231 WatchpointMap::traceAll(WeakMapTracer *trc)
232 {
233 JSRuntime *rt = trc->runtime;
234 for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
235 if (WatchpointMap *wpmap = comp->watchpointMap)
236 wpmap->trace(trc);
237 }
238 }
239
240 void
241 WatchpointMap::trace(WeakMapTracer *trc)
242 {
243 for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
244 Map::Entry &entry = r.front();
245 trc->callback(trc, nullptr,
246 entry.key().object.get(), JSTRACE_OBJECT,
247 entry.value().closure.get(), JSTRACE_OBJECT);
248 }
249 }

mercurial