1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/jswatchpoint.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,249 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "jswatchpoint.h" 1.11 + 1.12 +#include "jsatom.h" 1.13 +#include "jscompartment.h" 1.14 +#include "jsfriendapi.h" 1.15 + 1.16 +#include "gc/Marking.h" 1.17 + 1.18 +#include "jsgcinlines.h" 1.19 + 1.20 +using namespace js; 1.21 +using namespace js::gc; 1.22 + 1.23 +inline HashNumber 1.24 +DefaultHasher<WatchKey>::hash(const Lookup &key) 1.25 +{ 1.26 + return DefaultHasher<JSObject *>::hash(key.object.get()) ^ HashId(key.id.get()); 1.27 +} 1.28 + 1.29 +namespace { 1.30 + 1.31 +class AutoEntryHolder { 1.32 + typedef WatchpointMap::Map Map; 1.33 + Map ↦ 1.34 + Map::Ptr p; 1.35 + uint32_t gen; 1.36 + RootedObject obj; 1.37 + RootedId id; 1.38 + 1.39 + public: 1.40 + AutoEntryHolder(JSContext *cx, Map &map, Map::Ptr p) 1.41 + : map(map), p(p), gen(map.generation()), obj(cx, p->key().object), id(cx, p->key().id) 1.42 + { 1.43 + JS_ASSERT(!p->value().held); 1.44 + p->value().held = true; 1.45 + } 1.46 + 1.47 + ~AutoEntryHolder() { 1.48 + if (gen != map.generation()) 1.49 + p = map.lookup(WatchKey(obj, id)); 1.50 + if (p) 1.51 + p->value().held = false; 1.52 + } 1.53 +}; 1.54 + 1.55 +} /* anonymous namespace */ 1.56 + 1.57 +bool 1.58 +WatchpointMap::init() 1.59 +{ 1.60 + return map.init(); 1.61 +} 1.62 + 1.63 +bool 1.64 +WatchpointMap::watch(JSContext *cx, HandleObject obj, HandleId id, 1.65 + JSWatchPointHandler handler, HandleObject closure) 1.66 +{ 1.67 + JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); 1.68 + 1.69 + if (!obj->setWatched(cx)) 1.70 + return false; 1.71 + 1.72 + Watchpoint w(handler, closure, false); 1.73 + if (!map.put(WatchKey(obj, id), w)) { 1.74 + js_ReportOutOfMemory(cx); 1.75 + return false; 1.76 + } 1.77 + /* 1.78 + * For generational GC, we don't need to post-barrier writes to the 1.79 + * hashtable here because we mark all watchpoints as part of root marking in 1.80 + * markAll(). 1.81 + */ 1.82 + return true; 1.83 +} 1.84 + 1.85 +void 1.86 +WatchpointMap::unwatch(JSObject *obj, jsid id, 1.87 + JSWatchPointHandler *handlerp, JSObject **closurep) 1.88 +{ 1.89 + if (Map::Ptr p = map.lookup(WatchKey(obj, id))) { 1.90 + if (handlerp) 1.91 + *handlerp = p->value().handler; 1.92 + if (closurep) { 1.93 + // Read barrier to prevent an incorrectly gray closure from escaping the 1.94 + // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp 1.95 + JS::ExposeGCThingToActiveJS(p->value().closure, JSTRACE_OBJECT); 1.96 + *closurep = p->value().closure; 1.97 + } 1.98 + map.remove(p); 1.99 + } 1.100 +} 1.101 + 1.102 +void 1.103 +WatchpointMap::unwatchObject(JSObject *obj) 1.104 +{ 1.105 + for (Map::Enum e(map); !e.empty(); e.popFront()) { 1.106 + Map::Entry &entry = e.front(); 1.107 + if (entry.key().object == obj) 1.108 + e.removeFront(); 1.109 + } 1.110 +} 1.111 + 1.112 +void 1.113 +WatchpointMap::clear() 1.114 +{ 1.115 + map.clear(); 1.116 +} 1.117 + 1.118 +bool 1.119 +WatchpointMap::triggerWatchpoint(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp) 1.120 +{ 1.121 + Map::Ptr p = map.lookup(WatchKey(obj, id)); 1.122 + if (!p || p->value().held) 1.123 + return true; 1.124 + 1.125 + AutoEntryHolder holder(cx, map, p); 1.126 + 1.127 + /* Copy the entry, since GC would invalidate p. */ 1.128 + JSWatchPointHandler handler = p->value().handler; 1.129 + RootedObject closure(cx, p->value().closure); 1.130 + 1.131 + /* Determine the property's old value. */ 1.132 + Value old; 1.133 + old.setUndefined(); 1.134 + if (obj->isNative()) { 1.135 + if (Shape *shape = obj->nativeLookup(cx, id)) { 1.136 + if (shape->hasSlot()) 1.137 + old = obj->nativeGetSlot(shape->slot()); 1.138 + } 1.139 + } 1.140 + 1.141 + // Read barrier to prevent an incorrectly gray closure from escaping the 1.142 + // watchpoint. See the comment before UnmarkGrayChildren in gc/Marking.cpp 1.143 + JS::ExposeGCThingToActiveJS(closure, JSTRACE_OBJECT); 1.144 + 1.145 + /* Call the handler. */ 1.146 + return handler(cx, obj, id, old, vp.address(), closure); 1.147 +} 1.148 + 1.149 +bool 1.150 +WatchpointMap::markCompartmentIteratively(JSCompartment *c, JSTracer *trc) 1.151 +{ 1.152 + if (!c->watchpointMap) 1.153 + return false; 1.154 + return c->watchpointMap->markIteratively(trc); 1.155 +} 1.156 + 1.157 +bool 1.158 +WatchpointMap::markIteratively(JSTracer *trc) 1.159 +{ 1.160 + bool marked = false; 1.161 + for (Map::Enum e(map); !e.empty(); e.popFront()) { 1.162 + Map::Entry &entry = e.front(); 1.163 + JSObject *priorKeyObj = entry.key().object; 1.164 + jsid priorKeyId(entry.key().id.get()); 1.165 + bool objectIsLive = 1.166 + IsObjectMarked(const_cast<EncapsulatedPtrObject *>(&entry.key().object)); 1.167 + if (objectIsLive || entry.value().held) { 1.168 + if (!objectIsLive) { 1.169 + MarkObject(trc, const_cast<EncapsulatedPtrObject *>(&entry.key().object), 1.170 + "held Watchpoint object"); 1.171 + marked = true; 1.172 + } 1.173 + 1.174 + JS_ASSERT(JSID_IS_STRING(priorKeyId) || JSID_IS_INT(priorKeyId)); 1.175 + MarkId(trc, const_cast<EncapsulatedId *>(&entry.key().id), "WatchKey::id"); 1.176 + 1.177 + if (entry.value().closure && !IsObjectMarked(&entry.value().closure)) { 1.178 + MarkObject(trc, &entry.value().closure, "Watchpoint::closure"); 1.179 + marked = true; 1.180 + } 1.181 + 1.182 + /* We will sweep this entry in sweepAll if !objectIsLive. */ 1.183 + if (priorKeyObj != entry.key().object || priorKeyId != entry.key().id) 1.184 + e.rekeyFront(WatchKey(entry.key().object, entry.key().id)); 1.185 + } 1.186 + } 1.187 + return marked; 1.188 +} 1.189 + 1.190 +void 1.191 +WatchpointMap::markAll(JSTracer *trc) 1.192 +{ 1.193 + for (Map::Enum e(map); !e.empty(); e.popFront()) { 1.194 + Map::Entry &entry = e.front(); 1.195 + WatchKey key = entry.key(); 1.196 + WatchKey prior = key; 1.197 + JS_ASSERT(JSID_IS_STRING(prior.id) || JSID_IS_INT(prior.id)); 1.198 + 1.199 + MarkObject(trc, const_cast<EncapsulatedPtrObject *>(&key.object), 1.200 + "held Watchpoint object"); 1.201 + MarkId(trc, const_cast<EncapsulatedId *>(&key.id), "WatchKey::id"); 1.202 + MarkObject(trc, &entry.value().closure, "Watchpoint::closure"); 1.203 + 1.204 + if (prior.object != key.object || prior.id != key.id) 1.205 + e.rekeyFront(key); 1.206 + } 1.207 +} 1.208 + 1.209 +void 1.210 +WatchpointMap::sweepAll(JSRuntime *rt) 1.211 +{ 1.212 + for (GCCompartmentsIter c(rt); !c.done(); c.next()) { 1.213 + if (WatchpointMap *wpmap = c->watchpointMap) 1.214 + wpmap->sweep(); 1.215 + } 1.216 +} 1.217 + 1.218 +void 1.219 +WatchpointMap::sweep() 1.220 +{ 1.221 + for (Map::Enum e(map); !e.empty(); e.popFront()) { 1.222 + Map::Entry &entry = e.front(); 1.223 + JSObject *obj(entry.key().object); 1.224 + if (IsObjectAboutToBeFinalized(&obj)) { 1.225 + JS_ASSERT(!entry.value().held); 1.226 + e.removeFront(); 1.227 + } else if (obj != entry.key().object) { 1.228 + e.rekeyFront(WatchKey(obj, entry.key().id)); 1.229 + } 1.230 + } 1.231 +} 1.232 + 1.233 +void 1.234 +WatchpointMap::traceAll(WeakMapTracer *trc) 1.235 +{ 1.236 + JSRuntime *rt = trc->runtime; 1.237 + for (CompartmentsIter comp(rt, SkipAtoms); !comp.done(); comp.next()) { 1.238 + if (WatchpointMap *wpmap = comp->watchpointMap) 1.239 + wpmap->trace(trc); 1.240 + } 1.241 +} 1.242 + 1.243 +void 1.244 +WatchpointMap::trace(WeakMapTracer *trc) 1.245 +{ 1.246 + for (Map::Range r = map.all(); !r.empty(); r.popFront()) { 1.247 + Map::Entry &entry = r.front(); 1.248 + trc->callback(trc, nullptr, 1.249 + entry.key().object.get(), JSTRACE_OBJECT, 1.250 + entry.value().closure.get(), JSTRACE_OBJECT); 1.251 + } 1.252 +}