New Upstream Release - uglify-js

Ready changes

Summary

Merged new upstream version: 3.17.4 (was: 3.17.3).

Resulting package

Built on 2022-11-23T04:24 (took 9m43s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases libjs-uglify-jsapt install -t fresh-releases node-uglify-jsapt install -t fresh-releases uglifyjs

Lintian Result

Diff

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..6edcf864
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.js    text eol=lf
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..f6dd9e08
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,51 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+<!-- Note: sub-optimal but correct code is not a bug -->
+
+**Uglify version (`uglifyjs -V`)**
+
+**JavaScript input**
+
+<!--
+A complete parsable JS program exhibiting the issue with UglifyJS alone
+- without third party tools or libraries.
+
+Ideally the input should be as small as possible, but may be large if isolating
+the problem proves to be difficult. The most important thing is that the
+standalone program reliably exhibits the bug when minified. Provide a link to a
+gist if necessary.
+
+Solely providing minified output without the original uglify JS input is not
+useful in determining the cause of the problem. Issues without a reproducible
+test case will be closed.
+-->
+
+**The `uglifyjs` CLI command executed or `minify()` options used.**
+
+<!--
+Command-line or API call to UglifyJS without third party tools or libraries.
+
+For users using bundlers or transpilers, you may be able to gather the required
+information through setting the `UGLIFY_BUG_REPORT` environment variable:
+
+    export UGLIFY_BUG_REPORT=1      (bash)
+    set UGLIFY_BUG_REPORT=1         (Command Prompt)
+    $Env:UGLIFY_BUG_REPORT=1        (PowerShell)
+
+before running your usual build process. The resulting "minified" output should
+contain the necessary details for this report.
+-->
+
+**JavaScript output or error produced.**
+
+<!--
+Only minified code that produces different output (or error) from the original
+upon execution would be considered a bug.
+-->
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..957e3cc6
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,58 @@
+name: Build testing
+on:
+  pull_request:
+  push:
+    branches: [ master ]
+jobs:
+  ufuzz:
+    strategy:
+      fail-fast: false
+      matrix:
+        options:
+          - '-mb braces'
+          - '--ie -c'
+          - '-mc'
+          - '-p acorn --toplevel -mco spidermonkey'
+          - '--toplevel -mc passes=3,pure_getters,unsafe'
+        script:
+          - acorn.sh
+          - bootstrap.sh
+          - buble.sh
+          - butternut.sh
+          - mathjs.sh
+          - rollup-es.sh
+          - rollup-ts.sh
+          - sucrase.sh
+          - web-tooling-benchmark.sh
+        include:
+          - node: '14'
+            script: acorn.sh
+          - node: '14'
+            script: bootstrap.sh
+          - node: '14'
+            script: buble.sh
+          - node: '14'
+            script: butternut.sh
+          - node: '14'
+            script: mathjs.sh
+          - node: '8'
+            script: rollup-es.sh
+          - node: '14'
+            script: rollup-ts.sh
+          - node: '14'
+            script: sucrase.sh
+          - node: '14'
+            script: web-tooling-benchmark.sh
+    name: ${{ matrix.script }} ${{ matrix.options }}
+    runs-on: ubuntu-latest
+    env:
+      NODE: ${{ matrix.node }}
+      OPTIONS: ${{ matrix.options }}
+      SCRIPT: ${{ matrix.script }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Perform uglify, build & test
+        shell: bash
+        run: |
+          . ./test/release/install.sh
+          ./test/release/$SCRIPT $OPTIONS
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..53c44d3f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,29 @@
+name: CI
+on:
+  pull_request:
+  push:
+    branches: [ master ]
+jobs:
+  test:
+    strategy:
+      matrix:
+        node: [ '0.10', '0.12', '4', '6', '8', '10', '12', '14', '16', latest ]
+        os: [ ubuntu-latest, windows-latest ]
+        script: [ compress, mocha, release/benchmark, release/jetstream ]
+    name: ${{ matrix.node }} ${{ matrix.os }} ${{ matrix.script }}
+    runs-on: ${{ matrix.os }}
+    env:
+      NODE: ${{ matrix.node }}
+      TYPE: ${{ matrix.script }}
+      UGLIFY_GITHUB_LAG: 10000
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/cache@v3
+        with:
+          path: tmp
+          key: tmp ${{ matrix.script }}
+      - name: Perform tests
+        shell: bash
+        run: |
+          . ./test/release/install.sh
+          node test/$TYPE
diff --git a/.github/workflows/ufuzz.yml b/.github/workflows/ufuzz.yml
new file mode 100644
index 00000000..43b6ed55
--- /dev/null
+++ b/.github/workflows/ufuzz.yml
@@ -0,0 +1,44 @@
+name: Fuzzing
+on:
+  pull_request:
+  schedule:
+    - cron: '*/15 * * * *'
+  workflow_dispatch:
+  workflow_run:
+    branches: [ master ]
+    types: [ completed ]
+    workflows: [ 'Build testing', CI ]
+env:
+  BASE_URL: https://api.github.com/repos/${{ github.repository }}
+  TOKEN: ${{ github.token }}
+jobs:
+  ufuzz:
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - node: '16'
+            os: macos-latest
+          - node: '12'
+            os: ubuntu-latest
+          - node: '8'
+            os: ubuntu-latest
+          - node: '12'
+            os: windows-latest
+          - node: '8'
+            os: windows-latest
+    name: ${{ matrix.node }} ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    env:
+      NODE: ${{ matrix.node }}
+    steps:
+      - uses: actions/checkout@v3
+      - name: Perform fuzzing
+        shell: bash
+        run: |
+          . ./test/release/install.sh
+          if [[ $GITHUB_EVENT_NAME == "pull_request" ]]; then
+            node test/ufuzz/job 5000
+          else
+            node test/ufuzz/job $BASE_URL $TOKEN $GITHUB_RUN_NUMBER
+          fi
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..6000c6c3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/node_modules/
+/npm-debug.log
+tmp/
diff --git a/debian/changelog b/debian/changelog
index dc4c109d..7956eb1e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+uglify-js (3.17.4-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 23 Nov 2022 04:17:32 -0000
+
 uglify-js (3.17.3-2) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/lib/compress.js b/lib/compress.js
index 29543727..cde397cf 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -501,6 +501,23 @@ Compressor.prototype.compress = function(node) {
         if (parent instanceof AST_VarDef) return parent.value === node;
     }
 
+    function make_ref(ref, fixed) {
+        var node = make_node(AST_SymbolRef, ref);
+        node.fixed = fixed || make_node(AST_Undefined, ref);
+        return node;
+    }
+
+    function replace_ref(resolve, fixed) {
+        return function(node) {
+            var ref = resolve(node);
+            var node = make_ref(ref, fixed);
+            var def = ref.definition();
+            def.references.push(node);
+            def.replaced++;
+            return node;
+        };
+    }
+
     var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
     (function(def) {
         def(AST_Node, noop);
@@ -705,22 +722,6 @@ Compressor.prototype.compress = function(node) {
             });
         }
 
-        function make_ref(ref, fixed) {
-            var node = make_node(AST_SymbolRef, ref);
-            node.fixed = fixed || make_node(AST_Undefined, ref);
-            return node;
-        }
-
-        function replace_ref(ref, fixed) {
-            return function() {
-                var node = make_ref(ref, fixed);
-                var def = ref.definition();
-                def.references.push(node);
-                def.replaced++;
-                return node;
-            };
-        }
-
         function ref_once(compressor, def) {
             return compressor.option("unused")
                 && !def.scope.pinned()
@@ -1021,7 +1022,9 @@ Compressor.prototype.compress = function(node) {
                     };
                     left.fixed.assigns = !fixed || !fixed.assigns ? [ ld.orig[0] ] : fixed.assigns.slice();
                     left.fixed.assigns.push(node);
-                    left.fixed.to_binary = replace_ref(left, fixed);
+                    left.fixed.to_binary = replace_ref(function(node) {
+                        return node.left;
+                    }, fixed);
                 } else {
                     left.walk(tw);
                     ld.fixed = false;
@@ -1529,7 +1532,9 @@ Compressor.prototype.compress = function(node) {
                         });
                     };
                     exp.fixed.assigns = fixed && fixed.assigns;
-                    exp.fixed.to_prefix = replace_ref(exp, d.fixed);
+                    exp.fixed.to_prefix = replace_ref(function(node) {
+                        return node.expression;
+                    }, d.fixed);
                 }
             } else {
                 exp.walk(tw);
@@ -2156,7 +2161,7 @@ Compressor.prototype.compress = function(node) {
                         abort = true;
                         folded = make_node(AST_Binary, candidate, {
                             operator: compound,
-                            left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary() : lhs,
+                            left: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_binary(candidate) : lhs,
                             right: rvalue,
                         });
                     }
@@ -2220,7 +2225,7 @@ Compressor.prototype.compress = function(node) {
                     }
                     if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, {
                         operator: candidate.operator,
-                        expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix() : lhs,
+                        expression: lhs.fixed && lhs.definition().fixed ? lhs.fixed.to_prefix(candidate) : lhs,
                     });
                     if (candidate instanceof AST_UnaryPrefix) {
                         clear_write_only(candidate);
@@ -3536,7 +3541,7 @@ Compressor.prototype.compress = function(node) {
             var declare_only, jump, merge_jump;
             var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
             var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
-            var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(in_lambda));
+            var drop_return_void = !(in_try && in_try.bfinally && in_async_generator(scope));
             var multiple_if_returns = has_multiple_if_returns(statements);
             for (var i = statements.length; --i >= 0;) {
                 var stat = statements[i];
@@ -6662,15 +6667,15 @@ Compressor.prototype.compress = function(node) {
             do {
                 var head = first.shift();
                 if (tail.index > head.index) continue;
-                var id = head.definition.id;
-                if (!(id in prev)) continue;
-                var head_refs = references[id];
+                var prev_def = head.definition;
+                if (!(prev_def.id in prev)) continue;
+                var head_refs = references[prev_def.id];
                 if (!head_refs) continue;
                 if (head_refs.start.block !== tail_refs.start.block
                     || !mergeable(head_refs, tail_refs)
                     || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
-                    || compressor.option("webkit") && is_funarg(def) !== is_funarg(head.definition)
-                    || head.definition.const_redefs
+                    || compressor.option("webkit") && is_funarg(def) !== is_funarg(prev_def)
+                    || prev_def.const_redefs
                     || !all(head_refs.scopes, function(scope) {
                         return scope.find_variable(def.name) === def;
                     })) {
@@ -6682,12 +6687,14 @@ Compressor.prototype.compress = function(node) {
                     sym.name = def.name;
                     if (sym instanceof AST_SymbolRef) {
                         def.references.push(sym);
+                        prev_def.replaced++;
                     } else {
                         def.orig.push(sym);
+                        prev_def.eliminated++;
                     }
                 });
-                if (!head.definition.fixed) def.fixed = false;
-                merged[id] = def;
+                if (!prev_def.fixed) def.fixed = false;
+                merged[prev_def.id] = def;
                 changed = true;
                 break;
             } while (first.length);
@@ -7263,6 +7270,7 @@ Compressor.prototype.compress = function(node) {
                         return in_list ? List.skip : make_node(AST_EmptyStatement, node);
                     }
                 }
+                descend_scope();
                 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
                     node.name = null;
                 }
@@ -7357,6 +7365,7 @@ Compressor.prototype.compress = function(node) {
                     }
                     fns_with_marked_args.push(node);
                 }
