Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5538ec7bd8 | ||
|
|
f101d6429b | ||
|
|
f36a1eaa8b | ||
|
|
a64bdda9ae | ||
|
|
01d19b4b52 | ||
|
|
f0c1a01bc2 | ||
|
|
7be680d3f8 | ||
|
|
57dab1e1db | ||
|
|
21b3c890a1 | ||
|
|
fb0ec720a4 | ||
|
|
7971ed33d1 | ||
|
|
885835a655 | ||
|
|
4c64554808 | ||
|
|
548beeb6b1 | ||
|
|
e3066f9577 | ||
|
|
e391367488 | ||
|
|
18ddf2f7b5 | ||
|
|
f8ee5a0785 | ||
|
|
b467a3c877 | ||
|
|
f2d48e9019 | ||
|
|
5e314bf3e9 | ||
|
|
05ba26c7c8 | ||
|
|
87b72364a4 | ||
|
|
0e3ff1f970 | ||
|
|
ec3e74d7f4 | ||
|
|
62bda71c85 | ||
|
|
83e0939088 | ||
|
|
9798d96e37 | ||
|
|
ac2caf1088 | ||
|
|
8511e80f48 | ||
|
|
91bc3f1f92 | ||
|
|
8463b48f90 | ||
|
|
e3342a3cf6 | ||
|
|
7bf59b5bcd | ||
|
|
025f3e9596 | ||
|
|
8258edd8a5 | ||
|
|
8669ca219b | ||
|
|
71652690b6 | ||
|
|
37693d2812 | ||
|
|
8fbe200012 | ||
|
|
1a34a13e33 | ||
|
|
ef772b0049 | ||
|
|
6fcabbde08 | ||
|
|
14f290f8ab | ||
|
|
e2e09d5754 | ||
|
|
448a8d3845 | ||
|
|
514936beb8 | ||
|
|
f5c09d0bbf | ||
|
|
014f655c5f | ||
|
|
bf30dc3038 | ||
|
|
ef2ef07cbd | ||
|
|
1a4440080d | ||
|
|
ac0086a745 | ||
|
|
2494daaf68 | ||
|
|
9b404f9de6 | ||
|
|
5344b7dab8 | ||
|
|
0007a53b9c | ||
|
|
1dd05f44eb | ||
|
|
bf7b122ab2 | ||
|
|
e29048b54a | ||
|
|
2eeb640eca | ||
|
|
ceb200fe81 | ||
|
|
f5f8239057 | ||
|
|
6f9d051784 | ||
|
|
931862e97f | ||
|
|
1d0127de21 | ||
|
|
2d8fc61677 | ||
|
|
1e31011874 | ||
|
|
75cdbf19aa | ||
|
|
4339bd5cfa | ||
|
|
1ab2fdaa10 | ||
|
|
eda540f6ec | ||
|
|
90a330da16 | ||
|
|
cad1f9cbd1 | ||
|
|
f6203bd5a8 | ||
|
|
03cf94ebe8 | ||
|
|
c3087dd179 | ||
|
|
2c305af478 | ||
|
|
72e6f64ca8 | ||
|
|
b9fac687ff | ||
|
|
2c88eb6fbe | ||
|
|
a67e3bfdd3 | ||
|
|
27142df4f5 | ||
|
|
5e4c7f4245 | ||
|
|
b521b4b926 | ||
|
|
aa9de76370 | ||
|
|
5a083a938d | ||
|
|
7a30d826b8 | ||
|
|
be55a09edf | ||
|
|
15a148ff6d | ||
|
|
428e19fed2 | ||
|
|
f65e55dff4 | ||
|
|
b634018618 | ||
|
|
fa3300f314 | ||
|
|
bd0886a2c0 | ||
|
|
248f304f02 | ||
|
|
dc5f70eab5 | ||
|
|
df8c5623af | ||
|
|
a790c09c91 | ||
|
|
8f35a363d9 | ||
|
|
d2190c2bf3 | ||
|
|
ea10642572 | ||
|
|
547561a568 | ||
|
|
c16d538ce7 | ||
|
|
dfa395f6ff |
@@ -1,7 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
|
before_install: "npm install -g npm"
|
||||||
node_js:
|
node_js:
|
||||||
- "0.4"
|
|
||||||
- "0.6"
|
|
||||||
- "0.8"
|
- "0.8"
|
||||||
- "0.10"
|
- "0.10"
|
||||||
- "0.11"
|
- "0.11"
|
||||||
|
|||||||
47
README.md
47
README.md
@@ -54,6 +54,10 @@ The available options are:
|
|||||||
--source-map-url The path to the source map to be added in //#
|
--source-map-url The path to the source map to be added in //#
|
||||||
sourceMappingURL. Defaults to the value passed with
|
sourceMappingURL. Defaults to the value passed with
|
||||||
--source-map. [string]
|
--source-map. [string]
|
||||||
|
--source-map-include-sources
|
||||||
|
Pass this flag if you want to include the content of
|
||||||
|
source files in the source map as sourcesContent
|
||||||
|
property. [boolean]
|
||||||
--in-source-map Input source map, useful if you're compressing JS that was
|
--in-source-map Input source map, useful if you're compressing JS that was
|
||||||
generated from some other original code.
|
generated from some other original code.
|
||||||
--screw-ie8 Pass this flag if you don't care about full compliance
|
--screw-ie8 Pass this flag if you don't care about full compliance
|
||||||
@@ -169,7 +173,7 @@ To enable the mangler you need to pass `--mangle` (`-m`). The following
|
|||||||
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
- `toplevel` — mangle names declared in the toplevel scope (disabled by
|
||||||
default).
|
default).
|
||||||
|
|
||||||
- `eval` — mangle names visible in scopes where `eval` or `when` are used
|
- `eval` — mangle names visible in scopes where `eval` or `with` are used
|
||||||
(disabled by default).
|
(disabled by default).
|
||||||
|
|
||||||
When mangling is enabled but you want to prevent certain names from being
|
When mangling is enabled but you want to prevent certain names from being
|
||||||
@@ -249,6 +253,13 @@ to set `true`; it's effectively a shortcut for `foo=true`).
|
|||||||
statement would get discarded. The current implementation adds some
|
statement would get discarded. The current implementation adds some
|
||||||
overhead (compression will be slower).
|
overhead (compression will be slower).
|
||||||
|
|
||||||
|
- `drop_console` -- default `false`. Pass `true` to discard calls to
|
||||||
|
`console.*` functions.
|
||||||
|
|
||||||
|
- `keep_fargs` -- default `false`. Pass `true` to prevent the
|
||||||
|
compressor from discarding unused function arguments. You need this
|
||||||
|
for code which relies on `Function.length`.
|
||||||
|
|
||||||
### The `unsafe` option
|
### The `unsafe` option
|
||||||
|
|
||||||
It enables some transformations that *might* break code logic in certain
|
It enables some transformations that *might* break code logic in certain
|
||||||
@@ -393,6 +404,38 @@ Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but
|
|||||||
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
converting the SpiderMonkey tree that Acorn produces takes another 150ms so
|
||||||
in total it's a bit more than just using UglifyJS's own parser.
|
in total it's a bit more than just using UglifyJS's own parser.
|
||||||
|
|
||||||
|
### Using UglifyJS to transform SpiderMonkey AST
|
||||||
|
|
||||||
|
Now you can use UglifyJS as any other intermediate tool for transforming
|
||||||
|
JavaScript ASTs in SpiderMonkey format.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function uglify(ast, options, mangle) {
|
||||||
|
// Conversion from SpiderMonkey AST to internal format
|
||||||
|
var uAST = UglifyJS.AST_Node.from_mozilla_ast(ast);
|
||||||
|
|
||||||
|
// Compression
|
||||||
|
uAST.figure_out_scope();
|
||||||
|
uAST = uAST.transform(UglifyJS.Compressor(options));
|
||||||
|
|
||||||
|
// Mangling (optional)
|
||||||
|
if (mangle) {
|
||||||
|
uAST.figure_out_scope();
|
||||||
|
uAST.compute_char_frequency();
|
||||||
|
uAST.mangle_names();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back-conversion to SpiderMonkey AST
|
||||||
|
return uAST.to_mozilla_ast();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Check out
|
||||||
|
[original blog post](http://rreverser.com/using-mozilla-ast-with-uglifyjs/)
|
||||||
|
for details.
|
||||||
|
|
||||||
API Reference
|
API Reference
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
@@ -502,7 +545,7 @@ something like this:
|
|||||||
```javascript
|
```javascript
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = fs.readFileSync(file);
|
var code = fs.readFileSync(file, "utf8");
|
||||||
toplevel = UglifyJS.parse(code, {
|
toplevel = UglifyJS.parse(code, {
|
||||||
filename: file,
|
filename: file,
|
||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
|
|||||||
25
bin/uglifyjs
25
bin/uglifyjs
@@ -22,6 +22,7 @@ mangling you need to use `-c` and `-m`.\
|
|||||||
.describe("source-map", "Specify an output file where to generate source map.")
|
.describe("source-map", "Specify an output file where to generate source map.")
|
||||||
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
.describe("source-map-root", "The path to the original source to be included in the source map.")
|
||||||
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
|
||||||
|
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
|
||||||
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
|
||||||
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
.describe("screw-ie8", "Pass this flag if you don't care about full compliance with Internet Explorer 6-8 quirks (by default UglifyJS will try to be IE-proof).")
|
||||||
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
.describe("expr", "Parse a single expression, rather than a program (for parsing JSON)")
|
||||||
@@ -62,6 +63,8 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.describe("lint", "Display some scope warnings")
|
.describe("lint", "Display some scope warnings")
|
||||||
.describe("v", "Verbose")
|
.describe("v", "Verbose")
|
||||||
.describe("V", "Print version number and exit.")
|
.describe("V", "Print version number and exit.")
|
||||||
|
.describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.")
|
||||||
|
.describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.")
|
||||||
|
|
||||||
.alias("p", "prefix")
|
.alias("p", "prefix")
|
||||||
.alias("o", "output")
|
.alias("o", "output")
|
||||||
@@ -87,6 +90,7 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.string("p")
|
.string("p")
|
||||||
|
|
||||||
.boolean("expr")
|
.boolean("expr")
|
||||||
|
.boolean("source-map-include-sources")
|
||||||
.boolean("screw-ie8")
|
.boolean("screw-ie8")
|
||||||
.boolean("export-all")
|
.boolean("export-all")
|
||||||
.boolean("self")
|
.boolean("self")
|
||||||
@@ -96,6 +100,8 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
.boolean("spidermonkey")
|
.boolean("spidermonkey")
|
||||||
.boolean("lint")
|
.boolean("lint")
|
||||||
.boolean("V")
|
.boolean("V")
|
||||||
|
.boolean("noerr")
|
||||||
|
.boolean("bare-returns")
|
||||||
|
|
||||||
.wrap(80)
|
.wrap(80)
|
||||||
|
|
||||||
@@ -104,6 +110,12 @@ You need to pass an argument to this option to specify the name that your module
|
|||||||
|
|
||||||
normalize(ARGS);
|
normalize(ARGS);
|
||||||
|
|
||||||
|
if (ARGS.noerr) {
|
||||||
|
UglifyJS.DefaultsError.croak = function(msg, defs) {
|
||||||
|
sys.error("WARN: " + msg);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (ARGS.version || ARGS.V) {
|
if (ARGS.version || ARGS.V) {
|
||||||
var json = require("../package.json");
|
var json = require("../package.json");
|
||||||
sys.puts(json.name + ' ' + json.version);
|
sys.puts(json.name + ' ' + json.version);
|
||||||
@@ -210,6 +222,7 @@ var STATS = {};
|
|||||||
var OUTPUT_FILE = ARGS.o;
|
var OUTPUT_FILE = ARGS.o;
|
||||||
var TOPLEVEL = null;
|
var TOPLEVEL = null;
|
||||||
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
var P_RELATIVE = ARGS.p && ARGS.p == "relative";
|
||||||
|
var SOURCES_CONTENT = {};
|
||||||
|
|
||||||
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
|
||||||
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
|
||||||
@@ -247,6 +260,7 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SOURCES_CONTENT[file] = code;
|
||||||
time_it("parse", function(){
|
time_it("parse", function(){
|
||||||
if (ARGS.spidermonkey) {
|
if (ARGS.spidermonkey) {
|
||||||
var program = JSON.parse(code);
|
var program = JSON.parse(code);
|
||||||
@@ -266,6 +280,7 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
filename : file,
|
filename : file,
|
||||||
toplevel : TOPLEVEL,
|
toplevel : TOPLEVEL,
|
||||||
expression : ARGS.expr,
|
expression : ARGS.expr,
|
||||||
|
bare_returns : ARGS.bare_returns,
|
||||||
});
|
});
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
if (ex instanceof UglifyJS.JS_Parse_Error) {
|
||||||
@@ -329,6 +344,15 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
if (MANGLE) time_it("mangle", function(){
|
if (MANGLE) time_it("mangle", function(){
|
||||||
TOPLEVEL.mangle_names(MANGLE);
|
TOPLEVEL.mangle_names(MANGLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (ARGS.source_map_include_sources) {
|
||||||
|
for (var file in SOURCES_CONTENT) {
|
||||||
|
if (SOURCES_CONTENT.hasOwnProperty(file)) {
|
||||||
|
SOURCE_MAP.get().setSourceContent(file, SOURCES_CONTENT[file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
time_it("generate", function(){
|
time_it("generate", function(){
|
||||||
TOPLEVEL.print(output);
|
TOPLEVEL.print(output);
|
||||||
});
|
});
|
||||||
@@ -349,7 +373,6 @@ async.eachLimit(files, 1, function (file, cb) {
|
|||||||
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
fs.writeFileSync(OUTPUT_FILE, output, "utf8");
|
||||||
} else {
|
} else {
|
||||||
sys.print(output);
|
sys.print(output);
|
||||||
sys.error("\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ARGS.stats) {
|
if (ARGS.stats) {
|
||||||
|
|||||||
12
lib/ast.js
12
lib/ast.js
@@ -295,10 +295,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
var parameters = [];
|
var parameters = [];
|
||||||
|
|
||||||
arg_parameter_pairs.forEach(function(pair) {
|
arg_parameter_pairs.forEach(function(pair) {
|
||||||
var split = pair.split(":");
|
var splitAt = pair.lastIndexOf(":");
|
||||||
|
|
||||||
args.push(split[0]);
|
args.push(pair.substr(0, splitAt));
|
||||||
parameters.push(split[1]);
|
parameters.push(pair.substr(splitAt + 1));
|
||||||
});
|
});
|
||||||
|
|
||||||
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
var wrapped_tl = "(function(" + parameters.join(",") + "){ '$ORIG'; })(" + args.join(",") + ")";
|
||||||
@@ -498,12 +498,6 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
|
|||||||
}
|
}
|
||||||
}, AST_Block);
|
}, AST_Block);
|
||||||
|
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the catch block
|
|
||||||
// should introduce another scope, as the argname should be visible
|
|
||||||
// only inside the catch block. However, doing it this way because of
|
|
||||||
// IE which simply introduces the name in the surrounding scope. If
|
|
||||||
// we ever want to fix this then AST_Catch should inherit from
|
|
||||||
// AST_Scope.
|
|
||||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
|
|||||||
165
lib/compress.js
165
lib/compress.js
@@ -61,6 +61,7 @@ function Compressor(options, false_by_default) {
|
|||||||
loops : !false_by_default,
|
loops : !false_by_default,
|
||||||
unused : !false_by_default,
|
unused : !false_by_default,
|
||||||
hoist_funs : !false_by_default,
|
hoist_funs : !false_by_default,
|
||||||
|
keep_fargs : false,
|
||||||
hoist_vars : false,
|
hoist_vars : false,
|
||||||
if_return : !false_by_default,
|
if_return : !false_by_default,
|
||||||
join_vars : !false_by_default,
|
join_vars : !false_by_default,
|
||||||
@@ -70,6 +71,8 @@ function Compressor(options, false_by_default) {
|
|||||||
pure_funcs : null,
|
pure_funcs : null,
|
||||||
negate_iife : !false_by_default,
|
negate_iife : !false_by_default,
|
||||||
screw_ie8 : false,
|
screw_ie8 : false,
|
||||||
|
drop_console : false,
|
||||||
|
angular : false,
|
||||||
|
|
||||||
warnings : true,
|
warnings : true,
|
||||||
global_defs : {}
|
global_defs : {}
|
||||||
@@ -197,6 +200,9 @@ merge(Compressor.prototype, {
|
|||||||
var CHANGED;
|
var CHANGED;
|
||||||
do {
|
do {
|
||||||
CHANGED = false;
|
CHANGED = false;
|
||||||
|
if (compressor.option("angular")) {
|
||||||
|
statements = process_for_angular(statements);
|
||||||
|
}
|
||||||
statements = eliminate_spurious_blocks(statements);
|
statements = eliminate_spurious_blocks(statements);
|
||||||
if (compressor.option("dead_code")) {
|
if (compressor.option("dead_code")) {
|
||||||
statements = eliminate_dead_code(statements, compressor);
|
statements = eliminate_dead_code(statements, compressor);
|
||||||
@@ -218,6 +224,50 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
|
function process_for_angular(statements) {
|
||||||
|
function make_injector(func, name) {
|
||||||
|
return make_node(AST_SimpleStatement, func, {
|
||||||
|
body: make_node(AST_Assign, func, {
|
||||||
|
operator: "=",
|
||||||
|
left: make_node(AST_Dot, name, {
|
||||||
|
expression: make_node(AST_SymbolRef, name, name),
|
||||||
|
property: "$inject"
|
||||||
|
}),
|
||||||
|
right: make_node(AST_Array, func, {
|
||||||
|
elements: func.argnames.map(function(sym){
|
||||||
|
return make_node(AST_String, sym, { value: sym.name });
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return statements.reduce(function(a, stat){
|
||||||
|
a.push(stat);
|
||||||
|
var token = stat.start;
|
||||||
|
var comments = token.comments_before;
|
||||||
|
if (comments && comments.length > 0) {
|
||||||
|
var last = comments.pop();
|
||||||
|
if (/@ngInject/.test(last.value)) {
|
||||||
|
// case 1: defun
|
||||||
|
if (stat instanceof AST_Defun) {
|
||||||
|
a.push(make_injector(stat, stat.name));
|
||||||
|
}
|
||||||
|
else if (stat instanceof AST_Definitions) {
|
||||||
|
stat.definitions.forEach(function(def){
|
||||||
|
if (def.value && def.value instanceof AST_Lambda) {
|
||||||
|
a.push(make_injector(def.value, def.name));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
function eliminate_spurious_blocks(statements) {
|
function eliminate_spurious_blocks(statements) {
|
||||||
var seen_dirs = [];
|
var seen_dirs = [];
|
||||||
return statements.reduce(function(a, stat){
|
return statements.reduce(function(a, stat){
|
||||||
@@ -327,7 +377,7 @@ merge(Compressor.prototype, {
|
|||||||
stat = stat.clone();
|
stat = stat.clone();
|
||||||
stat.condition = stat.condition.negate(compressor);
|
stat.condition = stat.condition.negate(compressor);
|
||||||
stat.body = make_node(AST_BlockStatement, stat, {
|
stat.body = make_node(AST_BlockStatement, stat, {
|
||||||
body: ret
|
body: as_statement_array(stat.alternative).concat(ret)
|
||||||
});
|
});
|
||||||
stat.alternative = make_node(AST_BlockStatement, stat, {
|
stat.alternative = make_node(AST_BlockStatement, stat, {
|
||||||
body: body
|
body: body
|
||||||
@@ -725,6 +775,14 @@ merge(Compressor.prototype, {
|
|||||||
if (d && d.constant && d.init) return ev(d.init, compressor);
|
if (d && d.constant && d.init) return ev(d.init, compressor);
|
||||||
throw def;
|
throw def;
|
||||||
});
|
});
|
||||||
|
def(AST_Dot, function(compressor){
|
||||||
|
if (compressor.option("unsafe") && this.property == "length") {
|
||||||
|
var str = ev(this.expression, compressor);
|
||||||
|
if (typeof str == "string")
|
||||||
|
return str.length;
|
||||||
|
}
|
||||||
|
throw def;
|
||||||
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
node.DEFMETHOD("_eval", func);
|
node.DEFMETHOD("_eval", func);
|
||||||
});
|
});
|
||||||
@@ -839,7 +897,9 @@ merge(Compressor.prototype, {
|
|||||||
|| this.operator == "--"
|
|| this.operator == "--"
|
||||||
|| this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor){ return false });
|
def(AST_SymbolRef, function(compressor){
|
||||||
|
return this.global() && this.undeclared();
|
||||||
|
});
|
||||||
def(AST_Object, function(compressor){
|
def(AST_Object, function(compressor){
|
||||||
for (var i = this.properties.length; --i >= 0;)
|
for (var i = this.properties.length; --i >= 0;)
|
||||||
if (this.properties[i].has_side_effects(compressor))
|
if (this.properties[i].has_side_effects(compressor))
|
||||||
@@ -995,6 +1055,7 @@ merge(Compressor.prototype, {
|
|||||||
var tt = new TreeTransformer(
|
var tt = new TreeTransformer(
|
||||||
function before(node, descend, in_list) {
|
function before(node, descend, in_list) {
|
||||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||||
|
if (!compressor.option("keep_fargs")) {
|
||||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||||
var sym = a[i];
|
var sym = a[i];
|
||||||
if (sym.unreferenced()) {
|
if (sym.unreferenced()) {
|
||||||
@@ -1009,6 +1070,7 @@ merge(Compressor.prototype, {
|
|||||||
else break;
|
else break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (node instanceof AST_Defun && node !== self) {
|
if (node instanceof AST_Defun && node !== self) {
|
||||||
if (!member(node.name.definition(), in_use)) {
|
if (!member(node.name.definition(), in_use)) {
|
||||||
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
compressor.warn("Dropping unused function {name} [{file}:{line},{col}]", {
|
||||||
@@ -1669,10 +1731,10 @@ merge(Compressor.prototype, {
|
|||||||
return arg.value;
|
return arg.value;
|
||||||
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
||||||
var ast = parse(code);
|
var ast = parse(code);
|
||||||
ast.figure_out_scope();
|
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||||
var comp = new Compressor(compressor.options);
|
var comp = new Compressor(compressor.options);
|
||||||
ast = ast.transform(comp);
|
ast = ast.transform(comp);
|
||||||
ast.figure_out_scope();
|
ast.figure_out_scope({ screw_ie8: compressor.option("screw_ie8") });
|
||||||
ast.mangle_names();
|
ast.mangle_names();
|
||||||
var fun;
|
var fun;
|
||||||
try {
|
try {
|
||||||
@@ -1685,6 +1747,7 @@ merge(Compressor.prototype, {
|
|||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
if (ex !== ast) throw ex;
|
if (ex !== ast) throw ex;
|
||||||
};
|
};
|
||||||
|
if (!fun) return self;
|
||||||
var args = fun.argnames.map(function(arg, i){
|
var args = fun.argnames.map(function(arg, i){
|
||||||
return make_node(AST_String, self.args[i], {
|
return make_node(AST_String, self.args[i], {
|
||||||
value: arg.print_to_string()
|
value: arg.print_to_string()
|
||||||
@@ -1773,6 +1836,14 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Undefined, self).transform(compressor);
|
return make_node(AST_Undefined, self).transform(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (compressor.option("drop_console")) {
|
||||||
|
if (self.expression instanceof AST_PropAccess &&
|
||||||
|
self.expression.expression instanceof AST_SymbolRef &&
|
||||||
|
self.expression.expression.name == "console" &&
|
||||||
|
self.expression.expression.undeclared()) {
|
||||||
|
return make_node(AST_Undefined, self).transform(compressor);
|
||||||
|
}
|
||||||
|
}
|
||||||
return self.evaluate(compressor)[0];
|
return self.evaluate(compressor)[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1811,16 +1882,34 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("cascade")) {
|
if (compressor.option("cascade")) {
|
||||||
if (self.car instanceof AST_Assign
|
if (self.car instanceof AST_Assign
|
||||||
&& !self.car.left.has_side_effects(compressor)
|
&& !self.car.left.has_side_effects(compressor)) {
|
||||||
&& self.car.left.equivalent_to(self.cdr)) {
|
if (self.car.left.equivalent_to(self.cdr)) {
|
||||||
return self.car;
|
return self.car;
|
||||||
}
|
}
|
||||||
|
if (self.cdr instanceof AST_Call
|
||||||
|
&& self.cdr.expression.equivalent_to(self.car.left)) {
|
||||||
|
self.cdr.expression = self.car;
|
||||||
|
return self.cdr;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!self.car.has_side_effects(compressor)
|
if (!self.car.has_side_effects(compressor)
|
||||||
&& !self.cdr.has_side_effects(compressor)
|
&& !self.cdr.has_side_effects(compressor)
|
||||||
&& self.car.equivalent_to(self.cdr)) {
|
&& self.car.equivalent_to(self.cdr)) {
|
||||||
return self.car;
|
return self.car;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (self.cdr instanceof AST_UnaryPrefix
|
||||||
|
&& self.cdr.operator == "void"
|
||||||
|
&& !self.cdr.expression.has_side_effects(compressor)) {
|
||||||
|
self.cdr.operator = self.car;
|
||||||
|
return self.cdr;
|
||||||
|
}
|
||||||
|
if (self.cdr instanceof AST_Undefined) {
|
||||||
|
return make_node(AST_UnaryPrefix, self, {
|
||||||
|
operator : "void",
|
||||||
|
expression : self.car
|
||||||
|
});
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2192,7 +2281,7 @@ merge(Compressor.prototype, {
|
|||||||
* ==>
|
* ==>
|
||||||
* exp = foo ? something : something_else;
|
* exp = foo ? something : something_else;
|
||||||
*/
|
*/
|
||||||
self = make_node(AST_Assign, self, {
|
return make_node(AST_Assign, self, {
|
||||||
operator: consequent.operator,
|
operator: consequent.operator,
|
||||||
left: consequent.left,
|
left: consequent.left,
|
||||||
right: make_node(AST_Conditional, self, {
|
right: make_node(AST_Conditional, self, {
|
||||||
@@ -2202,6 +2291,49 @@ merge(Compressor.prototype, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (consequent instanceof AST_Call
|
||||||
|
&& alternative.TYPE === consequent.TYPE
|
||||||
|
&& consequent.args.length == alternative.args.length
|
||||||
|
&& consequent.expression.equivalent_to(alternative.expression)) {
|
||||||
|
if (consequent.args.length == 0) {
|
||||||
|
return make_node(AST_Seq, self, {
|
||||||
|
car: self.condition,
|
||||||
|
cdr: consequent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (consequent.args.length == 1) {
|
||||||
|
consequent.args[0] = make_node(AST_Conditional, self, {
|
||||||
|
condition: self.condition,
|
||||||
|
consequent: consequent.args[0],
|
||||||
|
alternative: alternative.args[0]
|
||||||
|
});
|
||||||
|
return consequent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x?y?z:a:a --> x&&y?z:a
|
||||||
|
if (consequent instanceof AST_Conditional
|
||||||
|
&& consequent.alternative.equivalent_to(alternative)) {
|
||||||
|
return make_node(AST_Conditional, self, {
|
||||||
|
condition: make_node(AST_Binary, self, {
|
||||||
|
left: self.condition,
|
||||||
|
operator: "&&",
|
||||||
|
right: consequent.condition
|
||||||
|
}),
|
||||||
|
consequent: consequent.consequent,
|
||||||
|
alternative: alternative
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// x=y?1:1 --> x=1
|
||||||
|
if (consequent instanceof AST_Constant
|
||||||
|
&& alternative instanceof AST_Constant
|
||||||
|
&& consequent.equivalent_to(alternative)) {
|
||||||
|
if (self.condition.has_side_effects(compressor)) {
|
||||||
|
return AST_Seq.from_array([self.condition, make_node_from_constant(compressor, consequent.value, self)]);
|
||||||
|
} else {
|
||||||
|
return make_node_from_constant(compressor, consequent.value, self);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2239,12 +2371,31 @@ merge(Compressor.prototype, {
|
|||||||
return make_node(AST_Dot, self, {
|
return make_node(AST_Dot, self, {
|
||||||
expression : self.expression,
|
expression : self.expression,
|
||||||
property : prop
|
property : prop
|
||||||
|
}).optimize(compressor);
|
||||||
|
}
|
||||||
|
var v = parseFloat(prop);
|
||||||
|
if (!isNaN(v) && v.toString() == prop) {
|
||||||
|
self.property = make_node(AST_Number, self.property, {
|
||||||
|
value: v
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
OPT(AST_Dot, function(self, compressor){
|
||||||
|
var prop = self.property;
|
||||||
|
if (RESERVED_WORDS(prop) && !compressor.option("screw_ie8")) {
|
||||||
|
return make_node(AST_Sub, self, {
|
||||||
|
expression : self.expression,
|
||||||
|
property : make_node(AST_String, self, {
|
||||||
|
value: prop
|
||||||
|
})
|
||||||
|
}).optimize(compressor);
|
||||||
|
}
|
||||||
|
return self.evaluate(compressor)[0];
|
||||||
|
});
|
||||||
|
|
||||||
function literals_in_boolean_context(self, compressor) {
|
function literals_in_boolean_context(self, compressor) {
|
||||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||||
return make_node(AST_True, self);
|
return make_node(AST_True, self);
|
||||||
|
|||||||
@@ -46,37 +46,44 @@
|
|||||||
(function(){
|
(function(){
|
||||||
|
|
||||||
var MOZ_TO_ME = {
|
var MOZ_TO_ME = {
|
||||||
TryStatement : function(M) {
|
ExpressionStatement: function(M) {
|
||||||
|
var expr = M.expression;
|
||||||
|
if (expr.type === "Literal" && typeof expr.value === "string") {
|
||||||
|
return new AST_Directive({
|
||||||
|
start: my_start_token(M),
|
||||||
|
end: my_end_token(M),
|
||||||
|
value: expr.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new AST_SimpleStatement({
|
||||||
|
start: my_start_token(M),
|
||||||
|
end: my_end_token(M),
|
||||||
|
body: from_moz(expr)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
TryStatement: function(M) {
|
||||||
|
var handlers = M.handlers || [M.handler];
|
||||||
|
if (handlers.length > 1 || M.guardedHandlers && M.guardedHandlers.length) {
|
||||||
|
throw new Error("Multiple catch clauses are not supported.");
|
||||||
|
}
|
||||||
return new AST_Try({
|
return new AST_Try({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
body : from_moz(M.block).body,
|
body : from_moz(M.block).body,
|
||||||
bcatch : from_moz(M.handlers[0]),
|
bcatch : from_moz(handlers[0]),
|
||||||
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
bfinally : M.finalizer ? new AST_Finally(from_moz(M.finalizer)) : null
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
CatchClause : function(M) {
|
Property: function(M) {
|
||||||
return new AST_Catch({
|
var key = M.key;
|
||||||
start : my_start_token(M),
|
|
||||||
end : my_end_token(M),
|
|
||||||
argname : from_moz(M.param),
|
|
||||||
body : from_moz(M.body).body
|
|
||||||
});
|
|
||||||
},
|
|
||||||
ObjectExpression : function(M) {
|
|
||||||
return new AST_Object({
|
|
||||||
start : my_start_token(M),
|
|
||||||
end : my_end_token(M),
|
|
||||||
properties : M.properties.map(function(prop){
|
|
||||||
var key = prop.key;
|
|
||||||
var name = key.type == "Identifier" ? key.name : key.value;
|
var name = key.type == "Identifier" ? key.name : key.value;
|
||||||
var args = {
|
var args = {
|
||||||
start : my_start_token(key),
|
start : my_start_token(key),
|
||||||
end : my_end_token(prop.value),
|
end : my_end_token(M.value),
|
||||||
key : name,
|
key : name,
|
||||||
value : from_moz(prop.value)
|
value : from_moz(M.value)
|
||||||
};
|
};
|
||||||
switch (prop.kind) {
|
switch (M.kind) {
|
||||||
case "init":
|
case "init":
|
||||||
return new AST_ObjectKeyVal(args);
|
return new AST_ObjectKeyVal(args);
|
||||||
case "set":
|
case "set":
|
||||||
@@ -86,13 +93,21 @@
|
|||||||
args.value.name = from_moz(key);
|
args.value.name = from_moz(key);
|
||||||
return new AST_ObjectGetter(args);
|
return new AST_ObjectGetter(args);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ObjectExpression: function(M) {
|
||||||
|
return new AST_Object({
|
||||||
|
start : my_start_token(M),
|
||||||
|
end : my_end_token(M),
|
||||||
|
properties : M.properties.map(function(prop){
|
||||||
|
prop.type = "Property";
|
||||||
|
return from_moz(prop)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
SequenceExpression : function(M) {
|
SequenceExpression: function(M) {
|
||||||
return AST_Seq.from_array(M.expressions.map(from_moz));
|
return AST_Seq.from_array(M.expressions.map(from_moz));
|
||||||
},
|
},
|
||||||
MemberExpression : function(M) {
|
MemberExpression: function(M) {
|
||||||
return new (M.computed ? AST_Sub : AST_Dot)({
|
return new (M.computed ? AST_Sub : AST_Dot)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
@@ -100,7 +115,7 @@
|
|||||||
expression : from_moz(M.object)
|
expression : from_moz(M.object)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
SwitchCase : function(M) {
|
SwitchCase: function(M) {
|
||||||
return new (M.test ? AST_Case : AST_Default)({
|
return new (M.test ? AST_Case : AST_Default)({
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M),
|
end : my_end_token(M),
|
||||||
@@ -108,7 +123,14 @@
|
|||||||
body : M.consequent.map(from_moz)
|
body : M.consequent.map(from_moz)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
Literal : function(M) {
|
VariableDeclaration: function(M) {
|
||||||
|
return new (M.kind === "const" ? AST_Const : AST_Var)({
|
||||||
|
start : my_start_token(M),
|
||||||
|
end : my_end_token(M),
|
||||||
|
definitions : M.declarations.map(from_moz)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
Literal: function(M) {
|
||||||
var val = M.value, args = {
|
var val = M.value, args = {
|
||||||
start : my_start_token(M),
|
start : my_start_token(M),
|
||||||
end : my_end_token(M)
|
end : my_end_token(M)
|
||||||
@@ -128,12 +150,9 @@
|
|||||||
return new AST_RegExp(args);
|
return new AST_RegExp(args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UnaryExpression: From_Moz_Unary,
|
|
||||||
UpdateExpression: From_Moz_Unary,
|
|
||||||
Identifier: function(M) {
|
Identifier: function(M) {
|
||||||
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
var p = FROM_MOZ_STACK[FROM_MOZ_STACK.length - 2];
|
||||||
return new (M.name == "this" ? AST_This
|
return new ( p.type == "LabeledStatement" ? AST_Label
|
||||||
: p.type == "LabeledStatement" ? AST_Label
|
|
||||||
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
: p.type == "VariableDeclarator" && p.id === M ? (p.kind == "const" ? AST_SymbolConst : AST_SymbolVar)
|
||||||
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
: p.type == "FunctionExpression" ? (p.id === M ? AST_SymbolLambda : AST_SymbolFunarg)
|
||||||
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
: p.type == "FunctionDeclaration" ? (p.id === M ? AST_SymbolDefun : AST_SymbolFunarg)
|
||||||
@@ -147,7 +166,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function From_Moz_Unary(M) {
|
MOZ_TO_ME.UpdateExpression =
|
||||||
|
MOZ_TO_ME.UnaryExpression = function To_Moz_Unary(M) {
|
||||||
var prefix = "prefix" in M ? M.prefix
|
var prefix = "prefix" in M ? M.prefix
|
||||||
: M.type == "UnaryExpression" ? true : false;
|
: M.type == "UnaryExpression" ? true : false;
|
||||||
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
return new (prefix ? AST_UnaryPrefix : AST_UnaryPostfix)({
|
||||||
@@ -158,14 +178,9 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var ME_TO_MOZ = {};
|
|
||||||
|
|
||||||
map("Node", AST_Node);
|
|
||||||
map("Program", AST_Toplevel, "body@body");
|
map("Program", AST_Toplevel, "body@body");
|
||||||
map("Function", AST_Function, "id>name, params@argnames, body%body");
|
|
||||||
map("EmptyStatement", AST_EmptyStatement);
|
map("EmptyStatement", AST_EmptyStatement);
|
||||||
map("BlockStatement", AST_BlockStatement, "body@body");
|
map("BlockStatement", AST_BlockStatement, "body@body");
|
||||||
map("ExpressionStatement", AST_SimpleStatement, "expression>body");
|
|
||||||
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
|
map("IfStatement", AST_If, "test>condition, consequent>body, alternate>alternative");
|
||||||
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
|
map("LabeledStatement", AST_LabeledStatement, "label>label, body>body");
|
||||||
map("BreakStatement", AST_Break, "label>label");
|
map("BreakStatement", AST_Break, "label>label");
|
||||||
@@ -180,71 +195,257 @@
|
|||||||
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
|
map("ForInStatement", AST_ForIn, "left>init, right>object, body>body");
|
||||||
map("DebuggerStatement", AST_Debugger);
|
map("DebuggerStatement", AST_Debugger);
|
||||||
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
|
map("FunctionDeclaration", AST_Defun, "id>name, params@argnames, body%body");
|
||||||
map("VariableDeclaration", AST_Var, "declarations@definitions");
|
|
||||||
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
|
map("VariableDeclarator", AST_VarDef, "id>name, init>value");
|
||||||
|
map("CatchClause", AST_Catch, "param>argname, body%body");
|
||||||
|
|
||||||
map("ThisExpression", AST_This);
|
map("ThisExpression", AST_This);
|
||||||
map("ArrayExpression", AST_Array, "elements@elements");
|
map("ArrayExpression", AST_Array, "elements@elements");
|
||||||
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
|
map("FunctionExpression", AST_Function, "id>name, params@argnames, body%body");
|
||||||
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
|
map("BinaryExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||||
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
|
||||||
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
|
map("LogicalExpression", AST_Binary, "operator=operator, left>left, right>right");
|
||||||
|
map("AssignmentExpression", AST_Assign, "operator=operator, left>left, right>right");
|
||||||
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
|
map("ConditionalExpression", AST_Conditional, "test>condition, consequent>consequent, alternate>alternative");
|
||||||
map("NewExpression", AST_New, "callee>expression, arguments@args");
|
map("NewExpression", AST_New, "callee>expression, arguments@args");
|
||||||
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
map("CallExpression", AST_Call, "callee>expression, arguments@args");
|
||||||
|
|
||||||
|
def_to_moz(AST_Directive, function To_Moz_Directive(M) {
|
||||||
|
return {
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
expression: {
|
||||||
|
type: "Literal",
|
||||||
|
value: M.value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_SimpleStatement, function To_Moz_ExpressionStatement(M) {
|
||||||
|
return {
|
||||||
|
type: "ExpressionStatement",
|
||||||
|
expression: to_moz(M.body)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_SwitchBranch, function To_Moz_SwitchCase(M) {
|
||||||
|
return {
|
||||||
|
type: "SwitchCase",
|
||||||
|
test: to_moz(M.expression),
|
||||||
|
consequent: M.body.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Try, function To_Moz_TryStatement(M) {
|
||||||
|
return {
|
||||||
|
type: "TryStatement",
|
||||||
|
block: to_moz_block(M),
|
||||||
|
handler: to_moz(M.bcatch),
|
||||||
|
guardedHandlers: [],
|
||||||
|
finalizer: to_moz(M.bfinally)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Catch, function To_Moz_CatchClause(M) {
|
||||||
|
return {
|
||||||
|
type: "CatchClause",
|
||||||
|
param: to_moz(M.argname),
|
||||||
|
guard: null,
|
||||||
|
body: to_moz_block(M)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Definitions, function To_Moz_VariableDeclaration(M) {
|
||||||
|
return {
|
||||||
|
type: "VariableDeclaration",
|
||||||
|
kind: M instanceof AST_Const ? "const" : "var",
|
||||||
|
declarations: M.definitions.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Seq, function To_Moz_SequenceExpression(M) {
|
||||||
|
return {
|
||||||
|
type: "SequenceExpression",
|
||||||
|
expressions: M.to_array().map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_PropAccess, function To_Moz_MemberExpression(M) {
|
||||||
|
var isComputed = M instanceof AST_Sub;
|
||||||
|
return {
|
||||||
|
type: "MemberExpression",
|
||||||
|
object: to_moz(M.expression),
|
||||||
|
computed: isComputed,
|
||||||
|
property: isComputed ? to_moz(M.property) : {type: "Identifier", name: M.property}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Unary, function To_Moz_Unary(M) {
|
||||||
|
return {
|
||||||
|
type: M.operator == "++" || M.operator == "--" ? "UpdateExpression" : "UnaryExpression",
|
||||||
|
operator: M.operator,
|
||||||
|
prefix: M instanceof AST_UnaryPrefix,
|
||||||
|
argument: to_moz(M.expression)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Binary, function To_Moz_BinaryExpression(M) {
|
||||||
|
return {
|
||||||
|
type: M.operator == "&&" || M.operator == "||" ? "LogicalExpression" : "BinaryExpression",
|
||||||
|
left: to_moz(M.left),
|
||||||
|
operator: M.operator,
|
||||||
|
right: to_moz(M.right)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Object, function To_Moz_ObjectExpression(M) {
|
||||||
|
return {
|
||||||
|
type: "ObjectExpression",
|
||||||
|
properties: M.properties.map(to_moz)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||||
|
var key = (
|
||||||
|
is_identifier(M.key)
|
||||||
|
? {type: "Identifier", name: M.key}
|
||||||
|
: {type: "Literal", value: M.key}
|
||||||
|
);
|
||||||
|
var kind;
|
||||||
|
if (M instanceof AST_ObjectKeyVal) {
|
||||||
|
kind = "init";
|
||||||
|
} else
|
||||||
|
if (M instanceof AST_ObjectGetter) {
|
||||||
|
kind = "get";
|
||||||
|
} else
|
||||||
|
if (M instanceof AST_ObjectSetter) {
|
||||||
|
kind = "set";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "Property",
|
||||||
|
kind: kind,
|
||||||
|
key: key,
|
||||||
|
value: to_moz(M.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Symbol, function To_Moz_Identifier(M) {
|
||||||
|
var def = M.definition();
|
||||||
|
return {
|
||||||
|
type: "Identifier",
|
||||||
|
name: def ? def.mangled_name || def.name : M.name
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Constant, function To_Moz_Literal(M) {
|
||||||
|
var value = M.value;
|
||||||
|
if (typeof value === 'number' && (value < 0 || (value === 0 && 1 / value < 0))) {
|
||||||
|
return {
|
||||||
|
type: "UnaryExpression",
|
||||||
|
operator: "-",
|
||||||
|
prefix: true,
|
||||||
|
argument: {
|
||||||
|
type: "Literal",
|
||||||
|
value: -value
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "Literal",
|
||||||
|
value: value
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
def_to_moz(AST_Atom, function To_Moz_Atom(M) {
|
||||||
|
return {
|
||||||
|
type: "Identifier",
|
||||||
|
name: String(M.value)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Boolean.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||||
|
AST_Null.DEFMETHOD("to_mozilla_ast", AST_Constant.prototype.to_mozilla_ast);
|
||||||
|
AST_Hole.DEFMETHOD("to_mozilla_ast", function To_Moz_ArrayHole() { return null });
|
||||||
|
|
||||||
|
AST_Block.DEFMETHOD("to_mozilla_ast", AST_BlockStatement.prototype.to_mozilla_ast);
|
||||||
|
AST_Lambda.DEFMETHOD("to_mozilla_ast", AST_Function.prototype.to_mozilla_ast);
|
||||||
|
|
||||||
/* -----[ tools ]----- */
|
/* -----[ tools ]----- */
|
||||||
|
|
||||||
function my_start_token(moznode) {
|
function my_start_token(moznode) {
|
||||||
|
var loc = moznode.loc;
|
||||||
|
var range = moznode.range;
|
||||||
return new AST_Token({
|
return new AST_Token({
|
||||||
file : moznode.loc && moznode.loc.source,
|
file : loc && loc.source,
|
||||||
line : moznode.loc && moznode.loc.start.line,
|
line : loc && loc.start.line,
|
||||||
col : moznode.loc && moznode.loc.start.column,
|
col : loc && loc.start.column,
|
||||||
pos : moznode.start,
|
pos : range ? range[0] : moznode.start,
|
||||||
endpos : moznode.start
|
endpos : range ? range[0] : moznode.start
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function my_end_token(moznode) {
|
function my_end_token(moznode) {
|
||||||
|
var loc = moznode.loc;
|
||||||
|
var range = moznode.range;
|
||||||
return new AST_Token({
|
return new AST_Token({
|
||||||
file : moznode.loc && moznode.loc.source,
|
file : loc && loc.source,
|
||||||
line : moznode.loc && moznode.loc.end.line,
|
line : loc && loc.end.line,
|
||||||
col : moznode.loc && moznode.loc.end.column,
|
col : loc && loc.end.column,
|
||||||
pos : moznode.end,
|
pos : range ? range[1] : moznode.end,
|
||||||
endpos : moznode.end
|
endpos : range ? range[1] : moznode.end
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function map(moztype, mytype, propmap) {
|
function map(moztype, mytype, propmap) {
|
||||||
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
|
var moz_to_me = "function From_Moz_" + moztype + "(M){\n";
|
||||||
moz_to_me += "return new mytype({\n" +
|
moz_to_me += "return new " + mytype.name + "({\n" +
|
||||||
"start: my_start_token(M),\n" +
|
"start: my_start_token(M),\n" +
|
||||||
"end: my_end_token(M)";
|
"end: my_end_token(M)";
|
||||||
|
|
||||||
|
var me_to_moz = "function To_Moz_" + moztype + "(M){\n";
|
||||||
|
me_to_moz += "return {\n" +
|
||||||
|
"type: " + JSON.stringify(moztype);
|
||||||
|
|
||||||
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
|
if (propmap) propmap.split(/\s*,\s*/).forEach(function(prop){
|
||||||
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
|
var m = /([a-z0-9$_]+)(=|@|>|%)([a-z0-9$_]+)/i.exec(prop);
|
||||||
if (!m) throw new Error("Can't understand property map: " + prop);
|
if (!m) throw new Error("Can't understand property map: " + prop);
|
||||||
var moz = "M." + m[1], how = m[2], my = m[3];
|
var moz = m[1], how = m[2], my = m[3];
|
||||||
moz_to_me += ",\n" + my + ": ";
|
moz_to_me += ",\n" + my + ": ";
|
||||||
if (how == "@") {
|
me_to_moz += ",\n" + moz + ": ";
|
||||||
moz_to_me += moz + ".map(from_moz)";
|
switch (how) {
|
||||||
} else if (how == ">") {
|
case "@":
|
||||||
moz_to_me += "from_moz(" + moz + ")";
|
moz_to_me += "M." + moz + ".map(from_moz)";
|
||||||
} else if (how == "=") {
|
me_to_moz += "M." + my + ".map(to_moz)";
|
||||||
moz_to_me += moz;
|
break;
|
||||||
} else if (how == "%") {
|
case ">":
|
||||||
moz_to_me += "from_moz(" + moz + ").body";
|
moz_to_me += "from_moz(M." + moz + ")";
|
||||||
} else throw new Error("Can't understand operator in propmap: " + prop);
|
me_to_moz += "to_moz(M." + my + ")";
|
||||||
|
break;
|
||||||
|
case "=":
|
||||||
|
moz_to_me += "M." + moz;
|
||||||
|
me_to_moz += "M." + my;
|
||||||
|
break;
|
||||||
|
case "%":
|
||||||
|
moz_to_me += "from_moz(M." + moz + ").body";
|
||||||
|
me_to_moz += "to_moz_block(M)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Can't understand operator in propmap: " + prop);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
moz_to_me += "\n})}";
|
|
||||||
|
|
||||||
// moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
moz_to_me += "\n})\n}";
|
||||||
// console.log(moz_to_me);
|
me_to_moz += "\n}\n}";
|
||||||
|
|
||||||
moz_to_me = new Function("mytype", "my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
//moz_to_me = parse(moz_to_me).print_to_string({ beautify: true });
|
||||||
mytype, my_start_token, my_end_token, from_moz
|
//me_to_moz = parse(me_to_moz).print_to_string({ beautify: true });
|
||||||
|
//console.log(moz_to_me);
|
||||||
|
|
||||||
|
moz_to_me = new Function("my_start_token", "my_end_token", "from_moz", "return(" + moz_to_me + ")")(
|
||||||
|
my_start_token, my_end_token, from_moz
|
||||||
);
|
);
|
||||||
return MOZ_TO_ME[moztype] = moz_to_me;
|
me_to_moz = new Function("to_moz", "to_moz_block", "return(" + me_to_moz + ")")(
|
||||||
|
to_moz, to_moz_block
|
||||||
|
);
|
||||||
|
MOZ_TO_ME[moztype] = moz_to_me;
|
||||||
|
def_to_moz(mytype, me_to_moz);
|
||||||
};
|
};
|
||||||
|
|
||||||
var FROM_MOZ_STACK = null;
|
var FROM_MOZ_STACK = null;
|
||||||
@@ -264,4 +465,46 @@
|
|||||||
return ast;
|
return ast;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function moz_sub_loc(token) {
|
||||||
|
return token.line ? {
|
||||||
|
line: token.line,
|
||||||
|
column: token.col
|
||||||
|
} : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function set_moz_loc(mynode, moznode) {
|
||||||
|
var start = mynode.start;
|
||||||
|
var end = mynode.end;
|
||||||
|
if (start.pos != null && end.pos != null) {
|
||||||
|
moznode.range = [start.pos, end.pos];
|
||||||
|
}
|
||||||
|
if (start.line) {
|
||||||
|
moznode.loc = {
|
||||||
|
start: moz_sub_loc(start),
|
||||||
|
end: moz_sub_loc(end)
|
||||||
|
};
|
||||||
|
if (start.file) {
|
||||||
|
moznode.loc.source = start.file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moznode;
|
||||||
|
};
|
||||||
|
|
||||||
|
function def_to_moz(mytype, handler) {
|
||||||
|
mytype.DEFMETHOD("to_mozilla_ast", function() {
|
||||||
|
return set_moz_loc(this, handler(this));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function to_moz(node) {
|
||||||
|
return node != null ? node.to_mozilla_ast() : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function to_moz_block(node) {
|
||||||
|
return {
|
||||||
|
type: "BlockStatement",
|
||||||
|
body: node.body.map(to_moz)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ function OutputStream(options) {
|
|||||||
quote_keys : false,
|
quote_keys : false,
|
||||||
space_colon : true,
|
space_colon : true,
|
||||||
ascii_only : false,
|
ascii_only : false,
|
||||||
|
unescape_regexps : false,
|
||||||
inline_script : false,
|
inline_script : false,
|
||||||
width : 80,
|
width : 80,
|
||||||
max_line_len : 32000,
|
max_line_len : 32000,
|
||||||
@@ -386,13 +387,20 @@ function OutputStream(options) {
|
|||||||
var comments = start.comments_before || [];
|
var comments = start.comments_before || [];
|
||||||
|
|
||||||
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
|
||||||
// if this node is `return` or `throw`, we cannot allow comments before
|
// and https://github.com/mishoo/UglifyJS2/issues/372
|
||||||
// the returned or thrown value.
|
if (self instanceof AST_Exit && self.value) {
|
||||||
if (self instanceof AST_Exit && self.value
|
self.value.walk(new TreeWalker(function(node){
|
||||||
&& self.value.start.comments_before
|
if (node.start && node.start.comments_before) {
|
||||||
&& self.value.start.comments_before.length > 0) {
|
comments = comments.concat(node.start.comments_before);
|
||||||
comments = comments.concat(self.value.start.comments_before);
|
node.start.comments_before = [];
|
||||||
self.value.start.comments_before = [];
|
}
|
||||||
|
if (node instanceof AST_Function ||
|
||||||
|
node instanceof AST_Array ||
|
||||||
|
node instanceof AST_Object)
|
||||||
|
{
|
||||||
|
return true; // don't go inside.
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.test) {
|
if (c.test) {
|
||||||
@@ -426,7 +434,13 @@ function OutputStream(options) {
|
|||||||
/* -----[ PARENTHESES ]----- */
|
/* -----[ PARENTHESES ]----- */
|
||||||
|
|
||||||
function PARENS(nodetype, func) {
|
function PARENS(nodetype, func) {
|
||||||
|
if (Array.isArray(nodetype)) {
|
||||||
|
nodetype.forEach(function(nodetype){
|
||||||
|
PARENS(nodetype, func);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
nodetype.DEFMETHOD("needs_parens", func);
|
nodetype.DEFMETHOD("needs_parens", func);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PARENS(AST_Node, function(){
|
PARENS(AST_Node, function(){
|
||||||
@@ -445,7 +459,7 @@ function OutputStream(options) {
|
|||||||
return first_in_statement(output);
|
return first_in_statement(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Unary, function(output){
|
PARENS([ AST_Unary, AST_Undefined ], function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_PropAccess && p.expression === this;
|
return p instanceof AST_PropAccess && p.expression === this;
|
||||||
});
|
});
|
||||||
@@ -456,7 +470,7 @@ function OutputStream(options) {
|
|||||||
|| p instanceof AST_Unary // !(foo, bar, baz)
|
|| p instanceof AST_Unary // !(foo, bar, baz)
|
||||||
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|
|| p instanceof AST_Binary // 1 + (2, 3) + 4 ==> 8
|
||||||
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
|| p instanceof AST_VarDef // var a = (1, 2), b = a + a; ==> b == 4
|
||||||
|| p instanceof AST_Dot // (1, {foo:2}).foo ==> 2
|
|| p instanceof AST_PropAccess // (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||||
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
|| p instanceof AST_Array // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||||
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
|| p instanceof AST_ObjectProperty // { foo: (1, 2) }.foo ==> 2
|
||||||
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
|| p instanceof AST_Conditional /* (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||||
@@ -541,7 +555,7 @@ function OutputStream(options) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
function assign_and_conditional_paren_rules(output) {
|
PARENS([ AST_Assign, AST_Conditional ], function (output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
if (p instanceof AST_Unary)
|
if (p instanceof AST_Unary)
|
||||||
@@ -558,10 +572,7 @@ function OutputStream(options) {
|
|||||||
// (a = foo)["prop"] —or— (a = foo).prop
|
// (a = foo)["prop"] —or— (a = foo).prop
|
||||||
if (p instanceof AST_PropAccess && p.expression === this)
|
if (p instanceof AST_PropAccess && p.expression === this)
|
||||||
return true;
|
return true;
|
||||||
};
|
});
|
||||||
|
|
||||||
PARENS(AST_Assign, assign_and_conditional_paren_rules);
|
|
||||||
PARENS(AST_Conditional, assign_and_conditional_paren_rules);
|
|
||||||
|
|
||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
|
|
||||||
@@ -648,7 +659,7 @@ function OutputStream(options) {
|
|||||||
output.print("for");
|
output.print("for");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
if (self.init) {
|
if (self.init && !(self.init instanceof AST_EmptyStatement)) {
|
||||||
if (self.init instanceof AST_Definitions) {
|
if (self.init instanceof AST_Definitions) {
|
||||||
self.init.print(output);
|
self.init.print(output);
|
||||||
} else {
|
} else {
|
||||||
@@ -988,8 +999,12 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_UnaryPrefix, function(self, output){
|
DEFPRINT(AST_UnaryPrefix, function(self, output){
|
||||||
var op = self.operator;
|
var op = self.operator;
|
||||||
output.print(op);
|
output.print(op);
|
||||||
if (/^[a-z]/i.test(op))
|
if (/^[a-z]/i.test(op)
|
||||||
|
|| (/[+-]$/.test(op)
|
||||||
|
&& self.expression instanceof AST_UnaryPrefix
|
||||||
|
&& /^[+-]/.test(self.expression.operator))) {
|
||||||
output.space();
|
output.space();
|
||||||
|
}
|
||||||
self.expression.print(output);
|
self.expression.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_UnaryPostfix, function(self, output){
|
DEFPRINT(AST_UnaryPostfix, function(self, output){
|
||||||
@@ -1111,10 +1126,47 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Number, function(self, output){
|
DEFPRINT(AST_Number, function(self, output){
|
||||||
output.print(make_num(self.getValue()));
|
output.print(make_num(self.getValue()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function regexp_safe_literal(code) {
|
||||||
|
return [
|
||||||
|
0x5c , // \
|
||||||
|
0x2f , // /
|
||||||
|
0x2e , // .
|
||||||
|
0x2b , // +
|
||||||
|
0x2a , // *
|
||||||
|
0x3f , // ?
|
||||||
|
0x28 , // (
|
||||||
|
0x29 , // )
|
||||||
|
0x5b , // [
|
||||||
|
0x5d , // ]
|
||||||
|
0x7b , // {
|
||||||
|
0x7d , // }
|
||||||
|
0x24 , // $
|
||||||
|
0x5e , // ^
|
||||||
|
0x3a , // :
|
||||||
|
0x7c , // |
|
||||||
|
0x21 , // !
|
||||||
|
0x0a , // \n
|
||||||
|
0x0d , // \r
|
||||||
|
0x00 , // \0
|
||||||
|
0xfeff , // Unicode BOM
|
||||||
|
0x2028 , // unicode "line separator"
|
||||||
|
0x2029 , // unicode "paragraph separator"
|
||||||
|
].indexOf(code) < 0;
|
||||||
|
};
|
||||||
|
|
||||||
DEFPRINT(AST_RegExp, function(self, output){
|
DEFPRINT(AST_RegExp, function(self, output){
|
||||||
var str = self.getValue().toString();
|
var str = self.getValue().toString();
|
||||||
if (output.option("ascii_only"))
|
if (output.option("ascii_only")) {
|
||||||
str = output.to_ascii(str);
|
str = output.to_ascii(str);
|
||||||
|
} else if (output.option("unescape_regexps")) {
|
||||||
|
str = str.split("\\\\").map(function(str){
|
||||||
|
return str.replace(/\\u[0-9a-fA-F]{4}|\\x[0-9a-fA-F]{2}/g, function(s){
|
||||||
|
var code = parseInt(s.substr(2), 16);
|
||||||
|
return regexp_safe_literal(code) ? String.fromCharCode(code) : s;
|
||||||
|
});
|
||||||
|
}).join("\\\\");
|
||||||
|
}
|
||||||
output.print(str);
|
output.print(str);
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||||
|
|||||||
16
lib/parse.js
16
lib/parse.js
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
|
var KEYWORDS = 'break case catch const continue debugger default delete do else finally for function if in instanceof new return switch throw try typeof var void while with';
|
||||||
var KEYWORDS_ATOM = 'false null true';
|
var KEYWORDS_ATOM = 'false null true';
|
||||||
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile'
|
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized this throws transient volatile yield'
|
||||||
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
|
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
|
||||||
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
|
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
|
||||||
|
|
||||||
@@ -168,14 +168,7 @@ function is_identifier_char(ch) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function is_identifier_string(str){
|
function is_identifier_string(str){
|
||||||
var i = str.length;
|
return /^[a-z_$][a-z0-9_$]*$/i.test(str);
|
||||||
if (i == 0) return false;
|
|
||||||
if (!is_identifier_start(str.charCodeAt(0))) return false;
|
|
||||||
while (--i >= 0) {
|
|
||||||
if (!is_identifier_char(str.charAt(i)))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
@@ -616,6 +609,7 @@ function parse($TEXT, options) {
|
|||||||
toplevel : null,
|
toplevel : null,
|
||||||
expression : false,
|
expression : false,
|
||||||
html5_comments : true,
|
html5_comments : true,
|
||||||
|
bare_returns : false,
|
||||||
});
|
});
|
||||||
|
|
||||||
var S = {
|
var S = {
|
||||||
@@ -795,7 +789,7 @@ function parse($TEXT, options) {
|
|||||||
return if_();
|
return if_();
|
||||||
|
|
||||||
case "return":
|
case "return":
|
||||||
if (S.in_function == 0)
|
if (S.in_function == 0 && !options.bare_returns)
|
||||||
croak("'return' outside of function");
|
croak("'return' outside of function");
|
||||||
return new AST_Return({
|
return new AST_Return({
|
||||||
value: ( is("punc", ";")
|
value: ( is("punc", ";")
|
||||||
@@ -1381,7 +1375,7 @@ function parse($TEXT, options) {
|
|||||||
condition : expr,
|
condition : expr,
|
||||||
consequent : yes,
|
consequent : yes,
|
||||||
alternative : expression(false, no_in),
|
alternative : expression(false, no_in),
|
||||||
end : peek()
|
end : prev()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return expr;
|
return expr;
|
||||||
|
|||||||
54
lib/scope.js
54
lib/scope.js
@@ -71,27 +71,34 @@ SymbolDef.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||||
// This does what ast_add_scope did in UglifyJS v1.
|
options = defaults(options, {
|
||||||
//
|
screw_ie8: false
|
||||||
// Part of it could be done at parse time, but it would complicate
|
});
|
||||||
// the parser (and it's already kinda complex). It's also worth
|
|
||||||
// having it separated because we might need to call it multiple
|
|
||||||
// times on the same tree.
|
|
||||||
|
|
||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var scope = self.parent_scope = null;
|
var scope = self.parent_scope = null;
|
||||||
|
var defun = null;
|
||||||
var nesting = 0;
|
var nesting = 0;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
|
if (options.screw_ie8 && node instanceof AST_Catch) {
|
||||||
|
var save_scope = scope;
|
||||||
|
scope = new AST_Scope(node);
|
||||||
|
scope.init_scope_vars(nesting);
|
||||||
|
scope.parent_scope = save_scope;
|
||||||
|
descend();
|
||||||
|
scope = save_scope;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_Scope) {
|
if (node instanceof AST_Scope) {
|
||||||
node.init_scope_vars(nesting);
|
node.init_scope_vars(nesting);
|
||||||
var save_scope = node.parent_scope = scope;
|
var save_scope = node.parent_scope = scope;
|
||||||
++nesting;
|
var save_defun = defun;
|
||||||
scope = node;
|
defun = scope = node;
|
||||||
descend();
|
++nesting; descend(); --nesting;
|
||||||
scope = save_scope;
|
scope = save_scope;
|
||||||
--nesting;
|
defun = save_defun;
|
||||||
return true; // don't descend again in TreeWalker
|
return true; // don't descend again in TreeWalker
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Directive) {
|
if (node instanceof AST_Directive) {
|
||||||
@@ -108,7 +115,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolLambda) {
|
if (node instanceof AST_SymbolLambda) {
|
||||||
scope.def_function(node);
|
defun.def_function(node);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolDefun) {
|
else if (node instanceof AST_SymbolDefun) {
|
||||||
// Careful here, the scope where this should be defined is
|
// Careful here, the scope where this should be defined is
|
||||||
@@ -116,22 +123,17 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// scope when we encounter the AST_Defun node (which is
|
// scope when we encounter the AST_Defun node (which is
|
||||||
// instanceof AST_Scope) but we get to the symbol a bit
|
// instanceof AST_Scope) but we get to the symbol a bit
|
||||||
// later.
|
// later.
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = defun.parent_scope).def_function(node);
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolVar
|
else if (node instanceof AST_SymbolVar
|
||||||
|| node instanceof AST_SymbolConst) {
|
|| node instanceof AST_SymbolConst) {
|
||||||
var def = scope.def_variable(node);
|
var def = defun.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst;
|
||||||
def.init = tw.parent().value;
|
def.init = tw.parent().value;
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
(options.screw_ie8 ? scope : defun)
|
||||||
// `catch` argument name should be visible only inside the
|
.def_variable(node);
|
||||||
// catch block. For a quick fix AST_Catch should inherit
|
|
||||||
// from AST_Scope. Keeping it this way because of IE,
|
|
||||||
// which doesn't obey the standard. (it introduces the
|
|
||||||
// identifier in the enclosing scope)
|
|
||||||
scope.def_variable(node);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
@@ -244,6 +246,11 @@ AST_Scope.DEFMETHOD("next_mangled", function(options){
|
|||||||
out: while (true) {
|
out: while (true) {
|
||||||
var m = base54(++this.cname);
|
var m = base54(++this.cname);
|
||||||
if (!is_identifier(m)) continue; // skip over "do"
|
if (!is_identifier(m)) continue; // skip over "do"
|
||||||
|
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/242 -- do not
|
||||||
|
// shadow a name excepted from mangling.
|
||||||
|
if (options.except.indexOf(m) >= 0) continue;
|
||||||
|
|
||||||
// we must ensure that the mangled name does not shadow a name
|
// we must ensure that the mangled name does not shadow a name
|
||||||
// from some parent scope that is referenced in this or in
|
// from some parent scope that is referenced in this or in
|
||||||
// inner scopes.
|
// inner scopes.
|
||||||
@@ -358,6 +365,10 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (options.screw_ie8 && node instanceof AST_SymbolCatch) {
|
||||||
|
to_mangle.push(node.definition());
|
||||||
|
return;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
to_mangle.forEach(function(def){ def.mangle(options) });
|
to_mangle.forEach(function(def){ def.mangle(options) });
|
||||||
@@ -521,6 +532,7 @@ AST_Toplevel.DEFMETHOD("scope_warnings", function(options){
|
|||||||
}
|
}
|
||||||
if (options.unreferenced
|
if (options.unreferenced
|
||||||
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
|
&& (node instanceof AST_SymbolDeclaration || node instanceof AST_Label)
|
||||||
|
&& !(node instanceof AST_SymbolCatch)
|
||||||
&& node.unreferenced()) {
|
&& node.unreferenced()) {
|
||||||
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
|
AST_Node.warn("{type} {name} is declared but not referenced [{file}:{line},{col}]", {
|
||||||
type: node instanceof AST_Label ? "Label" : "Symbol",
|
type: node instanceof AST_Label ? "Label" : "Symbol",
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ function SourceMap(options) {
|
|||||||
file : null,
|
file : null,
|
||||||
root : null,
|
root : null,
|
||||||
orig : null,
|
orig : null,
|
||||||
|
|
||||||
|
orig_line_diff : 0,
|
||||||
|
dest_line_diff : 0,
|
||||||
});
|
});
|
||||||
var generator = new MOZ_SourceMap.SourceMapGenerator({
|
var generator = new MOZ_SourceMap.SourceMapGenerator({
|
||||||
file : options.file,
|
file : options.file,
|
||||||
@@ -61,14 +64,17 @@ function SourceMap(options) {
|
|||||||
line: orig_line,
|
line: orig_line,
|
||||||
column: orig_col
|
column: orig_col
|
||||||
});
|
});
|
||||||
|
if (info.source === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
source = info.source;
|
source = info.source;
|
||||||
orig_line = info.line;
|
orig_line = info.line;
|
||||||
orig_col = info.column;
|
orig_col = info.column;
|
||||||
name = info.name;
|
name = info.name || name;
|
||||||
}
|
}
|
||||||
generator.addMapping({
|
generator.addMapping({
|
||||||
generated : { line: gen_line, column: gen_col },
|
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
|
||||||
original : { line: orig_line, column: orig_col },
|
original : { line: orig_line + options.orig_line_diff, column: orig_col },
|
||||||
source : source,
|
source : source,
|
||||||
name : name
|
name : name
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,16 +82,23 @@ function repeat_string(str, i) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function DefaultsError(msg, defs) {
|
function DefaultsError(msg, defs) {
|
||||||
|
Error.call(this, msg);
|
||||||
this.msg = msg;
|
this.msg = msg;
|
||||||
this.defs = defs;
|
this.defs = defs;
|
||||||
};
|
};
|
||||||
|
DefaultsError.prototype = Object.create(Error.prototype);
|
||||||
|
DefaultsError.prototype.constructor = DefaultsError;
|
||||||
|
|
||||||
|
DefaultsError.croak = function(msg, defs) {
|
||||||
|
throw new DefaultsError(msg, defs);
|
||||||
|
};
|
||||||
|
|
||||||
function defaults(args, defs, croak) {
|
function defaults(args, defs, croak) {
|
||||||
if (args === true)
|
if (args === true)
|
||||||
args = {};
|
args = {};
|
||||||
var ret = args || {};
|
var ret = args || {};
|
||||||
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
|
if (croak) for (var i in ret) if (ret.hasOwnProperty(i) && !defs.hasOwnProperty(i))
|
||||||
throw new DefaultsError("`" + i + "` is not a supported option", defs);
|
DefaultsError.croak("`" + i + "` is not a supported option", defs);
|
||||||
for (var i in defs) if (defs.hasOwnProperty(i)) {
|
for (var i in defs) if (defs.hasOwnProperty(i)) {
|
||||||
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
|
ret[i] = (args && args.hasOwnProperty(i)) ? args[i] : defs[i];
|
||||||
}
|
}
|
||||||
|
|||||||
13
package.json
13
package.json
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"main": "tools/node.js",
|
||||||
"version": "2.4.4",
|
"version": "2.4.16",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
@@ -16,15 +16,22 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async" : "~0.2.6",
|
"async" : "~0.2.6",
|
||||||
"source-map" : "~0.1.7",
|
"source-map" : "0.1.34",
|
||||||
"optimist" : "~0.3.5",
|
"optimist": "~0.3.5",
|
||||||
"uglify-to-browserify": "~1.0.0"
|
"uglify-to-browserify": "~1.0.0"
|
||||||
},
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"acorn": "~0.6.0",
|
||||||
|
"escodegen": "~1.3.3",
|
||||||
|
"esfuzz": "~0.3.1",
|
||||||
|
"estraverse": "~1.5.1"
|
||||||
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
"transform": [ "uglify-to-browserify" ]
|
"transform": [ "uglify-to-browserify" ]
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs" : "bin/uglifyjs"
|
"uglifyjs" : "bin/uglifyjs"
|
||||||
},
|
},
|
||||||
|
"license": "BSD",
|
||||||
"scripts": {"test": "node test/run-tests.js"}
|
"scripts": {"test": "node test/run-tests.js"}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,3 +141,174 @@ ifs_6: {
|
|||||||
x = foo || bar || baz || boo ? 20 : 10;
|
x = foo || bar || baz || boo ? 20 : 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cond_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
do_something(x);
|
||||||
|
} else {
|
||||||
|
do_something(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
do_something(some_condition() ? x : y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_2: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
x = new FooBar(1);
|
||||||
|
} else {
|
||||||
|
x = new FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x = new FooBar(some_condition() ? 1 : 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_3: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
new FooBar(1);
|
||||||
|
} else {
|
||||||
|
FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
some_condition() ? new FooBar(1) : FooBar(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_4: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
do_something();
|
||||||
|
} else {
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
some_condition(), do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_5: {
|
||||||
|
options = {
|
||||||
|
conditionals: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
if (some_condition()) {
|
||||||
|
if (some_other_condition()) {
|
||||||
|
do_something();
|
||||||
|
} else {
|
||||||
|
alternate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alternate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (some_condition()) {
|
||||||
|
if (some_other_condition()) {
|
||||||
|
do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
some_condition() && some_other_condition() ? do_something() : alternate();
|
||||||
|
some_condition() && some_other_condition() && do_something();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_7: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
var x, y, z, a, b;
|
||||||
|
// compress these
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else if (z) {
|
||||||
|
x = 2;
|
||||||
|
} else {
|
||||||
|
x = 3-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = y ? 'foo' : 'fo'+'o';
|
||||||
|
|
||||||
|
x = y ? 'foo' : y ? 'foo' : 'fo'+'o';
|
||||||
|
|
||||||
|
// Compress conditions that have side effects
|
||||||
|
if (condition()) {
|
||||||
|
x = 10+10;
|
||||||
|
} else {
|
||||||
|
x = 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z) {
|
||||||
|
x = 'fuji';
|
||||||
|
} else if (condition()) {
|
||||||
|
x = 'fu'+'ji';
|
||||||
|
} else {
|
||||||
|
x = 'fuji';
|
||||||
|
}
|
||||||
|
|
||||||
|
x = condition() ? 'foobar' : 'foo'+'bar';
|
||||||
|
|
||||||
|
// don't compress these
|
||||||
|
x = y ? a : b;
|
||||||
|
|
||||||
|
x = y ? 'foo' : 'fo';
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var x, y, z, a, b;
|
||||||
|
x = 2;
|
||||||
|
x = 2;
|
||||||
|
x = 'foo';
|
||||||
|
x = 'foo';
|
||||||
|
x = (condition(), 20);
|
||||||
|
x = z ? 'fuji' : (condition(), 'fuji');
|
||||||
|
x = (condition(), 'foobar');
|
||||||
|
x = y ? a : b;
|
||||||
|
x = y ? 'foo' : 'fo';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cond_7_1: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
evaluate : true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
// access to global should be assumed to have side effects
|
||||||
|
if (y) {
|
||||||
|
x = 1+1;
|
||||||
|
} else {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
x = (y, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -119,3 +119,47 @@ unused_keep_setter_arg: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unused_var_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
used_var_in_catch: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function foo() {
|
||||||
|
try {
|
||||||
|
foo();
|
||||||
|
} catch(ex) {
|
||||||
|
var x = 10;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,16 +60,16 @@ negate_iife_4: {
|
|||||||
};
|
};
|
||||||
input: {
|
input: {
|
||||||
if ((function(){ return true })()) {
|
if ((function(){ return true })()) {
|
||||||
console.log(true);
|
foo(true);
|
||||||
} else {
|
} else {
|
||||||
console.log(false);
|
bar(false);
|
||||||
}
|
}
|
||||||
(function(){
|
(function(){
|
||||||
console.log("something");
|
console.log("something");
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
!function(){ return true }() ? console.log(false) : console.log(true), function(){
|
!function(){ return true }() ? bar(false) : foo(true), function(){
|
||||||
console.log("something");
|
console.log("something");
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ dot_properties: {
|
|||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
a["*"] = "asterisk";
|
a["*"] = "asterisk";
|
||||||
a.\u0EB3 = "unicode";
|
a["\u0EB3"] = "unicode";
|
||||||
a[""] = "whitespace";
|
a[""] = "whitespace";
|
||||||
a["1_1"] = "foo";
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,27 @@ dot_properties_es5: {
|
|||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a.if = "if";
|
a.if = "if";
|
||||||
a["*"] = "asterisk";
|
a["*"] = "asterisk";
|
||||||
a.\u0EB3 = "unicode";
|
a["\u0EB3"] = "unicode";
|
||||||
a[""] = "whitespace";
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evaluate_length: {
|
||||||
|
options = {
|
||||||
|
properties: true,
|
||||||
|
unsafe: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
a = "foo".length;
|
||||||
|
a = ("foo" + "bar")["len" + "gth"];
|
||||||
|
a = b.length;
|
||||||
|
a = ("foo" + b).length;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
a = 3;
|
||||||
|
a = 6;
|
||||||
|
a = b.length;
|
||||||
|
a = ("foo" + b).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -91,9 +91,11 @@ make_sequences_4: {
|
|||||||
lift_sequences_1: {
|
lift_sequences_1: {
|
||||||
options = { sequences: true };
|
options = { sequences: true };
|
||||||
input: {
|
input: {
|
||||||
|
var foo, x, y, bar;
|
||||||
foo = !(x(), y(), bar());
|
foo = !(x(), y(), bar());
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var foo, x, y, bar;
|
||||||
x(), y(), foo = !bar();
|
x(), y(), foo = !bar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,10 +103,12 @@ lift_sequences_1: {
|
|||||||
lift_sequences_2: {
|
lift_sequences_2: {
|
||||||
options = { sequences: true, evaluate: true };
|
options = { sequences: true, evaluate: true };
|
||||||
input: {
|
input: {
|
||||||
|
var foo, bar;
|
||||||
foo.x = (foo = {}, 10);
|
foo.x = (foo = {}, 10);
|
||||||
bar = (bar = {}, 10);
|
bar = (bar = {}, 10);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var foo, bar;
|
||||||
foo.x = (foo = {}, 10),
|
foo.x = (foo = {}, 10),
|
||||||
bar = {}, bar = 10;
|
bar = {}, bar = 10;
|
||||||
}
|
}
|
||||||
@@ -113,9 +117,11 @@ lift_sequences_2: {
|
|||||||
lift_sequences_3: {
|
lift_sequences_3: {
|
||||||
options = { sequences: true, conditionals: true };
|
options = { sequences: true, conditionals: true };
|
||||||
input: {
|
input: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
x = (foo(), bar(), baz()) ? 10 : 20;
|
x = (foo(), bar(), baz()) ? 10 : 20;
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
foo(), bar(), x = baz() ? 10 : 20;
|
foo(), bar(), x = baz() ? 10 : 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,9 +129,11 @@ lift_sequences_3: {
|
|||||||
lift_sequences_4: {
|
lift_sequences_4: {
|
||||||
options = { side_effects: true };
|
options = { side_effects: true };
|
||||||
input: {
|
input: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
x = (foo, bar, baz);
|
x = (foo, bar, baz);
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
|
var x, foo, bar, baz;
|
||||||
x = baz;
|
x = baz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
103
test/mozilla-ast.js
Normal file
103
test/mozilla-ast.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
// Testing UglifyJS <-> SpiderMonkey AST conversion
|
||||||
|
// through generative testing.
|
||||||
|
|
||||||
|
var UglifyJS = require(".."),
|
||||||
|
escodegen = require("escodegen"),
|
||||||
|
esfuzz = require("esfuzz"),
|
||||||
|
estraverse = require("estraverse"),
|
||||||
|
prefix = Array(20).join("\b") + " ";
|
||||||
|
|
||||||
|
// Normalizes input AST for UglifyJS in order to get correct comparison.
|
||||||
|
|
||||||
|
function normalizeInput(ast) {
|
||||||
|
return estraverse.replace(ast, {
|
||||||
|
enter: function(node, parent) {
|
||||||
|
switch (node.type) {
|
||||||
|
// Internally mark all the properties with semi-standard type "Property".
|
||||||
|
case "ObjectExpression":
|
||||||
|
node.properties.forEach(function (property) {
|
||||||
|
property.type = "Property";
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Since UglifyJS doesn"t recognize different types of property keys,
|
||||||
|
// decision on SpiderMonkey node type is based on check whether key
|
||||||
|
// can be valid identifier or not - so we do in input AST.
|
||||||
|
case "Property":
|
||||||
|
var key = node.key;
|
||||||
|
if (key.type === "Literal" && typeof key.value === "string" && UglifyJS.is_identifier(key.value)) {
|
||||||
|
node.key = {
|
||||||
|
type: "Identifier",
|
||||||
|
name: key.value
|
||||||
|
};
|
||||||
|
} else if (key.type === "Identifier" && !UglifyJS.is_identifier(key.name)) {
|
||||||
|
node.key = {
|
||||||
|
type: "Literal",
|
||||||
|
value: key.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// UglifyJS internally flattens all the expression sequences - either
|
||||||
|
// to one element (if sequence contains only one element) or flat list.
|
||||||
|
case "SequenceExpression":
|
||||||
|
node.expressions = node.expressions.reduce(function flatten(list, expr) {
|
||||||
|
return list.concat(expr.type === "SequenceExpression" ? expr.expressions.reduce(flatten, []) : [expr]);
|
||||||
|
}, []);
|
||||||
|
if (node.expressions.length === 1) {
|
||||||
|
return node.expressions[0];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function(options) {
|
||||||
|
console.log("--- UglifyJS <-> Mozilla AST conversion");
|
||||||
|
|
||||||
|
for (var counter = 0; counter < options.iterations; counter++) {
|
||||||
|
process.stdout.write(prefix + counter + "/" + options.iterations);
|
||||||
|
|
||||||
|
var ast1 = normalizeInput(esfuzz.generate({
|
||||||
|
maxDepth: options.maxDepth
|
||||||
|
}));
|
||||||
|
|
||||||
|
var ast2 =
|
||||||
|
UglifyJS
|
||||||
|
.AST_Node
|
||||||
|
.from_mozilla_ast(ast1)
|
||||||
|
.to_mozilla_ast();
|
||||||
|
|
||||||
|
var astPair = [
|
||||||
|
{name: 'expected', value: ast1},
|
||||||
|
{name: 'actual', value: ast2}
|
||||||
|
];
|
||||||
|
|
||||||
|
var jsPair = astPair.map(function(item) {
|
||||||
|
return {
|
||||||
|
name: item.name,
|
||||||
|
value: escodegen.generate(item.value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (jsPair[0].value !== jsPair[1].value) {
|
||||||
|
var fs = require("fs");
|
||||||
|
var acorn = require("acorn");
|
||||||
|
|
||||||
|
fs.existsSync("tmp") || fs.mkdirSync("tmp");
|
||||||
|
|
||||||
|
jsPair.forEach(function (item) {
|
||||||
|
var fileName = "tmp/dump_" + item.name;
|
||||||
|
var ast = acorn.parse(item.value);
|
||||||
|
fs.writeFileSync(fileName + ".js", item.value);
|
||||||
|
fs.writeFileSync(fileName + ".json", JSON.stringify(ast, null, 2));
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdout.write("\n");
|
||||||
|
throw new Error("Got different outputs, check out tmp/dump_*.{js,json} for codes and ASTs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdout.write(prefix + "Probability of error is less than " + (100 / options.iterations) + "%, stopping.\n");
|
||||||
|
};
|
||||||
@@ -17,6 +17,12 @@ if (failures) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var run_ast_conversion_tests = require("./mozilla-ast");
|
||||||
|
|
||||||
|
run_ast_conversion_tests({
|
||||||
|
iterations: 1000
|
||||||
|
});
|
||||||
|
|
||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function tmpl() {
|
function tmpl() {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ for (var i in UglifyJS) {
|
|||||||
|
|
||||||
exports.minify = function(files, options) {
|
exports.minify = function(files, options) {
|
||||||
options = UglifyJS.defaults(options, {
|
options = UglifyJS.defaults(options, {
|
||||||
|
spidermonkey : false,
|
||||||
outSourceMap : null,
|
outSourceMap : null,
|
||||||
sourceRoot : null,
|
sourceRoot : null,
|
||||||
inSourceMap : null,
|
inSourceMap : null,
|
||||||
@@ -60,22 +61,28 @@ exports.minify = function(files, options) {
|
|||||||
output : null,
|
output : null,
|
||||||
compress : {}
|
compress : {}
|
||||||
});
|
});
|
||||||
if (typeof files == "string")
|
|
||||||
files = [ files ];
|
|
||||||
|
|
||||||
UglifyJS.base54.reset();
|
UglifyJS.base54.reset();
|
||||||
|
|
||||||
// 1. parse
|
// 1. parse
|
||||||
var toplevel = null;
|
var toplevel = null,
|
||||||
|
sourcesContent = {};
|
||||||
|
|
||||||
|
if (options.spidermonkey) {
|
||||||
|
toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
|
||||||
|
} else {
|
||||||
|
if (typeof files == "string")
|
||||||
|
files = [ files ];
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = options.fromString
|
var code = options.fromString
|
||||||
? file
|
? file
|
||||||
: fs.readFileSync(file, "utf8");
|
: fs.readFileSync(file, "utf8");
|
||||||
|
sourcesContent[file] = code;
|
||||||
toplevel = UglifyJS.parse(code, {
|
toplevel = UglifyJS.parse(code, {
|
||||||
filename: options.fromString ? "?" : file,
|
filename: options.fromString ? "?" : file,
|
||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 2. compress
|
// 2. compress
|
||||||
if (options.compress) {
|
if (options.compress) {
|
||||||
@@ -105,12 +112,25 @@ exports.minify = function(files, options) {
|
|||||||
orig: inMap,
|
orig: inMap,
|
||||||
root: options.sourceRoot
|
root: options.sourceRoot
|
||||||
});
|
});
|
||||||
|
if (options.sourceMapIncludeSources) {
|
||||||
|
for (var file in sourcesContent) {
|
||||||
|
if (sourcesContent.hasOwnProperty(file)) {
|
||||||
|
output.source_map.get().setSourceContent(file, sourcesContent[file]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (options.output) {
|
if (options.output) {
|
||||||
UglifyJS.merge(output, options.output);
|
UglifyJS.merge(output, options.output);
|
||||||
}
|
}
|
||||||
var stream = UglifyJS.OutputStream(output);
|
var stream = UglifyJS.OutputStream(output);
|
||||||
toplevel.print(stream);
|
toplevel.print(stream);
|
||||||
|
|
||||||
|
if(options.outSourceMap){
|
||||||
|
stream += "\n//# sourceMappingURL=" + options.outSourceMap;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code : stream + "",
|
code : stream + "",
|
||||||
map : output.source_map + ""
|
map : output.source_map + ""
|
||||||
|
|||||||
Reference in New Issue
Block a user