Bumping manifests a=b2g-bump
[gecko.git] / js / src / jswatchpoint.cpp
blob4de64410dc9ebf97d31f1b398ef7ee5f2004d689
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/. */
7 #include "jswatchpoint.h"
9 #include "jsatom.h"
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
13 #include "gc/Marking.h"
15 #include "jsgcinlines.h"
17 using namespace js;
18 using namespace js::gc;
20 inline HashNumber
21 DefaultHasher<WatchKey>::hash(const Lookup& key)
23 return DefaultHasher<JSObject*>::hash(key.object.get()) ^ HashId(key.id.get());
26 namespace {
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;
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)
40 MOZ_ASSERT(!p->value().held);
41 p->value().held = true;
44 ~AutoEntryHolder() {
45 if (gen != map.generation())
46 p = map.lookup(WatchKey(obj, id));
47 if (p)
48 p->value().held = false;
52 } /* anonymous namespace */
54 bool
55 WatchpointMap::init()
57 return map.init();
60 bool
61 WatchpointMap::watch(JSContext* cx, HandleObject obj, HandleId id,
62 JSWatchPointHandler handler, HandleObject closure)
64 MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id) || JSID_IS_SYMBOL(id));
66 if (!obj->setWatched(cx))
67 return false;
69 Watchpoint w(handler, closure, false);
70 if (!map.put(WatchKey(obj, id), w)) {
71 js_ReportOutOfMemory(cx);
72 return false;
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().
79 return true;
82 void
83 WatchpointMap::unwatch(JSObject* obj, jsid id,
84 JSWatchPointHandler* handlerp, JSObject** closurep)
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::ExposeObjectToActiveJS(p->value().closure);
93 *closurep = p->value().closure;
95 map.remove(p);
99 void
100 WatchpointMap::unwatchObject(JSObject* obj)
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();
109 void
110 WatchpointMap::clear()
112 map.clear();
115 bool
116 WatchpointMap::triggerWatchpoint(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
118 Map::Ptr p = map.lookup(WatchKey(obj, id));
119 if (!p || p->value().held)
120 return true;
122 AutoEntryHolder holder(cx, map, p);
124 /* Copy the entry, since GC would invalidate p. */
125 JSWatchPointHandler handler = p->value().handler;
126 RootedObject closure(cx, p->value().closure);
128 /* Determine the property's old value. */
129 Value old;
130 old.setUndefined();
131 if (obj->isNative()) {
132 NativeObject* nobj = &obj->as<NativeObject>();
133 if (Shape* shape = nobj->lookup(cx, id)) {
134 if (shape->hasSlot())
135 old = nobj->getSlot(shape->slot());
139 // Read barrier to prevent an incorrectly gray closure from escaping the
140 // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp
141 JS::ExposeObjectToActiveJS(closure);
143 /* Call the handler. */
144 return handler(cx, obj, id, old, vp.address(), closure);
147 bool
148 WatchpointMap::markCompartmentIteratively(JSCompartment* c, JSTracer* trc)
150 if (!c->watchpointMap)
151 return false;
152 return c->watchpointMap->markIteratively(trc);
155 bool
156 WatchpointMap::markIteratively(JSTracer* trc)
158 bool marked = false;
159 for (Map::Enum e(map); !e.empty(); e.popFront()) {
160 Map::Entry& entry = e.front();
161 JSObject* priorKeyObj = entry.key().object;
162 jsid priorKeyId(entry.key().id.get());
163 bool objectIsLive =
164 IsObjectMarked(const_cast<PreBarrieredObject*>(&entry.key().object));
165 if (objectIsLive || entry.value().held) {
166 if (!objectIsLive) {
167 MarkObject(trc, const_cast<PreBarrieredObject*>(&entry.key().object),
168 "held Watchpoint object");
169 marked = true;
172 MOZ_ASSERT(JSID_IS_STRING(priorKeyId) ||
173 JSID_IS_INT(priorKeyId) ||
174 JSID_IS_SYMBOL(priorKeyId));
175 MarkId(trc, const_cast<PreBarrieredId*>(&entry.key().id), "WatchKey::id");
177 if (entry.value().closure && !IsObjectMarked(&entry.value().closure)) {
178 MarkObject(trc, &entry.value().closure, "Watchpoint::closure");
179 marked = true;
182 /* We will sweep this entry in sweepAll if !objectIsLive. */
183 if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id)
184 e.rekeyFront(WatchKey(entry.key().object, entry.key().id));
187 return marked;
190 void
191 WatchpointMap::markAll(JSTracer* trc)
193 for (Map::Enum e(map); !e.empty(); e.popFront()) {
194 Map::Entry& entry = e.front();
195 WatchKey key = entry.key();
196 WatchKey prior = key;
197 MOZ_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id) || JSID_IS_SYMBOL(prior.id));
199 MarkObject(trc, const_cast<PreBarrieredObject*>(&key.object),
200 "held Watchpoint object");
201 MarkId(trc, const_cast<PreBarrieredId*>(&key.id), "WatchKey::id");
202 MarkObject(trc, &entry.value().closure, "Watchpoint::closure");
204 if (prior.object != key.object || prior.id != key.id)
205 e.rekeyFront(key);
209 void
210 WatchpointMap::sweepAll(JSRuntime* rt)
212 for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
213 if (WatchpointMap* wpmap = c->watchpointMap)
214 wpmap->sweep();
218 void
219 WatchpointMap::sweep()
221 for (Map::Enum e(map); !e.empty(); e.popFront()) {
222 Map::Entry& entry = e.front();
223 JSObject* obj(entry.key().object);
224 if (IsObjectAboutToBeFinalized(&obj)) {
225 MOZ_ASSERT(!entry.value().held);
226 e.removeFront();
227 } else if (obj != entry.key().object) {
228 e.rekeyFront(WatchKey(obj, entry.key().id));
233 void
234 WatchpointMap::traceAll(WeakMapTracer* trc)
236 JSRuntime* rt = trc->runtime;
237 for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) {
238 if (WatchpointMap* wpmap = comp->watchpointMap)
239 wpmap->trace(trc);
243 void
244 WatchpointMap::trace(WeakMapTracer* trc)
246 for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
247 Map::Entry& entry = r.front();
248 trc->callback(trc, nullptr,
249 JS::GCCellPtr(entry.key().object.get()),
250 JS::GCCellPtr(entry.value().closure.get()));