fix corner cases in reduce_vars (#1524)
Avoid variable substitution in the following cases: - use of variable before declaration - declaration within conditional code blocks - declaration within loop body fixes #1518 fixes #1525
This commit is contained in:
107
lib/compress.js
107
lib/compress.js
@@ -179,38 +179,105 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
|
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){
|
||||||
var reduce_vars = rescan && compressor.option("reduce_vars");
|
var reduce_vars = rescan && compressor.option("reduce_vars");
|
||||||
var unsafe = compressor.option("unsafe");
|
var safe_ids = [];
|
||||||
|
push();
|
||||||
var tw = new TreeWalker(function(node){
|
var tw = new TreeWalker(function(node){
|
||||||
|
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
|
||||||
|
node._squeezed = false;
|
||||||
|
node._optimized = false;
|
||||||
|
}
|
||||||
if (reduce_vars) {
|
if (reduce_vars) {
|
||||||
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
|
if (node instanceof AST_Toplevel) node.globals.each(reset_def);
|
||||||
if (node instanceof AST_Scope) node.variables.each(reset_def);
|
if (node instanceof AST_Scope) node.variables.each(reset_def);
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
var d = node.definition();
|
var d = node.definition();
|
||||||
d.references.push(node);
|
d.references.push(node);
|
||||||
if (d.fixed && (d.orig.length > 1 || isModified(node, 0))) {
|
if (!d.fixed || isModified(node, 0) || !is_safe(d)) {
|
||||||
d.fixed = false;
|
d.fixed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Call && node.expression instanceof AST_Function) {
|
if (node instanceof AST_VarDef) {
|
||||||
node.expression.argnames.forEach(function(arg, i) {
|
var d = node.name.definition();
|
||||||
arg.definition().init = node.args[i] || make_node(AST_Undefined, node);
|
if (d.fixed === undefined) {
|
||||||
|
d.fixed = node.value || make_node(AST_Undefined, node);
|
||||||
|
mark_as_safe(d);
|
||||||
|
} else {
|
||||||
|
d.fixed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var iife;
|
||||||
|
if (node instanceof AST_Function
|
||||||
|
&& (iife = tw.parent()) instanceof AST_Call
|
||||||
|
&& iife.expression === node) {
|
||||||
|
node.argnames.forEach(function(arg, i) {
|
||||||
|
var d = arg.definition();
|
||||||
|
d.fixed = iife.args[i] || make_node(AST_Undefined, iife);
|
||||||
|
mark_as_safe(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
if (node instanceof AST_If || node instanceof AST_DWLoop) {
|
||||||
if (!(node instanceof AST_Directive || node instanceof AST_Constant)) {
|
node.condition.walk(tw);
|
||||||
node._squeezed = false;
|
push();
|
||||||
node._optimized = false;
|
node.body.walk(tw);
|
||||||
|
pop();
|
||||||
|
if (node.alternative) {
|
||||||
|
push();
|
||||||
|
node.alternative.walk(tw);
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_LabeledStatement) {
|
||||||
|
push();
|
||||||
|
node.body.walk(tw);
|
||||||
|
pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_For) {
|
||||||
|
if (node.init) node.init.walk(tw);
|
||||||
|
push();
|
||||||
|
if (node.condition) node.condition.walk(tw);
|
||||||
|
node.body.walk(tw);
|
||||||
|
if (node.step) node.step.walk(tw);
|
||||||
|
pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_ForIn) {
|
||||||
|
if (node.init instanceof AST_SymbolRef) {
|
||||||
|
node.init.definition().fixed = false;
|
||||||
|
}
|
||||||
|
node.object.walk(tw);
|
||||||
|
push();
|
||||||
|
node.body.walk(tw);
|
||||||
|
pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.walk(tw);
|
this.walk(tw);
|
||||||
|
|
||||||
|
function mark_as_safe(def) {
|
||||||
|
safe_ids[safe_ids.length - 1][def.id] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_safe(def) {
|
||||||
|
for (var i = safe_ids.length, id = def.id; --i >= 0;) {
|
||||||
|
if (safe_ids[i][id]) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function push() {
|
||||||
|
safe_ids.push(Object.create(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
function pop() {
|
||||||
|
safe_ids.pop();
|
||||||
|
}
|
||||||
|
|
||||||
function reset_def(def) {
|
function reset_def(def) {
|
||||||
def.fixed = true;
|
def.fixed = undefined;
|
||||||
def.references = [];
|
def.references = [];
|
||||||
def.should_replace = undefined;
|
def.should_replace = undefined;
|
||||||
if (unsafe && def.init) {
|
|
||||||
def.init._evaluated = undefined;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isModified(node, level) {
|
function isModified(node, level) {
|
||||||
@@ -1263,14 +1330,14 @@ merge(Compressor.prototype, {
|
|||||||
this._evaluating = true;
|
this._evaluating = true;
|
||||||
try {
|
try {
|
||||||
var d = this.definition();
|
var d = this.definition();
|
||||||
if (compressor.option("reduce_vars") && d.fixed && d.init) {
|
if (compressor.option("reduce_vars") && d.fixed) {
|
||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
if (d.init._evaluated === undefined) {
|
if (!HOP(d.fixed, "_evaluated")) {
|
||||||
d.init._evaluated = ev(d.init, compressor);
|
d.fixed._evaluated = ev(d.fixed, compressor);
|
||||||
}
|
}
|
||||||
return d.init._evaluated;
|
return d.fixed._evaluated;
|
||||||
}
|
}
|
||||||
return ev(d.init, compressor);
|
return ev(d.fixed, compressor);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._evaluating = false;
|
this._evaluating = false;
|
||||||
@@ -3046,9 +3113,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
if (compressor.option("evaluate") && compressor.option("reduce_vars")) {
|
||||||
var d = self.definition();
|
var d = self.definition();
|
||||||
if (d.fixed && d.init) {
|
if (d.fixed) {
|
||||||
if (d.should_replace === undefined) {
|
if (d.should_replace === undefined) {
|
||||||
var init = d.init.evaluate(compressor);
|
var init = d.fixed.evaluate(compressor);
|
||||||
if (init.length > 1) {
|
if (init.length > 1) {
|
||||||
var value = init[0].print_to_string().length;
|
var value = init[0].print_to_string().length;
|
||||||
var name = d.name.length;
|
var name = d.name.length;
|
||||||
|
|||||||
@@ -470,3 +470,122 @@ multi_def_2: {
|
|||||||
var repeatLength = this.getBits(bitsLength) + bitsOffset;
|
var repeatLength = this.getBits(bitsLength) + bitsOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use_before_var: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(t);
|
||||||
|
var t = 1;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(t);
|
||||||
|
var t = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_var_if: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (f())
|
||||||
|
var t = 1;
|
||||||
|
if (!t)
|
||||||
|
console.log(t);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (f())
|
||||||
|
var t = 1;
|
||||||
|
if (!t)
|
||||||
|
console.log(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_var_label: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
l: {
|
||||||
|
if (f()) break l;
|
||||||
|
var t = 1;
|
||||||
|
}
|
||||||
|
console.log(t);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
l: {
|
||||||
|
if (f()) break l;
|
||||||
|
var t = 1;
|
||||||
|
}
|
||||||
|
console.log(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_var_for: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
x(a, b, d);
|
||||||
|
for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) {
|
||||||
|
var d = 4, e = 5;
|
||||||
|
x(a, b, c, d, e);
|
||||||
|
}
|
||||||
|
x(a, b, c, d, e)
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
x(1, b, d);
|
||||||
|
for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) {
|
||||||
|
var d = 4, e = 5;
|
||||||
|
x(1, b, 3, d, e);
|
||||||
|
}
|
||||||
|
x(1, b, 3, d, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_var_for_in: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1, b = 2;
|
||||||
|
for (b in (function() {
|
||||||
|
return x(a, b, c);
|
||||||
|
})()) {
|
||||||
|
var c = 3, d = 4;
|
||||||
|
x(a, b, c, d);
|
||||||
|
}
|
||||||
|
x(a, b, c, d);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1, b = 2;
|
||||||
|
for (b in (function() {
|
||||||
|
return x(1, b, c);
|
||||||
|
})()) {
|
||||||
|
var c = 3, d = 4;
|
||||||
|
x(1, b, c, d);
|
||||||
|
}
|
||||||
|
x(1, b, c, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user