From 6a958619a0dece91d74ee828e4e71d0ab7219026 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 2 Jun 2011 21:58:46 -0500 Subject: [PATCH] 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. --HG-- rename : js/src/jit-test/tests/debug/Debug-ctor.js => js/src/jit-test/tests/debug/Debug-ctor-01.js --- .../debug/{Debug-ctor.js => Debug-ctor-01.js} | 13 --- js/src/jit-test/tests/debug/Debug-ctor-02.js | 17 ++++ js/src/jit-test/tests/debug/Debug-ctor-03.js | 6 ++ .../tests/debug/Debug-debuggees-01.js | 1 - .../tests/debug/Debug-debuggees-02.js | 3 +- .../tests/debug/Debug-debuggees-03.js | 1 - .../tests/debug/Debug-debuggees-04.js | 1 - .../tests/debug/Debug-debuggees-05.js | 1 - .../tests/debug/Debug-debuggees-06.js | 1 - .../tests/debug/Debug-debuggees-07.js | 1 - .../tests/debug/Debug-debuggees-08.js | 1 - .../tests/debug/Debug-debuggees-10.js | 2 - .../tests/debug/Debug-debuggees-12.js | 2 - .../tests/debug/Debug-debuggees-13.js | 2 - .../tests/debug/Debug-debuggees-14.js | 18 ++-- .../jit-test/tests/debug/Debug-enabled-01.js | 2 - .../tests/debug/Debug-getYoungestFrame-01.js | 1 - .../tests/debug/Debug-getYoungestFrame-02.js | 1 - js/src/jit-test/tests/debug/Debug-multi-01.js | 1 - js/src/jit-test/tests/debug/Debug-multi-02.js | 1 - js/src/jit-test/tests/debug/Debug-multi-03.js | 1 - js/src/jit-test/tests/debug/Frame-01.js | 91 ++++++------------- js/src/jit-test/tests/debug/Frame-02.js | 47 ++++------ js/src/jit-test/tests/debug/Frame-03.js | 37 ++++---- .../tests/debug/Frame-arguments-01.js | 1 - .../tests/debug/Frame-arguments-02.js | 1 - .../tests/debug/Frame-arguments-03.js | 1 - .../tests/debug/Frame-arguments-04.js | 1 - .../tests/debug/Frame-arguments-05.js | 1 - .../tests/debug/Frame-arguments-06.js | 1 - js/src/jit-test/tests/debug/Frame-eval-01.js | 1 - js/src/jit-test/tests/debug/Frame-eval-02.js | 1 - js/src/jit-test/tests/debug/Frame-eval-03.js | 1 - js/src/jit-test/tests/debug/Frame-eval-04.js | 1 - js/src/jit-test/tests/debug/Frame-eval-05.js | 1 - js/src/jit-test/tests/debug/Frame-eval-06.js | 1 - js/src/jit-test/tests/debug/Frame-eval-07.js | 1 - js/src/jit-test/tests/debug/Frame-eval-08.js | 1 - js/src/jit-test/tests/debug/Frame-eval-09.js | 1 - js/src/jit-test/tests/debug/Frame-eval-10.js | 1 - .../tests/debug/Frame-evalWithBindings-01.js | 1 - .../tests/debug/Frame-evalWithBindings-02.js | 2 - .../tests/debug/Frame-evalWithBindings-03.js | 2 - .../tests/debug/Frame-evalWithBindings-04.js | 2 - .../tests/debug/Frame-evalWithBindings-05.js | 1 - .../tests/debug/Frame-evalWithBindings-06.js | 2 - .../tests/debug/Frame-evalWithBindings-07.js | 2 - .../tests/debug/Frame-evalWithBindings-08.js | 2 - .../tests/debug/Frame-evalWithBindings-09.js | 2 - .../tests/debug/Frame-evalWithBindings-10.js | 2 - .../jit-test/tests/debug/Frame-identity-04.js | 1 - js/src/jit-test/tests/debug/Frame-live-03.js | 25 +++-- js/src/jit-test/tests/debug/Frame-live-04.js | 28 ++++++ js/src/jit-test/tests/debug/Frame-older-01.js | 37 ++++---- js/src/jit-test/tests/debug/Frame-this-01.js | 1 - js/src/jit-test/tests/debug/Frame-this-02.js | 1 - js/src/jit-test/tests/debug/Frame-this-03.js | 1 - js/src/jit-test/tests/debug/Frame-this-04.js | 1 - js/src/jit-test/tests/debug/Object-01.js | 1 - .../jit-test/tests/debug/Object-apply-01.js | 1 - .../jit-test/tests/debug/Object-apply-02.js | 1 - .../jit-test/tests/debug/Object-apply-03.js | 1 - .../jit-test/tests/debug/Object-apply-04.js | 1 - .../jit-test/tests/debug/Object-callable.js | 1 - js/src/jit-test/tests/debug/Object-class.js | 4 +- .../tests/debug/Object-identity-01.js | 2 - .../tests/debug/Object-identity-02.js | 2 - .../tests/debug/Object-identity-03.js | 1 - js/src/jit-test/tests/debug/Object-name-01.js | 2 - js/src/jit-test/tests/debug/Object-name-02.js | 2 - .../tests/debug/Object-parameterNames.js | 1 - js/src/jit-test/tests/debug/Object-proto.js | 2 +- js/src/jit-test/tests/debug/dispatch-01.js | 1 - js/src/jit-test/tests/debug/dispatch-02.js | 1 - js/src/jit-test/tests/debug/gc-01.js | 1 - js/src/jit-test/tests/debug/gc-02.js | 1 - js/src/jit-test/tests/debug/gc-03.js | 1 - js/src/jit-test/tests/debug/gc-04.js | 1 - js/src/jit-test/tests/debug/gc-05.js | 1 - js/src/jit-test/tests/debug/gc-06.js | 2 - js/src/jit-test/tests/debug/gc-07.js | 2 - js/src/jit-test/tests/debug/gc-08.js | 1 - .../tests/debug/hooks-debuggerHandler-01.js | 7 +- .../tests/debug/hooks-debuggerHandler-02.js | 1 - .../tests/debug/hooks-debuggerHandler-03.js | 1 - js/src/jit-test/tests/debug/hooks-deleted.js | 1 - js/src/jit-test/tests/debug/hooks-throw-01.js | 1 - js/src/jit-test/tests/debug/hooks-throw-04.js | 2 - js/src/jit-test/tests/debug/resumption-01.js | 2 - js/src/jit-test/tests/debug/resumption-02.js | 3 - js/src/jit-test/tests/debug/surfaces-01.js | 2 +- js/src/jit-test/tests/debug/surfaces-02.js | 1 - js/src/jit-test/tests/debug/surfaces-03.js | 1 - .../tests/debug/uncaughtExceptionHook-01.js | 1 - .../tests/debug/uncaughtExceptionHook-02.js | 1 - .../uncaughtExceptionHook-resumption-01.js | 1 - .../uncaughtExceptionHook-resumption-02.js | 1 - .../uncaughtExceptionHook-resumption-03.js | 1 - js/src/jsanalyze.cpp | 4 +- js/src/jsapi-tests/testDebugger.cpp | 2 +- js/src/jsapi.cpp | 2 +- js/src/jscntxt.h | 2 +- js/src/jscompartment.cpp | 53 ++++++++++- js/src/jscompartment.h | 54 +++++++++-- js/src/jsdbg.cpp | 68 +++++++------- js/src/jsdbg.h | 10 +- js/src/jsdbgapi.cpp | 61 +------------ js/src/jsgc.cpp | 2 +- js/src/jsinterpinlines.h | 8 +- js/src/methodjit/Compiler.cpp | 2 +- js/src/methodjit/FrameState.cpp | 2 +- js/src/methodjit/Retcon.cpp | 2 +- js/src/vm/GlobalObject.cpp | 4 +- 113 files changed, 310 insertions(+), 408 deletions(-) rename js/src/jit-test/tests/debug/{Debug-ctor.js => Debug-ctor-01.js} (71%) create mode 100644 js/src/jit-test/tests/debug/Debug-ctor-02.js create mode 100644 js/src/jit-test/tests/debug/Debug-ctor-03.js create mode 100644 js/src/jit-test/tests/debug/Frame-live-04.js diff --git a/js/src/jit-test/tests/debug/Debug-ctor.js b/js/src/jit-test/tests/debug/Debug-ctor-01.js similarity index 71% rename from js/src/jit-test/tests/debug/Debug-ctor.js rename to js/src/jit-test/tests/debug/Debug-ctor-01.js index 6d07a045858..cd95aad4790 100644 --- a/js/src/jit-test/tests/debug/Debug-ctor.js +++ b/js/src/jit-test/tests/debug/Debug-ctor-01.js @@ -1,5 +1,3 @@ -// |jit-test| debug - load(libdir + 'asserts.js'); // Debug rejects arguments that aren't cross-compartment wrappers. @@ -21,14 +19,3 @@ 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); diff --git a/js/src/jit-test/tests/debug/Debug-ctor-02.js b/js/src/jit-test/tests/debug/Debug-ctor-02.js new file mode 100644 index 00000000000..eadafd84e04 --- /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); diff --git a/js/src/jit-test/tests/debug/Debug-ctor-03.js b/js/src/jit-test/tests/debug/Debug-ctor-03.js new file mode 100644 index 00000000000..0b4d1e9aecb --- /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);"); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-01.js b/js/src/jit-test/tests/debug/Debug-debuggees-01.js index f2a2893898e..770d2e35992 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-01.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // A Debug object created with no argument initially has no debuggees. var dbg = new Debug; var debuggees = dbg.getDebuggees(); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-02.js b/js/src/jit-test/tests/debug/Debug-debuggees-02.js index 3a4d5ea200e..a606625bab0 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-02.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-02.js @@ -1,5 +1,4 @@ -// |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'); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-03.js b/js/src/jit-test/tests/debug/Debug-debuggees-03.js index 36b931a8fd2..12e3857dcc2 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-03.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debug hooks fire based on debuggees. var g1 = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-04.js b/js/src/jit-test/tests/debug/Debug-debuggees-04.js index 725422452e5..5f78d264c13 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-04.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // hasDebuggee tests. var g1 = newGlobal('new-compartment'), g1w; diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-05.js b/js/src/jit-test/tests/debug/Debug-debuggees-05.js index 7599e25b382..f8aa79cf882 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-05.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-05.js @@ -1,4 +1,3 @@ -// |jit-test| debug // addDebuggee returns different Debug.Object wrappers for different Debug objects. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-06.js b/js/src/jit-test/tests/debug/Debug-debuggees-06.js index ccc2614f99a..13a82dc8503 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-06.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-06.js @@ -1,4 +1,3 @@ -// |jit-test| debug // {has,add,remove}Debuggee throw a TypeError if the argument is invalid. load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-07.js b/js/src/jit-test/tests/debug/Debug-debuggees-07.js index 691dc991a4e..6d38a878819 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-07.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-07.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Handle proto-less objects passed to addDebuggee. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-08.js b/js/src/jit-test/tests/debug/Debug-debuggees-08.js index a81e9fa4d9d..facf01abe42 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-08.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-08.js @@ -1,4 +1,3 @@ -// |jit-test| debug // addDebuggee(obj), where obj is not global, adds obj's global. // Adding a debuggee more than once is redundant. diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-10.js b/js/src/jit-test/tests/debug/Debug-debuggees-10.js index 4c3038ffd42..8dcaa1aa02a 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-10.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-10.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-12.js b/js/src/jit-test/tests/debug/Debug-debuggees-12.js index 5e9be612871..944aa854927 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-12.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-12.js @@ -1,6 +1,4 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-13.js b/js/src/jit-test/tests/debug/Debug-debuggees-13.js index c71e8978a41..0aed7aa254d 100644 --- a/js/src/jit-test/tests/debug/Debug-debuggees-13.js +++ b/js/src/jit-test/tests/debug/Debug-debuggees-13.js @@ -1,6 +1,4 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Debug-debuggees-14.js b/js/src/jit-test/tests/debug/Debug-debuggees-14.js index 6e19e218273..2329bb5641d 100644 --- 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);"); diff --git a/js/src/jit-test/tests/debug/Debug-enabled-01.js b/js/src/jit-test/tests/debug/Debug-enabled-01.js index e4041379778..9e4ad316961 100644 --- a/js/src/jit-test/tests/debug/Debug-enabled-01.js +++ b/js/src/jit-test/tests/debug/Debug-enabled-01.js @@ -1,5 +1,3 @@ -// |jit-test| debug - var desc = Object.getOwnPropertyDescriptor(Debug.prototype, "enabled"); assertEq(typeof desc.get, 'function'); assertEq(typeof desc.set, 'function'); diff --git a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js index 38d58cc1b6d..4cc56970f02 100644 --- a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js +++ b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // getYoungestFrame basics. load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js index fdffa6aa401..53be1a4e3d7 100644 --- a/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js +++ b/js/src/jit-test/tests/debug/Debug-getYoungestFrame-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Hooks and Debug.prototype.getYoungestFrame produce the same Frame object. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Debug-multi-01.js b/js/src/jit-test/tests/debug/Debug-multi-01.js index 27ba90a890e..5b9255df418 100644 --- a/js/src/jit-test/tests/debug/Debug-multi-01.js +++ b/js/src/jit-test/tests/debug/Debug-multi-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // When there are multiple debuggers, their hooks are called in order. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Debug-multi-02.js b/js/src/jit-test/tests/debug/Debug-multi-02.js index 59a448df3f6..a24f86aa3cb 100644 --- a/js/src/jit-test/tests/debug/Debug-multi-02.js +++ b/js/src/jit-test/tests/debug/Debug-multi-02.js @@ -1,4 +1,3 @@ -// |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. diff --git a/js/src/jit-test/tests/debug/Debug-multi-03.js b/js/src/jit-test/tests/debug/Debug-multi-03.js index 1a495149ed1..ed78138021a 100644 --- a/js/src/jit-test/tests/debug/Debug-multi-03.js +++ b/js/src/jit-test/tests/debug/Debug-multi-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Q: But who shall debug the debuggers? A: jimb var log = ''; diff --git a/js/src/jit-test/tests/debug/Frame-01.js b/js/src/jit-test/tests/debug/Frame-01.js index 1ed41e146c7..ee9b502169e 100644 --- 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); diff --git a/js/src/jit-test/tests/debug/Frame-02.js b/js/src/jit-test/tests/debug/Frame-02.js index 0ebad76a26e..99305eee97e 100644 --- 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); diff --git a/js/src/jit-test/tests/debug/Frame-03.js b/js/src/jit-test/tests/debug/Frame-03.js index 55ef8d86e78..3d19ca6e2a7 100644 --- 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); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-01.js b/js/src/jit-test/tests/debug/Frame-arguments-01.js index 7c6099b5b31..b243a94abb7 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-01.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Frame.prototype.arguments with primitive values var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-02.js b/js/src/jit-test/tests/debug/Frame-arguments-02.js index 2e1a6418707..ae6deb2a12e 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-02.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Object arguments. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-03.js b/js/src/jit-test/tests/debug/Frame-arguments-03.js index ee7d7a6d4d1..8284ebeb87c 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-03.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Destructuring arguments. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-04.js b/js/src/jit-test/tests/debug/Frame-arguments-04.js index 57b5457149d..c618e6b3e89 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-04.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // frame.arguments works for all live frames var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-05.js b/js/src/jit-test/tests/debug/Frame-arguments-05.js index 57ee49de83a..ca2b161d924 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-05.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-05.js @@ -1,4 +1,3 @@ -// |jit-test| debug // frame.arguments is "live" (it reflects assignments to arguments). var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-arguments-06.js b/js/src/jit-test/tests/debug/Frame-arguments-06.js index d9cb415ff9c..e8957888b6c 100644 --- a/js/src/jit-test/tests/debug/Frame-arguments-06.js +++ b/js/src/jit-test/tests/debug/Frame-arguments-06.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Test extracting frame.arguments element getters and calling them in // various awkward ways. diff --git a/js/src/jit-test/tests/debug/Frame-eval-01.js b/js/src/jit-test/tests/debug/Frame-eval-01.js index 6d57b1ffb4e..57b6f8c8224 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-01.js +++ b/js/src/jit-test/tests/debug/Frame-eval-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // simplest possible test of Debug.Frame.prototype.eval var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-02.js b/js/src/jit-test/tests/debug/Frame-eval-02.js index 5cf5b04285b..daaacc1afce 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-02.js +++ b/js/src/jit-test/tests/debug/Frame-eval-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // frame.eval() throws if frame is not live load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Frame-eval-03.js b/js/src/jit-test/tests/debug/Frame-eval-03.js index 51410c382ff..13f1412627b 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-03.js +++ b/js/src/jit-test/tests/debug/Frame-eval-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Test eval-ing names in a topmost script frame load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Frame-eval-04.js b/js/src/jit-test/tests/debug/Frame-eval-04.js index 48bb1b05ee3..73017cfe5d7 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-04.js +++ b/js/src/jit-test/tests/debug/Frame-eval-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // frame.eval SyntaxErrors are reflected, not thrown var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-05.js b/js/src/jit-test/tests/debug/Frame-eval-05.js index 8e48b20832c..986a18622eb 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-05.js +++ b/js/src/jit-test/tests/debug/Frame-eval-05.js @@ -1,4 +1,3 @@ -// |jit-test| debug // var declarations in strict frame.eval do not modify the frame var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-06.js b/js/src/jit-test/tests/debug/Frame-eval-06.js index e799219ef2b..2a573f553c5 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-06.js +++ b/js/src/jit-test/tests/debug/Frame-eval-06.js @@ -1,4 +1,3 @@ -// |jit-test| debug // frame.eval throws if frame is a generator frame that isn't currently on the stack load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Frame-eval-07.js b/js/src/jit-test/tests/debug/Frame-eval-07.js index 8f7fdd2a05a..fba815527a7 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-07.js +++ b/js/src/jit-test/tests/debug/Frame-eval-07.js @@ -1,4 +1,3 @@ -// |jit-test| debug // test frame.eval in non-top frames var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-08.js b/js/src/jit-test/tests/debug/Frame-eval-08.js index a1405b96301..ae01b07ca48 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-08.js +++ b/js/src/jit-test/tests/debug/Frame-eval-08.js @@ -1,4 +1,3 @@ -// |jit-test| debug // The arguments can escape from a function via a debugging hook. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-09.js b/js/src/jit-test/tests/debug/Frame-eval-09.js index e8081e9df92..50918fb894e 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-09.js +++ b/js/src/jit-test/tests/debug/Frame-eval-09.js @@ -1,4 +1,3 @@ -// |jit-test| debug // assigning to local variables in frame.eval code var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-eval-10.js b/js/src/jit-test/tests/debug/Frame-eval-10.js index 694631c3690..5cd3d2641fa 100644 --- a/js/src/jit-test/tests/debug/Frame-eval-10.js +++ b/js/src/jit-test/tests/debug/Frame-eval-10.js @@ -1,4 +1,3 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js index ebaf01fc08e..8892d411ae0 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // evalWithBindings basics var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js index bc23b258ab3..29c10167501 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-02.js @@ -1,6 +1,4 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js index e7f670addff..13b2e82bb8e 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-03.js @@ -1,6 +1,4 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js index 270b67258c0..e0f59736733 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-04.js @@ -1,6 +1,4 @@ -// |jit-test| debug // evalWithBindings works on non-top frames. - var g = newGlobal('new-compartment'); var dbg = new Debug(g); var f1; diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js index 88fd99263a0..45b192549eb 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-05.js @@ -1,4 +1,3 @@ -// |jit-test| debug // evalWithBindings code can assign to the bindings. var g = newGlobal('new-compartment'); var dbg = new Debug(g); diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js index d6c8a7d1745..6d3abea9662 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-06.js @@ -1,6 +1,4 @@ -// |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 = { diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js index c2508ebece4..3f47e37e3c8 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-07.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js index 9168bb31932..a59d672eea9 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-08.js @@ -1,6 +1,4 @@ -// |jit-test| debug // evalWithBindings ignores non-enumerable and non-own properties. - var g = newGlobal('new-compartment'); var dbg = new Debug(g); var hits = 0; diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js index d98574f5c5b..8f761572cb2 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-09.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js b/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js index 9dd30432ae5..5407c8e9c28 100644 --- a/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js +++ b/js/src/jit-test/tests/debug/Frame-evalWithBindings-10.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Frame-identity-04.js b/js/src/jit-test/tests/debug/Frame-identity-04.js index 8cf70f95a5b..8e10b73f823 100644 --- a/js/src/jit-test/tests/debug/Frame-identity-04.js +++ b/js/src/jit-test/tests/debug/Frame-identity-04.js @@ -1,4 +1,3 @@ -// |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. diff --git a/js/src/jit-test/tests/debug/Frame-live-03.js b/js/src/jit-test/tests/debug/Frame-live-03.js index 56cfcc966cf..e2c3f31bb44 100644 --- a/js/src/jit-test/tests/debug/Frame-live-03.js +++ b/js/src/jit-test/tests/debug/Frame-live-03.js @@ -4,18 +4,15 @@ 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); diff --git a/js/src/jit-test/tests/debug/Frame-live-04.js b/js/src/jit-test/tests/debug/Frame-live-04.js new file mode 100644 index 00000000000..4fd6275ddfe --- /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); diff --git a/js/src/jit-test/tests/debug/Frame-older-01.js b/js/src/jit-test/tests/debug/Frame-older-01.js index e2419eaeacf..bde3b4cbe8c 100644 --- a/js/src/jit-test/tests/debug/Frame-older-01.js +++ b/js/src/jit-test/tests/debug/Frame-older-01.js @@ -2,24 +2,21 @@ // 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"); diff --git a/js/src/jit-test/tests/debug/Frame-this-01.js b/js/src/jit-test/tests/debug/Frame-this-01.js index 862c3e88e61..5eb81aa5935 100644 --- a/js/src/jit-test/tests/debug/Frame-this-01.js +++ b/js/src/jit-test/tests/debug/Frame-this-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Frame.prototype.this in strict-mode functions, with primitive values var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-this-02.js b/js/src/jit-test/tests/debug/Frame-this-02.js index c87b10656a7..5ce0d4b4781 100644 --- a/js/src/jit-test/tests/debug/Frame-this-02.js +++ b/js/src/jit-test/tests/debug/Frame-this-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Frame.prototype.this in strict direct eval frames var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Frame-this-03.js b/js/src/jit-test/tests/debug/Frame-this-03.js index c98b426fdcf..78454d20326 100644 --- a/js/src/jit-test/tests/debug/Frame-this-03.js +++ b/js/src/jit-test/tests/debug/Frame-this-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Frame.prototype.this in non-strict-mode functions, with primitive values function classOf(obj) { diff --git a/js/src/jit-test/tests/debug/Frame-this-04.js b/js/src/jit-test/tests/debug/Frame-this-04.js index 913320262ed..2efbb397615 100644 --- a/js/src/jit-test/tests/debug/Frame-this-04.js +++ b/js/src/jit-test/tests/debug/Frame-this-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debug.Frame.prototype.this in functions, with object values function classOf(obj) { diff --git a/js/src/jit-test/tests/debug/Object-01.js b/js/src/jit-test/tests/debug/Object-01.js index b8dccd3bf8c..7bc74f431b7 100644 --- a/js/src/jit-test/tests/debug/Object-01.js +++ b/js/src/jit-test/tests/debug/Object-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debug.Object basics var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Object-apply-01.js b/js/src/jit-test/tests/debug/Object-apply-01.js index 8598d38669f..30e0d6fe5b4 100644 --- a/js/src/jit-test/tests/debug/Object-apply-01.js +++ b/js/src/jit-test/tests/debug/Object-apply-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // tests calling script functions via Debug.Object.prototype.apply/call load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Object-apply-02.js b/js/src/jit-test/tests/debug/Object-apply-02.js index ddd758c23ed..1eef67edb40 100644 --- a/js/src/jit-test/tests/debug/Object-apply-02.js +++ b/js/src/jit-test/tests/debug/Object-apply-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // tests calling native functions via Debug.Object.prototype.apply/call load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/Object-apply-03.js b/js/src/jit-test/tests/debug/Object-apply-03.js index f1a24b215de..2bff00c74be 100644 --- a/js/src/jit-test/tests/debug/Object-apply-03.js +++ b/js/src/jit-test/tests/debug/Object-apply-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // reentering the debugger several times via debuggerHandler and apply/call on a single stack var g = newGlobal("new-compartment"); diff --git a/js/src/jit-test/tests/debug/Object-apply-04.js b/js/src/jit-test/tests/debug/Object-apply-04.js index 78b69463895..6da2e3b918e 100644 --- a/js/src/jit-test/tests/debug/Object-apply-04.js +++ b/js/src/jit-test/tests/debug/Object-apply-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debug.Object.prototype.apply/call works with function proxies var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Object-callable.js b/js/src/jit-test/tests/debug/Object-callable.js index a8e9056e418..49313643f90 100644 --- a/js/src/jit-test/tests/debug/Object-callable.js +++ b/js/src/jit-test/tests/debug/Object-callable.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Test Debug.Object.prototype.callable. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Object-class.js b/js/src/jit-test/tests/debug/Object-class.js index 33493efcff2..529137da665 100644 --- a/js/src/jit-test/tests/debug/Object-class.js +++ b/js/src/jit-test/tests/debug/Object-class.js @@ -1,5 +1,4 @@ -// |jit-test| debug - +// Basic tests for Debug.Object.prototype.class. var g = newGlobal('new-compartment'); var dbg = new Debug(g); var hits = 0; @@ -14,6 +13,5 @@ dbg.hooks = { hits++; } }; - g.eval("(function () { debugger; })(Object.prototype, [], eval, new Date, Proxy.create({}));"); assertEq(hits, 1); diff --git a/js/src/jit-test/tests/debug/Object-identity-01.js b/js/src/jit-test/tests/debug/Object-identity-01.js index 957cdd92bdd..d8f4c5a9cd3 100644 --- a/js/src/jit-test/tests/debug/Object-identity-01.js +++ b/js/src/jit-test/tests/debug/Object-identity-01.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Object-identity-02.js b/js/src/jit-test/tests/debug/Object-identity-02.js index c082874472e..a58d73956bb 100644 --- a/js/src/jit-test/tests/debug/Object-identity-02.js +++ b/js/src/jit-test/tests/debug/Object-identity-02.js @@ -1,6 +1,4 @@ -// |jit-test| debug // Different objects get different Debug.Object wrappers. - var g = newGlobal('new-compartment'); var dbg = Debug(g); var hits = 0; diff --git a/js/src/jit-test/tests/debug/Object-identity-03.js b/js/src/jit-test/tests/debug/Object-identity-03.js index 75e4abbd145..81feb786263 100644 --- a/js/src/jit-test/tests/debug/Object-identity-03.js +++ b/js/src/jit-test/tests/debug/Object-identity-03.js @@ -1,4 +1,3 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/Object-name-01.js b/js/src/jit-test/tests/debug/Object-name-01.js index b59eca69dfb..d59e36237ff 100644 --- a/js/src/jit-test/tests/debug/Object-name-01.js +++ b/js/src/jit-test/tests/debug/Object-name-01.js @@ -1,6 +1,4 @@ -// |jit-test| debug // Debug.Object.prototype.name - var g = newGlobal('new-compartment'); var dbg = Debug(g); var name, hits; diff --git a/js/src/jit-test/tests/debug/Object-name-02.js b/js/src/jit-test/tests/debug/Object-name-02.js index 3096335dd15..b81c155476d 100644 --- a/js/src/jit-test/tests/debug/Object-name-02.js +++ b/js/src/jit-test/tests/debug/Object-name-02.js @@ -1,6 +1,4 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/Object-parameterNames.js b/js/src/jit-test/tests/debug/Object-parameterNames.js index e89343626c8..0e365bfa0d2 100644 --- a/js/src/jit-test/tests/debug/Object-parameterNames.js +++ b/js/src/jit-test/tests/debug/Object-parameterNames.js @@ -1,4 +1,3 @@ -// |jit-test| debug load(libdir + 'array-compare.js'); var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/Object-proto.js b/js/src/jit-test/tests/debug/Object-proto.js index caecdcf17d9..511ce8343c7 100644 --- a/js/src/jit-test/tests/debug/Object-proto.js +++ b/js/src/jit-test/tests/debug/Object-proto.js @@ -1,4 +1,4 @@ -// |jit-test| debug +// Debug.Object.prototype.proto var g = newGlobal('new-compartment'); var dbgeval = function () { var dbg = new Debug(g); diff --git a/js/src/jit-test/tests/debug/dispatch-01.js b/js/src/jit-test/tests/debug/dispatch-01.js index 6239a91d490..2a1c3bf875c 100644 --- a/js/src/jit-test/tests/debug/dispatch-01.js +++ b/js/src/jit-test/tests/debug/dispatch-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Test removing hooks during dispatch. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/dispatch-02.js b/js/src/jit-test/tests/debug/dispatch-02.js index e5b81511a36..0644fb3e216 100644 --- a/js/src/jit-test/tests/debug/dispatch-02.js +++ b/js/src/jit-test/tests/debug/dispatch-02.js @@ -1,4 +1,3 @@ -// |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. diff --git a/js/src/jit-test/tests/debug/gc-01.js b/js/src/jit-test/tests/debug/gc-01.js index f0526bbb922..6844ca1e2e9 100644 --- a/js/src/jit-test/tests/debug/gc-01.js +++ b/js/src/jit-test/tests/debug/gc-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debuggers with enabled hooks should not be GC'd even if they are otherwise // unreachable. diff --git a/js/src/jit-test/tests/debug/gc-02.js b/js/src/jit-test/tests/debug/gc-02.js index a4ff9f9d731..2765f6ac691 100644 --- a/js/src/jit-test/tests/debug/gc-02.js +++ b/js/src/jit-test/tests/debug/gc-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Dispatching an event to a debugger must keep enough of it gc-alive to avoid // crashing. diff --git a/js/src/jit-test/tests/debug/gc-03.js b/js/src/jit-test/tests/debug/gc-03.js index d6e1d57e198..27b95606013 100644 --- a/js/src/jit-test/tests/debug/gc-03.js +++ b/js/src/jit-test/tests/debug/gc-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Storing a property on a Debug.Object protects it from GC as long as the // referent is alive. diff --git a/js/src/jit-test/tests/debug/gc-04.js b/js/src/jit-test/tests/debug/gc-04.js index 8ea9a6f6d15..11031b54586 100644 --- a/js/src/jit-test/tests/debug/gc-04.js +++ b/js/src/jit-test/tests/debug/gc-04.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Storing a Debug.Object as a key in a WeakMap protects it from GC as long as // the referent is alive. diff --git a/js/src/jit-test/tests/debug/gc-05.js b/js/src/jit-test/tests/debug/gc-05.js index df3076899b5..324e2968f26 100644 --- a/js/src/jit-test/tests/debug/gc-05.js +++ b/js/src/jit-test/tests/debug/gc-05.js @@ -1,4 +1,3 @@ -// |jit-test| debug // If a Debug survives its debuggee, its object cache must still be swept. var g2arr = []; // non-debuggee globals diff --git a/js/src/jit-test/tests/debug/gc-06.js b/js/src/jit-test/tests/debug/gc-06.js index 92d0d895c16..522bcb4fe62 100644 --- a/js/src/jit-test/tests/debug/gc-06.js +++ b/js/src/jit-test/tests/debug/gc-06.js @@ -1,6 +1,4 @@ -// |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')); diff --git a/js/src/jit-test/tests/debug/gc-07.js b/js/src/jit-test/tests/debug/gc-07.js index 2b1de3e8b37..79e71d1d51a 100644 --- a/js/src/jit-test/tests/debug/gc-07.js +++ b/js/src/jit-test/tests/debug/gc-07.js @@ -1,6 +1,4 @@ -// |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; diff --git a/js/src/jit-test/tests/debug/gc-08.js b/js/src/jit-test/tests/debug/gc-08.js index 4d5e16a39c4..5c9cfa40a9a 100644 --- a/js/src/jit-test/tests/debug/gc-08.js +++ b/js/src/jit-test/tests/debug/gc-08.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debuggers with enabled throw hooks should not be GC'd even if they are // otherwise unreachable. diff --git a/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js b/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js index 9ae99dbf8ff..fcabbee1185 100644 --- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js +++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-01.js @@ -1,13 +1,8 @@ -// |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); diff --git a/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js b/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js index 0f61e6c6a06..e79f8ecdcda 100644 --- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js +++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Activity in the debugger compartment should not trigger debug hooks. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js b/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js index 2c5af905cc0..2c4cecdeb1f 100644 --- a/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js +++ b/js/src/jit-test/tests/debug/hooks-debuggerHandler-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // A debugger statement in a debuggerHandler should not reenter. var g = newGlobal('new-compartment'); diff --git a/js/src/jit-test/tests/debug/hooks-deleted.js b/js/src/jit-test/tests/debug/hooks-deleted.js index 27ee53a6f03..eef212289bb 100644 --- a/js/src/jit-test/tests/debug/hooks-deleted.js +++ b/js/src/jit-test/tests/debug/hooks-deleted.js @@ -1,4 +1,3 @@ -// |jit-test| debug // If a hook is deleted after setHooks or overwritten with a primitive, it // simply isn't called. diff --git a/js/src/jit-test/tests/debug/hooks-throw-01.js b/js/src/jit-test/tests/debug/hooks-throw-01.js index 3ab429af98f..8757bf3ac03 100644 --- a/js/src/jit-test/tests/debug/hooks-throw-01.js +++ b/js/src/jit-test/tests/debug/hooks-throw-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Basic throw hook test. load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/hooks-throw-04.js b/js/src/jit-test/tests/debug/hooks-throw-04.js index f5343caee6a..d5e6e38cd22 100644 --- a/js/src/jit-test/tests/debug/hooks-throw-04.js +++ b/js/src/jit-test/tests/debug/hooks-throw-04.js @@ -1,6 +1,4 @@ -// |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 = ''; diff --git a/js/src/jit-test/tests/debug/resumption-01.js b/js/src/jit-test/tests/debug/resumption-01.js index 5438dfc6d0a..7f4cab188f5 100644 --- a/js/src/jit-test/tests/debug/resumption-01.js +++ b/js/src/jit-test/tests/debug/resumption-01.js @@ -1,10 +1,8 @@ -// |jit-test| debug // Simple {throw:} resumption. load(libdir + "asserts.js"); var g = newGlobal('new-compartment'); - var dbg = Debug(g); dbg.hooks = { debuggerHandler: function (stack) { diff --git a/js/src/jit-test/tests/debug/resumption-02.js b/js/src/jit-test/tests/debug/resumption-02.js index efdb64f5487..f24451991f3 100644 --- a/js/src/jit-test/tests/debug/resumption-02.js +++ b/js/src/jit-test/tests/debug/resumption-02.js @@ -1,8 +1,5 @@ -// |jit-test| debug // Simple {return:} resumption. - var g = newGlobal('new-compartment'); - var dbg = Debug(g); dbg.hooks = { debuggerHandler: function (stack) { diff --git a/js/src/jit-test/tests/debug/surfaces-01.js b/js/src/jit-test/tests/debug/surfaces-01.js index 8a04693773c..495937ba884 100644 --- a/js/src/jit-test/tests/debug/surfaces-01.js +++ b/js/src/jit-test/tests/debug/surfaces-01.js @@ -1,4 +1,4 @@ -// |jit-test| debug +// Check superficial characteristics of functions and properties (not functionality). function checkFunction(obj, name, nargs) { var desc = Object.getOwnPropertyDescriptor(obj, name); diff --git a/js/src/jit-test/tests/debug/surfaces-02.js b/js/src/jit-test/tests/debug/surfaces-02.js index f599b42eaec..ceac1fbd941 100644 --- a/js/src/jit-test/tests/debug/surfaces-02.js +++ b/js/src/jit-test/tests/debug/surfaces-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Debug.prototype.hooks load(libdir + 'asserts.js'); diff --git a/js/src/jit-test/tests/debug/surfaces-03.js b/js/src/jit-test/tests/debug/surfaces-03.js index 523a03620e8..6ee6ec1b1da 100644 --- a/js/src/jit-test/tests/debug/surfaces-03.js +++ b/js/src/jit-test/tests/debug/surfaces-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // dumb basics of uncaughtExceptionHook load(libdir + 'asserts.js'); diff --git a/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js b/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js index 4ca3230568f..5d9f0e87bf3 100644 --- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js +++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Uncaught exceptions in the debugger itself are delivered to the // uncaughtExceptionHook. diff --git a/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js b/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js index ec4bd8a6db8..5c77d0d3713 100644 --- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js +++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // Returning a bad resumption value causes an exception that is reported to the // uncaughtExceptionHook. diff --git a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js index e30cb793327..c253317fc6c 100644 --- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js +++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-01.js @@ -1,4 +1,3 @@ -// |jit-test| debug // uncaughtExceptionHook returns a resumption value. load(libdir + "asserts.js"); diff --git a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js index f2fbda68ab4..a15257e218c 100644 --- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js +++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-02.js @@ -1,4 +1,3 @@ -// |jit-test| debug // uncaughtExceptionHook resumption value other than undefined causes further // hooks to be skipped. diff --git a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js index 67c64d99f2c..ffa67171346 100644 --- a/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js +++ b/js/src/jit-test/tests/debug/uncaughtExceptionHook-resumption-03.js @@ -1,4 +1,3 @@ -// |jit-test| debug // After hooks.throw throws, if uncaughtExceptionHook returns undefined, // the original exception continues to propagate. diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index e7ebbeaaeb2..91c138e0245 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -315,7 +315,7 @@ Script::analyze(JSContext *cx, JSScript *script) * 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); } @@ -330,7 +330,7 @@ Script::analyze(JSContext *cx, JSScript *script) * 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; /* diff --git a/js/src/jsapi-tests/testDebugger.cpp b/js/src/jsapi-tests/testDebugger.cpp index 3201f15f117..45498f2f50a 100644 --- a/js/src/jsapi-tests/testDebugger.cpp +++ b/js/src/jsapi-tests/testDebugger.cpp @@ -139,7 +139,7 @@ BEGIN_TEST(testDebugger_debugObjectVsDebugMode) EVAL("debuggee.eval('debugger; debugger; debugger;');\n" "hits;\n", &v); - CHECK_SAME(v, JSVAL_ONE); + CHECK_SAME(v, INT_TO_JSVAL(4)); return true; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 00cfadff2bf..595287fc56c 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -707,7 +707,7 @@ JSRuntime::init(uint32 maxbytes) return false; #endif - debugMode = JS_FALSE; + debugMode = false; return js_InitThreads(this); } diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ba5f56a48ee..f188894e598 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -495,7 +495,7 @@ struct JSRuntime { /* * 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. */ diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 30e56054107..05459ce7025 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -87,7 +87,7 @@ JSCompartment::JSCompartment(JSRuntime *rt) emptyWithShape(NULL), initialRegExpShape(NULL), initialStringShape(NULL), - debugMode(rt->debugMode), + debugModeBits(rt->debugMode * DebugFromC), mathCache(NULL) { JS_INIT_CLIST(&scripts); @@ -601,3 +601,54 @@ 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 +} diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index f307d18593f..93b82d4cda4 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -462,8 +462,11 @@ struct JS_FRIEND_API(JSCompartment) { 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; @@ -528,13 +531,52 @@ struct JS_FRIEND_API(JSCompartment) { 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); + } } }; diff --git a/js/src/jsdbg.cpp b/js/src/jsdbg.cpp index 7118b69d5de..7bb69f4ba8a 100644 --- a/js/src/jsdbg.cpp +++ b/js/src/jsdbg.cpp @@ -160,16 +160,10 @@ Debug::Debug(JSObject *dbg, JSObject *hooks) 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); } @@ -616,8 +610,9 @@ Debug::trace(JSTracer *trc, JSObject *obj) } 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)); @@ -636,47 +631,49 @@ Debug::sweepAll(JSRuntime *rt) // 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); } @@ -822,7 +819,7 @@ Debug::removeDebuggee(JSContext *cx, uintN argc, Value *vp) 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; } @@ -930,10 +927,7 @@ Debug::construct(JSContext *cx, uintN argc, Value *vp) 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 @@ -942,10 +936,9 @@ Debug::addDebuggeeGlobal(JSContext *cx, GlobalObject *obj) Vector 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; } @@ -963,12 +956,17 @@ Debug::addDebuggeeGlobal(JSContext *cx, GlobalObject *obj) } // 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; @@ -979,12 +977,12 @@ 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 @@ -1026,7 +1024,7 @@ Debug::removeDebuggeeGlobal(GlobalObject *global, GlobalObjectSet::Enum *compart if (compartmentEnum) compartmentEnum->removeFront(); else - global->compartment()->removeDebuggee(global); + global->compartment()->removeDebuggee(cx, global); } if (debugEnum) debugEnum->removeFront(); diff --git a/js/src/jsdbg.h b/js/src/jsdbg.h index 9761fdb986a..d08be9adf98 100644 --- a/js/src/jsdbg.h +++ b/js/src/jsdbg.h @@ -83,7 +83,8 @@ class Debug { 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); @@ -134,7 +135,6 @@ class Debug { 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. */ @@ -153,9 +153,9 @@ class Debug { // 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); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index d4db795301a..de6673137d1 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -101,7 +101,7 @@ typedef struct JSTrap { JS_PUBLIC_API(JSBool) JS_GetDebugMode(JSContext *cx) { - return cx->compartment->debugMode; + return cx->compartment->debugMode(); } JS_PUBLIC_API(JSBool) @@ -113,7 +113,7 @@ JS_SetDebugMode(JSContext *cx, JSBool debug) JS_PUBLIC_API(void) JS_SetRuntimeDebugMode(JSRuntime *rt, JSBool debug) { - rt->debugMode = debug; + rt->debugMode = !!debug; } namespace js { @@ -159,60 +159,7 @@ ScriptDebugEpilogue(JSContext *cx, StackFrame *fp, bool okArg) 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) @@ -225,7 +172,7 @@ js_SetSingleStepMode(JSContext *cx, JSScript *script, JSBool 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 */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index ec916ea3f31..7240a2bb93e 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2369,7 +2369,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM js_SweepWatchPoints(cx); if (!comp) - Debug::sweepAll(rt); + Debug::sweepAll(cx); /* * We finalize objects before other GC things to ensure that object's finalizer diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 9b1ec580afe..99f432c4a28 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -353,7 +353,7 @@ ScriptPrologue(JSContext *cx, StackFrame *fp) fp->functionThis().setObject(*obj); } - if (cx->compartment->debugMode) + if (cx->compartment->debugMode()) ScriptDebugPrologue(cx, fp); return true; } @@ -361,7 +361,7 @@ ScriptPrologue(JSContext *cx, StackFrame *fp) inline bool ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok) { - if (cx->compartment->debugMode) + if (cx->compartment->debugMode()) ok = ScriptDebugEpilogue(cx, fp, ok); /* @@ -382,7 +382,7 @@ 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; } @@ -392,7 +392,7 @@ 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; } diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 0db553b77d7..97a7a453c56 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -114,7 +114,7 @@ mjit::Compiler::Compiler(JSContext *cx, StackFrame *fp) 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 diff --git a/js/src/methodjit/FrameState.cpp b/js/src/methodjit/FrameState.cpp index 933e95c35b6..0f890361343 100644 --- a/js/src/methodjit/FrameState.cpp +++ b/js/src/methodjit/FrameState.cpp @@ -80,7 +80,7 @@ FrameState::init() 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 diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index 05dd2e1e07e..8f370fdd3b8 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -202,7 +202,7 @@ Recompiler::recompile(StackFrame *fp, Vector &patches, Vector &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); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index df7cda6b467..aac866a11d0 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -253,10 +253,10 @@ GlobalObject::addDebug(JSContext *cx, Debug *dbg) 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;