Merge branch 'master' into harmony-2.8.0

This commit is contained in:
alexlamsl
2017-02-28 02:28:58 +08:00
8 changed files with 216 additions and 99 deletions

View File

@@ -1407,9 +1407,7 @@ merge(Compressor.prototype, {
&& (comments = this.start.comments_before) && (comments = this.start.comments_before)
&& comments.length && comments.length
&& /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) { && /[@#]__PURE__/.test((last_comment = comments[comments.length - 1]).value)) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start); pure = last_comment;
last_comment.value = last_comment.value.replace(/[@#]__PURE__/g, ' ');
pure = true;
} }
return this.pure = pure; return this.pure = pure;
}); });
@@ -1776,8 +1774,7 @@ merge(Compressor.prototype, {
line : def.name.start.line, line : def.name.start.line,
col : def.name.start.col col : def.name.start.col
}; };
if (def.value && def.value.has_side_effects(compressor)) { if (def.value && (def._unused_side_effects = def.value.drop_side_effect_free(compressor))) {
def._unused_side_effects = true;
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w); compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", w);
return true; return true;
} }
@@ -1797,7 +1794,7 @@ merge(Compressor.prototype, {
for (var i = 0; i < def.length;) { for (var i = 0; i < def.length;) {
var x = def[i]; var x = def[i];
if (x._unused_side_effects) { if (x._unused_side_effects) {
side_effects.push(x.value); side_effects.push(x._unused_side_effects);
def.splice(i, 1); def.splice(i, 1);
} else { } else {
if (side_effects.length > 0) { if (side_effects.length > 0) {
@@ -2040,6 +2037,10 @@ merge(Compressor.prototype, {
def(AST_This, return_null); def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){ def(AST_Call, function(compressor, first_in_statement){
if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this; if (!this.has_pure_annotation(compressor) && compressor.pure_funcs(this)) return this;
if (this.pure) {
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
this.pure.value = this.pure.value.replace(/[@#]__PURE__/g, ' ');
}
var args = trim(this.args, compressor, first_in_statement); var args = trim(this.args, compressor, first_in_statement);
return args && AST_Seq.from_array(args); return args && AST_Seq.from_array(args);
}); });
@@ -2091,8 +2092,17 @@ merge(Compressor.prototype, {
case "typeof": case "typeof":
if (this.expression instanceof AST_SymbolRef) return null; if (this.expression instanceof AST_SymbolRef) return null;
default: default:
if (first_in_statement && is_iife_call(this.expression)) return this; var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
return this.expression.drop_side_effect_free(compressor, first_in_statement); if (first_in_statement
&& this instanceof AST_UnaryPrefix
&& is_iife_call(expression)) {
if (expression === this.expression && this.operator.length === 1) return this;
return make_node(AST_UnaryPrefix, this, {
operator: this.operator.length === 1 ? this.operator : "!",
expression: expression
});
}
return expression;
} }
}); });
def(AST_SymbolRef, function() { def(AST_SymbolRef, function() {
@@ -2140,10 +2150,6 @@ merge(Compressor.prototype, {
OPT(AST_SimpleStatement, function(self, compressor){ OPT(AST_SimpleStatement, function(self, compressor){
if (compressor.option("side_effects")) { if (compressor.option("side_effects")) {
var body = self.body; var body = self.body;
if (!body.has_side_effects(compressor)) {
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
return make_node(AST_EmptyStatement, self);
}
var node = body.drop_side_effect_free(compressor, true); var node = body.drop_side_effect_free(compressor, true);
if (!node) { if (!node) {
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start); compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
@@ -2780,17 +2786,10 @@ merge(Compressor.prototype, {
} }
} }
if (!self.car.has_side_effects(compressor) if (!self.car.has_side_effects(compressor)
&& !self.cdr.has_side_effects(compressor)
&& self.car.equivalent_to(self.cdr)) { && self.car.equivalent_to(self.cdr)) {
return self.car; return self.car;
} }
} }
if (self.cdr instanceof AST_UnaryPrefix
&& self.cdr.operator == "void"
&& !self.cdr.expression.has_side_effects(compressor)) {
self.cdr.expression = self.car;
return self.cdr;
}
if (self.cdr instanceof AST_Undefined) { if (self.cdr instanceof AST_Undefined) {
return make_node(AST_UnaryPrefix, self, { return make_node(AST_UnaryPrefix, self, {
operator : "void", operator : "void",
@@ -2819,8 +2818,20 @@ merge(Compressor.prototype, {
}); });
OPT(AST_UnaryPrefix, function(self, compressor){ OPT(AST_UnaryPrefix, function(self, compressor){
self = self.lift_sequences(compressor); var seq = self.lift_sequences(compressor);
if (seq !== self) {
return seq;
}
var e = self.expression; var e = self.expression;
if (compressor.option("side_effects") && self.operator == "void") {
e = e.drop_side_effect_free(compressor);
if (e) {
self.expression = e;
return self;
} else {
return make_node(AST_Undefined, self);
}
}
if (compressor.option("booleans") && compressor.in_boolean_context()) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
switch (self.operator) { switch (self.operator) {
case "!": case "!":
@@ -2837,13 +2848,10 @@ merge(Compressor.prototype, {
// typeof always returns a non-empty string, thus it's // typeof always returns a non-empty string, thus it's
// always true in booleans // always true in booleans
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
if (self.expression.has_side_effects(compressor)) { return make_node(AST_Seq, self, {
return make_node(AST_Seq, self, { car: e,
car: self.expression, cdr: make_node(AST_True, self)
cdr: make_node(AST_True, self) }).optimize(compressor);
});
}
return make_node(AST_True, self);
} }
} }
return self.evaluate(compressor)[0]; return self.evaluate(compressor)[0];
@@ -2973,13 +2981,10 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) { if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start); compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
if (self.left.has_side_effects(compressor)) { return make_node(AST_Seq, self, {
return make_node(AST_Seq, self, { car: self.left,
car: self.left, cdr: make_node(AST_False)
cdr: make_node(AST_False) }).optimize(compressor);
}).optimize(compressor);
}
return make_node(AST_False, self);
} }
if (ll.length > 1 && ll[1]) { if (ll.length > 1 && ll[1]) {
return rr[0]; return rr[0];
@@ -2993,13 +2998,10 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) { if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
if (self.left.has_side_effects(compressor)) { return make_node(AST_Seq, self, {
return make_node(AST_Seq, self, { car: self.left,
car: self.left, cdr: make_node(AST_True, self)
cdr: make_node(AST_True) }).optimize(compressor);
}).optimize(compressor);
}
return make_node(AST_True, self);
} }
if (ll.length > 1 && !ll[1]) { if (ll.length > 1 && !ll[1]) {
return rr[0]; return rr[0];
@@ -3011,10 +3013,19 @@ merge(Compressor.prototype, {
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1] && !self.right.has_side_effects(compressor)) || if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) {
(rr.length > 1 && rr[0] instanceof AST_String && rr[1] && !self.left.has_side_effects(compressor))) {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_True, self); return make_node(AST_Seq, self, {
car: self.right,
cdr: make_node(AST_True, self)
}).optimize(compressor);
}
if (rr.length > 1 && rr[0] instanceof AST_String && rr[1]) {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_Seq, self, {
car: self.left,
cdr: make_node(AST_True, self)
}).optimize(compressor);
} }
break; break;
} }
@@ -3280,7 +3291,8 @@ merge(Compressor.prototype, {
&& alternative instanceof AST_Assign && alternative instanceof AST_Assign
&& consequent.operator == alternative.operator && consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left) && consequent.left.equivalent_to(alternative.left)
&& !consequent.left.has_side_effects(compressor) && (!consequent.left.has_side_effects(compressor)
|| !self.condition.has_side_effects(compressor))
) { ) {
/* /*
* Stuff like this: * Stuff like this:
@@ -3298,25 +3310,19 @@ merge(Compressor.prototype, {
}) })
}); });
} }
// x ? y(a) : y(b) --> y(x ? a : b)
if (consequent instanceof AST_Call if (consequent instanceof AST_Call
&& alternative.TYPE === consequent.TYPE && alternative.TYPE === consequent.TYPE
&& consequent.args.length == alternative.args.length && consequent.args.length == 1
&& !consequent.expression.has_side_effects(compressor) && alternative.args.length == 1
&& consequent.expression.equivalent_to(alternative.expression)) { && consequent.expression.equivalent_to(alternative.expression)
if (consequent.args.length == 0) { && !consequent.expression.has_side_effects(compressor)) {
return make_node(AST_Seq, self, { consequent.args[0] = make_node(AST_Conditional, self, {
car: self.condition, condition: self.condition,
cdr: consequent consequent: consequent.args[0],
}); alternative: alternative.args[0]
} });
if (consequent.args.length == 1) { return consequent;
consequent.args[0] = make_node(AST_Conditional, self, {
condition: self.condition,
consequent: consequent.args[0],
alternative: alternative.args[0]
});
return consequent;
}
} }
// x?y?z:a:a --> x&&y?z:a // x?y?z:a:a --> x&&y?z:a
if (consequent instanceof AST_Conditional if (consequent instanceof AST_Conditional
@@ -3331,16 +3337,12 @@ merge(Compressor.prototype, {
alternative: alternative alternative: alternative
}); });
} }
// y?1:1 --> 1 // x ? y : y --> x, y
if (consequent.is_constant() if (consequent.equivalent_to(alternative)) {
&& alternative.is_constant() return make_node(AST_Seq, self, {
&& consequent.equivalent_to(alternative)) { car: self.condition,
var consequent_value = consequent.evaluate(compressor)[0]; cdr: consequent
if (self.condition.has_side_effects(compressor)) { }).optimize(compressor);
return AST_Seq.from_array([self.condition, consequent_value]);
} else {
return consequent_value;
}
} }
if (is_true(self.consequent)) { if (is_true(self.consequent)) {
@@ -3499,8 +3501,12 @@ merge(Compressor.prototype, {
}); });
function literals_in_boolean_context(self, compressor) { function literals_in_boolean_context(self, compressor) {
if (compressor.option("booleans") && compressor.in_boolean_context() && !self.has_side_effects(compressor)) { if (compressor.option("booleans") && compressor.in_boolean_context()) {
return make_node(AST_True, self); var best = first_in_statement(compressor) ? best_of_statement : best_of;
return best(self, make_node(AST_Seq, self, {
car: self,
cdr: make_node(AST_True, self)
}).optimize(compressor));
} }
return self; return self;
}; };

View File

@@ -50,7 +50,8 @@ ifs_3_should_warn: {
conditionals : true, conditionals : true,
dead_code : true, dead_code : true,
evaluate : true, evaluate : true,
booleans : true booleans : true,
side_effects : true,
}; };
input: { input: {
var x, y; var x, y;
@@ -135,16 +136,28 @@ ifs_6: {
comparisons: true comparisons: true
}; };
input: { input: {
var x; var x, y;
if (!foo && !bar && !baz && !boo) { if (!foo && !bar && !baz && !boo) {
x = 10; x = 10;
} else { } else {
x = 20; x = 20;
} }
if (y) {
x[foo] = 10;
} else {
x[foo] = 20;
}
if (foo) {
x[bar] = 10;
} else {
x[bar] = 20;
}
} }
expect: { expect: {
var x; var x, y;
x = foo || bar || baz || boo ? 20 : 10; x = foo || bar || baz || boo ? 20 : 10;
x[foo] = y ? 10 : 20;
foo ? x[bar] = 10 : x[bar] = 20;
} }
} }
@@ -159,10 +172,16 @@ cond_1: {
} else { } else {
do_something(y); do_something(y);
} }
if (some_condition()) {
side_effects(x);
} else {
side_effects(y);
}
} }
expect: { expect: {
var do_something; var do_something;
do_something(some_condition() ? x : y); do_something(some_condition() ? x : y);
some_condition() ? side_effects(x) : side_effects(y);
} }
} }
@@ -213,10 +232,16 @@ cond_4: {
} else { } else {
do_something(); do_something();
} }
if (some_condition()) {
side_effects();
} else {
side_effects();
}
} }
expect: { expect: {
var do_something; var do_something;
some_condition(), do_something(); some_condition(), do_something();
some_condition(), side_effects();
} }
} }
@@ -250,7 +275,8 @@ cond_5: {
cond_7: { cond_7: {
options = { options = {
conditionals: true, conditionals: true,
evaluate : true evaluate : true,
side_effects: true,
}; };
input: { input: {
var x, y, z, a, b; var x, y, z, a, b;
@@ -714,6 +740,7 @@ issue_1154: {
conditionals: true, conditionals: true,
evaluate : true, evaluate : true,
booleans : true, booleans : true,
side_effects: true,
}; };
input: { input: {
function f1(x) { return x ? -1 : -1; } function f1(x) { return x ? -1 : -1; }
@@ -742,7 +769,7 @@ issue_1154: {
function g2() { return g(), 2; } function g2() { return g(), 2; }
function g3() { return g(), -4; } function g3() { return g(), -4; }
function g4() { return g(), !1; } function g4() { return g(), !1; }
function g5() { return g(), void 0; } function g5() { return void g(); }
function g6() { return g(), "number"; } function g6() { return g(), "number"; }
} }
} }
@@ -750,7 +777,8 @@ issue_1154: {
no_evaluate: { no_evaluate: {
options = { options = {
conditionals: true, conditionals: true,
evaluate : false evaluate : false,
side_effects: true,
} }
input: { input: {
function f(b) { function f(b) {

View File

@@ -64,7 +64,8 @@ dead_code_constant_boolean_should_warn_more: {
loops : true, loops : true,
booleans : true, booleans : true,
conditionals : true, conditionals : true,
evaluate : true evaluate : true,
side_effects : true,
}; };
input: { input: {
while (!((foo && bar) || (x + "0"))) { while (!((foo && bar) || (x + "0"))) {

View File

@@ -718,25 +718,35 @@ in_boolean_context: {
options = { options = {
booleans: true, booleans: true,
evaluate: true, evaluate: true,
sequences: true,
side_effects: true,
} }
input: { input: {
!42; console.log(
!"foo"; !42,
![1, 2]; !"foo",
!/foo/; ![1, 2],
!b(42); !/foo/,
!b("foo"); !b(42),
!b([1, 2]); !b("foo"),
!b(/foo/); !b([1, 2]),
!b(/foo/),
![1, foo()],
![1, foo(), 2]
);
} }
expect: { expect: {
!1; console.log(
!1; !1,
!1; !1,
!1; !1,
!b(42); !1,
!b("foo"); !b(42),
!b([1, 2]); !b("foo"),
!b(/foo/); !b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1)
);
} }
} }

View File

@@ -116,3 +116,61 @@ pure_function_calls_toplevel: {
"WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]", "WARN: Dropping unused variable iife1 [test/compress/issue-1261.js:84,12]",
] ]
} }
should_warn: {
options = {
booleans: true,
conditionals: true,
evaluate: true,
side_effects: true,
}
input: {
/* @__PURE__ */(function(){x})(), void/* @__PURE__ */(function(){y})();
/* @__PURE__ */(function(){x})() || true ? foo() : bar();
true || /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() && false ? foo() : bar();
false && /* @__PURE__ */(function(){y})() ? foo() : bar();
/* @__PURE__ */(function(){x})() + "foo" ? bar() : baz();
"foo" + /* @__PURE__ */(function(){y})() ? bar() : baz();
/* @__PURE__ */(function(){x})() ? foo() : foo();
[/* @__PURE__ */(function(){x})()] ? foo() : bar();
!{ foo: /* @__PURE__ */(function(){x})() } ? bar() : baz();
}
expect: {
foo();
foo();
bar();
bar();
bar();
bar();
foo();
foo();
baz();
}
expect_warnings: [
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,61]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:128,23]",
"WARN: Dropping side-effect-free statement [test/compress/issue-1261.js:128,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:129,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:129,23]",
"WARN: Condition always true [test/compress/issue-1261.js:129,23]",
"WARN: Boolean || always true [test/compress/issue-1261.js:130,8]",
"WARN: Condition always true [test/compress/issue-1261.js:130,8]",
"WARN: Boolean && always false [test/compress/issue-1261.js:131,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:131,23]",
"WARN: Condition always false [test/compress/issue-1261.js:131,23]",
"WARN: Boolean && always false [test/compress/issue-1261.js:132,8]",
"WARN: Condition always false [test/compress/issue-1261.js:132,8]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:133,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:133,23]",
"WARN: Condition always true [test/compress/issue-1261.js:133,23]",
"WARN: + in boolean context always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:134,31]",
"WARN: Condition always true [test/compress/issue-1261.js:134,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:135,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:136,24]",
"WARN: Condition always true [test/compress/issue-1261.js:136,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:137,31]",
"WARN: Condition always false [test/compress/issue-1261.js:137,8]",
]
}

View File

@@ -35,7 +35,7 @@ string_plus_optimization: {
throw "nope"; throw "nope";
} }
try { try {
console.log('0' + throwing_function() ? "yes" : "no"); console.log((throwing_function(), "yes"));
} catch (ex) { } catch (ex) {
console.log(ex); console.log(ex);
} }

View File

@@ -29,6 +29,7 @@ typeof_in_boolean_context: {
booleans : true, booleans : true,
evaluate : true, evaluate : true,
conditionals : true, conditionals : true,
side_effects : true,
}; };
input: { input: {
function f1(x) { return typeof x ? "yes" : "no"; } function f1(x) { return typeof x ? "yes" : "no"; }
@@ -36,12 +37,14 @@ typeof_in_boolean_context: {
typeof 0 ? foo() : bar(); typeof 0 ? foo() : bar();
!typeof console.log(1); !typeof console.log(1);
var a = !typeof console.log(2); var a = !typeof console.log(2);
if (typeof (1 + foo()));
} }
expect: { expect: {
function f1(x) { return "yes"; } function f1(x) { return "yes"; }
function f2() { return g(), "Yes"; } function f2() { return g(), "Yes"; }
foo(); foo();
!(console.log(1), !0); console.log(1);
var a = !(console.log(2), !0); var a = !(console.log(2), !0);
foo();
} }
} }

View File

@@ -154,6 +154,17 @@ describe("minify", function() {
var code = result.code; var code = result.code;
assert.strictEqual(code, "// comment1 comment2\nbar();"); assert.strictEqual(code, "// comment1 comment2\nbar();");
}); });
it("should not drop #__PURE__ hint if function is retained", function() {
var result = Uglify.minify("var a = /*#__PURE__*/(function(){return 1})();", {
fromString: true,
output: {
comments: "all",
beautify: false,
}
});
var code = result.code;
assert.strictEqual(code, "var a=/*#__PURE__*/function(){return 1}();");
})
}); });
describe("JS_Parse_Error", function() { describe("JS_Parse_Error", function() {