1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is an implementation of watchpoints for SpiderMonkey.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Jason Orendorff <jorendorff@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "jswatchpoint.h"
41 : #include "jsatom.h"
42 : #include "jsgcmark.h"
43 : #include "jsobjinlines.h"
44 :
45 : using namespace js;
46 : using namespace js::gc;
47 :
48 : inline HashNumber
49 9629 : DefaultHasher<WatchKey>::hash(const Lookup &key)
50 : {
51 9629 : return DefaultHasher<JSObject *>::hash(key.object.get()) ^ HashId(key.id.get());
52 : }
53 :
54 : class AutoEntryHolder {
55 : typedef WatchpointMap::Map Map;
56 : Map ↦
57 : Map::Ptr p;
58 : uint32_t gen;
59 : WatchKey key;
60 :
61 : public:
62 3825 : AutoEntryHolder(Map &map, Map::Ptr p)
63 3825 : : map(map), p(p), gen(map.generation()), key(p->key) {
64 3825 : JS_ASSERT(!p->value.held);
65 3825 : p->value.held = true;
66 3825 : }
67 :
68 7650 : ~AutoEntryHolder() {
69 3825 : if (gen != map.generation())
70 1509 : p = map.lookup(key);
71 3825 : if (p)
72 3825 : p->value.held = false;
73 3825 : }
74 : };
75 :
76 : bool
77 225 : WatchpointMap::init()
78 : {
79 225 : return map.init();
80 : }
81 :
82 : bool
83 3672 : WatchpointMap::watch(JSContext *cx, JSObject *obj, jsid id,
84 : JSWatchPointHandler handler, JSObject *closure)
85 : {
86 3672 : JS_ASSERT(id == js_CheckForStringIndex(id));
87 3672 : JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
88 :
89 3672 : if (!obj->setWatched(cx))
90 0 : return false;
91 :
92 7344 : Watchpoint w;
93 3672 : w.handler = handler;
94 3672 : w.closure = closure;
95 3672 : w.held = false;
96 3672 : if (!map.put(WatchKey(obj, id), w)) {
97 0 : js_ReportOutOfMemory(cx);
98 0 : return false;
99 : }
100 3672 : return true;
101 : }
102 :
103 : void
104 9 : WatchpointMap::unwatch(JSObject *obj, jsid id,
105 : JSWatchPointHandler *handlerp, JSObject **closurep)
106 : {
107 9 : JS_ASSERT(id == js_CheckForStringIndex(id));
108 9 : if (Map::Ptr p = map.lookup(WatchKey(obj, id))) {
109 9 : if (handlerp)
110 0 : *handlerp = p->value.handler;
111 9 : if (closurep)
112 0 : *closurep = p->value.closure;
113 9 : map.remove(p);
114 : }
115 9 : }
116 :
117 : void
118 0 : WatchpointMap::unwatchObject(JSObject *obj)
119 : {
120 0 : for (Map::Enum r(map); !r.empty(); r.popFront()) {
121 0 : Map::Entry &e = r.front();
122 0 : if (e.key.object == obj)
123 0 : r.removeFront();
124 : }
125 0 : }
126 :
127 : void
128 216 : WatchpointMap::clear()
129 : {
130 216 : map.clear();
131 216 : }
132 :
133 : bool
134 4439 : WatchpointMap::triggerWatchpoint(JSContext *cx, JSObject *obj, jsid id, Value *vp)
135 : {
136 4439 : JS_ASSERT(id == js_CheckForStringIndex(id));
137 4439 : Map::Ptr p = map.lookup(WatchKey(obj, id));
138 4439 : if (!p || p->value.held)
139 614 : return true;
140 :
141 7650 : AutoEntryHolder holder(map, p);
142 :
143 : /* Copy the entry, since GC would invalidate p. */
144 3825 : JSWatchPointHandler handler = p->value.handler;
145 3825 : JSObject *closure = p->value.closure;
146 :
147 : /* Determine the property's old value. */
148 : Value old;
149 3825 : old.setUndefined();
150 3825 : if (obj->isNative()) {
151 3825 : if (const Shape *shape = obj->nativeLookup(cx, id)) {
152 702 : if (shape->hasSlot())
153 675 : old = obj->nativeGetSlot(shape->slot());
154 : }
155 : }
156 :
157 : /* Call the handler. */
158 3825 : return handler(cx, obj, id, old, vp, closure);
159 : }
160 :
161 : bool
162 77192 : WatchpointMap::markAllIteratively(JSTracer *trc)
163 : {
164 77192 : JSRuntime *rt = trc->runtime;
165 77192 : bool mutated = false;
166 245879 : for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
167 168687 : if (c->watchpointMap)
168 468 : mutated |= c->watchpointMap->markIteratively(trc);
169 : }
170 77192 : return mutated;
171 : }
172 :
173 : bool
174 468 : WatchpointMap::markIteratively(JSTracer *trc)
175 : {
176 468 : bool marked = false;
177 504 : for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
178 36 : Map::Entry &e = r.front();
179 36 : bool objectIsLive = !IsAboutToBeFinalized(e.key.object);
180 36 : if (objectIsLive || e.value.held) {
181 18 : if (!objectIsLive) {
182 0 : HeapPtrObject tmp(e.key.object);
183 0 : MarkObject(trc, &tmp, "held Watchpoint object");
184 0 : JS_ASSERT(tmp == e.key.object);
185 0 : marked = true;
186 : }
187 :
188 18 : const HeapId &id = e.key.id;
189 18 : JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
190 36 : HeapId tmp(id.get());
191 18 : MarkId(trc, &tmp, "WatchKey::id");
192 18 : JS_ASSERT(tmp.get() == id.get());
193 :
194 18 : if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) {
195 0 : MarkObject(trc, &e.value.closure, "Watchpoint::closure");
196 0 : marked = true;
197 : }
198 : }
199 : }
200 468 : return marked;
201 : }
202 :
203 : void
204 0 : WatchpointMap::markAll(JSTracer *trc)
205 : {
206 0 : for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
207 0 : Map::Entry &e = r.front();
208 0 : HeapPtrObject tmpObj(e.key.object);
209 0 : MarkObject(trc, &tmpObj, "held Watchpoint object");
210 0 : JS_ASSERT(tmpObj == e.key.object);
211 :
212 0 : const HeapId &id = e.key.id;
213 0 : JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
214 0 : HeapId tmpId(id.get());
215 0 : MarkId(trc, &tmpId, "WatchKey::id");
216 0 : JS_ASSERT(tmpId.get() == id.get());
217 :
218 0 : MarkObject(trc, &e.value.closure, "Watchpoint::closure");
219 : }
220 0 : }
221 :
222 : void
223 38427 : WatchpointMap::sweepAll(JSRuntime *rt)
224 : {
225 122124 : for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
226 83697 : if (WatchpointMap *wpmap = c->watchpointMap)
227 234 : wpmap->sweep();
228 : }
229 38427 : }
230 :
231 : void
232 234 : WatchpointMap::sweep()
233 : {
234 252 : for (Map::Enum r(map); !r.empty(); r.popFront()) {
235 18 : Map::Entry &e = r.front();
236 18 : if (IsAboutToBeFinalized(e.key.object)) {
237 9 : JS_ASSERT(!e.value.held);
238 9 : r.removeFront();
239 : }
240 : }
241 234 : }
242 :
243 : void
244 0 : WatchpointMap::traceAll(WeakMapTracer *trc)
245 : {
246 0 : JSRuntime *rt = trc->runtime;
247 0 : for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
248 0 : if (WatchpointMap *wpmap = (*c)->watchpointMap)
249 0 : wpmap->trace(trc);
250 : }
251 0 : }
252 :
253 : void
254 0 : WatchpointMap::trace(WeakMapTracer *trc)
255 : {
256 0 : for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
257 0 : Map::Entry &e = r.front();
258 : trc->callback(trc, NULL,
259 0 : e.key.object.get(), JSTRACE_OBJECT,
260 0 : e.value.closure.get(), JSTRACE_OBJECT);
261 : }
262 0 : }
|