Bug 1282257 - Create accessor properties in Debugger.Object.prototype to expose the target and the handler of a proxy object. r=jimb
authorOriol <oriol-bugzilla@hotmail.com>
Fri, 22 Jul 2016 10:50:00 -0400
changeset 306406 af78c13c85e52b6246b39055c7738fad73aec5db
parent 306405 304a57bfb0d482153eec4b7760916cf85f1a180d
child 306407 8d2a4af272e3a6247241fa5b1634f6c705cd8a82
push id30484
push usercbook@mozilla.com
push dateMon, 25 Jul 2016 13:51:04 +0000
treeherdermozilla-central@e23f2ec25e96 [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 (scripted) 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 (scripted) 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 (scripted) 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
@@ -40,16 +40,18 @@
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
 
+#include "proxy/ScriptedProxyHandler.h"
+
 using namespace js;
 
 using JS::dbg::AutoEntryMonitor;
 using JS::dbg::Builder;
 using js::frontend::IsIdentifier;
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::MakeScopeExit;
@@ -8470,16 +8472,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->isScriptedProxy());
+    return true;
+}
+
+/* static */ bool
+DebuggerObject::proxyTargetGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGOBJECT(cx, argc, vp, "get proxyTarget", args, object)
+
+    if (!object->isScriptedProxy()) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    Rooted<DebuggerObject*> result(cx);
+    if (!DebuggerObject::scriptedProxyTarget(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->isScriptedProxy()) {
+        args.rval().setUndefined();
+        return true;
+    }
+    Rooted<DebuggerObject*> result(cx);
+    if (!DebuggerObject::scriptedProxyHandler(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)
 
     bool result;
     if (!DebuggerObject::isPromise(cx, object, result))
@@ -9040,16 +9086,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),
@@ -9159,16 +9208,22 @@ DebuggerObject::isArrowFunction() const
 }
 
 bool
 DebuggerObject::isGlobal() const
 {
     return referent()->is<GlobalObject>();
 }
 
+bool
+DebuggerObject::isScriptedProxy() const
+{
+    return js::IsScriptedProxy(referent());
+}
+
 /* static */ bool
 DebuggerObject::isPromise(JSContext* cx, Handle<DebuggerObject*> object,
                           bool& result)
 {
     JSObject* referent = object->referent();
     if (IsCrossCompartmentWrapper(referent)) {
         referent = CheckedUnwrap(referent);
 
@@ -9834,16 +9889,38 @@ DebuggerObject::requireGlobal(JSContext*
                                   "a global object", nullptr);
         }
         return false;
     }
 
     return true;
 }
 
+/* static */ bool
+DebuggerObject::scriptedProxyTarget(JSContext* cx, Handle<DebuggerObject*> object,
+                                    MutableHandle<DebuggerObject*> result)
+{
+    MOZ_ASSERT(object->isScriptedProxy());
+    RootedObject referent(cx, object->referent());
+    Debugger* dbg = object->owner();
+    RootedObject unwrapped(cx, js::GetProxyTargetObject(referent));
+    return dbg->wrapDebuggeeObject(cx, unwrapped, result);
+}
+
+/* static */ bool
+DebuggerObject::scriptedProxyHandler(JSContext* cx, Handle<DebuggerObject*> object,
+                                     MutableHandle<DebuggerObject*> result)
+{
+    MOZ_ASSERT(object->isScriptedProxy());
+    RootedObject referent(cx, object->referent());
+    Debugger* dbg = object->owner();
+    RootedObject unwrapped(cx, ScriptedProxyHandler::handlerObject(referent));
+    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
@@ -1233,16 +1233,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 scriptedProxyTarget(JSContext* cx, Handle<DebuggerObject*> object,
+                                                 MutableHandle<DebuggerObject*> result);
+    static MOZ_MUST_USE bool scriptedProxyHandler(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);
@@ -1283,16 +1287,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 isScriptedProxy() const;
     JSAtom* name() const;
     JSAtom* displayName() const;
 
   private:
     enum {
         OWNER_SLOT
     };
 
@@ -1331,16 +1336,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);