summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/debug/Frame-onStep-12.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/debug/Frame-onStep-12.js')
-rw-r--r--js/src/jit-test/tests/debug/Frame-onStep-12.js118
1 files changed, 118 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/debug/Frame-onStep-12.js b/js/src/jit-test/tests/debug/Frame-onStep-12.js
new file mode 100644
index 0000000000..c751c278a5
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-onStep-12.js
@@ -0,0 +1,118 @@
+// Check that stepping doesn't make it look like unreachable code is running.
+
+// Because our script source notes record only those bytecode offsets
+// at which source positions change, the default behavior in the
+// absence of a source note is to attribute a bytecode instruction to
+// the same source location as the preceding instruction. When control
+// flows from the preceding bytecode to the one we're emitting, that's
+// usually plausible. But successors in the bytecode stream are not
+// necessarily successors in the control flow graph. If the preceding
+// bytecode was a back edge of a loop, or the jump at the end of a
+// 'then' clause, its source position can be completely unrelated to
+// that of its successor.
+//
+// We try to avoid showing such nonsense offsets to the user by
+// requiring breakpoints and single-stepping to stop only at a line's
+// entry points, as reported by Debugger.Script.prototype.getLineOffsets;
+// and then ensuring that those entry points are all offsets mentioned
+// explicitly in the source notes, and hence deliberately attributed
+// to the given bytecode.
+//
+// This bit of JavaScript compiles to bytecode ending in a branch
+// instruction whose source position is the body of an unreachable
+// loop. The first instruction of the bytecode we emit following it
+// will inherit this nonsense position, if we have not explicitly
+// emitted a source note for said instruction.
+//
+// This test steps across such code and verifies that control never
+// appears to enter the unreachable loop.
+
+var bitOfCode = `debugger; // +0
+ if(false) { // +1
+ for(var b=0; b<0; b++) { // +2
+ c = 2 // +3
+ } // +4
+ }`; // +5
+
+var g = newGlobal({newCompartment: true});
+var dbg = Debugger(g);
+
+g.eval("function nothing() { }\n");
+
+var log = '';
+dbg.onDebuggerStatement = function(frame) {
+ let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber;
+ frame.onStep = function() {
+ let foundLine = this.script.getOffsetLocation(this.offset).lineNumber;
+ if (this.script.getLineOffsets(foundLine).indexOf(this.offset) >= 0) {
+ log += (foundLine - debugLine).toString(16);
+ }
+ };
+};
+
+function testOne(name, body, expected) {
+ print(name);
+ log = '';
+ g.eval(`function ${name} () { ${body} }`);
+ g.eval(`${name}();`);
+ assertEq(log, expected);
+}
+
+
+
+// Test the instructions at the end of a "try".
+testOne("testTryFinally",
+ `try {
+ ${bitOfCode}
+ } finally { // +6
+ } // +7
+ nothing(); // +8
+ `, "1689");
+
+// The same but without a finally clause.
+testOne("testTryCatch",
+ `try {
+ ${bitOfCode}
+ } catch (e) { // +6
+ } // +7
+ nothing(); // +8
+ `, "189");
+
+// Test the instructions at the end of a "catch".
+testOne("testCatchFinally",
+ `try {
+ throw new TypeError();
+ } catch (e) {
+ ${bitOfCode}
+ } finally { // +6
+ } // +7
+ nothing(); // +8
+ `, "1689");
+
+// Test the instruction at the end of a "finally" clause.
+testOne("testFinally",
+ `try {
+ } finally {
+ ${bitOfCode}
+ } // +6
+ nothing(); // +7
+ `, "178");
+
+// Test the instruction at the end of a "then" clause.
+testOne("testThen",
+ `if (1 === 1) {
+ ${bitOfCode}
+ } else { // +6
+ } // +7
+ nothing(); // +8
+ `, "189");
+
+// Test the instructions leaving a switch block.
+testOne("testSwitch",
+ `var x = 5;
+ switch (x) {
+ case 5:
+ ${bitOfCode}
+ } // +6
+ nothing(); // +7
+ `, "178");