Automatically turn debug mode on/off when adding/removing debuggees.
☠☠ backed out by 903f84bae528 ☠ ☠
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 02 Jun 2011 21:58:46 -0500
changeset 74462 d02083014f57890a0e9977094ff51625e9ead9a6
parent 74461 9ac0d2ba9ac020baddf21845a5af6cc7d7c802cc
child 74463 720b3fe3c84efd6336c37451d7cc8e357a5931db
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone6.0a1
Automatically turn debug mode on/off when adding/removing debuggees. This allows most of the tests to run without the -d command-line flag. Now a compartment is in debug mode if * JSD1 wants debug mode on, thanks to a JS_SetDebugMode* call; OR * JSD2 wants debug mode on, because a live Debug object has a debuggee global in that compartment. Since this patch only adds the second half of the rule, JSD1 should be unaffected. The new rule has three issues: 1. When removeDebuggee is called, it can cause debug mode to be turned off for a compartment. If any scripts from that compartment are on the stack, and the methodjit is enabled, returning to those stack frames will crash. 2. When a Debug object is GC'd, it can cause debug mode to be turned off for one or more compartments. This causes the same problem with returning to deleted methodjit code, but the fix is different: such Debug objects simply should not be GC'd. 3. Setting .enabled to false still does not turn off debug mode anywhere, so it does not reduce overhead as much as it should. A possible fix for issue #1 would be to make such removeDebuggee calls throw. The fix to issues #2 and #3 is to tweak the rule--and to tweak the rule for Debug object GC-reachability.
js/src/jit-test/tests/debug/Debug-ctor-01.js
js/src/jit-test/tests/debug/Debug-ctor-02.js
js/src/jit-test/tests/debug/Debug-ctor-03.js
js/src/jit-test/tests/debug/Debug-ctor.js
js/src/jit-test/tests/debug/Debug-debuggees-01.js
js/src/jit-test/tests/debug/Debug-debuggees-02.js
js/src/jit-test/tests/debug/Debug-debuggees-03.js
js/src/jit-test/tests/debug/Debug-debuggees-04.js
js/src/jit-test/tests/debug/Debug-debuggees-05.js
js/src/jit-test/tests/debug/Debug-debuggees-06.js
js/src/jit-test/tests/debug/Debug-debuggees-07.js
js/src/jit-test/tests/debug/Debug-debuggees-08.js
js/src/jit-test/tests/debug/Debug-debuggees-10.js
js/src/jit-test/tests/debug/Debug-debuggees-12.js
js/src/jit-test/tests/debug/Debug-debuggees-13.js
js/src/jit-test/tests/debug/Debug-debuggees-14.js
js/src/jit-test/tests/debug/Debug-enabled-01.js
js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js
js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js
js/src/jit-test/tests/debug/Debug-multi-01.js
js/src/jit-test/tests/debug/Debug-multi-02.js
js/src/jit-test/tests/debug/Debug-multi-03.js
js/src/jit-test/tests/debug/Frame-01.js
js/src/jit-test/tests/debug/Frame-02.js
js/src/jit-test/tests/debug/Frame-03.js
js/src/jit-test/tests/debug/Frame-arguments-01.js
js/src/jit-test/tests/debug/Frame-arguments-02.js
js/src/jit-test/tests/debug/Frame-arguments-03.js
js/src/jit-test/tests/debug/Frame-arguments-04.js
js/src/jit-test/tests/debug/Frame-arguments-05.js
js/src/jit-test/tests/debug/Frame-arguments-06.js
js/src/jit-test/tests/debug/Frame-eval-01.js
js/src/jit-test/tests/debug/Frame-eval-02.js
js/src/jit-test/tests/debug/Frame-eval-03.js
js/src/jit-test/tests/debug/Frame-eval-04.js
js/src/jit-test/tests/debug/Frame-eval-05.js
js/src/jit-test/tests/debug/Frame-eval-06.js
js/src/jit-test/tests/debug/Frame-eval-07.js
js/src/jit-test/tests/debug/Frame-eval-08.js
js/src/jit-test/tests/debug/Frame-eval-09.js
js/src/jit-test/tests/debug/Frame-eval-10.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js
js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js
js/src/jit-test/tests/debug/Frame-identity-04.js
js/src/jit-test/tests/debug/Frame-live-03.js
js/src/jit-test/tests/debug/Frame-live-04.js
js/src/jit-test/tests/debug/Frame-older-01.js
js/src/jit-test/tests/debug/Frame-this-01.js
js/src/jit-test/tests/debug/Frame-this-02.js
js/src/jit-test/tests/debug/Frame-this-03.js
js/src/jit-test/tests/debug/Frame-this-04.js
js/src/jit-test/tests/debug/Object-01.js
js/src/jit-test/tests/debug/Object-apply-01.js
js/src/jit-test/tests/debug/Object-apply-02.js
js/src/jit-test/tests/debug/Object-apply-03.js
js/src/jit-test/tests/debug/Object-apply-04.js
js/src/jit-test/tests/debug/Object-callable.js
js/src/jit-test/tests/debug/Object-class.js
js/src/jit-test/tests/debug/Object-identity-01.js
js/src/jit-test/tests/debug/Object-identity-02.js
js/src/jit-test/tests/debug/Object-identity-03.js
js/src/jit-test/tests/debug/Object-name-01.js
js/src/jit-test/tests/debug/Object-name-02.js
js/src/jit-test/tests/debug/Object-parameterNames.js
js/src/jit-test/tests/debug/Object-proto.js
js/src/jit-test/tests/debug/dispatch-01.js
js/src/jit-test/tests/debug/dispatch-02.js
js/src/jit-test/tests/debug/gc-01.js
js/src/jit-test/tests/debug/gc-02.js
js/src/jit-test/tests/debug/gc-03.js
js/src/jit-test/tests/debug/gc-04.js
js/src/jit-test/tests/debug/gc-05.js
js/src/jit-test/tests/debug/gc-06.js
js/src/jit-test/tests/debug/gc-07.js
js/src/jit-test/tests/debug/gc-08.js
js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js
js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js
js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js
js/src/jit-test/tests/debug/hooks-deleted.js
js/src/jit-test/tests/debug/hooks-throw-01.js
js/src/jit-test/tests/debug/hooks-throw-04.js
js/src/jit-test/tests/debug/resumption-01.js
js/src/jit-test/tests/debug/resumption-02.js
js/src/jit-test/tests/debug/surfaces-01.js
js/src/jit-test/tests/debug/surfaces-02.js
js/src/jit-test/tests/debug/surfaces-03.js
js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js
js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js
js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js
js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js
js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js
js/src/jsanalyze.cpp
js/src/jsapi-tests/testDebugger.cpp
js/src/jsapi.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbg.cpp
js/src/jsdbg.h
js/src/jsdbgapi.cpp
js/src/jsgc.cpp
js/src/jsinterpinlines.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/FrameState.cpp
js/src/methodjit/Retcon.cpp
js/src/vm/GlobalObject.cpp
rename from js/src/jit-test/tests/debug/Debug-ctor.js
rename to js/src/jit-test/tests/debug/Debug-ctor-01.js
--- a/js/src/jit-test/tests/debug/Debug-ctor.js
+++ b/js/src/jit-test/tests/debug/Debug-ctor-01.js
@@ -1,10 +1,8 @@
-// |jit-test| debug
-
 load(libdir + 'asserts.js');
 
 // Debug rejects arguments that aren't cross-compartment wrappers.
 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);
