author | Jason Orendorff <jorendorff@mozilla.com> |
Fri, 17 Jun 2011 11:12:33 -0500 | |
changeset 74473 | 6885f4c66607709010eae5d47f39b6b22ee3f707 |
parent 74471 | a95ab01cda09f6a4f344ed1898e4ff1bfd65a6d5 (diff) |
parent 74472 | feb8c7a919b366b7d8c5390007cddf9c828a81d0 (current diff) |
child 74474 | ac45a3dbfe3304dca3b9c9b0de4464f1e102a0b3 |
push id | 20986 |
push user | khuey@mozilla.com |
push date | Sun, 14 Aug 2011 11:45:15 +0000 |
treeherder | mozilla-central@2de3cff973b2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 7.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/jsdbg.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsdbg.h | file | annotate | diff | comparison | revisions |
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,33 +1,30 @@ // |jit-test| debug // frame properties throw 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.live, true); - assertEq(frame.type, "call"); - assertEq(frame.this instanceof Object, true); - assertEq(frame.older instanceof Debug.Frame, true); - assertEq(frame.callee instanceof Debug.Object, true); - assertEq(frame.generator, false); - assertEq(frame.constructing, false); - assertEq(frame.arguments.length, 0); - f = frame; - } - }; - } + ")()"); +var f; +Debug(g).hooks = { + debuggerHandler: function (frame) { + assertEq(frame.live, true); + assertEq(frame.type, "call"); + assertEq(frame.this instanceof Object, true); + assertEq(frame.older instanceof Debug.Frame, true); + assertEq(frame.callee instanceof Debug.Object, true); + assertEq(frame.generator, false); + assertEq(frame.constructing, false); + assertEq(frame.arguments.length, 0); + f = frame; + } +}; -(function () { debugger; }).call({}); -assertEq(g.f.live, false); -assertThrowsInstanceOf(function () { g.f.type; }, g.Error); -assertThrowsInstanceOf(function () { g.f.this; }, g.Error); -assertThrowsInstanceOf(function () { g.f.older; }, g.Error); -assertThrowsInstanceOf(function () { g.f.callee; }, g.Error); -assertThrowsInstanceOf(function () { g.f.generator; }, g.Error); -assertThrowsInstanceOf(function () { g.f.constructing; }, g.Error); -assertThrowsInstanceOf(function () { g.f.arguments; }, g.Error); +g.eval("(function () { debugger; }).call({});"); +assertEq(f.live, false); +assertThrowsInstanceOf(function () { f.type; }, Error); +assertThrowsInstanceOf(function () { f.this; }, Error); +assertThrowsInstanceOf(function () { f.older; }, Error); +assertThrowsInstanceOf(function () { f.callee; }, Error); +assertThrowsInstanceOf(function () { f.generator; }, Error); +assertThrowsInstanceOf(function () { f.constructing; }, Error); +assertThrowsInstanceOf(function () { f.arguments; }, 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 @@ -528,17 +528,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() @@ -595,8 +595,71 @@ 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->stack.space()); !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 enabledBefore = debugMode(); + bool enabledAfter = (debugModeBits & ~uintN(DebugFromC)) || b; + + // 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). + // + // We do allow disabling debug mode while scripts are on the stack. In + // that case the debug-mode code for those scripts remains, so subsequently + // hooks may be called erroneously, even though debug mode is supposedly + // off, and we have to live with it. + // + bool onStack = false; + if (enabledBefore != enabledAfter) { + onStack = haveScriptsOnStack(cx); + if (b && onStack) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NOT_IDLE); + return false; + } + } + + debugModeBits = (debugModeBits & ~uintN(DebugFromC)) | (b * DebugFromC); + JS_ASSERT(debugMode() == enabledAfter); + if (enabledBefore != enabledAfter && !onStack) + 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 @@ -456,18 +456,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(); @@ -522,23 +525,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) @@ -591,70 +585,73 @@ Debug::trace(JSTracer *trc) MarkObject(trc, *frameobj, "live Debug.Frame"); } // Trace the referent -> Debug.Object weak map. objects.trace(trc); } 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()) { // 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 */ @@ -790,17 +787,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); @@ -899,32 +896,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(); @@ -932,38 +925,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); @@ -995,17 +993,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 @@ -103,17 +103,18 @@ class Debug { // // Using CCWs for keys when it's really their referents' liveness that determines the // table entry's liveness is delicate; see comments on ObjectMapMarkPolicy. typedef WeakMap<JSObject *, JSObject *, DefaultHasher<JSObject *>, ObjectMapMarkPolicy> ObjectWeakMap; ObjectWeakMap 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 traceObject(JSTracer *trc, JSObject *obj); @@ -155,17 +156,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, @@ -174,19 +174,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->stack.space()); !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 @@ -2324,17 +2324,17 @@ MarkAndSweep(JSContext *cx, JSCompartmen WeakMapBase::sweepAll(&gcmarker); 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) { }
--- 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
--- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -2553,28 +2553,18 @@ nsXPConnect::CheckForDebugMode(JSRuntime JSCompartment *comp = *p; if (!comp->principals) { /* Ignore special compartments (atoms, JSD compartments) */ continue; } /* ParticipatesInCycleCollection means "on the main thread" */ if (xpc::CompartmentParticipatesInCycleCollection(cx, comp)) { - if (gDesiredDebugMode) { - if (!JS_SetDebugModeForCompartment(cx, comp, JS_TRUE)) - goto fail; - } else { - /* - * Debugging may be turned off with live scripts, so just - * mark future scripts to be compiled into non-debug mode. - * Existing scripts will continue to call JSD callbacks, - * which will have no effect. - */ - comp->debugMode = JS_FALSE; - } + if (!JS_SetDebugModeForCompartment(cx, comp, gDesiredDebugMode)) + goto fail; } } } if (gDesiredDebugMode) { rv = jsds->ActivateDebugger(rt); }