Bug 1282257 - Create accessor properties in Debugger.Object.prototype to expose the target and the handler of a proxy object. r=jimb
--- a/js/src/doc/Debugger/Debugger.Object.md
+++ b/js/src/doc/Debugger/Debugger.Object.md
@@ -183,36 +183,29 @@ from its prototype:
`boundArguments`
: If the referent is a bound debuggee function, this is an array (in the
Debugger object's compartment) that contains the debuggee values of the
`arguments` object it was bound to. If the referent is either not a bound
function, not a debuggee function, or not a function at all, this is
`undefined`.
-`proxyHandler`
-: If the referent is a proxy whose handler object was allocated by
- debuggee code, this is its handler object—the object whose methods are
- invoked to implement accesses of the proxy's properties. If the referent
- is not a proxy whose handler object was allocated by debuggee code, this
- is `null`.
+`isProxy`
+: If the referent is a (scripted) proxy, return `true`. If the referent is not
+ a proxy, return `false`.
-`proxyCallTrap`
-: If the referent is a function proxy whose handler object was allocated
- by debuggee code, this is its call trap function—the function called
- when the function proxy is called. If the referent is not a function
- proxy whose handler object was allocated by debuggee code, this is
- `null`.
+`proxyTarget`
+: If the referent is a (scripted) proxy, return a `Debugger.Object` instance
+ referring to the ECMAScript `[[ProxyTarget]]` of the referent. If the referent
+ is not a proxy, return `undefined`.
-`proxyConstructTrap`
-: If the referent is a function proxy whose handler object was allocated
- by debuggee code, its construction trap function—the function called
- when the function proxy is called via a `new` expression. If the
- referent is not a function proxy whose handler object was allocated by
- debuggee code, this is `null`.
+`proxyHandler`
+: If the referent is a (scripted) proxy, return a `Debugger.Object` instance
+ referring to the ECMAScript `[[ProxyHandler]]` of the referent. If the referent
+ is not a proxy, return `undefined`.
`promiseState`
: If the referent is a [`Promise`][promise], this is an object describing
the Promise's current state, with the following properties:
`state`
: A string indicating whether the [`Promise`][promise] is pending or
has been fulfilled or rejected.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-proxy.js
@@ -0,0 +1,32 @@
+// Debugger.Object.prototype.isProxy recognizes (scripted) proxies.
+// Debugger.Object.prototype.proxyTarget exposes the [[Proxytarget]] of a proxy.
+// Debugger.Object.prototype.proxyHandler exposes the [[ProxyHandler]] of a proxy.
+
+var g = newGlobal();
+var dbg = new Debugger;
+var gDO = dbg.addDebuggee(g);
+
+g.eval('var target = [1,2,3];');
+g.eval('var handler = {has: ()=>false};');
+g.eval('var proxy = new Proxy(target, handler);');
+g.eval('var proxyProxy = new Proxy(proxy, proxy);');
+
+var target = gDO.getOwnPropertyDescriptor('target').value;
+var handler = gDO.getOwnPropertyDescriptor('handler').value;
+var proxy = gDO.getOwnPropertyDescriptor('proxy').value;
+var proxyProxy = gDO.getOwnPropertyDescriptor('proxyProxy').value;
+
+assertEq(target.isProxy, false);
+assertEq(handler.isProxy, false);
+assertEq(proxy.isProxy, true);
+assertEq(proxyProxy.isProxy, true);
+
+assertEq(target.proxyTarget, undefined);
+assertEq(handler.proxyTarget, undefined);
+assertEq(proxy.proxyTarget, target);
+assertEq(proxyProxy.proxyTarget, proxy);
+
+assertEq(target.proxyHandler, undefined);
+assertEq(handler.proxyHandler, undefined);
+assertEq(proxy.proxyHandler, handler);
+assertEq(proxyProxy.proxyTarget, proxy);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -8283,16 +8283,60 @@ DebuggerObject::errorMessageNameGetter(J
if (result)
args.rval().setString(result);
else
args.rval().setUndefined();
return true;
}
+/* static */ bool
+DebuggerObject::isProxyGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ THIS_DEBUGOBJECT(cx, argc, vp, "get isProxy", args, object)
+
+ args.rval().setBoolean(object->isProxy());
+ return true;
+}
+
+/* static */ bool
+DebuggerObject::proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ THIS_DEBUGOBJECT(cx, argc, vp, "get proxyTarget", args, object)
+
+ if (!object->isProxy()) {
+ args.rval().setUndefined();
+ return true;
+ }
+
+ Rooted<DebuggerObject*> result(cx);
+ if (!DebuggerObject::proxyTarget(cx, object, &result))
+ return false;
+
+ args.rval().setObject(*result);
+ return true;
+}
+
+/* static */ bool
+DebuggerObject::proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ THIS_DEBUGOBJECT(cx, argc, vp, "get proxyHandler", args, object)
+
+ if (!object->isProxy()) {
+ args.rval().setUndefined();
+ return true;
+ }
+ Rooted<DebuggerObject*> result(cx);
+ if (!DebuggerObject::proxyHandler(cx, object, &result))
+ return false;
+
+ args.rval().setObject(*result);
+ return true;
+}
+
#ifdef SPIDERMONKEY_PROMISE
/* static */ bool
DebuggerObject::isPromiseGetter(JSContext* cx, unsigned argc, Value* vp)
{
THIS_DEBUGOBJECT(cx, argc, vp, "get isPromise", args, object)
args.rval().setBoolean(object->isPromise());
return true;
@@ -8849,16 +8893,19 @@ const JSPropertySpec DebuggerObject::pro
JS_PSG("script", DebuggerObject::scriptGetter, 0),
JS_PSG("environment", DebuggerObject::environmentGetter, 0),
JS_PSG("boundTargetFunction", DebuggerObject::boundTargetFunctionGetter, 0),
JS_PSG("boundThis", DebuggerObject::boundThisGetter, 0),
JS_PSG("boundArguments", DebuggerObject::boundArgumentsGetter, 0),
JS_PSG("global", DebuggerObject::globalGetter, 0),
JS_PSG("allocationSite", DebuggerObject::allocationSiteGetter, 0),
JS_PSG("errorMessageName", DebuggerObject::errorMessageNameGetter, 0),
+ JS_PSG("isProxy", DebuggerObject::isProxyGetter, 0),
+ JS_PSG("proxyTarget", DebuggerObject::proxyTargetGetter, 0),
+ JS_PSG("proxyHandler", DebuggerObject::proxyHandlerGetter, 0),
JS_PS_END
};
#ifdef SPIDERMONKEY_PROMISE
const JSPropertySpec DebuggerObject::promiseProperties_[] = {
JS_PSG("isPromise", DebuggerObject::isPromiseGetter, 0),
JS_PSG("promiseState", DebuggerObject::promiseStateGetter, 0),
JS_PSG("promiseLifetime", DebuggerObject::promiseLifetimeGetter, 0),
@@ -8978,16 +9025,22 @@ DebuggerObject::isPromise() const
{
JSObject* obj = referent();
if (IsCrossCompartmentWrapper(obj))
obj = CheckedUnwrap(obj);
return obj->is<PromiseObject>();
}
+bool
+DebuggerObject::isProxy() const
+{
+ return js::IsScriptedProxy(referent());
+}
+
/* static */ bool
DebuggerObject::getClassName(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandleString result)
{
RootedObject referent(cx, object->referent());
const char* className;
{
@@ -9635,16 +9688,38 @@ DebuggerObject::requireGlobal(JSContext*
"a global object", nullptr);
}
return false;
}
return true;
}
+/* static */ bool
+DebuggerObject::proxyTarget(JSContext* cx, Handle<DebuggerObject*> object,
+ MutableHandle<DebuggerObject*> result)
+{
+ MOZ_ASSERT(isProxy());
+ RootedObject referent(cx, object->referent());
+ Debugger* dbg = object->owner();
+ RootedObject unwrapped(cx, js::GetProxyTargetObject(referent));
+ return dbg->wrapDebuggeeObject(cx, unwrapped, result);
+}
+
+/* static */ bool
+DebuggerObject::proxyHandler(JSContext* cx, Handle<DebuggerObject*> object,
+ MutableHandle<DebuggerObject*> result)
+{
+ MOZ_ASSERT(isProxy());
+ RootedObject referent(cx, object->referent());
+ Debugger* dbg = object->owner();
+ RootedObject unwrapped(cx, js::GetProxyExtra(referent, 0).toObjectOrNull());
+ return dbg->wrapDebuggeeObject(cx, unwrapped, result);
+}
+
/*** Debugger.Environment ************************************************************************/
void
DebuggerEnv_trace(JSTracer* trc, JSObject* obj)
{
/*
* There is a barrier on private pointers, so the Unbarriered marking
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1197,16 +1197,20 @@ class DebuggerObject : public NativeObje
static MOZ_MUST_USE bool getBoundThis(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandleValue result);
static MOZ_MUST_USE bool getBoundArguments(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandle<ValueVector> result);
static MOZ_MUST_USE bool getAllocationSite(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandleObject result);
static MOZ_MUST_USE bool getErrorMessageName(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandleString result);
+ static MOZ_MUST_USE bool proxyTarget(JSContext* cx, Handle<DebuggerObject*> object,
+ MutableHandle<DebuggerObject*> result);
+ static MOZ_MUST_USE bool proxyHandler(JSContext* cx, Handle<DebuggerObject*> object,
+ MutableHandle<DebuggerObject*> result);
// Methods
static MOZ_MUST_USE bool isExtensible(JSContext* cx, Handle<DebuggerObject*> object,
bool& result);
static MOZ_MUST_USE bool isSealed(JSContext* cx, Handle<DebuggerObject*> object, bool& result);
static MOZ_MUST_USE bool isFrozen(JSContext* cx, Handle<DebuggerObject*> object, bool& result);
static MOZ_MUST_USE bool getPrototypeOf(JSContext* cx, Handle<DebuggerObject*> object,
MutableHandle<DebuggerObject*> result);
@@ -1246,16 +1250,17 @@ class DebuggerObject : public NativeObje
// Infallible properties
bool isCallable() const;
bool isFunction() const;
bool isDebuggeeFunction() const;
bool isBoundFunction() const;
bool isArrowFunction() const;
bool isGlobal() const;
bool isPromise() const;
+ bool isProxy() const;
JSAtom* name() const;
JSAtom* displayName() const;
private:
enum {
OWNER_SLOT
};
@@ -1294,16 +1299,19 @@ class DebuggerObject : public NativeObje
static MOZ_MUST_USE bool scriptGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool boundTargetFunctionGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool boundThisGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool boundArgumentsGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool globalGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool allocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool errorMessageNameGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool isProxyGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp);
+ static MOZ_MUST_USE bool proxyHandlerGetter(JSContext* cx, unsigned argc, Value* vp);
#ifdef SPIDERMONKEY_PROMISE
static MOZ_MUST_USE bool isPromiseGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseStateGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseLifetimeGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseTimeToResolutionGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseAllocationSiteGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseResolutionSiteGetter(JSContext* cx, unsigned argc, Value* vp);
static MOZ_MUST_USE bool promiseIDGetter(JSContext* cx, unsigned argc, Value* vp);