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 MOZ_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 MOZ_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 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
);
148 WatchpointMap::markCompartmentIteratively(JSCompartment
* c
, JSTracer
* trc
)
150 if (!c
->watchpointMap
)
152 return c
->watchpointMap
->markIteratively(trc
);
156 WatchpointMap::markIteratively(JSTracer
* trc
)
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());
164 IsObjectMarked(const_cast<PreBarrieredObject
*>(&entry
.key().object
));
165 if (objectIsLive
|| entry
.value().held
) {
167 MarkObject(trc
, const_cast<PreBarrieredObject
*>(&entry
.key().object
),
168 "held Watchpoint object");
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");
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
));
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
)
210 WatchpointMap::sweepAll(JSRuntime
* rt
)
212 for (GCCompartmentsIter
c(rt
); !c
.done(); c
.next()) {
213 if (WatchpointMap
* wpmap
= c
->watchpointMap
)
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
);
227 } else if (obj
!= entry
.key().object
) {
228 e
.rekeyFront(WatchKey(obj
, entry
.key().id
));
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
)
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()));