retrofit `try-catch-finally` as block-scoped (#4178)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sun, 4 Oct 2020 21:30:14 +0000 (22:30 +0100)
committerGitHub <noreply@github.com>
Sun, 4 Oct 2020 21:30:14 +0000 (05:30 +0800)
- support optional catch binding

lib/ast.js
lib/compress.js
lib/output.js
lib/parse.js
lib/scope.js
lib/transform.js
test/compress/functions.js
test/reduce.js

index 7e691c2..ba4e6fd 100644 (file)
@@ -702,28 +702,30 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
             if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
         }
     },
-}, AST_Block);
+}, AST_BlockScope);
 
 var AST_Catch = DEFNODE("Catch", "argname", {
     $documentation: "A `catch` node; only makes sense as part of a `try` statement",
     $propdoc: {
-        argname: "[AST_SymbolCatch] symbol for the exception"
+        argname: "[AST_SymbolCatch?] symbol for the exception, or null if not present",
     },
     walk: function(visitor) {
         var node = this;
         visitor.visit(node, function() {
-            node.argname.walk(visitor);
+            if (node.argname) node.argname.walk(visitor);
             walk_body(node, visitor);
         });
     },
     _validate: function() {
-        if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
+        if (this.argname != null) {
+            if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
+        }
     },
 }, AST_BlockScope);
 
 var AST_Finally = DEFNODE("Finally", null, {
     $documentation: "A `finally` node; only makes sense as part of a `try` statement"
-}, AST_Block);
+}, AST_BlockScope);
 
 /* -----[ VAR ]----- */
 
index 7535d2b..63645a5 100644 (file)
@@ -4457,9 +4457,11 @@ merge(Compressor.prototype, {
                 walk_body(node, tw);
                 pop();
                 if (node.bcatch) {
-                    var def = node.bcatch.argname.definition();
-                    references[def.id] = false;
-                    if (def = def.redefined()) references[def.id] = false;
+                    if (node.bcatch.argname) {
+                        var def = node.bcatch.argname.definition();
+                        references[def.id] = false;
+                        if (def = def.redefined()) references[def.id] = false;
+                    }
                     push();
                     if (node.bfinally) segment.block = node.bcatch;
                     walk_body(node.bcatch, tw);
@@ -7055,7 +7057,7 @@ merge(Compressor.prototype, {
                 child = scope;
                 scope = compressor.parent(++level);
                 if (scope instanceof AST_Catch) {
-                    catches[scope.argname.name] = true;
+                    if (scope.argname) catches[scope.argname.name] = true;
                 } else if (scope instanceof AST_DWLoop) {
                     in_loop = [];
                 } else if (scope instanceof AST_For) {
index c2fc964..d99c61b 100644 (file)
@@ -1099,10 +1099,12 @@ function OutputStream(options) {
     DEFPRINT(AST_Catch, function(output) {
         var self = this;
         output.print("catch");
-        output.space();
-        output.with_parens(function() {
-            self.argname.print(output);
-        });
+        if (self.argname) {
+            output.space();
+            output.with_parens(function() {
+                self.argname.print(output);
+            });
+        }
         output.space();
         print_braced(self, output);
     });
index 3989ae6..f24be3c 100644 (file)
@@ -1130,9 +1130,12 @@ function parse($TEXT, options) {
         if (is("keyword", "catch")) {
             var start = S.token;
             next();
-            expect("(");
-            var name = as_symbol(AST_SymbolCatch);
-            expect(")");
+            var name = null;
+            if (is("punc", "(")) {
+                next();
+                name = as_symbol(AST_SymbolCatch);
+                expect(")");
+            }
             bcatch = new AST_Catch({
                 start   : start,
                 argname : name,
index 359bd88..86fd395 100644 (file)
@@ -114,6 +114,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
             });
             return true;
         }
+        if (node instanceof AST_Try) {
+            walk_scope(function() {
+                walk_body(node, tw);
+            });
+            if (node.bcatch) node.bcatch.walk(tw);
+            if (node.bfinally) node.bfinally.walk(tw);
+            return true;
+        }
         if (node instanceof AST_BlockScope) {
             walk_scope(descend);
             return true;
@@ -468,7 +476,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
             node.mangled_name = name;
             return true;
         }
-        if (!options.ie8 && node instanceof AST_Catch) {
+        if (!options.ie8 && node instanceof AST_Catch && node.argname) {
             var def = node.argname.definition();
             var redef = defer_redef(def, node.argname);
             descend();
index 153713d..13598ea 100644 (file)
@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
         if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
     });
     DEF(AST_Catch, function(self, tw) {
-        self.argname = self.argname.transform(tw);
+        if (self.argname) self.argname = self.argname.transform(tw);
         self.body = do_list(self.body, tw);
     });
     DEF(AST_Definitions, function(self, tw) {
index b68a04a..3abfb30 100644 (file)
@@ -4987,3 +4987,41 @@ catch_defun: {
     }
     expect_stdout: true
 }
+
+catch_no_argname: {
+    options = {
+        inline: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = "PASS";
+        function f() {
+            return a;
+        }
+        try {
+            throw a;
+        } catch {
+            function g() {
+                return a;
+            }
+            console.log(a, f(), g());
+        }
+        console.log(a, f(), g());
+    }
+    expect: {
+        var a = "PASS";
+        try {
+            throw a;
+        } catch {
+            console.log(a, a, a);
+        }
+        console.log(a, a, a);
+    }
+    expect_stdout: [
+        "PASS PASS PASS",
+        "PASS PASS PASS",
+    ]
+    node_version: ">=10"
+}
index 9568835..4ca88ef 100644 (file)
@@ -427,7 +427,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
                     if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
                         return to_sequence(node.args);
                     }
-                    if (node instanceof U.AST_Catch) {
+                    if (node instanceof U.AST_Catch && node.argname) {
                         descend(node, this);
                         node.body.unshift(new U.AST_SimpleStatement({
                             body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),