+                return node;
             }
             if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
                 node.argname.transform(trimmer);
@@ -8823,7 +8832,8 @@ Compressor.prototype.compress = function(node) {
                 var negated = node.clone();
                 negated.operator = op == "&&" ? "||" : "&&";
                 negated.left = left.negate(compressor, first_in_statement);
-                if (negated.operator == negated.right.operator) swap_chain(negated);
+                var negated_rhs = negated.right.tail_node();
+                if (negated_rhs instanceof AST_Binary && negated.operator == negated_rhs.operator) swap_chain(negated);
                 var best = first_in_statement ? best_of_statement : best_of_expression;
                 return op == "&&" ? best(node, negated) : best(negated, node);
             }
@@ -11607,7 +11617,14 @@ Compressor.prototype.compress = function(node) {
     }
 
     function swap_chain(self, compressor) {
-        var rhs = self.right;
+        var rhs = self.right.tail_node();
+        if (rhs !== self.right) {
+            var exprs = self.right.expressions.slice(0, -1);
+            exprs.push(rhs.left);
+            rhs = rhs.clone();
+            rhs.left = make_sequence(self.right, exprs);
+            self.right = rhs;
+        }
         self.left = make_node(AST_Binary, self, {
             operator: self.operator,
             left: self.left,
@@ -11799,15 +11816,16 @@ Compressor.prototype.compress = function(node) {
                     left: self.left.right,
                     right: self.right,
                 });
-                var after = before.optimize(compressor);
+                var after = before.transform(compressor);
                 if (before !== after) {
                     self.left = self.left.left;
                     self.right = after;
                 }
             }
             // x && (y && z) ---> x && y && z
-            // x || (y || z) ---> x || y || z
-            if (self.right instanceof AST_Binary && self.operator == self.right.operator) swap_chain(self, compressor);
+            // w || (x, y || z) ---> w || (x, y) || z
+            var rhs = self.right.tail_node();
+            if (rhs instanceof AST_Binary && self.operator == rhs.operator) swap_chain(self, compressor);
         }
         if (compressor.option("strings") && self.operator == "+") {
             // "foo" + 42 + "" ---> "foo" + 42
@@ -11833,12 +11851,13 @@ Compressor.prototype.compress = function(node) {
                 return self.optimize(compressor);
             }
             // "x" + (y + "z") ---> "x" + y + "z"
-            // x + ("y" + z) ---> x + "y" + z
-            if (self.right instanceof AST_Binary
-                && self.operator == self.right.operator
-                && (self.left.is_string(compressor) && self.right.is_string(compressor)
-                    || self.right.left.is_string(compressor)
-                        && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
+            // w + (x, "y" + z) ---> w + (x, "y") + z
+            var rhs = self.right.tail_node();
+            if (rhs instanceof AST_Binary
+                && self.operator == rhs.operator
+                && (self.left.is_string(compressor) && rhs.is_string(compressor)
+                    || rhs.left.is_string(compressor)
+                        && (self.left.is_constant() || !rhs.right.has_side_effects(compressor)))) {
                 swap_chain(self, compressor);
             }
         }
@@ -12766,34 +12785,19 @@ Compressor.prototype.compress = function(node) {
         }
         if (compressor.option("assignments")) {
             if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
-                var ref;
                 // x = expr1 OP expr2
-                if ((ref = self.right.left) instanceof AST_SymbolRef
-                    && ref.name == self.left.name
+                if (self.right.left instanceof AST_SymbolRef
+                    && self.right.left.name == self.left.name
                     && ASSIGN_OPS[self.right.operator]) {
                     // x = x - 2 ---> x -= 2
-                    if (self.left.fixed) self.left.fixed.to_binary = function() {
-                        return ref;
-                    };
-                    return make_node(AST_Assign, self, {
-                        operator: self.right.operator + "=",
-                        left: self.left,
-                        right: self.right.right,
-                    });
+                    return make_compound(self.right.right);
                 }
-                if ((ref = self.right.right) instanceof AST_SymbolRef
-                    && ref.name == self.left.name
+                if (self.right.right instanceof AST_SymbolRef
+                    && self.right.right.name == self.left.name
                     && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
                     && !self.right.left.has_side_effects(compressor)) {
                     // x = 2 & x ---> x &= 2
-                    if (self.left.fixed) self.left.fixed.to_binary = function() {
-                        return ref;
-                    };
-                    return make_node(AST_Assign, self, {
-                        operator: self.right.operator + "=",
-                        left: self.left,
-                        right: self.right.left,
-                    });
+                    return make_compound(self.right.left);
                 }
             }
             if ((self.operator == "-=" || self.operator == "+="
@@ -12856,6 +12860,18 @@ Compressor.prototype.compress = function(node) {
             return find_try(compressor, level, node, scope, may_throw, sync);
         }
 
+        function make_compound(rhs) {
+            var fixed = self.left.fixed;
+            if (fixed) fixed.to_binary = replace_ref(function(node) {
+                return node.left;
+            }, fixed);
+            return make_node(AST_Assign, self, {
+                operator: self.right.operator + "=",
+                left: self.left,
+                right: rhs,
+            });
+        }
+
         function strip_assignment(def) {
             if (def) def.fixed = false;
             return (self.operator != "=" ? make_node(AST_Binary, self, {
@@ -13101,7 +13117,7 @@ Compressor.prototype.compress = function(node) {
                 operator: "||",
                 left: booleanize(condition),
                 right: alternative,
-            });
+            }).optimize(compressor);
         }
         if (is_false(consequent)) {
             // c ? false : true ---> !c
@@ -13111,20 +13127,20 @@ Compressor.prototype.compress = function(node) {
                 operator: "&&",
                 left: booleanize(condition.negate(compressor)),
                 right: alternative,
-            });
+            }).optimize(compressor);
         }
         // c ? x : true ---> !c || x
         if (is_true(alternative)) return make_node(AST_Binary, self, {
             operator: "||",
             left: booleanize(condition.negate(compressor)),
             right: consequent,
-        });
+        }).optimize(compressor);
         // c ? x : false ---> !!c && x
         if (is_false(alternative)) return make_node(AST_Binary, self, {
             operator: "&&",
             left: booleanize(condition),
             right: consequent,
-        });
+        }).optimize(compressor);
         if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
         return self;
 
