Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9fa178f86 | ||
|
|
53355bdb24 | ||
|
|
f05c99d89f | ||
|
|
b49230ab8d | ||
|
|
78856a3dab | ||
|
|
1e5e13ed81 | ||
|
|
64270b9778 | ||
|
|
e312c5c2a7 | ||
|
|
1a5fd3e052 | ||
|
|
5a7e54cf72 | ||
|
|
39f8a62703 | ||
|
|
46be3f2bf1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
tmp/
|
tmp/
|
||||||
|
node_modules/
|
||||||
|
|||||||
@@ -810,7 +810,7 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
|||||||
|
|
||||||
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||||
$documentation: "Reference to a label symbol",
|
$documentation: "Reference to a label symbol",
|
||||||
}, AST_SymbolRef);
|
}, AST_Symbol);
|
||||||
|
|
||||||
var AST_This = DEFNODE("This", null, {
|
var AST_This = DEFNODE("This", null, {
|
||||||
$documentation: "The `this` symbol",
|
$documentation: "The `this` symbol",
|
||||||
|
|||||||
@@ -885,7 +885,7 @@ merge(Compressor.prototype, {
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (node instanceof AST_SymbolRef && !(node instanceof AST_LabelRef)) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
push_uniq(in_use, node.definition());
|
push_uniq(in_use, node.definition());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -908,8 +908,7 @@ merge(Compressor.prototype, {
|
|||||||
if (decl instanceof AST_SymbolDeclaration) {
|
if (decl instanceof AST_SymbolDeclaration) {
|
||||||
decl.init.forEach(function(init){
|
decl.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) {
|
||||||
&& !(node instanceof AST_LabelRef)) {
|
|
||||||
push_uniq(in_use, node.definition());
|
push_uniq(in_use, node.definition());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -920,7 +919,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
// pass 3: we should drop declarations not in_use
|
// pass 3: we should drop declarations not in_use
|
||||||
var tt = new TreeTransformer(
|
var tt = new TreeTransformer(
|
||||||
function before(node, descend) {
|
function before(node, descend, in_list) {
|
||||||
if (node instanceof AST_Lambda) {
|
if (node instanceof AST_Lambda) {
|
||||||
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];
|
||||||
@@ -1011,6 +1010,19 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_For && node.init instanceof AST_BlockStatement) {
|
||||||
|
descend(node, this);
|
||||||
|
// certain combination of unused name + side effect leads to:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/44
|
||||||
|
// that's an invalid AST.
|
||||||
|
// We fix it at this stage by moving the `var` outside the `for`.
|
||||||
|
var body = node.init.body.slice(0, -1);
|
||||||
|
node.init = node.init.body.slice(-1)[0].body;
|
||||||
|
body.push(node);
|
||||||
|
return in_list ? MAP.splice(body) : make_node(AST_BlockStatement, node, {
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
}
|
||||||
if (node instanceof AST_Scope && node !== self)
|
if (node instanceof AST_Scope && node !== self)
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -1118,11 +1130,57 @@ merge(Compressor.prototype, {
|
|||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function if_break_in_loop(self, compressor) {
|
||||||
|
function drop_it(rest) {
|
||||||
|
rest = as_statement_array(rest);
|
||||||
|
if (self.body instanceof AST_BlockStatement) {
|
||||||
|
self.body = self.body.clone();
|
||||||
|
self.body.body = rest.concat(self.body.body.slice(1));
|
||||||
|
self.body = self.body.transform(compressor);
|
||||||
|
} else {
|
||||||
|
self.body = make_node(AST_BlockStatement, self.body, {
|
||||||
|
body: rest
|
||||||
|
}).transform(compressor);
|
||||||
|
}
|
||||||
|
if_break_in_loop(self, compressor);
|
||||||
|
}
|
||||||
|
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
|
||||||
|
if (first instanceof AST_If) {
|
||||||
|
if (first.body instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(first.body.label) === self) {
|
||||||
|
if (self.condition) {
|
||||||
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
|
left: self.condition,
|
||||||
|
operator: "&&",
|
||||||
|
right: first.condition.negate(compressor),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.condition = first.condition.negate(compressor);
|
||||||
|
}
|
||||||
|
drop_it(first.alternative);
|
||||||
|
}
|
||||||
|
else if (first.alternative instanceof AST_Break
|
||||||
|
&& compressor.loopcontrol_target(first.alternative.label) === self) {
|
||||||
|
if (self.condition) {
|
||||||
|
self.condition = make_node(AST_Binary, self.condition, {
|
||||||
|
left: self.condition,
|
||||||
|
operator: "&&",
|
||||||
|
right: first.condition,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.condition = first.condition;
|
||||||
|
}
|
||||||
|
drop_it(first.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
OPT(AST_While, function(self, compressor) {
|
OPT(AST_While, function(self, compressor) {
|
||||||
if (!compressor.option("loops")) return self;
|
if (!compressor.option("loops")) return self;
|
||||||
self = AST_DWLoop.prototype.optimize.call(self, compressor);
|
self = AST_DWLoop.prototype.optimize.call(self, compressor);
|
||||||
if (self instanceof AST_While) {
|
if (self instanceof AST_While) {
|
||||||
self = make_node(AST_For, self, self);
|
if_break_in_loop(self, compressor);
|
||||||
|
self = make_node(AST_For, self, self).transform(compressor);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
@@ -1151,6 +1209,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if_break_in_loop(self, compressor);
|
||||||
return self;
|
return self;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1367,9 +1426,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
left: exp.expression,
|
left: make_node(AST_String, self, { value: "" }),
|
||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
right: exp.expression
|
||||||
}).transform(compressor);
|
}).transform(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,7 +470,22 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
PARENS(AST_PropAccess, function(output){
|
PARENS(AST_PropAccess, function(output){
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_New && p.expression === this;
|
if (p instanceof AST_New && p.expression === this) {
|
||||||
|
// i.e. new (foo.bar().baz)
|
||||||
|
//
|
||||||
|
// if there's one call into this subtree, then we need
|
||||||
|
// parens around it too, otherwise the call will be
|
||||||
|
// interpreted as passing the arguments to the upper New
|
||||||
|
// expression.
|
||||||
|
try {
|
||||||
|
this.walk(new TreeWalker(function(node){
|
||||||
|
if (node instanceof AST_Call) throw p;
|
||||||
|
}));
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex !== p) throw ex;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Call, function(output){
|
PARENS(AST_Call, function(output){
|
||||||
|
|||||||
11
lib/scope.js
11
lib/scope.js
@@ -59,7 +59,7 @@ SymbolDef.prototype = {
|
|||||||
unmangleable: function(options) {
|
unmangleable: function(options) {
|
||||||
return this.global
|
return this.global
|
||||||
|| this.undeclared
|
|| this.undeclared
|
||||||
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with));
|
|| (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with));
|
||||||
},
|
},
|
||||||
mangle: function(options) {
|
mangle: function(options) {
|
||||||
if (!this.mangled_name && !this.unmangleable(options))
|
if (!this.mangled_name && !this.unmangleable(options))
|
||||||
@@ -347,11 +347,15 @@ AST_Symbol.DEFMETHOD("global", function(){
|
|||||||
return this.definition().global;
|
return this.definition().global;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
|
||||||
options = defaults(options, {
|
return defaults(options, {
|
||||||
except : [],
|
except : [],
|
||||||
eval : false,
|
eval : false,
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
||||||
|
options = this._default_mangler_options(options);
|
||||||
// We only need to mangle declaration nodes. Special logic wired
|
// We only need to mangle declaration nodes. Special logic wired
|
||||||
// into the code generator will display the mangled name if it's
|
// into the code generator will display the mangled name if it's
|
||||||
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
// present (and for AST_SymbolRef-s it'll use the mangled name of
|
||||||
@@ -387,6 +391,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
|
|||||||
});
|
});
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
|
||||||
|
options = this._default_mangler_options(options);
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
if (node instanceof AST_Constant)
|
if (node instanceof AST_Constant)
|
||||||
base54.consider(node.print_to_string());
|
base54.consider(node.print_to_string());
|
||||||
|
|||||||
@@ -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.1.8",
|
"version": "2.1.11",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
"url": "https://github.com/mishoo/UglifyJS2.git"
|
"url": "https://github.com/mishoo/UglifyJS2.git"
|
||||||
}],
|
}],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"source-map" : "*",
|
"source-map" : "~0.1.7",
|
||||||
"optimist" : "*"
|
"optimist" : "~0.3.5"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"uglifyjs2" : "bin/uglifyjs2"
|
"uglifyjs2" : "bin/uglifyjs2"
|
||||||
|
|||||||
31
test/compress/issue-44.js
Normal file
31
test/compress/issue-44.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
issue_44_valid_ast_1: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function a(b) {
|
||||||
|
for (var i = 0, e = b.qoo(); ; i++) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a(b) {
|
||||||
|
var i = 0;
|
||||||
|
for (b.qoo(); ; i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_44_valid_ast_2: {
|
||||||
|
options = { unused: true };
|
||||||
|
input: {
|
||||||
|
function a(b) {
|
||||||
|
if (foo) for (var i = 0, e = b.qoo(); ; i++) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function a(b) {
|
||||||
|
if (foo) {
|
||||||
|
var i = 0;
|
||||||
|
for (b.qoo(); ; i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
test/compress/loops.js
Normal file
123
test/compress/loops.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
while_becomes_for: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
while (foo()) bar();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();)
|
||||||
|
if (foo()) break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo(););
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && !foo();) {
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), !foo());) z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_1: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;;) if (foo()) bar(); else break;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; foo(); ) bar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_2: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) baz();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_3: {
|
||||||
|
options = { loops: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && foo();) {
|
||||||
|
baz();
|
||||||
|
stuff1();
|
||||||
|
stuff2();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_if_else_break_4: {
|
||||||
|
options = { loops: true, sequences: true };
|
||||||
|
input: {
|
||||||
|
for (;bar();) {
|
||||||
|
x();
|
||||||
|
y();
|
||||||
|
if (foo()) baz();
|
||||||
|
else break;
|
||||||
|
z();
|
||||||
|
k();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (; bar() && (x(), y(), foo());) baz(), z(), k();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user