enhance dead_code (#5014)
This commit is contained in:
@@ -1714,6 +1714,15 @@ merge(Compressor.prototype, {
|
|||||||
return stat instanceof AST_LambdaDefinition;
|
return stat instanceof AST_LambdaDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_last_statement(body, stat) {
|
||||||
|
var index = body.lastIndexOf(stat);
|
||||||
|
if (index < 0) return false;
|
||||||
|
while (++index < body.length) {
|
||||||
|
if (!is_declaration(body[index], true)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function tighten_body(statements, compressor) {
|
function tighten_body(statements, compressor) {
|
||||||
var in_loop, in_try, scope;
|
var in_loop, in_try, scope;
|
||||||
find_loop_scope_try();
|
find_loop_scope_try();
|
||||||
@@ -3168,15 +3177,6 @@ merge(Compressor.prototype, {
|
|||||||
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
|
return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
|
||||||
}
|
}
|
||||||
|
|
||||||
function is_last_statement(body, stat) {
|
|
||||||
var index = body.lastIndexOf(stat);
|
|
||||||
if (index < 0) return false;
|
|
||||||
while (++index < body.length) {
|
|
||||||
if (!is_declaration(body[index], true)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function last_of(predicate) {
|
function last_of(predicate) {
|
||||||
var block = self, stat, level = 0;
|
var block = self, stat, level = 0;
|
||||||
do {
|
do {
|
||||||
@@ -11140,28 +11140,36 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_PropAccess) return true;
|
if (node instanceof AST_PropAccess) return true;
|
||||||
if (!found && node instanceof AST_SymbolRef && node.definition() === def) {
|
if (!found && node instanceof AST_SymbolRef && node.definition() === def) {
|
||||||
if (in_try(level, parent)) return true;
|
if (in_try(level, parent)) return true;
|
||||||
def.fixed = false;
|
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
})) break;
|
})) break;
|
||||||
if (found) return strip_assignment();
|
if (!found) continue;
|
||||||
} else if (parent instanceof AST_Exit) {
|
return strip_assignment(def);
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_Exit) {
|
||||||
if (!local) break;
|
if (!local) break;
|
||||||
if (in_try(level, parent)) break;
|
if (in_try(level, parent)) break;
|
||||||
if (is_reachable(scope, [ def ])) break;
|
if (is_reachable(scope, [ def ])) break;
|
||||||
def.fixed = false;
|
return strip_assignment(def);
|
||||||
return strip_assignment();
|
}
|
||||||
} else if (parent instanceof AST_VarDef) {
|
if (parent instanceof AST_SimpleStatement) {
|
||||||
|
if (!local) break;
|
||||||
|
if (is_reachable(scope, [ def ])) break;
|
||||||
|
var stat;
|
||||||
|
do {
|
||||||
|
stat = parent;
|
||||||
|
parent = compressor.parent(level++);
|
||||||
|
if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
|
||||||
|
} while (is_tail_block(stat, parent));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_VarDef) {
|
||||||
if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
|
if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
|
||||||
if (parent.name.definition() !== def) continue;
|
if (parent.name.definition() !== def) continue;
|
||||||
if (in_try(level, parent)) break;
|
if (in_try(level, parent)) break;
|
||||||
def.fixed = false;
|
return strip_assignment(def);
|
||||||
return strip_assignment();
|
|
||||||
}
|
}
|
||||||
} while (parent instanceof AST_Binary && parent.right === node
|
} while (is_tail(node, parent));
|
||||||
|| parent instanceof AST_Conditional && parent.condition !== node
|
|
||||||
|| parent instanceof AST_Sequence && parent.tail_node() === node
|
|
||||||
|| parent instanceof AST_UnaryPrefix);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compressor.option("sequences")) {
|
if (compressor.option("sequences")) {
|
||||||
@@ -11206,26 +11214,55 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return try_evaluate(compressor, self);
|
return try_evaluate(compressor, self);
|
||||||
|
|
||||||
|
function is_tail(node, parent) {
|
||||||
|
if (parent instanceof AST_Binary) {
|
||||||
|
return parent.right === node || parent.right.is_constant_expression(scope);
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_Conditional) {
|
||||||
|
return parent.condition !== node
|
||||||
|
|| parent.consequent.is_constant_expression(scope)
|
||||||
|
&& parent.alternative.is_constant_expression(scope);
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_Sequence) {
|
||||||
|
var exprs = parent.expressions;
|
||||||
|
var stop = exprs.indexOf(node);
|
||||||
|
if (stop < 0) return false;
|
||||||
|
for (var i = exprs.length; --i > stop;) {
|
||||||
|
if (!exprs[i].is_constant_expression(scope)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (parent instanceof AST_UnaryPrefix) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_tail_block(stat, parent) {
|
||||||
|
if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
|
||||||
|
if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
|
||||||
|
if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
|
||||||
|
if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
|
||||||
|
if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
|
||||||
|
}
|
||||||
|
|
||||||
function in_try(level, node) {
|
function in_try(level, node) {
|
||||||
var right = self.right;
|
var right = self.right;
|
||||||
self.right = make_node(AST_Null, right);
|
self.right = make_node(AST_Null, right);
|
||||||
var may_throw = node.may_throw(compressor);
|
var may_throw = node.may_throw(compressor);
|
||||||
self.right = right;
|
self.right = right;
|
||||||
var parent;
|
for (var parent; parent = compressor.parent(level++); node = parent) {
|
||||||
while (parent = compressor.parent(level++)) {
|
|
||||||
if (parent === scope) return false;
|
if (parent === scope) return false;
|
||||||
if (parent instanceof AST_Try) {
|
if (parent instanceof AST_Try) {
|
||||||
if (parent.bfinally) return true;
|
if (parent.bfinally && parent.bfinally !== node) return true;
|
||||||
if (may_throw && parent.bcatch) return true;
|
if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function strip_assignment() {
|
function strip_assignment(def) {
|
||||||
|
if (def) def.fixed = false;
|
||||||
return (self.operator != "=" ? make_node(AST_Binary, self, {
|
return (self.operator != "=" ? make_node(AST_Binary, self, {
|
||||||
operator: self.operator.slice(0, -1),
|
operator: self.operator.slice(0, -1),
|
||||||
left: self.left,
|
left: self.left,
|
||||||
right: self.right
|
right: self.right,
|
||||||
}) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
|
}) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -939,6 +939,170 @@ catch_return_assign: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch_return_assign_may_throw: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
return e = console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
return console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
finally_return_assign: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} finally {
|
||||||
|
return a = "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function(a) {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} finally {
|
||||||
|
return "PASS";
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
last_assign_statement: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
a = a("PASS");
|
||||||
|
}
|
||||||
|
f(console.log);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
a("PASS");
|
||||||
|
}
|
||||||
|
f(console.log);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
last_assign_if_else: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
a = console.log("foo");
|
||||||
|
else {
|
||||||
|
console.log("bar");
|
||||||
|
a = console.log("baz");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(42);
|
||||||
|
f(null);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
if (a)
|
||||||
|
console.log("foo");
|
||||||
|
else {
|
||||||
|
console.log("bar");
|
||||||
|
console.log("baz");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(42);
|
||||||
|
f(null);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
"baz",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
last_assign_catch: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
e = console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f() {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
last_assign_finally: {
|
||||||
|
options = {
|
||||||
|
dead_code: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
try {
|
||||||
|
throw a.log;
|
||||||
|
} catch (e) {
|
||||||
|
a = e;
|
||||||
|
} finally {
|
||||||
|
a = a("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(console);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
try {
|
||||||
|
throw a.log;
|
||||||
|
} catch (e) {
|
||||||
|
a = e;
|
||||||
|
} finally {
|
||||||
|
a("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(console);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
issue_3578: {
|
issue_3578: {
|
||||||
options = {
|
options = {
|
||||||
dead_code: true,
|
dead_code: true,
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ rm -rf tmp/rollup \
|
|||||||
- "postpublish": "pinst --enable",
|
- "postpublish": "pinst --enable",
|
||||||
- "prepare": "npm run build",
|
- "prepare": "npm run build",
|
||||||
- "prepublishOnly": "pinst --disable && npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all",
|
- "prepublishOnly": "pinst --disable && npm ci && npm run lint:nofix && npm run security && npm run build:bootstrap && npm run test:all",
|
||||||
|
@@ -93 +89 @@
|
||||||
|
- "is-reference": "lukastaegert/is-reference#update-class-features",
|
||||||
|
+ "is-reference": "3.0.0",
|
||||||
--- a/test/cli/index.js
|
--- a/test/cli/index.js
|
||||||
+++ b/test/cli/index.js
|
+++ b/test/cli/index.js
|
||||||
@@ -13,0 +14,3 @@ sander.rimrafSync(__dirname, 'node_modules');
|
@@ -13,0 +14,3 @@ sander.rimrafSync(__dirname, 'node_modules');
|
||||||
|
|||||||
Reference in New Issue
Block a user