Bug 1282257 - Create accessor properties in Debugger.Object.prototype to expose the target and the handler of a proxy object. r=jimb
☠☠ backed out by 6f5fc76550ed ☠ ☠
authorOriol <oriol-bugzilla@hotmail.com>
Tue, 19 Jul 2016 15:00:00 +0200
changeset 389951 b5000cae87e9967bcc096ebcc9d693cae8e79b6b
parent 389950 49bef2408c15ae8e76ee485bd8c338573fa8a1c7
child 389952 cae3143edbd4d45359acb39fd18989ce986246e3
push id23566
push usertkuo@mozilla.com
push dateWed, 20 Jul 2016 10:57:43 +0000
reviewersjimb
bugs1282257
milestone50.0a1
Bug 1282257 - Create accessor properties in Debugger.Object.prototype to expose the target and the handler of a proxy object. r=jimb
js/src/doc/Debugger/Debugger.Object.md
js/src/jit-test/tests/debug/Object-proxy.js
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
--- 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);