js/src/jswatchpoint.cpp

changeset 0
6474c204b198
     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 &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 +}

mercurial