Skip to content

Commit

Permalink
support logical assignment operators (#4813)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlamsl committed Mar 22, 2021
1 parent 51bdb72 commit f9055df
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 43 deletions.
88 changes: 52 additions & 36 deletions lib/compress.js
Expand Up @@ -821,35 +821,19 @@ merge(Compressor.prototype, {
} 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;
Expand Down Expand Up @@ -900,6 +884,36 @@ merge(Compressor.prototype, {
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;
Expand Down Expand Up @@ -1979,7 +1993,9 @@ merge(Compressor.prototype, {

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);
}
Expand Down Expand Up @@ -2014,7 +2030,8 @@ merge(Compressor.prototype, {
}

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;
Expand Down Expand Up @@ -7211,11 +7228,10 @@ merge(Compressor.prototype, {
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;
Expand Down
7 changes: 5 additions & 2 deletions lib/parse.js
Expand Up @@ -104,12 +104,15 @@ var OPERATORS = makePredicate([
">>=",
"<<=",
">>>=",
"&=",
"|=",
"^=",
"&=",
"&&",
"||",
"??",
"&&=",
"||=",
"??=",
]);

var NEWLINE_CHARS = "\n\r\u2028\u2029";
Expand Down Expand Up @@ -653,7 +656,7 @@ var UNARY_PREFIX = makePredicate("typeof void delete -- ++ ! ~ - +");

var UNARY_POSTFIX = makePredicate("-- ++");

var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= |= ^= &=");
var ASSIGNMENT = makePredicate("= += -= /= *= %= **= >>= <<= >>>= &= |= ^= &&= ||= ??=");

var PRECEDENCE = function(a, ret) {
for (var i = 0; i < a.length;) {
Expand Down
74 changes: 74 additions & 0 deletions test/compress/assignments.js
Expand Up @@ -475,3 +475,77 @@ issue_4521: {
}
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"
}
19 changes: 18 additions & 1 deletion test/compress/collapse_vars.js
Expand Up @@ -2868,7 +2868,7 @@ lvalues_def: {
expect_stdout: true
}

compound_assignment: {
compound_assignment_1: {
options = {
collapse_vars: true,
}
Expand All @@ -2887,6 +2887,23 @@ compound_assignment: {
expect_stdout: "4"
}

compound_assignment_2: {
options = {
collapse_vars: true,
}
input: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect: {
var a;
a = 1;
for (a += a + 2; console.log(a););
}
expect_stdout: "4"
}

issue_2187_1: {
options = {
collapse_vars: true,
Expand Down
12 changes: 8 additions & 4 deletions test/ufuzz/index.js
Expand Up @@ -149,6 +149,7 @@ var SUPPORT = function(matrix) {
for_of: "for (var a of []);",
generator: "function* f(){}",
let: "let a;",
logical_assignment: "[].p ??= 0;",
new_target: "function f() { new.target; }",
nullish: "0 ?? 0",
rest: "var [...a] = [];",
Expand Down Expand Up @@ -262,10 +263,13 @@ ASSIGNMENTS = ASSIGNMENTS.concat([
">>=",
">>>=",
]);
if (SUPPORT.exponentiation) {
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
ASSIGNMENTS.push("**=");
}
ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
if (SUPPORT.exponentiation) ASSIGNMENTS.push("**=");
if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([
"&&=",
"||=",
"??=",
]);

var UNARY_SAFE = [
"+",
Expand Down

0 comments on commit f9055df

Please sign in to comment.