add Infinity/NaN, string/number member expressions/calls and Math calls static evaluation
This commit is contained in:
parent
43c0a0e65f
commit
cafd7f8e39
@ -7,14 +7,14 @@ export default {
|
||||
"minification.removeDebugger": require("./minification/remove-debugger"),
|
||||
"minification.removeConsole": require("./minification/remove-console"),
|
||||
"utility.inlineEnvironmentVariables": require("./utility/inline-environment-variables"),
|
||||
"minification.inlineExpressions": require("./minification/inline-expressions"),
|
||||
"minification.deadCodeElimination": require("./minification/dead-code-elimination"),
|
||||
"minification.constantFolding": require("./minification/constant-folding"),
|
||||
_modules: require("./internal/modules"),
|
||||
"spec.functionName": require("./spec/function-name"),
|
||||
|
||||
//- builtin-basic
|
||||
// this is where the bulk of the ES6 transformations take place, none of them require traversal state
|
||||
// so they can all be concatenated together for performance
|
||||
"minification.deadCodeElimination": require("./minification/dead-code-elimination"),
|
||||
"es7.classProperties": require("./es7/class-properties"),
|
||||
"es7.trailingFunctionCommas": require("./es7/trailing-function-commas"),
|
||||
"es7.asyncFunctions": require("./es7/async-functions"),
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
/* eslint eqeqeq: 0 */
|
||||
|
||||
const VALID_CALLEES = ["String", "Number", "Math"];
|
||||
|
||||
/**
|
||||
* Walk the input `node` and statically evaluate if it's truthy.
|
||||
*
|
||||
@ -74,8 +76,28 @@ export function evaluate(): { confident: boolean; value: any } {
|
||||
}
|
||||
}
|
||||
|
||||
if (path.isIdentifier({ name: "undefined" })) {
|
||||
return undefined;
|
||||
if (path.isIdentifier() && !path.scope.hasBinding(node.name, true)) {
|
||||
if (node.name === "undefined") {
|
||||
return undefined;
|
||||
} else if (node.name === "Infinity") {
|
||||
return Infinity;
|
||||
} else if (node.name === "NaN") {
|
||||
return NaN;
|
||||
}
|
||||
}
|
||||
|
||||
// "foo".length
|
||||
if (path.isMemberExpression() && !path.parentPath.isCallExpression({ callee: node })) {
|
||||
let property = path.get("property");
|
||||
let object = path.get("object");
|
||||
|
||||
if (object.isLiteral() && property.isIdentifier()) {
|
||||
let value = object.node.value;
|
||||
let type = typeof value;
|
||||
if (type === "number" || type === "string") {
|
||||
return value[property.node.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((path.isIdentifier() || path.isMemberExpression()) && path.isReferenced()) {
|
||||
@ -134,6 +156,44 @@ export function evaluate(): { confident: boolean; value: any } {
|
||||
}
|
||||
}
|
||||
|
||||
if (path.isCallExpression()) {
|
||||
var callee = path.get("callee");
|
||||
var context;
|
||||
var func;
|
||||
|
||||
// Number(1);
|
||||
if (callee.isIdentifier() && !path.scope.getBinding(callee.node.name, true) && VALID_CALLEES.indexOf(callee.node.name) >= 0) {
|
||||
func = global[node.callee.name];
|
||||
}
|
||||
|
||||
if (callee.isMemberExpression()) {
|
||||
let object = callee.get("object");
|
||||
var property = callee.get("property");
|
||||
|
||||
// Math.min(1, 2)
|
||||
if (object.isIdentifier() && property.isIdentifier() && VALID_CALLEES.indexOf(object.node.name) >= 0) {
|
||||
context = global[object.node.name];
|
||||
func = context[property.node.name];
|
||||
}
|
||||
|
||||
// "abc".charCodeAt(4)
|
||||
if (object.isLiteral() && property.isIdentifier()) {
|
||||
let type = typeof object.node.value;
|
||||
if (type === "string" || type === "number") {
|
||||
context = object.node.value;
|
||||
func = context[property.node.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (func) {
|
||||
var args = path.get("arguments").map(evaluate);
|
||||
if (!confident) return;
|
||||
|
||||
return func.apply(context, args);
|
||||
}
|
||||
}
|
||||
|
||||
confident = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,9 +77,19 @@ var collectorVisitor = {
|
||||
},
|
||||
|
||||
BlockScoped(node, parent, scope) {
|
||||
if (this.isFunctionDeclaration()) return;
|
||||
if (scope.path === this) scope = scope.parent;
|
||||
scope.getBlockParent().registerDeclaration(this);
|
||||
}
|
||||
},
|
||||
|
||||
Block(node, parent, scope) {
|
||||
var paths = this.get("body");
|
||||
for (var path of (paths: Array)) {
|
||||
if (path.isFunctionDeclaration()) {
|
||||
scope.getBlockParent().registerDeclaration(path);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
var renameVisitor = {
|
||||
@ -132,7 +142,13 @@ export default class Scope {
|
||||
}
|
||||
|
||||
static globals = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));
|
||||
static contextVariables = ["this", "arguments", "super", "undefined"];
|
||||
|
||||
static contextVariables = [
|
||||
"arguments",
|
||||
"undefined",
|
||||
"Infinity",
|
||||
"NaN"
|
||||
];
|
||||
|
||||
/**
|
||||
* Description
|
||||
@ -539,14 +555,29 @@ export default class Scope {
|
||||
* Description
|
||||
*/
|
||||
|
||||
isPure(node, constantsOnly) {
|
||||
isPure(node, constantsOnly?: boolean) {
|
||||
if (t.isIdentifier(node)) {
|
||||
var bindingInfo = this.getBinding(node.name);
|
||||
return !!bindingInfo && (!constantsOnly || (constantsOnly && bindingInfo.constant));
|
||||
var binding = this.getBinding(node.name);
|
||||
if (!binding) return false;
|
||||
if (constantsOnly) return binding.constant;
|
||||
return true;
|
||||
} else if (t.isClass(node)) {
|
||||
return !node.superClass;
|
||||
return !node.superClass || this.isPure(node.superClass, constantsOnly);
|
||||
} else if (t.isBinary(node)) {
|
||||
return this.isPure(node.left, constantsOnly) && this.isPure(node.right, constantsOnly);
|
||||
} else if (t.isArrayExpression(node)) {
|
||||
for (var elem of (node.elements: Array)) {
|
||||
if (!this.isPure(elem, constantsOnly)) return false;
|
||||
}
|
||||
return true;
|
||||
} else if (t.isObjectExpression(node)) {
|
||||
for (var prop of (node.properties: Array)) {
|
||||
if (!this.isPure(prop, constantsOnly)) return false;
|
||||
}
|
||||
return true;
|
||||
} else if (t.isProperty(node)) {
|
||||
if (node.computed && !t.isPure(node.key, constantsOnly)) return false;
|
||||
return t.isPure(node.value, constantsOnly);
|
||||
} else {
|
||||
return t.isPure(node);
|
||||
}
|
||||
@ -821,13 +852,13 @@ export default class Scope {
|
||||
* Description
|
||||
*/
|
||||
|
||||
hasBinding(name: string) {
|
||||
hasBinding(name: string, noGlobals?) {
|
||||
if (!name) return false;
|
||||
if (this.hasOwnBinding(name)) return true;
|
||||
if (this.parentHasBinding(name)) return true;
|
||||
if (this.parentHasBinding(name, noGlobals)) return true;
|
||||
if (this.hasUid(name)) return true;
|
||||
if (includes(Scope.globals, name)) return true;
|
||||
if (includes(Scope.contextVariables, name)) return true;
|
||||
if (!noGlobals && includes(Scope.globals, name)) return true;
|
||||
if (!noGlobals && includes(Scope.contextVariables, name)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -835,8 +866,8 @@ export default class Scope {
|
||||
* Description
|
||||
*/
|
||||
|
||||
parentHasBinding(name: string) {
|
||||
return this.parent && this.parent.hasBinding(name);
|
||||
parentHasBinding(name: string, noGlobals?) {
|
||||
return this.parent && this.parent.hasBinding(name, noGlobals);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -8,10 +8,10 @@
|
||||
"LabeledStatement": ["Statement"],
|
||||
"VariableDeclaration": ["Statement", "Declaration"],
|
||||
|
||||
"BreakStatement": ["Statement", "Terminatorless"],
|
||||
"ContinueStatement": ["Statement", "Terminatorless"],
|
||||
"ReturnStatement": ["Statement", "Terminatorless"],
|
||||
"ThrowStatement": ["Statement", "Terminatorless"],
|
||||
"BreakStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||
"ContinueStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||
"ReturnStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||
"ThrowStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||
|
||||
"DoWhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
||||
"WhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
||||
@ -32,8 +32,8 @@
|
||||
"FunctionDeclaration": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"],
|
||||
"FunctionExpression": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"],
|
||||
|
||||
"BlockStatement": ["Scopable", "BlockParent", "Statement"],
|
||||
"Program": ["Scopable", "BlockParent", "FunctionParent"],
|
||||
"BlockStatement": ["Scopable", "BlockParent", "Block", "Statement"],
|
||||
"Program": ["Scopable", "BlockParent", "Block", "FunctionParent"],
|
||||
"CatchClause": ["Scopable"],
|
||||
|
||||
"LogicalExpression": ["Binary", "Expression"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user