Bug 799272: Implement Debugger.Object.prototype.unwrap. r=luke
authorJim Blandy <jimb@mozilla.com>
Sat, 13 Oct 2012 16:04:16 -0700
changeset 110333 147285c880287c930ba2d1afb197d0da7837b4d7
parent 110332 708db1ae8d66f218b3dea706729846d69737daef
child 110334 f67e5b827cdf0817f8721cbdc05c14210f6d2e21
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersluke
bugs799272
milestone19.0a1
Bug 799272: Implement Debugger.Object.prototype.unwrap. r=luke
js/src/jit-test/tests/debug/Object-unwrap-01.js
js/src/jit-test/tests/debug/Object-unwrap-02.js
js/src/vm/Debugger.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-unwrap-01.js
@@ -0,0 +1,23 @@
+// Check Debugger.Object.prototype.unwrap surfaces.
+
+load(libdir + 'asserts.js');
+
+var dbg = new Debugger();
+var g = newGlobal();
+var gw = dbg.addDebuggee(g);
+
+assertEq(Object.getOwnPropertyDescriptor(gw, 'unwrap'), undefined);
+var d = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(gw), 'unwrap');
+assertEq(d.enumerable, false);
+assertEq(d.configurable, true);
+assertEq(d.writable, true);
+
+assertEq(typeof gw.unwrap, "function");
+assertEq(gw.unwrap.length, 0);
+assertEq(gw.unwrap.name, "unwrap");
+
+// It can be called.
+gw.unwrap();
+
+// You shouldn't be able to apply the accessor to the prototype.
+assertThrowsInstanceOf(function () { Debugger.Object.prototype.unwrap(); }, TypeError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Object-unwrap-02.js
@@ -0,0 +1,23 @@
+// Debugger.Object.prototype.unwrap unwraps Debugger.Objects referring to
+// cross-compartment wrappers.
+
+var dbg = new Debugger();
+
+var g1 = newGlobal();
+var dg1 = dbg.addDebuggee(g1);
+assertEq(dg1.unwrap(), dg1);
+
+var g2 = newGlobal();
+var dg2 = dbg.addDebuggee(g2);
+
+var dg1g2 = dg1.makeDebuggeeValue(g2);
+assertEq(dg1g2.global, dg1);
+assertEq(dg1g2.unwrap(), dg2);
+
+// Try an ordinary object, not a global.
+var g2o = g2.Object();
+var dg2o = dg2.makeDebuggeeValue(g2o);
+var dg1g2o = dg1.makeDebuggeeValue(g2o);
+assertEq(dg1g2o.global, dg1);
+assertEq(dg1g2o.unwrap(), dg2o);
+assertEq(dg1g2o.unwrap().unwrap(), dg2o);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4273,16 +4273,42 @@ DebuggerObject_evalInGlobalWithBindings(
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "evalInGlobalWithBindings", args, dbg, referent);
     if (!RequireGlobalObject(cx, args.thisv(), referent))
         return false;
 
     return DebuggerGenericEval(cx, "Debugger.Object.prototype.evalInGlobalWithBindings",
                                args[0], &args[1], vp, dbg, referent, NULL);
 }
 
+static JSBool
+DebuggerObject_unwrap(JSContext *cx, unsigned argc, Value *vp)
+{
+    THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "unwrap", args, dbg, referent);
+    JSObject *unwrapped = UnwrapOneChecked(cx, referent);
+    if (!unwrapped) {
+        // If we were terminated, then pass that along.
+        if (!cx->isExceptionPending())
+            return false;
+
+        // If the unwrap operation threw an exception, assume it's a
+        // security exception, and return null. It seems like the wrappers
+        // in use in Firefox just call JS_ReportError, so we have no way to
+        // distinguish genuine should-not-unwrap errors from other kinds of
+        // errors.
+        cx->clearPendingException();
+        vp->setNull();
+        return true;
+    }
+
+    *vp = ObjectValue(*unwrapped);
+    if (!dbg->wrapDebuggeeValue(cx, vp))
+        return false;
+    return true;
+}
+
 static JSPropertySpec DebuggerObject_properties[] = {
     JS_PSG("proto", DebuggerObject_getProto, 0),
     JS_PSG("class", DebuggerObject_getClass, 0),
     JS_PSG("callable", DebuggerObject_getCallable, 0),
     JS_PSG("name", DebuggerObject_getName, 0),
     JS_PSG("displayName", DebuggerObject_getDisplayName, 0),
     JS_PSG("parameterNames", DebuggerObject_getParameterNames, 0),
     JS_PSG("script", DebuggerObject_getScript, 0),
@@ -4303,16 +4329,17 @@ static JSFunctionSpec DebuggerObject_met
     JS_FN("isSealed", DebuggerObject_isSealed, 0, 0),
     JS_FN("isFrozen", DebuggerObject_isFrozen, 0, 0),
     JS_FN("isExtensible", DebuggerObject_isExtensible, 0, 0),
     JS_FN("apply", DebuggerObject_apply, 0, 0),
     JS_FN("call", DebuggerObject_call, 0, 0),
     JS_FN("makeDebuggeeValue", DebuggerObject_makeDebuggeeValue, 1, 0),
     JS_FN("evalInGlobal", DebuggerObject_evalInGlobal, 1, 0),
     JS_FN("evalInGlobalWithBindings", DebuggerObject_evalInGlobalWithBindings, 2, 0),
+    JS_FN("unwrap", DebuggerObject_unwrap, 0, 0),
     JS_FS_END
 };
 
 
 /*** Debugger.Environment ************************************************************************/
 
 static void
 DebuggerEnv_trace(JSTracer *trc, RawObject obj)