зеркало из https://github.com/mozilla/gecko-dev.git
Bug 993085 - Part 1: Add the Debugger.Memory.prototype.trackingAllocationSites accessor property r=jimb
This commit is contained in:
Родитель
1dddfef6bc
Коммит
87b69e2b9e
|
@ -362,6 +362,13 @@ class MOZ_STACK_CLASS CallArgs : public detail::CallArgsBase<detail::IncludeUsed
|
|||
return args;
|
||||
}
|
||||
|
||||
public:
|
||||
/*
|
||||
* Returns true if there are at least |required| arguments passed in. If
|
||||
* false, it reports an error message on the context.
|
||||
*/
|
||||
bool requireAtLeast(JSContext *cx, const char *fnname, unsigned required);
|
||||
|
||||
};
|
||||
|
||||
MOZ_ALWAYS_INLINE CallArgs
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# `Debugger.Memory`
|
||||
|
||||
If `dbg` is a `Debugger` instance, then `dbg.memory` is an instance of
|
||||
`Debugger.Memory` whose methods and accessors operate on `dbg`. This class
|
||||
exists only to hold member functions and accessors related to memory analysis,
|
||||
keeping them separate from other `Debugger` facilities.
|
||||
|
||||
## Accessor Properties of the `Debugger.Memory.prototype` Object
|
||||
|
||||
<code id="trackingallocationsites">trackingAllocationSites</code>
|
||||
: A boolean value indicating whether this `Debugger.Memory` instance is
|
||||
capturing the JavaScript execution stack when each Object is allocated. This
|
||||
accessor property has both a getter and setter: assigning to it enables or
|
||||
disables the allocation site tracking. Reading the accessor produces `true`
|
||||
if the Debugger is capturing stacks for Object allocations, and `false`
|
||||
otherwise. Allocation site tracking is initially disabled in a new Debugger.
|
||||
|
||||
Assignment is fallible: if the Debugger cannot track allocation sites, it
|
||||
throws an `Error` instance.
|
||||
|
||||
You can retrieve the allocation site for a given object with the
|
||||
[`Debugger.Object.prototype.allocationSite`][allocation-site] accessor
|
||||
property.
|
|
@ -199,6 +199,11 @@ from its prototype:
|
|||
wrapper's global, not the wrapped object's global. The result refers to
|
||||
the global directly, not via a wrapper.
|
||||
|
||||
<code id="allocationsite">allocationSite</code>
|
||||
: If [object allocation site tracking][tracking-allocs] was enabled when this
|
||||
`Debugger.Object`'s referent was allocated, return the
|
||||
[JavaScript execution stack][saved-frame] captured at the time of the
|
||||
allocation. Otherwise, return `null`.
|
||||
|
||||
|
||||
## Function Properties of the Debugger.Object prototype
|
||||
|
|
|
@ -250,6 +250,9 @@ other kinds of objects.
|
|||
[`Debugger.Object`][object] instance this method returns does hold a strong
|
||||
reference to the added global.)
|
||||
|
||||
If this debugger is [tracking allocation sites][tracking-allocs] and cannot
|
||||
track allocation sites for <i>global</i>, this method throws an `Error`.
|
||||
|
||||
<code>removeDebuggee(<i>global</i>)</code>
|
||||
: Remove the global object designated by <i>global</i> from this
|
||||
`Debugger` instance's set of debuggees. Return `undefined`.
|
||||
|
|
|
@ -29,6 +29,7 @@ markdown Debugger.Frame.md Debugger-API/Debugger.Frame
|
|||
|
||||
markdown Debugger.Object.md Debugger-API/Debugger.Object
|
||||
label 'object' "Debugger.Object"
|
||||
label 'allocation-site' '#allocationsite' "Debugger.Object: allocationSite"
|
||||
|
||||
markdown Debugger.Script.md Debugger-API/Debugger.Script
|
||||
label 'script' "Debugger.Script"
|
||||
|
@ -36,6 +37,10 @@ markdown Debugger.Script.md Debugger-API/Debugger.Script
|
|||
markdown Debugger.Source.md Debugger-API/Debugger.Source
|
||||
label 'source' "Debugger.Source"
|
||||
|
||||
markdown Debugger.Memory.md Debugger-API/Debugger.Memory
|
||||
label 'memory' "Debugger.Memory"
|
||||
label 'tracking-allocs' '#trackingallocationsites' "Debugger.Memory: trackingAllocationSites"
|
||||
|
||||
# Images:
|
||||
RBASE=https://mdn.mozillademos.org/files
|
||||
resource 'img-shadows' shadows.svg $RBASE/7225/shadows.svg
|
||||
|
@ -45,3 +50,4 @@ resource 'img-example-alert' debugger-alert.png $RBASE/7231
|
|||
|
||||
# External links:
|
||||
absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol"
|
||||
absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame"
|
||||
|
|
|
@ -9,6 +9,9 @@ the moment, we have:
|
|||
- `js/src/doc/Debugger`, SpiderMonkey's JavaScript debugging API, commonly
|
||||
known as `Debugger`.
|
||||
|
||||
- `js/src/doc/SavedFrame`, SpiderMonkey's compact representation for captured
|
||||
call stacks.
|
||||
|
||||
and that's it.
|
||||
|
||||
To format the documentation, you'll need to install [Pandoc][], a
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# `SavedFrame`
|
||||
|
||||
A `SavedFrame` instance is a singly linked list of stack frames. It represents a
|
||||
JavaScript call stack at a past moment of execution. Younger frames hold a
|
||||
reference to the frames that invoked them. The older tails are shared across
|
||||
many younger frames.
|
||||
|
||||
|
||||
## Accessor Properties of the `SavedFrame.prototype` Object
|
||||
|
||||
`source`
|
||||
: The source URL for this stack frame, as a string.
|
||||
|
||||
`line`
|
||||
: The line number for this stack frame.
|
||||
|
||||
`column`
|
||||
: The column number for this stack frame.
|
||||
|
||||
`functionDisplayName`
|
||||
: Either SpiderMonkey's inferred name for this stack frame's function, or
|
||||
`null`.
|
||||
|
||||
`parent`
|
||||
: Either this stack frame's parent stack frame (the next older frame), or
|
||||
`null` if this is the oldest frame in the captured stack.
|
||||
|
||||
|
||||
## Function Properties of the `SavedFrame.prototype` Object
|
||||
|
||||
`toString`
|
||||
: Return this frame and its parents formatted as a human readable stack trace
|
||||
string.
|
|
@ -0,0 +1,6 @@
|
|||
### Description of SavedFrame docs: how to format, where to install.
|
||||
### See js/src/doc/README.md for a description.
|
||||
|
||||
base-url https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/
|
||||
|
||||
markdown SavedFrame.md SavedFrame
|
|
@ -0,0 +1,37 @@
|
|||
// Test that we can track allocation sites by setting
|
||||
// Debugger.Memory.prototype.trackingAllocationSites to true and then get the
|
||||
// allocation site via Debugger.Object.prototype.allocationSite.
|
||||
|
||||
const root = newGlobal();
|
||||
|
||||
const dbg = new Debugger();
|
||||
const wrappedRoot = dbg.addDebuggee(root);
|
||||
|
||||
assertEq(dbg.memory.trackingAllocationSites, false);
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
assertEq(dbg.memory.trackingAllocationSites, true);
|
||||
|
||||
root.eval("(" + function immediate() {
|
||||
this.tests = [
|
||||
{ name: "object literal", object: ({}), line: Error().lineNumber },
|
||||
{ name: "array literal", object: [], line: Error().lineNumber },
|
||||
{ name: "regexp literal", object: /(two|2)\s*problems/, line: Error().lineNumber },
|
||||
{ name: "new constructor", object: new function Ctor(){}, line: Error().lineNumber },
|
||||
{ name: "new Object", object: new Object(), line: Error().lineNumber },
|
||||
{ name: "new Array", object: new Array(), line: Error().lineNumber },
|
||||
{ name: "new Date", object: new Date(), line: Error().lineNumber }
|
||||
];
|
||||
} + "());");
|
||||
|
||||
dbg.memory.trackingAllocationSites = false;
|
||||
assertEq(dbg.memory.trackingAllocationSites, false);
|
||||
|
||||
for (let { name, object, line } of root.tests) {
|
||||
print("Entering test: " + name);
|
||||
|
||||
let wrappedObject = wrappedRoot.makeDebuggeeValue(object);
|
||||
let allocationSite = wrappedObject.allocationSite;
|
||||
print("Allocation site: " + allocationSite);
|
||||
|
||||
assertEq(allocationSite.line, line);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Test that we don't get allocation sites when nobody has asked for them.
|
||||
|
||||
const root = newGlobal();
|
||||
|
||||
const dbg = new Debugger();
|
||||
const wrappedRoot = dbg.addDebuggee(root);
|
||||
|
||||
dbg.memory.trackingAllocationSites = true;
|
||||
root.eval("this.obj = {};");
|
||||
dbg.memory.trackingAllocationSites = false;
|
||||
root.eval("this.obj2 = {};");
|
||||
|
||||
let wrappedObj = wrappedRoot.makeDebuggeeValue(root.obj);
|
||||
let allocationSite = wrappedObj.allocationSite;
|
||||
assertEq(allocationSite != null && typeof allocationSite == "object", true);
|
||||
|
||||
let wrappedObj2 = wrappedRoot.makeDebuggeeValue(root.obj2);
|
||||
let allocationSite2 = wrappedObj2.allocationSite;
|
||||
assertEq(allocationSite2, null);
|
|
@ -0,0 +1,64 @@
|
|||
// Test that multiple Debuggers behave reasonably. Since we're not keeping a
|
||||
// per-compartment count of how many Debuggers have requested allocation
|
||||
// tracking, assert that attempts to request allocation tracking from multiple
|
||||
// debuggers throws.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
let root1 = newGlobal();
|
||||
let root2 = newGlobal();
|
||||
|
||||
let dbg1 = new Debugger();
|
||||
let dbg2 = new Debugger();
|
||||
|
||||
let d1r1 = dbg1.addDebuggee(root1);
|
||||
let d2r1 = dbg2.addDebuggee(root1);
|
||||
|
||||
let wrappedObj, allocationSite;
|
||||
|
||||
function isTrackingAllocations(global, dbgObj) {
|
||||
const site = dbgObj.makeDebuggeeValue(global.eval("({})")).allocationSite;
|
||||
if (site) {
|
||||
assertEq(typeof site, "object");
|
||||
}
|
||||
return !!site;
|
||||
}
|
||||
|
||||
// Can't track allocations if a different debugger is already tracking them.
|
||||
dbg1.memory.trackingAllocationSites = true;
|
||||
assertThrowsInstanceOf(() => dbg2.memory.trackingAllocationSites = true,
|
||||
Error);
|
||||
|
||||
// Removing root as a debuggee from dbg1 should disable the allocation hook.
|
||||
dbg1.removeDebuggee(root1);
|
||||
assertEq(isTrackingAllocations(root1, d1r1), false);
|
||||
|
||||
// Tracking allocations in dbg2 should work now that dbg1 isn't debugging root1.
|
||||
dbg2.memory.trackingAllocationSites = true;
|
||||
assertEq(isTrackingAllocations(root1, d2r1), true);
|
||||
|
||||
// Adding root back as a debuggee in dbg1 should fail now because it will
|
||||
// attempt to track allocations in root, but dbg2 is already doing that.
|
||||
assertThrowsInstanceOf(() => dbg1.addDebuggee(root1),
|
||||
Error);
|
||||
assertEq(dbg1.hasDebuggee(root1), false);
|
||||
|
||||
// Adding a new debuggee to a debugger that is tracking allocations should
|
||||
// enable the hook for the new debuggee.
|
||||
dbg2.removeDebuggee(root1);
|
||||
d1r1 = dbg1.addDebuggee(root1);
|
||||
assertEq(isTrackingAllocations(root1, d1r1), true);
|
||||
|
||||
// Setting trackingAllocationSites to true should throw if the debugger cannot
|
||||
// install the allocation hooks for *every* debuggee.
|
||||
dbg1.memory.trackingAllocationSites = true;
|
||||
dbg1.addDebuggee(root1);
|
||||
dbg2.memory.trackingAllocationSites = false;
|
||||
let d2r2 = dbg2.addDebuggee(root2);
|
||||
dbg2.addDebuggee(root1);
|
||||
assertThrowsInstanceOf(() => dbg2.memory.trackingAllocationSites = true,
|
||||
Error);
|
||||
|
||||
// And after it throws, its trackingAllocationSites accessor should reflect that
|
||||
// allocation site tracking is still disabled in that Debugger.
|
||||
assertEq(isTrackingAllocations(root2, d2r2), false);
|
|
@ -237,7 +237,7 @@ MSG_DEF(JSMSG_SC_NOT_TRANSFERABLE, 183, 0, JSEXN_TYPEERR, "invalid transferab
|
|||
MSG_DEF(JSMSG_SC_DUP_TRANSFERABLE, 184, 0, JSEXN_TYPEERR, "duplicate transferable for structured clone")
|
||||
MSG_DEF(JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE, 185, 0, JSEXN_TYPEERR, "proxy can't report an extensible object as non-extensible")
|
||||
MSG_DEF(JSMSG_SYMBOL_TO_STRING, 186, 0, JSEXN_TYPEERR, "can't convert symbol to string")
|
||||
MSG_DEF(JSMSG_UNUSED187, 187, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET, 187, 0, JSEXN_ERR, "Cannot track object allocation, because other tools are already doing so")
|
||||
MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
|
||||
MSG_DEF(JSMSG_SYMBOL_TO_PRIMITIVE, 189, 0, JSEXN_TYPEERR, "can't convert symbol object to primitive")
|
||||
MSG_DEF(JSMSG_UNUSED190, 190, 0, JSEXN_NONE, "")
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jsdate.h"
|
||||
#include "jsexn.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsiter.h"
|
||||
|
@ -115,6 +116,19 @@ using js::frontend::Parser;
|
|||
JS_STATIC_ASSERT((jschar)-1 > 0);
|
||||
JS_STATIC_ASSERT(sizeof(jschar) == 2);
|
||||
|
||||
bool
|
||||
JS::CallArgs::requireAtLeast(JSContext *cx, const char *fnname, unsigned required) {
|
||||
if (length() < required) {
|
||||
char numArgsStr[40];
|
||||
JS_snprintf(numArgsStr, sizeof numArgsStr, "%u", required - 1);
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
fnname, numArgsStr, required == 2 ? "" : "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(int64_t)
|
||||
JS_Now()
|
||||
{
|
||||
|
|
|
@ -345,6 +345,9 @@ struct JSCompartment
|
|||
|
||||
bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
|
||||
void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
|
||||
void forgetObjectMetadataCallback() {
|
||||
objectMetadataCallback = nullptr;
|
||||
}
|
||||
bool callObjectMetadataCallback(JSContext *cx, JSObject **obj) const {
|
||||
return objectMetadataCallback(cx, obj);
|
||||
}
|
||||
|
|
|
@ -22,4 +22,11 @@ js::Debugger::onLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool ok)
|
|||
return ok;
|
||||
}
|
||||
|
||||
/* static */ inline js::Debugger *
|
||||
js::Debugger::fromJSObject(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(js::GetObjectClass(obj) == &jsclass);
|
||||
return (Debugger *) obj->getPrivate();
|
||||
}
|
||||
|
||||
#endif /* vm_Debugger_inl_h */
|
||||
|
|
|
@ -86,19 +86,6 @@ enum {
|
|||
|
||||
/*** Utils ***************************************************************************************/
|
||||
|
||||
static bool
|
||||
ReportMoreArgsNeeded(JSContext *cx, const char *name, unsigned required)
|
||||
{
|
||||
JS_ASSERT(required > 0);
|
||||
JS_ASSERT(required <= 10);
|
||||
char s[2];
|
||||
s[0] = '0' + (required - 1);
|
||||
s[1] = '\0';
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
name, s, required == 2 ? "" : "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
EnsureFunctionHasScript(JSContext *cx, HandleFunction fun)
|
||||
{
|
||||
|
@ -118,14 +105,8 @@ GetOrCreateFunctionScript(JSContext *cx, HandleFunction fun)
|
|||
return fun->nonLazyScript();
|
||||
}
|
||||
|
||||
#define REQUIRE_ARGC(name, n) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (argc < (n)) \
|
||||
return ReportMoreArgsNeeded(cx, name, n); \
|
||||
JS_END_MACRO
|
||||
|
||||
static bool
|
||||
ReportObjectRequired(JSContext *cx)
|
||||
bool
|
||||
js::ReportObjectRequired(JSContext *cx)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return false;
|
||||
|
@ -388,7 +369,7 @@ Breakpoint::nextInSite()
|
|||
/*** Debugger hook dispatch **********************************************************************/
|
||||
|
||||
Debugger::Debugger(JSContext *cx, JSObject *dbg)
|
||||
: object(dbg), uncaughtExceptionHook(nullptr), enabled(true),
|
||||
: object(dbg), uncaughtExceptionHook(nullptr), enabled(true), trackingAllocationSites(false),
|
||||
frames(cx->runtime()), scripts(cx), sources(cx), objects(cx), environments(cx)
|
||||
{
|
||||
assertSameCompartment(cx, dbg);
|
||||
|
@ -426,13 +407,6 @@ Debugger::init(JSContext *cx)
|
|||
return ok;
|
||||
}
|
||||
|
||||
Debugger *
|
||||
Debugger::fromJSObject(JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(js::GetObjectClass(obj) == &jsclass);
|
||||
return (Debugger *) obj->getPrivate();
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSCRIPT_OWNER));
|
||||
JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGSOURCE_OWNER));
|
||||
JS_STATIC_ASSERT(unsigned(JSSLOT_DEBUGFRAME_OWNER) == unsigned(JSSLOT_DEBUGOBJECT_OWNER));
|
||||
|
@ -1848,8 +1822,10 @@ Debugger::getEnabled(JSContext *cx, unsigned argc, Value *vp)
|
|||
bool
|
||||
Debugger::setEnabled(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.set enabled", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "set enabled", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.set enabled", 1))
|
||||
return false;
|
||||
|
||||
bool enabled = ToBoolean(args[0]);
|
||||
|
||||
if (enabled != dbg->enabled) {
|
||||
|
@ -1896,8 +1872,9 @@ bool
|
|||
Debugger::setHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which)
|
||||
{
|
||||
JS_ASSERT(which >= 0 && which < HookCount);
|
||||
REQUIRE_ARGC("Debugger.setHook", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "setHook", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.setHook", 1))
|
||||
return false;
|
||||
if (args[0].isObject()) {
|
||||
if (!args[0].toObject().isCallable())
|
||||
return ReportIsNotFunction(cx, args[0], args.length() - 1);
|
||||
|
@ -2005,8 +1982,9 @@ Debugger::getUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp)
|
|||
bool
|
||||
Debugger::setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.set uncaughtExceptionHook", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "set uncaughtExceptionHook", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1))
|
||||
return false;
|
||||
if (!args[0].isNull() && (!args[0].isObject() || !args[0].toObject().isCallable())) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ASSIGN_FUNCTION_OR_NULL,
|
||||
"uncaughtExceptionHook");
|
||||
|
@ -2016,11 +1994,21 @@ Debugger::setUncaughtExceptionHook(JSContext *cx, unsigned argc, Value *vp)
|
|||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::getMemory(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER(cx, argc, vp, "get memory", args, dbg);
|
||||
args.rval().set(dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE));
|
||||
Value memoryValue = dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
|
||||
|
||||
if (!memoryValue.isObject()) {
|
||||
RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
|
||||
if (!memory)
|
||||
return false;
|
||||
memoryValue = ObjectValue(*memory);
|
||||
}
|
||||
|
||||
args.rval().set(memoryValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2068,8 +2056,9 @@ Debugger::unwrapDebuggeeArgument(JSContext *cx, const Value &v)
|
|||
bool
|
||||
Debugger::addDebuggee(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.addDebuggee", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "addDebuggee", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1))
|
||||
return false;
|
||||
Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
|
||||
if (!global)
|
||||
return false;
|
||||
|
@ -2113,9 +2102,10 @@ Debugger::addAllGlobalsAsDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
|||
bool
|
||||
Debugger::removeDebuggee(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.removeDebuggee", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "removeDebuggee", args, dbg);
|
||||
GlobalObject *global = dbg->unwrapDebuggeeArgument(cx, args[0]);
|
||||
if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1))
|
||||
return false;
|
||||
Rooted<GlobalObject *> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
|
||||
if (!global)
|
||||
return false;
|
||||
if (dbg->debuggees.has(global)) {
|
||||
|
@ -2132,7 +2122,8 @@ Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
|||
THIS_DEBUGGER(cx, argc, vp, "removeAllDebuggees", args, dbg);
|
||||
|
||||
for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
|
||||
if (!dbg->removeDebuggeeGlobal(cx, e.front(), nullptr, &e))
|
||||
Rooted<GlobalObject *> global(cx, e.front());
|
||||
if (!dbg->removeDebuggeeGlobal(cx, global, nullptr, &e))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2143,8 +2134,9 @@ Debugger::removeAllDebuggees(JSContext *cx, unsigned argc, Value *vp)
|
|||
bool
|
||||
Debugger::hasDebuggee(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.hasDebuggee", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "hasDebuggee", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1))
|
||||
return false;
|
||||
GlobalObject *global = dbg->unwrapDebuggeeArgument(cx, args[0]);
|
||||
if (!global)
|
||||
return false;
|
||||
|
@ -2239,14 +2231,7 @@ Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
return false;
|
||||
for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP; slot++)
|
||||
obj->setReservedSlot(slot, proto->getReservedSlot(slot));
|
||||
/* Create the Debugger.Memory instance accessible by the
|
||||
* |Debugger.prototype.memory| getter. */
|
||||
Value memoryProto = obj->getReservedSlot(JSSLOT_DEBUG_MEMORY_PROTO);
|
||||
RootedObject memory(cx, NewObjectWithGivenProto(cx, &DebuggerMemory::class_,
|
||||
&memoryProto.toObject(), nullptr));
|
||||
if (!memory)
|
||||
return false;
|
||||
obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, ObjectValue(*memory));
|
||||
obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
|
||||
|
||||
/* Construct the underlying C++ object. */
|
||||
auto dbg = cx->make_unique<Debugger>(cx, obj.get());
|
||||
|
@ -2324,6 +2309,22 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are tracking allocation sites, we need to add the object metadata
|
||||
* callback to this debuggee compartment.
|
||||
*/
|
||||
bool setMetadataCallback = false;
|
||||
if (trackingAllocationSites) {
|
||||
if (debuggeeCompartment->hasObjectMetadataCallback()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
|
||||
return false;
|
||||
}
|
||||
|
||||
debuggeeCompartment->setObjectMetadataCallback(SavedStacksMetadataCallback);
|
||||
setMetadataCallback = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Each debugger-debuggee relation must be stored in up to three places.
|
||||
* JSCompartment::addDebuggee enables debug mode if needed.
|
||||
|
@ -2347,6 +2348,11 @@ Debugger::addDebuggeeGlobal(JSContext *cx,
|
|||
JS_ASSERT(v->back() == this);
|
||||
v->popBack();
|
||||
}
|
||||
|
||||
/* Don't leave the object metadata hook set if we OOM'd. */
|
||||
if (setMetadataCallback)
|
||||
debuggeeCompartment->forgetObjectMetadataCallback();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2412,10 +2418,17 @@ Debugger::cleanupDebuggeeGlobalBeforeRemoval(FreeOp *fop, GlobalObject *global,
|
|||
bp->destroy(fop);
|
||||
}
|
||||
JS_ASSERT_IF(debuggees.empty(), !firstBreakpoint());
|
||||
|
||||
/*
|
||||
* If we are tracking allocation sites, we need to remove the object
|
||||
* metadata callback from this global's compartment.
|
||||
*/
|
||||
if (trackingAllocationSites)
|
||||
global->compartment()->forgetObjectMetadataCallback();
|
||||
}
|
||||
|
||||
bool
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, Handle<GlobalObject *> global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
{
|
||||
|
@ -2424,7 +2437,7 @@ Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
|||
}
|
||||
|
||||
bool
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
Debugger::removeDebuggeeGlobal(JSContext *cx, Handle<GlobalObject *> global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum)
|
||||
|
@ -2916,8 +2929,9 @@ Debugger::findAllGlobals(JSContext *cx, unsigned argc, Value *vp)
|
|||
bool
|
||||
Debugger::makeGlobalObjectReference(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.makeGlobalObjectReference", 1);
|
||||
THIS_DEBUGGER(cx, argc, vp, "makeGlobalObjectReference", args, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1))
|
||||
return false;
|
||||
|
||||
Rooted<GlobalObject *> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
|
||||
if (!global)
|
||||
|
@ -3242,8 +3256,9 @@ ScriptOffset(JSContext *cx, JSScript *script, const Value &v, size_t *offsetp)
|
|||
static bool
|
||||
DebuggerScript_getOffsetLine(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Script.getOffsetLine", 1);
|
||||
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getOffsetLine", args, obj, script);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLine", 1))
|
||||
return false;
|
||||
size_t offset;
|
||||
if (!ScriptOffset(cx, script, args[0], &offset))
|
||||
return false;
|
||||
|
@ -3609,7 +3624,8 @@ static bool
|
|||
DebuggerScript_getLineOffsets(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "getLineOffsets", args, obj, script);
|
||||
REQUIRE_ARGC("Debugger.Script.getLineOffsets", 1);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1))
|
||||
return false;
|
||||
|
||||
/* Parse lineno argument. */
|
||||
RootedValue linenoValue(cx, args[0]);
|
||||
|
@ -3749,8 +3765,9 @@ Debugger::propagateForcedReturn(JSContext *cx, AbstractFramePtr frame, HandleVal
|
|||
static bool
|
||||
DebuggerScript_setBreakpoint(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Script.setBreakpoint", 2);
|
||||
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "setBreakpoint", args, obj, script);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2))
|
||||
return false;
|
||||
Debugger *dbg = Debugger::fromChildJSObject(obj);
|
||||
|
||||
if (!dbg->observesScript(script)) {
|
||||
|
@ -3819,8 +3836,9 @@ DebuggerScript_getBreakpoints(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerScript_clearBreakpoint(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Script.clearBreakpoint", 1);
|
||||
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "clearBreakpoint", args, obj, script);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1))
|
||||
return false;
|
||||
Debugger *dbg = Debugger::fromChildJSObject(obj);
|
||||
|
||||
JSObject *handler = NonNullObject(cx, args[0]);
|
||||
|
@ -3845,8 +3863,9 @@ DebuggerScript_clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerScript_isInCatchScope(JSContext *cx, unsigned argc, Value* vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Script.isInCatchScope", 1);
|
||||
THIS_DEBUGSCRIPT_SCRIPT(cx, argc, vp, "isInCatchScope", args, obj, script);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!ScriptOffset(cx, script, args[0], &offset))
|
||||
|
@ -4652,8 +4671,9 @@ DebuggerFrame_getOnStep(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerFrame_setOnStep(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Frame.set onStep", 1);
|
||||
THIS_FRAME(cx, argc, vp, "set onStep", args, thisobj, frame);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Frame.set onStep", 1))
|
||||
return false;
|
||||
if (!IsValidHook(args[0])) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
|
||||
return false;
|
||||
|
@ -4690,8 +4710,9 @@ DebuggerFrame_getOnPop(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerFrame_setOnPop(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Frame.set onPop", 1);
|
||||
THIS_FRAME(cx, argc, vp, "set onPop", args, thisobj, frame);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Frame.set onPop", 1))
|
||||
return false;
|
||||
(void) frame; // Silence GCC warning
|
||||
if (!IsValidHook(args[0])) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE_OR_UNDEFINED);
|
||||
|
@ -4891,7 +4912,8 @@ static bool
|
|||
DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_FRAME_ITER(cx, argc, vp, "eval", args, thisobj, _, iter);
|
||||
REQUIRE_ARGC("Debugger.Frame.prototype.eval", 1);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.eval", 1))
|
||||
return false;
|
||||
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
|
||||
UpdateFrameIterPc(iter);
|
||||
return DebuggerGenericEval(cx, "Debugger.Frame.prototype.eval",
|
||||
|
@ -4903,7 +4925,8 @@ static bool
|
|||
DebuggerFrame_evalWithBindings(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_FRAME_ITER(cx, argc, vp, "evalWithBindings", args, thisobj, _, iter);
|
||||
REQUIRE_ARGC("Debugger.Frame.prototype.evalWithBindings", 2);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Frame.prototype.evalWithBindings", 2))
|
||||
return false;
|
||||
Debugger *dbg = Debugger::fromChildJSObject(thisobj);
|
||||
UpdateFrameIterPc(iter);
|
||||
return DebuggerGenericEval(cx, "Debugger.Frame.prototype.evalWithBindings",
|
||||
|
@ -5298,6 +5321,18 @@ DebuggerObject_getGlobal(JSContext *cx, unsigned argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerObject_getAllocationSite(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get allocationSite", args, obj);
|
||||
|
||||
RootedObject metadata(cx, obj->getMetadata());
|
||||
if (!cx->compartment()->wrap(cx, &metadata))
|
||||
return false;
|
||||
args.rval().setObjectOrNull(metadata);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DebuggerObject_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -5383,7 +5418,8 @@ static bool
|
|||
DebuggerObject_defineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperty", args, dbg, obj);
|
||||
REQUIRE_ARGC("Debugger.Object.defineProperty", 2);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.defineProperty", 2))
|
||||
return false;
|
||||
|
||||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(cx, args[0], &id))
|
||||
|
@ -5418,7 +5454,8 @@ static bool
|
|||
DebuggerObject_defineProperties(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "defineProperties", args, dbg, obj);
|
||||
REQUIRE_ARGC("Debugger.Object.defineProperties", 1);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.defineProperties", 1))
|
||||
return false;
|
||||
|
||||
RootedValue arg(cx, args[0]);
|
||||
RootedObject props(cx, ToObject(cx, arg));
|
||||
|
@ -5666,8 +5703,9 @@ DebuggerObject_call(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerObject_makeDebuggeeValue(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Object.prototype.makeDebuggeeValue", 1);
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "makeDebuggeeValue", args, dbg, referent);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.makeDebuggeeValue", 1))
|
||||
return false;
|
||||
|
||||
RootedValue arg0(cx, args[0]);
|
||||
|
||||
|
@ -5730,8 +5768,9 @@ RequireGlobalObject(JSContext *cx, HandleValue dbgobj, HandleObject referent)
|
|||
static bool
|
||||
DebuggerObject_evalInGlobal(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobal", 1);
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobal", args, dbg, referent);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.evalInGlobal", 1))
|
||||
return false;
|
||||
if (!RequireGlobalObject(cx, args.thisv(), referent))
|
||||
return false;
|
||||
|
||||
|
@ -5743,8 +5782,9 @@ DebuggerObject_evalInGlobal(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerObject_evalInGlobalWithBindings(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Object.prototype.evalInGlobalWithBindings", 2);
|
||||
THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobalWithBindings", args, dbg, referent);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Object.prototype.evalInGlobalWithBindings", 2))
|
||||
return false;
|
||||
if (!RequireGlobalObject(cx, args.thisv(), referent))
|
||||
return false;
|
||||
|
||||
|
@ -5797,6 +5837,7 @@ static const JSPropertySpec DebuggerObject_properties[] = {
|
|||
JS_PSG("boundThis", DebuggerObject_getBoundThis, 0),
|
||||
JS_PSG("boundArguments", DebuggerObject_getBoundArguments, 0),
|
||||
JS_PSG("global", DebuggerObject_getGlobal, 0),
|
||||
JS_PSG("allocationSite", DebuggerObject_getAllocationSite, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
|
@ -6058,8 +6099,9 @@ DebuggerEnv_names(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerEnv_find(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Environment.find", 1);
|
||||
THIS_DEBUGENV_OWNER(cx, argc, vp, "find", args, envobj, env, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Environment.find", 1))
|
||||
return false;
|
||||
|
||||
RootedId id(cx);
|
||||
if (!ValueToIdentifier(cx, args[0], &id))
|
||||
|
@ -6087,8 +6129,9 @@ DebuggerEnv_find(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerEnv_getVariable(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Environment.getVariable", 1);
|
||||
THIS_DEBUGENV_OWNER(cx, argc, vp, "getVariable", args, envobj, env, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Environment.getVariable", 1))
|
||||
return false;
|
||||
|
||||
RootedId id(cx);
|
||||
if (!ValueToIdentifier(cx, args[0], &id))
|
||||
|
@ -6124,8 +6167,9 @@ DebuggerEnv_getVariable(JSContext *cx, unsigned argc, Value *vp)
|
|||
static bool
|
||||
DebuggerEnv_setVariable(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
REQUIRE_ARGC("Debugger.Environment.setVariable", 2);
|
||||
THIS_DEBUGENV_OWNER(cx, argc, vp, "setVariable", args, envobj, env, dbg);
|
||||
if (!args.requireAtLeast(cx, "Debugger.Environment.setVariable", 2))
|
||||
return false;
|
||||
|
||||
RootedId id(cx);
|
||||
if (!ValueToIdentifier(cx, args[0], &id))
|
||||
|
|
|
@ -158,6 +158,7 @@ typedef JSObject Env;
|
|||
class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
{
|
||||
friend class Breakpoint;
|
||||
friend class DebuggerMemory;
|
||||
friend class mozilla::LinkedListElement<Debugger>;
|
||||
friend bool (::JS_DefineDebuggerObject)(JSContext *cx, JS::HandleObject obj);
|
||||
|
||||
|
@ -189,6 +190,7 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
GlobalObjectSet debuggees; /* Debuggee globals. Cross-compartment weak references. */
|
||||
js::HeapPtrObject uncaughtExceptionHook; /* Strong reference. */
|
||||
bool enabled;
|
||||
bool trackingAllocationSites;
|
||||
JSCList breakpoints; /* Circular list of all js::Breakpoints in this debugger */
|
||||
|
||||
/*
|
||||
|
@ -243,10 +245,10 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
|||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnu);
|
||||
bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
bool removeDebuggeeGlobal(JSContext *cx, Handle<GlobalObject *> global,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
bool removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
|
||||
bool removeDebuggeeGlobal(JSContext *cx, Handle<GlobalObject *> global,
|
||||
AutoDebugModeInvalidation &invalidate,
|
||||
GlobalObjectSet::Enum *compartmentEnum,
|
||||
GlobalObjectSet::Enum *debugEnum);
|
||||
|
@ -766,6 +768,8 @@ EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFrameP
|
|||
mozilla::Range<const jschar> chars, const char *filename, unsigned lineno,
|
||||
MutableHandleValue rval);
|
||||
|
||||
}
|
||||
bool ReportObjectRequired(JSContext *cx);
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* vm_Debugger_h */
|
||||
|
|
|
@ -6,8 +6,31 @@
|
|||
|
||||
#include "vm/DebuggerMemory.h"
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/SavedStacks.h"
|
||||
|
||||
#include "vm/Debugger-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
/* static */ DebuggerMemory *
|
||||
DebuggerMemory::create(JSContext *cx, Debugger *dbg)
|
||||
{
|
||||
|
||||
Value memoryProto = dbg->object->getReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO);
|
||||
RootedObject memory(cx, NewObjectWithGivenProto(cx, &class_,
|
||||
&memoryProto.toObject(), nullptr));
|
||||
if (!memory)
|
||||
return nullptr;
|
||||
|
||||
dbg->object->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_INSTANCE, ObjectValue(*memory));
|
||||
memory->setReservedSlot(JSSLOT_DEBUGGER, ObjectValue(*dbg->object));
|
||||
|
||||
return &memory->as<DebuggerMemory>();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::construct(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -19,7 +42,7 @@ DebuggerMemory::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* static */ const Class DebuggerMemory::class_ = {
|
||||
"Memory",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
|
||||
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGGER_MEMORY_COUNT),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(DebuggerMemory::JSSLOT_COUNT),
|
||||
|
||||
JS_PropertyStub, // addProperty
|
||||
JS_DeletePropertyStub, // delProperty
|
||||
|
@ -36,7 +59,112 @@ DebuggerMemory::construct(JSContext *cx, unsigned argc, Value *vp)
|
|||
nullptr // trace
|
||||
};
|
||||
|
||||
/* static */ DebuggerMemory *
|
||||
DebuggerMemory::checkThis(JSContext *cx, CallArgs &args, const char *fnName)
|
||||
{
|
||||
const Value &thisValue = args.thisv();
|
||||
|
||||
if (!thisValue.isObject()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject &thisObject = thisValue.toObject();
|
||||
if (!thisObject.is<DebuggerMemory>()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
DebuggerMemory::class_.name, fnName, thisObject.getClass()->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check for Debugger.Memory.prototype, which has the same class as
|
||||
// Debugger.Memory instances, however doesn't actually represent an instance
|
||||
// of Debugger.Memory. It is the only object that is<DebuggerMemory>() but
|
||||
// doesn't have a Debugger instance.
|
||||
if (thisObject.getReservedSlot(JSSLOT_DEBUGGER).isUndefined()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||
DebuggerMemory::class_.name, fnName, "prototype object");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &thisObject.as<DebuggerMemory>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the |DebuggerMemory *| from the current this value and handle any errors
|
||||
* that might occur therein.
|
||||
*
|
||||
* These parameters must already exist when calling this macro:
|
||||
* - JSContext *cx
|
||||
* - unsigned argc
|
||||
* - Value *vp
|
||||
* - const char *fnName
|
||||
* These parameters will be defined after calling this macro:
|
||||
* - CallArgs args
|
||||
* - DebuggerMemory *memory (will be non-null)
|
||||
*/
|
||||
#define THIS_DEBUGGER_MEMORY(cx, argc, vp, fnName, args, memory) \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
Rooted<DebuggerMemory *> memory(cx, checkThis(cx, args, fnName)); \
|
||||
if (!memory) \
|
||||
return false
|
||||
|
||||
Debugger *
|
||||
DebuggerMemory::getDebugger()
|
||||
{
|
||||
return Debugger::fromJSObject(&getReservedSlot(JSSLOT_DEBUGGER).toObject());
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set trackingAllocationSites)", args, memory);
|
||||
if (!args.requireAtLeast(cx, "(set trackingAllocationSites)", 1))
|
||||
return false;
|
||||
|
||||
Debugger *dbg = memory->getDebugger();
|
||||
bool enabling = ToBoolean(args[0]);
|
||||
|
||||
if (enabling == dbg->trackingAllocationSites) {
|
||||
// Nothing to do here...
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (enabling) {
|
||||
for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
|
||||
JSCompartment *compartment = r.front()->compartment();
|
||||
if (compartment->hasObjectMetadataCallback()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
|
||||
JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront()) {
|
||||
if (enabling) {
|
||||
r.front()->compartment()->setObjectMetadataCallback(SavedStacksMetadataCallback);
|
||||
} else {
|
||||
r.front()->compartment()->forgetObjectMetadataCallback();
|
||||
}
|
||||
}
|
||||
|
||||
dbg->trackingAllocationSites = enabling;
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
DebuggerMemory::getTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get trackingAllocationSites)", args, memory);
|
||||
args.rval().setBoolean(memory->getDebugger()->trackingAllocationSites);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ const JSPropertySpec DebuggerMemory::properties[] = {
|
||||
JS_PSGS("trackingAllocationSites", DebuggerMemory::getTrackingAllocationSites,
|
||||
DebuggerMemory::setTrackingAllocationSites, 0),
|
||||
JS_PS_END
|
||||
};
|
||||
|
||||
|
|
|
@ -16,17 +16,27 @@
|
|||
namespace js {
|
||||
|
||||
class DebuggerMemory : public JSObject {
|
||||
friend class Debugger;
|
||||
|
||||
static DebuggerMemory *checkThis(JSContext *cx, CallArgs &args, const char *fnName);
|
||||
|
||||
Debugger *getDebugger();
|
||||
|
||||
public:
|
||||
static DebuggerMemory *create(JSContext *cx, Debugger *dbg);
|
||||
|
||||
enum {
|
||||
JSSLOT_DEBUGGER_MEMORY_COUNT
|
||||
JSSLOT_DEBUGGER,
|
||||
JSSLOT_COUNT
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
||||
|
||||
static const Class class_;
|
||||
static const JSPropertySpec properties[];
|
||||
static const JSFunctionSpec methods[];
|
||||
|
||||
static bool setTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp);
|
||||
static bool getTrackingAllocationSites(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
|
|
@ -612,13 +612,13 @@ SavedStacks::createFrameFromLookup(JSContext *cx, const SavedFrame::Lookup &look
|
|||
if (!frameObj)
|
||||
return nullptr;
|
||||
|
||||
SavedFrame &f = frameObj->as<SavedFrame>();
|
||||
f.initFromLookup(lookup);
|
||||
RootedSavedFrame f(cx, &frameObj->as<SavedFrame>());
|
||||
f->initFromLookup(lookup);
|
||||
|
||||
if (!JSObject::freeze(cx, frameObj))
|
||||
return nullptr;
|
||||
|
||||
return &f;
|
||||
return f.get();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче