diff --git a/js/public/OldDebugAPI.h b/js/public/OldDebugAPI.h index 9b604f71078b..9e67bb466ca8 100644 --- a/js/public/OldDebugAPI.h +++ b/js/public/OldDebugAPI.h @@ -37,6 +37,69 @@ JS_GetScriptFilename(JSScript *script); namespace JS { +class FrameDescription +{ + public: + explicit FrameDescription(const js::FrameIter& iter); + FrameDescription(const FrameDescription &rhs); + ~FrameDescription(); + + unsigned lineno() { + if (!linenoComputed_) { + lineno_ = JS_PCToLineNumber(nullptr, script_, pc_); + linenoComputed_ = true; + } + return lineno_; + } + + const char *filename() const { + return filename_; + } + + JSFlatString *funDisplayName() const { + return funDisplayName_ ? JS_ASSERT_STRING_IS_FLAT(funDisplayName_) : nullptr; + } + + // Both these locations should be traced during GC but otherwise not used; + // they are implementation details. + Heap &markedLocation1() { + return script_; + } + Heap &markedLocation2() { + return funDisplayName_; + } + + private: + void operator=(const FrameDescription &) MOZ_DELETE; + + // These fields are always initialized: + Heap funDisplayName_; + const char *filename_; + + // One of script_ xor scriptSource_ is non-null. + Heap script_; + js::ScriptSource *scriptSource_; + + // For script_-having frames, lineno_ is lazily computed as an optimization. + bool linenoComputed_; + unsigned lineno_; + + // pc_ is non-null iff script_ is non-null. If !pc_, linenoComputed_ = true. + jsbytecode *pc_; +}; + +struct StackDescription +{ + unsigned nframes; + FrameDescription *frames; +}; + +extern JS_PUBLIC_API(StackDescription *) +DescribeStack(JSContext *cx, unsigned maxFrames); + +extern JS_PUBLIC_API(void) +FreeStackDescription(JSContext *cx, StackDescription *desc); + extern JS_PUBLIC_API(char *) FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool showThisProps); diff --git a/js/src/vm/OldDebugAPI.cpp b/js/src/vm/OldDebugAPI.cpp index 234f4a6a0de4..260b1bb77335 100644 --- a/js/src/vm/OldDebugAPI.cpp +++ b/js/src/vm/OldDebugAPI.cpp @@ -895,6 +895,85 @@ js_CallContextDebugHandler(JSContext *cx) } } +/* + * A contructor that crates a FrameDescription from a ScriptFrameIter, to avoid + * constructing a FrameDescription on the stack just to append it to a vector. + * FrameDescription contains Heap fields that should not live on the stack. + */ +JS::FrameDescription::FrameDescription(const FrameIter& iter) + : scriptSource_(nullptr), + linenoComputed_(false), + pc_(nullptr) +{ + if (iter.isNonEvalFunctionFrame()) + funDisplayName_ = iter.functionDisplayAtom(); + + if (iter.hasScript()) { + script_ = iter.script(); + pc_ = iter.pc(); + filename_ = script_->filename(); + } else { + scriptSource_ = iter.scriptSource(); + scriptSource_->incref(); + filename_ = scriptSource_->filename(); + lineno_ = iter.computeLine(); + linenoComputed_ = true; + } +} + +JS::FrameDescription::FrameDescription(const FrameDescription &rhs) + : funDisplayName_(rhs.funDisplayName_), + filename_(rhs.filename_), + script_(rhs.script_), + scriptSource_(rhs.scriptSource_), + linenoComputed_(rhs.linenoComputed_), + lineno_(rhs.lineno_), + pc_(rhs.pc_) +{ + if (scriptSource_) + scriptSource_->incref(); +} + + +JS::FrameDescription::~FrameDescription() +{ + if (scriptSource_) + scriptSource_->decref(); +} + +JS_PUBLIC_API(JS::StackDescription *) +JS::DescribeStack(JSContext *cx, unsigned maxFrames) +{ + Vector frames(cx); + + NonBuiltinFrameIter i(cx, FrameIter::ALL_CONTEXTS, + FrameIter::GO_THROUGH_SAVED, + cx->compartment()->principals); + for ( ; !i.done(); ++i) { + if (!frames.append(i)) + return nullptr; + if (frames.length() == maxFrames) + break; + } + + JS::StackDescription *desc = js_new(); + if (!desc) + return nullptr; + + desc->nframes = frames.length(); + desc->frames = frames.extractRawBuffer(); + return desc; +} + +JS_PUBLIC_API(void) +JS::FreeStackDescription(JSContext *cx, JS::StackDescription *desc) +{ + for (size_t i = 0; i < desc->nframes; ++i) + desc->frames[i].~FrameDescription(); + js_free(desc->frames); + js_delete(desc); +} + namespace { class AutoPropertyDescArray