michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JS debugging API. michael@0: */ michael@0: michael@0: #include "js/OldDebugAPI.h" michael@0: michael@0: #include michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jsfun.h" michael@0: #include "jsgc.h" michael@0: #include "jsobj.h" michael@0: #include "jsopcode.h" michael@0: #include "jsprf.h" michael@0: #include "jsscript.h" michael@0: #include "jsstr.h" michael@0: #include "jstypes.h" michael@0: #include "jswatchpoint.h" michael@0: michael@0: #include "frontend/SourceNotes.h" michael@0: #include "jit/AsmJSModule.h" michael@0: #include "vm/Debugger.h" michael@0: #include "vm/Shape.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsinferinlines.h" michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "vm/Debugger-inl.h" michael@0: #include "vm/Interpreter-inl.h" michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: michael@0: using mozilla::PodZero; michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_GetDebugMode(JSContext *cx) michael@0: { michael@0: return cx->compartment()->debugMode(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetDebugMode(JSContext *cx, bool debug) michael@0: { michael@0: return JS_SetDebugModeForCompartment(cx, cx->compartment(), debug); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_SetRuntimeDebugMode(JSRuntime *rt, bool debug) michael@0: { michael@0: rt->debugMode = !!debug; michael@0: } michael@0: michael@0: static bool michael@0: IsTopFrameConstructing(JSContext *cx, AbstractFramePtr frame) michael@0: { michael@0: ScriptFrameIter iter(cx); michael@0: JS_ASSERT(iter.abstractFramePtr() == frame); michael@0: return iter.isConstructing(); michael@0: } michael@0: michael@0: JSTrapStatus michael@0: js::ScriptDebugPrologue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame()); michael@0: michael@0: if (!frame.script()->selfHosted()) { michael@0: JSAbstractFramePtr jsframe(frame.raw(), pc); michael@0: if (frame.isFramePushedByExecute()) { michael@0: if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook) michael@0: frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame), michael@0: true, 0, cx->runtime()->debugHooks.executeHookData)); michael@0: } else { michael@0: if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook) michael@0: frame.setHookData(hook(cx, jsframe, IsTopFrameConstructing(cx, frame), michael@0: true, 0, cx->runtime()->debugHooks.callHookData)); michael@0: } michael@0: } michael@0: michael@0: RootedValue rval(cx); michael@0: JSTrapStatus status = Debugger::onEnterFrame(cx, frame, &rval); michael@0: switch (status) { michael@0: case JSTRAP_CONTINUE: michael@0: break; michael@0: case JSTRAP_THROW: michael@0: cx->setPendingException(rval); michael@0: break; michael@0: case JSTRAP_ERROR: michael@0: cx->clearPendingException(); michael@0: break; michael@0: case JSTRAP_RETURN: michael@0: frame.setReturnValue(rval); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("bad Debugger::onEnterFrame JSTrapStatus value"); michael@0: } michael@0: return status; michael@0: } michael@0: michael@0: bool michael@0: js::ScriptDebugEpilogue(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc, bool okArg) michael@0: { michael@0: JS_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame()); michael@0: michael@0: bool ok = okArg; michael@0: michael@0: // We don't add hook data for self-hosted scripts, so we don't need to check for them, here. michael@0: if (void *hookData = frame.maybeHookData()) { michael@0: JSAbstractFramePtr jsframe(frame.raw(), pc); michael@0: if (frame.isFramePushedByExecute()) { michael@0: if (JSInterpreterHook hook = cx->runtime()->debugHooks.executeHook) michael@0: hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData); michael@0: } else { michael@0: if (JSInterpreterHook hook = cx->runtime()->debugHooks.callHook) michael@0: hook(cx, jsframe, IsTopFrameConstructing(cx, frame), false, &ok, hookData); michael@0: } michael@0: } michael@0: michael@0: return Debugger::onLeaveFrame(cx, frame, ok); michael@0: } michael@0: michael@0: JSTrapStatus michael@0: js::DebugExceptionUnwind(JSContext *cx, AbstractFramePtr frame, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(cx->compartment()->debugMode()); michael@0: michael@0: if (!cx->runtime()->debugHooks.throwHook && cx->compartment()->getDebuggees().empty()) michael@0: return JSTRAP_CONTINUE; michael@0: michael@0: /* Call debugger throw hook if set. */ michael@0: RootedValue rval(cx); michael@0: JSTrapStatus status = Debugger::onExceptionUnwind(cx, &rval); michael@0: if (status == JSTRAP_CONTINUE) { michael@0: if (JSThrowHook handler = cx->runtime()->debugHooks.throwHook) { michael@0: RootedScript script(cx, frame.script()); michael@0: status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.throwHookData); michael@0: } michael@0: } michael@0: michael@0: switch (status) { michael@0: case JSTRAP_ERROR: michael@0: cx->clearPendingException(); michael@0: break; michael@0: michael@0: case JSTRAP_RETURN: michael@0: cx->clearPendingException(); michael@0: frame.setReturnValue(rval); michael@0: break; michael@0: michael@0: case JSTRAP_THROW: michael@0: cx->setPendingException(rval); michael@0: break; michael@0: michael@0: case JSTRAP_CONTINUE: michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid trap status"); michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_SetDebugModeForAllCompartments(JSContext *cx, bool debug) michael@0: { michael@0: for (ZonesIter zone(cx->runtime(), SkipAtoms); !zone.done(); zone.next()) { michael@0: // Invalidate a zone at a time to avoid doing a zone-wide CellIter michael@0: // per compartment. michael@0: AutoDebugModeInvalidation invalidate(zone); michael@0: for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { michael@0: // Ignore special compartments (atoms, JSD compartments) michael@0: if (c->principals) { michael@0: if (!c->setDebugModeFromC(cx, !!debug, invalidate)) michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, bool debug) michael@0: { michael@0: AutoDebugModeInvalidation invalidate(comp); michael@0: return comp->setDebugModeFromC(cx, !!debug, invalidate); michael@0: } michael@0: michael@0: static bool michael@0: CheckDebugMode(JSContext *cx) michael@0: { michael@0: bool debugMode = JS_GetDebugMode(cx); michael@0: /* michael@0: * :TODO: michael@0: * This probably should be an assertion, since it's indicative of a severe michael@0: * API misuse. michael@0: */ michael@0: if (!debugMode) { michael@0: JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, michael@0: nullptr, JSMSG_NEED_DEBUG_MODE); michael@0: } michael@0: return debugMode; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetSingleStepMode(JSContext *cx, HandleScript script, bool singleStep) michael@0: { michael@0: assertSameCompartment(cx, script); michael@0: michael@0: if (!CheckDebugMode(cx)) michael@0: return false; michael@0: michael@0: return script->setStepModeFlag(cx, singleStep); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetTrap(JSContext *cx, HandleScript script, jsbytecode *pc, JSTrapHandler handler, michael@0: HandleValue closure) michael@0: { michael@0: assertSameCompartment(cx, script, closure); michael@0: michael@0: if (!CheckDebugMode(cx)) michael@0: return false; michael@0: michael@0: BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc); michael@0: if (!site) michael@0: return false; michael@0: site->setTrap(cx->runtime()->defaultFreeOp(), handler, closure); michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, michael@0: JSTrapHandler *handlerp, jsval *closurep) michael@0: { michael@0: if (BreakpointSite *site = script->getBreakpointSite(pc)) { michael@0: site->clearTrap(cx->runtime()->defaultFreeOp(), handlerp, closurep); michael@0: } else { michael@0: if (handlerp) michael@0: *handlerp = nullptr; michael@0: if (closurep) michael@0: *closurep = JSVAL_VOID; michael@0: } michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_ClearScriptTraps(JSRuntime *rt, JSScript *script) michael@0: { michael@0: script->clearTraps(rt->defaultFreeOp()); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_ClearAllTrapsForCompartment(JSContext *cx) michael@0: { michael@0: cx->compartment()->clearTraps(cx->runtime()->defaultFreeOp()); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure) michael@0: { michael@0: rt->debugHooks.interruptHook = hook; michael@0: rt->debugHooks.interruptHookData = closure; michael@0: michael@0: for (ActivationIterator iter(rt); !iter.done(); ++iter) { michael@0: if (iter->isInterpreter()) michael@0: iter->asInterpreter()->enableInterruptsUnconditionally(); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep) michael@0: { michael@0: if (hoop) michael@0: *hoop = rt->debugHooks.interruptHook; michael@0: if (closurep) michael@0: *closurep = rt->debugHooks.interruptHookData; michael@0: rt->debugHooks.interruptHook = 0; michael@0: rt->debugHooks.interruptHookData = 0; michael@0: return true; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetWatchPoint(JSContext *cx, HandleObject origobj, HandleId id, michael@0: JSWatchPointHandler handler, HandleObject closure) michael@0: { michael@0: assertSameCompartment(cx, origobj); michael@0: michael@0: RootedObject obj(cx, GetInnerObject(cx, origobj)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: RootedId propid(cx); michael@0: michael@0: if (JSID_IS_INT(id)) { michael@0: propid = id; michael@0: } else if (JSID_IS_OBJECT(id)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH_PROP); michael@0: return false; michael@0: } else { michael@0: RootedValue val(cx, IdToValue(id)); michael@0: if (!ValueToId(cx, val, &propid)) michael@0: return false; michael@0: } michael@0: michael@0: if (!obj->isNative() || obj->is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH, michael@0: obj->getClass()->name); michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Use sparse indexes for watched objects, as dense elements can be written michael@0: * to without checking the watchpoint map. michael@0: */ michael@0: if (!JSObject::sparsifyDenseElements(cx, obj)) michael@0: return false; michael@0: michael@0: types::MarkTypePropertyNonData(cx, obj, propid); michael@0: michael@0: WatchpointMap *wpmap = cx->compartment()->watchpointMap; michael@0: if (!wpmap) { michael@0: wpmap = cx->runtime()->new_(); michael@0: if (!wpmap || !wpmap->init()) { michael@0: js_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: cx->compartment()->watchpointMap = wpmap; michael@0: } michael@0: return wpmap->watch(cx, obj, propid, handler, closure); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsid id, michael@0: JSWatchPointHandler *handlerp, JSObject **closurep) michael@0: { michael@0: assertSameCompartment(cx, obj, id); michael@0: michael@0: if (WatchpointMap *wpmap = cx->compartment()->watchpointMap) michael@0: wpmap->unwatch(obj, id, handlerp, closurep); michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) michael@0: { michael@0: assertSameCompartment(cx, obj); michael@0: michael@0: if (WatchpointMap *wpmap = cx->compartment()->watchpointMap) michael@0: wpmap->unwatchObject(obj); michael@0: return true; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(unsigned) michael@0: JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) michael@0: { michael@0: return js::PCToLineNumber(script, pc); michael@0: } michael@0: michael@0: JS_PUBLIC_API(jsbytecode *) michael@0: JS_LineNumberToPC(JSContext *cx, JSScript *script, unsigned lineno) michael@0: { michael@0: return js_LineNumberToPC(script, lineno); michael@0: } michael@0: michael@0: JS_PUBLIC_API(jsbytecode *) michael@0: JS_EndPC(JSContext *cx, JSScript *script) michael@0: { michael@0: return script->codeEnd(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_GetLinePCs(JSContext *cx, JSScript *script, michael@0: unsigned startLine, unsigned maxLines, michael@0: unsigned* count, unsigned** retLines, jsbytecode*** retPCs) michael@0: { michael@0: size_t len = (script->length() > maxLines ? maxLines : script->length()); michael@0: unsigned *lines = cx->pod_malloc(len); michael@0: if (!lines) michael@0: return false; michael@0: michael@0: jsbytecode **pcs = cx->pod_malloc(len); michael@0: if (!pcs) { michael@0: js_free(lines); michael@0: return false; michael@0: } michael@0: michael@0: unsigned lineno = script->lineno(); michael@0: unsigned offset = 0; michael@0: unsigned i = 0; michael@0: for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { michael@0: offset += SN_DELTA(sn); michael@0: SrcNoteType type = (SrcNoteType) SN_TYPE(sn); michael@0: if (type == SRC_SETLINE || type == SRC_NEWLINE) { michael@0: if (type == SRC_SETLINE) michael@0: lineno = (unsigned) js_GetSrcNoteOffset(sn, 0); michael@0: else michael@0: lineno++; michael@0: michael@0: if (lineno >= startLine) { michael@0: lines[i] = lineno; michael@0: pcs[i] = script->offsetToPC(offset); michael@0: if (++i >= maxLines) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: *count = i; michael@0: if (retLines) michael@0: *retLines = lines; michael@0: else michael@0: js_free(lines); michael@0: michael@0: if (retPCs) michael@0: *retPCs = pcs; michael@0: else michael@0: js_free(pcs); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(unsigned) michael@0: JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun) michael@0: { michael@0: return fun->nargs(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun) michael@0: { michael@0: return fun->nonLazyScript()->bindings.count() > 0; michael@0: } michael@0: michael@0: extern JS_PUBLIC_API(uintptr_t *) michael@0: JS_GetFunctionLocalNameArray(JSContext *cx, JSFunction *fun, void **memp) michael@0: { michael@0: RootedScript script(cx, fun->nonLazyScript()); michael@0: BindingVector bindings(cx); michael@0: if (!FillBindingVector(script, &bindings)) michael@0: return nullptr; michael@0: michael@0: LifoAlloc &lifo = cx->tempLifoAlloc(); michael@0: michael@0: // Store the LifoAlloc::Mark right before the allocation. michael@0: LifoAlloc::Mark mark = lifo.mark(); michael@0: void *mem = lifo.alloc(sizeof(LifoAlloc::Mark) + bindings.length() * sizeof(uintptr_t)); michael@0: if (!mem) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: *memp = mem; michael@0: *reinterpret_cast(mem) = mark; michael@0: michael@0: // Munge data into the API this method implements. Avert your eyes! michael@0: uintptr_t *names = reinterpret_cast((char*)mem + sizeof(LifoAlloc::Mark)); michael@0: for (size_t i = 0; i < bindings.length(); i++) michael@0: names[i] = reinterpret_cast(bindings[i].name()); michael@0: michael@0: return names; michael@0: } michael@0: michael@0: extern JS_PUBLIC_API(JSAtom *) michael@0: JS_LocalNameToAtom(uintptr_t w) michael@0: { michael@0: return reinterpret_cast(w); michael@0: } michael@0: michael@0: extern JS_PUBLIC_API(JSString *) michael@0: JS_AtomKey(JSAtom *atom) michael@0: { michael@0: return atom; michael@0: } michael@0: michael@0: extern JS_PUBLIC_API(void) michael@0: JS_ReleaseFunctionLocalNameArray(JSContext *cx, void *mem) michael@0: { michael@0: cx->tempLifoAlloc().release(*reinterpret_cast(mem)); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSScript *) michael@0: JS_GetFunctionScript(JSContext *cx, HandleFunction fun) michael@0: { michael@0: if (fun->isNative()) michael@0: return nullptr; michael@0: if (fun->isInterpretedLazy()) { michael@0: AutoCompartment funCompartment(cx, fun); michael@0: JSScript *script = fun->getOrCreateScript(cx); michael@0: if (!script) michael@0: MOZ_CRASH(); michael@0: return script; michael@0: } michael@0: return fun->nonLazyScript(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSNative) michael@0: JS_GetFunctionNative(JSContext *cx, JSFunction *fun) michael@0: { michael@0: return fun->maybeNative(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSPrincipals *) michael@0: JS_GetScriptPrincipals(JSScript *script) michael@0: { michael@0: return script->principals(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSPrincipals *) michael@0: JS_GetScriptOriginPrincipals(JSScript *script) michael@0: { michael@0: return script->originPrincipals(); michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(JSFunction *) michael@0: JS_GetScriptFunction(JSContext *cx, JSScript *script) michael@0: { michael@0: script->ensureNonLazyCanonicalFunction(cx); michael@0: return script->functionNonDelazifying(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSObject *) michael@0: JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj) michael@0: { michael@0: return obj->enclosingScope(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(const char *) michael@0: JS_GetDebugClassName(JSObject *obj) michael@0: { michael@0: if (obj->is()) michael@0: return obj->as().scope().getClass()->name; michael@0: return obj->getClass()->name; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(const char *) michael@0: JS_GetScriptFilename(JSScript *script) michael@0: { michael@0: return script->filename(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(const jschar *) michael@0: JS_GetScriptSourceMap(JSContext *cx, JSScript *script) michael@0: { michael@0: ScriptSource *source = script->scriptSource(); michael@0: JS_ASSERT(source); michael@0: return source->hasSourceMapURL() ? source->sourceMapURL() : nullptr; michael@0: } michael@0: michael@0: JS_PUBLIC_API(unsigned) michael@0: JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) michael@0: { michael@0: return script->lineno(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(unsigned) michael@0: JS_GetScriptLineExtent(JSContext *cx, JSScript *script) michael@0: { michael@0: return js_GetScriptLineExtent(script); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JSVersion) michael@0: JS_GetScriptVersion(JSContext *cx, JSScript *script) michael@0: { michael@0: return VersionNumber(script->getVersion()); michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_GetScriptIsSelfHosted(JSScript *script) michael@0: { michael@0: return script->selfHosted(); michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) michael@0: { michael@0: rt->debugHooks.newScriptHook = hook; michael@0: rt->debugHooks.newScriptHookData = callerdata; michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, michael@0: void *callerdata) michael@0: { michael@0: rt->debugHooks.destroyScriptHook = hook; michael@0: rt->debugHooks.destroyScriptHookData = callerdata; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: /* This all should be reworked to avoid requiring JSScopeProperty types. */ michael@0: michael@0: static bool michael@0: GetPropertyDesc(JSContext *cx, JSObject *obj_, HandleShape shape, JSPropertyDesc *pd) michael@0: { michael@0: assertSameCompartment(cx, obj_); michael@0: pd->id = IdToValue(shape->propid()); michael@0: michael@0: RootedObject obj(cx, obj_); michael@0: michael@0: bool wasThrowing = cx->isExceptionPending(); michael@0: RootedValue lastException(cx, UndefinedValue()); michael@0: if (wasThrowing) { michael@0: if (!cx->getPendingException(&lastException)) michael@0: return false; michael@0: } michael@0: cx->clearPendingException(); michael@0: michael@0: Rooted id(cx, shape->propid()); michael@0: RootedValue value(cx); michael@0: if (!baseops::GetProperty(cx, obj, id, &value)) { michael@0: if (!cx->isExceptionPending()) { michael@0: pd->flags = JSPD_ERROR; michael@0: pd->value = JSVAL_VOID; michael@0: } else { michael@0: pd->flags = JSPD_EXCEPTION; michael@0: if (!cx->getPendingException(&value)) michael@0: return false; michael@0: pd->value = value; michael@0: } michael@0: } else { michael@0: pd->flags = 0; michael@0: pd->value = value; michael@0: } michael@0: michael@0: if (wasThrowing) michael@0: cx->setPendingException(lastException); michael@0: michael@0: pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0) michael@0: | (!shape->writable() ? JSPD_READONLY : 0) michael@0: | (!shape->configurable() ? JSPD_PERMANENT : 0); michael@0: pd->spare = 0; michael@0: pd->alias = JSVAL_VOID; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_GetPropertyDescArray(JSContext *cx, JS::HandleObject obj, JSPropertyDescArray *pda) michael@0: { michael@0: assertSameCompartment(cx, obj); michael@0: uint32_t i = 0; michael@0: JSPropertyDesc *pd = nullptr; michael@0: michael@0: if (obj->is()) { michael@0: AutoIdVector props(cx); michael@0: if (!Proxy::enumerate(cx, obj, props)) michael@0: return false; michael@0: michael@0: pd = cx->pod_calloc(props.length()); michael@0: if (!pd) michael@0: return false; michael@0: michael@0: for (i = 0; i < props.length(); ++i) { michael@0: pd[i].id = JSVAL_NULL; michael@0: pd[i].value = JSVAL_NULL; michael@0: if (!AddValueRoot(cx, &pd[i].id, nullptr)) michael@0: goto bad; michael@0: pd[i].id = IdToValue(props[i]); michael@0: if (!AddValueRoot(cx, &pd[i].value, nullptr)) michael@0: goto bad; michael@0: if (!Proxy::get(cx, obj, obj, props.handleAt(i), MutableHandleValue::fromMarkedLocation(&pd[i].value))) michael@0: goto bad; michael@0: } michael@0: michael@0: pda->length = props.length(); michael@0: pda->array = pd; michael@0: return true; michael@0: } michael@0: michael@0: const Class *clasp; michael@0: clasp = obj->getClass(); michael@0: if (!obj->isNative() || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_CANT_DESCRIBE_PROPS, clasp->name); michael@0: return false; michael@0: } michael@0: if (!clasp->enumerate(cx, obj)) michael@0: return false; michael@0: michael@0: /* Return an empty pda early if obj has no own properties. */ michael@0: if (obj->nativeEmpty()) { michael@0: pda->length = 0; michael@0: pda->array = nullptr; michael@0: return true; michael@0: } michael@0: michael@0: pd = cx->pod_malloc(obj->propertyCount()); michael@0: if (!pd) michael@0: return false; michael@0: michael@0: { michael@0: Shape::Range r(cx, obj->lastProperty()); michael@0: RootedShape shape(cx); michael@0: for (; !r.empty(); r.popFront()) { michael@0: pd[i].id = JSVAL_NULL; michael@0: pd[i].value = JSVAL_NULL; michael@0: pd[i].alias = JSVAL_NULL; michael@0: if (!AddValueRoot(cx, &pd[i].id, nullptr)) michael@0: goto bad; michael@0: if (!AddValueRoot(cx, &pd[i].value, nullptr)) michael@0: goto bad; michael@0: shape = const_cast(&r.front()); michael@0: if (!GetPropertyDesc(cx, obj, shape, &pd[i])) michael@0: goto bad; michael@0: if ((pd[i].flags & JSPD_ALIAS) && !AddValueRoot(cx, &pd[i].alias, nullptr)) michael@0: goto bad; michael@0: if (++i == obj->propertyCount()) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: pda->length = i; michael@0: pda->array = pd; michael@0: return true; michael@0: michael@0: bad: michael@0: pda->length = i + 1; michael@0: pda->array = pd; michael@0: JS_PutPropertyDescArray(cx, pda); michael@0: return false; michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) michael@0: { michael@0: JSPropertyDesc *pd; michael@0: uint32_t i; michael@0: michael@0: pd = pda->array; michael@0: for (i = 0; i < pda->length; i++) { michael@0: RemoveRoot(cx->runtime(), &pd[i].id); michael@0: RemoveRoot(cx->runtime(), &pd[i].value); michael@0: if (pd[i].flags & JSPD_ALIAS) michael@0: RemoveRoot(cx->runtime(), &pd[i].alias); michael@0: } michael@0: js_free(pd); michael@0: pda->array = nullptr; michael@0: pda->length = 0; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure) michael@0: { michael@0: rt->debugHooks.debuggerHandler = handler; michael@0: rt->debugHooks.debuggerHandlerData = closure; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) michael@0: { michael@0: rt->debugHooks.sourceHandler = handler; michael@0: rt->debugHooks.sourceHandlerData = closure; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) michael@0: { michael@0: rt->debugHooks.executeHook = hook; michael@0: rt->debugHooks.executeHookData = closure; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) michael@0: { michael@0: rt->debugHooks.callHook = hook; michael@0: rt->debugHooks.callHookData = closure; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure) michael@0: { michael@0: rt->debugHooks.throwHook = hook; michael@0: rt->debugHooks.throwHookData = closure; michael@0: return true; michael@0: } michael@0: michael@0: JS_PUBLIC_API(bool) michael@0: JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) michael@0: { michael@0: rt->debugHooks.debugErrorHook = hook; michael@0: rt->debugHooks.debugErrorHookData = closure; michael@0: return true; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: JS_PUBLIC_API(const JSDebugHooks *) michael@0: JS_GetGlobalDebugHooks(JSRuntime *rt) michael@0: { michael@0: return &rt->debugHooks; michael@0: } michael@0: michael@0: /************************************************************************/ michael@0: michael@0: extern JS_PUBLIC_API(void) michael@0: JS_DumpPCCounts(JSContext *cx, HandleScript script) michael@0: { michael@0: JS_ASSERT(script->hasScriptCounts()); michael@0: michael@0: Sprinter sprinter(cx); michael@0: if (!sprinter.init()) michael@0: return; michael@0: michael@0: fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno()); michael@0: js_DumpPCCounts(cx, script, &sprinter); michael@0: fputs(sprinter.string(), stdout); michael@0: fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename(), (int) script->lineno()); michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_DumpCompartmentPCCounts(JSContext *cx) michael@0: { michael@0: for (CellIter i(cx->zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) { michael@0: RootedScript script(cx, i.get()); michael@0: if (script->compartment() != cx->compartment()) michael@0: continue; michael@0: michael@0: if (script->hasScriptCounts()) michael@0: JS_DumpPCCounts(cx, script); michael@0: } michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: js::CanCallContextDebugHandler(JSContext *cx) michael@0: { michael@0: return !!cx->runtime()->debugHooks.debuggerHandler; michael@0: } michael@0: michael@0: static JSTrapStatus michael@0: CallContextDebugHandler(JSContext *cx, JSScript *script, jsbytecode *bc, Value *rval) michael@0: { michael@0: if (!cx->runtime()->debugHooks.debuggerHandler) michael@0: return JSTRAP_RETURN; michael@0: michael@0: return cx->runtime()->debugHooks.debuggerHandler(cx, script, bc, rval, michael@0: cx->runtime()->debugHooks.debuggerHandlerData); michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: js_CallContextDebugHandler(JSContext *cx) michael@0: { michael@0: NonBuiltinFrameIter iter(cx); michael@0: michael@0: // If there is no script to debug, then abort execution even if the user michael@0: // clicks 'Debug' in the slow-script dialog. michael@0: if (!iter.hasScript()) michael@0: return false; michael@0: michael@0: // Even if script was running during the operation callback, it's possible michael@0: // it was a builtin which 'iter' will have skipped over. michael@0: if (iter.done()) michael@0: return false; michael@0: michael@0: RootedValue rval(cx); michael@0: RootedScript script(cx, iter.script()); michael@0: switch (CallContextDebugHandler(cx, script, iter.pc(), rval.address())) { michael@0: case JSTRAP_ERROR: michael@0: JS_ClearPendingException(cx); michael@0: return false; michael@0: case JSTRAP_THROW: michael@0: JS_SetPendingException(cx, rval); michael@0: return false; michael@0: case JSTRAP_RETURN: michael@0: case JSTRAP_CONTINUE: michael@0: default: michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid michael@0: * constructing a FrameDescription on the stack just to append it to a vector. michael@0: * FrameDescription contains Heap fields that should not live on the stack. michael@0: */ michael@0: JS::FrameDescription::FrameDescription(const ScriptFrameIter& iter) michael@0: : script_(iter.script()), michael@0: funDisplayName_(nullptr), michael@0: pc_(iter.pc()), michael@0: linenoComputed(false) michael@0: { michael@0: if (JSFunction *fun = iter.maybeCallee()) michael@0: funDisplayName_ = fun->displayAtom(); michael@0: } michael@0: michael@0: JS_PUBLIC_API(JS::StackDescription *) michael@0: JS::DescribeStack(JSContext *cx, unsigned maxFrames) michael@0: { michael@0: Vector frames(cx); michael@0: michael@0: NonBuiltinScriptFrameIter i(cx, ScriptFrameIter::ALL_CONTEXTS, michael@0: ScriptFrameIter::GO_THROUGH_SAVED, michael@0: cx->compartment()->principals); michael@0: for ( ; !i.done(); ++i) { michael@0: if (!frames.append(i)) michael@0: return nullptr; michael@0: if (frames.length() == maxFrames) michael@0: break; michael@0: } michael@0: michael@0: JS::StackDescription *desc = js_new(); michael@0: if (!desc) michael@0: return nullptr; michael@0: michael@0: desc->nframes = frames.length(); michael@0: desc->frames = frames.extractRawBuffer(); michael@0: return desc; michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc) michael@0: { michael@0: for (size_t i = 0; i < desc->nframes; ++i) michael@0: desc->frames[i].~FrameDescription(); michael@0: js_free(desc->frames); michael@0: js_delete(desc); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: class AutoPropertyDescArray michael@0: { michael@0: JSContext *cx_; michael@0: JSPropertyDescArray descArray_; michael@0: michael@0: public: michael@0: AutoPropertyDescArray(JSContext *cx) michael@0: : cx_(cx) michael@0: { michael@0: PodZero(&descArray_); michael@0: } michael@0: ~AutoPropertyDescArray() michael@0: { michael@0: if (descArray_.array) michael@0: JS_PutPropertyDescArray(cx_, &descArray_); michael@0: } michael@0: michael@0: void fetch(JS::HandleObject obj) { michael@0: JS_ASSERT(!descArray_.array); michael@0: if (!JS_GetPropertyDescArray(cx_, obj, &descArray_)) michael@0: descArray_.array = nullptr; michael@0: } michael@0: michael@0: JSPropertyDescArray * operator ->() { michael@0: return &descArray_; michael@0: } michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: static const char * michael@0: FormatValue(JSContext *cx, const Value &vArg, JSAutoByteString &bytes) michael@0: { michael@0: RootedValue v(cx, vArg); michael@0: michael@0: /* michael@0: * We could use Maybe here, but G++ can't quite follow michael@0: * that, and warns about uninitialized members being used in the michael@0: * destructor. michael@0: */ michael@0: RootedString str(cx); michael@0: if (v.isObject()) { michael@0: AutoCompartment ac(cx, &v.toObject()); michael@0: str = ToString(cx, v); michael@0: } else { michael@0: str = ToString(cx, v); michael@0: } michael@0: michael@0: if (!str) michael@0: return nullptr; michael@0: const char *buf = bytes.encodeLatin1(cx, str); michael@0: if (!buf) michael@0: return nullptr; michael@0: const char *found = strstr(buf, "function "); michael@0: if (found && (found - buf <= 2)) michael@0: return "[function]"; michael@0: return buf; michael@0: } michael@0: michael@0: static char * michael@0: FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num, michael@0: bool showArgs, bool showLocals, bool showThisProps) michael@0: { michael@0: JS_ASSERT(!cx->isExceptionPending()); michael@0: RootedScript script(cx, iter.script()); michael@0: jsbytecode* pc = iter.pc(); michael@0: michael@0: RootedObject scopeChain(cx, iter.scopeChain()); michael@0: JSAutoCompartment ac(cx, scopeChain); michael@0: michael@0: const char *filename = script->filename(); michael@0: unsigned lineno = PCToLineNumber(script, pc); michael@0: RootedFunction fun(cx, iter.maybeCallee()); michael@0: RootedString funname(cx); michael@0: if (fun) michael@0: funname = fun->atom(); michael@0: michael@0: RootedValue thisVal(cx); michael@0: AutoPropertyDescArray thisProps(cx); michael@0: if (iter.computeThis(cx)) { michael@0: thisVal = iter.computedThisValue(); michael@0: if (showThisProps && !thisVal.isPrimitive()) { michael@0: RootedObject thisObj(cx, &thisVal.toObject()); michael@0: thisProps.fetch(thisObj); michael@0: } michael@0: } michael@0: michael@0: // print the frame number and function name michael@0: if (funname) { michael@0: JSAutoByteString funbytes; michael@0: buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname)); michael@0: } else if (fun) { michael@0: buf = JS_sprintf_append(buf, "%d anonymous(", num); michael@0: } else { michael@0: buf = JS_sprintf_append(buf, "%d ", num); michael@0: } michael@0: if (!buf) michael@0: return buf; michael@0: michael@0: if (showArgs && iter.hasArgs()) { michael@0: BindingVector bindings(cx); michael@0: if (fun && fun->isInterpreted()) { michael@0: if (!FillBindingVector(script, &bindings)) michael@0: return buf; michael@0: } michael@0: michael@0: michael@0: bool first = true; michael@0: for (unsigned i = 0; i < iter.numActualArgs(); i++) { michael@0: RootedValue arg(cx); michael@0: if (i < iter.numFormalArgs() && script->formalIsAliased(i)) { michael@0: for (AliasedFormalIter fi(script); ; fi++) { michael@0: if (fi.frameIndex() == i) { michael@0: arg = iter.callObj().aliasedVar(fi); michael@0: break; michael@0: } michael@0: } michael@0: } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) { michael@0: arg = iter.argsObj().arg(i); michael@0: } else { michael@0: arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING); michael@0: } michael@0: michael@0: JSAutoByteString valueBytes; michael@0: const char *value = FormatValue(cx, arg, valueBytes); michael@0: michael@0: JSAutoByteString nameBytes; michael@0: const char *name = nullptr; michael@0: michael@0: if (i < bindings.length()) { michael@0: name = nameBytes.encodeLatin1(cx, bindings[i].name()); michael@0: if (!buf) michael@0: return nullptr; michael@0: } michael@0: michael@0: if (value) { michael@0: buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", michael@0: !first ? ", " : "", michael@0: name ? name :"", michael@0: name ? " = " : "", michael@0: arg.isString() ? "\"" : "", michael@0: value ? value : "?unknown?", michael@0: arg.isString() ? "\"" : ""); michael@0: if (!buf) michael@0: return buf; michael@0: michael@0: first = false; michael@0: } else { michael@0: buf = JS_sprintf_append(buf, " \n"); michael@0: if (!buf) michael@0: return buf; michael@0: cx->clearPendingException(); michael@0: michael@0: } michael@0: } michael@0: } michael@0: michael@0: // print filename and line number michael@0: buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", michael@0: fun ? ")" : "", michael@0: filename ? filename : "", michael@0: lineno); michael@0: if (!buf) michael@0: return buf; michael@0: michael@0: michael@0: // Note: Right now we don't dump the local variables anymore, because michael@0: // that is hard to support across all the JITs etc. michael@0: michael@0: // print the value of 'this' michael@0: if (showLocals) { michael@0: if (!thisVal.isUndefined()) { michael@0: JSAutoByteString thisValBytes; michael@0: RootedString thisValStr(cx, ToString(cx, thisVal)); michael@0: const char *str = nullptr; michael@0: if (thisValStr && michael@0: (str = thisValBytes.encodeLatin1(cx, thisValStr))) michael@0: { michael@0: buf = JS_sprintf_append(buf, " this = %s\n", str); michael@0: if (!buf) michael@0: return buf; michael@0: } else { michael@0: buf = JS_sprintf_append(buf, " \n"); michael@0: cx->clearPendingException(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // print the properties of 'this', if it is an object michael@0: if (showThisProps && thisProps->array) { michael@0: for (uint32_t i = 0; i < thisProps->length; i++) { michael@0: JSPropertyDesc* desc = &thisProps->array[i]; michael@0: if (desc->flags & JSPD_ENUMERATE) { michael@0: JSAutoByteString nameBytes; michael@0: JSAutoByteString valueBytes; michael@0: const char *name = FormatValue(cx, desc->id, nameBytes); michael@0: const char *value = FormatValue(cx, desc->value, valueBytes); michael@0: if (name && value) { michael@0: buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", michael@0: name, michael@0: desc->value.isString() ? "\"" : "", michael@0: value, michael@0: desc->value.isString() ? "\"" : ""); michael@0: if (!buf) michael@0: return buf; michael@0: } else { michael@0: buf = JS_sprintf_append(buf, " \n"); michael@0: cx->clearPendingException(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: JS_ASSERT(!cx->isExceptionPending()); michael@0: return buf; michael@0: } michael@0: michael@0: JS_PUBLIC_API(char *) michael@0: JS::FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps) michael@0: { michael@0: int num = 0; michael@0: michael@0: for (NonBuiltinScriptFrameIter i(cx); !i.done(); ++i) { michael@0: buf = FormatFrame(cx, i, buf, num, showArgs, showLocals, showThisProps); michael@0: num++; michael@0: } michael@0: michael@0: if (!num) michael@0: buf = JS_sprintf_append(buf, "JavaScript stack is empty\n"); michael@0: michael@0: return buf; michael@0: } michael@0: michael@0: JSAbstractFramePtr::JSAbstractFramePtr(void *raw, jsbytecode *pc) michael@0: : ptr_(uintptr_t(raw)), pc_(pc) michael@0: { } michael@0: michael@0: JSObject * michael@0: JSAbstractFramePtr::scopeChain(JSContext *cx) michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: RootedObject scopeChain(cx, frame.scopeChain()); michael@0: AutoCompartment ac(cx, scopeChain); michael@0: return GetDebugScopeForFrame(cx, frame, pc()); michael@0: } michael@0: michael@0: JSObject * michael@0: JSAbstractFramePtr::callObject(JSContext *cx) michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: if (!frame.isFunctionFrame()) michael@0: return nullptr; michael@0: michael@0: JSObject *o = GetDebugScopeForFrame(cx, frame, pc()); michael@0: michael@0: /* michael@0: * Given that fp is a function frame and GetDebugScopeForFrame always fills michael@0: * in missing scopes, we can expect to find fp's CallObject on 'o'. Note: michael@0: * - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with michael@0: * a DebugScopeObject proxy. michael@0: * - If fp is an eval-in-function, then fp has no callobj of its own and michael@0: * JS_GetFrameCallObject will return the innermost function's callobj. michael@0: */ michael@0: while (o) { michael@0: ScopeObject &scope = o->as().scope(); michael@0: if (scope.is()) michael@0: return o; michael@0: o = o->enclosingScope(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: JSFunction * michael@0: JSAbstractFramePtr::maybeFun() michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: return frame.maybeFun(); michael@0: } michael@0: michael@0: JSScript * michael@0: JSAbstractFramePtr::script() michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: return frame.script(); michael@0: } michael@0: michael@0: bool michael@0: JSAbstractFramePtr::getThisValue(JSContext *cx, MutableHandleValue thisv) michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: michael@0: RootedObject scopeChain(cx, frame.scopeChain()); michael@0: js::AutoCompartment ac(cx, scopeChain); michael@0: if (!ComputeThis(cx, frame)) michael@0: return false; michael@0: michael@0: thisv.set(frame.thisValue()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: JSAbstractFramePtr::isDebuggerFrame() michael@0: { michael@0: AbstractFramePtr frame(*this); michael@0: return frame.isDebuggerFrame(); michael@0: } michael@0: michael@0: bool michael@0: JSAbstractFramePtr::evaluateInStackFrame(JSContext *cx, michael@0: const char *bytes, unsigned length, michael@0: const char *filename, unsigned lineno, michael@0: MutableHandleValue rval) michael@0: { michael@0: if (!CheckDebugMode(cx)) michael@0: return false; michael@0: michael@0: size_t len = length; michael@0: jschar *chars = InflateString(cx, bytes, &len); michael@0: if (!chars) michael@0: return false; michael@0: length = (unsigned) len; michael@0: michael@0: bool ok = evaluateUCInStackFrame(cx, chars, length, filename, lineno, rval); michael@0: js_free(chars); michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: bool michael@0: JSAbstractFramePtr::evaluateUCInStackFrame(JSContext *cx, michael@0: const jschar *chars, unsigned length, michael@0: const char *filename, unsigned lineno, michael@0: MutableHandleValue rval) michael@0: { michael@0: if (!CheckDebugMode(cx)) michael@0: return false; michael@0: michael@0: RootedObject scope(cx, scopeChain(cx)); michael@0: Rooted env(cx, scope); michael@0: if (!env) michael@0: return false; michael@0: michael@0: AbstractFramePtr frame(*this); michael@0: if (!ComputeThis(cx, frame)) michael@0: return false; michael@0: RootedValue thisv(cx, frame.thisValue()); michael@0: michael@0: js::AutoCompartment ac(cx, env); michael@0: return EvaluateInEnv(cx, env, thisv, frame, ConstTwoByteChars(chars, length), length, michael@0: filename, lineno, rval); michael@0: } michael@0: michael@0: JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx) michael@0: { michael@0: // Show all frames on the stack whose principal is subsumed by the current principal. michael@0: NonBuiltinScriptFrameIter iter(cx, michael@0: ScriptFrameIter::ALL_CONTEXTS, michael@0: ScriptFrameIter::GO_THROUGH_SAVED, michael@0: cx->compartment()->principals); michael@0: data_ = iter.copyData(); michael@0: } michael@0: michael@0: JSBrokenFrameIterator::~JSBrokenFrameIterator() michael@0: { michael@0: js_free((ScriptFrameIter::Data *)data_); michael@0: } michael@0: michael@0: bool michael@0: JSBrokenFrameIterator::done() const michael@0: { michael@0: NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_); michael@0: return iter.done(); michael@0: } michael@0: michael@0: JSBrokenFrameIterator & michael@0: JSBrokenFrameIterator::operator++() michael@0: { michael@0: ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_; michael@0: NonBuiltinScriptFrameIter iter(*data); michael@0: ++iter; michael@0: *data = iter.data_; michael@0: return *this; michael@0: } michael@0: michael@0: JSAbstractFramePtr michael@0: JSBrokenFrameIterator::abstractFramePtr() const michael@0: { michael@0: NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_); michael@0: return JSAbstractFramePtr(iter.abstractFramePtr().raw(), iter.pc()); michael@0: } michael@0: michael@0: jsbytecode * michael@0: JSBrokenFrameIterator::pc() const michael@0: { michael@0: NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_); michael@0: return iter.pc(); michael@0: } michael@0: michael@0: bool michael@0: JSBrokenFrameIterator::isConstructing() const michael@0: { michael@0: NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_); michael@0: return iter.isConstructing(); michael@0: }