diff --git a/package.json b/package.json
index dcaa7df5..5776de16 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
   "description": "JavaScript parser, mangler/compressor and beautifier toolkit",
   "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
   "license": "BSD-2-Clause",
-  "version": "3.17.3",
+  "version": "3.17.4",
   "engines": {
     "node": ">=0.8.0"
   },
diff --git a/test/compress/concat-strings.js b/test/compress/concat-strings.js
index d2790d08..ac252b06 100644
--- a/test/compress/concat-strings.js
+++ b/test/compress/concat-strings.js
@@ -273,6 +273,23 @@ concat_9: {
     expect_stdout: true
 }
 
+concat_sequence: {
+    options = {
+        collapse_vars: true,
+        strings: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        console.log(12 + (a = null, "34" + a));
+    }
+    expect: {
+        console.log(12 + "34" + null);
+    }
+    expect_stdout: "1234null"
+}
+
 issue_3689: {
     options = {
         strings: true,
diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js
index f42cce97..66de5c7d 100644
--- a/test/compress/conditionals.js
+++ b/test/compress/conditionals.js
@@ -3051,3 +3051,20 @@ issue_5694: {
     }
     expect_stdout: "NaN"
 }
+
+issue_5712: {
+    options = {
+        booleans: true,
+        conditionals: true,
+        evaluate: true,
+    }
+    input: {
+        var a = 0;
+        a || (++a).toString() && a && console.log("PASS");
+    }
+    expect: {
+        var a = 0;
+        a || (++a).toString() && a && console.log("PASS");
+    }
+    expect_stdout: "PASS"
+}
diff --git a/test/compress/merge_vars.js b/test/compress/merge_vars.js
index 7082491e..e7b1be3a 100644
--- a/test/compress/merge_vars.js
+++ b/test/compress/merge_vars.js
@@ -3845,3 +3845,47 @@ issue_5471_2: {
     }
     expect_stdout: "PASS"
 }
