New rule: a Debug object cannot be attached to a compartment that is not in debug mode. Includes a jsapi-test to check that we do not crash if you turn debug mode off while a Debug object is already attached. (This changeset moves all the Debug object tests under jit-tests because the jit-test runner lets tests ask for debug mode.)
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 27 Apr 2011 17:37:14 -0500
changeset 74396 20a64f3083771eebde13e2cb1ffcf9d4b6e2beec
parent 74395 3c24ff3d8904e534643a4cd139ddbf099343dd57
child 74397 d806bd4f6a1ed852e8a151a297eff997cebc5f1d
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone6.0a1
New rule: a Debug object cannot be attached to a compartment that is not in debug mode. Includes a jsapi-test to check that we do not crash if you turn debug mode off while a Debug object is already attached. (This changeset moves all the Debug object tests under jit-tests because the jit-test runner lets tests ask for debug mode.)
js/src/jit-test/README
js/src/jit-test/lib/asserts.js
js/src/jit-test/tests/debug/debug-object-01.js
js/src/jit-test/tests/debug/debug-object-02.js
js/src/jit-test/tests/debug/debug-object-03.js
js/src/jit-test/tests/debug/debug-object-04.js
js/src/jit-test/tests/debug/debug-object-05.js
js/src/jit-test/tests/debug/debug-object-06.js
js/src/jit-test/tests/debug/debug-object-07.js
js/src/jit-test/tests/debug/debug-object-08.js
js/src/jit-test/tests/debug/debug-object-09.js
js/src/jit-test/tests/debug/debug-object-10.js
js/src/jit-test/tests/debug/debug-object-11.js
js/src/jit-test/tests/debug/debug-object-12.js
js/src/jit-test/tests/debug/debug-object-13.js
js/src/jit-test/tests/debug/debug-object-14.js
js/src/jit-test/tests/debug/debug-object-15.js
js/src/jit-test/tests/debug/debug-object-16.js
js/src/jit-test/tests/debug/debug-object-17.js
js/src/jit-test/tests/debug/debug-object-18.js
js/src/jit-test/tests/debug/debug-object-19.js
js/src/jit-test/tests/debug/debug-object-20.js
js/src/jit-test/tests/debug/debug-object-21.js
js/src/jsapi-tests/testDebugger.cpp
js/src/jscompartment.h
js/src/jsdbg.cpp
js/src/jsdbg.h
js/src/jsdbgapi.cpp
js/src/tests/js1_8_5/extensions/debug-object-01.js
js/src/tests/js1_8_5/extensions/debug-object-02.js
js/src/tests/js1_8_5/extensions/debug-object-03.js
js/src/tests/js1_8_5/extensions/debug-object-04.js
js/src/tests/js1_8_5/extensions/debug-object-05.js
js/src/tests/js1_8_5/extensions/debug-object-06.js
js/src/tests/js1_8_5/extensions/debug-object-07.js
js/src/tests/js1_8_5/extensions/debug-object-08.js
js/src/tests/js1_8_5/extensions/debug-object-09.js
js/src/tests/js1_8_5/extensions/debug-object-10.js
js/src/tests/js1_8_5/extensions/debug-object-11.js
js/src/tests/js1_8_5/extensions/debug-object-12.js
js/src/tests/js1_8_5/extensions/debug-object-13.js
js/src/tests/js1_8_5/extensions/debug-object-14.js
js/src/tests/js1_8_5/extensions/debug-object-15.js
js/src/tests/js1_8_5/extensions/debug-object-16.js
js/src/tests/js1_8_5/extensions/debug-object-17.js
js/src/tests/js1_8_5/extensions/debug-object-18.js
js/src/tests/js1_8_5/extensions/debug-object-19.js
js/src/tests/js1_8_5/extensions/debug-object-20.js
js/src/tests/js1_8_5/extensions/debug-object-21.js
js/src/tests/js1_8_5/extensions/jstests.list
js/src/tests/js1_8_5/extensions/shell.js
--- a/js/src/jit-test/README
+++ b/js/src/jit-test/README
@@ -47,28 +47,30 @@ test is run. For example:
     // |jit-test| allow-oom;
 
 The general format in EBNF is:
 
     metaline  ::= cookie { item ";" }
     cookie    ::= "|jit-test|"
     item      ::= flag | attribute
 
-    flag      ::= "slow" | "allow-oom"
+    flag      ::= "slow" | "allow-oom" | "valgrind" | "mjitalways" | "debug"
 
     attribute ::= name ":" value
     name      ::= "TMFLAGS" | "error"
     value     ::= <string>
 
 The metaline may appear anywhere in the first line of the file: this allows it
 to be placed inside any kind of comment.
 
 The meaning of the items:
 
     slow         Test runs slowly. Do not run if the --no-slow option is given.
     allow-oom    If the test runs out of memory, it counts as passing.
     valgrind     Run test under valgrind.
