react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / node_modules / commoner / node_modules / recast / lib / fast-path.js
81169 viewsvar assert = require("assert");1var types = require("./types");2var n = types.namedTypes;3var Node = n.Node;4var isArray = types.builtInTypes.array;5var isNumber = types.builtInTypes.number;67function FastPath(value) {8assert.ok(this instanceof FastPath);9this.stack = [value];10}1112var FPp = FastPath.prototype;13module.exports = FastPath;1415// Static convenience function for coercing a value to a FastPath.16FastPath.from = function(obj) {17if (obj instanceof FastPath) {18// Return a defensive copy of any existing FastPath instances.19return obj.copy();20}2122if (obj instanceof types.NodePath) {23// For backwards compatibility, unroll NodePath instances into24// lightweight FastPath [..., name, value] stacks.25var copy = Object.create(FastPath.prototype);26var stack = [obj.value];27for (var pp; (pp = obj.parentPath); obj = pp)28stack.push(obj.name, pp.value);29copy.stack = stack.reverse();30return copy;31}3233// Otherwise use obj as the value of the new FastPath instance.34return new FastPath(obj);35};3637FPp.copy = function copy() {38var copy = Object.create(FastPath.prototype);39copy.stack = this.stack.slice(0);40return copy;41};4243// The name of the current property is always the penultimate element of44// this.stack, and always a String.45FPp.getName = function getName() {46var s = this.stack;47var len = s.length;48if (len > 1) {49return s[len - 2];50}51// Since the name is always a string, null is a safe sentinel value to52// return if we do not know the name of the (root) value.53return null;54};5556// The value of the current property is always the final element of57// this.stack.58FPp.getValue = function getValue() {59var s = this.stack;60return s[s.length - 1];61};6263FPp.getNode = function getNode() {64var s = this.stack;6566for (var i = s.length - 1; i >= 0; i -= 2) {67var value = s[i];68if (n.Node.check(value)) {69return value;70}71}7273return null;74};7576FPp.getParentNode = function getParentNode() {77var s = this.stack;78var count = 0;7980for (var i = s.length - 1; i >= 0; i -= 2) {81var value = s[i];82if (n.Node.check(value) && count++ > 0) {83return value;84}85}8687return null;88};8990// The length of the stack can be either even or odd, depending on whether91// or not we have a name for the root value. The difference between the92// index of the root value and the index of the final value is always93// even, though, which allows us to return the root value in constant time94// (i.e. without iterating backwards through the stack).95FPp.getRootValue = function getRootValue() {96var s = this.stack;97if (s.length % 2 === 0) {98return s[1];99}100return s[0];101};102103// Temporarily push properties named by string arguments given after the104// callback function onto this.stack, then call the callback with a105// reference to this (modified) FastPath object. Note that the stack will106// be restored to its original state after the callback is finished, so it107// is probably a mistake to retain a reference to the path.108FPp.call = function call(callback/*, name1, name2, ... */) {109var s = this.stack;110var origLen = s.length;111var value = s[origLen - 1];112var argc = arguments.length;113for (var i = 1; i < argc; ++i) {114var name = arguments[i];115value = value[name];116s.push(name, value);117}118var result = callback(this);119s.length = origLen;120return result;121};122123// Similar to FastPath.prototype.call, except that the value obtained by124// accessing this.getValue()[name1][name2]... should be array-like. The125// callback will be called with a reference to this path object for each126// element of the array.127FPp.each = function each(callback/*, name1, name2, ... */) {128var s = this.stack;129var origLen = s.length;130var value = s[origLen - 1];131var argc = arguments.length;132133for (var i = 1; i < argc; ++i) {134var name = arguments[i];135value = value[name];136s.push(name, value);137}138139for (var i = 0; i < value.length; ++i) {140if (i in value) {141s.push(i, value[i]);142// If the callback needs to know the value of i, call143// path.getName(), assuming path is the parameter name.144callback(this);145s.length -= 2;146}147}148149s.length = origLen;150};151152// Similar to FastPath.prototype.each, except that the results of the153// callback function invocations are stored in an array and returned at154// the end of the iteration.155FPp.map = function map(callback/*, name1, name2, ... */) {156var s = this.stack;157var origLen = s.length;158var value = s[origLen - 1];159var argc = arguments.length;160161for (var i = 1; i < argc; ++i) {162var name = arguments[i];163value = value[name];164s.push(name, value);165}166167var result = new Array(value.length);168169for (var i = 0; i < value.length; ++i) {170if (i in value) {171s.push(i, value[i]);172result[i] = callback(this, i);173s.length -= 2;174}175}176177s.length = origLen;178179return result;180};181182// Inspired by require("ast-types").NodePath.prototype.needsParens, but183// more efficient because we're iterating backwards through a stack.184FPp.needsParens = function(assumeExpressionContext) {185var parent = this.getParentNode();186if (!parent) {187return false;188}189190var name = this.getName();191var node = this.getNode();192193// Only expressions need parentheses.194if (!n.Expression.check(node)) {195return false;196}197198// Identifiers never need parentheses.199if (node.type === "Identifier") {200return false;201}202203switch (node.type) {204case "UnaryExpression":205case "SpreadElement":206case "SpreadProperty":207return parent.type === "MemberExpression"208&& name === "object"209&& parent.object === node;210211case "BinaryExpression":212case "LogicalExpression":213switch (parent.type) {214case "CallExpression":215return name === "callee"216&& parent.callee === node;217218case "UnaryExpression":219case "SpreadElement":220case "SpreadProperty":221return true;222223case "MemberExpression":224return name === "object"225&& parent.object === node;226227case "BinaryExpression":228case "LogicalExpression":229var po = parent.operator;230var pp = PRECEDENCE[po];231var no = node.operator;232var np = PRECEDENCE[no];233234if (pp > np) {235return true;236}237238if (pp === np && name === "right") {239assert.strictEqual(parent.right, node);240return true;241}242243default:244return false;245}246247case "SequenceExpression":248switch (parent.type) {249case "ForStatement":250// Although parentheses wouldn't hurt around sequence251// expressions in the head of for loops, traditional style252// dictates that e.g. i++, j++ should not be wrapped with253// parentheses.254return false;255256case "ExpressionStatement":257return name !== "expression";258259default:260// Otherwise err on the side of overparenthesization, adding261// explicit exceptions above if this proves overzealous.262return true;263}264265case "YieldExpression":266switch (parent.type) {267case "BinaryExpression":268case "LogicalExpression":269case "UnaryExpression":270case "SpreadElement":271case "SpreadProperty":272case "CallExpression":273case "MemberExpression":274case "NewExpression":275case "ConditionalExpression":276case "YieldExpression":277return true;278279default:280return false;281}282283case "Literal":284return parent.type === "MemberExpression"285&& isNumber.check(node.value)286&& name === "object"287&& parent.object === node;288289case "AssignmentExpression":290case "ConditionalExpression":291switch (parent.type) {292case "UnaryExpression":293case "SpreadElement":294case "SpreadProperty":295case "BinaryExpression":296case "LogicalExpression":297return true;298299case "CallExpression":300return name === "callee"301&& parent.callee === node;302303case "ConditionalExpression":304return name === "test"305&& parent.test === node;306307case "MemberExpression":308return name === "object"309&& parent.object === node;310311default:312return false;313}314315default:316if (parent.type === "NewExpression" &&317name === "callee" &&318parent.callee === node) {319return containsCallExpression(node);320}321}322323if (assumeExpressionContext !== true &&324!this.canBeFirstInStatement() &&325this.firstInStatement())326return true;327328return false;329};330331function isBinary(node) {332return n.BinaryExpression.check(node)333|| n.LogicalExpression.check(node);334}335336function isUnaryLike(node) {337return n.UnaryExpression.check(node)338// I considered making SpreadElement and SpreadProperty subtypes339// of UnaryExpression, but they're not really Expression nodes.340|| (n.SpreadElement && n.SpreadElement.check(node))341|| (n.SpreadProperty && n.SpreadProperty.check(node));342}343344var PRECEDENCE = {};345[["||"],346["&&"],347["|"],348["^"],349["&"],350["==", "===", "!=", "!=="],351["<", ">", "<=", ">=", "in", "instanceof"],352[">>", "<<", ">>>"],353["+", "-"],354["*", "/", "%"]355].forEach(function(tier, i) {356tier.forEach(function(op) {357PRECEDENCE[op] = i;358});359});360361function containsCallExpression(node) {362if (n.CallExpression.check(node)) {363return true;364}365366if (isArray.check(node)) {367return node.some(containsCallExpression);368}369370if (n.Node.check(node)) {371return types.someField(node, function(name, child) {372return containsCallExpression(child);373});374}375376return false;377}378379FPp.canBeFirstInStatement = function() {380var node = this.getNode();381return !n.FunctionExpression.check(node)382&& !n.ObjectExpression.check(node);383};384385FPp.firstInStatement = function() {386var s = this.stack;387var parentName, parent;388var childName, child;389390for (var i = s.length - 1; i >= 0; i -= 2) {391if (n.Node.check(s[i])) {392childName = parentName;393child = parent;394parentName = s[i - 1];395parent = s[i];396}397398if (!parent || !child) {399continue;400}401402if (n.BlockStatement.check(parent) &&403parentName === "body" &&404childName === 0) {405assert.strictEqual(parent.body[0], child);406return true;407}408409if (n.ExpressionStatement.check(parent) &&410childName === "expression") {411assert.strictEqual(parent.expression, child);412return true;413}414415if (n.SequenceExpression.check(parent) &&416parentName === "expressions" &&417childName === 0) {418assert.strictEqual(parent.expressions[0], child);419continue;420}421422if (n.CallExpression.check(parent) &&423childName === "callee") {424assert.strictEqual(parent.callee, child);425continue;426}427428if (n.MemberExpression.check(parent) &&429childName === "object") {430assert.strictEqual(parent.object, child);431continue;432}433434if (n.ConditionalExpression.check(parent) &&435childName === "test") {436assert.strictEqual(parent.test, child);437continue;438}439440if (isBinary(parent) &&441childName === "left") {442assert.strictEqual(parent.left, child);443continue;444}445446if (n.UnaryExpression.check(parent) &&447!parent.prefix &&448childName === "argument") {449assert.strictEqual(parent.argument, child);450continue;451}452453return false;454}455456return true;457};458459460