fix incorrect context in variable substitution (#1791)
`AST_Node.optimize()` is context-aware, so don't cache its results to be used elsewhere. Also fixed a few cases of AST corruption and beef up safety of `pure_getters`.
This commit is contained in:
@@ -283,10 +283,16 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_VarDef) {
|
||||
var d = node.name.definition();
|
||||
if (d.fixed == null) {
|
||||
d.fixed = node.value && function() {
|
||||
return node.value;
|
||||
};
|
||||
if (node.value) {
|
||||
d.fixed = function() {
|
||||
return node.value;
|
||||
};
|
||||
descend();
|
||||
} else {
|
||||
d.fixed = null;
|
||||
}
|
||||
mark_as_safe(d);
|
||||
return true;
|
||||
} else if (node.value) {
|
||||
d.fixed = false;
|
||||
}
|
||||
@@ -1160,13 +1166,42 @@ merge(Compressor.prototype, {
|
||||
&& !node.expression.has_side_effects(compressor);
|
||||
}
|
||||
|
||||
// may_eq_null()
|
||||
// returns true if this node may evaluate to null or undefined
|
||||
(function(def) {
|
||||
def(AST_Node, return_false);
|
||||
def(AST_Node, return_true);
|
||||
def(AST_Null, return_true);
|
||||
def(AST_Undefined, return_true);
|
||||
def(AST_Constant, return_false);
|
||||
def(AST_Array, return_false);
|
||||
def(AST_Object, return_false);
|
||||
def(AST_Function, return_false);
|
||||
def(AST_UnaryPostfix, return_false);
|
||||
def(AST_UnaryPrefix, function() {
|
||||
return this.operator == "void";
|
||||
});
|
||||
def(AST_Binary, function(compressor) {
|
||||
switch (this.operator) {
|
||||
case "&&":
|
||||
return this.left.may_eq_null(compressor);
|
||||
case "||":
|
||||
return this.left.may_eq_null(compressor)
|
||||
&& this.right.may_eq_null(compressor);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
})
|
||||
def(AST_Assign, function(compressor) {
|
||||
return this.operator == "="
|
||||
&& this.right.may_eq_null(compressor);
|
||||
})
|
||||
def(AST_Conditional, function(compressor) {
|
||||
return this.consequent.may_eq_null(compressor)
|
||||
|| this.alternative.may_eq_null(compressor);
|
||||
})
|
||||
def(AST_Seq, function(compressor) {
|
||||
return this.cdr.may_eq_null(compressor);
|
||||
});
|
||||
def(AST_PropAccess, function(compressor) {
|
||||
return !compressor.option("unsafe");
|
||||
});
|
||||
@@ -3055,8 +3090,9 @@ merge(Compressor.prototype, {
|
||||
if (this.expression instanceof AST_Seq) {
|
||||
var seq = this.expression;
|
||||
var x = seq.to_array();
|
||||
this.expression = x.pop();
|
||||
x.push(this);
|
||||
var e = this.clone();
|
||||
e.expression = x.pop();
|
||||
x.push(e);
|
||||
seq = AST_Seq.from_array(x).transform(compressor);
|
||||
return seq;
|
||||
}
|
||||
@@ -3110,9 +3146,14 @@ merge(Compressor.prototype, {
|
||||
if (e instanceof AST_Binary
|
||||
&& (self.operator == "+" || self.operator == "-")
|
||||
&& (e.operator == "*" || e.operator == "/" || e.operator == "%")) {
|
||||
self.expression = e.left;
|
||||
e.left = self;
|
||||
return e;
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: e.operator,
|
||||
left: make_node(AST_UnaryPrefix, e.left, {
|
||||
operator: self.operator,
|
||||
expression: e.left
|
||||
}),
|
||||
right: e.right
|
||||
});
|
||||
}
|
||||
// avoids infinite recursion of numerals
|
||||
if (self.operator != "-"
|
||||
@@ -3131,23 +3172,25 @@ merge(Compressor.prototype, {
|
||||
if (this.left instanceof AST_Seq) {
|
||||
var seq = this.left;
|
||||
var x = seq.to_array();
|
||||
this.left = x.pop();
|
||||
x.push(this);
|
||||
var e = this.clone();
|
||||
e.left = x.pop();
|
||||
x.push(e);
|
||||
return AST_Seq.from_array(x).optimize(compressor);
|
||||
}
|
||||
if (this.right instanceof AST_Seq && !this.left.has_side_effects(compressor)) {
|
||||
var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
|
||||
var root = this.right;
|
||||
var root = this.right.clone();
|
||||
var cursor, seq = root;
|
||||
while (assign || !seq.car.has_side_effects(compressor)) {
|
||||
cursor = seq;
|
||||
if (seq.cdr instanceof AST_Seq) {
|
||||
seq = seq.cdr;
|
||||
seq = seq.cdr = seq.cdr.clone();
|
||||
} else break;
|
||||
}
|
||||
if (cursor) {
|
||||
this.right = cursor.cdr;
|
||||
cursor.cdr = this;
|
||||
var e = this.clone();
|
||||
e.right = cursor.cdr;
|
||||
cursor.cdr = e;
|
||||
return root.optimize(compressor);
|
||||
}
|
||||
}
|
||||
@@ -3547,9 +3590,8 @@ merge(Compressor.prototype, {
|
||||
if (d.should_replace === undefined) {
|
||||
var init = fixed.evaluate(compressor);
|
||||
if (init !== fixed) {
|
||||
init = make_node_from_constant(init, fixed).optimize(compressor);
|
||||
init = best_of_expression(init, fixed);
|
||||
var value = init.print_to_string().length;
|
||||
init = make_node_from_constant(init, fixed);
|
||||
var value = best_of_expression(init.optimize(compressor), fixed).print_to_string().length;
|
||||
var name = d.name.length;
|
||||
var freq = d.references.length;
|
||||
var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq;
|
||||
@@ -3559,7 +3601,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
if (d.should_replace) {
|
||||
return d.should_replace.clone(true);
|
||||
return best_of_expression(d.should_replace.optimize(compressor), fixed).clone(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1866,3 +1866,75 @@ delay_def: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
booleans: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function(a) {
|
||||
if (a != 0);
|
||||
switch (a) {
|
||||
case 0:
|
||||
return "FAIL";
|
||||
case false:
|
||||
return "PASS";
|
||||
}
|
||||
}(false));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(a) {
|
||||
if (!1);
|
||||
switch (!1) {
|
||||
case 0:
|
||||
return "FAIL";
|
||||
case !1:
|
||||
return "PASS";
|
||||
}
|
||||
}(!1));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
side_effects_assign: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var a = typeof void (a && a.in == 1, 0);
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = typeof void (a && a.in);
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
pure_getters: {
|
||||
options = {
|
||||
pure_getters: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
var a = (a.b, 2);
|
||||
} catch (e) {}
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
try {
|
||||
var a = (a.b, 2);
|
||||
} catch (e) {}
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user