From 7c4e941f796c7f4eec57d4163b1b70e9c336cdbb Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Thu, 11 Dec 2014 13:22:44 -0700 Subject: [PATCH] Bug 1013219 - set the line number of the terminating retrval; r=jimb,ejpbruel,fitzgen --- .../browser_dbg_scripts-switching-01.js | 6 +- .../browser_dbg_scripts-switching-02.js | 6 +- devtools/server/tests/unit/head_dbg.js | 25 +++- ...column-with-no-offsets-at-end-of-script.js | 2 +- ...n-line-with-no-offsets-at-end-of-script.js | 1 + .../server/tests/unit/test_breakpoint-13.js | 111 +++++++++--------- .../server/tests/unit/test_breakpoint-14.js | 107 +++++++++-------- .../tests/unit/test_get-executable-lines.js | 2 +- ...n-line-with-no-offsets-at-end-of-script.js | 4 +- .../server/tests/unit/test_stepping-03.js | 2 +- .../server/tests/unit/test_stepping-06.js | 2 +- .../server/tests/unit/test_stepping-07.js | 89 ++++++++++++++ devtools/server/tests/unit/xpcshell.ini | 1 + js/src/frontend/BytecodeEmitter.cpp | 3 + js/src/frontend/Parser.cpp | 2 + js/src/jit-test/tests/debug/Frame-onPop-23.js | 34 ++++++ .../jit-test/tests/debug/Frame-onStep-11.js | 2 +- .../jit-test/tests/debug/Frame-onStep-12.js | 14 +-- .../jit-test/tests/debug/Frame-onStep-13.js | 29 +++++ .../debug/Script-getAllColumnOffsets-01.js | 2 +- .../debug/Script-getAllColumnOffsets-06.js | 6 +- .../jit-test/tests/debug/Script-startLine.js | 10 +- 22 files changed, 318 insertions(+), 142 deletions(-) create mode 100644 devtools/server/tests/unit/test_stepping-07.js create mode 100644 js/src/jit-test/tests/debug/Frame-onPop-23.js create mode 100644 js/src/jit-test/tests/debug/Frame-onStep-13.js diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js index c4bfb126989..fd43c66ea48 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-01.js @@ -136,11 +136,11 @@ function test() { is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); - ok(isCaretPos(gPanel, 5), + ok(isCaretPos(gPanel, 6), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), 4, + is(gEditor.getDebugLocation(), 5, "Editor debugger location is correct."); - ok(gEditor.hasLineClass(4, "debug-line"), + ok(gEditor.hasLineClass(5, "debug-line"), "The debugged line is highlighted appropriately (3)."); } diff --git a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js index 0800bc93036..3a9923e1c21 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg_scripts-switching-02.js @@ -131,11 +131,11 @@ function test() { "The second source is not displayed."); // The editor's debug location takes a tick to update. - ok(isCaretPos(gPanel, 5), + ok(isCaretPos(gPanel, 6), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), 4, + is(gEditor.getDebugLocation(), 5, "Editor debugger location is correct."); - ok(gEditor.hasLineClass(4, "debug-line"), + ok(gEditor.hasLineClass(5, "debug-line"), "The debugged line is highlighted appropriately."); deferred.resolve(); diff --git a/devtools/server/tests/unit/head_dbg.js b/devtools/server/tests/unit/head_dbg.js index e07439951a5..ccd10c91e5e 100644 --- a/devtools/server/tests/unit/head_dbg.js +++ b/devtools/server/tests/unit/head_dbg.js @@ -372,10 +372,13 @@ function attachTestThread(aClient, aTitle, aCallback) { // thread, and then resume it. Pass |aCallback| the thread's response to // the 'resume' packet, a TabClient for the tab, and a ThreadClient for the // thread. -function attachTestTabAndResume(aClient, aTitle, aCallback) { - attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) { - aThreadClient.resume(function (aResponse) { - aCallback(aResponse, aTabClient, aThreadClient); +function attachTestTabAndResume(aClient, aTitle, aCallback = () => {}) { + return new Promise((resolve, reject) => { + attachTestThread(aClient, aTitle, function(aResponse, aTabClient, aThreadClient) { + aThreadClient.resume(function (aResponse) { + aCallback(aResponse, aTabClient, aThreadClient); + resolve([aResponse, aTabClient, aThreadClient]); + }); }); }); } @@ -724,6 +727,20 @@ function stepIn(client, threadClient) { .then(() => paused); } +/** + * Resume JS execution for a step over and wait for the pause after the step + * has been taken. + * + * @param DebuggerClient client + * @param ThreadClient threadClient + * @returns Promise + */ +function stepOver(client, threadClient) { + dumpn("Stepping over."); + return threadClient.stepOver() + .then(() => waitForPause(client)); +} + /** * Get the list of `count` frames currently on stack, starting at the index * `first` for the specified thread. diff --git a/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js index 48dabd5e043..401dfe728b9 100644 --- a/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js +++ b/devtools/server/tests/unit/setBreakpoint-on-column-with-no-offsets-at-end-of-script.js @@ -1,5 +1,5 @@ "use strict"; function f() { - function g() { var a = 1; var b = 2; } g(); + function g() { var a = 1; var b = 2; return; } g(); } diff --git a/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js index 52453c7b387..84ecf7b04b4 100644 --- a/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js +++ b/devtools/server/tests/unit/setBreakpoint-on-line-with-no-offsets-at-end-of-script.js @@ -4,6 +4,7 @@ function f() { function g() { var a = 1; var b = 2; + return; } diff --git a/devtools/server/tests/unit/test_breakpoint-13.js b/devtools/server/tests/unit/test_breakpoint-13.js index 307b058a411..69b3b2f16a0 100644 --- a/devtools/server/tests/unit/test_breakpoint-13.js +++ b/devtools/server/tests/unit/test_breakpoint-13.js @@ -39,69 +39,68 @@ function test_simple_breakpoint() let source = gThreadClient.source(aPacket.frame.where.source); let location = { line: gDebuggee.line0 + 2 }; - source.setBreakpoint(location, function (aResponse, bpClient) { - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the stepping worked. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + source.setBreakpoint(location, Task.async(function*(aResponse, bpClient) { + const testCallbacks = [ + function(aPacket) { + // Check that the stepping worked. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { // Entered the foo function call frame. do_check_eq(aPacket.frame.where.line, location.line); do_check_neq(aPacket.why.type, "breakpoint"); do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // At the end of the foo function call frame. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_neq(aPacket.why.type, "breakpoint"); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // Check that the breakpoint wasn't the reason for this pause, but + // that the frame is about to be popped while stepping. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_neq(aPacket.why.type, "breakpoint"); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); + }, + function(aPacket) { + // The foo function call frame was just popped from the stack. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.poppedFrames.length, 1); + }, + function(aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + ]; - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the breakpoint wasn't the reason for this pause, but - // that the frame is about to be popped while stepping. - do_check_eq(aPacket.frame.where.line, location.line); - do_check_neq(aPacket.why.type, "breakpoint"); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // The foo function call frame was just popped from the stack. - do_check_eq(gDebuggee.a, 1); - do_check_eq(gDebuggee.b, undefined); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.poppedFrames.length, 1); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - - // Remove the breakpoint and finish. - bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); - - }); - // Step past the debugger statement. - gThreadClient.stepIn(); - }); - // Step into the debugger statement. - gThreadClient.stepIn(); - }); - // Get back to the frame above. - gThreadClient.stepIn(); - }); - // Step to the end of the function call frame. - gThreadClient.stepIn(); - }); - - // Step into the function call. + for (let callback of testCallbacks) { + let waiter = waitForPause(gThreadClient); gThreadClient.stepIn(); - }); - // Step into the next line with the function call. + let packet = yield waiter; + callback(packet); + } + + // Remove the breakpoint and finish. + let waiter = waitForPause(gThreadClient); gThreadClient.stepIn(); - }); + yield waiter; + bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); + })); }); Cu.evalInSandbox("var line0 = Error().lineNumber;\n" + diff --git a/devtools/server/tests/unit/test_breakpoint-14.js b/devtools/server/tests/unit/test_breakpoint-14.js index 163b357bb37..b25e2802a9a 100644 --- a/devtools/server/tests/unit/test_breakpoint-14.js +++ b/devtools/server/tests/unit/test_breakpoint-14.js @@ -39,67 +39,66 @@ function test_simple_breakpoint() let source = gThreadClient.source(aPacket.frame.where.source); let location = { line: gDebuggee.line0 + 2 }; - source.setBreakpoint(location, function (aResponse, bpClient) { - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the stepping worked. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { + source.setBreakpoint(location, Task.async(function*(aResponse, bpClient) { + const testCallbacks = [ + function(aPacket) { + // Check that the stepping worked. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { // Reached the breakpoint. do_check_eq(aPacket.frame.where.line, location.line); do_check_eq(aPacket.why.type, "breakpoint"); do_check_neq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // Stepped to the closing brace of the function. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // The frame is about to be popped while stepping. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 3); + do_check_neq(aPacket.why.type, "breakpoint"); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); + }, + function(aPacket) { + // The foo function call frame was just popped from the stack. + do_check_eq(gDebuggee.a, 1); + do_check_eq(gDebuggee.b, undefined); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); + do_check_eq(aPacket.why.type, "resumeLimit"); + do_check_eq(aPacket.poppedFrames.length, 1); + }, + function(aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + function(aPacket) { + // Check that the debugger statement wasn't the reason for this pause. + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); + do_check_neq(aPacket.why.type, "debuggerStatement"); + do_check_eq(aPacket.why.type, "resumeLimit"); + }, + ]; - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // The frame is about to be popped while stepping. - do_check_eq(aPacket.frame.where.line, location.line); - do_check_neq(aPacket.why.type, "breakpoint"); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // The foo function call frame was just popped from the stack. - do_check_eq(gDebuggee.a, 1); - do_check_eq(gDebuggee.b, undefined); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); - do_check_eq(aPacket.why.type, "resumeLimit"); - do_check_eq(aPacket.poppedFrames.length, 1); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 6); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - - gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { - // Check that the debugger statement wasn't the reason for this pause. - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); - do_check_neq(aPacket.why.type, "debuggerStatement"); - do_check_eq(aPacket.why.type, "resumeLimit"); - - // Remove the breakpoint and finish. - bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); - - }); - // Step past the debugger statement. - gThreadClient.stepOver(); - }); - // Step over the debugger statement. - gThreadClient.stepOver(); - }); - // Get back to the frame above. - gThreadClient.stepOver(); - }); - // Step to the end of the function call frame. - gThreadClient.stepOver(); - }); - // Step over the function call. + for (let callback of testCallbacks) { + let waiter = waitForPause(gThreadClient); gThreadClient.stepOver(); - }); - // Step over to the next line with the function call. + let packet = yield waiter; + callback(packet); + } + + // Remove the breakpoint and finish. + let waiter = waitForPause(gThreadClient); gThreadClient.stepOver(); - }); + yield waiter; + bpClient.remove(() => gThreadClient.resume(() => gClient.close(gCallback))); + })); }); Cu.evalInSandbox("var line0 = Error().lineNumber;\n" + diff --git a/devtools/server/tests/unit/test_get-executable-lines.js b/devtools/server/tests/unit/test_get-executable-lines.js index 2228fa5259f..d3be7efef62 100644 --- a/devtools/server/tests/unit/test_get-executable-lines.js +++ b/devtools/server/tests/unit/test_get-executable-lines.js @@ -38,7 +38,7 @@ function test_executable_lines() { do_check_true(!error); let source = gThreadClient.source(sources[0]); source.getExecutableLines(function(lines){ - do_check_true(arrays_equal([2, 5, 7, 8, 12, 14, 16], lines)); + do_check_true(arrays_equal([2, 5, 7, 8, 10, 12, 14, 16], lines)); finishClient(gClient); }); }); diff --git a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js index 96ab3647526..7d6925636ff 100644 --- a/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js +++ b/devtools/server/tests/unit/test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js @@ -26,12 +26,12 @@ function run_test() { let { source } = yield promise; let sourceClient = threadClient.source(source); - let location = { line: 7 }; + let location = { line: 8 }; let [packet, breakpointClient] = yield setBreakpoint(sourceClient, location); do_check_false(packet.isPending); // NOTE: Change this when bug 1148356 lands do_check_true("actualLocation" in packet); let actualLocation = packet.actualLocation; - do_check_eq(actualLocation.line, 10); + do_check_eq(actualLocation.line, 11); packet = yield executeOnNextTickAndWaitForPause(function () { Cu.evalInSandbox("f()", global); diff --git a/devtools/server/tests/unit/test_stepping-03.js b/devtools/server/tests/unit/test_stepping-03.js index 6bb3ce1a40b..10706d63fdb 100644 --- a/devtools/server/tests/unit/test_stepping-03.js +++ b/devtools/server/tests/unit/test_stepping-03.js @@ -38,7 +38,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check the return value. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 4); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 5); do_check_eq(aPacket.why.type, "resumeLimit"); // Check that stepping worked. do_check_eq(gDebuggee.a, 1); diff --git a/devtools/server/tests/unit/test_stepping-06.js b/devtools/server/tests/unit/test_stepping-06.js index e2c58bcae85..766cc18b258 100644 --- a/devtools/server/tests/unit/test_stepping-06.js +++ b/devtools/server/tests/unit/test_stepping-06.js @@ -51,7 +51,7 @@ function test_simple_stepping() gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) { // Check that the return value is undefined. do_check_eq(aPacket.type, "paused"); - do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 7); + do_check_eq(aPacket.frame.where.line, gDebuggee.line0 + 8); do_check_eq(aPacket.why.type, "resumeLimit"); do_check_eq(aPacket.why.frameFinished.return.type, "undefined"); diff --git a/devtools/server/tests/unit/test_stepping-07.js b/devtools/server/tests/unit/test_stepping-07.js new file mode 100644 index 00000000000..c883326bc1d --- /dev/null +++ b/devtools/server/tests/unit/test_stepping-07.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Check that stepping over an implicit return makes sense. Bug 1155966. + */ + +var gDebuggee; +var gClient; +var gCallback; + +function run_test() { + do_test_pending(); + run_test_with_server(DebuggerServer, function () { + run_test_with_server(WorkerDebuggerServer, do_test_finished); + }); +}; + +function run_test_with_server(aServer, aCallback) { + gCallback = aCallback; + initTestDebuggerServer(aServer); + gDebuggee = addTestGlobal("test-stepping", aServer); + gClient = new DebuggerClient(aServer.connectPipe()); + gClient.connect(testSteppingAndReturns); +} + +const testSteppingAndReturns = Task.async(function*() { + const [attachResponse, tabClient, threadClient] = yield attachTestTabAndResume(gClient, "test-stepping"); + ok(!attachResponse.error, "Should not get an error attaching"); + + dumpn("Evaluating test code and waiting for first debugger statement"); + const dbgStmt1 = yield executeOnNextTickAndWaitForPause(evaluateTestCode, gClient) + equal(dbgStmt1.frame.where.line, 3, + "Should be at debugger statement on line 3") + + dumpn("Testing stepping with implicit return"); + const step1 = yield stepOver(gClient, threadClient); + equal(step1.frame.where.line, 4, "Should step to line 4"); + const step2 = yield stepOver(gClient, threadClient); + equal(step2.frame.where.line, 7, + "Should step to line 7, the implicit return at the last line of the function"); + // This assertion doesn't pass yet. You would need to do *another* + // step at the end of this function to get the frameFinished. + // See bug 923975. + // + // ok(step2.why.frameFinished, "This should be the implicit function return"); + + dumpn("Continuing and waiting for second debugger statement"); + const dbgStmt2 = yield resumeAndWaitForPause(gClient, threadClient); + equal(dbgStmt2.frame.where.line, 12, + "Should be at debugger statement on line 3") + + dumpn("Testing stepping with explicit return"); + const step3 = yield stepOver(gClient, threadClient); + equal(step3.frame.where.line, 13, "Should step to line 13"); + const step4 = yield stepOver(gClient, threadClient); + equal(step4.frame.where.line, 13, "Should step out of the function from line 13"); + ok(step4.why.frameFinished, "This should be the explicit function return"); + + finishClient(gClient, gCallback); +}); + +function evaluateTestCode() { + Cu.evalInSandbox( + ` // 1 + function implicitReturn() { // 2 + debugger; // 3 + if (this.someUndefinedProperty) { // 4 + yikes(); // 5 + } // 6 + } // 7 + // 8 + var yes = true; // 9 + function explicitReturn() { // 10 + if (yes) { // 11 + debugger; // 12 + return 1; // 13 + } // 14 + } // 15 + // 16 + implicitReturn(); // 17 + explicitReturn(); // 18 + `, // 19 + gDebuggee, + "1.8", + "test_stepping-07-test-code.js", + 1 + ); +} diff --git a/devtools/server/tests/unit/xpcshell.ini b/devtools/server/tests/unit/xpcshell.ini index 5236cbe44ee..d600efb1ff1 100644 --- a/devtools/server/tests/unit/xpcshell.ini +++ b/devtools/server/tests/unit/xpcshell.ini @@ -205,6 +205,7 @@ reason = bug 820380 [test_stepping-04.js] [test_stepping-05.js] [test_stepping-06.js] +[test_stepping-07.js] [test_framebindings-01.js] [test_framebindings-02.js] [test_framebindings-03.js] diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 90e57ba56cb..b799edf73b7 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3592,6 +3592,9 @@ BytecodeEmitter::emitFunctionScript(ParseNode* body) if (!emitTree(body)) return false; + if (!updateSourceCoordNotes(body->pn_pos.end)) + return false; + if (sc->isFunctionBox()) { if (sc->asFunctionBox()->isGenerator()) { // If we fall off the end of a generator, do a final yield. diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 1885d73bd07..6036fa38bb5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3188,6 +3188,8 @@ Parser::functionArgsAndBodyGeneric(InHandling inHandling, return false; } + handler.setEndPosition(body, pos().begin); + return finishFunctionDefinition(pn, funbox, body); } diff --git a/js/src/jit-test/tests/debug/Frame-onPop-23.js b/js/src/jit-test/tests/debug/Frame-onPop-23.js new file mode 100644 index 00000000000..d49e1d456c9 --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-onPop-23.js @@ -0,0 +1,34 @@ +// Check that the line number reported at an onPop stop makes sense, +// even when it happens on an "artificial" instruction. + +var g = newGlobal(); + +// This bit of code arranges for the line number of the "artificial" +// instruction to be something nonsensical -- the middle of a loop +// which cannot be entered. +g.eval(`function f() { + debugger; // +0 + if(false) { // +1 + for(var b=0; b<0; b++) { // +2 + c = 2; // +3 + } // +4 + } // +5 +} // +6 +`); + +var dbg = Debugger(g); + +let debugLine; +let foundLine; + +dbg.onDebuggerStatement = function(frame) { + debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber; + frame.onPop = function(c) { + foundLine = this.script.getOffsetLocation(this.offset).lineNumber; + }; +}; + +g.eval("f();\n"); + +// The stop should happen on the closing brace of the function. +assertEq(foundLine == debugLine + 6, true); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-11.js b/js/src/jit-test/tests/debug/Frame-onStep-11.js index 21521ae5250..dae366165c4 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-11.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-11.js @@ -33,4 +33,4 @@ dbg.onDebuggerStatement = function(frame) { g.f(); -assertEq(foundLines, ",1,2,3,4,5,6,7,8,10"); +assertEq(foundLines, ",1,2,3,4,5,6,7,8,10,11"); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-12.js b/js/src/jit-test/tests/debug/Frame-onStep-12.js index 517ccbf1d97..4db38b31360 100644 --- a/js/src/jit-test/tests/debug/Frame-onStep-12.js +++ b/js/src/jit-test/tests/debug/Frame-onStep-12.js @@ -65,7 +65,7 @@ testOne("testTryFinally", } finally { // +6 } // +7 nothing(); // +8 - `, "168"); + `, "1689"); // The same but without a finally clause. testOne("testTryCatch", @@ -74,7 +74,7 @@ testOne("testTryCatch", } catch (e) { // +6 } // +7 nothing(); // +8 - `, "18"); + `, "189"); // Test the instructions at the end of a "catch". testOne("testCatchFinally", @@ -85,7 +85,7 @@ testOne("testCatchFinally", } finally { // +6 } // +7 nothing(); // +8 - `, "168"); + `, "1689"); // The same but without a finally clause. This relies on a // SpiderMonkey extension, because otherwise there's no way to see @@ -98,7 +98,7 @@ testOne("testCatch", } catch (e) { // +6 } // +7 nothing(); // +8 - `, "18"); + `, "189"); // Test the instruction at the end of a "finally" clause. testOne("testFinally", @@ -107,7 +107,7 @@ testOne("testFinally", ${bitOfCode} } // +6 nothing(); // +7 - `, "17"); + `, "178"); // Test the instruction at the end of a "then" clause. testOne("testThen", @@ -116,7 +116,7 @@ testOne("testThen", } else { // +6 } // +7 nothing(); // +8 - `, "18"); + `, "189"); // Test the instructions leaving a switch block. testOne("testSwitch", @@ -126,4 +126,4 @@ testOne("testSwitch", ${bitOfCode} } // +6 nothing(); // +7 - `, "17"); + `, "178"); diff --git a/js/src/jit-test/tests/debug/Frame-onStep-13.js b/js/src/jit-test/tests/debug/Frame-onStep-13.js new file mode 100644 index 00000000000..cc2c18c6d64 --- /dev/null +++ b/js/src/jit-test/tests/debug/Frame-onStep-13.js @@ -0,0 +1,29 @@ +// Stepping over a not-taken "if" that is at the end of the function +// should move to the end of the function, not somewhere in the body +// of the "if". + +var g = newGlobal(); +g.eval(`function f() { // 1 + var a,c; // 2 + debugger; // 3 + if(false) { // 4 + for(var b=0; b<0; b++) { // 5 + c = 2; // 6 + } // 7 + } // 8 +} // 9 +`); + +var dbg = Debugger(g); +var badStep = false; + +dbg.onDebuggerStatement = function(frame) { + let debugLine = frame.script.getOffsetLocation(frame.offset).lineNumber; + assertEq(debugLine, 3); + frame.onStep = function() { + let foundLine = this.script.getOffsetLocation(this.offset).lineNumber; + assertEq(foundLine <= 4 || foundLine >= 8, true); + }; +}; + +g.eval("f();\n"); diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js index 50c38dffe1c..620d5624415 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-01.js @@ -16,4 +16,4 @@ Debugger(global).onDebuggerStatement = function (frame) { global.log = ''; global.eval("function f(n) { for (var i = 0; i < n; ++i) log += '. '; log += '! '; } debugger;"); global.f(3); -assertEq(global.log, "25 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 69 "); +assertEq(global.log, "25 32 44 . 39 32 44 . 39 32 44 . 39 32 57 ! 70 "); diff --git a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js index fe9bdb62179..20b49add880 100644 --- a/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js +++ b/js/src/jit-test/tests/debug/Script-getAllColumnOffsets-06.js @@ -16,7 +16,7 @@ Debugger(global).onDebuggerStatement = function (frame) { global.log = ""; global.eval("function ppppp() { return 1; }"); // 1 2 3 4 -// 0123456789012345678901234567890123456789012345678 +// 01234567890123456789012345678901234567890123456789 global.eval("function f(){ 1 && ppppp(ppppp()) && new Error() } debugger;"); global.f(); @@ -24,5 +24,5 @@ global.f(); // 25 - Inner print() // 19 - Outer print() // 37 - new Error() -// 48 - Exit the function body -assertEq(global.log, "14 25 19 37 48 "); +// 49 - Exit the function body +assertEq(global.log, "14 25 19 37 49 "); diff --git a/js/src/jit-test/tests/debug/Script-startLine.js b/js/src/jit-test/tests/debug/Script-startLine.js index 4d3c4995010..f5b2d108505 100644 --- a/js/src/jit-test/tests/debug/Script-startLine.js +++ b/js/src/jit-test/tests/debug/Script-startLine.js @@ -55,7 +55,9 @@ g.eval("/* Any copyright is dedicated to the Public Domain.\n" + " eval(\"42;\");\n" + " function foo() {}\n" + " if (true) {\n" + - " foo();\n" + // <- this is +6 and must be within the extent - " }\n" + - "}"); -test(g.secondCall, 7); + " foo();\n" + + // The "missing" newline here is a trick to make a newline + // source note come at the end. A real newline between the two + // closing braces causes a setline note instead. + " } }"); +test(g.secondCall, 8);