Skip to content

Commit

Permalink
Clean up instrumentation code.
Browse files Browse the repository at this point in the history
  • Loading branch information
omphalos committed Dec 28, 2013
1 parent 718d689 commit 070fdf4
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 140 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ JavaScript execution logs.

![earhorn](https://raw.github.com/omphalos/earhorn/master/logo.jpg)

Earhorn instruments your JavaScript and shows you a detailed, reversible, line-by-line log of JavaScript execution, sort of like a console.log on steroids.
Earhorn instruments your JavaScript and shows you a detailed, reversible, line-by-line log of JavaScript execution, sort of like console.log's crazy uncle.

Demo
====
Expand Down
147 changes: 82 additions & 65 deletions earhorn.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
bufferSize: 100,
flushInterval: 25,
handleErrors: true,
logModifiedCode: true,
verbose: false,
quiet: true
}

Expand Down Expand Up @@ -118,6 +118,21 @@
return type.indexOf('Expression', type.length - 'Expression'.length) >= 0
}

// Wrap Identifiers with calls to our logger, eh$(...)

scripts[name] = {
body: body,
modified: modified
}

function getLocationUpdate(node) {
return 'eh$loc="' +
(node.loc.start.line - 1) + ',' +
node.loc.start.column + ',' +
(node.loc.end.line - 1) + ',' +
node.loc.end.column + '"'
}

var instrumentedExpressions = [
'NewExpression',
'CallExpression',
Expand All @@ -142,60 +157,6 @@
'BinaryExpression',
'ThrowStatement'
]

var skippedMemberExpressionParents = [
'AssignmentExpression',
'UnaryExpression',
'UpdateExpression',
'CallExpression', /*
'NewExpression' */
]

// Wrap Identifiers with calls to our logger, eh$(...)

scripts[name] = {
body: body,
modified: modified
}

var handleErrors = settings.instrumentation.handleErrors
, tryPrefix = handleErrors ? 'var eh$log; try {' : ''
, catchSuffix = handleErrors ?
'} catch(err) { eh$("' + name + '",eh$loc,err,true); throw err; }' :
''

var instrumentedCode

try {
if(!settings.instrumentation.quiet)
console.log('parsing', name)
instrumentedCode = falafel(body, { loc: true, raw: true }, visitNode).toString()
scripts[name].parseError = null
announce(name)
} catch(err) {
console.error(name, err.toString(), body)

var e = err.toString()
, colon1 = e.indexOf(': ')
, colon2 = e.indexOf(': ', colon1 + 1)
, message = e.substring(colon2 + ': '.length)

scripts[name].parseError = {
line: err.lineNumber - 1,
ch: err.column,
message: message
}
announce(name)
throw err
}

function getLocationUpdate(node) {
return 'eh$loc="' +
(node.loc.start.line - 1) + ',' +
node.loc.start.column + ',' +
(node.loc.end.line - 1) + ',' +
node.loc.end.column + '"'
}

function visitNode(node) {

Expand All @@ -209,11 +170,13 @@
if(node.type === 'Literal')
return

if(
if(
node.parent.type === 'BlockStatement' && node.parent.parent && (
node.parent.parent.type === 'FunctionDeclaration' ||
node.parent.parent.type === 'FunctionExpression')) {

// Add try/catch inside function bodies.

if(node.parent.body[0] === node) {
node.update(tryPrefix + node.source())
}
Expand All @@ -226,8 +189,6 @@
}

if(
(node.parent.type === 'CallExpression' &&
node !== node.parent.callee) ||
(node.parent.type === 'IfStatement' &&
node === node.parent.test) ||
(node.parent.type === 'WhileStatement' &&
Expand All @@ -240,16 +201,41 @@
node === node.parent.discriminant) ||
(node.parent.type === 'MemberExpression' &&
node === node.parent.object) ||

instrumentedExpressions.indexOf(node.type) >= 0 ||
(instrumentedParentTypes.
indexOf(node.parent.type) >= 0) ||
(node.type === 'MemberExpression' &&
skippedMemberExpressionParents.indexOf(node.parent.type) < 0)) {

(instrumentedParentTypes.indexOf(node.parent.type) >= 0) ||

(node.type === 'MemberExpression' &&

// Disallow eh$(eh$(a).b) = 123;
// Allow eh$(a).b = 123;
node.parent.type !== 'AssignmentExpression' &&

// Disallow eh$(eh$(a).b)++;
// Allow eh$(a).b++;
node.parent.type !== 'UpdateExpression' &&

/* Example explaining why we can't wrap CallExpression:
// This works.
[].forEach(function() { })
// This fails.
function returnArg(arg) { return arg }
returnArg([].forEach)(function() { })
In the instrumented code we would have a call to eh$,
which is basically a fancy version of returnArg. */
node.parent.type !== 'CallExpression')) {

/* Add parens to fix NewExpression:
new eh$(X)() // Broken, without parens.
new (eh$(X)()) // Fixed, with parens. */

var parenStart
, parenEnd
if(node.parent.type === 'NewExpression') {

if(node.parent.type === 'NewExpression') {
parenStart = '('
parenEnd = ')'
} else parenStart = parenEnd = ''
Expand All @@ -261,6 +247,37 @@
')' + parenEnd)
}
}

var handleErrors = settings.instrumentation.handleErrors
, tryPrefix = handleErrors ? 'var eh$log; try {' : ''
, catchSuffix = handleErrors ?
'} catch(err) { eh$("' + name + '",eh$loc,err,true); throw err; }' :
''

var instrumentedCode

try {
if(!settings.instrumentation.quiet)
console.log('parsing', name)
instrumentedCode = falafel(body, { loc: true, raw: true }, visitNode).toString()
scripts[name].parseError = null
announce(name)
} catch(err) {
console.error(name, err.toString(), body)

var e = err.toString()
, colon1 = e.indexOf(': ')
, colon2 = e.indexOf(': ', colon1 + 1)
, message = e.substring(colon2 + ': '.length)

scripts[name].parseError = {
line: err.lineNumber - 1,
ch: err.column,
message: message
}
announce(name)
throw err
}

instrumentedCode =
tryPrefix +
Expand Down
4 changes: 2 additions & 2 deletions examples/todomvc-jquery/js/app.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
earhorn$('app.js', /*global jquery, Handlebars */
earhorn$('app.js', function() {/*global jquery, Handlebars */
jQuery(function ($) {
'use strict';

Expand Down Expand Up @@ -181,4 +181,4 @@ jQuery(function ($) {
};

App.init();
}));
})})();
Loading

0 comments on commit 070fdf4

Please sign in to comment.