+
+issue_5714: {
+    options = {
+        inline: true,
+        join_vars: true,
+        loops: true,
+        merge_vars: true,
+        unused: true,
+    }
+    input: {
+        "use strict";
+        console.log(function() {
+            var i = 1;
+            while (i--) {
+                var a = function f(b) {
+                    console.log(b);
+                    var c = function(d) {
+                        console.log(typeof d);
+                    }(console);
+                }();
+                var e = 42;
+            }
+            return e;
+        }());
+    }
+    expect: {
+        "use strict";
+        console.log(function() {
+            for (var i = 1; i--;) {
+                var b = void 0;
+                console.log(b);
+                b = console,
+                console.log(typeof b);
+                var e = 42;
+            }
+            return e;
+        }());
+    }
+    expect_stdout: [
+        "undefined",
+        "object",
+        "42",
+    ]
+}
diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js
index 885887ed..93eccafe 100644
--- a/test/compress/reduce_vars.js
+++ b/test/compress/reduce_vars.js
@@ -7927,3 +7927,138 @@ issue_5623: {
     }
     expect_stdout: "1"
 }
+
+issue_5716_1: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        merge_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var b = [ c, c ], c = function() {
+                return b++ + (a = b);
+            }();
+        }
+        f();
+        console.log(a);
+    }
+    expect: {
+        c = [ c, c ],
+        void (c = ++c);
+        var c;
+        console.log(c);
+    }
+    expect_stdout: "NaN"
+}
+
+issue_5716_2: {
+    options = {
+        collapse_vars: true,
+        inline: true,
+        merge_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var b = [ c, c ], c = function() {
+                return (b += 4) + (a = b += 2);
+            }();
+        }
+        f();
+        console.log(a);
+    }
+    expect: {
+        void (c = c = (c = [ c, c ]) + 4 + 2);
+        var c;
+        console.log(c);
+    }
+    expect_stdout: ",42"
+}
+
+issue_5716_3: {
+    options = {
+        assignments: true,
+        collapse_vars: true,
+        inline: true,
+        merge_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var b = [ c, c ], c = function() {
+                return (b = b + 4) + (a = b += 2);
+            }();
+        }
+        f();
+        console.log(a);
+    }
+    expect: {
+        void (c = c = (c = [ c, c ]) + 4 + 2);
+        var c;
+        console.log(c);
+    }
+    expect_stdout: ",42"
+}
+
+issue_5716_4: {
+    options = {
+        assignments: true,
+        collapse_vars: true,
+        inline: true,
+        merge_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var b = [ c, c ], c = function() {
+                return (b = true | b) + (a = b *= 42);
+            }();
+        }
+        f();
+        console.log(a);
+    }
+    expect: {
+        void (c = c = ((c = [ c, c ]) | true) * 42);
+        var c;
+        console.log(c);
+    }
+    expect_stdout: "42"
+}
+
+issue_5716_5: {
+    options = {
+        assignments: true,
+        reduce_vars: true,
+    }
+    input: {
+        console.log(function() {
+            return 0 || (a = 42 | a);
+            var a = function() {
+                return a;
+            };
+        }());
+    }
+    expect: {
+        console.log(function() {
+            return 0 || (a |= 42);
+            var a = function() {
+                return a;
+            };
+        }());
+    }
+    expect_stdout: "42"
+}
diff --git a/test/compress/rests.js b/test/compress/rests.js
index b18ab0b4..4d020d23 100644
--- a/test/compress/rests.js
+++ b/test/compress/rests.js
@@ -1624,3 +1624,21 @@ issue_5552_4: {
     expect_stdout: "PASS"
     node_version: ">=6"
 }
