Skip to content

Commit

Permalink
Merge 2d2e2b0 into 1e1da30
Browse files Browse the repository at this point in the history
  • Loading branch information
twada committed Apr 15, 2015
2 parents 1e1da30 + 2d2e2b0 commit a33357d
Show file tree
Hide file tree
Showing 13 changed files with 4,842 additions and 4,052 deletions.
8 changes: 2 additions & 6 deletions bower.json
Expand Up @@ -32,14 +32,10 @@
"url": "git://github.com/twada/espower.git"
},
"devDependencies": {
"esprima": "~1.2.2",
"escodegen": "1.4.1",
"estraverse": "~1.9.0",
"mocha": "~2.1.0",
"requirejs": "~2.1.15",
"mocha": "~2.2.4",
"requirejs": "~2.1.17",
"assert": "Jxck/assert"
},
"resolutions": {
"esprima": "~1.2.2"
}
}
8,601 changes: 4,635 additions & 3,966 deletions build/espower.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions esp.js
@@ -1,9 +1,10 @@
var espower = require('./index'),
esprima = require('esprima'),
acorn = require('acorn'),
escodegen = require('escodegen');

var jsCode = process.argv[2];
var jsAst = esprima.parse(jsCode, {tolerant: true, loc: true, tokens: true});
var parserOptions = {ecmaVersion: 6, locations: true};
var jsAst = acorn.parse(jsCode, parserOptions);
var modifiedAst = espower(jsAst);

console.log(escodegen.generate(modifiedAst));
7 changes: 7 additions & 0 deletions lib/assertion-visitor.js
Expand Up @@ -66,6 +66,10 @@ AssertionVisitor.prototype.enter = function (currentNode, parentNode) {
}
};

AssertionVisitor.prototype.leave = function (currentNode, parentNode) {
// nothing to do now
};

