Compare commits

...

7 Commits

Author SHA1 Message Date
Alex Lam S.L
70bb304a0a v3.6.0 2019-05-30 15:30:00 +08:00
Alex Lam S.L
9d3b1efd86 fix corner case in assignments (#3430)
fixes #3429
2019-05-30 05:01:53 +08:00
Alex Lam S.L
482e1baea3 enhance assignments & unused (#3428)
closes #3427
2019-05-29 01:21:08 +08:00
Alex Lam S.L
e4f5ba1d29 v3.5.15 2019-05-21 14:26:58 +08:00
Alex Lam S.L
b9053c7a25 fix corner case in keep_fargs (#3424)
fixes #3423
2019-05-21 12:55:34 +08:00
Alex Lam S.L
d357a7aabc v3.5.14 2019-05-20 00:13:06 +08:00
Alex Lam S.L
ae77ebe5a5 fix corner case in arguments (#3421)
fixes #3420
2019-05-19 12:59:40 +08:00
7 changed files with 463 additions and 45 deletions

View File

@@ -105,10 +105,10 @@ function Compressor(options, false_by_default) {
}
if (this.options["inline"] === true) this.options["inline"] = 3;
var keep_fargs = this.options["keep_fargs"];
this.drop_fargs = keep_fargs == "strict" ? function(lambda) {
this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
if (lambda.length_read) return false;
var name = lambda.name;
if (!name) return true;
if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
if (name.fixed_value() !== lambda) return false;
var def = name.definition();
if (def.direct_access) return false;
@@ -527,12 +527,9 @@ merge(Compressor.prototype, {
function mark_assignment_to_arguments(node) {
if (!(node instanceof AST_Sub)) return;
var expr = node.expression;
var prop = node.property;
if (expr instanceof AST_SymbolRef
&& is_arguments(expr.definition())
&& prop instanceof AST_Number) {
expr.definition().reassigned = true;
}
if (!(expr instanceof AST_SymbolRef)) return;
var def = expr.definition();
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
}
var suppressor = new TreeWalker(function(node) {
@@ -2285,8 +2282,7 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
return !compressor.option("pure_getters")
|| this._dot_throw(compressor);
return !compressor.option("pure_getters") || this._dot_throw(compressor);
});
function is_strict(compressor) {
return /strict/.test(compressor.option("pure_getters"));
@@ -2294,7 +2290,15 @@ merge(Compressor.prototype, {
def(AST_Node, is_strict);
def(AST_Array, return_false);
def(AST_Assign, function(compressor) {
return this.operator == "=" && this.right._dot_throw(compressor);
if (this.operator != "=") return false;
var rhs = this.right;
if (!rhs._dot_throw(compressor)) return false;
var sym = this.left;
if (!(sym instanceof AST_SymbolRef)) return true;
if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
return rhs.right._dot_throw(compressor);
}
return true;
});
def(AST_Binary, function(compressor) {
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
@@ -3621,7 +3625,7 @@ merge(Compressor.prototype, {
var value = null;
if (node instanceof AST_Assign) {
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
value = node.right;
value = get_rhs(node);
}
} else if (!in_use) {
value = make_node(AST_Number, node, {
@@ -3641,7 +3645,7 @@ merge(Compressor.prototype, {
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = compressor.drop_fargs(node);
var trim = compressor.drop_fargs(node, parent);
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (!(sym.definition().id in in_use_ids)) {
@@ -3832,6 +3836,7 @@ merge(Compressor.prototype, {
};
}
});
tt.push(compressor.parent());
self.transform(tt);
function verify_safe_usage(def, read, modified) {
@@ -3845,6 +3850,15 @@ merge(Compressor.prototype, {
}
}
function get_rhs(assign) {
var rhs = assign.right;
if (!assign.write_only) return rhs;
if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
var sym = assign.left;
if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
}
function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
@@ -3852,9 +3866,10 @@ merge(Compressor.prototype, {
prop.walk(tw);
});
if (node instanceof AST_Assign) {
node.right.walk(tw);
var right = get_rhs(node);
right.walk(tw);
if (node.left === sym) {
if (!node_def.chained && sym.fixed_value(true) === node.right) {
if (!node_def.chained && sym.fixed_value(true) === right) {
fixed_ids[node_def.id] = node;
}
if (!node.write_only) {
@@ -4165,12 +4180,14 @@ merge(Compressor.prototype, {
});
def(AST_Assign, function(compressor) {
var left = this.left;
if (left.has_side_effects(compressor)
|| compressor.has_directive("use strict")
&& left instanceof AST_PropAccess
&& left.expression.is_constant()) {
return this;
if (left instanceof AST_PropAccess) {
var expr = left.expression;
if (expr instanceof AST_Assign && !expr.may_throw_on_access(compressor)) {
expr.write_only = true;
}
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
this.write_only = true;
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
return this.right.drop_side_effect_free(compressor);
@@ -4245,8 +4262,9 @@ merge(Compressor.prototype, {
});
def(AST_Constant, return_null);
def(AST_Dot, function(compressor, first_in_statement) {
if (this.expression.may_throw_on_access(compressor)) return this;
return this.expression.drop_side_effect_free(compressor, first_in_statement);
var expr = this.expression;
if (expr.may_throw_on_access(compressor)) return this;
return expr.drop_side_effect_free(compressor, first_in_statement);
});
def(AST_Function, function(compressor) {
return this.name && compressor.option("ie8") ? this : null;
@@ -5558,20 +5576,29 @@ merge(Compressor.prototype, {
self.right = tmp;
}
}
if (commutativeOperators[self.operator]) {
if (self.right.is_constant()
&& !self.left.is_constant()) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse();
}
if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
// if right is a constant, whatever side effects the
// left side might have could not influence the
// result. hence, force switch.
if (!(self.left instanceof AST_Binary
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
reverse();
}
}
self = self.lift_sequences(compressor);
if (compressor.option("assignments") && lazy_op[self.operator]) {
var assign = self.right;
// a || (a = x) => a = a || x
// a && (a = x) => a = a && x
if (self.left instanceof AST_SymbolRef
&& assign instanceof AST_Assign
&& assign.operator == "="
&& self.left.equivalent_to(assign.left)) {
self.right = assign.right;
assign.right = self;
return assign;
}
}
if (compressor.option("comparisons")) switch (self.operator) {
case "===":
case "!==":
@@ -6682,23 +6709,30 @@ merge(Compressor.prototype, {
}
}
}
var fn;
var parent = compressor.parent();
var def, fn, fn_parent;
if (compressor.option("arguments")
&& expr instanceof AST_SymbolRef
&& is_arguments(expr.definition())
&& is_arguments(def = expr.definition())
&& prop instanceof AST_Number
&& (fn = expr.scope) === compressor.find_parent(AST_Lambda)) {
&& (fn = expr.scope) === find_lambda()) {
var index = prop.getValue();
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
if (!def.deleted) def.deleted = [];
def.deleted[index] = true;
}
var argname = fn.argnames[index];
if (argname && compressor.has_directive("use strict")) {
var def = argname.definition();
if (def.deleted && def.deleted[index]) {
argname = null;
} else if (argname && compressor.has_directive("use strict")) {
var arg_def = argname.definition();
if (!compressor.option("reduce_vars")
|| expr.definition().reassigned
|| def.assignments
|| def.orig.length > 1) {
|| def.reassigned
|| arg_def.assignments
|| arg_def.orig.length > 1) {
argname = null;
}
} else if (!argname && compressor.drop_fargs(fn) && index < fn.argnames.length + 5) {
} else if (!argname && index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
while (index >= fn.argnames.length) {
argname = make_node(AST_SymbolFunarg, fn, {
name: fn.make_var_name("argument_" + fn.argnames.length),
@@ -6711,14 +6745,14 @@ merge(Compressor.prototype, {
if (argname && find_if(function(node) {
return node.name === argname.name;
}, fn.argnames) === argname) {
expr.definition().reassigned = false;
def.reassigned = false;
var sym = make_node(AST_SymbolRef, self, argname);
sym.reference({});
delete argname.__unused;
return sym;
}
}
if (is_lhs(compressor.self(), compressor.parent())) return self;
if (is_lhs(compressor.self(), parent)) return self;
if (key !== prop) {
var sub = self.flatten_object(property, compressor);
if (sub) {
@@ -6767,6 +6801,16 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self);
}
return self;
function find_lambda() {
var i = 0, p;
while (p = compressor.parent(i++)) {
if (p instanceof AST_Lambda) {
fn_parent = compressor.parent(i);
return p;
}
}
}
});
AST_Scope.DEFMETHOD("contains_this", function() {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.5.13",
"version": "3.6.0",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -622,3 +622,157 @@ issue_3282_2_passes: {
}
expect_stdout: true
}
issue_3420_1: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect: {
console.log(function() {
return function() {
return arguments[0];
};
}().length);
}
expect_stdout: "0"
}
issue_3420_2: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_3: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect: {
"use strict";
var foo = function() {
delete arguments[0];
};
foo();
}
expect_stdout: true
}
issue_3420_4: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_5: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
!function() {
console.log(arguments[0]);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect: {
"use strict";
!function(argument_0) {
console.log(argument_0);
delete arguments[0];
console.log(arguments[0]);
}(42);
}
expect_stdout: [
"42",
"undefined",
]
}
issue_3420_6: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
console.log(function() {
return delete arguments[0];
}());
}
expect: {
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}
issue_3420_7: {
options = {
arguments: true,
keep_fargs: "strict",
}
input: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect: {
"use strict";
console.log(function() {
return delete arguments[0];
}());
}
expect_stdout: "true"
}

View File

@@ -311,3 +311,65 @@ issue_3375: {
}
expect_stdout: "string"
}
issue_3427: {
options = {
assignments: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a || (a = {});
})();
}
expect: {}
}
issue_3429_1: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a = "PASS";
(function(b) {
b && (b = a = "FAIL");
})();
console.log(a);
}
expect: {
var a = "PASS";
(function(b) {
b = b && (a = "FAIL");
})();
console.log(a);
}
expect_stdout: "PASS"
}
issue_3429_2: {
options = {
assignments: true,
side_effects: true,
unused: true,
}
input: {
var a;
(function(b) {
b || (b = a = "FAIL");
})(42);
console.log(a);
}
expect: {
var a;
(function(b) {
b = b || (a = "FAIL");
})(42);
console.log(a);
}
expect_stdout: "undefined"
}

View File

@@ -2028,3 +2028,37 @@ issue_3375: {
}
expect_stdout: "0 0"
}
issue_3427_1: {
options = {
sequences: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var a;
a = a || {};
})();
}
expect: {}
}
issue_3427_2: {
options = {
unused: true,
}
input: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect: {
(function() {
var s = "PASS";
console.log(s = s || "FAIL");
})();
}
expect_stdout: "PASS"
}

View File

@@ -1051,3 +1051,107 @@ function_name_mangle_ie8: {
expect_exact: "(function(){console.log(typeof function o(){})})();"
expect_stdout: "function"
}
issue_3420_1: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
console.log(function() {
return function(a, b, c, d) {
return a + b;
};
}().length);
}
expect: {
console.log(function() {
return function(a, b, c, d) {
return a + b;
};
}().length);
}
expect_stdout: "4"
}
issue_3420_2: {
options = {
inline: true,
keep_fargs: "strict",
unused: true,
}
input: {
console.log(function() {
return function(a, b, c, d) {
return a + b;
};
}().length);
}
expect: {
console.log(function(a, b, c, d) {
return a + b;
}.length);
}
expect_stdout: "4"
}
issue_3420_3: {
options = {
inline: true,
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
console.log(function() {
function f(a, b, c, d) {
return a + b;
}
return f;
}().length);
}
expect: {
console.log(function(a, b, c, d) {
return a + b;
}.length);
}
expect_stdout: "4"
}
issue_3423_1: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
function f(g) {
console.log(g.length);
}
f(function(a) {});
}
expect: {
function f(g) {
console.log(g.length);
}
f(function(a) {});
}
expect_stdout: "1"
}
issue_3423_2: {
options = {
keep_fargs: "strict",
unused: true,
}
input: {
new function(a) {
console.log(this.constructor.length);
}();
}
expect: {
new function(a) {
console.log(this.constructor.length);
}();
}
expect_stdout: "1"
}

View File

@@ -1187,3 +1187,23 @@ drop_arguments: {
}
expect_stdout: "PASS"
}
issue_3427: {
options = {
assignments: true,
collapse_vars: true,
inline: true,
pure_getters: "strict",
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a;
(function(b) {
b.p = 42;
})(a || (a = {}));
}
expect: {}
}