aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-10-04 22:30:14 +0100
committerGitHub <noreply@github.com>2020-10-05 05:30:14 +0800
commit8f0521d51d0292cc84782628cda1a9d16b083ed6 (patch)
treeadd858505f6b35a72f76f0a19c5d36f222432725
parentf9946767c9e517561253eed22558fb154daf84d1 (diff)
downloadtracifyjs-8f0521d51d0292cc84782628cda1a9d16b083ed6.tar.gz
tracifyjs-8f0521d51d0292cc84782628cda1a9d16b083ed6.zip
retrofit `try-catch-finally` as block-scoped (#4178)
- support optional catch binding
-rw-r--r--lib/ast.js12
-rw-r--r--lib/compress.js10
-rw-r--r--lib/output.js10
-rw-r--r--lib/parse.js9
-rw-r--r--lib/scope.js10
-rw-r--r--lib/transform.js2
-rw-r--r--test/compress/functions.js38
-rw-r--r--test/reduce.js2
8 files changed, 74 insertions, 19 deletions
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)),