Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6064bea3db | ||
|
|
98978fc827 | ||
|
|
16430acc1f | ||
|
|
320c110b33 | ||
|
|
dbe33bbfc5 | ||
|
|
b5c3253b49 | ||
|
|
5cc90db7d0 | ||
|
|
f427e5efc7 | ||
|
|
e48802ad29 | ||
|
|
13c4dfcabd | ||
|
|
1abde9c8b0 | ||
|
|
4f555e2232 | ||
|
|
642ba2e92c | ||
|
|
089ac908b7 | ||
|
|
0d3fd2ef30 | ||
|
|
e98119496a | ||
|
|
bdfcbf496b | ||
|
|
dba8da4800 | ||
|
|
60c0f40250 | ||
|
|
e02771a5f2 | ||
|
|
f96f796f71 |
24
README.md
24
README.md
@@ -14,7 +14,7 @@ Install
|
|||||||
|
|
||||||
From NPM:
|
From NPM:
|
||||||
|
|
||||||
npm install uglify-js2
|
npm install uglify-js
|
||||||
|
|
||||||
From Git:
|
From Git:
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ From Git:
|
|||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
uglifyjs2 [input files] [options]
|
uglifyjs [input files] [options]
|
||||||
|
|
||||||
UglifyJS2 can take multiple input files. It's recommended that you pass the
|
UglifyJS2 can take multiple input files. It's recommended that you pass the
|
||||||
input files first, then pass the options. UglifyJS will parse input files
|
input files first, then pass the options. UglifyJS will parse input files
|
||||||
@@ -98,12 +98,12 @@ map.
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
uglifyjs2 /home/doe/work/foo/src/js/file1.js \
|
uglifyjs /home/doe/work/foo/src/js/file1.js \
|
||||||
/home/doe/work/foo/src/js/file2.js \
|
/home/doe/work/foo/src/js/file2.js \
|
||||||
-o foo.min.js \
|
-o foo.min.js \
|
||||||
--source-map foo.min.js.map \
|
--source-map foo.min.js.map \
|
||||||
--source-map-root http://foo.com/src \
|
--source-map-root http://foo.com/src \
|
||||||
-p 5 -c -m
|
-p 5 -c -m
|
||||||
|
|
||||||
The above will compress and mangle `file1.js` and `file2.js`, will drop the
|
The above will compress and mangle `file1.js` and `file2.js`, will drop the
|
||||||
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
|
output in `foo.min.js` and the source map in `foo.min.js.map`. The source
|
||||||
@@ -140,7 +140,7 @@ When mangling is enabled but you want to prevent certain names from being
|
|||||||
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
mangled, you can declare those names with `--reserved` (`-r`) — pass a
|
||||||
comma-separated list of names. For example:
|
comma-separated list of names. For example:
|
||||||
|
|
||||||
uglifyjs2 ... -m -r '$,require,exports'
|
uglifyjs ... -m -r '$,require,exports'
|
||||||
|
|
||||||
to prevent the `require`, `exports` and `$` names from being changed.
|
to prevent the `require`, `exports` and `$` names from being changed.
|
||||||
|
|
||||||
@@ -206,7 +206,7 @@ separate file and include it into the build. For example you can have a
|
|||||||
|
|
||||||
and build your code like this:
|
and build your code like this:
|
||||||
|
|
||||||
uglifyjs2 build/defines.js js/foo.js js/bar.js... -c
|
uglifyjs build/defines.js js/foo.js js/bar.js... -c
|
||||||
|
|
||||||
UglifyJS will notice the constants and, since they cannot be altered, it
|
UglifyJS will notice the constants and, since they cannot be altered, it
|
||||||
will evaluate references to them to the value itself and drop unreachable
|
will evaluate references to them to the value itself and drop unreachable
|
||||||
@@ -288,7 +288,7 @@ SpiderMonkey AST. It has a small CLI utility that parses one file and dumps
|
|||||||
the AST in JSON on the standard output. To use UglifyJS to mangle and
|
the AST in JSON on the standard output. To use UglifyJS to mangle and
|
||||||
compress that:
|
compress that:
|
||||||
|
|
||||||
acorn file.js | uglifyjs2 --spidermonkey -m -c
|
acorn file.js | uglifyjs --spidermonkey -m -c
|
||||||
|
|
||||||
The `--spidermonkey` option tells UglifyJS that all input files are not
|
The `--spidermonkey` option tells UglifyJS that all input files are not
|
||||||
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
|
JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we
|
||||||
@@ -310,7 +310,7 @@ API Reference
|
|||||||
Assuming installation via NPM, you can load UglifyJS in your application
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
like this:
|
like this:
|
||||||
|
|
||||||
var UglifyJS = require("uglify-js2");
|
var UglifyJS = require("uglify-js");
|
||||||
|
|
||||||
It exports a lot of names, but I'll discuss here the basics that are needed
|
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||||
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||||
|
|||||||
@@ -287,9 +287,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
|||||||
},
|
},
|
||||||
wrap_commonjs: function(name, export_all) {
|
wrap_commonjs: function(name, export_all) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var to_export = [];
|
||||||
if (export_all) {
|
if (export_all) {
|
||||||
self.figure_out_scope();
|
self.figure_out_scope();
|
||||||
var to_export = [];
|
|
||||||
self.walk(new TreeWalker(function(node){
|
self.walk(new TreeWalker(function(node){
|
||||||
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
if (node instanceof AST_SymbolDeclaration && node.definition().global) {
|
||||||
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
if (!find_if(function(n){ return n.name == node.name }, to_export))
|
||||||
|
|||||||
229
lib/compress.js
229
lib/compress.js
@@ -554,12 +554,24 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_UnaryPrefix, function(){
|
def(AST_UnaryPrefix, function(){
|
||||||
return this.operator == "typeof";
|
return this.operator == "typeof";
|
||||||
});
|
});
|
||||||
def(AST_Binary, function(){
|
def(AST_Binary, function(compressor){
|
||||||
return this.operator == "+" &&
|
return this.operator == "+" &&
|
||||||
(this.left.is_string() || this.right.is_string());
|
(this.left.is_string(compressor) || this.right.is_string(compressor));
|
||||||
});
|
});
|
||||||
def(AST_Assign, function(){
|
def(AST_Assign, function(compressor){
|
||||||
return this.operator == "=" && this.right.is_string();
|
return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
|
||||||
|
});
|
||||||
|
def(AST_Seq, function(compressor){
|
||||||
|
return this.cdr.is_string(compressor);
|
||||||
|
});
|
||||||
|
def(AST_Conditional, function(compressor){
|
||||||
|
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
|
||||||
|
});
|
||||||
|
def(AST_Call, function(compressor){
|
||||||
|
return compressor.option("unsafe")
|
||||||
|
&& this.expression instanceof AST_SymbolRef
|
||||||
|
&& this.expression.name == "String"
|
||||||
|
&& this.expression.undeclared();
|
||||||
});
|
});
|
||||||
})(function(node, func){
|
})(function(node, func){
|
||||||
node.DEFMETHOD("is_string", func);
|
node.DEFMETHOD("is_string", func);
|
||||||
@@ -814,10 +826,12 @@ merge(Compressor.prototype, {
|
|||||||
(function(def){
|
(function(def){
|
||||||
def(AST_Statement, function(){ return null });
|
def(AST_Statement, function(){ return null });
|
||||||
def(AST_Jump, function(){ return this });
|
def(AST_Jump, function(){ return this });
|
||||||
def(AST_BlockStatement, function(){
|
function block_aborts(){
|
||||||
var n = this.body.length;
|
var n = this.body.length;
|
||||||
return n > 0 && aborts(this.body[n - 1]);
|
return n > 0 && aborts(this.body[n - 1]);
|
||||||
});
|
};
|
||||||
|
def(AST_BlockStatement, block_aborts);
|
||||||
|
def(AST_SwitchBranch, block_aborts);
|
||||||
def(AST_If, function(){
|
def(AST_If, function(){
|
||||||
return this.alternative && aborts(this.body) && aborts(this.alternative);
|
return this.alternative && aborts(this.body) && aborts(this.alternative);
|
||||||
});
|
});
|
||||||
@@ -869,18 +883,23 @@ merge(Compressor.prototype, {
|
|||||||
&& !self.uses_eval
|
&& !self.uses_eval
|
||||||
) {
|
) {
|
||||||
var in_use = [];
|
var in_use = [];
|
||||||
|
var initializations = new Dictionary();
|
||||||
// pass 1: find out which symbols are directly used in
|
// pass 1: find out which symbols are directly used in
|
||||||
// this scope (not in nested scopes).
|
// this scope (not in nested scopes).
|
||||||
var scope = this;
|
var scope = this;
|
||||||
var tw = new TreeWalker(function(node, descend){
|
var tw = new TreeWalker(function(node, descend){
|
||||||
if (node !== self) {
|
if (node !== self) {
|
||||||
if (node instanceof AST_Defun) {
|
if (node instanceof AST_Defun) {
|
||||||
|
initializations.add(node.name.name, node);
|
||||||
return true; // don't go in nested scopes
|
return true; // don't go in nested scopes
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Definitions && scope === self) {
|
if (node instanceof AST_Definitions && scope === self) {
|
||||||
node.definitions.forEach(function(def){
|
node.definitions.forEach(function(def){
|
||||||
if (def.value && def.value.has_side_effects()) {
|
if (def.value) {
|
||||||
def.value.walk(tw);
|
initializations.add(def.name.name, def.value);
|
||||||
|
if (def.value.has_side_effects()) {
|
||||||
|
def.value.walk(tw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
@@ -905,16 +924,15 @@ merge(Compressor.prototype, {
|
|||||||
for (var i = 0; i < in_use.length; ++i) {
|
for (var i = 0; i < in_use.length; ++i) {
|
||||||
in_use[i].orig.forEach(function(decl){
|
in_use[i].orig.forEach(function(decl){
|
||||||
// undeclared globals will be instanceof AST_SymbolRef
|
// undeclared globals will be instanceof AST_SymbolRef
|
||||||
if (decl instanceof AST_SymbolDeclaration) {
|
var init = initializations.get(decl.name);
|
||||||
decl.init.forEach(function(init){
|
if (init) init.forEach(function(init){
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
push_uniq(in_use, node.definition());
|
push_uniq(in_use, node.definition());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
init.walk(tw);
|
|
||||||
});
|
});
|
||||||
}
|
init.walk(tw);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// pass 3: we should drop declarations not in_use
|
// pass 3: we should drop declarations not in_use
|
||||||
@@ -1086,13 +1104,71 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
self = self.transform(tt);
|
self = self.transform(tt);
|
||||||
if (vars_found > 0) hoisted.unshift(make_node(AST_Var, self, {
|
if (vars_found > 0) {
|
||||||
definitions: vars.map(function(def){
|
// collect only vars which don't show up in self's arguments list
|
||||||
def = def.clone();
|
var defs = [];
|
||||||
def.value = null;
|
vars.each(function(def, name){
|
||||||
return def;
|
if (self instanceof AST_Lambda
|
||||||
})
|
&& find_if(function(x){ return x.name == def.name.name },
|
||||||
}));
|
self.argnames)) {
|
||||||
|
vars.del(name);
|
||||||
|
} else {
|
||||||
|
def = def.clone();
|
||||||
|
def.value = null;
|
||||||
|
defs.push(def);
|
||||||
|
vars.set(name, def);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (defs.length > 0) {
|
||||||
|
// try to merge in assignments
|
||||||
|
for (var i = 0; i < self.body.length;) {
|
||||||
|
if (self.body[i] instanceof AST_SimpleStatement) {
|
||||||
|
var expr = self.body[i].body, sym, assign;
|
||||||
|
if (expr instanceof AST_Assign
|
||||||
|
&& expr.operator == "="
|
||||||
|
&& (sym = expr.left) instanceof AST_Symbol
|
||||||
|
&& vars.has(sym.name))
|
||||||
|
{
|
||||||
|
var def = vars.get(sym.name);
|
||||||
|
if (def.value) break;
|
||||||
|
def.value = expr.right;
|
||||||
|
remove(defs, def);
|
||||||
|
defs.push(def);
|
||||||
|
self.body.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (expr instanceof AST_Seq
|
||||||
|
&& (assign = expr.car) instanceof AST_Assign
|
||||||
|
&& assign.operator == "="
|
||||||
|
&& (sym = assign.left) instanceof AST_Symbol
|
||||||
|
&& vars.has(sym.name))
|
||||||
|
{
|
||||||
|
var def = vars.get(sym.name);
|
||||||
|
if (def.value) break;
|
||||||
|
def.value = assign.right;
|
||||||
|
remove(defs, def);
|
||||||
|
defs.push(def);
|
||||||
|
self.body[i].body = expr.cdr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self.body[i] instanceof AST_EmptyStatement) {
|
||||||
|
self.body.splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (self.body[i] instanceof AST_BlockStatement) {
|
||||||
|
var tmp = [ i, 1 ].concat(self.body[i].body);
|
||||||
|
self.body.splice.apply(self.body, tmp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
defs = make_node(AST_Var, self, {
|
||||||
|
definitions: defs
|
||||||
|
});
|
||||||
|
hoisted.push(defs);
|
||||||
|
};
|
||||||
|
}
|
||||||
self.body = dirs.concat(hoisted, self.body);
|
self.body = dirs.concat(hoisted, self.body);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
@@ -1348,6 +1424,79 @@ merge(Compressor.prototype, {
|
|||||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||||
last_branch.body.pop();
|
last_branch.body.pop();
|
||||||
}
|
}
|
||||||
|
var exp = self.expression.evaluate(compressor);
|
||||||
|
out: if (exp.length == 2) try {
|
||||||
|
// constant expression
|
||||||
|
self.expression = exp[0];
|
||||||
|
if (!compressor.option("dead_code")) break out;
|
||||||
|
var value = exp[1];
|
||||||
|
var in_if = false;
|
||||||
|
var in_block = false;
|
||||||
|
var started = false;
|
||||||
|
var stopped = false;
|
||||||
|
var ruined = false;
|
||||||
|
var tt = new TreeTransformer(function(node, descend, in_list){
|
||||||
|
if (node instanceof AST_Lambda || node instanceof AST_SimpleStatement) {
|
||||||
|
// no need to descend these node types
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Switch && node === self) {
|
||||||
|
node = node.clone();
|
||||||
|
descend(node, this);
|
||||||
|
return ruined ? node : make_node(AST_BlockStatement, node, {
|
||||||
|
body: node.body.reduce(function(a, branch){
|
||||||
|
return a.concat(branch.body);
|
||||||
|
}, [])
|
||||||
|
}).transform(compressor);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_If || node instanceof AST_Try) {
|
||||||
|
var save = in_if;
|
||||||
|
in_if = !in_block;
|
||||||
|
descend(node, this);
|
||||||
|
in_if = save;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_StatementWithBody || node instanceof AST_Switch) {
|
||||||
|
var save = in_block;
|
||||||
|
in_block = true;
|
||||||
|
descend(node, this);
|
||||||
|
in_block = save;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
|
||||||
|
if (in_if) {
|
||||||
|
ruined = true;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (in_block) return node;
|
||||||
|
stopped = true;
|
||||||
|
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
|
||||||
|
}
|
||||||
|
else if (node instanceof AST_SwitchBranch && this.parent() === self) {
|
||||||
|
if (stopped) return MAP.skip;
|
||||||
|
if (node instanceof AST_Case) {
|
||||||
|
var exp = node.expression.evaluate(compressor);
|
||||||
|
if (exp.length < 2) {
|
||||||
|
// got a case with non-constant expression, baling out
|
||||||
|
throw self;
|
||||||
|
}
|
||||||
|
if (exp[1] === value || started) {
|
||||||
|
started = true;
|
||||||
|
if (aborts(node)) stopped = true;
|
||||||
|
descend(node, this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
return MAP.skip;
|
||||||
|
}
|
||||||
|
descend(node, this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tt.stack = compressor.stack.slice(); // so that's able to see parent nodes
|
||||||
|
self = self.transform(tt);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== self) throw ex;
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1462,8 +1611,19 @@ merge(Compressor.prototype, {
|
|||||||
OPT(AST_Seq, function(self, compressor){
|
OPT(AST_Seq, function(self, compressor){
|
||||||
if (!compressor.option("side_effects"))
|
if (!compressor.option("side_effects"))
|
||||||
return self;
|
return self;
|
||||||
if (!self.car.has_side_effects())
|
if (!self.car.has_side_effects()) {
|
||||||
return self.cdr;
|
// we shouldn't compress (1,eval)(something) to
|
||||||
|
// eval(something) because that changes the meaning of
|
||||||
|
// eval (becomes lexical instead of global).
|
||||||
|
var p;
|
||||||
|
if (!(self.cdr instanceof AST_SymbolRef
|
||||||
|
&& self.cdr.name == "eval"
|
||||||
|
&& self.cdr.undeclared()
|
||||||
|
&& (p = compressor.parent()) instanceof AST_Call
|
||||||
|
&& p.expression === self)) {
|
||||||
|
return self.cdr;
|
||||||
|
}
|
||||||
|
}
|
||||||
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()
|
&& !self.car.left.has_side_effects()
|
||||||
@@ -1549,10 +1709,12 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
function reverse(op) {
|
function reverse(op) {
|
||||||
if (op) self.operator = op;
|
if (!(self.left.has_side_effects() && self.right.has_side_effects())) {
|
||||||
var tmp = self.left;
|
if (op) self.operator = op;
|
||||||
self.left = self.right;
|
var tmp = self.left;
|
||||||
self.right = tmp;
|
self.left = self.right;
|
||||||
|
self.right = tmp;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (commutativeOperators(self.operator)) {
|
if (commutativeOperators(self.operator)) {
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
@@ -1564,7 +1726,7 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("comparisons")) switch (self.operator) {
|
if (compressor.option("comparisons")) switch (self.operator) {
|
||||||
case "===":
|
case "===":
|
||||||
case "!==":
|
case "!==":
|
||||||
if ((self.left.is_string() && self.right.is_string()) ||
|
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
|
||||||
(self.left.is_boolean() && self.right.is_boolean())) {
|
(self.left.is_boolean() && self.right.is_boolean())) {
|
||||||
self.operator = self.operator.substr(0, 2);
|
self.operator = self.operator.substr(0, 2);
|
||||||
}
|
}
|
||||||
@@ -1642,6 +1804,11 @@ merge(Compressor.prototype, {
|
|||||||
case "<=": reverse(">="); break;
|
case "<=": reverse(">="); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (self.operator == "+" && self.right instanceof AST_String
|
||||||
|
&& self.right.getValue() === "" && self.left instanceof AST_Binary
|
||||||
|
&& self.left.operator == "+" && self.left.is_string(compressor)) {
|
||||||
|
return self.left;
|
||||||
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ function OutputStream(options) {
|
|||||||
source_map : null,
|
source_map : null,
|
||||||
bracketize : false,
|
bracketize : false,
|
||||||
semicolons : true,
|
semicolons : true,
|
||||||
comments : false
|
comments : false,
|
||||||
|
preserve_line : false
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = 0;
|
||||||
@@ -154,6 +155,18 @@ function OutputStream(options) {
|
|||||||
might_need_semicolon = false;
|
might_need_semicolon = false;
|
||||||
maybe_newline();
|
maybe_newline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
|
||||||
|
var target_line = stack[stack.length - 1].start.line;
|
||||||
|
while (current_line < target_line) {
|
||||||
|
OUTPUT += "\n";
|
||||||
|
current_pos++;
|
||||||
|
current_line++;
|
||||||
|
current_col = 0;
|
||||||
|
might_need_space = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (might_need_space) {
|
if (might_need_space) {
|
||||||
var prev = last_char();
|
var prev = last_char();
|
||||||
if ((is_identifier_char(prev)
|
if ((is_identifier_char(prev)
|
||||||
@@ -327,10 +340,10 @@ function OutputStream(options) {
|
|||||||
/* -----[ utils ]----- */
|
/* -----[ utils ]----- */
|
||||||
|
|
||||||
function DEFPRINT(nodetype, generator) {
|
function DEFPRINT(nodetype, generator) {
|
||||||
nodetype.DEFMETHOD("print", function(stream){
|
nodetype.DEFMETHOD("print", function(stream, force_parens){
|
||||||
var self = this;
|
var self = this;
|
||||||
stream.push_node(self);
|
stream.push_node(self);
|
||||||
if (self.needs_parens(stream)) {
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
stream.with_parens(function(){
|
stream.with_parens(function(){
|
||||||
self.add_comments(stream);
|
self.add_comments(stream);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
@@ -454,18 +467,6 @@ function OutputStream(options) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// for (var i = (foo in bar);;); ← perhaps useless, but valid syntax
|
|
||||||
if (this.operator == "in") {
|
|
||||||
// the “NoIn” stuff :-\
|
|
||||||
// UglifyJS 1.3.3 misses this one.
|
|
||||||
if ((p instanceof AST_For || p instanceof AST_ForIn) && p.init === this)
|
|
||||||
return true;
|
|
||||||
if (p instanceof AST_VarDef) {
|
|
||||||
var v = output.parent(1), p2 = output.parent(2);
|
|
||||||
if ((p2 instanceof AST_For || p2 instanceof AST_ForIn) && p2.init === v)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_PropAccess, function(output){
|
PARENS(AST_PropAccess, function(output){
|
||||||
@@ -609,7 +610,11 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function(){
|
output.with_parens(function(){
|
||||||
if (self.init) {
|
if (self.init) {
|
||||||
self.init.print(output);
|
if (self.init instanceof AST_Definitions) {
|
||||||
|
self.init.print(output);
|
||||||
|
} else {
|
||||||
|
parenthesize_for_noin(self.init, output, true);
|
||||||
|
}
|
||||||
output.print(";");
|
output.print(";");
|
||||||
output.space();
|
output.space();
|
||||||
} else {
|
} else {
|
||||||
@@ -853,13 +858,32 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Const, function(self, output){
|
DEFPRINT(AST_Const, function(self, output){
|
||||||
self._do_print(output, "const");
|
self._do_print(output, "const");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function parenthesize_for_noin(node, output, noin) {
|
||||||
|
if (!noin) node.print(output);
|
||||||
|
else try {
|
||||||
|
// need to take some precautions here:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/60
|
||||||
|
node.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Binary && node.operator == "in")
|
||||||
|
throw output;
|
||||||
|
}));
|
||||||
|
node.print(output);
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== output) throw ex;
|
||||||
|
node.print(output, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
DEFPRINT(AST_VarDef, function(self, output){
|
DEFPRINT(AST_VarDef, function(self, output){
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
if (self.value) {
|
if (self.value) {
|
||||||
output.space();
|
output.space();
|
||||||
output.print("=");
|
output.print("=");
|
||||||
output.space();
|
output.space();
|
||||||
self.value.print(output);
|
var p = output.parent(1);
|
||||||
|
var noin = p instanceof AST_For || p instanceof AST_ForIn;
|
||||||
|
parenthesize_for_noin(self.value, output, noin);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
11
lib/scope.js
11
lib/scope.js
@@ -110,9 +110,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
labels.del(l.name);
|
labels.del(l.name);
|
||||||
return true; // no descend again
|
return true; // no descend again
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolDeclaration) {
|
|
||||||
node.init_scope_vars();
|
|
||||||
}
|
|
||||||
if (node instanceof AST_Symbol) {
|
if (node instanceof AST_Symbol) {
|
||||||
node.scope = scope;
|
node.scope = scope;
|
||||||
}
|
}
|
||||||
@@ -128,8 +125,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// scope. Don't like this fix but seems we can't do any
|
// scope. Don't like this fix but seems we can't do any
|
||||||
// better. IE: please die. Please!
|
// better. IE: please die. Please!
|
||||||
(node.scope = scope.parent_scope).def_function(node);
|
(node.scope = scope.parent_scope).def_function(node);
|
||||||
|
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -138,14 +133,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|
|||||||
// 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 = scope.parent_scope).def_function(node);
|
||||||
node.init.push(tw.parent());
|
|
||||||
}
|
}
|
||||||
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 = scope.def_variable(node);
|
||||||
def.constant = node instanceof AST_SymbolConst;
|
def.constant = node instanceof AST_SymbolConst;
|
||||||
def = tw.parent();
|
def = tw.parent();
|
||||||
if (def.value) node.init.push(def);
|
|
||||||
}
|
}
|
||||||
else if (node instanceof AST_SymbolCatch) {
|
else if (node instanceof AST_SymbolCatch) {
|
||||||
// XXX: this is wrong according to ECMA-262 (12.4). the
|
// XXX: this is wrong according to ECMA-262 (12.4). the
|
||||||
@@ -246,10 +239,6 @@ AST_SymbolRef.DEFMETHOD("reference", function() {
|
|||||||
this.frame = this.scope.nesting - def.scope.nesting;
|
this.frame = this.scope.nesting - def.scope.nesting;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_SymbolDeclaration.DEFMETHOD("init_scope_vars", function(){
|
|
||||||
this.init = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
AST_Label.DEFMETHOD("init_scope_vars", function(){
|
||||||
this.references = [];
|
this.references = [];
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
x = this;
|
x = this;
|
||||||
descend(x, tw);
|
descend(x, tw);
|
||||||
} else {
|
} else {
|
||||||
tw.stack[tw.stack - 1] = x = this.clone();
|
tw.stack[tw.stack.length - 1] = x = this.clone();
|
||||||
descend(x, tw);
|
descend(x, tw);
|
||||||
y = tw.after(x, in_list);
|
y = tw.after(x, in_list);
|
||||||
if (y !== undefined) x = y;
|
if (y !== undefined) x = y;
|
||||||
|
|||||||
@@ -255,6 +255,14 @@ Dictionary.prototype = {
|
|||||||
this._values["$" + key] = val;
|
this._values["$" + key] = val;
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
add: function(key, val) {
|
||||||
|
if (this.has(key)) {
|
||||||
|
this.get(key).push(val);
|
||||||
|
} else {
|
||||||
|
this.set(key, [ val ]);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
},
|
||||||
get: function(key) { return this._values["$" + key] },
|
get: function(key) { return this._values["$" + key] },
|
||||||
del: function(key) {
|
del: function(key) {
|
||||||
if (this.has(key)) {
|
if (this.has(key)) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "uglify-js2",
|
"name": "uglify-js",
|
||||||
"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.1.11",
|
"version": "2.2.2",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"optimist" : "~0.3.5"
|
"optimist" : "~0.3.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs2" : "bin/uglifyjs2"
|
"uglifyjs" : "bin/uglifyjs"
|
||||||
},
|
},
|
||||||
"scripts": {"test": "node test/run-tests.js"}
|
"scripts": {"test": "node test/run-tests.js"}
|
||||||
}
|
}
|
||||||
|
|||||||
30
test/compress/issue-59.js
Normal file
30
test/compress/issue-59.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
keep_continue: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
evaluate: true
|
||||||
|
};
|
||||||
|
input: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
while (a) {
|
||||||
|
if (b) {
|
||||||
|
switch (true) {
|
||||||
|
case c():
|
||||||
|
d();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
210
test/compress/switch.js
Normal file
210
test/compress/switch.js
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
constant_switch_1: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1+1) {
|
||||||
|
case 1: foo(); break;
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz(); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_2: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1) {
|
||||||
|
case 1: foo();
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
foo();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_3: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (10) {
|
||||||
|
case 1: foo();
|
||||||
|
case 1+1: bar(); break;
|
||||||
|
case 1+1+1: baz();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_4: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (2) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
bar();
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_5: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
// the break inside the if ruins our job
|
||||||
|
// we can still get rid of irrelevant cases.
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break;
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
// XXX: we could optimize this better by inventing an outer
|
||||||
|
// labeled block, but that's kinda tricky.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_6: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_7: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
for (var x = 0; x < 10; x++) {
|
||||||
|
if (x > 5) break; // this break refers to the for, not to the switch; thus it
|
||||||
|
// shouldn't ruin our optimization
|
||||||
|
console.log(x);
|
||||||
|
}
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
foo();
|
||||||
|
x();
|
||||||
|
if (foo) break OUT;
|
||||||
|
for (var x = 0; x < 10; x++) {
|
||||||
|
if (x > 5) break;
|
||||||
|
console.log(x);
|
||||||
|
}
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_8: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
for (;;) break OUT;
|
||||||
|
y();
|
||||||
|
break;
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
x();
|
||||||
|
for (;;) break OUT;
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constant_switch_9: {
|
||||||
|
options = { dead_code: true, evaluate: true };
|
||||||
|
input: {
|
||||||
|
OUT: switch (1) {
|
||||||
|
case 1:
|
||||||
|
x();
|
||||||
|
for (;;) if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
case 1+1:
|
||||||
|
bar();
|
||||||
|
default:
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
OUT: {
|
||||||
|
x();
|
||||||
|
for (;;) if (foo) break OUT;
|
||||||
|
y();
|
||||||
|
bar();
|
||||||
|
def();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user