fix corner cases of catch variable inlining (#4169)

This commit is contained in:
Alex Lam S.L
2020-10-03 00:02:28 +01:00
committed by GitHub
parent 35465d590e
commit baf4903aa7
5 changed files with 193 additions and 26 deletions

View File

@@ -1037,18 +1037,6 @@ merge(Compressor.prototype, {
return false;
}
function find_variable(compressor, name) {
var scope, i = 0;
while (scope = compressor.parent(i++)) {
if (scope instanceof AST_Scope) break;
if (scope instanceof AST_Catch) {
scope = scope.argname.definition().scope;
break;
}
}
return scope.find_variable(name);
}
function make_node(ctor, orig, props) {
if (!props) props = {};
if (orig) {
@@ -4177,13 +4165,7 @@ merge(Compressor.prototype, {
var scopes = [];
self.walk(new TreeWalker(function(node, descend) {
if (!result) return true;
if (node instanceof AST_Catch) {
scopes.push(node.argname.scope);
descend();
scopes.pop();
return true;
}
if (node instanceof AST_Scope) {
if (node instanceof AST_BlockScope) {
if (node === self) return;
scopes.push(node);
descend();
@@ -4191,14 +4173,14 @@ merge(Compressor.prototype, {
return true;
}
if (node instanceof AST_SymbolRef) {
if (self.inlined) {
if (self.inlined || node.redef) {
result = false;
return true;
}
if (self.variables.has(node.name)) return true;
var def = node.definition();
if (member(def.scope, scopes)) return true;
if (scope) {
if (scope && !def.redefined()) {
var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) {
result = "f";
@@ -5173,6 +5155,13 @@ merge(Compressor.prototype, {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
in_use.push(node_def);
if (node.scope !== node_def.scope) {
var redef = node_def.redefined();
if (redef && !(redef.id in in_use_ids)) {
in_use_ids[redef.id] = true;
in_use.push(redef);
}
}
}
if (track_assigns(node_def, node)) add_assigns(node_def, node);
return true;
@@ -6814,7 +6803,7 @@ merge(Compressor.prototype, {
&& !fn.pinned()
&& !(fn.name && fn instanceof AST_Function)
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
&& fn.is_constant_expression(compressor.find_parent(AST_Scope)))
&& fn.is_constant_expression(compressor.find_parent(AST_BlockScope)))
&& (value = can_flatten_body(stat))
&& !fn.contains_this()) {
var replacing = exp === fn || compressor.option("unused") && def.references.length - def.replaced == 1;
@@ -8081,7 +8070,9 @@ merge(Compressor.prototype, {
single_use = false;
} else if (fixed.name && fixed.name.definition() !== def) {
single_use = false;
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
} else if (fixed.parent_scope !== self.scope
|| !(self.scope instanceof AST_Scope)
|| def.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
@@ -8229,7 +8220,7 @@ merge(Compressor.prototype, {
OPT(AST_Undefined, function(self, compressor) {
if (compressor.option("unsafe_undefined")) {
var undef = find_variable(compressor, "undefined");
var undef = compressor.find_parent(AST_BlockScope).find_variable("undefined");
if (undef) {
var ref = make_node(AST_SymbolRef, self, {
name : "undefined",
@@ -8255,7 +8246,7 @@ merge(Compressor.prototype, {
if (lhs && is_atomic(lhs, self)) return self;
if (compressor.option("keep_infinity")
&& !(lhs && !is_atomic(lhs, self))
&& !find_variable(compressor, "Infinity"))
&& !compressor.find_parent(AST_BlockScope).find_variable("Infinity"))
return self;
return make_node(AST_Binary, self, {
operator: "/",
@@ -8271,7 +8262,7 @@ merge(Compressor.prototype, {
OPT(AST_NaN, function(self, compressor) {
var lhs = is_lhs(compressor.self(), compressor.parent());
if (lhs && !is_atomic(lhs, self)
|| find_variable(compressor, "NaN")) {
|| compressor.find_parent(AST_BlockScope).find_variable("NaN")) {
return make_node(AST_Binary, self, {
operator: "/",
left: make_node(AST_Number, self, {

View File

@@ -228,6 +228,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
old_def.defun = new_def.scope;
old_def.orig.concat(old_def.references).forEach(function(node) {
node.redef = true;
node.thedef = new_def;
node.reference(options);
});

View File

@@ -2992,3 +2992,34 @@ issue_4146: {
}
expect_stdout: "function"
}
single_use_catch_redefined: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}

View File

@@ -4853,3 +4853,42 @@ direct_inline: {
}
expect_stdout: "21"
}
direct_inline_catch_redefined: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, a, g());
}
expect_stdout: true
}

View File

@@ -2714,3 +2714,108 @@ issue_2737: {
}
expect_stdout: "function"
}
single_use_catch_redefined: {
options = {
ie8: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
single_use_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect: {
var a = 1;
try {
throw 2;
} catch (a) {
function g() {
return a;
}
}
console.log(g());
}
expect_stdout: true
}
direct_inline_catch_redefined: {
options = {
ie8: true,
inline: true,
reduce_vars: true,
toplevel: true,
}
input: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, f(), g());
}
expect: {
var a = 1;
function f() {
return a;
}
try {
throw 2;
} catch (a) {
function g() {
return a;
}
console.log(a, f(), g());
}
console.log(a, a, g());
}
expect_stdout: true
}