Bug 918802 - Move debugger's pretty printing into a worker; r=past

This commit is contained in:
Nick Fitzgerald 2013-09-30 15:31:44 -07:00
parent 61b995950f
commit 2b8c2950bf
22 changed files with 6061 additions and 255 deletions

View File

@ -102,6 +102,9 @@ support-files =
[browser_dbg_pretty-print-04.js]
[browser_dbg_pretty-print-05.js]
[browser_dbg_pretty-print-06.js]
[browser_dbg_pretty-print-07.js]
[browser_dbg_pretty-print-08.js]
[browser_dbg_pretty-print-09.js]
[browser_dbg_progress-listener-bug.js]
[browser_dbg_reload-preferred-script-01.js]
[browser_dbg_reload-preferred-script-02.js]

View File

@ -0,0 +1,46 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test basic pretty printing functionality. Would be an xpcshell test, except
// for bug 921252.
let gTab, gDebuggee, gPanel, gClient, gThreadClient;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
findSource();
});
}
function findSource() {
gThreadClient.getSources(({ error, sources }) => {
ok(!error);
sources = sources.filter(s => s.url.contains('code_ugly-2.js'));
is(sources.length, 1);
prettyPrintSource(sources[0]);
});
}
function prettyPrintSource(source) {
gThreadClient.source(source).prettyPrint(4, testPrettyPrinted);
}
function testPrettyPrinted({ error, source}) {
ok(!error);
ok(source.contains("\n "));
closeDebuggerAndFinish(gPanel);
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = null;
});

View File

@ -0,0 +1,95 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test stepping through pretty printed sources.
let gTab, gDebuggee, gPanel, gClient, gThreadClient, gSource;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
gDebuggee.noop = x => x;
findSource();
});
}
let CODE_URL;
const BP_LOCATION = {
line: 5,
column: 11
};
function findSource() {
gThreadClient.getSources(({ error, sources }) => {
ok(!error);
sources = sources.filter(s => s.url.contains("code_ugly-3.js"));
is(sources.length, 1);
[gSource] = sources;
CODE_URL = BP_LOCATION.url = gSource.url;
prettyPrintSource(sources[0]);
});
}
function prettyPrintSource(source) {
gThreadClient.source(gSource).prettyPrint(2, runCode);
}
function runCode({ error }) {
ok(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.main3();
}
function testDbgStatement(event, { why, frame }) {
is(why.type, "debuggerStatement");
const { url, line, column } = frame.where;
is(url, CODE_URL);
is(line, 3);
setBreakpoint();
}
function setBreakpoint() {
gThreadClient.setBreakpoint(BP_LOCATION, ({ error, actualLocation }) => {
ok(!error);
ok(!actualLocation);
testStepping();
});
}
function testStepping() {
gClient.addOneTimeListener("paused", (event, { why, frame }) => {
is(why.type, "resumeLimit");
const { url, line } = frame.where;
is(url, CODE_URL);
is(line, 4);
testHitBreakpoint();
});
gThreadClient.stepIn();
}
function testHitBreakpoint() {
gClient.addOneTimeListener("paused", (event, { why, frame }) => {
is(why.type, "breakpoint");
const { url, line, column } = frame.where;
is(url, CODE_URL);
is(line, BP_LOCATION.line);
is(column, BP_LOCATION.column);
resumeDebuggerThenCloseAndFinish(gPanel);
});
gThreadClient.resume();
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = gSource = null;
});

View File

