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:
Brian Hackett 2019-09-08 01:11:43 +00:00
parent 00dac0b506
commit 1ec1028ab4
4 changed files with 125 additions and 13 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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

View 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"]);
}