1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/vm/Debugger.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,6148 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "vm/Debugger-inl.h" 1.11 + 1.12 +#include "jscntxt.h" 1.13 +#include "jscompartment.h" 1.14 +#include "jshashutil.h" 1.15 +#include "jsnum.h" 1.16 +#include "jsobj.h" 1.17 +#include "jswrapper.h" 1.18 +#include "frontend/BytecodeCompiler.h" 1.19 +#include "gc/Marking.h" 1.20 +#include "jit/BaselineJIT.h" 1.21 +#include "js/Vector.h" 1.22 +#include "vm/ArgumentsObject.h" 1.23 +#include "vm/DebuggerMemory.h" 1.24 +#include "vm/WrapperObject.h" 1.25 +#include "jsgcinlines.h" 1.26 +#include "jsobjinlines.h" 1.27 +#include "jsopcodeinlines.h" 1.28 +#include "jsscriptinlines.h" 1.29 +#include "vm/ObjectImpl-inl.h" 1.30 +#include "vm/Stack-inl.h" 1.31 + 1.32 +using namespace js; 1.33 + 1.34 +using js::frontend::IsIdentifier; 1.35 +using mozilla::ArrayLength; 1.36 +using mozilla::Maybe; 1.37 + 1.38 + 1.39 +/*** Forward declarations ************************************************************************/ 1.40 + 1.41 +extern const Class DebuggerFrame_class; 1.42 + 1.43 +enum { 1.44 + JSSLOT_DEBUGFRAME_OWNER, 1.45 + JSSLOT_DEBUGFRAME_ARGUMENTS, 1.46 + JSSLOT_DEBUGFRAME_ONSTEP_HANDLER, 1.47 + JSSLOT_DEBUGFRAME_ONPOP_HANDLER, 1.48 + JSSLOT_DEBUGFRAME_COUNT 1.49 +}; 1.50 + 1.51 +extern const Class DebuggerArguments_class; 1.52 + 1.53 +enum { 1.54 + JSSLOT_DEBUGARGUMENTS_FRAME, 1.55 + JSSLOT_DEBUGARGUMENTS_COUNT 1.56 +}; 1.57 + 1.58 +extern const Class DebuggerEnv_class; 1.59 + 1.60 +enum { 1.61 + JSSLOT_DEBUGENV_OWNER, 1.62 + JSSLOT_DEBUGENV_COUNT 1.63 +}; 1.64 + 1.65 +extern const Class DebuggerObject_class; 1.66 + 1.67 +enum { 1.68 + JSSLOT_DEBUGOBJECT_OWNER, 1.69 + JSSLOT_DEBUGOBJECT_COUNT 1.70 +}; 1.71 + 1.72 +extern const Class DebuggerScript_class; 1.73 + 1.74 +enum { 1.75 + JSSLOT_DEBUGSCRIPT_OWNER, 1.76 + JSSLOT_DEBUGSCRIPT_COUNT 1.77 +}; 1.78 + 1.79 +extern const Class DebuggerSource_class; 1.80 + 1.81 +enum { 1.82 + JSSLOT_DEBUGSOURCE_OWNER, 1.83 + JSSLOT_DEBUGSOURCE_COUNT 1.84 +}; 1.85 + 1.86 + 1.87 +/*** Utils ***************************************************************************************/ 1.88 + 1.89 +static bool 1.90 +ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required) 1.91 +{ 1.92 + JS_ASSERT(required > 0); 1.93 + JS_ASSERT(required <= 10); 1.94 + char s[2]; 1.95 + s[0] = '0' + (required - 1); 1.96 + s[1] = '\0'; 1.97 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, 1.98 + name, s, required == 2 ? "" : "s"); 1.99 + return false; 1.100 +} 1.101 + 1.102 +static inline bool 1.103 +EnsureFunctionHasScript(JSContext *cx, HandleFunction fun) 1.104 +{ 1.105 + if (fun->isInterpretedLazy()) { 1.106 + AutoCompartment ac(cx, fun); 1.107 + return !!fun->getOrCreateScript(cx); 1.108 + } 1.109 + return true; 1.110 +} 1.111 + 1.112 +static inline JSScript * 1.113 +GetOrCreateFunctionScript(JSContext *cx, HandleFunction fun) 1.114 +{ 1.115 + MOZ_ASSERT(fun->isInterpreted()); 1.116 + if (!EnsureFunctionHasScript(cx, fun)) 1.117 + return nullptr; 1.118 + return fun->nonLazyScript(); 1.119 +} 1.120 + 1.121 +#define REQUIRE_ARGC(name, n) \ 1.122 + JS_BEGIN_MACRO \ 1.123 + if (argc < (n)) \ 1.124 + return ReportMoreArgsNeeded(cx, name, n); \ 1.125 + JS_END_MACRO 1.126 + 1.127 +static bool 1.128 +ReportObjectRequired(JSContext *cx) 1.129 +{ 1.130 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); 1.131 + return false; 1.132 +} 1.133 + 1.134 +static bool 1.135 +ValueToIdentifier(JSContext *cx, HandleValue v, MutableHandleId id) 1.136 +{ 1.137 + if (!ValueToId<CanGC>(cx, v, id)) 1.138 + return false; 1.139 + if (!JSID_IS_ATOM(id) || !IsIdentifier(JSID_TO_ATOM(id))) { 1.140 + RootedValue val(cx, v); 1.141 + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE, 1.142 + JSDVG_SEARCH_STACK, val, js::NullPtr(), "not an identifier", 1.143 + nullptr); 1.144 + return false; 1.145 + } 1.146 + return true; 1.147 +} 1.148 + 1.149 +/* 1.150 + * A range of all the Debugger.Frame objects for a particular AbstractFramePtr. 1.151 + * 1.152 + * FIXME This checks only current debuggers, so it relies on a hack in 1.153 + * Debugger::removeDebuggeeGlobal to make sure only current debuggers 1.154 + * have Frame objects with .live === true. 1.155 + */ 1.156 +class Debugger::FrameRange 1.157 +{ 1.158 + AbstractFramePtr frame; 1.159 + 1.160 + /* The debuggers in |fp|'s compartment, or nullptr if there are none. */ 1.161 + GlobalObject::DebuggerVector *debuggers; 1.162 + 1.163 + /* 1.164 + * The index of the front Debugger.Frame's debugger in debuggers. 1.165 + * nextDebugger < debuggerCount if and only if the range is not empty. 1.166 + */ 1.167 + size_t debuggerCount, nextDebugger; 1.168 + 1.169 + /* 1.170 + * If the range is not empty, this is front Debugger.Frame's entry in its 1.171 + * debugger's frame table. 1.172 + */ 1.173 + FrameMap::Ptr entry; 1.174 + 1.175 + public: 1.176 + /* 1.177 + * Return a range containing all Debugger.Frame instances referring to 1.178 + * |fp|. |global| is |fp|'s global object; if nullptr or omitted, we 1.179 + * compute it ourselves from |fp|. 1.180 + * 1.181 + * We keep an index into the compartment's debugger list, and a 1.182 + * FrameMap::Ptr into the current debugger's frame map. Thus, if the set of 1.183 + * debuggers in |fp|'s compartment changes, this range becomes invalid. 1.184 + * Similarly, if stack frames are added to or removed from frontDebugger(), 1.185 + * then the range's front is invalid until popFront is called. 1.186 + */ 1.187 + FrameRange(AbstractFramePtr frame, GlobalObject *global = nullptr) 1.188 + : frame(frame) 1.189 + { 1.190 + nextDebugger = 0; 1.191 + 1.192 + /* Find our global, if we were not given one. */ 1.193 + if (!global) 1.194 + global = &frame.script()->global(); 1.195 + 1.196 + /* The frame and global must match. */ 1.197 + JS_ASSERT(&frame.script()->global() == global); 1.198 + 1.199 + /* Find the list of debuggers we'll iterate over. There may be none. */ 1.200 + debuggers = global->getDebuggers(); 1.201 + if (debuggers) { 1.202 + debuggerCount = debuggers->length(); 1.203 + findNext(); 1.204 + } else { 1.205 + debuggerCount = 0; 1.206 + } 1.207 + } 1.208 + 1.209 + bool empty() const { 1.210 + return nextDebugger >= debuggerCount; 1.211 + } 1.212 + 1.213 + JSObject *frontFrame() const { 1.214 + JS_ASSERT(!empty()); 1.215 + return entry->value(); 1.216 + } 1.217 + 1.218 + Debugger *frontDebugger() const { 1.219 + JS_ASSERT(!empty()); 1.220 + return (*debuggers)[nextDebugger]; 1.221 + } 1.222 + 1.223 + /* 1.224 + * Delete the front frame from its Debugger's frame map. After this call, 1.225 + * the range's front is invalid until popFront is called. 1.226 + */ 1.227 + void removeFrontFrame() const { 1.228 + JS_ASSERT(!empty()); 1.229 + frontDebugger()->frames.remove(entry); 1.230 + } 1.231 + 1.232 + void popFront() { 1.233 + JS_ASSERT(!empty()); 1.234 + nextDebugger++; 1.235 + findNext(); 1.236 + } 1.237 + 1.238 + private: 1.239 + /* 1.240 + * Either make this range refer to the first appropriate Debugger.Frame at 1.241 + * or after nextDebugger, or make it empty. 1.242 + */ 1.243 + void findNext() { 1.244 + while (!empty()) { 1.245 + Debugger *dbg = (*debuggers)[nextDebugger]; 1.246 + entry = dbg->frames.lookup(frame); 1.247 + if (entry) 1.248 + break; 1.249 + nextDebugger++; 1.250 + } 1.251 + } 1.252 +}; 1.253 + 1.254 +/*** Breakpoints *********************************************************************************/ 1.255 + 1.256 +BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc) 1.257 + : script(script), pc(pc), enabledCount(0), 1.258 + trapHandler(nullptr), trapClosure(UndefinedValue()) 1.259 +{ 1.260 + JS_ASSERT(!script->hasBreakpointsAt(pc)); 1.261 + JS_INIT_CLIST(&breakpoints); 1.262 +} 1.263 + 1.264 +void 1.265 +BreakpointSite::recompile(FreeOp *fop) 1.266 +{ 1.267 +#ifdef JS_ION 1.268 + if (script->hasBaselineScript()) 1.269 + script->baselineScript()->toggleDebugTraps(script, pc); 1.270 +#endif 1.271 +} 1.272 + 1.273 +void 1.274 +BreakpointSite::inc(FreeOp *fop) 1.275 +{ 1.276 + enabledCount++; 1.277 + if (enabledCount == 1 && !trapHandler) 1.278 + recompile(fop); 1.279 +} 1.280 + 1.281 +void 1.282 +BreakpointSite::dec(FreeOp *fop) 1.283 +{ 1.284 + JS_ASSERT(enabledCount > 0); 1.285 + enabledCount--; 1.286 + if (enabledCount == 0 && !trapHandler) 1.287 + recompile(fop); 1.288 +} 1.289 + 1.290 +void 1.291 +BreakpointSite::setTrap(FreeOp *fop, JSTrapHandler handler, const Value &closure) 1.292 +{ 1.293 + trapHandler = handler; 1.294 + trapClosure = closure; 1.295 + 1.296 + if (enabledCount == 0) 1.297 + recompile(fop); 1.298 +} 1.299 + 1.300 +void 1.301 +BreakpointSite::clearTrap(FreeOp *fop, JSTrapHandler *handlerp, Value *closurep) 1.302 +{ 1.303 + if (handlerp) 1.304 + *handlerp = trapHandler; 1.305 + if (closurep) 1.306 + *closurep = trapClosure; 1.307 + 1.308 + trapHandler = nullptr; 1.309 + trapClosure = UndefinedValue(); 1.310 + if (enabledCount == 0) { 1.311 + if (!fop->runtime()->isHeapBusy()) { 1.312 + /* If the GC is running then the script is being destroyed. */ 1.313 + recompile(fop); 1.314 + } 1.315 + destroyIfEmpty(fop); 1.316 + } 1.317 +} 1.318 + 1.319 +void 1.320 +BreakpointSite::destroyIfEmpty(FreeOp *fop) 1.321 +{ 1.322 + if (JS_CLIST_IS_EMPTY(&breakpoints) && !trapHandler) 1.323 + script->destroyBreakpointSite(fop, pc); 1.324 +} 1.325 + 1.326 +Breakpoint * 1.327 +BreakpointSite::firstBreakpoint() const 1.328 +{ 1.329 + if (JS_CLIST_IS_EMPTY(&breakpoints)) 1.330 + return nullptr; 1.331 + return Breakpoint::fromSiteLinks(JS_NEXT_LINK(&breakpoints)); 1.332 +} 1.333 + 1.334 +bool 1.335 +BreakpointSite::hasBreakpoint(Breakpoint *bp) 1.336 +{ 1.337 + for (Breakpoint *p = firstBreakpoint(); p; p = p->nextInSite()) 1.338 + if (p == bp) 1.339 + return true; 1.340 + return false; 1.341 +} 1.342 + 1.343 +Breakpoint::Breakpoint(Debugger *debugger, BreakpointSite *site, JSObject *handler) 1.344 + : debugger(debugger), site(site), handler(handler) 1.345 +{ 1.346 + JS_ASSERT(handler->compartment() == debugger->object->compartment()); 1.347 + JS_APPEND_LINK(&debuggerLinks, &debugger->breakpoints); 1.348 + JS_APPEND_LINK(&siteLinks, &site->breakpoints); 1.349 +} 1.350 + 1.351 +Breakpoint * 1.352 +Breakpoint::fromDebuggerLinks(JSCList *links) 1.353 +{ 1.354 + return (Breakpoint *) ((unsigned char *) links - offsetof(Breakpoint, debuggerLinks)); 1.355 +} 1.356 + 1.357 +Breakpoint * 1.358 +Breakpoint::fromSiteLinks(JSCList *links) 1.359 +{ 1.360 + return (Breakpoint *) ((unsigned char *) links - offsetof(Breakpoint, siteLinks)); 1.361 +} 1.362 + 1.363 +void 1.364 +Breakpoint::destroy(FreeOp *fop) 1.365 +{ 1.366 + if (debugger->enabled) 1.367 + site->dec(fop); 1.368 + JS_REMOVE_LINK(&debuggerLinks); 1.369 + JS_REMOVE_LINK(&siteLinks); 1.370 + site->destroyIfEmpty(fop); 1.371 + fop->delete_(this); 1.372 +} 1.373 + 1.374 +Breakpoint * 1.375 +Breakpoint::nextInDebugger() 1.376 +{ 1.377 + JSCList *link = JS_NEXT_LINK(&debuggerLinks); 1.378 + return (link == &debugger->breakpoints) ? nullptr : fromDebuggerLinks(link); 1.379 +} 1.380 + 1.381 +Breakpoint * 1.382 +Breakpoint::nextInSite() 1.383 +{ 1.384 + JSCList *link = JS_NEXT_LINK(&siteLinks); 1.385 + return (link == &site->breakpoints) ? nullptr : fromSiteLinks(link); 1.386 +} 1.387 + 1.388 +/*** Debugger hook dispatch **********************************************************************/ 1.389 + 1.390 +Debugger::Debugger(JSContext *cx, JSObject *dbg) 1.391 + : object(dbg), uncaughtExceptionHook(nullptr), enabled(true), 1.392 + frames(cx->runtime()), scripts(cx), sources(cx), objects(cx), environments(cx) 1.393 +{ 1.394 + assertSameCompartment(cx, dbg); 1.395 + 1.396 + cx->runtime()->debuggerList.insertBack(this); 1.397 + JS_INIT_CLIST(&breakpoints); 1.398 + JS_INIT_CLIST(&onNewGlobalObjectWatchersLink); 1.399 +} 1.400 + 1.401 +Debugger::~Debugger() 1.402 +{ 1.403 + JS_ASSERT_IF(debuggees.initialized(), debuggees.empty()); 1.404 + 1.405 + /* 1.406 + * Since the inactive state for this link is a singleton cycle, it's always 1.407 + * safe to apply JS_REMOVE_LINK to it, regardless of whether we're in the list or not. 1.408 + * 1.409 + * We don't have to worry about locking here since Debugger is not 1.410 + * background finalized. 1.411 + */ 1.412 + JS_REMOVE_LINK(&onNewGlobalObjectWatchersLink); 1.413 +} 1.414 + 1.415 +bool 1.416 +Debugger::init(JSContext *cx) 1.417 +{ 1.418 + bool ok = debuggees.init() && 1.419 + frames.init() && 1.420 + scripts.init() && 1.421 + sources.init() && 1.422 + objects.init() && 1.423 + environments.init(); 1.424 + if (!ok) 1.425 + js_ReportOutOfMemory(cx); 1.426 + return ok; 1.427 +} 1.428 + 1.429 +Debugger * 1.430 +Debugger::fromJSObject(JSObject *obj) 1.431 +{ 1.432 + JS_ASSERT(js::GetObjectClass(obj) == &jsclass); 1.433 + return (Debugger *) obj->getPrivate(); 1.434 +} 1.435 + 1.436 +JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER)); 1.437 +JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSOURCE_OWNER)); 1.438 +JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER)); 1.439 +JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGENV_OWNER)); 1.440 + 1.441 +Debugger * 1.442 +Debugger::fromChildJSObject(JSObject *obj) 1.443 +{ 1.444 + JS_ASSERT(obj->getClass() == &DebuggerFrame_class || 1.445 + obj->getClass() == &DebuggerScript_class || 1.446 + obj->getClass() == &DebuggerSource_class || 1.447 + obj->getClass() == &DebuggerObject_class || 1.448 + obj->getClass() == &DebuggerEnv_class); 1.449 + JSObject *dbgobj = &obj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER).toObject(); 1.450 + return fromJSObject(dbgobj); 1.451 +} 1.452 + 1.453 +bool 1.454 +Debugger::getScriptFrameWithIter(JSContext *cx, AbstractFramePtr frame, 1.455 + const ScriptFrameIter *maybeIter, MutableHandleValue vp) 1.456 +{ 1.457 + MOZ_ASSERT_IF(maybeIter, maybeIter->abstractFramePtr() == frame); 1.458 + 1.459 + FrameMap::AddPtr p = frames.lookupForAdd(frame); 1.460 + if (!p) { 1.461 + /* Create and populate the Debugger.Frame object. */ 1.462 + JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject(); 1.463 + JSObject *frameobj = 1.464 + NewObjectWithGivenProto(cx, &DebuggerFrame_class, proto, nullptr); 1.465 + if (!frameobj) 1.466 + return false; 1.467 + 1.468 + // Eagerly copy ScriptFrameIter data if we've already walked the 1.469 + // stack. 1.470 + if (maybeIter) { 1.471 + AbstractFramePtr data = maybeIter->copyDataAsAbstractFramePtr(); 1.472 + if (!data) 1.473 + return false; 1.474 + frameobj->setPrivate(data.raw()); 1.475 + } else { 1.476 + frameobj->setPrivate(frame.raw()); 1.477 + } 1.478 + 1.479 + frameobj->setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*object)); 1.480 + 1.481 + if (!frames.add(p, frame, frameobj)) { 1.482 + js_ReportOutOfMemory(cx); 1.483 + return false; 1.484 + } 1.485 + } 1.486 + vp.setObject(*p->value()); 1.487 + return true; 1.488 +} 1.489 + 1.490 +JSObject * 1.491 +Debugger::getHook(Hook hook) const 1.492 +{ 1.493 + JS_ASSERT(hook >= 0 && hook < HookCount); 1.494 + const Value &v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + hook); 1.495 + return v.isUndefined() ? nullptr : &v.toObject(); 1.496 +} 1.497 + 1.498 +bool 1.499 +Debugger::hasAnyLiveHooks() const 1.500 +{ 1.501 + if (!enabled) 1.502 + return false; 1.503 + 1.504 + if (getHook(OnDebuggerStatement) || 1.505 + getHook(OnExceptionUnwind) || 1.506 + getHook(OnNewScript) || 1.507 + getHook(OnEnterFrame)) 1.508 + { 1.509 + return true; 1.510 + } 1.511 + 1.512 + /* If any breakpoints are in live scripts, return true. */ 1.513 + for (Breakpoint *bp = firstBreakpoint(); bp; bp = bp->nextInDebugger()) { 1.514 + if (IsScriptMarked(&bp->site->script)) 1.515 + return true; 1.516 + } 1.517 + 1.518 + for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) { 1.519 + JSObject *frameObj = r.front().value(); 1.520 + if (!frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() || 1.521 + !frameObj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined()) 1.522 + return true; 1.523 + } 1.524 + 1.525 + return false; 1.526 +} 1.527 + 1.528 +JSTrapStatus 1.529 +Debugger::slowPathOnEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) 1.530 +{ 1.531 + /* Build the list of recipients. */ 1.532 + AutoValueVector triggered(cx); 1.533 + Handle<GlobalObject*> global = cx->global(); 1.534 + 1.535 + if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) { 1.536 + for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) { 1.537 + Debugger *dbg = *p; 1.538 + if (dbg->observesFrame(frame) && dbg->observesEnterFrame() && 1.539 + !triggered.append(ObjectValue(*dbg->toJSObject()))) 1.540 + { 1.541 + return JSTRAP_ERROR; 1.542 + } 1.543 + } 1.544 + } 1.545 + 1.546 + /* Deliver the event, checking again as in dispatchHook. */ 1.547 + for (Value *p = triggered.begin(); p != triggered.end(); p++) { 1.548 + Debugger *dbg = Debugger::fromJSObject(&p->toObject()); 1.549 + if (dbg->debuggees.has(global) && dbg->observesEnterFrame()) { 1.550 + JSTrapStatus status = dbg->fireEnterFrame(cx, frame, vp); 1.551 + if (status != JSTRAP_CONTINUE) 1.552 + return status; 1.553 + } 1.554 + } 1.555 + 1.556 + return JSTRAP_CONTINUE; 1.557 +} 1.558 + 1.559 +static void 1.560 +DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp *fop, AbstractFramePtr frame, 1.561 + JSObject *frameobj); 1.562 + 1.563 +static void 1.564 +DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj); 1.565 + 1.566 +/* 1.567 + * Handle leaving a frame with debuggers watching. |frameOk| indicates whether 1.568 + * the frame is exiting normally or abruptly. Set |cx|'s exception and/or 1.569 + * |cx->fp()|'s return value, and return a new success value. 1.570 + */ 1.571 +bool 1.572 +Debugger::slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool frameOk) 1.573 +{ 1.574 + Handle<GlobalObject*> global = cx->global(); 1.575 + 1.576 + /* Save the frame's completion value. */ 1.577 + JSTrapStatus status; 1.578 + RootedValue value(cx); 1.579 + Debugger::resultToCompletion(cx, frameOk, frame.returnValue(), &status, &value); 1.580 + 1.581 + /* Build a list of the recipients. */ 1.582 + AutoObjectVector frames(cx); 1.583 + for (FrameRange r(frame, global); !r.empty(); r.popFront()) { 1.584 + if (!frames.append(r.frontFrame())) { 1.585 + cx->clearPendingException(); 1.586 + return false; 1.587 + } 1.588 + } 1.589 + 1.590 + /* For each Debugger.Frame, fire its onPop handler, if any. */ 1.591 + for (JSObject **p = frames.begin(); p != frames.end(); p++) { 1.592 + RootedObject frameobj(cx, *p); 1.593 + Debugger *dbg = Debugger::fromChildJSObject(frameobj); 1.594 + 1.595 + if (dbg->enabled && 1.596 + !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined()) { 1.597 + RootedValue handler(cx, frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER)); 1.598 + 1.599 + Maybe<AutoCompartment> ac; 1.600 + ac.construct(cx, dbg->object); 1.601 + 1.602 + RootedValue completion(cx); 1.603 + if (!dbg->newCompletionValue(cx, status, value, &completion)) { 1.604 + status = dbg->handleUncaughtException(ac, false); 1.605 + break; 1.606 + } 1.607 + 1.608 + /* Call the onPop handler. */ 1.609 + RootedValue rval(cx); 1.610 + bool hookOk = Invoke(cx, ObjectValue(*frameobj), handler, 1, completion.address(), 1.611 + &rval); 1.612 + RootedValue nextValue(cx); 1.613 + JSTrapStatus nextStatus = dbg->parseResumptionValue(ac, hookOk, rval, &nextValue); 1.614 + 1.615 + /* 1.616 + * At this point, we are back in the debuggee compartment, and any error has 1.617 + * been wrapped up as a completion value. 1.618 + */ 1.619 + JS_ASSERT(cx->compartment() == global->compartment()); 1.620 + JS_ASSERT(!cx->isExceptionPending()); 1.621 + 1.622 + /* JSTRAP_CONTINUE means "make no change". */ 1.623 + if (nextStatus != JSTRAP_CONTINUE) { 1.624 + status = nextStatus; 1.625 + value = nextValue; 1.626 + } 1.627 + } 1.628 + } 1.629 + 1.630 + /* 1.631 + * Clean up all Debugger.Frame instances. Use a fresh FrameRange, as one 1.632 + * debugger's onPop handler could have caused another debugger to create its 1.633 + * own Debugger.Frame instance. 1.634 + */ 1.635 + for (FrameRange r(frame, global); !r.empty(); r.popFront()) { 1.636 + RootedObject frameobj(cx, r.frontFrame()); 1.637 + Debugger *dbg = r.frontDebugger(); 1.638 + JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj)); 1.639 + 1.640 + FreeOp *fop = cx->runtime()->defaultFreeOp(); 1.641 + DebuggerFrame_freeScriptFrameIterData(fop, frameobj); 1.642 + DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj); 1.643 + 1.644 + dbg->frames.remove(frame); 1.645 + } 1.646 + 1.647 + /* 1.648 + * If this is an eval frame, then from the debugger's perspective the 1.649 + * script is about to be destroyed. Remove any breakpoints in it. 1.650 + */ 1.651 + if (frame.isEvalFrame()) { 1.652 + RootedScript script(cx, frame.script()); 1.653 + script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), nullptr, nullptr); 1.654 + } 1.655 + 1.656 + /* Establish (status, value) as our resumption value. */ 1.657 + switch (status) { 1.658 + case JSTRAP_RETURN: 1.659 + frame.setReturnValue(value); 1.660 + return true; 1.661 + 1.662 + case JSTRAP_THROW: 1.663 + cx->setPendingException(value); 1.664 + return false; 1.665 + 1.666 + case JSTRAP_ERROR: 1.667 + JS_ASSERT(!cx->isExceptionPending()); 1.668 + return false; 1.669 + 1.670 + default: 1.671 + MOZ_ASSUME_UNREACHABLE("bad final trap status"); 1.672 + } 1.673 +} 1.674 + 1.675 +bool 1.676 +Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, MutableHandleValue rval) 1.677 +{ 1.678 + if (!env) { 1.679 + rval.setNull(); 1.680 + return true; 1.681 + } 1.682 + 1.683 + /* 1.684 + * DebuggerEnv should only wrap a debug scope chain obtained (transitively) 1.685 + * from GetDebugScopeFor(Frame|Function). 1.686 + */ 1.687 + JS_ASSERT(!env->is<ScopeObject>()); 1.688 + 1.689 + JSObject *envobj; 1.690 + DependentAddPtr<ObjectWeakMap> p(cx, environments, env); 1.691 + if (p) { 1.692 + envobj = p->value(); 1.693 + } else { 1.694 + /* Create a new Debugger.Environment for env. */ 1.695 + JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject(); 1.696 + envobj = NewObjectWithGivenProto(cx, &DebuggerEnv_class, proto, nullptr, TenuredObject); 1.697 + if (!envobj) 1.698 + return false; 1.699 + envobj->setPrivateGCThing(env); 1.700 + envobj->setReservedSlot(JSSLOT_DEBUGENV_OWNER, ObjectValue(*object)); 1.701 + if (!p.add(cx, environments, env, envobj)) { 1.702 + js_ReportOutOfMemory(cx); 1.703 + return false; 1.704 + } 1.705 + 1.706 + CrossCompartmentKey key(CrossCompartmentKey::DebuggerEnvironment, object, env); 1.707 + if (!object->compartment()->putWrapper(cx, key, ObjectValue(*envobj))) { 1.708 + environments.remove(env); 1.709 + js_ReportOutOfMemory(cx); 1.710 + return false; 1.711 + } 1.712 + } 1.713 + rval.setObject(*envobj); 1.714 + return true; 1.715 +} 1.716 + 1.717 +bool 1.718 +Debugger::wrapDebuggeeValue(JSContext *cx, MutableHandleValue vp) 1.719 +{ 1.720 + assertSameCompartment(cx, object.get()); 1.721 + 1.722 + if (vp.isObject()) { 1.723 + RootedObject obj(cx, &vp.toObject()); 1.724 + 1.725 + if (obj->is<JSFunction>()) { 1.726 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.727 + if (!EnsureFunctionHasScript(cx, fun)) 1.728 + return false; 1.729 + } 1.730 + 1.731 + DependentAddPtr<ObjectWeakMap> p(cx, objects, obj); 1.732 + if (p) { 1.733 + vp.setObject(*p->value()); 1.734 + } else { 1.735 + /* Create a new Debugger.Object for obj. */ 1.736 + JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject(); 1.737 + JSObject *dobj = 1.738 + NewObjectWithGivenProto(cx, &DebuggerObject_class, proto, nullptr, TenuredObject); 1.739 + if (!dobj) 1.740 + return false; 1.741 + dobj->setPrivateGCThing(obj); 1.742 + dobj->setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*object)); 1.743 + 1.744 + if (!p.add(cx, objects, obj, dobj)) { 1.745 + js_ReportOutOfMemory(cx); 1.746 + return false; 1.747 + } 1.748 + 1.749 + if (obj->compartment() != object->compartment()) { 1.750 + CrossCompartmentKey key(CrossCompartmentKey::DebuggerObject, object, obj); 1.751 + if (!object->compartment()->putWrapper(cx, key, ObjectValue(*dobj))) { 1.752 + objects.remove(obj); 1.753 + js_ReportOutOfMemory(cx); 1.754 + return false; 1.755 + } 1.756 + } 1.757 + 1.758 + vp.setObject(*dobj); 1.759 + } 1.760 + } else if (vp.isMagic()) { 1.761 + RootedObject optObj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); 1.762 + if (!optObj) 1.763 + return false; 1.764 + 1.765 + // We handle two sentinel values: missing arguments (overloading 1.766 + // JS_OPTIMIZED_ARGUMENTS) and optimized out slots (JS_OPTIMIZED_OUT). 1.767 + // Other magic values should not have escaped. 1.768 + PropertyName *name; 1.769 + if (vp.whyMagic() == JS_OPTIMIZED_ARGUMENTS) { 1.770 + name = cx->names().missingArguments; 1.771 + } else { 1.772 + MOZ_ASSERT(vp.whyMagic() == JS_OPTIMIZED_OUT); 1.773 + name = cx->names().optimizedOut; 1.774 + } 1.775 + 1.776 + RootedValue trueVal(cx, BooleanValue(true)); 1.777 + if (!JSObject::defineProperty(cx, optObj, name, trueVal)) 1.778 + return false; 1.779 + 1.780 + vp.setObject(*optObj); 1.781 + } else if (!cx->compartment()->wrap(cx, vp)) { 1.782 + vp.setUndefined(); 1.783 + return false; 1.784 + } 1.785 + 1.786 + return true; 1.787 +} 1.788 + 1.789 +bool 1.790 +Debugger::unwrapDebuggeeValue(JSContext *cx, MutableHandleValue vp) 1.791 +{ 1.792 + assertSameCompartment(cx, object.get(), vp); 1.793 + if (vp.isObject()) { 1.794 + JSObject *dobj = &vp.toObject(); 1.795 + if (dobj->getClass() != &DebuggerObject_class) { 1.796 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, 1.797 + "Debugger", "Debugger.Object", dobj->getClass()->name); 1.798 + return false; 1.799 + } 1.800 + 1.801 + Value owner = dobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER); 1.802 + if (owner.isUndefined() || &owner.toObject() != object) { 1.803 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.804 + owner.isUndefined() 1.805 + ? JSMSG_DEBUG_OBJECT_PROTO 1.806 + : JSMSG_DEBUG_OBJECT_WRONG_OWNER); 1.807 + return false; 1.808 + } 1.809 + 1.810 + vp.setObject(*static_cast<JSObject*>(dobj->getPrivate())); 1.811 + } 1.812 + return true; 1.813 +} 1.814 + 1.815 +JSTrapStatus 1.816 +Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment> &ac, 1.817 + MutableHandleValue *vp, bool callHook) 1.818 +{ 1.819 + JSContext *cx = ac.ref().context()->asJSContext(); 1.820 + if (cx->isExceptionPending()) { 1.821 + if (callHook && uncaughtExceptionHook) { 1.822 + RootedValue exc(cx); 1.823 + if (!cx->getPendingException(&exc)) 1.824 + return JSTRAP_ERROR; 1.825 + cx->clearPendingException(); 1.826 + RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook)); 1.827 + RootedValue rv(cx); 1.828 + if (Invoke(cx, ObjectValue(*object), fval, 1, exc.address(), &rv)) 1.829 + return vp ? parseResumptionValue(ac, true, rv, *vp, false) : JSTRAP_CONTINUE; 1.830 + } 1.831 + 1.832 + if (cx->isExceptionPending()) { 1.833 + JS_ReportPendingException(cx); 1.834 + cx->clearPendingException(); 1.835 + } 1.836 + } 1.837 + ac.destroy(); 1.838 + return JSTRAP_ERROR; 1.839 +} 1.840 + 1.841 +JSTrapStatus 1.842 +Debugger::handleUncaughtException(Maybe<AutoCompartment> &ac, MutableHandleValue vp, bool callHook) 1.843 +{ 1.844 + return handleUncaughtExceptionHelper(ac, &vp, callHook); 1.845 +} 1.846 + 1.847 +JSTrapStatus 1.848 +Debugger::handleUncaughtException(Maybe<AutoCompartment> &ac, bool callHook) 1.849 +{ 1.850 + return handleUncaughtExceptionHelper(ac, nullptr, callHook); 1.851 +} 1.852 + 1.853 +void 1.854 +Debugger::resultToCompletion(JSContext *cx, bool ok, const Value &rv, 1.855 + JSTrapStatus *status, MutableHandleValue value) 1.856 +{ 1.857 + JS_ASSERT_IF(ok, !cx->isExceptionPending()); 1.858 + 1.859 + if (ok) { 1.860 + *status = JSTRAP_RETURN; 1.861 + value.set(rv); 1.862 + } else if (cx->isExceptionPending()) { 1.863 + *status = JSTRAP_THROW; 1.864 + if (!cx->getPendingException(value)) 1.865 + *status = JSTRAP_ERROR; 1.866 + cx->clearPendingException(); 1.867 + } else { 1.868 + *status = JSTRAP_ERROR; 1.869 + value.setUndefined(); 1.870 + } 1.871 +} 1.872 + 1.873 +bool 1.874 +Debugger::newCompletionValue(JSContext *cx, JSTrapStatus status, Value value_, 1.875 + MutableHandleValue result) 1.876 +{ 1.877 + /* 1.878 + * We must be in the debugger's compartment, since that's where we want 1.879 + * to construct the completion value. 1.880 + */ 1.881 + assertSameCompartment(cx, object.get()); 1.882 + 1.883 + RootedId key(cx); 1.884 + RootedValue value(cx, value_); 1.885 + 1.886 + switch (status) { 1.887 + case JSTRAP_RETURN: 1.888 + key = NameToId(cx->names().return_); 1.889 + break; 1.890 + 1.891 + case JSTRAP_THROW: 1.892 + key = NameToId(cx->names().throw_); 1.893 + break; 1.894 + 1.895 + case JSTRAP_ERROR: 1.896 + result.setNull(); 1.897 + return true; 1.898 + 1.899 + default: 1.900 + MOZ_ASSUME_UNREACHABLE("bad status passed to Debugger::newCompletionValue"); 1.901 + } 1.902 + 1.903 + /* Common tail for JSTRAP_RETURN and JSTRAP_THROW. */ 1.904 + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); 1.905 + if (!obj || 1.906 + !wrapDebuggeeValue(cx, &value) || 1.907 + !DefineNativeProperty(cx, obj, key, value, JS_PropertyStub, JS_StrictPropertyStub, 1.908 + JSPROP_ENUMERATE)) 1.909 + { 1.910 + return false; 1.911 + } 1.912 + 1.913 + result.setObject(*obj); 1.914 + return true; 1.915 +} 1.916 + 1.917 +bool 1.918 +Debugger::receiveCompletionValue(Maybe<AutoCompartment> &ac, bool ok, 1.919 + HandleValue val, 1.920 + MutableHandleValue vp) 1.921 +{ 1.922 + JSContext *cx = ac.ref().context()->asJSContext(); 1.923 + 1.924 + JSTrapStatus status; 1.925 + RootedValue value(cx); 1.926 + resultToCompletion(cx, ok, val, &status, &value); 1.927 + ac.destroy(); 1.928 + return newCompletionValue(cx, status, value, vp); 1.929 +} 1.930 + 1.931 +JSTrapStatus 1.932 +Debugger::parseResumptionValue(Maybe<AutoCompartment> &ac, bool ok, const Value &rv, MutableHandleValue vp, 1.933 + bool callHook) 1.934 +{ 1.935 + vp.setUndefined(); 1.936 + if (!ok) 1.937 + return handleUncaughtException(ac, vp, callHook); 1.938 + if (rv.isUndefined()) { 1.939 + ac.destroy(); 1.940 + return JSTRAP_CONTINUE; 1.941 + } 1.942 + if (rv.isNull()) { 1.943 + ac.destroy(); 1.944 + return JSTRAP_ERROR; 1.945 + } 1.946 + 1.947 + /* Check that rv is {return: val} or {throw: val}. */ 1.948 + JSContext *cx = ac.ref().context()->asJSContext(); 1.949 + Rooted<JSObject*> obj(cx); 1.950 + RootedShape shape(cx); 1.951 + RootedId returnId(cx, NameToId(cx->names().return_)); 1.952 + RootedId throwId(cx, NameToId(cx->names().throw_)); 1.953 + bool okResumption = rv.isObject(); 1.954 + if (okResumption) { 1.955 + obj = &rv.toObject(); 1.956 + okResumption = obj->is<JSObject>(); 1.957 + } 1.958 + if (okResumption) { 1.959 + shape = obj->lastProperty(); 1.960 + okResumption = shape->previous() && 1.961 + !shape->previous()->previous() && 1.962 + (shape->propid() == returnId || shape->propid() == throwId) && 1.963 + shape->isDataDescriptor(); 1.964 + } 1.965 + if (!okResumption) { 1.966 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION); 1.967 + return handleUncaughtException(ac, vp, callHook); 1.968 + } 1.969 + 1.970 + RootedValue v(cx, vp.get()); 1.971 + if (!NativeGet(cx, obj, obj, shape, &v) || !unwrapDebuggeeValue(cx, &v)) 1.972 + return handleUncaughtException(ac, &v, callHook); 1.973 + 1.974 + ac.destroy(); 1.975 + if (!cx->compartment()->wrap(cx, &v)) { 1.976 + vp.setUndefined(); 1.977 + return JSTRAP_ERROR; 1.978 + } 1.979 + vp.set(v); 1.980 + 1.981 + return shape->propid() == returnId ? JSTRAP_RETURN : JSTRAP_THROW; 1.982 +} 1.983 + 1.984 +static bool 1.985 +CallMethodIfPresent(JSContext *cx, HandleObject obj, const char *name, int argc, Value *argv, 1.986 + MutableHandleValue rval) 1.987 +{ 1.988 + rval.setUndefined(); 1.989 + JSAtom *atom = Atomize(cx, name, strlen(name)); 1.990 + if (!atom) 1.991 + return false; 1.992 + 1.993 + RootedId id(cx, AtomToId(atom)); 1.994 + RootedValue fval(cx); 1.995 + return JSObject::getGeneric(cx, obj, obj, id, &fval) && 1.996 + (!js_IsCallable(fval) || Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval)); 1.997 +} 1.998 + 1.999 +JSTrapStatus 1.1000 +Debugger::fireDebuggerStatement(JSContext *cx, MutableHandleValue vp) 1.1001 +{ 1.1002 + RootedObject hook(cx, getHook(OnDebuggerStatement)); 1.1003 + JS_ASSERT(hook); 1.1004 + JS_ASSERT(hook->isCallable()); 1.1005 + 1.1006 + Maybe<AutoCompartment> ac; 1.1007 + ac.construct(cx, object); 1.1008 + 1.1009 + ScriptFrameIter iter(cx); 1.1010 + 1.1011 + RootedValue scriptFrame(cx); 1.1012 + if (!getScriptFrame(cx, iter, &scriptFrame)) 1.1013 + return handleUncaughtException(ac, false); 1.1014 + 1.1015 + RootedValue rv(cx); 1.1016 + bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptFrame.address(), &rv); 1.1017 + return parseResumptionValue(ac, ok, rv, vp); 1.1018 +} 1.1019 + 1.1020 +JSTrapStatus 1.1021 +Debugger::fireExceptionUnwind(JSContext *cx, MutableHandleValue vp) 1.1022 +{ 1.1023 + RootedObject hook(cx, getHook(OnExceptionUnwind)); 1.1024 + JS_ASSERT(hook); 1.1025 + JS_ASSERT(hook->isCallable()); 1.1026 + 1.1027 + RootedValue exc(cx); 1.1028 + if (!cx->getPendingException(&exc)) 1.1029 + return JSTRAP_ERROR; 1.1030 + cx->clearPendingException(); 1.1031 + 1.1032 + Maybe<AutoCompartment> ac; 1.1033 + ac.construct(cx, object); 1.1034 + 1.1035 + JS::AutoValueArray<2> argv(cx); 1.1036 + argv[0].setUndefined(); 1.1037 + argv[1].set(exc); 1.1038 + 1.1039 + ScriptFrameIter iter(cx); 1.1040 + if (!getScriptFrame(cx, iter, argv[0]) || !wrapDebuggeeValue(cx, argv[1])) 1.1041 + return handleUncaughtException(ac, false); 1.1042 + 1.1043 + RootedValue rv(cx); 1.1044 + bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv.begin(), &rv); 1.1045 + JSTrapStatus st = parseResumptionValue(ac, ok, rv, vp); 1.1046 + if (st == JSTRAP_CONTINUE) 1.1047 + cx->setPendingException(exc); 1.1048 + return st; 1.1049 +} 1.1050 + 1.1051 +JSTrapStatus 1.1052 +Debugger::fireEnterFrame(JSContext *cx, AbstractFramePtr frame, MutableHandleValue vp) 1.1053 +{ 1.1054 + RootedObject hook(cx, getHook(OnEnterFrame)); 1.1055 + JS_ASSERT(hook); 1.1056 + JS_ASSERT(hook->isCallable()); 1.1057 + 1.1058 + Maybe<AutoCompartment> ac; 1.1059 + ac.construct(cx, object); 1.1060 + 1.1061 + RootedValue scriptFrame(cx); 1.1062 + if (!getScriptFrame(cx, frame, &scriptFrame)) 1.1063 + return handleUncaughtException(ac, false); 1.1064 + 1.1065 + RootedValue rv(cx); 1.1066 + bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptFrame.address(), &rv); 1.1067 + return parseResumptionValue(ac, ok, rv, vp); 1.1068 +} 1.1069 + 1.1070 +void 1.1071 +Debugger::fireNewScript(JSContext *cx, HandleScript script) 1.1072 +{ 1.1073 + RootedObject hook(cx, getHook(OnNewScript)); 1.1074 + JS_ASSERT(hook); 1.1075 + JS_ASSERT(hook->isCallable()); 1.1076 + 1.1077 + Maybe<AutoCompartment> ac; 1.1078 + ac.construct(cx, object); 1.1079 + 1.1080 + JSObject *dsobj = wrapScript(cx, script); 1.1081 + if (!dsobj) { 1.1082 + handleUncaughtException(ac, false); 1.1083 + return; 1.1084 + } 1.1085 + 1.1086 + RootedValue scriptObject(cx, ObjectValue(*dsobj)); 1.1087 + RootedValue rv(cx); 1.1088 + if (!Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptObject.address(), &rv)) 1.1089 + handleUncaughtException(ac, true); 1.1090 +} 1.1091 + 1.1092 +JSTrapStatus 1.1093 +Debugger::dispatchHook(JSContext *cx, MutableHandleValue vp, Hook which) 1.1094 +{ 1.1095 + JS_ASSERT(which == OnDebuggerStatement || which == OnExceptionUnwind); 1.1096 + 1.1097 + /* 1.1098 + * Determine which debuggers will receive this event, and in what order. 1.1099 + * Make a copy of the list, since the original is mutable and we will be 1.1100 + * calling into arbitrary JS. 1.1101 + * 1.1102 + * Note: In the general case, 'triggered' contains references to objects in 1.1103 + * different compartments--every compartment *except* this one. 1.1104 + */ 1.1105 + AutoValueVector triggered(cx); 1.1106 + Handle<GlobalObject*> global = cx->global(); 1.1107 + if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) { 1.1108 + for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) { 1.1109 + Debugger *dbg = *p; 1.1110 + if (dbg->enabled && dbg->getHook(which)) { 1.1111 + if (!triggered.append(ObjectValue(*dbg->toJSObject()))) 1.1112 + return JSTRAP_ERROR; 1.1113 + } 1.1114 + } 1.1115 + } 1.1116 + 1.1117 + /* 1.1118 + * Deliver the event to each debugger, checking again to make sure it 1.1119 + * should still be delivered. 1.1120 + */ 1.1121 + for (Value *p = triggered.begin(); p != triggered.end(); p++) { 1.1122 + Debugger *dbg = Debugger::fromJSObject(&p->toObject()); 1.1123 + if (dbg->debuggees.has(global) && dbg->enabled && dbg->getHook(which)) { 1.1124 + JSTrapStatus st = (which == OnDebuggerStatement) 1.1125 + ? dbg->fireDebuggerStatement(cx, vp) 1.1126 + : dbg->fireExceptionUnwind(cx, vp); 1.1127 + if (st != JSTRAP_CONTINUE) 1.1128 + return st; 1.1129 + } 1.1130 + } 1.1131 + return JSTRAP_CONTINUE; 1.1132 +} 1.1133 + 1.1134 +static bool 1.1135 +AddNewScriptRecipients(GlobalObject::DebuggerVector *src, HandleScript script, 1.1136 + AutoValueVector *dest) 1.1137 +{ 1.1138 + bool wasEmpty = dest->length() == 0; 1.1139 + for (Debugger **p = src->begin(); p != src->end(); p++) { 1.1140 + Debugger *dbg = *p; 1.1141 + Value v = ObjectValue(*dbg->toJSObject()); 1.1142 + if (dbg->observesScript(script) && dbg->observesNewScript() && 1.1143 + (wasEmpty || Find(dest->begin(), dest->end(), v) == dest->end()) && 1.1144 + !dest->append(v)) 1.1145 + { 1.1146 + return false; 1.1147 + } 1.1148 + } 1.1149 + return true; 1.1150 +} 1.1151 + 1.1152 +void 1.1153 +Debugger::slowPathOnNewScript(JSContext *cx, HandleScript script, GlobalObject *compileAndGoGlobal_) 1.1154 +{ 1.1155 + Rooted<GlobalObject*> compileAndGoGlobal(cx, compileAndGoGlobal_); 1.1156 + 1.1157 + JS_ASSERT(script->compileAndGo() == !!compileAndGoGlobal); 1.1158 + 1.1159 + /* 1.1160 + * Build the list of recipients. For compile-and-go scripts, this is the 1.1161 + * same as the generic Debugger::dispatchHook code, but non-compile-and-go 1.1162 + * scripts are not tied to particular globals. We deliver them to every 1.1163 + * debugger observing any global in the script's compartment. 1.1164 + */ 1.1165 + AutoValueVector triggered(cx); 1.1166 + if (script->compileAndGo()) { 1.1167 + if (GlobalObject::DebuggerVector *debuggers = compileAndGoGlobal->getDebuggers()) { 1.1168 + if (!AddNewScriptRecipients(debuggers, script, &triggered)) 1.1169 + return; 1.1170 + } 1.1171 + } else { 1.1172 + GlobalObjectSet &debuggees = script->compartment()->getDebuggees(); 1.1173 + for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { 1.1174 + if (!AddNewScriptRecipients(r.front()->getDebuggers(), script, &triggered)) 1.1175 + return; 1.1176 + } 1.1177 + } 1.1178 + 1.1179 + /* 1.1180 + * Deliver the event to each debugger, checking again as in 1.1181 + * Debugger::dispatchHook. 1.1182 + */ 1.1183 + for (Value *p = triggered.begin(); p != triggered.end(); p++) { 1.1184 + Debugger *dbg = Debugger::fromJSObject(&p->toObject()); 1.1185 + if ((!compileAndGoGlobal || dbg->debuggees.has(compileAndGoGlobal)) && 1.1186 + dbg->enabled && dbg->getHook(OnNewScript)) { 1.1187 + dbg->fireNewScript(cx, script); 1.1188 + } 1.1189 + } 1.1190 +} 1.1191 + 1.1192 +JSTrapStatus 1.1193 +Debugger::onTrap(JSContext *cx, MutableHandleValue vp) 1.1194 +{ 1.1195 + MOZ_ASSERT(cx->compartment()->debugMode()); 1.1196 + 1.1197 + ScriptFrameIter iter(cx); 1.1198 + RootedScript script(cx, iter.script()); 1.1199 + Rooted<GlobalObject*> scriptGlobal(cx, &script->global()); 1.1200 + jsbytecode *pc = iter.pc(); 1.1201 + BreakpointSite *site = script->getBreakpointSite(pc); 1.1202 + JSOp op = JSOp(*pc); 1.1203 + 1.1204 + /* Build list of breakpoint handlers. */ 1.1205 + Vector<Breakpoint *> triggered(cx); 1.1206 + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) { 1.1207 + if (!triggered.append(bp)) 1.1208 + return JSTRAP_ERROR; 1.1209 + } 1.1210 + 1.1211 + for (Breakpoint **p = triggered.begin(); p != triggered.end(); p++) { 1.1212 + Breakpoint *bp = *p; 1.1213 + 1.1214 + /* Handlers can clear breakpoints. Check that bp still exists. */ 1.1215 + if (!site || !site->hasBreakpoint(bp)) 1.1216 + continue; 1.1217 + 1.1218 + 1.1219 + /* 1.1220 + * There are two reasons we have to check whether dbg is enabled and 1.1221 + * debugging scriptGlobal. 1.1222 + * 1.1223 + * One is just that one breakpoint handler can disable other Debuggers 1.1224 + * or remove debuggees. 1.1225 + * 1.1226 + * The other has to do with non-compile-and-go scripts, which have no 1.1227 + * specific global--until they are executed. Only now do we know which 1.1228 + * global the script is running against. 1.1229 + */ 1.1230 + Debugger *dbg = bp->debugger; 1.1231 + if (dbg->enabled && dbg->debuggees.lookup(scriptGlobal)) { 1.1232 + Maybe<AutoCompartment> ac; 1.1233 + ac.construct(cx, dbg->object); 1.1234 + 1.1235 + RootedValue scriptFrame(cx); 1.1236 + if (!dbg->getScriptFrame(cx, iter, &scriptFrame)) 1.1237 + return dbg->handleUncaughtException(ac, false); 1.1238 + RootedValue rv(cx); 1.1239 + Rooted<JSObject*> handler(cx, bp->handler); 1.1240 + bool ok = CallMethodIfPresent(cx, handler, "hit", 1, scriptFrame.address(), &rv); 1.1241 + JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv, vp, true); 1.1242 + if (st != JSTRAP_CONTINUE) 1.1243 + return st; 1.1244 + 1.1245 + /* Calling JS code invalidates site. Reload it. */ 1.1246 + site = script->getBreakpointSite(pc); 1.1247 + } 1.1248 + } 1.1249 + 1.1250 + if (site && site->trapHandler) { 1.1251 + JSTrapStatus st = site->trapHandler(cx, script, pc, vp.address(), site->trapClosure); 1.1252 + if (st != JSTRAP_CONTINUE) 1.1253 + return st; 1.1254 + } 1.1255 + 1.1256 + /* By convention, return the true op to the interpreter in vp. */ 1.1257 + vp.setInt32(op); 1.1258 + return JSTRAP_CONTINUE; 1.1259 +} 1.1260 + 1.1261 +JSTrapStatus 1.1262 +Debugger::onSingleStep(JSContext *cx, MutableHandleValue vp) 1.1263 +{ 1.1264 + ScriptFrameIter iter(cx); 1.1265 + 1.1266 + /* 1.1267 + * We may be stepping over a JSOP_EXCEPTION, that pushes the context's 1.1268 + * pending exception for a 'catch' clause to handle. Don't let the 1.1269 + * onStep handlers mess with that (other than by returning a resumption 1.1270 + * value). 1.1271 + */ 1.1272 + RootedValue exception(cx, UndefinedValue()); 1.1273 + bool exceptionPending = cx->isExceptionPending(); 1.1274 + if (exceptionPending) { 1.1275 + if (!cx->getPendingException(&exception)) 1.1276 + return JSTRAP_ERROR; 1.1277 + cx->clearPendingException(); 1.1278 + } 1.1279 + 1.1280 + /* 1.1281 + * Build list of Debugger.Frame instances referring to this frame with 1.1282 + * onStep handlers. 1.1283 + */ 1.1284 + AutoObjectVector frames(cx); 1.1285 + for (FrameRange r(iter.abstractFramePtr()); !r.empty(); r.popFront()) { 1.1286 + JSObject *frame = r.frontFrame(); 1.1287 + if (!frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() && 1.1288 + !frames.append(frame)) 1.1289 + { 1.1290 + return JSTRAP_ERROR; 1.1291 + } 1.1292 + } 1.1293 + 1.1294 +#ifdef DEBUG 1.1295 + /* 1.1296 + * Validate the single-step count on this frame's script, to ensure that 1.1297 + * we're not receiving traps we didn't ask for. Even when frames is 1.1298 + * non-empty (and thus we know this trap was requested), do the check 1.1299 + * anyway, to make sure the count has the correct non-zero value. 1.1300 + * 1.1301 + * The converse --- ensuring that we do receive traps when we should --- can 1.1302 + * be done with unit tests. 1.1303 + */ 1.1304 + { 1.1305 + uint32_t stepperCount = 0; 1.1306 + JSScript *trappingScript = iter.script(); 1.1307 + GlobalObject *global = cx->global(); 1.1308 + if (GlobalObject::DebuggerVector *debuggers = global->getDebuggers()) { 1.1309 + for (Debugger **p = debuggers->begin(); p != debuggers->end(); p++) { 1.1310 + Debugger *dbg = *p; 1.1311 + for (FrameMap::Range r = dbg->frames.all(); !r.empty(); r.popFront()) { 1.1312 + AbstractFramePtr frame = r.front().key(); 1.1313 + JSObject *frameobj = r.front().value(); 1.1314 + if (frame.script() == trappingScript && 1.1315 + !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined()) 1.1316 + { 1.1317 + stepperCount++; 1.1318 + } 1.1319 + } 1.1320 + } 1.1321 + } 1.1322 + if (trappingScript->compileAndGo()) 1.1323 + JS_ASSERT(stepperCount == trappingScript->stepModeCount()); 1.1324 + else 1.1325 + JS_ASSERT(stepperCount <= trappingScript->stepModeCount()); 1.1326 + } 1.1327 +#endif 1.1328 + 1.1329 + /* Preserve the debuggee's iterValue while handlers run. */ 1.1330 + class PreserveIterValue { 1.1331 + JSContext *cx; 1.1332 + RootedValue savedIterValue; 1.1333 + 1.1334 + public: 1.1335 + PreserveIterValue(JSContext *cx) : cx(cx), savedIterValue(cx, cx->iterValue) { 1.1336 + cx->iterValue.setMagic(JS_NO_ITER_VALUE); 1.1337 + } 1.1338 + ~PreserveIterValue() { 1.1339 + cx->iterValue = savedIterValue; 1.1340 + } 1.1341 + }; 1.1342 + PreserveIterValue piv(cx); 1.1343 + 1.1344 + /* Call all the onStep handlers we found. */ 1.1345 + for (JSObject **p = frames.begin(); p != frames.end(); p++) { 1.1346 + RootedObject frame(cx, *p); 1.1347 + Debugger *dbg = Debugger::fromChildJSObject(frame); 1.1348 + 1.1349 + Maybe<AutoCompartment> ac; 1.1350 + ac.construct(cx, dbg->object); 1.1351 + 1.1352 + const Value &handler = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER); 1.1353 + RootedValue rval(cx); 1.1354 + bool ok = Invoke(cx, ObjectValue(*frame), handler, 0, nullptr, &rval); 1.1355 + JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rval, vp); 1.1356 + if (st != JSTRAP_CONTINUE) 1.1357 + return st; 1.1358 + } 1.1359 + 1.1360 + vp.setUndefined(); 1.1361 + if (exceptionPending) 1.1362 + cx->setPendingException(exception); 1.1363 + return JSTRAP_CONTINUE; 1.1364 +} 1.1365 + 1.1366 +JSTrapStatus 1.1367 +Debugger::fireNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global, MutableHandleValue vp) 1.1368 +{ 1.1369 + RootedObject hook(cx, getHook(OnNewGlobalObject)); 1.1370 + JS_ASSERT(hook); 1.1371 + JS_ASSERT(hook->isCallable()); 1.1372 + 1.1373 + Maybe<AutoCompartment> ac; 1.1374 + ac.construct(cx, object); 1.1375 + 1.1376 + RootedValue wrappedGlobal(cx, ObjectValue(*global)); 1.1377 + if (!wrapDebuggeeValue(cx, &wrappedGlobal)) 1.1378 + return handleUncaughtException(ac, false); 1.1379 + 1.1380 + RootedValue rv(cx); 1.1381 + 1.1382 + // onNewGlobalObject is infallible, and thus is only allowed to return 1.1383 + // undefined as a resumption value. If it returns anything else, we throw. 1.1384 + // And if that happens, or if the hook itself throws, we invoke the 1.1385 + // uncaughtExceptionHook so that we never leave an exception pending on the 1.1386 + // cx. This allows JS_NewGlobalObject to avoid handling failures from debugger 1.1387 + // hooks. 1.1388 + bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, wrappedGlobal.address(), &rv); 1.1389 + if (ok && !rv.isUndefined()) { 1.1390 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.1391 + JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED); 1.1392 + ok = false; 1.1393 + } 1.1394 + // NB: Even though we don't care about what goes into it, we have to pass vp 1.1395 + // to handleUncaughtException so that it parses resumption values from the 1.1396 + // uncaughtExceptionHook and tells the caller whether we should execute the 1.1397 + // rest of the onNewGlobalObject hooks or not. 1.1398 + JSTrapStatus status = ok ? JSTRAP_CONTINUE 1.1399 + : handleUncaughtException(ac, vp, true); 1.1400 + JS_ASSERT(!cx->isExceptionPending()); 1.1401 + return status; 1.1402 +} 1.1403 + 1.1404 +void 1.1405 +Debugger::slowPathOnNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global) 1.1406 +{ 1.1407 + JS_ASSERT(!JS_CLIST_IS_EMPTY(&cx->runtime()->onNewGlobalObjectWatchers)); 1.1408 + if (global->compartment()->options().invisibleToDebugger()) 1.1409 + return; 1.1410 + 1.1411 + /* 1.1412 + * Make a copy of the runtime's onNewGlobalObjectWatchers before running the 1.1413 + * handlers. Since one Debugger's handler can disable another's, the list 1.1414 + * can be mutated while we're walking it. 1.1415 + */ 1.1416 + AutoObjectVector watchers(cx); 1.1417 + for (JSCList *link = JS_LIST_HEAD(&cx->runtime()->onNewGlobalObjectWatchers); 1.1418 + link != &cx->runtime()->onNewGlobalObjectWatchers; 1.1419 + link = JS_NEXT_LINK(link)) { 1.1420 + Debugger *dbg = fromOnNewGlobalObjectWatchersLink(link); 1.1421 + JS_ASSERT(dbg->observesNewGlobalObject()); 1.1422 + if (!watchers.append(dbg->object)) 1.1423 + return; 1.1424 + } 1.1425 + 1.1426 + JSTrapStatus status = JSTRAP_CONTINUE; 1.1427 + RootedValue value(cx); 1.1428 + 1.1429 + for (size_t i = 0; i < watchers.length(); i++) { 1.1430 + Debugger *dbg = fromJSObject(watchers[i]); 1.1431 + 1.1432 + // We disallow resumption values from onNewGlobalObject hooks, because we 1.1433 + // want the debugger hooks for global object creation to be infallible. 1.1434 + // But if an onNewGlobalObject hook throws, and the uncaughtExceptionHook 1.1435 + // decides to raise an error, we want to at least avoid invoking the rest 1.1436 + // of the onNewGlobalObject handlers in the list (not for any super 1.1437 + // compelling reason, just because it seems like the right thing to do). 1.1438 + // So we ignore whatever comes out in |value|, but break out of the loop 1.1439 + // if a non-success trap status is returned. 1.1440 + if (dbg->observesNewGlobalObject()) { 1.1441 + status = dbg->fireNewGlobalObject(cx, global, &value); 1.1442 + if (status != JSTRAP_CONTINUE && status != JSTRAP_RETURN) 1.1443 + break; 1.1444 + } 1.1445 + } 1.1446 + JS_ASSERT(!cx->isExceptionPending()); 1.1447 +} 1.1448 + 1.1449 + 1.1450 +/*** Debugger JSObjects **************************************************************************/ 1.1451 + 1.1452 +void 1.1453 +Debugger::markKeysInCompartment(JSTracer *trc) 1.1454 +{ 1.1455 + /* 1.1456 + * WeakMap::Range is deliberately private, to discourage C++ code from 1.1457 + * enumerating WeakMap keys. However in this case we need access, so we 1.1458 + * make a base-class reference. Range is public in HashMap. 1.1459 + */ 1.1460 + objects.markKeys(trc); 1.1461 + environments.markKeys(trc); 1.1462 + scripts.markKeys(trc); 1.1463 + sources.markKeys(trc); 1.1464 +} 1.1465 + 1.1466 +/* 1.1467 + * Ordinarily, WeakMap keys and values are marked because at some point it was 1.1468 + * discovered that the WeakMap was live; that is, some object containing the 1.1469 + * WeakMap was marked during mark phase. 1.1470 + * 1.1471 + * However, during compartment GC, we have to do something about 1.1472 + * cross-compartment WeakMaps in non-GC'd compartments. If their keys and values 1.1473 + * might need to be marked, we have to do it manually. 1.1474 + * 1.1475 + * Each Debugger object keeps found cross-compartment WeakMaps: objects, scripts, 1.1476 + * script source objects, and environments. They have the nice property that all 1.1477 + * their values are in the same compartment as the Debugger object, so we only 1.1478 + * need to mark the keys. We must simply mark all keys that are in a compartment 1.1479 + * being GC'd. 1.1480 + * 1.1481 + * We must scan all Debugger objects regardless of whether they *currently* 1.1482 + * have any debuggees in a compartment being GC'd, because the WeakMap 1.1483 + * entries persist even when debuggees are removed. 1.1484 + * 1.1485 + * This happens during the initial mark phase, not iterative marking, because 1.1486 + * all the edges being reported here are strong references. 1.1487 + */ 1.1488 +void 1.1489 +Debugger::markCrossCompartmentDebuggerObjectReferents(JSTracer *trc) 1.1490 +{ 1.1491 + JSRuntime *rt = trc->runtime(); 1.1492 + 1.1493 + /* 1.1494 + * Mark all objects in comp that are referents of Debugger.Objects in other 1.1495 + * compartments. 1.1496 + */ 1.1497 + for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) { 1.1498 + if (!dbg->object->zone()->isCollecting()) 1.1499 + dbg->markKeysInCompartment(trc); 1.1500 + } 1.1501 +} 1.1502 + 1.1503 +/* 1.1504 + * This method has two tasks: 1.1505 + * 1. Mark Debugger objects that are unreachable except for debugger hooks that 1.1506 + * may yet be called. 1.1507 + * 2. Mark breakpoint handlers. 1.1508 + * 1.1509 + * This happens during the iterative part of the GC mark phase. This method 1.1510 + * returns true if it has to mark anything; GC calls it repeatedly until it 1.1511 + * returns false. 1.1512 + */ 1.1513 +bool 1.1514 +Debugger::markAllIteratively(GCMarker *trc) 1.1515 +{ 1.1516 + bool markedAny = false; 1.1517 + 1.1518 + /* 1.1519 + * Find all Debugger objects in danger of GC. This code is a little 1.1520 + * convoluted since the easiest way to find them is via their debuggees. 1.1521 + */ 1.1522 + JSRuntime *rt = trc->runtime(); 1.1523 + for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) { 1.1524 + GlobalObjectSet &debuggees = c->getDebuggees(); 1.1525 + for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) { 1.1526 + GlobalObject *global = e.front(); 1.1527 + if (!IsObjectMarked(&global)) 1.1528 + continue; 1.1529 + else if (global != e.front()) 1.1530 + e.rekeyFront(global); 1.1531 + 1.1532 + /* 1.1533 + * Every debuggee has at least one debugger, so in this case 1.1534 + * getDebuggers can't return nullptr. 1.1535 + */ 1.1536 + const GlobalObject::DebuggerVector *debuggers = global->getDebuggers(); 1.1537 + JS_ASSERT(debuggers); 1.1538 + for (Debugger * const *p = debuggers->begin(); p != debuggers->end(); p++) { 1.1539 + Debugger *dbg = *p; 1.1540 + 1.1541 + /* 1.1542 + * dbg is a Debugger with at least one debuggee. Check three things: 1.1543 + * - dbg is actually in a compartment that is being marked 1.1544 + * - it isn't already marked 1.1545 + * - it actually has hooks that might be called 1.1546 + */ 1.1547 + HeapPtrObject &dbgobj = dbg->toJSObjectRef(); 1.1548 + if (!dbgobj->zone()->isGCMarking()) 1.1549 + continue; 1.1550 + 1.1551 + bool dbgMarked = IsObjectMarked(&dbgobj); 1.1552 + if (!dbgMarked && dbg->hasAnyLiveHooks()) { 1.1553 + /* 1.1554 + * obj could be reachable only via its live, enabled 1.1555 + * debugger hooks, which may yet be called. 1.1556 + */ 1.1557 + MarkObject(trc, &dbgobj, "enabled Debugger"); 1.1558 + markedAny = true; 1.1559 + dbgMarked = true; 1.1560 + } 1.1561 + 1.1562 + if (dbgMarked) { 1.1563 + /* Search for breakpoints to mark. */ 1.1564 + for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) { 1.1565 + if (IsScriptMarked(&bp->site->script)) { 1.1566 + /* 1.1567 + * The debugger and the script are both live. 1.1568 + * Therefore the breakpoint handler is live. 1.1569 + */ 1.1570 + if (!IsObjectMarked(&bp->getHandlerRef())) { 1.1571 + MarkObject(trc, &bp->getHandlerRef(), "breakpoint handler"); 1.1572 + markedAny = true; 1.1573 + } 1.1574 + } 1.1575 + } 1.1576 + } 1.1577 + } 1.1578 + } 1.1579 + } 1.1580 + return markedAny; 1.1581 +} 1.1582 + 1.1583 +/* 1.1584 + * Mark all debugger-owned GC things unconditionally. This is used by the minor 1.1585 + * GC: the minor GC cannot apply the weak constraints of the full GC because it 1.1586 + * visits only part of the heap. 1.1587 + */ 1.1588 +void 1.1589 +Debugger::markAll(JSTracer *trc) 1.1590 +{ 1.1591 + JSRuntime *rt = trc->runtime(); 1.1592 + for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) { 1.1593 + GlobalObjectSet &debuggees = dbg->debuggees; 1.1594 + for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) { 1.1595 + GlobalObject *global = e.front(); 1.1596 + 1.1597 + MarkObjectUnbarriered(trc, &global, "Global Object"); 1.1598 + if (global != e.front()) 1.1599 + e.rekeyFront(global); 1.1600 + } 1.1601 + 1.1602 + HeapPtrObject &dbgobj = dbg->toJSObjectRef(); 1.1603 + MarkObject(trc, &dbgobj, "Debugger Object"); 1.1604 + 1.1605 + dbg->scripts.trace(trc); 1.1606 + dbg->sources.trace(trc); 1.1607 + dbg->objects.trace(trc); 1.1608 + dbg->environments.trace(trc); 1.1609 + 1.1610 + for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) { 1.1611 + MarkScriptUnbarriered(trc, &bp->site->script, "breakpoint script"); 1.1612 + MarkObject(trc, &bp->getHandlerRef(), "breakpoint handler"); 1.1613 + } 1.1614 + } 1.1615 +} 1.1616 + 1.1617 +void 1.1618 +Debugger::traceObject(JSTracer *trc, JSObject *obj) 1.1619 +{ 1.1620 + if (Debugger *dbg = Debugger::fromJSObject(obj)) 1.1621 + dbg->trace(trc); 1.1622 +} 1.1623 + 1.1624 +void 1.1625 +Debugger::trace(JSTracer *trc) 1.1626 +{ 1.1627 + if (uncaughtExceptionHook) 1.1628 + MarkObject(trc, &uncaughtExceptionHook, "hooks"); 1.1629 + 1.1630 + /* 1.1631 + * Mark Debugger.Frame objects. These are all reachable from JS, because the 1.1632 + * corresponding JS frames are still on the stack. 1.1633 + * 1.1634 + * (Once we support generator frames properly, we will need 1.1635 + * weakly-referenced Debugger.Frame objects as well, for suspended generator 1.1636 + * frames.) 1.1637 + */ 1.1638 + for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) { 1.1639 + RelocatablePtrObject &frameobj = r.front().value(); 1.1640 + JS_ASSERT(frameobj->getPrivate()); 1.1641 + MarkObject(trc, &frameobj, "live Debugger.Frame"); 1.1642 + } 1.1643 + 1.1644 + /* Trace the weak map from JSScript instances to Debugger.Script objects. */ 1.1645 + scripts.trace(trc); 1.1646 + 1.1647 + /* Trace the referent ->Debugger.Source weak map */ 1.1648 + sources.trace(trc); 1.1649 + 1.1650 + /* Trace the referent -> Debugger.Object weak map. */ 1.1651 + objects.trace(trc); 1.1652 + 1.1653 + /* Trace the referent -> Debugger.Environment weak map. */ 1.1654 + environments.trace(trc); 1.1655 +} 1.1656 + 1.1657 +void 1.1658 +Debugger::sweepAll(FreeOp *fop) 1.1659 +{ 1.1660 + JSRuntime *rt = fop->runtime(); 1.1661 + 1.1662 + for (Debugger *dbg = rt->debuggerList.getFirst(); dbg; dbg = dbg->getNext()) { 1.1663 + if (IsObjectAboutToBeFinalized(&dbg->object)) { 1.1664 + /* 1.1665 + * dbg is being GC'd. Detach it from its debuggees. The debuggee 1.1666 + * might be GC'd too. Since detaching requires access to both 1.1667 + * objects, this must be done before finalize time. 1.1668 + */ 1.1669 + for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) { 1.1670 + // We can't recompile on-stack scripts here, and we 1.1671 + // can only toggle debug mode to off, so we use an 1.1672 + // infallible variant of removeDebuggeeGlobal. 1.1673 + dbg->removeDebuggeeGlobalUnderGC(fop, e.front(), nullptr, &e); 1.1674 + } 1.1675 + } 1.1676 + } 1.1677 + 1.1678 + for (gc::GCCompartmentGroupIter comp(rt); !comp.done(); comp.next()) { 1.1679 + /* For each debuggee being GC'd, detach it from all its debuggers. */ 1.1680 + GlobalObjectSet &debuggees = comp->getDebuggees(); 1.1681 + for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) { 1.1682 + GlobalObject *global = e.front(); 1.1683 + if (IsObjectAboutToBeFinalized(&global)) { 1.1684 + // See infallibility note above. 1.1685 + detachAllDebuggersFromGlobal(fop, global, &e); 1.1686 + } 1.1687 + else if (global != e.front()) 1.1688 + e.rekeyFront(global); 1.1689 + } 1.1690 + } 1.1691 +} 1.1692 + 1.1693 +void 1.1694 +Debugger::detachAllDebuggersFromGlobal(FreeOp *fop, GlobalObject *global, 1.1695 + GlobalObjectSet::Enum *compartmentEnum) 1.1696 +{ 1.1697 + const GlobalObject::DebuggerVector *debuggers = global->getDebuggers(); 1.1698 + JS_ASSERT(!debuggers->empty()); 1.1699 + while (!debuggers->empty()) 1.1700 + debuggers->back()->removeDebuggeeGlobalUnderGC(fop, global, compartmentEnum, nullptr); 1.1701 +} 1.1702 + 1.1703 +/* static */ void 1.1704 +Debugger::findCompartmentEdges(Zone *zone, js::gc::ComponentFinder<Zone> &finder) 1.1705 +{ 1.1706 + /* 1.1707 + * For debugger cross compartment wrappers, add edges in the opposite 1.1708 + * direction to those already added by JSCompartment::findOutgoingEdges. 1.1709 + * This ensure that debuggers and their debuggees are finalized in the same 1.1710 + * group. 1.1711 + */ 1.1712 + for (Debugger *dbg = zone->runtimeFromMainThread()->debuggerList.getFirst(); 1.1713 + dbg; 1.1714 + dbg = dbg->getNext()) 1.1715 + { 1.1716 + Zone *w = dbg->object->zone(); 1.1717 + if (w == zone || !w->isGCMarking()) 1.1718 + continue; 1.1719 + if (dbg->scripts.hasKeyInZone(zone) || 1.1720 + dbg->sources.hasKeyInZone(zone) || 1.1721 + dbg->objects.hasKeyInZone(zone) || 1.1722 + dbg->environments.hasKeyInZone(zone)) 1.1723 + { 1.1724 + finder.addEdgeTo(w); 1.1725 + } 1.1726 + } 1.1727 +} 1.1728 + 1.1729 +void 1.1730 +Debugger::finalize(FreeOp *fop, JSObject *obj) 1.1731 +{ 1.1732 + Debugger *dbg = fromJSObject(obj); 1.1733 + if (!dbg) 1.1734 + return; 1.1735 + fop->delete_(dbg); 1.1736 +} 1.1737 + 1.1738 +const Class Debugger::jsclass = { 1.1739 + "Debugger", 1.1740 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.1741 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), 1.1742 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.1743 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize, 1.1744 + nullptr, /* call */ 1.1745 + nullptr, /* hasInstance */ 1.1746 + nullptr, /* construct */ 1.1747 + Debugger::traceObject 1.1748 +}; 1.1749 + 1.1750 +Debugger * 1.1751 +Debugger::fromThisValue(JSContext *cx, const CallArgs &args, const char *fnname) 1.1752 +{ 1.1753 + if (!args.thisv().isObject()) { 1.1754 + ReportObjectRequired(cx); 1.1755 + return nullptr; 1.1756 + } 1.1757 + JSObject *thisobj = &args.thisv().toObject(); 1.1758 + if (thisobj->getClass() != &Debugger::jsclass) { 1.1759 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.1760 + "Debugger", fnname, thisobj->getClass()->name); 1.1761 + return nullptr; 1.1762 + } 1.1763 + 1.1764 + /* 1.1765 + * Forbid Debugger.prototype, which is of the Debugger JSClass but isn't 1.1766 + * really a Debugger object. The prototype object is distinguished by 1.1767 + * having a nullptr private value. 1.1768 + */ 1.1769 + Debugger *dbg = fromJSObject(thisobj); 1.1770 + if (!dbg) { 1.1771 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.1772 + "Debugger", fnname, "prototype object"); 1.1773 + } 1.1774 + return dbg; 1.1775 +} 1.1776 + 1.1777 +#define THIS_DEBUGGER(cx, argc, vp, fnname, args, dbg) \ 1.1778 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.1779 + Debugger *dbg = Debugger::fromThisValue(cx, args, fnname); \ 1.1780 + if (!dbg) \ 1.1781 + return false 1.1782 + 1.1783 +bool 1.1784 +Debugger::getEnabled(JSContext *cx, unsigned argc, Value *vp) 1.1785 +{ 1.1786 + THIS_DEBUGGER(cx, argc, vp, "get enabled", args, dbg); 1.1787 + args.rval().setBoolean(dbg->enabled); 1.1788 + return true; 1.1789 +} 1.1790 + 1.1791 +bool 1.1792 +Debugger::setEnabled(JSContext *cx, unsigned argc, Value *vp) 1.1793 +{ 1.1794 + REQUIRE_ARGC("Debugger.set enabled", 1); 1.1795 + THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg); 1.1796 + bool enabled = ToBoolean(args[0]); 1.1797 + 1.1798 + if (enabled != dbg->enabled) { 1.1799 + for (Breakpoint *bp = dbg->firstBreakpoint(); bp; bp = bp->nextInDebugger()) { 1.1800 + if (enabled) 1.1801 + bp->site->inc(cx->runtime()->defaultFreeOp()); 1.1802 + else 1.1803 + bp->site->dec(cx->runtime()->defaultFreeOp()); 1.1804 + } 1.1805 + 1.1806 + /* 1.1807 + * Add or remove ourselves from the runtime's list of Debuggers 1.1808 + * that care about new globals. 1.1809 + */ 1.1810 + if (dbg->getHook(OnNewGlobalObject)) { 1.1811 + if (enabled) { 1.1812 + /* If we were not enabled, the link should be a singleton list. */ 1.1813 + JS_ASSERT(JS_CLIST_IS_EMPTY(&dbg->onNewGlobalObjectWatchersLink)); 1.1814 + JS_APPEND_LINK(&dbg->onNewGlobalObjectWatchersLink, 1.1815 + &cx->runtime()->onNewGlobalObjectWatchers); 1.1816 + } else { 1.1817 + /* If we were enabled, the link should be inserted in the list. */ 1.1818 + JS_ASSERT(!JS_CLIST_IS_EMPTY(&dbg->onNewGlobalObjectWatchersLink)); 1.1819 + JS_REMOVE_AND_INIT_LINK(&dbg->onNewGlobalObjectWatchersLink); 1.1820 + } 1.1821 + } 1.1822 + } 1.1823 + 1.1824 + dbg->enabled = enabled; 1.1825 + args.rval().setUndefined(); 1.1826 + return true; 1.1827 +} 1.1828 + 1.1829 +bool 1.1830 +Debugger::getHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which) 1.1831 +{ 1.1832 + JS_ASSERT(which >= 0 && which < HookCount); 1.1833 + THIS_DEBUGGER(cx, argc, vp, "getHook", args, dbg); 1.1834 + args.rval().set(dbg->object->getReservedSlot(JSSLOT_DEBUG_HOOK_START + which)); 1.1835 + return true; 1.1836 +} 1.1837 + 1.1838 +bool 1.1839 +Debugger::setHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which) 1.1840 +{ 1.1841 + JS_ASSERT(which >= 0 && which < HookCount); 1.1842 + REQUIRE_ARGC("Debugger.setHook", 1); 1.1843 + THIS_DEBUGGER(cx, argc, vp, "setHook", args, dbg); 1.1844 + if (args[0].isObject()) { 1.1845 + if (!args[0].toObject().isCallable()) 1.1846 + return ReportIsNotFunction(cx, args[0], args.length() - 1); 1.1847 + } else if (!args[0].isUndefined()) { 1.1848 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED); 1.1849 + return false; 1.1850 + } 1.1851 + dbg->object->setReservedSlot(JSSLOT_DEBUG_HOOK_START + which, args[0]); 1.1852 + args.rval().setUndefined(); 1.1853 + return true; 1.1854 +} 1.1855 + 1.1856 +bool 1.1857 +Debugger::getOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp) 1.1858 +{ 1.1859 + return getHookImpl(cx, argc, vp, OnDebuggerStatement); 1.1860 +} 1.1861 + 1.1862 +bool 1.1863 +Debugger::setOnDebuggerStatement(JSContext *cx, unsigned argc, Value *vp) 1.1864 +{ 1.1865 + return setHookImpl(cx, argc, vp, OnDebuggerStatement); 1.1866 +} 1.1867 + 1.1868 +bool 1.1869 +Debugger::getOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp) 1.1870 +{ 1.1871 + return getHookImpl(cx, argc, vp, OnExceptionUnwind); 1.1872 +} 1.1873 + 1.1874 +bool 1.1875 +Debugger::setOnExceptionUnwind(JSContext *cx, unsigned argc, Value *vp) 1.1876 +{ 1.1877 + return setHookImpl(cx, argc, vp, OnExceptionUnwind); 1.1878 +} 1.1879 + 1.1880 +bool 1.1881 +Debugger::getOnNewScript(JSContext *cx, unsigned argc, Value *vp) 1.1882 +{ 1.1883 + return getHookImpl(cx, argc, vp, OnNewScript); 1.1884 +} 1.1885 + 1.1886 +bool 1.1887 +Debugger::setOnNewScript(JSContext *cx, unsigned argc, Value *vp) 1.1888 +{ 1.1889 + return setHookImpl(cx, argc, vp, OnNewScript); 1.1890 +} 1.1891 + 1.1892 +bool 1.1893 +Debugger::getOnEnterFrame(JSContext *cx, unsigned argc, Value *vp) 1.1894 +{ 1.1895 + return getHookImpl(cx, argc, vp, OnEnterFrame); 1.1896 +} 1.1897 + 1.1898 +bool 1.1899 +Debugger::setOnEnterFrame(JSContext *cx, unsigned argc, Value *vp) 1.1900 +{ 1.1901 + return setHookImpl(cx, argc, vp, OnEnterFrame); 1.1902 +} 1.1903 + 1.1904 +bool 1.1905 +Debugger::getOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp) 1.1906 +{ 1.1907 + return getHookImpl(cx, argc, vp, OnNewGlobalObject); 1.1908 +} 1.1909 + 1.1910 +bool 1.1911 +Debugger::setOnNewGlobalObject(JSContext *cx, unsigned argc, Value *vp) 1.1912 +{ 1.1913 + THIS_DEBUGGER(cx, argc, vp, "setOnNewGlobalObject", args, dbg); 1.1914 + RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject)); 1.1915 + 1.1916 + if (!setHookImpl(cx, argc, vp, OnNewGlobalObject)) 1.1917 + return false; 1.1918 + 1.1919 + /* 1.1920 + * Add or remove ourselves from the runtime's list of Debuggers that 1.1921 + * care about new globals. 1.1922 + */ 1.1923 + if (dbg->enabled) { 1.1924 + JSObject *newHook = dbg->getHook(OnNewGlobalObject); 1.1925 + if (!oldHook && newHook) { 1.1926 + /* If we didn't have a hook, the link should be a singleton list. */ 1.1927 + JS_ASSERT(JS_CLIST_IS_EMPTY(&dbg->onNewGlobalObjectWatchersLink)); 1.1928 + JS_APPEND_LINK(&dbg->onNewGlobalObjectWatchersLink, 1.1929 + &cx->runtime()->onNewGlobalObjectWatchers); 1.1930 + } else if (oldHook && !newHook) { 1.1931 + /* If we did have a hook, the link should be inserted in the list. */ 1.1932 + JS_ASSERT(!JS_CLIST_IS_EMPTY(&dbg->onNewGlobalObjectWatchersLink)); 1.1933 + JS_REMOVE_AND_INIT_LINK(&dbg->onNewGlobalObjectWatchersLink); 1.1934 + } 1.1935 + } 1.1936 + 1.1937 + return true; 1.1938 +} 1.1939 + 1.1940 +bool 1.1941 +Debugger::getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp) 1.1942 +{ 1.1943 + THIS_DEBUGGER(cx, argc, vp, "get uncaughtExceptionHook", args, dbg); 1.1944 + args.rval().setObjectOrNull(dbg->uncaughtExceptionHook); 1.1945 + return true; 1.1946 +} 1.1947 + 1.1948 +bool 1.1949 +Debugger::setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp) 1.1950 +{ 1.1951 + REQUIRE_ARGC("Debugger.set uncaughtExceptionHook", 1); 1.1952 + THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg); 1.1953 + if (!args[0].isNull() && (!args[0].isObject() || !args[0].toObject().isCallable())) { 1.1954 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ASSIGN_FUNCTION_OR_NULL, 1.1955 + "uncaughtExceptionHook"); 1.1956 + return false; 1.1957 + } 1.1958 + dbg->uncaughtExceptionHook = args[0].toObjectOrNull(); 1.1959 + args.rval().setUndefined(); 1.1960 + return true; 1.1961 +} 1.1962 +bool 1.1963 +Debugger::getMemory(JSContext *cx, unsigned argc, Value *vp) 1.1964 +{ 1.1965 + THIS_DEBUGGER(cx, argc, vp, "get memory", args, dbg); 1.1966 + args.rval().set(dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE)); 1.1967 + return true; 1.1968 +} 1.1969 + 1.1970 +GlobalObject * 1.1971 +Debugger::unwrapDebuggeeArgument(JSContext *cx, const Value &v) 1.1972 +{ 1.1973 + if (!v.isObject()) { 1.1974 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.1975 + "argument", "not a global object"); 1.1976 + return nullptr; 1.1977 + } 1.1978 + 1.1979 + RootedObject obj(cx, &v.toObject()); 1.1980 + 1.1981 + /* If it's a Debugger.Object belonging to this debugger, dereference that. */ 1.1982 + if (obj->getClass() == &DebuggerObject_class) { 1.1983 + RootedValue rv(cx, v); 1.1984 + if (!unwrapDebuggeeValue(cx, &rv)) 1.1985 + return nullptr; 1.1986 + obj = &rv.toObject(); 1.1987 + } 1.1988 + 1.1989 + /* If we have a cross-compartment wrapper, dereference as far as is secure. */ 1.1990 + obj = CheckedUnwrap(obj); 1.1991 + if (!obj) { 1.1992 + JS_ReportError(cx, "Permission denied to access object"); 1.1993 + return nullptr; 1.1994 + } 1.1995 + 1.1996 + /* If that produced an outer window, innerize it. */ 1.1997 + obj = GetInnerObject(cx, obj); 1.1998 + if (!obj) 1.1999 + return nullptr; 1.2000 + 1.2001 + /* If that didn't produce a global object, it's an error. */ 1.2002 + if (!obj->is<GlobalObject>()) { 1.2003 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.2004 + "argument", "not a global object"); 1.2005 + return nullptr; 1.2006 + } 1.2007 + 1.2008 + return &obj->as<GlobalObject>(); 1.2009 +} 1.2010 + 1.2011 +bool 1.2012 +Debugger::addDebuggee(JSContext *cx, unsigned argc, Value *vp) 1.2013 +{ 1.2014 + REQUIRE_ARGC("Debugger.addDebuggee", 1); 1.2015 + THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg); 1.2016 + Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0])); 1.2017 + if (!global) 1.2018 + return false; 1.2019 + 1.2020 + if (!dbg->addDebuggeeGlobal(cx, global)) 1.2021 + return false; 1.2022 + 1.2023 + RootedValue v(cx, ObjectValue(*global)); 1.2024 + if (!dbg->wrapDebuggeeValue(cx, &v)) 1.2025 + return false; 1.2026 + args.rval().set(v); 1.2027 + return true; 1.2028 +} 1.2029 + 1.2030 +bool 1.2031 +Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp) 1.2032 +{ 1.2033 + THIS_DEBUGGER(cx, argc, vp, "addAllGlobalsAsDebuggees", args, dbg); 1.2034 + for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) { 1.2035 + // Invalidate a zone at a time to avoid doing a zone-wide CellIter 1.2036 + // per compartment. 1.2037 + AutoDebugModeInvalidation invalidate(zone); 1.2038 + 1.2039 + for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { 1.2040 + if (c == dbg->object->compartment() || c->options().invisibleToDebugger()) 1.2041 + continue; 1.2042 + c->zone()->scheduledForDestruction = false; 1.2043 + GlobalObject *global = c->maybeGlobal(); 1.2044 + if (global) { 1.2045 + Rooted<GlobalObject*> rg(cx, global); 1.2046 + if (!dbg->addDebuggeeGlobal(cx, rg, invalidate)) 1.2047 + return false; 1.2048 + } 1.2049 + } 1.2050 + } 1.2051 + 1.2052 + args.rval().setUndefined(); 1.2053 + return true; 1.2054 +} 1.2055 + 1.2056 +bool 1.2057 +Debugger::removeDebuggee(JSContext *cx, unsigned argc, Value *vp) 1.2058 +{ 1.2059 + REQUIRE_ARGC("Debugger.removeDebuggee", 1); 1.2060 + THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg); 1.2061 + GlobalObject *global = dbg->unwrapDebuggeeArgument(cx, args[0]); 1.2062 + if (!global) 1.2063 + return false; 1.2064 + if (dbg->debuggees.has(global)) { 1.2065 + if (!dbg->removeDebuggeeGlobal(cx, global, nullptr, nullptr)) 1.2066 + return false; 1.2067 + } 1.2068 + args.rval().setUndefined(); 1.2069 + return true; 1.2070 +} 1.2071 + 1.2072 +bool 1.2073 +Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp) 1.2074 +{ 1.2075 + THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg); 1.2076 + 1.2077 + for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) { 1.2078 + if (!dbg->removeDebuggeeGlobal(cx, e.front(), nullptr, &e)) 1.2079 + return false; 1.2080 + } 1.2081 + 1.2082 + args.rval().setUndefined(); 1.2083 + return true; 1.2084 +} 1.2085 + 1.2086 +bool 1.2087 +Debugger::hasDebuggee(JSContext *cx, unsigned argc, Value *vp) 1.2088 +{ 1.2089 + REQUIRE_ARGC("Debugger.hasDebuggee", 1); 1.2090 + THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg); 1.2091 + GlobalObject *global = dbg->unwrapDebuggeeArgument(cx, args[0]); 1.2092 + if (!global) 1.2093 + return false; 1.2094 + args.rval().setBoolean(!!dbg->debuggees.lookup(global)); 1.2095 + return true; 1.2096 +} 1.2097 + 1.2098 +bool 1.2099 +Debugger::getDebuggees(JSContext *cx, unsigned argc, Value *vp) 1.2100 +{ 1.2101 + THIS_DEBUGGER(cx, argc, vp, "getDebuggees", args, dbg); 1.2102 + RootedObject arrobj(cx, NewDenseAllocatedArray(cx, dbg->debuggees.count())); 1.2103 + if (!arrobj) 1.2104 + return false; 1.2105 + arrobj->ensureDenseInitializedLength(cx, 0, dbg->debuggees.count()); 1.2106 + unsigned i = 0; 1.2107 + for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) { 1.2108 + RootedValue v(cx, ObjectValue(*e.front())); 1.2109 + if (!dbg->wrapDebuggeeValue(cx, &v)) 1.2110 + return false; 1.2111 + arrobj->setDenseElement(i++, v); 1.2112 + } 1.2113 + args.rval().setObject(*arrobj); 1.2114 + return true; 1.2115 +} 1.2116 + 1.2117 +bool 1.2118 +Debugger::getNewestFrame(JSContext *cx, unsigned argc, Value *vp) 1.2119 +{ 1.2120 + THIS_DEBUGGER(cx, argc, vp, "getNewestFrame", args, dbg); 1.2121 + 1.2122 + /* Since there may be multiple contexts, use AllFramesIter. */ 1.2123 + for (AllFramesIter i(cx); !i.done(); ++i) { 1.2124 + if (dbg->observesFrame(i)) { 1.2125 + // Ensure that Ion frames are rematerialized. Only rematerialized 1.2126 + // Ion frames may be used as AbstractFramePtrs. 1.2127 + if (i.isIon() && !i.ensureHasRematerializedFrame()) 1.2128 + return false; 1.2129 + AbstractFramePtr frame = i.abstractFramePtr(); 1.2130 + ScriptFrameIter iter(i.activation()->cx(), ScriptFrameIter::GO_THROUGH_SAVED); 1.2131 + while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != frame) 1.2132 + ++iter; 1.2133 + return dbg->getScriptFrame(cx, iter, args.rval()); 1.2134 + } 1.2135 + } 1.2136 + args.rval().setNull(); 1.2137 + return true; 1.2138 +} 1.2139 + 1.2140 +bool 1.2141 +Debugger::clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp) 1.2142 +{ 1.2143 + THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg); 1.2144 + for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) 1.2145 + r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), 1.2146 + dbg, NullPtr()); 1.2147 + return true; 1.2148 +} 1.2149 + 1.2150 +bool 1.2151 +Debugger::construct(JSContext *cx, unsigned argc, Value *vp) 1.2152 +{ 1.2153 + CallArgs args = CallArgsFromVp(argc, vp); 1.2154 + 1.2155 + /* Check that the arguments, if any, are cross-compartment wrappers. */ 1.2156 + for (unsigned i = 0; i < args.length(); i++) { 1.2157 + const Value &arg = args[i]; 1.2158 + if (!arg.isObject()) 1.2159 + return ReportObjectRequired(cx); 1.2160 + JSObject *argobj = &arg.toObject(); 1.2161 + if (!argobj->is<CrossCompartmentWrapperObject>()) { 1.2162 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CCW_REQUIRED, 1.2163 + "Debugger"); 1.2164 + return false; 1.2165 + } 1.2166 + } 1.2167 + 1.2168 + /* Get Debugger.prototype. */ 1.2169 + RootedValue v(cx); 1.2170 + RootedObject callee(cx, &args.callee()); 1.2171 + if (!JSObject::getProperty(cx, callee, callee, cx->names().prototype, &v)) 1.2172 + return false; 1.2173 + RootedObject proto(cx, &v.toObject()); 1.2174 + JS_ASSERT(proto->getClass() == &Debugger::jsclass); 1.2175 + /* 1.2176 + * Make the new Debugger object. Each one has a reference to 1.2177 + * Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The 1.2178 + * rest of the reserved slots are for hooks; they default to undefined. 1.2179 + */ 1.2180 + RootedObject obj(cx, NewObjectWithGivenProto(cx, &Debugger::jsclass, proto, nullptr)); 1.2181 + if (!obj) 1.2182 + return false; 1.2183 + for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++) 1.2184 + obj->setReservedSlot(slot, proto->getReservedSlot(slot)); 1.2185 + /* Create the Debugger.Memory instance accessible by the 1.2186 + * |Debugger.prototype.memory| getter. */ 1.2187 + Value memoryProto = obj->getReservedSlot(JSSLOT_DEBUG_MEMORY_PROTO); 1.2188 + RootedObject memory(cx, NewObjectWithGivenProto(cx, &DebuggerMemory::class_, 1.2189 + &memoryProto.toObject(), nullptr)); 1.2190 + if (!memory) 1.2191 + return false; 1.2192 + obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, ObjectValue(*memory)); 1.2193 + 1.2194 + /* Construct the underlying C++ object. */ 1.2195 + Debugger *dbg = cx->new_<Debugger>(cx, obj.get()); 1.2196 + if (!dbg) 1.2197 + return false; 1.2198 + if (!dbg->init(cx)) { 1.2199 + js_delete(dbg); 1.2200 + return false; 1.2201 + } 1.2202 + obj->setPrivate(dbg); 1.2203 + /* Now the JSObject owns the js::Debugger instance, so we needn't delete it. */ 1.2204 + 1.2205 + /* Add the initial debuggees, if any. */ 1.2206 + for (unsigned i = 0; i < args.length(); i++) { 1.2207 + Rooted<GlobalObject*> 1.2208 + debuggee(cx, &args[i].toObject().as<ProxyObject>().private_().toObject().global()); 1.2209 + if (!dbg->addDebuggeeGlobal(cx, debuggee)) 1.2210 + return false; 1.2211 + } 1.2212 + 1.2213 + args.rval().setObject(*obj); 1.2214 + return true; 1.2215 +} 1.2216 + 1.2217 +bool 1.2218 +Debugger::addDebuggeeGlobal(JSContext *cx, Handle<GlobalObject*> global) 1.2219 +{ 1.2220 + AutoDebugModeInvalidation invalidate(global->compartment()); 1.2221 + return addDebuggeeGlobal(cx, global, invalidate); 1.2222 +} 1.2223 + 1.2224 +bool 1.2225 +Debugger::addDebuggeeGlobal(JSContext *cx, 1.2226 + Handle<GlobalObject*> global, 1.2227 + AutoDebugModeInvalidation &invalidate) 1.2228 +{ 1.2229 + if (debuggees.has(global)) 1.2230 + return true; 1.2231 + 1.2232 + // Callers should generally be unable to get a reference to a debugger- 1.2233 + // invisible global in order to pass it to addDebuggee. But this is possible 1.2234 + // with certain testing aides we expose in the shell, so just make addDebuggee 1.2235 + // throw in that case. 1.2236 + JSCompartment *debuggeeCompartment = global->compartment(); 1.2237 + if (debuggeeCompartment->options().invisibleToDebugger()) { 1.2238 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.2239 + JSMSG_DEBUG_CANT_DEBUG_GLOBAL); 1.2240 + return false; 1.2241 + } 1.2242 + 1.2243 + /* 1.2244 + * Check for cycles. If global's compartment is reachable from this 1.2245 + * Debugger object's compartment by following debuggee-to-debugger links, 1.2246 + * then adding global would create a cycle. (Typically nobody is debugging 1.2247 + * the debugger, in which case we zip through this code without looping.) 1.2248 + */ 1.2249 + Vector<JSCompartment *> visited(cx); 1.2250 + if (!visited.append(object->compartment())) 1.2251 + return false; 1.2252 + for (size_t i = 0; i < visited.length(); i++) { 1.2253 + JSCompartment *c = visited[i]; 1.2254 + if (c == debuggeeCompartment) { 1.2255 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP); 1.2256 + return false; 1.2257 + } 1.2258 + 1.2259 + /* 1.2260 + * Find all compartments containing debuggers debugging global objects 1.2261 + * in c. Add those compartments to visited. 1.2262 + */ 1.2263 + for (GlobalObjectSet::Range r = c->getDebuggees().all(); !r.empty(); r.popFront()) { 1.2264 + GlobalObject::DebuggerVector *v = r.front()->getDebuggers(); 1.2265 + for (Debugger **p = v->begin(); p != v->end(); p++) { 1.2266 + JSCompartment *next = (*p)->object->compartment(); 1.2267 + if (Find(visited, next) == visited.end() && !visited.append(next)) 1.2268 + return false; 1.2269 + } 1.2270 + } 1.2271 + } 1.2272 + 1.2273 + /* 1.2274 + * Each debugger-debuggee relation must be stored in up to three places. 1.2275 + * JSCompartment::addDebuggee enables debug mode if needed. 1.2276 + */ 1.2277 + AutoCompartment ac(cx, global); 1.2278 + GlobalObject::DebuggerVector *v = GlobalObject::getOrCreateDebuggers(cx, global); 1.2279 + if (!v || !v->append(this)) { 1.2280 + js_ReportOutOfMemory(cx); 1.2281 + } else { 1.2282 + if (!debuggees.put(global)) { 1.2283 + js_ReportOutOfMemory(cx); 1.2284 + } else { 1.2285 + if (global->getDebuggers()->length() > 1) 1.2286 + return true; 1.2287 + if (debuggeeCompartment->addDebuggee(cx, global, invalidate)) 1.2288 + return true; 1.2289 + 1.2290 + /* Maintain consistency on error. */ 1.2291 + debuggees.remove(global); 1.2292 + } 1.2293 + JS_ASSERT(v->back() == this); 1.2294 + v->popBack(); 1.2295 + } 1.2296 + return false; 1.2297 +} 1.2298 + 1.2299 +void 1.2300 +Debugger::cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global, 1.2301 + AutoDebugModeInvalidation &invalidate, 1.2302 + GlobalObjectSet::Enum *compartmentEnum, 1.2303 + GlobalObjectSet::Enum *debugEnum) 1.2304 +{ 1.2305 + /* 1.2306 + * Each debuggee is in two HashSets: one for its compartment and one for 1.2307 + * its debugger (this). The caller might be enumerating either set; if so, 1.2308 + * use HashSet::Enum::removeFront rather than HashSet::remove below, to 1.2309 + * avoid invalidating the live enumerator. 1.2310 + */ 1.2311 + JS_ASSERT(global->compartment()->getDebuggees().has(global)); 1.2312 + JS_ASSERT_IF(compartmentEnum, compartmentEnum->front() == global); 1.2313 + JS_ASSERT(debuggees.has(global)); 1.2314 + JS_ASSERT_IF(debugEnum, debugEnum->front() == global); 1.2315 + 1.2316 + /* 1.2317 + * FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame 1.2318 + * objects referring to a particular JS stack frame. This is hard if 1.2319 + * Debugger objects that are no longer debugging the relevant global might 1.2320 + * have live Frame objects. So we take the easy way out and kill them here. 1.2321 + * This is a bug, since it's observable and contrary to the spec. One 1.2322 + * possible fix would be to put such objects into a compartment-wide bag 1.2323 + * which slowPathOnLeaveFrame would have to examine. 1.2324 + */ 1.2325 + for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) { 1.2326 + AbstractFramePtr frame = e.front().key(); 1.2327 + JSObject *frameobj = e.front().value(); 1.2328 + if (&frame.script()->global() == global) { 1.2329 + DebuggerFrame_freeScriptFrameIterData(fop, frameobj); 1.2330 + DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame, frameobj); 1.2331 + e.removeFront(); 1.2332 + } 1.2333 + } 1.2334 + 1.2335 + GlobalObject::DebuggerVector *v = global->getDebuggers(); 1.2336 + Debugger **p; 1.2337 + for (p = v->begin(); p != v->end(); p++) { 1.2338 + if (*p == this) 1.2339 + break; 1.2340 + } 1.2341 + JS_ASSERT(p != v->end()); 1.2342 + 1.2343 + /* 1.2344 + * The relation must be removed from up to three places: *v and debuggees 1.2345 + * for sure, and possibly the compartment's debuggee set. 1.2346 + */ 1.2347 + v->erase(p); 1.2348 + if (debugEnum) 1.2349 + debugEnum->removeFront(); 1.2350 + else 1.2351 + debuggees.remove(global); 1.2352 + 1.2353 + /* Remove all breakpoints for the debuggee. */ 1.2354 + Breakpoint *nextbp; 1.2355 + for (Breakpoint *bp = firstBreakpoint(); bp; bp = nextbp) { 1.2356 + nextbp = bp->nextInDebugger(); 1.2357 + if (bp->site->script->compartment() == global->compartment()) 1.2358 + bp->destroy(fop); 1.2359 + } 1.2360 + JS_ASSERT_IF(debuggees.empty(), !firstBreakpoint()); 1.2361 +} 1.2362 + 1.2363 +bool 1.2364 +Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global, 1.2365 + GlobalObjectSet::Enum *compartmentEnum, 1.2366 + GlobalObjectSet::Enum *debugEnum) 1.2367 +{ 1.2368 + AutoDebugModeInvalidation invalidate(global->compartment()); 1.2369 + return removeDebuggeeGlobal(cx, global, invalidate, compartmentEnum, debugEnum); 1.2370 +} 1.2371 + 1.2372 +bool 1.2373 +Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global, 1.2374 + AutoDebugModeInvalidation &invalidate, 1.2375 + GlobalObjectSet::Enum *compartmentEnum, 1.2376 + GlobalObjectSet::Enum *debugEnum) 1.2377 +{ 1.2378 + cleanupDebuggeeGlobalBeforeRemoval(cx->runtime()->defaultFreeOp(), global, 1.2379 + invalidate, compartmentEnum, debugEnum); 1.2380 + 1.2381 + // The debuggee needs to be removed from the compartment last to save a root. 1.2382 + if (global->getDebuggers()->empty()) 1.2383 + return global->compartment()->removeDebuggee(cx, global, invalidate, compartmentEnum); 1.2384 + 1.2385 + return true; 1.2386 +} 1.2387 + 1.2388 +void 1.2389 +Debugger::removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global, 1.2390 + GlobalObjectSet::Enum *compartmentEnum, 1.2391 + GlobalObjectSet::Enum *debugEnum) 1.2392 +{ 1.2393 + AutoDebugModeInvalidation invalidate(global->compartment()); 1.2394 + removeDebuggeeGlobalUnderGC(fop, global, invalidate, compartmentEnum, debugEnum); 1.2395 +} 1.2396 + 1.2397 +void 1.2398 +Debugger::removeDebuggeeGlobalUnderGC(FreeOp *fop, GlobalObject *global, 1.2399 + AutoDebugModeInvalidation &invalidate, 1.2400 + GlobalObjectSet::Enum *compartmentEnum, 1.2401 + GlobalObjectSet::Enum *debugEnum) 1.2402 +{ 1.2403 + cleanupDebuggeeGlobalBeforeRemoval(fop, global, invalidate, compartmentEnum, debugEnum); 1.2404 + 1.2405 + /* 1.2406 + * The debuggee needs to be removed from the compartment last, as this can 1.2407 + * trigger GCs if the compartment's debug mode is being changed, and the 1.2408 + * global cannot be rooted on the stack without a cx. 1.2409 + */ 1.2410 + if (global->getDebuggers()->empty()) 1.2411 + global->compartment()->removeDebuggeeUnderGC(fop, global, invalidate, compartmentEnum); 1.2412 +} 1.2413 + 1.2414 +/* 1.2415 + * A class for parsing 'findScripts' query arguments and searching for 1.2416 + * scripts that match the criteria they represent. 1.2417 + */ 1.2418 +class Debugger::ScriptQuery { 1.2419 + public: 1.2420 + /* Construct a ScriptQuery to use matching scripts for |dbg|. */ 1.2421 + ScriptQuery(JSContext *cx, Debugger *dbg): 1.2422 + cx(cx), debugger(dbg), compartments(cx->runtime()), url(cx), displayURL(cx), 1.2423 + displayURLChars(nullptr), innermostForCompartment(cx->runtime()) 1.2424 + {} 1.2425 + 1.2426 + /* 1.2427 + * Initialize this ScriptQuery. Raise an error and return false if we 1.2428 + * haven't enough memory. 1.2429 + */ 1.2430 + bool init() { 1.2431 + if (!compartments.init() || 1.2432 + !innermostForCompartment.init()) 1.2433 + { 1.2434 + js_ReportOutOfMemory(cx); 1.2435 + return false; 1.2436 + } 1.2437 + 1.2438 + return true; 1.2439 + } 1.2440 + 1.2441 + /* 1.2442 + * Parse the query object |query|, and prepare to match only the scripts 1.2443 + * it specifies. 1.2444 + */ 1.2445 + bool parseQuery(HandleObject query) { 1.2446 + /* 1.2447 + * Check for a 'global' property, which limits the results to those 1.2448 + * scripts scoped to a particular global object. 1.2449 + */ 1.2450 + RootedValue global(cx); 1.2451 + if (!JSObject::getProperty(cx, query, query, cx->names().global, &global)) 1.2452 + return false; 1.2453 + if (global.isUndefined()) { 1.2454 + matchAllDebuggeeGlobals(); 1.2455 + } else { 1.2456 + GlobalObject *globalObject = debugger->unwrapDebuggeeArgument(cx, global); 1.2457 + if (!globalObject) 1.2458 + return false; 1.2459 + 1.2460 + /* 1.2461 + * If the given global isn't a debuggee, just leave the set of 1.2462 + * acceptable globals empty; we'll return no scripts. 1.2463 + */ 1.2464 + if (debugger->debuggees.has(globalObject)) { 1.2465 + if (!matchSingleGlobal(globalObject)) 1.2466 + return false; 1.2467 + } 1.2468 + } 1.2469 + 1.2470 + /* Check for a 'url' property. */ 1.2471 + if (!JSObject::getProperty(cx, query, query, cx->names().url, &url)) 1.2472 + return false; 1.2473 + if (!url.isUndefined() && !url.isString()) { 1.2474 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.2475 + "query object's 'url' property", "neither undefined nor a string"); 1.2476 + return false; 1.2477 + } 1.2478 + 1.2479 + /* Check for a 'line' property. */ 1.2480 + RootedValue lineProperty(cx); 1.2481 + if (!JSObject::getProperty(cx, query, query, cx->names().line, &lineProperty)) 1.2482 + return false; 1.2483 + if (lineProperty.isUndefined()) { 1.2484 + hasLine = false; 1.2485 + } else if (lineProperty.isNumber()) { 1.2486 + if (url.isUndefined()) { 1.2487 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.2488 + JSMSG_QUERY_LINE_WITHOUT_URL); 1.2489 + return false; 1.2490 + } 1.2491 + double doubleLine = lineProperty.toNumber(); 1.2492 + if (doubleLine <= 0 || (unsigned int) doubleLine != doubleLine) { 1.2493 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE); 1.2494 + return false; 1.2495 + } 1.2496 + hasLine = true; 1.2497 + line = doubleLine; 1.2498 + } else { 1.2499 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.2500 + "query object's 'line' property", 1.2501 + "neither undefined nor an integer"); 1.2502 + return false; 1.2503 + } 1.2504 + 1.2505 + /* Check for an 'innermost' property. */ 1.2506 + PropertyName *innermostName = cx->names().innermost; 1.2507 + RootedValue innermostProperty(cx); 1.2508 + if (!JSObject::getProperty(cx, query, query, innermostName, &innermostProperty)) 1.2509 + return false; 1.2510 + innermost = ToBoolean(innermostProperty); 1.2511 + if (innermost) { 1.2512 + /* Technically, we need only check hasLine, but this is clearer. */ 1.2513 + if (url.isUndefined() || !hasLine) { 1.2514 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, 1.2515 + JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL); 1.2516 + return false; 1.2517 + } 1.2518 + } 1.2519 + 1.2520 + /* Check for a 'displayURL' property. */ 1.2521 + if (!JSObject::getProperty(cx, query, query, cx->names().displayURL, &displayURL)) 1.2522 + return false; 1.2523 + if (!displayURL.isUndefined() && !displayURL.isString()) { 1.2524 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, 1.2525 + "query object's 'displayURL' property", 1.2526 + "neither undefined nor a string"); 1.2527 + return false; 1.2528 + } 1.2529 + 1.2530 + return true; 1.2531 + } 1.2532 + 1.2533 + /* Set up this ScriptQuery appropriately for a missing query argument. */ 1.2534 + bool omittedQuery() { 1.2535 + url.setUndefined(); 1.2536 + hasLine = false; 1.2537 + innermost = false; 1.2538 + displayURLChars = nullptr; 1.2539 + return matchAllDebuggeeGlobals(); 1.2540 + } 1.2541 + 1.2542 + /* 1.2543 + * Search all relevant compartments and the stack for scripts matching 1.2544 + * this query, and append the matching scripts to |vector|. 1.2545 + */ 1.2546 + bool findScripts(AutoScriptVector *v) { 1.2547 + if (!prepareQuery()) 1.2548 + return false; 1.2549 + 1.2550 + JSCompartment *singletonComp = nullptr; 1.2551 + if (compartments.count() == 1) 1.2552 + singletonComp = compartments.all().front(); 1.2553 + 1.2554 + /* Search each compartment for debuggee scripts. */ 1.2555 + vector = v; 1.2556 + oom = false; 1.2557 + IterateScripts(cx->runtime(), singletonComp, this, considerScript); 1.2558 + if (oom) { 1.2559 + js_ReportOutOfMemory(cx); 1.2560 + return false; 1.2561 + } 1.2562 + 1.2563 + /* 1.2564 + * For most queries, we just accumulate results in 'vector' as we find 1.2565 + * them. But if this is an 'innermost' query, then we've accumulated the 1.2566 + * results in the 'innermostForCompartment' map. In that case, we now need to 1.2567 + * walk that map and populate 'vector'. 1.2568 + */ 1.2569 + if (innermost) { 1.2570 + for (CompartmentToScriptMap::Range r = innermostForCompartment.all(); 1.2571 + !r.empty(); 1.2572 + r.popFront()) { 1.2573 + if (!v->append(r.front().value())) { 1.2574 + js_ReportOutOfMemory(cx); 1.2575 + return false; 1.2576 + } 1.2577 + } 1.2578 + } 1.2579 + 1.2580 + return true; 1.2581 + } 1.2582 + 1.2583 + private: 1.2584 + /* The context in which we should do our work. */ 1.2585 + JSContext *cx; 1.2586 + 1.2587 + /* The debugger for which we conduct queries. */ 1.2588 + Debugger *debugger; 1.2589 + 1.2590 + typedef HashSet<JSCompartment *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy> 1.2591 + CompartmentSet; 1.2592 + 1.2593 + /* A script must be in one of these compartments to match the query. */ 1.2594 + CompartmentSet compartments; 1.2595 + 1.2596 + /* If this is a string, matching scripts have urls equal to it. */ 1.2597 + RootedValue url; 1.2598 + 1.2599 + /* url as a C string. */ 1.2600 + JSAutoByteString urlCString; 1.2601 + 1.2602 + /* If this is a string, matching scripts' sources have displayURLs equal to 1.2603 + * it. */ 1.2604 + RootedValue displayURL; 1.2605 + 1.2606 + /* displayURL as a jschar* */ 1.2607 + const jschar *displayURLChars; 1.2608 + size_t displayURLLength; 1.2609 + 1.2610 + /* True if the query contained a 'line' property. */ 1.2611 + bool hasLine; 1.2612 + 1.2613 + /* The line matching scripts must cover. */ 1.2614 + unsigned int line; 1.2615 + 1.2616 + /* True if the query has an 'innermost' property whose value is true. */ 1.2617 + bool innermost; 1.2618 + 1.2619 + typedef HashMap<JSCompartment *, JSScript *, DefaultHasher<JSCompartment *>, RuntimeAllocPolicy> 1.2620 + CompartmentToScriptMap; 1.2621 + 1.2622 + /* 1.2623 + * For 'innermost' queries, a map from compartments to the innermost script 1.2624 + * we've seen so far in that compartment. (Template instantiation code size 1.2625 + * explosion ho!) 1.2626 + */ 1.2627 + CompartmentToScriptMap innermostForCompartment; 1.2628 + 1.2629 + /* The vector to which to append the scripts found. */ 1.2630 + AutoScriptVector *vector; 1.2631 + 1.2632 + /* Indicates whether OOM has occurred while matching. */ 1.2633 + bool oom; 1.2634 + 1.2635 + bool addCompartment(JSCompartment *comp) { 1.2636 + { 1.2637 + // All scripts in the debuggee compartment must be visible, so 1.2638 + // delazify everything. 1.2639 + AutoCompartment ac(cx, comp); 1.2640 + if (!comp->ensureDelazifyScriptsForDebugMode(cx)) 1.2641 + return false; 1.2642 + } 1.2643 + return compartments.put(comp); 1.2644 + } 1.2645 + 1.2646 + /* Arrange for this ScriptQuery to match only scripts that run in |global|. */ 1.2647 + bool matchSingleGlobal(GlobalObject *global) { 1.2648 + JS_ASSERT(compartments.count() == 0); 1.2649 + if (!addCompartment(global->compartment())) { 1.2650 + js_ReportOutOfMemory(cx); 1.2651 + return false; 1.2652 + } 1.2653 + return true; 1.2654 + } 1.2655 + 1.2656 + /* 1.2657 + * Arrange for this ScriptQuery to match all scripts running in debuggee 1.2658 + * globals. 1.2659 + */ 1.2660 + bool matchAllDebuggeeGlobals() { 1.2661 + JS_ASSERT(compartments.count() == 0); 1.2662 + /* Build our compartment set from the debugger's set of debuggee globals. */ 1.2663 + for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) { 1.2664 + if (!addCompartment(r.front()->compartment())) { 1.2665 + js_ReportOutOfMemory(cx); 1.2666 + return false; 1.2667 + } 1.2668 + } 1.2669 + return true; 1.2670 + } 1.2671 + 1.2672 + /* 1.2673 + * Given that parseQuery or omittedQuery has been called, prepare to match 1.2674 + * scripts. Set urlCString and displayURLChars as appropriate. 1.2675 + */ 1.2676 + bool prepareQuery() { 1.2677 + /* Compute urlCString and displayURLChars, if a url or displayURL was 1.2678 + * given respectively. */ 1.2679 + if (url.isString()) { 1.2680 + if (!urlCString.encodeLatin1(cx, url.toString())) 1.2681 + return false; 1.2682 + } 1.2683 + if (displayURL.isString()) { 1.2684 + JSString *s = displayURL.toString(); 1.2685 + displayURLChars = s->getChars(cx); 1.2686 + displayURLLength = s->length(); 1.2687 + if (!displayURLChars) 1.2688 + return false; 1.2689 + } 1.2690 + 1.2691 + return true; 1.2692 + } 1.2693 + 1.2694 + static void considerScript(JSRuntime *rt, void *data, JSScript *script) { 1.2695 + ScriptQuery *self = static_cast<ScriptQuery *>(data); 1.2696 + self->consider(script); 1.2697 + } 1.2698 + 1.2699 + /* 1.2700 + * If |script| matches this query, append it to |vector| or place it in 1.2701 + * |innermostForCompartment|, as appropriate. Set |oom| if an out of memory 1.2702 + * condition occurred. 1.2703 + */ 1.2704 + void consider(JSScript *script) { 1.2705 + // We check for presence of script->code() because it is possible that 1.2706 + // the script was created and thus exposed to GC, but *not* fully 1.2707 + // initialized from fullyInit{FromEmitter,Trivial} due to errors. 1.2708 + if (oom || script->selfHosted() || !script->code()) 1.2709 + return; 1.2710 + JSCompartment *compartment = script->compartment(); 1.2711 + if (!compartments.has(compartment)) 1.2712 + return; 1.2713 + if (urlCString.ptr()) { 1.2714 + bool gotFilename = false; 1.2715 + if (script->filename() && strcmp(script->filename(), urlCString.ptr()) == 0) 1.2716 + gotFilename = true; 1.2717 + 1.2718 + bool gotSourceURL = false; 1.2719 + if (!gotFilename && script->scriptSource()->introducerFilename() && 1.2720 + strcmp(script->scriptSource()->introducerFilename(), urlCString.ptr()) == 0) 1.2721 + { 1.2722 + gotSourceURL = true; 1.2723 + } 1.2724 + if (!gotFilename && !gotSourceURL) 1.2725 + return; 1.2726 + } 1.2727 + if (hasLine) { 1.2728 + if (line < script->lineno() || script->lineno() + js_GetScriptLineExtent(script) < line) 1.2729 + return; 1.2730 + } 1.2731 + if (displayURLChars) { 1.2732 + if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL()) 1.2733 + return; 1.2734 + const jschar *s = script->scriptSource()->displayURL(); 1.2735 + if (CompareChars(s, js_strlen(s), displayURLChars, displayURLLength) != 0) { 1.2736 + return; 1.2737 + } 1.2738 + } 1.2739 + 1.2740 + if (innermost) { 1.2741 + /* 1.2742 + * For 'innermost' queries, we don't place scripts in |vector| right 1.2743 + * away; we may later find another script that is nested inside this 1.2744 + * one. Instead, we record the innermost script we've found so far 1.2745 + * for each compartment in innermostForCompartment, and only 1.2746 + * populate |vector| at the bottom of findScripts, when we've 1.2747 + * traversed all the scripts. 1.2748 + * 1.2749 + * So: check this script against the innermost one we've found so 1.2750 + * far (if any), as recorded in innermostForCompartment, and replace 1.2751 + * that if it's better. 1.2752 + */ 1.2753 + CompartmentToScriptMap::AddPtr p = innermostForCompartment.lookupForAdd(compartment); 1.2754 + if (p) { 1.2755 + /* Is our newly found script deeper than the last one we found? */ 1.2756 + JSScript *incumbent = p->value(); 1.2757 + if (script->staticLevel() > incumbent->staticLevel()) 1.2758 + p->value() = script; 1.2759 + } else { 1.2760 + /* 1.2761 + * This is the first matching script we've encountered for this 1.2762 + * compartment, so it is thus the innermost such script. 1.2763 + */ 1.2764 + if (!innermostForCompartment.add(p, compartment, script)) { 1.2765 + oom = true; 1.2766 + return; 1.2767 + } 1.2768 + } 1.2769 + } else { 1.2770 + /* Record this matching script in the results vector. */ 1.2771 + if (!vector->append(script)) { 1.2772 + oom = true; 1.2773 + return; 1.2774 + } 1.2775 + } 1.2776 + 1.2777 + return; 1.2778 + } 1.2779 +}; 1.2780 + 1.2781 +bool 1.2782 +Debugger::findScripts(JSContext *cx, unsigned argc, Value *vp) 1.2783 +{ 1.2784 + THIS_DEBUGGER(cx, argc, vp, "findScripts", args, dbg); 1.2785 + 1.2786 + ScriptQuery query(cx, dbg); 1.2787 + if (!query.init()) 1.2788 + return false; 1.2789 + 1.2790 + if (args.length() >= 1) { 1.2791 + RootedObject queryObject(cx, NonNullObject(cx, args[0])); 1.2792 + if (!queryObject || !query.parseQuery(queryObject)) 1.2793 + return false; 1.2794 + } else { 1.2795 + if (!query.omittedQuery()) 1.2796 + return false; 1.2797 + } 1.2798 + 1.2799 + /* 1.2800 + * Accumulate the scripts in an AutoScriptVector, instead of creating 1.2801 + * the JS array as we go, because we mustn't allocate JS objects or GC 1.2802 + * while we use the CellIter. 1.2803 + */ 1.2804 + AutoScriptVector scripts(cx); 1.2805 + 1.2806 + if (!query.findScripts(&scripts)) 1.2807 + return false; 1.2808 + 1.2809 + RootedObject result(cx, NewDenseAllocatedArray(cx, scripts.length())); 1.2810 + if (!result) 1.2811 + return false; 1.2812 + 1.2813 + result->ensureDenseInitializedLength(cx, 0, scripts.length()); 1.2814 + 1.2815 + for (size_t i = 0; i < scripts.length(); i++) { 1.2816 + JSObject *scriptObject = dbg->wrapScript(cx, scripts.handleAt(i)); 1.2817 + if (!scriptObject) 1.2818 + return false; 1.2819 + result->setDenseElement(i, ObjectValue(*scriptObject)); 1.2820 + } 1.2821 + 1.2822 + args.rval().setObject(*result); 1.2823 + return true; 1.2824 +} 1.2825 + 1.2826 +bool 1.2827 +Debugger::findAllGlobals(JSContext *cx, unsigned argc, Value *vp) 1.2828 +{ 1.2829 + THIS_DEBUGGER(cx, argc, vp, "findAllGlobals", args, dbg); 1.2830 + 1.2831 + RootedObject result(cx, NewDenseEmptyArray(cx)); 1.2832 + if (!result) 1.2833 + return false; 1.2834 + 1.2835 + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { 1.2836 + if (c->options().invisibleToDebugger()) 1.2837 + continue; 1.2838 + 1.2839 + c->zone()->scheduledForDestruction = false; 1.2840 + 1.2841 + GlobalObject *global = c->maybeGlobal(); 1.2842 + 1.2843 + if (cx->runtime()->isSelfHostingGlobal(global)) 1.2844 + continue; 1.2845 + 1.2846 + if (global) { 1.2847 + /* 1.2848 + * We pulled |global| out of nowhere, so it's possible that it was 1.2849 + * marked gray by XPConnect. Since we're now exposing it to JS code, 1.2850 + * we need to mark it black. 1.2851 + */ 1.2852 + JS::ExposeGCThingToActiveJS(global, JSTRACE_OBJECT); 1.2853 + 1.2854 + RootedValue globalValue(cx, ObjectValue(*global)); 1.2855 + if (!dbg->wrapDebuggeeValue(cx, &globalValue)) 1.2856 + return false; 1.2857 + if (!NewbornArrayPush(cx, result, globalValue)) 1.2858 + return false; 1.2859 + } 1.2860 + } 1.2861 + 1.2862 + args.rval().setObject(*result); 1.2863 + return true; 1.2864 +} 1.2865 + 1.2866 +bool 1.2867 +Debugger::makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp) 1.2868 +{ 1.2869 + REQUIRE_ARGC("Debugger.makeGlobalObjectReference", 1); 1.2870 + THIS_DEBUGGER(cx, argc, vp, "makeGlobalObjectReference", args, dbg); 1.2871 + 1.2872 + Rooted<GlobalObject *> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0])); 1.2873 + if (!global) 1.2874 + return false; 1.2875 + 1.2876 + args.rval().setObject(*global); 1.2877 + return dbg->wrapDebuggeeValue(cx, args.rval()); 1.2878 +} 1.2879 + 1.2880 +const JSPropertySpec Debugger::properties[] = { 1.2881 + JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0), 1.2882 + JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement, 1.2883 + Debugger::setOnDebuggerStatement, 0), 1.2884 + JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind, 1.2885 + Debugger::setOnExceptionUnwind, 0), 1.2886 + JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0), 1.2887 + JS_PSGS("onEnterFrame", Debugger::getOnEnterFrame, Debugger::setOnEnterFrame, 0), 1.2888 + JS_PSGS("onNewGlobalObject", Debugger::getOnNewGlobalObject, Debugger::setOnNewGlobalObject, 0), 1.2889 + JS_PSGS("uncaughtExceptionHook", Debugger::getUncaughtExceptionHook, 1.2890 + Debugger::setUncaughtExceptionHook, 0), 1.2891 + JS_PSG("memory", Debugger::getMemory, 0), 1.2892 + JS_PS_END 1.2893 +}; 1.2894 +const JSFunctionSpec Debugger::methods[] = { 1.2895 + JS_FN("addDebuggee", Debugger::addDebuggee, 1, 0), 1.2896 + JS_FN("addAllGlobalsAsDebuggees", Debugger::addAllGlobalsAsDebuggees, 0, 0), 1.2897 + JS_FN("removeDebuggee", Debugger::removeDebuggee, 1, 0), 1.2898 + JS_FN("removeAllDebuggees", Debugger::removeAllDebuggees, 0, 0), 1.2899 + JS_FN("hasDebuggee", Debugger::hasDebuggee, 1, 0), 1.2900 + JS_FN("getDebuggees", Debugger::getDebuggees, 0, 0), 1.2901 + JS_FN("getNewestFrame", Debugger::getNewestFrame, 0, 0), 1.2902 + JS_FN("clearAllBreakpoints", Debugger::clearAllBreakpoints, 1, 0), 1.2903 + JS_FN("findScripts", Debugger::findScripts, 1, 0), 1.2904 + JS_FN("findAllGlobals", Debugger::findAllGlobals, 0, 0), 1.2905 + JS_FN("makeGlobalObjectReference", Debugger::makeGlobalObjectReference, 1, 0), 1.2906 + JS_FS_END 1.2907 +}; 1.2908 + 1.2909 + 1.2910 +/*** Debugger.Script *****************************************************************************/ 1.2911 + 1.2912 +static inline JSScript * 1.2913 +GetScriptReferent(JSObject *obj) 1.2914 +{ 1.2915 + JS_ASSERT(obj->getClass() == &DebuggerScript_class); 1.2916 + return static_cast<JSScript *>(obj->getPrivate()); 1.2917 +} 1.2918 + 1.2919 +static void 1.2920 +DebuggerScript_trace(JSTracer *trc, JSObject *obj) 1.2921 +{ 1.2922 + /* This comes from a private pointer, so no barrier needed. */ 1.2923 + if (JSScript *script = GetScriptReferent(obj)) { 1.2924 + MarkCrossCompartmentScriptUnbarriered(trc, obj, &script, "Debugger.Script referent"); 1.2925 + obj->setPrivateUnbarriered(script); 1.2926 + } 1.2927 +} 1.2928 + 1.2929 +const Class DebuggerScript_class = { 1.2930 + "Script", 1.2931 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.2932 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT), 1.2933 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.2934 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.2935 + nullptr, /* call */ 1.2936 + nullptr, /* hasInstance */ 1.2937 + nullptr, /* construct */ 1.2938 + DebuggerScript_trace 1.2939 +}; 1.2940 + 1.2941 +JSObject * 1.2942 +Debugger::newDebuggerScript(JSContext *cx, HandleScript script) 1.2943 +{ 1.2944 + assertSameCompartment(cx, object.get()); 1.2945 + 1.2946 + JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject(); 1.2947 + JS_ASSERT(proto); 1.2948 + JSObject *scriptobj = NewObjectWithGivenProto(cx, &DebuggerScript_class, proto, nullptr, TenuredObject); 1.2949 + if (!scriptobj) 1.2950 + return nullptr; 1.2951 + scriptobj->setReservedSlot(JSSLOT_DEBUGSCRIPT_OWNER, ObjectValue(*object)); 1.2952 + scriptobj->setPrivateGCThing(script); 1.2953 + 1.2954 + return scriptobj; 1.2955 +} 1.2956 + 1.2957 +JSObject * 1.2958 +Debugger::wrapScript(JSContext *cx, HandleScript script) 1.2959 +{ 1.2960 + assertSameCompartment(cx, object.get()); 1.2961 + JS_ASSERT(cx->compartment() != script->compartment()); 1.2962 + DependentAddPtr<ScriptWeakMap> p(cx, scripts, script); 1.2963 + if (!p) { 1.2964 + JSObject *scriptobj = newDebuggerScript(cx, script); 1.2965 + if (!scriptobj) 1.2966 + return nullptr; 1.2967 + 1.2968 + if (!p.add(cx, scripts, script, scriptobj)) { 1.2969 + js_ReportOutOfMemory(cx); 1.2970 + return nullptr; 1.2971 + } 1.2972 + 1.2973 + CrossCompartmentKey key(CrossCompartmentKey::DebuggerScript, object, script); 1.2974 + if (!object->compartment()->putWrapper(cx, key, ObjectValue(*scriptobj))) { 1.2975 + scripts.remove(script); 1.2976 + js_ReportOutOfMemory(cx); 1.2977 + return nullptr; 1.2978 + } 1.2979 + } 1.2980 + 1.2981 + JS_ASSERT(GetScriptReferent(p->value()) == script); 1.2982 + return p->value(); 1.2983 +} 1.2984 + 1.2985 +static JSObject * 1.2986 +DebuggerScript_check(JSContext *cx, const Value &v, const char *clsname, const char *fnname) 1.2987 +{ 1.2988 + if (!v.isObject()) { 1.2989 + ReportObjectRequired(cx); 1.2990 + return nullptr; 1.2991 + } 1.2992 + JSObject *thisobj = &v.toObject(); 1.2993 + if (thisobj->getClass() != &DebuggerScript_class) { 1.2994 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.2995 + clsname, fnname, thisobj->getClass()->name); 1.2996 + return nullptr; 1.2997 + } 1.2998 + 1.2999 + /* 1.3000 + * Check for Debugger.Script.prototype, which is of class DebuggerScript_class 1.3001 + * but whose script is null. 1.3002 + */ 1.3003 + if (!GetScriptReferent(thisobj)) { 1.3004 + JS_ASSERT(!GetScriptReferent(thisobj)); 1.3005 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.3006 + clsname, fnname, "prototype object"); 1.3007 + return nullptr; 1.3008 + } 1.3009 + 1.3010 + return thisobj; 1.3011 +} 1.3012 + 1.3013 +static JSObject * 1.3014 +DebuggerScript_checkThis(JSContext *cx, const CallArgs &args, const char *fnname) 1.3015 +{ 1.3016 + return DebuggerScript_check(cx, args.thisv(), "Debugger.Script", fnname); 1.3017 +} 1.3018 + 1.3019 +#define THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, fnname, args, obj, script) \ 1.3020 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.3021 + RootedObject obj(cx, DebuggerScript_checkThis(cx, args, fnname)); \ 1.3022 + if (!obj) \ 1.3023 + return false; \ 1.3024 + Rooted<JSScript*> script(cx, GetScriptReferent(obj)) 1.3025 + 1.3026 +static bool 1.3027 +DebuggerScript_getUrl(JSContext *cx, unsigned argc, Value *vp) 1.3028 +{ 1.3029 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get url)", args, obj, script); 1.3030 + 1.3031 + if (script->filename()) { 1.3032 + JSString *str; 1.3033 + if (script->scriptSource()->introducerFilename()) 1.3034 + str = js_NewStringCopyZ<CanGC>(cx, script->scriptSource()->introducerFilename()); 1.3035 + else 1.3036 + str = js_NewStringCopyZ<CanGC>(cx, script->filename()); 1.3037 + if (!str) 1.3038 + return false; 1.3039 + args.rval().setString(str); 1.3040 + } else { 1.3041 + args.rval().setNull(); 1.3042 + } 1.3043 + return true; 1.3044 +} 1.3045 + 1.3046 +static bool 1.3047 +DebuggerScript_getStartLine(JSContext *cx, unsigned argc, Value *vp) 1.3048 +{ 1.3049 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get startLine)", args, obj, script); 1.3050 + args.rval().setNumber(uint32_t(script->lineno())); 1.3051 + return true; 1.3052 +} 1.3053 + 1.3054 +static bool 1.3055 +DebuggerScript_getLineCount(JSContext *cx, unsigned argc, Value *vp) 1.3056 +{ 1.3057 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get lineCount)", args, obj, script); 1.3058 + 1.3059 + unsigned maxLine = js_GetScriptLineExtent(script); 1.3060 + args.rval().setNumber(double(maxLine)); 1.3061 + return true; 1.3062 +} 1.3063 + 1.3064 +static bool 1.3065 +DebuggerScript_getSource(JSContext *cx, unsigned argc, Value *vp) 1.3066 +{ 1.3067 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get source)", args, obj, script); 1.3068 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3069 + 1.3070 + RootedScriptSource source(cx, &UncheckedUnwrap(script->sourceObject())->as<ScriptSourceObject>()); 1.3071 + RootedObject sourceObject(cx, dbg->wrapSource(cx, source)); 1.3072 + if (!sourceObject) 1.3073 + return false; 1.3074 + 1.3075 + args.rval().setObject(*sourceObject); 1.3076 + return true; 1.3077 +} 1.3078 + 1.3079 +static bool 1.3080 +DebuggerScript_getSourceStart(JSContext *cx, unsigned argc, Value *vp) 1.3081 +{ 1.3082 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceStart)", args, obj, script); 1.3083 + args.rval().setNumber(uint32_t(script->sourceStart())); 1.3084 + return true; 1.3085 +} 1.3086 + 1.3087 +static bool 1.3088 +DebuggerScript_getSourceLength(JSContext *cx, unsigned argc, Value *vp) 1.3089 +{ 1.3090 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceEnd)", args, obj, script); 1.3091 + args.rval().setNumber(uint32_t(script->sourceEnd() - script->sourceStart())); 1.3092 + return true; 1.3093 +} 1.3094 + 1.3095 +static bool 1.3096 +DebuggerScript_getStaticLevel(JSContext *cx, unsigned argc, Value *vp) 1.3097 +{ 1.3098 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get staticLevel)", args, obj, script); 1.3099 + args.rval().setNumber(uint32_t(script->staticLevel())); 1.3100 + return true; 1.3101 +} 1.3102 + 1.3103 +static bool 1.3104 +DebuggerScript_getSourceMapUrl(JSContext *cx, unsigned argc, Value *vp) 1.3105 +{ 1.3106 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get sourceMapURL)", args, obj, script); 1.3107 + 1.3108 + ScriptSource *source = script->scriptSource(); 1.3109 + JS_ASSERT(source); 1.3110 + 1.3111 + if (source->hasSourceMapURL()) { 1.3112 + JSString *str = JS_NewUCStringCopyZ(cx, source->sourceMapURL()); 1.3113 + if (!str) 1.3114 + return false; 1.3115 + args.rval().setString(str); 1.3116 + } else { 1.3117 + args.rval().setNull(); 1.3118 + } 1.3119 + 1.3120 + return true; 1.3121 +} 1.3122 + 1.3123 +static bool 1.3124 +DebuggerScript_getGlobal(JSContext *cx, unsigned argc, Value *vp) 1.3125 +{ 1.3126 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "(get global)", args, obj, script); 1.3127 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3128 + 1.3129 + RootedValue v(cx, ObjectValue(script->global())); 1.3130 + if (!dbg->wrapDebuggeeValue(cx, &v)) 1.3131 + return false; 1.3132 + args.rval().set(v); 1.3133 + return true; 1.3134 +} 1.3135 + 1.3136 +static bool 1.3137 +DebuggerScript_getChildScripts(JSContext *cx, unsigned argc, Value *vp) 1.3138 +{ 1.3139 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getChildScripts", args, obj, script); 1.3140 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3141 + 1.3142 + RootedObject result(cx, NewDenseEmptyArray(cx)); 1.3143 + if (!result) 1.3144 + return false; 1.3145 + if (script->hasObjects()) { 1.3146 + /* 1.3147 + * script->savedCallerFun indicates that this is a direct eval script 1.3148 + * and the calling function is stored as script->objects()->vector[0]. 1.3149 + * It is not really a child script of this script, so skip it using 1.3150 + * innerObjectsStart(). 1.3151 + */ 1.3152 + ObjectArray *objects = script->objects(); 1.3153 + RootedFunction fun(cx); 1.3154 + RootedScript funScript(cx); 1.3155 + RootedObject obj(cx), s(cx); 1.3156 + for (uint32_t i = script->innerObjectsStart(); i < objects->length; i++) { 1.3157 + obj = objects->vector[i]; 1.3158 + if (obj->is<JSFunction>()) { 1.3159 + fun = &obj->as<JSFunction>(); 1.3160 + funScript = GetOrCreateFunctionScript(cx, fun); 1.3161 + if (!funScript) 1.3162 + return false; 1.3163 + s = dbg->wrapScript(cx, funScript); 1.3164 + if (!s || !NewbornArrayPush(cx, result, ObjectValue(*s))) 1.3165 + return false; 1.3166 + } 1.3167 + } 1.3168 + } 1.3169 + args.rval().setObject(*result); 1.3170 + return true; 1.3171 +} 1.3172 + 1.3173 +static bool 1.3174 +ScriptOffset(JSContext *cx, JSScript *script, const Value &v, size_t *offsetp) 1.3175 +{ 1.3176 + double d; 1.3177 + size_t off; 1.3178 + 1.3179 + bool ok = v.isNumber(); 1.3180 + if (ok) { 1.3181 + d = v.toNumber(); 1.3182 + off = size_t(d); 1.3183 + } 1.3184 + if (!ok || off != d || !IsValidBytecodeOffset(cx, script, off)) { 1.3185 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_OFFSET); 1.3186 + return false; 1.3187 + } 1.3188 + *offsetp = off; 1.3189 + return true; 1.3190 +} 1.3191 + 1.3192 +static bool 1.3193 +DebuggerScript_getOffsetLine(JSContext *cx, unsigned argc, Value *vp) 1.3194 +{ 1.3195 + REQUIRE_ARGC("Debugger.Script.getOffsetLine", 1); 1.3196 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetLine", args, obj, script); 1.3197 + size_t offset; 1.3198 + if (!ScriptOffset(cx, script, args[0], &offset)) 1.3199 + return false; 1.3200 + unsigned lineno = JS_PCToLineNumber(cx, script, script->offsetToPC(offset)); 1.3201 + args.rval().setNumber(lineno); 1.3202 + return true; 1.3203 +} 1.3204 + 1.3205 +namespace { 1.3206 + 1.3207 +class BytecodeRangeWithPosition : private BytecodeRange 1.3208 +{ 1.3209 + public: 1.3210 + using BytecodeRange::empty; 1.3211 + using BytecodeRange::frontPC; 1.3212 + using BytecodeRange::frontOpcode; 1.3213 + using BytecodeRange::frontOffset; 1.3214 + 1.3215 + BytecodeRangeWithPosition(JSContext *cx, JSScript *script) 1.3216 + : BytecodeRange(cx, script), lineno(script->lineno()), column(0), 1.3217 + sn(script->notes()), snpc(script->code()) 1.3218 + { 1.3219 + if (!SN_IS_TERMINATOR(sn)) 1.3220 + snpc += SN_DELTA(sn); 1.3221 + updatePosition(); 1.3222 + while (frontPC() != script->main()) 1.3223 + popFront(); 1.3224 + } 1.3225 + 1.3226 + void popFront() { 1.3227 + BytecodeRange::popFront(); 1.3228 + if (!empty()) 1.3229 + updatePosition(); 1.3230 + } 1.3231 + 1.3232 + size_t frontLineNumber() const { return lineno; } 1.3233 + size_t frontColumnNumber() const { return column; } 1.3234 + 1.3235 + private: 1.3236 + void updatePosition() { 1.3237 + /* 1.3238 + * Determine the current line number by reading all source notes up to 1.3239 + * and including the current offset. 1.3240 + */ 1.3241 + while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) { 1.3242 + SrcNoteType type = (SrcNoteType) SN_TYPE(sn); 1.3243 + if (type == SRC_COLSPAN) { 1.3244 + ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0); 1.3245 + 1.3246 + if (colspan >= SN_COLSPAN_DOMAIN / 2) 1.3247 + colspan -= SN_COLSPAN_DOMAIN; 1.3248 + JS_ASSERT(ptrdiff_t(column) + colspan >= 0); 1.3249 + column += colspan; 1.3250 + } if (type == SRC_SETLINE) { 1.3251 + lineno = size_t(js_GetSrcNoteOffset(sn, 0)); 1.3252 + column = 0; 1.3253 + } else if (type == SRC_NEWLINE) { 1.3254 + lineno++; 1.3255 + column = 0; 1.3256 + } 1.3257 + 1.3258 + sn = SN_NEXT(sn); 1.3259 + snpc += SN_DELTA(sn); 1.3260 + } 1.3261 + } 1.3262 + 1.3263 + size_t lineno; 1.3264 + size_t column; 1.3265 + jssrcnote *sn; 1.3266 + jsbytecode *snpc; 1.3267 +}; 1.3268 + 1.3269 +/* 1.3270 + * FlowGraphSummary::populate(cx, script) computes a summary of script's 1.3271 + * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}. 1.3272 + * 1.3273 + * An instruction on a given line is an entry point for that line if it can be 1.3274 + * reached from (an instruction on) a different line. We distinguish between the 1.3275 + * following cases: 1.3276 + * - hasNoEdges: 1.3277 + * The instruction cannot be reached, so the instruction is not an entry 1.3278 + * point for the line it is on. 1.3279 + * - hasSingleEdge: 1.3280 + * - hasMultipleEdgesFromSingleLine: 1.3281 + * The instruction can be reached from a single line. If this line is 1.3282 + * different from the line the instruction is on, the instruction is an 1.3283 + * entry point for that line. 1.3284 + * - hasMultipleEdgesFromMultipleLines: 1.3285 + * The instruction can be reached from multiple lines. At least one of 1.3286 + * these lines is guaranteed to be different from the line the instruction 1.3287 + * is on, so the instruction is an entry point for that line. 1.3288 + * 1.3289 + * Similarly, an instruction on a given position (line/column pair) is an 1.3290 + * entry point for that position if it can be reached from (an instruction on) a 1.3291 + * different position. Again, we distinguish between the following cases: 1.3292 + * - hasNoEdges: 1.3293 + * The instruction cannot be reached, so the instruction is not an entry 1.3294 + * point for the position it is on. 1.3295 + * - hasSingleEdge: 1.3296 + * The instruction can be reached from a single position. If this line is 1.3297 + * different from the position the instruction is on, the instruction is 1.3298 + * an entry point for that position. 1.3299 + * - hasMultipleEdgesFromSingleLine: 1.3300 + * - hasMultipleEdgesFromMultipleLines: 1.3301 + * The instruction can be reached from multiple positions. At least one 1.3302 + * of these positions is guaranteed to be different from the position the 1.3303 + * instruction is on, so the instruction is an entry point for that 1.3304 + * position. 1.3305 + */ 1.3306 +class FlowGraphSummary { 1.3307 + public: 1.3308 + class Entry { 1.3309 + public: 1.3310 + static Entry createWithNoEdges() { 1.3311 + return Entry(SIZE_MAX, 0); 1.3312 + } 1.3313 + 1.3314 + static Entry createWithSingleEdge(size_t lineno, size_t column) { 1.3315 + return Entry(lineno, column); 1.3316 + } 1.3317 + 1.3318 + static Entry createWithMultipleEdgesFromSingleLine(size_t lineno) { 1.3319 + return Entry(lineno, SIZE_MAX); 1.3320 + } 1.3321 + 1.3322 + static Entry createWithMultipleEdgesFromMultipleLines() { 1.3323 + return Entry(SIZE_MAX, SIZE_MAX); 1.3324 + } 1.3325 + 1.3326 + Entry() {} 1.3327 + 1.3328 + bool hasNoEdges() const { 1.3329 + return lineno_ == SIZE_MAX && column_ != SIZE_MAX; 1.3330 + } 1.3331 + 1.3332 + bool hasSingleEdge() const { 1.3333 + return lineno_ != SIZE_MAX && column_ != SIZE_MAX; 1.3334 + } 1.3335 + 1.3336 + bool hasMultipleEdgesFromSingleLine() const { 1.3337 + return lineno_ != SIZE_MAX && column_ == SIZE_MAX; 1.3338 + } 1.3339 + 1.3340 + bool hasMultipleEdgesFromMultipleLines() const { 1.3341 + return lineno_ == SIZE_MAX && column_ == SIZE_MAX; 1.3342 + } 1.3343 + 1.3344 + bool operator==(const Entry &other) const { 1.3345 + return lineno_ == other.lineno_ && column_ == other.column_; 1.3346 + } 1.3347 + 1.3348 + bool operator!=(const Entry &other) const { 1.3349 + return lineno_ != other.lineno_ || column_ != other.column_; 1.3350 + } 1.3351 + 1.3352 + size_t lineno() const { 1.3353 + return lineno_; 1.3354 + } 1.3355 + 1.3356 + size_t column() const { 1.3357 + return column_; 1.3358 + } 1.3359 + 1.3360 + private: 1.3361 + Entry(size_t lineno, size_t column) : lineno_(lineno), column_(column) {} 1.3362 + 1.3363 + size_t lineno_; 1.3364 + size_t column_; 1.3365 + }; 1.3366 + 1.3367 + FlowGraphSummary(JSContext *cx) : entries_(cx) {} 1.3368 + 1.3369 + Entry &operator[](size_t index) { 1.3370 + return entries_[index]; 1.3371 + } 1.3372 + 1.3373 + bool populate(JSContext *cx, JSScript *script) { 1.3374 + if (!entries_.growBy(script->length())) 1.3375 + return false; 1.3376 + unsigned mainOffset = script->pcToOffset(script->main()); 1.3377 + entries_[mainOffset] = Entry::createWithMultipleEdgesFromMultipleLines(); 1.3378 + for (size_t i = mainOffset + 1; i < script->length(); i++) 1.3379 + entries_[i] = Entry::createWithNoEdges(); 1.3380 + 1.3381 + size_t prevLineno = script->lineno(); 1.3382 + size_t prevColumn = 0; 1.3383 + JSOp prevOp = JSOP_NOP; 1.3384 + for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) { 1.3385 + size_t lineno = r.frontLineNumber(); 1.3386 + size_t column = r.frontColumnNumber(); 1.3387 + JSOp op = r.frontOpcode(); 1.3388 + 1.3389 + if (FlowsIntoNext(prevOp)) 1.3390 + addEdge(prevLineno, prevColumn, r.frontOffset()); 1.3391 + 1.3392 + if (js_CodeSpec[op].type() == JOF_JUMP) { 1.3393 + addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC())); 1.3394 + } else if (op == JSOP_TABLESWITCH) { 1.3395 + jsbytecode *pc = r.frontPC(); 1.3396 + size_t offset = r.frontOffset(); 1.3397 + ptrdiff_t step = JUMP_OFFSET_LEN; 1.3398 + size_t defaultOffset = offset + GET_JUMP_OFFSET(pc); 1.3399 + pc += step; 1.3400 + addEdge(lineno, column, defaultOffset); 1.3401 + 1.3402 + int32_t low = GET_JUMP_OFFSET(pc); 1.3403 + pc += JUMP_OFFSET_LEN; 1.3404 + int ncases = GET_JUMP_OFFSET(pc) - low + 1; 1.3405 + pc += JUMP_OFFSET_LEN; 1.3406 + 1.3407 + for (int i = 0; i < ncases; i++) { 1.3408 + size_t target = offset + GET_JUMP_OFFSET(pc); 1.3409 + addEdge(lineno, column, target); 1.3410 + pc += step; 1.3411 + } 1.3412 + } 1.3413 + 1.3414 + prevLineno = lineno; 1.3415 + prevColumn = column; 1.3416 + prevOp = op; 1.3417 + } 1.3418 + 1.3419 + return true; 1.3420 + } 1.3421 + 1.3422 + private: 1.3423 + void addEdge(size_t sourceLineno, size_t sourceColumn, size_t targetOffset) { 1.3424 + if (entries_[targetOffset].hasNoEdges()) 1.3425 + entries_[targetOffset] = Entry::createWithSingleEdge(sourceLineno, sourceColumn); 1.3426 + else if (entries_[targetOffset].lineno() != sourceLineno) 1.3427 + entries_[targetOffset] = Entry::createWithMultipleEdgesFromMultipleLines(); 1.3428 + else if (entries_[targetOffset].column() != sourceColumn) 1.3429 + entries_[targetOffset] = Entry::createWithMultipleEdgesFromSingleLine(sourceLineno); 1.3430 + } 1.3431 + 1.3432 + Vector<Entry> entries_; 1.3433 +}; 1.3434 + 1.3435 +} /* anonymous namespace */ 1.3436 + 1.3437 +static bool 1.3438 +DebuggerScript_getAllOffsets(JSContext *cx, unsigned argc, Value *vp) 1.3439 +{ 1.3440 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllOffsets", args, obj, script); 1.3441 + 1.3442 + /* 1.3443 + * First pass: determine which offsets in this script are jump targets and 1.3444 + * which line numbers jump to them. 1.3445 + */ 1.3446 + FlowGraphSummary flowData(cx); 1.3447 + if (!flowData.populate(cx, script)) 1.3448 + return false; 1.3449 + 1.3450 + /* Second pass: build the result array. */ 1.3451 + RootedObject result(cx, NewDenseEmptyArray(cx)); 1.3452 + if (!result) 1.3453 + return false; 1.3454 + for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) { 1.3455 + size_t offset = r.frontOffset(); 1.3456 + size_t lineno = r.frontLineNumber(); 1.3457 + 1.3458 + /* Make a note, if the current instruction is an entry point for the current line. */ 1.3459 + if (!flowData[offset].hasNoEdges() && flowData[offset].lineno() != lineno) { 1.3460 + /* Get the offsets array for this line. */ 1.3461 + RootedObject offsets(cx); 1.3462 + RootedValue offsetsv(cx); 1.3463 + 1.3464 + RootedId id(cx, INT_TO_JSID(lineno)); 1.3465 + 1.3466 + bool found; 1.3467 + if (!js::HasOwnProperty(cx, result, id, &found)) 1.3468 + return false; 1.3469 + if (found && !JSObject::getGeneric(cx, result, result, id, &offsetsv)) 1.3470 + return false; 1.3471 + 1.3472 + if (offsetsv.isObject()) { 1.3473 + offsets = &offsetsv.toObject(); 1.3474 + } else { 1.3475 + JS_ASSERT(offsetsv.isUndefined()); 1.3476 + 1.3477 + /* 1.3478 + * Create an empty offsets array for this line. 1.3479 + * Store it in the result array. 1.3480 + */ 1.3481 + RootedId id(cx); 1.3482 + RootedValue v(cx, NumberValue(lineno)); 1.3483 + offsets = NewDenseEmptyArray(cx); 1.3484 + if (!offsets || 1.3485 + !ValueToId<CanGC>(cx, v, &id)) 1.3486 + { 1.3487 + return false; 1.3488 + } 1.3489 + 1.3490 + RootedValue value(cx, ObjectValue(*offsets)); 1.3491 + if (!JSObject::defineGeneric(cx, result, id, value)) 1.3492 + return false; 1.3493 + } 1.3494 + 1.3495 + /* Append the current offset to the offsets array. */ 1.3496 + if (!NewbornArrayPush(cx, offsets, NumberValue(offset))) 1.3497 + return false; 1.3498 + } 1.3499 + } 1.3500 + 1.3501 + args.rval().setObject(*result); 1.3502 + return true; 1.3503 +} 1.3504 + 1.3505 +static bool 1.3506 +DebuggerScript_getAllColumnOffsets(JSContext *cx, unsigned argc, Value *vp) 1.3507 +{ 1.3508 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getAllColumnOffsets", args, obj, script); 1.3509 + 1.3510 + /* 1.3511 + * First pass: determine which offsets in this script are jump targets and 1.3512 + * which positions jump to them. 1.3513 + */ 1.3514 + FlowGraphSummary flowData(cx); 1.3515 + if (!flowData.populate(cx, script)) 1.3516 + return false; 1.3517 + 1.3518 + /* Second pass: build the result array. */ 1.3519 + RootedObject result(cx, NewDenseEmptyArray(cx)); 1.3520 + if (!result) 1.3521 + return false; 1.3522 + for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) { 1.3523 + size_t lineno = r.frontLineNumber(); 1.3524 + size_t column = r.frontColumnNumber(); 1.3525 + size_t offset = r.frontOffset(); 1.3526 + 1.3527 + /* Make a note, if the current instruction is an entry point for the current position. */ 1.3528 + if (!flowData[offset].hasNoEdges() && 1.3529 + (flowData[offset].lineno() != lineno || 1.3530 + flowData[offset].column() != column)) { 1.3531 + RootedObject entry(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); 1.3532 + if (!entry) 1.3533 + return false; 1.3534 + 1.3535 + RootedId id(cx, NameToId(cx->names().lineNumber)); 1.3536 + RootedValue value(cx, NumberValue(lineno)); 1.3537 + if (!JSObject::defineGeneric(cx, entry, id, value)) 1.3538 + return false; 1.3539 + 1.3540 + value = NumberValue(column); 1.3541 + if (!JSObject::defineProperty(cx, entry, cx->names().columnNumber, value)) 1.3542 + return false; 1.3543 + 1.3544 + id = NameToId(cx->names().offset); 1.3545 + value = NumberValue(offset); 1.3546 + if (!JSObject::defineGeneric(cx, entry, id, value)) 1.3547 + return false; 1.3548 + 1.3549 + if (!NewbornArrayPush(cx, result, ObjectValue(*entry))) 1.3550 + return false; 1.3551 + } 1.3552 + } 1.3553 + 1.3554 + args.rval().setObject(*result); 1.3555 + return true; 1.3556 +} 1.3557 + 1.3558 +static bool 1.3559 +DebuggerScript_getLineOffsets(JSContext *cx, unsigned argc, Value *vp) 1.3560 +{ 1.3561 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineOffsets", args, obj, script); 1.3562 + REQUIRE_ARGC("Debugger.Script.getLineOffsets", 1); 1.3563 + 1.3564 + /* Parse lineno argument. */ 1.3565 + RootedValue linenoValue(cx, args[0]); 1.3566 + size_t lineno; 1.3567 + if (!ToNumber(cx, &linenoValue)) 1.3568 + return false; 1.3569 + { 1.3570 + double d = linenoValue.toNumber(); 1.3571 + lineno = size_t(d); 1.3572 + if (lineno != d) { 1.3573 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_LINE); 1.3574 + return false; 1.3575 + } 1.3576 + } 1.3577 + 1.3578 + /* 1.3579 + * First pass: determine which offsets in this script are jump targets and 1.3580 + * which line numbers jump to them. 1.3581 + */ 1.3582 + FlowGraphSummary flowData(cx); 1.3583 + if (!flowData.populate(cx, script)) 1.3584 + return false; 1.3585 + 1.3586 + /* Second pass: build the result array. */ 1.3587 + RootedObject result(cx, NewDenseEmptyArray(cx)); 1.3588 + if (!result) 1.3589 + return false; 1.3590 + for (BytecodeRangeWithPosition r(cx, script); !r.empty(); r.popFront()) { 1.3591 + size_t offset = r.frontOffset(); 1.3592 + 1.3593 + /* If the op at offset is an entry point, append offset to result. */ 1.3594 + if (r.frontLineNumber() == lineno && 1.3595 + !flowData[offset].hasNoEdges() && 1.3596 + flowData[offset].lineno() != lineno) 1.3597 + { 1.3598 + if (!NewbornArrayPush(cx, result, NumberValue(offset))) 1.3599 + return false; 1.3600 + } 1.3601 + } 1.3602 + 1.3603 + args.rval().setObject(*result); 1.3604 + return true; 1.3605 +} 1.3606 + 1.3607 +bool 1.3608 +Debugger::observesFrame(AbstractFramePtr frame) const 1.3609 +{ 1.3610 + return observesScript(frame.script()); 1.3611 +} 1.3612 + 1.3613 +bool 1.3614 +Debugger::observesFrame(const ScriptFrameIter &iter) const 1.3615 +{ 1.3616 + return observesScript(iter.script()); 1.3617 +} 1.3618 + 1.3619 +bool 1.3620 +Debugger::observesScript(JSScript *script) const 1.3621 +{ 1.3622 + if (!enabled) 1.3623 + return false; 1.3624 + return observesGlobal(&script->global()) && (!script->selfHosted() || 1.3625 + SelfHostedFramesVisible()); 1.3626 +} 1.3627 + 1.3628 +/* static */ bool 1.3629 +Debugger::replaceFrameGuts(JSContext *cx, AbstractFramePtr from, AbstractFramePtr to, 1.3630 + ScriptFrameIter &iter) 1.3631 +{ 1.3632 + for (Debugger::FrameRange r(from); !r.empty(); r.popFront()) { 1.3633 + RootedObject frameobj(cx, r.frontFrame()); 1.3634 + Debugger *dbg = r.frontDebugger(); 1.3635 + JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj)); 1.3636 + 1.3637 + // Update frame object's ScriptFrameIter::data pointer. 1.3638 + DebuggerFrame_freeScriptFrameIterData(cx->runtime()->defaultFreeOp(), frameobj); 1.3639 + ScriptFrameIter::Data *data = iter.copyData(); 1.3640 + if (!data) 1.3641 + return false; 1.3642 + frameobj->setPrivate(data); 1.3643 + 1.3644 + // Remove the old entry before mutating the HashMap. 1.3645 + r.removeFrontFrame(); 1.3646 + 1.3647 + // Add the frame object with |to| as key. 1.3648 + if (!dbg->frames.putNew(to, frameobj)) { 1.3649 + js_ReportOutOfMemory(cx); 1.3650 + return false; 1.3651 + } 1.3652 + } 1.3653 + 1.3654 + return true; 1.3655 +} 1.3656 + 1.3657 +/* static */ bool 1.3658 +Debugger::handleBaselineOsr(JSContext *cx, InterpreterFrame *from, jit::BaselineFrame *to) 1.3659 +{ 1.3660 + ScriptFrameIter iter(cx); 1.3661 + JS_ASSERT(iter.abstractFramePtr() == to); 1.3662 + return replaceFrameGuts(cx, from, to, iter); 1.3663 +} 1.3664 + 1.3665 +/* static */ bool 1.3666 +Debugger::handleIonBailout(JSContext *cx, jit::RematerializedFrame *from, jit::BaselineFrame *to) 1.3667 +{ 1.3668 + // When we return to a bailed-out Ion real frame, we must update all 1.3669 + // Debugger.Frames that refer to its inline frames. However, since we 1.3670 + // can't pop individual inline frames off the stack (we can only pop the 1.3671 + // real frame that contains them all, as a unit), we cannot assume that 1.3672 + // the frame we're dealing with is the top frame. Advance the iterator 1.3673 + // across any inlined frames younger than |to|, the baseline frame 1.3674 + // reconstructed during bailout from the Ion frame corresponding to 1.3675 + // |from|. 1.3676 + ScriptFrameIter iter(cx); 1.3677 + while (iter.abstractFramePtr() != to) 1.3678 + ++iter; 1.3679 + return replaceFrameGuts(cx, from, to, iter); 1.3680 +} 1.3681 + 1.3682 +static bool 1.3683 +DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp) 1.3684 +{ 1.3685 + REQUIRE_ARGC("Debugger.Script.setBreakpoint", 2); 1.3686 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script); 1.3687 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3688 + 1.3689 + if (!dbg->observesScript(script)) { 1.3690 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGING); 1.3691 + return false; 1.3692 + } 1.3693 + 1.3694 + size_t offset; 1.3695 + if (!ScriptOffset(cx, script, args[0], &offset)) 1.3696 + return false; 1.3697 + 1.3698 + JSObject *handler = NonNullObject(cx, args[1]); 1.3699 + if (!handler) 1.3700 + return false; 1.3701 + 1.3702 + jsbytecode *pc = script->offsetToPC(offset); 1.3703 + BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc); 1.3704 + if (!site) 1.3705 + return false; 1.3706 + site->inc(cx->runtime()->defaultFreeOp()); 1.3707 + if (cx->runtime()->new_<Breakpoint>(dbg, site, handler)) { 1.3708 + args.rval().setUndefined(); 1.3709 + return true; 1.3710 + } 1.3711 + site->dec(cx->runtime()->defaultFreeOp()); 1.3712 + site->destroyIfEmpty(cx->runtime()->defaultFreeOp()); 1.3713 + return false; 1.3714 +} 1.3715 + 1.3716 +static bool 1.3717 +DebuggerScript_getBreakpoints(JSContext *cx, unsigned argc, Value *vp) 1.3718 +{ 1.3719 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getBreakpoints", args, obj, script); 1.3720 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3721 + 1.3722 + jsbytecode *pc; 1.3723 + if (args.length() > 0) { 1.3724 + size_t offset; 1.3725 + if (!ScriptOffset(cx, script, args[0], &offset)) 1.3726 + return false; 1.3727 + pc = script->offsetToPC(offset); 1.3728 + } else { 1.3729 + pc = nullptr; 1.3730 + } 1.3731 + 1.3732 + RootedObject arr(cx, NewDenseEmptyArray(cx)); 1.3733 + if (!arr) 1.3734 + return false; 1.3735 + 1.3736 + for (unsigned i = 0; i < script->length(); i++) { 1.3737 + BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i)); 1.3738 + if (site && (!pc || site->pc == pc)) { 1.3739 + for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) { 1.3740 + if (bp->debugger == dbg && 1.3741 + !NewbornArrayPush(cx, arr, ObjectValue(*bp->getHandler()))) 1.3742 + { 1.3743 + return false; 1.3744 + } 1.3745 + } 1.3746 + } 1.3747 + } 1.3748 + args.rval().setObject(*arr); 1.3749 + return true; 1.3750 +} 1.3751 + 1.3752 +static bool 1.3753 +DebuggerScript_clearBreakpoint(JSContext *cx, unsigned argc, Value *vp) 1.3754 +{ 1.3755 + REQUIRE_ARGC("Debugger.Script.clearBreakpoint", 1); 1.3756 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script); 1.3757 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3758 + 1.3759 + JSObject *handler = NonNullObject(cx, args[0]); 1.3760 + if (!handler) 1.3761 + return false; 1.3762 + 1.3763 + script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg, handler); 1.3764 + args.rval().setUndefined(); 1.3765 + return true; 1.3766 +} 1.3767 + 1.3768 +static bool 1.3769 +DebuggerScript_clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp) 1.3770 +{ 1.3771 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearAllBreakpoints", args, obj, script); 1.3772 + Debugger *dbg = Debugger::fromChildJSObject(obj); 1.3773 + script->clearBreakpointsIn(cx->runtime()->defaultFreeOp(), dbg, nullptr); 1.3774 + args.rval().setUndefined(); 1.3775 + return true; 1.3776 +} 1.3777 + 1.3778 +static bool 1.3779 +DebuggerScript_isInCatchScope(JSContext *cx, unsigned argc, Value* vp) 1.3780 +{ 1.3781 + REQUIRE_ARGC("Debugger.Script.isInCatchScope", 1); 1.3782 + THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "isInCatchScope", args, obj, script); 1.3783 + 1.3784 + size_t offset; 1.3785 + if (!ScriptOffset(cx, script, args[0], &offset)) 1.3786 + return false; 1.3787 + 1.3788 + /* 1.3789 + * Try note ranges are relative to the mainOffset of the script, so adjust 1.3790 + * offset accordingly. 1.3791 + */ 1.3792 + offset -= script->mainOffset(); 1.3793 + 1.3794 + args.rval().setBoolean(false); 1.3795 + if (script->hasTrynotes()) { 1.3796 + JSTryNote* tnBegin = script->trynotes()->vector; 1.3797 + JSTryNote* tnEnd = tnBegin + script->trynotes()->length; 1.3798 + while (tnBegin != tnEnd) { 1.3799 + if (tnBegin->start <= offset && 1.3800 + offset <= tnBegin->start + tnBegin->length && 1.3801 + tnBegin->kind == JSTRY_CATCH) 1.3802 + { 1.3803 + args.rval().setBoolean(true); 1.3804 + break; 1.3805 + } 1.3806 + ++tnBegin; 1.3807 + } 1.3808 + } 1.3809 + return true; 1.3810 +} 1.3811 + 1.3812 +static bool 1.3813 +DebuggerScript_construct(JSContext *cx, unsigned argc, Value *vp) 1.3814 +{ 1.3815 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.3816 + "Debugger.Script"); 1.3817 + return false; 1.3818 +} 1.3819 + 1.3820 +static const JSPropertySpec DebuggerScript_properties[] = { 1.3821 + JS_PSG("url", DebuggerScript_getUrl, 0), 1.3822 + JS_PSG("startLine", DebuggerScript_getStartLine, 0), 1.3823 + JS_PSG("lineCount", DebuggerScript_getLineCount, 0), 1.3824 + JS_PSG("source", DebuggerScript_getSource, 0), 1.3825 + JS_PSG("sourceStart", DebuggerScript_getSourceStart, 0), 1.3826 + JS_PSG("sourceLength", DebuggerScript_getSourceLength, 0), 1.3827 + JS_PSG("staticLevel", DebuggerScript_getStaticLevel, 0), 1.3828 + JS_PSG("sourceMapURL", DebuggerScript_getSourceMapUrl, 0), 1.3829 + JS_PSG("global", DebuggerScript_getGlobal, 0), 1.3830 + JS_PS_END 1.3831 +}; 1.3832 + 1.3833 +static const JSFunctionSpec DebuggerScript_methods[] = { 1.3834 + JS_FN("getChildScripts", DebuggerScript_getChildScripts, 0, 0), 1.3835 + JS_FN("getAllOffsets", DebuggerScript_getAllOffsets, 0, 0), 1.3836 + JS_FN("getAllColumnOffsets", DebuggerScript_getAllColumnOffsets, 0, 0), 1.3837 + JS_FN("getLineOffsets", DebuggerScript_getLineOffsets, 1, 0), 1.3838 + JS_FN("getOffsetLine", DebuggerScript_getOffsetLine, 0, 0), 1.3839 + JS_FN("setBreakpoint", DebuggerScript_setBreakpoint, 2, 0), 1.3840 + JS_FN("getBreakpoints", DebuggerScript_getBreakpoints, 1, 0), 1.3841 + JS_FN("clearBreakpoint", DebuggerScript_clearBreakpoint, 1, 0), 1.3842 + JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0), 1.3843 + JS_FN("isInCatchScope", DebuggerScript_isInCatchScope, 1, 0), 1.3844 + JS_FS_END 1.3845 +}; 1.3846 + 1.3847 + 1.3848 +/*** Debugger.Source *****************************************************************************/ 1.3849 + 1.3850 +static inline ScriptSourceObject * 1.3851 +GetSourceReferent(JSObject *obj) 1.3852 +{ 1.3853 + JS_ASSERT(obj->getClass() == &DebuggerSource_class); 1.3854 + return static_cast<ScriptSourceObject *>(obj->getPrivate()); 1.3855 +} 1.3856 + 1.3857 +static void 1.3858 +DebuggerSource_trace(JSTracer *trc, JSObject *obj) 1.3859 +{ 1.3860 + /* 1.3861 + * There is a barrier on private pointers, so the Unbarriered marking 1.3862 + * is okay. 1.3863 + */ 1.3864 + if (JSObject *referent = GetSourceReferent(obj)) { 1.3865 + MarkCrossCompartmentObjectUnbarriered(trc, obj, &referent, "Debugger.Source referent"); 1.3866 + obj->setPrivateUnbarriered(referent); 1.3867 + } 1.3868 +} 1.3869 + 1.3870 +const Class DebuggerSource_class = { 1.3871 + "Source", 1.3872 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.3873 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSOURCE_COUNT), 1.3874 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.3875 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.3876 + nullptr, /* call */ 1.3877 + nullptr, /* hasInstance */ 1.3878 + nullptr, /* construct */ 1.3879 + DebuggerSource_trace 1.3880 +}; 1.3881 + 1.3882 +JSObject * 1.3883 +Debugger::newDebuggerSource(JSContext *cx, HandleScriptSource source) 1.3884 +{ 1.3885 + assertSameCompartment(cx, object.get()); 1.3886 + 1.3887 + JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject(); 1.3888 + JS_ASSERT(proto); 1.3889 + JSObject *sourceobj = NewObjectWithGivenProto(cx, &DebuggerSource_class, proto, nullptr, TenuredObject); 1.3890 + if (!sourceobj) 1.3891 + return nullptr; 1.3892 + sourceobj->setReservedSlot(JSSLOT_DEBUGSOURCE_OWNER, ObjectValue(*object)); 1.3893 + sourceobj->setPrivateGCThing(source); 1.3894 + 1.3895 + return sourceobj; 1.3896 +} 1.3897 + 1.3898 +JSObject * 1.3899 +Debugger::wrapSource(JSContext *cx, HandleScriptSource source) 1.3900 +{ 1.3901 + assertSameCompartment(cx, object.get()); 1.3902 + JS_ASSERT(cx->compartment() != source->compartment()); 1.3903 + DependentAddPtr<SourceWeakMap> p(cx, sources, source); 1.3904 + if (!p) { 1.3905 + JSObject *sourceobj = newDebuggerSource(cx, source); 1.3906 + if (!sourceobj) 1.3907 + return nullptr; 1.3908 + 1.3909 + if (!p.add(cx, sources, source, sourceobj)) { 1.3910 + js_ReportOutOfMemory(cx); 1.3911 + return nullptr; 1.3912 + } 1.3913 + 1.3914 + CrossCompartmentKey key(CrossCompartmentKey::DebuggerSource, object, source); 1.3915 + if (!object->compartment()->putWrapper(cx, key, ObjectValue(*sourceobj))) { 1.3916 + sources.remove(source); 1.3917 + js_ReportOutOfMemory(cx); 1.3918 + return nullptr; 1.3919 + } 1.3920 + } 1.3921 + 1.3922 + JS_ASSERT(GetSourceReferent(p->value()) == source); 1.3923 + return p->value(); 1.3924 +} 1.3925 + 1.3926 +static bool 1.3927 +DebuggerSource_construct(JSContext *cx, unsigned argc, Value *vp) 1.3928 +{ 1.3929 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.3930 + "Debugger.Source"); 1.3931 + return false; 1.3932 +} 1.3933 + 1.3934 +static JSObject * 1.3935 +DebuggerSource_checkThis(JSContext *cx, const CallArgs &args, const char *fnname) 1.3936 +{ 1.3937 + if (!args.thisv().isObject()) { 1.3938 + ReportObjectRequired(cx); 1.3939 + return nullptr; 1.3940 + } 1.3941 + 1.3942 + JSObject *thisobj = &args.thisv().toObject(); 1.3943 + if (thisobj->getClass() != &DebuggerSource_class) { 1.3944 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.3945 + "Debugger.Source", fnname, thisobj->getClass()->name); 1.3946 + return nullptr; 1.3947 + } 1.3948 + 1.3949 + if (!GetSourceReferent(thisobj)) { 1.3950 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.3951 + "Debugger.Frame", fnname, "prototype object"); 1.3952 + return nullptr; 1.3953 + } 1.3954 + 1.3955 + return thisobj; 1.3956 +} 1.3957 + 1.3958 +#define THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, fnname, args, obj, sourceObject) \ 1.3959 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.3960 + RootedObject obj(cx, DebuggerSource_checkThis(cx, args, fnname)); \ 1.3961 + if (!obj) \ 1.3962 + return false; \ 1.3963 + RootedScriptSource sourceObject(cx, GetSourceReferent(obj)); \ 1.3964 + if (!sourceObject) \ 1.3965 + return false; 1.3966 + 1.3967 +static bool 1.3968 +DebuggerSource_getText(JSContext *cx, unsigned argc, Value *vp) 1.3969 +{ 1.3970 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, sourceObject); 1.3971 + 1.3972 + ScriptSource *ss = sourceObject->source(); 1.3973 + bool hasSourceData = ss->hasSourceData(); 1.3974 + if (!ss->hasSourceData() && !JSScript::loadSource(cx, ss, &hasSourceData)) 1.3975 + return false; 1.3976 + 1.3977 + JSString *str = hasSourceData ? ss->substring(cx, 0, ss->length()) 1.3978 + : js_NewStringCopyZ<CanGC>(cx, "[no source]"); 1.3979 + if (!str) 1.3980 + return false; 1.3981 + 1.3982 + args.rval().setString(str); 1.3983 + return true; 1.3984 +} 1.3985 + 1.3986 +static bool 1.3987 +DebuggerSource_getUrl(JSContext *cx, unsigned argc, Value *vp) 1.3988 +{ 1.3989 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, sourceObject); 1.3990 + 1.3991 + ScriptSource *ss = sourceObject->source(); 1.3992 + if (ss->filename()) { 1.3993 + JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->filename()); 1.3994 + if (!str) 1.3995 + return false; 1.3996 + args.rval().setString(str); 1.3997 + } else { 1.3998 + args.rval().setNull(); 1.3999 + } 1.4000 + return true; 1.4001 +} 1.4002 + 1.4003 +static bool 1.4004 +DebuggerSource_getDisplayURL(JSContext *cx, unsigned argc, Value *vp) 1.4005 +{ 1.4006 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get url)", args, obj, sourceObject); 1.4007 + 1.4008 + ScriptSource *ss = sourceObject->source(); 1.4009 + JS_ASSERT(ss); 1.4010 + 1.4011 + if (ss->hasDisplayURL()) { 1.4012 + JSString *str = JS_NewUCStringCopyZ(cx, ss->displayURL()); 1.4013 + if (!str) 1.4014 + return false; 1.4015 + args.rval().setString(str); 1.4016 + } else { 1.4017 + args.rval().setNull(); 1.4018 + } 1.4019 + 1.4020 + return true; 1.4021 +} 1.4022 + 1.4023 +static bool 1.4024 +DebuggerSource_getElement(JSContext *cx, unsigned argc, Value *vp) 1.4025 +{ 1.4026 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get element)", args, obj, sourceObject); 1.4027 + 1.4028 + if (sourceObject->element()) { 1.4029 + args.rval().setObjectOrNull(sourceObject->element()); 1.4030 + if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval())) 1.4031 + return false; 1.4032 + } else { 1.4033 + args.rval().setUndefined(); 1.4034 + } 1.4035 + return true; 1.4036 +} 1.4037 + 1.4038 +static bool 1.4039 +DebuggerSource_getElementProperty(JSContext *cx, unsigned argc, Value *vp) 1.4040 +{ 1.4041 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get elementAttributeName)", args, obj, sourceObject); 1.4042 + args.rval().set(sourceObject->elementAttributeName()); 1.4043 + return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval()); 1.4044 +} 1.4045 + 1.4046 +static bool 1.4047 +DebuggerSource_getIntroductionScript(JSContext *cx, unsigned argc, Value *vp) 1.4048 +{ 1.4049 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionScript)", args, obj, sourceObject); 1.4050 + 1.4051 + RootedScript script(cx, sourceObject->introductionScript()); 1.4052 + if (script) { 1.4053 + RootedObject scriptDO(cx, Debugger::fromChildJSObject(obj)->wrapScript(cx, script)); 1.4054 + if (!scriptDO) 1.4055 + return false; 1.4056 + args.rval().setObject(*scriptDO); 1.4057 + } else { 1.4058 + args.rval().setUndefined(); 1.4059 + } 1.4060 + return true; 1.4061 +} 1.4062 + 1.4063 +static bool 1.4064 +DebuggerSource_getIntroductionOffset(JSContext *cx, unsigned argc, Value *vp) 1.4065 +{ 1.4066 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionOffset)", args, obj, sourceObject); 1.4067 + 1.4068 + // Regardless of what's recorded in the ScriptSourceObject and 1.4069 + // ScriptSource, only hand out the introduction offset if we also have 1.4070 + // the script within which it applies. 1.4071 + ScriptSource *ss = sourceObject->source(); 1.4072 + if (ss->hasIntroductionOffset() && sourceObject->introductionScript()) 1.4073 + args.rval().setInt32(ss->introductionOffset()); 1.4074 + else 1.4075 + args.rval().setUndefined(); 1.4076 + return true; 1.4077 +} 1.4078 + 1.4079 +static bool 1.4080 +DebuggerSource_getIntroductionType(JSContext *cx, unsigned argc, Value *vp) 1.4081 +{ 1.4082 + THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get introductionType)", args, obj, sourceObject); 1.4083 + 1.4084 + ScriptSource *ss = sourceObject->source(); 1.4085 + if (ss->hasIntroductionType()) { 1.4086 + JSString *str = js_NewStringCopyZ<CanGC>(cx, ss->introductionType()); 1.4087 + if (!str) 1.4088 + return false; 1.4089 + args.rval().setString(str); 1.4090 + } else { 1.4091 + args.rval().setUndefined(); 1.4092 + } 1.4093 + return true; 1.4094 +} 1.4095 + 1.4096 +static const JSPropertySpec DebuggerSource_properties[] = { 1.4097 + JS_PSG("text", DebuggerSource_getText, 0), 1.4098 + JS_PSG("url", DebuggerSource_getUrl, 0), 1.4099 + JS_PSG("element", DebuggerSource_getElement, 0), 1.4100 + JS_PSG("displayURL", DebuggerSource_getDisplayURL, 0), 1.4101 + JS_PSG("introductionScript", DebuggerSource_getIntroductionScript, 0), 1.4102 + JS_PSG("introductionOffset", DebuggerSource_getIntroductionOffset, 0), 1.4103 + JS_PSG("introductionType", DebuggerSource_getIntroductionType, 0), 1.4104 + JS_PSG("elementAttributeName", DebuggerSource_getElementProperty, 0), 1.4105 + JS_PS_END 1.4106 +}; 1.4107 + 1.4108 +static const JSFunctionSpec DebuggerSource_methods[] = { 1.4109 + JS_FS_END 1.4110 +}; 1.4111 + 1.4112 + 1.4113 +/*** Debugger.Frame ******************************************************************************/ 1.4114 + 1.4115 +static void 1.4116 +UpdateFrameIterPc(FrameIter &iter) 1.4117 +{ 1.4118 + if (iter.abstractFramePtr().isRematerializedFrame()) { 1.4119 +#ifdef DEBUG 1.4120 + // Rematerialized frames don't need their pc updated. The reason we 1.4121 + // need to update pc is because we might get the same Debugger.Frame 1.4122 + // object for multiple re-entries into debugger code from debuggee 1.4123 + // code. This reentrancy is not possible with rematerialized frames, 1.4124 + // because when returning to debuggee code, we would have bailed out 1.4125 + // to baseline. 1.4126 + // 1.4127 + // We walk the stack to assert that it doesn't need updating. 1.4128 + jit::RematerializedFrame *frame = iter.abstractFramePtr().asRematerializedFrame(); 1.4129 + jit::IonJSFrameLayout *jsFrame = (jit::IonJSFrameLayout *)frame->top(); 1.4130 + jit::JitActivation *activation = iter.activation()->asJit(); 1.4131 + 1.4132 + ActivationIterator activationIter(activation->cx()->runtime()); 1.4133 + while (activationIter.activation() != activation) 1.4134 + ++activationIter; 1.4135 + 1.4136 + jit::JitFrameIterator jitIter(activationIter); 1.4137 + while (!jitIter.isIonJS() || jitIter.jsFrame() != jsFrame) 1.4138 + ++jitIter; 1.4139 + 1.4140 + jit::InlineFrameIterator ionInlineIter(activation->cx(), &jitIter); 1.4141 + while (ionInlineIter.frameNo() != frame->frameNo()) 1.4142 + ++ionInlineIter; 1.4143 + 1.4144 + MOZ_ASSERT(ionInlineIter.pc() == iter.pc()); 1.4145 +#endif 1.4146 + return; 1.4147 + } 1.4148 + 1.4149 + iter.updatePcQuadratic(); 1.4150 +} 1.4151 + 1.4152 +static void 1.4153 +DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj) 1.4154 +{ 1.4155 + AbstractFramePtr frame = AbstractFramePtr::FromRaw(obj->getPrivate()); 1.4156 + if (frame.isScriptFrameIterData()) 1.4157 + fop->delete_((ScriptFrameIter::Data *) frame.raw()); 1.4158 + obj->setPrivate(nullptr); 1.4159 +} 1.4160 + 1.4161 +static void 1.4162 +DebuggerFrame_maybeDecrementFrameScriptStepModeCount(FreeOp *fop, AbstractFramePtr frame, 1.4163 + JSObject *frameobj) 1.4164 +{ 1.4165 + /* If this frame has an onStep handler, decrement the script's count. */ 1.4166 + if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined()) 1.4167 + frame.script()->decrementStepModeCount(fop); 1.4168 +} 1.4169 + 1.4170 +static void 1.4171 +DebuggerFrame_finalize(FreeOp *fop, JSObject *obj) 1.4172 +{ 1.4173 + DebuggerFrame_freeScriptFrameIterData(fop, obj); 1.4174 +} 1.4175 + 1.4176 +const Class DebuggerFrame_class = { 1.4177 + "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT), 1.4178 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.4179 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, DebuggerFrame_finalize 1.4180 +}; 1.4181 + 1.4182 +static JSObject * 1.4183 +CheckThisFrame(JSContext *cx, const CallArgs &args, const char *fnname, bool checkLive) 1.4184 +{ 1.4185 + if (!args.thisv().isObject()) { 1.4186 + ReportObjectRequired(cx); 1.4187 + return nullptr; 1.4188 + } 1.4189 + JSObject *thisobj = &args.thisv().toObject(); 1.4190 + if (thisobj->getClass() != &DebuggerFrame_class) { 1.4191 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.4192 + "Debugger.Frame", fnname, thisobj->getClass()->name); 1.4193 + return nullptr; 1.4194 + } 1.4195 + 1.4196 + /* 1.4197 + * Forbid Debugger.Frame.prototype, which is of class DebuggerFrame_class 1.4198 + * but isn't really a working Debugger.Frame object. The prototype object 1.4199 + * is distinguished by having a nullptr private value. Also, forbid popped 1.4200 + * frames. 1.4201 + */ 1.4202 + if (!thisobj->getPrivate()) { 1.4203 + if (thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_OWNER).isUndefined()) { 1.4204 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.4205 + "Debugger.Frame", fnname, "prototype object"); 1.4206 + return nullptr; 1.4207 + } 1.4208 + if (checkLive) { 1.4209 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_LIVE, 1.4210 + "Debugger.Frame"); 1.4211 + return nullptr; 1.4212 + } 1.4213 + } 1.4214 + return thisobj; 1.4215 +} 1.4216 + 1.4217 +/* 1.4218 + * To make frequently fired hooks like onEnterFrame more performant, 1.4219 + * Debugger.Frame methods should not create a ScriptFrameIter unless it 1.4220 + * absolutely needs to. That is, unless the method has to call a method on 1.4221 + * ScriptFrameIter that's otherwise not available on AbstractFramePtr. 1.4222 + * 1.4223 + * When a Debugger.Frame is first created, its private slot is set to the 1.4224 + * AbstractFramePtr itself. The first time the users asks for a 1.4225 + * ScriptFrameIter, we construct one, have it settle on the frame pointed to 1.4226 + * by the AbstractFramePtr and cache its internal Data in the Debugger.Frame 1.4227 + * object's private slot. Subsequent uses of the Debugger.Frame object will 1.4228 + * always create a ScriptFrameIter from the cached Data. 1.4229 + * 1.4230 + * Methods that only need the AbstractFramePtr should use THIS_FRAME. 1.4231 + * Methods that need a ScriptFrameIterator should use THIS_FRAME_ITER. 1.4232 + */ 1.4233 + 1.4234 +#define THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj) \ 1.4235 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.4236 + RootedObject thisobj(cx, CheckThisFrame(cx, args, fnname, true)); \ 1.4237 + if (!thisobj) \ 1.4238 + return false 1.4239 + 1.4240 +#define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame) \ 1.4241 + THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \ 1.4242 + AbstractFramePtr frame = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \ 1.4243 + if (frame.isScriptFrameIterData()) { \ 1.4244 + ScriptFrameIter iter(*(ScriptFrameIter::Data *)(frame.raw())); \ 1.4245 + frame = iter.abstractFramePtr(); \ 1.4246 + } 1.4247 + 1.4248 +#define THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter) \ 1.4249 + THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj); \ 1.4250 + Maybe<ScriptFrameIter> maybeIter; \ 1.4251 + { \ 1.4252 + AbstractFramePtr f = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \ 1.4253 + if (f.isScriptFrameIterData()) { \ 1.4254 + maybeIter.construct(*(ScriptFrameIter::Data *)(f.raw())); \ 1.4255 + } else { \ 1.4256 + maybeIter.construct(cx, ScriptFrameIter::ALL_CONTEXTS, \ 1.4257 + ScriptFrameIter::GO_THROUGH_SAVED); \ 1.4258 + ScriptFrameIter &iter = maybeIter.ref(); \ 1.4259 + while (iter.isIon() || iter.abstractFramePtr() != f) \ 1.4260 + ++iter; \ 1.4261 + AbstractFramePtr data = iter.copyDataAsAbstractFramePtr(); \ 1.4262 + if (!data) \ 1.4263 + return false; \ 1.4264 + thisobj->setPrivate(data.raw()); \ 1.4265 + } \ 1.4266 + } \ 1.4267 + ScriptFrameIter &iter = maybeIter.ref() 1.4268 + 1.4269 +#define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, frame, dbg) \ 1.4270 + THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame); \ 1.4271 + Debugger *dbg = Debugger::fromChildJSObject(thisobj) 1.4272 + 1.4273 +#define THIS_FRAME_OWNER_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter, dbg) \ 1.4274 + THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter); \ 1.4275 + Debugger *dbg = Debugger::fromChildJSObject(thisobj) 1.4276 + 1.4277 +static bool 1.4278 +DebuggerFrame_getType(JSContext *cx, unsigned argc, Value *vp) 1.4279 +{ 1.4280 + THIS_FRAME(cx, argc, vp, "get type", args, thisobj, frame); 1.4281 + 1.4282 + /* 1.4283 + * Indirect eval frames are both isGlobalFrame() and isEvalFrame(), so the 1.4284 + * order of checks here is significant. 1.4285 + */ 1.4286 + args.rval().setString(frame.isEvalFrame() 1.4287 + ? cx->names().eval 1.4288 + : frame.isGlobalFrame() 1.4289 + ? cx->names().global 1.4290 + : cx->names().call); 1.4291 + return true; 1.4292 +} 1.4293 + 1.4294 +static bool 1.4295 +DebuggerFrame_getImplementation(JSContext *cx, unsigned argc, Value *vp) 1.4296 +{ 1.4297 + THIS_FRAME(cx, argc, vp, "get implementation", args, thisobj, frame); 1.4298 + 1.4299 + const char *s; 1.4300 + if (frame.isBaselineFrame()) 1.4301 + s = "baseline"; 1.4302 + else if (frame.isRematerializedFrame()) 1.4303 + s = "ion"; 1.4304 + else 1.4305 + s = "interpreter"; 1.4306 + 1.4307 + JSAtom *str = Atomize(cx, s, strlen(s)); 1.4308 + if (!str) 1.4309 + return false; 1.4310 + 1.4311 + args.rval().setString(str); 1.4312 + return true; 1.4313 +} 1.4314 + 1.4315 +static bool 1.4316 +DebuggerFrame_getEnvironment(JSContext *cx, unsigned argc, Value *vp) 1.4317 +{ 1.4318 + THIS_FRAME_OWNER_ITER(cx, argc, vp, "get environment", args, thisobj, _, iter, dbg); 1.4319 + 1.4320 + Rooted<Env*> env(cx); 1.4321 + { 1.4322 + AutoCompartment ac(cx, iter.abstractFramePtr().scopeChain()); 1.4323 + UpdateFrameIterPc(iter); 1.4324 + env = GetDebugScopeForFrame(cx, iter.abstractFramePtr(), iter.pc()); 1.4325 + if (!env) 1.4326 + return false; 1.4327 + } 1.4328 + 1.4329 + return dbg->wrapEnvironment(cx, env, args.rval()); 1.4330 +} 1.4331 + 1.4332 +static bool 1.4333 +DebuggerFrame_getCallee(JSContext *cx, unsigned argc, Value *vp) 1.4334 +{ 1.4335 + THIS_FRAME(cx, argc, vp, "get callee", args, thisobj, frame); 1.4336 + RootedValue calleev(cx, frame.isNonEvalFunctionFrame() ? frame.calleev() : NullValue()); 1.4337 + if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &calleev)) 1.4338 + return false; 1.4339 + args.rval().set(calleev); 1.4340 + return true; 1.4341 +} 1.4342 + 1.4343 +static bool 1.4344 +DebuggerFrame_getGenerator(JSContext *cx, unsigned argc, Value *vp) 1.4345 +{ 1.4346 + THIS_FRAME(cx, argc, vp, "get generator", args, thisobj, frame); 1.4347 + args.rval().setBoolean(frame.isGeneratorFrame()); 1.4348 + return true; 1.4349 +} 1.4350 + 1.4351 +static bool 1.4352 +DebuggerFrame_getConstructing(JSContext *cx, unsigned argc, Value *vp) 1.4353 +{ 1.4354 + THIS_FRAME_ITER(cx, argc, vp, "get constructing", args, thisobj, _, iter); 1.4355 + args.rval().setBoolean(iter.isFunctionFrame() && iter.isConstructing()); 1.4356 + return true; 1.4357 +} 1.4358 + 1.4359 +static bool 1.4360 +DebuggerFrame_getThis(JSContext *cx, unsigned argc, Value *vp) 1.4361 +{ 1.4362 + THIS_FRAME_ITER(cx, argc, vp, "get this", args, thisobj, _, iter); 1.4363 + RootedValue thisv(cx); 1.4364 + { 1.4365 + AutoCompartment ac(cx, iter.scopeChain()); 1.4366 + if (!iter.computeThis(cx)) 1.4367 + return false; 1.4368 + thisv = iter.computedThisValue(); 1.4369 + } 1.4370 + if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &thisv)) 1.4371 + return false; 1.4372 + args.rval().set(thisv); 1.4373 + return true; 1.4374 +} 1.4375 + 1.4376 +static bool 1.4377 +DebuggerFrame_getOlder(JSContext *cx, unsigned argc, Value *vp) 1.4378 +{ 1.4379 + THIS_FRAME_ITER(cx, argc, vp, "get this", args, thisobj, _, iter); 1.4380 + Debugger *dbg = Debugger::fromChildJSObject(thisobj); 1.4381 + 1.4382 + for (++iter; !iter.done(); ++iter) { 1.4383 + if (dbg->observesFrame(iter)) { 1.4384 + if (iter.isIon() && !iter.ensureHasRematerializedFrame()) 1.4385 + return false; 1.4386 + return dbg->getScriptFrame(cx, iter, args.rval()); 1.4387 + } 1.4388 + } 1.4389 + args.rval().setNull(); 1.4390 + return true; 1.4391 +} 1.4392 + 1.4393 +const Class DebuggerArguments_class = { 1.4394 + "Arguments", JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT), 1.4395 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.4396 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub 1.4397 +}; 1.4398 + 1.4399 +/* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */ 1.4400 +static bool 1.4401 +DebuggerArguments_getArg(JSContext *cx, unsigned argc, Value *vp) 1.4402 +{ 1.4403 + CallArgs args = CallArgsFromVp(argc, vp); 1.4404 + int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32(); 1.4405 + 1.4406 + /* Check that the this value is an Arguments object. */ 1.4407 + if (!args.thisv().isObject()) { 1.4408 + ReportObjectRequired(cx); 1.4409 + return false; 1.4410 + } 1.4411 + RootedObject argsobj(cx, &args.thisv().toObject()); 1.4412 + if (argsobj->getClass() != &DebuggerArguments_class) { 1.4413 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.4414 + "Arguments", "getArgument", argsobj->getClass()->name); 1.4415 + return false; 1.4416 + } 1.4417 + 1.4418 + /* 1.4419 + * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME 1.4420 + * to check that it is still live and get the fp. 1.4421 + */ 1.4422 + args.setThis(argsobj->getReservedSlot(JSSLOT_DEBUGARGUMENTS_FRAME)); 1.4423 + THIS_FRAME(cx, argc, vp, "get argument", ca2, thisobj, frame); 1.4424 + 1.4425 + /* 1.4426 + * Since getters can be extracted and applied to other objects, 1.4427 + * there is no guarantee this object has an ith argument. 1.4428 + */ 1.4429 + JS_ASSERT(i >= 0); 1.4430 + RootedValue arg(cx); 1.4431 + RootedScript script(cx); 1.4432 + if (unsigned(i) < frame.numActualArgs()) { 1.4433 + script = frame.script(); 1.4434 + if (unsigned(i) < frame.numFormalArgs() && script->formalIsAliased(i)) { 1.4435 + for (AliasedFormalIter fi(script); ; fi++) { 1.4436 + if (fi.frameIndex() == unsigned(i)) { 1.4437 + arg = frame.callObj().aliasedVar(fi); 1.4438 + break; 1.4439 + } 1.4440 + } 1.4441 + } else if (script->argsObjAliasesFormals() && frame.hasArgsObj()) { 1.4442 + arg = frame.argsObj().arg(i); 1.4443 + } else { 1.4444 + arg = frame.unaliasedActual(i, DONT_CHECK_ALIASING); 1.4445 + } 1.4446 + } else { 1.4447 + arg.setUndefined(); 1.4448 + } 1.4449 + 1.4450 + if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg)) 1.4451 + return false; 1.4452 + args.rval().set(arg); 1.4453 + return true; 1.4454 +} 1.4455 + 1.4456 +static bool 1.4457 +DebuggerFrame_getArguments(JSContext *cx, unsigned argc, Value *vp) 1.4458 +{ 1.4459 + THIS_FRAME(cx, argc, vp, "get arguments", args, thisobj, frame); 1.4460 + Value argumentsv = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS); 1.4461 + if (!argumentsv.isUndefined()) { 1.4462 + JS_ASSERT(argumentsv.isObjectOrNull()); 1.4463 + args.rval().set(argumentsv); 1.4464 + return true; 1.4465 + } 1.4466 + 1.4467 + RootedObject argsobj(cx); 1.4468 + if (frame.hasArgs()) { 1.4469 + /* Create an arguments object. */ 1.4470 + Rooted<GlobalObject*> global(cx, &args.callee().global()); 1.4471 + JSObject *proto = GlobalObject::getOrCreateArrayPrototype(cx, global); 1.4472 + if (!proto) 1.4473 + return false; 1.4474 + argsobj = NewObjectWithGivenProto(cx, &DebuggerArguments_class, proto, global); 1.4475 + if (!argsobj) 1.4476 + return false; 1.4477 + SetReservedSlot(argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj)); 1.4478 + 1.4479 + JS_ASSERT(frame.numActualArgs() <= 0x7fffffff); 1.4480 + unsigned fargc = frame.numActualArgs(); 1.4481 + RootedValue fargcVal(cx, Int32Value(fargc)); 1.4482 + if (!DefineNativeProperty(cx, argsobj, cx->names().length, 1.4483 + fargcVal, nullptr, nullptr, 1.4484 + JSPROP_PERMANENT | JSPROP_READONLY)) 1.4485 + { 1.4486 + return false; 1.4487 + } 1.4488 + 1.4489 + Rooted<jsid> id(cx); 1.4490 + for (unsigned i = 0; i < fargc; i++) { 1.4491 + RootedFunction getobj(cx); 1.4492 + getobj = NewFunction(cx, js::NullPtr(), DebuggerArguments_getArg, 0, 1.4493 + JSFunction::NATIVE_FUN, global, js::NullPtr(), 1.4494 + JSFunction::ExtendedFinalizeKind); 1.4495 + if (!getobj) 1.4496 + return false; 1.4497 + id = INT_TO_JSID(i); 1.4498 + if (!getobj || 1.4499 + !DefineNativeProperty(cx, argsobj, id, UndefinedHandleValue, 1.4500 + JS_DATA_TO_FUNC_PTR(PropertyOp, getobj.get()), nullptr, 1.4501 + JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER)) 1.4502 + { 1.4503 + return false; 1.4504 + } 1.4505 + getobj->setExtendedSlot(0, Int32Value(i)); 1.4506 + } 1.4507 + } else { 1.4508 + argsobj = nullptr; 1.4509 + } 1.4510 + args.rval().setObjectOrNull(argsobj); 1.4511 + thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, args.rval()); 1.4512 + return true; 1.4513 +} 1.4514 + 1.4515 +static bool 1.4516 +DebuggerFrame_getScript(JSContext *cx, unsigned argc, Value *vp) 1.4517 +{ 1.4518 + THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frame); 1.4519 + Debugger *debug = Debugger::fromChildJSObject(thisobj); 1.4520 + 1.4521 + RootedObject scriptObject(cx); 1.4522 + if (frame.isFunctionFrame() && !frame.isEvalFrame()) { 1.4523 + RootedFunction callee(cx, frame.callee()); 1.4524 + if (callee->isInterpreted()) { 1.4525 + RootedScript script(cx, callee->nonLazyScript()); 1.4526 + scriptObject = debug->wrapScript(cx, script); 1.4527 + if (!scriptObject) 1.4528 + return false; 1.4529 + } 1.4530 + } else { 1.4531 + /* 1.4532 + * We got eval, JS_Evaluate*, or JS_ExecuteScript non-function script 1.4533 + * frames. 1.4534 + */ 1.4535 + RootedScript script(cx, frame.script()); 1.4536 + scriptObject = debug->wrapScript(cx, script); 1.4537 + if (!scriptObject) 1.4538 + return false; 1.4539 + } 1.4540 + args.rval().setObjectOrNull(scriptObject); 1.4541 + return true; 1.4542 +} 1.4543 + 1.4544 +static bool 1.4545 +DebuggerFrame_getOffset(JSContext *cx, unsigned argc, Value *vp) 1.4546 +{ 1.4547 + THIS_FRAME_ITER(cx, argc, vp, "get offset", args, thisobj, _, iter); 1.4548 + JSScript *script = iter.script(); 1.4549 + UpdateFrameIterPc(iter); 1.4550 + jsbytecode *pc = iter.pc(); 1.4551 + size_t offset = script->pcToOffset(pc); 1.4552 + args.rval().setNumber(double(offset)); 1.4553 + return true; 1.4554 +} 1.4555 + 1.4556 +static bool 1.4557 +DebuggerFrame_getLive(JSContext *cx, unsigned argc, Value *vp) 1.4558 +{ 1.4559 + CallArgs args = CallArgsFromVp(argc, vp); 1.4560 + JSObject *thisobj = CheckThisFrame(cx, args, "get live", false); 1.4561 + if (!thisobj) 1.4562 + return false; 1.4563 + bool hasFrame = !!thisobj->getPrivate(); 1.4564 + args.rval().setBoolean(hasFrame); 1.4565 + return true; 1.4566 +} 1.4567 + 1.4568 +static bool 1.4569 +IsValidHook(const Value &v) 1.4570 +{ 1.4571 + return v.isUndefined() || (v.isObject() && v.toObject().isCallable()); 1.4572 +} 1.4573 + 1.4574 +static bool 1.4575 +DebuggerFrame_getOnStep(JSContext *cx, unsigned argc, Value *vp) 1.4576 +{ 1.4577 + THIS_FRAME(cx, argc, vp, "get onStep", args, thisobj, frame); 1.4578 + (void) frame; // Silence GCC warning 1.4579 + Value handler = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER); 1.4580 + JS_ASSERT(IsValidHook(handler)); 1.4581 + args.rval().set(handler); 1.4582 + return true; 1.4583 +} 1.4584 + 1.4585 +static bool 1.4586 +DebuggerFrame_setOnStep(JSContext *cx, unsigned argc, Value *vp) 1.4587 +{ 1.4588 + REQUIRE_ARGC("Debugger.Frame.set onStep", 1); 1.4589 + THIS_FRAME(cx, argc, vp, "set onStep", args, thisobj, frame); 1.4590 + if (!IsValidHook(args[0])) { 1.4591 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED); 1.4592 + return false; 1.4593 + } 1.4594 + 1.4595 + Value prior = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER); 1.4596 + if (!args[0].isUndefined() && prior.isUndefined()) { 1.4597 + // Single stepping toggled off->on. 1.4598 + AutoCompartment ac(cx, frame.scopeChain()); 1.4599 + if (!frame.script()->incrementStepModeCount(cx)) 1.4600 + return false; 1.4601 + } else if (args[0].isUndefined() && !prior.isUndefined()) { 1.4602 + // Single stepping toggled on->off. 1.4603 + frame.script()->decrementStepModeCount(cx->runtime()->defaultFreeOp()); 1.4604 + } 1.4605 + 1.4606 + /* Now that the step mode switch has succeeded, we can install the handler. */ 1.4607 + thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER, args[0]); 1.4608 + args.rval().setUndefined(); 1.4609 + return true; 1.4610 +} 1.4611 + 1.4612 +static bool 1.4613 +DebuggerFrame_getOnPop(JSContext *cx, unsigned argc, Value *vp) 1.4614 +{ 1.4615 + THIS_FRAME(cx, argc, vp, "get onPop", args, thisobj, frame); 1.4616 + (void) frame; // Silence GCC warning 1.4617 + Value handler = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER); 1.4618 + JS_ASSERT(IsValidHook(handler)); 1.4619 + args.rval().set(handler); 1.4620 + return true; 1.4621 +} 1.4622 + 1.4623 +static bool 1.4624 +DebuggerFrame_setOnPop(JSContext *cx, unsigned argc, Value *vp) 1.4625 +{ 1.4626 + REQUIRE_ARGC("Debugger.Frame.set onPop", 1); 1.4627 + THIS_FRAME(cx, argc, vp, "set onPop", args, thisobj, frame); 1.4628 + (void) frame; // Silence GCC warning 1.4629 + if (!IsValidHook(args[0])) { 1.4630 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED); 1.4631 + return false; 1.4632 + } 1.4633 + 1.4634 + thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER, args[0]); 1.4635 + args.rval().setUndefined(); 1.4636 + return true; 1.4637 +} 1.4638 + 1.4639 +/* 1.4640 + * Evaluate |chars[0..length-1]| in the environment |env|, treating that 1.4641 + * source as appearing starting at |lineno| in |filename|. Store the return 1.4642 + * value in |*rval|. Use |thisv| as the 'this' value. 1.4643 + * 1.4644 + * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env| 1.4645 + * must be either |frame|'s DebugScopeObject, or some extension of that 1.4646 + * environment; either way, |frame|'s scope is where newly declared variables 1.4647 + * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|. 1.4648 + */ 1.4649 +bool 1.4650 +js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame, 1.4651 + ConstTwoByteChars chars, unsigned length, const char *filename, unsigned lineno, 1.4652 + MutableHandleValue rval) 1.4653 +{ 1.4654 + assertSameCompartment(cx, env, frame); 1.4655 + JS_ASSERT_IF(frame, thisv.get() == frame.thisValue()); 1.4656 + 1.4657 + JS_ASSERT(!IsPoisonedPtr(chars.get())); 1.4658 + 1.4659 + /* 1.4660 + * NB: This function breaks the assumption that the compiler can see all 1.4661 + * calls and properly compute a static level. In practice, any non-zero 1.4662 + * static level will suffice. 1.4663 + */ 1.4664 + CompileOptions options(cx); 1.4665 + options.setCompileAndGo(true) 1.4666 + .setForEval(true) 1.4667 + .setNoScriptRval(false) 1.4668 + .setFileAndLine(filename, lineno) 1.4669 + .setCanLazilyParse(false) 1.4670 + .setIntroductionType("debugger eval"); 1.4671 + RootedScript callerScript(cx, frame ? frame.script() : nullptr); 1.4672 + SourceBufferHolder srcBuf(chars.get(), length, SourceBufferHolder::NoOwnership); 1.4673 + RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, callerScript, 1.4674 + options, srcBuf, 1.4675 + /* source = */ nullptr, 1.4676 + /* staticLevel = */ frame ? 1 : 0)); 1.4677 + if (!script) 1.4678 + return false; 1.4679 + 1.4680 + script->setActiveEval(); 1.4681 + ExecuteType type = !frame ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG; 1.4682 + return ExecuteKernel(cx, script, *env, thisv, type, frame, rval.address()); 1.4683 +} 1.4684 + 1.4685 +enum EvalBindings { EvalHasExtraBindings = true, EvalWithDefaultBindings = false }; 1.4686 + 1.4687 +static bool 1.4688 +DebuggerGenericEval(JSContext *cx, const char *fullMethodName, const Value &code, 1.4689 + EvalBindings evalWithBindings, HandleValue bindings, HandleValue options, 1.4690 + MutableHandleValue vp, Debugger *dbg, HandleObject scope, 1.4691 + ScriptFrameIter *iter) 1.4692 +{ 1.4693 + /* Either we're specifying the frame, or a global. */ 1.4694 + JS_ASSERT_IF(iter, !scope); 1.4695 + JS_ASSERT_IF(!iter, scope && scope->is<GlobalObject>()); 1.4696 + 1.4697 + /* Check the first argument, the eval code string. */ 1.4698 + if (!code.isString()) { 1.4699 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, 1.4700 + fullMethodName, "string", InformalValueTypeName(code)); 1.4701 + return false; 1.4702 + } 1.4703 + Rooted<JSFlatString *> flat(cx, code.toString()->ensureFlat(cx)); 1.4704 + if (!flat) 1.4705 + return false; 1.4706 + 1.4707 + /* 1.4708 + * Gather keys and values of bindings, if any. This must be done in the 1.4709 + * debugger compartment, since that is where any exceptions must be 1.4710 + * thrown. 1.4711 + */ 1.4712 + AutoIdVector keys(cx); 1.4713 + AutoValueVector values(cx); 1.4714 + if (evalWithBindings) { 1.4715 + RootedObject bindingsobj(cx, NonNullObject(cx, bindings)); 1.4716 + if (!bindingsobj || 1.4717 + !GetPropertyNames(cx, bindingsobj, JSITER_OWNONLY, &keys) || 1.4718 + !values.growBy(keys.length())) 1.4719 + { 1.4720 + return false; 1.4721 + } 1.4722 + for (size_t i = 0; i < keys.length(); i++) { 1.4723 + MutableHandleValue valp = values.handleAt(i); 1.4724 + if (!JSObject::getGeneric(cx, bindingsobj, bindingsobj, keys.handleAt(i), valp) || 1.4725 + !dbg->unwrapDebuggeeValue(cx, valp)) 1.4726 + { 1.4727 + return false; 1.4728 + } 1.4729 + } 1.4730 + } 1.4731 + 1.4732 + /* Set options from object if provided. */ 1.4733 + JSAutoByteString url_bytes; 1.4734 + char *url = nullptr; 1.4735 + unsigned lineNumber = 1; 1.4736 + 1.4737 + if (options.isObject()) { 1.4738 + RootedObject opts(cx, &options.toObject()); 1.4739 + RootedValue v(cx); 1.4740 + 1.4741 + if (!JS_GetProperty(cx, opts, "url", &v)) 1.4742 + return false; 1.4743 + if (!v.isUndefined()) { 1.4744 + RootedString url_str(cx, ToString<CanGC>(cx, v)); 1.4745 + if (!url_str) 1.4746 + return false; 1.4747 + url = url_bytes.encodeLatin1(cx, url_str); 1.4748 + if (!url) 1.4749 + return false; 1.4750 + } 1.4751 + 1.4752 + if (!JS_GetProperty(cx, opts, "lineNumber", &v)) 1.4753 + return false; 1.4754 + if (!v.isUndefined()) { 1.4755 + uint32_t lineno; 1.4756 + if (!ToUint32(cx, v, &lineno)) 1.4757 + return false; 1.4758 + lineNumber = lineno; 1.4759 + } 1.4760 + } 1.4761 + 1.4762 + Maybe<AutoCompartment> ac; 1.4763 + if (iter) 1.4764 + ac.construct(cx, iter->scopeChain()); 1.4765 + else 1.4766 + ac.construct(cx, scope); 1.4767 + 1.4768 + RootedValue thisv(cx); 1.4769 + Rooted<Env *> env(cx); 1.4770 + if (iter) { 1.4771 + /* ExecuteInEnv requires 'fp' to have a computed 'this" value. */ 1.4772 + if (!iter->computeThis(cx)) 1.4773 + return false; 1.4774 + thisv = iter->computedThisValue(); 1.4775 + env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc()); 1.4776 + if (!env) 1.4777 + return false; 1.4778 + } else { 1.4779 + /* 1.4780 + * Use the global as 'this'. If the global is an inner object, it 1.4781 + * should have a thisObject hook that returns the appropriate outer 1.4782 + * object. 1.4783 + */ 1.4784 + JSObject *thisObj = JSObject::thisObject(cx, scope); 1.4785 + if (!thisObj) 1.4786 + return false; 1.4787 + thisv = ObjectValue(*thisObj); 1.4788 + env = scope; 1.4789 + } 1.4790 + 1.4791 + /* If evalWithBindings, create the inner environment. */ 1.4792 + if (evalWithBindings) { 1.4793 + /* TODO - This should probably be a Call object, like ES5 strict eval. */ 1.4794 + env = NewObjectWithGivenProto(cx, &JSObject::class_, nullptr, env); 1.4795 + if (!env) 1.4796 + return false; 1.4797 + RootedId id(cx); 1.4798 + for (size_t i = 0; i < keys.length(); i++) { 1.4799 + id = keys[i]; 1.4800 + MutableHandleValue val = values.handleAt(i); 1.4801 + if (!cx->compartment()->wrap(cx, val) || 1.4802 + !DefineNativeProperty(cx, env, id, val, nullptr, nullptr, 0)) 1.4803 + { 1.4804 + return false; 1.4805 + } 1.4806 + } 1.4807 + } 1.4808 + 1.4809 + /* Run the code and produce the completion value. */ 1.4810 + RootedValue rval(cx); 1.4811 + JS::Anchor<JSString *> anchor(flat); 1.4812 + AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr(); 1.4813 + bool ok = EvaluateInEnv(cx, env, thisv, frame, 1.4814 + ConstTwoByteChars(flat->chars(), flat->length()), 1.4815 + flat->length(), url ? url : "debugger eval code", lineNumber, &rval); 1.4816 + return dbg->receiveCompletionValue(ac, ok, rval, vp); 1.4817 +} 1.4818 + 1.4819 +static bool 1.4820 +DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp) 1.4821 +{ 1.4822 + THIS_FRAME_ITER(cx, argc, vp, "eval", args, thisobj, _, iter); 1.4823 + REQUIRE_ARGC("Debugger.Frame.prototype.eval", 1); 1.4824 + Debugger *dbg = Debugger::fromChildJSObject(thisobj); 1.4825 + UpdateFrameIterPc(iter); 1.4826 + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval", 1.4827 + args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, 1.4828 + args.get(1), args.rval(), dbg, js::NullPtr(), &iter); 1.4829 +} 1.4830 + 1.4831 +static bool 1.4832 +DebuggerFrame_evalWithBindings(JSContext *cx, unsigned argc, Value *vp) 1.4833 +{ 1.4834 + THIS_FRAME_ITER(cx, argc, vp, "evalWithBindings", args, thisobj, _, iter); 1.4835 + REQUIRE_ARGC("Debugger.Frame.prototype.evalWithBindings", 2); 1.4836 + Debugger *dbg = Debugger::fromChildJSObject(thisobj); 1.4837 + UpdateFrameIterPc(iter); 1.4838 + return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings", 1.4839 + args[0], EvalHasExtraBindings, args[1], args.get(2), 1.4840 + args.rval(), dbg, js::NullPtr(), &iter); 1.4841 +} 1.4842 + 1.4843 +static bool 1.4844 +DebuggerFrame_construct(JSContext *cx, unsigned argc, Value *vp) 1.4845 +{ 1.4846 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.4847 + "Debugger.Frame"); 1.4848 + return false; 1.4849 +} 1.4850 + 1.4851 +static const JSPropertySpec DebuggerFrame_properties[] = { 1.4852 + JS_PSG("arguments", DebuggerFrame_getArguments, 0), 1.4853 + JS_PSG("callee", DebuggerFrame_getCallee, 0), 1.4854 + JS_PSG("constructing", DebuggerFrame_getConstructing, 0), 1.4855 + JS_PSG("environment", DebuggerFrame_getEnvironment, 0), 1.4856 + JS_PSG("generator", DebuggerFrame_getGenerator, 0), 1.4857 + JS_PSG("live", DebuggerFrame_getLive, 0), 1.4858 + JS_PSG("offset", DebuggerFrame_getOffset, 0), 1.4859 + JS_PSG("older", DebuggerFrame_getOlder, 0), 1.4860 + JS_PSG("script", DebuggerFrame_getScript, 0), 1.4861 + JS_PSG("this", DebuggerFrame_getThis, 0), 1.4862 + JS_PSG("type", DebuggerFrame_getType, 0), 1.4863 + JS_PSG("implementation", DebuggerFrame_getImplementation, 0), 1.4864 + JS_PSGS("onStep", DebuggerFrame_getOnStep, DebuggerFrame_setOnStep, 0), 1.4865 + JS_PSGS("onPop", DebuggerFrame_getOnPop, DebuggerFrame_setOnPop, 0), 1.4866 + JS_PS_END 1.4867 +}; 1.4868 + 1.4869 +static const JSFunctionSpec DebuggerFrame_methods[] = { 1.4870 + JS_FN("eval", DebuggerFrame_eval, 1, 0), 1.4871 + JS_FN("evalWithBindings", DebuggerFrame_evalWithBindings, 1, 0), 1.4872 + JS_FS_END 1.4873 +}; 1.4874 + 1.4875 + 1.4876 +/*** Debugger.Object *****************************************************************************/ 1.4877 + 1.4878 +static void 1.4879 +DebuggerObject_trace(JSTracer *trc, JSObject *obj) 1.4880 +{ 1.4881 + /* 1.4882 + * There is a barrier on private pointers, so the Unbarriered marking 1.4883 + * is okay. 1.4884 + */ 1.4885 + if (JSObject *referent = (JSObject *) obj->getPrivate()) { 1.4886 + MarkCrossCompartmentObjectUnbarriered(trc, obj, &referent, "Debugger.Object referent"); 1.4887 + obj->setPrivateUnbarriered(referent); 1.4888 + } 1.4889 +} 1.4890 + 1.4891 +const Class DebuggerObject_class = { 1.4892 + "Object", 1.4893 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.4894 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), 1.4895 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.4896 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.4897 + nullptr, /* call */ 1.4898 + nullptr, /* hasInstance */ 1.4899 + nullptr, /* construct */ 1.4900 + DebuggerObject_trace 1.4901 +}; 1.4902 + 1.4903 +static JSObject * 1.4904 +DebuggerObject_checkThis(JSContext *cx, const CallArgs &args, const char *fnname) 1.4905 +{ 1.4906 + if (!args.thisv().isObject()) { 1.4907 + ReportObjectRequired(cx); 1.4908 + return nullptr; 1.4909 + } 1.4910 + JSObject *thisobj = &args.thisv().toObject(); 1.4911 + if (thisobj->getClass() != &DebuggerObject_class) { 1.4912 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.4913 + "Debugger.Object", fnname, thisobj->getClass()->name); 1.4914 + return nullptr; 1.4915 + } 1.4916 + 1.4917 + /* 1.4918 + * Forbid Debugger.Object.prototype, which is of class DebuggerObject_class 1.4919 + * but isn't a real working Debugger.Object. The prototype object is 1.4920 + * distinguished by having no referent. 1.4921 + */ 1.4922 + if (!thisobj->getPrivate()) { 1.4923 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.4924 + "Debugger.Object", fnname, "prototype object"); 1.4925 + return nullptr; 1.4926 + } 1.4927 + return thisobj; 1.4928 +} 1.4929 + 1.4930 +#define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj) \ 1.4931 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.4932 + RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ 1.4933 + if (!obj) \ 1.4934 + return false; \ 1.4935 + obj = (JSObject *) obj->getPrivate(); \ 1.4936 + JS_ASSERT(obj) 1.4937 + 1.4938 +#define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \ 1.4939 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.4940 + RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname)); \ 1.4941 + if (!obj) \ 1.4942 + return false; \ 1.4943 + Debugger *dbg = Debugger::fromChildJSObject(obj); \ 1.4944 + obj = (JSObject *) obj->getPrivate(); \ 1.4945 + JS_ASSERT(obj) 1.4946 + 1.4947 +static bool 1.4948 +DebuggerObject_construct(JSContext *cx, unsigned argc, Value *vp) 1.4949 +{ 1.4950 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.4951 + "Debugger.Object"); 1.4952 + return false; 1.4953 +} 1.4954 + 1.4955 +static bool 1.4956 +DebuggerObject_getProto(JSContext *cx, unsigned argc, Value *vp) 1.4957 +{ 1.4958 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get proto", args, dbg, refobj); 1.4959 + RootedObject proto(cx); 1.4960 + { 1.4961 + AutoCompartment ac(cx, refobj); 1.4962 + if (!JSObject::getProto(cx, refobj, &proto)) 1.4963 + return false; 1.4964 + } 1.4965 + RootedValue protov(cx, ObjectOrNullValue(proto)); 1.4966 + if (!dbg->wrapDebuggeeValue(cx, &protov)) 1.4967 + return false; 1.4968 + args.rval().set(protov); 1.4969 + return true; 1.4970 +} 1.4971 + 1.4972 +static bool 1.4973 +DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp) 1.4974 +{ 1.4975 + THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj); 1.4976 + const char *className; 1.4977 + { 1.4978 + AutoCompartment ac(cx, refobj); 1.4979 + className = JSObject::className(cx, refobj); 1.4980 + } 1.4981 + JSAtom *str = Atomize(cx, className, strlen(className)); 1.4982 + if (!str) 1.4983 + return false; 1.4984 + args.rval().setString(str); 1.4985 + return true; 1.4986 +} 1.4987 + 1.4988 +static bool 1.4989 +DebuggerObject_getCallable(JSContext *cx, unsigned argc, Value *vp) 1.4990 +{ 1.4991 + THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get callable", args, refobj); 1.4992 + args.rval().setBoolean(refobj->isCallable()); 1.4993 + return true; 1.4994 +} 1.4995 + 1.4996 +static bool 1.4997 +DebuggerObject_getName(JSContext *cx, unsigned argc, Value *vp) 1.4998 +{ 1.4999 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get name", args, dbg, obj); 1.5000 + if (!obj->is<JSFunction>()) { 1.5001 + args.rval().setUndefined(); 1.5002 + return true; 1.5003 + } 1.5004 + 1.5005 + JSString *name = obj->as<JSFunction>().atom(); 1.5006 + if (!name) { 1.5007 + args.rval().setUndefined(); 1.5008 + return true; 1.5009 + } 1.5010 + 1.5011 + RootedValue namev(cx, StringValue(name)); 1.5012 + if (!dbg->wrapDebuggeeValue(cx, &namev)) 1.5013 + return false; 1.5014 + args.rval().set(namev); 1.5015 + return true; 1.5016 +} 1.5017 + 1.5018 +static bool 1.5019 +DebuggerObject_getDisplayName(JSContext *cx, unsigned argc, Value *vp) 1.5020 +{ 1.5021 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get display name", args, dbg, obj); 1.5022 + if (!obj->is<JSFunction>()) { 1.5023 + args.rval().setUndefined(); 1.5024 + return true; 1.5025 + } 1.5026 + 1.5027 + JSString *name = obj->as<JSFunction>().displayAtom(); 1.5028 + if (!name) { 1.5029 + args.rval().setUndefined(); 1.5030 + return true; 1.5031 + } 1.5032 + 1.5033 + RootedValue namev(cx, StringValue(name)); 1.5034 + if (!dbg->wrapDebuggeeValue(cx, &namev)) 1.5035 + return false; 1.5036 + args.rval().set(namev); 1.5037 + return true; 1.5038 +} 1.5039 + 1.5040 +static bool 1.5041 +DebuggerObject_getParameterNames(JSContext *cx, unsigned argc, Value *vp) 1.5042 +{ 1.5043 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get parameterNames", args, dbg, obj); 1.5044 + if (!obj->is<JSFunction>()) { 1.5045 + args.rval().setUndefined(); 1.5046 + return true; 1.5047 + } 1.5048 + 1.5049 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.5050 + 1.5051 + /* Only hand out parameter info for debuggee functions. */ 1.5052 + if (!dbg->observesGlobal(&fun->global())) { 1.5053 + args.rval().setUndefined(); 1.5054 + return true; 1.5055 + } 1.5056 + 1.5057 + RootedObject result(cx, NewDenseAllocatedArray(cx, fun->nargs())); 1.5058 + if (!result) 1.5059 + return false; 1.5060 + result->ensureDenseInitializedLength(cx, 0, fun->nargs()); 1.5061 + 1.5062 + if (fun->isInterpreted()) { 1.5063 + RootedScript script(cx, GetOrCreateFunctionScript(cx, fun)); 1.5064 + if (!script) 1.5065 + return false; 1.5066 + 1.5067 + JS_ASSERT(fun->nargs() == script->bindings.numArgs()); 1.5068 + 1.5069 + if (fun->nargs() > 0) { 1.5070 + BindingVector bindings(cx); 1.5071 + if (!FillBindingVector(script, &bindings)) 1.5072 + return false; 1.5073 + for (size_t i = 0; i < fun->nargs(); i++) { 1.5074 + Value v; 1.5075 + if (bindings[i].name()->length() == 0) 1.5076 + v = UndefinedValue(); 1.5077 + else 1.5078 + v = StringValue(bindings[i].name()); 1.5079 + result->setDenseElement(i, v); 1.5080 + } 1.5081 + } 1.5082 + } else { 1.5083 + for (size_t i = 0; i < fun->nargs(); i++) 1.5084 + result->setDenseElement(i, UndefinedValue()); 1.5085 + } 1.5086 + 1.5087 + args.rval().setObject(*result); 1.5088 + return true; 1.5089 +} 1.5090 + 1.5091 +static bool 1.5092 +DebuggerObject_getScript(JSContext *cx, unsigned argc, Value *vp) 1.5093 +{ 1.5094 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj); 1.5095 + 1.5096 + if (!obj->is<JSFunction>()) { 1.5097 + args.rval().setUndefined(); 1.5098 + return true; 1.5099 + } 1.5100 + 1.5101 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.5102 + if (fun->isBuiltin()) { 1.5103 + args.rval().setUndefined(); 1.5104 + return true; 1.5105 + } 1.5106 + 1.5107 + RootedScript script(cx, GetOrCreateFunctionScript(cx, fun)); 1.5108 + if (!script) 1.5109 + return false; 1.5110 + 1.5111 + /* Only hand out debuggee scripts. */ 1.5112 + if (!dbg->observesScript(script)) { 1.5113 + args.rval().setNull(); 1.5114 + return true; 1.5115 + } 1.5116 + 1.5117 + RootedObject scriptObject(cx, dbg->wrapScript(cx, script)); 1.5118 + if (!scriptObject) 1.5119 + return false; 1.5120 + 1.5121 + args.rval().setObject(*scriptObject); 1.5122 + return true; 1.5123 +} 1.5124 + 1.5125 +static bool 1.5126 +DebuggerObject_getEnvironment(JSContext *cx, unsigned argc, Value *vp) 1.5127 +{ 1.5128 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get environment", args, dbg, obj); 1.5129 + 1.5130 + /* Don't bother switching compartments just to check obj's type and get its env. */ 1.5131 + if (!obj->is<JSFunction>() || !obj->as<JSFunction>().isInterpreted()) { 1.5132 + args.rval().setUndefined(); 1.5133 + return true; 1.5134 + } 1.5135 + 1.5136 + /* Only hand out environments of debuggee functions. */ 1.5137 + if (!dbg->observesGlobal(&obj->global())) { 1.5138 + args.rval().setNull(); 1.5139 + return true; 1.5140 + } 1.5141 + 1.5142 + Rooted<Env*> env(cx); 1.5143 + { 1.5144 + AutoCompartment ac(cx, obj); 1.5145 + RootedFunction fun(cx, &obj->as<JSFunction>()); 1.5146 + env = GetDebugScopeForFunction(cx, fun); 1.5147 + if (!env) 1.5148 + return false; 1.5149 + } 1.5150 + 1.5151 + return dbg->wrapEnvironment(cx, env, args.rval()); 1.5152 +} 1.5153 + 1.5154 +static bool 1.5155 +DebuggerObject_getGlobal(JSContext *cx, unsigned argc, Value *vp) 1.5156 +{ 1.5157 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get global", args, dbg, obj); 1.5158 + 1.5159 + RootedValue v(cx, ObjectValue(obj->global())); 1.5160 + if (!dbg->wrapDebuggeeValue(cx, &v)) 1.5161 + return false; 1.5162 + args.rval().set(v); 1.5163 + return true; 1.5164 +} 1.5165 + 1.5166 +static bool 1.5167 +DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp) 1.5168 +{ 1.5169 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyDescriptor", args, dbg, obj); 1.5170 + 1.5171 + RootedId id(cx); 1.5172 + if (!ValueToId<CanGC>(cx, args.get(0), &id)) 1.5173 + return false; 1.5174 + 1.5175 + /* Bug: This can cause the debuggee to run! */ 1.5176 + Rooted<PropertyDescriptor> desc(cx); 1.5177 + { 1.5178 + Maybe<AutoCompartment> ac; 1.5179 + ac.construct(cx, obj); 1.5180 + if (!cx->compartment()->wrapId(cx, id.address())) 1.5181 + return false; 1.5182 + 1.5183 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5184 + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) 1.5185 + return false; 1.5186 + } 1.5187 + 1.5188 + if (desc.object()) { 1.5189 + /* Rewrap the debuggee values in desc for the debugger. */ 1.5190 + if (!dbg->wrapDebuggeeValue(cx, desc.value())) 1.5191 + return false; 1.5192 + 1.5193 + if (desc.hasGetterObject()) { 1.5194 + RootedValue get(cx, ObjectOrNullValue(desc.getterObject())); 1.5195 + if (!dbg->wrapDebuggeeValue(cx, &get)) 1.5196 + return false; 1.5197 + desc.setGetterObject(get.toObjectOrNull()); 1.5198 + } 1.5199 + if (desc.hasSetterObject()) { 1.5200 + RootedValue set(cx, ObjectOrNullValue(desc.setterObject())); 1.5201 + if (!dbg->wrapDebuggeeValue(cx, &set)) 1.5202 + return false; 1.5203 + desc.setSetterObject(set.toObjectOrNull()); 1.5204 + } 1.5205 + } 1.5206 + 1.5207 + return NewPropertyDescriptorObject(cx, desc, args.rval()); 1.5208 +} 1.5209 + 1.5210 +static bool 1.5211 +DebuggerObject_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp) 1.5212 +{ 1.5213 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "getOwnPropertyNames", args, dbg, obj); 1.5214 + 1.5215 + AutoIdVector keys(cx); 1.5216 + { 1.5217 + Maybe<AutoCompartment> ac; 1.5218 + ac.construct(cx, obj); 1.5219 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5220 + if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys)) 1.5221 + return false; 1.5222 + } 1.5223 + 1.5224 + AutoValueVector vals(cx); 1.5225 + if (!vals.resize(keys.length())) 1.5226 + return false; 1.5227 + 1.5228 + for (size_t i = 0, len = keys.length(); i < len; i++) { 1.5229 + jsid id = keys[i]; 1.5230 + if (JSID_IS_INT(id)) { 1.5231 + JSString *str = Int32ToString<CanGC>(cx, JSID_TO_INT(id)); 1.5232 + if (!str) 1.5233 + return false; 1.5234 + vals[i].setString(str); 1.5235 + } else if (JSID_IS_ATOM(id)) { 1.5236 + vals[i].setString(JSID_TO_STRING(id)); 1.5237 + if (!cx->compartment()->wrap(cx, vals.handleAt(i))) 1.5238 + return false; 1.5239 + } else { 1.5240 + vals[i].setObject(*JSID_TO_OBJECT(id)); 1.5241 + if (!dbg->wrapDebuggeeValue(cx, vals.handleAt(i))) 1.5242 + return false; 1.5243 + } 1.5244 + } 1.5245 + 1.5246 + JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin()); 1.5247 + if (!aobj) 1.5248 + return false; 1.5249 + args.rval().setObject(*aobj); 1.5250 + return true; 1.5251 +} 1.5252 + 1.5253 +static bool 1.5254 +DebuggerObject_defineProperty(JSContext *cx, unsigned argc, Value *vp) 1.5255 +{ 1.5256 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj); 1.5257 + REQUIRE_ARGC("Debugger.Object.defineProperty", 2); 1.5258 + 1.5259 + RootedId id(cx); 1.5260 + if (!ValueToId<CanGC>(cx, args[0], &id)) 1.5261 + return false; 1.5262 + 1.5263 + AutoPropDescArrayRooter descs(cx); 1.5264 + if (!descs.reserve(3)) // desc, unwrappedDesc, rewrappedDesc 1.5265 + return false; 1.5266 + PropDesc *desc = descs.append(); 1.5267 + if (!desc || !desc->initialize(cx, args[1], false)) 1.5268 + return false; 1.5269 + desc->clearPd(); 1.5270 + 1.5271 + PropDesc *unwrappedDesc = descs.append(); 1.5272 + if (!unwrappedDesc || !desc->unwrapDebuggerObjectsInto(cx, dbg, obj, unwrappedDesc)) 1.5273 + return false; 1.5274 + if (!unwrappedDesc->checkGetter(cx) || !unwrappedDesc->checkSetter(cx)) 1.5275 + return false; 1.5276 + 1.5277 + { 1.5278 + PropDesc *rewrappedDesc = descs.append(); 1.5279 + if (!rewrappedDesc) 1.5280 + return false; 1.5281 + RootedId wrappedId(cx); 1.5282 + 1.5283 + Maybe<AutoCompartment> ac; 1.5284 + ac.construct(cx, obj); 1.5285 + if (!unwrappedDesc->wrapInto(cx, obj, id, wrappedId.address(), rewrappedDesc)) 1.5286 + return false; 1.5287 + 1.5288 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5289 + bool dummy; 1.5290 + if (!DefineProperty(cx, obj, wrappedId, *rewrappedDesc, true, &dummy)) 1.5291 + return false; 1.5292 + } 1.5293 + 1.5294 + args.rval().setUndefined(); 1.5295 + return true; 1.5296 +} 1.5297 + 1.5298 +static bool 1.5299 +DebuggerObject_defineProperties(JSContext *cx, unsigned argc, Value *vp) 1.5300 +{ 1.5301 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperties", args, dbg, obj); 1.5302 + REQUIRE_ARGC("Debugger.Object.defineProperties", 1); 1.5303 + 1.5304 + RootedValue arg(cx, args[0]); 1.5305 + RootedObject props(cx, ToObject(cx, arg)); 1.5306 + if (!props) 1.5307 + return false; 1.5308 + 1.5309 + AutoIdVector ids(cx); 1.5310 + AutoPropDescArrayRooter descs(cx); 1.5311 + if (!ReadPropertyDescriptors(cx, props, false, &ids, &descs)) 1.5312 + return false; 1.5313 + size_t n = ids.length(); 1.5314 + 1.5315 + AutoPropDescArrayRooter unwrappedDescs(cx); 1.5316 + for (size_t i = 0; i < n; i++) { 1.5317 + if (!unwrappedDescs.append()) 1.5318 + return false; 1.5319 + if (!descs[i].unwrapDebuggerObjectsInto(cx, dbg, obj, &unwrappedDescs[i])) 1.5320 + return false; 1.5321 + if (!unwrappedDescs[i].checkGetter(cx) || !unwrappedDescs[i].checkSetter(cx)) 1.5322 + return false; 1.5323 + } 1.5324 + 1.5325 + { 1.5326 + AutoIdVector rewrappedIds(cx); 1.5327 + AutoPropDescArrayRooter rewrappedDescs(cx); 1.5328 + 1.5329 + Maybe<AutoCompartment> ac; 1.5330 + ac.construct(cx, obj); 1.5331 + RootedId id(cx); 1.5332 + for (size_t i = 0; i < n; i++) { 1.5333 + if (!rewrappedIds.append(JSID_VOID) || !rewrappedDescs.append()) 1.5334 + return false; 1.5335 + id = ids[i]; 1.5336 + if (!unwrappedDescs[i].wrapInto(cx, obj, id, &rewrappedIds[i], &rewrappedDescs[i])) 1.5337 + return false; 1.5338 + } 1.5339 + 1.5340 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5341 + for (size_t i = 0; i < n; i++) { 1.5342 + bool dummy; 1.5343 + if (!DefineProperty(cx, obj, rewrappedIds.handleAt(i), 1.5344 + rewrappedDescs[i], true, &dummy)) 1.5345 + { 1.5346 + return false; 1.5347 + } 1.5348 + } 1.5349 + } 1.5350 + 1.5351 + args.rval().setUndefined(); 1.5352 + return true; 1.5353 +} 1.5354 + 1.5355 +/* 1.5356 + * This does a non-strict delete, as a matter of API design. The case where the 1.5357 + * property is non-configurable isn't necessarily exceptional here. 1.5358 + */ 1.5359 +static bool 1.5360 +DebuggerObject_deleteProperty(JSContext *cx, unsigned argc, Value *vp) 1.5361 +{ 1.5362 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "deleteProperty", args, dbg, obj); 1.5363 + RootedValue nameArg(cx, args.get(0)); 1.5364 + 1.5365 + Maybe<AutoCompartment> ac; 1.5366 + ac.construct(cx, obj); 1.5367 + if (!cx->compartment()->wrap(cx, &nameArg)) 1.5368 + return false; 1.5369 + 1.5370 + bool succeeded; 1.5371 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5372 + if (!JSObject::deleteByValue(cx, obj, nameArg, &succeeded)) 1.5373 + return false; 1.5374 + args.rval().setBoolean(succeeded); 1.5375 + return true; 1.5376 +} 1.5377 + 1.5378 +enum SealHelperOp { Seal, Freeze, PreventExtensions }; 1.5379 + 1.5380 +static bool 1.5381 +DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op, const char *name) 1.5382 +{ 1.5383 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, name, args, dbg, obj); 1.5384 + 1.5385 + Maybe<AutoCompartment> ac; 1.5386 + ac.construct(cx, obj); 1.5387 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5388 + bool ok; 1.5389 + if (op == Seal) { 1.5390 + ok = JSObject::seal(cx, obj); 1.5391 + } else if (op == Freeze) { 1.5392 + ok = JSObject::freeze(cx, obj); 1.5393 + } else { 1.5394 + JS_ASSERT(op == PreventExtensions); 1.5395 + bool extensible; 1.5396 + if (!JSObject::isExtensible(cx, obj, &extensible)) 1.5397 + return false; 1.5398 + if (!extensible) { 1.5399 + args.rval().setUndefined(); 1.5400 + return true; 1.5401 + } 1.5402 + ok = JSObject::preventExtensions(cx, obj); 1.5403 + } 1.5404 + if (!ok) 1.5405 + return false; 1.5406 + args.rval().setUndefined(); 1.5407 + return true; 1.5408 +} 1.5409 + 1.5410 +static bool 1.5411 +DebuggerObject_seal(JSContext *cx, unsigned argc, Value *vp) 1.5412 +{ 1.5413 + return DebuggerObject_sealHelper(cx, argc, vp, Seal, "seal"); 1.5414 +} 1.5415 + 1.5416 +static bool 1.5417 +DebuggerObject_freeze(JSContext *cx, unsigned argc, Value *vp) 1.5418 +{ 1.5419 + return DebuggerObject_sealHelper(cx, argc, vp, Freeze, "freeze"); 1.5420 +} 1.5421 + 1.5422 +static bool 1.5423 +DebuggerObject_preventExtensions(JSContext *cx, unsigned argc, Value *vp) 1.5424 +{ 1.5425 + return DebuggerObject_sealHelper(cx, argc, vp, PreventExtensions, "preventExtensions"); 1.5426 +} 1.5427 + 1.5428 +static bool 1.5429 +DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op, 1.5430 + const char *name) 1.5431 +{ 1.5432 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, name, args, dbg, obj); 1.5433 + 1.5434 + Maybe<AutoCompartment> ac; 1.5435 + ac.construct(cx, obj); 1.5436 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5437 + bool r; 1.5438 + if (op == Seal) { 1.5439 + if (!JSObject::isSealed(cx, obj, &r)) 1.5440 + return false; 1.5441 + } else if (op == Freeze) { 1.5442 + if (!JSObject::isFrozen(cx, obj, &r)) 1.5443 + return false; 1.5444 + } else { 1.5445 + if (!JSObject::isExtensible(cx, obj, &r)) 1.5446 + return false; 1.5447 + } 1.5448 + args.rval().setBoolean(r); 1.5449 + return true; 1.5450 +} 1.5451 + 1.5452 +static bool 1.5453 +DebuggerObject_isSealed(JSContext *cx, unsigned argc, Value *vp) 1.5454 +{ 1.5455 + return DebuggerObject_isSealedHelper(cx, argc, vp, Seal, "isSealed"); 1.5456 +} 1.5457 + 1.5458 +static bool 1.5459 +DebuggerObject_isFrozen(JSContext *cx, unsigned argc, Value *vp) 1.5460 +{ 1.5461 + return DebuggerObject_isSealedHelper(cx, argc, vp, Freeze, "isFrozen"); 1.5462 +} 1.5463 + 1.5464 +static bool 1.5465 +DebuggerObject_isExtensible(JSContext *cx, unsigned argc, Value *vp) 1.5466 +{ 1.5467 + return DebuggerObject_isSealedHelper(cx, argc, vp, PreventExtensions, "isExtensible"); 1.5468 +} 1.5469 + 1.5470 +enum ApplyOrCallMode { ApplyMode, CallMode }; 1.5471 + 1.5472 +static bool 1.5473 +ApplyOrCall(JSContext *cx, unsigned argc, Value *vp, ApplyOrCallMode mode) 1.5474 +{ 1.5475 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "apply", args, dbg, obj); 1.5476 + 1.5477 + /* 1.5478 + * Any JS exceptions thrown must be in the debugger compartment, so do 1.5479 + * sanity checks and fallible conversions before entering the debuggee. 1.5480 + */ 1.5481 + RootedValue calleev(cx, ObjectValue(*obj)); 1.5482 + if (!obj->isCallable()) { 1.5483 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.5484 + "Debugger.Object", "apply", obj->getClass()->name); 1.5485 + return false; 1.5486 + } 1.5487 + 1.5488 + /* 1.5489 + * Unwrap Debugger.Objects. This happens in the debugger's compartment since 1.5490 + * that is where any exceptions must be reported. 1.5491 + */ 1.5492 + RootedValue thisv(cx, args.get(0)); 1.5493 + if (!dbg->unwrapDebuggeeValue(cx, &thisv)) 1.5494 + return false; 1.5495 + unsigned callArgc = 0; 1.5496 + Value *callArgv = nullptr; 1.5497 + AutoValueVector argv(cx); 1.5498 + if (mode == ApplyMode) { 1.5499 + if (args.length() >= 2 && !args[1].isNullOrUndefined()) { 1.5500 + if (!args[1].isObject()) { 1.5501 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_APPLY_ARGS, 1.5502 + js_apply_str); 1.5503 + return false; 1.5504 + } 1.5505 + RootedObject argsobj(cx, &args[1].toObject()); 1.5506 + if (!GetLengthProperty(cx, argsobj, &callArgc)) 1.5507 + return false; 1.5508 + callArgc = unsigned(Min(callArgc, ARGS_LENGTH_MAX)); 1.5509 + if (!argv.growBy(callArgc) || !GetElements(cx, argsobj, callArgc, argv.begin())) 1.5510 + return false; 1.5511 + callArgv = argv.begin(); 1.5512 + } 1.5513 + } else { 1.5514 + callArgc = args.length() > 0 ? unsigned(Min(args.length() - 1, ARGS_LENGTH_MAX)) : 0; 1.5515 + callArgv = args.array() + 1; 1.5516 + } 1.5517 + 1.5518 + AutoArrayRooter callArgvRooter(cx, callArgc, callArgv); 1.5519 + for (unsigned i = 0; i < callArgc; i++) { 1.5520 + if (!dbg->unwrapDebuggeeValue(cx, callArgvRooter.handleAt(i))) 1.5521 + return false; 1.5522 + } 1.5523 + 1.5524 + /* 1.5525 + * Enter the debuggee compartment and rewrap all input value for that compartment. 1.5526 + * (Rewrapping always takes place in the destination compartment.) 1.5527 + */ 1.5528 + Maybe<AutoCompartment> ac; 1.5529 + ac.construct(cx, obj); 1.5530 + if (!cx->compartment()->wrap(cx, &calleev) || !cx->compartment()->wrap(cx, &thisv)) 1.5531 + return false; 1.5532 + 1.5533 + RootedValue arg(cx); 1.5534 + for (unsigned i = 0; i < callArgc; i++) { 1.5535 + if (!cx->compartment()->wrap(cx, callArgvRooter.handleAt(i))) 1.5536 + return false; 1.5537 + } 1.5538 + 1.5539 + /* 1.5540 + * Call the function. Use receiveCompletionValue to return to the debugger 1.5541 + * compartment and populate args.rval(). 1.5542 + */ 1.5543 + RootedValue rval(cx); 1.5544 + bool ok = Invoke(cx, thisv, calleev, callArgc, callArgv, &rval); 1.5545 + return dbg->receiveCompletionValue(ac, ok, rval, args.rval()); 1.5546 +} 1.5547 + 1.5548 +static bool 1.5549 +DebuggerObject_apply(JSContext *cx, unsigned argc, Value *vp) 1.5550 +{ 1.5551 + return ApplyOrCall(cx, argc, vp, ApplyMode); 1.5552 +} 1.5553 + 1.5554 +static bool 1.5555 +DebuggerObject_call(JSContext *cx, unsigned argc, Value *vp) 1.5556 +{ 1.5557 + return ApplyOrCall(cx, argc, vp, CallMode); 1.5558 +} 1.5559 + 1.5560 +static bool 1.5561 +DebuggerObject_makeDebuggeeValue(JSContext *cx, unsigned argc, Value *vp) 1.5562 +{ 1.5563 + REQUIRE_ARGC("Debugger.Object.prototype.makeDebuggeeValue", 1); 1.5564 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "makeDebuggeeValue", args, dbg, referent); 1.5565 + 1.5566 + RootedValue arg0(cx, args[0]); 1.5567 + 1.5568 + /* Non-objects are already debuggee values. */ 1.5569 + if (arg0.isObject()) { 1.5570 + // Enter this Debugger.Object's referent's compartment, and wrap the 1.5571 + // argument as appropriate for references from there. 1.5572 + { 1.5573 + AutoCompartment ac(cx, referent); 1.5574 + if (!cx->compartment()->wrap(cx, &arg0)) 1.5575 + return false; 1.5576 + } 1.5577 + 1.5578 + // Back in the debugger's compartment, produce a new Debugger.Object 1.5579 + // instance referring to the wrapped argument. 1.5580 + if (!dbg->wrapDebuggeeValue(cx, &arg0)) 1.5581 + return false; 1.5582 + } 1.5583 + 1.5584 + args.rval().set(arg0); 1.5585 + return true; 1.5586 +} 1.5587 + 1.5588 +static bool 1.5589 +RequireGlobalObject(JSContext *cx, HandleValue dbgobj, HandleObject referent) 1.5590 +{ 1.5591 + RootedObject obj(cx, referent); 1.5592 + 1.5593 + if (!obj->is<GlobalObject>()) { 1.5594 + const char *isWrapper = ""; 1.5595 + const char *isWindowProxy = ""; 1.5596 + 1.5597 + /* Help the poor programmer by pointing out wrappers around globals... */ 1.5598 + if (obj->is<WrapperObject>()) { 1.5599 + obj = js::UncheckedUnwrap(obj); 1.5600 + isWrapper = "a wrapper around "; 1.5601 + } 1.5602 + 1.5603 + /* ... and WindowProxies around Windows. */ 1.5604 + if (IsOuterObject(obj)) { 1.5605 + obj = JS_ObjectToInnerObject(cx, obj); 1.5606 + isWindowProxy = "a WindowProxy referring to "; 1.5607 + } 1.5608 + 1.5609 + if (obj->is<GlobalObject>()) { 1.5610 + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_WRAPPER_IN_WAY, 1.5611 + JSDVG_SEARCH_STACK, dbgobj, js::NullPtr(), 1.5612 + isWrapper, isWindowProxy); 1.5613 + } else { 1.5614 + js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_DEBUG_BAD_REFERENT, 1.5615 + JSDVG_SEARCH_STACK, dbgobj, js::NullPtr(), 1.5616 + "a global object", nullptr); 1.5617 + } 1.5618 + return false; 1.5619 + } 1.5620 + 1.5621 + return true; 1.5622 +} 1.5623 + 1.5624 +static bool 1.5625 +DebuggerObject_evalInGlobal(JSContext *cx, unsigned argc, Value *vp) 1.5626 +{ 1.5627 + REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobal", 1); 1.5628 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobal", args, dbg, referent); 1.5629 + if (!RequireGlobalObject(cx, args.thisv(), referent)) 1.5630 + return false; 1.5631 + 1.5632 + return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobal", 1.5633 + args[0], EvalWithDefaultBindings, JS::UndefinedHandleValue, 1.5634 + args.get(1), args.rval(), dbg, referent, nullptr); 1.5635 +} 1.5636 + 1.5637 +static bool 1.5638 +DebuggerObject_evalInGlobalWithBindings(JSContext *cx, unsigned argc, Value *vp) 1.5639 +{ 1.5640 + REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobalWithBindings", 2); 1.5641 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobalWithBindings", args, dbg, referent); 1.5642 + if (!RequireGlobalObject(cx, args.thisv(), referent)) 1.5643 + return false; 1.5644 + 1.5645 + return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobalWithBindings", 1.5646 + args[0], EvalHasExtraBindings, args[1], args.get(2), 1.5647 + args.rval(), dbg, referent, nullptr); 1.5648 +} 1.5649 + 1.5650 +static bool 1.5651 +DebuggerObject_unwrap(JSContext *cx, unsigned argc, Value *vp) 1.5652 +{ 1.5653 + THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "unwrap", args, dbg, referent); 1.5654 + JSObject *unwrapped = UnwrapOneChecked(referent); 1.5655 + if (!unwrapped) { 1.5656 + args.rval().setNull(); 1.5657 + return true; 1.5658 + } 1.5659 + 1.5660 + args.rval().setObject(*unwrapped); 1.5661 + if (!dbg->wrapDebuggeeValue(cx, args.rval())) 1.5662 + return false; 1.5663 + return true; 1.5664 +} 1.5665 + 1.5666 +static bool 1.5667 +DebuggerObject_unsafeDereference(JSContext *cx, unsigned argc, Value *vp) 1.5668 +{ 1.5669 + THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "unsafeDereference", args, referent); 1.5670 + args.rval().setObject(*referent); 1.5671 + if (!cx->compartment()->wrap(cx, args.rval())) 1.5672 + return false; 1.5673 + 1.5674 + // Wrapping should outerize inner objects. 1.5675 + JS_ASSERT(!IsInnerObject(&args.rval().toObject())); 1.5676 + 1.5677 + return true; 1.5678 +} 1.5679 + 1.5680 +static const JSPropertySpec DebuggerObject_properties[] = { 1.5681 + JS_PSG("proto", DebuggerObject_getProto, 0), 1.5682 + JS_PSG("class", DebuggerObject_getClass, 0), 1.5683 + JS_PSG("callable", DebuggerObject_getCallable, 0), 1.5684 + JS_PSG("name", DebuggerObject_getName, 0), 1.5685 + JS_PSG("displayName", DebuggerObject_getDisplayName, 0), 1.5686 + JS_PSG("parameterNames", DebuggerObject_getParameterNames, 0), 1.5687 + JS_PSG("script", DebuggerObject_getScript, 0), 1.5688 + JS_PSG("environment", DebuggerObject_getEnvironment, 0), 1.5689 + JS_PSG("global", DebuggerObject_getGlobal, 0), 1.5690 + JS_PS_END 1.5691 +}; 1.5692 + 1.5693 +static const JSFunctionSpec DebuggerObject_methods[] = { 1.5694 + JS_FN("getOwnPropertyDescriptor", DebuggerObject_getOwnPropertyDescriptor, 1, 0), 1.5695 + JS_FN("getOwnPropertyNames", DebuggerObject_getOwnPropertyNames, 0, 0), 1.5696 + JS_FN("defineProperty", DebuggerObject_defineProperty, 2, 0), 1.5697 + JS_FN("defineProperties", DebuggerObject_defineProperties, 1, 0), 1.5698 + JS_FN("deleteProperty", DebuggerObject_deleteProperty, 1, 0), 1.5699 + JS_FN("seal", DebuggerObject_seal, 0, 0), 1.5700 + JS_FN("freeze", DebuggerObject_freeze, 0, 0), 1.5701 + JS_FN("preventExtensions", DebuggerObject_preventExtensions, 0, 0), 1.5702 + JS_FN("isSealed", DebuggerObject_isSealed, 0, 0), 1.5703 + JS_FN("isFrozen", DebuggerObject_isFrozen, 0, 0), 1.5704 + JS_FN("isExtensible", DebuggerObject_isExtensible, 0, 0), 1.5705 + JS_FN("apply", DebuggerObject_apply, 0, 0), 1.5706 + JS_FN("call", DebuggerObject_call, 0, 0), 1.5707 + JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0), 1.5708 + JS_FN("evalInGlobal", DebuggerObject_evalInGlobal, 1, 0), 1.5709 + JS_FN("evalInGlobalWithBindings", DebuggerObject_evalInGlobalWithBindings, 2, 0), 1.5710 + JS_FN("unwrap", DebuggerObject_unwrap, 0, 0), 1.5711 + JS_FN("unsafeDereference", DebuggerObject_unsafeDereference, 0, 0), 1.5712 + JS_FS_END 1.5713 +}; 1.5714 + 1.5715 + 1.5716 +/*** Debugger.Environment ************************************************************************/ 1.5717 + 1.5718 +static void 1.5719 +DebuggerEnv_trace(JSTracer *trc, JSObject *obj) 1.5720 +{ 1.5721 + /* 1.5722 + * There is a barrier on private pointers, so the Unbarriered marking 1.5723 + * is okay. 1.5724 + */ 1.5725 + if (Env *referent = (JSObject *) obj->getPrivate()) { 1.5726 + MarkCrossCompartmentObjectUnbarriered(trc, obj, &referent, "Debugger.Environment referent"); 1.5727 + obj->setPrivateUnbarriered(referent); 1.5728 + } 1.5729 +} 1.5730 + 1.5731 +const Class DebuggerEnv_class = { 1.5732 + "Environment", 1.5733 + JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | 1.5734 + JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT), 1.5735 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.5736 + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nullptr, 1.5737 + nullptr, /* call */ 1.5738 + nullptr, /* hasInstance */ 1.5739 + nullptr, /* construct */ 1.5740 + DebuggerEnv_trace 1.5741 +}; 1.5742 + 1.5743 +static JSObject * 1.5744 +DebuggerEnv_checkThis(JSContext *cx, const CallArgs &args, const char *fnname, 1.5745 + bool requireDebuggee = true) 1.5746 +{ 1.5747 + if (!args.thisv().isObject()) { 1.5748 + ReportObjectRequired(cx); 1.5749 + return nullptr; 1.5750 + } 1.5751 + JSObject *thisobj = &args.thisv().toObject(); 1.5752 + if (thisobj->getClass() != &DebuggerEnv_class) { 1.5753 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.5754 + "Debugger.Environment", fnname, thisobj->getClass()->name); 1.5755 + return nullptr; 1.5756 + } 1.5757 + 1.5758 + /* 1.5759 + * Forbid Debugger.Environment.prototype, which is of class DebuggerEnv_class 1.5760 + * but isn't a real working Debugger.Environment. The prototype object is 1.5761 + * distinguished by having no referent. 1.5762 + */ 1.5763 + if (!thisobj->getPrivate()) { 1.5764 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, 1.5765 + "Debugger.Environment", fnname, "prototype object"); 1.5766 + return nullptr; 1.5767 + } 1.5768 + 1.5769 + /* 1.5770 + * Forbid access to Debugger.Environment objects that are not debuggee 1.5771 + * environments. 1.5772 + */ 1.5773 + if (requireDebuggee) { 1.5774 + Rooted<Env*> env(cx, static_cast<Env *>(thisobj->getPrivate())); 1.5775 + if (!Debugger::fromChildJSObject(thisobj)->observesGlobal(&env->global())) { 1.5776 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NOT_DEBUGGEE, 1.5777 + "Debugger.Environment", "environment"); 1.5778 + return nullptr; 1.5779 + } 1.5780 + } 1.5781 + 1.5782 + return thisobj; 1.5783 +} 1.5784 + 1.5785 +#define THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env) \ 1.5786 + CallArgs args = CallArgsFromVp(argc, vp); \ 1.5787 + JSObject *envobj = DebuggerEnv_checkThis(cx, args, fnname); \ 1.5788 + if (!envobj) \ 1.5789 + return false; \ 1.5790 + Rooted<Env*> env(cx, static_cast<Env *>(envobj->getPrivate())); \ 1.5791 + JS_ASSERT(env); \ 1.5792 + JS_ASSERT(!env->is<ScopeObject>()) 1.5793 + 1.5794 +#define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg) \ 1.5795 + THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env); \ 1.5796 + Debugger *dbg = Debugger::fromChildJSObject(envobj) 1.5797 + 1.5798 +static bool 1.5799 +DebuggerEnv_construct(JSContext *cx, unsigned argc, Value *vp) 1.5800 +{ 1.5801 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR, 1.5802 + "Debugger.Environment"); 1.5803 + return false; 1.5804 +} 1.5805 + 1.5806 +static bool 1.5807 +IsDeclarative(Env *env) 1.5808 +{ 1.5809 + return env->is<DebugScopeObject>() && env->as<DebugScopeObject>().isForDeclarative(); 1.5810 +} 1.5811 + 1.5812 +static bool 1.5813 +IsWith(Env *env) 1.5814 +{ 1.5815 + return env->is<DebugScopeObject>() && 1.5816 + env->as<DebugScopeObject>().scope().is<DynamicWithObject>(); 1.5817 +} 1.5818 + 1.5819 +static bool 1.5820 +DebuggerEnv_getType(JSContext *cx, unsigned argc, Value *vp) 1.5821 +{ 1.5822 + THIS_DEBUGENV(cx, argc, vp, "get type", args, envobj, env); 1.5823 + 1.5824 + /* Don't bother switching compartments just to check env's class. */ 1.5825 + const char *s; 1.5826 + if (IsDeclarative(env)) 1.5827 + s = "declarative"; 1.5828 + else if (IsWith(env)) 1.5829 + s = "with"; 1.5830 + else 1.5831 + s = "object"; 1.5832 + 1.5833 + JSAtom *str = Atomize(cx, s, strlen(s), InternAtom); 1.5834 + if (!str) 1.5835 + return false; 1.5836 + args.rval().setString(str); 1.5837 + return true; 1.5838 +} 1.5839 + 1.5840 +static bool 1.5841 +DebuggerEnv_getParent(JSContext *cx, unsigned argc, Value *vp) 1.5842 +{ 1.5843 + THIS_DEBUGENV_OWNER(cx, argc, vp, "get parent", args, envobj, env, dbg); 1.5844 + 1.5845 + /* Don't bother switching compartments just to get env's parent. */ 1.5846 + Rooted<Env*> parent(cx, env->enclosingScope()); 1.5847 + return dbg->wrapEnvironment(cx, parent, args.rval()); 1.5848 +} 1.5849 + 1.5850 +static bool 1.5851 +DebuggerEnv_getObject(JSContext *cx, unsigned argc, Value *vp) 1.5852 +{ 1.5853 + THIS_DEBUGENV_OWNER(cx, argc, vp, "get type", args, envobj, env, dbg); 1.5854 + 1.5855 + /* 1.5856 + * Don't bother switching compartments just to check env's class and 1.5857 + * possibly get its proto. 1.5858 + */ 1.5859 + if (IsDeclarative(env)) { 1.5860 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_NO_SCOPE_OBJECT); 1.5861 + return false; 1.5862 + } 1.5863 + 1.5864 + JSObject *obj; 1.5865 + if (IsWith(env)) { 1.5866 + obj = &env->as<DebugScopeObject>().scope().as<DynamicWithObject>().object(); 1.5867 + } else { 1.5868 + obj = env; 1.5869 + JS_ASSERT(!obj->is<DebugScopeObject>()); 1.5870 + } 1.5871 + 1.5872 + args.rval().setObject(*obj); 1.5873 + if (!dbg->wrapDebuggeeValue(cx, args.rval())) 1.5874 + return false; 1.5875 + return true; 1.5876 +} 1.5877 + 1.5878 +static bool 1.5879 +DebuggerEnv_getCallee(JSContext *cx, unsigned argc, Value *vp) 1.5880 +{ 1.5881 + THIS_DEBUGENV_OWNER(cx, argc, vp, "get callee", args, envobj, env, dbg); 1.5882 + 1.5883 + args.rval().setNull(); 1.5884 + 1.5885 + if (!env->is<DebugScopeObject>()) 1.5886 + return true; 1.5887 + 1.5888 + JSObject &scope = env->as<DebugScopeObject>().scope(); 1.5889 + if (!scope.is<CallObject>()) 1.5890 + return true; 1.5891 + 1.5892 + CallObject &callobj = scope.as<CallObject>(); 1.5893 + if (callobj.isForEval()) 1.5894 + return true; 1.5895 + 1.5896 + args.rval().setObject(callobj.callee()); 1.5897 + if (!dbg->wrapDebuggeeValue(cx, args.rval())) 1.5898 + return false; 1.5899 + return true; 1.5900 +} 1.5901 + 1.5902 +static bool 1.5903 +DebuggerEnv_getInspectable(JSContext *cx, unsigned argc, Value *vp) 1.5904 +{ 1.5905 + CallArgs args = CallArgsFromVp(argc, vp); 1.5906 + JSObject *envobj = DebuggerEnv_checkThis(cx, args, "get inspectable", false); 1.5907 + if (!envobj) 1.5908 + return false; 1.5909 + Rooted<Env*> env(cx, static_cast<Env *>(envobj->getPrivate())); 1.5910 + JS_ASSERT(env); 1.5911 + JS_ASSERT(!env->is<ScopeObject>()); 1.5912 + 1.5913 + Debugger *dbg = Debugger::fromChildJSObject(envobj); 1.5914 + 1.5915 + args.rval().setBoolean(dbg->observesGlobal(&env->global())); 1.5916 + return true; 1.5917 +} 1.5918 + 1.5919 +static bool 1.5920 +DebuggerEnv_names(JSContext *cx, unsigned argc, Value *vp) 1.5921 +{ 1.5922 + THIS_DEBUGENV_OWNER(cx, argc, vp, "names", args, envobj, env, dbg); 1.5923 + 1.5924 + AutoIdVector keys(cx); 1.5925 + { 1.5926 + Maybe<AutoCompartment> ac; 1.5927 + ac.construct(cx, env); 1.5928 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5929 + if (!GetPropertyNames(cx, env, JSITER_HIDDEN, &keys)) 1.5930 + return false; 1.5931 + } 1.5932 + 1.5933 + RootedObject arr(cx, NewDenseEmptyArray(cx)); 1.5934 + if (!arr) 1.5935 + return false; 1.5936 + RootedId id(cx); 1.5937 + for (size_t i = 0, len = keys.length(); i < len; i++) { 1.5938 + id = keys[i]; 1.5939 + if (JSID_IS_ATOM(id) && IsIdentifier(JSID_TO_ATOM(id))) { 1.5940 + if (!cx->compartment()->wrapId(cx, id.address())) 1.5941 + return false; 1.5942 + if (!NewbornArrayPush(cx, arr, StringValue(JSID_TO_STRING(id)))) 1.5943 + return false; 1.5944 + } 1.5945 + } 1.5946 + args.rval().setObject(*arr); 1.5947 + return true; 1.5948 +} 1.5949 + 1.5950 +static bool 1.5951 +DebuggerEnv_find(JSContext *cx, unsigned argc, Value *vp) 1.5952 +{ 1.5953 + REQUIRE_ARGC("Debugger.Environment.find", 1); 1.5954 + THIS_DEBUGENV_OWNER(cx, argc, vp, "find", args, envobj, env, dbg); 1.5955 + 1.5956 + RootedId id(cx); 1.5957 + if (!ValueToIdentifier(cx, args[0], &id)) 1.5958 + return false; 1.5959 + 1.5960 + { 1.5961 + Maybe<AutoCompartment> ac; 1.5962 + ac.construct(cx, env); 1.5963 + if (!cx->compartment()->wrapId(cx, id.address())) 1.5964 + return false; 1.5965 + 1.5966 + /* This can trigger resolve hooks. */ 1.5967 + ErrorCopier ec(ac, dbg->toJSObject()); 1.5968 + RootedShape prop(cx); 1.5969 + RootedObject pobj(cx); 1.5970 + for (; env && !prop; env = env->enclosingScope()) { 1.5971 + if (!JSObject::lookupGeneric(cx, env, id, &pobj, &prop)) 1.5972 + return false; 1.5973 + if (prop) 1.5974 + break; 1.5975 + } 1.5976 + } 1.5977 + 1.5978 + return dbg->wrapEnvironment(cx, env, args.rval()); 1.5979 +} 1.5980 + 1.5981 +static bool 1.5982 +DebuggerEnv_getVariable(JSContext *cx, unsigned argc, Value *vp) 1.5983 +{ 1.5984 + REQUIRE_ARGC("Debugger.Environment.getVariable", 1); 1.5985 + THIS_DEBUGENV_OWNER(cx, argc, vp, "getVariable", args, envobj, env, dbg); 1.5986 + 1.5987 + RootedId id(cx); 1.5988 + if (!ValueToIdentifier(cx, args[0], &id)) 1.5989 + return false; 1.5990 + 1.5991 + RootedValue v(cx); 1.5992 + { 1.5993 + Maybe<AutoCompartment> ac; 1.5994 + ac.construct(cx, env); 1.5995 + if (!cx->compartment()->wrapId(cx, id.address())) 1.5996 + return false; 1.5997 + 1.5998 + /* This can trigger getters. */ 1.5999 + ErrorCopier ec(ac, dbg->toJSObject()); 1.6000 + 1.6001 + // For DebugScopeObjects, we get sentinel values for optimized out 1.6002 + // slots and arguments instead of throwing (the default behavior). 1.6003 + // 1.6004 + // See wrapDebuggeeValue for how the sentinel values are wrapped. 1.6005 + if (env->is<DebugScopeObject>()) { 1.6006 + if (!env->as<DebugScopeObject>().getMaybeSentinelValue(cx, id, &v)) 1.6007 + return false; 1.6008 + } else { 1.6009 + if (!JSObject::getGeneric(cx, env, env, id, &v)) 1.6010 + return false; 1.6011 + } 1.6012 + } 1.6013 + 1.6014 + if (!dbg->wrapDebuggeeValue(cx, &v)) 1.6015 + return false; 1.6016 + args.rval().set(v); 1.6017 + return true; 1.6018 +} 1.6019 + 1.6020 +static bool 1.6021 +DebuggerEnv_setVariable(JSContext *cx, unsigned argc, Value *vp) 1.6022 +{ 1.6023 + REQUIRE_ARGC("Debugger.Environment.setVariable", 2); 1.6024 + THIS_DEBUGENV_OWNER(cx, argc, vp, "setVariable", args, envobj, env, dbg); 1.6025 + 1.6026 + RootedId id(cx); 1.6027 + if (!ValueToIdentifier(cx, args[0], &id)) 1.6028 + return false; 1.6029 + 1.6030 + RootedValue v(cx, args[1]); 1.6031 + if (!dbg->unwrapDebuggeeValue(cx, &v)) 1.6032 + return false; 1.6033 + 1.6034 + { 1.6035 + Maybe<AutoCompartment> ac; 1.6036 + ac.construct(cx, env); 1.6037 + if (!cx->compartment()->wrapId(cx, id.address()) || !cx->compartment()->wrap(cx, &v)) 1.6038 + return false; 1.6039 + 1.6040 + /* This can trigger setters. */ 1.6041 + ErrorCopier ec(ac, dbg->toJSObject()); 1.6042 + 1.6043 + /* Make sure the environment actually has the specified binding. */ 1.6044 + bool has; 1.6045 + if (!JSObject::hasProperty(cx, env, id, &has)) 1.6046 + return false; 1.6047 + if (!has) { 1.6048 + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_VARIABLE_NOT_FOUND); 1.6049 + return false; 1.6050 + } 1.6051 + 1.6052 + /* Just set the property. */ 1.6053 + if (!JSObject::setGeneric(cx, env, env, id, &v, true)) 1.6054 + return false; 1.6055 + } 1.6056 + 1.6057 + args.rval().setUndefined(); 1.6058 + return true; 1.6059 +} 1.6060 + 1.6061 +static const JSPropertySpec DebuggerEnv_properties[] = { 1.6062 + JS_PSG("type", DebuggerEnv_getType, 0), 1.6063 + JS_PSG("object", DebuggerEnv_getObject, 0), 1.6064 + JS_PSG("parent", DebuggerEnv_getParent, 0), 1.6065 + JS_PSG("callee", DebuggerEnv_getCallee, 0), 1.6066 + JS_PSG("inspectable", DebuggerEnv_getInspectable, 0), 1.6067 + JS_PS_END 1.6068 +}; 1.6069 + 1.6070 +static const JSFunctionSpec DebuggerEnv_methods[] = { 1.6071 + JS_FN("names", DebuggerEnv_names, 0, 0), 1.6072 + JS_FN("find", DebuggerEnv_find, 1, 0), 1.6073 + JS_FN("getVariable", DebuggerEnv_getVariable, 1, 0), 1.6074 + JS_FN("setVariable", DebuggerEnv_setVariable, 2, 0), 1.6075 + JS_FS_END 1.6076 +}; 1.6077 + 1.6078 + 1.6079 + 1.6080 +/*** Glue ****************************************************************************************/ 1.6081 + 1.6082 +extern JS_PUBLIC_API(bool) 1.6083 +JS_DefineDebuggerObject(JSContext *cx, HandleObject obj) 1.6084 +{ 1.6085 + RootedObject 1.6086 + objProto(cx), 1.6087 + debugCtor(cx), 1.6088 + debugProto(cx), 1.6089 + frameProto(cx), 1.6090 + scriptProto(cx), 1.6091 + sourceProto(cx), 1.6092 + objectProto(cx), 1.6093 + envProto(cx), 1.6094 + memoryProto(cx); 1.6095 + objProto = obj->as<GlobalObject>().getOrCreateObjectPrototype(cx); 1.6096 + if (!objProto) 1.6097 + return false; 1.6098 + debugProto = js_InitClass(cx, obj, 1.6099 + objProto, &Debugger::jsclass, Debugger::construct, 1.6100 + 1, Debugger::properties, Debugger::methods, nullptr, nullptr, 1.6101 + debugCtor.address()); 1.6102 + if (!debugProto) 1.6103 + return false; 1.6104 + 1.6105 + frameProto = js_InitClass(cx, debugCtor, objProto, &DebuggerFrame_class, 1.6106 + DebuggerFrame_construct, 0, 1.6107 + DebuggerFrame_properties, DebuggerFrame_methods, 1.6108 + nullptr, nullptr); 1.6109 + if (!frameProto) 1.6110 + return false; 1.6111 + 1.6112 + scriptProto = js_InitClass(cx, debugCtor, objProto, &DebuggerScript_class, 1.6113 + DebuggerScript_construct, 0, 1.6114 + DebuggerScript_properties, DebuggerScript_methods, 1.6115 + nullptr, nullptr); 1.6116 + if (!scriptProto) 1.6117 + return false; 1.6118 + 1.6119 + sourceProto = js_InitClass(cx, debugCtor, sourceProto, &DebuggerSource_class, 1.6120 + DebuggerSource_construct, 0, 1.6121 + DebuggerSource_properties, DebuggerSource_methods, 1.6122 + nullptr, nullptr); 1.6123 + if (!sourceProto) 1.6124 + return false; 1.6125 + 1.6126 + objectProto = js_InitClass(cx, debugCtor, objProto, &DebuggerObject_class, 1.6127 + DebuggerObject_construct, 0, 1.6128 + DebuggerObject_properties, DebuggerObject_methods, 1.6129 + nullptr, nullptr); 1.6130 + if (!objectProto) 1.6131 + return false; 1.6132 + envProto = js_InitClass(cx, debugCtor, objProto, &DebuggerEnv_class, 1.6133 + DebuggerEnv_construct, 0, 1.6134 + DebuggerEnv_properties, DebuggerEnv_methods, 1.6135 + nullptr, nullptr); 1.6136 + if (!envProto) 1.6137 + return false; 1.6138 + memoryProto = js_InitClass(cx, debugCtor, objProto, &DebuggerMemory::class_, 1.6139 + DebuggerMemory::construct, 0, DebuggerMemory::properties, 1.6140 + DebuggerMemory::methods, nullptr, nullptr); 1.6141 + if (!memoryProto) 1.6142 + return false; 1.6143 + 1.6144 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO, ObjectValue(*frameProto)); 1.6145 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO, ObjectValue(*objectProto)); 1.6146 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO, ObjectValue(*scriptProto)); 1.6147 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO, ObjectValue(*sourceProto)); 1.6148 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO, ObjectValue(*envProto)); 1.6149 + debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO, ObjectValue(*memoryProto)); 1.6150 + return true; 1.6151 +}