} else if (!(left instanceof AST_Destructured || left instanceof AST_SymbolRef)) {
mark_assignment_to_arguments(left);
return;
- } else if (node.operator == "=") {
- node.right.walk(tw);
- scan_declaration(tw, compressor, left, function() {
- return node.right;
- }, function(sym, fixed, walk) {
- if (!(sym instanceof AST_SymbolRef)) {
- mark_assignment_to_arguments(sym);
- walk();
- return;
- }
- var d = sym.definition();
- d.assignments++;
- if (fixed
- && !is_modified(compressor, tw, node, node.right, 0)
- && !sym.in_arg
- && safe_to_assign(tw, d)) {
- push_ref(d, sym);
- mark(tw, d);
- if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
- tw.loop_ids[d.id] = tw.in_loop;
- mark_escaped(tw, d, sym.scope, node, node.right, 0, 1);
- sym.fixed = d.fixed = fixed;
- sym.fixed.assigns = [ node ];
- } else {
- walk();
- d.fixed = false;
- }
- });
- } else {
+ } else switch (node.operator) {
+ case "=":
+ walk_assign();
+ break;
+ case "&&=":
+ case "||=":
+ case "??=":
+ left.walk(tw);
+ push(tw);
+ walk_assign();
+ pop(tw);
+ break;
+ default:
var d = left.definition();
d.assignments++;
var fixed = d.fixed;
lhs.walk(tw);
}
}
+
+ function walk_assign() {
+ node.right.walk(tw);
+ scan_declaration(tw, compressor, left, function() {
+ return node.right;
+ }, function(sym, fixed, walk) {
+ if (!(sym instanceof AST_SymbolRef)) {
+ mark_assignment_to_arguments(sym);
+ walk();
+ return;
+ }
+ var d = sym.definition();
+ d.assignments++;
+ if (fixed
+ && !is_modified(compressor, tw, node, node.right, 0)
+ && !sym.in_arg
+ && safe_to_assign(tw, d)) {
+ push_ref(d, sym);
+ mark(tw, d);
+ if (d.single_use && left instanceof AST_Destructured) d.single_use = false;
+ tw.loop_ids[d.id] = tw.in_loop;
+ mark_escaped(tw, d, sym.scope, node, node.right, 0, 1);
+ sym.fixed = d.fixed = fixed;
+ sym.fixed.assigns = [ node ];
+ } else {
+ walk();
+ d.fixed = false;
+ }
+ });
+ }
});
def(AST_Binary, function(tw) {
if (!lazy_op[this.operator]) return;
function should_stop(node, parent) {
if (node === rvalue) return true;
- if (parent instanceof AST_For) return node !== parent.init;
+ if (parent instanceof AST_For) {
+ if (node !== parent.init) return true;
+ }
if (node instanceof AST_Assign) {
return node.operator != "=" && lhs.equivalent_to(node.left);
}
}
function in_conditional(node, parent) {
- if (parent instanceof AST_Binary) return lazy_op[parent.operator] && parent.left !== node;
+ if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
+ if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
if (parent instanceof AST_Case) return parent.expression !== node;
if (parent instanceof AST_Conditional) return parent.condition !== node;
return parent instanceof AST_If && parent.condition !== node;
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
}
if (left.has_side_effects(compressor)) return this;
- this.write_only = true;
- if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
- return this.right.drop_side_effect_free(compressor);
- }
- return this;
+ var right = this.right;
+ this.write_only = !(lazy_op[this.operator.slice(0, -1)] && right.has_side_effects(compressor));
+ if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
+ return right.drop_side_effect_free(compressor);
});
def(AST_Await, function(compressor) {
if (!compressor.option("awaits")) return this;
}
expect_stdout: "42"
}
+
+logical_assignments: {
+ input: {
+ var a = 42, b = null, c;
+ a &&= "foo";
+ b ||= "bar";
+ c ??= "baz";
+ console.log(a, b, c);
+ }
+ expect_exact: 'var a=42,b=null,c;a&&="foo";b||="bar";c??="baz";console.log(a,b,c);'
+ expect_stdout: "foo bar baz"
+ node_version: ">=15"
+}
+
+logical_collapse_vars: {
+ options = {
+ collapse_vars: true,
+ }
+ input: {
+ var a = "FAIL", b = false;
+ a = "PASS";
+ b ??= a;
+ console.log(a);
+ }
+ expect: {
+ var a = "FAIL", b = false;
+ a = "PASS";
+ b ??= a;
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=15"
+}
+
+logical_reduce_vars: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var a = "PASS", b = 42;
+ b ??= a = "FAIL";
+ console.log(a);
+ }
+ expect: {
+ var a = "PASS", b = 42;
+ b ??= a = "FAIL";
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=15"
+}
+
+logical_side_effects: {
+ options = {
+ side_effects: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var a = "PASS", b = 42;
+ b ??= a = "FAIL";
+ console.log(a);
+ }
+ expect: {
+ var a = "PASS", b = 42;
+ b ??= a = "FAIL";
+ console.log(a);
+ }
+ expect_stdout: "PASS"
+ node_version: ">=15"
+}