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;
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){
if (compressor.option("unsafe")) {
var key = this.property;
@@ -1725,11 +1759,16 @@ merge(Compressor.prototype, {
key = ev(key, compressor);
if (key === this.property) return this;
}
var val = ev(this.expression, compressor);
if (val === this.expression) return this;
if (val && HOP(val, key)) {
return val[key];
var exp = this.expression;
var val;
if (exp instanceof AST_SymbolRef && exp.undeclared()) {
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;
});
@@ -1739,22 +1778,22 @@ merge(Compressor.prototype, {
"valueOf",
];
var native_fns = {
Array: makePredicate([
Array: [
"indexOf",
"join",
"lastIndexOf",
"slice",
].concat(object_fns)),
Boolean: makePredicate(object_fns),
Number: makePredicate([
].concat(object_fns),
Boolean: object_fns,
Number: [
"toExponential",
"toFixed",
"toPrecision",
].concat(object_fns)),
RegExp: makePredicate([
].concat(object_fns),
RegExp: [
"test",
].concat(object_fns)),
String: makePredicate([
].concat(object_fns),
String: [
"charAt",
"charCodeAt",
"concat",
@@ -1769,8 +1808,45 @@ merge(Compressor.prototype, {
"substr",
"substring",
"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){
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
@@ -1779,18 +1855,23 @@ merge(Compressor.prototype, {
key = ev(key, compressor);
if (key === exp.property) return this;
}
var val = ev(exp.expression, compressor);
if (val === exp.expression) return this;
if ((val && native_fns[val.constructor.name] || return_false)(key)) {
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);
var val;
var e = exp.expression;
if (e instanceof AST_SymbolRef && e.undeclared()) {
if (!(static_fns[e.name] || return_false)(key)) return this;
val = global_objs[e.name];
} else {
val = ev(e, compressor);
if (val === e || !(val && native_fns[val.constructor.name] || return_false)(key)) return this;
}
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;
});

View File

@@ -1085,3 +1085,75 @@ string_charCodeAt: {
}
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
}