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 */