Bug 1252958 - Implement Debugger.adoptDebuggeeValue;r=jimb
authorEddy Bruel <ejpbruel@mozilla.com>
Fri, 01 Apr 2016 10:50:30 +0200
changeset 291211 6001a9e0ec92fa83ab76f2628d1d0638b6db523d
parent 291210 70e88ad137197c2b5cc73916c15b5385684b3a2e
child 291212 19bd9167df871bdcd04000e6983da746fa667365
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1252958
milestone48.0a1
Bug 1252958 - Implement Debugger.adoptDebuggeeValue;r=jimb
js/src/doc/Debugger/Debugger.md
js/src/jit-test/tests/debug/Debugger-adoptDebuggeeValue.js
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
--- a/js/src/doc/Debugger/Debugger.md
+++ b/js/src/doc/Debugger/Debugger.md
@@ -494,16 +494,27 @@ other kinds of objects.
     available to privileged code.
 
 <code>makeGlobalObjectReference(<i>global</i>)</code>
 :   Return the [`Debugger.Object`][object] whose referent is the global object
     designated by <i>global</i>, without adding the designated global as a
     debuggee. If <i>global</i> does not designate a global object, throw a
     `TypeError`. Determine which global is designated by <i>global</i>
     using the same rules as [`Debugger.prototype.addDebuggee`][add].
+
+<code>adoptDebuggeeValue(<i>value</i>)</code>
+:    Given a debuggee value `value` owned by an arbitrary `Debugger`, return an
+     equivalent debuggee value owned by this `Debugger`.
+
+     If `value` is a primitive value, return it unchanged. If `value` is a
+     `Debugger.Object` owned by an arbitrary `Debugger`, return an equivalent
+     `Debugger.Object` owned by this `Debugger`. Otherwise, if `value` is some
+     other kind of object, and hence not a proper debuggee value, throw a
+     TypeError instead.
+
 ## Static methods of the Debugger Object
 
 The functions described below are not called with a `this` value.
 
 <code id="isCompilableUnit">isCompilableUnit(<i>source</i>)</code>
 :   Given a string of source code, designated by <i>source</i>, return false if
     the string might become a valid JavaScript statement with the addition of
     more lines. Otherwise return true. The intent is to support interactive
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-adoptDebuggeeValue.js
@@ -0,0 +1,39 @@
+// simplest possible test of Debugger.adoptDebuggeeValue
+
+load(libdir + "asserts.js");
+
+var g = newGlobal();
+
+var dbg1 = new Debugger();
+var gDO1 = dbg1.addDebuggee(g);
+var obj1 = gDO1.executeInGlobal("({})").return;
+
+var dbg2 = Debugger(g);
+var gDO2 = dbg2.addDebuggee(g);
+var obj2 = gDO2.executeInGlobal("({})").return;
+
+assertThrowsInstanceOf(function () {
+  obj1.defineProperty("foo", {
+    configurable: true,
+    enumerable: true,
+    value: obj2,
+    writable: true
+  });
+}, Error);
+
+let obj3 = dbg1.adoptDebuggeeValue(obj2);
+
+obj1.defineProperty("foo", {
+  configurable: true,
+  enumerable: true,
+  value: obj3,
+  writable: true
+});
+
+assertThrowsInstanceOf(function () {
+  dbg1.adoptDebuggeeValue({});
+}, TypeError);
+
+assertThrowsInstanceOf(function () {
+  dbg1.adoptDebuggeeValue(Debugger.Object.prototype);
+}, TypeError);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -914,32 +914,49 @@ Debugger::wrapDebuggeeValue(JSContext* c
     } else if (!cx->compartment()->wrap(cx, vp)) {
         vp.setUndefined();
         return false;
     }
 
     return true;
 }
 
-bool
-Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
+static NativeObject*
+ToNativeDebuggerObject(JSContext* cx, MutableHandleObject obj)
 {
     if (obj->getClass() != &DebuggerObject_class) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
-                             "Debugger", "Debugger.Object", obj->getClass()->name);
-        return false;
-    }
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_NOT_EXPECTED_TYPE, "Debugger",
+                             "Debugger.Object", obj->getClass()->name);
+        return nullptr;
+    }
+
     NativeObject* ndobj = &obj->as<NativeObject>();
 
     Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
