make traversal code simpler

This commit is contained in:
Sebastian McKenzie
2014-12-27 17:01:47 +11:00
parent 13a2d469a3
commit e96d02c972
11 changed files with 178 additions and 165 deletions

View File

@@ -9,8 +9,10 @@ function CommonJSFormatter(file) {
DefaultFormatter.apply(this, arguments);
var hasNonDefaultExports = false;
traverse(file.ast, function (node) {
if (t.isExportDeclaration(node) && !node.default) hasNonDefaultExports = true;
traverse(file.ast, {
enter: function (node) {
if (t.isExportDeclaration(node) && !node.default) hasNonDefaultExports = true;
}
});
this.hasNonDefaultExports = hasNonDefaultExports;
}
@@ -31,9 +33,6 @@ CommonJSFormatter.prototype.importSpecifier = function (specifier, node, nodes)
]));
} else {
// import foo from "foo";
if (specifier.default) {
specifier.id = t.identifier("default");
}
var templateName = "require-assign";

View File

@@ -5,7 +5,6 @@ var t = require("../../types");
var _ = require("lodash");
var SETTER_MODULE_NAMESPACE = t.identifier("m");
var DEFAULT_IDENTIFIER = t.identifier("default");
var NULL_SETTER = t.literal(null);
function SystemFormatter(file) {
@@ -117,9 +116,6 @@ SystemFormatter.prototype.importSpecifier = function (specifier, node) {
var variableName = t.getSpecifierName(specifier);
// import foo from "foo";
if (specifier.default) {
specifier.id = DEFAULT_IDENTIFIER;
}
var MODULE_NAME = node.source.value;

View File

@@ -15,38 +15,42 @@ var go = function (getBody, node, file, scope) {
// traverse the function and find all alias functions so we can alias
// `arguments` and `this` if necessary
traverse(node, function (node) {
if (!node._aliasFunction) {
if (t.isFunction(node)) {
// stop traversal of this node as it'll be hit again by this transformer
return false;
} else {
return;
traverse(node, {
enter: function (node) {
if (!node._aliasFunction) {
if (t.isFunction(node)) {
// stop traversal of this node as it'll be hit again by this transformer
return false;
} else {
return;
}
}
// traverse all child nodes of this function and find `arguments` and `this`
traverse(node, {
enter: function (node, parent) {
if (t.isFunction(node) && !node._aliasFunction) {
return false;
}
if (node._ignoreAliasFunctions) return false;
var getId;
if (t.isIdentifier(node) && node.name === "arguments") {
getId = getArgumentsId;
} else if (t.isThisExpression(node)) {
getId = getThisId;
} else {
return;
}
if (t.isReferenced(node, parent)) return getId();
}
});
return false;
}
// traverse all child nodes of this function and find `arguments` and `this`
traverse(node, function (node, parent) {
if (t.isFunction(node) && !node._aliasFunction) {
return false;
}
if (node._ignoreAliasFunctions) return false;
var getId;
if (t.isIdentifier(node) && node.name === "arguments") {
getId = getArgumentsId;
} else if (t.isThisExpression(node)) {
getId = getThisId;
} else {
return;
}
if (t.isReferenced(node, parent)) return getId();
});
return false;
});
var body;

View File

@@ -247,17 +247,19 @@ Class.prototype.replaceInstanceSuperReferences = function (methodNode) {
var method = methodNode.value;
var self = this;
traverse(method, function (node, parent) {
if (t.isIdentifier(node, { name: "super" })) {
return self.superIdentifier(methodNode, node, parent);
} else if (t.isCallExpression(node)) {
var callee = node.callee;
if (!t.isMemberExpression(callee)) return;
if (callee.object.name !== "super") return;
traverse(method, {
enter: function (node, parent) {
if (t.isIdentifier(node, { name: "super" })) {
return self.superIdentifier(methodNode, node, parent);
} else if (t.isCallExpression(node)) {
var callee = node.callee;
if (!t.isMemberExpression(callee)) return;
if (callee.object.name !== "super") return;
// super.test(); -> ClassName.prototype.MethodName.call(this);
callee.property = t.memberExpression(callee.property, t.identifier("call"));
node.arguments.unshift(t.thisExpression());
// super.test(); -> ClassName.prototype.MethodName.call(this);
callee.property = t.memberExpression(callee.property, t.identifier("call"));
node.arguments.unshift(t.thisExpression());
}
}
});
};

View File

@@ -69,12 +69,14 @@ exports.ForStatement = function (node, parent, file) {
if (!hasConstants) return;
traverse(node, function (child, parent, scope) {
if (child._ignoreConstant) return;
if (t.isVariableDeclaration(child)) return;
traverse(node, {
enter: function (child, parent, scope) {
if (child._ignoreConstant) return;
if (t.isVariableDeclaration(child)) return;
if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) {
check(parent, getIds(child), scope);
if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) {
check(parent, getIds(child), scope);
}
}
});
};

View File

@@ -37,7 +37,7 @@ exports.Function = function (node, parent, file, scope) {
};
check(def, node);
traverse(def, check);
traverse(def, { enter: check });
});
// we're accessing a variable that's already defined within this function

View File

@@ -176,7 +176,7 @@ LetScoping.prototype.remap = function () {
var traverseReplace = function (node, parent) {
replace(node, parent);
traverse(node, replace);
traverse(node, { enter: replace });
};
var loopParent = this.loopParent;
@@ -186,7 +186,7 @@ LetScoping.prototype.remap = function () {
traverseReplace(loopParent.update, loopParent);
}
traverse(block, replace);
traverse(block, { enter: replace });
};
/**
@@ -275,33 +275,35 @@ LetScoping.prototype.checkLoop = function () {
hasBreak: false,
};
traverse(this.block, function (node, parent) {
var replace;
traverse(this.block, {
enter: function (node, parent) {
var replace;
if (t.isFunction(node) || t.isLoop(node)) {
return false;
}
if (node && !node.label) {
if (t.isBreakStatement(node)) {
if (t.isSwitchCase(parent)) return;
has.hasBreak = true;
replace = t.returnStatement(t.literal("break"));
} else if (t.isContinueStatement(node)) {
has.hasContinue = true;
replace = t.returnStatement(t.literal("continue"));
if (t.isFunction(node) || t.isLoop(node)) {
return false;
}
}
if (t.isReturnStatement(node)) {
has.hasReturn = true;
replace = t.returnStatement(t.objectExpression([
t.property("init", t.identifier("v"), node.argument || t.identifier("undefined"))
]));
}
if (node && !node.label) {
if (t.isBreakStatement(node)) {
if (t.isSwitchCase(parent)) return;
if (replace) return t.inherits(replace, node);
has.hasBreak = true;
replace = t.returnStatement(t.literal("break"));
} else if (t.isContinueStatement(node)) {
has.hasContinue = true;
replace = t.returnStatement(t.literal("continue"));
}
}
if (t.isReturnStatement(node)) {
has.hasReturn = true;
replace = t.returnStatement(t.objectExpression([
t.property("init", t.identifier("v"), node.argument || t.identifier("undefined"))
]));
}
if (replace) return t.inherits(replace, node);
}
});
return has;
@@ -314,19 +316,21 @@ LetScoping.prototype.checkLoop = function () {
LetScoping.prototype.hoistVarDeclarations = function () {
var self = this;
traverse(this.block, function (node, parent) {
if (t.isForStatement(node)) {
if (isVar(node.init, node)) {
node.init = t.sequenceExpression(self.pushDeclar(node.init));
traverse(this.block, {
enter: function (node, parent) {
if (t.isForStatement(node)) {
if (isVar(node.init, node)) {
node.init = t.sequenceExpression(self.pushDeclar(node.init));
}
} else if (t.isFor(node)) {
if (isVar(node.left, node)) {
node.left = node.left.declarations[0].id;
}
} else if (isVar(node, parent)) {
return self.pushDeclar(node).map(t.expressionStatement);
} else if (t.isFunction(node)) {
return false;
}
} else if (t.isFor(node)) {
if (isVar(node.left, node)) {
node.left = node.left.declarations[0].id;
}
} else if (isVar(node, parent)) {
return self.pushDeclar(node).map(t.expressionStatement);
} else if (t.isFunction(node)) {
return false;
}
});
};
@@ -359,31 +363,35 @@ LetScoping.prototype.getLetReferences = function () {
// traverse through this block, stopping on functions and checking if they
// contain any outside let references
traverse(this.block, function (node, parent, scope) {
if (t.isFunction(node)) {
traverse(node, function (node, parent) {
// not an identifier so we have no use
if (!t.isIdentifier(node)) return;
traverse(this.block, {
enter: function (node, parent, scope) {
if (t.isFunction(node)) {
traverse(node, {
enter: function (node, parent) {
// not an identifier so we have no use
if (!t.isIdentifier(node)) return;
// not a direct reference
if (!t.isReferenced(node, parent)) return;
// not a direct reference
if (!t.isReferenced(node, parent)) return;
// this scope has a variable with the same name so it couldn't belong
// to our let scope
if (scope.hasOwn(node.name)) return;
// this scope has a variable with the same name so it couldn't belong
// to our let scope
if (scope.hasOwn(node.name)) return;
closurify = true;
closurify = true;
// this key doesn't appear just outside our scope
if (!_.contains(self.info.outsideKeys, node.name)) return;
// this key doesn't appear just outside our scope
if (!_.contains(self.info.outsideKeys, node.name)) return;
// push this badboy
self.letReferences[node.name] = node;
});
// push this badboy
self.letReferences[node.name] = node;
}
});
return false;
} else if (t.isLoop(node)) {
return false;
return false;
} else if (t.isLoop(node)) {
return false;
}
}
});

View File

@@ -21,11 +21,13 @@ exports.MethodDefinition = function (node, parent, file, scope) {
var memoId = t.memberExpression(t.thisExpression(), t.identifier(key));
var doneId = t.memberExpression(t.thisExpression(), t.identifier(key + "Done"));
traverse(value, function (node) {
if (t.isFunction(node)) return;
traverse(value, {
enter: function (node) {
if (t.isFunction(node)) return;
if (t.isReturnStatement(node) && node.argument) {
node.argument = t.assignmentExpression("=", memoId, node.argument);
if (t.isReturnStatement(node) && node.argument) {
node.argument = t.assignmentExpression("=", memoId, node.argument);
}
}
});

View File

@@ -4,14 +4,14 @@ var Scope = require("./scope");
var t = require("../types");
var _ = require("lodash");
function traverse(parent, callbacks, opts) {
function traverse(parent, opts, scope) {
// falsy node
if (!parent) return;
// array of nodes
if (_.isArray(parent)) {
_.each(parent, function (node) {
traverse(node, callbacks, opts);
traverse(node, opts, scope);
});
return;
}
@@ -21,13 +21,6 @@ function traverse(parent, callbacks, opts) {
if (!keys) return;
opts = opts || {};
if (_.isArray(opts)) opts = { blacklist: opts };
// blacklist these node types from being traversed
var blacklistTypes = opts.blacklist || [];
// normalise callbacks
if (_.isFunction(callbacks)) callbacks = { enter: callbacks };
for (var i in keys) {
var key = keys[i];
@@ -41,7 +34,7 @@ function traverse(parent, callbacks, opts) {
if (!node) return;
// type is blacklisted
if (blacklistTypes.indexOf(node.type) > -1) return;
if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1) return;
// replace node
var maybeReplace = function (result) {
@@ -58,12 +51,12 @@ function traverse(parent, callbacks, opts) {
};
//
var opts2 = { scope: opts.scope, blacklist: opts.blacklist };
if (t.isScope(node)) opts2.scope = new Scope(node, opts.scope);
var ourScope = scope;
if (t.isScope(node)) ourScope = new Scope(node, scope);
// enter
if (callbacks.enter) {
var result = callbacks.enter(node, parent, opts2.scope);
if (opts.enter) {
var result = opts.enter(node, parent, ourScope);
maybeReplace(result);
// stop iteration
@@ -71,11 +64,11 @@ function traverse(parent, callbacks, opts) {
}
// traverse node
traverse(node, callbacks, opts2);
traverse(node, opts, ourScope);
// exit
if (callbacks.exit) {
maybeReplace(callbacks.exit(node, parent, opts2.scope));
if (opts.exit) {
maybeReplace(opts.exit(node, parent, ourScope));
}
};
@@ -113,7 +106,7 @@ traverse.removeProperties = function (tree) {
};
clear(tree);
traverse(tree, clear);
traverse(tree, { enter: clear });
return tree;
};
@@ -129,12 +122,15 @@ traverse.hasType = function (tree, type, blacklistTypes) {
// the type we're looking for is the same as the passed node
if (tree.type === type) return true;
traverse(tree, function (node) {
if (node.type === type) {
has = true;
return false;
traverse(tree, {
blacklist: blacklistTypes,
enter: function (node) {
if (node.type === type) {
has = true;
return false;
}
}
}, { blacklist: blacklistTypes });
});
return has;
};

View File

@@ -82,31 +82,33 @@ Scope.prototype.getInfo = function () {
// Program, Function - var variables
if (t.isProgram(block) || t.isFunction(block)) {
traverse(block, function (node, parent, scope) {
if (t.isFor(node)) {
_.each(FOR_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) add(declar);
});
traverse(block, {
enter: function (node, parent, scope) {
if (t.isFor(node)) {
_.each(FOR_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) add(declar);
});
}
// this block is a function so we'll stop since none of the variables
// declared within are accessible
if (t.isFunction(node)) return false;
// function identifier doesn't belong to this scope
if (block.id && node === block.id) return;
if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) {
add(node, true);
}
// we've ran into a declaration!
// we'll let the BlockStatement scope deal with `let` declarations unless
if (t.isDeclaration(node) && !t.isLet(node)) {
add(node);
}
}
// this block is a function so we'll stop since none of the variables
// declared within are accessible
if (t.isFunction(node)) return false;
// function identifier doesn't belong to this scope
if (block.id && node === block.id) return;
if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) {
add(node, true);
}
// we've ran into a declaration!
// we'll let the BlockStatement scope deal with `let` declarations unless
if (t.isDeclaration(node) && !t.isLet(node)) {
add(node);
}
}, { scope: this });
}, this);
}
// Function - params, rest

View File

@@ -150,9 +150,11 @@ exports.template = function (name, nodes, keepExpression) {
template = _.cloneDeep(template);
if (!_.isEmpty(nodes)) {
traverse(template, function (node) {
if (t.isIdentifier(node) && _.has(nodes, node.name)) {
return nodes[node.name];
traverse(template, {
enter: function (node) {
if (t.isIdentifier(node) && _.has(nodes, node.name)) {
return nodes[node.name];
}
}
});
}