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.removeDebugger": require("./minification/remove-debugger"),
|
||||||
"minification.removeConsole": require("./minification/remove-console"),
|
"minification.removeConsole": require("./minification/remove-console"),
|
||||||
"utility.inlineEnvironmentVariables": require("./utility/inline-environment-variables"),
|
"utility.inlineEnvironmentVariables": require("./utility/inline-environment-variables"),
|
||||||
"minification.inlineExpressions": require("./minification/inline-expressions"),
|
"minification.constantFolding": require("./minification/constant-folding"),
|
||||||
"minification.deadCodeElimination": require("./minification/dead-code-elimination"),
|
|
||||||
_modules: require("./internal/modules"),
|
_modules: require("./internal/modules"),
|
||||||
"spec.functionName": require("./spec/function-name"),
|
"spec.functionName": require("./spec/function-name"),
|
||||||
|
|
||||||
//- builtin-basic
|
//- builtin-basic
|
||||||
// this is where the bulk of the ES6 transformations take place, none of them require traversal state
|
// 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
|
// so they can all be concatenated together for performance
|
||||||
|
"minification.deadCodeElimination": require("./minification/dead-code-elimination"),
|
||||||
"es7.classProperties": require("./es7/class-properties"),
|
"es7.classProperties": require("./es7/class-properties"),
|
||||||
"es7.trailingFunctionCommas": require("./es7/trailing-function-commas"),
|
"es7.trailingFunctionCommas": require("./es7/trailing-function-commas"),
|
||||||
"es7.asyncFunctions": require("./es7/async-functions"),
|
"es7.asyncFunctions": require("./es7/async-functions"),
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
/* eslint eqeqeq: 0 */
|
/* eslint eqeqeq: 0 */
|
||||||
|
|
||||||
|
const VALID_CALLEES = ["String", "Number", "Math"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk the input `node` and statically evaluate if it's truthy.
|
* 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)) {
|
||||||
return undefined;
|
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()) {
|
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;
|
confident = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,9 +77,19 @@ var collectorVisitor = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
BlockScoped(node, parent, scope) {
|
BlockScoped(node, parent, scope) {
|
||||||
|
if (this.isFunctionDeclaration()) return;
|
||||||
if (scope.path === this) scope = scope.parent;
|
if (scope.path === this) scope = scope.parent;
|
||||||
scope.getBlockParent().registerDeclaration(this);
|
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 = {
|
var renameVisitor = {
|
||||||
@ -132,7 +142,13 @@ export default class Scope {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static globals = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));
|
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
|
* Description
|
||||||
@ -539,14 +555,29 @@ export default class Scope {
|
|||||||
* Description
|
* Description
|
||||||
*/
|
*/
|
||||||
|
|
||||||
isPure(node, constantsOnly) {
|
isPure(node, constantsOnly?: boolean) {
|
||||||
if (t.isIdentifier(node)) {
|
if (t.isIdentifier(node)) {
|
||||||
var bindingInfo = this.getBinding(node.name);
|
var binding = this.getBinding(node.name);
|
||||||
return !!bindingInfo && (!constantsOnly || (constantsOnly && bindingInfo.constant));
|
if (!binding) return false;
|
||||||
|
if (constantsOnly) return binding.constant;
|
||||||
|
return true;
|
||||||
} else if (t.isClass(node)) {
|
} else if (t.isClass(node)) {
|
||||||
return !node.superClass;
|
return !node.superClass || this.isPure(node.superClass, constantsOnly);
|
||||||
} else if (t.isBinary(node)) {
|
} else if (t.isBinary(node)) {
|
||||||
return this.isPure(node.left, constantsOnly) && this.isPure(node.right, constantsOnly);
|
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 {
|
} else {
|
||||||
return t.isPure(node);
|
return t.isPure(node);
|
||||||
}
|
}
|
||||||
@ -821,13 +852,13 @@ export default class Scope {
|
|||||||
* Description
|
* Description
|
||||||
*/
|
*/
|
||||||
|
|
||||||
hasBinding(name: string) {
|
hasBinding(name: string, noGlobals?) {
|
||||||
if (!name) return false;
|
if (!name) return false;
|
||||||
if (this.hasOwnBinding(name)) return true;
|
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 (this.hasUid(name)) return true;
|
||||||
if (includes(Scope.globals, name)) return true;
|
if (!noGlobals && includes(Scope.globals, name)) return true;
|
||||||
if (includes(Scope.contextVariables, name)) return true;
|
if (!noGlobals && includes(Scope.contextVariables, name)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -835,8 +866,8 @@ export default class Scope {
|
|||||||
* Description
|
* Description
|
||||||
*/
|
*/
|
||||||
|
|
||||||
parentHasBinding(name: string) {
|
parentHasBinding(name: string, noGlobals?) {
|
||||||
return this.parent && this.parent.hasBinding(name);
|
return this.parent && this.parent.hasBinding(name, noGlobals);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,10 +8,10 @@
|
|||||||
"LabeledStatement": ["Statement"],
|
"LabeledStatement": ["Statement"],
|
||||||
"VariableDeclaration": ["Statement", "Declaration"],
|
"VariableDeclaration": ["Statement", "Declaration"],
|
||||||
|
|
||||||
"BreakStatement": ["Statement", "Terminatorless"],
|
"BreakStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||||
"ContinueStatement": ["Statement", "Terminatorless"],
|
"ContinueStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||||
"ReturnStatement": ["Statement", "Terminatorless"],
|
"ReturnStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||||
"ThrowStatement": ["Statement", "Terminatorless"],
|
"ThrowStatement": ["Statement", "Terminatorless", "CompletionStatement"],
|
||||||
|
|
||||||
"DoWhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
"DoWhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
||||||
"WhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
"WhileStatement": ["Statement", "BlockParent", "Loop", "While", "Scopable"],
|
||||||
@ -32,8 +32,8 @@
|
|||||||
"FunctionDeclaration": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"],
|
"FunctionDeclaration": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"],
|
||||||
"FunctionExpression": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"],
|
"FunctionExpression": ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"],
|
||||||
|
|
||||||
"BlockStatement": ["Scopable", "BlockParent", "Statement"],
|
"BlockStatement": ["Scopable", "BlockParent", "Block", "Statement"],
|
||||||
"Program": ["Scopable", "BlockParent", "FunctionParent"],
|
"Program": ["Scopable", "BlockParent", "Block", "FunctionParent"],
|
||||||
"CatchClause": ["Scopable"],
|
"CatchClause": ["Scopable"],
|
||||||
|
|
||||||
"LogicalExpression": ["Binary", "Expression"],
|
"LogicalExpression": ["Binary", "Expression"],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user