+
+issue_5705: {
+    options = {
+        reduce_vars: true,
+        rests: true,
+        unused: true,
+    }
+    input: {
+        (function(...a) {
+            var b = { ...a };
+        })(console.log("PASS"));
+    }
+    expect: {
+        (function() {})(console.log("PASS"));
+    }
+    expect_stdout: "PASS"
+    node_version: ">=8.3.0"
+}
diff --git a/test/compress/transform.js b/test/compress/transform.js
index a06d08a9..e3621eb2 100644
--- a/test/compress/transform.js
+++ b/test/compress/transform.js
@@ -127,7 +127,7 @@ if_return: {
                 if (w) {
                     if (y) return;
                 } else if (z) return;
-                return x != y && (x && w(), y && z()), !0;
+                return x != y && (x && w(), y) && z(), !0;
             }
         }
     }
diff --git a/test/compress/yields.js b/test/compress/yields.js
index 8478d541..835eff1c 100644
--- a/test/compress/yields.js
+++ b/test/compress/yields.js
@@ -2018,3 +2018,61 @@ issue_5684: {
     expect_stdout: "PASS"
     node_version: ">=10"
 }
+
+issue_5707: {
+    options = {
+        hoist_props: true,
+        reduce_vars: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+        yields: true,
+    }
+    input: {
+        var a, b;
+        function* f(c = (b = 42, console.log("PASS"))) {}
+        b = f();
+    }
+    expect: {
+        console.log("PASS");
+    }
+    expect_stdout: "PASS"
+    node_version: ">=6"
+}
+
+issue_5710: {
+    options = {
+        conditionals: true,
+        if_return: true,
+    }
+    input: {
+        (async function*() {
+            try {
+                switch (42) {
+                  case 42:
+                    {
+                        if (console.log("PASS"))
+                            return;
+                        return null;
+                    }
+                    break;
+                }
+            } finally {}
+        })().next();
+    }
+    expect: {
+        (async function*() {
+            try {
+                switch (42) {
+                  case 42:
+                    if (console.log("PASS"))
+                        return;
+                    return null;
+                    break;
+                }
+            } finally {}
+        })().next();
+    }
+    expect_stdout: "PASS"
+    node_version: ">=10"
+}

More details

Full run details