js/src/vm/Debugger.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/Debugger.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,768 @@
     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 +#ifndef vm_Debugger_h
    1.11 +#define vm_Debugger_h
    1.12 +
    1.13 +#include "mozilla/LinkedList.h"
    1.14 +
    1.15 +#include "jsclist.h"
    1.16 +#include "jscntxt.h"
    1.17 +#include "jscompartment.h"
    1.18 +#include "jsweakmap.h"
    1.19 +
    1.20 +#include "gc/Barrier.h"
    1.21 +#include "js/HashTable.h"
    1.22 +#include "vm/GlobalObject.h"
    1.23 +
    1.24 +namespace js {
    1.25 +
    1.26 +class Breakpoint;
    1.27 +
    1.28 +/*
    1.29 + * A weakmap that supports the keys being in different compartments to the
    1.30 + * values, although all values must be in the same compartment.
    1.31 + *
    1.32 + * The Key and Value classes must support the compartment() method.
    1.33 + *
    1.34 + * The purpose of this is to allow the garbage collector to easily find edges
    1.35 + * from debugee object compartments to debugger compartments when calculating
    1.36 + * the compartment groups.  Note that these edges are the inverse of the edges
    1.37 + * stored in the cross compartment map.
    1.38 + *
    1.39 + * The current implementation results in all debuggee object compartments being
    1.40 + * swept in the same group as the debugger.  This is a conservative approach,
    1.41 + * and compartments may be unnecessarily grouped, however it results in a
    1.42 + * simpler and faster implementation.
    1.43 + *
    1.44 + * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
    1.45 + * debugger compartments. If it is false, we assert that such entries are never
    1.46 + * created.
    1.47 + */
    1.48 +template <class Key, class Value, bool InvisibleKeysOk=false>
    1.49 +class DebuggerWeakMap : private WeakMap<Key, Value, DefaultHasher<Key> >
    1.50 +{
    1.51 +  private:
    1.52 +    typedef HashMap<JS::Zone *,
    1.53 +                    uintptr_t,
    1.54 +                    DefaultHasher<JS::Zone *>,
    1.55 +                    RuntimeAllocPolicy> CountMap;
    1.56 +
    1.57 +    CountMap zoneCounts;
    1.58 +
    1.59 +  public:
    1.60 +    typedef WeakMap<Key, Value, DefaultHasher<Key> > Base;
    1.61 +    explicit DebuggerWeakMap(JSContext *cx)
    1.62 +        : Base(cx), zoneCounts(cx->runtime()) { }
    1.63 +
    1.64 +  public:
    1.65 +    /* Expose those parts of HashMap public interface that are used by Debugger methods. */
    1.66 +
    1.67 +    typedef typename Base::Entry Entry;
    1.68 +    typedef typename Base::Ptr Ptr;
    1.69 +    typedef typename Base::AddPtr AddPtr;
    1.70 +    typedef typename Base::Range Range;
    1.71 +    typedef typename Base::Enum Enum;
    1.72 +    typedef typename Base::Lookup Lookup;
    1.73 +
    1.74 +    /* Expose WeakMap public interface */
    1.75 +
    1.76 +    using Base::clearWithoutCallingDestructors;
    1.77 +    using Base::lookupForAdd;
    1.78 +    using Base::all;
    1.79 +    using Base::trace;
    1.80 +
    1.81 +    bool init(uint32_t len = 16) {
    1.82 +        return Base::init(len) && zoneCounts.init();
    1.83 +    }
    1.84 +
    1.85 +    template<typename KeyInput, typename ValueInput>
    1.86 +    bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    1.87 +        JS_ASSERT(v->compartment() == Base::compartment);
    1.88 +        JS_ASSERT(!k->compartment()->options_.mergeable());
    1.89 +        JS_ASSERT_IF(!InvisibleKeysOk, !k->compartment()->options_.invisibleToDebugger());
    1.90 +        JS_ASSERT(!Base::has(k));
    1.91 +        if (!incZoneCount(k->zone()))
    1.92 +            return false;
    1.93 +        bool ok = Base::relookupOrAdd(p, k, v);
    1.94 +        if (!ok)
    1.95 +            decZoneCount(k->zone());
    1.96 +        return ok;
    1.97 +    }
    1.98 +
    1.99 +    void remove(const Lookup &l) {
   1.100 +        JS_ASSERT(Base::has(l));
   1.101 +        Base::remove(l);
   1.102 +        decZoneCount(l->zone());
   1.103 +    }
   1.104 +
   1.105 +  public:
   1.106 +    void markKeys(JSTracer *tracer) {
   1.107 +        for (Enum e(*static_cast<Base *>(this)); !e.empty(); e.popFront()) {
   1.108 +            Key key = e.front().key();
   1.109 +            gc::Mark(tracer, &key, "Debugger WeakMap key");
   1.110 +            if (key != e.front().key())
   1.111 +                e.rekeyFront(key);
   1.112 +            key.unsafeSet(nullptr);
   1.113 +        }
   1.114 +    }
   1.115 +
   1.116 +    bool hasKeyInZone(JS::Zone *zone) {
   1.117 +        CountMap::Ptr p = zoneCounts.lookup(zone);
   1.118 +        JS_ASSERT_IF(p, p->value() > 0);
   1.119 +        return p;
   1.120 +    }
   1.121 +
   1.122 +  private:
   1.123 +    /* Override sweep method to also update our edge cache. */
   1.124 +    void sweep() {
   1.125 +        for (Enum e(*static_cast<Base *>(this)); !e.empty(); e.popFront()) {
   1.126 +            Key k(e.front().key());
   1.127 +            if (gc::IsAboutToBeFinalized(&k)) {
   1.128 +                e.removeFront();
   1.129 +                decZoneCount(k->zone());
   1.130 +            }
   1.131 +        }
   1.132 +        Base::assertEntriesNotAboutToBeFinalized();
   1.133 +    }
   1.134 +
   1.135 +    bool incZoneCount(JS::Zone *zone) {
   1.136 +        CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
   1.137 +        if (!p)
   1.138 +            return false;
   1.139 +        ++p->value();
   1.140 +        return true;
   1.141 +    }
   1.142 +
   1.143 +    void decZoneCount(JS::Zone *zone) {
   1.144 +        CountMap::Ptr p = zoneCounts.lookup(zone);
   1.145 +        JS_ASSERT(p);
   1.146 +        JS_ASSERT(p->value() > 0);
   1.147 +        --p->value();
   1.148 +        if (p->value() == 0)
   1.149 +            zoneCounts.remove(zone);
   1.150 +    }
   1.151 +};
   1.152 +
   1.153 +/*
   1.154 + * Env is the type of what ES5 calls "lexical environments" (runtime
   1.155 + * activations of lexical scopes). This is currently just JSObject, and is
   1.156 + * implemented by Call, Block, With, and DeclEnv objects, among others--but
   1.157 + * environments and objects are really two different concepts.
   1.158 + */
   1.159 +typedef JSObject Env;
   1.160 +
   1.161 +class Debugger : private mozilla::LinkedListElement<Debugger>
   1.162 +{
   1.163 +    friend class Breakpoint;
   1.164 +    friend class mozilla::LinkedListElement<Debugger>;
   1.165 +    friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
   1.166 +
   1.167 +  public:
   1.168 +    enum Hook {
   1.169 +        OnDebuggerStatement,
   1.170 +        OnExceptionUnwind,
   1.171 +        OnNewScript,
   1.172 +        OnEnterFrame,
   1.173 +        OnNewGlobalObject,
   1.174 +        HookCount
   1.175 +    };
   1.176 +    enum {
   1.177 +        JSSLOT_DEBUG_PROTO_START,
   1.178 +        JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
   1.179 +        JSSLOT_DEBUG_ENV_PROTO,
   1.180 +        JSSLOT_DEBUG_OBJECT_PROTO,
   1.181 +        JSSLOT_DEBUG_SCRIPT_PROTO,
   1.182 +        JSSLOT_DEBUG_SOURCE_PROTO,
   1.183 +        JSSLOT_DEBUG_MEMORY_PROTO,
   1.184 +        JSSLOT_DEBUG_PROTO_STOP,
   1.185 +        JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
   1.186 +        JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
   1.187 +        JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
   1.188 +        JSSLOT_DEBUG_COUNT
   1.189 +    };
   1.190 +  private:
   1.191 +    HeapPtrObject object;               /* The Debugger object. Strong reference. */
   1.192 +    GlobalObjectSet debuggees;          /* Debuggee globals. Cross-compartment weak references. */
   1.193 +    js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
   1.194 +    bool enabled;
   1.195 +    JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
   1.196 +
   1.197 +    /*
   1.198 +     * If this Debugger is enabled, and has a onNewGlobalObject handler, then
   1.199 +     * this link is inserted into the circular list headed by
   1.200 +     * JSRuntime::onNewGlobalObjectWatchers. Otherwise, this is set to a
   1.201 +     * singleton cycle.
   1.202 +     */
   1.203 +    JSCList onNewGlobalObjectWatchersLink;
   1.204 +
   1.205 +    /*
   1.206 +     * Map from stack frames that are currently on the stack to Debugger.Frame
   1.207 +     * instances.
   1.208 +     *
   1.209 +     * The keys are always live stack frames. We drop them from this map as
   1.210 +     * soon as they leave the stack (see slowPathOnLeaveFrame) and in
   1.211 +     * removeDebuggee.
   1.212 +     *
   1.213 +     * We don't trace the keys of this map (the frames are on the stack and
   1.214 +     * thus necessarily live), but we do trace the values. It's like a WeakMap
   1.215 +     * that way, but since stack frames are not gc-things, the implementation
   1.216 +     * has to be different.
   1.217 +     */
   1.218 +    typedef HashMap<AbstractFramePtr,
   1.219 +                    RelocatablePtrObject,
   1.220 +                    DefaultHasher<AbstractFramePtr>,
   1.221 +                    RuntimeAllocPolicy> FrameMap;
   1.222 +    FrameMap frames;
   1.223 +
   1.224 +    /* An ephemeral map from JSScript* to Debugger.Script instances. */
   1.225 +    typedef DebuggerWeakMap<EncapsulatedPtrScript, RelocatablePtrObject> ScriptWeakMap;
   1.226 +    ScriptWeakMap scripts;
   1.227 +
   1.228 +    /* The map from debuggee source script objects to their Debugger.Source instances. */
   1.229 +    typedef DebuggerWeakMap<EncapsulatedPtrObject, RelocatablePtrObject, true> SourceWeakMap;
   1.230 +    SourceWeakMap sources;
   1.231 +
   1.232 +    /* The map from debuggee objects to their Debugger.Object instances. */
   1.233 +    typedef DebuggerWeakMap<EncapsulatedPtrObject, RelocatablePtrObject> ObjectWeakMap;
   1.234 +    ObjectWeakMap objects;
   1.235 +
   1.236 +    /* The map from debuggee Envs to Debugger.Environment instances. */
   1.237 +    ObjectWeakMap environments;
   1.238 +
   1.239 +    class FrameRange;
   1.240 +    class ScriptQuery;
   1.241 +
   1.242 +    bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
   1.243 +    bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
   1.244 +                           AutoDebugModeInvalidation &invalidate);
   1.245 +    void cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global,
   1.246 +                                            AutoDebugModeInvalidation &invalidate,
   1.247 +                                            GlobalObjectSet::Enum *compartmentEnum,
   1.248 +                                            GlobalObjectSet::Enum *debugEnu);
   1.249 +    bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
   1.250 +                              GlobalObjectSet::Enum *compartmentEnum,
   1.251 +                              GlobalObjectSet::Enum *debugEnum);
   1.252 +    bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
   1.253 +                              AutoDebugModeInvalidation &invalidate,
   1.254 +                              GlobalObjectSet::Enum *compartmentEnum,
   1.255 +                              GlobalObjectSet::Enum *debugEnum);
   1.256 +    void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
   1.257 +                                     GlobalObjectSet::Enum *compartmentEnum,
   1.258 +                                     GlobalObjectSet::Enum *debugEnum);
   1.259 +    void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
   1.260 +                                     AutoDebugModeInvalidation &invalidate,
   1.261 +                                     GlobalObjectSet::Enum *compartmentEnum,
   1.262 +                                     GlobalObjectSet::Enum *debugEnum);
   1.263 +
   1.264 +    /*
   1.265 +     * Cope with an error or exception in a debugger hook.
   1.266 +     *
   1.267 +     * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
   1.268 +     * addition, vp is given, then parse the value returned by
   1.269 +     * uncaughtExceptionHook as a resumption value.
   1.270 +     *
   1.271 +     * If there is no uncaughtExceptionHook, or if it fails, report and clear
   1.272 +     * the pending exception on ac.context and return JSTRAP_ERROR.
   1.273 +     *
   1.274 +     * This always calls ac.leave(); ac is a parameter because this method must
   1.275 +     * do some things in the debugger compartment and some things in the
   1.276 +     * debuggee compartment.
   1.277 +     */
   1.278 +    JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment> &ac, bool callHook);
   1.279 +    JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment> &ac, MutableHandleValue vp, bool callHook);
   1.280 +
   1.281 +    JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment> &ac,
   1.282 +                                               MutableHandleValue *vp, bool callHook);
   1.283 +
   1.284 +    /*
   1.285 +     * Handle the result of a hook that is expected to return a resumption
   1.286 +     * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
   1.287 +     * when we return from a debugging hook to debuggee code. The interpreter wants
   1.288 +     * a (JSTrapStatus, Value) pair telling it how to proceed.
   1.289 +     *
   1.290 +     * Precondition: ac is entered. We are in the debugger compartment.
   1.291 +     *
   1.292 +     * Postcondition: This called ac.leave(). See handleUncaughtException.
   1.293 +     *
   1.294 +     * If ok is false, the hook failed. If an exception is pending in
   1.295 +     * ac.context(), return handleUncaughtException(ac, vp, callhook).
   1.296 +     * Otherwise just return JSTRAP_ERROR.
   1.297 +     *
   1.298 +     * If ok is true, there must be no exception pending in ac.context(). rv may be:
   1.299 +     *     undefined - Return JSTRAP_CONTINUE to continue execution normally.
   1.300 +     *     {return: value} or {throw: value} - Call unwrapDebuggeeValue to
   1.301 +     *         unwrap value. Store the result in *vp and return JSTRAP_RETURN
   1.302 +     *         or JSTRAP_THROW. The interpreter will force the current frame to
   1.303 +     *         return or throw an exception.
   1.304 +     *     null - Return JSTRAP_ERROR to terminate the debuggee with an
   1.305 +     *         uncatchable error.
   1.306 +     *     anything else - Make a new TypeError the pending exception and
   1.307 +     *         return handleUncaughtException(ac, vp, callHook).
   1.308 +     */
   1.309 +    JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment> &ac, bool ok, const Value &rv,
   1.310 +                                      MutableHandleValue vp, bool callHook = true);
   1.311 +
   1.312 +    GlobalObject *unwrapDebuggeeArgument(JSContext *cx, const Value &v);
   1.313 +
   1.314 +    static void traceObject(JSTracer *trc, JSObject *obj);
   1.315 +    void trace(JSTracer *trc);
   1.316 +    static void finalize(FreeOp *fop, JSObject *obj);
   1.317 +    void markKeysInCompartment(JSTracer *tracer);
   1.318 +
   1.319 +    static const Class jsclass;
   1.320 +
   1.321 +    static Debugger *fromThisValue(JSContext *cx, const CallArgs &ca, const char *fnname);
   1.322 +    static bool getEnabled(JSContext *cx, unsigned argc, Value *vp);
   1.323 +    static bool setEnabled(JSContext *cx, unsigned argc, Value *vp);
   1.324 +    static bool getHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which);
   1.325 +    static bool setHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which);
   1.326 +    static bool getOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp);
   1.327 +    static bool setOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp);
   1.328 +    static bool getOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
   1.329 +    static bool setOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
   1.330 +    static bool getOnNewScript(JSContext *cx, unsigned argc, Value *vp);
   1.331 +    static bool setOnNewScript(JSContext *cx, unsigned argc, Value *vp);
   1.332 +    static bool getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
   1.333 +    static bool setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
   1.334 +    static bool getOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
   1.335 +    static bool setOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
   1.336 +    static bool getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
   1.337 +    static bool setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
   1.338 +    static bool getMemory(JSContext *cx, unsigned argc, Value *vp);
   1.339 +    static bool addDebuggee(JSContext *cx, unsigned argc, Value *vp);
   1.340 +    static bool addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp);
   1.341 +    static bool removeDebuggee(JSContext *cx, unsigned argc, Value *vp);
   1.342 +    static bool removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp);
   1.343 +    static bool hasDebuggee(JSContext *cx, unsigned argc, Value *vp);
   1.344 +    static bool getDebuggees(JSContext *cx, unsigned argc, Value *vp);
   1.345 +    static bool getNewestFrame(JSContext *cx, unsigned argc, Value *vp);
   1.346 +    static bool clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp);
   1.347 +    static bool findScripts(JSContext *cx, unsigned argc, Value *vp);
   1.348 +    static bool findAllGlobals(JSContext *cx, unsigned argc, Value *vp);
   1.349 +    static bool makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp);
   1.350 +    static bool construct(JSContext *cx, unsigned argc, Value *vp);
   1.351 +    static const JSPropertySpec properties[];
   1.352 +    static const JSFunctionSpec methods[];
   1.353 +
   1.354 +    JSObject *getHook(Hook hook) const;
   1.355 +    bool hasAnyLiveHooks() const;
   1.356 +
   1.357 +    static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame,
   1.358 +                                             MutableHandleValue vp);
   1.359 +    static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
   1.360 +    static void slowPathOnNewScript(JSContext *cx, HandleScript script,
   1.361 +                                    GlobalObject *compileAndGoGlobal);
   1.362 +    static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
   1.363 +    static JSTrapStatus dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which);
   1.364 +
   1.365 +    JSTrapStatus fireDebuggerStatement(JSContext *cx, MutableHandleValue vp);
   1.366 +    JSTrapStatus fireExceptionUnwind(JSContext *cx, MutableHandleValue vp);
   1.367 +    JSTrapStatus fireEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp);
   1.368 +    JSTrapStatus fireNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global, MutableHandleValue vp);
   1.369 +
   1.370 +    /*
   1.371 +     * Allocate and initialize a Debugger.Script instance whose referent is
   1.372 +     * |script|.
   1.373 +     */
   1.374 +    JSObject *newDebuggerScript(JSContext *cx, HandleScript script);
   1.375 +
   1.376 +    /*
   1.377 +     * Allocate and initialize a Debugger.Source instance whose referent is
   1.378 +     * |source|.
   1.379 +     */
   1.380 +    JSObject *newDebuggerSource(JSContext *cx, js::HandleScriptSource source);
   1.381 +
   1.382 +    /*
   1.383 +     * Receive a "new script" event from the engine. A new script was compiled
   1.384 +     * or deserialized.
   1.385 +     */
   1.386 +    void fireNewScript(JSContext *cx, HandleScript script);
   1.387 +
   1.388 +    /*
   1.389 +     * Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
   1.390 +     * its data if we need to make a new Debugger.Frame.
   1.391 +     */
   1.392 +    bool getScriptFrameWithIter(JSContext *cx, AbstractFramePtr frame,
   1.393 +                                const ScriptFrameIter *maybeIter, MutableHandleValue vp);
   1.394 +
   1.395 +    inline Breakpoint *firstBreakpoint() const;
   1.396 +
   1.397 +    static inline Debugger *fromOnNewGlobalObjectWatchersLink(JSCList *link);
   1.398 +
   1.399 +    static bool replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePtr to,
   1.400 +                                 ScriptFrameIter &iter);
   1.401 +
   1.402 +  public:
   1.403 +    Debugger(JSContext *cx, JSObject *dbg);
   1.404 +    ~Debugger();
   1.405 +
   1.406 +    bool init(JSContext *cx);
   1.407 +    inline const js::HeapPtrObject &toJSObject() const;
   1.408 +    inline js::HeapPtrObject &toJSObjectRef();
   1.409 +    static inline Debugger *fromJSObject(JSObject *obj);
   1.410 +    static Debugger *fromChildJSObject(JSObject *obj);
   1.411 +
   1.412 +    /*********************************** Methods for interaction with the GC. */
   1.413 +
   1.414 +    /*
   1.415 +     * A Debugger object is live if:
   1.416 +     *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
   1.417 +     *   * it is in the middle of dispatching an event (the event dispatching
   1.418 +     *     code roots it in this case); OR
   1.419 +     *   * it is enabled, and it is debugging at least one live compartment,
   1.420 +     *     and at least one of the following is true:
   1.421 +     *       - it has a debugger hook installed
   1.422 +     *       - it has a breakpoint set on a live script
   1.423 +     *       - it has a watchpoint set on a live object.
   1.424 +     *
   1.425 +     * Debugger::markAllIteratively handles the last case. If it finds any
   1.426 +     * Debugger objects that are definitely live but not yet marked, it marks
   1.427 +     * them and returns true. If not, it returns false.
   1.428 +     */
   1.429 +    static void markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer);
   1.430 +    static bool markAllIteratively(GCMarker *trc);
   1.431 +    static void markAll(JSTracer *trc);
   1.432 +    static void sweepAll(FreeOp *fop);
   1.433 +    static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
   1.434 +                                             GlobalObjectSet::Enum *compartmentEnum);
   1.435 +    static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder<JS::Zone> &finder);
   1.436 +
   1.437 +    static inline JSTrapStatus onEnterFrame(JSContext *cx, AbstractFramePtr frame,
   1.438 +                                            MutableHandleValue vp);
   1.439 +    static inline bool onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
   1.440 +    static inline JSTrapStatus onDebuggerStatement(JSContext *cx, MutableHandleValue vp);
   1.441 +    static inline JSTrapStatus onExceptionUnwind(JSContext *cx, MutableHandleValue vp);
   1.442 +    static inline void onNewScript(JSContext *cx, HandleScript script,
   1.443 +                                   GlobalObject *compileAndGoGlobal);
   1.444 +    static inline void onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
   1.445 +    static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
   1.446 +    static JSTrapStatus onSingleStep(JSContext *cx, MutableHandleValue vp);
   1.447 +    static bool handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to);
   1.448 +    static bool handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to);
   1.449 +
   1.450 +    /************************************* Functions for use by Debugger.cpp. */
   1.451 +
   1.452 +    inline bool observesEnterFrame() const;
   1.453 +    inline bool observesNewScript() const;
   1.454 +    inline bool observesNewGlobalObject() const;
   1.455 +    inline bool observesGlobal(GlobalObject *global) const;
   1.456 +    bool observesFrame(AbstractFramePtr frame) const;
   1.457 +    bool observesFrame(const ScriptFrameIter &iter) const;
   1.458 +    bool observesScript(JSScript *script) const;
   1.459 +
   1.460 +    /*
   1.461 +     * If env is nullptr, call vp->setNull() and return true. Otherwise, find
   1.462 +     * or create a Debugger.Environment object for the given Env. On success,
   1.463 +     * store the Environment object in *vp and return true.
   1.464 +     */
   1.465 +    bool wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue vp);
   1.466 +
   1.467 +    /*
   1.468 +     * Like cx->compartment()->wrap(cx, vp), but for the debugger compartment.
   1.469 +     *
   1.470 +     * Preconditions: *vp is a value from a debuggee compartment; cx is in the
   1.471 +     * debugger's compartment.
   1.472 +     *
   1.473 +     * If *vp is an object, this produces a (new or existing) Debugger.Object
   1.474 +     * wrapper for it. Otherwise this is the same as JSCompartment::wrap.
   1.475 +     *
   1.476 +     * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
   1.477 +     * of the form { optimizedOut: true }.
   1.478 +     *
   1.479 +     * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
   1.480 +     * arguments, this produces a plain object of the form { missingArguments:
   1.481 +     * true }.
   1.482 +     */
   1.483 +    bool wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
   1.484 +
   1.485 +    /*
   1.486 +     * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
   1.487 +     * compartment.
   1.488 +     *
   1.489 +     * Preconditions: cx is in the debugger compartment. *vp is a value in that
   1.490 +     * compartment. (*vp should be a "debuggee value", meaning it is the
   1.491 +     * debugger's reflection of a value in the debuggee.)
   1.492 +     *
   1.493 +     * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
   1.494 +     * is an object, throw a TypeError, because it is not a debuggee
   1.495 +     * value. Otherwise *vp is a primitive, so leave it alone.
   1.496 +     *
   1.497 +     * When passing values from the debuggee to the debugger:
   1.498 +     *     enter debugger compartment;
   1.499 +     *     call wrapDebuggeeValue;  // compartment- and debugger-wrapping
   1.500 +     *
   1.501 +     * When passing values from the debugger to the debuggee:
   1.502 +     *     call unwrapDebuggeeValue;  // debugger-unwrapping
   1.503 +     *     enter debuggee compartment;
   1.504 +     *     call cx->compartment()->wrap;  // compartment-rewrapping
   1.505 +     *
   1.506 +     * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
   1.507 +     * two different kinds of symmetry at work: regardless of which direction
   1.508 +     * we're going, we want any exceptions to be created and thrown in the
   1.509 +     * debugger compartment--mirror symmetry. But compartment wrapping always
   1.510 +     * happens in the target compartment--rotational symmetry.)
   1.511 +     */
   1.512 +    bool unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
   1.513 +
   1.514 +    /*
   1.515 +     * Store the Debugger.Frame object for frame in *vp.
   1.516 +     *
   1.517 +     * Use this if you have already access to a frame pointer without having
   1.518 +     * to incur the cost of walking the stack.
   1.519 +     */
   1.520 +    bool getScriptFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) {
   1.521 +        return getScriptFrameWithIter(cx, frame, nullptr, vp);
   1.522 +    }
   1.523 +
   1.524 +    /*
   1.525 +     * Store the Debugger.Frame object for iter in *vp. Eagerly copies a
   1.526 +     * ScriptFrameIter::Data.
   1.527 +     *
   1.528 +     * Use this if you had to make a ScriptFrameIter to get the required
   1.529 +     * frame, in which case the cost of walking the stack has already been
   1.530 +     * paid.
   1.531 +     */
   1.532 +    bool getScriptFrame(JSContext *cx, const ScriptFrameIter &iter, MutableHandleValue vp) {
   1.533 +        return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
   1.534 +    }
   1.535 +
   1.536 +    /*
   1.537 +     * Set |*status| and |*value| to a (JSTrapStatus, Value) pair reflecting a
   1.538 +     * standard SpiderMonkey call state: a boolean success value |ok|, a return
   1.539 +     * value |rv|, and a context |cx| that may or may not have an exception set.
   1.540 +     * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
   1.541 +     * to be false).
   1.542 +     */
   1.543 +    static void resultToCompletion(JSContext *cx, bool ok, const Value &rv,
   1.544 +                                   JSTrapStatus *status, MutableHandleValue value);
   1.545 +
   1.546 +    /*
   1.547 +     * Set |*result| to a JavaScript completion value corresponding to |status|
   1.548 +     * and |value|. |value| should be the return value or exception value, not
   1.549 +     * wrapped as a debuggee value. |cx| must be in the debugger compartment.
   1.550 +     */
   1.551 +    bool newCompletionValue(JSContext *cx, JSTrapStatus status, Value value,
   1.552 +                            MutableHandleValue result);
   1.553 +
   1.554 +    /*
   1.555 +     * Precondition: we are in the debuggee compartment (ac is entered) and ok
   1.556 +     * is true if the operation in the debuggee compartment succeeded, false on
   1.557 +     * error or exception.
   1.558 +     *
   1.559 +     * Postcondition: we are in the debugger compartment, having called
   1.560 +     * ac.leave() even if an error occurred.
   1.561 +     *
   1.562 +     * On success, a completion value is in vp and ac.context does not have a
   1.563 +     * pending exception. (This ordinarily returns true even if the ok argument
   1.564 +     * is false.)
   1.565 +     */
   1.566 +    bool receiveCompletionValue(mozilla::Maybe<AutoCompartment> &ac, bool ok,
   1.567 +                                HandleValue val,
   1.568 +                                MutableHandleValue vp);
   1.569 +
   1.570 +    /*
   1.571 +     * Return the Debugger.Script object for |script|, or create a new one if
   1.572 +     * needed. The context |cx| must be in the debugger compartment; |script|
   1.573 +     * must be a script in a debuggee compartment.
   1.574 +     */
   1.575 +    JSObject *wrapScript(JSContext *cx, HandleScript script);
   1.576 +
   1.577 +    /*
   1.578 +     * Return the Debugger.Source object for |source|, or create a new one if
   1.579 +     * needed. The context |cx| must be in the debugger compartment; |source|
   1.580 +     * must be a script source object in a debuggee compartment.
   1.581 +     */
   1.582 +    JSObject *wrapSource(JSContext *cx, js::HandleScriptSource source);
   1.583 +
   1.584 +  private:
   1.585 +    Debugger(const Debugger &) MOZ_DELETE;
   1.586 +    Debugger & operator=(const Debugger &) MOZ_DELETE;
   1.587 +};
   1.588 +
   1.589 +class BreakpointSite {
   1.590 +    friend class Breakpoint;
   1.591 +    friend struct ::JSCompartment;
   1.592 +    friend class ::JSScript;
   1.593 +    friend class Debugger;
   1.594 +
   1.595 +  public:
   1.596 +    JSScript *script;
   1.597 +    jsbytecode * const pc;
   1.598 +
   1.599 +  private:
   1.600 +    JSCList breakpoints;  /* cyclic list of all js::Breakpoints at this instruction */
   1.601 +    size_t enabledCount;  /* number of breakpoints in the list that are enabled */
   1.602 +    JSTrapHandler trapHandler;  /* trap state */
   1.603 +    HeapValue trapClosure;
   1.604 +
   1.605 +    void recompile(FreeOp *fop);
   1.606 +
   1.607 +  public:
   1.608 +    BreakpointSite(JSScript *script, jsbytecode *pc);
   1.609 +    Breakpoint *firstBreakpoint() const;
   1.610 +    bool hasBreakpoint(Breakpoint *bp);
   1.611 +    bool hasTrap() const { return !!trapHandler; }
   1.612 +
   1.613 +    void inc(FreeOp *fop);
   1.614 +    void dec(FreeOp *fop);
   1.615 +    void setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure);
   1.616 +    void clearTrap(FreeOp *fop, JSTrapHandler *handlerp = nullptr, Value *closurep = nullptr);
   1.617 +    void destroyIfEmpty(FreeOp *fop);
   1.618 +};
   1.619 +
   1.620 +/*
   1.621 + * Each Breakpoint is a member of two linked lists: its debugger's list and its
   1.622 + * site's list.
   1.623 + *
   1.624 + * GC rules:
   1.625 + *   - script is live, breakpoint exists, and debugger is enabled
   1.626 + *      ==> debugger is live
   1.627 + *   - script is live, breakpoint exists, and debugger is live
   1.628 + *      ==> retain the breakpoint and the handler object is live
   1.629 + *
   1.630 + * Debugger::markAllIteratively implements these two rules. It uses
   1.631 + * Debugger::hasAnyLiveHooks to check for rule 1.
   1.632 + *
   1.633 + * Nothing else causes a breakpoint to be retained, so if its script or
   1.634 + * debugger is collected, the breakpoint is destroyed during GC sweep phase,
   1.635 + * even if the debugger compartment isn't being GC'd. This is implemented in
   1.636 + * JSCompartment::sweepBreakpoints.
   1.637 + */
   1.638 +class Breakpoint {
   1.639 +    friend struct ::JSCompartment;
   1.640 +    friend class Debugger;
   1.641 +
   1.642 +  public:
   1.643 +    Debugger * const debugger;
   1.644 +    BreakpointSite * const site;
   1.645 +  private:
   1.646 +    /* |handler| is marked unconditionally during minor GC. */
   1.647 +    js::EncapsulatedPtrObject handler;
   1.648 +    JSCList debuggerLinks;
   1.649 +    JSCList siteLinks;
   1.650 +
   1.651 +  public:
   1.652 +    static Breakpoint *fromDebuggerLinks(JSCList *links);
   1.653 +    static Breakpoint *fromSiteLinks(JSCList *links);
   1.654 +    Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler);
   1.655 +    void destroy(FreeOp *fop);
   1.656 +    Breakpoint *nextInDebugger();
   1.657 +    Breakpoint *nextInSite();
   1.658 +    const EncapsulatedPtrObject &getHandler() const { return handler; }
   1.659 +    EncapsulatedPtrObject &getHandlerRef() { return handler; }
   1.660 +};
   1.661 +
   1.662 +Breakpoint *
   1.663 +Debugger::firstBreakpoint() const
   1.664 +{
   1.665 +    if (JS_CLIST_IS_EMPTY(&breakpoints))
   1.666 +        return nullptr;
   1.667 +    return Breakpoint::fromDebuggerLinks(JS_NEXT_LINK(&breakpoints));
   1.668 +}
   1.669 +
   1.670 +Debugger *
   1.671 +Debugger::fromOnNewGlobalObjectWatchersLink(JSCList *link) {
   1.672 +    char *p = reinterpret_cast<char *>(link);
   1.673 +    return reinterpret_cast<Debugger *>(p - offsetof(Debugger, onNewGlobalObjectWatchersLink));
   1.674 +}
   1.675 +
   1.676 +const js::HeapPtrObject &
   1.677 +Debugger::toJSObject() const
   1.678 +{
   1.679 +    JS_ASSERT(object);
   1.680 +    return object;
   1.681 +}
   1.682 +
   1.683 +js::HeapPtrObject &
   1.684 +Debugger::toJSObjectRef()
   1.685 +{
   1.686 +    JS_ASSERT(object);
   1.687 +    return object;
   1.688 +}
   1.689 +
   1.690 +bool
   1.691 +Debugger::observesEnterFrame() const
   1.692 +{
   1.693 +    return enabled && getHook(OnEnterFrame);
   1.694 +}
   1.695 +
   1.696 +bool
   1.697 +Debugger::observesNewScript() const
   1.698 +{
   1.699 +    return enabled && getHook(OnNewScript);
   1.700 +}
   1.701 +
   1.702 +bool
   1.703 +Debugger::observesNewGlobalObject() const
   1.704 +{
   1.705 +    return enabled && getHook(OnNewGlobalObject);
   1.706 +}
   1.707 +
   1.708 +bool
   1.709 +Debugger::observesGlobal(GlobalObject *global) const
   1.710 +{
   1.711 +    return debuggees.has(global);
   1.712 +}
   1.713 +
   1.714 +JSTrapStatus
   1.715 +Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp)
   1.716 +{
   1.717 +    if (cx->compartment()->getDebuggees().empty())
   1.718 +        return JSTRAP_CONTINUE;
   1.719 +    return slowPathOnEnterFrame(cx, frame, vp);
   1.720 +}
   1.721 +
   1.722 +JSTrapStatus
   1.723 +Debugger::onDebuggerStatement(JSContext *cx, MutableHandleValue vp)
   1.724 +{
   1.725 +    return cx->compartment()->getDebuggees().empty()
   1.726 +           ? JSTRAP_CONTINUE
   1.727 +           : dispatchHook(cx, vp, OnDebuggerStatement);
   1.728 +}
   1.729 +
   1.730 +JSTrapStatus
   1.731 +Debugger::onExceptionUnwind(JSContext *cx, MutableHandleValue vp)
   1.732 +{
   1.733 +    return cx->compartment()->getDebuggees().empty()
   1.734 +           ? JSTRAP_CONTINUE
   1.735 +           : dispatchHook(cx, vp, OnExceptionUnwind);
   1.736 +}
   1.737 +
   1.738 +void
   1.739 +Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal)
   1.740 +{
   1.741 +    JS_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal);
   1.742 +    JS_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal == &script->uninlinedGlobal());
   1.743 +    // We early return in slowPathOnNewScript for self-hosted scripts, so we can
   1.744 +    // ignore those in our assertion here.
   1.745 +    JS_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
   1.746 +                 !script->selfHosted(),
   1.747 +                 script->compartment()->firedOnNewGlobalObject);
   1.748 +    JS_ASSERT_IF(!script->compileAndGo(), !compileAndGoGlobal);
   1.749 +    if (!script->compartment()->getDebuggees().empty())
   1.750 +        slowPathOnNewScript(cx, script, compileAndGoGlobal);
   1.751 +}
   1.752 +
   1.753 +void
   1.754 +Debugger::onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global)
   1.755 +{
   1.756 +    JS_ASSERT(!global->compartment()->firedOnNewGlobalObject);
   1.757 +#ifdef DEBUG
   1.758 +    global->compartment()->firedOnNewGlobalObject = true;
   1.759 +#endif
   1.760 +    if (!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers))
   1.761 +        Debugger::slowPathOnNewGlobalObject(cx, global);
   1.762 +}
   1.763 +
   1.764 +extern bool
   1.765 +EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
   1.766 +              ConstTwoByteChars chars, unsigned length, const char *filename, unsigned lineno,
   1.767 +              MutableHandleValue rval);
   1.768 +
   1.769 +}
   1.770 +
   1.771 +#endif /* vm_Debugger_h */

mercurial