diff --git a/toolkit/devtools/server/actors/tracer.js b/toolkit/devtools/server/actors/tracer.js index 4517ac264de4..8885515dde77 100644 --- a/toolkit/devtools/server/actors/tracer.js +++ b/toolkit/devtools/server/actors/tracer.js @@ -442,10 +442,14 @@ TraceTypes.register("name", TraceTypes.Events.enterFrame, function({ frame }) { : "(" + frame.type + ")"; }); -TraceTypes.register("callsite", TraceTypes.Events.enterFrame, function({ frame }) { +TraceTypes.register("location", TraceTypes.Events.enterFrame, function({ frame }) { if (!frame.script) { return undefined; } + // We should return the location of the start of the script, but + // Debugger.Script does not provide complete start locations + // (bug 901138). Instead, return the current offset (the location of + // the first statement in the function). return { url: frame.script.url, line: frame.script.getOffsetLine(frame.offset), @@ -453,6 +457,18 @@ TraceTypes.register("callsite", TraceTypes.Events.enterFrame, function({ frame } }; }); +TraceTypes.register("callsite", TraceTypes.Events.enterFrame, function({ frame }) { + let older = frame.older; + if (!older || !older.script) { + return undefined; + } + return { + url: older.script.url, + line: older.script.getOffsetLine(older.offset), + column: getOffsetColumn(older.offset, older.script) + }; +}); + TraceTypes.register("time", TraceTypes.Events.enterFrame, timeSinceTraceStarted); TraceTypes.register("time", TraceTypes.Events.exitFrame, timeSinceTraceStarted); diff --git a/toolkit/devtools/server/tests/unit/test_trace_actor-05.js b/toolkit/devtools/server/tests/unit/test_trace_actor-05.js index e941c9635915..9bd489817633 100644 --- a/toolkit/devtools/server/tests/unit/test_trace_actor-05.js +++ b/toolkit/devtools/server/tests/unit/test_trace_actor-05.js @@ -2,8 +2,8 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /** - * Simple tests for "callsite", "time", "parameterNames", "arguments", - * and "return" trace types. + * Simple tests for "location", "callsite", "time", "parameterNames", + * "arguments", and "return" trace types. */ let {defer} = devtools.require("sdk/core/promise"); @@ -28,6 +28,29 @@ function run_test() do_test_pending(); } +function check_number(value, name) +{ + do_check_eq(typeof value, "number", name + ' should be a number'); + do_check_true(!isNaN(value), name + ' should be a number'); +} + +function check_location(actual, expected, name) +{ + do_check_eq(typeof actual, "object", + name + ' missing expected source location'); + + check_number(actual.line, name + ' line'); + check_number(actual.column, name + ' column'); + + do_check_eq(actual.url, expected.url, + name + ' location should have url ' + expected.url); + do_check_eq(actual.line, expected.line, + name + ' location should have source line of ' + expected.line); + do_check_eq(actual.column, expected.column, + name + ' location should have source column of ' + expected.line); + +} + function test_enter_exit_frame() { let packets = []; @@ -35,32 +58,30 @@ function test_enter_exit_frame() gTraceClient.addListener("enteredFrame", function(aEvent, aPacket) { do_check_eq(aPacket.type, "enteredFrame", 'enteredFrame response should have type "enteredFrame"'); - do_check_eq(typeof aPacket.sequence, "number", - 'enteredFrame response should have sequence number'); - do_check_true(!isNaN(aPacket.sequence), - 'enteredFrame sequence should be a number'); do_check_eq(typeof aPacket.name, "string", 'enteredFrame response should have function name'); - do_check_eq(typeof aPacket.callsite, "object", - 'enteredFrame response should have callsite'); - do_check_eq(typeof aPacket.time, "number", - 'enteredFrame response should have time'); - do_check_true(!isNaN(aPacket.time), - 'enteredFrame time should be a number'); + do_check_eq(typeof aPacket.location, "object", + 'enteredFrame response should have source location'); + + check_number(aPacket.sequence, 'enteredFrame sequence'); + check_number(aPacket.time, 'enteredFrame time'); + check_number(aPacket.location.line, 'enteredFrame source line'); + check_number(aPacket.location.column, 'enteredFrame source column'); + if (aPacket.callsite) { + check_number(aPacket.callsite.line, 'enteredFrame callsite line'); + check_number(aPacket.callsite.column, 'enteredFrame callsite column'); + } + packets[aPacket.sequence] = aPacket; }); gTraceClient.addListener("exitedFrame", function(aEvent, aPacket) { do_check_eq(aPacket.type, "exitedFrame", 'exitedFrame response should have type "exitedFrame"'); - do_check_eq(typeof aPacket.sequence, "number", - 'exitedFrame response should have sequence number'); - do_check_true(!isNaN(aPacket.sequence), - 'exitedFrame sequence should be a number'); - do_check_eq(typeof aPacket.time, "number", - 'exitedFrame response should have time'); - do_check_true(!isNaN(aPacket.time), - 'exitedFrame time should be a number'); + + check_number(aPacket.sequence, 'exitedFrame sequence'); + check_number(aPacket.time, 'exitedFrame time'); + packets[aPacket.sequence] = aPacket; }); @@ -68,28 +89,42 @@ function test_enter_exit_frame() .then(eval_code) .then(stop_trace) .then(function() { - do_check_eq(packets[2].name, "foo", - 'Third packet in sequence should be entry to "foo" frame'); + let url = getFileUrl("tracerlocations.js"); - do_check_eq(typeof packets[2].parameterNames, "object", + check_location(packets[0].location, { url: url, line: 1, column: 0 }, + 'global entry packet'); + + do_check_eq(packets[1].name, "foo", + 'Second packet in sequence should be entry to "foo" frame'); + + // foo's definition is at tracerlocations.js:3:0, but + // Debugger.Script does not provide complete definition + // locations (bug 901138). tracerlocations.js:4:2 is the first + // statement in the function (used as an approximation). + check_location(packets[1].location, { url: url, line: 4, column: 2 }, + 'foo source'); + check_location(packets[1].callsite, { url: url, line: 8, column: 0 }, + 'foo callsite'); + + do_check_eq(typeof packets[1].parameterNames, "object", 'foo entry packet should have parameterNames'); - do_check_eq(packets[2].parameterNames.length, 1, + do_check_eq(packets[1].parameterNames.length, 1, 'foo should have only one formal parameter'); - do_check_eq(packets[2].parameterNames[0], "x", + do_check_eq(packets[1].parameterNames[0], "x", 'foo should have formal parameter "x"'); - do_check_eq(typeof packets[2].arguments, "object", + do_check_eq(typeof packets[1].arguments, "object", 'foo entry packet should have arguments'); - do_check_true(Array.isArray(packets[2].arguments), + do_check_true(Array.isArray(packets[1].arguments), 'foo entry packet arguments should be an array'); - do_check_eq(packets[2].arguments.length, 1, + do_check_eq(packets[1].arguments.length, 1, 'foo should have only one actual parameter'); - do_check_eq(packets[2].arguments[0], 42, + do_check_eq(packets[1].arguments[0], 42, 'foo should have actual parameter 42'); - do_check_eq(typeof packets[3].return, "string", + do_check_eq(typeof packets[2].return, "string", 'Fourth packet in sequence should be exit from "foo" frame'); - do_check_eq(packets[3].return, "bar", + do_check_eq(packets[2].return, "bar", 'foo should return "bar"'); finishClient(gClient); @@ -100,7 +135,7 @@ function start_trace() { let deferred = defer(); gTraceClient.startTrace( - ["name", "callsite", "time", "parameterNames", "arguments", "return"], + ["name", "location", "callsite", "time", "parameterNames", "arguments", "return"], null, function() { deferred.resolve(); }); return deferred.promise; @@ -108,12 +143,9 @@ function start_trace() function eval_code() { - gDebuggee.eval("(" + function() { - function foo(x) { - return "bar"; - } - foo(42); - } + ")()"); + let code = readFile("tracerlocations.js"); + Components.utils.evalInSandbox(code, gDebuggee, "1.8", + getFileUrl("tracerlocations.js"), 1); } function stop_trace() diff --git a/toolkit/devtools/server/tests/unit/tracerlocations.js b/toolkit/devtools/server/tests/unit/tracerlocations.js new file mode 100644 index 000000000000..aa4677c0f70a --- /dev/null +++ b/toolkit/devtools/server/tests/unit/tracerlocations.js @@ -0,0 +1,8 @@ +// For JS Tracer tests dealing with source locations. + +function foo(x) { + x += 6; + return "bar"; +} + +foo(42);