aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-12-19 12:28:38 +0800
committerGitHub <noreply@github.com>2020-12-19 12:28:38 +0800
commite6dd471f8fff84c878153008139150a090c5ba19 (patch)
treeec207ca4b7416a79b9abba17fdda35b44e5ef743
parent0f55bd92f18bd27628f1cfd10c9fb5d70f4d4d29 (diff)
downloadtracifyjs-e6dd471f8fff84c878153008139150a090c5ba19.tar.gz
tracifyjs-e6dd471f8fff84c878153008139150a090c5ba19.zip
support destructuring of `catch` variable (#4412)
-rw-r--r--lib/ast.js10
-rw-r--r--lib/compress.js76
-rw-r--r--lib/parse.js2
-rw-r--r--test/compress/destructured.js40
-rw-r--r--test/ufuzz/index.js42
5 files changed, 120 insertions, 50 deletions
diff --git a/lib/ast.js b/lib/ast.js
index fcbfb710..e966cefa 100644
--- a/lib/ast.js
+++ b/lib/ast.js
@@ -786,7 +786,7 @@ var AST_Try = DEFNODE("Try", "bcatch bfinally", {
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, or null if not present",
+ argname: "[(AST_Destructured|AST_SymbolCatch)?] symbol for the exception, or null if not present",
},
walk: function(visitor) {
var node = this;
@@ -796,9 +796,9 @@ var AST_Catch = DEFNODE("Catch", "argname", {
});
},
_validate: function() {
- if (this.argname != null) {
- if (!(this.argname instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
- }
+ if (this.argname != null) validate_destructured(this.argname, function(node) {
+ if (!(node instanceof AST_SymbolCatch)) throw new Error("argname must be AST_SymbolCatch");
+ });
},
}, AST_Block);
@@ -868,7 +868,7 @@ var AST_Var = DEFNODE("Var", null, {
var AST_VarDef = DEFNODE("VarDef", "name value", {
$documentation: "A variable declaration; only appears in a AST_Definitions node",
$propdoc: {
- name: "[AST_SymbolVar] name of the variable",
+ name: "[AST_Destructured|AST_SymbolVar] name of the variable",
value: "[AST_Node?] initializer, or null of there's no initializer"
},
walk: function(visitor) {
diff --git a/lib/compress.js b/lib/compress.js
index f33b97bc..1aab3680 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -4949,11 +4949,13 @@ 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;
- }
+ if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
+ if (node instanceof AST_SymbolCatch) {
+ var def = node.definition();
+ references[def.id] = false;
+ if (def = def.redefined()) references[def.id] = false;
+ }
+ }, tw);
push();
if (node.bfinally) segment.block = node.bcatch;
walk_body(node.bcatch, tw);
@@ -5317,6 +5319,34 @@ merge(Compressor.prototype, {
var unused_fn_names = [];
var calls_to_drop_args = [];
var fns_with_marked_args = [];
+ var trimmer = new TreeTransformer(function(node) {
+ if (node instanceof AST_DestructuredArray) {
+ var trim = true;
+ for (var i = node.elements.length; --i >= 0;) {
+ var sym = node.elements[i];
+ if (!(sym instanceof AST_SymbolDeclaration)) {
+ node.elements[i] = sym.transform(trimmer);
+ trim = false;
+ } else if (sym.definition().id in in_use_ids) {
+ trim = false;
+ } else if (trim) {
+ node.elements.pop();
+ } else {
+ node.elements[i] = make_node(AST_Hole, sym);
+ }
+ }
+ return node;
+ }
+ if (node instanceof AST_DestructuredKeyVal) {
+ if (!(node.value instanceof AST_SymbolDeclaration)) {
+ node.value = node.value.transform(trimmer);
+ return node;
+ }
+ if (typeof node.key != "string") return node;
+ if (node.value.definition().id in in_use_ids) return node;
+ return List.skip;
+ }
+ });
var tt = new TreeTransformer(function(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
@@ -5383,34 +5413,7 @@ merge(Compressor.prototype, {
for (var a = node.argnames, i = a.length; --i >= 0;) {
var sym = a[i];
if (sym instanceof AST_Destructured) {
- sym.transform(new TreeTransformer(function(node) {
- if (node instanceof AST_DestructuredArray) {
- var trim = true;
- for (var i = node.elements.length; --i >= 0;) {
- var sym = node.elements[i];
- if (!(sym instanceof AST_SymbolFunarg)) {
- node.elements[i] = sym.transform(this);
- trim = false;
- } else if (sym.definition().id in in_use_ids) {
- trim = false;
- } else if (trim) {
- node.elements.pop();
- } else {
- node.elements[i] = make_node(AST_Hole, sym);
- }
- }
- return node;
- }
- if (node instanceof AST_DestructuredKeyVal) {
- if (!(node.value instanceof AST_SymbolFunarg)) {
- node.value = node.value.transform(this);
- return node;
- }
- if (typeof node.key != "string") return node;
- if (node.value.definition().id in in_use_ids) return node;
- return List.skip;
- }
- }));
+ sym.transform(trimmer);
trim = false;
continue;
}
@@ -5429,6 +5432,9 @@ merge(Compressor.prototype, {
fns_with_marked_args.push(node);
}
}
+ if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
+ node.argname.transform(trimmer);
+ }
if (node instanceof AST_Definitions && !(parent instanceof AST_ForIn && parent.init === node)) {
// place uninitialized names at the start
var body = [], head = [], tail = [];
@@ -7263,7 +7269,9 @@ merge(Compressor.prototype, {
self.body = tighten_body(self.body, compressor);
if (compressor.option("dead_code")) {
if (has_declarations_only(self)
- && !(self.bcatch && self.bcatch.argname && !can_drop_symbol(self.bcatch.argname))) {
+ && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
+ return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
+ }, true))) {
var body = [];
if (self.bcatch) {
extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
diff --git a/lib/parse.js b/lib/parse.js
index f760d0de..3b7d15ca 100644
--- a/lib/parse.js
+++ b/lib/parse.js
@@ -1216,7 +1216,7 @@ function parse($TEXT, options) {
var name = null;
if (is("punc", "(")) {
next();
- name = as_symbol(AST_SymbolCatch);
+ name = maybe_destructured(AST_SymbolCatch);
expect(")");
}
bcatch = new AST_Catch({
diff --git a/test/compress/destructured.js b/test/compress/destructured.js
index 8e1cf68c..4c36e451 100644
--- a/test/compress/destructured.js
+++ b/test/compress/destructured.js
@@ -750,6 +750,46 @@ simple_var: {
node_version: ">=6"
}
+drop_catch: {
+ options = {
+ dead_code: true,
+ }
+ input: {
+ try {} catch ({
+ [console.log("FAIL")]: e,
+ }) {} finally {
+ console.log("PASS");
+ }
+ }
+ expect: {
+ console.log("PASS");
+ }
+ expect_stdout: "PASS"
+ node_version: ">=6"
+}
+
+drop_catch_var: {
+ options = {
+ unused: true,
+ }
+ input: {
+ try {
+ throw new Error("PASS");
+ } catch ({ name, message }) {
+ console.log(message);
+ }
+ }
+ expect: {
+ try {
+ throw new Error("PASS");
+ } catch ({ message }) {
+ console.log(message);
+ }
+ }
+ expect_stdout: "PASS"
+ node_version: ">=6"
+}
+
collapse_vars_1: {
options = {
collapse_vars: true,
diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js
index eb38a759..848b22bc 100644
--- a/test/ufuzz/index.js
+++ b/test/ufuzz/index.js
@@ -858,22 +858,44 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
if (n !== 1) {
// the catch var should only be accessible in the catch clause...
// we have to do go through some trouble here to prevent leaking it
- var nameLenBefore = VAR_NAMES.length;
mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
- if (SUPPORT.catch_omit_var && rng(20) == 0) {
+ var block_len = block_vars.length;
+ var nameLenBefore = VAR_NAMES.length;
+ var unique_len = unique_vars.length;
+ var offset = SUPPORT.catch_omit_var ? 0 : SUPPORT.destructuring ? 1 : 2;
+ switch (offset + rng(20 - offset)) {
+ case 0:
s += " catch { ";
- } else {
- var catchName = createVarName(MANDATORY);
- if (!catch_redef) unique_vars.push(catchName);
- s += " catch (" + catchName + ") { ";
+ break;
+ case 1:
+ var name = createVarName(MANDATORY);
+ block_vars.push(name);
+ var message = createVarName(MANDATORY);
+ block_vars.push(message);
+ if (SUPPORT.computed_key && rng(10) == 0) {
+ s += " catch ({ message: " + message + ", ";
+ addAvoidVars([ name ]);
+ s += "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]: " + name;
+ removeAvoidVars([ name ]);
+ s += " }) { ";
+ } else {
+ s += " catch ({ name: " + name + ", message: " + message + " }) { ";
+ }
+ break;
+ default:
+ var name = createVarName(MANDATORY);
+ if (!catch_redef) unique_vars.push(name);
+ s += " catch (" + name + ") { ";
+ break;
}
- var freshCatchName = VAR_NAMES.length !== nameLenBefore;
+ var catches = VAR_NAMES.length - nameLenBefore;
s += defns() + "\n";
s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
s += " }";
- // remove catch name
- if (!catch_redef) unique_vars.pop();
- if (freshCatchName) VAR_NAMES.splice(nameLenBefore, 1);
+ // remove catch variables
+ block_vars.length = block_len;
+ if (catches > 0) VAR_NAMES.splice(nameLenBefore, catches);
+ unique_vars.length = unique_len;
});
}
if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";