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"
10 #include "jscompartment.h"
11 #include "jsfriendapi.h"
13 #include "gc/Marking.h"
15 #include "jsgcinlines.h"
18 using namespace js::gc
;
21 DefaultHasher
<WatchKey
>::hash(const Lookup
& key
)
23 return DefaultHasher
<JSObject
*>::hash(key
.object
.get()) ^ HashId(key
.id
.get());
28 class AutoEntryHolder
{
29 typedef WatchpointMap::Map Map
;
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 JS_ASSERT(!p
->value().held
);
41 p
->value().held
= true;
45 if (gen
!= map
.generation())
46 p
= map
.lookup(WatchKey(obj
, id
));
48 p
->value().held
= false;
52 } /* anonymous namespace */
61 WatchpointMap::watch(JSContext
* cx
, HandleObject obj
, HandleId id
,
62 JSWatchPointHandler handler
, HandleObject closure
)
64 JS_ASSERT(JSID_IS_STRING(id
) || JSID_IS_INT(id
) || JSID_IS_SYMBOL(id
));
66 if (!obj
->setWatched(cx
))
69 Watchpoint
w(handler
, closure
, false);
70 if (!map
.put(WatchKey(obj
, id
), w
)) {
71 js_ReportOutOfMemory(cx
);
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
83 WatchpointMap::unwatch(JSObject
* obj
, jsid id
,
84 JSWatchPointHandler
* handlerp
, JSObject
** closurep
)
86 if (Map::Ptr p
= map
.lookup(WatchKey(obj
, id
))) {
88 *handlerp
= p
->value().handler
;
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
;
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
)
110 WatchpointMap::clear()
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
)
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. */
131 if (obj
->isNative()) {
132 if (Shape
* shape
= obj
->nativeLookup(cx
, id
)) {
133 if (shape
->hasSlot())
134 old
= obj
->nativeGetSlot(shape
->slot());
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::ExposeObjectToActiveJS(closure
);
142 /* Call the handler. */
143 return handler(cx
, obj
, id
, old
, vp
.address(), closure
);
147 WatchpointMap::markCompartmentIteratively(JSCompartment
* c
, JSTracer
* trc
)
149 if (!c
->watchpointMap
)
151 return c
->watchpointMap
->markIteratively(trc
);
155 WatchpointMap::markIteratively(JSTracer
* trc
)
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());
163 IsObjectMarked(const_cast<PreBarrieredObject
*>(&entry
.key().object
));
164 if (objectIsLive
|| entry
.value().held
) {
166 MarkObject(trc
, const_cast<PreBarrieredObject
*>(&entry
.key().object
),
167 "held Watchpoint object");
171 JS_ASSERT(JSID_IS_STRING(priorKeyId
) ||
172 JSID_IS_INT(priorKeyId
) ||
173 JSID_IS_SYMBOL(priorKeyId
));
174 MarkId(trc
, const_cast<PreBarrieredId
*>(&entry
.key().id
), "WatchKey::id");
176 if (entry
.value().closure
&& !IsObjectMarked(&entry
.value().closure
)) {
177 MarkObject(trc
, &entry
.value().closure
, "Watchpoint::closure");
181 /* We will sweep this entry in sweepAll if !objectIsLive. */
182 if (priorKeyObj
!= entry
.key().object
|| priorKeyId
!= entry
.key().id
)
183 e
.rekeyFront(WatchKey(entry
.key().object
, entry
.key().id
));
190 WatchpointMap::markAll(JSTracer
* trc
)
192 for (Map::Enum
e(map
); !e
.empty(); e
.popFront()) {
193 Map::Entry
& entry
= e
.front();
194 WatchKey key
= entry
.key();
195 WatchKey prior
= key
;
196 JS_ASSERT(JSID_IS_STRING(prior
.id
) || JSID_IS_INT(prior
.id
) || JSID_IS_SYMBOL(prior
.id
));
198 MarkObject(trc
, const_cast<PreBarrieredObject
*>(&key
.object
),
199 "held Watchpoint object");
200 MarkId(trc
, const_cast<PreBarrieredId
*>(&key
.id
), "WatchKey::id");
201 MarkObject(trc
, &entry
.value().closure
, "Watchpoint::closure");
203 if (prior
.object
!= key
.object
|| prior
.id
!= key
.id
)
209 WatchpointMap::sweepAll(JSRuntime
* rt
)
211 for (GCCompartmentsIter
c(rt
); !c
.done(); c
.next()) {
212 if (WatchpointMap
* wpmap
= c
->watchpointMap
)
218 WatchpointMap::sweep()
220 for (Map::Enum
e(map
); !e
.empty(); e
.popFront()) {
221 Map::Entry
& entry
= e
.front();
222 JSObject
* obj(entry
.key().object
);
223 if (IsObjectAboutToBeFinalized(&obj
)) {
224 JS_ASSERT(!entry
.value().held
);
226 } else if (obj
!= entry
.key().object
) {
227 e
.rekeyFront(WatchKey(obj
, entry
.key().id
));
233 WatchpointMap::traceAll(WeakMapTracer
* trc
)
235 JSRuntime
* rt
= trc
->runtime
;
236 for (CompartmentsIter
comp(rt
, SkipAtoms
); !comp
.done(); comp
.next()) {
237 if (WatchpointMap
* wpmap
= comp
->watchpointMap
)
243 WatchpointMap::trace(WeakMapTracer
* trc
)
245 for (Map::Range r
= map
.all(); !r
.empty(); r
.popFront()) {
246 Map::Entry
& entry
= r
.front();
247 trc
->callback(trc
, nullptr,
248 entry
.key().object
.get(), JSTRACE_OBJECT
,
249 entry
.value().closure
.get(), JSTRACE_OBJECT
);