From: Alex Lam S.L Date: Sun, 4 Oct 2020 21:30:14 +0000 (+0100) Subject: retrofit `try-catch-finally` as block-scoped (#4178) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=8f0521d51d0292cc84782628cda1a9d16b083ed6;p=UglifyJS.git retrofit `try-catch-finally` as block-scoped (#4178) - support optional catch binding --- diff --git a/lib/ast.js b/lib/ast.js index 7e691c2c..ba4e6fde 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -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 ]----- */ diff --git a/lib/compress.js b/lib/compress.js index 7535d2bc..63645a5e 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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) { diff --git a/lib/output.js b/lib/output.js index c2fc964a..d99c61b8 100644 --- a/lib/output.js +++ b/lib/output.js @@ -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); }); diff --git a/lib/parse.js b/lib/parse.js index 3989ae65..f24be3cb 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -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, diff --git a/lib/scope.js b/lib/scope.js index 359bd883..86fd3957 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -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(); diff --git a/lib/transform.js b/lib/transform.js index 153713dc..13598ea1 100644 --- a/lib/transform.js +++ b/lib/transform.js @@ -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) { diff --git a/test/compress/functions.js b/test/compress/functions.js index b68a04aa..3abfb30f 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -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" +} diff --git a/test/reduce.js b/test/reduce.js index 95688358..4ca88eff 100644 --- a/test/reduce.js +++ b/test/reduce.js @@ -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)),