retrofit try-catch-finally as block-scoped (#4178)

- support optional catch binding
This commit is contained in:
Alex Lam S.L
2020-10-04 22:30:14 +01:00
committed by GitHub
parent f9946767c9
commit 8f0521d51d
8 changed files with 74 additions and 19 deletions

View 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 != 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 ]----- */

View File

@@ -4457,9 +4457,11 @@ merge(Compressor.prototype, {
walk_body(node, tw);
pop();
if (node.bcatch) {
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) {

View File

@@ -1099,10 +1099,12 @@ function OutputStream(options) {
DEFPRINT(AST_Catch, function(output) {
var self = this;
output.print("catch");
if (self.argname) {
output.space();
output.with_parens(function() {
self.argname.print(output);
});
}
output.space();
print_braced(self, output);
});

View 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);
var name = null;
if (is("punc", "(")) {
next();
name = as_symbol(AST_SymbolCatch);
expect(")");
}
bcatch = new AST_Catch({
start : start,
argname : name,

View 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();

View 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) {

View 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"
}

View 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)),