js/src/vm/OldDebugAPI.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/OldDebugAPI.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1348 @@
     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 +/*
    1.11 + * JS debugging API.
    1.12 + */
    1.13 +
    1.14 +#include "js/OldDebugAPI.h"
    1.15 +
    1.16 +#include <string.h>
    1.17 +
    1.18 +#include "jscntxt.h"
    1.19 +#include "jsfun.h"
    1.20 +#include "jsgc.h"
    1.21 +#include "jsobj.h"
    1.22 +#include "jsopcode.h"
    1.23 +#include "jsprf.h"
    1.24 +#include "jsscript.h"
    1.25 +#include "jsstr.h"
    1.26 +#include "jstypes.h"
    1.27 +#include "jswatchpoint.h"
    1.28 +
    1.29 +#include "frontend/SourceNotes.h"
    1.30 +#include "jit/AsmJSModule.h"
    1.31 +#include "vm/Debugger.h"
    1.32 +#include "vm/Shape.h"
    1.33 +
    1.34 +#include "jsatominlines.h"
    1.35 +#include "jsinferinlines.h"
    1.36 +#include "jsscriptinlines.h"
    1.37 +
    1.38 +#include "vm/Debugger-inl.h"
    1.39 +#include "vm/Interpreter-inl.h"
    1.40 +#include "vm/Stack-inl.h"
    1.41 +
    1.42 +using namespace js;
    1.43 +using namespace js::gc;
    1.44 +
    1.45 +using mozilla::PodZero;
    1.46 +
    1.47 +JS_PUBLIC_API(bool)
    1.48 +JS_GetDebugMode(JSContext *cx)
    1.49 +{
    1.50 +    return cx->compartment()->debugMode();
    1.51 +}
    1.52 +
    1.53 +JS_PUBLIC_API(bool)
    1.54 +JS_SetDebugMode(JSContext *cx, bool debug)
    1.55 +{
    1.56 +    return JS_SetDebugModeForCompartment(cx, cx->compartment(), debug);
    1.57 +}
    1.58 +
    1.59 +JS_PUBLIC_API(void)
    1.60 +JS_SetRuntimeDebugMode(JSRuntime *rt, bool debug)
    1.61 +{
    1.62 +    rt->debugMode = !!debug;
    1.63 +}
    1.64 +
    1.65 +static bool
    1.66 +IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
    1.67 +{
    1.68 +    ScriptFrameIter iter(cx);
    1.69 +    JS_ASSERT(iter.abstractFramePtr() == frame);
    1.70 +    return iter.isConstructing();
    1.71 +}
    1.72 +
    1.73 +JSTrapStatus
    1.74 +js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
    1.75 +{
    1.76 +    JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
    1.77 +
    1.78 +    if (!frame.script()->selfHosted()) {
    1.79 +        JSAbstractFramePtr jsframe(frame.raw(), pc);
    1.80 +        if (frame.isFramePushedByExecute()) {
    1.81 +            if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
    1.82 +                frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
    1.83 +                                       true, 0, cx->runtime()->debugHooks.executeHookData));
    1.84 +        } else {
    1.85 +            if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
    1.86 +                frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame),
    1.87 +                                       true, 0, cx->runtime()->debugHooks.callHookData));
    1.88 +        }
    1.89 +    }
    1.90 +
    1.91 +    RootedValue rval(cx);
    1.92 +    JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval);
    1.93 +    switch (status) {
    1.94 +      case JSTRAP_CONTINUE:
    1.95 +        break;
    1.96 +      case JSTRAP_THROW:
    1.97 +        cx->setPendingException(rval);
    1.98 +        break;
    1.99 +      case JSTRAP_ERROR:
   1.100 +        cx->clearPendingException();
   1.101 +        break;
   1.102 +      case JSTRAP_RETURN:
   1.103 +        frame.setReturnValue(rval);
   1.104 +        break;
   1.105 +      default:
   1.106 +        MOZ_ASSUME_UNREACHABLE("bad Debugger::onEnterFrame JSTrapStatus value");
   1.107 +    }
   1.108 +    return status;
   1.109 +}
   1.110 +
   1.111 +bool
   1.112 +js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg)
   1.113 +{
   1.114 +    JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
   1.115 +
   1.116 +    bool ok = okArg;
   1.117 +
   1.118 +    // We don't add hook data for self-hosted scripts, so we don't need to check for them, here.
   1.119 +    if (void *hookData = frame.maybeHookData()) {
   1.120 +        JSAbstractFramePtr jsframe(frame.raw(), pc);
   1.121 +        if (frame.isFramePushedByExecute()) {
   1.122 +            if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook)
   1.123 +                hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
   1.124 +        } else {
   1.125 +            if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook)
   1.126 +                hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData);
   1.127 +        }
   1.128 +    }
   1.129 +
   1.130 +    return Debugger::onLeaveFrame(cx, frame, ok);
   1.131 +}
   1.132 +
   1.133 +JSTrapStatus
   1.134 +js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
   1.135 +{
   1.136 +    JS_ASSERT(cx->compartment()->debugMode());
   1.137 +
   1.138 +    if (!cx->runtime()->debugHooks.throwHook && cx->compartment()->getDebuggees().empty())
   1.139 +        return JSTRAP_CONTINUE;
   1.140 +
   1.141 +    /* Call debugger throw hook if set. */
   1.142 +    RootedValue rval(cx);
   1.143 +    JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval);
   1.144 +    if (status == JSTRAP_CONTINUE) {
   1.145 +        if (JSThrowHook handler = cx->runtime()->debugHooks.throwHook) {
   1.146 +            RootedScript script(cx, frame.script());
   1.147 +            status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.throwHookData);
   1.148 +        }
   1.149 +    }
   1.150 +
   1.151 +    switch (status) {
   1.152 +      case JSTRAP_ERROR:
   1.153 +        cx->clearPendingException();
   1.154 +        break;
   1.155 +
   1.156 +      case JSTRAP_RETURN:
   1.157 +        cx->clearPendingException();
   1.158 +        frame.setReturnValue(rval);
   1.159 +        break;
   1.160 +
   1.161 +      case JSTRAP_THROW:
   1.162 +        cx->setPendingException(rval);
   1.163 +        break;
   1.164 +
   1.165 +      case JSTRAP_CONTINUE:
   1.166 +        break;
   1.167 +
   1.168 +      default:
   1.169 +        MOZ_ASSUME_UNREACHABLE("Invalid trap status");
   1.170 +    }
   1.171 +
   1.172 +    return status;
   1.173 +}
   1.174 +
   1.175 +JS_FRIEND_API(bool)
   1.176 +JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug)
   1.177 +{
   1.178 +    for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) {
   1.179 +        // Invalidate a zone at a time to avoid doing a zone-wide CellIter
   1.180 +        // per compartment.
   1.181 +        AutoDebugModeInvalidation invalidate(zone);
   1.182 +        for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
   1.183 +            // Ignore special compartments (atoms, JSD compartments)
   1.184 +            if (c->principals) {
   1.185 +                if (!c->setDebugModeFromC(cx, !!debug, invalidate))
   1.186 +                    return false;
   1.187 +            }
   1.188 +        }
   1.189 +    }
   1.190 +    return true;
   1.191 +}
   1.192 +
   1.193 +JS_FRIEND_API(bool)
   1.194 +JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug)
   1.195 +{
   1.196 +    AutoDebugModeInvalidation invalidate(comp);
   1.197 +    return comp->setDebugModeFromC(cx, !!debug, invalidate);
   1.198 +}
   1.199 +
   1.200 +static bool
   1.201 +CheckDebugMode(JSContext *cx)
   1.202 +{
   1.203 +    bool debugMode = JS_GetDebugMode(cx);
   1.204 +    /*
   1.205 +     * :TODO:
   1.206 +     * This probably should be an assertion, since it's indicative of a severe
   1.207 +     * API misuse.
   1.208 +     */
   1.209 +    if (!debugMode) {
   1.210 +        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
   1.211 +                                     nullptr, JSMSG_NEED_DEBUG_MODE);
   1.212 +    }
   1.213 +    return debugMode;
   1.214 +}
   1.215 +
   1.216 +JS_PUBLIC_API(bool)
   1.217 +JS_SetSingleStepMode(JSContext *cx, HandleScript script, bool singleStep)
   1.218 +{
   1.219 +    assertSameCompartment(cx, script);
   1.220 +
   1.221 +    if (!CheckDebugMode(cx))
   1.222 +        return false;
   1.223 +
   1.224 +    return script->setStepModeFlag(cx, singleStep);
   1.225 +}
   1.226 +
   1.227 +JS_PUBLIC_API(bool)
   1.228 +JS_SetTrap(JSContext *cx, HandleScript script, jsbytecode *pc, JSTrapHandler handler,
   1.229 +           HandleValue closure)
   1.230 +{
   1.231 +    assertSameCompartment(cx, script, closure);
   1.232 +
   1.233 +    if (!CheckDebugMode(cx))
   1.234 +        return false;
   1.235 +
   1.236 +    BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
   1.237 +    if (!site)
   1.238 +        return false;
   1.239 +    site->setTrap(cx->runtime()->defaultFreeOp(), handler, closure);
   1.240 +    return true;
   1.241 +}
   1.242 +
   1.243 +JS_PUBLIC_API(void)
   1.244 +JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
   1.245 +             JSTrapHandler *handlerp, jsval *closurep)
   1.246 +{
   1.247 +    if (BreakpointSite *site = script->getBreakpointSite(pc)) {
   1.248 +        site->clearTrap(cx->runtime()->defaultFreeOp(), handlerp, closurep);
   1.249 +    } else {
   1.250 +        if (handlerp)
   1.251 +            *handlerp = nullptr;
   1.252 +        if (closurep)
   1.253 +            *closurep = JSVAL_VOID;
   1.254 +    }
   1.255 +}
   1.256 +
   1.257 +JS_PUBLIC_API(void)
   1.258 +JS_ClearScriptTraps(JSRuntime *rt, JSScript *script)
   1.259 +{
   1.260 +    script->clearTraps(rt->defaultFreeOp());
   1.261 +}
   1.262 +
   1.263 +JS_PUBLIC_API(void)
   1.264 +JS_ClearAllTrapsForCompartment(JSContext *cx)
   1.265 +{
   1.266 +    cx->compartment()->clearTraps(cx->runtime()->defaultFreeOp());
   1.267 +}
   1.268 +
   1.269 +JS_PUBLIC_API(bool)
   1.270 +JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
   1.271 +{
   1.272 +    rt->debugHooks.interruptHook = hook;
   1.273 +    rt->debugHooks.interruptHookData = closure;
   1.274 +
   1.275 +    for (ActivationIterator iter(rt); !iter.done(); ++iter) {
   1.276 +        if (iter->isInterpreter())
   1.277 +            iter->asInterpreter()->enableInterruptsUnconditionally();
   1.278 +    }
   1.279 +
   1.280 +    return true;
   1.281 +}
   1.282 +
   1.283 +JS_PUBLIC_API(bool)
   1.284 +JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
   1.285 +{
   1.286 +    if (hoop)
   1.287 +        *hoop = rt->debugHooks.interruptHook;
   1.288 +    if (closurep)
   1.289 +        *closurep = rt->debugHooks.interruptHookData;
   1.290 +    rt->debugHooks.interruptHook = 0;
   1.291 +    rt->debugHooks.interruptHookData = 0;
   1.292 +    return true;
   1.293 +}
   1.294 +
   1.295 +/************************************************************************/
   1.296 +
   1.297 +JS_PUBLIC_API(bool)
   1.298 +JS_SetWatchPoint(JSContext *cx, HandleObject origobj, HandleId id,
   1.299 +                 JSWatchPointHandler handler, HandleObject closure)
   1.300 +{
   1.301 +    assertSameCompartment(cx, origobj);
   1.302 +
   1.303 +    RootedObject obj(cx, GetInnerObject(cx, origobj));
   1.304 +    if (!obj)
   1.305 +        return false;
   1.306 +
   1.307 +    RootedId propid(cx);
   1.308 +
   1.309 +    if (JSID_IS_INT(id)) {
   1.310 +        propid = id;
   1.311 +    } else if (JSID_IS_OBJECT(id)) {
   1.312 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP);
   1.313 +        return false;
   1.314 +    } else {
   1.315 +        RootedValue val(cx, IdToValue(id));
   1.316 +        if (!ValueToId<CanGC>(cx, val, &propid))
   1.317 +            return false;
   1.318 +    }
   1.319 +
   1.320 +    if (!obj->isNative() || obj->is<TypedArrayObject>()) {
   1.321 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
   1.322 +                             obj->getClass()->name);
   1.323 +        return false;
   1.324 +    }
   1.325 +
   1.326 +    /*
   1.327 +     * Use sparse indexes for watched objects, as dense elements can be written
   1.328 +     * to without checking the watchpoint map.
   1.329 +     */
   1.330 +    if (!JSObject::sparsifyDenseElements(cx, obj))
   1.331 +        return false;
   1.332 +
   1.333 +    types::MarkTypePropertyNonData(cx, obj, propid);
   1.334 +
   1.335 +    WatchpointMap *wpmap = cx->compartment()->watchpointMap;
   1.336 +    if (!wpmap) {
   1.337 +        wpmap = cx->runtime()->new_<WatchpointMap>();
   1.338 +        if (!wpmap || !wpmap->init()) {
   1.339 +            js_ReportOutOfMemory(cx);
   1.340 +            return false;
   1.341 +        }
   1.342 +        cx->compartment()->watchpointMap = wpmap;
   1.343 +    }
   1.344 +    return wpmap->watch(cx, obj, propid, handler, closure);
   1.345 +}
   1.346 +
   1.347 +JS_PUBLIC_API(bool)
   1.348 +JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
   1.349 +                   JSWatchPointHandler *handlerp, JSObject **closurep)
   1.350 +{
   1.351 +    assertSameCompartment(cx, obj, id);
   1.352 +
   1.353 +    if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
   1.354 +        wpmap->unwatch(obj, id, handlerp, closurep);
   1.355 +    return true;
   1.356 +}
   1.357 +
   1.358 +JS_PUBLIC_API(bool)
   1.359 +JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
   1.360 +{
   1.361 +    assertSameCompartment(cx, obj);
   1.362 +
   1.363 +    if (WatchpointMap *wpmap = cx->compartment()->watchpointMap)
   1.364 +        wpmap->unwatchObject(obj);
   1.365 +    return true;
   1.366 +}
   1.367 +
   1.368 +/************************************************************************/
   1.369 +
   1.370 +JS_PUBLIC_API(unsigned)
   1.371 +JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
   1.372 +{
   1.373 +    return js::PCToLineNumber(script, pc);
   1.374 +}
   1.375 +
   1.376 +JS_PUBLIC_API(jsbytecode *)
   1.377 +JS_LineNumberToPC(JSContext *cx, JSScript *script, unsigned lineno)
   1.378 +{
   1.379 +    return js_LineNumberToPC(script, lineno);
   1.380 +}
   1.381 +
   1.382 +JS_PUBLIC_API(jsbytecode *)
   1.383 +JS_EndPC(JSContext *cx, JSScript *script)
   1.384 +{
   1.385 +    return script->codeEnd();
   1.386 +}
   1.387 +
   1.388 +JS_PUBLIC_API(bool)
   1.389 +JS_GetLinePCs(JSContext *cx, JSScript *script,
   1.390 +              unsigned startLine, unsigned maxLines,
   1.391 +              unsigned* count, unsigned** retLines, jsbytecode*** retPCs)
   1.392 +{
   1.393 +    size_t len = (script->length() > maxLines ? maxLines : script->length());
   1.394 +    unsigned *lines = cx->pod_malloc<unsigned>(len);
   1.395 +    if (!lines)
   1.396 +        return false;
   1.397 +
   1.398 +    jsbytecode **pcs = cx->pod_malloc<jsbytecode*>(len);
   1.399 +    if (!pcs) {
   1.400 +        js_free(lines);
   1.401 +        return false;
   1.402 +    }
   1.403 +
   1.404 +    unsigned lineno = script->lineno();
   1.405 +    unsigned offset = 0;
   1.406 +    unsigned i = 0;
   1.407 +    for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
   1.408 +        offset += SN_DELTA(sn);
   1.409 +        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
   1.410 +        if (type == SRC_SETLINE || type == SRC_NEWLINE) {
   1.411 +            if (type == SRC_SETLINE)
   1.412 +                lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
   1.413 +            else
   1.414 +                lineno++;
   1.415 +
   1.416 +            if (lineno >= startLine) {
   1.417 +                lines[i] = lineno;
   1.418 +                pcs[i] = script->offsetToPC(offset);
   1.419 +                if (++i >= maxLines)
   1.420 +                    break;
   1.421 +            }
   1.422 +        }
   1.423 +    }
   1.424 +
   1.425 +    *count = i;
   1.426 +    if (retLines)
   1.427 +        *retLines = lines;
   1.428 +    else
   1.429 +        js_free(lines);
   1.430 +
   1.431 +    if (retPCs)
   1.432 +        *retPCs = pcs;
   1.433 +    else
   1.434 +        js_free(pcs);
   1.435 +
   1.436 +    return true;
   1.437 +}
   1.438 +
   1.439 +JS_PUBLIC_API(unsigned)
   1.440 +JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
   1.441 +{
   1.442 +    return fun->nargs();
   1.443 +}
   1.444 +
   1.445 +JS_PUBLIC_API(bool)
   1.446 +JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
   1.447 +{
   1.448 +    return fun->nonLazyScript()->bindings.count() > 0;
   1.449 +}
   1.450 +
   1.451 +extern JS_PUBLIC_API(uintptr_t *)
   1.452 +JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **memp)
   1.453 +{
   1.454 +    RootedScript script(cx, fun->nonLazyScript());
   1.455 +    BindingVector bindings(cx);
   1.456 +    if (!FillBindingVector(script, &bindings))
   1.457 +        return nullptr;
   1.458 +
   1.459 +    LifoAlloc &lifo = cx->tempLifoAlloc();
   1.460 +
   1.461 +    // Store the LifoAlloc::Mark right before the allocation.
   1.462 +    LifoAlloc::Mark mark = lifo.mark();
   1.463 +    void *mem = lifo.alloc(sizeof(LifoAlloc::Mark) + bindings.length() * sizeof(uintptr_t));
   1.464 +    if (!mem) {
   1.465 +        js_ReportOutOfMemory(cx);
   1.466 +        return nullptr;
   1.467 +    }
   1.468 +    *memp = mem;
   1.469 +    *reinterpret_cast<LifoAlloc::Mark*>(mem) = mark;
   1.470 +
   1.471 +    // Munge data into the API this method implements.  Avert your eyes!
   1.472 +    uintptr_t *names = reinterpret_cast<uintptr_t*>((char*)mem + sizeof(LifoAlloc::Mark));
   1.473 +    for (size_t i = 0; i < bindings.length(); i++)
   1.474 +        names[i] = reinterpret_cast<uintptr_t>(bindings[i].name());
   1.475 +
   1.476 +    return names;
   1.477 +}
   1.478 +
   1.479 +extern JS_PUBLIC_API(JSAtom *)
   1.480 +JS_LocalNameToAtom(uintptr_t w)
   1.481 +{
   1.482 +    return reinterpret_cast<JSAtom *>(w);
   1.483 +}
   1.484 +
   1.485 +extern JS_PUBLIC_API(JSString *)
   1.486 +JS_AtomKey(JSAtom *atom)
   1.487 +{
   1.488 +    return atom;
   1.489 +}
   1.490 +
   1.491 +extern JS_PUBLIC_API(void)
   1.492 +JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mem)
   1.493 +{
   1.494 +    cx->tempLifoAlloc().release(*reinterpret_cast<LifoAlloc::Mark*>(mem));
   1.495 +}
   1.496 +
   1.497 +JS_PUBLIC_API(JSScript *)
   1.498 +JS_GetFunctionScript(JSContext *cx, HandleFunction fun)
   1.499 +{
   1.500 +    if (fun->isNative())
   1.501 +        return nullptr;
   1.502 +    if (fun->isInterpretedLazy()) {
   1.503 +        AutoCompartment funCompartment(cx, fun);
   1.504 +        JSScript *script = fun->getOrCreateScript(cx);
   1.505 +        if (!script)
   1.506 +            MOZ_CRASH();
   1.507 +        return script;
   1.508 +    }
   1.509 +    return fun->nonLazyScript();
   1.510 +}
   1.511 +
   1.512 +JS_PUBLIC_API(JSNative)
   1.513 +JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
   1.514 +{
   1.515 +    return fun->maybeNative();
   1.516 +}
   1.517 +
   1.518 +JS_PUBLIC_API(JSPrincipals *)
   1.519 +JS_GetScriptPrincipals(JSScript *script)
   1.520 +{
   1.521 +    return script->principals();
   1.522 +}
   1.523 +
   1.524 +JS_PUBLIC_API(JSPrincipals *)
   1.525 +JS_GetScriptOriginPrincipals(JSScript *script)
   1.526 +{
   1.527 +    return script->originPrincipals();
   1.528 +}
   1.529 +
   1.530 +/************************************************************************/
   1.531 +
   1.532 +JS_PUBLIC_API(JSFunction *)
   1.533 +JS_GetScriptFunction(JSContext *cx, JSScript *script)
   1.534 +{
   1.535 +    script->ensureNonLazyCanonicalFunction(cx);
   1.536 +    return script->functionNonDelazifying();
   1.537 +}
   1.538 +
   1.539 +JS_PUBLIC_API(JSObject *)
   1.540 +JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
   1.541 +{
   1.542 +    return obj->enclosingScope();
   1.543 +}
   1.544 +
   1.545 +JS_PUBLIC_API(const char *)
   1.546 +JS_GetDebugClassName(JSObject *obj)
   1.547 +{
   1.548 +    if (obj->is<DebugScopeObject>())
   1.549 +        return obj->as<DebugScopeObject>().scope().getClass()->name;
   1.550 +    return obj->getClass()->name;
   1.551 +}
   1.552 +
   1.553 +/************************************************************************/
   1.554 +
   1.555 +JS_PUBLIC_API(const char *)
   1.556 +JS_GetScriptFilename(JSScript *script)
   1.557 +{
   1.558 +    return script->filename();
   1.559 +}
   1.560 +
   1.561 +JS_PUBLIC_API(const jschar *)
   1.562 +JS_GetScriptSourceMap(JSContext *cx, JSScript *script)
   1.563 +{
   1.564 +    ScriptSource *source = script->scriptSource();
   1.565 +    JS_ASSERT(source);
   1.566 +    return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr;
   1.567 +}
   1.568 +
   1.569 +JS_PUBLIC_API(unsigned)
   1.570 +JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
   1.571 +{
   1.572 +    return script->lineno();
   1.573 +}
   1.574 +
   1.575 +JS_PUBLIC_API(unsigned)
   1.576 +JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
   1.577 +{
   1.578 +    return js_GetScriptLineExtent(script);
   1.579 +}
   1.580 +
   1.581 +JS_PUBLIC_API(JSVersion)
   1.582 +JS_GetScriptVersion(JSContext *cx, JSScript *script)
   1.583 +{
   1.584 +    return VersionNumber(script->getVersion());
   1.585 +}
   1.586 +
   1.587 +JS_PUBLIC_API(bool)
   1.588 +JS_GetScriptIsSelfHosted(JSScript *script)
   1.589 +{
   1.590 +    return script->selfHosted();
   1.591 +}
   1.592 +
   1.593 +/***************************************************************************/
   1.594 +
   1.595 +JS_PUBLIC_API(void)
   1.596 +JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
   1.597 +{
   1.598 +    rt->debugHooks.newScriptHook = hook;
   1.599 +    rt->debugHooks.newScriptHookData = callerdata;
   1.600 +}
   1.601 +
   1.602 +JS_PUBLIC_API(void)
   1.603 +JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
   1.604 +                        void *callerdata)
   1.605 +{
   1.606 +    rt->debugHooks.destroyScriptHook = hook;
   1.607 +    rt->debugHooks.destroyScriptHookData = callerdata;
   1.608 +}
   1.609 +
   1.610 +/***************************************************************************/
   1.611 +
   1.612 +/* This all should be reworked to avoid requiring JSScopeProperty types. */
   1.613 +
   1.614 +static bool
   1.615 +GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd)
   1.616 +{
   1.617 +    assertSameCompartment(cx, obj_);
   1.618 +    pd->id = IdToValue(shape->propid());
   1.619 +
   1.620 +    RootedObject obj(cx, obj_);
   1.621 +
   1.622 +    bool wasThrowing = cx->isExceptionPending();
   1.623 +    RootedValue lastException(cx, UndefinedValue());
   1.624 +    if (wasThrowing) {
   1.625 +        if (!cx->getPendingException(&lastException))
   1.626 +            return false;
   1.627 +    }
   1.628 +    cx->clearPendingException();
   1.629 +
   1.630 +    Rooted<jsid> id(cx, shape->propid());
   1.631 +    RootedValue value(cx);
   1.632 +    if (!baseops::GetProperty(cx, obj, id, &value)) {
   1.633 +        if (!cx->isExceptionPending()) {
   1.634 +            pd->flags = JSPD_ERROR;
   1.635 +            pd->value = JSVAL_VOID;
   1.636 +        } else {
   1.637 +            pd->flags = JSPD_EXCEPTION;
   1.638 +            if (!cx->getPendingException(&value))
   1.639 +                return false;
   1.640 +            pd->value = value;
   1.641 +        }
   1.642 +    } else {
   1.643 +        pd->flags = 0;
   1.644 +        pd->value = value;
   1.645 +    }
   1.646 +
   1.647 +    if (wasThrowing)
   1.648 +        cx->setPendingException(lastException);
   1.649 +
   1.650 +    pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
   1.651 +              |  (!shape->writable()  ? JSPD_READONLY  : 0)
   1.652 +              |  (!shape->configurable() ? JSPD_PERMANENT : 0);
   1.653 +    pd->spare = 0;
   1.654 +    pd->alias = JSVAL_VOID;
   1.655 +
   1.656 +    return true;
   1.657 +}
   1.658 +
   1.659 +JS_PUBLIC_API(bool)
   1.660 +JS_GetPropertyDescArray(JSContext *cx, JS::HandleObject obj, JSPropertyDescArray *pda)
   1.661 +{
   1.662 +    assertSameCompartment(cx, obj);
   1.663 +    uint32_t i = 0;
   1.664 +    JSPropertyDesc *pd = nullptr;
   1.665 +
   1.666 +    if (obj->is<DebugScopeObject>()) {
   1.667 +        AutoIdVector props(cx);
   1.668 +        if (!Proxy::enumerate(cx, obj, props))
   1.669 +            return false;
   1.670 +
   1.671 +        pd = cx->pod_calloc<JSPropertyDesc>(props.length());
   1.672 +        if (!pd)
   1.673 +            return false;
   1.674 +
   1.675 +        for (i = 0; i < props.length(); ++i) {
   1.676 +            pd[i].id = JSVAL_NULL;
   1.677 +            pd[i].value = JSVAL_NULL;
   1.678 +            if (!AddValueRoot(cx, &pd[i].id, nullptr))
   1.679 +                goto bad;
   1.680 +            pd[i].id = IdToValue(props[i]);
   1.681 +            if (!AddValueRoot(cx, &pd[i].value, nullptr))
   1.682 +                goto bad;
   1.683 +            if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value)))
   1.684 +                goto bad;
   1.685 +        }
   1.686 +
   1.687 +        pda->length = props.length();
   1.688 +        pda->array = pd;
   1.689 +        return true;
   1.690 +    }
   1.691 +
   1.692 +    const Class *clasp;
   1.693 +    clasp = obj->getClass();
   1.694 +    if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
   1.695 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
   1.696 +                             JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
   1.697 +        return false;
   1.698 +    }
   1.699 +    if (!clasp->enumerate(cx, obj))
   1.700 +        return false;
   1.701 +
   1.702 +    /* Return an empty pda early if obj has no own properties. */
   1.703 +    if (obj->nativeEmpty()) {
   1.704 +        pda->length = 0;
   1.705 +        pda->array = nullptr;
   1.706 +        return true;
   1.707 +    }
   1.708 +
   1.709 +    pd = cx->pod_malloc<JSPropertyDesc>(obj->propertyCount());
   1.710 +    if (!pd)
   1.711 +        return false;
   1.712 +
   1.713 +    {
   1.714 +        Shape::Range<CanGC> r(cx, obj->lastProperty());
   1.715 +        RootedShape shape(cx);
   1.716 +        for (; !r.empty(); r.popFront()) {
   1.717 +            pd[i].id = JSVAL_NULL;
   1.718 +            pd[i].value = JSVAL_NULL;
   1.719 +            pd[i].alias = JSVAL_NULL;
   1.720 +            if (!AddValueRoot(cx, &pd[i].id, nullptr))
   1.721 +                goto bad;
   1.722 +            if (!AddValueRoot(cx, &pd[i].value, nullptr))
   1.723 +                goto bad;
   1.724 +            shape = const_cast<Shape *>(&r.front());
   1.725 +            if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
   1.726 +                goto bad;
   1.727 +            if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, nullptr))
   1.728 +                goto bad;
   1.729 +            if (++i == obj->propertyCount())
   1.730 +                break;
   1.731 +        }
   1.732 +    }
   1.733 +
   1.734 +    pda->length = i;
   1.735 +    pda->array = pd;
   1.736 +    return true;
   1.737 +
   1.738 +bad:
   1.739 +    pda->length = i + 1;
   1.740 +    pda->array = pd;
   1.741 +    JS_PutPropertyDescArray(cx, pda);
   1.742 +    return false;
   1.743 +}
   1.744 +
   1.745 +JS_PUBLIC_API(void)
   1.746 +JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
   1.747 +{
   1.748 +    JSPropertyDesc *pd;
   1.749 +    uint32_t i;
   1.750 +
   1.751 +    pd = pda->array;
   1.752 +    for (i = 0; i < pda->length; i++) {
   1.753 +        RemoveRoot(cx->runtime(), &pd[i].id);
   1.754 +        RemoveRoot(cx->runtime(), &pd[i].value);
   1.755 +        if (pd[i].flags & JSPD_ALIAS)
   1.756 +            RemoveRoot(cx->runtime(), &pd[i].alias);
   1.757 +    }
   1.758 +    js_free(pd);
   1.759 +    pda->array = nullptr;
   1.760 +    pda->length = 0;
   1.761 +}
   1.762 +
   1.763 +/************************************************************************/
   1.764 +
   1.765 +JS_PUBLIC_API(bool)
   1.766 +JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
   1.767 +{
   1.768 +    rt->debugHooks.debuggerHandler = handler;
   1.769 +    rt->debugHooks.debuggerHandlerData = closure;
   1.770 +    return true;
   1.771 +}
   1.772 +
   1.773 +JS_PUBLIC_API(bool)
   1.774 +JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
   1.775 +{
   1.776 +    rt->debugHooks.sourceHandler = handler;
   1.777 +    rt->debugHooks.sourceHandlerData = closure;
   1.778 +    return true;
   1.779 +}
   1.780 +
   1.781 +JS_PUBLIC_API(bool)
   1.782 +JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
   1.783 +{
   1.784 +    rt->debugHooks.executeHook = hook;
   1.785 +    rt->debugHooks.executeHookData = closure;
   1.786 +    return true;
   1.787 +}
   1.788 +
   1.789 +JS_PUBLIC_API(bool)
   1.790 +JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
   1.791 +{
   1.792 +    rt->debugHooks.callHook = hook;
   1.793 +    rt->debugHooks.callHookData = closure;
   1.794 +    return true;
   1.795 +}
   1.796 +
   1.797 +JS_PUBLIC_API(bool)
   1.798 +JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
   1.799 +{
   1.800 +    rt->debugHooks.throwHook = hook;
   1.801 +    rt->debugHooks.throwHookData = closure;
   1.802 +    return true;
   1.803 +}
   1.804 +
   1.805 +JS_PUBLIC_API(bool)
   1.806 +JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
   1.807 +{
   1.808 +    rt->debugHooks.debugErrorHook = hook;
   1.809 +    rt->debugHooks.debugErrorHookData = closure;
   1.810 +    return true;
   1.811 +}
   1.812 +
   1.813 +/************************************************************************/
   1.814 +
   1.815 +JS_PUBLIC_API(const JSDebugHooks *)
   1.816 +JS_GetGlobalDebugHooks(JSRuntime *rt)
   1.817 +{
   1.818 +    return &rt->debugHooks;
   1.819 +}
   1.820 +
   1.821 +/************************************************************************/
   1.822 +
   1.823 +extern JS_PUBLIC_API(void)
   1.824 +JS_DumpPCCounts(JSContext *cx, HandleScript script)
   1.825 +{
   1.826 +    JS_ASSERT(script->hasScriptCounts());
   1.827 +
   1.828 +    Sprinter sprinter(cx);
   1.829 +    if (!sprinter.init())
   1.830 +        return;
   1.831 +
   1.832 +    fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
   1.833 +    js_DumpPCCounts(cx, script, &sprinter);
   1.834 +    fputs(sprinter.string(), stdout);
   1.835 +    fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno());
   1.836 +}
   1.837 +
   1.838 +JS_PUBLIC_API(void)
   1.839 +JS_DumpCompartmentPCCounts(JSContext *cx)
   1.840 +{
   1.841 +    for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
   1.842 +        RootedScript script(cx, i.get<JSScript>());
   1.843 +        if (script->compartment() != cx->compartment())
   1.844 +            continue;
   1.845 +
   1.846 +        if (script->hasScriptCounts())
   1.847 +            JS_DumpPCCounts(cx, script);
   1.848 +    }
   1.849 +}
   1.850 +
   1.851 +JS_FRIEND_API(bool)
   1.852 +js::CanCallContextDebugHandler(JSContext *cx)
   1.853 +{
   1.854 +    return !!cx->runtime()->debugHooks.debuggerHandler;
   1.855 +}
   1.856 +
   1.857 +static JSTrapStatus
   1.858 +CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval)
   1.859 +{
   1.860 +    if (!cx->runtime()->debugHooks.debuggerHandler)
   1.861 +        return JSTRAP_RETURN;
   1.862 +
   1.863 +    return cx->runtime()->debugHooks.debuggerHandler(cx, script, bc, rval,
   1.864 +                                                     cx->runtime()->debugHooks.debuggerHandlerData);
   1.865 +}
   1.866 +
   1.867 +JS_FRIEND_API(bool)
   1.868 +js_CallContextDebugHandler(JSContext *cx)
   1.869 +{
   1.870 +    NonBuiltinFrameIter iter(cx);
   1.871 +
   1.872 +    // If there is no script to debug, then abort execution even if the user
   1.873 +    // clicks 'Debug' in the slow-script dialog.
   1.874 +    if (!iter.hasScript())
   1.875 +        return false;
   1.876 +
   1.877 +    // Even if script was running during the operation callback, it's possible
   1.878 +    // it was a builtin which 'iter' will have skipped over.
   1.879 +    if (iter.done())
   1.880 +        return false;
   1.881 +
   1.882 +    RootedValue rval(cx);
   1.883 +    RootedScript script(cx, iter.script());
   1.884 +    switch (CallContextDebugHandler(cx, script, iter.pc(), rval.address())) {
   1.885 +      case JSTRAP_ERROR:
   1.886 +        JS_ClearPendingException(cx);
   1.887 +        return false;
   1.888 +      case JSTRAP_THROW:
   1.889 +        JS_SetPendingException(cx, rval);
   1.890 +        return false;
   1.891 +      case JSTRAP_RETURN:
   1.892 +      case JSTRAP_CONTINUE:
   1.893 +      default:
   1.894 +        return true;
   1.895 +    }
   1.896 +}
   1.897 +
   1.898 +/*
   1.899 + * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid
   1.900 + * constructing a FrameDescription on the stack just to append it to a vector.
   1.901 + * FrameDescription contains Heap<T> fields that should not live on the stack.
   1.902 + */
   1.903 +JS::FrameDescription::FrameDescription(const ScriptFrameIter& iter)
   1.904 +  : script_(iter.script()),
   1.905 +    funDisplayName_(nullptr),
   1.906 +    pc_(iter.pc()),
   1.907 +    linenoComputed(false)
   1.908 +{
   1.909 +    if (JSFunction *fun = iter.maybeCallee())
   1.910 +        funDisplayName_ = fun->displayAtom();
   1.911 +}
   1.912 +
   1.913 +JS_PUBLIC_API(JS::StackDescription *)
   1.914 +JS::DescribeStack(JSContext *cx, unsigned maxFrames)
   1.915 +{
   1.916 +    Vector<FrameDescription> frames(cx);
   1.917 +
   1.918 +    NonBuiltinScriptFrameIter i(cx, ScriptFrameIter::ALL_CONTEXTS,
   1.919 +                                ScriptFrameIter::GO_THROUGH_SAVED,
   1.920 +                                cx->compartment()->principals);
   1.921 +    for ( ; !i.done(); ++i) {
   1.922 +        if (!frames.append(i))
   1.923 +            return nullptr;
   1.924 +        if (frames.length() == maxFrames)
   1.925 +            break;
   1.926 +    }
   1.927 +
   1.928 +    JS::StackDescription *desc = js_new<JS::StackDescription>();
   1.929 +    if (!desc)
   1.930 +        return nullptr;
   1.931 +
   1.932 +    desc->nframes = frames.length();
   1.933 +    desc->frames = frames.extractRawBuffer();
   1.934 +    return desc;
   1.935 +}
   1.936 +
   1.937 +JS_PUBLIC_API(void)
   1.938 +JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc)
   1.939 +{
   1.940 +    for (size_t i = 0; i < desc->nframes; ++i)
   1.941 +        desc->frames[i].~FrameDescription();
   1.942 +    js_free(desc->frames);
   1.943 +    js_delete(desc);
   1.944 +}
   1.945 +
   1.946 +namespace {
   1.947 +
   1.948 +class AutoPropertyDescArray
   1.949 +{
   1.950 +    JSContext *cx_;
   1.951 +    JSPropertyDescArray descArray_;
   1.952 +
   1.953 +  public:
   1.954 +    AutoPropertyDescArray(JSContext *cx)
   1.955 +      : cx_(cx)
   1.956 +    {
   1.957 +        PodZero(&descArray_);
   1.958 +    }
   1.959 +    ~AutoPropertyDescArray()
   1.960 +    {
   1.961 +        if (descArray_.array)
   1.962 +            JS_PutPropertyDescArray(cx_, &descArray_);
   1.963 +    }
   1.964 +
   1.965 +    void fetch(JS::HandleObject obj) {
   1.966 +        JS_ASSERT(!descArray_.array);
   1.967 +        if (!JS_GetPropertyDescArray(cx_, obj, &descArray_))
   1.968 +            descArray_.array = nullptr;
   1.969 +    }
   1.970 +
   1.971 +    JSPropertyDescArray * operator ->() {
   1.972 +        return &descArray_;
   1.973 +    }
   1.974 +};
   1.975 +
   1.976 +} /* anonymous namespace */
   1.977 +
   1.978 +static const char *
   1.979 +FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes)
   1.980 +{
   1.981 +    RootedValue v(cx, vArg);
   1.982 +
   1.983 +    /*
   1.984 +     * We could use Maybe<AutoCompartment> here, but G++ can't quite follow
   1.985 +     * that, and warns about uninitialized members being used in the
   1.986 +     * destructor.
   1.987 +     */
   1.988 +    RootedString str(cx);
   1.989 +    if (v.isObject()) {
   1.990 +        AutoCompartment ac(cx, &v.toObject());
   1.991 +        str = ToString<CanGC>(cx, v);
   1.992 +    } else {
   1.993 +        str = ToString<CanGC>(cx, v);
   1.994 +    }
   1.995 +
   1.996 +    if (!str)
   1.997 +        return nullptr;
   1.998 +    const char *buf = bytes.encodeLatin1(cx, str);
   1.999 +    if (!buf)
  1.1000 +        return nullptr;
  1.1001 +    const char *found = strstr(buf, "function ");
  1.1002 +    if (found && (found - buf <= 2))
  1.1003 +        return "[function]";
  1.1004 +    return buf;
  1.1005 +}
  1.1006 +
  1.1007 +static char *
  1.1008 +FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num,
  1.1009 +            bool showArgs, bool showLocals, bool showThisProps)
  1.1010 +{
  1.1011 +    JS_ASSERT(!cx->isExceptionPending());
  1.1012 +    RootedScript script(cx, iter.script());
  1.1013 +    jsbytecode* pc = iter.pc();
  1.1014 +
  1.1015 +    RootedObject scopeChain(cx, iter.scopeChain());
  1.1016 +    JSAutoCompartment ac(cx, scopeChain);
  1.1017 +
  1.1018 +    const char *filename = script->filename();
  1.1019 +    unsigned lineno = PCToLineNumber(script, pc);
  1.1020 +    RootedFunction fun(cx, iter.maybeCallee());
  1.1021 +    RootedString funname(cx);
  1.1022 +    if (fun)
  1.1023 +        funname = fun->atom();
  1.1024 +
  1.1025 +    RootedValue thisVal(cx);
  1.1026 +    AutoPropertyDescArray thisProps(cx);
  1.1027 +    if (iter.computeThis(cx)) {
  1.1028 +        thisVal = iter.computedThisValue();
  1.1029 +        if (showThisProps && !thisVal.isPrimitive()) {
  1.1030 +            RootedObject thisObj(cx, &thisVal.toObject());
  1.1031 +            thisProps.fetch(thisObj);
  1.1032 +        }
  1.1033 +    }
  1.1034 +
  1.1035 +    // print the frame number and function name
  1.1036 +    if (funname) {
  1.1037 +        JSAutoByteString funbytes;
  1.1038 +        buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname));
  1.1039 +    } else if (fun) {
  1.1040 +        buf = JS_sprintf_append(buf, "%d anonymous(", num);
  1.1041 +    } else {
  1.1042 +        buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
  1.1043 +    }
  1.1044 +    if (!buf)
  1.1045 +        return buf;
  1.1046 +
  1.1047 +    if (showArgs && iter.hasArgs()) {
  1.1048 +        BindingVector bindings(cx);
  1.1049 +        if (fun && fun->isInterpreted()) {
  1.1050 +            if (!FillBindingVector(script, &bindings))
  1.1051 +                return buf;
  1.1052 +        }
  1.1053 +
  1.1054 +
  1.1055 +        bool first = true;
  1.1056 +        for (unsigned i = 0; i < iter.numActualArgs(); i++) {
  1.1057 +            RootedValue arg(cx);
  1.1058 +            if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
  1.1059 +                for (AliasedFormalIter fi(script); ; fi++) {
  1.1060 +                    if (fi.frameIndex() == i) {
  1.1061 +                        arg = iter.callObj().aliasedVar(fi);
  1.1062 +                        break;
  1.1063 +                    }
  1.1064 +                }
  1.1065 +            } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
  1.1066 +                arg = iter.argsObj().arg(i);
  1.1067 +            } else {
  1.1068 +                arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
  1.1069 +            }
  1.1070 +
  1.1071 +            JSAutoByteString valueBytes;
  1.1072 +            const char *value = FormatValue(cx, arg, valueBytes);
  1.1073 +
  1.1074 +            JSAutoByteString nameBytes;
  1.1075 +            const char *name = nullptr;
  1.1076 +
  1.1077 +            if (i < bindings.length()) {
  1.1078 +                name = nameBytes.encodeLatin1(cx, bindings[i].name());
  1.1079 +                if (!buf)
  1.1080 +                    return nullptr;
  1.1081 +            }
  1.1082 +
  1.1083 +            if (value) {
  1.1084 +                buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
  1.1085 +                                        !first ? ", " : "",
  1.1086 +                                        name ? name :"",
  1.1087 +                                        name ? " = " : "",
  1.1088 +                                        arg.isString() ? "\"" : "",
  1.1089 +                                        value ? value : "?unknown?",
  1.1090 +                                        arg.isString() ? "\"" : "");
  1.1091 +                if (!buf)
  1.1092 +                    return buf;
  1.1093 +
  1.1094 +                first = false;
  1.1095 +            } else {
  1.1096 +                buf = JS_sprintf_append(buf, "    <Failed to get argument while inspecting stack frame>\n");
  1.1097 +                if (!buf)
  1.1098 +                    return buf;
  1.1099 +                cx->clearPendingException();
  1.1100 +
  1.1101 +            }
  1.1102 +        }
  1.1103 +    }
  1.1104 +
  1.1105 +    // print filename and line number
  1.1106 +    buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
  1.1107 +                            fun ? ")" : "",
  1.1108 +                            filename ? filename : "<unknown>",
  1.1109 +                            lineno);
  1.1110 +    if (!buf)
  1.1111 +        return buf;
  1.1112 +
  1.1113 +
  1.1114 +    // Note: Right now we don't dump the local variables anymore, because
  1.1115 +    // that is hard to support across all the JITs etc.
  1.1116 +
  1.1117 +    // print the value of 'this'
  1.1118 +    if (showLocals) {
  1.1119 +        if (!thisVal.isUndefined()) {
  1.1120 +            JSAutoByteString thisValBytes;
  1.1121 +            RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
  1.1122 +            const char *str = nullptr;
  1.1123 +            if (thisValStr &&
  1.1124 +                (str = thisValBytes.encodeLatin1(cx, thisValStr)))
  1.1125 +            {
  1.1126 +                buf = JS_sprintf_append(buf, "    this = %s\n", str);
  1.1127 +                if (!buf)
  1.1128 +                    return buf;
  1.1129 +            } else {
  1.1130 +                buf = JS_sprintf_append(buf, "    <failed to get 'this' value>\n");
  1.1131 +                cx->clearPendingException();
  1.1132 +            }
  1.1133 +        }
  1.1134 +    }
  1.1135 +
  1.1136 +    // print the properties of 'this', if it is an object
  1.1137 +    if (showThisProps && thisProps->array) {
  1.1138 +        for (uint32_t i = 0; i < thisProps->length; i++) {
  1.1139 +            JSPropertyDesc* desc = &thisProps->array[i];
  1.1140 +            if (desc->flags & JSPD_ENUMERATE) {
  1.1141 +                JSAutoByteString nameBytes;
  1.1142 +                JSAutoByteString valueBytes;
  1.1143 +                const char *name = FormatValue(cx, desc->id, nameBytes);
  1.1144 +                const char *value = FormatValue(cx, desc->value, valueBytes);
  1.1145 +                if (name && value) {
  1.1146 +                    buf = JS_sprintf_append(buf, "    this.%s = %s%s%s\n",
  1.1147 +                                            name,
  1.1148 +                                            desc->value.isString() ? "\"" : "",
  1.1149 +                                            value,
  1.1150 +                                            desc->value.isString() ? "\"" : "");
  1.1151 +                    if (!buf)
  1.1152 +                        return buf;
  1.1153 +                } else {
  1.1154 +                    buf = JS_sprintf_append(buf, "    <Failed to format values while inspecting stack frame>\n");
  1.1155 +                    cx->clearPendingException();
  1.1156 +                }
  1.1157 +            }
  1.1158 +        }
  1.1159 +    }
  1.1160 +
  1.1161 +    JS_ASSERT(!cx->isExceptionPending());
  1.1162 +    return buf;
  1.1163 +}
  1.1164 +
  1.1165 +JS_PUBLIC_API(char *)
  1.1166 +JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps)
  1.1167 +{
  1.1168 +    int num = 0;
  1.1169 +
  1.1170 +    for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
  1.1171 +        buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps);
  1.1172 +        num++;
  1.1173 +    }
  1.1174 +
  1.1175 +    if (!num)
  1.1176 +        buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
  1.1177 +
  1.1178 +    return buf;
  1.1179 +}
  1.1180 +
  1.1181 +JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc)
  1.1182 +  : ptr_(uintptr_t(raw)), pc_(pc)
  1.1183 +{ }
  1.1184 +
  1.1185 +JSObject *
  1.1186 +JSAbstractFramePtr::scopeChain(JSContext *cx)
  1.1187 +{
  1.1188 +    AbstractFramePtr frame(*this);
  1.1189 +    RootedObject scopeChain(cx, frame.scopeChain());
  1.1190 +    AutoCompartment ac(cx, scopeChain);
  1.1191 +    return GetDebugScopeForFrame(cx, frame, pc());
  1.1192 +}
  1.1193 +
  1.1194 +JSObject *
  1.1195 +JSAbstractFramePtr::callObject(JSContext *cx)
  1.1196 +{
  1.1197 +    AbstractFramePtr frame(*this);
  1.1198 +    if (!frame.isFunctionFrame())
  1.1199 +        return nullptr;
  1.1200 +
  1.1201 +    JSObject *o = GetDebugScopeForFrame(cx, frame, pc());
  1.1202 +
  1.1203 +    /*
  1.1204 +     * Given that fp is a function frame and GetDebugScopeForFrame always fills
  1.1205 +     * in missing scopes, we can expect to find fp's CallObject on 'o'. Note:
  1.1206 +     *  - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with
  1.1207 +     *    a DebugScopeObject proxy.
  1.1208 +     *  - If fp is an eval-in-function, then fp has no callobj of its own and
  1.1209 +     *    JS_GetFrameCallObject will return the innermost function's callobj.
  1.1210 +     */
  1.1211 +    while (o) {
  1.1212 +        ScopeObject &scope = o->as<DebugScopeObject>().scope();
  1.1213 +        if (scope.is<CallObject>())
  1.1214 +            return o;
  1.1215 +        o = o->enclosingScope();
  1.1216 +    }
  1.1217 +    return nullptr;
  1.1218 +}
  1.1219 +
  1.1220 +JSFunction *
  1.1221 +JSAbstractFramePtr::maybeFun()
  1.1222 +{
  1.1223 +    AbstractFramePtr frame(*this);
  1.1224 +    return frame.maybeFun();
  1.1225 +}
  1.1226 +
  1.1227 +JSScript *
  1.1228 +JSAbstractFramePtr::script()
  1.1229 +{
  1.1230 +    AbstractFramePtr frame(*this);
  1.1231 +    return frame.script();
  1.1232 +}
  1.1233 +
  1.1234 +bool
  1.1235 +JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
  1.1236 +{
  1.1237 +    AbstractFramePtr frame(*this);
  1.1238 +
  1.1239 +    RootedObject scopeChain(cx, frame.scopeChain());
  1.1240 +    js::AutoCompartment ac(cx, scopeChain);
  1.1241 +    if (!ComputeThis(cx, frame))
  1.1242 +        return false;
  1.1243 +
  1.1244 +    thisv.set(frame.thisValue());
  1.1245 +    return true;
  1.1246 +}
  1.1247 +
  1.1248 +bool
  1.1249 +JSAbstractFramePtr::isDebuggerFrame()
  1.1250 +{
  1.1251 +    AbstractFramePtr frame(*this);
  1.1252 +    return frame.isDebuggerFrame();
  1.1253 +}
  1.1254 +
  1.1255 +bool
  1.1256 +JSAbstractFramePtr::evaluateInStackFrame(JSContext *cx,
  1.1257 +                                         const char *bytes, unsigned length,
  1.1258 +                                         const char *filename, unsigned lineno,
  1.1259 +                                         MutableHandleValue rval)
  1.1260 +{
  1.1261 +    if (!CheckDebugMode(cx))
  1.1262 +        return false;
  1.1263 +
  1.1264 +    size_t len = length;
  1.1265 +    jschar *chars = InflateString(cx, bytes, &len);
  1.1266 +    if (!chars)
  1.1267 +        return false;
  1.1268 +    length = (unsigned) len;
  1.1269 +
  1.1270 +    bool ok = evaluateUCInStackFrame(cx, chars, length, filename, lineno, rval);
  1.1271 +    js_free(chars);
  1.1272 +
  1.1273 +    return ok;
  1.1274 +}
  1.1275 +
  1.1276 +bool
  1.1277 +JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
  1.1278 +                                           const jschar *chars, unsigned length,
  1.1279 +                                           const char *filename, unsigned lineno,
  1.1280 +                                           MutableHandleValue rval)
  1.1281 +{
  1.1282 +    if (!CheckDebugMode(cx))
  1.1283 +        return false;
  1.1284 +
  1.1285 +    RootedObject scope(cx, scopeChain(cx));
  1.1286 +    Rooted<Env*> env(cx, scope);
  1.1287 +    if (!env)
  1.1288 +        return false;
  1.1289 +
  1.1290 +    AbstractFramePtr frame(*this);
  1.1291 +    if (!ComputeThis(cx, frame))
  1.1292 +        return false;
  1.1293 +    RootedValue thisv(cx, frame.thisValue());
  1.1294 +
  1.1295 +    js::AutoCompartment ac(cx, env);
  1.1296 +    return EvaluateInEnv(cx, env, thisv, frame, ConstTwoByteChars(chars, length), length,
  1.1297 +                         filename, lineno, rval);
  1.1298 +}
  1.1299 +
  1.1300 +JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx)
  1.1301 +{
  1.1302 +    // Show all frames on the stack whose principal is subsumed by the current principal.
  1.1303 +    NonBuiltinScriptFrameIter iter(cx,
  1.1304 +                                   ScriptFrameIter::ALL_CONTEXTS,
  1.1305 +                                   ScriptFrameIter::GO_THROUGH_SAVED,
  1.1306 +                                   cx->compartment()->principals);
  1.1307 +    data_ = iter.copyData();
  1.1308 +}
  1.1309 +
  1.1310 +JSBrokenFrameIterator::~JSBrokenFrameIterator()
  1.1311 +{
  1.1312 +    js_free((ScriptFrameIter::Data *)data_);
  1.1313 +}
  1.1314 +
  1.1315 +bool
  1.1316 +JSBrokenFrameIterator::done() const
  1.1317 +{
  1.1318 +    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1.1319 +    return iter.done();
  1.1320 +}
  1.1321 +
  1.1322 +JSBrokenFrameIterator &
  1.1323 +JSBrokenFrameIterator::operator++()
  1.1324 +{
  1.1325 +    ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_;
  1.1326 +    NonBuiltinScriptFrameIter iter(*data);
  1.1327 +    ++iter;
  1.1328 +    *data = iter.data_;
  1.1329 +    return *this;
  1.1330 +}
  1.1331 +
  1.1332 +JSAbstractFramePtr
  1.1333 +JSBrokenFrameIterator::abstractFramePtr() const
  1.1334 +{
  1.1335 +    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1.1336 +    return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc());
  1.1337 +}
  1.1338 +
  1.1339 +jsbytecode *
  1.1340 +JSBrokenFrameIterator::pc() const
  1.1341 +{
  1.1342 +    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1.1343 +    return iter.pc();
  1.1344 +}
  1.1345 +
  1.1346 +bool
  1.1347 +JSBrokenFrameIterator::isConstructing() const
  1.1348 +{
  1.1349 +    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
  1.1350 +    return iter.isConstructing();
  1.1351 +}

mercurial