js/src/vm/Debugger.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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 #ifndef vm_Debugger_h
     8 #define vm_Debugger_h
    10 #include "mozilla/LinkedList.h"
    12 #include "jsclist.h"
    13 #include "jscntxt.h"
    14 #include "jscompartment.h"
    15 #include "jsweakmap.h"
    17 #include "gc/Barrier.h"
    18 #include "js/HashTable.h"
    19 #include "vm/GlobalObject.h"
    21 namespace js {
    23 class Breakpoint;
    25 /*
    26  * A weakmap that supports the keys being in different compartments to the
    27  * values, although all values must be in the same compartment.
    28  *
    29  * The Key and Value classes must support the compartment() method.
    30  *
    31  * The purpose of this is to allow the garbage collector to easily find edges
    32  * from debugee object compartments to debugger compartments when calculating
    33  * the compartment groups.  Note that these edges are the inverse of the edges
    34  * stored in the cross compartment map.
    35  *
    36  * The current implementation results in all debuggee object compartments being
    37  * swept in the same group as the debugger.  This is a conservative approach,
    38  * and compartments may be unnecessarily grouped, however it results in a
    39  * simpler and faster implementation.
    40  *
    41  * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
    42  * debugger compartments. If it is false, we assert that such entries are never
    43  * created.
    44  */
    45 template <class Key, class Value, bool InvisibleKeysOk=false>
    46 class DebuggerWeakMap : private WeakMap<Key, Value, DefaultHasher<Key> >
    47 {
    48   private:
    49     typedef HashMap<JS::Zone *,
    50                     uintptr_t,
    51                     DefaultHasher<JS::Zone *>,
    52                     RuntimeAllocPolicy> CountMap;
    54     CountMap zoneCounts;
    56   public:
    57     typedef WeakMap<Key, Value, DefaultHasher<Key> > Base;
    58     explicit DebuggerWeakMap(JSContext *cx)
    59         : Base(cx), zoneCounts(cx->runtime()) { }
    61   public:
    62     /* Expose those parts of HashMap public interface that are used by Debugger methods. */
    64     typedef typename Base::Entry Entry;
    65     typedef typename Base::Ptr Ptr;
    66     typedef typename Base::AddPtr AddPtr;
    67     typedef typename Base::Range Range;
    68     typedef typename Base::Enum Enum;
    69     typedef typename Base::Lookup Lookup;
    71     /* Expose WeakMap public interface */
    73     using Base::clearWithoutCallingDestructors;
    74     using Base::lookupForAdd;
    75     using Base::all;
    76     using Base::trace;
    78     bool init(uint32_t len = 16) {
    79         return Base::init(len) && zoneCounts.init();
    80     }
    82     template<typename KeyInput, typename ValueInput>
    83     bool relookupOrAdd(AddPtr &p, const KeyInput &k, const ValueInput &v) {
    84         JS_ASSERT(v->compartment() == Base::compartment);
    85         JS_ASSERT(!k->compartment()->options_.mergeable());
    86         JS_ASSERT_IF(!InvisibleKeysOk, !k->compartment()->options_.invisibleToDebugger());
    87         JS_ASSERT(!Base::has(k));
    88         if (!incZoneCount(k->zone()))
    89             return false;
    90         bool ok = Base::relookupOrAdd(p, k, v);
    91         if (!ok)
    92             decZoneCount(k->zone());
    93         return ok;
    94     }
    96     void remove(const Lookup &l) {
    97         JS_ASSERT(Base::has(l));
    98         Base::remove(l);
    99         decZoneCount(l->zone());
   100     }
   102   public:
   103     void markKeys(JSTracer *tracer) {
   104         for (Enum e(*static_cast<Base *>(this)); !e.empty(); e.popFront()) {
   105             Key key = e.front().key();
   106             gc::Mark(tracer, &key, "Debugger WeakMap key");
   107             if (key != e.front().key())
   108                 e.rekeyFront(key);
   109             key.unsafeSet(nullptr);
   110         }
   111     }
   113     bool hasKeyInZone(JS::Zone *zone) {
   114         CountMap::Ptr p = zoneCounts.lookup(zone);
   115         JS_ASSERT_IF(p, p->value() > 0);
   116         return p;
   117     }
   119   private:
   120     /* Override sweep method to also update our edge cache. */
   121     void sweep() {
   122         for (Enum e(*static_cast<Base *>(this)); !e.empty(); e.popFront()) {
   123             Key k(e.front().key());
   124             if (gc::IsAboutToBeFinalized(&k)) {
   125                 e.removeFront();
   126                 decZoneCount(k->zone());
   127             }
   128         }
   129         Base::assertEntriesNotAboutToBeFinalized();
   130     }
   132     bool incZoneCount(JS::Zone *zone) {
   133         CountMap::Ptr p = zoneCounts.lookupWithDefault(zone, 0);
   134         if (!p)
   135             return false;
   136         ++p->value();
   137         return true;
   138     }
   140     void decZoneCount(JS::Zone *zone) {
   141         CountMap::Ptr p = zoneCounts.lookup(zone);
   142         JS_ASSERT(p);
   143         JS_ASSERT(p->value() > 0);
   144         --p->value();
   145         if (p->value() == 0)
   146             zoneCounts.remove(zone);
   147     }
   148 };
   150 /*
   151  * Env is the type of what ES5 calls "lexical environments" (runtime
   152  * activations of lexical scopes). This is currently just JSObject, and is
   153  * implemented by Call, Block, With, and DeclEnv objects, among others--but
   154  * environments and objects are really two different concepts.
   155  */
   156 typedef JSObject Env;
   158 class Debugger : private mozilla::LinkedListElement<Debugger>
   159 {
   160     friend class Breakpoint;
   161     friend class mozilla::LinkedListElement<Debugger>;
   162     friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
   164   public:
   165     enum Hook {
   166         OnDebuggerStatement,
   167         OnExceptionUnwind,
   168         OnNewScript,
   169         OnEnterFrame,
   170         OnNewGlobalObject,
   171         HookCount
   172     };
   173     enum {
   174         JSSLOT_DEBUG_PROTO_START,
   175         JSSLOT_DEBUG_FRAME_PROTO = JSSLOT_DEBUG_PROTO_START,
   176         JSSLOT_DEBUG_ENV_PROTO,
   177         JSSLOT_DEBUG_OBJECT_PROTO,
   178         JSSLOT_DEBUG_SCRIPT_PROTO,
   179         JSSLOT_DEBUG_SOURCE_PROTO,
   180         JSSLOT_DEBUG_MEMORY_PROTO,
   181         JSSLOT_DEBUG_PROTO_STOP,
   182         JSSLOT_DEBUG_HOOK_START = JSSLOT_DEBUG_PROTO_STOP,
   183         JSSLOT_DEBUG_HOOK_STOP = JSSLOT_DEBUG_HOOK_START + HookCount,
   184         JSSLOT_DEBUG_MEMORY_INSTANCE = JSSLOT_DEBUG_HOOK_STOP,
   185         JSSLOT_DEBUG_COUNT
   186     };
   187   private:
   188     HeapPtrObject object;               /* The Debugger object. Strong reference. */
   189     GlobalObjectSet debuggees;          /* Debuggee globals. Cross-compartment weak references. */
   190     js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
   191     bool enabled;
   192     JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
   194     /*
   195      * If this Debugger is enabled, and has a onNewGlobalObject handler, then
   196      * this link is inserted into the circular list headed by
   197      * JSRuntime::onNewGlobalObjectWatchers. Otherwise, this is set to a
   198      * singleton cycle.
   199      */
   200     JSCList onNewGlobalObjectWatchersLink;
   202     /*
   203      * Map from stack frames that are currently on the stack to Debugger.Frame
   204      * instances.
   205      *
   206      * The keys are always live stack frames. We drop them from this map as
   207      * soon as they leave the stack (see slowPathOnLeaveFrame) and in
   208      * removeDebuggee.
   209      *
   210      * We don't trace the keys of this map (the frames are on the stack and
   211      * thus necessarily live), but we do trace the values. It's like a WeakMap
   212      * that way, but since stack frames are not gc-things, the implementation
   213      * has to be different.
   214      */
   215     typedef HashMap<AbstractFramePtr,
   216                     RelocatablePtrObject,
   217                     DefaultHasher<AbstractFramePtr>,
   218                     RuntimeAllocPolicy> FrameMap;
   219     FrameMap frames;
   221     /* An ephemeral map from JSScript* to Debugger.Script instances. */
   222     typedef DebuggerWeakMap<EncapsulatedPtrScript, RelocatablePtrObject> ScriptWeakMap;
   223     ScriptWeakMap scripts;
   225     /* The map from debuggee source script objects to their Debugger.Source instances. */
   226     typedef DebuggerWeakMap<EncapsulatedPtrObject, RelocatablePtrObject, true> SourceWeakMap;
   227     SourceWeakMap sources;
   229     /* The map from debuggee objects to their Debugger.Object instances. */
   230     typedef DebuggerWeakMap<EncapsulatedPtrObject, RelocatablePtrObject> ObjectWeakMap;
   231     ObjectWeakMap objects;
   233     /* The map from debuggee Envs to Debugger.Environment instances. */
   234     ObjectWeakMap environments;
   236     class FrameRange;
   237     class ScriptQuery;
   239     bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj);
   240     bool addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> obj,
   241                            AutoDebugModeInvalidation &invalidate);
   242     void cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global,
   243                                             AutoDebugModeInvalidation &invalidate,
   244                                             GlobalObjectSet::Enum *compartmentEnum,
   245                                             GlobalObjectSet::Enum *debugEnu);
   246     bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
   247                               GlobalObjectSet::Enum *compartmentEnum,
   248                               GlobalObjectSet::Enum *debugEnum);
   249     bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
   250                               AutoDebugModeInvalidation &invalidate,
   251                               GlobalObjectSet::Enum *compartmentEnum,
   252                               GlobalObjectSet::Enum *debugEnum);
   253     void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
   254                                      GlobalObjectSet::Enum *compartmentEnum,
   255                                      GlobalObjectSet::Enum *debugEnum);
   256     void removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global,
   257                                      AutoDebugModeInvalidation &invalidate,
   258                                      GlobalObjectSet::Enum *compartmentEnum,
   259                                      GlobalObjectSet::Enum *debugEnum);
   261     /*
   262      * Cope with an error or exception in a debugger hook.
   263      *
   264      * If callHook is true, then call the uncaughtExceptionHook, if any. If, in
   265      * addition, vp is given, then parse the value returned by
   266      * uncaughtExceptionHook as a resumption value.
   267      *
   268      * If there is no uncaughtExceptionHook, or if it fails, report and clear
   269      * the pending exception on ac.context and return JSTRAP_ERROR.
   270      *
   271      * This always calls ac.leave(); ac is a parameter because this method must
   272      * do some things in the debugger compartment and some things in the
   273      * debuggee compartment.
   274      */
   275     JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment> &ac, bool callHook);
   276     JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment> &ac, MutableHandleValue vp, bool callHook);
   278     JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment> &ac,
   279                                                MutableHandleValue *vp, bool callHook);
   281     /*
   282      * Handle the result of a hook that is expected to return a resumption
   283      * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
   284      * when we return from a debugging hook to debuggee code. The interpreter wants
   285      * a (JSTrapStatus, Value) pair telling it how to proceed.
   286      *
   287      * Precondition: ac is entered. We are in the debugger compartment.
   288      *
   289      * Postcondition: This called ac.leave(). See handleUncaughtException.
   290      *
   291      * If ok is false, the hook failed. If an exception is pending in
   292      * ac.context(), return handleUncaughtException(ac, vp, callhook).
   293      * Otherwise just return JSTRAP_ERROR.
   294      *
   295      * If ok is true, there must be no exception pending in ac.context(). rv may be:
   296      *     undefined - Return JSTRAP_CONTINUE to continue execution normally.
   297      *     {return: value} or {throw: value} - Call unwrapDebuggeeValue to
   298      *         unwrap value. Store the result in *vp and return JSTRAP_RETURN
   299      *         or JSTRAP_THROW. The interpreter will force the current frame to
   300      *         return or throw an exception.
   301      *     null - Return JSTRAP_ERROR to terminate the debuggee with an
   302      *         uncatchable error.
   303      *     anything else - Make a new TypeError the pending exception and
   304      *         return handleUncaughtException(ac, vp, callHook).
   305      */
   306     JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment> &ac, bool ok, const Value &rv,
   307                                       MutableHandleValue vp, bool callHook = true);
   309     GlobalObject *unwrapDebuggeeArgument(JSContext *cx, const Value &v);
   311     static void traceObject(JSTracer *trc, JSObject *obj);
   312     void trace(JSTracer *trc);
   313     static void finalize(FreeOp *fop, JSObject *obj);
   314     void markKeysInCompartment(JSTracer *tracer);
   316     static const Class jsclass;
   318     static Debugger *fromThisValue(JSContext *cx, const CallArgs &ca, const char *fnname);
   319     static bool getEnabled(JSContext *cx, unsigned argc, Value *vp);
   320     static bool setEnabled(JSContext *cx, unsigned argc, Value *vp);
   321     static bool getHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which);
   322     static bool setHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which);
   323     static bool getOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp);
   324     static bool setOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp);
   325     static bool getOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
   326     static bool setOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp);
   327     static bool getOnNewScript(JSContext *cx, unsigned argc, Value *vp);
   328     static bool setOnNewScript(JSContext *cx, unsigned argc, Value *vp);
   329     static bool getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
   330     static bool setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp);
   331     static bool getOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
   332     static bool setOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp);
   333     static bool getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
   334     static bool setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp);
   335     static bool getMemory(JSContext *cx, unsigned argc, Value *vp);
   336     static bool addDebuggee(JSContext *cx, unsigned argc, Value *vp);
   337     static bool addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp);
   338     static bool removeDebuggee(JSContext *cx, unsigned argc, Value *vp);
   339     static bool removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp);
   340     static bool hasDebuggee(JSContext *cx, unsigned argc, Value *vp);
   341     static bool getDebuggees(JSContext *cx, unsigned argc, Value *vp);
   342     static bool getNewestFrame(JSContext *cx, unsigned argc, Value *vp);
   343     static bool clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp);
   344     static bool findScripts(JSContext *cx, unsigned argc, Value *vp);
   345     static bool findAllGlobals(JSContext *cx, unsigned argc, Value *vp);
   346     static bool makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp);
   347     static bool construct(JSContext *cx, unsigned argc, Value *vp);
   348     static const JSPropertySpec properties[];
   349     static const JSFunctionSpec methods[];
   351     JSObject *getHook(Hook hook) const;
   352     bool hasAnyLiveHooks() const;
   354     static JSTrapStatus slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame,
   355                                              MutableHandleValue vp);
   356     static bool slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
   357     static void slowPathOnNewScript(JSContext *cx, HandleScript script,
   358                                     GlobalObject *compileAndGoGlobal);
   359     static void slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
   360     static JSTrapStatus dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which);
   362     JSTrapStatus fireDebuggerStatement(JSContext *cx, MutableHandleValue vp);
   363     JSTrapStatus fireExceptionUnwind(JSContext *cx, MutableHandleValue vp);
   364     JSTrapStatus fireEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp);
   365     JSTrapStatus fireNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global, MutableHandleValue vp);
   367     /*
   368      * Allocate and initialize a Debugger.Script instance whose referent is
   369      * |script|.
   370      */
   371     JSObject *newDebuggerScript(JSContext *cx, HandleScript script);
   373     /*
   374      * Allocate and initialize a Debugger.Source instance whose referent is
   375      * |source|.
   376      */
   377     JSObject *newDebuggerSource(JSContext *cx, js::HandleScriptSource source);
   379     /*
   380      * Receive a "new script" event from the engine. A new script was compiled
   381      * or deserialized.
   382      */
   383     void fireNewScript(JSContext *cx, HandleScript script);
   385     /*
   386      * Gets a Debugger.Frame object. If maybeIter is non-null, we eagerly copy
   387      * its data if we need to make a new Debugger.Frame.
   388      */
   389     bool getScriptFrameWithIter(JSContext *cx, AbstractFramePtr frame,
   390                                 const ScriptFrameIter *maybeIter, MutableHandleValue vp);
   392     inline Breakpoint *firstBreakpoint() const;
   394     static inline Debugger *fromOnNewGlobalObjectWatchersLink(JSCList *link);
   396     static bool replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePtr to,
   397                                  ScriptFrameIter &iter);
   399   public:
   400     Debugger(JSContext *cx, JSObject *dbg);
   401     ~Debugger();
   403     bool init(JSContext *cx);
   404     inline const js::HeapPtrObject &toJSObject() const;
   405     inline js::HeapPtrObject &toJSObjectRef();
   406     static inline Debugger *fromJSObject(JSObject *obj);
   407     static Debugger *fromChildJSObject(JSObject *obj);
   409     /*********************************** Methods for interaction with the GC. */
   411     /*
   412      * A Debugger object is live if:
   413      *   * the Debugger JSObject is live (Debugger::trace handles this case); OR
   414      *   * it is in the middle of dispatching an event (the event dispatching
   415      *     code roots it in this case); OR
   416      *   * it is enabled, and it is debugging at least one live compartment,
   417      *     and at least one of the following is true:
   418      *       - it has a debugger hook installed
   419      *       - it has a breakpoint set on a live script
   420      *       - it has a watchpoint set on a live object.
   421      *
   422      * Debugger::markAllIteratively handles the last case. If it finds any
   423      * Debugger objects that are definitely live but not yet marked, it marks
   424      * them and returns true. If not, it returns false.
   425      */
   426     static void markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer);
   427     static bool markAllIteratively(GCMarker *trc);
   428     static void markAll(JSTracer *trc);
   429     static void sweepAll(FreeOp *fop);
   430     static void detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global,
   431                                              GlobalObjectSet::Enum *compartmentEnum);
   432     static void findCompartmentEdges(JS::Zone *v, gc::ComponentFinder<JS::Zone> &finder);
   434     static inline JSTrapStatus onEnterFrame(JSContext *cx, AbstractFramePtr frame,
   435                                             MutableHandleValue vp);
   436     static inline bool onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok);
   437     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, MutableHandleValue vp);
   438     static inline JSTrapStatus onExceptionUnwind(JSContext *cx, MutableHandleValue vp);
   439     static inline void onNewScript(JSContext *cx, HandleScript script,
   440                                    GlobalObject *compileAndGoGlobal);
   441     static inline void onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global);
   442     static JSTrapStatus onTrap(JSContext *cx, MutableHandleValue vp);
   443     static JSTrapStatus onSingleStep(JSContext *cx, MutableHandleValue vp);
   444     static bool handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to);
   445     static bool handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to);
   447     /************************************* Functions for use by Debugger.cpp. */
   449     inline bool observesEnterFrame() const;
   450     inline bool observesNewScript() const;
   451     inline bool observesNewGlobalObject() const;
   452     inline bool observesGlobal(GlobalObject *global) const;
   453     bool observesFrame(AbstractFramePtr frame) const;
   454     bool observesFrame(const ScriptFrameIter &iter) const;
   455     bool observesScript(JSScript *script) const;
   457     /*
   458      * If env is nullptr, call vp->setNull() and return true. Otherwise, find
   459      * or create a Debugger.Environment object for the given Env. On success,
   460      * store the Environment object in *vp and return true.
   461      */
   462     bool wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue vp);
   464     /*
   465      * Like cx->compartment()->wrap(cx, vp), but for the debugger compartment.
   466      *
   467      * Preconditions: *vp is a value from a debuggee compartment; cx is in the
   468      * debugger's compartment.
   469      *
   470      * If *vp is an object, this produces a (new or existing) Debugger.Object
   471      * wrapper for it. Otherwise this is the same as JSCompartment::wrap.
   472      *
   473      * If *vp is a magic JS_OPTIMIZED_OUT value, this produces a plain object
   474      * of the form { optimizedOut: true }.
   475      *
   476      * If *vp is a magic JS_OPTIMIZED_ARGUMENTS value signifying missing
   477      * arguments, this produces a plain object of the form { missingArguments:
   478      * true }.
   479      */
   480     bool wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
   482     /*
   483      * Unwrap a Debug.Object, without rewrapping it for any particular debuggee
   484      * compartment.
   485      *
   486      * Preconditions: cx is in the debugger compartment. *vp is a value in that
   487      * compartment. (*vp should be a "debuggee value", meaning it is the
   488      * debugger's reflection of a value in the debuggee.)
   489      *
   490      * If *vp is a Debugger.Object, store the referent in *vp. Otherwise, if *vp
   491      * is an object, throw a TypeError, because it is not a debuggee
   492      * value. Otherwise *vp is a primitive, so leave it alone.
   493      *
   494      * When passing values from the debuggee to the debugger:
   495      *     enter debugger compartment;
   496      *     call wrapDebuggeeValue;  // compartment- and debugger-wrapping
   497      *
   498      * When passing values from the debugger to the debuggee:
   499      *     call unwrapDebuggeeValue;  // debugger-unwrapping
   500      *     enter debuggee compartment;
   501      *     call cx->compartment()->wrap;  // compartment-rewrapping
   502      *
   503      * (Extreme nerd sidebar: Unwrapping happens in two steps because there are
   504      * two different kinds of symmetry at work: regardless of which direction
   505      * we're going, we want any exceptions to be created and thrown in the
   506      * debugger compartment--mirror symmetry. But compartment wrapping always
   507      * happens in the target compartment--rotational symmetry.)
   508      */
   509     bool unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp);
   511     /*
   512      * Store the Debugger.Frame object for frame in *vp.
   513      *
   514      * Use this if you have already access to a frame pointer without having
   515      * to incur the cost of walking the stack.
   516      */
   517     bool getScriptFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) {
   518         return getScriptFrameWithIter(cx, frame, nullptr, vp);
   519     }
   521     /*
   522      * Store the Debugger.Frame object for iter in *vp. Eagerly copies a
   523      * ScriptFrameIter::Data.
   524      *
   525      * Use this if you had to make a ScriptFrameIter to get the required
   526      * frame, in which case the cost of walking the stack has already been
   527      * paid.
   528      */
   529     bool getScriptFrame(JSContext *cx, const ScriptFrameIter &iter, MutableHandleValue vp) {
   530         return getScriptFrameWithIter(cx, iter.abstractFramePtr(), &iter, vp);
   531     }
   533     /*
   534      * Set |*status| and |*value| to a (JSTrapStatus, Value) pair reflecting a
   535      * standard SpiderMonkey call state: a boolean success value |ok|, a return
   536      * value |rv|, and a context |cx| that may or may not have an exception set.
   537      * If an exception was pending on |cx|, it is cleared (and |ok| is asserted
   538      * to be false).
   539      */
   540     static void resultToCompletion(JSContext *cx, bool ok, const Value &rv,
   541                                    JSTrapStatus *status, MutableHandleValue value);
   543     /*
   544      * Set |*result| to a JavaScript completion value corresponding to |status|
   545      * and |value|. |value| should be the return value or exception value, not
   546      * wrapped as a debuggee value. |cx| must be in the debugger compartment.
   547      */
   548     bool newCompletionValue(JSContext *cx, JSTrapStatus status, Value value,
   549                             MutableHandleValue result);
   551     /*
   552      * Precondition: we are in the debuggee compartment (ac is entered) and ok
   553      * is true if the operation in the debuggee compartment succeeded, false on
   554      * error or exception.
   555      *
   556      * Postcondition: we are in the debugger compartment, having called
   557      * ac.leave() even if an error occurred.
   558      *
   559      * On success, a completion value is in vp and ac.context does not have a
   560      * pending exception. (This ordinarily returns true even if the ok argument
   561      * is false.)
   562      */
   563     bool receiveCompletionValue(mozilla::Maybe<AutoCompartment> &ac, bool ok,
   564                                 HandleValue val,
   565                                 MutableHandleValue vp);
   567     /*
   568      * Return the Debugger.Script object for |script|, or create a new one if
   569      * needed. The context |cx| must be in the debugger compartment; |script|
   570      * must be a script in a debuggee compartment.
   571      */
   572     JSObject *wrapScript(JSContext *cx, HandleScript script);
   574     /*
   575      * Return the Debugger.Source object for |source|, or create a new one if
   576      * needed. The context |cx| must be in the debugger compartment; |source|
   577      * must be a script source object in a debuggee compartment.
   578      */
   579     JSObject *wrapSource(JSContext *cx, js::HandleScriptSource source);
   581   private:
   582     Debugger(const Debugger &) MOZ_DELETE;
   583     Debugger & operator=(const Debugger &) MOZ_DELETE;
   584 };
   586 class BreakpointSite {
   587     friend class Breakpoint;
   588     friend struct ::JSCompartment;
   589     friend class ::JSScript;
   590     friend class Debugger;
   592   public:
   593     JSScript *script;
   594     jsbytecode * const pc;
   596   private:
   597     JSCList breakpoints;  /* cyclic list of all js::Breakpoints at this instruction */
   598     size_t enabledCount;  /* number of breakpoints in the list that are enabled */
   599     JSTrapHandler trapHandler;  /* trap state */
   600     HeapValue trapClosure;
   602     void recompile(FreeOp *fop);
   604   public:
   605     BreakpointSite(JSScript *script, jsbytecode *pc);
   606     Breakpoint *firstBreakpoint() const;
   607     bool hasBreakpoint(Breakpoint *bp);
   608     bool hasTrap() const { return !!trapHandler; }
   610     void inc(FreeOp *fop);
   611     void dec(FreeOp *fop);
   612     void setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure);
   613     void clearTrap(FreeOp *fop, JSTrapHandler *handlerp = nullptr, Value *closurep = nullptr);
   614     void destroyIfEmpty(FreeOp *fop);
   615 };
   617 /*
   618  * Each Breakpoint is a member of two linked lists: its debugger's list and its
   619  * site's list.
   620  *
   621  * GC rules:
   622  *   - script is live, breakpoint exists, and debugger is enabled
   623  *      ==> debugger is live
   624  *   - script is live, breakpoint exists, and debugger is live
   625  *      ==> retain the breakpoint and the handler object is live
   626  *
   627  * Debugger::markAllIteratively implements these two rules. It uses
   628  * Debugger::hasAnyLiveHooks to check for rule 1.
   629  *
   630  * Nothing else causes a breakpoint to be retained, so if its script or
   631  * debugger is collected, the breakpoint is destroyed during GC sweep phase,
   632  * even if the debugger compartment isn't being GC'd. This is implemented in
   633  * JSCompartment::sweepBreakpoints.
   634  */
   635 class Breakpoint {
   636     friend struct ::JSCompartment;
   637     friend class Debugger;
   639   public:
   640     Debugger * const debugger;
   641     BreakpointSite * const site;
   642   private:
   643     /* |handler| is marked unconditionally during minor GC. */
   644     js::EncapsulatedPtrObject handler;
   645     JSCList debuggerLinks;
   646     JSCList siteLinks;
   648   public:
   649     static Breakpoint *fromDebuggerLinks(JSCList *links);
   650     static Breakpoint *fromSiteLinks(JSCList *links);
   651     Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler);
   652     void destroy(FreeOp *fop);
   653     Breakpoint *nextInDebugger();
   654     Breakpoint *nextInSite();
   655     const EncapsulatedPtrObject &getHandler() const { return handler; }
   656     EncapsulatedPtrObject &getHandlerRef() { return handler; }
   657 };
   659 Breakpoint *
   660 Debugger::firstBreakpoint() const
   661 {
   662     if (JS_CLIST_IS_EMPTY(&breakpoints))
   663         return nullptr;
   664     return Breakpoint::fromDebuggerLinks(JS_NEXT_LINK(&breakpoints));
   665 }
   667 Debugger *
   668 Debugger::fromOnNewGlobalObjectWatchersLink(JSCList *link) {
   669     char *p = reinterpret_cast<char *>(link);
   670     return reinterpret_cast<Debugger *>(p - offsetof(Debugger, onNewGlobalObjectWatchersLink));
   671 }
   673 const js::HeapPtrObject &
   674 Debugger::toJSObject() const
   675 {
   676     JS_ASSERT(object);
   677     return object;
   678 }
   680 js::HeapPtrObject &
   681 Debugger::toJSObjectRef()
   682 {
   683     JS_ASSERT(object);
   684     return object;
   685 }
   687 bool
   688 Debugger::observesEnterFrame() const
   689 {
   690     return enabled && getHook(OnEnterFrame);
   691 }
   693 bool
   694 Debugger::observesNewScript() const
   695 {
   696     return enabled && getHook(OnNewScript);
   697 }
   699 bool
   700 Debugger::observesNewGlobalObject() const
   701 {
   702     return enabled && getHook(OnNewGlobalObject);
   703 }
   705 bool
   706 Debugger::observesGlobal(GlobalObject *global) const
   707 {
   708     return debuggees.has(global);
   709 }
   711 JSTrapStatus
   712 Debugger::onEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp)
   713 {
   714     if (cx->compartment()->getDebuggees().empty())
   715         return JSTRAP_CONTINUE;
   716     return slowPathOnEnterFrame(cx, frame, vp);
   717 }
   719 JSTrapStatus
   720 Debugger::onDebuggerStatement(JSContext *cx, MutableHandleValue vp)
   721 {
   722     return cx->compartment()->getDebuggees().empty()
   723            ? JSTRAP_CONTINUE
   724            : dispatchHook(cx, vp, OnDebuggerStatement);
   725 }
   727 JSTrapStatus
   728 Debugger::onExceptionUnwind(JSContext *cx, MutableHandleValue vp)
   729 {
   730     return cx->compartment()->getDebuggees().empty()
   731            ? JSTRAP_CONTINUE
   732            : dispatchHook(cx, vp, OnExceptionUnwind);
   733 }
   735 void
   736 Debugger::onNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal)
   737 {
   738     JS_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal);
   739     JS_ASSERT_IF(script->compileAndGo(), compileAndGoGlobal == &script->uninlinedGlobal());
   740     // We early return in slowPathOnNewScript for self-hosted scripts, so we can
   741     // ignore those in our assertion here.
   742     JS_ASSERT_IF(!script->compartment()->options().invisibleToDebugger() &&
   743                  !script->selfHosted(),
   744                  script->compartment()->firedOnNewGlobalObject);
   745     JS_ASSERT_IF(!script->compileAndGo(), !compileAndGoGlobal);
   746     if (!script->compartment()->getDebuggees().empty())
   747         slowPathOnNewScript(cx, script, compileAndGoGlobal);
   748 }
   750 void
   751 Debugger::onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global)
   752 {
   753     JS_ASSERT(!global->compartment()->firedOnNewGlobalObject);
   754 #ifdef DEBUG
   755     global->compartment()->firedOnNewGlobalObject = true;
   756 #endif
   757     if (!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers))
   758         Debugger::slowPathOnNewGlobalObject(cx, global);
   759 }
   761 extern bool
   762 EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
   763               ConstTwoByteChars chars, unsigned length, const char *filename, unsigned lineno,
   764               MutableHandleValue rval);
   766 }
   768 #endif /* vm_Debugger_h */

mercurial