aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2021-03-22 20:59:43 +0000
committerGitHub <noreply@github.com>2021-03-23 04:59:43 +0800
commitf9055df44d86c8537afc34f00bd255792f80422f (patch)
treedcc6102ab6485002fb7255d45d2ae014c7a737f6
parent51bdb7281bb555f0cc56ca15560a8ab514b0a632 (diff)
downloadtracifyjs-f9055df44d86c8537afc34f00bd255792f80422f.tar.gz
tracifyjs-f9055df44d86c8537afc34f00bd255792f80422f.zip
support logical assignment operators (#4813)
-rw-r--r--lib/compress.js88
-rw-r--r--lib/parse.js7
-rw-r--r--test/compress/assignments.js74
-rw-r--r--test/compress/collapse_vars.js19
-rw-r--r--test/ufuzz/index.js12
5 files changed, 157 insertions, 43 deletions
diff --git a/lib/compress.js b/lib/compress.js
index ce12da2d..95cccb56 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -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;
@@ -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;
@@ -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);
}
@@ -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;
@@ -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;
diff --git a/lib/parse.js b/lib/parse.js
index b5abf0d7..c3ae96d9 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -104,12 +104,15 @@ var OPERATORS = makePredicate([
">>=",
"<<=",
">>>=",
+ "&=",
"|=",
"^=",
- "&=",
"&&",
"||",
"??",
+ "&&=",
+ "||=",
+ "??=",
]);
var NEWLINE_CHARS = "\n\r\u2028\u2029";
@@ -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;) {
diff --git a/test/compress/assignments.js b/test/compress/assignments.js
index 5bc971a5..274a0d63 100644
--- a/test/compress/assignments.js
+++ b/test/compress/assignments.js
@@ -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"
+}
diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js
index 8530d387..eaff0127 100644
--- a/test/compress/collapse_vars.js
+++ b/test/compress/collapse_vars.js
@@ -2868,7 +2868,7 @@ lvalues_def: {
expect_stdout: true
}
-compound_assignment: {
+compound_assignment_1: {
options = {
collapse_vars: true,
}
@@ -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,
diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js
index d91fe040..e2bcdf2c 100644
--- a/test/ufuzz/index.js
+++ b/test/ufuzz/index.js
@@ -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] = [];",
@@ -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 = [
"+",