fix AST_Node.optimize() (#1602)
Liberal use of `Compressor.transform()` and `AST_Node.optimize()` presents an issue for look-up operations like `TreeWalker.in_boolean_context()` and `TreeWalker.parent()`. This is an incremental fix such that `AST_Node.optimize()` would now contain the correct stack information when called correctly.
This commit is contained in:
@@ -152,13 +152,14 @@ merge(Compressor.prototype, {
|
||||
was_scope = true;
|
||||
}
|
||||
descend(node, this);
|
||||
node = node.optimize(this);
|
||||
if (was_scope && node instanceof AST_Scope) {
|
||||
node.drop_unused(this);
|
||||
descend(node, this);
|
||||
descend(node, this);
|
||||
var opt = node.optimize(this);
|
||||
if (was_scope && opt instanceof AST_Scope) {
|
||||
opt.drop_unused(this);
|
||||
descend(opt, this);
|
||||
}
|
||||
node._squeezed = true;
|
||||
return node;
|
||||
if (opt === node) opt._squeezed = true;
|
||||
return opt;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -171,8 +172,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.has_directive("use asm")) return self;
|
||||
var opt = optimizer(self, compressor);
|
||||
opt._optimized = true;
|
||||
if (opt === self) return opt;
|
||||
return opt.transform(compressor);
|
||||
return opt;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -914,7 +914,7 @@ merge(Compressor.prototype, {
|
||||
if (stat instanceof AST_LoopControl) {
|
||||
var lct = compressor.loopcontrol_target(stat.label);
|
||||
if ((stat instanceof AST_Break
|
||||
&& lct instanceof AST_BlockStatement
|
||||
&& !(lct instanceof AST_IterationStatement)
|
||||
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||
&& loop_body(lct) === self)) {
|
||||
if (stat.label) {
|
||||
@@ -1646,8 +1646,8 @@ merge(Compressor.prototype, {
|
||||
return thing && thing.aborts();
|
||||
};
|
||||
(function(def){
|
||||
def(AST_Statement, function(){ return null });
|
||||
def(AST_Jump, function(){ return this });
|
||||
def(AST_Statement, return_null);
|
||||
def(AST_Jump, return_this);
|
||||
function block_aborts(){
|
||||
var n = this.body.length;
|
||||
return n > 0 && aborts(this.body[n - 1]);
|
||||
@@ -2077,14 +2077,6 @@ merge(Compressor.prototype, {
|
||||
// drop_side_effect_free()
|
||||
// remove side-effect-free parts which only affects return value
|
||||
(function(def){
|
||||
function return_this() {
|
||||
return this;
|
||||
}
|
||||
|
||||
function return_null() {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Drop side-effect-free elements from an array of expressions.
|
||||
// Returns an array of expressions with side-effects or null
|
||||
// if all elements were dropped. Note: original array may be
|
||||
@@ -2358,7 +2350,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,7 +2358,7 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2385,8 +2377,8 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (is_empty(self.body) && is_empty(self.alternative)) {
|
||||
return make_node(AST_SimpleStatement, self.condition, {
|
||||
body: self.condition
|
||||
}).transform(compressor);
|
||||
body: self.condition.clone()
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_SimpleStatement
|
||||
&& self.alternative instanceof AST_SimpleStatement) {
|
||||
@@ -2396,7 +2388,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 +2404,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 +2422,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
|
||||
@@ -2440,18 +2432,21 @@ merge(Compressor.prototype, {
|
||||
condition : self.condition,
|
||||
consequent : self.body.value || make_node(AST_Undefined, self.body),
|
||||
alternative : self.alternative.value || make_node(AST_Undefined, self.alternative)
|
||||
})
|
||||
}).transform(compressor);
|
||||
}).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_If
|
||||
&& !self.body.alternative
|
||||
&& !self.alternative) {
|
||||
self.condition = make_node(AST_Binary, self.condition, {
|
||||
operator: "&&",
|
||||
left: self.condition,
|
||||
right: self.body.condition
|
||||
}).transform(compressor);
|
||||
self.body = self.body.body;
|
||||
self = make_node(AST_If, self, {
|
||||
condition: make_node(AST_Binary, self.condition, {
|
||||
operator: "&&",
|
||||
left: self.condition,
|
||||
right: self.body.condition
|
||||
}),
|
||||
body: self.body.body,
|
||||
alternative: null
|
||||
});
|
||||
}
|
||||
if (aborts(self.body)) {
|
||||
if (self.alternative) {
|
||||
@@ -2459,7 +2454,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 +2464,7 @@ merge(Compressor.prototype, {
|
||||
self.alternative = null;
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: [ self, body ]
|
||||
}).transform(compressor);
|
||||
}).optimize(compressor);
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
@@ -126,9 +126,11 @@ function merge(obj, ext) {
|
||||
return count;
|
||||
};
|
||||
|
||||
function noop() {};
|
||||
function noop() {}
|
||||
function return_false() { return false; }
|
||||
function return_true() { return true; }
|
||||
function return_this() { return this; }
|
||||
function return_null() { return null; }
|
||||
|
||||
var MAP = (function(){
|
||||
function MAP(a, f, backwards) {
|
||||
|
||||
129
test/compress/transform.js
Normal file
129
test/compress/transform.js
Normal file
@@ -0,0 +1,129 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if_else_empty: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
if ({} ? a : b); else {}
|
||||
}
|
||||
expect: {
|
||||
!{} ? b : a;
|
||||
}
|
||||
}
|
||||
|
||||
label_if_break: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
dead_code: true,
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
L: if (true) {
|
||||
a;
|
||||
break L;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
a;
|
||||
}
|
||||
}
|
||||
|
||||
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););
|
||||
}
|
||||
}
|
||||
|
||||
if_return: {
|
||||
options = {
|
||||
booleans: true,
|
||||
conditionals: true,
|
||||
if_return: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f(w, x, y, z) {
|
||||
if (x) return;
|
||||
if (w) {
|
||||
if (y) return;
|
||||
} else if (z) return;
|
||||
if (x == y) return true;
|
||||
|
||||
if (x) w();
|
||||
if (y) z();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function f(w, x, y, z) {
|
||||
if (!x) {
|
||||
if (w) {
|
||||
if (y) return;
|
||||
} else if (z) return;
|
||||
return x == y || (x && w(), y && z(), !0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user