enhance collapse_vars (#1862)
- extend expression types
- `a++`
- `a=x;`
- extend scan range
- `for(init;;);`
- `switch(expr){case expr:}`
- `a = x; a = a || y;`
- terminate upon `debugger;`
closes #1821
fixes #27
fixes #315
fixes #1858
This commit is contained in:
parent
5a25d24b56
commit
dee5a27516
357
lib/compress.js
357
lib/compress.js
|
|
@ -512,6 +512,10 @@ merge(Compressor.prototype, {
|
||||||
return fixed();
|
return fixed();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function is_lhs_read_only(lhs) {
|
||||||
|
return lhs instanceof AST_SymbolRef && lhs.definition().orig[0] instanceof AST_SymbolLambda;
|
||||||
|
}
|
||||||
|
|
||||||
function find_variable(compressor, name) {
|
function find_variable(compressor, name) {
|
||||||
var scope, i = 0;
|
var scope, i = 0;
|
||||||
while (scope = compressor.parent(i++)) {
|
while (scope = compressor.parent(i++)) {
|
||||||
|
|
@ -643,174 +647,210 @@ merge(Compressor.prototype, {
|
||||||
statements = join_consecutive_vars(statements, compressor);
|
statements = join_consecutive_vars(statements, compressor);
|
||||||
}
|
}
|
||||||
if (compressor.option("collapse_vars")) {
|
if (compressor.option("collapse_vars")) {
|
||||||
statements = collapse_single_use_vars(statements, compressor);
|
statements = collapse(statements, compressor);
|
||||||
}
|
}
|
||||||
} while (CHANGED && max_iter-- > 0);
|
} while (CHANGED && max_iter-- > 0);
|
||||||
|
|
||||||
return statements;
|
return statements;
|
||||||
|
|
||||||
function collapse_single_use_vars(statements, compressor) {
|
// Search from right to left for assignment-like expressions:
|
||||||
// Iterate statements backwards looking for a statement with a var/const
|
// - `var a = x;`
|
||||||
// declaration immediately preceding it. Grab the rightmost var definition
|
// - `a = x;`
|
||||||
// and if it has exactly one reference then attempt to replace its reference
|
// - `++a`
|
||||||
// in the statement with the var value and then erase the var definition.
|
// For each candidate, scan from left to right for first usage, then try
|
||||||
|
// to fold assignment into the site for compression.
|
||||||
|
// Will not attempt to collapse assignments into or past code blocks
|
||||||
|
// which are not sequentially executed, e.g. loops and conditionals.
|
||||||
|
function collapse(statements, compressor) {
|
||||||
var scope = compressor.find_parent(AST_Scope);
|
var scope = compressor.find_parent(AST_Scope);
|
||||||
var stat_index;
|
if (scope.uses_eval || scope.uses_with) return statements;
|
||||||
var prev_stat_index;
|
var candidates = [];
|
||||||
var def_stat_index;
|
var stat_index = statements.length;
|
||||||
var stat;
|
while (--stat_index >= 0) {
|
||||||
var var_defs;
|
extract_candidates(statements[stat_index]);
|
||||||
var var_defs_index;
|
while (candidates.length > 0) {
|
||||||
for (stat_index = statements.length; --stat_index >= 0;) {
|
var candidate = candidates.pop();
|
||||||
stat = statements[stat_index];
|
var lhs = get_lhs(candidate);
|
||||||
// Scan variable definitions from right to left.
|
if (!lhs || is_lhs_read_only(lhs)) continue;
|
||||||
if (stat instanceof AST_Definitions) {
|
var lvalues = get_lvalues(candidate);
|
||||||
prev_stat_index = stat_index;
|
if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
|
||||||
var_defs = stat.definitions;
|
var side_effects = value_has_side_effects(candidate);
|
||||||
for (def_stat_index = var_defs.length; --def_stat_index >= 1;) {
|
var hit = false, abort = false, replaced = false;
|
||||||
stat = var_defs[def_stat_index];
|
var tt = new TreeTransformer(function(node, descend) {
|
||||||
scan_var_defs(def_stat_index);
|
if (abort) return node;
|
||||||
|
// Skip nodes before `candidate` as quickly as possible
|
||||||
|
if (!hit) {
|
||||||
|
if (node === candidate) {
|
||||||
|
hit = true;
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
} else if (stat_index > 0) {
|
return;
|
||||||
// The variable definition must precede a statement.
|
|
||||||
prev_stat_index = stat_index - 1;
|
|
||||||
var prev_stat = statements[prev_stat_index];
|
|
||||||
if (!(prev_stat instanceof AST_Definitions)) continue;
|
|
||||||
var_defs = prev_stat.definitions;
|
|
||||||
scan_var_defs(var_defs.length);
|
|
||||||
}
|
}
|
||||||
}
|
// Stop immediately if these node types are encountered
|
||||||
|
|
||||||
return statements;
|
|
||||||
|
|
||||||
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) 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) break;
|
|
||||||
var_names_seen[var_name] = true;
|
|
||||||
|
|
||||||
// Only interested in non-constant values.
|
|
||||||
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"
|
|
||||||
|| def.global && !compressor.toplevel(def)) {
|
|
||||||
side_effects_encountered = true;
|
|
||||||
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) break;
|
|
||||||
|
|
||||||
// Restrict var replacement to constants if side effects encountered.
|
|
||||||
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;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect lvalues in var value.
|
|
||||||
var tw = new TreeWalker(function(node){
|
|
||||||
if (node instanceof AST_SymbolRef && is_lvalue(node, tw.parent())) {
|
|
||||||
lvalues[node.name] = lvalues_encountered = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var_decl.value.walk(tw);
|
|
||||||
|
|
||||||
// Replace the non-constant single use var in statement if side effect free.
|
|
||||||
var unwind = false;
|
|
||||||
var tt = new TreeTransformer(
|
|
||||||
function preorder(node) {
|
|
||||||
if (unwind || node instanceof AST_Scope && node !== scope) return node;
|
|
||||||
var parent = tt.parent();
|
var parent = tt.parent();
|
||||||
if (node instanceof AST_Try
|
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|
||||||
|
|| node instanceof AST_Debugger
|
||||||
|
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|
||||||
|
|| node instanceof AST_SymbolRef && node.undeclared()
|
||||||
|
|| node instanceof AST_Try
|
||||||
|| node instanceof AST_With
|
|| node instanceof AST_With
|
||||||
|| node instanceof AST_Case
|
|| parent instanceof AST_For && node !== parent.init) {
|
||||||
|| node instanceof AST_IterationStatement
|
abort = true;
|
||||||
|| (parent instanceof AST_If && node !== parent.condition)
|
return node;
|
||||||
|| (parent instanceof AST_Conditional && node !== parent.condition)
|
|
||||||
|| (node instanceof AST_SymbolRef
|
|
||||||
&& value_has_side_effects
|
|
||||||
&& !are_references_in_scope(node.definition(), scope))
|
|
||||||
|| (parent instanceof AST_Binary
|
|
||||||
&& (parent.operator == "&&" || parent.operator == "||")
|
|
||||||
&& node === parent.right)
|
|
||||||
|| (parent instanceof AST_Switch && node !== parent.expression)) {
|
|
||||||
return side_effects_encountered = unwind = true, node;
|
|
||||||
}
|
}
|
||||||
function are_references_in_scope(def, scope) {
|
// Replace variable with assignment when found
|
||||||
if (def.orig.length === 1
|
if (!(node instanceof AST_SymbolDeclaration)
|
||||||
&& def.orig[0] instanceof AST_SymbolDefun) return true;
|
&& !is_lhs(node, parent)
|
||||||
if (def.scope !== scope) return false;
|
&& lhs.equivalent_to(node)) {
|
||||||
var refs = def.references;
|
CHANGED = replaced = abort = true;
|
||||||
for (var i = 0, len = refs.length; i < len; i++) {
|
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
|
||||||
if (refs[i].scope !== scope) return false;
|
name: node.print_to_string(),
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function postorder(node) {
|
|
||||||
if (unwind) return node;
|
|
||||||
if (node === ref)
|
|
||||||
return unwind = true, replace_var(var_decl, node, tt.parent(), false);
|
|
||||||
if (side_effects_encountered |= node.has_side_effects(compressor))
|
|
||||||
return unwind = true, node;
|
|
||||||
if (lvalues_encountered && node instanceof AST_SymbolRef && node.name in lvalues) {
|
|
||||||
side_effects_encountered = true;
|
|
||||||
return unwind = true, node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
stat.transform(tt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_lvalue(node, parent) {
|
|
||||||
return node instanceof AST_SymbolRef && is_lhs(node, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replace_var(var_decl, node, parent, is_constant) {
|
|
||||||
if (is_lvalue(node, parent)) return node;
|
|
||||||
|
|
||||||
// Remove var definition and return its value to the TreeTransformer to replace.
|
|
||||||
var value = maintain_this_binding(parent, node, var_decl.value);
|
|
||||||
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--;
|
|
||||||
}
|
|
||||||
// Further optimize statement after substitution.
|
|
||||||
stat.reset_opt_flags(compressor);
|
|
||||||
|
|
||||||
compressor.info("Collapsing {type} {name} [{file}:{line},{col}]", {
|
|
||||||
type: is_constant ? "constant" : "variable",
|
|
||||||
name: var_decl.name.name,
|
|
||||||
file: node.start.file,
|
file: node.start.file,
|
||||||
line: node.start.line,
|
line: node.start.line,
|
||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
CHANGED = true;
|
if (candidate instanceof AST_UnaryPostfix) {
|
||||||
return value;
|
return make_node(AST_UnaryPrefix, candidate, candidate);
|
||||||
|
}
|
||||||
|
if (candidate instanceof AST_VarDef) {
|
||||||
|
var def = candidate.name.definition();
|
||||||
|
if (def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
|
||||||
|
return maintain_this_binding(parent, node, candidate.value);
|
||||||
|
}
|
||||||
|
return make_node(AST_Assign, candidate, {
|
||||||
|
operator: "=",
|
||||||
|
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
|
||||||
|
right: candidate.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
// These node types have child nodes that execute sequentially,
|
||||||
|
// but are otherwise not safe to scan into or beyond them.
|
||||||
|
var sym;
|
||||||
|
if (node instanceof AST_Call
|
||||||
|
|| node instanceof AST_Exit
|
||||||
|
|| node instanceof AST_PropAccess
|
||||||
|
|| node instanceof AST_SymbolRef
|
||||||
|
&& (lvalues[node.name]
|
||||||
|
|| side_effects && !references_in_scope(node.definition()))
|
||||||
|
|| (sym = lhs_or_def(node)) && get_symbol(sym).name in lvalues
|
||||||
|
|| parent instanceof AST_Binary
|
||||||
|
&& (parent.operator == "&&" || parent.operator == "||")
|
||||||
|
|| parent instanceof AST_Case
|
||||||
|
|| parent instanceof AST_Conditional
|
||||||
|
|| parent instanceof AST_For
|
||||||
|
|| parent instanceof AST_If) {
|
||||||
|
if (!(node instanceof AST_Scope)) descend(node, tt);
|
||||||
|
abort = true;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
// Skip (non-executed) functions and (leading) default case in switch statements
|
||||||
|
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
|
||||||
|
});
|
||||||
|
for (var i = stat_index; !abort && i < statements.length; i++) {
|
||||||
|
statements[i].transform(tt);
|
||||||
|
}
|
||||||
|
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statements;
|
||||||
|
|
||||||
|
function extract_candidates(expr) {
|
||||||
|
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
|
||||||
|
|| expr instanceof AST_Unary && (expr.operator == "++" || expr.operator == "--")) {
|
||||||
|
candidates.push(expr);
|
||||||
|
} else if (expr instanceof AST_Sequence) {
|
||||||
|
expr.expressions.forEach(extract_candidates);
|
||||||
|
} else if (expr instanceof AST_Definitions) {
|
||||||
|
expr.definitions.forEach(function(var_def) {
|
||||||
|
if (var_def.value) candidates.push(var_def);
|
||||||
|
});
|
||||||
|
} else if (expr instanceof AST_SimpleStatement) {
|
||||||
|
extract_candidates(expr.body);
|
||||||
|
} else if (expr instanceof AST_For && expr.init) {
|
||||||
|
extract_candidates(expr.init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_lhs(expr) {
|
||||||
|
if (expr instanceof AST_VarDef) {
|
||||||
|
var def = expr.name.definition();
|
||||||
|
if (def.orig.length > 1
|
||||||
|
|| def.references.length == 1 && (!def.global || compressor.toplevel(def))) {
|
||||||
|
return make_node(AST_SymbolRef, expr.name, expr.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return expr[expr instanceof AST_Assign ? "left" : "expression"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_symbol(node) {
|
||||||
|
while (node instanceof AST_PropAccess) node = node.expression;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_lvalues(expr) {
|
||||||
|
var lvalues = Object.create(null);
|
||||||
|
if (expr instanceof AST_Unary) return lvalues;
|
||||||
|
var scope;
|
||||||
|
var tw = new TreeWalker(function(node, descend) {
|
||||||
|
if (node instanceof AST_Scope) {
|
||||||
|
var save_scope = scope;
|
||||||
|
descend();
|
||||||
|
scope = save_scope;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (node instanceof AST_SymbolRef || node instanceof AST_PropAccess) {
|
||||||
|
var sym = get_symbol(node);
|
||||||
|
if (sym instanceof AST_SymbolRef) {
|
||||||
|
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw);
|
||||||
|
return lvalues;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lhs_or_def(node) {
|
||||||
|
if (node instanceof AST_VarDef) return node.value && node.name;
|
||||||
|
return is_lhs(node.left, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_candidate(expr) {
|
||||||
|
var found = false;
|
||||||
|
return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
|
||||||
|
if (found) return node;
|
||||||
|
if (node === expr) {
|
||||||
|
found = true;
|
||||||
|
if (node instanceof AST_VarDef) {
|
||||||
|
remove(node.name.definition().orig, node.name);
|
||||||
|
}
|
||||||
|
return in_list ? MAP.skip : null;
|
||||||
|
}
|
||||||
|
}, function(node) {
|
||||||
|
if (node instanceof AST_Sequence) switch (node.expressions.length) {
|
||||||
|
case 0: return null;
|
||||||
|
case 1: return node.expressions[0];
|
||||||
|
}
|
||||||
|
if (node instanceof AST_Definitions && node.definitions.length == 0
|
||||||
|
|| node instanceof AST_SimpleStatement && !node.body) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function value_has_side_effects(expr) {
|
||||||
|
if (expr instanceof AST_Unary) return false;
|
||||||
|
return expr[expr instanceof AST_Assign ? "right" : "value"].has_side_effects(compressor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function references_in_scope(def) {
|
||||||
|
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return true;
|
||||||
|
if (def.scope !== scope) return false;
|
||||||
|
return def.references.every(function(ref) {
|
||||||
|
return ref.scope === scope;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2022,7 +2062,8 @@ merge(Compressor.prototype, {
|
||||||
var var_defs = var_defs_by_id.get(sym.id);
|
var var_defs = var_defs_by_id.get(sym.id);
|
||||||
if (var_defs.length > 1 && !def.value) {
|
if (var_defs.length > 1 && !def.value) {
|
||||||
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||||
var_defs.splice(var_defs.indexOf(def), 1);
|
remove(var_defs, def);
|
||||||
|
remove(sym.orig, def.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2055,6 +2096,7 @@ merge(Compressor.prototype, {
|
||||||
} else {
|
} else {
|
||||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||||
}
|
}
|
||||||
|
remove(sym.orig, def.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
|
||||||
|
|
@ -2062,7 +2104,8 @@ merge(Compressor.prototype, {
|
||||||
if (var_defs.length > 1) {
|
if (var_defs.length > 1) {
|
||||||
var def = tail.pop();
|
var def = tail.pop();
|
||||||
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
|
||||||
var_defs.splice(var_defs.indexOf(def), 1);
|
remove(var_defs, def);
|
||||||
|
remove(def.name.definition().orig, def.name);
|
||||||
side_effects.unshift(make_node(AST_Assign, def, {
|
side_effects.unshift(make_node(AST_Assign, def, {
|
||||||
operator: "=",
|
operator: "=",
|
||||||
left: make_node(AST_SymbolRef, def.name, def.name),
|
left: make_node(AST_SymbolRef, def.name, def.name),
|
||||||
|
|
@ -3130,9 +3173,7 @@ merge(Compressor.prototype, {
|
||||||
&& (left.operator == "++" || left.operator == "--")) {
|
&& (left.operator == "++" || left.operator == "--")) {
|
||||||
left = left.expression;
|
left = left.expression;
|
||||||
} else left = null;
|
} else left = null;
|
||||||
if (!left ||
|
if (!left || is_lhs_read_only(left)) {
|
||||||
left instanceof AST_SymbolRef
|
|
||||||
&& left.definition().orig[0] instanceof AST_SymbolLambda) {
|
|
||||||
expressions[++i] = cdr;
|
expressions[++i] = cdr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,10 @@ collapse_vars_side_effects_1: {
|
||||||
log(x, s.charAt(i++), y, 7);
|
log(x, s.charAt(i++), y, 7);
|
||||||
}
|
}
|
||||||
function f4() {
|
function f4() {
|
||||||
var log = console.log.bind(console),
|
var i = 10,
|
||||||
i = 10,
|
|
||||||
x = i += 2,
|
x = i += 2,
|
||||||
y = i += 3;
|
y = i += 3;
|
||||||
log(x, i += 4, y, i);
|
console.log.bind(console)(x, i += 4, y, i);
|
||||||
}
|
}
|
||||||
f1(), f2(), f3(), f4();
|
f1(), f2(), f3(), f4();
|
||||||
}
|
}
|
||||||
|
|
@ -671,8 +670,8 @@ collapse_vars_lvalues: {
|
||||||
function f4(x) { var a = (x -= 3); return x + a; }
|
function f4(x) { var a = (x -= 3); return x + a; }
|
||||||
function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; }
|
function f5(x) { var w = e1(), v = e2(), c = v = --x; return (w = x) - c; }
|
||||||
function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); }
|
function f6(x) { var w = e1(), v = e2(); return (v = --x) - (w = x); }
|
||||||
function f7(x) { var w = e1(), c = e2() - x; return (w = x) - c; }
|
function f7(x) { var w = e1(); return (w = x) - (e2() - x); }
|
||||||
function f8(x) { var w = e1(), v = e2(); return (w = x) - (v - x); }
|
function f8(x) { var w = e1(); return (w = x) - (e2() - x); }
|
||||||
function f9(x) { var w = e1(); return e2() - x - (w = x); }
|
function f9(x) { var w = e1(); return e2() - x - (w = x); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -703,8 +702,8 @@ collapse_vars_lvalues_drop_assign: {
|
||||||
function f4(x) { var a = (x -= 3); return x + a; }
|
function f4(x) { var a = (x -= 3); return x + a; }
|
||||||
function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; }
|
function f5(x) { e1(); var v = e2(), c = v = --x; return x - c; }
|
||||||
function f6(x) { e1(), e2(); return --x - x; }
|
function f6(x) { e1(), e2(); return --x - x; }
|
||||||
function f7(x) { e1(); var c = e2() - x; return x - c; }
|
function f7(x) { e1(); return x - (e2() - x); }
|
||||||
function f8(x) { e1(); var v = e2(); return x - (v - x); }
|
function f8(x) { e1(); return x - (e2() - x); }
|
||||||
function f9(x) { e1(); return e2() - x - x; }
|
function f9(x) { e1(); return e2() - x - x; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1047,10 +1046,9 @@ collapse_vars_object: {
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
function f0(x, y) {
|
function f0(x, y) {
|
||||||
var z = x + y;
|
|
||||||
return {
|
return {
|
||||||
get b() { return 7; },
|
get b() { return 7; },
|
||||||
r: z
|
r: x + y
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function f1(x, y) {
|
function f1(x, y) {
|
||||||
|
|
@ -1677,3 +1675,514 @@ var_defs: {
|
||||||
}
|
}
|
||||||
expect_stdout: "97"
|
expect_stdout: "97"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assignment: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
var a;
|
||||||
|
a = x;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for_init: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
var a = x;
|
||||||
|
var b = y;
|
||||||
|
for (a; b;);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
var b = y;
|
||||||
|
for (x; b;);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_case: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(x, y, z) {
|
||||||
|
var a = x();
|
||||||
|
var b = y();
|
||||||
|
var c = z;
|
||||||
|
switch (a) {
|
||||||
|
default: d();
|
||||||
|
case b: e();
|
||||||
|
case c: f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(x, y, z) {
|
||||||
|
var c = z;
|
||||||
|
switch (x()) {
|
||||||
|
default: d();
|
||||||
|
case y(): e();
|
||||||
|
case c: f();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_27: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(jQuery) {
|
||||||
|
var $;
|
||||||
|
$ = jQuery;
|
||||||
|
$("body").addClass("foo");
|
||||||
|
})(jQuery);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(jQuery) {
|
||||||
|
jQuery("body").addClass("foo");
|
||||||
|
})(jQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modified: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f1(b) {
|
||||||
|
var a = b;
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
function f2(b) {
|
||||||
|
var a = b;
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
function f3(b) {
|
||||||
|
var a = b++;
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
function f4(b) {
|
||||||
|
var a = b++;
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
function f5(b) {
|
||||||
|
var a = function() {
|
||||||
|
return b;
|
||||||
|
}();
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f1(b) {
|
||||||
|
return b + b;
|
||||||
|
}
|
||||||
|
function f2(b) {
|
||||||
|
var a = b;
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
function f3(b) {
|
||||||
|
var a = b++;
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
function f4(b) {
|
||||||
|
var a = b++;
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
function f5(b) {
|
||||||
|
var a = function() {
|
||||||
|
return b;
|
||||||
|
}();
|
||||||
|
return b++ + a;
|
||||||
|
}
|
||||||
|
console.log(f1(1), f2(1), f3(1), f4(1), f5(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "2 2 3 3 2"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_1858: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(x) {
|
||||||
|
var a = {}, b = a.b = x;
|
||||||
|
return a.b + b;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(x) {
|
||||||
|
var a = {}, b = a.b = x;
|
||||||
|
return a.b + b;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
anonymous_function: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f(a) {
|
||||||
|
f ^= 0;
|
||||||
|
return f * a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function f(a) {
|
||||||
|
f ^= 0;
|
||||||
|
return f * a;
|
||||||
|
}(1));
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
side_effects_property: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = [];
|
||||||
|
var b = 0;
|
||||||
|
a[b++] = function() { return 42;};
|
||||||
|
var c = a[b++]();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = [];
|
||||||
|
var b = 0;
|
||||||
|
a[b++] = function() { return 42;};
|
||||||
|
var c = a[b++]();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
undeclared: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(x, y) {
|
||||||
|
var a;
|
||||||
|
a = x;
|
||||||
|
b = y;
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(x, y) {
|
||||||
|
var a;
|
||||||
|
a = x;
|
||||||
|
b = y;
|
||||||
|
return b + a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ref_scope: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = 1, b = 2, c = 3;
|
||||||
|
var a = c++, b = b /= a;
|
||||||
|
return function() {
|
||||||
|
return a;
|
||||||
|
}() + b;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = 1, b = 2, c = 3;
|
||||||
|
b = b /= a = c++;
|
||||||
|
return function() {
|
||||||
|
return a;
|
||||||
|
}() + b;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
chained_1: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 2;
|
||||||
|
var a = 3 / a;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 3 / (a = 2);
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
chained_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
var a = 2;
|
||||||
|
a = 3 / a;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
a = 3 / (a = 2);
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
chained_3: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a, b) {
|
||||||
|
var c = a, c = b;
|
||||||
|
b++;
|
||||||
|
return c;
|
||||||
|
}(1, 2));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a, b) {
|
||||||
|
var c = a, c = b;
|
||||||
|
b++;
|
||||||
|
return c;
|
||||||
|
}(1, 2));
|
||||||
|
}
|
||||||
|
expect_stdout: "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_binary_1: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
a++;
|
||||||
|
(function() {} || a || 3).toString();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
a++;
|
||||||
|
(function() {} || a || 3).toString();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean_binary_2: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var c = 0;
|
||||||
|
c += 1;
|
||||||
|
(function() {
|
||||||
|
c = 1 + c;
|
||||||
|
} || 9).toString();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var c = 0;
|
||||||
|
c += 1;
|
||||||
|
(function() {
|
||||||
|
c = 1 + c;
|
||||||
|
} || 9).toString();
|
||||||
|
console.log(c);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_lvalues: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a, b = 10;
|
||||||
|
var a = (--b || a || 3).toString(), c = --b + -a;
|
||||||
|
console.log(null, a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a, b = 10;
|
||||||
|
var a = (--b || a || 3).toString(), c = --b + -a;
|
||||||
|
console.log(null, a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
double_def: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = x, a = a && y;
|
||||||
|
a();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = x;
|
||||||
|
(a = a && y)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toplevel_single_reference: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
for (var b in x) {
|
||||||
|
var a = b;
|
||||||
|
b(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
for (var b in x)
|
||||||
|
b(a = b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unused_orig: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
passes: 2,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 1;
|
||||||
|
console.log(function(b) {
|
||||||
|
var a;
|
||||||
|
var c = b;
|
||||||
|
for (var d in c) {
|
||||||
|
var a = c[0];
|
||||||
|
return --b + a;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
} catch (e) {
|
||||||
|
--b + a;
|
||||||
|
}
|
||||||
|
a && a.NaN;
|
||||||
|
}([2]), a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 1;
|
||||||
|
console.log(function(b) {
|
||||||
|
var c = b;
|
||||||
|
for (var d in c) {
|
||||||
|
var a = c[0];
|
||||||
|
return --b + a;
|
||||||
|
}
|
||||||
|
a && a.NaN;
|
||||||
|
}([2]), a);
|
||||||
|
}
|
||||||
|
expect_stdout: "3 1"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_315: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
evaluate: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(s) {
|
||||||
|
var w, _i, _len, _ref, _results;
|
||||||
|
_ref = s.trim().split(" ");
|
||||||
|
_results = [];
|
||||||
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||||
|
w = _ref[_i];
|
||||||
|
_results.push(w.toLowerCase());
|
||||||
|
}
|
||||||
|
return _results;
|
||||||
|
}("test"));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var w, _i, _len, _ref, _results;
|
||||||
|
for (_results = [], _i = 0, _len = (_ref = "test".trim().split(" ")).length; _i < _len ; _i++)
|
||||||
|
w = _ref[_i], _results.push(w.toLowerCase());
|
||||||
|
return _results;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
lvalues_def: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 0, b = 1;
|
||||||
|
var a = b++, b = +function() {}();
|
||||||
|
a && a[a++];
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 0, b = 1;
|
||||||
|
var a = b++, b = +void 0;
|
||||||
|
a && a[a++];
|
||||||
|
console.log(a, b);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
}
|
||||||
|
|
||||||
|
compound_assignment: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a;
|
||||||
|
a = 1;
|
||||||
|
a += a + 2;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a;
|
||||||
|
a = 1;
|
||||||
|
a += a + 2;
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "4"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1114,3 +1114,36 @@ issue_1838: {
|
||||||
"}",
|
"}",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var_catch_toplevel: {
|
||||||
|
options = {
|
||||||
|
conditionals: true,
|
||||||
|
negate_iife: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
a--;
|
||||||
|
try {
|
||||||
|
a++;
|
||||||
|
} catch(a) {
|
||||||
|
if (a) var a;
|
||||||
|
var a = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!function() {
|
||||||
|
a--;
|
||||||
|
try {
|
||||||
|
a++;
|
||||||
|
} catch(a) {
|
||||||
|
var a;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,7 @@ chained_evaluation_1: {
|
||||||
expect: {
|
expect: {
|
||||||
(function() {
|
(function() {
|
||||||
(function() {
|
(function() {
|
||||||
var c;
|
f(1).bar = 1;
|
||||||
c = f(1);
|
|
||||||
c.bar = 1;
|
|
||||||
})();
|
})();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +44,8 @@ chained_evaluation_2: {
|
||||||
expect: {
|
expect: {
|
||||||
(function() {
|
(function() {
|
||||||
(function() {
|
(function() {
|
||||||
var c, b = "long piece of string";
|
var b = "long piece of string";
|
||||||
c = f(b);
|
f(b).bar = b;
|
||||||
c.bar = b;
|
|
||||||
})();
|
})();
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user