improve unused on built-in functions (#2817)
This commit is contained in:
216
lib/compress.js
216
lib/compress.js
@@ -2113,6 +2113,95 @@ merge(Compressor.prototype, {
|
|||||||
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
|
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convert_to_predicate(obj) {
|
||||||
|
for (var key in obj) {
|
||||||
|
obj[key] = makePredicate(obj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var object_fns = [
|
||||||
|
"constructor",
|
||||||
|
"toString",
|
||||||
|
"valueOf",
|
||||||
|
];
|
||||||
|
var native_fns = {
|
||||||
|
Array: [
|
||||||
|
"indexOf",
|
||||||
|
"join",
|
||||||
|
"lastIndexOf",
|
||||||
|
"slice",
|
||||||
|
].concat(object_fns),
|
||||||
|
Boolean: object_fns,
|
||||||
|
Number: [
|
||||||
|
"toExponential",
|
||||||
|
"toFixed",
|
||||||
|
"toPrecision",
|
||||||
|
].concat(object_fns),
|
||||||
|
Object: object_fns,
|
||||||
|
RegExp: [
|
||||||
|
"test",
|
||||||
|
].concat(object_fns),
|
||||||
|
String: [
|
||||||
|
"charAt",
|
||||||
|
"charCodeAt",
|
||||||
|
"concat",
|
||||||
|
"indexOf",
|
||||||
|
"italics",
|
||||||
|
"lastIndexOf",
|
||||||
|
"match",
|
||||||
|
"replace",
|
||||||
|
"search",
|
||||||
|
"slice",
|
||||||
|
"split",
|
||||||
|
"substr",
|
||||||
|
"substring",
|
||||||
|
"trim",
|
||||||
|
].concat(object_fns),
|
||||||
|
};
|
||||||
|
convert_to_predicate(native_fns);
|
||||||
|
var static_fns = {
|
||||||
|
Array: [
|
||||||
|
"isArray",
|
||||||
|
],
|
||||||
|
Math: [
|
||||||
|
"abs",
|
||||||
|
"acos",
|
||||||
|
"asin",
|
||||||
|
"atan",
|
||||||
|
"ceil",
|
||||||
|
"cos",
|
||||||
|
"exp",
|
||||||
|
"floor",
|
||||||
|
"log",
|
||||||
|
"round",
|
||||||
|
"sin",
|
||||||
|
"sqrt",
|
||||||
|
"tan",
|
||||||
|
"atan2",
|
||||||
|
"pow",
|
||||||
|
"max",
|
||||||
|
"min",
|
||||||
|
],
|
||||||
|
Number: [
|
||||||
|
"isFinite",
|
||||||
|
"isNaN",
|
||||||
|
],
|
||||||
|
Object: [
|
||||||
|
"create",
|
||||||
|
"getOwnPropertyDescriptor",
|
||||||
|
"getOwnPropertyNames",
|
||||||
|
"getPrototypeOf",
|
||||||
|
"isExtensible",
|
||||||
|
"isFrozen",
|
||||||
|
"isSealed",
|
||||||
|
"keys",
|
||||||
|
],
|
||||||
|
String: [
|
||||||
|
"fromCharCode",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
convert_to_predicate(static_fns);
|
||||||
|
|
||||||
// methods to evaluate a constant expression
|
// methods to evaluate a constant expression
|
||||||
(function(def){
|
(function(def){
|
||||||
// If the node has been successfully reduced to a constant,
|
// If the node has been successfully reduced to a constant,
|
||||||
@@ -2278,13 +2367,9 @@ merge(Compressor.prototype, {
|
|||||||
Array: Array,
|
Array: Array,
|
||||||
Math: Math,
|
Math: Math,
|
||||||
Number: Number,
|
Number: Number,
|
||||||
|
Object: Object,
|
||||||
String: String,
|
String: String,
|
||||||
};
|
};
|
||||||
function convert_to_predicate(obj) {
|
|
||||||
for (var key in obj) {
|
|
||||||
obj[key] = makePredicate(obj[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var static_values = {
|
var static_values = {
|
||||||
Math: [
|
Math: [
|
||||||
"E",
|
"E",
|
||||||
@@ -2325,77 +2410,6 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
var object_fns = [
|
|
||||||
"constructor",
|
|
||||||
"toString",
|
|
||||||
"valueOf",
|
|
||||||
];
|
|
||||||
var native_fns = {
|
|
||||||
Array: [
|
|
||||||
"indexOf",
|
|
||||||
"join",
|
|
||||||
"lastIndexOf",
|
|
||||||
"slice",
|
|
||||||
].concat(object_fns),
|
|
||||||
Boolean: object_fns,
|
|
||||||
Number: [
|
|
||||||
"toExponential",
|
|
||||||
"toFixed",
|
|
||||||
"toPrecision",
|
|
||||||
].concat(object_fns),
|
|
||||||
RegExp: [
|
|
||||||
"test",
|
|
||||||
].concat(object_fns),
|
|
||||||
String: [
|
|
||||||
"charAt",
|
|
||||||
"charCodeAt",
|
|
||||||
"concat",
|
|
||||||
"indexOf",
|
|
||||||
"italics",
|
|
||||||
"lastIndexOf",
|
|
||||||
"match",
|
|
||||||
"replace",
|
|
||||||
"search",
|
|
||||||
"slice",
|
|
||||||
"split",
|
|
||||||
"substr",
|
|
||||||
"substring",
|
|
||||||
"trim",
|
|
||||||
].concat(object_fns),
|
|
||||||
};
|
|
||||||
convert_to_predicate(native_fns);
|
|
||||||
var static_fns = {
|
|
||||||
Array: [
|
|
||||||
"isArray",
|
|
||||||
],
|
|
||||||
Math: [
|
|
||||||
"abs",
|
|
||||||
"acos",
|
|
||||||
"asin",
|
|
||||||
"atan",
|
|
||||||
"ceil",
|
|
||||||
"cos",
|
|
||||||
"exp",
|
|
||||||
"floor",
|
|
||||||
"log",
|
|
||||||
"round",
|
|
||||||
"sin",
|
|
||||||
"sqrt",
|
|
||||||
"tan",
|
|
||||||
"atan2",
|
|
||||||
"pow",
|
|
||||||
"max",
|
|
||||||
"min"
|
|
||||||
],
|
|
||||||
Number: [
|
|
||||||
"isFinite",
|
|
||||||
"isNaN",
|
|
||||||
],
|
|
||||||
String: [
|
|
||||||
"fromCharCode",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
convert_to_predicate(static_fns);
|
|
||||||
def(AST_Call, function(compressor, depth) {
|
def(AST_Call, function(compressor, depth) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
|
||||||
@@ -2420,7 +2434,16 @@ merge(Compressor.prototype, {
|
|||||||
if (arg === value) return this;
|
if (arg === value) return this;
|
||||||
args.push(value);
|
args.push(value);
|
||||||
}
|
}
|
||||||
return val[key].apply(val, args);
|
try {
|
||||||
|
return val[key].apply(val, args);
|
||||||
|
} catch (ex) {
|
||||||
|
compressor.warn("Error evaluating {code} [{file}:{line},{col}]", {
|
||||||
|
code: this.print_to_string(),
|
||||||
|
file: this.start.file,
|
||||||
|
line: this.start.line,
|
||||||
|
col: this.start.col
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
@@ -2511,9 +2534,34 @@ merge(Compressor.prototype, {
|
|||||||
if (compressor.option("unsafe")) {
|
if (compressor.option("unsafe")) {
|
||||||
var expr = this.expression;
|
var expr = this.expression;
|
||||||
if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true;
|
if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true;
|
||||||
|
if (expr instanceof AST_Dot
|
||||||
|
&& is_undeclared_ref(expr.expression)
|
||||||
|
&& (static_fns[expr.expression.name] || return_false)(expr.property)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return this.pure || !compressor.pure_funcs(this);
|
return this.pure || !compressor.pure_funcs(this);
|
||||||
});
|
});
|
||||||
|
AST_Node.DEFMETHOD("is_call_pure", return_false);
|
||||||
|
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
|
||||||
|
if (!compressor.option("unsafe")) return;
|
||||||
|
var expr = this.expression;
|
||||||
|
var fns = return_false;
|
||||||
|
if (expr instanceof AST_Array) {
|
||||||
|
fns = native_fns.Array;
|
||||||
|
} else if (expr.is_boolean()) {
|
||||||
|
fns = native_fns.Boolean;
|
||||||
|
} else if (expr.is_number(compressor)) {
|
||||||
|
fns = native_fns.Number;
|
||||||
|
} else if (expr instanceof AST_RegExp) {
|
||||||
|
fns = native_fns.RegExp;
|
||||||
|
} else if (expr.is_string(compressor)) {
|
||||||
|
fns = native_fns.String;
|
||||||
|
} else if (!this.may_throw_on_access(compressor)) {
|
||||||
|
fns = native_fns.Object;
|
||||||
|
}
|
||||||
|
return fns(this.property);
|
||||||
|
});
|
||||||
|
|
||||||
// determine if expression has side effects
|
// determine if expression has side effects
|
||||||
(function(def){
|
(function(def){
|
||||||
@@ -2534,8 +2582,12 @@ merge(Compressor.prototype, {
|
|||||||
return any(this.body, compressor);
|
return any(this.body, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Call, function(compressor){
|
def(AST_Call, function(compressor){
|
||||||
return !this.is_expr_pure(compressor)
|
if (!this.is_expr_pure(compressor)
|
||||||
|| any(this.args, compressor);
|
&& (!this.expression.is_call_pure(compressor)
|
||||||
|
|| this.expression.has_side_effects(compressor))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return any(this.args, compressor);
|
||||||
});
|
});
|
||||||
def(AST_Switch, function(compressor){
|
def(AST_Switch, function(compressor){
|
||||||
return this.expression.has_side_effects(compressor)
|
return this.expression.has_side_effects(compressor)
|
||||||
@@ -3365,6 +3417,12 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_This, return_null);
|
def(AST_This, return_null);
|
||||||
def(AST_Call, function(compressor, first_in_statement){
|
def(AST_Call, function(compressor, first_in_statement){
|
||||||
if (!this.is_expr_pure(compressor)) {
|
if (!this.is_expr_pure(compressor)) {
|
||||||
|
if (this.expression.is_call_pure(compressor)) {
|
||||||
|
var exprs = this.args.slice();
|
||||||
|
exprs.unshift(this.expression.expression);
|
||||||
|
exprs = trim(exprs, compressor, first_in_statement);
|
||||||
|
return exprs && make_sequence(this, exprs);
|
||||||
|
}
|
||||||
if (this.expression instanceof AST_Function
|
if (this.expression instanceof AST_Function
|
||||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||||
var node = this.clone();
|
var node = this.clone();
|
||||||
|
|||||||
@@ -4089,3 +4089,26 @@ cascade_forin: {
|
|||||||
"2",
|
"2",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe_builtin: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
unsafe: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function f(a) {
|
||||||
|
var b = Math.abs(a);
|
||||||
|
return Math.pow(b, 2);
|
||||||
|
}
|
||||||
|
console.log(f(-1), f(2));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function f(a) {
|
||||||
|
return Math.pow(Math.abs(a), 2);
|
||||||
|
}
|
||||||
|
console.log(f(-1), f(2));
|
||||||
|
}
|
||||||
|
expect_stdout: "1 4"
|
||||||
|
}
|
||||||
|
|||||||
@@ -862,3 +862,20 @@ issue_2749: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe_builtin: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(!w).constructor(x);
|
||||||
|
Math.abs(y);
|
||||||
|
[ 1, 2, z ].valueOf();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
w, x;
|
||||||
|
y;
|
||||||
|
z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1194,6 +1194,9 @@ issue_2231_1: {
|
|||||||
console.log(Object.keys(void 0));
|
console.log(Object.keys(void 0));
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
|
expect_warnings: [
|
||||||
|
"WARN: Error evaluating Object.keys(void 0) [test/compress/evaluate.js:1191,20]",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
issue_2231_2: {
|
issue_2231_2: {
|
||||||
@@ -1208,6 +1211,23 @@ issue_2231_2: {
|
|||||||
console.log(Object.getOwnPropertyNames(null));
|
console.log(Object.getOwnPropertyNames(null));
|
||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
|
expect_warnings: [
|
||||||
|
"WARN: Error evaluating Object.getOwnPropertyNames(null) [test/compress/evaluate.js:1208,20]",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_2231_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(Object.keys({ foo: "bar" })[0]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
expect_stdout: "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
self_comparison_1: {
|
self_comparison_1: {
|
||||||
@@ -1330,13 +1350,13 @@ issue_2535_3: {
|
|||||||
}
|
}
|
||||||
expect_stdout: true
|
expect_stdout: true
|
||||||
expect_warnings: [
|
expect_warnings: [
|
||||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1316,20]",
|
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1336,20]",
|
||||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1317,20]",
|
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1337,20]",
|
||||||
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1318,20]",
|
"WARN: Dropping side-effect-free && [test/compress/evaluate.js:1338,20]",
|
||||||
"WARN: Condition left of && always false [test/compress/evaluate.js:1318,20]",
|
"WARN: Condition left of && always false [test/compress/evaluate.js:1338,20]",
|
||||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1319,20]",
|
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1339,20]",
|
||||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1320,20]",
|
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1340,20]",
|
||||||
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1321,20]",
|
"WARN: Dropping side-effect-free || [test/compress/evaluate.js:1341,20]",
|
||||||
"WARN: Condition left of || always true [test/compress/evaluate.js:1321,20]",
|
"WARN: Condition left of || always true [test/compress/evaluate.js:1341,20]",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user