Compare commits

...

8 Commits

Author SHA1 Message Date
Alex Lam S.L
f67a6b0e43 v3.0.19 2017-06-22 03:24:22 +08:00
Alex Lam S.L
343ea326c2 ensure mangling works if catch reuses a scope variable (#2123)
fixes #2120
2017-06-20 02:14:05 +08:00
Alex Lam S.L
1c150c632f v3.0.18 2017-06-18 15:01:20 +08:00
Alex Lam S.L
0a0f4f5591 make defensive copies when inline (#2116)
fixes #2114
2017-06-17 14:32:37 +08:00
Alex Lam S.L
931daa85bf fix loss of context in collapse_vars & cascade (#2112)
fixes #2110
2017-06-16 21:18:43 +08:00
Alex Lam S.L
00e4f7b3c1 in-place tigten_body() (#2111)
By reducing copies of `AST_Node` arrays, we should be able to reduce:
- memory pressure
- potential bugs caused by multiple references in AST
- duplicated executions of `OPT()`
2017-06-16 19:19:54 +08:00
Alex Lam S.L
11e63bc335 correctly determine scope of AST_This (#2109)
fixes #2107
2017-06-16 14:54:46 +08:00
Alex Lam S.L
33405bb24b enforce inline scope restriction (#2106)
fixes #2105
2017-06-16 03:21:38 +08:00
7 changed files with 476 additions and 193 deletions

View File

@@ -668,26 +668,24 @@ merge(Compressor.prototype, {
var CHANGED, max_iter = 10; var CHANGED, max_iter = 10;
do { do {
CHANGED = false; CHANGED = false;
statements = eliminate_spurious_blocks(statements); eliminate_spurious_blocks(statements);
if (compressor.option("dead_code")) { if (compressor.option("dead_code")) {
statements = eliminate_dead_code(statements, compressor); eliminate_dead_code(statements, compressor);
} }
if (compressor.option("if_return")) { if (compressor.option("if_return")) {
statements = handle_if_return(statements, compressor); handle_if_return(statements, compressor);
} }
if (compressor.sequences_limit > 0) { if (compressor.sequences_limit > 0) {
statements = sequencesize(statements, compressor); sequencesize(statements, compressor);
} }
if (compressor.option("join_vars")) { if (compressor.option("join_vars")) {
statements = join_consecutive_vars(statements, compressor); join_consecutive_vars(statements, compressor);
} }
if (compressor.option("collapse_vars")) { if (compressor.option("collapse_vars")) {
statements = collapse(statements, compressor); collapse(statements, compressor);
} }
} while (CHANGED && max_iter-- > 0); } while (CHANGED && max_iter-- > 0);
return statements;
// Search from right to left for assignment-like expressions: // Search from right to left for assignment-like expressions:
// - `var a = x;` // - `var a = x;`
// - `a = x;` // - `a = x;`
@@ -724,6 +722,7 @@ merge(Compressor.prototype, {
// Stop immediately if these node types are encountered // Stop immediately if these node types are encountered
var parent = tt.parent(); var parent = tt.parent();
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left) if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger || node instanceof AST_Debugger
|| node instanceof AST_IterationStatement && !(node instanceof AST_For) || node instanceof AST_IterationStatement && !(node instanceof AST_For)
|| node instanceof AST_SymbolRef && node.undeclared() || node instanceof AST_SymbolRef && node.undeclared()
@@ -789,7 +788,6 @@ merge(Compressor.prototype, {
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1); if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
} }
} }
return statements;
function extract_candidates(expr) { function extract_candidates(expr) {
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor) if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
@@ -891,59 +889,60 @@ merge(Compressor.prototype, {
function eliminate_spurious_blocks(statements) { function eliminate_spurious_blocks(statements) {
var seen_dirs = []; var seen_dirs = [];
return statements.reduce(function(a, stat){ for (var i = 0; i < statements.length;) {
var stat = statements[i];
if (stat instanceof AST_BlockStatement) { if (stat instanceof AST_BlockStatement) {
CHANGED = true; CHANGED = true;
a.push.apply(a, eliminate_spurious_blocks(stat.body)); eliminate_spurious_blocks(stat.body);
[].splice.apply(statements, [i, 1].concat(stat.body));
i += stat.body.length;
} else if (stat instanceof AST_EmptyStatement) { } else if (stat instanceof AST_EmptyStatement) {
CHANGED = true; CHANGED = true;
statements.splice(i, 1);
} else if (stat instanceof AST_Directive) { } else if (stat instanceof AST_Directive) {
if (seen_dirs.indexOf(stat.value) < 0) { if (seen_dirs.indexOf(stat.value) < 0) {
a.push(stat); i++;
seen_dirs.push(stat.value); seen_dirs.push(stat.value);
} else { } else {
CHANGED = true; CHANGED = true;
statements.splice(i, 1);
} }
} else { } else i++;
a.push(stat); }
} }
return a;
}, []);
};
function handle_if_return(statements, compressor) { function handle_if_return(statements, compressor) {
var self = compressor.self(); var self = compressor.self();
var multiple_if_returns = has_multiple_if_returns(statements); var multiple_if_returns = has_multiple_if_returns(statements);
var in_lambda = self instanceof AST_Lambda; var in_lambda = self instanceof AST_Lambda;
var ret = []; // Optimized statements, build from tail to front for (var i = statements.length; --i >= 0;) {
loop: for (var i = statements.length; --i >= 0;) {
var stat = statements[i]; var stat = statements[i];
switch (true) { var next = statements[i + 1];
case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
CHANGED = true; CHANGED = true;
// note, ret.length is probably always zero statements.length--;
// because we drop unreachable code before this continue;
// step. nevertheless, it's good to check. }
continue loop;
case stat instanceof AST_If: if (stat instanceof AST_If) {
var ab = aborts(stat.body); var ab = aborts(stat.body);
if (can_merge_flow(ab)) { if (can_merge_flow(ab)) {
if (ab.label) { if (ab.label) {
remove(ab.label.thedef.references, ab); remove(ab.label.thedef.references, ab);
} }
CHANGED = true; CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
var body = as_statement_array_with_return(stat.body, ab);
stat = stat.clone(); stat = stat.clone();
stat.condition = stat.condition.negate(compressor); stat.condition = stat.condition.negate(compressor);
var body = as_statement_array_with_return(stat.body, ab);
stat.body = make_node(AST_BlockStatement, stat, { stat.body = make_node(AST_BlockStatement, stat, {
body: as_statement_array(stat.alternative).concat(ret) body: as_statement_array(stat.alternative).concat(extract_functions())
}); });
stat.alternative = make_node(AST_BlockStatement, stat, { stat.alternative = make_node(AST_BlockStatement, stat, {
body: body body: body
}); });
ret = [ stat.transform(compressor) ].concat(funs); statements[i] = stat.transform(compressor);
continue loop; continue;
} }
var ab = aborts(stat.alternative); var ab = aborts(stat.alternative);
@@ -952,81 +951,71 @@ merge(Compressor.prototype, {
remove(ab.label.thedef.references, ab); remove(ab.label.thedef.references, ab);
} }
CHANGED = true; CHANGED = true;
var funs = extract_functions_from_statement_array(ret);
stat = stat.clone(); stat = stat.clone();
stat.body = make_node(AST_BlockStatement, stat.body, { stat.body = make_node(AST_BlockStatement, stat.body, {
body: as_statement_array(stat.body).concat(ret) body: as_statement_array(stat.body).concat(extract_functions())
}); });
var body = as_statement_array_with_return(stat.alternative, ab); var body = as_statement_array_with_return(stat.alternative, ab);
stat.alternative = make_node(AST_BlockStatement, stat.alternative, { stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
body: body body: body
}); });
ret = [ stat.transform(compressor) ].concat(funs); statements[i] = stat.transform(compressor);
continue loop; continue;
} }
}
if (stat.body instanceof AST_Return) { if (stat instanceof AST_If && stat.body instanceof AST_Return) {
var value = stat.body.value; var value = stat.body.value;
//--- //---
// pretty silly case, but: // pretty silly case, but:
// if (foo()) return; return; ==> foo(); return; // if (foo()) return; return; ==> foo(); return;
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value) if (!value && !stat.alternative
&& !value && !stat.alternative) { && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
CHANGED = true; CHANGED = true;
var cond = make_node(AST_SimpleStatement, stat.condition, { statements[i] = make_node(AST_SimpleStatement, stat.condition, {
body: stat.condition body: stat.condition
}); });
ret.unshift(cond); continue;
continue loop; }
} //---
//--- // if (foo()) return x; return y; ==> return foo() ? x : y;
// if (foo()) return x; return y; ==> return foo() ? x : y; if (value && !stat.alternative && next instanceof AST_Return && next.value) {
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) { CHANGED = true;
CHANGED = true; stat = stat.clone();
stat = stat.clone(); stat.alternative = next;
stat.alternative = ret[0]; statements.splice(i, 2, stat.transform(compressor));
ret[0] = stat.transform(compressor); continue;
continue loop; }
} //---
//--- // if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined; if (multiple_if_returns && in_lambda && value && !stat.alternative
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return) && (!next || next instanceof AST_Return)) {
&& value && !stat.alternative && in_lambda) { CHANGED = true;
CHANGED = true; stat = stat.clone();
stat = stat.clone(); stat.alternative = next || make_node(AST_Return, stat, {
stat.alternative = ret[0] || make_node(AST_Return, stat, { value: null
value: null });
}); statements.splice(i, next ? 2 : 1, stat.transform(compressor));
ret[0] = stat.transform(compressor); continue;
continue loop; }
} //---
//--- // if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e; //
// // if sequences is not enabled, this can lead to an endless loop (issue #866).
// if sequences is not enabled, this can lead to an endless loop (issue #866). // however, with sequences on this helps producing slightly better output for
// however, with sequences on this helps producing slightly better output for // the example code.
// the example code. var prev = statements[i - 1];
if (compressor.option("sequences") if (compressor.option("sequences") && in_lambda && !stat.alternative
&& i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return && prev instanceof AST_If && prev.body instanceof AST_Return
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement && i + 2 == statements.length && next instanceof AST_SimpleStatement) {
&& !stat.alternative) { CHANGED = true;
CHANGED = true; statements.push(make_node(AST_Return, next, {
ret.push(make_node(AST_Return, ret[0], { value: null
value: null }).transform(compressor));
}).transform(compressor)); continue;
ret.unshift(stat);
continue loop;
}
} }
ret.unshift(stat);
break;
default:
ret.unshift(stat);
break;
} }
} }
return ret;
function has_multiple_if_returns(statements) { function has_multiple_if_returns(statements) {
var n = 0; var n = 0;
@@ -1051,6 +1040,18 @@ merge(Compressor.prototype, {
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct; || ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
} }
function extract_functions() {
var tail = statements.slice(i + 1);
statements.length = i + 1;
return tail.filter(function(stat) {
if (stat instanceof AST_Defun) {
statements.push(stat);
return false;
}
return true;
});
}
function as_statement_array_with_return(node, ab) { function as_statement_array_with_return(node, ab) {
var body = as_statement_array(node).slice(0, -1); var body = as_statement_array(node).slice(0, -1);
if (ab.value) { if (ab.value) {
@@ -1060,49 +1061,52 @@ merge(Compressor.prototype, {
} }
return body; return body;
} }
}; }
function eliminate_dead_code(statements, compressor) { function eliminate_dead_code(statements, compressor) {
var has_quit = false; var has_quit;
var orig = statements.length;
var self = compressor.self(); var self = compressor.self();
statements = statements.reduce(function(a, stat){ for (var i = 0, n = 0, len = statements.length; i < len; i++) {
if (has_quit) { var stat = statements[i];
extract_declarations_from_unreachable_code(compressor, stat, a); if (stat instanceof AST_LoopControl) {
} else { var lct = compressor.loopcontrol_target(stat);
if (stat instanceof AST_LoopControl) { if (stat instanceof AST_Break
var lct = compressor.loopcontrol_target(stat); && !(lct instanceof AST_IterationStatement)
if ((stat instanceof AST_Break && loop_body(lct) === self
&& !(lct instanceof AST_IterationStatement) || stat instanceof AST_Continue
&& loop_body(lct) === self) || (stat instanceof AST_Continue && loop_body(lct) === self) {
&& loop_body(lct) === self)) { if (stat.label) {
if (stat.label) { remove(stat.label.thedef.references, stat);
remove(stat.label.thedef.references, stat);
}
} else {
a.push(stat);
} }
} else { } else {
a.push(stat); statements[n++] = stat;
} }
if (aborts(stat)) has_quit = true; } else {
statements[n++] = stat;
} }
return a; if (aborts(stat)) {
}, []); has_quit = statements.slice(i + 1);
CHANGED = statements.length != orig; break;
return statements; }
}; }
statements.length = n;
CHANGED = n != len;
if (has_quit) has_quit.forEach(function(stat) {
extract_declarations_from_unreachable_code(compressor, stat, statements);
});
}
function sequencesize(statements, compressor) { function sequencesize(statements, compressor) {
if (statements.length < 2) return statements; if (statements.length < 2) return;
var seq = [], ret = []; var seq = [], n = 0;
function push_seq() { function push_seq() {
if (!seq.length) return; if (!seq.length) return;
var body = make_sequence(seq[0], seq); var body = make_sequence(seq[0], seq);
ret.push(make_node(AST_SimpleStatement, body, { body: body })); statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
seq = []; seq = [];
}; }
statements.forEach(function(stat){ for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (stat instanceof AST_SimpleStatement) { if (stat instanceof AST_SimpleStatement) {
if (seq.length >= compressor.sequences_limit) push_seq(); if (seq.length >= compressor.sequences_limit) push_seq();
var body = stat.body; var body = stat.body;
@@ -1110,18 +1114,18 @@ merge(Compressor.prototype, {
if (body) merge_sequence(seq, body); if (body) merge_sequence(seq, body);
} else { } else {
push_seq(); push_seq();
ret.push(stat); statements[n++] = stat;
} }
}); }
push_seq(); push_seq();
ret = sequencesize_2(ret, compressor); statements.length = n;
CHANGED = ret.length != statements.length; sequencesize_2(statements, compressor);
return ret; CHANGED = statements.length != len;
}; }
function sequencesize_2(statements, compressor) { function sequencesize_2(statements, compressor) {
function cons_seq(right) { function cons_seq(right) {
ret.pop(); n--;
var left = prev.body; var left = prev.body;
if (!(left instanceof AST_Sequence)) { if (!(left instanceof AST_Sequence)) {
left = make_node(AST_Sequence, left, { left = make_node(AST_Sequence, left, {
@@ -1131,8 +1135,9 @@ merge(Compressor.prototype, {
merge_sequence(left.expressions, right); merge_sequence(left.expressions, right);
return left.transform(compressor); return left.transform(compressor);
}; };
var ret = [], prev = null; var n = 0, prev;
statements.forEach(function(stat){ for (var i = 0, len = statements.length; i < len; i++) {
var stat = statements[i];
if (prev) { if (prev) {
if (stat instanceof AST_For) { if (stat instanceof AST_For) {
try { try {
@@ -1145,7 +1150,7 @@ merge(Compressor.prototype, {
} }
else if (!stat.init) { else if (!stat.init) {
stat.init = prev.body.drop_side_effect_free(compressor); stat.init = prev.body.drop_side_effect_free(compressor);
ret.pop(); n--;
} }
} catch(ex) { } catch(ex) {
if (ex !== cons_seq) throw ex; if (ex !== cons_seq) throw ex;
@@ -1167,15 +1172,16 @@ merge(Compressor.prototype, {
stat.expression = cons_seq(stat.expression); stat.expression = cons_seq(stat.expression);
} }
} }
ret.push(stat); statements[n++] = stat;
prev = stat instanceof AST_SimpleStatement ? stat : null; prev = stat instanceof AST_SimpleStatement ? stat : null;
}); }
return ret; statements.length = n;
}; }
function join_consecutive_vars(statements, compressor) { function join_consecutive_vars(statements, compressor) {
var prev = null; for (var i = 0, j = -1, len = statements.length; i < len; i++) {
return statements.reduce(function(a, stat){ var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) { if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
prev.definitions = prev.definitions.concat(stat.definitions); prev.definitions = prev.definitions.concat(stat.definitions);
CHANGED = true; CHANGED = true;
@@ -1184,35 +1190,19 @@ merge(Compressor.prototype, {
&& prev instanceof AST_Var && prev instanceof AST_Var
&& (!stat.init || stat.init.TYPE == prev.TYPE)) { && (!stat.init || stat.init.TYPE == prev.TYPE)) {
CHANGED = true; CHANGED = true;
a.pop();
if (stat.init) { if (stat.init) {
stat.init.definitions = prev.definitions.concat(stat.init.definitions); stat.init.definitions = prev.definitions.concat(stat.init.definitions);
} else { } else {
stat.init = prev; stat.init = prev;
} }
a.push(stat); statements[j] = stat;
prev = stat;
} }
else { else {
prev = stat; statements[++j] = stat;
a.push(stat);
} }
return a;
}, []);
};
};
function extract_functions_from_statement_array(statements) {
var funs = [];
for (var i = statements.length - 1; i >= 0; --i) {
var stat = statements[i];
if (stat instanceof AST_Defun) {
statements.splice(i, 1);
funs.unshift(stat);
} }
} statements.length = j + 1;
return funs; };
} }
function extract_declarations_from_unreachable_code(compressor, stat, target) { function extract_declarations_from_unreachable_code(compressor, stat, target) {
@@ -1989,12 +1979,12 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Block, function(self, compressor){ OPT(AST_Block, function(self, compressor){
self.body = tighten_body(self.body, compressor); tighten_body(self.body, compressor);
return self; return self;
}); });
OPT(AST_BlockStatement, function(self, compressor){ OPT(AST_BlockStatement, function(self, compressor){
self.body = tighten_body(self.body, compressor); tighten_body(self.body, compressor);
switch (self.body.length) { switch (self.body.length) {
case 1: return self.body[0]; case 1: return self.body[0];
case 0: return make_node(AST_EmptyStatement, self); case 0: return make_node(AST_EmptyStatement, self);
@@ -2901,7 +2891,7 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Try, function(self, compressor){ OPT(AST_Try, function(self, compressor){
self.body = tighten_body(self.body, compressor); tighten_body(self.body, compressor);
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null; if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
if (all(self.body, is_empty)) { if (all(self.body, is_empty)) {
var body = []; var body = [];
@@ -2944,18 +2934,18 @@ merge(Compressor.prototype, {
OPT(AST_Call, function(self, compressor){ OPT(AST_Call, function(self, compressor){
var exp = self.expression; var exp = self.expression;
if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) { var fn = exp;
var fixed = exp.fixed_value();
if (fixed instanceof AST_Function) exp = fixed;
}
if (compressor.option("unused") if (compressor.option("unused")
&& exp instanceof AST_Function && (fn instanceof AST_Function
&& !exp.uses_arguments || compressor.option("reduce_vars")
&& !exp.uses_eval) { && fn instanceof AST_SymbolRef
&& (fn = fn.fixed_value()) instanceof AST_Function)
&& !fn.uses_arguments
&& !fn.uses_eval) {
var pos = 0, last = 0; var pos = 0, last = 0;
for (var i = 0, len = self.args.length; i < len; i++) { for (var i = 0, len = self.args.length; i < len; i++) {
var trim = i >= exp.argnames.length; var trim = i >= fn.argnames.length;
if (trim || exp.argnames[i].__unused) { if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor); var node = self.args[i].drop_side_effect_free(compressor);
if (node) { if (node) {
self.args[pos++] = node; self.args[pos++] = node;
@@ -3156,15 +3146,15 @@ merge(Compressor.prototype, {
} }
} }
} }
if (exp instanceof AST_Function) { var stat = fn instanceof AST_Function && fn.body[0];
var stat = exp.body[0]; if (compressor.option("inline") && stat instanceof AST_Return) {
if (compressor.option("inline") && stat instanceof AST_Return) { var value = stat.value;
var value = stat && stat.value; if (!value || value.is_constant_expression()) {
if (!value || value.is_constant_expression()) { var args = self.args.concat(value || make_node(AST_Undefined, self));
var args = self.args.concat(value || make_node(AST_Undefined, self)); return make_sequence(self, args).transform(compressor);
return make_sequence(self, args).transform(compressor);
}
} }
}
if (exp instanceof AST_Function) {
if (compressor.option("inline") if (compressor.option("inline")
&& !exp.name && !exp.name
&& exp.body.length == 1 && exp.body.length == 1
@@ -3187,16 +3177,19 @@ merge(Compressor.prototype, {
if (exp.argnames.length > 0) { if (exp.argnames.length > 0) {
fn.body.push(make_node(AST_Var, self, { fn.body.push(make_node(AST_Var, self, {
definitions: exp.argnames.map(function(sym, i) { definitions: exp.argnames.map(function(sym, i) {
var arg = self.args[i];
return make_node(AST_VarDef, sym, { return make_node(AST_VarDef, sym, {
name: sym, name: sym,
value: self.args[i] || make_node(AST_Undefined, self) value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
}); });
}) })
})); }));
} }
if (self.args.length > exp.argnames.length) { if (self.args.length > exp.argnames.length) {
fn.body.push(make_node(AST_SimpleStatement, self, { fn.body.push(make_node(AST_SimpleStatement, self, {
body: make_sequence(self, self.args.slice(exp.argnames.length)) body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
return node.clone(true);
}))
})); }));
} }
fn.body.push(make_node(AST_Return, self, { fn.body.push(make_node(AST_Return, self, {
@@ -3207,18 +3200,21 @@ merge(Compressor.prototype, {
if (body.length == 1 && body[0] instanceof AST_Return) { if (body.length == 1 && body[0] instanceof AST_Return) {
value = body[0].value; value = body[0].value;
if (!value) return make_node(AST_Undefined, self); if (!value) return make_node(AST_Undefined, self);
value.walk(new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (value === self) return true; if (value === self) return true;
if (node instanceof AST_SymbolRef && matches(node.scope.find_variable(node)) if (node instanceof AST_SymbolRef) {
|| node instanceof AST_This && matches(node)) { var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = self;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = self; value = self;
return true; return true;
} }
});
function matches(ref) { value.walk(tw);
return ref && ref.scope.parent_scope === fn.parent_scope;
}
}));
if (value !== self) value = best_of(compressor, value, self); if (value !== self) value = best_of(compressor, value, self);
} else { } else {
value = self; value = self;
@@ -3357,6 +3353,7 @@ merge(Compressor.prototype, {
field = "left"; field = "left";
} }
} else if (cdr instanceof AST_Call } else if (cdr instanceof AST_Call
&& !(left instanceof AST_PropAccess && cdr.expression.equivalent_to(left))
|| cdr instanceof AST_PropAccess || cdr instanceof AST_PropAccess
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) { || cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
field = "expression"; field = "expression";

View File

@@ -79,7 +79,7 @@ SymbolDef.prototype = {
if (options.ie8 && sym instanceof AST_SymbolLambda) if (options.ie8 && sym instanceof AST_SymbolLambda)
s = s.parent_scope; s = s.parent_scope;
var def; var def;
if (this.defun && (def = this.defun.variables.get(this.name))) { if (def = this.redefined()) {
this.mangled_name = def.mangled_name || def.name; this.mangled_name = def.mangled_name || def.name;
} else } else
this.mangled_name = s.next_mangled(options, this); this.mangled_name = s.next_mangled(options, this);
@@ -87,6 +87,9 @@ SymbolDef.prototype = {
cache.set(this.name, this.mangled_name); cache.set(this.name, this.mangled_name);
} }
} }
},
redefined: function() {
return this.defun && this.defun.variables.get(this.name);
} }
}; };
@@ -206,6 +209,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.reference(options); node.reference(options);
return true; return true;
} }
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
}
}); });
self.walk(tw); self.walk(tw);

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.0.17", "version": "3.0.19",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -1108,3 +1108,47 @@ var_catch_toplevel: {
}(); }();
} }
} }
issue_2105: {
options = {
collapse_vars: true,
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
!function(factory) {
factory();
}( function() {
return function(fn) {
fn()().prop();
}( function() {
function bar() {
var quux = function() {
console.log("PASS");
}, foo = function() {
console.log;
quux();
};
return { prop: foo };
}
return bar;
} );
});
}
expect: {
!void function() {
var quux = function() {
console.log("PASS");
};
return {
prop: function() {
console.log;
quux();
}
};
}().prop();
}
expect_stdout: "PASS"
}

View File

@@ -414,3 +414,99 @@ inner_ref: {
} }
expect_stdout: "1 undefined" expect_stdout: "1 undefined"
} }
issue_2107: {
options = {
cascade: true,
collapse_vars: true,
inline: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function() {
c++;
}(c++ + new function() {
this.a = 0;
var a = (c = c + 1) + (c = 1 + c);
return c++ + a;
}());
console.log(c);
}
expect: {
var c = 0;
c++, new function() {
this.a = 0, c = 1 + (c += 1), c++;
}(), c++, console.log(c);
}
expect_stdout: "5"
}
issue_2114_1: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
!function() {
0;
}((c += 1, c = 1 + c, function() {
var b = void (b && (b.b += (c += 1, 0)));
}()));
console.log(c);
}
expect_stdout: "2"
}
issue_2114_2: {
options = {
collapse_vars: true,
if_return: true,
inline: true,
keep_fargs: false,
passes: 2,
side_effects: true,
unused: true,
}
input: {
var c = 0;
!function(a) {
a = 0;
}([ {
0: c = c + 1,
length: c = 1 + c
}, typeof void function a() {
var b = function f1(a) {
}(b && (b.b += (c = c + 1, 0)));
}() ]);
console.log(c);
}
expect: {
var c = 0;
c = 1 + (c += 1), function() {
var b = void (b && (b.b += (c += 1, 0)));
}();
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -178,3 +178,66 @@ impure_getter_2: {
} }
expect: {} expect: {}
} }
issue_2110_1: {
options = {
cascade: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
return f.g = function() {
return this;
}, f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}
issue_2110_2: {
options = {
collapse_vars: true,
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f() {
function f() {}
function g() {
return this;
}
f.g = g;
return f.g();
}
console.log(typeof f());
}
expect: {
function f() {
function f() {}
f.g = function() {
return this;
};
return f.g();
}
console.log(typeof f());
}
expect_stdout: "function"
}

View File

@@ -255,3 +255,73 @@ issue_1586_2: {
} }
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}" expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
} }
issue_2120_1: {
mangle = {
ie8: false,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (t) {
try {
throw 0;
} catch (a) {
if (t) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}
issue_2120_2: {
mangle = {
ie8: true,
}
input: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect: {
"aaaaaaaa";
var a = 1, b = "FAIL";
try {
throw 1;
} catch (c) {
try {
throw 0;
} catch (a) {
if (c) b = "PASS";
}
}
console.log(b);
}
expect_stdout: "PASS"
}