mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
278ef20681
--HG-- extra : rebase_source : b344fae9d8f62e045bac61a4481cf80c32bd38d1
1413 lines
39 KiB
C++
1413 lines
39 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/*
|
|
* JS debugging API.
|
|
*/
|
|
|
|
#include "jsdbgapi.h"
|
|
|
|
#include <string.h>
|
|
#include "jsprvtd.h"
|
|
#include "jstypes.h"
|
|
#include "jsapi.h"
|
|
#include "jscntxt.h"
|
|
#include "jsfun.h"
|
|
#include "jsgc.h"
|
|
#include "jsinterp.h"
|
|
#include "jsobj.h"
|
|
#include "jsopcode.h"
|
|
#include "jsscript.h"
|
|
#include "jsstr.h"
|
|
#include "jswatchpoint.h"
|
|
#include "jswrapper.h"
|
|
|
|
#include "frontend/BytecodeEmitter.h"
|
|
#include "vm/Debugger.h"
|
|
#include "vm/Shape.h"
|
|
|
|
#ifdef JS_ASMJS
|
|
#include "ion/AsmJSModule.h"
|
|
#endif
|
|
|
|
#include "jsatominlines.h"
|
|
#include "jsinferinlines.h"
|
|
#include "jsobjinlines.h"
|
|
#include "jsinterpinlines.h"
|
|
#include "jsscriptinlines.h"
|
|
|
|
#include "vm/Stack-inl.h"
|
|
|
|
using namespace js;
|
|
using namespace js::gc;
|
|
|
|
using mozilla::PodZero;
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_GetDebugMode(JSContext *cx)
|
|
{
|
|
return cx->compartment->debugMode();
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetDebugMode(JSContext *cx, JSBool debug)
|
|
{
|
|
return JS_SetDebugModeForCompartment(cx, cx->compartment, debug);
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
|
|
{
|
|
rt->debugMode = !!debug;
|
|
}
|
|
|
|
static bool
|
|
IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame)
|
|
{
|
|
ScriptFrameIter iter(cx);
|
|
JS_ASSERT(iter.abstractFramePtr() == frame);
|
|
return iter.isConstructing();
|
|
}
|
|
|
|
JSTrapStatus
|
|
js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame)
|
|
{
|
|
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->fp());
|
|
|
|
if (!frame.script()->selfHosted) {
|
|
if (frame.isFramePushedByExecute()) {
|
|
if (JSInterpreterHook hook = cx->runtime->debugHooks.executeHook)
|
|
frame.setHookData(hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
|
|
true, 0, cx->runtime->debugHooks.executeHookData));
|
|
} else {
|
|
if (JSInterpreterHook hook = cx->runtime->debugHooks.callHook)
|
|
frame.setHookData(hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame),
|
|
true, 0, cx->runtime->debugHooks.callHookData));
|
|
}
|
|
}
|
|
|
|
RootedValue rval(cx);
|
|
JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval);
|
|
switch (status) {
|
|
case JSTRAP_CONTINUE:
|
|
break;
|
|
case JSTRAP_THROW:
|
|
cx->setPendingException(rval);
|
|
break;
|
|
case JSTRAP_ERROR:
|
|
cx->clearPendingException();
|
|
break;
|
|
case JSTRAP_RETURN:
|
|
frame.setReturnValue(rval);
|
|
break;
|
|
default:
|
|
JS_NOT_REACHED("bad Debugger::onEnterFrame JSTrapStatus value");
|
|
}
|
|
return status;
|
|
}
|
|
|
|
bool
|
|
js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, bool okArg)
|
|
{
|
|
JS_ASSERT_IF(frame.isStackFrame(), frame.asStackFrame() == cx->fp());
|
|
JSBool ok = okArg;
|
|
|
|
// We don't add hook data for self-hosted scripts, so we don't need to check for them, here.
|
|
if (void *hookData = frame.maybeHookData()) {
|
|
if (frame.isFramePushedByExecute()) {
|
|
if (JSInterpreterHook hook = cx->runtime->debugHooks.executeHook)
|
|
hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
|
} else {
|
|
if (JSInterpreterHook hook = cx->runtime->debugHooks.callHook)
|
|
hook(cx, Jsvalify(frame), IsTopFrameConstructing(cx, frame), false, &ok, hookData);
|
|
}
|
|
}
|
|
|
|
return Debugger::onLeaveFrame(cx, frame, ok);
|
|
}
|
|
|
|
JSTrapStatus
|
|
js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc)
|
|
{
|
|
JS_ASSERT(cx->compartment->debugMode());
|
|
|
|
if (!cx->runtime->debugHooks.throwHook && cx->compartment->getDebuggees().empty())
|
|
return JSTRAP_CONTINUE;
|
|
|
|
/* Call debugger throw hook if set. */
|
|
RootedValue rval(cx);
|
|
JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval);
|
|
if (status == JSTRAP_CONTINUE) {
|
|
if (JSThrowHook handler = cx->runtime->debugHooks.throwHook) {
|
|
RootedScript script(cx, frame.script());
|
|
status = handler(cx, script, pc, rval.address(), cx->runtime->debugHooks.throwHookData);
|
|
}
|
|
}
|
|
|
|
switch (status) {
|
|
case JSTRAP_ERROR:
|
|
cx->clearPendingException();
|
|
break;
|
|
|
|
case JSTRAP_RETURN:
|
|
cx->clearPendingException();
|
|
frame.setReturnValue(rval);
|
|
break;
|
|
|
|
case JSTRAP_THROW:
|
|
cx->setPendingException(rval);
|
|
break;
|
|
|
|
case JSTRAP_CONTINUE:
|
|
break;
|
|
|
|
default:
|
|
JS_NOT_REACHED("Invalid trap status");
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
JS_SetDebugModeForAllCompartments(JSContext *cx, JSBool debug)
|
|
{
|
|
AutoDebugModeGC dmgc(cx->runtime);
|
|
|
|
for (CompartmentsIter c(cx->runtime); !c.done(); c.next()) {
|
|
// Ignore special compartments (atoms, JSD compartments)
|
|
if (c->principals) {
|
|
if (!c->setDebugModeFromC(cx, !!debug, dmgc))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
|
|
{
|
|
AutoDebugModeGC dmgc(cx->runtime);
|
|
return comp->setDebugModeFromC(cx, !!debug, dmgc);
|
|
}
|
|
|
|
static JSBool
|
|
CheckDebugMode(JSContext *cx)
|
|
{
|
|
JSBool debugMode = JS_GetDebugMode(cx);
|
|
/*
|
|
* :TODO:
|
|
* This probably should be an assertion, since it's indicative of a severe
|
|
* API misuse.
|
|
*/
|
|
if (!debugMode) {
|
|
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
|
|
NULL, JSMSG_NEED_DEBUG_MODE);
|
|
}
|
|
return debugMode;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetSingleStepMode(JSContext *cx, JSScript *scriptArg, JSBool singleStep)
|
|
{
|
|
RootedScript script(cx, scriptArg);
|
|
assertSameCompartment(cx, script);
|
|
|
|
if (!CheckDebugMode(cx))
|
|
return JS_FALSE;
|
|
|
|
return script->setStepModeFlag(cx, singleStep);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetTrap(JSContext *cx, JSScript *scriptArg, jsbytecode *pc, JSTrapHandler handler, jsval closureArg)
|
|
{
|
|
RootedScript script(cx, scriptArg);
|
|
RootedValue closure(cx, closureArg);
|
|
assertSameCompartment(cx, script, closure);
|
|
|
|
if (!CheckDebugMode(cx))
|
|
return false;
|
|
|
|
BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
|
|
if (!site)
|
|
return false;
|
|
site->setTrap(cx->runtime->defaultFreeOp(), handler, closure);
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|
JSTrapHandler *handlerp, jsval *closurep)
|
|
{
|
|
if (BreakpointSite *site = script->getBreakpointSite(pc)) {
|
|
site->clearTrap(cx->runtime->defaultFreeOp(), handlerp, closurep);
|
|
} else {
|
|
if (handlerp)
|
|
*handlerp = NULL;
|
|
if (closurep)
|
|
*closurep = JSVAL_VOID;
|
|
}
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_ClearScriptTraps(JSContext *cx, JSScript *script)
|
|
{
|
|
script->clearTraps(cx->runtime->defaultFreeOp());
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_ClearAllTrapsForCompartment(JSContext *cx)
|
|
{
|
|
cx->compartment->clearTraps(cx->runtime->defaultFreeOp());
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
|
|
{
|
|
rt->debugHooks.interruptHook = hook;
|
|
rt->debugHooks.interruptHookData = closure;
|
|
for (InterpreterFrames *f = rt->interpreterFrames; f; f = f->older)
|
|
f->enableInterruptsUnconditionally();
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
|
|
{
|
|
if (hoop)
|
|
*hoop = rt->debugHooks.interruptHook;
|
|
if (closurep)
|
|
*closurep = rt->debugHooks.interruptHookData;
|
|
rt->debugHooks.interruptHook = 0;
|
|
rt->debugHooks.interruptHookData = 0;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetWatchPoint(JSContext *cx, JSObject *obj_, jsid id_,
|
|
JSWatchPointHandler handler, JSObject *closure_)
|
|
{
|
|
assertSameCompartment(cx, obj_);
|
|
|
|
RootedId id(cx, id_);
|
|
RootedObject origobj(cx, obj_), closure(cx, closure_);
|
|
RootedObject obj(cx, GetInnerObject(cx, origobj));
|
|
if (!obj)
|
|
return false;
|
|
|
|
RootedValue v(cx);
|
|
unsigned attrs;
|
|
|
|
RootedId propid(cx);
|
|
|
|
if (JSID_IS_INT(id)) {
|
|
propid = id;
|
|
} else if (JSID_IS_OBJECT(id)) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH_PROP);
|
|
return false;
|
|
} else {
|
|
if (!ValueToId<CanGC>(cx, IdToValue(id), &propid))
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* If, by unwrapping and innerizing, we changed the object, check
|
|
* again to make sure that we're allowed to set a watch point.
|
|
*/
|
|
if (origobj != obj && !CheckAccess(cx, obj, propid, JSACC_WATCH, &v, &attrs))
|
|
return false;
|
|
|
|
if (!obj->isNative()) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH,
|
|
obj->getClass()->name);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Use sparse indexes for watched objects, as dense elements can be written
|
|
* to without checking the watchpoint map.
|
|
*/
|
|
if (!JSObject::sparsifyDenseElements(cx, obj))
|
|
return false;
|
|
|
|
types::MarkTypePropertyConfigured(cx, obj, propid);
|
|
|
|
WatchpointMap *wpmap = cx->compartment->watchpointMap;
|
|
if (!wpmap) {
|
|
wpmap = cx->runtime->new_<WatchpointMap>();
|
|
if (!wpmap || !wpmap->init()) {
|
|
js_ReportOutOfMemory(cx);
|
|
return false;
|
|
}
|
|
cx->compartment->watchpointMap = wpmap;
|
|
}
|
|
return wpmap->watch(cx, obj, propid, handler, closure);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id,
|
|
JSWatchPointHandler *handlerp, JSObject **closurep)
|
|
{
|
|
assertSameCompartment(cx, obj, id);
|
|
|
|
if (WatchpointMap *wpmap = cx->compartment->watchpointMap)
|
|
wpmap->unwatch(obj, id, handlerp, closurep);
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj)
|
|
{
|
|
assertSameCompartment(cx, obj);
|
|
|
|
if (WatchpointMap *wpmap = cx->compartment->watchpointMap)
|
|
wpmap->unwatchObject(obj);
|
|
return true;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_ClearAllWatchPoints(JSContext *cx)
|
|
{
|
|
if (JSCompartment *comp = cx->compartment) {
|
|
if (WatchpointMap *wpmap = comp->watchpointMap)
|
|
wpmap->clear();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(unsigned)
|
|
JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
|
|
{
|
|
return js::PCToLineNumber(script, pc);
|
|
}
|
|
|
|
JS_PUBLIC_API(jsbytecode *)
|
|
JS_LineNumberToPC(JSContext *cx, JSScript *script, unsigned lineno)
|
|
{
|
|
return js_LineNumberToPC(script, lineno);
|
|
}
|
|
|
|
JS_PUBLIC_API(jsbytecode *)
|
|
JS_EndPC(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->code + script->length;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_GetLinePCs(JSContext *cx, JSScript *script,
|
|
unsigned startLine, unsigned maxLines,
|
|
unsigned* count, unsigned** retLines, jsbytecode*** retPCs)
|
|
{
|
|
size_t len = (script->length > maxLines ? maxLines : script->length);
|
|
unsigned *lines = cx->pod_malloc<unsigned>(len);
|
|
if (!lines)
|
|
return JS_FALSE;
|
|
|
|
jsbytecode **pcs = cx->pod_malloc<jsbytecode*>(len);
|
|
if (!pcs) {
|
|
js_free(lines);
|
|
return JS_FALSE;
|
|
}
|
|
|
|
unsigned lineno = script->lineno;
|
|
unsigned offset = 0;
|
|
unsigned i = 0;
|
|
for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
|
|
offset += SN_DELTA(sn);
|
|
SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
|
|
if (type == SRC_SETLINE || type == SRC_NEWLINE) {
|
|
if (type == SRC_SETLINE)
|
|
lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
|
|
else
|
|
lineno++;
|
|
|
|
if (lineno >= startLine) {
|
|
lines[i] = lineno;
|
|
pcs[i] = script->code + offset;
|
|
if (++i >= maxLines)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*count = i;
|
|
if (retLines)
|
|
*retLines = lines;
|
|
else
|
|
js_free(lines);
|
|
|
|
if (retPCs)
|
|
*retPCs = pcs;
|
|
else
|
|
js_free(pcs);
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(unsigned)
|
|
JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return fun->nargs;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return fun->nonLazyScript()->bindings.count() > 0;
|
|
}
|
|
|
|
extern JS_PUBLIC_API(uintptr_t *)
|
|
JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **memp)
|
|
{
|
|
RootedScript script(cx, fun->nonLazyScript());
|
|
BindingVector bindings(cx);
|
|
if (!FillBindingVector(script, &bindings))
|
|
return NULL;
|
|
|
|
LifoAlloc &lifo = cx->tempLifoAlloc();
|
|
|
|
// Store the LifoAlloc::Mark right before the allocation.
|
|
LifoAlloc::Mark mark = lifo.mark();
|
|
void *mem = lifo.alloc(sizeof(LifoAlloc::Mark) + bindings.length() * sizeof(uintptr_t));
|
|
if (!mem) {
|
|
js_ReportOutOfMemory(cx);
|
|
return NULL;
|
|
}
|
|
*memp = mem;
|
|
*reinterpret_cast<LifoAlloc::Mark*>(mem) = mark;
|
|
|
|
// Munge data into the API this method implements. Avert your eyes!
|
|
uintptr_t *names = reinterpret_cast<uintptr_t*>((char*)mem + sizeof(LifoAlloc::Mark));
|
|
for (size_t i = 0; i < bindings.length(); i++)
|
|
names[i] = reinterpret_cast<uintptr_t>(bindings[i].name());
|
|
|
|
return names;
|
|
}
|
|
|
|
extern JS_PUBLIC_API(JSAtom *)
|
|
JS_LocalNameToAtom(uintptr_t w)
|
|
{
|
|
return reinterpret_cast<JSAtom *>(w);
|
|
}
|
|
|
|
extern JS_PUBLIC_API(JSString *)
|
|
JS_AtomKey(JSAtom *atom)
|
|
{
|
|
return atom;
|
|
}
|
|
|
|
extern JS_PUBLIC_API(void)
|
|
JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mem)
|
|
{
|
|
cx->tempLifoAlloc().release(*reinterpret_cast<LifoAlloc::Mark*>(mem));
|
|
}
|
|
|
|
JS_PUBLIC_API(JSScript *)
|
|
JS_GetFunctionScript(JSContext *cx, JSFunction *fun)
|
|
{
|
|
if (fun->isNative())
|
|
return NULL;
|
|
if (fun->isInterpretedLazy()) {
|
|
RootedFunction rootedFun(cx, fun);
|
|
AutoCompartment funCompartment(cx, rootedFun);
|
|
JSScript *script = rootedFun->getOrCreateScript(cx);
|
|
if (!script)
|
|
MOZ_CRASH();
|
|
return script;
|
|
}
|
|
return fun->nonLazyScript();
|
|
}
|
|
|
|
JS_PUBLIC_API(JSNative)
|
|
JS_GetFunctionNative(JSContext *cx, JSFunction *fun)
|
|
{
|
|
return fun->maybeNative();
|
|
}
|
|
|
|
JS_PUBLIC_API(JSPrincipals *)
|
|
JS_GetScriptPrincipals(JSScript *script)
|
|
{
|
|
return script->principals();
|
|
}
|
|
|
|
JS_PUBLIC_API(JSPrincipals *)
|
|
JS_GetScriptOriginPrincipals(JSScript *script)
|
|
{
|
|
return script->originPrincipals;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSFunction *)
|
|
JS_GetScriptFunction(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->function();
|
|
}
|
|
|
|
JS_PUBLIC_API(JSObject *)
|
|
JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
|
|
{
|
|
return obj->enclosingScope();
|
|
}
|
|
|
|
JS_PUBLIC_API(const char *)
|
|
JS_GetDebugClassName(JSObject *obj)
|
|
{
|
|
if (obj->isDebugScope())
|
|
return obj->asDebugScope().scope().getClass()->name;
|
|
return obj->getClass()->name;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(const char *)
|
|
JS_GetScriptFilename(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->filename();
|
|
}
|
|
|
|
JS_PUBLIC_API(const jschar *)
|
|
JS_GetScriptSourceMap(JSContext *cx, JSScript *script)
|
|
{
|
|
ScriptSource *source = script->scriptSource();
|
|
JS_ASSERT(source);
|
|
return source->hasSourceMap() ? source->sourceMap() : NULL;
|
|
}
|
|
|
|
JS_PUBLIC_API(unsigned)
|
|
JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script)
|
|
{
|
|
return script->lineno;
|
|
}
|
|
|
|
JS_PUBLIC_API(unsigned)
|
|
JS_GetScriptLineExtent(JSContext *cx, JSScript *script)
|
|
{
|
|
return js_GetScriptLineExtent(script);
|
|
}
|
|
|
|
JS_PUBLIC_API(JSVersion)
|
|
JS_GetScriptVersion(JSContext *cx, JSScript *script)
|
|
{
|
|
return VersionNumber(script->getVersion());
|
|
}
|
|
|
|
JS_PUBLIC_API(bool)
|
|
JS_GetScriptUserBit(JSScript *script)
|
|
{
|
|
return script->userBit;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetScriptUserBit(JSScript *script, bool b)
|
|
{
|
|
script->userBit = b;
|
|
}
|
|
|
|
JS_PUBLIC_API(bool)
|
|
JS_GetScriptIsSelfHosted(JSScript *script)
|
|
{
|
|
return script->selfHosted;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata)
|
|
{
|
|
rt->debugHooks.newScriptHook = hook;
|
|
rt->debugHooks.newScriptHookData = callerdata;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook,
|
|
void *callerdata)
|
|
{
|
|
rt->debugHooks.destroyScriptHook = hook;
|
|
rt->debugHooks.destroyScriptHookData = callerdata;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
|
|
/* This all should be reworked to avoid requiring JSScopeProperty types. */
|
|
|
|
static JSBool
|
|
GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd)
|
|
{
|
|
assertSameCompartment(cx, obj_);
|
|
pd->id = IdToJsval(shape->propid());
|
|
|
|
RootedObject obj(cx, obj_);
|
|
|
|
JSBool wasThrowing = cx->isExceptionPending();
|
|
RootedValue lastException(cx, UndefinedValue());
|
|
if (wasThrowing)
|
|
lastException = cx->getPendingException();
|
|
cx->clearPendingException();
|
|
|
|
Rooted<jsid> id(cx, shape->propid());
|
|
RootedValue value(cx);
|
|
if (!baseops::GetProperty(cx, obj, id, &value)) {
|
|
if (!cx->isExceptionPending()) {
|
|
pd->flags = JSPD_ERROR;
|
|
pd->value = JSVAL_VOID;
|
|
} else {
|
|
pd->flags = JSPD_EXCEPTION;
|
|
pd->value = cx->getPendingException();
|
|
}
|
|
} else {
|
|
pd->flags = 0;
|
|
pd->value = value;
|
|
}
|
|
|
|
if (wasThrowing)
|
|
cx->setPendingException(lastException);
|
|
|
|
pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
|
|
| (!shape->writable() ? JSPD_READONLY : 0)
|
|
| (!shape->configurable() ? JSPD_PERMANENT : 0);
|
|
pd->spare = 0;
|
|
pd->alias = JSVAL_VOID;
|
|
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_GetPropertyDescArray(JSContext *cx, JSObject *obj_, JSPropertyDescArray *pda)
|
|
{
|
|
RootedObject obj(cx, obj_);
|
|
|
|
assertSameCompartment(cx, obj);
|
|
uint32_t i = 0;
|
|
JSPropertyDesc *pd = NULL;
|
|
|
|
if (obj->isDebugScope()) {
|
|
AutoIdVector props(cx);
|
|
if (!Proxy::enumerate(cx, obj, props))
|
|
return false;
|
|
|
|
pd = cx->pod_calloc<JSPropertyDesc>(props.length());
|
|
if (!pd)
|
|
return false;
|
|
|
|
for (i = 0; i < props.length(); ++i) {
|
|
pd[i].id = JSVAL_NULL;
|
|
pd[i].value = JSVAL_NULL;
|
|
if (!AddValueRoot(cx, &pd[i].id, NULL))
|
|
goto bad;
|
|
pd[i].id = IdToValue(props[i]);
|
|
if (!AddValueRoot(cx, &pd[i].value, NULL))
|
|
goto bad;
|
|
if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value)))
|
|
goto bad;
|
|
}
|
|
|
|
pda->length = props.length();
|
|
pda->array = pd;
|
|
return true;
|
|
}
|
|
|
|
Class *clasp;
|
|
clasp = obj->getClass();
|
|
if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
|
JSMSG_CANT_DESCRIBE_PROPS, clasp->name);
|
|
return false;
|
|
}
|
|
if (!clasp->enumerate(cx, obj))
|
|
return false;
|
|
|
|
/* Return an empty pda early if obj has no own properties. */
|
|
if (obj->nativeEmpty()) {
|
|
pda->length = 0;
|
|
pda->array = NULL;
|
|
return true;
|
|
}
|
|
|
|
pd = cx->pod_malloc<JSPropertyDesc>(obj->propertyCount());
|
|
if (!pd)
|
|
return false;
|
|
|
|
{
|
|
Shape::Range<CanGC> r(cx, obj->lastProperty());
|
|
RootedShape shape(cx);
|
|
for (; !r.empty(); r.popFront()) {
|
|
pd[i].id = JSVAL_NULL;
|
|
pd[i].value = JSVAL_NULL;
|
|
pd[i].alias = JSVAL_NULL;
|
|
if (!AddValueRoot(cx, &pd[i].id, NULL))
|
|
goto bad;
|
|
if (!AddValueRoot(cx, &pd[i].value, NULL))
|
|
goto bad;
|
|
shape = const_cast<Shape *>(&r.front());
|
|
if (!GetPropertyDesc(cx, obj, shape, &pd[i]))
|
|
goto bad;
|
|
if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, NULL))
|
|
goto bad;
|
|
if (++i == obj->propertyCount())
|
|
break;
|
|
}
|
|
}
|
|
|
|
pda->length = i;
|
|
pda->array = pd;
|
|
return true;
|
|
|
|
bad:
|
|
pda->length = i + 1;
|
|
pda->array = pd;
|
|
JS_PutPropertyDescArray(cx, pda);
|
|
return false;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda)
|
|
{
|
|
JSPropertyDesc *pd;
|
|
uint32_t i;
|
|
|
|
pd = pda->array;
|
|
for (i = 0; i < pda->length; i++) {
|
|
js_RemoveRoot(cx->runtime, &pd[i].id);
|
|
js_RemoveRoot(cx->runtime, &pd[i].value);
|
|
if (pd[i].flags & JSPD_ALIAS)
|
|
js_RemoveRoot(cx->runtime, &pd[i].alias);
|
|
}
|
|
js_free(pd);
|
|
pda->array = NULL;
|
|
pda->length = 0;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
|
|
{
|
|
rt->debugHooks.debuggerHandler = handler;
|
|
rt->debugHooks.debuggerHandlerData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
|
|
{
|
|
rt->debugHooks.sourceHandler = handler;
|
|
rt->debugHooks.sourceHandlerData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
|
|
{
|
|
rt->debugHooks.executeHook = hook;
|
|
rt->debugHooks.executeHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure)
|
|
{
|
|
rt->debugHooks.callHook = hook;
|
|
rt->debugHooks.callHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
|
|
{
|
|
rt->debugHooks.throwHook = hook;
|
|
rt->debugHooks.throwHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
JS_PUBLIC_API(JSBool)
|
|
JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
|
|
{
|
|
rt->debugHooks.debugErrorHook = hook;
|
|
rt->debugHooks.debugErrorHookData = closure;
|
|
return JS_TRUE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_FRIEND_API(void)
|
|
js_RevertVersion(JSContext *cx)
|
|
{
|
|
cx->clearVersionOverride();
|
|
}
|
|
|
|
JS_PUBLIC_API(const JSDebugHooks *)
|
|
JS_GetGlobalDebugHooks(JSRuntime *rt)
|
|
{
|
|
return &rt->debugHooks;
|
|
}
|
|
|
|
/************************************************************************/
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_DumpBytecode(JSContext *cx, JSScript *scriptArg)
|
|
{
|
|
#if defined(DEBUG)
|
|
Rooted<JSScript*> script(cx, scriptArg);
|
|
|
|
Sprinter sprinter(cx);
|
|
if (!sprinter.init())
|
|
return;
|
|
|
|
fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), script->lineno);
|
|
js_Disassemble(cx, script, true, &sprinter);
|
|
fputs(sprinter.string(), stdout);
|
|
fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), script->lineno);
|
|
#endif
|
|
}
|
|
|
|
extern JS_PUBLIC_API(void)
|
|
JS_DumpPCCounts(JSContext *cx, JSScript *scriptArg)
|
|
{
|
|
#if defined(DEBUG)
|
|
Rooted<JSScript*> script(cx, scriptArg);
|
|
JS_ASSERT(script->hasScriptCounts);
|
|
|
|
Sprinter sprinter(cx);
|
|
if (!sprinter.init())
|
|
return;
|
|
|
|
fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), script->lineno);
|
|
js_DumpPCCounts(cx, script, &sprinter);
|
|
fputs(sprinter.string(), stdout);
|
|
fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), script->lineno);
|
|
#endif
|
|
}
|
|
|
|
namespace {
|
|
|
|
typedef Vector<JSScript *, 0, SystemAllocPolicy> ScriptsToDump;
|
|
|
|
static void
|
|
DumpBytecodeScriptCallback(JSRuntime *rt, void *data, JSScript *script)
|
|
{
|
|
static_cast<ScriptsToDump *>(data)->append(script);
|
|
}
|
|
|
|
} /* anonymous namespace */
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_DumpCompartmentBytecode(JSContext *cx)
|
|
{
|
|
ScriptsToDump scripts;
|
|
IterateScripts(cx->runtime, cx->compartment, &scripts, DumpBytecodeScriptCallback);
|
|
|
|
for (size_t i = 0; i < scripts.length(); i++) {
|
|
if (scripts[i]->enclosingScriptsCompiledSuccessfully())
|
|
JS_DumpBytecode(cx, scripts[i]);
|
|
}
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS_DumpCompartmentPCCounts(JSContext *cx)
|
|
{
|
|
for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
|
|
JSScript *script = i.get<JSScript>();
|
|
if (script->compartment() != cx->compartment)
|
|
continue;
|
|
|
|
if (script->hasScriptCounts && script->enclosingScriptsCompiledSuccessfully())
|
|
JS_DumpPCCounts(cx, script);
|
|
}
|
|
|
|
#if defined(JS_ASMJS) && defined(DEBUG)
|
|
for (unsigned thingKind = FINALIZE_OBJECT0; thingKind < FINALIZE_OBJECT_LIMIT; thingKind++) {
|
|
for (CellIter i(cx->zone(), (AllocKind) thingKind); !i.done(); i.next()) {
|
|
JSObject *obj = i.get<JSObject>();
|
|
if (obj->compartment() != cx->compartment)
|
|
continue;
|
|
|
|
if (IsAsmJSModuleObject(obj)) {
|
|
AsmJSModule &module = AsmJSModuleObjectToModule(obj);
|
|
|
|
Sprinter sprinter(cx);
|
|
if (!sprinter.init())
|
|
return;
|
|
|
|
fprintf(stdout, "--- Asm.js Module ---\n");
|
|
|
|
for (size_t i = 0; i < module.numFunctionCounts(); i++) {
|
|
ion::IonScriptCounts *counts = module.functionCounts(i);
|
|
DumpIonScriptCounts(&sprinter, counts);
|
|
}
|
|
|
|
fputs(sprinter.string(), stdout);
|
|
fprintf(stdout, "--- END Asm.js Module ---\n");
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
JS_FRIEND_API(JSBool)
|
|
js_CallContextDebugHandler(JSContext *cx)
|
|
{
|
|
NonBuiltinScriptFrameIter iter(cx);
|
|
JS_ASSERT(!iter.done());
|
|
|
|
RootedValue rval(cx);
|
|
RootedScript script(cx, iter.script());
|
|
switch (js::CallContextDebugHandler(cx, script, iter.pc(), rval.address())) {
|
|
case JSTRAP_ERROR:
|
|
JS_ClearPendingException(cx);
|
|
return JS_FALSE;
|
|
case JSTRAP_THROW:
|
|
JS_SetPendingException(cx, rval);
|
|
return JS_FALSE;
|
|
case JSTRAP_RETURN:
|
|
case JSTRAP_CONTINUE:
|
|
default:
|
|
return JS_TRUE;
|
|
}
|
|
}
|
|
|
|
JS_PUBLIC_API(JS::StackDescription *)
|
|
JS::DescribeStack(JSContext *cx, unsigned maxFrames)
|
|
{
|
|
Vector<FrameDescription> frames(cx);
|
|
|
|
for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
|
|
FrameDescription desc;
|
|
desc.script = i.script();
|
|
desc.lineno = PCToLineNumber(i.script(), i.pc());
|
|
desc.fun = i.maybeCallee();
|
|
if (!frames.append(desc))
|
|
return NULL;
|
|
if (frames.length() == maxFrames)
|
|
break;
|
|
}
|
|
|
|
JS::StackDescription *desc = js_new<JS::StackDescription>();
|
|
if (!desc)
|
|
return NULL;
|
|
|
|
desc->nframes = frames.length();
|
|
desc->frames = frames.extractRawBuffer();
|
|
return desc;
|
|
}
|
|
|
|
JS_PUBLIC_API(void)
|
|
JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc)
|
|
{
|
|
js_delete(desc->frames);
|
|
js_delete(desc);
|
|
}
|
|
|
|
class AutoPropertyDescArray
|
|
{
|
|
JSContext *cx_;
|
|
JSPropertyDescArray descArray_;
|
|
|
|
public:
|
|
AutoPropertyDescArray(JSContext *cx)
|
|
: cx_(cx)
|
|
{
|
|
PodZero(&descArray_);
|
|
}
|
|
~AutoPropertyDescArray()
|
|
{
|
|
if (descArray_.array)
|
|
JS_PutPropertyDescArray(cx_, &descArray_);
|
|
}
|
|
|
|
void fetch(JSObject *obj) {
|
|
JS_ASSERT(!descArray_.array);
|
|
if (!JS_GetPropertyDescArray(cx_, obj, &descArray_))
|
|
descArray_.array = NULL;
|
|
}
|
|
|
|
JSPropertyDescArray * operator ->() {
|
|
return &descArray_;
|
|
}
|
|
};
|
|
|
|
static const char *
|
|
FormatValue(JSContext *cx, const Value &v, JSAutoByteString &bytes)
|
|
{
|
|
JSString *str = ToString<CanGC>(cx, v);
|
|
if (!str)
|
|
return NULL;
|
|
const char *buf = bytes.encodeLatin1(cx, str);
|
|
if (!buf)
|
|
return NULL;
|
|
const char *found = strstr(buf, "function ");
|
|
if (found && (found - buf <= 2))
|
|
return "[function]";
|
|
return buf;
|
|
}
|
|
|
|
static char *
|
|
FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num,
|
|
JSBool showArgs, JSBool showLocals, JSBool showThisProps)
|
|
{
|
|
RootedScript script(cx, iter.script());
|
|
jsbytecode* pc = iter.pc();
|
|
|
|
RootedObject scopeChain(cx, iter.scopeChain());
|
|
JSAutoCompartment ac(cx, scopeChain);
|
|
|
|
const char *filename = script->filename();
|
|
unsigned lineno = PCToLineNumber(script, pc);
|
|
RootedFunction fun(cx, iter.maybeCallee());
|
|
RootedString funname(cx);
|
|
if (fun)
|
|
funname = fun->atom();
|
|
|
|
RootedObject callObj(cx);
|
|
AutoPropertyDescArray callProps(cx);
|
|
|
|
if (!iter.isIon() && (showArgs || showLocals)) {
|
|
JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr()));
|
|
callObj = frame.callObject(cx);
|
|
if (callObj)
|
|
callProps.fetch(callObj);
|
|
}
|
|
|
|
RootedValue thisVal(cx);
|
|
AutoPropertyDescArray thisProps(cx);
|
|
if (iter.computeThis()) {
|
|
thisVal = iter.thisv();
|
|
if (showThisProps && !thisVal.isPrimitive())
|
|
thisProps.fetch(&thisVal.toObject());
|
|
}
|
|
|
|
// print the frame number and function name
|
|
if (funname) {
|
|
JSAutoByteString funbytes;
|
|
buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname));
|
|
} else if (fun) {
|
|
buf = JS_sprintf_append(buf, "%d anonymous(", num);
|
|
} else {
|
|
buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num);
|
|
}
|
|
if (!buf)
|
|
return buf;
|
|
|
|
// print the function arguments
|
|
if (showArgs && callObj) {
|
|
uint32_t namedArgCount = 0;
|
|
for (uint32_t i = 0; i < callProps->length; i++) {
|
|
JSPropertyDesc* desc = &callProps->array[i];
|
|
JSAutoByteString nameBytes;
|
|
const char *name = NULL;
|
|
if (JSVAL_IS_STRING(desc->id))
|
|
name = FormatValue(cx, desc->id, nameBytes);
|
|
|
|
JSAutoByteString valueBytes;
|
|
const char *value = FormatValue(cx, desc->value, valueBytes);
|
|
|
|
buf = JS_sprintf_append(buf, "%s%s%s%s%s%s",
|
|
namedArgCount ? ", " : "",
|
|
name ? name :"",
|
|
name ? " = " : "",
|
|
desc->value.isString() ? "\"" : "",
|
|
value ? value : "?unknown?",
|
|
desc->value.isString() ? "\"" : "");
|
|
if (!buf)
|
|
return buf;
|
|
namedArgCount++;
|
|
}
|
|
|
|
// print any unnamed trailing args (found in 'arguments' object)
|
|
RootedValue val(cx);
|
|
if (JS_GetProperty(cx, callObj, "arguments", val.address()) && val.isObject()) {
|
|
uint32_t argCount;
|
|
RootedObject argsObj(cx, &val.toObject());
|
|
if (JS_GetProperty(cx, argsObj, "length", val.address()) &&
|
|
ToUint32(cx, val, &argCount) &&
|
|
argCount > namedArgCount)
|
|
{
|
|
for (uint32_t k = namedArgCount; k < argCount; k++) {
|
|
char number[8];
|
|
JS_snprintf(number, 8, "%d", (int) k);
|
|
|
|
if (JS_GetProperty(cx, argsObj, number, val.address())) {
|
|
JSAutoByteString valueBytes;
|
|
const char *value = FormatValue(cx, val, valueBytes);
|
|
buf = JS_sprintf_append(buf, "%s%s%s%s",
|
|
k ? ", " : "",
|
|
val.isString() ? "\"" : "",
|
|
value ? value : "?unknown?",
|
|
val.isString() ? "\"" : "");
|
|
if (!buf)
|
|
return buf;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// print filename and line number
|
|
buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n",
|
|
fun ? ")" : "",
|
|
filename ? filename : "<unknown>",
|
|
lineno);
|
|
if (!buf)
|
|
return buf;
|
|
|
|
// print local variables
|
|
if (showLocals && callProps->array) {
|
|
for (uint32_t i = 0; i < callProps->length; i++) {
|
|
JSPropertyDesc* desc = &callProps->array[i];
|
|
JSAutoByteString nameBytes;
|
|
JSAutoByteString valueBytes;
|
|
const char *name = FormatValue(cx, desc->id, nameBytes);
|
|
const char *value = FormatValue(cx, desc->value, valueBytes);
|
|
|
|
if (name && value) {
|
|
buf = JS_sprintf_append(buf, " %s = %s%s%s\n",
|
|
name,
|
|
desc->value.isString() ? "\"" : "",
|
|
value,
|
|
desc->value.isString() ? "\"" : "");
|
|
if (!buf)
|
|
return buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
// print the value of 'this'
|
|
if (showLocals) {
|
|
if (!thisVal.isUndefined()) {
|
|
JSAutoByteString thisValBytes;
|
|
RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
|
|
if (thisValStr) {
|
|
if (const char *str = thisValBytes.encodeLatin1(cx, thisValStr)) {
|
|
buf = JS_sprintf_append(buf, " this = %s\n", str);
|
|
if (!buf)
|
|
return buf;
|
|
}
|
|
}
|
|
} else {
|
|
buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n");
|
|
}
|
|
}
|
|
|
|
// print the properties of 'this', if it is an object
|
|
if (showThisProps && thisProps->array) {
|
|
for (uint32_t i = 0; i < thisProps->length; i++) {
|
|
JSPropertyDesc* desc = &thisProps->array[i];
|
|
if (desc->flags & JSPD_ENUMERATE) {
|
|
JSAutoByteString nameBytes;
|
|
JSAutoByteString valueBytes;
|
|
const char *name = FormatValue(cx, desc->id, nameBytes);
|
|
const char *value = FormatValue(cx, desc->value, valueBytes);
|
|
if (name && value) {
|
|
buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n",
|
|
name,
|
|
desc->value.isString() ? "\"" : "",
|
|
value,
|
|
desc->value.isString() ? "\"" : "");
|
|
if (!buf)
|
|
return buf;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
JS_PUBLIC_API(char *)
|
|
JS::FormatStackDump(JSContext *cx, char *buf,
|
|
JSBool showArgs, JSBool showLocals,
|
|
JSBool showThisProps)
|
|
{
|
|
int num = 0;
|
|
|
|
for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) {
|
|
buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps);
|
|
num++;
|
|
}
|
|
|
|
if (!num)
|
|
buf = JS_sprintf_append(buf, "JavaScript stack is empty\n");
|
|
|
|
return buf;
|
|
}
|
|
|
|
JSAbstractFramePtr::JSAbstractFramePtr(void *raw)
|
|
: ptr_(uintptr_t(raw))
|
|
{ }
|
|
|
|
JSObject *
|
|
JSAbstractFramePtr::scopeChain(JSContext *cx)
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
JS_ASSERT_IF(frame.isStackFrame(),
|
|
cx->stack.space().containsSlow(frame.asStackFrame()));
|
|
RootedObject scopeChain(cx, frame.scopeChain());
|
|
AutoCompartment ac(cx, scopeChain);
|
|
return GetDebugScopeForFrame(cx, frame);
|
|
}
|
|
|
|
JSObject *
|
|
JSAbstractFramePtr::callObject(JSContext *cx)
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
JS_ASSERT_IF(frame.isStackFrame(),
|
|
cx->stack.space().containsSlow(frame.asStackFrame()));
|
|
|
|
if (!frame.isFunctionFrame())
|
|
return NULL;
|
|
|
|
JSObject *o = GetDebugScopeForFrame(cx, frame);
|
|
|
|
/*
|
|
* Given that fp is a function frame and GetDebugScopeForFrame always fills
|
|
* in missing scopes, we can expect to find fp's CallObject on 'o'. Note:
|
|
* - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with
|
|
* a DebugScopeObject proxy.
|
|
* - If fp is an eval-in-function, then fp has no callobj of its own and
|
|
* JS_GetFrameCallObject will return the innermost function's callobj.
|
|
*/
|
|
while (o) {
|
|
ScopeObject &scope = o->asDebugScope().scope();
|
|
if (scope.isCall())
|
|
return o;
|
|
o = o->enclosingScope();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
JSFunction *
|
|
JSAbstractFramePtr::maybeFun()
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
return frame.maybeFun();
|
|
}
|
|
|
|
JSScript *
|
|
JSAbstractFramePtr::script()
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
return frame.script();
|
|
}
|
|
|
|
bool
|
|
JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv)
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
|
|
RootedObject scopeChain(cx, frame.scopeChain());
|
|
js::AutoCompartment ac(cx, scopeChain);
|
|
if (!ComputeThis(cx, frame))
|
|
return false;
|
|
|
|
thisv.set(frame.thisValue());
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
JSAbstractFramePtr::isDebuggerFrame()
|
|
{
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
return frame.isDebuggerFrame();
|
|
}
|
|
|
|
bool
|
|
JSAbstractFramePtr::evaluateInStackFrame(JSContext *cx,
|
|
const char *bytes, unsigned length,
|
|
const char *filename, unsigned lineno,
|
|
MutableHandleValue rval)
|
|
{
|
|
if (!CheckDebugMode(cx))
|
|
return false;
|
|
|
|
size_t len = length;
|
|
jschar *chars = InflateString(cx, bytes, &len);
|
|
if (!chars)
|
|
return false;
|
|
length = (unsigned) len;
|
|
|
|
bool ok = evaluateUCInStackFrame(cx, chars, length, filename, lineno, rval);
|
|
js_free(chars);
|
|
|
|
return ok;
|
|
}
|
|
|
|
bool
|
|
JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx,
|
|
const jschar *chars, unsigned length,
|
|
const char *filename, unsigned lineno,
|
|
MutableHandleValue rval)
|
|
{
|
|
if (!CheckDebugMode(cx))
|
|
return false;
|
|
|
|
RootedObject scope(cx, scopeChain(cx));
|
|
Rooted<Env*> env(cx, scope);
|
|
if (!env)
|
|
return false;
|
|
|
|
AbstractFramePtr frame = Valueify(*this);
|
|
if (!ComputeThis(cx, frame))
|
|
return false;
|
|
RootedValue thisv(cx, frame.thisValue());
|
|
|
|
js::AutoCompartment ac(cx, env);
|
|
return EvaluateInEnv(cx, env, thisv, frame, StableCharPtr(chars, length), length,
|
|
filename, lineno, rval);
|
|
}
|
|
|
|
JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx)
|
|
{
|
|
NonBuiltinScriptFrameIter iter(cx);
|
|
data_ = iter.copyData();
|
|
}
|
|
|
|
JSBrokenFrameIterator::~JSBrokenFrameIterator()
|
|
{
|
|
js_free((ScriptFrameIter::Data *)data_);
|
|
}
|
|
|
|
bool
|
|
JSBrokenFrameIterator::done() const
|
|
{
|
|
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
|
|
return iter.done();
|
|
}
|
|
|
|
JSBrokenFrameIterator &
|
|
JSBrokenFrameIterator::operator++()
|
|
{
|
|
ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_;
|
|
NonBuiltinScriptFrameIter iter(*data);
|
|
++iter;
|
|
*data = iter.data_;
|
|
return *this;
|
|
}
|
|
|
|
JSAbstractFramePtr
|
|
JSBrokenFrameIterator::abstractFramePtr() const
|
|
{
|
|
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
|
|
return Jsvalify(iter.abstractFramePtr());
|
|
}
|
|
|
|
jsbytecode *
|
|
JSBrokenFrameIterator::pc() const
|
|
{
|
|
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
|
|
return iter.pc();
|
|
}
|
|
|
|
bool
|
|
JSBrokenFrameIterator::isConstructing() const
|
|
{
|
|
NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
|
|
return iter.isConstructing();
|
|
}
|