Merge branch 'master' into harmony-v3.0.18

This commit is contained in:
alexlamsl
2017-06-18 15:49:49 +08:00
5 changed files with 397 additions and 196 deletions

View File

@@ -695,26 +695,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;`
@@ -752,6 +750,7 @@ merge(Compressor.prototype, {
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_Await || node instanceof AST_Await
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger || node instanceof AST_Debugger
|| node instanceof AST_Destructuring || node instanceof AST_Destructuring
|| node instanceof AST_IterationStatement && !(node instanceof AST_For) || node instanceof AST_IterationStatement && !(node instanceof AST_For)
@@ -818,7 +817,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)
@@ -921,59 +919,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 && all(stat.body, can_be_evicted_from_block)) { if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
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);
@@ -982,81 +981,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;
@@ -1074,15 +1063,29 @@ merge(Compressor.prototype, {
} }
function can_merge_flow(ab) { function can_merge_flow(ab) {
if (!ab || !all(ret, function(stat) { if (!ab) return false;
return !(stat instanceof AST_Const || stat instanceof AST_Let); for (var j = i + 1, len = statements.length; j < len; j++) {
})) return false; var stat = statements[j];
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
}
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null; var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value) return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|| ab instanceof AST_Continue && self === loop_body(lct) || ab instanceof AST_Continue && self === loop_body(lct)
|| 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) {
@@ -1092,49 +1095,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;
@@ -1142,18 +1148,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, {
@@ -1163,8 +1169,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 {
@@ -1177,7 +1184,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;
@@ -1199,15 +1206,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;
@@ -1216,35 +1224,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) {
@@ -2050,13 +2042,12 @@ merge(Compressor.prototype, {
}); });
OPT(AST_Block, function(self, compressor){ OPT(AST_Block, function(self, compressor){
if (self.body instanceof AST_Node) { return self; } if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
self.body = 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: case 1:
if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If
@@ -3057,7 +3048,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 = [];
@@ -3114,18 +3105,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;
@@ -3330,15 +3321,15 @@ merge(Compressor.prototype, {
} }
} }
} }
if (exp instanceof AST_Function && !self.expression.is_generator && !self.expression.async) { 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 && !exp.is_generator && !exp.async) {
if (compressor.option("inline") if (compressor.option("inline")
&& !exp.name && !exp.name
&& exp.body.length == 1 && exp.body.length == 1
@@ -3361,16 +3352,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, {
@@ -3381,18 +3375,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;
@@ -3531,6 +3528,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

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"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.18",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -1294,3 +1294,47 @@ issue_2063: {
var a; var a;
} }
} }
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"
}