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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -15,7 +15,7 @@ export function ClassDeclaration(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({
@ -37,7 +37,7 @@ var verifyConstructorVisitor = traverse.explode({
state.hasBareSuper = true;
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: {
enter(node, parent, scope, state) {
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
*/
constructor(node: Object, parent: Object, scope: Scope, file: File) {
this.parent = parent;
this.scope = scope;
this.node = node;
constructor(path: TraversalPath, file: File) {
this.parent = path.parent;
this.scope = path.scope;
this.node = path.node;
this.path = path;
this.file = file;
this.hasInstanceMutators = false;
@ -71,11 +72,11 @@ class ClassTransformer {
this.staticMutatorMap = {};
this.hasConstructor = false;
this.className = node.id;
this.classRef = node.id || scope.generateUidIdentifier("class");
this.className = this.node.id;
this.classRef = this.node.id || this.scope.generateUidIdentifier("class");
this.superName = node.superClass || t.identifier("Function");
this.hasSuper = !!node.superClass;
this.superName = this.node.superClass || t.identifier("Function");
this.hasSuper = !!this.node.superClass;
this.isLoose = file.isLoose("es6.classes");
}
@ -174,11 +175,13 @@ class ClassTransformer {
var classBody = this.node.body.body;
var body = this.body;
var classBodyPaths = this.path.get("body").get("body");
for (var i = 0; i < classBody.length; i++) {
var node = classBody[i];
if (t.isMethodDefinition(node)) {
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({
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
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";
if (this.isLoose) helperName += "-loose";
constructor.body.body.push(util.template(helperName, {
@ -249,17 +252,17 @@ class ClassTransformer {
* Description
*/
verifyConstructor(node: Object) {
verifyConstructor(path: TraversalPath) {
var state = {
hasBareSuper: false,
hasSuper: this.hasSuper,
file: this.file
};
traverse(node, verifyConstructorVisitor, this.scope, state);
path.traverse(verifyConstructorVisitor, state);
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) {
scope.traverse(node, visitor, {
this.traverse(visitor, {
constants: scope.getAllBindingsOfKind("const"),
file: file
});

View File

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

View File

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

View File

@ -49,41 +49,42 @@ exports.Function = function (node, parent, scope, file) {
body.push(defNode);
};
for (var i = 0; i < node.params.length; i++) {
var param = node.params[i];
var params = this.get("params");
for (var i = 0; i < params.length; i++) {
var param = params[i];
if (!t.isAssignmentPattern(param)) {
if (!t.isRestElement(param)) {
if (!param.isAssignmentPattern()) {
if (!param.isRestElement()) {
lastNonDefaultParam = i + 1;
}
if (!t.isIdentifier(param)) {
scope.traverse(param, iifeVisitor, state);
if (!param.isIdentifier()) {
param.traverse(iifeVisitor, state);
}
if (file.transformers["es6.blockScopingTDZ"].canRun() && t.isIdentifier(param)) {
pushDefNode(param, t.identifier("undefined"), i);
if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) {
pushDefNode(param.node, t.identifier("undefined"), i);
}
continue;
}
var left = param.left;
var right = param.right;
var left = param.get("left");
var right = param.get("right");
var placeholder = scope.generateUidIdentifier("x");
placeholder._isDefaultPlaceholder = true;
node.params[i] = placeholder;
if (!state.iife) {
if (t.isIdentifier(right) && scope.hasOwnBinding(right.name)) {
if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) {
state.iife = true;
} 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

View File

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

View File

@ -81,7 +81,7 @@ export function CallExpression(node, parent, scope) {
var callee = node.callee;
if (t.isMemberExpression(callee)) {
if (this.get("callee").isMemberExpression()) {
var temp = scope.generateTempBasedOnNode(callee.object);
if (temp) {
callee.object = t.assignmentExpression("=", temp, callee.object);
@ -101,7 +101,7 @@ export function NewExpression(node, parent, scope, file) {
var args = node.arguments;
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);

View File

@ -7,7 +7,7 @@ export function UnaryExpression(node, parent, scope, file) {
if (node.operator === "typeof") {
var call = t.callExpression(file.addHelper("typeof"), [node.argument]);
if (t.isIdentifier(node.argument)) {
if (this.get("argument").isIdentifier()) {
var undefLiteral = t.literal("undefined");
return t.conditionalExpression(
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";
exports.Function = function (node, parent, scope, file) {
var tailCall = new TailCallTransformer(node, scope, file);
var tailCall = new TailCallTransformer(this, scope, file);
tailCall.run();
};
@ -18,11 +18,11 @@ function returnBlock(expr) {
var firstPass = {
enter(node, parent, scope, state) {
if (this.isIfStatement()) {
if (t.isReturnStatement(node.alternate)) {
if (this.get("alternate").isReturnStatement()) {
t.ensureBlock(node, "alternate");
}
if (t.isReturnStatement(node.consequent)) {
if (this.get("consequent").isReturnStatement()) {
t.ensureBlock(node, "consequent");
}
} else if (this.isReturnStatement()) {
@ -85,17 +85,18 @@ var thirdPass = {
};
class TailCallTransformer {
constructor(node, scope, file) {
constructor(path, scope, file) {
this.hasTailRecursion = false;
this.needsArguments = false;
this.setsArguments = false;
this.needsThis = false;
this.ownerId = node.id;
this.ownerId = path.node.id;
this.vars = [];
this.scope = scope;
this.path = path;
this.file = file;
this.node = node;
this.node = path.node;
}
getArgumentsId() {
@ -156,7 +157,7 @@ class TailCallTransformer {
if (!ownerId) return;
// traverse the function and look for tail recursion
scope.traverse(node, firstPass, this);
this.path.traverse(firstPass, this);
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) {
scope.traverse(node, thirdPass, this);
this.path.traverse(thirdPass, this);
}
var body = t.ensureBlock(node).body;

View File

@ -44,6 +44,7 @@ export default {
// needs to be before `_aliasFunction` due to define property closure
"es6.properties.computed": require("./es6/properties.computed"),
"optimisation.es6.forOf": require("./optimisation/flow.for-of"),
"es6.forOf": require("./es6/for-of"),
"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`
scope.traverse(node, functionChildrenVisitor, state);
this.traverse(functionChildrenVisitor, state);
return this.skip();
}
};
var go = function (getBody, node, scope) {
function aliasFunction(getBody, path, scope) {
var argumentsId;
var thisId;
@ -56,7 +56,7 @@ var go = function (getBody, node, scope) {
// traverse the function and find all alias functions so we can alias
// `arguments` and `this` if necessary
scope.traverse(node, functionVisitor, state);
path.traverse(functionVisitor, state);
var body;
@ -77,16 +77,16 @@ var go = function (getBody, node, scope) {
};
export function Program(node, parent, scope) {
go(function () {
aliasFunction(function () {
return node.body;
}, node, scope);
}, this, scope);
};
export function FunctionDeclaration(node, parent, scope) {
go(function () {
aliasFunction(function () {
t.ensureBlock(node);
return node.body.body;
}, node, scope);
}, this, scope);
}
export { FunctionDeclaration as FunctionExpression };

View File

@ -59,7 +59,7 @@ export function ExportDeclaration(node, parent, scope) {
} else if (t.isVariableDeclaration(declar)) {
// export var foo = "bar";
var specifiers = [];
var bindings = t.getBindingIdentifiers(declar);
var bindings = this.get("declaration").getBindingIdentifiers();
for (var key in bindings) {
var id = bindings[key];
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) {
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 * as t from "../../../types";
var isSymboliterator = t.buildMatchMemberExpression("Symbol.iterator");
var isSymbolIterator = t.buildMatchMemberExpression("Symbol.iterator");
var coreHas = function (node) {
return node.name !== "_" && has(core, node.name);
@ -47,7 +47,7 @@ var astVisitor = {
if (!callee.computed) return false;
prop = callee.property;
if (!isSymboliterator(prop)) return false;
if (!isSymbolIterator(prop)) return false;
return util.template("corejs-iterator", {
CORE_ID: file.get("coreIdentifier"),
@ -59,7 +59,7 @@ var astVisitor = {
if (node.operator !== "in") return;
var left = node.left;
if (!isSymboliterator(left)) return;
if (!isSymbolIterator(left)) return;
return util.template("corejs-is-iterator", {
CORE_ID: file.get("coreIdentifier"),
@ -76,7 +76,7 @@ export function manipulateOptions(opts) {
}
export function Program(node, parent, scope, file) {
scope.traverse(node, astVisitor, file);
this.traverse(astVisitor, file);
}
export function pre(file) {

View File

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

View File

@ -20,7 +20,7 @@ function toStatements(node) {
export var optional = true;
export function ConditionalExpression(node, parent, scope) {
var evaluateTest = t.evaluateTruthy(node.test, scope);
var evaluateTest = this.get("test").evaluateTruthy();
if (evaluateTest === true) {
return node.consequent;
} else if (evaluateTest === false) {
@ -32,9 +32,8 @@ export var IfStatement = {
exit(node, parent, scope) {
var consequent = node.consequent;
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
// the consequent and completely ignore the alternate

View File

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

View File

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

View File

@ -14,7 +14,7 @@ function check(source, 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);
}
}

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
*/

View File

@ -1,10 +1,8 @@
module.exports = traverse;
import TraversalContext from "./context";
import includes from "lodash/collection/includes";
import * as t from "../types";
function traverse(parent, opts, scope, state) {
export default function traverse(parent, opts, scope, state, parentPath) {
if (!parent) return;
if (!opts.noScope && !scope) {
@ -20,10 +18,10 @@ function traverse(parent, opts, scope, state) {
// array of nodes
if (Array.isArray(parent)) {
for (var i = 0; i < parent.length; i++) {
traverse.node(parent[i], opts, scope, state);
traverse.node(parent[i], opts, scope, state, parentPath);
}
} 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.
*
@ -18,15 +16,15 @@ import * as t from "./index";
*
*/
export function evaluateTruthy(node: Object, scope: Scope): boolean {
var res = evaluate(node, scope);
export function evaluateTruthy(): boolean {
var res = this.evaluate();
if (res.confident) return !!res.value;
}
/**
* 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
* 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 value = evaluate(node);
var value = evaluate(this);
if (!confident) value = undefined;
return {
confident: confident,
value: value
};
function evaluate(node) {
function evaluate(path) {
if (!confident) return;
if (t.isSequenceExpression(node)) {
return evaluate(node.expressions[node.expressions.length - 1]);
var node = path.node;
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) {
// we have a regex and we can't represent it natively
} else {
@ -63,24 +64,29 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
}
}
if (t.isConditionalExpression(node)) {
if (evaluate(node.test)) {
return evaluate(node.consequent);
if (path.isConditionalExpression()) {
if (evaluate(path.get("test"))) {
return evaluate(path.get("consequent"));
} else {
return evaluate(node.alternate);
return evaluate(path.get("alternate"));
}
}
if (t.isIdentifier(node)) {
if (node.name === "undefined") {
if (path.isIdentifier({ name: "undefined" })) {
return undefined;
}
if (path.isIdentifier() || path.isMemberExpression()) {
path = path.resolve();
if (path) {
return evaluate(path);
} else {
return evaluate(scope.getImmutableBindingValue(node.name));
return confident = false;
}
}
if (t.isUnaryExpression(node, { prefix: true })) {
var arg = evaluate(node.argument);
if (path.isUnaryExpression({ prefix: true })) {
var arg = evaluate(path.get("argument"));
switch (node.operator) {
case "void": return undefined;
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
}
if (t.isLogicalExpression(node)) {
let left = evaluate(node.left);
let right = evaluate(node.right);
if (path.isLogicalExpression()) {
let left = evaluate(path.get("left"));
let right = evaluate(path.get("right"));
switch (node.operator) {
case "||": return left || right;
@ -103,9 +109,9 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
}
}
if (t.isBinaryExpression(node)) {
let left = evaluate(node.left);
let right = evaluate(node.right);
if (path.isBinaryExpression()) {
let left = evaluate(path.get("left"));
let right = evaluate(path.get("right"));
switch (node.operator) {
case "-": return left - right;

View File

@ -2,10 +2,11 @@ import isBoolean from "lodash/lang/isBoolean";
import isNumber from "lodash/lang/isNumber";
import isRegExp from "lodash/lang/isRegExp";
import isString from "lodash/lang/isString";
import traverse from "./index";
import traverse from "../index";
import includes from "lodash/collection/includes";
import Scope from "./scope";
import * as t from "../types";
import assign from "lodash/object/assign";
import Scope from "../scope";
import * as t from "../../types";
export default class TraversalPath {
constructor(parent, container) {
@ -112,6 +113,13 @@ export default class TraversalPath {
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() {
return this.container[this.key];
}
@ -133,9 +141,6 @@ export default class TraversalPath {
// potentially create new scope
this.setScope();
// refresh scope with new/removed bindings
this._refresh(oldNode, replacements);
var file = this.scope && this.scope.file;
if (file) {
for (var i = 0; i < replacements.length; i++) {
@ -170,12 +175,12 @@ export default class TraversalPath {
}
}
isBlacklisted() {
isBlacklisted(): boolean {
var blacklist = this.opts.blacklist;
return blacklist && blacklist.indexOf(this.node.type) > -1;
}
visit() {
visit(): boolean {
if (this.isBlacklisted()) return false;
this.call("enter");
@ -215,11 +220,22 @@ export default class TraversalPath {
}
}
has(key) {
has(key): boolean {
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) {
return this.typeInfo;
}
@ -246,10 +262,15 @@ export default class TraversalPath {
resolve(): ?TraversalPath {
if (this.isVariableDeclarator()) {
if (this.get("id").isIdentifier()) {
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()) {
var binding = this.scope.getBinding(this.node.name);
if (!binding) return;
if (!binding || binding.reassigned) return;
if (binding.path === this) {
return this;
@ -270,27 +291,44 @@ export default class TraversalPath {
if (!prop.isProperty()) continue;
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 {
return this;
}
}
inferType(path: TraversalPath) {
inferType(path: TraversalPath): ?Object {
path = path.resolve();
if (!path) return;
if (path.isRestElement() || path.isArrayExpression()) {
if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) {
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()) {
return t.genericTypeAnnotation(t.identifier("Object"));
}
if (path.isFunction()) {
return t.identifier("Function");
}
if (path.isLiteral()) {
var value = path.node.value;
if (isString(value)) return t.stringTypeAnnotation();
@ -304,39 +342,44 @@ export default class TraversalPath {
}
}
isScope() {
isScope(): boolean {
return t.isScope(this.node, this.parent);
}
isReferencedIdentifier(opts) {
isReferencedIdentifier(opts): boolean {
return t.isReferencedIdentifier(this.node, this.parent, opts);
}
isReferenced() {
isReferenced(): boolean {
return t.isReferenced(this.node, this.parent);
}
isBlockScoped() {
isBlockScoped(): boolean {
return t.isBlockScoped(this.node);
}
isVar() {
isVar(): boolean {
return t.isVar(this.node);
}
isScope() {
isScope(): boolean {
return t.isScope(this.node, this.parent);
}
isTypeGeneric(genericName: string, hasTypeParameters?): boolean {
var type = this.getTypeAnnotation().annotation;
isTypeGeneric(genericName: string, opts = {}): boolean {
var typeInfo = this.getTypeAnnotation();
var type = typeInfo.annotation;
if (!type) return false;
if (type.inferred && opts.inference === false) {
return false;
}
if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) {
return false;
}
if (hasTypeParameters && !type.typeParameters) {
if (opts.requireTypeParameters && !type.typeParameters) {
return false;
}
@ -348,10 +391,64 @@ export default class TraversalPath {
}
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++) {
let type = t.TYPES[i];
let typeKey = `is${type}`;

View File

@ -75,7 +75,7 @@ export default class Scope {
if (cached) {
return cached;
} else {
path.setData("scope", this);
//path.setData("scope", this);
}
this.parent = parent;
@ -245,7 +245,7 @@ export default class Scope {
if (t.isReferencedIdentifier(node, parent) && node.name === oldName) {
node.name = newName;
} else if (t.isDeclaration(node)) {
var ids = t.getBindingIdentifiers(node);
var ids = this.getBindingIdentifiers();
for (var name in ids) {
if (name === oldName) ids[name].name = newName;
}
@ -272,7 +272,7 @@ export default class Scope {
if (t.isIdentifier(node)) {
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)) {
@ -613,26 +613,6 @@ export default class Scope {
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
*/

View File

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

View File

@ -93,7 +93,7 @@
"TypeofTypeAnnotation": ["argument"],
"TypeAlias": ["id", "typeParameters", "right"],
"TypeAnnotation": ["typeAnnotation"],
"TypeCastExpression": ["expression"],
"TypeCastExpression": ["expression", "typeAnnotation"],
"TypeParameterDeclaration": ["params"],
"TypeParameterInstantiation": ["params"],
"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;
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;
_arr.push(i * i);

View File

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

View File

@ -10,7 +10,7 @@ function add() {
var _iteratorError = undefined;
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;
_ref.push(i * _this.multiplier);