@ -0,0 +1,73 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test pretty printing source mapped sources.
var gDebuggee;
var gClient;
var gThreadClient;
var gSource;
let gTab, gDebuggee, gPanel, gClient, gThreadClient;
const TAB_URL = EXAMPLE_URL + "doc_pretty-print-2.html";
function test() {
initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
gTab = aTab;
gDebuggee = aDebuggee;
gPanel = aPanel;
gClient = gPanel.panelWin.gClient;
gThreadClient = gPanel.panelWin.DebuggerController.activeThread;
findSource();
});
}
const dataUrl = s => "data:text/javascript," + s;
// These should match the instructions in code_ugly-4.js.
const A = "function a(){b()}";
const A_URL = dataUrl(A);
const B = "function b(){debugger}";
const B_URL = dataUrl(B);
function findSource() {
gThreadClient.getSources(({ error, sources }) => {
ok(!error);
sources = sources.filter(s => s.url === B_URL);
is(sources.length, 1);
prettyPrint(sources[0]);
});
}
function prettyPrint(source) {
gThreadClient.source(source).prettyPrint(2, runCode);
}
function runCode({ error }) {
ok(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.a();
}
function testDbgStatement(event, { frame, why }) {
dump("FITZGEN: inside testDbgStatement\n");
try {
is(why.type, "debuggerStatement");
const { url, line, column } = frame.where;
is(url, B_URL);
is(line, 2);
is(column, 2);
resumeDebuggerThenCloseAndFinish(gPanel);
} catch (e) {
dump("FITZGEN: got an error! " + DevToolsUtils.safeErrorString(e) + "\n");
}
}
registerCleanupFunction(function() {
gTab = gDebuggee = gPanel = gClient = gThreadClient = null;
});

View File

@ -0,0 +1 @@
function main2() { var a = 1 + 3; var b = a++; return b + a; }

View File

@ -0,0 +1 @@
function main3() { var a = 1; debugger; noop(a); return 10; };

View File

@ -0,0 +1,24 @@
function a(){b()}function b(){debugger}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWJjLmpzIiwic291cmNlcyI6WyJkYXRhOnRleHQvamF2YXNjcmlwdCxmdW5jdGlvbiBhKCl7YigpfSIsImRhdGE6dGV4dC9qYXZhc2NyaXB0LGZ1bmN0aW9uIGIoKXtkZWJ1Z2dlcn0iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsaUJDQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMifQ==
// Generate this file by evaluating the following in a browser-environment
// scratchpad:
//
// Components.utils.import('resource://gre/modules/devtools/SourceMap.jsm');
//
// let dataUrl = s => "data:text/javascript," + s;
//
// let A = "function a(){b()}";
// let A_URL = dataUrl(A);
// let B = "function b(){debugger}";
// let B_URL = dataUrl(B);
//
// let result = (new SourceNode(null, null, null, [
// new SourceNode(1, 0, A_URL, A),
// B.split("").map((ch, i) => new SourceNode(1, i, B_URL, ch))
// ])).toStringWithSourceMap({
// file: "abc.js"
// });
//
// result.code + "\n//# sourceMappingURL=data:application/json;base64," + btoa(JSON.stringify(result.map));

View File

@ -0,0 +1,10 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE html>
<head>
<meta charset="utf-8"/>
<title>Debugger Pretty Printing Test Page</title>
</head>
<script src="code_ugly-2.js"></script>
<script src="code_ugly-3.js"></script>
<script src="code_ugly-4.js"></script>

View File

@ -6,6 +6,9 @@
/* General utilities used throughout devtools. */
let { Promise: promise } = Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
let { Services } = Components.utils.import("resource://gre/modules/Services.jsm", {});
/**
* Turn the error |aError| into a string, without fail.
*/
@ -79,3 +82,55 @@ this.makeInfallible = function makeInfallible(aHandler, aName) {
}
}
}
const executeSoon = aFn => {
Services.tm.mainThread.dispatch({
run: this.makeInfallible(aFn)
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}
/**
* Like Array.prototype.forEach, but doesn't cause jankiness when iterating over
* very large arrays by yielding to the browser and continuing execution on the
* next tick.
*
* @param Array aArray
* The array being iterated over.
* @param Function aFn
* The function called on each item in the array.
* @returns Promise
* A promise that is resolved once the whole array has been iterated
* over.
*/
this.yieldingEach = function yieldingEach(aArray, aFn) {
const deferred = promise.defer();
let i = 0;
let len = aArray.length;
(function loop() {
const start = Date.now();
while (i < len) {
// Don't block the main thread for longer than 16 ms at a time. To
// maintain 60fps, you have to render every frame in at least 16ms; we
// aren't including time spent in non-JS here, but this is Good
// Enough(tm).
if (Date.now() - start > 16) {
executeSoon(loop);
return;
}
try {
aFn(aArray[i++]);
} catch (e) {
deferred.reject(e);
return;
}
}
deferred.resolve();
}());
return deferred.promise;
}

View File

@ -21,5 +21,6 @@ Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
this.DevToolsUtils = {
safeErrorString: safeErrorString,
reportException: reportException,
makeInfallible: makeInfallible
makeInfallible: makeInfallible,
yieldingEach: yieldingEach
};

View File

@ -30,3 +30,23 @@ JSON data is exported, and we can load package.json as a module.
6. Copy the estraverse.js that escodegen depends on into our tree:
$ cp node_modules/estraverse/estraverse.js /path/to/mozilla-central/devtools/escodegen/estraverse.js
7. Build the version of the escodegen that we can use in workers:
First we need to alias `self` as `window`:
$ echo 'let window = self;' >> /path/to/mozilla-central/toolkit/devtools/escodegen/escodegen.worker.js
Then we need to add the browser build of the source map library:
$ git clone https://github.com/mozilla/source-map
$ cd source-map
$ git co <latest release tag compatible with escodegen>
$ npm run-script build
$ cat dist/source-map.js >> /path/to/mozilla-central/toolkit/devtools/escodegen/escodegen.worker.js
Then we need to build the browser version of escodegen:
$ cd /path/to/escodegen
$ npm run-script build
$ cat escodegen.browser.js >> /path/to/mozilla-central/toolkit/devtools/escodegen/escodegen.worker.js

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ JS_MODULES_PATH = 'modules/devtools/escodegen'
EXTRA_JS_MODULES += [
'escodegen.js',
'escodegen.worker.js',
'estraverse.js',
'package.json.js',
]

View File

@ -0,0 +1,54 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; js-indent-level: 2; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This file is meant to be loaded as a ChromeWorker. It accepts messages which
* have data of the form:
*
* { id, url, indent, ast }
*
* Where `id` is a unique ID to identify this request, `url` is the url of the
* source being pretty printed, `indent` is the number of spaces to indent the
* code by, and `ast` is the source's abstract syntax tree.
*
* On success, the worker responds with a message of the form:
*
* { id, code, mappings }
*
* Where `id` is the same unique ID from the request, `code` is the pretty
* printed source text, and `mappings` is an array or source mappings from the
* pretty printed code to the AST's source locations.
*
* In the case of an error, the worker responds with a message of the form:
*
* { error }
*/
importScripts("resource://gre/modules/devtools/escodegen/escodegen.worker.js");
self.onmessage = ({ data: { id, url, indent, ast } }) => {
try {
const prettified = escodegen.generate(ast, {
format: {
indent: {
style: " ".repeat(indent)
}
},
sourceMap: url,
sourceMapWithCode: true
});
self.postMessage({
id: id,
code: prettified.code,
mappings: prettified.map._mappings
});
} catch (e) {
self.postMessage({
error: e.message + "\n" + e.stack
});
}
};

View File

@ -468,6 +468,38 @@ ThreadActor.prototype = {
return this._sources;
},
_prettyPrintWorker: null,
get prettyPrintWorker() {
if (!this._prettyPrintWorker) {
this._prettyPrintWorker = new ChromeWorker(
"resource://gre/modules/devtools/server/actors/pretty-print-worker.js");
this._prettyPrintWorker.addEventListener(
"error", this._onPrettyPrintError, false);
if (wantLogging) {
this._prettyPrintWorker.addEventListener("message", this._onPrettyPrintMsg, false);
const postMsg = this._prettyPrintWorker.postMessage;
this._prettyPrintWorker.postMessage = data => {
dumpn("Sending message to prettyPrintWorker: "
+ JSON.stringify(data, null, 2) + "\n");
return postMsg.call(this._prettyPrintWorker, data);
};
}
}
return this._prettyPrintWorker;
},
_onPrettyPrintError: function (error) {
reportError(new Error(error));
},
_onPrettyPrintMsg: function ({ data }) {
dumpn("Received message from prettyPrintWorker: "
+ JSON.stringify(data, null, 2) + "\n");
},
/**
* Keep track of all of the nested event loops we use to pause the debuggee
* when we hit a breakpoint/debugger statement/etc in one place so we can
@ -599,6 +631,15 @@ ThreadActor.prototype = {
this.clearDebuggees();
if (this._prettyPrintWorker) {
this._prettyPrintWorker.removeEventListener(
"error", this._onPrettyPrintError, false);
this._prettyPrintWorker.removeEventListener(
"message", this._onPrettyPrintMsg, false);
this._prettyPrintWorker.terminate();
this._prettyPrintWorker = null;
}
if (!this.dbg) {
return;
}
@ -2337,6 +2378,10 @@ SourceActor.prototype = {
get threadActor() this._threadActor,
get url() this._url,
get prettyPrintWorker() {
return this.threadActor.prettyPrintWorker;
},
form: function SA_form() {
return {
actor: this.actorID,
@ -2403,7 +2448,7 @@ SourceActor.prototype = {
onPrettyPrint: function ({ indent }) {
return this._getSourceText()
.then(this._parseAST)
.then(this._generatePrettyCodeAndMap(indent))
.then(this._sendToPrettyPrintWorker(indent))
.then(this._invertSourceMap)
.then(this._saveMap)
.then(this.onSource)
@ -2422,23 +2467,45 @@ SourceActor.prototype = {
},
/**
* Take the number of spaces to indent and return a function that takes an AST
* and generates code and a source map from the ugly code to the pretty code.
* Return a function that sends a request to the pretty print worker, waits on
* the worker's response, and then returns the pretty printed code.
*
* @param Number aIndent
* The number of spaces to indent by the code by, when we send the
* request to the pretty print worker.
* @returns Function
* Returns a function which takes an AST, and returns a promise that
* is resolved with `{ code, mappings }` where `code` is the pretty
* printed code, and `mappings` is an array of source mappings.
*/
_generatePrettyCodeAndMap: function SA__generatePrettyCodeAndMap(aNumSpaces) {
let indent = "";
for (let i = 0; i < aNumSpaces; i++) {
indent += " ";
}
return aAST => escodegen.generate(aAST, {
format: {
indent: {
style: indent
_sendToPrettyPrintWorker: function SA__sendToPrettyPrintWorker(aIndent) {
return aAST => {
const deferred = promise.defer();
const id = Math.random();
const onReply = ({ data }) => {
if (data.id !== id) {
return;
}
},
sourceMap: this._url,
sourceMapWithCode: true
});
this.prettyPrintWorker.removeEventListener("message", onReply, false);
if (data.error) {
deferred.reject(new Error(data.error));
} else {
deferred.resolve(data);
}
};
this.prettyPrintWorker.addEventListener("message", onReply, false);
this.prettyPrintWorker.postMessage({
id: id,
url: this._url,
indent: aIndent,
ast: aAST
});
return deferred.promise;
};
},
/**
@ -2449,35 +2516,55 @@ SourceActor.prototype = {
*
* Note that the source map is modified in place.
*/
_invertSourceMap: function SA__invertSourceMap({ code, map }) {
// XXX bug 918802: Monkey punch the source map consumer, because iterating
// over all mappings and inverting each of them, and then creating a new
// SourceMapConsumer is *way* too slow.
_invertSourceMap: function SA__invertSourceMap({ code, mappings }) {
const generator = new SourceMapGenerator({ file: this._url });
return DevToolsUtils.yieldingEach(mappings, m => {
let mapping = {
generated: {
line: m.generatedLine,
column: m.generatedColumn
}
};
if (m.source) {
mapping.source = m.source;
mapping.original = {
line: m.originalLine,
column: m.originalColumn
};
mapping.name = m.name;
}
generator.addMapping(mapping);
}).then(() => {
generator.setSourceContent(this._url, code);
const consumer = SourceMapConsumer.fromSourceMap(generator);
map.setSourceContent(this._url, code);
const consumer = new SourceMapConsumer.fromSourceMap(map);
const getOrigPos = consumer.originalPositionFor.bind(consumer);
const getGenPos = consumer.generatedPositionFor.bind(consumer);
// XXX bug 918802: Monkey punch the source map consumer, because iterating
// over all mappings and inverting each of them, and then creating a new
// SourceMapConsumer is slow.
consumer.originalPositionFor = ({ line, column }) => {
const location = getGenPos({
const getOrigPos = consumer.originalPositionFor.bind(consumer);
const getGenPos = consumer.generatedPositionFor.bind(consumer);
consumer.originalPositionFor = ({ line, column }) => {
const location = getGenPos({
line: line,
column: column,
source: this._url
});
location.source = this._url;
return location;
};
consumer.generatedPositionFor = ({ line, column }) => getOrigPos({
line: line,
column: column,
source: this._url
column: column
});
location.source = this._url;
return location;
};
consumer.generatedPositionFor = ({ line, column }) => getOrigPos({
line: line,
column: column
return {
code: code,
map: consumer
};
});
return {
code: code,
map: consumer
};
},
/**

View File

@ -37,6 +37,7 @@ var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
var gGlobal = Cu.Sandbox(systemPrincipal);
gGlobal.ChromeWorker = ChromeWorker;
Cu.evalInSandbox(loadSubScript, gGlobal, "1.8");
gGlobal.loadSubScript("resource://gre/modules/devtools/server/main.js");

View File

@ -1,42 +0,0 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var gDebuggee;
var gClient;
var gThreadClient;
// Test basic pretty printing functionality
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-pretty-print");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-pretty-print", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
evalCode();
});
});
do_test_pending();
}
function evalCode() {
gClient.addOneTimeListener("newSource", prettyPrintSource);
const code = "" + function main() { let a = 1 + 3; let b = a++; return b + a; };
Cu.evalInSandbox(
code,
gDebuggee,
"1.8",
"data:text/javascript," + code);
}
function prettyPrintSource(event, { source }) {
gThreadClient.source(source).prettyPrint(4, testPrettyPrinted);
}
function testPrettyPrinted({ error, source}) {
do_check_true(!error);
do_check_true(source.contains("\n "));
finishClient(gClient);
}

View File

@ -1,94 +0,0 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
var gDebuggee;
var gClient;
var gThreadClient;
var gSource;
// Test stepping through pretty printed sources.
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-pretty-print");
gDebuggee.noop = x => x;
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-pretty-print", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
evalCode();
});
});
do_test_pending();
}
const CODE = "" + function main() { var a = 1; debugger; noop(a); return 10; };
const CODE_URL = "data:text/javascript," + CODE;
const BP_LOCATION = {
url: CODE_URL,
line: 5,
column: 2
};
function evalCode() {
gClient.addOneTimeListener("newSource", prettyPrintSource);
Cu.evalInSandbox(
CODE,
gDebuggee,
"1.8",
CODE_URL,
1
);
}
function prettyPrintSource(event, { source }) {
gSource = source;
gThreadClient.source(gSource).prettyPrint(2, runCode);
}
function runCode({ error }) {
do_check_true(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.main();
}
function testDbgStatement(event, { why, frame }) {
do_check_eq(why.type, "debuggerStatement");
const { url, line, column } = frame.where;
do_check_eq(url, CODE_URL);
do_check_eq(line, 3);
setBreakpoint();
}
function setBreakpoint() {
gThreadClient.setBreakpoint(BP_LOCATION, ({ error, actualLocation }) => {
do_check_true(!error);
do_check_true(!actualLocation);
testStepping();
});
}
function testStepping() {
gClient.addOneTimeListener("paused", (event, { why, frame }) => {
do_check_eq(why.type, "resumeLimit");
const { url, line } = frame.where;
do_check_eq(url, CODE_URL);
do_check_eq(line, 4);
testHitBreakpoint();
});
gThreadClient.stepIn();
}
function testHitBreakpoint() {
gClient.addOneTimeListener("paused", (event, { why, frame }) => {
do_check_eq(why.type, "breakpoint");
const { url, line, column } = frame.where;
do_check_eq(url, CODE_URL);
do_check_eq(line, BP_LOCATION.line);
do_check_eq(column, BP_LOCATION.column);
finishClient(gClient);
});
gThreadClient.resume();
}

View File

@ -1,74 +0,0 @@
/* -*- Mode: javascript; js-indent-level: 2; -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Test pretty printing source mapped sources.
var gDebuggee;
var gClient;
var gThreadClient;
var gSource;
Components.utils.import('resource:///modules/devtools/SourceMap.jsm');
function run_test() {
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-pretty-print");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestTabAndResume(gClient, "test-pretty-print", function(aResponse, aTabClient, aThreadClient) {
gThreadClient = aThreadClient;
evalCode();
});
});
do_test_pending();
}
const dataUrl = s => "data:text/javascript," + s;
const A = "function a(){b()}";
const A_URL = dataUrl(A);
const B = "function b(){debugger}";
const B_URL = dataUrl(B);
function evalCode() {
let { code, map } = (new SourceNode(null, null, null, [
new SourceNode(1, 0, A_URL, A),
B.split("").map((ch, i) => new SourceNode(1, i, B_URL, ch))
])).toStringWithSourceMap({
file: "abc.js"
});
code += "//# sourceMappingURL=data:text/json;base64," + btoa(map.toString());
gClient.addListener("newSource", waitForB);
Components.utils.evalInSandbox(code, gDebuggee, "1.8",
"http://example.com/foo.js", 1);
}
function waitForB(event, { source }) {
if (source.url !== B_URL) {
return;
}
gClient.removeListener("newSource", waitForB);
prettyPrint(source);
}
function prettyPrint(source) {
gThreadClient.source(source).prettyPrint(2, runCode);
}
function runCode({ error }) {
do_check_true(!error);
gClient.addOneTimeListener("paused", testDbgStatement);
gDebuggee.a();
}
function testDbgStatement(event, { frame, why }) {
do_check_eq(why.type, "debuggerStatement");
const { url, line, column } = frame.where;
do_check_eq(url, B_URL);
do_check_eq(line, 2);
do_check_eq(column, 2);
finishClient(gClient);
}

View File

@ -167,9 +167,6 @@ reason = bug 820380
[test_longstringactor.js]
[test_longstringgrips-01.js]
[test_longstringgrips-02.js]
[test_pretty_print-01.js]
[test_pretty_print-02.js]
[test_pretty_print-03.js]
[test_source-01.js]
skip-if = toolkit == "gonk"
reason = bug 820380

View File

@ -59,7 +59,7 @@ this.DebuggerTransport = function DebuggerTransport(aInput, aOutput)
DebuggerTransport.prototype = {
/**
* Transmit a packet.
*
*
* This method returns immediately, without waiting for the entire
* packet to be transmitted, registering event handlers as needed to
* transmit the entire packet. Packets are transmitted in the order

View File

@ -1258,7 +1258,12 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
return;
}
else {
throw new Error('Invalid mapping.');
throw new Error('Invalid mapping: ' + JSON.stringify({
generated: aGenerated,
source: aSource,
orginal: aOriginal,
name: aName
}));
}
};
@ -1335,6 +1340,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so
SourceMapGenerator.prototype._generateSourcesContent =
function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
return aSources.map(function (source) {
if (!this._sourcesContents) {
return null;
}
if (aSourceRoot) {
source = util.relative(aSourceRoot, source);
}