+    mjitalways   Run js with -a, whether --jitflags says to or not
+    debug        Run js with -d, whether --jitflags says to or not
 
     error        The test should be considered to pass iff it throws the
                  given JS exception.
     TMFLAGS      Set the environment variable TMFLAGS to the given value.
 
 * END
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/asserts.js
@@ -0,0 +1,36 @@
+
+if (typeof assertThrowsInstanceOf === 'undefined') {
+    var assertThrowsInstanceOf = function assertThrowsInstanceOf(f, ctor, msg) {
+        var fullmsg;
+        try {
+            f();
+        } catch (exc) {
+            if (exc instanceof ctor)
+                return;
+            fullmsg = "Assertion failed: expected exception " + ctor.name + ", got " + exc;
+        }
+        if (fullmsg === undefined)
+            fullmsg = "Assertion failed: expected exception " + ctor.name + ", no exception thrown";
+        if (msg !== undefined)
+            fullmsg += " - " + msg;
+        throw new Error(fullmsg);
+    };
+}
+
+if (typeof assertThrowsValue === 'undefined') {
+    var assertThrowsValue = function assertThrowsValue(f, val, msg) {
+        var fullmsg;
+        try {
+            f();
+        } catch (exc) {
+            if ((exc === val) === (val === val) && (val !== 0 || 1 / exc === 1 / val))
+                return;
+            fullmsg = "Assertion failed: expected exception " + val + ", got " + exc;
+        }
+        if (fullmsg === undefined)
+            fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown";
+        if (msg !== undefined)
+            fullmsg += " - " + msg;
+        throw new Error(fullmsg);
+    };
+}
rename from js/src/tests/js1_8_5/extensions/debug-object-01.js
rename to js/src/jit-test/tests/debug/debug-object-01.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-01.js
+++ b/js/src/jit-test/tests/debug/debug-object-01.js
@@ -1,10 +1,17 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+
+function checkFunction(obj, name, nargs) {
+    var desc = Object.getOwnPropertyDescriptor(obj, name);
+    assertEq(desc.configurable, true, name + " should be configurable");
+    assertEq(desc.writable, true, name + " should be writable");
+    assertEq(desc.enumerable, false, name + " should be non-enumerable");
+    assertEq(desc.value, obj[name]);  // well obviously
+    assertEq(typeof desc.value, 'function', name + " should be a function");
+    assertEq(desc.value.length, nargs, name + " should have .length === " + nargs);
+}
 
 checkFunction(this, "Debug", 1);
 
 assertEq(Debug.prototype.constructor, Debug);
 assertEq(Object.prototype.toString.call(Debug.prototype), "[object Debug]");
 assertEq(Object.getPrototypeOf(Debug.prototype), Object.prototype);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-02.js
rename to js/src/jit-test/tests/debug/debug-object-02.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-02.js
+++ b/js/src/jit-test/tests/debug/debug-object-02.js
@@ -1,37 +1,36 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+
+load(libdir + 'asserts.js');
 
 // Debug rejects arguments that aren't cross-compartment wrappers.
-assertThrows(function () { Debug(); }, TypeError);
-assertThrows(function () { Debug(null); }, TypeError);
-assertThrows(function () { Debug(true); }, TypeError);
-assertThrows(function () { Debug(42); }, TypeError);
-assertThrows(function () { Debug("bad"); }, TypeError);
-assertThrows(function () { Debug(function () {}); }, TypeError);
-assertThrows(function () { Debug(this); }, TypeError);
-assertThrows(function () { new Debug(); }, TypeError);
-assertThrows(function () { new Debug(null); }, TypeError);
-assertThrows(function () { new Debug(true); }, TypeError);
-assertThrows(function () { new Debug(42); }, TypeError);
-assertThrows(function () { new Debug("bad"); }, TypeError);
-assertThrows(function () { new Debug(function () {}); }, TypeError);
-assertThrows(function () { new Debug(this); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(null); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(true); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(42); }, TypeError);
+assertThrowsInstanceOf(function () { Debug("bad"); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(function () {}); }, TypeError);
+assertThrowsInstanceOf(function () { Debug(this); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(null); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(true); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(42); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug("bad"); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(function () {}); }, TypeError);
+assertThrowsInstanceOf(function () { new Debug(this); }, TypeError);
 
 // Very basic tests of Debug creation.
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 assertEq(dbg instanceof Debug, true);
 assertEq(Object.getPrototypeOf(dbg), Debug.prototype);
 
 // The reverse.
 var g2 = newGlobal('new-compartment');
 g2.debuggeeGlobal = this;
 g2.eval("var dbg = new Debug(debuggeeGlobal);");
 assertEq(g2.eval("dbg instanceof Debug"), true);
 
 // The Debug constructor from this compartment will not accept as its argument
 // an Object from this compartment. Shenanigans won't fool the membrane.
 g2.outer = this;
