Skip to content

Commit

Permalink
feat(espower): extract and export traversal rules
Browse files Browse the repository at this point in the history
  • Loading branch information
twada committed Apr 12, 2015
1 parent 48e875f commit 5ca0e27
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 72 deletions.
5 changes: 5 additions & 0 deletions index.js
Expand Up @@ -35,4 +35,9 @@ espower.defaultOptions = defaultOptions;
espower.EspowerError = EspowerError;
espower.Instrumentor = Instrumentor;
espower.AssertionVisitor = AssertionVisitor;
espower.rules = {
supportedNodeTypes: require('./lib/rules/supported-node-types'),
toBeSkipped: require('./lib/rules/to-be-skipped'),
toBeCaptured: require('./lib/rules/to-be-captured')
};
module.exports = espower;
76 changes: 4 additions & 72 deletions lib/instrumentor.js
@@ -1,28 +1,15 @@
'use strict';

var estraverse = require('estraverse'),
syntax = estraverse.Syntax,
escallmatch = require('escallmatch'),
SourceMapConsumer = require('source-map').SourceMapConsumer,
clone = require('clone'),
AssertionVisitor = require('./assertion-visitor'),
EspowerError = require('./espower-error'),
typeName = require('type-name'),
syntax = estraverse.Syntax,
supportedNodeTypes = [
syntax.Identifier,
syntax.MemberExpression,
syntax.CallExpression,
syntax.UnaryExpression,
syntax.BinaryExpression,
syntax.LogicalExpression,
syntax.AssignmentExpression,
syntax.ObjectExpression,
syntax.NewExpression,
syntax.ArrayExpression,
syntax.ConditionalExpression,
syntax.UpdateExpression,
syntax.Property
];
toBeSkipped = require('./rules/to-be-skipped'),
toBeCaptured = require('./rules/to-be-captured'),
typeName = require('type-name');

function Instrumentor (options) {
verifyOptionPrerequisites(options);
Expand Down Expand Up @@ -101,61 +88,6 @@ function isCalleeOfParent(currentNode, parentNode) {
parentNode.callee === currentNode;
}

function toBeCaptured (currentNode) {
switch(currentNode.type) {
case syntax.Identifier:
case syntax.MemberExpression:
case syntax.CallExpression:
case syntax.UnaryExpression:
case syntax.BinaryExpression:
case syntax.LogicalExpression:
case syntax.AssignmentExpression:
case syntax.UpdateExpression:
case syntax.NewExpression:
return true;
default:
return false;
}
}

function toBeSkipped (currentNode, parentNode, currentPath) {
return !isSupportedNodeType(currentNode) ||
isLeftHandSideOfAssignment(parentNode, currentPath) ||
isObjectLiteralKey(parentNode, currentPath) ||
isUpdateExpression(parentNode) ||
isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentPath) ||
isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath);
}

function isLeftHandSideOfAssignment(parentNode, currentPath) {
// Do not instrument left due to 'Invalid left-hand side in assignment'
return parentNode.type === syntax.AssignmentExpression && currentPath === 'left';
}

function isObjectLiteralKey(parentNode, currentPath) {
// Do not instrument Object literal key
return parentNode.type === syntax.Property && parentNode.kind === 'init' && currentPath === 'key';
}

function isUpdateExpression(parentNode) {
// Just wrap UpdateExpression, not digging in.
return parentNode.type === syntax.UpdateExpression;
}

function isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentPath) {
// Do not instrument non-computed property of MemberExpression within CallExpression.
return currentNode.type === syntax.Identifier && parentNode.type === syntax.MemberExpression && !parentNode.computed && currentPath === 'property';
}

function isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath) {
// 'typeof Identifier' or 'delete Identifier' is not instrumented
return currentNode.type === syntax.Identifier && parentNode.type === syntax.UnaryExpression && (parentNode.operator === 'typeof' || parentNode.operator === 'delete') && currentPath === 'argument';
}

function isSupportedNodeType (node) {
return supportedNodeTypes.indexOf(node.type) !== -1;
}

function verifyAstPrerequisites (ast, options) {
var errorMessage;
if (typeof ast.loc === 'undefined') {
Expand Down
20 changes: 20 additions & 0 deletions lib/rules/supported-node-types.js
@@ -0,0 +1,20 @@
'use strict';

var estraverse = require('estraverse'),
syntax = estraverse.Syntax;

module.exports = [
syntax.Identifier,
syntax.MemberExpression,
syntax.CallExpression,
syntax.UnaryExpression,
syntax.BinaryExpression,
syntax.LogicalExpression,
syntax.AssignmentExpression,
syntax.ObjectExpression,
syntax.NewExpression,
syntax.ArrayExpression,
syntax.ConditionalExpression,
syntax.UpdateExpression,
syntax.Property
];
21 changes: 21 additions & 0 deletions lib/rules/to-be-captured.js
@@ -0,0 +1,21 @@
'use strict';

var estraverse = require('estraverse'),
syntax = estraverse.Syntax;

module.exports = function toBeCaptured (currentNode) {
switch(currentNode.type) {
case syntax.Identifier:
case syntax.MemberExpression:
case syntax.CallExpression:
case syntax.UnaryExpression:
case syntax.BinaryExpression:
case syntax.LogicalExpression:
case syntax.AssignmentExpression:
case syntax.UpdateExpression:
case syntax.NewExpression:
return true;
default:
return false;
}
};
43 changes: 43 additions & 0 deletions lib/rules/to-be-skipped.js
@@ -0,0 +1,43 @@
'use strict';

var estraverse = require('estraverse'),
syntax = estraverse.Syntax,
supportedNodeTypes = require('./supported-node-types');

function isLeftHandSideOfAssignment(parentNode, currentPath) {
// Do not instrument left due to 'Invalid left-hand side in assignment'
return parentNode.type === syntax.AssignmentExpression && currentPath === 'left';
}

function isObjectLiteralKey(parentNode, currentPath) {
// Do not instrument Object literal key
return parentNode.type === syntax.Property && parentNode.kind === 'init' && currentPath === 'key';
}

function isUpdateExpression(parentNode) {
// Just wrap UpdateExpression, not digging in.
return parentNode.type === syntax.UpdateExpression;
}

function isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentPath) {
// Do not instrument non-computed property of MemberExpression within CallExpression.
return currentNode.type === syntax.Identifier && parentNode.type === syntax.MemberExpression && !parentNode.computed && currentPath === 'property';
}

function isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath) {
// 'typeof Identifier' or 'delete Identifier' is not instrumented
return currentNode.type === syntax.Identifier && parentNode.type === syntax.UnaryExpression && (parentNode.operator === 'typeof' || parentNode.operator === 'delete') && currentPath === 'argument';
}

function isSupportedNodeType (node) {
return supportedNodeTypes.indexOf(node.type) !== -1;
}

module.exports = function toBeSkipped (currentNode, parentNode, currentPath) {
return !isSupportedNodeType(currentNode) ||
isLeftHandSideOfAssignment(parentNode, currentPath) ||
isObjectLiteralKey(parentNode, currentPath) ||
isUpdateExpression(parentNode) ||
isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentPath) ||
isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath);
};

0 comments on commit 5ca0e27

Please sign in to comment.