add Infinity/NaN, string/number member expressions/calls and Math calls static evaluation

This commit is contained in:
Sebastian McKenzie 2015-06-02 16:18:08 +01:00
parent 43c0a0e65f
commit cafd7f8e39
4 changed files with 113 additions and 22 deletions

View File

@ -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"),

View File

@ -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" })) {
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;
}
}

View File

@ -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);
}
/**

View File

@ -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"],