start migration of core from nodes to paths

This commit is contained in:
Sebastian McKenzie 2015-03-14 01:00:02 +11:00
parent 41a8257005
commit c906bd3edc
38 changed files with 273 additions and 205 deletions

View File

@ -464,7 +464,7 @@ export default class File {
this.scope = this.path.scope; this.scope = this.path.scope;
this.ast = ast; this.ast = ast;
traverse(ast, { this.path.traverse({
enter(node, parent, scope) { enter(node, parent, scope) {
if (this.isScope()) { if (this.isScope()) {
for (var key in scope.bindings) { for (var key in scope.bindings) {
@ -472,7 +472,7 @@ export default class File {
} }
} }
} }
}, this.scope); });
} }
transform(ast) { transform(ast) {

View File

@ -27,7 +27,7 @@ export default function (exports, opts) {
}; };
exports.JSXNamespacedName = function (node, parent, scope, file) { exports.JSXNamespacedName = function (node, parent, scope, file) {
throw file.errorWithNode(node, messages.get("JSXNamespacedTags")); throw this.errorWithNode(messages.get("JSXNamespacedTags"));
}; };
exports.JSXMemberExpression = { exports.JSXMemberExpression = {

View File

@ -219,7 +219,7 @@ export default class ReplaceSupers {
var thisReference; var thisReference;
if (isIllegalBareSuper(node, parent)) { if (isIllegalBareSuper(node, parent)) {
throw this.file.errorWithNode(node, messages.get("classesIllegalBareSuper")); throw this.errorWithNode(messages.get("classesIllegalBareSuper"));
} }
if (t.isCallExpression(node)) { if (t.isCallExpression(node)) {

View File

@ -45,7 +45,7 @@ var importsVisitor = {
ImportDeclaration: { ImportDeclaration: {
enter(node, parent, scope, formatter) { enter(node, parent, scope, formatter) {
formatter.hasLocalImports = true; formatter.hasLocalImports = true;
extend(formatter.localImports, t.getBindingIdentifiers(node)); extend(formatter.localImports, this.getBindingIdentifiers());
formatter.bumpImportOccurences(node); formatter.bumpImportOccurences(node);
} }
} }
@ -54,11 +54,11 @@ var importsVisitor = {
var exportsVisitor = { var exportsVisitor = {
ExportDeclaration: { ExportDeclaration: {
enter(node, parent, scope, formatter) { enter(node, parent, scope, formatter) {
var declar = node.declaration; var declar = this.get("declaration");
formatter.hasLocalImports = true; formatter.hasLocalImports = true;
if (declar && t.isStatement(declar)) { if (declar.isStatement()) {
extend(formatter.localExports, t.getBindingIdentifiers(declar)); extend(formatter.localExports, declar.getBindingIdentifiers());
} }
if (!node.default) { if (!node.default) {
@ -105,16 +105,16 @@ export default class DefaultFormatter {
} }
getLocalExports() { getLocalExports() {
this.file.scope.traverse(this.file.ast, exportsVisitor, this); this.file.path.traverse(exportsVisitor, this);
} }
getLocalImports() { getLocalImports() {
this.file.scope.traverse(this.file.ast, importsVisitor, this); this.file.path.traverse(importsVisitor, this);
} }
remapAssignments() { remapAssignments() {
if (this.hasLocalImports) { if (this.hasLocalImports) {
this.file.scope.traverse(this.file.ast, remapVisitor, this); this.file.path.traverse(remapVisitor, this);
} }
} }

View File

@ -1,4 +1,5 @@
import includes from "lodash/collection/includes"; import includes from "lodash/collection/includes";
import traverse from "../traversal";
/** /**
* This class is responsible for traversing over the provided `File`s * This class is responsible for traversing over the provided `File`s
@ -59,7 +60,7 @@ export default class TransformerPass {
file.log.debug(`Running transformer ${this.transformer.key}`); file.log.debug(`Running transformer ${this.transformer.key}`);
file.scope.traverse(file.ast, this.handlers, file); traverse(file.ast, this.handlers, file.scope, file);
this.ran = true; this.ran = true;
} }

View File

@ -32,7 +32,7 @@ export function BlockStatement(node, parent, scope, file) {
var letRefs = node._letReferences; var letRefs = node._letReferences;
if (!letRefs) return; if (!letRefs) return;
scope.traverse(node, visitor, { this.traverse(visitor, {
letRefs: letRefs, letRefs: letRefs,
file: file file: file
}); });

View File

@ -111,7 +111,7 @@ function traverseReplace(node, parent, scope, remaps) {
var letReferenceBlockVisitor = { var letReferenceBlockVisitor = {
enter(node, parent, scope, state) { enter(node, parent, scope, state) {
if (this.isFunction()) { if (this.isFunction()) {
scope.traverse(node, letReferenceFunctionVisitor, state); this.traverse(letReferenceFunctionVisitor, state);
return this.skip(); return this.skip();
} }
} }
@ -173,7 +173,7 @@ var loopVisitor = {
if (this.isLoop()) { if (this.isLoop()) {
state.ignoreLabeless = true; state.ignoreLabeless = true;
scope.traverse(node, loopVisitor, state); this.traverse(loopVisitor, state);
state.ignoreLabeless = false; state.ignoreLabeless = false;
} }

View File

@ -15,7 +15,7 @@ export function ClassDeclaration(node, parent, scope, file) {
} }
export function ClassExpression(node, parent, scope, file) { export function ClassExpression(node, parent, scope, file) {
return new ClassTransformer(node, parent, scope, file).run(); return new ClassTransformer(this, file).run();
} }
var verifyConstructorVisitor = traverse.explode({ var verifyConstructorVisitor = traverse.explode({
@ -37,7 +37,7 @@ var verifyConstructorVisitor = traverse.explode({
state.hasBareSuper = true; state.hasBareSuper = true;
if (!state.hasSuper) { if (!state.hasSuper) {
throw state.file.errorWithNode(node, "super call is only allowed in derived constructor"); throw this.errorWithNode("super call is only allowed in derived constructor");
} }
} }
} }
@ -46,7 +46,7 @@ var verifyConstructorVisitor = traverse.explode({
ThisExpression: { ThisExpression: {
enter(node, parent, scope, state) { enter(node, parent, scope, state) {
if (state.hasSuper && !state.hasBareSuper) { if (state.hasSuper && !state.hasBareSuper) {
throw state.file.errorWithNode(node, "'this' is not allowed before super()"); throw this.errorWithNode("'this' is not allowed before super()");
} }
} }
} }
@ -58,10 +58,11 @@ class ClassTransformer {
* Description * Description
*/ */
constructor(node: Object, parent: Object, scope: Scope, file: File) { constructor(path: TraversalPath, file: File) {
this.parent = parent; this.parent = path.parent;
this.scope = scope; this.scope = path.scope;
this.node = node; this.node = path.node;
this.path = path;
this.file = file; this.file = file;
this.hasInstanceMutators = false; this.hasInstanceMutators = false;
@ -71,11 +72,11 @@ class ClassTransformer {
this.staticMutatorMap = {}; this.staticMutatorMap = {};
this.hasConstructor = false; this.hasConstructor = false;
this.className = node.id; this.className = this.node.id;
this.classRef = node.id || scope.generateUidIdentifier("class"); this.classRef = this.node.id || this.scope.generateUidIdentifier("class");
this.superName = node.superClass || t.identifier("Function"); this.superName = this.node.superClass || t.identifier("Function");
this.hasSuper = !!node.superClass; this.hasSuper = !!this.node.superClass;
this.isLoose = file.isLoose("es6.classes"); this.isLoose = file.isLoose("es6.classes");
} }
@ -174,11 +175,13 @@ class ClassTransformer {
var classBody = this.node.body.body; var classBody = this.node.body.body;
var body = this.body; var body = this.body;
var classBodyPaths = this.path.get("body").get("body");
for (var i = 0; i < classBody.length; i++) { for (var i = 0; i < classBody.length; i++) {
var node = classBody[i]; var node = classBody[i];
if (t.isMethodDefinition(node)) { if (t.isMethodDefinition(node)) {
var isConstructor = (!node.computed && t.isIdentifier(node.key, { name: "constructor" })) || t.isLiteral(node.key, { value: "constructor" }); var isConstructor = (!node.computed && t.isIdentifier(node.key, { name: "constructor" })) || t.isLiteral(node.key, { value: "constructor" });
if (isConstructor) this.verifyConstructor(node); if (isConstructor) this.verifyConstructor(classBodyPaths[i]);
var replaceSupers = new ReplaceSupers({ var replaceSupers = new ReplaceSupers({
methodNode: node, methodNode: node,
@ -206,7 +209,7 @@ class ClassTransformer {
} }
// we have no constructor, we have a super, and the super doesn't appear to be falsy // we have no constructor, we have a super, and the super doesn't appear to be falsy
if (!this.hasConstructor && this.hasSuper && t.evaluateTruthy(superName, this.scope) !== false) { if (!this.hasConstructor && this.hasSuper) { // todo: t.evaluateTruthy(superName, this.scope) !== false
var helperName = "class-super-constructor-call"; var helperName = "class-super-constructor-call";
if (this.isLoose) helperName += "-loose"; if (this.isLoose) helperName += "-loose";
constructor.body.body.push(util.template(helperName, { constructor.body.body.push(util.template(helperName, {
@ -249,17 +252,17 @@ class ClassTransformer {
* Description * Description
*/ */
verifyConstructor(node: Object) { verifyConstructor(path: TraversalPath) {
var state = { var state = {
hasBareSuper: false, hasBareSuper: false,
hasSuper: this.hasSuper, hasSuper: this.hasSuper,
file: this.file file: this.file
}; };
traverse(node, verifyConstructorVisitor, this.scope, state); path.traverse(verifyConstructorVisitor, state);
if (!state.hasBareSuper && this.hasSuper) { if (!state.hasBareSuper && this.hasSuper) {
throw this.file.errorWithNode(node, "Derived constructor must call super()"); throw path.errorWithNode("Derived constructor must call super()");
} }
} }

View File

@ -37,7 +37,7 @@ var visitor = {
}; };
export function Scopable(node, parent, scope, file) { export function Scopable(node, parent, scope, file) {
scope.traverse(node, visitor, { this.traverse(visitor, {
constants: scope.getAllBindingsOfKind("const"), constants: scope.getAllBindingsOfKind("const"),
file: file file: file
}); });

View File

@ -5,12 +5,10 @@ import * as t from "../../../types";
export var check = t.isForOfStatement; export var check = t.isForOfStatement;
export function ForOfStatement(node, parent, scope, file) { export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isTypeGeneric("Array")) { if (this.get("right").isArrayExpression()) {
return array(node, scope, file); return _ForOfStatementArray.call(this, node, scope, file);
} }
//
var callback = spec; var callback = spec;
if (file.isLoose("es6.forOf")) callback = loose; if (file.isLoose("es6.forOf")) callback = loose;
@ -42,7 +40,7 @@ export function ForOfStatement(node, parent, scope, file) {
} }
} }
var array = function (node, scope, file) { export function _ForOfStatementArray(node, scope, file) {
var nodes = []; var nodes = [];
var right = node.right; var right = node.right;
@ -78,7 +76,7 @@ var array = function (node, scope, file) {
nodes.push(loop); nodes.push(loop);
return nodes; return nodes;
}; }
var loose = function (node, parent, scope, file) { var loose = function (node, parent, scope, file) {
var left = node.left; var left = node.left;

View File

@ -26,7 +26,7 @@ export function ImportDeclaration(node, parent, scope, file) {
export function ExportDeclaration(node, parent, scope, file) { export function ExportDeclaration(node, parent, scope, file) {
// flow type // flow type
if (t.isTypeAlias(node.declaration)) return; if (this.get("declaration").isTypeAlias()) return;
var nodes = []; var nodes = [];
var i; var i;

View File

@ -49,41 +49,42 @@ exports.Function = function (node, parent, scope, file) {
body.push(defNode); body.push(defNode);
}; };
for (var i = 0; i < node.params.length; i++) { var params = this.get("params");
var param = node.params[i]; for (var i = 0; i < params.length; i++) {
var param = params[i];
if (!t.isAssignmentPattern(param)) { if (!param.isAssignmentPattern()) {
if (!t.isRestElement(param)) { if (!param.isRestElement()) {
lastNonDefaultParam = i + 1; lastNonDefaultParam = i + 1;
} }
if (!t.isIdentifier(param)) { if (!param.isIdentifier()) {
scope.traverse(param, iifeVisitor, state); param.traverse(iifeVisitor, state);
} }
if (file.transformers["es6.blockScopingTDZ"].canRun() && t.isIdentifier(param)) { if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) {
pushDefNode(param, t.identifier("undefined"), i); pushDefNode(param.node, t.identifier("undefined"), i);
} }
continue; continue;
} }
var left = param.left; var left = param.get("left");
var right = param.right; var right = param.get("right");
var placeholder = scope.generateUidIdentifier("x"); var placeholder = scope.generateUidIdentifier("x");
placeholder._isDefaultPlaceholder = true; placeholder._isDefaultPlaceholder = true;
node.params[i] = placeholder; node.params[i] = placeholder;
if (!state.iife) { if (!state.iife) {
if (t.isIdentifier(right) && scope.hasOwnBinding(right.name)) { if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) {
state.iife = true; state.iife = true;
} else { } else {
scope.traverse(right, iifeVisitor, state); right.traverse(iifeVisitor, state);
} }
} }
pushDefNode(left, right, i); pushDefNode(left.node, right.node, i);
} }
// we need to cut off all trailing default parameters // we need to cut off all trailing default parameters

View File

@ -15,7 +15,7 @@ var memberExpressionOptimisationVisitor = {
// to the wrong function // to the wrong function
if (this.isFunctionDeclaration() || this.isFunctionExpression()) { if (this.isFunctionDeclaration() || this.isFunctionExpression()) {
state.noOptimise = true; state.noOptimise = true;
scope.traverse(node, memberExpressionOptimisationVisitor, state); this.traverse(memberExpressionOptimisationVisitor, state);
state.noOptimise = false; state.noOptimise = false;
return this.skip(); return this.skip();
} }
@ -88,7 +88,7 @@ exports.Function = function (node, parent, scope, file) {
name: rest.name name: rest.name
}; };
scope.traverse(node, memberExpressionOptimisationVisitor, state); this.traverse(memberExpressionOptimisationVisitor, state);
// we only have shorthands and there's no other references // we only have shorthands and there's no other references
if (state.canOptimise && state.candidates.length) { if (state.canOptimise && state.candidates.length) {

View File

@ -81,7 +81,7 @@ export function CallExpression(node, parent, scope) {
var callee = node.callee; var callee = node.callee;
if (t.isMemberExpression(callee)) { if (this.get("callee").isMemberExpression()) {
var temp = scope.generateTempBasedOnNode(callee.object); var temp = scope.generateTempBasedOnNode(callee.object);
if (temp) { if (temp) {
callee.object = t.assignmentExpression("=", temp, callee.object); callee.object = t.assignmentExpression("=", temp, callee.object);
@ -101,7 +101,7 @@ export function NewExpression(node, parent, scope, file) {
var args = node.arguments; var args = node.arguments;
if (!hasSpread(args)) return; if (!hasSpread(args)) return;
var nativeType = t.isIdentifier(node.callee) && includes(t.NATIVE_TYPE_NAMES, node.callee.name); var nativeType = this.get("callee").isIdentifier() && includes(t.NATIVE_TYPE_NAMES, node.callee.name);
var nodes = build(args, scope); var nodes = build(args, scope);

View File

@ -7,7 +7,7 @@ export function UnaryExpression(node, parent, scope, file) {
if (node.operator === "typeof") { if (node.operator === "typeof") {
var call = t.callExpression(file.addHelper("typeof"), [node.argument]); var call = t.callExpression(file.addHelper("typeof"), [node.argument]);
if (t.isIdentifier(node.argument)) { if (this.get("argument").isIdentifier()) {
var undefLiteral = t.literal("undefined"); var undefLiteral = t.literal("undefined");
return t.conditionalExpression( return t.conditionalExpression(
t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral), t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral),

View File

@ -6,7 +6,7 @@ import map from "lodash/collection/map";
import * as t from "../../../types"; import * as t from "../../../types";
exports.Function = function (node, parent, scope, file) { exports.Function = function (node, parent, scope, file) {
var tailCall = new TailCallTransformer(node, scope, file); var tailCall = new TailCallTransformer(this, scope, file);
tailCall.run(); tailCall.run();
}; };
@ -18,11 +18,11 @@ function returnBlock(expr) {
var firstPass = { var firstPass = {
enter(node, parent, scope, state) { enter(node, parent, scope, state) {
if (this.isIfStatement()) { if (this.isIfStatement()) {
if (t.isReturnStatement(node.alternate)) { if (this.get("alternate").isReturnStatement()) {
t.ensureBlock(node, "alternate"); t.ensureBlock(node, "alternate");
} }
if (t.isReturnStatement(node.consequent)) { if (this.get("consequent").isReturnStatement()) {
t.ensureBlock(node, "consequent"); t.ensureBlock(node, "consequent");
} }
} else if (this.isReturnStatement()) { } else if (this.isReturnStatement()) {
@ -85,17 +85,18 @@ var thirdPass = {
}; };
class TailCallTransformer { class TailCallTransformer {
constructor(node, scope, file) { constructor(path, scope, file) {
this.hasTailRecursion = false; this.hasTailRecursion = false;
this.needsArguments = false; this.needsArguments = false;
this.setsArguments = false; this.setsArguments = false;
this.needsThis = false; this.needsThis = false;
this.ownerId = node.id; this.ownerId = path.node.id;
this.vars = []; this.vars = [];
this.scope = scope; this.scope = scope;
this.path = path;
this.file = file; this.file = file;
this.node = node; this.node = path.node;
} }
getArgumentsId() { getArgumentsId() {
@ -156,7 +157,7 @@ class TailCallTransformer {
if (!ownerId) return; if (!ownerId) return;
// traverse the function and look for tail recursion // traverse the function and look for tail recursion
scope.traverse(node, firstPass, this); this.path.traverse(firstPass, this);
if (!this.hasTailRecursion) return; if (!this.hasTailRecursion) return;
@ -167,10 +168,10 @@ class TailCallTransformer {
// //
scope.traverse(node, secondPass, this); this.path.traverse(secondPass, this);
if (!this.needsThis || !this.needsArguments) { if (!this.needsThis || !this.needsArguments) {
scope.traverse(node, thirdPass, this); this.path.traverse(thirdPass, this);
} }
var body = t.ensureBlock(node).body; var body = t.ensureBlock(node).body;

View File

@ -44,6 +44,7 @@ export default {
// needs to be before `_aliasFunction` due to define property closure // needs to be before `_aliasFunction` due to define property closure
"es6.properties.computed": require("./es6/properties.computed"), "es6.properties.computed": require("./es6/properties.computed"),
"optimisation.es6.forOf": require("./optimisation/flow.for-of"),
"es6.forOf": require("./es6/for-of"), "es6.forOf": require("./es6/for-of"),
"es6.regex.sticky": require("./es6/regex.sticky"), "es6.regex.sticky": require("./es6/regex.sticky"),

View File

@ -34,13 +34,13 @@ var functionVisitor = {
} }
// traverse all child nodes of this function and find `arguments` and `this` // traverse all child nodes of this function and find `arguments` and `this`
scope.traverse(node, functionChildrenVisitor, state); this.traverse(functionChildrenVisitor, state);
return this.skip(); return this.skip();
} }
}; };
var go = function (getBody, node, scope) { function aliasFunction(getBody, path, scope) {
var argumentsId; var argumentsId;
var thisId; var thisId;
@ -56,7 +56,7 @@ var go = function (getBody, node, scope) {
// traverse the function and find all alias functions so we can alias // traverse the function and find all alias functions so we can alias
// `arguments` and `this` if necessary // `arguments` and `this` if necessary
scope.traverse(node, functionVisitor, state); path.traverse(functionVisitor, state);
var body; var body;
@ -77,16 +77,16 @@ var go = function (getBody, node, scope) {
}; };
export function Program(node, parent, scope) { export function Program(node, parent, scope) {
go(function () { aliasFunction(function () {
return node.body; return node.body;
}, node, scope); }, this, scope);
}; };
export function FunctionDeclaration(node, parent, scope) { export function FunctionDeclaration(node, parent, scope) {
go(function () { aliasFunction(function () {
t.ensureBlock(node); t.ensureBlock(node);
return node.body.body; return node.body.body;
}, node, scope); }, this, scope);
} }
export { FunctionDeclaration as FunctionExpression }; export { FunctionDeclaration as FunctionExpression };

View File

@ -59,7 +59,7 @@ export function ExportDeclaration(node, parent, scope) {
} else if (t.isVariableDeclaration(declar)) { } else if (t.isVariableDeclaration(declar)) {
// export var foo = "bar"; // export var foo = "bar";
var specifiers = []; var specifiers = [];
var bindings = t.getBindingIdentifiers(declar); var bindings = this.get("declaration").getBindingIdentifiers();
for (var key in bindings) { for (var key in bindings) {
var id = bindings[key]; var id = bindings[key];
specifiers.push(t.exportSpecifier(id, id)); specifiers.push(t.exportSpecifier(id, id));

View File

@ -0,0 +1,11 @@
import { _ForOfStatementArray } from "../es6/for-of";
import * as t from "../../../types";
export var check = t.isForOfStatement;
export var optional = true;
export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isTypeGeneric("Array")) {
return _ForOfStatementArray.call(this, node, scope, file);
}
}

View File

@ -29,5 +29,5 @@ export function ImportDeclaration(node) {
} }
export function ExportDeclaration(node) { export function ExportDeclaration(node) {
if (t.isTypeAlias(node.declaration)) this.remove(); if (this.get("declaration").isTypeAlias()) this.remove();
} }

View File

@ -4,7 +4,7 @@ import core from "core-js/library";
import has from "lodash/object/has"; import has from "lodash/object/has";
import * as t from "../../../types"; import * as t from "../../../types";
var isSymboliterator = t.buildMatchMemberExpression("Symbol.iterator"); var isSymbolIterator = t.buildMatchMemberExpression("Symbol.iterator");
var coreHas = function (node) { var coreHas = function (node) {
return node.name !== "_" && has(core, node.name); return node.name !== "_" && has(core, node.name);
@ -47,7 +47,7 @@ var astVisitor = {
if (!callee.computed) return false; if (!callee.computed) return false;
prop = callee.property; prop = callee.property;
if (!isSymboliterator(prop)) return false; if (!isSymbolIterator(prop)) return false;
return util.template("corejs-iterator", { return util.template("corejs-iterator", {
CORE_ID: file.get("coreIdentifier"), CORE_ID: file.get("coreIdentifier"),
@ -59,7 +59,7 @@ var astVisitor = {
if (node.operator !== "in") return; if (node.operator !== "in") return;
var left = node.left; var left = node.left;
if (!isSymboliterator(left)) return; if (!isSymbolIterator(left)) return;
return util.template("corejs-is-iterator", { return util.template("corejs-is-iterator", {
CORE_ID: file.get("coreIdentifier"), CORE_ID: file.get("coreIdentifier"),
@ -76,7 +76,7 @@ export function manipulateOptions(opts) {
} }
export function Program(node, parent, scope, file) { export function Program(node, parent, scope, file) {
scope.traverse(node, astVisitor, file); this.traverse(astVisitor, file);
} }
export function pre(file) { export function pre(file) {

View File

@ -20,8 +20,7 @@ export function MethodDefinition(node, parent, scope, file) {
if (node.kind !== "memo") return; if (node.kind !== "memo") return;
node.kind = "get"; node.kind = "get";
var value = node.value; t.ensureBlock(node.value);
t.ensureBlock(value);
var key = node.key; var key = node.key;
@ -34,7 +33,7 @@ export function MethodDefinition(node, parent, scope, file) {
file: file file: file
}; };
scope.traverse(value, visitor, state); this.get("value").traverse(visitor, state);
return node; return node;
} }

View File

@ -20,7 +20,7 @@ function toStatements(node) {
export var optional = true; export var optional = true;
export function ConditionalExpression(node, parent, scope) { export function ConditionalExpression(node, parent, scope) {
var evaluateTest = t.evaluateTruthy(node.test, scope); var evaluateTest = this.get("test").evaluateTruthy();
if (evaluateTest === true) { if (evaluateTest === true) {
return node.consequent; return node.consequent;
} else if (evaluateTest === false) { } else if (evaluateTest === false) {
@ -32,9 +32,8 @@ export var IfStatement = {
exit(node, parent, scope) { exit(node, parent, scope) {
var consequent = node.consequent; var consequent = node.consequent;
var alternate = node.alternate; var alternate = node.alternate;
var test = node.test;
var evaluateTest = t.evaluateTruthy(test, scope); var evaluateTest = this.get("test").evaluateTruthy();
// we can check if a test will be truthy 100% and if so then we can inline // we can check if a test will be truthy 100% and if so then we can inline
// the consequent and completely ignore the alternate // the consequent and completely ignore the alternate

View File

@ -3,7 +3,7 @@ import * as t from "../../../types";
export var optional = true; export var optional = true;
export function Expression(node, parent, scope) { export function Expression(node, parent, scope) {
var res = t.evaluate(node, scope); var res = this.evaluate();
if (res.confident) return t.valueToNode(res.value); if (res.confident) return t.valueToNode(res.value);
} }

View File

@ -1,11 +1,9 @@
import * as t from "../../../types"; import * as t from "../../../types";
var isConsole = t.buildMatchMemberExpression("console", true);
export var optional = true; export var optional = true;
export function CallExpression(node, parent) { export function CallExpression(node, parent) {
if (isConsole(node.callee)) { if (this.get("callee").matchesPattern("console", true)) {
if (t.isExpressionStatement(parent)) { if (t.isExpressionStatement(parent)) {
this.parentPath.remove(); this.parentPath.remove();
} else { } else {

View File

@ -14,7 +14,7 @@ function check(source, file) {
} }
export function CallExpression(node, parent, scope, file) { export function CallExpression(node, parent, scope, file) {
if (t.isIdentifier(node.callee, { name: "require" }) && node.arguments.length === 1) { if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) {
check(node.arguments[0], file); check(node.arguments[0], file);
} }
} }

View File

@ -58,30 +58,6 @@ export default class Binding {
} }
} }
/**
* Description
*/
getValueIfImmutable() {
// can't guarantee this value is the same
if (this.reassigned) return;
var node = this.path.node;
if (t.isVariableDeclarator(node)) {
if (t.isIdentifier(node.id)) {
node = node.init;
} else {
// otherwise it's probably a destructuring like:
// var { foo } = "foo";
return;
}
}
if (t.isImmutable(node)) {
return node;
}
}
/** /**
* Description * Description
*/ */

View File

@ -1,10 +1,8 @@
module.exports = traverse;
import TraversalContext from "./context"; import TraversalContext from "./context";
import includes from "lodash/collection/includes"; import includes from "lodash/collection/includes";
import * as t from "../types"; import * as t from "../types";
function traverse(parent, opts, scope, state) { export default function traverse(parent, opts, scope, state, parentPath) {
if (!parent) return; if (!parent) return;
if (!opts.noScope && !scope) { if (!opts.noScope && !scope) {
@ -20,10 +18,10 @@ function traverse(parent, opts, scope, state) {
// array of nodes // array of nodes
if (Array.isArray(parent)) { if (Array.isArray(parent)) {
for (var i = 0; i < parent.length; i++) { for (var i = 0; i < parent.length; i++) {
traverse.node(parent[i], opts, scope, state); traverse.node(parent[i], opts, scope, state, parentPath);
} }
} else { } else {
traverse.node(parent, opts, scope, state); traverse.node(parent, opts, scope, state, parentPath);
} }
} }

View File

@ -1,5 +1,3 @@
import * as t from "./index";
/** /**
* Walk the input `node` and statically evaluate if it's truthy. * Walk the input `node` and statically evaluate if it's truthy.
* *
@ -18,15 +16,15 @@ import * as t from "./index";
* *
*/ */
export function evaluateTruthy(node: Object, scope: Scope): boolean { export function evaluateTruthy(): boolean {
var res = evaluate(node, scope); var res = this.evaluate();
if (res.confident) return !!res.value; if (res.confident) return !!res.value;
} }
/** /**
* Walk the input `node` and statically evaluate it. * Walk the input `node` and statically evaluate it.
* *
* Returns an pbject in the form `{ confident, value }`. `confident` indicates * Returns an object in the form `{ confident, value }`. `confident` indicates
* whether or not we had to drop out of evaluating the expression because of * whether or not we had to drop out of evaluating the expression because of
* hitting an unknown node that we couldn't confidently find the value of. * hitting an unknown node that we couldn't confidently find the value of.
* *
@ -38,24 +36,27 @@ export function evaluateTruthy(node: Object, scope: Scope): boolean {
* *
*/ */
export function evaluate(node: Object, scope: Scope): { confident: boolean; value: any } { export function evaluate(): { confident: boolean; value: any } {
var confident = true; var confident = true;
var value = evaluate(node); var value = evaluate(this);
if (!confident) value = undefined; if (!confident) value = undefined;
return { return {
confident: confident, confident: confident,
value: value value: value
}; };
function evaluate(node) { function evaluate(path) {
if (!confident) return; if (!confident) return;
if (t.isSequenceExpression(node)) { var node = path.node;
return evaluate(node.expressions[node.expressions.length - 1]);
if (path.isSequenceExpression()) {
var exprs = path.get("expressions");
return evaluate(exprs[exprs.length - 1]);
} }
if (t.isLiteral(node)) { if (path.isLiteral()) {
if (node.regex && node.value === null) { if (node.regex && node.value === null) {
// we have a regex and we can't represent it natively // we have a regex and we can't represent it natively
} else { } else {
@ -63,24 +64,29 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
} }
} }
if (t.isConditionalExpression(node)) { if (path.isConditionalExpression()) {
if (evaluate(node.test)) { if (evaluate(path.get("test"))) {
return evaluate(node.consequent); return evaluate(path.get("consequent"));
} else { } else {
return evaluate(node.alternate); return evaluate(path.get("alternate"));
} }
} }
if (t.isIdentifier(node)) { if (path.isIdentifier({ name: "undefined" })) {
if (node.name === "undefined") {
return undefined; return undefined;
}
if (path.isIdentifier() || path.isMemberExpression()) {
path = path.resolve();
if (path) {
return evaluate(path);
} else { } else {
return evaluate(scope.getImmutableBindingValue(node.name)); return confident = false;
} }
} }
if (t.isUnaryExpression(node, { prefix: true })) { if (path.isUnaryExpression({ prefix: true })) {
var arg = evaluate(node.argument); var arg = evaluate(path.get("argument"));
switch (node.operator) { switch (node.operator) {
case "void": return undefined; case "void": return undefined;
case "!": return !arg; case "!": return !arg;
@ -89,13 +95,13 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
} }
} }
if (t.isArrayExpression(node) || t.isObjectExpression(node)) { if (path.isArrayExpression() || path.isObjectExpression()) {
// we could evaluate these but it's probably impractical and not very useful // we could evaluate these but it's probably impractical and not very useful
} }
if (t.isLogicalExpression(node)) { if (path.isLogicalExpression()) {
let left = evaluate(node.left); let left = evaluate(path.get("left"));
let right = evaluate(node.right); let right = evaluate(path.get("right"));
switch (node.operator) { switch (node.operator) {
case "||": return left || right; case "||": return left || right;
@ -103,9 +109,9 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
} }
} }
if (t.isBinaryExpression(node)) { if (path.isBinaryExpression()) {
let left = evaluate(node.left); let left = evaluate(path.get("left"));
let right = evaluate(node.right); let right = evaluate(path.get("right"));
switch (node.operator) { switch (node.operator) {
case "-": return left - right; case "-": return left - right;

View File

@ -2,10 +2,11 @@ import isBoolean from "lodash/lang/isBoolean";
import isNumber from "lodash/lang/isNumber"; import isNumber from "lodash/lang/isNumber";
import isRegExp from "lodash/lang/isRegExp"; import isRegExp from "lodash/lang/isRegExp";
import isString from "lodash/lang/isString"; import isString from "lodash/lang/isString";
import traverse from "./index"; import traverse from "../index";
import includes from "lodash/collection/includes"; import includes from "lodash/collection/includes";
import Scope from "./scope"; import assign from "lodash/object/assign";
import * as t from "../types"; import Scope from "../scope";
import * as t from "../../types";
export default class TraversalPath { export default class TraversalPath {
constructor(parent, container) { constructor(parent, container) {
@ -112,6 +113,13 @@ export default class TraversalPath {
this._refresh(node, [node]); this._refresh(node, [node]);
} }
errorWithNode(msg, Error = SyntaxError) {
var loc = this.node.loc.start;
var err = new Error(`Line ${loc.line}: ${msg}`);
err.loc = loc;
return err;
}
get node() { get node() {
return this.container[this.key]; return this.container[this.key];
} }
@ -133,9 +141,6 @@ export default class TraversalPath {
// potentially create new scope // potentially create new scope
this.setScope(); this.setScope();
// refresh scope with new/removed bindings
this._refresh(oldNode, replacements);
var file = this.scope && this.scope.file; var file = this.scope && this.scope.file;
if (file) { if (file) {
for (var i = 0; i < replacements.length; i++) { for (var i = 0; i < replacements.length; i++) {
@ -170,12 +175,12 @@ export default class TraversalPath {
} }
} }
isBlacklisted() { isBlacklisted(): boolean {
var blacklist = this.opts.blacklist; var blacklist = this.opts.blacklist;
return blacklist && blacklist.indexOf(this.node.type) > -1; return blacklist && blacklist.indexOf(this.node.type) > -1;
} }
visit() { visit(): boolean {
if (this.isBlacklisted()) return false; if (this.isBlacklisted()) return false;
this.call("enter"); this.call("enter");
@ -215,11 +220,22 @@ export default class TraversalPath {
} }
} }
has(key) { has(key): boolean {
return !!this.node[key]; return !!this.node[key];
} }
getTypeAnnotation(): Object { is(key): boolean {
return this.has(key);
}
isnt(key): boolean {
return !this.has(key);
}
getTypeAnnotation(): {
inferred: boolean;
annotation: ?Object;
} {
if (this.typeInfo) { if (this.typeInfo) {
return this.typeInfo; return this.typeInfo;
} }
@ -246,10 +262,15 @@ export default class TraversalPath {
resolve(): ?TraversalPath { resolve(): ?TraversalPath {
if (this.isVariableDeclarator()) { if (this.isVariableDeclarator()) {
if (this.get("id").isIdentifier()) {
return this.get("init").resolve(); return this.get("init").resolve();
} else {
// otherwise it's a request for a destructuring declarator and i'm not
// ready to resolve those just yet
}
} else if (this.isIdentifier()) { } else if (this.isIdentifier()) {
var binding = this.scope.getBinding(this.node.name); var binding = this.scope.getBinding(this.node.name);
if (!binding) return; if (!binding || binding.reassigned) return;
if (binding.path === this) { if (binding.path === this) {
return this; return this;
@ -270,27 +291,44 @@ export default class TraversalPath {
if (!prop.isProperty()) continue; if (!prop.isProperty()) continue;
var key = prop.get("key"); var key = prop.get("key");
if (key.isIdentifier({ name: targetName }) || key.isLiteral({ value: targetName })) {
return prop.get("value"); // { foo: obj }
} var match = prop.isnt("computed") && key.isIdentifier({ name: targetName });
// { "foo": "obj" } or { ["foo"]: "obj" }
match ||= key.isLiteral({ value: targetName });
if (match) return prop.get("value");
} }
} else { } else {
return this; return this;
} }
} }
inferType(path: TraversalPath) { inferType(path: TraversalPath): ?Object {
path = path.resolve(); path = path.resolve();
if (!path) return; if (!path) return;
if (path.isRestElement() || path.isArrayExpression()) { if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) {
return t.genericTypeAnnotation(t.identifier("Array")); return t.genericTypeAnnotation(t.identifier("Array"));
} }
if (path.parentPath.isTypeCastExpression()) {
return path.parentPath.node.typeAnnotation;
}
if (path.isTypeCastExpression()) {
return path.node.typeAnnotation;
}
if (path.isObjectExpression()) { if (path.isObjectExpression()) {
return t.genericTypeAnnotation(t.identifier("Object")); return t.genericTypeAnnotation(t.identifier("Object"));
} }
if (path.isFunction()) {
return t.identifier("Function");
}
if (path.isLiteral()) { if (path.isLiteral()) {
var value = path.node.value; var value = path.node.value;
if (isString(value)) return t.stringTypeAnnotation(); if (isString(value)) return t.stringTypeAnnotation();
@ -304,39 +342,44 @@ export default class TraversalPath {
} }
} }
isScope() { isScope(): boolean {
return t.isScope(this.node, this.parent); return t.isScope(this.node, this.parent);
} }
isReferencedIdentifier(opts) { isReferencedIdentifier(opts): boolean {
return t.isReferencedIdentifier(this.node, this.parent, opts); return t.isReferencedIdentifier(this.node, this.parent, opts);
} }
isReferenced() { isReferenced(): boolean {
return t.isReferenced(this.node, this.parent); return t.isReferenced(this.node, this.parent);
} }
isBlockScoped() { isBlockScoped(): boolean {
return t.isBlockScoped(this.node); return t.isBlockScoped(this.node);
} }
isVar() { isVar(): boolean {
return t.isVar(this.node); return t.isVar(this.node);
} }
isScope() { isScope(): boolean {
return t.isScope(this.node, this.parent); return t.isScope(this.node, this.parent);
} }
isTypeGeneric(genericName: string, hasTypeParameters?): boolean { isTypeGeneric(genericName: string, opts = {}): boolean {
var type = this.getTypeAnnotation().annotation; var typeInfo = this.getTypeAnnotation();
var type = typeInfo.annotation;
if (!type) return false; if (!type) return false;
if (type.inferred && opts.inference === false) {
return false;
}
if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) { if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) {
return false; return false;
} }
if (hasTypeParameters && !type.typeParameters) { if (opts.requireTypeParameters && !type.typeParameters) {
return false; return false;
} }
@ -348,10 +391,64 @@ export default class TraversalPath {
} }
traverse(opts, state) { traverse(opts, state) {
traverse(this.node, opts, this.scope, state); traverse(this.node, opts, this.scope, state, this);
}
/**
* Match the current node if it matches the provided `pattern`.
*
* For example, given the match `React.createClass` it would match the
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
matchesPattern(pattern: string, allowPartial?: boolean): boolean {
var parts = pattern.split(".");
// not a member expression
if (!this.isMemberExpression()) return false;
var search = [this.node];
var i = 0;
while (search.length) {
var node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (parts[i] !== node.name) return false;
} else if (t.isLiteral(node)) {
// this part doesn't match
if (parts[i] !== node.value) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.push(node.object);
search.push(node.property);
continue;
}
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
} }
} }
return true;
}
}
assign(TraversalPath.prototype, require("./evaluation"));
for (var i = 0; i < t.TYPES.length; i++) { for (var i = 0; i < t.TYPES.length; i++) {
let type = t.TYPES[i]; let type = t.TYPES[i];
let typeKey = `is${type}`; let typeKey = `is${type}`;

View File

@ -75,7 +75,7 @@ export default class Scope {
if (cached) { if (cached) {
return cached; return cached;
} else { } else {
path.setData("scope", this); //path.setData("scope", this);
} }
this.parent = parent; this.parent = parent;
@ -245,7 +245,7 @@ export default class Scope {
if (t.isReferencedIdentifier(node, parent) && node.name === oldName) { if (t.isReferencedIdentifier(node, parent) && node.name === oldName) {
node.name = newName; node.name = newName;
} else if (t.isDeclaration(node)) { } else if (t.isDeclaration(node)) {
var ids = t.getBindingIdentifiers(node); var ids = this.getBindingIdentifiers();
for (var name in ids) { for (var name in ids) {
if (name === oldName) ids[name].name = newName; if (name === oldName) ids[name].name = newName;
} }
@ -272,7 +272,7 @@ export default class Scope {
if (t.isIdentifier(node)) { if (t.isIdentifier(node)) {
var binding = this.getBinding(node.name); var binding = this.getBinding(node.name);
if (binding && binding.isTypeGeneric("Array")) return node; if (binding && binding.isTypeGeneric("Array", { inference: false })) return node;
} }
if (t.isArrayExpression(node)) { if (t.isArrayExpression(node)) {
@ -613,26 +613,6 @@ export default class Scope {
return binding && binding.identifier; return binding && binding.identifier;
} }
/**
* Description
*/
getOwnImmutableBindingValue(name: string) {
return this._immutableBindingInfoToValue(this.getOwnBindingInfo(name));
}
/**
* Description
*/
getImmutableBindingValue(name: string) {
return this._immutableBindingInfoToValue(this.getBinding(name));
}
_immutableBindingInfoToValue(binding) {
if (binding) return binding.getValueIfImmutable();
}
/** /**
* Description * Description
*/ */

View File

@ -297,7 +297,6 @@ toFastProperties(t);
toFastProperties(t.VISITOR_KEYS); toFastProperties(t.VISITOR_KEYS);
exports.__esModule = true; exports.__esModule = true;
assign(t, require("./evaluators"));
assign(t, require("./retrievers")); assign(t, require("./retrievers"));
assign(t, require("./validators")); assign(t, require("./validators"));
assign(t, require("./converters")); assign(t, require("./converters"));

View File

@ -93,7 +93,7 @@
"TypeofTypeAnnotation": ["argument"], "TypeofTypeAnnotation": ["argument"],
"TypeAlias": ["id", "typeParameters", "right"], "TypeAlias": ["id", "typeParameters", "right"],
"TypeAnnotation": ["typeAnnotation"], "TypeAnnotation": ["typeAnnotation"],
"TypeCastExpression": ["expression"], "TypeCastExpression": ["expression", "typeAnnotation"],
"TypeParameterDeclaration": ["params"], "TypeParameterDeclaration": ["params"],
"TypeParameterInstantiation": ["params"], "TypeParameterInstantiation": ["params"],
"ObjectTypeAnnotation": ["key", "value"], "ObjectTypeAnnotation": ["key", "value"],

View File

@ -1 +1 @@
var arr = [for (i of [1, 2, 3]) i * i]; var arr = [for (i of nums) i * i];

View File

@ -7,7 +7,7 @@ var arr = (function () {
var _iteratorError = undefined; var _iteratorError = undefined;
try { try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value; var i = _step.value;
_arr.push(i * i); _arr.push(i * i);

View File

@ -1,5 +1,5 @@
function add() { function add() {
return [for (i of [1, 2, 3]) i * this.multiplier]; return [for (i of nums) i * this.multiplier];
} }
add.call({ multiplier: 5 }); add.call({ multiplier: 5 });

View File

@ -10,7 +10,7 @@ function add() {
var _iteratorError = undefined; var _iteratorError = undefined;
try { try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value; var i = _step.value;
_ref.push(i * _this.multiplier); _ref.push(i * _this.multiplier);