inlining of static methods & constants (#2211)

- guard by `unsafe`
- support `Array`, `Math`, `Number`, `Object` & `String`

fixes #2207
This commit is contained in:
Alex Lam S.L
2017-07-07 05:35:32 +08:00
committed by GitHub
parent 4b6ca5e742
commit 4f70d2e28c
2 changed files with 177 additions and 24 deletions

View File

@@ -1718,6 +1718,40 @@ merge(Compressor.prototype, {
this._eval = fixed._eval; this._eval = fixed._eval;
return value; return value;
}); });
var global_objs = {
Array: Array,
Boolean: Boolean,
Math: Math,
Number: Number,
RegExp: RegExp,
Object: Object,
String: String,
};
function convert_to_predicate(obj) {
for (var key in obj) {
obj[key] = makePredicate(obj[key]);
}
}
var static_values = {
Math: [
"E",
"LN10",
"LN2",
"LOG2E",
"LOG10E",
"PI",
"SQRT1_2",
"SQRT2",
],
Number: [
"MAX_VALUE",
"MIN_VALUE",
"NaN",
"NEGATIVE_INFINITY",
"POSITIVE_INFINITY",
],
};
convert_to_predicate(static_values);
def(AST_PropAccess, function(compressor){ def(AST_PropAccess, function(compressor){
if (compressor.option("unsafe")) { if (compressor.option("unsafe")) {
var key = this.property; var key = this.property;
@@ -1725,11 +1759,16 @@ merge(Compressor.prototype, {
key = ev(key, compressor); key = ev(key, compressor);
if (key === this.property) return this; if (key === this.property) return this;
} }
var val = ev(this.expression, compressor); var exp = this.expression;
if (val === this.expression) return this; var val;
if (val && HOP(val, key)) { if (exp instanceof AST_SymbolRef && exp.undeclared()) {
return val[key]; if (!(static_values[exp.name] || return_false)(key)) return this;
val = global_objs[exp.name];
} else {
val = ev(exp, compressor);
if (!val || val === exp || !HOP(val, key)) return this;
} }
return val[key];
} }
return this; return this;
}); });
@@ -1739,22 +1778,22 @@ merge(Compressor.prototype, {
"valueOf", "valueOf",
]; ];
var native_fns = { var native_fns = {
Array: makePredicate([ Array: [
"indexOf", "indexOf",
"join", "join",
"lastIndexOf", "lastIndexOf",
"slice", "slice",
].concat(object_fns)), ].concat(object_fns),
Boolean: makePredicate(object_fns), Boolean: object_fns,
Number: makePredicate([ Number: [
"toExponential", "toExponential",
"toFixed", "toFixed",
"toPrecision", "toPrecision",
].concat(object_fns)), ].concat(object_fns),
RegExp: makePredicate([ RegExp: [
"test", "test",
].concat(object_fns)), ].concat(object_fns),
String: makePredicate([ String: [
"charAt", "charAt",
"charCodeAt", "charCodeAt",
"concat", "concat",
@@ -1769,8 +1808,45 @@ merge(Compressor.prototype, {
"substr", "substr",
"substring", "substring",
"trim", "trim",
].concat(object_fns)), ].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: [
"keys",
"getOwnPropertyNames",
],
String: [
"fromCharCode",
],
};
convert_to_predicate(static_fns);
def(AST_Call, function(compressor){ def(AST_Call, function(compressor){
var exp = this.expression; var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) { if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
@@ -1779,18 +1855,23 @@ merge(Compressor.prototype, {
key = ev(key, compressor); key = ev(key, compressor);
if (key === exp.property) return this; if (key === exp.property) return this;
} }
var val = ev(exp.expression, compressor); var val;
if (val === exp.expression) return this; var e = exp.expression;
if ((val && native_fns[val.constructor.name] || return_false)(key)) { if (e instanceof AST_SymbolRef && e.undeclared()) {
var args = []; if (!(static_fns[e.name] || return_false)(key)) return this;
for (var i = 0, len = this.args.length; i < len; i++) { val = global_objs[e.name];
var arg = this.args[i]; } else {
var value = ev(arg, compressor); val = ev(e, compressor);
if (arg === value) return this; if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
args.push(value);
}
return val[key].apply(val, args);
} }
var args = [];
for (var i = 0, len = this.args.length; i < len; i++) {
var arg = this.args[i];
var value = ev(arg, compressor);
if (arg === value) return this;
args.push(value);
}
return val[key].apply(val, args);
} }
return this; return this;
}); });

View File

@@ -1085,3 +1085,75 @@ string_charCodeAt: {
} }
expect_stdout: "NaN" expect_stdout: "NaN"
} }
issue_2207_1: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(String.fromCharCode(65));
console.log(Math.max(3, 6, 2, 7, 3, 4));
console.log(Math.cos(1.2345));
console.log(Math.cos(1.2345) - Math.sin(4.321));
console.log(Math.pow(Math.PI, Math.E - Math.LN10));
}
expect: {
console.log("A");
console.log(7);
console.log(Math.cos(1.2345));
console.log(1.2543732512566947);
console.log(1.6093984514472044);
}
expect_stdout: true
}
issue_2207_2: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect: {
console.log(Math.E);
console.log(Math.LN10);
console.log(Math.LN2);
console.log(Math.LOG2E);
console.log(Math.LOG10E);
console.log(Math.PI);
console.log(Math.SQRT1_2);
console.log(Math.SQRT2);
}
expect_stdout: true
}
issue_2207_3: {
options = {
evaluate: true,
unsafe: true,
}
input: {
console.log(Number.MAX_VALUE);
console.log(Number.MIN_VALUE);
console.log(Number.NaN);
console.log(Number.NEGATIVE_INFINITY);
console.log(Number.POSITIVE_INFINITY);
}
expect: {
console.log(Number.MAX_VALUE);
console.log(5e-324);
console.log(NaN);
console.log(-1/0);
console.log(1/0);
}
expect_stdout: true
}