improve collapse_vars on AST_Var (#1828)
Perform the same cascaded scanning within `var` statement as we do on array of statements.
This commit is contained in:
@@ -644,21 +644,21 @@ merge(Compressor.prototype, {
|
||||
|
||||
var scope = compressor.find_parent(AST_Scope);
|
||||
var toplevel = compressor.option("toplevel");
|
||||
for (var stat_index = statements.length; --stat_index >= 0;) {
|
||||
var stat = statements[stat_index];
|
||||
|
||||
var var_names_seen = Object.create(null);
|
||||
var side_effects_encountered = false;
|
||||
var lvalues_encountered = false;
|
||||
var lvalues = Object.create(null);
|
||||
var prev_stat_index, var_defs, var_defs_index;
|
||||
|
||||
var stat_index;
|
||||
var prev_stat_index;
|
||||
var def_stat_index;
|
||||
var stat;
|
||||
var var_defs;
|
||||
var var_defs_index;
|
||||
for (stat_index = statements.length; --stat_index >= 0;) {
|
||||
stat = statements[stat_index];
|
||||
// Scan variable definitions from right to left.
|
||||
if (stat instanceof AST_Definitions) {
|
||||
prev_stat_index = stat_index;
|
||||
var_defs = stat.definitions;
|
||||
for (var_defs_index = var_defs.length - 1; --var_defs_index >= 0;) {
|
||||
if (collapse(var_defs[var_defs_index + 1])) break;
|
||||
for (def_stat_index = var_defs.length; --def_stat_index >= 1;) {
|
||||
stat = var_defs[def_stat_index];
|
||||
scan_var_defs(def_stat_index);
|
||||
}
|
||||
} else if (stat_index > 0) {
|
||||
// The variable definition must precede a statement.
|
||||
@@ -666,48 +666,51 @@ merge(Compressor.prototype, {
|
||||
var prev_stat = statements[prev_stat_index];
|
||||
if (!(prev_stat instanceof AST_Definitions)) continue;
|
||||
var_defs = prev_stat.definitions;
|
||||
for (var_defs_index = var_defs.length; --var_defs_index >= 0;) {
|
||||
if (collapse(stat)) break;
|
||||
}
|
||||
scan_var_defs(var_defs.length);
|
||||
}
|
||||
}
|
||||
|
||||
return statements;
|
||||
|
||||
function collapse(stat) {
|
||||
function scan_var_defs(end_pos) {
|
||||
var var_names_seen = Object.create(null);
|
||||
var side_effects_encountered = false;
|
||||
var lvalues_encountered = false;
|
||||
var lvalues = Object.create(null);
|
||||
for (var_defs_index = end_pos; --var_defs_index >= 0;) {
|
||||
var var_decl = var_defs[var_defs_index];
|
||||
// `drop_unused()` shuffles variables without values to the top,
|
||||
// so we can terminate upon first sighting as an optimization.
|
||||
if (var_decl.value == null) return true;
|
||||
if (var_decl.value == null) break;
|
||||
var var_name = var_decl.name.name;
|
||||
|
||||
// Bail if we've seen a var definition of same name before.
|
||||
if (var_name in var_names_seen) return true;
|
||||
if (var_name in var_names_seen) break;
|
||||
var_names_seen[var_name] = true;
|
||||
|
||||
// Only interested in non-constant values.
|
||||
if (var_decl.value.is_constant()) return;
|
||||
if (var_decl.value.is_constant()) continue;
|
||||
|
||||
// Only interested in cases with just one reference to the variable.
|
||||
var def = var_decl.name.definition();
|
||||
if (def.references.length !== 1
|
||||
|| var_name == "arguments" || (!toplevel && def.global)) {
|
||||
side_effects_encountered = true;
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
var ref = def.references[0];
|
||||
|
||||
// Don't replace ref if eval() or with statement in scope.
|
||||
if (ref.scope.uses_eval || ref.scope.uses_with) return true;
|
||||
if (ref.scope.uses_eval || ref.scope.uses_with) break;
|
||||
|
||||
// Restrict var replacement to constants if side effects encountered.
|
||||
if (side_effects_encountered |= lvalues_encountered) return;
|
||||
if (side_effects_encountered |= lvalues_encountered) continue;
|
||||
|
||||
var value_has_side_effects = var_decl.value.has_side_effects(compressor);
|
||||
// Non-constant single use vars can only be replaced in same scope.
|
||||
if (ref.scope !== scope) {
|
||||
side_effects_encountered |= value_has_side_effects;
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect lvalues in var value.
|
||||
@@ -764,6 +767,7 @@ merge(Compressor.prototype, {
|
||||
);
|
||||
stat.transform(tt);
|
||||
}
|
||||
}
|
||||
|
||||
function is_lvalue(node, parent) {
|
||||
return node instanceof AST_SymbolRef && is_lhs(node, parent);
|
||||
@@ -777,6 +781,7 @@ merge(Compressor.prototype, {
|
||||
var_decl.value = null;
|
||||
|
||||
var_defs.splice(var_defs_index, 1);
|
||||
def_stat_index--;
|
||||
if (var_defs.length === 0) {
|
||||
statements.splice(prev_stat_index, 1);
|
||||
stat_index--;
|
||||
|
||||
@@ -1654,3 +1654,26 @@ iife_2: {
|
||||
}(bar());
|
||||
}
|
||||
}
|
||||
|
||||
var_defs: {
|
||||
options = {
|
||||
collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
|
||||
comparisons:true, evaluate:true, booleans:true, loops:true, unused:true, hoist_funs:true,
|
||||
keep_fargs:true, if_return:true, join_vars:true, cascade:true, side_effects:true
|
||||
}
|
||||
input: {
|
||||
var f1 = function(x, y) {
|
||||
var a, b, r = x + y, q = r * r, z = q - r, a = z, b = 7;
|
||||
console.log(a + b);
|
||||
};
|
||||
f1("1", 0);
|
||||
}
|
||||
expect: {
|
||||
var f1 = function(x, y) {
|
||||
var r = x + y, a = r * r - r, b = 7;
|
||||
console.log(a + b);
|
||||
};
|
||||
f1("1", 0);
|
||||
}
|
||||
expect_stdout: "97"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user