aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-07-15 15:16:11 +0800
committerGitHub <noreply@github.com>2017-07-15 15:16:11 +0800
commita5ffe2c23fdfaf13f3466a01d9dd9d590c5e8672 (patch)
tree1c208aca7fc34b06470755bb9127da40b91347c7
parent9282e7b0c6f20bc95ba3d2bab2bbaccebab03c9a (diff)
downloadtracifyjs-a5ffe2c23fdfaf13f3466a01d9dd9d590c5e8672.tar.gz
tracifyjs-a5ffe2c23fdfaf13f3466a01d9dd9d590c5e8672.zip
drop `unused` builtin globals under `unsafe` (#2236)
fixes #2233
-rw-r--r--lib/compress.js85
-rw-r--r--lib/scope.js8
-rw-r--r--test/compress/dead-code.js79
3 files changed, 124 insertions, 48 deletions
diff --git a/lib/compress.js b/lib/compress.js
index 0330c682..6dc7b725 100644
--- a/lib/compress.js
+++ b/lib/compress.js
@@ -689,6 +689,16 @@ merge(Compressor.prototype, {
return false;
}
+ function is_undeclared_ref(node) {
+ return node instanceof AST_SymbolRef && node.definition().undeclared;
+ }
+
+ var global_names = makePredicate("Array Boolean console Error Function Math Number RegExp Object String");
+ AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
+ return !this.definition().undeclared
+ || compressor.option("unsafe") && global_names(this.name);
+ });
+
function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10;
do {
@@ -758,7 +768,7 @@ merge(Compressor.prototype, {
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|| node instanceof AST_Debugger
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
- || node instanceof AST_SymbolRef && node.undeclared()
+ || node instanceof AST_SymbolRef && !node.is_declared(compressor)
|| node instanceof AST_Try
|| node instanceof AST_With
|| parent instanceof AST_For && node !== parent.init) {
@@ -1320,12 +1330,12 @@ merge(Compressor.prototype, {
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
- var pure_getters = compressor.option("pure_getters");
- return !pure_getters || this._throw_on_access(pure_getters);
+ return !compressor.option("pure_getters")
+ || this._dot_throw(compressor);
});
- function is_strict(pure_getters) {
- return /strict/.test(pure_getters);
+ function is_strict(compressor) {
+ return /strict/.test(compressor.option("pure_getters"));
}
def(AST_Node, is_strict);
@@ -1333,8 +1343,8 @@ merge(Compressor.prototype, {
def(AST_Undefined, return_true);
def(AST_Constant, return_false);
def(AST_Array, return_false);
- def(AST_Object, function(pure_getters) {
- if (!is_strict(pure_getters)) return false;
+ def(AST_Object, function(compressor) {
+ if (!is_strict(compressor)) return false;
for (var i = this.properties.length; --i >=0;)
if (this.properties[i].value instanceof AST_Accessor) return true;
return false;
@@ -1344,37 +1354,38 @@ merge(Compressor.prototype, {
def(AST_UnaryPrefix, function() {
return this.operator == "void";
});
- def(AST_Binary, function(pure_getters) {
+ def(AST_Binary, function(compressor) {
switch (this.operator) {
case "&&":
- return this.left._throw_on_access(pure_getters);
+ return this.left._dot_throw(compressor);
case "||":
- return this.left._throw_on_access(pure_getters)
- && this.right._throw_on_access(pure_getters);
+ return this.left._dot_throw(compressor)
+ && this.right._dot_throw(compressor);
default:
return false;
}
})
- def(AST_Assign, function(pure_getters) {
+ def(AST_Assign, function(compressor) {
return this.operator == "="
- && this.right._throw_on_access(pure_getters);
+ && this.right._dot_throw(compressor);
})
- def(AST_Conditional, function(pure_getters) {
- return this.consequent._throw_on_access(pure_getters)
- || this.alternative._throw_on_access(pure_getters);
+ def(AST_Conditional, function(compressor) {
+ return this.consequent._dot_throw(compressor)
+ || this.alternative._dot_throw(compressor);
})
- def(AST_Sequence, function(pure_getters) {
- return this.expressions[this.expressions.length - 1]._throw_on_access(pure_getters);
+ def(AST_Sequence, function(compressor) {
+ return this.expressions[this.expressions.length - 1]._dot_throw(compressor);
});
- def(AST_SymbolRef, function(pure_getters) {
+ def(AST_SymbolRef, function(compressor) {
if (this.is_undefined) return true;
- if (!is_strict(pure_getters)) return false;
+ if (!is_strict(compressor)) return false;
+ if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
if (this.is_immutable()) return false;
var fixed = this.fixed_value();
- return !fixed || fixed._throw_on_access(pure_getters);
+ return !fixed || fixed._dot_throw(compressor);
});
})(function(node, func) {
- node.DEFMETHOD("_throw_on_access", func);
+ node.DEFMETHOD("_dot_throw", func);
});
/* -----[ boolean/negation helpers ]----- */
@@ -1735,11 +1746,8 @@ merge(Compressor.prototype, {
});
var global_objs = {
Array: Array,
- Boolean: Boolean,
Math: Math,
Number: Number,
- RegExp: RegExp,
- Object: Object,
String: String,
};
function convert_to_predicate(obj) {
@@ -1776,7 +1784,7 @@ merge(Compressor.prototype, {
}
var exp = this.expression;
var val;
- if (exp instanceof AST_SymbolRef && exp.undeclared()) {
+ if (is_undeclared_ref(exp)) {
if (!(static_values[exp.name] || return_false)(key)) return this;
val = global_objs[exp.name];
} else {
@@ -1868,7 +1876,7 @@ merge(Compressor.prototype, {
}
var val;
var e = exp.expression;
- if (e instanceof AST_SymbolRef && e.undeclared()) {
+ if (is_undeclared_ref(e)) {
if (!(static_fns[e.name] || return_false)(key)) return this;
val = global_objs[e.name];
} else {
@@ -2050,7 +2058,7 @@ merge(Compressor.prototype, {
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor){
- return this.undeclared();
+ return !this.is_declared(compressor);
});
def(AST_SymbolDeclaration, return_false);
def(AST_Object, function(compressor){
@@ -2684,8 +2692,8 @@ merge(Compressor.prototype, {
}
return expression;
});
- def(AST_SymbolRef, function() {
- return this.undeclared() ? this : null;
+ def(AST_SymbolRef, function(compressor) {
+ return this.is_declared(compressor) ? null : this;
});
def(AST_Object, function(compressor, first_in_statement){
var values = trim(this.properties, compressor, first_in_statement);
@@ -3147,7 +3155,7 @@ merge(Compressor.prototype, {
self.args.length = last;
}
if (compressor.option("unsafe")) {
- if (exp instanceof AST_SymbolRef && exp.undeclared()) {
+ if (is_undeclared_ref(exp)) {
switch (exp.name) {
case "Array":
if (self.args.length != 1) {
@@ -3274,8 +3282,7 @@ merge(Compressor.prototype, {
}
}
if (compressor.option("unsafe_Func")
- && exp instanceof AST_SymbolRef
- && exp.undeclared()
+ && is_undeclared_ref(exp)
&& exp.name == "Function") {
// new Function() => function(){}
if (self.args.length == 0) return make_node(AST_Function, self, {
@@ -3392,9 +3399,7 @@ merge(Compressor.prototype, {
while (name.expression) {
name = name.expression;
}
- if (name instanceof AST_SymbolRef
- && name.name == "console"
- && name.undeclared()) {
+ if (is_undeclared_ref(name) && name.name == "console") {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
@@ -3415,7 +3420,7 @@ merge(Compressor.prototype, {
OPT(AST_New, function(self, compressor){
if (compressor.option("unsafe")) {
var exp = self.expression;
- if (exp instanceof AST_SymbolRef && exp.undeclared()) {
+ if (is_undeclared_ref(exp)) {
switch (exp.name) {
case "Object":
case "RegExp":
@@ -3709,7 +3714,7 @@ merge(Compressor.prototype, {
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") {
var expr = self.right.expression;
- if (expr instanceof AST_SymbolRef ? !expr.undeclared()
+ if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
: !(expr instanceof AST_PropAccess && compressor.option("ie8"))) {
self.right = expr;
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
@@ -4035,7 +4040,7 @@ merge(Compressor.prototype, {
}
// testing against !self.scope.uses_with first is an optimization
if (!compressor.option("ie8")
- && self.undeclared()
+ && is_undeclared_ref(self)
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
@@ -4454,7 +4459,7 @@ merge(Compressor.prototype, {
&& self.expression instanceof AST_Dot
&& self.expression.property == "prototype") {
var exp = self.expression.expression;
- if (exp instanceof AST_SymbolRef && exp.undeclared()) switch (exp.name) {
+ if (is_undeclared_ref(exp)) switch (exp.name) {
case "Array":
self.expression = make_node(AST_Array, self.expression, {
elements: []
diff --git a/lib/scope.js b/lib/scope.js
index f8ecedb5..df7b2076 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -373,14 +373,6 @@ AST_Symbol.DEFMETHOD("unreferenced", function(){
&& !(this.scope.uses_eval || this.scope.uses_with);
});
-AST_Symbol.DEFMETHOD("undeclared", function(){
- return this.definition().undeclared;
-});
-
-AST_LabelRef.DEFMETHOD("undeclared", return_false);
-
-AST_Label.DEFMETHOD("undeclared", return_false);
-
AST_Symbol.DEFMETHOD("definition", function(){
return this.thedef;
});
diff --git a/test/compress/dead-code.js b/test/compress/dead-code.js
index 0fcbbe43..abf5297c 100644
--- a/test/compress/dead-code.js
+++ b/test/compress/dead-code.js
@@ -230,3 +230,82 @@ accessor: {
}
expect: {}
}
+
+issue_2233_1: {
+ options = {
+ pure_getters: "strict",
+ side_effects: true,
+ unsafe: true,
+ }
+ input: {
+ Array.isArray;
+ Boolean;
+ console.log;
+ Error.name;
+ Function.length;
+ Math.random;
+ Number.isNaN;
+ RegExp;
+ Object.defineProperty;
+ String.fromCharCode;
+ }
+ expect: {}
+ expect_stdout: true
+}
+
+issue_2233_2: {
+ options = {
+ pure_getters: "strict",
+ reduce_vars: true,
+ side_effects: true,
+ unsafe: true,
+ unused: true,
+ }
+ input: {
+ var RegExp;
+ Array.isArray;
+ RegExp;
+ UndeclaredGlobal;
+ function foo() {
+ var Number;
+ AnotherUndeclaredGlobal;
+ Math.sin;
+ Number.isNaN;
+ }
+ }
+ expect: {
+ var RegExp;
+ UndeclaredGlobal;
+ function foo() {
+ var Number;
+ AnotherUndeclaredGlobal;
+ Number.isNaN;
+ }
+ }
+}
+
+issue_2233_3: {
+ options = {
+ pure_getters: "strict",
+ reduce_vars: true,
+ side_effects: true,
+ toplevel: true,
+ unsafe: true,
+ unused: true,
+ }
+ input: {
+ var RegExp;
+ Array.isArray;
+ RegExp;
+ UndeclaredGlobal;
+ function foo() {
+ var Number;
+ AnotherUndeclaredGlobal;
+ Math.sin;
+ Number.isNaN;
+ }
+ }
+ expect: {
+ UndeclaredGlobal;
+ }
+}