зеркало из https://github.com/mozilla/gecko-dev.git
Bug 918802 - Move the debugger's pretty printing into a worker; r=past
This commit is contained in:
Родитель
892a11e37a
Коммит
afd341c3a7
|
@ -20,6 +20,9 @@ support-files =
|
|||
code_script-switching-02.js
|
||||
code_test-editor-mode
|
||||
code_ugly.js
|
||||
code_ugly-2.js
|
||||
code_ugly-3.js
|
||||
code_ugly-4.js
|
||||
doc_binary_search.html
|
||||
doc_blackboxing.html
|
||||
doc_closures.html
|
||||
|
@ -41,6 +44,7 @@ support-files =
|
|||
doc_minified.html
|
||||
doc_pause-exceptions.html
|
||||
doc_pretty-print.html
|
||||
doc_pretty-print-2.html
|
||||
doc_recursion-stack.html
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
|
@ -104,6 +108,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]
|
||||
|
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -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;
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
function main2() { var a = 1 + 3; var b = a++; return b + a; }
|
|
@ -0,0 +1 @@
|
|||
function main3() { var a = 1; debugger; noop(a); return 10; };
|
|
@ -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));
|
||||
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -21,5 +21,6 @@ Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
|||
this.DevToolsUtils = {
|
||||
safeErrorString: safeErrorString,
|
||||
reportException: reportException,
|
||||
makeInfallible: makeInfallible
|
||||
makeInfallible: makeInfallible,
|
||||
yieldingEach: yieldingEach
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -10,6 +10,7 @@ JS_MODULES_PATH = 'modules/devtools/escodegen'
|
|||
|
||||
EXTRA_JS_MODULES += [
|
||||
'escodegen.js',
|
||||
'escodegen.worker.js',
|
||||
'estraverse.js',
|
||||
'package.json.js',
|
||||
]
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
};
|
|
@ -467,6 +467,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
|
||||
|
@ -598,6 +630,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;
|
||||
}
|
||||
|
@ -2335,6 +2376,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,
|
||||
|
@ -2401,7 +2446,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)
|
||||
|
@ -2420,23 +2465,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 += " ";
|
||||
_sendToPrettyPrintWorker: function SA__sendToPrettyPrintWorker(aIndent) {
|
||||
return aAST => {
|
||||
const deferred = promise.defer();
|
||||
const id = Math.random();
|
||||
|
||||
const onReply = ({ data }) => {
|
||||
if (data.id !== id) {
|
||||
return;
|
||||
}
|
||||
return aAST => escodegen.generate(aAST, {
|
||||
format: {
|
||||
indent: {
|
||||
style: indent
|
||||
this.prettyPrintWorker.removeEventListener("message", onReply, false);
|
||||
|
||||
if (data.error) {
|
||||
deferred.reject(new Error(data.error));
|
||||
} else {
|
||||
deferred.resolve(data);
|
||||
}
|
||||
},
|
||||
sourceMap: this._url,
|
||||
sourceMapWithCode: true
|
||||
};
|
||||
|
||||
this.prettyPrintWorker.addEventListener("message", onReply, false);
|
||||
this.prettyPrintWorker.postMessage({
|
||||
id: id,
|
||||
url: this._url,
|
||||
indent: aIndent,
|
||||
ast: aAST
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -2447,13 +2514,32 @@ SourceActor.prototype = {
|
|||
*
|
||||
* Note that the source map is modified in place.
|
||||
*/
|
||||
_invertSourceMap: function SA__invertSourceMap({ code, map }) {
|
||||
_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);
|
||||
|
||||
// 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.
|
||||
// SourceMapConsumer is slow.
|
||||
|
||||
map.setSourceContent(this._url, code);
|
||||
const consumer = new SourceMapConsumer.fromSourceMap(map);
|
||||
const getOrigPos = consumer.originalPositionFor.bind(consumer);
|
||||
const getGenPos = consumer.generatedPositionFor.bind(consumer);
|
||||
|
||||
|
@ -2476,6 +2562,7 @@ SourceActor.prototype = {
|
|||
code: code,
|
||||
map: consumer
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -169,9 +169,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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче