if (stop_after === node) abort = true;
return node;
}
- // Stop immediately if these node types are encountered
var parent = scanner.parent();
- if (should_stop(node, parent)) {
- abort = true;
- return node;
- }
// Stop only if candidate is found within conditional branches
if (!stop_if_hit && in_conditional(node, parent)) {
stop_if_hit = parent;
}
+ // Cascade compound assignments
+ if (compound && scan_lhs && can_replace && !stop_if_hit
+ && node instanceof AST_Assign && node.operator != "=" && node.left.equivalent_to(lhs)) {
+ replaced++;
+ changed = true;
+ AST_Node.info("Cascading {node} [{file}:{line},{col}]", {
+ node: node,
+ file: node.start.file,
+ line: node.start.line,
+ col: node.start.col,
+ });
+ can_replace = false;
+ node.right.transform(scanner);
+ clear_write_only(candidate);
+ var assign = make_node(AST_Assign, node, {
+ operator: "=",
+ left: node.left,
+ right: make_node(AST_Binary, node, {
+ operator: node.operator.slice(0, -1),
+ left: abort ? candidate : make_node(AST_Binary, candidate, {
+ operator: compound,
+ left: lhs,
+ right: rvalue,
+ }),
+ right: node.right,
+ }),
+ });
+ abort = true;
+ return assign;
+ }
+ // Stop immediately if these node types are encountered
+ if (should_stop(node, parent)) {
+ abort = true;
+ return node;
+ }
// Skip transient nodes caused by single-use variable replacement
if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
// Replace variable with assignment when found
right: rvalue,
});
}
- var assign = candidate;
- while (assign.write_only) {
- assign.write_only = false;
- if (!(assign instanceof AST_Assign)) break;
- assign = assign.right;
- }
- assign = candidate.clone();
+ clear_write_only(candidate);
+ var assign = candidate.clone();
assign.right = rvalue;
return assign;
}
if (node instanceof AST_SymbolRef && node.definition() === def) {
if (is_lhs(node, multi_replacer.parent())) return node;
if (!--replaced) abort = true;
+ AST_Node.info("Replacing {node} [{file}:{line},{col}]", {
+ node: node,
+ file: node.start.file,
+ line: node.start.line,
+ col: node.start.col,
+ });
var ref = rvalue.clone();
ref.scope = node.scope;
ref.reference();
var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(candidate);
if (!scan_lhs && !scan_rhs) continue;
+ var compound = candidate instanceof AST_Assign && candidate.operator.slice(0, -1);
var funarg = candidate.name instanceof AST_SymbolFunarg;
var may_throw = return_false;
if (candidate.may_throw(compressor)) {
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
var rhs_value = get_rvalue(candidate);
- var rvalue;
- if (rhs_value instanceof AST_Sequence
- && !(candidate instanceof AST_Assign && candidate.operator != "=")) {
- rvalue = rhs_value.tail_node();
- } else {
- rvalue = rhs_value;
- }
+ var rvalue = !compound && rhs_value instanceof AST_Sequence ? rhs_value.tail_node() : rhs_value;
if (!side_effects) side_effects = value_has_side_effects();
var check_destructured = in_try || !lhs_local ? function(node) {
return node instanceof AST_Destructured;
function get_lhs(expr) {
if (expr instanceof AST_Assign) {
var lhs = expr.left;
- if (expr.operator != "=") return lhs;
if (!(lhs instanceof AST_SymbolRef)) return lhs;
var def = lhs.definition();
if (scope.uses_arguments && is_funarg(def)) return lhs;
assign_pos = 0;
}
}
- mangleable_var(expr.right);
+ if (expr.operator == "=") mangleable_var(expr.right);
return lhs;
}
if (expr instanceof AST_Binary) return expr.right.left;
};
}
+ function clear_write_only(assign) {
+ while (assign.write_only) {
+ assign.write_only = false;
+ if (!(assign instanceof AST_Assign)) break;
+ assign = assign.right;
+ }
+ }
+
function may_be_global(node) {
if (node instanceof AST_SymbolRef) {
node = node.fixed_value();
function is_lhs_local(lhs) {
var sym = root_expr(lhs);
- return sym instanceof AST_SymbolRef
- && sym.definition().scope.resolve() === scope
- && !(in_loop
- && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
- || candidate instanceof AST_Unary
- || candidate instanceof AST_Assign && candidate.operator != "="));
+ if (!(sym instanceof AST_SymbolRef)) return false;
+ if (sym.definition().scope.resolve() !== scope) return false;
+ if (!in_loop) return true;
+ if (compound) return false;
+ if (candidate instanceof AST_Unary) return false;
+ var lvalue = lvalues.get(sym.name);
+ return !lvalue || lvalue[0] === lhs;
}
function value_has_side_effects() {
log(node.name, text);
} else {
AST_Node.info(text + " [{file}:{line},{col}]", {
- name: node.print_to_string(),
+ name: node,
file: node.start.file,
line: node.start.line,
col : node.start.col,