-    if (owner.isUndefined() || &owner.toObject() != object) {
+    if (owner.isUndefined()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                             owner.isUndefined()
-                             ? JSMSG_DEBUG_OBJECT_PROTO
-                             : JSMSG_DEBUG_OBJECT_WRONG_OWNER);
+                             JSMSG_DEBUG_OBJECT_PROTO);
+        return nullptr;
+    }
+
+    return ndobj;
+}
+
+bool
+Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj)
+{
+    NativeObject* ndobj = ToNativeDebuggerObject(cx, obj);
+    if (!ndobj)
+        return false;
+
+    Value owner = ndobj->getReservedSlot(JSSLOT_DEBUGOBJECT_OWNER);
+    if (&owner.toObject() != object) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_DEBUG_OBJECT_WRONG_OWNER);
         return false;
     }
 
     obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
     return true;
 }
 
 bool
@@ -4738,16 +4755,43 @@ Debugger::drainTraceLoggerScriptCalls(JS
         return false;
 
     args.rval().setObject(*array);
 
     return true;
 }
 #endif
 
+bool
+Debugger::adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGGER(cx, argc, vp, "adoptDebuggeeValue", args, dbg);
+    if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1))
+        return false;
+
+    RootedValue v(cx, args[0]);
+    if (v.isObject()) {
+        RootedObject obj(cx, &v.toObject());
+        NativeObject* ndobj = ToNativeDebuggerObject(cx, &obj);
+        if (!ndobj) {
+            return false;
+        }
+
+        obj.set(static_cast<JSObject*>(ndobj->getPrivate()));
+        v = ObjectValue(*obj);
+
+        if (!dbg->wrapDebuggeeValue(cx, &v)) {
+            return false;
+        }
+    }
+
+    args.rval().set(v);
+    return true;
+}
+
 const JSPropertySpec Debugger::properties[] = {
     JS_PSGS("enabled", Debugger::getEnabled, Debugger::setEnabled, 0),
     JS_PSGS("onDebuggerStatement", Debugger::getOnDebuggerStatement,
             Debugger::setOnDebuggerStatement, 0),
     JS_PSGS("onExceptionUnwind", Debugger::getOnExceptionUnwind,
             Debugger::setOnExceptionUnwind, 0),
     JS_PSGS("onNewScript", Debugger::getOnNewScript, Debugger::setOnNewScript, 0),
     JS_PSGS("onNewPromise", Debugger::getOnNewPromise, Debugger::setOnNewPromise, 0),
@@ -4781,16 +4825,17 @@ const JSFunctionSpec Debugger::methods[]
     JS_FN("drainTraceLoggerScriptCalls", Debugger::drainTraceLoggerScriptCalls, 0, 0),
     JS_FN("startTraceLogger", Debugger::startTraceLogger, 0, 0),
     JS_FN("endTraceLogger", Debugger::endTraceLogger, 0, 0),
 # ifdef NIGHTLY_BUILD
     JS_FN("setupTraceLogger", Debugger::setupTraceLogger, 1, 0),
     JS_FN("drainTraceLogger", Debugger::drainTraceLogger, 0, 0),
 # endif
 #endif
+    JS_FN("adoptDebuggeeValue", Debugger::adoptDebuggeeValue, 1, 0),
     JS_FS_END
 };
 
 const JSFunctionSpec Debugger::static_methods[] {
     JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0),
     JS_FS_END
 };
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -604,16 +604,17 @@ class Debugger : private mozilla::Linked
     static bool drainTraceLoggerScriptCalls(JSContext* cx, unsigned argc, Value* vp);
     static bool startTraceLogger(JSContext* cx, unsigned argc, Value* vp);
     static bool endTraceLogger(JSContext* cx, unsigned argc, Value* vp);
     static bool isCompilableUnit(JSContext* cx, unsigned argc, Value* vp);
 #ifdef NIGHTLY_BUILD
     static bool setupTraceLogger(JSContext* cx, unsigned argc, Value* vp);
     static bool drainTraceLogger(JSContext* cx, unsigned argc, Value* vp);
 #endif
+    static bool adoptDebuggeeValue(JSContext* cx, unsigned argc, Value* vp);
     static bool construct(JSContext* cx, unsigned argc, Value* vp);
     static const JSPropertySpec properties[];
     static const JSFunctionSpec methods[];
     static const JSFunctionSpec static_methods[];
 
     static void removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx, AbstractFramePtr frame);
     static bool updateExecutionObservabilityOfFrames(JSContext* cx, const ExecutionObservableSet& obs,
                                                      IsObserving observing);