AssertionVisitor.prototype.verifyNotInstrumented = function (currentNode) {
if (currentNode.type !== syntax.CallExpression) {
return;
Expand Down Expand Up @@ -218,6 +222,9 @@ function addToProps (props, createNode, name, value) {
name: name
}),
value: value,
method: false,
shorthand: false,
computed: false,
kind: 'init'
}));
}
Expand Down
18 changes: 6 additions & 12 deletions lib/instrumentor.js
Expand Up @@ -27,9 +27,9 @@ Instrumentor.prototype.instrument = function (ast) {
enter: function (currentNode, parentNode) {
var controller = this,
path = controller.path(),
currentPath = path ? path[path.length - 1] : null;
currentKey = path ? path[path.length - 1] : null;
if (assertionVisitor) {
if (toBeSkipped(currentNode, parentNode, currentPath)) {
if (toBeSkipped(currentNode, parentNode, currentKey)) {
skipping = true;
return controller.skip();
}
Expand All @@ -49,7 +49,8 @@ Instrumentor.prototype.instrument = function (ast) {
},
leave: function (currentNode, parentNode) {
var path = this.path(),
resultTree = currentNode;
resultTree = currentNode,
currentKey = path ? path[path.length - 1] : null;
if (!assertionVisitor) {
return undefined;
}
Expand All @@ -58,16 +59,14 @@ Instrumentor.prototype.instrument = function (ast) {
return undefined;
}
if (assertionVisitor.isLeavingAssertion(path)) {
assertionVisitor.leave(currentNode, parentNode);
assertionVisitor = null;
return undefined;
}
if (!assertionVisitor.isCapturingArgument()) {
return undefined;
}
if (isCalleeOfParent(currentNode, parentNode)) {
return undefined;
}
if (toBeCaptured(currentNode)) {
if (toBeCaptured(currentNode, parentNode, currentKey)) {
resultTree = assertionVisitor.captureNode(currentNode, path);
}
if (assertionVisitor.isLeavingArgument(path)) {
Expand All @@ -79,11 +78,6 @@ Instrumentor.prototype.instrument = function (ast) {
return result;
};

function isCalleeOfParent(currentNode, parentNode) {
return (parentNode.type === syntax.CallExpression || parentNode.type === syntax.NewExpression) &&
parentNode.callee === currentNode;
}

function verifyAstPrerequisites (ast, options) {
var errorMessage;
if (typeof ast.loc === 'undefined') {
Expand Down
3 changes: 3 additions & 0 deletions lib/rules/supported-node-types.js
Expand Up @@ -16,5 +16,8 @@ module.exports = [
syntax.ArrayExpression,
syntax.ConditionalExpression,
syntax.UpdateExpression,
syntax.TemplateLiteral,
syntax.TaggedTemplateExpression,
syntax.SpreadElement,
syntax.Property
];
52 changes: 35 additions & 17 deletions lib/rules/to-be-captured.js
@@ -1,21 +1,39 @@
'use strict';

var estraverse = require('estraverse'),
syntax = estraverse.Syntax;
var estraverse = require('estraverse');
var syntax = estraverse.Syntax;
var caputuringTargetTypes = [
// syntax.Property,
syntax.ObjectExpression,
syntax.ArrayExpression,
// syntax.ConditionalExpression,
syntax.Identifier,
syntax.MemberExpression,
syntax.CallExpression,
syntax.UnaryExpression,
syntax.BinaryExpression,
syntax.LogicalExpression,
syntax.AssignmentExpression,
syntax.NewExpression,
syntax.UpdateExpression,
syntax.TemplateLiteral,
syntax.TaggedTemplateExpression
];

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;
}
function isCaputuringTargetType (currentNode) {
return caputuringTargetTypes.indexOf(currentNode.type) !== -1;
}

function isCalleeOfParent(parentNode, currentKey) {
return (parentNode.type === syntax.CallExpression || parentNode.type === syntax.NewExpression) && currentKey === 'callee';
}

function isChildOfTaggedTemplateExpression(parentNode) {
return parentNode.type === syntax.TaggedTemplateExpression;
}

module.exports = function toBeCaptured (currentNode, parentNode, currentKey) {
return isCaputuringTargetType(currentNode) &&
!isCalleeOfParent(parentNode, currentKey) &&
!isChildOfTaggedTemplateExpression(parentNode);
};
46 changes: 32 additions & 14 deletions lib/rules/to-be-skipped.js
Expand Up @@ -4,40 +4,58 @@ var estraverse = require('estraverse'),
syntax = estraverse.Syntax,
supportedNodeTypes = require('./supported-node-types');

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

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

function isObjectLiteralKey (parentNode, currentKey) {
return isChildOfObjectLiteral(parentNode) && currentKey === 'key';
}

function isObjectLiteralValue (parentNode, currentKey) {
return isChildOfObjectLiteral(parentNode) && currentKey === 'value';
}

function isNonComputedObjectLiteralKey(parentNode, currentKey) {
// Do not instrument non-computed Object literal key
return isObjectLiteralKey(parentNode, currentKey) && !parentNode.computed;
}

function isShorthandedValueOfObjectLiteral(parentNode, currentKey) {
// Do not instrument shorthanded Object literal value
return isObjectLiteralValue(parentNode, currentKey) && parentNode.shorthand;
}

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

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

function isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath) {
function isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentKey) {
// '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';
return currentNode.type === syntax.Identifier && parentNode.type === syntax.UnaryExpression && (parentNode.operator === 'typeof' || parentNode.operator === 'delete') && currentKey === 'argument';
}

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

module.exports = function toBeSkipped (currentNode, parentNode, currentPath) {
module.exports = function toBeSkipped (currentNode, parentNode, currentKey) {
return !isSupportedNodeType(currentNode) ||
isLeftHandSideOfAssignment(parentNode, currentPath) ||
isObjectLiteralKey(parentNode, currentPath) ||
isLeftHandSideOfAssignment(parentNode, currentKey) ||
isNonComputedObjectLiteralKey(parentNode, currentKey) ||
isShorthandedValueOfObjectLiteral(parentNode, currentKey) ||
isUpdateExpression(parentNode) ||
isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentPath) ||
isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentPath);
isCallExpressionWithNonComputedMemberExpression(currentNode, parentNode, currentKey) ||
isTypeOfOrDeleteUnaryExpression(currentNode, parentNode, currentKey);
};
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -13,7 +13,7 @@
"dependencies": {
"clone": "^1.0.1",
"deep-equal": "^1.0.0",
"escallmatch": "^1.1.0",
"escallmatch": "^1.3.0",
"escodegen": "^1.6.1",
"espurify": "^1.1.0",
"estraverse": "^3.1.0",
Expand All @@ -22,10 +22,10 @@
"xtend": "^4.0.0"
},
"devDependencies": {
"acorn": "^1.0.3",
"blanket": "^1.1.6",
"browserify": "^9.0.8",
"del": "^1.1.1",
"esprima": "^2.1.0",
"gulp": "^3.8.11",
"gulp-derequire": "^2.0.0",
"gulp-dereserve": "^0.2.0",
Expand Down
30 changes: 15 additions & 15 deletions test/espower_option_test.js
@@ -1,15 +1,15 @@
(function (root, factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['espower', 'esprima', 'escodegen', 'estraverse', 'source-map', 'assert'], factory);
define(['espower', 'acorn', 'escodegen', 'estraverse', 'source-map', 'assert'], factory);
} else if (typeof exports === 'object') {
factory(require('..'), require('esprima'), require('escodegen'), require('estraverse'), require('source-map'), require('assert'));
factory(require('..'), require('acorn'), require('escodegen'), require('estraverse'), require('source-map'), require('assert'));
} else {
factory(root.espower, root.esprima, root.escodegen, root.estraverse, root.sourceMap, root.assert);
factory(root.espower, root.acorn, root.escodegen, root.estraverse, root.sourceMap, root.assert);
}
}(this, function (
espower,
esprima,
acorn,
escodegen,
estraverse,
sourceMap,
Expand All @@ -28,7 +28,7 @@ if (typeof define === 'function' && define.amd) {


function instrument (jsCode, options) {
var jsAST = esprima.parse(jsCode, {tolerant: true, loc: true});
var jsAST = acorn.parse(jsCode, {ecmaVersion: 6, locations: true});
var espoweredAST = espower(jsAST, options);
var instrumentedCode = escodegen.generate(espoweredAST, {format: {compact: true}});
return instrumentedCode;
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('instrumentation tests for options', function () {
describe('destructive option', function () {
function destructiveOptionTest (testName, option, callback) {
it(testName, function () {
var tree = esprima.parse('assert(falsyStr);', {tolerant: true, loc: true, range: true}),
var tree = acorn.parse('assert(falsyStr);', {ecmaVersion: 6, locations: true, ranges: true}),
saved = deepCopy(tree),
result = espower(tree, option);
callback(assert, saved, tree, result);
Expand Down Expand Up @@ -166,7 +166,7 @@ describe('instrumentation tests for options', function () {

describe('option prerequisites', function () {
beforeEach(function () {
this.tree = esprima.parse('assert(falsyStr);', {tolerant: true, loc: true, range: true});
this.tree = acorn.parse('assert(falsyStr);', {ecmaVersion: 6, locations: true, ranges: true});
});
function optionPrerequisitesTest (name, options, expected) {
it(name, function () {
Expand Down Expand Up @@ -196,7 +196,7 @@ describe('option prerequisites', function () {
describe('AST prerequisites. Error should be thrown if location is missing.', function () {
beforeEach(function () {
this.jsCode = 'assert(falsyStr);';
this.tree = esprima.parse(this.jsCode, {tolerant: true, loc: false});
this.tree = acorn.parse(this.jsCode, {ecmaVersion: 6, locations: false});
});
it('error message when path option is not specified', function () {
try {
Expand Down Expand Up @@ -229,7 +229,7 @@ describe('AST prerequisites. Error should be thrown if AST is already instrument

it('when going to instrument "assert(falsyStr);" twice', function () {
var alreadyEspoweredCode = "assert(assert._expr(assert._capt(falsyStr,'arguments/0'),{content:'assert(falsyStr)',filepath:'/path/to/some_test.js',line:1}));";
var ast = esprima.parse(alreadyEspoweredCode, {tolerant: true, loc: true});
var ast = acorn.parse(alreadyEspoweredCode, {ecmaVersion: 6, locations: true});
try {
espower(ast, {destructive: false, source: alreadyEspoweredCode, path: '/path/to/baz_test.js'});
assert.ok(false, 'Error should be thrown');
Expand All @@ -244,7 +244,7 @@ describe('AST prerequisites. Error should be thrown if AST is already instrument

it('when going to instrument "browser.assert.element(foo);" twice', function () {
var alreadyEspoweredCode = "browser.assert.element(browser.assert._expr(browser.assert._capt(foo,'arguments/0'),{content:'browser.assert.element(foo)',line:1}));";
var ast = esprima.parse(alreadyEspoweredCode, {tolerant: true, loc: true});
var ast = acorn.parse(alreadyEspoweredCode, {ecmaVersion: 6, locations: true});
try {
espower(ast, {
destructive: false,
Expand All @@ -270,7 +270,7 @@ describe('AST prerequisites. Error should be thrown if AST is already instrument
describe('location information', function () {
it('preserve location of instrumented nodes.', function () {
var jsCode = 'assert((three * (seven * ten)) === three);',
tree = esprima.parse(jsCode, {tolerant: true, loc: true, range: true}),
tree = acorn.parse(jsCode, {ecmaVersion: 6, locations: true, ranges: true}),
result = espower(tree, {destructive: false, source: jsCode, path: '/path/to/baz_test.js'});
estraverse.traverse(result, function (node) {
if (typeof node.type === 'undefined') return;
Expand Down Expand Up @@ -322,7 +322,7 @@ describe('SourceMap support', function () {
var originalCode = 'var str = "foo";\nvar anotherStr = "bar"\n\nassert.equal(\nstr,\nanotherStr\n);';
// console.log(originalCode);

var compactResult = escodegen.generate(esprima.parse(originalCode, {tolerant: true, loc: true, source: originalPath}), {
var compactResult = escodegen.generate(acorn.parse(originalCode, {ecmaVersion: 6, locations: true, sourceFile: originalPath}), {
format: {
compact: true
},
Expand All @@ -335,7 +335,7 @@ describe('SourceMap support', function () {
var sourceMap = compactResult.map.toString();
// console.log(sourceMap);

var espoweredAST = espower(esprima.parse(compactCode, {tolerant: true, loc: true, source: originalPath}), {
var espoweredAST = espower(acorn.parse(compactCode, {ecmaVersion: 6, locations: true, sourceFile: originalPath}), {
patterns: [
'assert.equal(actual, expected, [message])'
],
Expand All @@ -354,7 +354,7 @@ describe('SourceMap support', function () {
var originalCode = 'var str = "foo";\nvar anotherStr = "bar"\n\nassert.equal(\nstr,\nanotherStr\n);';
// console.log(originalCode);

var compactResult = escodegen.generate(esprima.parse(originalCode, {tolerant: true, loc: true, source: originalBasePath + originalRelativePath}), {
var compactResult = escodegen.generate(acorn.parse(originalCode, {ecmaVersion: 6, locations: true, sourceFile: originalBasePath + originalRelativePath}), {
format: {
compact: true
},
Expand All @@ -368,7 +368,7 @@ describe('SourceMap support', function () {
var sourceMap = compactResult.map.toString();
// console.log(sourceMap);

var espoweredAST = espower(esprima.parse(compactCode, {tolerant: true, loc: true, source: originalBasePath + originalRelativePath}), {
var espoweredAST = espower(acorn.parse(compactCode, {ecmaVersion: 6, locations: true, sourceFile: originalBasePath + originalRelativePath}), {
patterns: [
'assert.equal(actual, expected, [message])'
],
Expand Down

0 comments on commit a33357d

Please sign in to comment.