retrofit try-catch-finally as block-scoped (#4178)
- support optional catch binding
This commit is contained in:
12
lib/ast.js
12
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");
|
if (!(this.bfinally instanceof AST_Finally)) throw new Error("bfinally must be AST_Finally");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
var AST_Catch = DEFNODE("Catch", "argname", {
|
var AST_Catch = DEFNODE("Catch", "argname", {
|
||||||
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
$documentation: "A `catch` node; only makes sense as part of a `try` statement",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
argname: "[AST_SymbolCatch] symbol for the exception"
|
argname: "[AST_SymbolCatch?] symbol for the exception, or null if not present",
|
||||||
},
|
},
|
||||||
walk: function(visitor) {
|
walk: function(visitor) {
|
||||||
var node = this;
|
var node = this;
|
||||||
visitor.visit(node, function() {
|
visitor.visit(node, function() {
|
||||||
node.argname.walk(visitor);
|
if (node.argname) node.argname.walk(visitor);
|
||||||
walk_body(node, visitor);
|
walk_body(node, visitor);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_validate: function() {
|
_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);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
var AST_Finally = DEFNODE("Finally", null, {
|
var AST_Finally = DEFNODE("Finally", null, {
|
||||||
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
$documentation: "A `finally` node; only makes sense as part of a `try` statement"
|
||||||
}, AST_Block);
|
}, AST_BlockScope);
|
||||||
|
|
||||||
/* -----[ VAR ]----- */
|
/* -----[ VAR ]----- */
|
||||||
|
|
||||||
|
|||||||
@@ -4457,9 +4457,11 @@ merge(Compressor.prototype, {
|
|||||||
walk_body(node, tw);
|
walk_body(node, tw);
|
||||||
pop();
|
pop();
|
||||||
if (node.bcatch) {
|
if (node.bcatch) {
|
||||||
var def = node.bcatch.argname.definition();
|
if (node.bcatch.argname) {
|
||||||
references[def.id] = false;
|
var def = node.bcatch.argname.definition();
|
||||||
if (def = def.redefined()) references[def.id] = false;
|
references[def.id] = false;
|
||||||
|
if (def = def.redefined()) references[def.id] = false;
|
||||||
|
}
|
||||||
push();
|
push();
|
||||||
if (node.bfinally) segment.block = node.bcatch;
|
if (node.bfinally) segment.block = node.bcatch;
|
||||||
walk_body(node.bcatch, tw);
|
walk_body(node.bcatch, tw);
|
||||||
@@ -7055,7 +7057,7 @@ merge(Compressor.prototype, {
|
|||||||
child = scope;
|
child = scope;
|
||||||
scope = compressor.parent(++level);
|
scope = compressor.parent(++level);
|
||||||
if (scope instanceof AST_Catch) {
|
if (scope instanceof AST_Catch) {
|
||||||
catches[scope.argname.name] = true;
|
if (scope.argname) catches[scope.argname.name] = true;
|
||||||
} else if (scope instanceof AST_DWLoop) {
|
} else if (scope instanceof AST_DWLoop) {
|
||||||
in_loop = [];
|
in_loop = [];
|
||||||
} else if (scope instanceof AST_For) {
|
} else if (scope instanceof AST_For) {
|
||||||
|
|||||||
@@ -1099,10 +1099,12 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_Catch, function(output) {
|
DEFPRINT(AST_Catch, function(output) {
|
||||||
var self = this;
|
var self = this;
|
||||||
output.print("catch");
|
output.print("catch");
|
||||||
output.space();
|
if (self.argname) {
|
||||||
output.with_parens(function() {
|
output.space();
|
||||||
self.argname.print(output);
|
output.with_parens(function() {
|
||||||
});
|
self.argname.print(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output);
|
print_braced(self, output);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1130,9 +1130,12 @@ function parse($TEXT, options) {
|
|||||||
if (is("keyword", "catch")) {
|
if (is("keyword", "catch")) {
|
||||||
var start = S.token;
|
var start = S.token;
|
||||||
next();
|
next();
|
||||||
expect("(");
|
var name = null;
|
||||||
var name = as_symbol(AST_SymbolCatch);
|
if (is("punc", "(")) {
|
||||||
expect(")");
|
next();
|
||||||
|
name = as_symbol(AST_SymbolCatch);
|
||||||
|
expect(")");
|
||||||
|
}
|
||||||
bcatch = new AST_Catch({
|
bcatch = new AST_Catch({
|
||||||
start : start,
|
start : start,
|
||||||
argname : name,
|
argname : name,
|
||||||
|
|||||||
10
lib/scope.js
10
lib/scope.js
@@ -114,6 +114,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
});
|
});
|
||||||
return true;
|
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) {
|
if (node instanceof AST_BlockScope) {
|
||||||
walk_scope(descend);
|
walk_scope(descend);
|
||||||
return true;
|
return true;
|
||||||
@@ -468,7 +476,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
|
|||||||
node.mangled_name = name;
|
node.mangled_name = name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!options.ie8 && node instanceof AST_Catch) {
|
if (!options.ie8 && node instanceof AST_Catch && node.argname) {
|
||||||
var def = node.argname.definition();
|
var def = node.argname.definition();
|
||||||
var redef = defer_redef(def, node.argname);
|
var redef = defer_redef(def, node.argname);
|
||||||
descend();
|
descend();
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
|
if (self.bfinally) self.bfinally = self.bfinally.transform(tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Catch, function(self, 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);
|
self.body = do_list(self.body, tw);
|
||||||
});
|
});
|
||||||
DEF(AST_Definitions, function(self, tw) {
|
DEF(AST_Definitions, function(self, tw) {
|
||||||
|
|||||||
@@ -4987,3 +4987,41 @@ catch_defun: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
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"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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") {
|
if (node.TYPE == "Call" && node.expression.print_to_string() == "console.log") {
|
||||||
return to_sequence(node.args);
|
return to_sequence(node.args);
|
||||||
}
|
}
|
||||||
if (node instanceof U.AST_Catch) {
|
if (node instanceof U.AST_Catch && node.argname) {
|
||||||
descend(node, this);
|
descend(node, this);
|
||||||
node.body.unshift(new U.AST_SimpleStatement({
|
node.body.unshift(new U.AST_SimpleStatement({
|
||||||
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
|
body: wrap_with_console_log(new U.AST_SymbolRef(node.argname)),
|
||||||
|
|||||||
Reference in New Issue
Block a user