Merge branch 'master' into harmony-v3.1.5

This commit is contained in:
alexlamsl
2017-10-22 00:35:00 +08:00
4 changed files with 533 additions and 65 deletions

View File

@@ -157,7 +157,7 @@ merge(Compressor.prototype, {
var last_count = 1 / 0;
for (var pass = 0; pass < passes; pass++) {
if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this, true);
node.reset_opt_flags(this);
node = node.transform(this);
if (passes > 1) {
var count = 0;
@@ -289,8 +289,9 @@ merge(Compressor.prototype, {
self.transform(tt);
});
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan) {
var reduce_vars = rescan && compressor.option("reduce_vars");
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars");
var unused = compressor.option("unused");
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
// properly assigned before use:
// - `push()` & `pop()` when visiting conditional branches
@@ -315,16 +316,31 @@ merge(Compressor.prototype, {
if (node instanceof AST_SymbolRef) {
var d = node.definition();
d.references.push(node);
if (d.fixed === undefined || !safe_to_read(d)
|| is_modified(node, 0, is_immutable(node))) {
if (d.fixed === undefined || !safe_to_read(d) || d.single_use == "m") {
d.fixed = false;
} else {
var parent = tw.parent();
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
var value = node.fixed_value();
if (unused) {
d.single_use = value
&& d.references.length == 1
&& loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
}
if (is_modified(node, 0, is_immutable(value))) {
if (d.single_use) {
d.single_use = "m";
} else {
d.fixed = false;
}
} else {
var parent = tw.parent();
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
|| parent instanceof AST_VarDef && node === parent.value) {
d.escaped = true;
}
}
}
}
@@ -419,8 +435,7 @@ merge(Compressor.prototype, {
pop();
return true;
}
if (node instanceof AST_Binary
&& (node.operator == "&&" || node.operator == "||")) {
if (node instanceof AST_Binary && lazy_op(node.operator)) {
node.left.walk(tw);
push();
node.right.walk(tw);
@@ -567,20 +582,8 @@ merge(Compressor.prototype, {
def.single_use = undefined;
}
function is_immutable(node) {
var value = node.fixed_value();
if (!value) return false;
if (value.is_constant()) return true;
if (compressor.option("unused")) {
var d = node.definition();
if (d.single_use === undefined) {
d.single_use = loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression();
}
if (d.references.length == 1 && d.single_use) return true;
}
return value instanceof AST_Lambda;
function is_immutable(value) {
return value && (value.is_constant() || value instanceof AST_Lambda);
}
function is_modified(node, level, immutable) {
@@ -818,6 +821,7 @@ merge(Compressor.prototype, {
// Locate symbols which may execute code outside of scanning range
var lvalues = get_lvalues(candidate);
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false;
@@ -879,17 +883,18 @@ merge(Compressor.prototype, {
var sym;
if (node instanceof AST_Call
|| node instanceof AST_Exit
|| node instanceof AST_PropAccess && node.has_side_effects(compressor)
|| node instanceof AST_PropAccess
&& (side_effects || node.expression.may_throw_on_access(compressor))
|| node instanceof AST_SymbolRef
&& (lvalues[node.name]
|| side_effects && !references_in_scope(node.definition()))
|| (sym = lhs_or_def(node)) && get_symbol(sym).name in lvalues
|| parent instanceof AST_Binary
&& (parent.operator == "&&" || parent.operator == "||")
|| parent instanceof AST_Case
|| parent instanceof AST_Conditional
|| parent instanceof AST_For
|| parent instanceof AST_If) {
|| (sym = lhs_or_def(node))
&& (sym instanceof AST_PropAccess || sym.name in lvalues)
|| (side_effects || !one_off)
&& (parent instanceof AST_Binary && lazy_op(parent.operator)
|| parent instanceof AST_Case
|| parent instanceof AST_Conditional
|| parent instanceof AST_If)) {
if (!(node instanceof AST_Scope)) descend(node, tt);
abort = true;
return node;
@@ -1002,27 +1007,14 @@ merge(Compressor.prototype, {
}
}
function get_symbol(node) {
while (node instanceof AST_PropAccess) node = node.expression;
return node;
}
function get_lvalues(expr) {
var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues;
var scope;
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Scope) {
var save_scope = scope;
descend();
scope = save_scope;
return true;
}
if (node instanceof AST_SymbolRef || node instanceof AST_PropAccess) {
var sym = get_symbol(node);
if (sym instanceof AST_SymbolRef) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
var sym = node;
while (sym instanceof AST_PropAccess) sym = sym.expression;
if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
}
});
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
@@ -1521,9 +1513,10 @@ merge(Compressor.prototype, {
return member(this.operator, unary_bool);
});
def(AST_Binary, function(){
return member(this.operator, binary_bool) ||
( (this.operator == "&&" || this.operator == "||") &&
this.left.is_boolean() && this.right.is_boolean() );
return member(this.operator, binary_bool)
|| lazy_op(this.operator)
&& this.left.is_boolean()
&& this.right.is_boolean();
});
def(AST_Conditional, function(){
return this.consequent.is_boolean() && this.alternative.is_boolean();
@@ -1595,6 +1588,7 @@ merge(Compressor.prototype, {
node.DEFMETHOD("is_string", func);
});
var lazy_op = makePredicate("&& ||");
var unary_side_effects = makePredicate("delete ++ --");
function is_lhs(node, parent) {
@@ -2861,14 +2855,12 @@ merge(Compressor.prototype, {
def(AST_Binary, function(compressor, first_in_statement){
var right = this.right.drop_side_effect_free(compressor);
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
switch (this.operator) {
case "&&":
case "||":
if (lazy_op(this.operator)) {
if (right === this.right) return this;
var node = this.clone();
node.right = right;
return node;
default:
} else {
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
if (!left) return this.right.drop_side_effect_free(compressor, first_in_statement);
return make_sequence(this, [ left, right ]);
@@ -3792,7 +3784,7 @@ merge(Compressor.prototype, {
}
if (cdr instanceof AST_Binary && !(cdr instanceof AST_Assign)) {
if (cdr.left.is_constant()) {
if (cdr.operator == "||" || cdr.operator == "&&") {
if (lazy_op(cdr.operator)) {
expressions[++i] = expressions[j];
break;
}
@@ -4292,8 +4284,7 @@ merge(Compressor.prototype, {
// "x" + (y + "z")==> "x" + y + "z"
if (self.right instanceof AST_Binary
&& self.right.operator == self.operator
&& (self.operator == "&&"
|| self.operator == "||"
&& (lazy_op(self.operator)
|| (self.operator == "+"
&& (self.right.left.is_string(compressor)
|| (self.left.is_string(compressor)
@@ -4345,6 +4336,7 @@ merge(Compressor.prototype, {
d.fixed = fixed = make_node(AST_Function, fixed, fixed);
}
if (compressor.option("unused")
&& fixed
&& d.references.length == 1
&& (d.single_use || is_func_expr(fixed)
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg)

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.1.4",
"version": "3.1.5",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -2874,3 +2874,419 @@ prop_side_effects_2: {
"2",
]
}
issue_2365: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
console.log(function(a) {
var b = a.f;
a.f++;
return b;
}({ f: 1 }));
console.log(function() {
var a = { f: 1 }, b = a.f;
a.f++;
return b;
}());
console.log({
f: 1,
g: function() {
var b = this.f;
this.f++;
return b;
}
}.g());
}
expect: {
console.log(function(a) {
var b = a.f;
a.f++;
return b;
}({ f: 1 }));
console.log(function() {
var a = { f: 1 }, b = a.f;
a.f++;
return b;
}());
console.log({
f: 1,
g: function() {
var b = this.f;
this.f++;
return b;
}
}.g());
}
expect_stdout: [
"1",
"1",
"1",
]
}
issue_2364_1: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo() {
var first = arguments[0];
var result = inc(first);
return foo.amount = first.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo() {
var first = arguments[0];
var result = inc(first);
return foo.amount = first.count, result;
}
var data = {
count: 0
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_2: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function callValidate() {
var validate = compilation.validate;
var result = validate.apply(null, arguments);
return callValidate.errors = validate.errors, result;
}
}
expect: {
function callValidate() {
var validate = compilation.validate;
var result = validate.apply(null, arguments);
return callValidate.errors = validate.errors, result;
}
}
}
issue_2364_3: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo(bar) {
var result = inc(bar);
return foo.amount = bar.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo(bar) {
var result = inc(bar);
return foo.amount = bar.count, result;
}
var data = {
count: 0,
};
var answer = foo(data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_4: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function inc(obj) {
return obj.count++;
}
function foo(bar, baz) {
var result = inc(bar);
return foo.amount = baz.count, result;
}
var data = {
count: 0,
};
var answer = foo(data, data);
console.log(foo.amount, answer);
}
expect: {
function inc(obj) {
return obj.count++;
}
function foo(bar, baz) {
var result = inc(bar);
return foo.amount = baz.count, result;
}
var data = {
count: 0,
};
var answer = foo(data, data);
console.log(foo.amount, answer);
}
expect_stdout: "1 0"
}
issue_2364_5: {
options = {
collapse_vars: true,
evaluate: true,
pure_getters: true,
properties: true,
reduce_vars: true,
unused: true,
}
input: {
function f0(o, a, h) {
var b = 3 - a;
var obj = o;
var seven = 7;
var prop = 'run';
var t = obj[prop](b)[seven] = h;
return t;
}
}
expect: {
function f0(o, a, h) {
return o.run(3 - a)[7] = h;
}
}
}
issue_2364_6: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var c = a.p;
b.p = "FAIL";
return c;
}
var o = {
p: "PASS"
}
console.log(f(o, o));
}
expect: {
function f(a, b) {
var c = a.p;
b.p = "FAIL";
return c;
}
var o = {
p: "PASS"
}
console.log(f(o, o));
}
expect_stdout: "PASS"
}
issue_2364_7: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var c = a.p;
b.f();
return c;
}
var o = {
p: "PASS",
f: function() {
this.p = "FAIL";
}
}
console.log(f(o, o));
}
expect: {
function f(a, b) {
var c = a.p;
b.f();
return c;
}
var o = {
p: "PASS",
f: function() {
this.p = "FAIL";
}
}
console.log(f(o, o));
}
expect_stdout: "PASS"
}
issue_2364_8: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b, c) {
var d = a[b.f = function() {
return "PASS";
}];
return c.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f({}, o, o));
}
expect: {
function f(a, b, c) {
var d = a[b.f = function() {
return "PASS";
}];
return c.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f({}, o, o));
}
expect_stdout: "PASS"
}
issue_2364_9: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function f(a, b) {
var d = a();
return b.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f(function() {
o.f = function() {
return "PASS";
};
}, o));
}
expect: {
function f(a, b) {
var d = a();
return b.f(d);
}
var o = {
f: function() {
return "FAIL";
}
};
console.log(f(function() {
o.f = function() {
return "PASS";
};
}, o));
}
expect_stdout: "PASS"
}
pure_getters_chain: {
options = {
collapse_vars: true,
pure_getters: true,
}
input: {
function o(t, r) {
var a = t[1], s = t[2], o = t[3], i = t[5];
return a <= 23 && s <= 59 && o <= 59 && (!r || i);
}
console.log(o([ , 23, 59, 59, , 42], 1));
}
expect: {
function o(t, r) {
return t[1] <= 23 && t[2] <= 59 && t[3] <= 59 && (!r || t[5]);
}
console.log(o([ , 23, 59, 59, , 42], 1));
}
expect_stdout: "42"
}
conditional_1: {
options = {
collapse_vars: true,
}
input: {
function f(a, b) {
var c = "";
var d = b ? ">" : "<";
if (a) c += "=";
return c += d;
}
console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1));
}
expect: {
function f(a, b) {
var c = "";
if (a) c += "=";
return c += b ? ">" : "<";
}
console.log(f(0, 0), f(0, 1), f(1, 0), f(1, 1));
}
expect_stdout: "< > =< =>"
}
conditional_2: {
options = {
collapse_vars: true,
}
input: {
function f(a, b) {
var c = a + 1, d = a + 2;
return b ? c : d;
}
console.log(f(3, 0), f(4, 1));
}
expect: {
function f(a, b) {
return b ? a + 1 : a + 2;
}
console.log(f(3, 0), f(4, 1));
}
expect_stdout: "5 5"
}

View File

@@ -1190,10 +1190,10 @@ defun_label: {
!function() {
console.log(function(a) {
L: {
if (a) break L;
if (2) break L;
return 1;
}
}(2));
}());
}();
}
expect_stdout: true
@@ -3051,3 +3051,63 @@ array_forof_2: {
expect_stdout: "3"
node_version: ">=0.12"
}
const_expr_1: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2
};
o.a++;
console.log(o.a, o.b);
}
expect: {
var o = {
a: 1,
b: 2
};
o.a++;
console.log(o.a, o.b);
}
expect_stdout: "2 2"
}
const_expr_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
Object.prototype.c = function() {
this.a++;
};
var o = {
a: 1,
b: 2
};
o.c();
console.log(o.a, o.b);
}
expect: {
Object.prototype.c = function() {
this.a++;
};
var o = {
a: 1,
b: 2
};
o.c();
console.log(o.a, o.b);
}
expect_stdout: "2 2"
}