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 345820 b5000cae87e9967bcc096ebcc9d693cae8e79b6b
parent 345819 49bef2408c15ae8e76ee485bd8c338573fa8a1c7
child 345821 cae3143edbd4d45359acb39fd18989ce986246e3
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1282257
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);