зеркало из https://github.com/mozilla/gecko-dev.git
Back out 5b70e599577c (bug 930141) for test_loader_paths.html bustage
This commit is contained in:
Родитель
4933bbe732
Коммит
af558e50f1
|
@ -80,9 +80,10 @@ function testStepping() {
|
|||
function testHitBreakpoint() {
|
||||
gClient.addOneTimeListener("paused", (event, { why, frame }) => {
|
||||
is(why.type, "breakpoint");
|
||||
const { url, line } = frame.where;
|
||||
const { url, line, column } = frame.where;
|
||||
is(url, CODE_URL);
|
||||
is(line, BP_LOCATION.line);
|
||||
is(column, BP_LOCATION.column);
|
||||
|
||||
resumeDebuggerThenCloseAndFinish(gPanel);
|
||||
});
|
||||
|
|
|
@ -55,9 +55,10 @@ function runCode({ error }) {
|
|||
|
||||
function testDbgStatement(event, { frame, why }) {
|
||||
is(why.type, "debuggerStatement");
|
||||
const { url, line } = frame.where;
|
||||
const { url, line, column } = frame.where;
|
||||
is(url, B_URL);
|
||||
is(line, 2);
|
||||
is(column, 2);
|
||||
|
||||
disablePrettyPrint();
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ const VARIABLES_VIEW_URL = "chrome://browser/content/devtools/widgets/VariablesV
|
|||
const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
const promise = require("sdk/core/promise");
|
||||
const Telemetry = require("devtools/shared/telemetry");
|
||||
const escodegen = require("escodegen/escodegen");
|
||||
const Editor = require("devtools/sourceeditor/editor");
|
||||
const TargetFactory = require("devtools/framework/target").TargetFactory;
|
||||
|
||||
|
@ -515,61 +516,25 @@ var Scratchpad = {
|
|||
return deferred.promise;
|
||||
},
|
||||
|
||||
_prettyPrintWorker: null,
|
||||
|
||||
/**
|
||||
* Get or create the worker that handles pretty printing.
|
||||
*/
|
||||
get prettyPrintWorker() {
|
||||
if (!this._prettyPrintWorker) {
|
||||
this._prettyPrintWorker = new ChromeWorker(
|
||||
"resource://gre/modules/devtools/server/actors/pretty-print-worker.js");
|
||||
|
||||
this._prettyPrintWorker.addEventListener("error", ({ message, filename, lineno }) => {
|
||||
DevToolsUtils.reportException(message + " @ " + filename + ":" + lineno);
|
||||
}, false);
|
||||
}
|
||||
return this._prettyPrintWorker;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pretty print the source text inside the scratchpad.
|
||||
*
|
||||
* @return Promise
|
||||
* A promise resolved with the pretty printed code, or rejected with
|
||||
* an error.
|
||||
*/
|
||||
prettyPrint: function SP_prettyPrint() {
|
||||
const uglyText = this.getText();
|
||||
const tabsize = Services.prefs.getIntPref("devtools.editor.tabsize");
|
||||
const id = Math.random();
|
||||
const deferred = promise.defer();
|
||||
|
||||
const onReply = ({ data }) => {
|
||||
if (data.id !== id) {
|
||||
return;
|
||||
}
|
||||
this.prettyPrintWorker.removeEventListener("message", onReply, false);
|
||||
|
||||
if (data.error) {
|
||||
let errorString = DevToolsUtils.safeErrorString(data.error);
|
||||
this.writeAsErrorComment(errorString);
|
||||
deferred.reject(errorString);
|
||||
} else {
|
||||
this.editor.setText(data.code);
|
||||
deferred.resolve(data.code);
|
||||
}
|
||||
};
|
||||
|
||||
this.prettyPrintWorker.addEventListener("message", onReply, false);
|
||||
this.prettyPrintWorker.postMessage({
|
||||
id: id,
|
||||
url: "(scratchpad)",
|
||||
indent: tabsize,
|
||||
source: uglyText
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
try {
|
||||
const ast = Reflect.parse(uglyText);
|
||||
const prettyText = escodegen.generate(ast, {
|
||||
format: {
|
||||
indent: {
|
||||
style: " ".repeat(tabsize)
|
||||
}
|
||||
}
|
||||
});
|
||||
this.editor.setText(prettyText);
|
||||
} catch (e) {
|
||||
this.writeAsErrorComment(DevToolsUtils.safeErrorString(e));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1410,11 +1375,6 @@ var Scratchpad = {
|
|||
this._sidebar = null;
|
||||
}
|
||||
|
||||
if (this._prettyPrintWorker) {
|
||||
this._prettyPrintWorker.terminate();
|
||||
this._prettyPrintWorker = null;
|
||||
}
|
||||
|
||||
scratchpadTargets = null;
|
||||
this.webConsoleClient = null;
|
||||
this.debuggerClient = null;
|
||||
|
|
|
@ -28,9 +28,10 @@ support-files = head.js
|
|||
[browser_scratchpad_long_string.js]
|
||||
[browser_scratchpad_open.js]
|
||||
[browser_scratchpad_open_error_console.js]
|
||||
# Disabled, as escodegen is being replaced - bug 930141
|
||||
# [browser_scratchpad_pprint-02.js]
|
||||
# [browser_scratchpad_pprint.js]
|
||||
[browser_scratchpad_throw_output.js]
|
||||
[browser_scratchpad_pprint-02.js]
|
||||
[browser_scratchpad_pprint.js]
|
||||
[browser_scratchpad_restore.js]
|
||||
[browser_scratchpad_tab_switch.js]
|
||||
[browser_scratchpad_ui.js]
|
||||
|
|
|
@ -15,26 +15,18 @@ function test()
|
|||
content.location = "data:text/html;charset=utf8,test Scratchpad pretty print.";
|
||||
}
|
||||
|
||||
let gTabsize;
|
||||
|
||||
function runTests(sw)
|
||||
{
|
||||
gTabsize = Services.prefs.getIntPref("devtools.editor.tabsize");
|
||||
const tabsize = Services.prefs.getIntPref("devtools.editor.tabsize");
|
||||
Services.prefs.setIntPref("devtools.editor.tabsize", 6);
|
||||
const space = " ".repeat(6);
|
||||
|
||||
const sp = sw.Scratchpad;
|
||||
sp.setText("function main() { console.log(5); }");
|
||||
sp.prettyPrint().then(() => {
|
||||
const prettyText = sp.getText();
|
||||
ok(prettyText.contains(space));
|
||||
finish();
|
||||
}).then(null, error => {
|
||||
ok(false, error);
|
||||
});
|
||||
}
|
||||
sp.prettyPrint();
|
||||
const prettyText = sp.getText();
|
||||
ok(prettyText.contains(space));
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
Services.prefs.setIntPref("devtools.editor.tabsize", gTabsize);
|
||||
gTabsize = null;
|
||||
});
|
||||
Services.prefs.setIntPref("devtools.editor.tabsize", tabsize);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -19,11 +19,8 @@ function runTests(sw)
|
|||
{
|
||||
const sp = sw.Scratchpad;
|
||||
sp.setText("function main() { console.log(5); }");
|
||||
sp.prettyPrint().then(() => {
|
||||
const prettyText = sp.getText();
|
||||
ok(prettyText.contains("\n"));
|
||||
finish();
|
||||
}).then(null, error => {
|
||||
ok(false, error);
|
||||
});
|
||||
sp.prettyPrint();
|
||||
const prettyText = sp.getText();
|
||||
ok(prettyText.contains("\n"));
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -64,10 +64,10 @@ var BuiltinProvider = {
|
|||
"devtools/output-parser": "resource://gre/modules/devtools/output-parser",
|
||||
"devtools/touch-events": "resource://gre/modules/devtools/touch-events",
|
||||
"devtools/client": "resource://gre/modules/devtools/client",
|
||||
"devtools/pretty-fast": "resource://gre/modules/devtools/pretty-fast.js",
|
||||
|
||||
"acorn": "resource://gre/modules/devtools/acorn.js",
|
||||
"acorn_loose": "resource://gre/modules/devtools/acorn_loose.js",
|
||||
"acorn": "resource://gre/modules/devtools/acorn",
|
||||
"escodegen": "resource://gre/modules/devtools/escodegen",
|
||||
"estraverse": "resource://gre/modules/devtools/escodegen/estraverse",
|
||||
|
||||
// Allow access to xpcshell test items from the loader.
|
||||
"xpcshell-test": "resource://test"
|
||||
|
@ -109,8 +109,9 @@ var SrcdirProvider = {
|
|||
let outputParserURI = this.fileURI(OS.Path.join(toolkitDir, "output-parser"));
|
||||
let touchEventsURI = this.fileURI(OS.Path.join(toolkitDir, "touch-events"));
|
||||
let clientURI = this.fileURI(OS.Path.join(toolkitDir, "client"));
|
||||
let prettyFastURI = this.fileURI(OS.Path.join(toolkitDir), "pretty-fast.js");
|
||||
let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
|
||||
let escodegenURI = this.fileURI(OS.Path.join(toolkitDir, "escodegen"));
|
||||
let estraverseURI = this.fileURI(OS.Path.join(toolkitDir, "escodegen", "estraverse"));
|
||||
this.loader = new loader.Loader({
|
||||
modules: {
|
||||
"toolkit/loader": loader,
|
||||
|
@ -128,9 +129,9 @@ var SrcdirProvider = {
|
|||
"devtools/output-parser": outputParserURI,
|
||||
"devtools/touch-events": touchEventsURI,
|
||||
"devtools/client": clientURI,
|
||||
"devtools/pretty-fast": prettyFastURI,
|
||||
|
||||
"acorn": acornURI,
|
||||
"escodegen": escodegenURI,
|
||||
"estraverse": estraverseURI
|
||||
},
|
||||
globals: loaderGlobals
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
JS_MODULES_PATH = 'modules/devtools'
|
||||
JS_MODULES_PATH = 'modules/devtools/acorn'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'acorn.js',
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
*/
|
||||
|
||||
function run_test() {
|
||||
const acorn = require("acorn");
|
||||
const acorn_loose = require("acorn_loose");
|
||||
const acorn = require("acorn/acorn");
|
||||
const acorn_loose = require("acorn/acorn_loose");
|
||||
do_check_true(isObject(acorn));
|
||||
do_check_true(isObject(acorn_loose));
|
||||
do_check_eq(typeof acorn.parse, "function");
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Test that acorn's lenient parser gives something usable.
|
||||
*/
|
||||
|
||||
const acorn_loose = require("acorn_loose");
|
||||
const acorn_loose = require("acorn/acorn_loose");
|
||||
|
||||
function run_test() {
|
||||
let actualAST = acorn_loose.parse_dammit("let x = 10");
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Test that Reflect and acorn create the same AST for ES5.
|
||||
*/
|
||||
|
||||
const acorn = require("acorn");
|
||||
const acorn = require("acorn/acorn");
|
||||
Cu.import("resource://gre/modules/reflect.jsm");
|
||||
|
||||
const testCode = "" + function main () {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,54 @@
|
|||
Assuming that escodegen's dependencies have not changed, to upgrade our tree's
|
||||
escodegen to a new version:
|
||||
|
||||
1. Clone the escodegen repository, and check out the version you want to upgrade
|
||||
to:
|
||||
|
||||
$ git clone https://github.com/Constellation/escodegen.git
|
||||
$ cd escodegen
|
||||
$ git checkout <version>
|
||||
|
||||
2. Make sure that all tests pass:
|
||||
|
||||
$ npm install .
|
||||
$ npm test
|
||||
|
||||
If there are any test failures, do not upgrade to that version of escodegen!
|
||||
|
||||
3. Copy escodegen.js to our tree:
|
||||
|
||||
$ cp escodegen.js /path/to/mozilla-central/toolkit/devtools/escodegen/escodegen.js
|
||||
|
||||
4. Copy the package.json to our tree, and append ".js" to make it work with our
|
||||
loader:
|
||||
|
||||
$ cp package.json /path/to/mozilla-central/toolkit/devtools/escodegen/package.json.js
|
||||
|
||||
5. Prepend `module.exports = ` to the package.json file contents, so that the
|
||||
JSON data is exported, and we can load package.json as a module.
|
||||
|
||||
Bug 933482: Note, this is a workaround for Bug 910594, which will allow the SDK loader to require JSON files. To remove ambiguity, comment out the `require('./package.json').version` line in `escodegen.js` so that when Bug 910594 is uplifted into central, it does not attempt to look for `package.json`, rather than `package.json.js`. This is a temporary workaround, and once Bug 933500 is solved, either `package.json` or `package.json.js` will work.
|
||||
|
||||
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
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/*jslint vars:false*/
|
||||
/*jshint indent:4*/
|
||||
/*global exports:true, define:true*/
|
||||
(function (root, factory) {
|
||||
'use strict';
|
||||
|
||||
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
|
||||
// and plain browser loading,
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['exports'], factory);
|
||||
} else if (typeof exports !== 'undefined') {
|
||||
factory(exports);
|
||||
} else {
|
||||
factory((root.estraverse = {}));
|
||||
}
|
||||
}(this, function (exports) {
|
||||
'use strict';
|
||||
|
||||
var Syntax,
|
||||
isArray,
|
||||
VisitorOption,
|
||||
VisitorKeys,
|
||||
BREAK,
|
||||
SKIP;
|
||||
|
||||
Syntax = {
|
||||
AssignmentExpression: 'AssignmentExpression',
|
||||
ArrayExpression: 'ArrayExpression',
|
||||
BlockStatement: 'BlockStatement',
|
||||
BinaryExpression: 'BinaryExpression',
|
||||
BreakStatement: 'BreakStatement',
|
||||
CallExpression: 'CallExpression',
|
||||
CatchClause: 'CatchClause',
|
||||
ConditionalExpression: 'ConditionalExpression',
|
||||
ContinueStatement: 'ContinueStatement',
|
||||
DebuggerStatement: 'DebuggerStatement',
|
||||
DirectiveStatement: 'DirectiveStatement',
|
||||
DoWhileStatement: 'DoWhileStatement',
|
||||
EmptyStatement: 'EmptyStatement',
|
||||
ExpressionStatement: 'ExpressionStatement',
|
||||
ForStatement: 'ForStatement',
|
||||
ForInStatement: 'ForInStatement',
|
||||
FunctionDeclaration: 'FunctionDeclaration',
|
||||
FunctionExpression: 'FunctionExpression',
|
||||
Identifier: 'Identifier',
|
||||
IfStatement: 'IfStatement',
|
||||
Literal: 'Literal',
|
||||
LabeledStatement: 'LabeledStatement',
|
||||
LogicalExpression: 'LogicalExpression',
|
||||
MemberExpression: 'MemberExpression',
|
||||
NewExpression: 'NewExpression',
|
||||
ObjectExpression: 'ObjectExpression',
|
||||
Program: 'Program',
|
||||
Property: 'Property',
|
||||
ReturnStatement: 'ReturnStatement',
|
||||
SequenceExpression: 'SequenceExpression',
|
||||
SwitchStatement: 'SwitchStatement',
|
||||
SwitchCase: 'SwitchCase',
|
||||
ThisExpression: 'ThisExpression',
|
||||
ThrowStatement: 'ThrowStatement',
|
||||
TryStatement: 'TryStatement',
|
||||
UnaryExpression: 'UnaryExpression',
|
||||
UpdateExpression: 'UpdateExpression',
|
||||
VariableDeclaration: 'VariableDeclaration',
|
||||
VariableDeclarator: 'VariableDeclarator',
|
||||
WhileStatement: 'WhileStatement',
|
||||
WithStatement: 'WithStatement',
|
||||
YieldExpression: 'YieldExpression'
|
||||
};
|
||||
|
||||
function ignoreJSHintError() { }
|
||||
|
||||
isArray = Array.isArray;
|
||||
if (!isArray) {
|
||||
isArray = function isArray(array) {
|
||||
return Object.prototype.toString.call(array) === '[object Array]';
|
||||
};
|
||||
}
|
||||
|
||||
function deepCopy(obj) {
|
||||
var ret = {}, key, val;
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
val = obj[key];
|
||||
if (typeof val === 'object' && val !== null) {
|
||||
ret[key] = deepCopy(val);
|
||||
} else {
|
||||
ret[key] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function shallowCopy(obj) {
|
||||
var ret = {}, key;
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
ret[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ignoreJSHintError(shallowCopy);
|
||||
|
||||
// based on LLVM libc++ upper_bound / lower_bound
|
||||
// MIT License
|
||||
|
||||
function upperBound(array, func) {
|
||||
var diff, len, i, current;
|
||||
|
||||
len = array.length;
|
||||
i = 0;
|
||||
|
||||
while (len) {
|
||||
diff = len >>> 1;
|
||||
current = i + diff;
|
||||
if (func(array[current])) {
|
||||
len = diff;
|
||||
} else {
|
||||
i = current + 1;
|
||||
len -= diff + 1;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function lowerBound(array, func) {
|
||||
var diff, len, i, current;
|
||||
|
||||
len = array.length;
|
||||
i = 0;
|
||||
|
||||
while (len) {
|
||||
diff = len >>> 1;
|
||||
current = i + diff;
|
||||
if (func(array[current])) {
|
||||
i = current + 1;
|
||||
len -= diff + 1;
|
||||
} else {
|
||||
len = diff;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
ignoreJSHintError(lowerBound);
|
||||
|
||||
VisitorKeys = {
|
||||
AssignmentExpression: ['left', 'right'],
|
||||
ArrayExpression: ['elements'],
|
||||
BlockStatement: ['body'],
|
||||
BinaryExpression: ['left', 'right'],
|
||||
BreakStatement: ['label'],
|
||||
CallExpression: ['callee', 'arguments'],
|
||||
CatchClause: ['param', 'body'],
|
||||
ConditionalExpression: ['test', 'consequent', 'alternate'],
|
||||
ContinueStatement: ['label'],
|
||||
DebuggerStatement: [],
|
||||
DirectiveStatement: [],
|
||||
DoWhileStatement: ['body', 'test'],
|
||||
EmptyStatement: [],
|
||||
ExpressionStatement: ['expression'],
|
||||
ForStatement: ['init', 'test', 'update', 'body'],
|
||||
ForInStatement: ['left', 'right', 'body'],
|
||||
FunctionDeclaration: ['id', 'params', 'body'],
|
||||
FunctionExpression: ['id', 'params', 'body'],
|
||||
Identifier: [],
|
||||
IfStatement: ['test', 'consequent', 'alternate'],
|
||||
Literal: [],
|
||||
LabeledStatement: ['label', 'body'],
|
||||
LogicalExpression: ['left', 'right'],
|
||||
MemberExpression: ['object', 'property'],
|
||||
NewExpression: ['callee', 'arguments'],
|
||||
ObjectExpression: ['properties'],
|
||||
Program: ['body'],
|
||||
Property: ['key', 'value'],
|
||||
ReturnStatement: ['argument'],
|
||||
SequenceExpression: ['expressions'],
|
||||
SwitchStatement: ['discriminant', 'cases'],
|
||||
SwitchCase: ['test', 'consequent'],
|
||||
ThisExpression: [],
|
||||
ThrowStatement: ['argument'],
|
||||
TryStatement: ['block', 'handlers', 'handler', 'guardedHandlers', 'finalizer'],
|
||||
UnaryExpression: ['argument'],
|
||||
UpdateExpression: ['argument'],
|
||||
VariableDeclaration: ['declarations'],
|
||||
VariableDeclarator: ['id', 'init'],
|
||||
WhileStatement: ['test', 'body'],
|
||||
WithStatement: ['object', 'body'],
|
||||
YieldExpression: ['argument']
|
||||
};
|
||||
|
||||
// unique id
|
||||
BREAK = {};
|
||||
SKIP = {};
|
||||
|
||||
VisitorOption = {
|
||||
Break: BREAK,
|
||||
Skip: SKIP
|
||||
};
|
||||
|
||||
function Reference(parent, key) {
|
||||
this.parent = parent;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
Reference.prototype.replace = function replace(node) {
|
||||
this.parent[this.key] = node;
|
||||
};
|
||||
|
||||
function Element(node, path, wrap, ref) {
|
||||
this.node = node;
|
||||
this.path = path;
|
||||
this.wrap = wrap;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
function Controller() { }
|
||||
|
||||
// API:
|
||||
// return property path array from root to current node
|
||||
Controller.prototype.path = function path() {
|
||||
var i, iz, j, jz, result, element;
|
||||
|
||||
function addToPath(result, path) {
|
||||
if (isArray(path)) {
|
||||
for (j = 0, jz = path.length; j < jz; ++j) {
|
||||
result.push(path[j]);
|
||||
}
|
||||
} else {
|
||||
result.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
// root node
|
||||
if (!this.__current.path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// first node is sentinel, second node is root element
|
||||
result = [];
|
||||
for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
|
||||
element = this.__leavelist[i];
|
||||
addToPath(result, element.path);
|
||||
}
|
||||
addToPath(result, this.__current.path);
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// return array of parent elements
|
||||
Controller.prototype.parents = function parents() {
|
||||
var i, iz, result;
|
||||
|
||||
// first node is sentinel
|
||||
result = [];
|
||||
for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
|
||||
result.push(this.__leavelist[i].node);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// return current node
|
||||
Controller.prototype.current = function current() {
|
||||
return this.__current.node;
|
||||
};
|
||||
|
||||
Controller.prototype.__execute = function __execute(callback, element) {
|
||||
var previous, result;
|
||||
|
||||
result = undefined;
|
||||
|
||||
previous = this.__current;
|
||||
this.__current = element;
|
||||
this.__state = null;
|
||||
if (callback) {
|
||||
result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
|
||||
}
|
||||
this.__current = previous;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// API:
|
||||
// notify control skip / break
|
||||
Controller.prototype.notify = function notify(flag) {
|
||||
this.__state = flag;
|
||||
};
|
||||
|
||||
// API:
|
||||
// skip child nodes of current node
|
||||
Controller.prototype.skip = function () {
|
||||
this.notify(SKIP);
|
||||
};
|
||||
|
||||
// API:
|
||||
// break traversals
|
||||
Controller.prototype['break'] = function () {
|
||||
this.notify(BREAK);
|
||||
};
|
||||
|
||||
Controller.prototype.__initialize = function(root, visitor) {
|
||||
this.visitor = visitor;
|
||||
this.root = root;
|
||||
this.__worklist = [];
|
||||
this.__leavelist = [];
|
||||
this.__current = null;
|
||||
this.__state = null;
|
||||
};
|
||||
|
||||
Controller.prototype.traverse = function traverse(root, visitor) {
|
||||
var worklist,
|
||||
leavelist,
|
||||
element,
|
||||
node,
|
||||
nodeType,
|
||||
ret,
|
||||
key,
|
||||
current,
|
||||
current2,
|
||||
candidates,
|
||||
candidate,
|
||||
sentinel;
|
||||
|
||||
this.__initialize(root, visitor);
|
||||
|
||||
sentinel = {};
|
||||
|
||||
// reference
|
||||
worklist = this.__worklist;
|
||||
leavelist = this.__leavelist;
|
||||
|
||||
// initialize
|
||||
worklist.push(new Element(root, null, null, null));
|
||||
leavelist.push(new Element(null, null, null, null));
|
||||
|
||||
while (worklist.length) {
|
||||
element = worklist.pop();
|
||||
|
||||
if (element === sentinel) {
|
||||
element = leavelist.pop();
|
||||
|
||||
ret = this.__execute(visitor.leave, element);
|
||||
|
||||
if (this.__state === BREAK || ret === BREAK) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.node) {
|
||||
|
||||
ret = this.__execute(visitor.enter, element);
|
||||
|
||||
if (this.__state === BREAK || ret === BREAK) {
|
||||
return;
|
||||
}
|
||||
|
||||
worklist.push(sentinel);
|
||||
leavelist.push(element);
|
||||
|
||||
if (this.__state === SKIP || ret === SKIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node = element.node;
|
||||
nodeType = element.wrap || node.type;
|
||||
candidates = VisitorKeys[nodeType];
|
||||
|
||||
current = candidates.length;
|
||||
while ((current -= 1) >= 0) {
|
||||
key = candidates[current];
|
||||
candidate = node[key];
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isArray(candidate)) {
|
||||
worklist.push(new Element(candidate, key, null, null));
|
||||
continue;
|
||||
}
|
||||
|
||||
current2 = candidate.length;
|
||||
while ((current2 -= 1) >= 0) {
|
||||
if (!candidate[current2]) {
|
||||
continue;
|
||||
}
|
||||
if (nodeType === Syntax.ObjectExpression && 'properties' === candidates[current]) {
|
||||
element = new Element(candidate[current2], [key, current2], 'Property', null);
|
||||
} else {
|
||||
element = new Element(candidate[current2], [key, current2], null, null);
|
||||
}
|
||||
worklist.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Controller.prototype.replace = function replace(root, visitor) {
|
||||
var worklist,
|
||||
leavelist,
|
||||
node,
|
||||
nodeType,
|
||||
target,
|
||||
element,
|
||||
current,
|
||||
current2,
|
||||
candidates,
|
||||
candidate,
|
||||
sentinel,
|
||||
outer,
|
||||
key;
|
||||
|
||||
this.__initialize(root, visitor);
|
||||
|
||||
sentinel = {};
|
||||
|
||||
// reference
|
||||
worklist = this.__worklist;
|
||||
leavelist = this.__leavelist;
|
||||
|
||||
// initialize
|
||||
outer = {
|
||||
root: root
|
||||
};
|
||||
element = new Element(root, null, null, new Reference(outer, 'root'));
|
||||
worklist.push(element);
|
||||
leavelist.push(element);
|
||||
|
||||
while (worklist.length) {
|
||||
element = worklist.pop();
|
||||
|
||||
if (element === sentinel) {
|
||||
element = leavelist.pop();
|
||||
|
||||
target = this.__execute(visitor.leave, element);
|
||||
|
||||
// node may be replaced with null,
|
||||
// so distinguish between undefined and null in this place
|
||||
if (target !== undefined && target !== BREAK && target !== SKIP) {
|
||||
// replace
|
||||
element.ref.replace(target);
|
||||
}
|
||||
|
||||
if (this.__state === BREAK || target === BREAK) {
|
||||
return outer.root;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
target = this.__execute(visitor.enter, element);
|
||||
|
||||
// node may be replaced with null,
|
||||
// so distinguish between undefined and null in this place
|
||||
if (target !== undefined && target !== BREAK && target !== SKIP) {
|
||||
// replace
|
||||
element.ref.replace(target);
|
||||
element.node = target;
|
||||
}
|
||||
|
||||
if (this.__state === BREAK || target === BREAK) {
|
||||
return outer.root;
|
||||
}
|
||||
|
||||
// node may be null
|
||||
node = element.node;
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
worklist.push(sentinel);
|
||||
leavelist.push(element);
|
||||
|
||||
if (this.__state === SKIP || target === SKIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nodeType = element.wrap || node.type;
|
||||
candidates = VisitorKeys[nodeType];
|
||||
|
||||
current = candidates.length;
|
||||
while ((current -= 1) >= 0) {
|
||||
key = candidates[current];
|
||||
candidate = node[key];
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isArray(candidate)) {
|
||||
worklist.push(new Element(candidate, key, null, new Reference(node, key)));
|
||||
continue;
|
||||
}
|
||||
|
||||
current2 = candidate.length;
|
||||
while ((current2 -= 1) >= 0) {
|
||||
if (!candidate[current2]) {
|
||||
continue;
|
||||
}
|
||||
if (nodeType === Syntax.ObjectExpression && 'properties' === candidates[current]) {
|
||||
element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
|
||||
} else {
|
||||
element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
|
||||
}
|
||||
worklist.push(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outer.root;
|
||||
};
|
||||
|
||||
function traverse(root, visitor) {
|
||||
var controller = new Controller();
|
||||
return controller.traverse(root, visitor);
|
||||
}
|
||||
|
||||
function replace(root, visitor) {
|
||||
var controller = new Controller();
|
||||
return controller.replace(root, visitor);
|
||||
}
|
||||
|
||||
function extendCommentRange(comment, tokens) {
|
||||
var target, token;
|
||||
|
||||
target = upperBound(tokens, function search(token) {
|
||||
return token.range[0] > comment.range[0];
|
||||
});
|
||||
|
||||
comment.extendedRange = [comment.range[0], comment.range[1]];
|
||||
|
||||
if (target !== tokens.length) {
|
||||
comment.extendedRange[1] = tokens[target].range[0];
|
||||
}
|
||||
|
||||
target -= 1;
|
||||
if (target >= 0) {
|
||||
if (target < tokens.length) {
|
||||
comment.extendedRange[0] = tokens[target].range[1];
|
||||
} else if (token.length) {
|
||||
comment.extendedRange[1] = tokens[tokens.length - 1].range[0];
|
||||
}
|
||||
}
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
function attachComments(tree, providedComments, tokens) {
|
||||
// At first, we should calculate extended comment ranges.
|
||||
var comments = [], comment, len, i, cursor;
|
||||
|
||||
if (!tree.range) {
|
||||
throw new Error('attachComments needs range information');
|
||||
}
|
||||
|
||||
// tokens array is empty, we attach comments to tree as 'leadingComments'
|
||||
if (!tokens.length) {
|
||||
if (providedComments.length) {
|
||||
for (i = 0, len = providedComments.length; i < len; i += 1) {
|
||||
comment = deepCopy(providedComments[i]);
|
||||
comment.extendedRange = [0, tree.range[0]];
|
||||
comments.push(comment);
|
||||
}
|
||||
tree.leadingComments = comments;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
for (i = 0, len = providedComments.length; i < len; i += 1) {
|
||||
comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
|
||||
}
|
||||
|
||||
// This is based on John Freeman's implementation.
|
||||
cursor = 0;
|
||||
traverse(tree, {
|
||||
enter: function (node) {
|
||||
var comment;
|
||||
|
||||
while (cursor < comments.length) {
|
||||
comment = comments[cursor];
|
||||
if (comment.extendedRange[1] > node.range[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (comment.extendedRange[1] === node.range[0]) {
|
||||
if (!node.leadingComments) {
|
||||
node.leadingComments = [];
|
||||
}
|
||||
node.leadingComments.push(comment);
|
||||
comments.splice(cursor, 1);
|
||||
} else {
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// already out of owned node
|
||||
if (cursor === comments.length) {
|
||||
return VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (comments[cursor].extendedRange[0] > node.range[1]) {
|
||||
return VisitorOption.Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cursor = 0;
|
||||
traverse(tree, {
|
||||
leave: function (node) {
|
||||
var comment;
|
||||
|
||||
while (cursor < comments.length) {
|
||||
comment = comments[cursor];
|
||||
if (node.range[1] < comment.extendedRange[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (node.range[1] === comment.extendedRange[0]) {
|
||||
if (!node.trailingComments) {
|
||||
node.trailingComments = [];
|
||||
}
|
||||
node.trailingComments.push(comment);
|
||||
comments.splice(cursor, 1);
|
||||
} else {
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// already out of owned node
|
||||
if (cursor === comments.length) {
|
||||
return VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (comments[cursor].extendedRange[0] > node.range[1]) {
|
||||
return VisitorOption.Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
exports.version = '1.3.0';
|
||||
exports.Syntax = Syntax;
|
||||
exports.traverse = traverse;
|
||||
exports.replace = replace;
|
||||
exports.attachComments = attachComments;
|
||||
exports.VisitorKeys = VisitorKeys;
|
||||
exports.VisitorOption = VisitorOption;
|
||||
exports.Controller = Controller;
|
||||
}));
|
||||
/* vim: set sw=4 ts=4 et tw=80 : */
|
|
@ -6,8 +6,11 @@
|
|||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
JS_MODULES_PATH = 'modules/devtools'
|
||||
JS_MODULES_PATH = 'modules/devtools/escodegen'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'pretty-fast.js',
|
||||
'escodegen.js',
|
||||
'escodegen.worker.js',
|
||||
'estraverse.js',
|
||||
'package.json.js',
|
||||
]
|
|
@ -0,0 +1,57 @@
|
|||
module.exports = {
|
||||
"name": "escodegen",
|
||||
"description": "ECMAScript code generator",
|
||||
"homepage": "http://github.com/Constellation/escodegen.html",
|
||||
"main": "escodegen.js",
|
||||
"bin": {
|
||||
"esgenerate": "./bin/esgenerate.js",
|
||||
"escodegen": "./bin/escodegen.js"
|
||||
},
|
||||
"version": "0.0.26",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Yusuke Suzuki",
|
||||
"email": "utatane.tea@gmail.com",
|
||||
"web": "http://github.com/Constellation"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/Constellation/escodegen.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"esprima": "~1.0.2",
|
||||
"estraverse": "~1.3.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"source-map": ">= 0.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esprima-moz": "*",
|
||||
"commonjs-everywhere": "~0.8.0",
|
||||
"q": "*",
|
||||
"bower": "*",
|
||||
"semver": "*",
|
||||
"chai": "~1.7.2",
|
||||
"grunt-contrib-jshint": "~0.5.0",
|
||||
"grunt-cli": "~0.1.9",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-mocha-test": "~0.6.2"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
"type": "BSD",
|
||||
"url": "http://github.com/Constellation/escodegen/raw/master/LICENSE.BSD"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"test": "grunt travis",
|
||||
"unit-test": "grunt test",
|
||||
"lint": "grunt lint",
|
||||
"release": "node tools/release.js",
|
||||
"build": "./node_modules/.bin/cjsify -ma path: tools/entry-point.js > escodegen.browser.js"
|
||||
}
|
||||
};
|
|
@ -7,11 +7,6 @@ const Cr = Components.results;
|
|||
const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
const { require } = devtools;
|
||||
|
||||
this.sourceMap = require("source-map");
|
||||
this.acorn = require("acorn");
|
||||
this.prettyFast = require("devtools/pretty-fast");
|
||||
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
|
||||
|
||||
// Register a console listener, so console messages don't just disappear
|
||||
// into the ether.
|
||||
let errorCount = 0;
|
||||
|
@ -36,7 +31,7 @@ let listener = {
|
|||
}
|
||||
}
|
||||
|
||||
do_throw("head_pretty-fast.js got console message: " + string + "\n");
|
||||
do_throw("head_dbg.js got console message: " + string + "\n");
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can generate source maps via escodegen.
|
||||
*/
|
||||
|
||||
Components.utils.import("resource://gre/modules/reflect.jsm");
|
||||
Components.utils.import("resource://gre/modules/devtools/SourceMap.jsm");
|
||||
const escodegen = require("escodegen/escodegen");
|
||||
|
||||
const testCode = "" + function main() {
|
||||
var a = 5 + 3;
|
||||
var b = 19 * 52;
|
||||
return a / b;
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
const ast = Reflect.parse(testCode);
|
||||
const { code, map } = escodegen.generate(ast, {
|
||||
format: {
|
||||
indent: {
|
||||
// Single space indents so we are mapping different locations.
|
||||
style: " "
|
||||
}
|
||||
},
|
||||
sourceMap: "testCode.js",
|
||||
sourceMapWithCode: true
|
||||
});
|
||||
const smc = new SourceMapConsumer(map.toString());
|
||||
|
||||
let mapping = smc.originalPositionFor({
|
||||
line: 2,
|
||||
column: 1
|
||||
});
|
||||
do_check_eq(mapping.source, "testCode.js");
|
||||
do_check_eq(mapping.line, 2);
|
||||
do_check_eq(mapping.column, 2);
|
||||
|
||||
mapping = smc.originalPositionFor({
|
||||
line: 2,
|
||||
column: 5
|
||||
});
|
||||
do_check_eq(mapping.source, "testCode.js");
|
||||
do_check_eq(mapping.line, 2);
|
||||
do_check_eq(mapping.column, 6);
|
||||
|
||||
mapping = smc.originalPositionFor({
|
||||
line: 3,
|
||||
column: 1
|
||||
});
|
||||
do_check_eq(mapping.source, "testCode.js");
|
||||
do_check_eq(mapping.line, 3);
|
||||
do_check_eq(mapping.column, 2);
|
||||
|
||||
mapping = smc.originalPositionFor({
|
||||
line: 3,
|
||||
column: 5
|
||||
});
|
||||
do_check_eq(mapping.source, "testCode.js");
|
||||
do_check_eq(mapping.line, 3);
|
||||
do_check_eq(mapping.column, 6);
|
||||
|
||||
mapping = smc.originalPositionFor({
|
||||
line: 4,
|
||||
column: 1
|
||||
});
|
||||
do_check_eq(mapping.source, "testCode.js");
|
||||
do_check_eq(mapping.line, 4);
|
||||
do_check_eq(mapping.column, 2);
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can require escodegen.
|
||||
*/
|
||||
|
||||
function run_test() {
|
||||
const escodegen = require("escodegen/escodegen");
|
||||
do_check_eq(typeof escodegen.generate, "function");
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we can go AST -> JS -> AST via escodegen and Reflect.
|
||||
*/
|
||||
|
||||
const escodegen = require("escodegen/escodegen");
|
||||
Components.utils.import("resource://gre/modules/reflect.jsm");
|
||||
|
||||
const testCode = "" + function main () {
|
||||
function makeAcc(n) {
|
||||
return function () {
|
||||
return ++n;
|
||||
};
|
||||
}
|
||||
|
||||
var acc = makeAcc(10);
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
acc();
|
||||
}
|
||||
|
||||
console.log(acc());
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
const originalAST = Reflect.parse(testCode);
|
||||
const generatedCode = escodegen.generate(originalAST);
|
||||
const generatedAST = Reflect.parse(generatedCode);
|
||||
|
||||
do_print("Original AST:");
|
||||
do_print(JSON.stringify(originalAST, null, 2));
|
||||
do_print("Generated AST:");
|
||||
do_print(JSON.stringify(generatedAST, null, 2));
|
||||
|
||||
checkEquivalentASTs(originalAST, generatedAST);
|
||||
}
|
||||
|
||||
const isObject = (obj) => typeof obj === "object" && obj !== null;
|
||||
const zip = (a, b) => {
|
||||
let pairs = [];
|
||||
for (let i = 0; i < a.length && i < b.length; i++) {
|
||||
pairs.push([a[i], b[i]]);
|
||||
}
|
||||
return pairs;
|
||||
};
|
||||
const isntLoc = k => k !== "loc";
|
||||
|
||||
function checkEquivalentASTs(expected, actual, prop = []) {
|
||||
do_print("Checking: " + prop.join(" "));
|
||||
|
||||
if (!isObject(expected)) {
|
||||
return void do_check_eq(expected, actual);
|
||||
}
|
||||
|
||||
do_check_true(isObject(actual));
|
||||
|
||||
if (Array.isArray(expected)) {
|
||||
do_check_true(Array.isArray(actual));
|
||||
do_check_eq(expected.length, actual.length);
|
||||
let i = 0;
|
||||
for (let [e, a] of zip(expected, actual)) {
|
||||
checkEquivalentASTs(a, e, prop.concat([i++]));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const expectedKeys = Object.keys(expected).filter(isntLoc).sort();
|
||||
const actualKeys = Object.keys(actual).filter(isntLoc).sort();
|
||||
do_check_eq(expectedKeys.length, actualKeys.length);
|
||||
for (let [ek, ak] of zip(expectedKeys, actualKeys)) {
|
||||
do_check_eq(ek, ak);
|
||||
checkEquivalentASTs(expected[ek], actual[ak], prop.concat([ek]));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
[DEFAULT]
|
||||
head = head_escodegen.js
|
||||
tail =
|
||||
|
||||
[test_import_escodegen.js]
|
||||
[test_same_ast.js]
|
||||
[test_generate_source_maps.js]
|
|
@ -14,6 +14,6 @@ PARALLEL_DIRS += [
|
|||
'webconsole',
|
||||
'apps',
|
||||
'styleinspector',
|
||||
'acorn',
|
||||
'pretty-fast'
|
||||
'escodegen',
|
||||
'acorn'
|
||||
]
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# UPGRADING
|
||||
|
||||
1. `git clone https://github.com/mozilla/pretty-fast.git`
|
||||
|
||||
2. Copy `pretty-fast/pretty-fast.js` to `toolkit/devtools/pretty-fast/pretty-fast.js`
|
||||
|
||||
3. Copy `pretty-fast/test.js` to `toolkit/devtools/pretty-fast/tests/unit/test.js`
|
|
@ -1,781 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE.md or:
|
||||
* http://opensource.org/licenses/BSD-2-Clause
|
||||
*/
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.prettyFast = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
"use strict";
|
||||
|
||||
var acorn = this.acorn || require("acorn");
|
||||
var sourceMap = this.sourceMap || require("source-map");
|
||||
var SourceNode = sourceMap.SourceNode;
|
||||
|
||||
// If any of these tokens are seen before a "[" token, we know that "[" token
|
||||
// is the start of an array literal, rather than a property access.
|
||||
//
|
||||
// The only exception is "}", which would need to be disambiguated by
|
||||
// parsing. The majority of the time, an open bracket following a closing
|
||||
// curly is going to be an array literal, so we brush the complication under
|
||||
// the rug, and handle the ambiguity by always assuming that it will be an
|
||||
// array literal.
|
||||
var PRE_ARRAY_LITERAL_TOKENS = {
|
||||
"typeof": true,
|
||||
"void": true,
|
||||
"delete": true,
|
||||
"case": true,
|
||||
"do": true,
|
||||
"=": true,
|
||||
"in": true,
|
||||
"{": true,
|
||||
"*": true,
|
||||
"/": true,
|
||||
"%": true,
|
||||
"else": true,
|
||||
";": true,
|
||||
"++": true,
|
||||
"--": true,
|
||||
"+": true,
|
||||
"-": true,
|
||||
"~": true,
|
||||
"!": true,
|
||||
":": true,
|
||||
"?": true,
|
||||
">>": true,
|
||||
">>>": true,
|
||||
"<<": true,
|
||||
"||": true,
|
||||
"&&": true,
|
||||
"<": true,
|
||||
">": true,
|
||||
"<=": true,
|
||||
">=": true,
|
||||
"instanceof": true,
|
||||
"&": true,
|
||||
"^": true,
|
||||
"|": true,
|
||||
"==": true,
|
||||
"!=": true,
|
||||
"===": true,
|
||||
"!==": true,
|
||||
",": true,
|
||||
|
||||
"}": true
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if we think that the given token starts an array literal.
|
||||
*
|
||||
* @param Object token
|
||||
* The token we want to determine if it is an array literal.
|
||||
* @param Object lastToken
|
||||
* The last token we added to the pretty printed results.
|
||||
*
|
||||
* @returns Boolean
|
||||
* True if we believe it is an array literal, false otherwise.
|
||||
*/
|
||||
function isArrayLiteral(token, lastToken) {
|
||||
if (token.type.type != "[") {
|
||||
return false;
|
||||
}
|
||||
if (!lastToken) {
|
||||
return true;
|
||||
}
|
||||
if (lastToken.type.isAssign) {
|
||||
return true;
|
||||
}
|
||||
return !!PRE_ARRAY_LITERAL_TOKENS[lastToken.type.keyword || lastToken.type.type];
|
||||
}
|
||||
|
||||
// If any of these tokens are followed by a token on a new line, we know that
|
||||
// ASI cannot happen.
|
||||
var PREVENT_ASI_AFTER_TOKENS = {
|
||||
// Binary operators
|
||||
"*": true,
|
||||
"/": true,
|
||||
"%": true,
|
||||
"+": true,
|
||||
"-": true,
|
||||
"<<": true,
|
||||
">>": true,
|
||||
">>>": true,
|
||||
"<": true,
|
||||
">": true,
|
||||
"<=": true,
|
||||
">=": true,
|
||||
"instanceof": true,
|
||||
"in": true,
|
||||
"==": true,
|
||||
"!=": true,
|
||||
"===": true,
|
||||
"!==": true,
|
||||
"&": true,
|
||||
"^": true,
|
||||
"|": true,
|
||||
"&&": true,
|
||||
"||": true,
|
||||
",": true,
|
||||
".": true,
|
||||
"=": true,
|
||||
"*=": true,
|
||||
"/=": true,
|
||||
"%=": true,
|
||||
"+=": true,
|
||||
"-=": true,
|
||||
"<<=": true,
|
||||
">>=": true,
|
||||
">>>=": true,
|
||||
"&=": true,
|
||||
"^=": true,
|
||||
"|=": true,
|
||||
// Unary operators
|
||||
"delete": true,
|
||||
"void": true,
|
||||
"typeof": true,
|
||||
"~": true,
|
||||
"!": true,
|
||||
"new": true,
|
||||
// Function calls and grouped expressions
|
||||
"(": true
|
||||
};
|
||||
|
||||
// If any of these tokens are on a line after the token before it, we know
|
||||
// that ASI cannot happen.
|
||||
var PREVENT_ASI_BEFORE_TOKENS = {
|
||||
// Binary operators
|
||||
"*": true,
|
||||
"/": true,
|
||||
"%": true,
|
||||
"<<": true,
|
||||
">>": true,
|
||||
">>>": true,
|
||||
"<": true,
|
||||
">": true,
|
||||
"<=": true,
|
||||
">=": true,
|
||||
"instanceof": true,
|
||||
"in": true,
|
||||
"==": true,
|
||||
"!=": true,
|
||||
"===": true,
|
||||
"!==": true,
|
||||
"&": true,
|
||||
"^": true,
|
||||
"|": true,
|
||||
"&&": true,
|
||||
"||": true,
|
||||
",": true,
|
||||
".": true,
|
||||
"=": true,
|
||||
"*=": true,
|
||||
"/=": true,
|
||||
"%=": true,
|
||||
"+=": true,
|
||||
"-=": true,
|
||||
"<<=": true,
|
||||
">>=": true,
|
||||
">>>=": true,
|
||||
"&=": true,
|
||||
"^=": true,
|
||||
"|=": true,
|
||||
// Function calls
|
||||
"(": true
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if Automatic Semicolon Insertion (ASI) occurs between these
|
||||
* tokens.
|
||||
*
|
||||
* @param Object token
|
||||
* The token we want to determine if it is an array literal.
|
||||
* @param Object lastToken
|
||||
* The last token we added to the pretty printed results.
|
||||
*
|
||||
* @returns Boolean
|
||||
* True if we believe ASI occurs.
|
||||
*/
|
||||
function isASI(token, lastToken) {
|
||||
if (!lastToken) {
|
||||
return false;
|
||||
}
|
||||
if (token.startLoc.line === lastToken.startLoc.line) {
|
||||
return false;
|
||||
}
|
||||
if (PREVENT_ASI_AFTER_TOKENS[lastToken.type.type || lastToken.type.keyword]) {
|
||||
return false;
|
||||
}
|
||||
if (PREVENT_ASI_BEFORE_TOKENS[token.type.type || token.type.keyword]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should add a newline after the given token.
|
||||
*
|
||||
* @param Object token
|
||||
* The token we are looking at.
|
||||
* @param Array stack
|
||||
* The stack of open parens/curlies/brackets/etc.
|
||||
*
|
||||
* @returns Boolean
|
||||
* True if we should add a newline.
|
||||
*/
|
||||
function isLineDelimiter(token, stack) {
|
||||
if (token.isArrayLiteral) {
|
||||
return true;
|
||||
}
|
||||
var ttt = token.type.type;
|
||||
var top = stack[stack.length - 1];
|
||||
return ttt == ";" && top != "("
|
||||
|| ttt == "{"
|
||||
|| ttt == "," && top != "("
|
||||
|| ttt == ":" && (top == "case" || top == "default");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the necessary whitespace to the result after we have added the given
|
||||
* token.
|
||||
*
|
||||
* @param Object token
|
||||
* The token that was just added to the result.
|
||||
* @param Function write
|
||||
* The function to write to the pretty printed results.
|
||||
* @param Array stack
|
||||
* The stack of open parens/curlies/brackets/etc.
|
||||
*
|
||||
* @returns Boolean
|
||||
* Returns true if we added a newline to result, false in all other
|
||||
* cases.
|
||||
*/
|
||||
function appendNewline(token, write, stack) {
|
||||
if (isLineDelimiter(token, stack)) {
|
||||
write("\n", token.startLoc.line, token.startLoc.column);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if we need to add a space between the last token we added and
|
||||
* the token we are about to add.
|
||||
*
|
||||
* @param Object token
|
||||
* The token we are about to add to the pretty printed code.
|
||||
* @param Object lastToken
|
||||
* The last token added to the pretty printed code.
|
||||
*/
|
||||
function needsSpaceAfter(token, lastToken) {
|
||||
if (lastToken) {
|
||||
if (lastToken.type.isLoop) {
|
||||
return true;
|
||||
}
|
||||
if (lastToken.type.isAssign) {
|
||||
return true;
|
||||
}
|
||||
if (lastToken.type.binop != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var ltt = lastToken.type.type;
|
||||
if (ltt == "?") {
|
||||
return true;
|
||||
}
|
||||
if (ltt == ":") {
|
||||
return true;
|
||||
}
|
||||
if (ltt == ",") {
|
||||
return true;
|
||||
}
|
||||
if (ltt == ";") {
|
||||
return true;
|
||||
}
|
||||
|
||||
var ltk = lastToken.type.keyword;
|
||||
if (ltk != null
|
||||
&& ltk != "debugger"
|
||||
&& ltk != "null"
|
||||
&& ltk != "true"
|
||||
&& ltk != "false"
|
||||
&& ltk != "this"
|
||||
&& ltk != "break"
|
||||
&& ltk != "continue"
|
||||
&& ltk != "default") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ltt == ")" && (token.type.type != ")"
|
||||
&& token.type.type != "]"
|
||||
&& token.type.type != ";"
|
||||
&& token.type.type != ",")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (token.type.isAssign) {
|
||||
return true;
|
||||
}
|
||||
if (token.type.binop != null) {
|
||||
return true;
|
||||
}
|
||||
if (token.type.type == "?") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the required whitespace before this token, whether that is a single
|
||||
* space, newline, and/or the indent on fresh lines.
|
||||
*
|
||||
* @param Object token
|
||||
* The token we are about to add to the pretty printed code.
|
||||
* @param Object lastToken
|
||||
* The last token we added to the pretty printed code.
|
||||
* @param Boolean addedNewline
|
||||
* Whether we added a newline after adding the last token to the pretty
|
||||
* printed code.
|
||||
* @param Function write
|
||||
* The function to write pretty printed code to the result SourceNode.
|
||||
* @param Object options
|
||||
* The options object.
|
||||
* @param Number indentLevel
|
||||
* The number of indents deep we are.
|
||||
* @param Array stack
|
||||
* The stack of open curlies, brackets, etc.
|
||||
*/
|
||||
function prependWhiteSpace(token, lastToken, addedNewline, write, options,
|
||||
indentLevel, stack) {
|
||||
var ttk = token.type.keyword;
|
||||
var ttt = token.type.type;
|
||||
var newlineAdded = addedNewline;
|
||||
|
||||
// Handle whitespace and newlines after "}" here instead of in
|
||||
// `isLineDelimiter` because it is only a line delimiter some of the
|
||||
// time. For example, we don't want to put "else if" on a new line after
|
||||
// the first if's block.
|
||||
if (lastToken && lastToken.type.type == "}") {
|
||||
if (ttk == "while" && stack[stack.length - 1] == "do") {
|
||||
write(" ",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
} else if (ttk == "else" ||
|
||||
ttk == "catch" ||
|
||||
ttk == "finally") {
|
||||
write(" ",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
} else if (ttt != "(" &&
|
||||
ttt != ";" &&
|
||||
ttt != "," &&
|
||||
ttt != ")" &&
|
||||
ttt != ".") {
|
||||
write("\n",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
newlineAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ttt == ":" && stack[stack.length - 1] == "?") {
|
||||
write(" ",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
}
|
||||
|
||||
if (lastToken && lastToken.type.type != "}" && ttk == "else") {
|
||||
write(" ",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
}
|
||||
|
||||
function ensureNewline() {
|
||||
if (!newlineAdded) {
|
||||
write("\n",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
newlineAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isASI(token, lastToken)) {
|
||||
ensureNewline();
|
||||
}
|
||||
|
||||
if (decrementsIndent(ttt, stack)) {
|
||||
ensureNewline();
|
||||
}
|
||||
|
||||
if (newlineAdded) {
|
||||
if (ttk == "case" || ttk == "default") {
|
||||
write(repeat(options.indent, indentLevel - 1),
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
} else {
|
||||
write(repeat(options.indent, indentLevel),
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
}
|
||||
} else if (needsSpaceAfter(token, lastToken)) {
|
||||
write(" ",
|
||||
lastToken.startLoc.line,
|
||||
lastToken.startLoc.column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repeat the `str` string `n` times.
|
||||
*
|
||||
* @param String str
|
||||
* The string to be repeated.
|
||||
* @param Number n
|
||||
* The number of times to repeat the string.
|
||||
*
|
||||
* @returns String
|
||||
* The repeated string.
|
||||
*/
|
||||
function repeat(str, n) {
|
||||
var result = "";
|
||||
while (n > 0) {
|
||||
if (n & 1) {
|
||||
result += str;
|
||||
}
|
||||
n >>= 1;
|
||||
str += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that we put "\n" into the output instead of actual newlines.
|
||||
*/
|
||||
function sanitizeNewlines(str) {
|
||||
return str.replace(/\n/g, "\\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given token to the pretty printed results.
|
||||
*
|
||||
* @param Object token
|
||||
* The token to add.
|
||||
* @param Function write
|
||||
* The function to write pretty printed code to the result SourceNode.
|
||||
* @param Object options
|
||||
* The options object.
|
||||
*/
|
||||
function addToken(token, write, options) {
|
||||
if (token.type.type == "string") {
|
||||
write("'" + sanitizeNewlines(token.value) + "'",
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
} else {
|
||||
write(String(token.value != null ? token.value : token.type.type),
|
||||
token.startLoc.line,
|
||||
token.startLoc.column);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given token type belongs on the stack.
|
||||
*/
|
||||
function belongsOnStack(token) {
|
||||
var ttt = token.type.type;
|
||||
var ttk = token.type.keyword;
|
||||
return ttt == "{"
|
||||
|| ttt == "("
|
||||
|| ttt == "["
|
||||
|| ttt == "?"
|
||||
|| ttk == "do"
|
||||
|| ttk == "case"
|
||||
|| ttk == "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given token should cause us to pop the stack.
|
||||
*/
|
||||
function shouldStackPop(token, stack) {
|
||||
var ttt = token.type.type;
|
||||
var ttk = token.type.keyword;
|
||||
var top = stack[stack.length - 1];
|
||||
return ttt == "]"
|
||||
|| ttt == ")"
|
||||
|| ttt == "}"
|
||||
|| (ttt == ":" && (top == "case" || top == "default" || top == "?"))
|
||||
|| (ttk == "while" && top == "do");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given token type should cause us to decrement the
|
||||
* indent level.
|
||||
*/
|
||||
function decrementsIndent(tokenType, stack) {
|
||||
return tokenType == "}"
|
||||
|| (tokenType == "]" && stack[stack.length - 1] == "[\n")
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given token should cause us to increment the indent
|
||||
* level.
|
||||
*/
|
||||
function incrementsIndent(token) {
|
||||
return token.type.type == "{" || token.isArrayLiteral;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment to the pretty printed code.
|
||||
*
|
||||
* @param Function write
|
||||
* The function to write pretty printed code to the result SourceNode.
|
||||
* @param Number indentLevel
|
||||
* The number of indents deep we are.
|
||||
* @param Object options
|
||||
* The options object.
|
||||
* @param Boolean block
|
||||
* True if the comment is a multiline block style comment.
|
||||
* @param String text
|
||||
* The text of the comment.
|
||||
* @param Number line
|
||||
* The line number to comment appeared on.
|
||||
* @param Number column
|
||||
* The column number the comment appeared on.
|
||||
*/
|
||||
function addComment(write, indentLevel, options, block, text, line, column) {
|
||||
var indentString = repeat(options.indent, indentLevel);
|
||||
|
||||
write(indentString, line, column);
|
||||
if (block) {
|
||||
write("/*");
|
||||
write(text
|
||||
.split(new RegExp("/\n" + indentString + "/", "g"))
|
||||
.join("\n" + indentString));
|
||||
write("*/");
|
||||
} else {
|
||||
write("//");
|
||||
write(text);
|
||||
}
|
||||
write("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function.
|
||||
*
|
||||
* @param String input
|
||||
* The ugly JS code we want to pretty print.
|
||||
* @param Object options
|
||||
* The options object. Provides configurability of the pretty
|
||||
* printing. Properties:
|
||||
* - url: The URL string of the ugly JS code.
|
||||
* - indent: The string to indent code by.
|
||||
*
|
||||
* @returns Object
|
||||
* An object with the following properties:
|
||||
* - code: The pretty printed code string.
|
||||
* - map: A SourceMapGenerator instance.
|
||||
*/
|
||||
return function prettyFast(input, options) {
|
||||
// The level of indents deep we are.
|
||||
var indentLevel = 0;
|
||||
|
||||
// We will accumulate the pretty printed code in this SourceNode.
|
||||
var result = new SourceNode();
|
||||
|
||||
/**
|
||||
* Write a pretty printed string to the result SourceNode.
|
||||
*
|
||||
* We buffer our writes so that we only create one mapping for each line in
|
||||
* the source map. This enhances performance by avoiding extraneous mapping
|
||||
* serialization, and flattening the tree that
|
||||
* `SourceNode#toStringWithSourceMap` will have to recursively walk. When
|
||||
* timing how long it takes to pretty print jQuery, this optimization
|
||||
* brought the time down from ~390 ms to ~190ms!
|
||||
*
|
||||
* @param String str
|
||||
* The string to be added to the result.
|
||||
* @param Number line
|
||||
* The line number the string came from in the ugly source.
|
||||
* @param Number column
|
||||
* The column number the string came from in the ugly source.
|
||||
*/
|
||||
var write = (function () {
|
||||
var buffer = [];
|
||||
var bufferLine = -1;
|
||||
var bufferColumn = -1;
|
||||
return function write(str, line, column) {
|
||||
if (line != null && bufferLine === -1) {
|
||||
bufferLine = line;
|
||||
}
|
||||
if (column != null && bufferColumn === -1) {
|
||||
bufferColumn = column;
|
||||
}
|
||||
buffer.push(str);
|
||||
|
||||
if (str == "\n") {
|
||||
var lineStr = "";
|
||||
for (var i = 0, len = buffer.length; i < len; i++) {
|
||||
lineStr += buffer[i];
|
||||
}
|
||||
result.add(new SourceNode(bufferLine, bufferColumn, options.url, lineStr));
|
||||
buffer.splice(0, buffer.length);
|
||||
bufferLine = -1;
|
||||
bufferColumn = -1;
|
||||
}
|
||||
}
|
||||
}());
|
||||
|
||||
// Whether or not we added a newline on after we added the last token.
|
||||
var addedNewline = false;
|
||||
|
||||
// The current token we will be adding to the pretty printed code.
|
||||
var token;
|
||||
|
||||
// Shorthand for token.type.type, so we don't have to repeatedly access
|
||||
// properties.
|
||||
var ttt;
|
||||
|
||||
// Shorthand for token.type.keyword, so we don't have to repeatedly access
|
||||
// properties.
|
||||
var ttk;
|
||||
|
||||
// The last token we added to the pretty printed code.
|
||||
var lastToken;
|
||||
|
||||
// Stack of token types/keywords that can affect whether we want to add a
|
||||
// newline or a space. We can make that decision based on what token type is
|
||||
// on the top of the stack. For example, a comma in a parameter list should
|
||||
// be followed by a space, while a comma in an object literal should be
|
||||
// followed by a newline.
|
||||
//
|
||||
// Strings that go on the stack:
|
||||
//
|
||||
// - "{"
|
||||
// - "("
|
||||
// - "["
|
||||
// - "[\n"
|
||||
// - "do"
|
||||
// - "?"
|
||||
// - "case"
|
||||
// - "default"
|
||||
//
|
||||
// The difference between "[" and "[\n" is that "[\n" is used when we are
|
||||
// treating "[" and "]" tokens as line delimiters and should increment and
|
||||
// decrement the indent level when we find them.
|
||||
var stack = [];
|
||||
|
||||
// Acorn's tokenizer will always yield comments *before* the token they
|
||||
// follow (unless the very first thing in the source is a comment), so we
|
||||
// have to queue the comments in order to pretty print them in the correct
|
||||
// location. For example, the source file:
|
||||
//
|
||||
// foo
|
||||
// // a
|
||||
// // b
|
||||
// bar
|
||||
//
|
||||
// When tokenized by acorn, gives us the following token stream:
|
||||
//
|
||||
// [ '// a', '// b', foo, bar ]
|
||||
var commentQueue = [];
|
||||
|
||||
var getToken = acorn.tokenize(input, {
|
||||
locations: true,
|
||||
sourceFile: options.url,
|
||||
onComment: function (block, text, start, end, startLoc, endLoc) {
|
||||
if (lastToken) {
|
||||
commentQueue.push({
|
||||
block: block,
|
||||
text: text,
|
||||
line: startLoc.line,
|
||||
column: startLoc.column
|
||||
});
|
||||
} else {
|
||||
addComment(write, indentLevel, options, block, text, startLoc.line,
|
||||
startLoc.column);
|
||||
addedNewline = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while (true) {
|
||||
token = getToken();
|
||||
|
||||
ttk = token.type.keyword;
|
||||
ttt = token.type.type;
|
||||
|
||||
if (ttt == "eof") {
|
||||
if (!addedNewline) {
|
||||
write("\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
token.isArrayLiteral = isArrayLiteral(token, lastToken);
|
||||
|
||||
if (belongsOnStack(token)) {
|
||||
if (token.isArrayLiteral) {
|
||||
stack.push("[\n");
|
||||
} else {
|
||||
stack.push(ttt || ttk);
|
||||
}
|
||||
}
|
||||
|
||||
if (decrementsIndent(ttt, stack)) {
|
||||
indentLevel--;
|
||||
}
|
||||
|
||||
prependWhiteSpace(token, lastToken, addedNewline, write, options,
|
||||
indentLevel, stack);
|
||||
addToken(token, write, options);
|
||||
addedNewline = appendNewline(token, write, stack);
|
||||
|
||||
if (shouldStackPop(token, stack)) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
if (incrementsIndent(token)) {
|
||||
indentLevel++;
|
||||
}
|
||||
|
||||
// Acorn's tokenizer re-uses tokens, so we have to copy the last token on
|
||||
// every iteration. We follow acorn's lead here, and reuse the lastToken
|
||||
// object the same way that acorn reuses the token object. This allows us
|
||||
// to avoid allocations and minimize GC pauses.
|
||||
if (!lastToken) {
|
||||
lastToken = { startLoc: {}, endLoc: {} };
|
||||
}
|
||||
lastToken.start = token.start;
|
||||
lastToken.end = token.end;
|
||||
lastToken.startLoc.line = token.startLoc.line;
|
||||
lastToken.startLoc.column = token.startLoc.column;
|
||||
lastToken.endLoc.line = token.endLoc.line;
|
||||
lastToken.endLoc.column = token.endLoc.column;
|
||||
lastToken.type = token.type;
|
||||
lastToken.value = token.value;
|
||||
lastToken.isArrayLiteral = token.isArrayLiteral;
|
||||
|
||||
// Apply all the comments that have been queued up.
|
||||
if (commentQueue.length) {
|
||||
if (!addedNewline) {
|
||||
write("\n");
|
||||
}
|
||||
for (var i = 0, n = commentQueue.length; i < n; i++) {
|
||||
var comment = commentQueue[i];
|
||||
addComment(write, indentLevel, options, comment.block, comment.text,
|
||||
comment.line, comment.column);
|
||||
}
|
||||
addedNewline = true;
|
||||
commentQueue.splice(0, commentQueue.length);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toStringWithSourceMap({ file: options.url });
|
||||
};
|
||||
|
||||
}.bind(this)));
|
|
@ -1,470 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 Mozilla Foundation and contributors
|
||||
* Licensed under the New BSD license. See LICENSE.md or:
|
||||
* http://opensource.org/licenses/BSD-2-Clause
|
||||
*/
|
||||
var prettyFast = this.prettyFast || require("./pretty-fast");
|
||||
|
||||
var testCases = [
|
||||
{
|
||||
name: "Simple function",
|
||||
input: "function foo() { bar(); }",
|
||||
output: "function foo() {\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
mappings: [
|
||||
// function foo() {
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 1
|
||||
},
|
||||
// bar();
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 2
|
||||
},
|
||||
// }
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 3
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Nested function",
|
||||
input: "function foo() { function bar() { debugger; } bar(); }",
|
||||
output: "function foo() {\n" +
|
||||
" function bar() {\n" +
|
||||
" debugger;\n" +
|
||||
" }\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
mappings: [
|
||||
// function bar() {
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 2
|
||||
},
|
||||
// debugger;
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 3
|
||||
},
|
||||
// bar();
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 5
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Immediately invoked function expression",
|
||||
input: "(function(){thingy()}())",
|
||||
output: "(function () {\n" +
|
||||
" thingy()\n" +
|
||||
"}())\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Single line comment",
|
||||
input: "// Comment\n" +
|
||||
"function foo() { bar(); }\n",
|
||||
output: "// Comment\n" +
|
||||
"function foo() {\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
mappings: [
|
||||
// // Comment
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Multi line comment",
|
||||
input: "/* Comment\n" +
|
||||
"more comment */\n" +
|
||||
"function foo() { bar(); }\n",
|
||||
output: "/* Comment\n" +
|
||||
"more comment */\n" +
|
||||
"function foo() {\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
mappings: [
|
||||
// /* Comment
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 1
|
||||
},
|
||||
// \nmore comment */
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Null assignment",
|
||||
input: "var i=null;\n",
|
||||
output: "var i = null;\n",
|
||||
mappings: [
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Undefined assignment",
|
||||
input: "var i=undefined;\n",
|
||||
output: "var i = undefined;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Void 0 assignment",
|
||||
input: "var i=void 0;\n",
|
||||
output: "var i = void 0;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "This property access",
|
||||
input: "var foo=this.foo;\n",
|
||||
output: "var foo = this.foo;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "True assignment",
|
||||
input: "var foo=true;\n",
|
||||
output: "var foo = true;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "False assignment",
|
||||
input: "var foo=false;\n",
|
||||
output: "var foo = false;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "For loop",
|
||||
input: "for (var i = 0; i < n; i++) { console.log(i); }",
|
||||
output: "for (var i = 0; i < n; i++) {\n" +
|
||||
" console.log(i);\n" +
|
||||
"}\n",
|
||||
mappings: [
|
||||
// for (var i = 0; i < n; i++) {
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 1
|
||||
},
|
||||
// console.log(i);
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 2
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "String with semicolon",
|
||||
input: "var foo = ';';\n",
|
||||
output: "var foo = ';';\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "String with quote",
|
||||
input: "var foo = \"'\";\n",
|
||||
output: "var foo = '\'';\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Function calls",
|
||||
input: "var result=func(a,b,c,d);",
|
||||
output: "var result = func(a, b, c, d);\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Regexp",
|
||||
input: "var r=/foobar/g;",
|
||||
output: "var r = /foobar/g;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "In operator",
|
||||
input: "if(foo in bar){doThing()}",
|
||||
output: "if (foo in bar) {\n" +
|
||||
" doThing()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "With statement",
|
||||
input: "with(obj){crock()}",
|
||||
output: "with (obj) {\n" +
|
||||
" crock()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "New expression",
|
||||
input: "var foo=new Foo();",
|
||||
output: "var foo = new Foo();\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Continue/break statements",
|
||||
input: "while(1){if(x){continue}if(y){break}}",
|
||||
output: "while (1) {\n" +
|
||||
" if (x) {\n" +
|
||||
" continue\n" +
|
||||
" }\n" +
|
||||
" if (y) {\n" +
|
||||
" break\n" +
|
||||
" }\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Instanceof",
|
||||
input: "var a=x instanceof y;",
|
||||
output: "var a = x instanceof y;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Binary operators",
|
||||
input: "var a=5*30;var b=5>>3;",
|
||||
output: "var a = 5 * 30;\n" +
|
||||
"var b = 5 >> 3;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Delete",
|
||||
input: "delete obj.prop;",
|
||||
output: "delete obj.prop;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Try/catch/finally statement",
|
||||
input: "try{dangerous()}catch(e){handle(e)}finally{cleanup()}",
|
||||
output: "try {\n" +
|
||||
" dangerous()\n" +
|
||||
"} catch (e) {\n" +
|
||||
" handle(e)\n" +
|
||||
"} finally {\n" +
|
||||
" cleanup()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "If/else statement",
|
||||
input: "if(c){then()}else{other()}",
|
||||
output: "if (c) {\n" +
|
||||
" then()\n" +
|
||||
"} else {\n" +
|
||||
" other()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "If/else without curlies",
|
||||
input: "if(c) a else b",
|
||||
output: "if (c) a else b\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Objects",
|
||||
input: "var o={a:1,\n" +
|
||||
" b:2};",
|
||||
output: "var o = {\n" +
|
||||
" a: 1,\n" +
|
||||
" b: 2\n" +
|
||||
"};\n",
|
||||
mappings: [
|
||||
// a: 1,
|
||||
{
|
||||
inputLine: 1,
|
||||
outputLine: 2
|
||||
},
|
||||
// b: 2
|
||||
{
|
||||
inputLine: 2,
|
||||
outputLine: 3
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
name: "Do/while loop",
|
||||
input: "do{x}while(y)",
|
||||
output: "do {\n" +
|
||||
" x\n" +
|
||||
"} while (y)\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Arrays",
|
||||
input: "var a=[1,2,3];",
|
||||
output: "var a = [\n" +
|
||||
" 1,\n" +
|
||||
" 2,\n" +
|
||||
" 3\n" +
|
||||
"];\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Code that relies on ASI",
|
||||
input: "var foo = 10\n" +
|
||||
"var bar = 20\n" +
|
||||
"function g() {\n" +
|
||||
" a()\n" +
|
||||
" b()\n" +
|
||||
"}",
|
||||
output: "var foo = 10\n" +
|
||||
"var bar = 20\n" +
|
||||
"function g() {\n" +
|
||||
" a()\n" +
|
||||
" b()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Ternary operator",
|
||||
input: "bar?baz:bang;",
|
||||
output: "bar ? baz : bang;\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Switch statements",
|
||||
input: "switch(x){case a:foo();break;default:bar()}",
|
||||
output: "switch (x) {\n" +
|
||||
"case a:\n" +
|
||||
" foo();\n" +
|
||||
" break;\n" +
|
||||
"default:\n" +
|
||||
" bar()\n" +
|
||||
"}\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Multiple single line comments",
|
||||
input: "function f() {\n" +
|
||||
" // a\n" +
|
||||
" // b\n" +
|
||||
" // c\n" +
|
||||
"}\n",
|
||||
output: "function f() {\n" +
|
||||
" // a\n" +
|
||||
" // b\n" +
|
||||
" // c\n" +
|
||||
"}\n",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Indented multiline comment",
|
||||
input: "function foo() {\n" +
|
||||
" /**\n" +
|
||||
" * java doc style comment\n" +
|
||||
" * more comment\n" +
|
||||
" */\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
output: "function foo() {\n" +
|
||||
" /**\n" +
|
||||
" * java doc style comment\n" +
|
||||
" * more comment\n" +
|
||||
" */\n" +
|
||||
" bar();\n" +
|
||||
"}\n",
|
||||
},
|
||||
|
||||
{
|
||||
name: "ASI return",
|
||||
input: "function f() {\n" +
|
||||
" return\n" +
|
||||
" {}\n" +
|
||||
"}\n",
|
||||
output: "function f() {\n" +
|
||||
" return\n" +
|
||||
" {\n" +
|
||||
" }\n" +
|
||||
"}\n",
|
||||
},
|
||||
|
||||
{
|
||||
name: "Non-ASI property access",
|
||||
input: "[1,2,3]\n" +
|
||||
"[0]",
|
||||
output: "[\n" +
|
||||
" 1,\n" +
|
||||
" 2,\n" +
|
||||
" 3\n" +
|
||||
"]\n" +
|
||||
"[0]\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Non-ASI in",
|
||||
input: "'x'\n" +
|
||||
"in foo",
|
||||
output: "'x' in foo\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Non-ASI function call",
|
||||
input: "f\n" +
|
||||
"()",
|
||||
output: "f()\n"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Non-ASI new",
|
||||
input: "new\n" +
|
||||
"F()",
|
||||
output: "new F()\n"
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
var sourceMap = this.sourceMap || require("source-map");
|
||||
|
||||
function run_test() {
|
||||
testCases.forEach(function (test) {
|
||||
console.log(test.name);
|
||||
|
||||
var actual = prettyFast(test.input, {
|
||||
indent: " ",
|
||||
url: "test.js"
|
||||
});
|
||||
|
||||
if (actual.code !== test.output) {
|
||||
throw new Error("Expected:\n" + test.output
|
||||
+ "\nGot:\n" + actual.code);
|
||||
}
|
||||
|
||||
if (test.mappings) {
|
||||
var smc = new sourceMap.SourceMapConsumer(actual.map.toJSON());
|
||||
test.mappings.forEach(function (m) {
|
||||
var query = { line: m.outputLine, column: 0 };
|
||||
var original = smc.originalPositionFor(query);
|
||||
if (original.line != m.inputLine) {
|
||||
throw new Error("Querying:\n" + JSON.stringify(query, null, 2) + "\n"
|
||||
+ "Expected line:\n" + m.inputLine + "\n"
|
||||
+ "Got:\n" + JSON.stringify(original, null, 2));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log("✓ All tests pass!");
|
||||
}
|
||||
|
||||
// Only run the tests if this is node and we are running this file
|
||||
// directly. (Firefox's test runner will import this test file, and then call
|
||||
// run_test itself.)
|
||||
if (typeof exports == "object") {
|
||||
run_test();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
[DEFAULT]
|
||||
head = head_pretty-fast.js
|
||||
tail =
|
||||
|
||||
[test.js]
|
|
@ -8,11 +8,11 @@
|
|||
* This file is meant to be loaded as a ChromeWorker. It accepts messages which
|
||||
* have data of the form:
|
||||
*
|
||||
* { id, url, indent, source }
|
||||
* { 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 `source` is the source text.
|
||||
* code by, and `ast` is the source's abstract syntax tree.
|
||||
*
|
||||
* On success, the worker responds with a message of the form:
|
||||
*
|
||||
|
@ -20,23 +20,25 @@
|
|||
*
|
||||
* 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 back to the ugly source text.
|
||||
* pretty printed code to the AST's source locations.
|
||||
*
|
||||
* In the case of an error, the worker responds with a message of the form:
|
||||
*
|
||||
* { id, error }
|
||||
* { error }
|
||||
*/
|
||||
|
||||
importScripts("resource://gre/modules/devtools/acorn.js");
|
||||
importScripts("resource://gre/modules/devtools/source-map.js");
|
||||
importScripts("resource://gre/modules/devtools/pretty-fast.js");
|
||||
importScripts("resource://gre/modules/devtools/escodegen/escodegen.worker.js");
|
||||
|
||||
self.onmessage = (event) => {
|
||||
const { data: { id, url, indent, source } } = event;
|
||||
self.onmessage = ({ data: { id, url, indent, ast } }) => {
|
||||
try {
|
||||
const prettified = prettyFast(source, {
|
||||
url: url,
|
||||
indent: " ".repeat(indent)
|
||||
const prettified = escodegen.generate(ast, {
|
||||
format: {
|
||||
indent: {
|
||||
style: " ".repeat(indent)
|
||||
}
|
||||
},
|
||||
sourceMap: url,
|
||||
sourceMapWithCode: true
|
||||
});
|
||||
|
||||
self.postMessage({
|
||||
|
@ -46,7 +48,6 @@ self.onmessage = (event) => {
|
|||
});
|
||||
} catch (e) {
|
||||
self.postMessage({
|
||||
id: id,
|
||||
error: e.message + "\n" + e.stack
|
||||
});
|
||||
}
|
||||
|
|
|
@ -497,8 +497,8 @@ ThreadActor.prototype = {
|
|||
return this._prettyPrintWorker;
|
||||
},
|
||||
|
||||
_onPrettyPrintError: function ({ message, filename, lineno }) {
|
||||
reportError(new Error(message + " @ " + filename + ":" + lineno));
|
||||
_onPrettyPrintError: function (error) {
|
||||
reportError(new Error(error));
|
||||
},
|
||||
|
||||
_onPrettyPrintMsg: function ({ data }) {
|
||||
|
@ -2459,6 +2459,7 @@ SourceActor.prototype = {
|
|||
onPrettyPrint: function ({ indent }) {
|
||||
this.threadActor.sources.prettyPrint(this._url, indent);
|
||||
return this._getSourceText()
|
||||
.then(this._parseAST)
|
||||
.then(this._sendToPrettyPrintWorker(indent))
|
||||
.then(this._invertSourceMap)
|
||||
.then(this._saveMap)
|
||||
|
@ -2479,6 +2480,13 @@ SourceActor.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the source content into an AST.
|
||||
*/
|
||||
_parseAST: function SA__parseAST({ content}) {
|
||||
return Reflect.parse(content);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -2492,7 +2500,7 @@ SourceActor.prototype = {
|
|||
* printed code, and `mappings` is an array of source mappings.
|
||||
*/
|
||||
_sendToPrettyPrintWorker: function SA__sendToPrettyPrintWorker(aIndent) {
|
||||
return ({ content }) => {
|
||||
return aAST => {
|
||||
const deferred = promise.defer();
|
||||
const id = Math.random();
|
||||
|
||||
|
@ -2514,7 +2522,7 @@ SourceActor.prototype = {
|
|||
id: id,
|
||||
url: this._url,
|
||||
indent: aIndent,
|
||||
source: content
|
||||
ast: aAST
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
|
|
@ -74,6 +74,7 @@ loadSubScript.call(this, "resource://gre/modules/commonjs/sdk/core/promise.js");
|
|||
this.require = loaderRequire;
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/SourceMap.jsm");
|
||||
const escodegen = localRequire("escodegen/escodegen");
|
||||
|
||||
loadSubScript.call(this, "resource://gre/modules/devtools/DevToolsUtils.js");
|
||||
|
||||
|
|
|
@ -7,4 +7,3 @@ include $(topsrcdir)/config/rules.mk
|
|||
|
||||
libs::
|
||||
$(NSINSTALL) $(srcdir)/*.jsm $(FINAL_TARGET)/modules/devtools
|
||||
$(NSINSTALL) $(srcdir)/*.js $(FINAL_TARGET)/modules/devtools
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче