enhance `merge_vars` (#4125)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 19 Sep 2020 03:16:23 +0000 (04:16 +0100)
committerGitHub <noreply@github.com>
Sat, 19 Sep 2020 03:16:23 +0000 (11:16 +0800)
lib/compress.js
test/compress/merge_vars.js

index cfd3e2b..c30e4b8 100644 (file)
@@ -4322,7 +4322,7 @@ merge(Compressor.prototype, {
 
     AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
         if (!compressor.option("merge_vars")) return;
-        var self = this, segment = null;
+        var self = this, segment = {};
         var first = [], last = [], index = 0;
         var declarations = new Dictionary();
         var references = Object.create(null);
@@ -4331,9 +4331,9 @@ merge(Compressor.prototype, {
             if (node instanceof AST_Assign) {
                 var sym = node.left;
                 if (!(sym instanceof AST_SymbolRef)) return;
-                if (node.operator != "=") mark(sym);
+                if (node.operator != "=") mark(sym, true, false);
                 node.right.walk(tw);
-                mark(sym, true);
+                mark(sym, false, true);
                 return true;
             }
             if (node instanceof AST_Binary) {
@@ -4357,6 +4357,7 @@ merge(Compressor.prototype, {
             if (node instanceof AST_For) {
                 if (node.init) node.init.walk(tw);
                 push();
+                segment.block = node;
                 if (node.condition) node.condition.walk(tw);
                 node.body.walk(tw);
                 if (node.step) node.step.walk(tw);
@@ -4366,6 +4367,7 @@ merge(Compressor.prototype, {
             if (node instanceof AST_ForIn) {
                 node.object.walk(tw);
                 push();
+                segment.block = node;
                 node.init.walk(tw);
                 node.body.walk(tw);
                 pop();
@@ -4385,12 +4387,14 @@ merge(Compressor.prototype, {
             }
             if (node instanceof AST_IterationStatement) {
                 push();
+                segment.block = node;
                 descend();
                 pop();
                 return true;
             }
             if (node instanceof AST_LabeledStatement) {
                 push();
+                segment.block = node;
                 node.body.walk(tw);
                 pop();
                 return true;
@@ -4401,6 +4405,7 @@ merge(Compressor.prototype, {
                     if (node.name) references[node.name.definition().id] = false;
                 }
                 push();
+                segment.block = node;
                 descend();
                 pop();
                 return true;
@@ -4422,11 +4427,11 @@ merge(Compressor.prototype, {
                 return true;
             }
             if (node instanceof AST_SymbolFunarg) {
-                if (!node.__unused) mark(node, true);
+                if (!node.__unused) mark(node, false, true);
                 return true;
             }
             if (node instanceof AST_SymbolRef) {
-                mark(node);
+                mark(node, true, false);
                 return true;
             }
             if (node instanceof AST_Try) {
@@ -4446,13 +4451,13 @@ merge(Compressor.prototype, {
                 if (!unary_arithmetic[node.operator]) return;
                 var sym = node.expression;
                 if (!(sym instanceof AST_SymbolRef)) return;
-                mark(sym);
+                mark(sym, true, true);
                 return true;
             }
             if (node instanceof AST_VarDef) {
                 if (node.value) {
                     node.value.walk(tw);
-                    mark(node.name, true);
+                    mark(node.name, false, true);
                 } else {
                     var id = node.name.definition().id;
                     if (!(id in references)) {
@@ -4479,7 +4484,7 @@ merge(Compressor.prototype, {
                 if (tail.index > head.index) continue;
                 var id = tail.definition.id;
                 if (!references[id]) continue;
-                if (references[def.id].segment !== references[id].segment) {
+                if (!mergeable()) {
                     skipped.unshift(tail);
                     continue;
                 }
@@ -4510,40 +4515,52 @@ merge(Compressor.prototype, {
             segment = Object.getPrototypeOf(segment);
         }
 
-        function read(def) {
-            prev[def.id] = last.length;
-            last.push({
-                index: index++,
-                definition: def,
-            });
-        }
-
-        function mark(sym, write_only) {
+        function mark(sym, read, write) {
             var def = sym.definition();
             if (def.id in references) {
                 var refs = references[def.id];
                 if (!refs) return;
-                if (refs.segment !== segment) return references[def.id] = false;
+                if (write ? refs.start !== segment : refs.start.block !== segment.block) {
+                    return references[def.id] = false;
+                }
                 refs.push(sym);
-                if (def.id in prev) last[prev[def.id]] = null;
-                read(def);
+                refs.end = segment;
+                if (def.id in prev) {
+                    last[prev[def.id]] = null;
+                } else if (!read) {
+                    return;
+                }
             } else if (self.variables.get(def.name) !== def || compressor.exposed(def)) {
-                references[def.id] = false;
+                return references[def.id] = false;
             } else {
                 var refs = declarations.get(def.id) || [];
                 refs.push(sym);
                 references[def.id] = refs;
-                if (write_only) {
-                    refs.segment = segment;
-                    first.push({
+                if (!read) {
+                    refs.start = segment;
+                    return first.push({
                         index: index++,
                         definition: def,
                     });
-                } else {
-                    refs.segment = self;
-                    read(def);
                 }
+                refs.start = self;
             }
+            prev[def.id] = last.length;
+            last.push({
+                index: index++,
+                definition: def,
+            });
+        }
+
+        function must_visit(base, segment) {
+            return base === segment || base.isPrototypeOf(segment);
+        }
+
+        function mergeable() {
+            var head = references[def.id];
+            var tail = references[id];
+            if (head.start.block !== tail.start.block) return false;
+            return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
         }
     });
 
index c63f33d..f1b0e70 100644 (file)
@@ -344,8 +344,8 @@ issue_4107: {
     }
     expect: {
         (function() {
-            (function(c) {
-                var a = console || c;
+            (function(a) {
+                a = console || a;
                 console.log(typeof a);
             })();
         })();
@@ -512,3 +512,2085 @@ issue_4115: {
     }
     expect_stdout: "undefined"
 }
+
+cross_branch_1_1: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            console.log(x);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            if (a)
+                y = "foo";
+            console.log(y);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_2: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                console.log(y);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_3: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                console.log(y);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_4: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a)
+                console.log(x);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a)
+                console.log(y);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_5: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_6: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a) {
+                console.log(y);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_7: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_8: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            y = "foo";
+            console.log(y);
+            if (a) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_1_9: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            y = "bar";
+            if (a)
+                console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect: {
+        var a;
+        function f() {
+            var y, y;
+            y = "foo";
+            console.log(y);
+            y = "bar";
+            if (a)
+                console.log(y);
+        }
+        a = 0;
+        f();
+        a = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_1: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                if (b)
+                    x = "foo";
+                console.log(x);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                if (b)
+                    y = "foo";
+                console.log(y);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "undefined",
+        "bar",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_2: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                if (b)
+                    console.log(x);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                if (b)
+                    console.log(y);
+            }
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "bar",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_3: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                if (b)
+                    x = "foo";
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                if (b)
+                    y = "foo";
+                console.log(y);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "undefined",
+        "bar",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_4: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                if (b) {
+                    x = "foo";
+                    console.log(x);
+                }
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                if (b) {
+                    y = "foo";
+                    console.log(y);
+                }
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "bar",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_5: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                if (b)
+                    console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                if (b)
+                    console.log(y);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "bar",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_6: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                if (b) {
+                    console.log(x);
+                    y = "bar";
+                }
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                if (b) {
+                    console.log(x);
+                    y = "bar";
+                }
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "undefined",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_7: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+                if (b)
+                    y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+                if (b)
+                    y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_8: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                if (b)
+                    console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                if (b)
+                    console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "bar",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_9: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                if (b)
+                    y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                if (b)
+                    y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_10: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                if (b)
+                    console.log(x);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a) {
+                if (b)
+                    console.log(y);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_11: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                if (b) {
+                    console.log(x);
+                    y = "bar";
+                }
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                if (b) {
+                    console.log(x);
+                    y = "bar";
+                }
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_12: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                if (b)
+                    y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                if (b)
+                    y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_13: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                if (b) {
+                    y = "bar";
+                    console.log(y);
+                }
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a) {
+                console.log(y);
+                if (b) {
+                    y = "bar";
+                    console.log(y);
+                }
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_14: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+                if (b)
+                    console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a) {
+                console.log(y);
+                y = "bar";
+                if (b)
+                    console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_15: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a) {
+                if (b)
+                    y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a) {
+                if (b)
+                    y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "undefined",
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2a_16: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a) {
+                y = "bar";
+                if (b)
+                    console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            console.log(y);
+            if (a) {
+                y = "bar";
+                if (b)
+                    console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "foo",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_1: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            if (b)
+                console.log(x);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a)
+                y = "foo";
+            if (b)
+                console.log(y);
+            y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "bar",
+        "bar",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_2: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            if (b) {
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            if (b) {
+                console.log(x);
+                y = "bar";
+            }
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "undefined",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_3: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            if (b) {
+                console.log(x);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a)
+                y = "foo";
+            if (b) {
+                console.log(y);
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_4: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            console.log(x);
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            console.log(x);
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "undefined",
+        "foo",
+        "undefined",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_5: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            console.log(x);
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a)
+                y = "foo";
+            console.log(y);
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_6: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+            }
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+            }
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_7: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+            }
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                console.log(y);
+            }
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_8: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a)
+                x = "foo";
+            console.log(x);
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a)
+                y = "foo";
+            console.log(y);
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_9: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+            }
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                console.log(y);
+            }
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_10: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            if (a) {
+                x = "foo";
+                console.log(x);
+                y = "bar";
+            }
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            if (a) {
+                y = "foo";
+                console.log(y);
+                y = "bar";
+            }
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_11: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a)
+                console.log(x);
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a)
+                console.log(x);
+            if (b)
+                y = "bar";
+            console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "undefined",
+        "foo",
+        "undefined",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_12: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a)
+                console.log(x);
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a)
+                console.log(y);
+            if (b) {
+                y = "bar";
+                console.log(y);
+            }
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_13: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a)
+                console.log(x);
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var y, y;
+            y = "foo";
+            if (a)
+                console.log(y);
+            y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_14: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+            }
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            if (a) {
+                console.log(x);
+                y = "bar";
+            }
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}
+
+cross_branch_2b_15: {
+    options = {
+        merge_vars: true,
+    }
+    input: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a)
+                y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect: {
+        var a, b;
+        function f() {
+            var x, y;
+            x = "foo";
+            console.log(x);
+            if (a)
+                y = "bar";
+            if (b)
+                console.log(y);
+        }
+        a = 0, b = 0;
+        f();
+        a = 1, b = 0;
+        f();
+        a = 0, b = 1;
+        f();
+        a = 1, b = 1;
+        f();
+    }
+    expect_stdout: [
+        "foo",
+        "foo",
+        "foo",
+        "undefined",
+        "foo",
+        "bar",
+    ]
+}