-assertThrows(function () { g2.eval("outer.Debug(outer.Object())"); }, TypeError);
-
-reportCompare(0, 0, 'ok');
+assertThrowsInstanceOf(function () { g2.eval("outer.Debug(outer.Object())"); }, TypeError);
rename from js/src/tests/js1_8_5/extensions/debug-object-03.js
rename to js/src/jit-test/tests/debug/debug-object-03.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-03.js
+++ b/js/src/jit-test/tests/debug/debug-object-03.js
@@ -1,18 +1,14 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 var g = newGlobal('new-compartment');
 g.log = '';
 
 var dbg = Debug(g);
 var hooks = {
     debuggerHandler: function (stack) {
         g.log += '!';
     }
 };
 dbg.hooks = hooks;
 assertEq(dbg.hooks, hooks);
 assertEq(g.eval("log += '1'; debugger; log += '2'; 3;"), 3);
 assertEq(g.log, '1!2');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-04.js
rename to js/src/jit-test/tests/debug/debug-object-04.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-04.js
+++ b/js/src/jit-test/tests/debug/debug-object-04.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Activity in the debugger compartment should not trigger debug hooks.
 
 var g = newGlobal('new-compartment');
 var hit = false;
 
 var dbg = Debug(g);
 dbg.hooks = {
     debuggerHandler: function (stack) {
@@ -22,10 +20,8 @@ assertEq(hit, false, "debugger statement
 
 g.outerEval = eval;
 g.eval("outerEval('debugger;');");
 assertEq(hit, false, "debugger statement in debugger compartment eval code should not hit");
 
 var g2 = newGlobal('new-compartment');
 g2.eval("debugger;");
 assertEq(hit, false, "debugger statement in unrelated non-debuggee compartment should not hit");
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-05.js
rename to js/src/jit-test/tests/debug/debug-object-05.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-05.js
+++ b/js/src/jit-test/tests/debug/debug-object-05.js
@@ -1,20 +1,16 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // A debugger statement in a debuggerHandler should not reenter.
 
 var g = newGlobal('new-compartment');
 var calls = 0;
 
 var dbg = Debug(g);
 dbg.hooks = {
     debuggerHandler: function (stack) {
         calls++;
         debugger;
     }
 };
 
 assertEq(g.eval("debugger; 7;"), 7);
 assertEq(calls, 1);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-06.js
rename to js/src/jit-test/tests/debug/debug-object-06.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-06.js
+++ b/js/src/jit-test/tests/debug/debug-object-06.js
@@ -1,32 +1,18 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+// Simple {throw:} resumption.
 
-// Simple {throw:} resumption.
+load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 
 var dbg = Debug(g);
 dbg.hooks = {
     debuggerHandler: function (stack) {
         return {throw: "oops"};
     }
 };
 
-var result = 'no exception thrown';
-try {
-    g.eval("debugger;");
-} catch (exc) {
-    result = exc;
-}
-assertEq(result, "oops");
+assertThrowsValue(function () { g.eval("debugger;"); }, "oops");
 
 g.eval("function f() { debugger; }");
-result = 'no exception thrown';
-try {
-    g.f();
-} catch (exc) {
-    result = exc;
-}
-assertEq(result, "oops");
-
-reportCompare(0, 0, 'ok');
+assertThrowsValue(function () { g.f(); }, "oops");
rename from js/src/tests/js1_8_5/extensions/debug-object-07.js
rename to js/src/jit-test/tests/debug/debug-object-07.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-07.js
+++ b/js/src/jit-test/tests/debug/debug-object-07.js
@@ -1,19 +1,15 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Simple {return:} resumption.
 
 var g = newGlobal('new-compartment');
 
 var dbg = Debug(g);
 dbg.hooks = {
     debuggerHandler: function (stack) {
         return {return: 1234};
     }
 };
 
 assertEq(g.eval("debugger; false;"), 1234);
 g.eval("function f() { debugger; return 'bad'; }");
 assertEq(g.f(), 1234);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-08.js
rename to js/src/jit-test/tests/debug/debug-object-08.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-08.js
+++ b/js/src/jit-test/tests/debug/debug-object-08.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // When there are multiple debuggers, their hooks are called in order.
 
 var g = newGlobal('new-compartment');
 var log;
 var arr = [];
 
 function addDebug(msg) {
     var dbg = new Debug(g);
@@ -33,10 +31,8 @@ arr[0].hooks = {
         log += 'a';
         return {return: 1};
     }
 };
 
 log = '';
 assertEq(g.eval("debugger; 0;"), 1);
 assertEq(log, 'a');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-09.js
rename to js/src/jit-test/tests/debug/debug-object-09.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-09.js
+++ b/js/src/jit-test/tests/debug/debug-object-09.js
@@ -1,32 +1,30 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+// Debug.prototype.hooks
 
-// Debug.prototype.hooks
+load(libdir + 'asserts.js');
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 gc();  // don't assert marking dbg.hooks
 var h = dbg.hooks;
 assertEq(typeof h, 'object');
 assertEq(Object.getOwnPropertyNames(h).length, 0);
 assertEq(Object.getPrototypeOf(h), Object.prototype);
 
-assertThrows(function () { dbg.hooks = null; }, TypeError);
-assertThrows(function () { dbg.hooks = "bad"; }, TypeError);
+assertThrowsInstanceOf(function () { dbg.hooks = null; }, TypeError);
+assertThrowsInstanceOf(function () { dbg.hooks = "bad"; }, TypeError);
 
 assertEq(Object.getOwnPropertyNames(dbg).length, 0);
 var desc = Object.getOwnPropertyDescriptor(Debug.prototype, "hooks");
 assertEq(desc.configurable, true);
 assertEq(desc.enumerable, false);
 
-assertThrows(function () { desc.get(); }, TypeError);
-assertThrows(function () { desc.get.call(undefined); }, TypeError);
-assertThrows(function () { desc.get.call(Debug.prototype); }, TypeError);
+assertThrowsInstanceOf(function () { desc.get(); }, TypeError);
+assertThrowsInstanceOf(function () { desc.get.call(undefined); }, TypeError);
+assertThrowsInstanceOf(function () { desc.get.call(Debug.prototype); }, TypeError);
 assertEq(desc.get.call(dbg), h);
 
-assertThrows(function () { desc.set(); }, TypeError);
-assertThrows(function () { desc.set.call(dbg); }, TypeError);
-assertThrows(function () { desc.set.call({}, {}); }, TypeError);
-assertThrows(function () { desc.set.call(Debug.prototype, {}); }, TypeError);
-
-reportCompare(0, 0, 'ok');
+assertThrowsInstanceOf(function () { desc.set(); }, TypeError);
+assertThrowsInstanceOf(function () { desc.set.call(dbg); }, TypeError);
+assertThrowsInstanceOf(function () { desc.set.call({}, {}); }, TypeError);
+assertThrowsInstanceOf(function () { desc.set.call(Debug.prototype, {}); }, TypeError);
rename from js/src/tests/js1_8_5/extensions/debug-object-10.js
rename to js/src/jit-test/tests/debug/debug-object-10.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-10.js
+++ b/js/src/jit-test/tests/debug/debug-object-10.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // If a hook is deleted after setHooks or overwritten with a primitive, it
 // simply isn't called.
 
 var g = newGlobal('new-compartment');
 var hit = false;
 var dbg = new Debug(g);
 dbg.hooks = {debuggerHandler: function (stack) { hit = 'fail';}};
 
@@ -16,10 +14,8 @@ assertEq(hit, false);
 dbg.hooks.debuggerHandler = null;
 g.eval("debugger;");
 assertEq(hit, false);
 
 // Reinstating the hook should work.
 dbg.hooks.debuggerHandler = function (stack) { hit = true; };
 g.eval("debugger;");
 assertEq(hit, true);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-11.js
rename to js/src/jit-test/tests/debug/debug-object-11.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-11.js
+++ b/js/src/jit-test/tests/debug/debug-object-11.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Debuggers with enabled hooks should not be GC'd even if they are otherwise
 // unreachable.
 
 var g = newGlobal('new-compartment');
 var actual = 0;
 var expected = 0;
 
 function f() {
@@ -15,10 +13,8 @@ function f() {
         expected += i;
     }
 }
 
 f();
 gc(); gc(); gc();
 g.eval("debugger;");
 assertEq(actual, expected);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-12.js
rename to js/src/jit-test/tests/debug/debug-object-12.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-12.js
+++ b/js/src/jit-test/tests/debug/debug-object-12.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Test adding hooks during dispatch. The behavior is deterministic and "nice",
 // but mainly what we are checking here is that we do not crash due to
 // modifying a data structure while we're iterating over it.
 
 var g = newGlobal('new-compartment');
 var n = 0;
 var hits;
 
@@ -30,10 +28,8 @@ assertEq(hits, 2);
 
 hits = 0;
 g.eval("debugger;");  // after this there are eight
 assertEq(hits, 4);
 
 hits = 0;
 g.eval("debugger;");
 assertEq(hits, 8);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-13.js
rename to js/src/jit-test/tests/debug/debug-object-13.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-13.js
+++ b/js/src/jit-test/tests/debug/debug-object-13.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Test removing hooks during dispatch.
 
 var g = newGlobal('new-compartment');
 var log = '';
 
 function addDebug(n) {
     for (var i = 0; i < n; i++) {
         var dbg = new Debug(g);
@@ -22,10 +20,8 @@ function addDebug(n) {
         };
     }
     dbg = null;
 }
 
 addDebug(10);
 g.eval("debugger;");
 assertEq(log, '0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-14.js
rename to js/src/jit-test/tests/debug/debug-object-14.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-14.js
+++ b/js/src/jit-test/tests/debug/debug-object-14.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Dispatching an event to a debugger must keep enough of it gc-alive to avoid
 // crashing.
 
 var g = newGlobal('new-compartment');
 var hits;
 
 function addDebug() {
     // These loops are here mainly to defeat the conservative GC. :-\
@@ -30,10 +28,8 @@ function addDebug() {
         }
     }
 }
 
 addDebug();
 hits = 0;
 g.eval("debugger;");
 assertEq(hits, 1);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-15.js
rename to js/src/jit-test/tests/debug/debug-object-15.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-15.js
+++ b/js/src/jit-test/tests/debug/debug-object-15.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Q: But who shall debug the debuggers?  A: jimb
 
 var log = '';
 
 function addDebug(g, id) {
     var debuggerGlobal = newGlobal('new-compartment');
     debuggerGlobal.debuggee = g;
     debuggerGlobal.id = id;
@@ -19,10 +17,8 @@ function addDebug(g, id) {
 }
 
 var base = newGlobal('new-compartment');
 var top = base;
 for (var i = 0; i < 8; i++)  // why have 2 debuggers when you can have 8
     top = addDebug(top, i);
 base.eval("debugger;");
 assertEq(log, '0123456776543210');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-16.js
rename to js/src/jit-test/tests/debug/debug-object-16.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-16.js
+++ b/js/src/jit-test/tests/debug/debug-object-16.js
@@ -1,10 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
 
 var desc = Object.getOwnPropertyDescriptor(Debug.prototype, "enabled");
 assertEq(typeof desc.get, 'function');
 assertEq(typeof desc.set, 'function');
 
 var g = newGlobal('new-compartment');
 var hits;
 var dbg = new Debug(g);
@@ -14,10 +13,8 @@ dbg.hooks = {debuggerHandler: function (
 var vals = [true, false, null, undefined, NaN, "blah", {}];
 for (var i = 0; i < vals.length; i++) {
     dbg.enabled = vals[i];
     assertEq(dbg.enabled, !!vals[i]);
     hits = 0;
     g.eval("debugger;");
     assertEq(hits, vals[i] ? 1 : 0);
 }
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-17.js
rename to js/src/jit-test/tests/debug/debug-object-17.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-17.js
+++ b/js/src/jit-test/tests/debug/debug-object-17.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Disabling a Debug object causes events to stop being delivered to it
 // immediately, even if we're in the middle of dispatching.
 
 var g = newGlobal('new-compartment');
 var log;
 
 var arr = [];
 for (var i = 0; i < 4; i++) {
@@ -19,10 +17,8 @@ for (var i = 0; i < 4; i++) {
                 arr[j].enabled = false;
         }
     };
 }
 
 log = '';
 g.eval("debugger; debugger;");
 assertEq(log, '0');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-18.js
rename to js/src/jit-test/tests/debug/debug-object-18.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-18.js
+++ b/js/src/jit-test/tests/debug/debug-object-18.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // Uncaught exceptions in the debugger itself are delivered to the
 // uncaughtExceptionHook.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var log;
 dbg.hooks = {
     debuggerHandler: function () {
@@ -17,10 +15,8 @@ dbg.uncaughtExceptionHook = function (ex
     assertEq(this, dbg);
     assertEq(exc instanceof TypeError, true);
     log += '!';
 };
 
 log = '';
 g.eval("debugger");
 assertEq(log, 'x!');
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-19.js
rename to js/src/jit-test/tests/debug/debug-object-19.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-19.js
+++ b/js/src/jit-test/tests/debug/debug-object-19.js
@@ -1,12 +1,12 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+// uncaughtExceptionHook returns a resumption value.
 
-// uncaughtExceptionHook returns a resumption value.
+load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var rv;
 dbg.hooks = {debuggerHandler: function () { throw 15; }};
 dbg.uncaughtExceptionHook = function (exc) {
     assertEq(exc, 15);
     return rv;
@@ -14,21 +14,13 @@ dbg.uncaughtExceptionHook = function (ex
 
 // case 1: undefined
 rv = undefined;
 g.eval("debugger");
 
 // case 2: throw
 rv = {throw: 57};
 var result;
-try {
-    g.eval("debugger");
-    result = 'no exception thrown';
-} catch (exc) {
-    result = 'caught ' + exc;
-}
-assertEq(result, 'caught 57');
+assertThrowsValue(function () { g.eval("debugger"); }, 57);
 
 // case 3: return
 rv = {return: 42};
 assertEq(g.eval("debugger;"), 42);
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-20.js
rename to js/src/jit-test/tests/debug/debug-object-20.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-20.js
+++ b/js/src/jit-test/tests/debug/debug-object-20.js
@@ -1,11 +1,9 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
-
+// |jit-test| debug
 // uncaughtExceptionHook resumption value other than undefined causes further
 // hooks to be skipped.
 
 var g = newGlobal('new-compartment');
 var log;
 
 function makeDebug(g, name) {
     var dbg = new Debug(g);
@@ -23,10 +21,8 @@ function makeDebug(g, name) {
 
 var arr = [];
 for (var i = 0; i < 6; i++)
     arr[i] = makeDebug(g, "" + i);
 
 log = '';
 assertEq(g.eval("debugger;"), 42);
 assertEq(log, "012");
-
-reportCompare(0, 0, 'ok');
rename from js/src/tests/js1_8_5/extensions/debug-object-21.js
rename to js/src/jit-test/tests/debug/debug-object-21.js
--- a/js/src/tests/js1_8_5/extensions/debug-object-21.js
+++ b/js/src/jit-test/tests/debug/debug-object-21.js
@@ -1,22 +1,20 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/licenses/publicdomain/
+// |jit-test| debug
+// dumb basics of uncaughtExceptionHook
 
-// dumb basics of uncaughtExceptionHook
+load(libdir + 'asserts.js');
 
 var desc = Object.getOwnPropertyDescriptor(Debug.prototype, "uncaughtExceptionHook");
 assertEq(typeof desc.get, 'function');
 assertEq(typeof desc.set, 'function');
 
-assertThrows(function () { Debug.prototype.uncaughtExceptionHook = null; }, TypeError);
+assertThrowsInstanceOf(function () { Debug.prototype.uncaughtExceptionHook = null; }, TypeError);
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 assertEq(desc.get.call(dbg), null);
-assertThrows(function () { dbg.uncaughtExceptionHook = []; }, TypeError);
-assertThrows(function () { dbg.uncaughtExceptionHook = 3; }, TypeError);
+assertThrowsInstanceOf(function () { dbg.uncaughtExceptionHook = []; }, TypeError);
+assertThrowsInstanceOf(function () { dbg.uncaughtExceptionHook = 3; }, TypeError);
 dbg.uncaughtExceptionHook = Math.sin;
 assertEq(dbg.uncaughtExceptionHook, Math.sin);
 dbg.uncaughtExceptionHook = null;
 assertEq(dbg.uncaughtExceptionHook, null);
-
-reportCompare(0, 0, 'ok');
--- a/js/src/jsapi-tests/testDebugger.cpp
+++ b/js/src/jsapi-tests/testDebugger.cpp
@@ -98,8 +98,49 @@ BEGIN_TEST(testDebugger_getThisStrict)
          "strict.call(42);\n"
          "(42).strict();\n"
          "strict.call(undefined);\n"
          "strict.call(null);\n");
     CHECK(!anyWrapped);
     return true;
 }
 END_TEST(testDebugger_getThisStrict)
+
+BEGIN_TEST(testDebugger_debugObjectVsDebugMode)
+{
+    CHECK(JS_DefineDebugObject(cx, global));
+    JSObject *debuggee = JS_NewCompartmentAndGlobalObject(cx, getGlobalClass(), NULL);
+    CHECK(debuggee);
+
+    {
+        JSAutoEnterCompartment ae;
+        CHECK(ae.enter(cx, debuggee));
+        CHECK(JS_SetDebugMode(cx, true));
+        CHECK(JS_InitStandardClasses(cx, debuggee));
+    }
+
+    JSObject *debuggeeWrapper = debuggee;
+    CHECK(JS_WrapObject(cx, &debuggeeWrapper));
+    jsval v = OBJECT_TO_JSVAL(debuggeeWrapper);
+    CHECK(JS_SetProperty(cx, global, "debuggee", &v));
+
+    EVAL("var dbg = new Debug(debuggee);\n"
+         "var hits = 0;\n"
+         "dbg.hooks = {debuggerHandler: function () { hits++; }};\n"
+         "debuggee.eval('debugger;');\n"
+         "hits;\n",
+         &v);
+    CHECK_SAME(v, JSVAL_ONE);
+
+    {
+        JSAutoEnterCompartment ae;
+        CHECK(ae.enter(cx, debuggee));
+        CHECK(JS_SetDebugMode(cx, false));
+    }
+
+    EVAL("debuggee.eval('debugger; debugger; debugger;');\n"
+         "hits;\n",
+         &v);
+    CHECK_SAME(v, JSVAL_ONE);
+    
+    return true;
+}
+END_TEST(testDebugger_debugObjectVsDebugMode)
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -512,17 +512,20 @@ struct JS_FRIEND_API(JSCompartment) {
         return mathCache ? mathCache : allocMathCache(cx);
     }
 
     size_t backEdgeCount(jsbytecode *pc) const;
     size_t incBackEdgeCount(jsbytecode *pc);
 
     const DebugVector &getDebuggers() const { return debuggers; }
 
-    bool addDebug(js::Debug *dbg) { return debuggers.append(dbg); }
+    bool addDebug(js::Debug *dbg) {
+        JS_ASSERT(debugMode);
+        return debuggers.append(dbg);
+    }
     void removeDebug(js::Debug *dbg);
 };
 
 #define JS_SCRIPTS_TO_GC(cx)    ((cx)->compartment->scriptsToGC)
 #define JS_PROPERTY_TREE(cx)    ((cx)->compartment->propertyTree)
 
 #ifdef DEBUG
 #define JS_COMPARTMENT_METER(x) x
--- a/js/src/jsdbg.cpp
+++ b/js/src/jsdbg.cpp
@@ -301,20 +301,26 @@ Debug::trace(JSTracer *trc, JSObject *ob
             MarkObject(trc, *dbg->uncaughtExceptionHook, "hooks");
     }
 }
 
 void
 Debug::finalize(JSContext *cx, JSObject *obj)
 {
     Debug *dbg = (Debug *) obj->getPrivate();
-    if (dbg && dbg->debuggeeCompartment) {
-        dbg->debuggeeCompartment->removeDebug(dbg);
-        dbg->debuggeeCompartment = NULL;
-    }
+    if (dbg && dbg->debuggeeCompartment)
+        dbg->detachFrom(dbg->debuggeeCompartment);
+}
+
+void
+Debug::detachFrom(JSCompartment *c)
+{
+    JS_ASSERT(c == debuggeeCompartment);
+    c->removeDebug(this);
+    debuggeeCompartment = NULL;
 }
 
 Class Debug::jsclass = {
     "Debug", JSCLASS_HAS_PRIVATE,
     PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
     EnumerateStub, ResolveStub, ConvertStub, Debug::finalize,
     NULL,                 /* reserved0   */
     NULL,                 /* checkAccess */
@@ -406,32 +412,38 @@ Debug::construct(JSContext *cx, uintN ar
     JSObject *argobj = &arg.toObject();
     if (!argobj->isWrapper() ||
         (!argobj->getWrapperHandler()->flags() & JSWrapper::CROSS_COMPARTMENT))
     {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CCW_REQUIRED, "Debug");
         return false;
     }
 
+    // Check that the target compartment is in debug mode.
+    JSCompartment *debuggeeCompartment = argobj->getProxyPrivate().toObject().compartment();
+    if (!debuggeeCompartment->debugMode) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DEBUG_MODE);
+        return false;
+    }
+
     // Get Debug.prototype.
     Value v;
     jsid prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
     if (!vp[0].toObject().getProperty(cx, prototypeId, &v))
         return false;
     JSObject *proto = &v.toObject();
     JS_ASSERT(proto->getClass() == &Debug::jsclass);
 
     // Make the new Debug object.
     JSObject *obj = NewNonFunction<WithProto::Given>(cx, &Debug::jsclass, proto, NULL);
     if (!obj)
         return false;
     JSObject *hooks = NewBuiltinClassInstance(cx, &js_ObjectClass);
     if (!hooks)
         return false;
-    JSCompartment *debuggeeCompartment = argobj->getProxyPrivate().toObject().compartment();
     Debug *dbg = cx->new_<Debug>(obj, hooks, debuggeeCompartment);
     if (!dbg)
         return false;
     if (!debuggeeCompartment->addDebug(dbg)) {
         js_ReportOutOfMemory(cx);
         return false;
     }
     obj->setPrivate(dbg);
--- a/js/src/jsdbg.h
+++ b/js/src/jsdbg.h
@@ -105,16 +105,17 @@ class Debug {
     // true. If not, it returns false.
     //
     static bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
 
     inline JSObject *toJSObject() const;
     static inline Debug *fromJSObject(JSObject *obj);
 
     inline bool observesCompartment(JSCompartment *c) const;
+    void detachFrom(JSCompartment *c);
 
     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
 };
 
 bool
 Debug::hasAnyLiveHooks() const
 {
     return observesDebuggerStatement();
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -46,16 +46,17 @@
 #include "jstypes.h"
 #include "jsstdint.h"
 #include "jsutil.h"
 #include "jsclist.h"
 #include "jshashtable.h"
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsversion.h"
+#include "jsdbg.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcmark.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsobj.h"
@@ -188,19 +189,26 @@ JS_SetDebugModeForCompartment(JSContext 
     // This should only be called when no scripts are live. It would even be
     // incorrect to discard just the non-live scripts' JITScripts because they
     // might share ICs with live scripts (bug 632343).
     JS_ASSERT(!CompartmentHasLiveScripts(comp));
 
     // All scripts compiled from this point on should be in the requested debugMode.
     comp->debugMode = !!debug;
 
+    // Detach any debuggers attached to this compartment.
+    if (debug) {
+        JS_ASSERT(comp->getDebuggers().empty());
+    } else {
+        while (!comp->getDebuggers().empty())
+            comp->getDebuggers().back()->detachFrom(comp);
+    }
+
     // Discard JIT code for any scripts that change debugMode. This function
     // assumes that 'comp' is in the same thread as 'cx'.
-
 #ifdef JS_METHODJIT
     JS::AutoEnterScriptCompartment ac;
 
     for (JSScript *script = (JSScript *)comp->scripts.next;
          &script->links != &comp->scripts;
          script = (JSScript *)script->links.next)
     {
         if (!script->debugMode == !debug)
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -37,29 +37,8 @@ script regress-627984-6.js
 script regress-627984-7.js
 script regress-630377.js
 script regress-631723.js
 skip-if(!xulRuntime.shell) script regress-636818.js
 script regress-636697.js
 script is-generator.js
 script weakmap.js
 script regress-650753.js
-skip-if(!xulRuntime.shell) script debug-object-01.js
-skip-if(!xulRuntime.shell) script debug-object-02.js
-skip-if(!xulRuntime.shell) script debug-object-03.js
-skip-if(!xulRuntime.shell) script debug-object-04.js
-skip-if(!xulRuntime.shell) script debug-object-05.js
-skip-if(!xulRuntime.shell) script debug-object-06.js
-skip-if(!xulRuntime.shell) script debug-object-07.js
-skip-if(!xulRuntime.shell) script debug-object-08.js
-skip-if(!xulRuntime.shell) script debug-object-09.js
-skip-if(!xulRuntime.shell) script debug-object-10.js
-skip-if(!xulRuntime.shell) script debug-object-11.js
-skip-if(!xulRuntime.shell) script debug-object-12.js
-skip-if(!xulRuntime.shell) script debug-object-13.js
-skip-if(!xulRuntime.shell) script debug-object-14.js
-skip-if(!xulRuntime.shell) script debug-object-15.js
-skip-if(!xulRuntime.shell) script debug-object-16.js
-skip-if(!xulRuntime.shell) script debug-object-17.js
-skip-if(!xulRuntime.shell) script debug-object-18.js
-skip-if(!xulRuntime.shell) script debug-object-19.js
-skip-if(!xulRuntime.shell) script debug-object-20.js
-skip-if(!xulRuntime.shell) script debug-object-21.js
--- a/js/src/tests/js1_8_5/extensions/shell.js
+++ b/js/src/tests/js1_8_5/extensions/shell.js
@@ -165,19 +165,8 @@ var Match =
 
         return matchObject(act, exp);
     }
 
     return { Pattern: Pattern,
              MatchError: MatchError };
 
 })();
-
-// used by several Debug object tests
-function checkFunction(obj, name, nargs) {
-    var desc = Object.getOwnPropertyDescriptor(obj, name);
-    assertEq(desc.configurable, true, name + " should be configurable");
-    assertEq(desc.writable, true, name + " should be writable");
-    assertEq(desc.enumerable, false, name + " should be non-enumerable");
-    assertEq(desc.value, obj[name]);  // well obviously
-    assertEq(typeof desc.value, 'function', name + " should be a function");
-    assertEq(desc.value.length, nargs, name + " should have .length === " + nargs);
-}