@@ -16,19 +14,8 @@ assertThrowsInstanceOf(function () { new
 assertThrowsInstanceOf(function () { new Debug(function () {}); }, TypeError);
 assertThrowsInstanceOf(function () { new Debug(this); }, TypeError);
 
 // From the main compartment, creating a Debug on a sandbox compartment.
 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;
-assertThrowsInstanceOf(function () { g2.eval("outer.Debug(outer.Object())"); }, TypeError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debug-ctor-02.js
@@ -0,0 +1,17 @@
+// |jit-test| debug
+// Test creating a Debug in a sandbox, debugging the initial global.
+//
+// This requires debug mode to already be on in the initial global, since it's
+// always on the stack in the shell. Hence the |jit-test| tag.
+
+load(libdir + 'asserts.js');
+
+var g = newGlobal('new-compartment');
+g.debuggeeGlobal = this;
+g.eval("var dbg = new Debug(debuggeeGlobal);");
+assertEq(g.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.
+g.parent = this;
+assertThrowsInstanceOf(function () { g.eval("parent.Debug(parent.Object())"); }, TypeError);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debug-ctor-03.js
@@ -0,0 +1,6 @@
+// If the debuggee cannot be put into debug mode, throw.
+var g = newGlobal('new-compartment');
+g.libdir = libdir;
+g.eval("load(libdir + 'asserts.js');");
+g.parent = this;
+g.eval("assertThrowsInstanceOf(function () { new Debug(parent); }, Error);");
--- a/js/src/jit-test/tests/debug/Debug-debuggees-01.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-01.js
@@ -1,6 +1,5 @@
-// |jit-test| debug
 // A Debug object created with no argument initially has no debuggees.
 var dbg = new Debug;
 var debuggees = dbg.getDebuggees();
 assertEq(Array.isArray(debuggees), true);
 assertEq(debuggees.length, 0);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-02.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-02.js
@@ -1,10 +1,9 @@
-// |jit-test| debug
-// The array returned by getDebuggees is just a snapshot, not live
+// The array returned by getDebuggees is just a snapshot, not live.
 var dbg = new Debug;
 var a1 = dbg.getDebuggees();
 var g = newGlobal('new-compartment');
 var gw = dbg.addDebuggee(g);
 assertEq(gw instanceof Debug.Object, true);
 var a2 = dbg.getDebuggees();
 assertEq(a2.length, 1);
 assertEq(a2[0], gw);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-03.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Debug hooks fire based on debuggees.
 
 var g1 = newGlobal('new-compartment');
 g1.eval("var g2 = newGlobal('same-compartment')");
 var g2 = g1.g2;
 g1.eval("function f() { debugger; g2.g(); }");
 g2.eval("function g() { debugger; }");
 
--- a/js/src/jit-test/tests/debug/Debug-debuggees-04.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // hasDebuggee tests.
 
 var g1 = newGlobal('new-compartment'), g1w;
 g1.eval("var g2 = newGlobal('same-compartment')");
 var g2 = g1.g2;
 var g1w, g2w;
 
 var dbg = new Debug;
--- a/js/src/jit-test/tests/debug/Debug-debuggees-05.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-05.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // addDebuggee returns different Debug.Object wrappers for different Debug objects.
 
 var g = newGlobal('new-compartment');
 var dbg1 = new Debug;
 var gw1 = dbg1.addDebuggee(g);
 var dbg2 = new Debug;
 var gw2 = dbg2.addDebuggee(g);
 assertEq(gw1 !== gw2, true);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-06.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-06.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // {has,add,remove}Debuggee throw a TypeError if the argument is invalid.
 
 load(libdir + "asserts.js");
 
 var dbg = new Debug;
 
 function check(val) {
     assertThrowsInstanceOf(function () { dbg.hasDebuggee(val); }, TypeError);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-07.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-07.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Handle proto-less objects passed to addDebuggee.
 
 var g = newGlobal('new-compartment');
 var obj = g.eval("Object.create(null)");
 var dbg = new Debug;
 
 // hasDebuggee and removeDebuggee must tolerate proto-less objects.
 assertEq(dbg.hasDebuggee(obj), false);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-08.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-08.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // addDebuggee(obj), where obj is not global, adds obj's global.
 // Adding a debuggee more than once is redundant.
 
 var dbg = new Debug;
 var g = newGlobal('new-compartment');
 var w = dbg.addDebuggee(g);
 assertEq(w instanceof Debug.Object, true);
 
--- a/js/src/jit-test/tests/debug/Debug-debuggees-10.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-10.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Allow diamonds in the graph of the compartment "debugs" relation.
-
 var program = newGlobal('new-compartment');
 var d1 = newGlobal('new-compartment');
 d1.top = this;
 var d2 = newGlobal('new-compartment');
 d2.top = this;
 var dbg = new Debug(d1, d2);
 d1.eval("var dbg = new Debug(top.program)");
 d2.eval("var dbg = new Debug(top.program)");
--- a/js/src/jit-test/tests/debug/Debug-debuggees-12.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-12.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Events in a non-debuggee are ignored, even if a debuggee is in the same compartment.
-
 var g1 = newGlobal('new-compartment');
 var g2 = g1.eval("newGlobal('same-compartment')");
 var dbg = new Debug(g1);
 var hits = 0;
 dbg.hooks = {debuggerHandler: function () { hits++; }};
 g1.eval("debugger;");
 assertEq(hits, 1);
 g2.eval("debugger;");
--- a/js/src/jit-test/tests/debug/Debug-debuggees-13.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-13.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Removing a debuggee does not detach the debugger from a compartment if another debuggee is in it.
-
 var g1 = newGlobal('new-compartment');
 var g2 = g1.eval("newGlobal('same-compartment')");
 var dbg = new Debug(g1, g2);
 var hits = 0;
 dbg.hooks = {debuggerHandler: function (frame) { hits++; }};
 dbg.removeDebuggee(g1);
 g2.eval("debugger;");
 assertEq(hits, 1);
--- a/js/src/jit-test/tests/debug/Debug-debuggees-14.js
+++ b/js/src/jit-test/tests/debug/Debug-debuggees-14.js
@@ -1,11 +1,7 @@
-// Reject non-debug-mode debuggees without asserting.
-
-load(libdir + "asserts.js");
-
-function f() {
-    var v = new Debug;
-    var g = newGlobal('new-compartment');
-    v.addDebuggee(g); // don't assert
-}
-
-assertThrowsInstanceOf(f, Error);
+// Adding a debuggee in a compartment that is already in debug mode works
+// even if a script from that compartment is on the stack.
+var g = newGlobal('new-compartment');
+var dbg1 = Debug(g);
+var dbg2 = Debug();
+g.parent = this;
+g.eval("parent.dbg2.addDebuggee(this);");
--- a/js/src/jit-test/tests/debug/Debug-enabled-01.js
+++ b/js/src/jit-test/tests/debug/Debug-enabled-01.js
@@ -1,10 +1,8 @@
-// |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);
 assertEq(dbg.enabled, true);
--- a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js
+++ b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // getYoungestFrame basics.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 assertEq(dbg.getYoungestFrame(), null);
 
--- a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js
+++ b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Hooks and Debug.prototype.getYoungestFrame produce the same Frame object.
 
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
 var savedFrame, savedCallee;
 dbg.hooks = {
     debuggerHandler: function (frame) {
--- a/js/src/jit-test/tests/debug/Debug-multi-01.js
+++ b/js/src/jit-test/tests/debug/Debug-multi-01.js
@@ -1,9 +1,8 @@
-// |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);
--- a/js/src/jit-test/tests/debug/Debug-multi-02.js
+++ b/js/src/jit-test/tests/debug/Debug-multi-02.js
@@ -1,9 +1,8 @@
-// |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;
 
--- a/js/src/jit-test/tests/debug/Debug-multi-03.js
+++ b/js/src/jit-test/tests/debug/Debug-multi-03.js
@@ -1,9 +1,8 @@
-// |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;
--- a/js/src/jit-test/tests/debug/Frame-01.js
+++ b/js/src/jit-test/tests/debug/Frame-01.js
@@ -1,70 +1,33 @@
-// |jit-test| debug
 // Test .type and .generator fields of topmost stack frame passed to debuggerHandler.
 
 var g = newGlobal('new-compartment');
-g.debuggeeGlobal = this;
-g.eval("var hits;");
-g.eval("(" + function () {
-        var dbg = Debug(debuggeeGlobal);
-        dbg.hooks = {
-            debuggerHandler: function (f) {
-                // print(uneval(expected));
-                assertEq(Object.getPrototypeOf(f), Debug.Frame.prototype);
-                assertEq(f.type, expected.type);
-                assertEq(f.generator, expected.generator);
-                assertEq(f.constructing, expected.constructing);
-                hits++;
-            }
-        };
-    } + ")()");
+var dbg = Debug(g);
+var expected, hits;
+dbg.hooks = {
+    debuggerHandler: function (f) {
+        assertEq(Object.getPrototypeOf(f), Debug.Frame.prototype);
+        assertEq(f.type, expected.type);
+        assertEq(f.generator, expected.generator);
+        assertEq(f.constructing, expected.constructing);
+        hits++;
+    }
+};
 
-g.expected = { type:"global", generator:false, constructing:false };
-g.hits = 0;
-debugger;
-assertEq(g.hits, 1);
-
-g.expected = { type:"call", generator:false, constructing:false };
-g.hits = 0;
-(function () { debugger; })();
-assertEq(g.hits, 1);
-
-g.expected = { type:"call", generator:false, constructing:true };
-g.hits = 0;
-new function() { debugger; };
-assertEq(g.hits, 1);
-
-g.expected = { type:"call", generator:false, constructing:false };
-g.hits = 0;
-new function () {
-    (function() { debugger; })();
-    assertEq(g.hits, 1);
+function test(code, expectobj, expectedHits) {
+    expected = expectobj;
+    hits = 0;
+    g.evaluate(code);
+    assertEq(hits, arguments.length < 3 ? 1 : expectedHits);
 }
 
-g.expected = { type:"eval", generator:false, constructing:false };
-g.hits = 0;
-eval("debugger;");
-assertEq(g.hits, 1);
-
-g.expected = { type:"eval", generator:false, constructing:false };
-g.hits = 0;
-this.eval("debugger;");  // indirect eval
-assertEq(g.hits, 1);
-
-g.expected = { type:"eval", generator:false, constructing:false };
-g.hits = 0;
-(function () { eval("debugger;"); })();
-assertEq(g.hits, 1);
-
-g.expected = { type:"eval", generator:false, constructing:false };
-g.hits = 0;
-new function () {
-    eval("debugger");
-    assertEq(g.hits, 1);
-}
-
-g.expected = { type:"call", generator:true, constructing:false };
-g.hits = 0;
-function gen() { debugger; yield 1; debugger; }
-for (var x in gen()) {
-}
-assertEq(g.hits, 2);
+test("debugger;", {type: "global", generator: false, constructing: false});
+test("(function () { debugger; })();", {type: "call", generator: false, constructing: false});
+test("new function() { debugger; };", {type: "call", generator: false, constructing: true});
+test("new function () { (function() { debugger; })(); }", {type: "call", generator: false, constructing: false});
+test("eval('debugger;');", {type: "eval", generator: false, constructing: false});
+test("this.eval('debugger;');  // indirect eval", {type: "eval", generator: false, constructing: false});
+test("(function () { eval('debugger;'); })();", {type: "eval", generator: false, constructing: false});
+test("new function () { eval('debugger'); }", {type: "eval", generator: false, constructing: false});
+test("function gen() { debugger; yield 1; debugger; }\n" +
+     "for (var x in gen()) {}\n",
+     {type: "call", generator: true, constructing: false}, 2);
--- a/js/src/jit-test/tests/debug/Frame-02.js
+++ b/js/src/jit-test/tests/debug/Frame-02.js
@@ -1,35 +1,26 @@
-// |jit-test| debug
 // When the debugger is triggered twice from the same stack frame, the same
 // Debug.Frame object must be passed to the hook both times.
 
 var g = newGlobal('new-compartment');
-g.debuggeeGlobal = this;
-g.eval("var hits, frame;");
-g.eval("(" + function () {
-        var dbg = Debug(debuggeeGlobal);
-        dbg.hooks = {
-            debuggerHandler: function (f) {
-                if (hits++ == 0)
-                    frame = f;
-                else
-                    assertEq(f, frame);
-            }
-        };
-    } + ")()");
+var hits, frame;
+var dbg = Debug(g);
+dbg.hooks = {
+    debuggerHandler: function (f) {
+        if (hits++ == 0)
+            frame = f;
+        else
+            assertEq(f, frame);
+    }
+};
 
-g.hits = 0;
-debugger;
-debugger;
-assertEq(g.hits, 2);
+hits = 0;
+g.evaluate("debugger; debugger;");
+assertEq(hits, 2);
 
-g.hits = 0;
-function f() {
-    debugger;
-    debugger;
-}
-f();
-assertEq(g.hits, 2);
+hits = 0;
+g.evaluate("function f() { debugger; debugger; }  f();");
+assertEq(hits, 2);
 
-g.hits = 0;
-eval("debugger; debugger;");
-assertEq(g.hits, 2);
+hits = 0;
+g.evaluate("eval('debugger; debugger;');");
+assertEq(hits, 2);
--- a/js/src/jit-test/tests/debug/Frame-03.js
+++ b/js/src/jit-test/tests/debug/Frame-03.js
@@ -1,26 +1,21 @@
-// |jit-test| debug
 // When the debugger is triggered from different stack frames that happen to
 // occupy the same memory, it must deliver different Debug.Frame objects.
 
 var g = newGlobal('new-compartment');
-g.debuggeeGlobal = this;
-g.eval("var hits;");
-g.eval("(" + function () {
-        var a = [];
-        var dbg = Debug(debuggeeGlobal);
-        dbg.hooks = {
-            debuggerHandler: function (frame) {
-                for (var i = 0; i < a.length; i++) 
-                    assertEq(a[i] === frame, false);
-                a.push(frame);
-                hits++;
-            }
-        };
-    } + ")()");
+var dbg = Debug(g);
+var hits;
+var a = [];
+dbg.hooks = {
+    debuggerHandler: function (frame) {
+        for (var i = 0; i < a.length; i++) 
+            assertEq(a[i] === frame, false);
+        a.push(frame);
+        hits++;
+    }
+};
 
-function f() { debugger; }
-function h() { debugger; f(); }
-g.hits = 0;
-for (var i = 0; i < 4; i++)
-    h();
-assertEq(g.hits, 8);
+g.eval("function f() { debugger; }");
+g.eval("function h() { debugger; f(); }");
+hits = 0;
+g.eval("for (var i = 0; i < 4; i++) h();");
+assertEq(hits, 8);
--- a/js/src/jit-test/tests/debug/Frame-arguments-01.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Frame.prototype.arguments with primitive values
 
 var g = newGlobal('new-compartment');
 g.args = null;
 var dbg = new Debug(g);
 var hits;
 var v;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/Frame-arguments-02.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Object arguments.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         var args = frame.arguments;
--- a/js/src/jit-test/tests/debug/Frame-arguments-03.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Destructuring arguments.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         var args = frame.arguments;
--- a/js/src/jit-test/tests/debug/Frame-arguments-04.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.arguments works for all live frames
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         for (var i = 0; i <= 4; i++) {
--- a/js/src/jit-test/tests/debug/Frame-arguments-05.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-05.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.arguments is "live" (it reflects assignments to arguments).
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var log = '';
 var args;
 dbg.hooks = {
     debuggerHandler: function (frame) {
--- a/js/src/jit-test/tests/debug/Frame-arguments-06.js
+++ b/js/src/jit-test/tests/debug/Frame-arguments-06.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Test extracting frame.arguments element getters and calling them in
 // various awkward ways.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
--- a/js/src/jit-test/tests/debug/Frame-eval-01.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // simplest possible test of Debug.Frame.prototype.eval
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var c;
 dbg.hooks = {debuggerHandler: function (frame) { c = frame.eval("2 + 2"); }};
 g.eval("debugger;");
 assertEq(c.return, 4);
--- a/js/src/jit-test/tests/debug/Frame-eval-02.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.eval() throws if frame is not live
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var f;
 dbg.hooks = {debuggerHandler: function (frame) { f = frame; }};
--- a/js/src/jit-test/tests/debug/Frame-eval-03.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Test eval-ing names in a topmost script frame
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/Frame-eval-04.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.eval SyntaxErrors are reflected, not thrown
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var exc, SEp;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         exc = frame.eval("#$@!").throw;
--- a/js/src/jit-test/tests/debug/Frame-eval-05.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-05.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // var declarations in strict frame.eval do not modify the frame
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var cv;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         cv = frame.eval("'use strict'; var a = 2; h();");
--- a/js/src/jit-test/tests/debug/Frame-eval-06.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-06.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.eval throws if frame is a generator frame that isn't currently on the stack
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 g.eval("function gen(a) { debugger; yield a; }");
 g.eval("function test() { debugger; }");
 var dbg = new Debug(g);
--- a/js/src/jit-test/tests/debug/Frame-eval-07.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-07.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // test frame.eval in non-top frames
 
 var g = newGlobal('new-compartment');
 var N = g.N = 12; // must be even
 assertEq(N % 2, 0);
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/Frame-eval-08.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-08.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // The arguments can escape from a function via a debugging hook.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 
 // capture arguments object and test function
 var args, testfn;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/Frame-eval-09.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-09.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // assigning to local variables in frame.eval code
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 dbg.hooks = {
     debuggerHandler: function (frame) {
         frame.eval("outerarg = 1; outervar = 2; innerarg = 3; innervar = 4;");
     }
--- a/js/src/jit-test/tests/debug/Frame-eval-10.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-10.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // frame.eval returns null if the eval code fails with an uncatchable error.
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	if (hits++ === 0)
 	    assertEq(frame.eval("debugger;"), null);
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // evalWithBindings basics
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.evalWithBindings("x", {x: 2}).return, 2);
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // evalWithBindings to call a method of a debuggee object
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug;
 var global = dbg.addDebuggee(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	var obj = frame.arguments[0];
 	var expected = frame.arguments[1];
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // arguments works in evalWithBindings (it does not interpose a function scope)
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug;
 var global = dbg.addDebuggee(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	var argc = frame.arguments.length;
 	assertEq(argc, 7);
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // evalWithBindings works on non-top frames.
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var f1;
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.older.evalWithBindings("q + r", {r: 3}).return, 5);
 
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // evalWithBindings code can assign to the bindings.
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.evalWithBindings("for (i = 0; i < 5; i++) {}  i;", {i: 10}).return, 5);
 	hits++;
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // In evalWithBindings code, assignment to any name not in the bindings works just as in eval.
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.evalWithBindings("y = z; x = w;", {z: 2, w: 3}).return, 3);
     }
 };
 g.eval("function f(x) { debugger; assertEq(x, 3); }");
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // var statements in strict evalWithBindings code behave like strict eval.
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.evalWithBindings("var i = a*a + b*b; i === 25;", {a: 3, b: 4}).return, true);
 	hits++;
     }
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // evalWithBindings ignores non-enumerable and non-own properties.
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	assertEq(frame.evalWithBindings("toString + constructor + length", []).return, 112233);
 	var obj = Object.create({constructor: "FAIL"}, {length: {value: "fail"}});
 	assertEq(frame.evalWithBindings("toString + constructor + length", obj).return, 112233);
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // evalWithBindings code is debuggee code, so it can trip the debugger. It nests!
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var f1;
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	f1 = frame;
 
--- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js
+++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Direct eval code under evalWithbindings sees both the bindings and the enclosing scope.
-
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
 	var code =
 	    "assertEq(a, 1234);\n" +
 	    "assertEq(b, null);\n" +
--- a/js/src/jit-test/tests/debug/Frame-identity-04.js
+++ b/js/src/jit-test/tests/debug/Frame-identity-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Test that on-stack Debug.Frames are not GC'd even if they are only reachable
 // from the js::Debug::frames table.
 
 var g = newGlobal('new-compartment');
 g.eval("function f(n) { if (n) f(n - 1); debugger; }");
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/Frame-live-03.js
+++ b/js/src/jit-test/tests/debug/Frame-live-03.js
@@ -1,21 +1,18 @@
 // |jit-test| debug
 // frame.type throws if !frame.live
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
-g.debuggeeGlobal = this;
-g.eval("var f;");
-g.eval("(" + function () {
-        Debug(debuggeeGlobal).hooks = {
-            debuggerHandler: function (frame) {
-                assertEq(frame.type, "call");
-                assertEq(frame.live, true);
-                f = frame;
-            }
-        };
-    } + ")()");
+var f;
+Debug(g).hooks = {
+    debuggerHandler: function (frame) {
+        assertEq(frame.type, "call");
+        assertEq(frame.live, true);
+        f = frame;
+    }
+};
 
-(function () { debugger; })();
-assertEq(g.f.live, false);
-assertThrowsInstanceOf(function () { g.f.type; }, g.Error);
+g.eval("(function () { debugger; })();");
+assertEq(f.live, false);
+assertThrowsInstanceOf(function () { f.type; }, Error);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-live-04.js
@@ -0,0 +1,28 @@
+// Frame.live is false for frames discarded during uncatchable error unwinding.
+var g = newGlobal('new-compartment');
+var dbg = Debug(g);
+var hits = 0;
+var snapshot;
+dbg.hooks = {
+    debuggerHandler: function (frame) {
+	var stack = [];
+	for (var f = frame; f; f = f.older) {
+	    if (f.type === "call")
+		stack.push(f);
+	}
+	snapshot = stack;
+	if (hits++ === 0)
+	    assertEq(frame.eval("x();"), null);
+	else
+	    return null;
+    }
+};
+
+g.eval("function z() { debugger; }");
+g.eval("function y() { z(); }");
+g.eval("function x() { y(); }");
+assertEq(g.eval("debugger; 'ok';"), "ok");
+assertEq(hits, 2);
+assertEq(snapshot.length, 3);
+for (var i = 0; i < snapshot.length; i++)
+    assertEq(snapshot[i].live, false);
--- a/js/src/jit-test/tests/debug/Frame-older-01.js
+++ b/js/src/jit-test/tests/debug/Frame-older-01.js
@@ -1,25 +1,22 @@
 // |jit-test| debug
 // Basic call chain.
 
 var g = newGlobal('new-compartment');
-g.debuggeeGlobal = this;
-g.result = null;
-g.eval("(" + function () {
-        var dbg = new Debug(debuggeeGlobal);
-        dbg.hooks = {
-            debuggerHandler: function (frame) {
-                var a = [];
-                assertEq(frame === frame.older, false);
-                for (; frame; frame = frame.older)
-                    a.push(frame.type === 'call' ? frame.callee.name : frame.type);
-                a.reverse();
-                result = a.join(", ");
-            }
-        };
-    } + ")();");
+var result = null;
+var dbg = new Debug(g);
+dbg.hooks = {
+    debuggerHandler: function (frame) {
+        var a = [];
+        assertEq(frame === frame.older, false);
+        for (; frame; frame = frame.older)
+            a.push(frame.type === 'call' ? frame.callee.name : frame.type);
+        a.reverse();
+        result = a.join(", ");
+    }
+};
 
-function first() { return second(); }
-function second() { return eval("third()"); }
-function third() { debugger; }
-first();
-assertEq(g.result, "global, first, second, eval, third");
+g.eval("function first() { return second(); }");
+g.eval("function second() { return eval('third()'); }");
+g.eval("function third() { debugger; }");
+g.evaluate("first();");
+assertEq(result, "global, first, second, eval, third");
--- a/js/src/jit-test/tests/debug/Frame-this-01.js
+++ b/js/src/jit-test/tests/debug/Frame-this-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Frame.prototype.this in strict-mode functions, with primitive values
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         hits++;
--- a/js/src/jit-test/tests/debug/Frame-this-02.js
+++ b/js/src/jit-test/tests/debug/Frame-this-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Frame.prototype.this in strict direct eval frames
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         hits++;
--- a/js/src/jit-test/tests/debug/Frame-this-03.js
+++ b/js/src/jit-test/tests/debug/Frame-this-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Frame.prototype.this in non-strict-mode functions, with primitive values
 
 function classOf(obj) {
     return Object.prototype.toString.call(obj).match(/^\[object (.*)\]$/)[1];
 }
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
--- a/js/src/jit-test/tests/debug/Frame-this-04.js
+++ b/js/src/jit-test/tests/debug/Frame-this-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Debug.Frame.prototype.this in functions, with object values
 
 function classOf(obj) {
     return Object.prototype.toString.call(obj).match(/^\[object (.*)\]$/)[1];
 }
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
--- a/js/src/jit-test/tests/debug/Object-01.js
+++ b/js/src/jit-test/tests/debug/Object-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Debug.Object basics
 
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         assertEq(frame.arguments[0], frame.callee);
--- a/js/src/jit-test/tests/debug/Object-apply-01.js
+++ b/js/src/jit-test/tests/debug/Object-apply-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // tests calling script functions via Debug.Object.prototype.apply/call
 
 load(libdir + "asserts.js");
 
 var g = newGlobal("new-compartment");
 g.eval("function f() { debugger; }");
 var dbg = new Debug(g);
 dbg.hooks = {debuggerHandler: function () {}};
--- a/js/src/jit-test/tests/debug/Object-apply-02.js
+++ b/js/src/jit-test/tests/debug/Object-apply-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // tests calling native functions via Debug.Object.prototype.apply/call
 
 load(libdir + "asserts.js");
 
 var g = newGlobal("new-compartment");
 g.eval("function f() { debugger; }");
 var dbg = new Debug(g);
 dbg.hooks = {debuggerHandler: function () {}};
--- a/js/src/jit-test/tests/debug/Object-apply-03.js
+++ b/js/src/jit-test/tests/debug/Object-apply-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // reentering the debugger several times via debuggerHandler and apply/call on a single stack
 
 var g = newGlobal("new-compartment");
 var dbg = Debug(g);
 
 function test(usingApply) {
     dbg.hooks = {
         debuggerHandler: function (frame) {
--- a/js/src/jit-test/tests/debug/Object-apply-04.js
+++ b/js/src/jit-test/tests/debug/Object-apply-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Debug.Object.prototype.apply/call works with function proxies
 
 var g = newGlobal('new-compartment');
 g.eval("function f() { debugger; }");
 var dbg = Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
--- a/js/src/jit-test/tests/debug/Object-callable.js
+++ b/js/src/jit-test/tests/debug/Object-callable.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Test Debug.Object.prototype.callable.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         assertEq(frame.arguments[0].callable, frame.arguments[1]);
--- a/js/src/jit-test/tests/debug/Object-class.js
+++ b/js/src/jit-test/tests/debug/Object-class.js
@@ -1,19 +1,17 @@
-// |jit-test| debug
-
+// Basic tests for Debug.Object.prototype.class.
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         var arr = frame.arguments;
         assertEq(arr[0].class, "Object");
         assertEq(arr[1].class, "Array");
         assertEq(arr[2].class, "Function");
         assertEq(arr[3].class, "Date");
         assertEq(arr[4].class, "Proxy");
         hits++;
     }
 };
-
 g.eval("(function () { debugger; })(Object.prototype, [], eval, new Date, Proxy.create({}));");
 assertEq(hits, 1);
--- a/js/src/jit-test/tests/debug/Object-identity-01.js
+++ b/js/src/jit-test/tests/debug/Object-identity-01.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Two references to the same object get the same Debug.Object wrapper.
-
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         assertEq(frame.arguments[0], frame.arguments[1]);
         hits++;
     }
--- a/js/src/jit-test/tests/debug/Object-identity-02.js
+++ b/js/src/jit-test/tests/debug/Object-identity-02.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Different objects get different Debug.Object wrappers.
-
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         assertEq(frame.arguments[0] === frame.arguments[1], false);
         hits++;
     }
--- a/js/src/jit-test/tests/debug/Object-identity-03.js
+++ b/js/src/jit-test/tests/debug/Object-identity-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // The same object gets the same Debug.Object wrapper at different times, if the difference would be observable.
 
 var N = HOTLOOP + 4;
 
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var wrappers = [];
 
--- a/js/src/jit-test/tests/debug/Object-name-01.js
+++ b/js/src/jit-test/tests/debug/Object-name-01.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Debug.Object.prototype.name
-
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var name, hits;
 dbg.hooks = {debuggerHandler: function (frame) { hits++; assertEq(frame.callee.name, name); }};
 
 hits = 0;
 name = "f";
 g.eval("(function f() { debugger; })();");
--- a/js/src/jit-test/tests/debug/Object-name-02.js
+++ b/js/src/jit-test/tests/debug/Object-name-02.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // The .name of a non-function object is undefined.
-
 var g = newGlobal('new-compartment');
 var hits = 0;
 var dbg = new Debug(g);
 dbg.hooks = {
     debuggerHandler: function (frame) {
         assertEq(frame.arguments[0].name, undefined);
         hits++;
     }
--- a/js/src/jit-test/tests/debug/Object-parameterNames.js
+++ b/js/src/jit-test/tests/debug/Object-parameterNames.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 load(libdir + 'array-compare.js');
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var hits = 0;
 dbg.hooks = {
     debuggerHandler: function (frame) {
         var arr = frame.arguments;
--- a/js/src/jit-test/tests/debug/Object-proto.js
+++ b/js/src/jit-test/tests/debug/Object-proto.js
@@ -1,9 +1,9 @@
-// |jit-test| debug
+// Debug.Object.prototype.proto
 var g = newGlobal('new-compartment');
 var dbgeval = function () {
         var dbg = new Debug(g);
         var hits = 0;
         g.eval("function f() { debugger; }");
         var lastval;
         dbg.hooks = {debuggerHandler: function (frame) { lastval = frame.arguments[0]; }};
         return function dbgeval(s) {
--- a/js/src/jit-test/tests/debug/dispatch-01.js
+++ b/js/src/jit-test/tests/debug/dispatch-01.js
@@ -1,9 +1,8 @@
-// |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);
--- a/js/src/jit-test/tests/debug/dispatch-02.js
+++ b/js/src/jit-test/tests/debug/dispatch-02.js
@@ -1,9 +1,8 @@
-// |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++) {
--- a/js/src/jit-test/tests/debug/gc-01.js
+++ b/js/src/jit-test/tests/debug/gc-01.js
@@ -1,9 +1,8 @@
-// |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() {
--- a/js/src/jit-test/tests/debug/gc-02.js
+++ b/js/src/jit-test/tests/debug/gc-02.js
@@ -1,9 +1,8 @@
-// |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. :-\
--- a/js/src/jit-test/tests/debug/gc-03.js
+++ b/js/src/jit-test/tests/debug/gc-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Storing a property on a Debug.Object protects it from GC as long as the
 // referent is alive.
 
 var g = newGlobal('new-compartment');
 var N = g.N = 3;
 var dbg = Debug(g);
 
 var i = 0;
--- a/js/src/jit-test/tests/debug/gc-04.js
+++ b/js/src/jit-test/tests/debug/gc-04.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Storing a Debug.Object as a key in a WeakMap protects it from GC as long as
 // the referent is alive.
 
 var g = newGlobal('new-compartment');
 var N = g.N = 10;
 var dbg = Debug(g);
 var cache = new WeakMap;
 
--- a/js/src/jit-test/tests/debug/gc-05.js
+++ b/js/src/jit-test/tests/debug/gc-05.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // If a Debug survives its debuggee, its object cache must still be swept.
 
 var g2arr = []; // non-debuggee globals
 var xarr = []; // debuggee objects
 
 var N = 4, M = 4;
 for (var i = 0; i < N; i++) {
     var g1 = newGlobal('new-compartment');
--- a/js/src/jit-test/tests/debug/gc-06.js
+++ b/js/src/jit-test/tests/debug/gc-06.js
@@ -1,8 +1,6 @@
-// |jit-test| debug
 // Debug objects do not keep debuggee globals live.
-
 var dbg = new Debug;
 for (var i = 0; i < 4; i++)
     dbg.addDebuggee(newGlobal('new-compartment'));
 gc();
 assertEq(dbg.getDebuggees().length <= 1, true);
--- a/js/src/jit-test/tests/debug/gc-07.js
+++ b/js/src/jit-test/tests/debug/gc-07.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // Don't assert with dead Debug.Object and live cross-compartment wrapper of referent.
-
 var g = newGlobal('new-compartment');
 for (var j = 0; j < 4; j++) {
     var dbg = new Debug;
     dbg.addDebuggee(g);
     dbg.enabled = false;
     dbg = null;
     gc(); gc();
 } 
--- a/js/src/jit-test/tests/debug/gc-08.js
+++ b/js/src/jit-test/tests/debug/gc-08.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Debuggers with enabled throw hooks should not be GC'd even if they are
 // otherwise unreachable.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var actual = 0;
 var expected = 0;
--- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js
+++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js
@@ -1,14 +1,9 @@
-// |jit-test| debug
 var g = newGlobal('new-compartment');
 g.log = '';
 
 var dbg = Debug(g);
-var hooks = {
-    debuggerHandler: function (stack) {
-        g.log += '!';
-    }
-};
+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');
--- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js
+++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js
@@ -1,9 +1,8 @@
-// |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) {
--- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js
+++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js
@@ -1,9 +1,8 @@
-// |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) {
--- a/js/src/jit-test/tests/debug/hooks-deleted.js
+++ b/js/src/jit-test/tests/debug/hooks-deleted.js
@@ -1,9 +1,8 @@
-// |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';}};
 
--- a/js/src/jit-test/tests/debug/hooks-throw-01.js
+++ b/js/src/jit-test/tests/debug/hooks-throw-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Basic throw hook test.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 var hit = false;
 dbg.hooks = {
--- a/js/src/jit-test/tests/debug/hooks-throw-04.js
+++ b/js/src/jit-test/tests/debug/hooks-throw-04.js
@@ -1,11 +1,9 @@
-// |jit-test| debug
 // hooks.throw is not called for exceptions thrown and handled in the debugger.
-
 var g = newGlobal('new-compartment');
 var dbg = Debug(g);
 g.log = '';
 dbg.hooks = {
     debuggerHandler: function (frame) {
         try {
             throw new Error("oops");
         } catch (exc) {
--- a/js/src/jit-test/tests/debug/resumption-01.js
+++ b/js/src/jit-test/tests/debug/resumption-01.js
@@ -1,15 +1,13 @@
-// |jit-test| debug
 // Simple {throw:} resumption.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('new-compartment');
-
 var dbg = Debug(g);
 dbg.hooks = {
     debuggerHandler: function (stack) {
         return {throw: "oops"};
     }
 };
 
 assertThrowsValue(function () { g.eval("debugger;"); }, "oops");
--- a/js/src/jit-test/tests/debug/resumption-02.js
+++ b/js/src/jit-test/tests/debug/resumption-02.js
@@ -1,13 +1,10 @@
-// |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);
--- a/js/src/jit-test/tests/debug/surfaces-01.js
+++ b/js/src/jit-test/tests/debug/surfaces-01.js
@@ -1,9 +1,9 @@
-// |jit-test| debug
+// Check superficial characteristics of functions and properties (not functionality).
 
 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");
--- a/js/src/jit-test/tests/debug/surfaces-02.js
+++ b/js/src/jit-test/tests/debug/surfaces-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // 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;
--- a/js/src/jit-test/tests/debug/surfaces-03.js
+++ b/js/src/jit-test/tests/debug/surfaces-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // 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');
 
--- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js
+++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js
@@ -1,9 +1,8 @@
-// |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 () {
--- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js
+++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // Returning a bad resumption value causes an exception that is reported to the
 // uncaughtExceptionHook.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 dbg.hooks = {debuggerHandler: function () { return {oops: "bad resumption value"}; }};
 dbg.uncaughtExceptionHook = function (exc) {
     assertEq(exc instanceof TypeError, true);
--- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js
+++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // 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; }};
--- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js
+++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js
@@ -1,9 +1,8 @@
-// |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);
--- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js
+++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js
@@ -1,9 +1,8 @@
-// |jit-test| debug
 // After hooks.throw throws, if uncaughtExceptionHook returns undefined,
 // the original exception continues to propagate.
 
 var g = newGlobal('new-compartment');
 var dbg = new Debug(g);
 var log = '';
 dbg.hooks = {throw: function () { log += "1"; throw new Error("oops"); }};
 dbg.uncaughtExceptionHook = function () { log += "2"; };
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -310,32 +310,32 @@ Script::analyze(JSContext *cx, JSScript 
     for (unsigned i = 0; i < nfixed; i++)
         locals[i] = LOCAL_CONDITIONALLY_DEFINED;
 
     /*
      * Treat locals as having a possible use-before-def if they could be accessed
      * by debug code or by eval, or if they could be accessed by an inner script.
      */
 
-    if (script->usesEval || cx->compartment->debugMode) {
+    if (script->usesEval || cx->compartment->debugMode()) {
         for (uint32 i = 0; i < nfixed; i++)
             setLocal(i, LOCAL_USE_BEFORE_DEF);
     }
 
     for (uint32 i = 0; i < script->nClosedVars; i++) {
         uint32 slot = script->getClosedVar(i);
         if (slot < nfixed)
             setLocal(slot, LOCAL_USE_BEFORE_DEF);
     }
 
     /*
      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
      * any safe point.
      */
-    if (cx->compartment->debugMode)
+    if (cx->compartment->debugMode())
         usesRval = true;
 
     /*
      * If we are in the middle of one or more jumps, the offset of the highest
      * target jumping over this bytecode.  Includes implicit jumps from
      * try/catch/finally blocks.
      */
     unsigned forwardJump = 0;
--- a/js/src/jsapi-tests/testDebugger.cpp
+++ b/js/src/jsapi-tests/testDebugger.cpp
@@ -134,13 +134,13 @@ BEGIN_TEST(testDebugger_debugObjectVsDeb
         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);
+    CHECK_SAME(v, INT_TO_JSVAL(4));
     
     return true;
 }
 END_TEST(testDebugger_debugObjectVsDebugMode)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -702,17 +702,17 @@ JSRuntime::init(uint32 maxbytes)
     stateChange = JS_NEW_CONDVAR(gcLock);
     if (!stateChange)
         return false;
     debuggerLock = JS_NEW_LOCK();
     if (!debuggerLock)
         return false;
 #endif
 
-    debugMode = JS_FALSE;
+    debugMode = false;
     return js_InitThreads(this);
 }
 
 JSRuntime::~JSRuntime()
 {
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
     if (!JS_CLIST_IS_EMPTY(&contextList)) {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -490,17 +490,17 @@ struct JSRuntime {
     JSCList             contextList;
 
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
 
     /*
      * Right now, we only support runtime-wide debugging.
      */
-    JSBool              debugMode;
+    bool                debugMode;
 
 #ifdef JS_TRACER
     /* True if any debug hooks not supported by the JIT are enabled. */
     bool debuggerInhibitsJIT() const {
         return (globalDebugHooks.interruptHook ||
                 globalDebugHooks.callHook);
     }
 #endif
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -82,17 +82,17 @@ JSCompartment::JSCompartment(JSRuntime *
     emptyArgumentsShape(NULL),
     emptyBlockShape(NULL),
     emptyCallShape(NULL),
     emptyDeclEnvShape(NULL),
     emptyEnumeratorShape(NULL),
     emptyWithShape(NULL),
     initialRegExpShape(NULL),
     initialStringShape(NULL),
-    debugMode(rt->debugMode),
+    debugModeBits(rt->debugMode * DebugFromC),
     mathCache(NULL)
 {
     JS_INIT_CLIST(&scripts);
 
     PodArrayZero(scriptsToGC);
 }
 
 JSCompartment::~JSCompartment()
@@ -596,8 +596,59 @@ JSCompartment::incBackEdgeCount(jsbyteco
     return 1;  /* oom not reported by backEdgeTable, so ignore. */
 }
 
 bool
 JSCompartment::isAboutToBeCollected(JSGCInvocationKind gckind)
 {
     return !hold && (arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT);
 }
+
+bool
+JSCompartment::haveScriptsOnStack(JSContext *cx)
+{
+    for (AllFramesIter i(cx); !i.done(); ++i) {
+        JSScript *script = i.fp()->maybeScript();
+        if (script && script->compartment == this)
+            return true;
+    }
+    return false;
+}
+
+bool
+JSCompartment::setDebugModeFromC(JSContext *cx, bool b)
+{
+    bool wasEnabled = debugMode();
+
+    // Debug mode can be enabled only when no scripts from the target
+    // compartment are on the stack. It would even be incorrect to discard just
+    // the non-live scripts' JITScripts because they might share ICs with live
+    // scripts (bug 632343).
+    if (b && !wasEnabled && haveScriptsOnStack(cx)) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
+        return false;
+    }
+
+    debugModeBits = (debugModeBits & ~uintN(DebugFromC)) | (b * DebugFromC);
+    if (b != wasEnabled)
+        updateForDebugMode(cx);
+    return true;
+}
+
+void
+JSCompartment::updateForDebugMode(JSContext *cx)
+{
+#ifdef JS_METHODJIT
+    bool mode = debugMode();
+
+    // Discard JIT code for any scripts that change debugMode. This assumes
+    // that 'comp' is in the same thread as 'cx'.
+    for (JSScript *script = (JSScript *) scripts.next;
+         &script->links != &scripts;
+         script = (JSScript *) script->links.next)
+    {
+        if (script->debugMode != mode) {
+            mjit::ReleaseScriptCode(cx, script);
+            script->debugMode = mode;
+        }
+    }
+#endif
+}
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -457,18 +457,21 @@ struct JS_FRIEND_API(JSCompartment) {
      * additions may cause these shapes to not be used by a RegExp or String
      * (even along the entire shape parent chain, should the object go into
      * dictionary mode). But because all the initial properties are
      * non-configurable, they will always map to fixed slots.
      */
     const js::Shape              *initialRegExpShape;
     const js::Shape              *initialStringShape;
 
-    bool                         debugMode;  // true iff debug mode on
-    JSCList                      scripts;    // scripts in this compartment
+  private:
+    enum { DebugFromC = 1, DebugFromJS = 2 };
+    uintN                        debugModeBits;  // see debugMode() below
+  public:
+    JSCList                      scripts;        // scripts in this compartment
 
     js::NativeIterCache          nativeIterCache;
 
     typedef js::Maybe<js::ToSourceCache> LazyToSourceCache;
     LazyToSourceCache            toSourceCache;
 
     JSCompartment(JSRuntime *rt);
     ~JSCompartment();
@@ -523,23 +526,62 @@ struct JS_FRIEND_API(JSCompartment) {
   public:
     js::MathCache *getMathCache(JSContext *cx) {
         return mathCache ? mathCache : allocMathCache(cx);
     }
 
     size_t backEdgeCount(jsbytecode *pc) const;
     size_t incBackEdgeCount(jsbytecode *pc);
 
+    /*
+     * There are dueling APIs for debug mode. It can be enabled or disabled via
+     * JS_SetDebugModeForCompartment. It is automatically enabled and disabled
+     * by Debug objects. Therefore debugModeBits has the DebugFromC bit set if
+     * the C API wants debug mode and the DebugFromJS bit set if debuggees is
+     * nonempty.
+     */
+    bool debugMode() const { return !!debugModeBits; }
+
+    /*
+     * True if any scripts from this compartment are on the JS stack in the
+     * calling thread. cx is a context in the calling thread, and it is assumed
+     * that no other thread is using this compartment.
+     */
+    bool haveScriptsOnStack(JSContext *cx);
+
+    bool setDebugModeFromC(JSContext *cx, bool b);
+
+    /* This is called only when debugMode() has just toggled. */
+    void updateForDebugMode(JSContext *cx);
+
     js::GlobalObjectSet &getDebuggees() { return debuggees; }
-    bool addDebuggee(js::GlobalObject *global) {
-        JS_ASSERT(debugMode);
-        return !!debuggees.put(global);
+
+    bool addDebuggee(JSContext *cx, js::GlobalObject *global) {
+        bool wasEnabled = debugMode();
+        if (!wasEnabled && haveScriptsOnStack(cx)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
+            return false;
+        }
+        if (!debuggees.put(global)) {
+            js_ReportOutOfMemory(cx);
+            return false;
+        }
+        debugModeBits |= DebugFromJS;
+        if (!wasEnabled)
+            updateForDebugMode(cx);
+        return true;
     }
-    void removeDebuggee(js::GlobalObject *global) {
+
+    void removeDebuggee(JSContext *cx, js::GlobalObject *global) {
+        JS_ASSERT(debuggees.has(global));
         debuggees.remove(global);
+        if (debuggees.empty()) {
+            debugModeBits &= ~DebugFromJS;
+            updateForDebugMode(cx);
+        }
     }
 };
 
 #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
@@ -155,26 +155,20 @@ Debug::Debug(JSObject *dbg, JSObject *ho
     // This always happens within a request on some cx.
     JSRuntime *rt = dbg->compartment()->rt;
     AutoLockGC lock(rt);
     JS_APPEND_LINK(&link, &rt->debuggerList);
 }
 
 Debug::~Debug()
 {
-    JS_ASSERT(object->compartment()->rt->gcRunning);
-    if (!debuggees.empty()) {
-        // This happens only during per-compartment GC. See comment in
-        // Debug::sweepAll.
-        JS_ASSERT(object->compartment()->rt->gcCurrentCompartment == object->compartment());
-        for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront())
-            removeDebuggeeGlobal(e.front(), NULL, &e);
-    }
+    JS_ASSERT(debuggees.empty());
 
     // This always happens in the GC thread, so no locking is required.
+    JS_ASSERT(object->compartment()->rt->gcRunning);
     JS_REMOVE_LINK(&link);
 }
 
 bool
 Debug::init(JSContext *cx)
 {
     bool ok = frames.init() && objects.init() && debuggees.init();
     if (!ok)
@@ -611,18 +605,19 @@ Debug::trace(JSTracer *trc, JSObject *ob
             JSObject *frameobj = e.front().value;
             JS_ASSERT(frameobj->getPrivate());
             MarkObject(trc, *frameobj, "live Debug.Frame");
         }
     }
 }
 
 void
-Debug::sweepAll(JSRuntime *rt)
+Debug::sweepAll(JSContext *cx)
 {
+    JSRuntime *rt = cx->runtime;
     for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
         Debug *dbg = (Debug *) ((unsigned char *) p - offsetof(Debug, link));
 
         if (dbg->object->isMarked()) {
             // Sweep ObjectMap entries for referents being collected.
             for (ObjectMap::Enum e(dbg->objects); !e.empty(); e.popFront()) {
                 JS_ASSERT(e.front().key->isMarked() == e.front().value->isMarked());
                 if (!e.front().value->isMarked())
@@ -631,57 +626,59 @@ Debug::sweepAll(JSRuntime *rt)
         } else {
             // If this Debug is being GC'd, detach it from its debuggees. In the case of
             // runtime-wide GC, the debuggee might be GC'd too. Since detaching requires
             // access to both objects, this must be done before finalize time. However, in
             // a per-compartment GC, it is impossible for both objects to be GC'd (since
             // they are in different compartments), so in that case we just wait for
             // Debug::finalize.
             for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
-                dbg->removeDebuggeeGlobal(e.front(), NULL, &e);
+                dbg->removeDebuggeeGlobal(cx, e.front(), NULL, &e);
         }
 
     }
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++)
-        sweepCompartment(*c);
+        sweepCompartment(cx, *c);
 }
 
 void
-Debug::detachAllDebuggersFromGlobal(GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum)
+Debug::detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum)
 {
     const GlobalObject::DebugVector *debuggers = global->getDebuggers();
     JS_ASSERT(!debuggers->empty());
     while (!debuggers->empty())
-        debuggers->back()->removeDebuggeeGlobal(global, compartmentEnum, NULL);
+        debuggers->back()->removeDebuggeeGlobal(cx, global, compartmentEnum, NULL);
 }
 
 void
-Debug::sweepCompartment(JSCompartment *compartment)
+Debug::sweepCompartment(JSContext *cx, JSCompartment *compartment)
 {
     // For each debuggee being GC'd, detach it from all its debuggers.
     GlobalObjectSet &debuggees = compartment->getDebuggees();
     for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
         GlobalObject *global = e.front();
         if (!global->isMarked())
-            detachAllDebuggersFromGlobal(global, &e);
+            detachAllDebuggersFromGlobal(cx, global, &e);
     }
 }
 
 void
-Debug::detachFromCompartment(JSCompartment *comp)
-{
-    for (GlobalObjectSet::Enum e(comp->getDebuggees()); !e.empty(); e.popFront())
-        detachAllDebuggersFromGlobal(e.front(), &e);
-}
-
-void
 Debug::finalize(JSContext *cx, JSObject *obj)
 {
     Debug *dbg = (Debug *) obj->getPrivate();
+    if (!dbg)
+        return;
+    if (!dbg->debuggees.empty()) {
+        // This happens only during per-compartment GC. See comment in
+        // Debug::sweepAll.
+        JS_ASSERT(cx->runtime->gcCurrentCompartment == dbg->object->compartment());
+        for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
+            dbg->removeDebuggeeGlobal(cx, e.front(), NULL, &e);
+    }
     cx->delete_(dbg);
 }
 
 Class Debug::jsclass = {
     "Debug", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
     PropertyStub, PropertyStub, PropertyStub, StrictPropertyStub,
     EnumerateStub, ResolveStub, ConvertStub, Debug::finalize,
     NULL,                 /* reserved0   */
@@ -817,17 +814,17 @@ Debug::removeDebuggee(JSContext *cx, uin
 {
     REQUIRE_ARGC("Debug.removeDebuggee", 1);
     THISOBJ(cx, vp, Debug, "removeDebuggee", thisobj, dbg);
     JSObject *referent = dbg->unwrapDebuggeeArgument(cx, vp);
     if (!referent)
         return false;
     GlobalObject *global = referent->getGlobal();
     if (dbg->debuggees.has(global))
-        dbg->removeDebuggeeGlobal(global, NULL, NULL);
+        dbg->removeDebuggeeGlobal(cx, global, NULL, NULL);
     vp->setUndefined();
     return true;
 }
 
 JSBool
 Debug::hasDebuggee(JSContext *cx, uintN argc, Value *vp)
 {
     REQUIRE_ARGC("Debug.hasDebuggee", 1);
@@ -925,32 +922,28 @@ Debug::construct(JSContext *cx, uintN ar
 
     vp->setObject(*obj);
     return true;
 }
 
 bool
 Debug::addDebuggeeGlobal(JSContext *cx, GlobalObject *obj)
 {
-    if (!obj->compartment()->debugMode) {
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DEBUG_MODE);
-        return false;
-    }
+    JSCompartment *debuggeeCompartment = obj->compartment();
 
     // Check for cycles. If obj's compartment is reachable from this Debug
     // object's compartment by following debuggee-to-debugger links, then
     // adding obj would create a cycle. (Typically nobody is debugging the
     // debugger, in which case we zip through this code without looping.)
     Vector<JSCompartment *> visited(cx);
     if (!visited.append(object->compartment()))
         return false;
-    JSCompartment *dest = obj->compartment();
     for (size_t i = 0; i < visited.length(); i++) {
         JSCompartment *c = visited[i];
-        if (c == dest) {
+        if (c == debuggeeCompartment) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_LOOP);
             return false;
         }
 
         // Find all compartments containing debuggers debugging global objects
         // in c. Add those compartments to visited.
         for (GlobalObjectSet::Range r = c->getDebuggees().all(); !r.empty(); r.popFront()) {
             GlobalObject::DebugVector *v = r.front()->getDebuggers();
@@ -958,38 +951,43 @@ Debug::addDebuggeeGlobal(JSContext *cx, 
                 JSCompartment *next = (*p)->object->compartment();
                 if (visited.find(next) == visited.end() && !visited.append(next))
                     return false;
             }
         }
     }
 
     // Each debugger-debuggee relation must be stored in up to three places.
+    // JSCompartment::addDebuggee enables debug mode if needed.
     GlobalObject::DebugVector *v = obj->getOrCreateDebuggers(cx);
-    if (!v || !v->append(this))
+    if (!v || !v->append(this)) {
+        js_ReportOutOfMemory(cx);
         goto fail1;
-    if (!debuggees.put(obj))
+    }
+    if (!debuggees.put(obj)) {
+        js_ReportOutOfMemory(cx);
         goto fail2;
-    if (obj->getDebuggers()->length() == 1 && !obj->compartment()->addDebuggee(obj))
+    }
+    if (obj->getDebuggers()->length() == 1 && !debuggeeCompartment->addDebuggee(cx, obj))
         goto fail3;
     return true;
 
     // Maintain consistency on error.
 fail3:
     debuggees.remove(obj);
 fail2:
     JS_ASSERT(v->back() == this);
     v->popBack();
 fail1:
-    js_ReportOutOfMemory(cx);
     return false;
 }
 
 void
-Debug::removeDebuggeeGlobal(GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum,
+Debug::removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
+                            GlobalObjectSet::Enum *compartmentEnum,
                             GlobalObjectSet::Enum *debugEnum)
 {
     // Each debuggee is in two HashSets: one for its compartment and one for
     // its debugger (this). The caller might be enumerating either set; if so,
     // use HashSet::Enum::removeFront rather than HashSet::remove below, to
     // avoid invalidating the live enumerator.
     JS_ASSERT(global->compartment()->getDebuggees().has(global));
     JS_ASSERT_IF(compartmentEnum, compartmentEnum->front() == global);
@@ -1021,17 +1019,17 @@ Debug::removeDebuggeeGlobal(GlobalObject
 
     // The relation must be removed from up to three places: *v and debuggees
     // for sure, and possibly the compartment's debuggee set.
     v->erase(p);
     if (v->empty()) {
         if (compartmentEnum)
             compartmentEnum->removeFront();
         else
-            global->compartment()->removeDebuggee(global);
+            global->compartment()->removeDebuggee(cx, global);
     }
     if (debugEnum)
         debugEnum->removeFront();
     else
         debuggees.remove(global);
 }
 
 JSPropertySpec Debug::properties[] = {
--- a/js/src/jsdbg.h
+++ b/js/src/jsdbg.h
@@ -78,17 +78,18 @@ class Debug {
     // Keys are referents, values are Debug.Object objects. The combination of
     // the a key being live and this Debug being live keeps the corresponding
     // Debug.Object alive.
     typedef HashMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, SystemAllocPolicy>
         ObjectMap;
     ObjectMap objects;
 
     bool addDebuggeeGlobal(JSContext *cx, GlobalObject *obj);
-    void removeDebuggeeGlobal(GlobalObject *global, GlobalObjectSet::Enum *compartmentEnum,
+    void removeDebuggeeGlobal(JSContext *cx, GlobalObject *global,
+                              GlobalObjectSet::Enum *compartmentEnum,
                               GlobalObjectSet::Enum *debugEnum);
 
     JSTrapStatus handleUncaughtException(AutoCompartment &ac, Value *vp, bool callHook);
     JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
                                       bool callHook = true);
     JSObject *unwrapDebuggeeArgument(JSContext *cx, Value *vp);
 
     static void trace(JSTracer *trc, JSObject *obj);
@@ -129,17 +130,16 @@ class Debug {
   public:
     Debug(JSObject *dbg, JSObject *hooks);
     ~Debug();
 
     bool init(JSContext *cx);
     inline JSObject *toJSObject() const;
     static inline Debug *fromJSObject(JSObject *obj);
     static Debug *fromChildJSObject(JSObject *obj);
-    static void detachFromCompartment(JSCompartment *comp);
 
     /*********************************** Methods for interaction with the GC. */
 
     // A Debug object is live if:
     //   * the Debug JSObject is live (Debug::trace handles this case); OR
     //   * it is in the middle of dispatching an event (the event dispatching
     //     code roots it in this case); OR
     //   * it is enabled, and it is debugging at least one live compartment,
@@ -148,19 +148,19 @@ class Debug {
     //       - it has a breakpoint set on a live script
     //       - it has a watchpoint set on a live object.
     //
     // The last case is handled by the mark() method. If it finds any Debug
     // objects that are definitely live but not yet marked, it marks them and
     // returns true. If not, it returns false.
     //
     static bool mark(GCMarker *trc, JSCompartment *compartment, JSGCInvocationKind gckind);
-    static void sweepAll(JSRuntime *rt);
-    static void sweepCompartment(JSCompartment *compartment);
-    static void detachAllDebuggersFromGlobal(GlobalObject *global,
+    static void sweepAll(JSContext *cx);
+    static void sweepCompartment(JSContext *cx, JSCompartment *compartment);
+    static void detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global,
                                              GlobalObjectSet::Enum *compartmentEnum);
 
     static inline void leaveStackFrame(JSContext *cx);
     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, js::Value *vp);
     static inline JSTrapStatus onThrow(JSContext *cx, js::Value *vp);
 
     /**************************************** Functions for use by jsdbg.cpp. */
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -96,29 +96,29 @@ typedef struct JSTrap {
 
 #define DBG_LOCK(rt)            JS_ACQUIRE_LOCK((rt)->debuggerLock)
 #define DBG_UNLOCK(rt)          JS_RELEASE_LOCK((rt)->debuggerLock)
 #define DBG_LOCK_EVAL(rt,expr)  (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
 
 JS_PUBLIC_API(JSBool)
 JS_GetDebugMode(JSContext *cx)
 {
-    return cx->compartment->debugMode;
+    return cx->compartment->debugMode();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetDebugMode(JSContext *cx, JSBool debug)
 {
     return JS_SetDebugModeForCompartment(cx, cx->compartment, debug);
 }
 
 JS_PUBLIC_API(void)
 JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug)
 {
-    rt->debugMode = debug;
+    rt->debugMode = !!debug;
 }
 
 namespace js {
 
 void
 ScriptDebugPrologue(JSContext *cx, StackFrame *fp)
 {
     if (fp->isFramePushedByExecute()) {
@@ -154,83 +154,30 @@ ScriptDebugEpilogue(JSContext *cx, Stack
     return ok;
 }
 
 } /* namespace js */
 
 JS_FRIEND_API(JSBool)
 JS_SetDebugModeForCompartment(JSContext *cx, JSCompartment *comp, JSBool debug)
 {
-    if (comp->debugMode == !!debug)
-        return JS_TRUE;
-
-    if (debug) {
-        // 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).
-        for (AllFramesIter i(cx); !i.done(); ++i) {
-            JSScript *script = i.fp()->maybeScript();
-            if (script && script->compartment == comp) {
-                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE);
-                return false;
-            }
-        }
-    }
-
-    // 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->getDebuggees().empty());
-    else
-        Debug::detachFromCompartment(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)
-            continue;
-
-        /*
-         * If compartment entry fails, debug mode is left partially on, leading
-         * to a small performance overhead but no loss of correctness. We set
-         * the debug flags to false so that the caller will not later attempt
-         * to use debugging features.
-         */
-        if (!ac.entered() && !ac.enter(cx, script)) {
-            comp->debugMode = JS_FALSE;
-            return JS_FALSE;
-        }
-
-        mjit::ReleaseScriptCode(cx, script);
-        script->debugMode = !!debug;
-    }
-#endif
-
-    return JS_TRUE;
+    return comp->setDebugModeFromC(cx, !!debug);
 }
 
 JS_FRIEND_API(JSBool)
 js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool singleStep)
 {
     assertSameCompartment(cx, script);
 
 #ifdef JS_METHODJIT
     if (!script->singleStepMode == !singleStep)
         return JS_TRUE;
 #endif
 
-    JS_ASSERT_IF(singleStep, cx->compartment->debugMode);
+    JS_ASSERT_IF(singleStep, cx->compartment->debugMode());
 
 #ifdef JS_METHODJIT
     /* request the next recompile to inject single step interrupts */
     script->singleStepMode = !!singleStep;
 
     js::mjit::JITScript *jit = script->jitNormal ? script->jitNormal : script->jitCtor;
     if (jit && script->singleStepMode != jit->singleStepMode) {
         js::mjit::Recompiler recompiler(cx, script);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2364,17 +2364,17 @@ MarkAndSweep(JSContext *cx, JSCompartmen
     WeakMap::sweep(cx);
 
     js_SweepAtomState(cx);
 
     /* Finalize watch points associated with unreachable objects. */
     js_SweepWatchPoints(cx);
 
     if (!comp)
-        Debug::sweepAll(rt);
+        Debug::sweepAll(cx);
 
     /*
      * We finalize objects before other GC things to ensure that object's finalizer
      * can access them even if they will be freed. Sweep the runtime's property trees
      * after finalizing objects, in case any had watchpoints referencing tree nodes.
      * Do this before sweeping compartments, so that we sweep all shapes in
      * unreachable compartments.
      */
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -348,25 +348,25 @@ ScriptPrologue(JSContext *cx, StackFrame
 
     if (fp->isConstructing()) {
         JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
         if (!obj)
             return false;
         fp->functionThis().setObject(*obj);
     }
 
-    if (cx->compartment->debugMode)
+    if (cx->compartment->debugMode())
         ScriptDebugPrologue(cx, fp);
     return true;
 }
 
 inline bool
 ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok)
 {
-    if (cx->compartment->debugMode)
+    if (cx->compartment->debugMode())
         ok = ScriptDebugEpilogue(cx, fp, ok);
 
     /*
      * If inline-constructing, replace primitive rval with the new object
      * passed in via |this|, and instrument this constructor invocation.
      */
     if (fp->isConstructing() && ok) {
         if (fp->returnValue().isPrimitive())
@@ -377,26 +377,26 @@ ScriptEpilogue(JSContext *cx, StackFrame
     return ok;
 }
 
 inline bool
 ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp)
 {
     if (!fp->isGeneratorFrame())
         return ScriptPrologue(cx, fp);
-    if (cx->compartment->debugMode)
+    if (cx->compartment->debugMode())
         ScriptDebugPrologue(cx, fp);
     return true;
 }
 
 inline bool
 ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok)
 {
     if (!fp->isYielding())
         return ScriptEpilogue(cx, fp, ok);
-    if (cx->compartment->debugMode)
+    if (cx->compartment->debugMode())
         return ScriptDebugEpilogue(cx, fp, ok);
     return ok;
 }
 
 }  /* namespace js */
 
 #endif /* jsinterpinlines_h__ */
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -109,17 +109,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     setElemICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
     callPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
     callSites(CompilerAllocPolicy(cx, *thisFromCtor())), 
     doubleList(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTables(CompilerAllocPolicy(cx, *thisFromCtor())),
     jumpTableOffsets(CompilerAllocPolicy(cx, *thisFromCtor())),
     stubcc(cx, *thisFromCtor(), frame, script),
-    debugMode_(cx->compartment->debugMode),
+    debugMode_(cx->compartment->debugMode()),
 #if defined JS_TRACER
     addTraceHints(cx->traceJitEnabled),
 #endif
     oomInVector(false),
     applyTricks(NoApplyTricks)
 #if defined DEBUG
     ,pcProfile(NULL)
 #endif
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -75,17 +75,17 @@ FrameState::init()
         return true;
     }
 
 #if defined JS_NUNBOX32
     if (!reifier.init(nentries))
         return false;
 #endif
 
-    eval = script->usesEval || cx->compartment->debugMode;
+    eval = script->usesEval || cx->compartment->debugMode();
 
     size_t totalBytes = sizeof(FrameEntry) * nentries +                     // entries[], w/ callee+this
                         sizeof(FrameEntry *) * nentries +                   // tracker.entries
                         (eval
                          ? 0
                          : sizeof(JSPackedBool) * script->nslots) +         // closedVars[]
                         (eval || usesArguments
                          ? 0
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -197,17 +197,17 @@ Recompiler::saveTraps(JITScript *jit, Ve
     return true;
 }
 
 bool
 Recompiler::recompile(StackFrame *fp, Vector<PatchableAddress> &patches,
                       Vector<CallSite> &sites)
 {
     /* If we get this far, the script is live, and we better be safe to re-jit. */
-    JS_ASSERT(cx->compartment->debugMode);
+    JS_ASSERT(cx->compartment->debugMode());
     JS_ASSERT(fp);
 
     Compiler c(cx, fp);
     if (!c.loadOldTraps(sites))
         return false;
     if (c.compile() != Compile_Okay)
         return false;
 
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -248,18 +248,18 @@ GlobalObject::addDebug(JSContext *cx, De
 {
     DebugVector *vec = getOrCreateDebuggers(cx);
     if (!vec)
         return false;
 #ifdef DEBUG
     for (Debug **p = vec->begin(); p != vec->end(); p++)
         JS_ASSERT(*p != dbg);
 #endif
-    if (vec->empty() && !compartment()->addDebuggee(this))
+    if (vec->empty() && !compartment()->addDebuggee(cx, this))
         return false;
     if (!vec->append(dbg)) {
-        compartment()->removeDebuggee(this);
+        compartment()->removeDebuggee(cx, this);
         return false;
     }
     return true;
 }
 
 } // namespace js