Compare commits

...

12 Commits

Author SHA1 Message Date
Mihai Bazon
a9fa178f86 v2.1.11 2012-11-12 13:24:52 +02:00
Mihai Bazon
53355bdb24 fix invalid AST produced by dropping unused variable
close #44
2012-11-12 13:23:57 +02:00
Mihai Bazon
f05c99d89f Merge pull request #41 from Skalman/toString-patch
Convert x.toString() to ""+x instead of x+""
2012-11-12 00:47:56 -08:00
Dan Wolff
b49230ab8d convert x.toString() to ""+x instead of x+""
In some places this can save one byte in whitespace, e.g. after return.
Example:

function f(arg) {
        // return""+arg - no space between return and ""
        return arg.toString();
}
2012-11-11 15:53:34 +02:00
Mihai Bazon
78856a3dab declare dependency versions
close #40
2012-11-09 16:43:49 +02:00
Mihai Bazon
1e5e13ed81 AST_LabelRef no longer inherits from AST_SymbolRef 2012-11-08 15:39:14 +02:00
Mihai Bazon
64270b9778 v2.1.10 2012-11-08 12:33:27 +02:00
Mihai Bazon
e312c5c2a7 fix API breakage
close #36, #38
2012-11-08 12:31:28 +02:00
Mihai Bazon
1a5fd3e052 optimization for if/break as first statement in a loop body
for(...; x; ...) if (y) break; → for(...; x&&!y; ...);

similarly for `while` and some combinations (i.e. the `break` appears in the
`else` clause, etc.)
2012-11-08 11:43:14 +02:00
Mihai Bazon
5a7e54cf72 ignore node_modules/ 2012-11-07 15:27:12 +02:00
Mihai Bazon
39f8a62703 v2.1.9 2012-11-07 13:31:58 +02:00
Mihai Bazon
46be3f2bf1 fix another small regression
we do need parens here: `new (foo.bar().baz)`, but not here: `new foo.bar.baz`
2012-11-07 13:31:43 +02:00
8 changed files with 249 additions and 15 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
tmp/
node_modules/

View File

@@ -810,7 +810,7 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
var AST_LabelRef = DEFNODE("LabelRef", null, {
$documentation: "Reference to a label symbol",
}, AST_SymbolRef);
}, AST_Symbol);
var AST_This = DEFNODE("This", null, {
$documentation: "The `this` symbol",

View File

@@ -885,7 +885,7 @@ merge(Compressor.prototype, {
});
return true;
}
if (node instanceof AST_SymbolRef && !(node instanceof AST_LabelRef)) {
if (node instanceof AST_SymbolRef) {
push_uniq(in_use, node.definition());
return true;
}
@@ -908,8 +908,7 @@ merge(Compressor.prototype, {
if (decl instanceof AST_SymbolDeclaration) {
decl.init.forEach(function(init){
var tw = new TreeWalker(function(node){
if (node instanceof AST_SymbolRef
&& !(node instanceof AST_LabelRef)) {
if (node instanceof AST_SymbolRef) {
push_uniq(in_use, node.definition());
}
});
@@ -920,7 +919,7 @@ merge(Compressor.prototype, {
}
// pass 3: we should drop declarations not in_use
var tt = new TreeTransformer(
function before(node, descend) {
function before(node, descend, in_list) {
if (node instanceof AST_Lambda) {
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
@@ -1011,6 +1010,19 @@ merge(Compressor.prototype, {
}
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)
return node;
}
@@ -1118,11 +1130,57 @@ merge(Compressor.prototype, {
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) {
if (!compressor.option("loops")) return self;
self = AST_DWLoop.prototype.optimize.call(self, compressor);
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;
});
@@ -1151,6 +1209,7 @@ merge(Compressor.prototype, {
}
}
}
if_break_in_loop(self, compressor);
return self;
});
@@ -1367,9 +1426,9 @@ merge(Compressor.prototype, {
}
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
return make_node(AST_Binary, self, {
left: exp.expression,
left: make_node(AST_String, self, { value: "" }),
operator: "+",
right: make_node(AST_String, self, { value: "" })
right: exp.expression
}).transform(compressor);
}
}

View File

@@ -470,7 +470,22 @@ function OutputStream(options) {
PARENS(AST_PropAccess, function(output){
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){

View File

@@ -59,7 +59,7 @@ SymbolDef.prototype = {
unmangleable: function(options) {
return this.global
|| 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) {
if (!this.mangled_name && !this.unmangleable(options))
@@ -347,11 +347,15 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = defaults(options, {
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
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
// 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
@@ -387,6 +391,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options){
options = this._default_mangler_options(options);
var tw = new TreeWalker(function(node){
if (node instanceof AST_Constant)
base54.consider(node.print_to_string());

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"main": "tools/node.js",
"version": "2.1.8",
"version": "2.1.11",
"engines": { "node" : ">=0.4.0" },
"maintainers": [{
"name": "Mihai Bazon",
@@ -15,8 +15,8 @@
"url": "https://github.com/mishoo/UglifyJS2.git"
}],
"dependencies": {
"source-map" : "*",
"optimist" : "*"
"source-map" : "~0.1.7",
"optimist" : "~0.3.5"
},
"bin": {
"uglifyjs2" : "bin/uglifyjs2"

31
test/compress/issue-44.js Normal file
View 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
View 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();
}
}