js/src/vm/Debugger.cpp

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

mercurial