mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 19:25:43 +00:00
Bug 1577007 - Add debugger method to compare underlying natives, r=jimb.
Differential Revision: https://phabricator.services.mozilla.com/D43680 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
00dac0b506
commit
1ec1028ab4
@ -54,6 +54,7 @@
|
|||||||
#include "vm/Runtime.h" // for JSAtomState
|
#include "vm/Runtime.h" // for JSAtomState
|
||||||
#include "vm/SavedFrame.h" // for SavedFrame
|
#include "vm/SavedFrame.h" // for SavedFrame
|
||||||
#include "vm/Scope.h" // for PositionalFormalParameterIter
|
#include "vm/Scope.h" // for PositionalFormalParameterIter
|
||||||
|
#include "vm/SelfHosting.h" // for GetClonedSelfHostedFunctionName
|
||||||
#include "vm/Shape.h" // for Shape
|
#include "vm/Shape.h" // for Shape
|
||||||
#include "vm/Stack.h" // for InvokeArgs
|
#include "vm/Stack.h" // for InvokeArgs
|
||||||
#include "vm/StringType.h" // for JSAtom, PropertyName
|
#include "vm/StringType.h" // for JSAtom, PropertyName
|
||||||
@ -1411,6 +1412,18 @@ bool DebuggerObject::makeDebuggeeNativeFunctionMethod(JSContext* cx,
|
|||||||
args.rval());
|
args.rval());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool DebuggerObject::isSameNativeMethod(JSContext* cx, unsigned argc,
|
||||||
|
Value* vp) {
|
||||||
|
THIS_DEBUGOBJECT(cx, argc, vp, "isSameNative", args, object);
|
||||||
|
if (!args.requireAtLeast(
|
||||||
|
cx, "Debugger.Object.prototype.isSameNative", 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DebuggerObject::isSameNative(cx, object, args[0], args.rval());
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc,
|
bool DebuggerObject::unsafeDereferenceMethod(JSContext* cx, unsigned argc,
|
||||||
Value* vp) {
|
Value* vp) {
|
||||||
@ -1605,6 +1618,7 @@ const JSFunctionSpec DebuggerObject::methods_[] = {
|
|||||||
JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
|
JS_FN("makeDebuggeeValue", DebuggerObject::makeDebuggeeValueMethod, 1, 0),
|
||||||
JS_FN("makeDebuggeeNativeFunction",
|
JS_FN("makeDebuggeeNativeFunction",
|
||||||
DebuggerObject::makeDebuggeeNativeFunctionMethod, 1, 0),
|
DebuggerObject::makeDebuggeeNativeFunctionMethod, 1, 0),
|
||||||
|
JS_FN("isSameNative", DebuggerObject::isSameNativeMethod, 1, 0),
|
||||||
JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
|
JS_FN("unsafeDereference", DebuggerObject::unsafeDereferenceMethod, 0, 0),
|
||||||
JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
|
JS_FN("unwrap", DebuggerObject::unwrapMethod, 0, 0),
|
||||||
JS_FN("setInstrumentation", DebuggerObject::setInstrumentationMethod, 2, 0),
|
JS_FN("setInstrumentation", DebuggerObject::setInstrumentationMethod, 2, 0),
|
||||||
@ -2503,6 +2517,20 @@ bool DebuggerObject::makeDebuggeeValue(JSContext* cx,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSFunction* EnsureNativeFunction(const Value& value,
|
||||||
|
bool allowExtended = true) {
|
||||||
|
if (!value.isObject() || !value.toObject().is<JSFunction>()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSFunction* fun = &value.toObject().as<JSFunction>();
|
||||||
|
if (!fun->isNative() || (fun->isExtended() && !allowExtended)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fun;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool DebuggerObject::makeDebuggeeNativeFunction(JSContext* cx,
|
bool DebuggerObject::makeDebuggeeNativeFunction(JSContext* cx,
|
||||||
HandleDebuggerObject object,
|
HandleDebuggerObject object,
|
||||||
@ -2511,19 +2539,10 @@ bool DebuggerObject::makeDebuggeeNativeFunction(JSContext* cx,
|
|||||||
RootedObject referent(cx, object->referent());
|
RootedObject referent(cx, object->referent());
|
||||||
Debugger* dbg = object->owner();
|
Debugger* dbg = object->owner();
|
||||||
|
|
||||||
if (!value.isObject()) {
|
// The logic below doesn't work with extended functions, so do not allow them.
|
||||||
JS_ReportErrorASCII(cx, "Need object");
|
RootedFunction fun(cx, EnsureNativeFunction(value,
|
||||||
return false;
|
/* allowExtended */ false));
|
||||||
}
|
if (!fun) {
|
||||||
|
|
||||||
RootedObject obj(cx, &value.toObject());
|
|
||||||
if (!obj->is<JSFunction>()) {
|
|
||||||
JS_ReportErrorASCII(cx, "Need function");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedFunction fun(cx, &obj->as<JSFunction>());
|
|
||||||
if (!fun->isNative() || fun->isExtended()) {
|
|
||||||
JS_ReportErrorASCII(cx, "Need native function");
|
JS_ReportErrorASCII(cx, "Need native function");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2553,6 +2572,43 @@ bool DebuggerObject::makeDebuggeeNativeFunction(JSContext* cx,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSAtom* MaybeGetSelfHostedFunctionName(const Value& v) {
|
||||||
|
if (!v.isObject() || !v.toObject().is<JSFunction>()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSFunction* fun = &v.toObject().as<JSFunction>();
|
||||||
|
if (!fun->isSelfHostedBuiltin()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetClonedSelfHostedFunctionName(fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool DebuggerObject::isSameNative(JSContext* cx, HandleDebuggerObject object,
|
||||||
|
HandleValue value,
|
||||||
|
MutableHandleValue result) {
|
||||||
|
RootedValue referentValue(cx, ObjectValue(*object->referent()));
|
||||||
|
|
||||||
|
RootedFunction fun(cx, EnsureNativeFunction(value));
|
||||||
|
if (!fun) {
|
||||||
|
RootedAtom selfHostedName(cx, MaybeGetSelfHostedFunctionName(value));
|
||||||
|
if (!selfHostedName) {
|
||||||
|
JS_ReportErrorASCII(cx, "Need native function");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setBoolean(
|
||||||
|
selfHostedName == MaybeGetSelfHostedFunctionName(referentValue));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedFunction referentFun(cx, EnsureNativeFunction(referentValue));
|
||||||
|
result.setBoolean(referentFun && referentFun->native() == fun->native());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool DebuggerObject::unsafeDereference(JSContext* cx,
|
bool DebuggerObject::unsafeDereference(JSContext* cx,
|
||||||
HandleDebuggerObject object,
|
HandleDebuggerObject object,
|
||||||
|
@ -146,6 +146,10 @@ class DebuggerObject : public NativeObject {
|
|||||||
static MOZ_MUST_USE bool makeDebuggeeNativeFunction(
|
static MOZ_MUST_USE bool makeDebuggeeNativeFunction(
|
||||||
JSContext* cx, HandleDebuggerObject object, HandleValue value,
|
JSContext* cx, HandleDebuggerObject object, HandleValue value,
|
||||||
MutableHandleValue result);
|
MutableHandleValue result);
|
||||||
|
static MOZ_MUST_USE bool isSameNative(JSContext* cx,
|
||||||
|
HandleDebuggerObject object,
|
||||||
|
HandleValue value,
|
||||||
|
MutableHandleValue result);
|
||||||
static MOZ_MUST_USE bool unsafeDereference(JSContext* cx,
|
static MOZ_MUST_USE bool unsafeDereference(JSContext* cx,
|
||||||
HandleDebuggerObject object,
|
HandleDebuggerObject object,
|
||||||
MutableHandleObject result);
|
MutableHandleObject result);
|
||||||
@ -313,6 +317,8 @@ class DebuggerObject : public NativeObject {
|
|||||||
static MOZ_MUST_USE bool makeDebuggeeNativeFunctionMethod(JSContext* cx,
|
static MOZ_MUST_USE bool makeDebuggeeNativeFunctionMethod(JSContext* cx,
|
||||||
unsigned argc,
|
unsigned argc,
|
||||||
Value* vp);
|
Value* vp);
|
||||||
|
static MOZ_MUST_USE bool isSameNativeMethod(JSContext* cx, unsigned argc,
|
||||||
|
Value* vp);
|
||||||
static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc,
|
static MOZ_MUST_USE bool unsafeDereferenceMethod(JSContext* cx, unsigned argc,
|
||||||
Value* vp);
|
Value* vp);
|
||||||
static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc,
|
static MOZ_MUST_USE bool unwrapMethod(JSContext* cx, unsigned argc,
|
||||||
|
@ -460,6 +460,10 @@ of exotic object like an opaque wrapper.
|
|||||||
can be accessed by code in the debuggee without going through a cross
|
can be accessed by code in the debuggee without going through a cross
|
||||||
compartment wrapper.
|
compartment wrapper.
|
||||||
|
|
||||||
|
<code>isSameNative(<i>value</i>)</code>
|
||||||
|
: If <i>value</i> is a native function in the debugger's compartment, return
|
||||||
|
whether the referent is a native function for the same C++ native.
|
||||||
|
|
||||||
<code>decompile([<i>pretty</i>])</code>
|
<code>decompile([<i>pretty</i>])</code>
|
||||||
: If the referent is a function that is debuggee code, return the
|
: If the referent is a function that is debuggee code, return the
|
||||||
JavaScript source code for a function definition equivalent to the
|
JavaScript source code for a function definition equivalent to the
|
||||||
|
46
js/src/jit-test/tests/debug/Object-isSameNative.js
Normal file
46
js/src/jit-test/tests/debug/Object-isSameNative.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// Test that the onNativeCall hook is called when expected.
|
||||||
|
|
||||||
|
load(libdir + 'eqArrayHelper.js');
|
||||||
|
|
||||||
|
var g = newGlobal({newCompartment: true});
|
||||||
|
var dbg = Debugger(g);
|
||||||
|
var gdbg = dbg.addDebuggee(g);
|
||||||
|
|
||||||
|
assertEq(gdbg.getProperty("print").return.isSameNative(print), true);
|
||||||
|
assertEq(gdbg.getProperty("print").return.isSameNative(newGlobal), false);
|
||||||
|
|
||||||
|
g.eval(`
|
||||||
|
const x = [];
|
||||||
|
Object.defineProperty(x, "a", {
|
||||||
|
get: print,
|
||||||
|
set: print,
|
||||||
|
});
|
||||||
|
function f() {
|
||||||
|
x.a++;
|
||||||
|
x.length = 0;
|
||||||
|
x.push(4, 5, 6);
|
||||||
|
x.sort(print);
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const comparisons = [
|
||||||
|
print,
|
||||||
|
Array.prototype.push,
|
||||||
|
Array.prototype.sort, // Note: self-hosted
|
||||||
|
newGlobal
|
||||||
|
];
|
||||||
|
|
||||||
|
const rv = [];
|
||||||
|
dbg.onNativeCall = (callee, reason) => {
|
||||||
|
for (const fn of comparisons) {
|
||||||
|
if (callee.isSameNative(fn)) {
|
||||||
|
rv.push(fn.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
rv.length = 0;
|
||||||
|
gdbg.executeInGlobal(`f()`);
|
||||||
|
assertEqArray(rv, ["print", "print", "push", "sort", "print", "print"]);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user