fix usage of transform() vs optimize()
Given the current `OPT()` node: - `optimize(compressor)` works on the same level, and will only `descend()` if returned instance is different - `transform(compressor)` works on child nodes, and will always `descend()` Other fixes - `loopcontrol_target()` should compare against `compressor.self()` since the current node may have already been displaced - remove obsolete optimisation in `AST_Binary` after #1477 fixes #1592
This commit is contained in:
parent
919d5e3482
commit
7550c53fd6
219
lib/compress.js
219
lib/compress.js
|
|
@ -384,7 +384,7 @@ merge(Compressor.prototype, {
|
|||
return new ctor(props);
|
||||
};
|
||||
|
||||
function make_node_from_constant(compressor, val, orig) {
|
||||
function make_node_from_constant(val, orig) {
|
||||
switch (typeof val) {
|
||||
case "string":
|
||||
return make_node(AST_String, orig, {
|
||||
|
|
@ -404,9 +404,9 @@ merge(Compressor.prototype, {
|
|||
|
||||
return make_node(AST_Number, orig, { value: val });
|
||||
case "boolean":
|
||||
return make_node(val ? AST_True : AST_False, orig).optimize(compressor);
|
||||
return make_node(val ? AST_True : AST_False, orig);
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, orig).transform(compressor);
|
||||
return make_node(AST_Undefined, orig);
|
||||
default:
|
||||
if (val === null) {
|
||||
return make_node(AST_Null, orig, { value: null });
|
||||
|
|
@ -1217,7 +1217,7 @@ merge(Compressor.prototype, {
|
|||
properties: props
|
||||
});
|
||||
}
|
||||
return make_node_from_constant(compressor, value, orig);
|
||||
return make_node_from_constant(value, orig);
|
||||
}
|
||||
def(AST_Node, noop);
|
||||
def(AST_Dot, function(compressor, suffix){
|
||||
|
|
@ -1243,20 +1243,24 @@ merge(Compressor.prototype, {
|
|||
node.DEFMETHOD("_find_defs", func);
|
||||
});
|
||||
|
||||
function best_of(ast1, ast2) {
|
||||
function best_of_expression(ast1, ast2) {
|
||||
return ast1.print_to_string().length >
|
||||
ast2.print_to_string().length
|
||||
? ast2 : ast1;
|
||||
}
|
||||
|
||||
function best_of_statement(ast1, ast2) {
|
||||
return best_of(make_node(AST_SimpleStatement, ast1, {
|
||||
return best_of_expression(make_node(AST_SimpleStatement, ast1, {
|
||||
body: ast1
|
||||
}), make_node(AST_SimpleStatement, ast2, {
|
||||
body: ast2
|
||||
})).body;
|
||||
}
|
||||
|
||||
function best_of(compressor, ast1, ast2) {
|
||||
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
|
||||
}
|
||||
|
||||
// methods to evaluate a constant expression
|
||||
(function (def){
|
||||
// The evaluate method returns an array with one or two
|
||||
|
|
@ -1277,11 +1281,18 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
var node;
|
||||
try {
|
||||
node = make_node_from_constant(compressor, val, this);
|
||||
node = make_node_from_constant(val, this);
|
||||
} catch(ex) {
|
||||
return [ this ];
|
||||
}
|
||||
return [ best_of(node, this), val ];
|
||||
return [ node, val ];
|
||||
});
|
||||
AST_Node.DEFMETHOD("evaluate_self", function(compressor){
|
||||
var ev = this.evaluate(compressor);
|
||||
if (ev.length > 1 && !ev[0].equivalent_to(this)) {
|
||||
return best_of(compressor, ev[0].optimize(compressor), this);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
var unaryPrefix = makePredicate("! ~ - +");
|
||||
AST_Node.DEFMETHOD("is_constant", function(){
|
||||
|
|
@ -1480,9 +1491,9 @@ merge(Compressor.prototype, {
|
|||
var stat = make_node(AST_SimpleStatement, alt, {
|
||||
body: alt
|
||||
});
|
||||
return best_of(negated, stat) === stat ? alt : negated;
|
||||
return best_of_expression(negated, stat) === stat ? alt : negated;
|
||||
}
|
||||
return best_of(negated, alt);
|
||||
return best_of_expression(negated, alt);
|
||||
}
|
||||
def(AST_Node, function(){
|
||||
return basic_negation(this);
|
||||
|
|
@ -1678,7 +1689,7 @@ merge(Compressor.prototype, {
|
|||
|
||||
OPT(AST_LabeledStatement, function(self, compressor){
|
||||
if (self.body instanceof AST_Break
|
||||
&& compressor.loopcontrol_target(self.body.label) === self.body) {
|
||||
&& compressor.loopcontrol_target(self.body.label) === compressor.self().body) {
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
return self.label.references.length == 0 ? self.body : self;
|
||||
|
|
@ -2242,7 +2253,9 @@ merge(Compressor.prototype, {
|
|||
|
||||
OPT(AST_DWLoop, function(self, compressor){
|
||||
var cond = self.condition.evaluate(compressor);
|
||||
self.condition = cond[0];
|
||||
if (cond.length > 1) {
|
||||
self.condition = best_of_expression(cond[0].transform(compressor), self.condition);
|
||||
}
|
||||
if (!compressor.option("loops")) return self;
|
||||
if (cond.length > 1) {
|
||||
if (cond[1]) {
|
||||
|
|
@ -2261,7 +2274,7 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
}
|
||||
if (self instanceof AST_While) {
|
||||
return make_node(AST_For, self, self).transform(compressor);
|
||||
return make_node(AST_For, self, self).optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
|
@ -2283,7 +2296,7 @@ merge(Compressor.prototype, {
|
|||
var first = self.body instanceof AST_BlockStatement ? self.body.body[0] : self.body;
|
||||
if (first instanceof AST_If) {
|
||||
if (first.body instanceof AST_Break
|
||||
&& compressor.loopcontrol_target(first.body.label) === self) {
|
||||
&& compressor.loopcontrol_target(first.body.label) === compressor.self()) {
|
||||
if (self.condition) {
|
||||
self.condition = make_node(AST_Binary, self.condition, {
|
||||
left: self.condition,
|
||||
|
|
@ -2296,7 +2309,7 @@ merge(Compressor.prototype, {
|
|||
drop_it(first.alternative);
|
||||
}
|
||||
else if (first.alternative instanceof AST_Break
|
||||
&& compressor.loopcontrol_target(first.alternative.label) === self) {
|
||||
&& compressor.loopcontrol_target(first.alternative.label) === compressor.self()) {
|
||||
if (self.condition) {
|
||||
self.condition = make_node(AST_Binary, self.condition, {
|
||||
left: self.condition,
|
||||
|
|
@ -2315,7 +2328,9 @@ merge(Compressor.prototype, {
|
|||
var cond = self.condition;
|
||||
if (cond) {
|
||||
cond = cond.evaluate(compressor);
|
||||
self.condition = cond[0];
|
||||
if (cond.length > 1) {
|
||||
self.condition = best_of_expression(cond[0].transform(compressor), self.condition);
|
||||
}
|
||||
}
|
||||
if (!compressor.option("loops")) return self;
|
||||
if (cond) {
|
||||
|
|
@ -2348,7 +2363,6 @@ merge(Compressor.prototype, {
|
|||
// “has no side effects”; also it doesn't work for cases like
|
||||
// `x && true`, though it probably should.
|
||||
var cond = self.condition.evaluate(compressor);
|
||||
self.condition = cond[0];
|
||||
if (cond.length > 1) {
|
||||
if (cond[1]) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
|
||||
|
|
@ -2358,7 +2372,7 @@ merge(Compressor.prototype, {
|
|||
extract_declarations_from_unreachable_code(compressor, self.alternative, a);
|
||||
}
|
||||
a.push(self.body);
|
||||
return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
|
||||
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
|
||||
}
|
||||
} else {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||
|
|
@ -2366,9 +2380,10 @@ merge(Compressor.prototype, {
|
|||
var a = [];
|
||||
extract_declarations_from_unreachable_code(compressor, self.body, a);
|
||||
if (self.alternative) a.push(self.alternative);
|
||||
return make_node(AST_BlockStatement, self, { body: a }).transform(compressor);
|
||||
return make_node(AST_BlockStatement, self, { body: a }).optimize(compressor);
|
||||
}
|
||||
}
|
||||
self.condition = best_of_expression(cond[0].transform(compressor), self.condition);
|
||||
}
|
||||
var negated = self.condition.negate(compressor);
|
||||
var self_condition_length = self.condition.print_to_string().length;
|
||||
|
|
@ -2386,7 +2401,7 @@ merge(Compressor.prototype, {
|
|||
if (is_empty(self.body) && is_empty(self.alternative)) {
|
||||
return make_node(AST_SimpleStatement, self.condition, {
|
||||
body: self.condition
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_SimpleStatement
|
||||
&& self.alternative instanceof AST_SimpleStatement) {
|
||||
|
|
@ -2396,7 +2411,7 @@ merge(Compressor.prototype, {
|
|||
consequent : statement_to_expression(self.body),
|
||||
alternative : statement_to_expression(self.alternative)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (is_empty(self.alternative) && self.body instanceof AST_SimpleStatement) {
|
||||
if (self_condition_length === negated_length && !negated_is_best
|
||||
|
|
@ -2412,14 +2427,14 @@ merge(Compressor.prototype, {
|
|||
left : negated,
|
||||
right : statement_to_expression(self.body)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
return make_node(AST_SimpleStatement, self, {
|
||||
body: make_node(AST_Binary, self, {
|
||||
operator : "&&",
|
||||
left : self.condition,
|
||||
right : statement_to_expression(self.body)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_EmptyStatement
|
||||
&& self.alternative
|
||||
|
|
@ -2430,7 +2445,7 @@ merge(Compressor.prototype, {
|
|||
left : self.condition,
|
||||
right : statement_to_expression(self.alternative)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_Exit
|
||||
&& self.alternative instanceof AST_Exit
|
||||
|
|
@ -2441,7 +2456,7 @@ merge(Compressor.prototype, {
|
|||
consequent : self.body.value || make_node(AST_Undefined, self.body),
|
||||
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_If
|
||||
&& !self.body.alternative
|
||||
|
|
@ -2459,7 +2474,7 @@ merge(Compressor.prototype, {
|
|||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: [ self, alt ]
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (aborts(self.alternative)) {
|
||||
|
|
@ -2469,7 +2484,7 @@ merge(Compressor.prototype, {
|
|||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: [ self, body ]
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
|
@ -2478,13 +2493,13 @@ merge(Compressor.prototype, {
|
|||
if (self.body.length == 0 && compressor.option("conditionals")) {
|
||||
return make_node(AST_SimpleStatement, self, {
|
||||
body: self.expression
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
for(;;) {
|
||||
var last_branch = self.body[self.body.length - 1];
|
||||
if (last_branch) {
|
||||
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === compressor.self())
|
||||
last_branch.body.pop();
|
||||
if (last_branch instanceof AST_Default && last_branch.body.length == 0) {
|
||||
self.body.pop();
|
||||
|
|
@ -2496,7 +2511,7 @@ merge(Compressor.prototype, {
|
|||
var exp = self.expression.evaluate(compressor);
|
||||
out: if (exp.length == 2) try {
|
||||
// constant expression
|
||||
self.expression = exp[0];
|
||||
self.expression = best_of_expression(exp[0].transform(compressor), self.expression);
|
||||
if (!compressor.option("dead_code")) break out;
|
||||
var value = exp[1];
|
||||
var in_if = false;
|
||||
|
|
@ -2509,14 +2524,14 @@ merge(Compressor.prototype, {
|
|||
// no need to descend these node types
|
||||
return node;
|
||||
}
|
||||
else if (node instanceof AST_Switch && node === self) {
|
||||
else if (node === self) {
|
||||
node = node.clone();
|
||||
descend(node, this);
|
||||
return ruined ? node : make_node(AST_BlockStatement, node, {
|
||||
body: node.body.reduce(function(a, branch){
|
||||
return a.concat(branch.body);
|
||||
}, [])
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
else if (node instanceof AST_If || node instanceof AST_Try) {
|
||||
var save = in_if;
|
||||
|
|
@ -2532,7 +2547,7 @@ merge(Compressor.prototype, {
|
|||
in_block = save;
|
||||
return node;
|
||||
}
|
||||
else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === self) {
|
||||
else if (node instanceof AST_Break && this.loopcontrol_target(node.label) === compressor.self()) {
|
||||
if (in_if) {
|
||||
ruined = true;
|
||||
return node;
|
||||
|
|
@ -2656,7 +2671,7 @@ merge(Compressor.prototype, {
|
|||
if (self.args.length != 1) {
|
||||
return make_node(AST_Array, self, {
|
||||
elements: self.args
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
case "Object":
|
||||
|
|
@ -2674,7 +2689,7 @@ merge(Compressor.prototype, {
|
|||
left: self.args[0],
|
||||
operator: "+",
|
||||
right: make_node(AST_String, self, { value: "" })
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
break;
|
||||
case "Number":
|
||||
if (self.args.length == 0) return make_node(AST_Number, self, {
|
||||
|
|
@ -2683,7 +2698,7 @@ merge(Compressor.prototype, {
|
|||
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||
expression: self.args[0],
|
||||
operator: "+"
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
case "Boolean":
|
||||
if (self.args.length == 0) return make_node(AST_False, self);
|
||||
if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
|
||||
|
|
@ -2692,7 +2707,7 @@ merge(Compressor.prototype, {
|
|||
operator: "!"
|
||||
}),
|
||||
operator: "!"
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
break;
|
||||
case "Function":
|
||||
// new Function() => function(){}
|
||||
|
|
@ -2757,7 +2772,7 @@ merge(Compressor.prototype, {
|
|||
left: make_node(AST_String, self, { value: "" }),
|
||||
operator: "+",
|
||||
right: exp.expression
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
else if (exp instanceof AST_Dot && exp.expression instanceof AST_Array && exp.property == "join") EXIT: {
|
||||
var separator;
|
||||
|
|
@ -2769,9 +2784,9 @@ merge(Compressor.prototype, {
|
|||
var elements = [];
|
||||
var consts = [];
|
||||
exp.expression.elements.forEach(function(el) {
|
||||
el = el.evaluate(compressor);
|
||||
if (el.length > 1) {
|
||||
consts.push(el[1]);
|
||||
var ev = el.evaluate(compressor);
|
||||
if (ev.length > 1) {
|
||||
consts.push(ev[1]);
|
||||
} else {
|
||||
if (consts.length > 0) {
|
||||
elements.push(make_node(AST_String, self, {
|
||||
|
|
@ -2779,7 +2794,7 @@ merge(Compressor.prototype, {
|
|||
}));
|
||||
consts.length = 0;
|
||||
}
|
||||
elements.push(el[0]);
|
||||
elements.push(el);
|
||||
}
|
||||
});
|
||||
if (consts.length > 0) {
|
||||
|
|
@ -2812,15 +2827,15 @@ merge(Compressor.prototype, {
|
|||
left : prev,
|
||||
right : el
|
||||
});
|
||||
}, first).transform(compressor);
|
||||
}, first).optimize(compressor);
|
||||
}
|
||||
// need this awkward cloning to not affect original element
|
||||
// best_of will decide which one to get through.
|
||||
// best_of_expression will decide which one to get through.
|
||||
var node = self.clone();
|
||||
node.expression = node.expression.clone();
|
||||
node.expression.expression = node.expression.expression.clone();
|
||||
node.expression.expression.elements = elements;
|
||||
return best_of(self, node);
|
||||
return best_of_expression(self, node);
|
||||
}
|
||||
}
|
||||
if (exp instanceof AST_Function) {
|
||||
|
|
@ -2828,13 +2843,13 @@ merge(Compressor.prototype, {
|
|||
var value = exp.body[0].value;
|
||||
if (!value || value.is_constant()) {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return AST_Seq.from_array(args).transform(compressor);
|
||||
return AST_Seq.from_array(args).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("side_effects")) {
|
||||
if (!AST_Block.prototype.has_side_effects.call(exp, compressor)) {
|
||||
var args = self.args.concat(make_node(AST_Undefined, self));
|
||||
return AST_Seq.from_array(args).transform(compressor);
|
||||
return AST_Seq.from_array(args).optimize(compressor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2847,7 +2862,7 @@ merge(Compressor.prototype, {
|
|||
if (name instanceof AST_SymbolRef
|
||||
&& name.name == "console"
|
||||
&& name.undeclared()) {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2869,7 +2884,7 @@ merge(Compressor.prototype, {
|
|||
case "Function":
|
||||
case "Error":
|
||||
case "Array":
|
||||
return make_node(AST_Call, self, self).transform(compressor);
|
||||
return make_node(AST_Call, self, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2932,7 +2947,7 @@ merge(Compressor.prototype, {
|
|||
var x = seq.to_array();
|
||||
this.expression = x.pop();
|
||||
x.push(this);
|
||||
seq = AST_Seq.from_array(x).transform(compressor);
|
||||
seq = AST_Seq.from_array(x);
|
||||
return seq;
|
||||
}
|
||||
}
|
||||
|
|
@ -2940,13 +2955,14 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
|
||||
OPT(AST_UnaryPostfix, function(self, compressor){
|
||||
return self.lift_sequences(compressor);
|
||||
var seq = self.lift_sequences(compressor);
|
||||
return seq !== self ? seq.optimize(compressor) : self;
|
||||
});
|
||||
|
||||
OPT(AST_UnaryPrefix, function(self, compressor){
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
return seq.optimize(compressor);
|
||||
}
|
||||
var e = self.expression;
|
||||
if (compressor.option("side_effects") && self.operator == "void") {
|
||||
|
|
@ -2955,7 +2971,7 @@ merge(Compressor.prototype, {
|
|||
self.expression = e;
|
||||
return self;
|
||||
} else {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
|
|
@ -2966,8 +2982,7 @@ merge(Compressor.prototype, {
|
|||
return e.expression;
|
||||
}
|
||||
if (e instanceof AST_Binary) {
|
||||
var statement = first_in_statement(compressor);
|
||||
self = (statement ? best_of_statement : best_of)(self, e.negate(compressor, statement));
|
||||
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
|
||||
}
|
||||
break;
|
||||
case "typeof":
|
||||
|
|
@ -2980,7 +2995,7 @@ merge(Compressor.prototype, {
|
|||
}).optimize(compressor);
|
||||
}
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
return self.evaluate_self(compressor);
|
||||
});
|
||||
|
||||
function has_side_effects_or_prop_access(node, compressor) {
|
||||
|
|
@ -2998,7 +3013,7 @@ merge(Compressor.prototype, {
|
|||
var x = seq.to_array();
|
||||
this.left = x.pop();
|
||||
x.push(this);
|
||||
seq = AST_Seq.from_array(x).transform(compressor);
|
||||
seq = AST_Seq.from_array(x);
|
||||
return seq;
|
||||
}
|
||||
if (this.right instanceof AST_Seq
|
||||
|
|
@ -3008,7 +3023,7 @@ merge(Compressor.prototype, {
|
|||
var x = seq.to_array();
|
||||
this.right = x.pop();
|
||||
x.push(this);
|
||||
seq = AST_Seq.from_array(x).transform(compressor);
|
||||
seq = AST_Seq.from_array(x);
|
||||
return seq;
|
||||
}
|
||||
}
|
||||
|
|
@ -3018,16 +3033,6 @@ merge(Compressor.prototype, {
|
|||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||
|
||||
OPT(AST_Binary, function(self, compressor){
|
||||
var lhs = self.left.evaluate(compressor);
|
||||
var rhs = self.right.evaluate(compressor);
|
||||
if (lhs.length > 1 && lhs[0].is_constant() !== self.left.is_constant()
|
||||
|| rhs.length > 1 && rhs[0].is_constant() !== self.right.is_constant()) {
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: self.operator,
|
||||
left: lhs[0],
|
||||
right: rhs[0]
|
||||
}).optimize(compressor);
|
||||
}
|
||||
function reversible() {
|
||||
return self.left instanceof AST_Constant
|
||||
|| self.right instanceof AST_Constant
|
||||
|
|
@ -3081,7 +3086,10 @@ merge(Compressor.prototype, {
|
|||
}
|
||||
}
|
||||
}
|
||||
self = self.lift_sequences(compressor);
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq.optimize(compressor);
|
||||
}
|
||||
if (compressor.option("comparisons")) switch (self.operator) {
|
||||
case "===":
|
||||
case "!==":
|
||||
|
|
@ -3102,7 +3110,7 @@ merge(Compressor.prototype, {
|
|||
if (expr instanceof AST_SymbolRef ? !expr.undeclared()
|
||||
: !(expr instanceof AST_PropAccess) || compressor.option("screw_ie8")) {
|
||||
self.right = expr;
|
||||
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
||||
self.left = make_node(AST_Undefined, self.left).transform(compressor);
|
||||
if (self.operator.length == 2) self.operator += "=";
|
||||
}
|
||||
}
|
||||
|
|
@ -3115,15 +3123,15 @@ merge(Compressor.prototype, {
|
|||
if ((ll.length > 1 && !ll[1]) || (rr.length > 1 && !rr[1])) {
|
||||
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.left,
|
||||
cdr: make_node(AST_False, self)
|
||||
car: self.left.transform(compressor),
|
||||
cdr: make_node(AST_False, self).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (ll.length > 1 && ll[1]) {
|
||||
return rr[0];
|
||||
return best_of(compressor, rr[0].optimize(compressor), self.right);
|
||||
}
|
||||
if (rr.length > 1 && rr[1]) {
|
||||
return ll[0];
|
||||
return best_of(compressor, ll[0].optimize(compressor), self.left);
|
||||
}
|
||||
break;
|
||||
case "||":
|
||||
|
|
@ -3132,15 +3140,15 @@ merge(Compressor.prototype, {
|
|||
if ((ll.length > 1 && ll[1]) || (rr.length > 1 && rr[1])) {
|
||||
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.left,
|
||||
cdr: make_node(AST_True, self)
|
||||
car: self.left.transform(compressor),
|
||||
cdr: make_node(AST_True, self).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (ll.length > 1 && !ll[1]) {
|
||||
return rr[0];
|
||||
return best_of(compressor, rr[0].optimize(compressor), self.right);
|
||||
}
|
||||
if (rr.length > 1 && !rr[1]) {
|
||||
return ll[0];
|
||||
return best_of(compressor, ll[0].optimize(compressor), self.left);
|
||||
}
|
||||
break;
|
||||
case "+":
|
||||
|
|
@ -3149,15 +3157,15 @@ merge(Compressor.prototype, {
|
|||
if (ll.length > 1 && ll[0] instanceof AST_String && ll[1]) {
|
||||
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||
return make_node(AST_Seq, self, {
|
||||
car: self.right,
|
||||
cdr: make_node(AST_True, self)
|
||||
car: self.right.transform(compressor),
|
||||
cdr: make_node(AST_True, self).transform(compressor)
|
||||
}).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)
|
||||
car: self.left.transform(compressor),
|
||||
cdr: make_node(AST_True, self).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
|
|
@ -3165,12 +3173,11 @@ merge(Compressor.prototype, {
|
|||
if (compressor.option("comparisons") && self.is_boolean()) {
|
||||
if (!(compressor.parent() instanceof AST_Binary)
|
||||
|| compressor.parent() instanceof AST_Assign) {
|
||||
var statement = first_in_statement(compressor);
|
||||
var negated = make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: self.negate(compressor, statement)
|
||||
expression: self.negate(compressor, first_in_statement(compressor))
|
||||
});
|
||||
self = (statement ? best_of_statement : best_of)(self, negated);
|
||||
self = best_of(compressor, self, negated);
|
||||
}
|
||||
if (compressor.option("unsafe_comps")) {
|
||||
switch (self.operator) {
|
||||
|
|
@ -3196,7 +3203,7 @@ merge(Compressor.prototype, {
|
|||
&& self.left.left.getValue() == ""
|
||||
&& self.right.is_string(compressor)) {
|
||||
self.left = self.left.right;
|
||||
return self.transform(compressor);
|
||||
return self.optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate")) {
|
||||
|
|
@ -3322,9 +3329,9 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
if (self.right instanceof AST_Constant
|
||||
&& !(self.left instanceof AST_Constant)) {
|
||||
self = best_of(reversed, self);
|
||||
self = best_of(compressor, reversed, self);
|
||||
} else {
|
||||
self = best_of(self, reversed);
|
||||
self = best_of(compressor, self, reversed);
|
||||
}
|
||||
}
|
||||
if (associative && self.is_number(compressor)) {
|
||||
|
|
@ -3417,11 +3424,10 @@ merge(Compressor.prototype, {
|
|||
operator : self.operator,
|
||||
left : self.left,
|
||||
right : self.right.left
|
||||
});
|
||||
self.right = self.right.right;
|
||||
return self.transform(compressor);
|
||||
}).transform(compressor);
|
||||
self.right = self.right.right.transform(compressor);
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
return self.evaluate_self(compressor);
|
||||
});
|
||||
|
||||
OPT(AST_SymbolRef, function(self, compressor){
|
||||
|
|
@ -3436,11 +3442,11 @@ merge(Compressor.prototype, {
|
|||
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
|
||||
switch (self.name) {
|
||||
case "undefined":
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
return make_node(AST_Undefined, self).optimize(compressor);
|
||||
case "NaN":
|
||||
return make_node(AST_NaN, self).transform(compressor);
|
||||
return make_node(AST_NaN, self).optimize(compressor);
|
||||
case "Infinity":
|
||||
return make_node(AST_Infinity, self).transform(compressor);
|
||||
return make_node(AST_Infinity, self).optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
||||
|
|
@ -3449,11 +3455,12 @@ merge(Compressor.prototype, {
|
|||
if (d.should_replace === undefined) {
|
||||
var init = d.fixed.evaluate(compressor);
|
||||
if (init.length > 1) {
|
||||
var value = init[0].print_to_string().length;
|
||||
init = best_of(compressor, init[0].optimize(compressor), d.fixed);
|
||||
var value = init.print_to_string().length;
|
||||
var name = d.name.length;
|
||||
var freq = d.references.length;
|
||||
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
|
||||
d.should_replace = value <= name + overhead ? init[0] : false;
|
||||
d.should_replace = value <= name + overhead ? init : false;
|
||||
} else {
|
||||
d.should_replace = false;
|
||||
}
|
||||
|
|
@ -3494,7 +3501,10 @@ merge(Compressor.prototype, {
|
|||
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
|
||||
var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ];
|
||||
OPT(AST_Assign, function(self, compressor){
|
||||
self = self.lift_sequences(compressor);
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq.optimize(compressor);
|
||||
}
|
||||
if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
|
||||
// x = expr1 OP expr2
|
||||
if (self.right.left instanceof AST_SymbolRef
|
||||
|
|
@ -3532,10 +3542,10 @@ merge(Compressor.prototype, {
|
|||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), self, self.alternative);
|
||||
}
|
||||
self.condition = best_of(compressor, cond[0].transform(compressor), self.condition);
|
||||
}
|
||||
var statement = first_in_statement(compressor);
|
||||
var negated = cond[0].negate(compressor, statement);
|
||||
if ((statement ? best_of_statement : best_of)(cond[0], negated) === negated) {
|
||||
var negated = self.condition.negate(compressor, first_in_statement(compressor));
|
||||
if (best_of(compressor, self.condition, negated) === negated) {
|
||||
self = make_node(AST_Conditional, self, {
|
||||
condition: negated,
|
||||
consequent: self.alternative,
|
||||
|
|
@ -3715,7 +3725,7 @@ merge(Compressor.prototype, {
|
|||
});
|
||||
}
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
return self.evaluate_self(compressor);
|
||||
});
|
||||
|
||||
OPT(AST_Dot, function(self, compressor){
|
||||
|
|
@ -3754,13 +3764,12 @@ merge(Compressor.prototype, {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return self.evaluate(compressor)[0];
|
||||
return self.evaluate_self(compressor);
|
||||
});
|
||||
|
||||
function literals_in_boolean_context(self, compressor) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
var best = first_in_statement(compressor) ? best_of_statement : best_of;
|
||||
return best(self, make_node(AST_Seq, self, {
|
||||
return best_of(compressor, self, make_node(AST_Seq, self, {
|
||||
car: self,
|
||||
cdr: make_node(AST_True, self)
|
||||
}).optimize(compressor));
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ iifes_returning_constants_keep_fargs_true: {
|
|||
console.log(void 0);
|
||||
console.log(2);
|
||||
console.log(6);
|
||||
console.log((a(), b(), 6));
|
||||
console.log((0, a(), b(), 6));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
68
test/compress/transform-optimize.js
Normal file
68
test/compress/transform-optimize.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
booleans_evaluate: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof void 0 != "undefined");
|
||||
console.log(1 == 1, 1 === 1)
|
||||
console.log(1 != 1, 1 !== 1)
|
||||
}
|
||||
expect: {
|
||||
console.log(!1);
|
||||
console.log(!0, !0);
|
||||
console.log(!1, !1);
|
||||
}
|
||||
}
|
||||
|
||||
booleans_global_defs: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
global_defs: {
|
||||
A: true,
|
||||
},
|
||||
}
|
||||
input: {
|
||||
console.log(A == 1);
|
||||
}
|
||||
expect: {
|
||||
console.log(!0);
|
||||
}
|
||||
}
|
||||
|
||||
condition_evaluate: {
|
||||
options = {
|
||||
booleans: true,
|
||||
dead_code: false,
|
||||
evaluate: true,
|
||||
loops: false,
|
||||
}
|
||||
input: {
|
||||
while (1 === 2);
|
||||
for (; 1 == true;);
|
||||
if (void 0 == null);
|
||||
}
|
||||
expect: {
|
||||
while (!1);
|
||||
for (; !0;);
|
||||
if (!0);
|
||||
}
|
||||
}
|
||||
|
||||
while_if_break: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
loops: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
while (a) {
|
||||
if (b) if(c) d;
|
||||
if (e) break;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
for(; a && (b && c && d, !e););
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user