Comparing version 0.1.4 to 0.1.5
@@ -67,3 +67,3 @@ // Copyright 2006-2008 the V8 project authors. All rights reserved. | ||
line = colors.white(line); | ||
} | ||
} | ||
} | ||
@@ -70,0 +70,0 @@ |
@@ -21,4 +21,3 @@ /** | ||
process.nextTick = function wrappedNextTick(callback) { | ||
arguments[0] = wrap(callback, 'process.nextTick') | ||
return nextTick.apply(this, arguments) | ||
return nextTick.call(this, wrap(callback, 'process.nextTick')) | ||
} | ||
@@ -48,4 +47,10 @@ | ||
, id | ||
, newCallback | ||
, origListener = listener | ||
if (this === process && type === 'uncaughtException') { | ||
listener = function(e) { | ||
if (e.domain == null) origListener(e) | ||
} | ||
} | ||
self._hookListeners = self._hookListeners || {} | ||
@@ -67,6 +72,3 @@ id = listener._hookId = listener._hookId || ''+process.hrtime() | ||
newCallback = wrap(listener, 'EventEmitter.on') | ||
if (newCallback !== listener) { | ||
self._hookListeners[id][type].push(newCallback) | ||
} | ||
self._hookListeners[id][type].push(wrap(listener, 'EventEmitter.on')) | ||
return onEvent.call(this, type, self._hookListeners[id][type].callback) | ||
@@ -73,0 +75,0 @@ } |
@@ -1,17 +0,72 @@ | ||
module.exports = trycatch | ||
var util = require('util') | ||
, FormatStackTrace = require('./formatStackTrace') | ||
, Block = require('./control-block') | ||
, filename1 = __filename | ||
, filename2 = require.resolve('./hook') | ||
, options = { | ||
'long-stack-traces': false | ||
} | ||
// use colors module, if available | ||
try { trycatch.colors = require('colors') } catch(err) {} | ||
var FormatStackTrace = require('./formatStackTrace'), | ||
filename1 = __filename, | ||
filename2 = require.resolve('./hook') | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
Error.stackTraceLimit = Infinity | ||
module.exports = trycatch | ||
// Replace built-in async functions, shim callbacks | ||
require('./hook')(generateShim) | ||
function trycatch(tryFn, catchFn) { | ||
trycatch.begin(tryFn, catchFn) | ||
} | ||
trycatch.begin = Block.begin | ||
trycatch.guard = Block.guard | ||
trycatch.configure = configure | ||
function configure(opts) { | ||
util._extend(options, opts) | ||
if (!!opts['long-stack-traces']) { | ||
// findToken fails when _TOKEN_ deeper than Error.stackTraceLimit | ||
Error.stackTraceLimit = Infinity | ||
trycatch.guard = longStackTracesGuard | ||
trycatch.begin = begin | ||
} else if (typeof opts['long-stack-traces'] !== 'undefined') { | ||
Error.stackTraceLimit = 10 | ||
trycatch.begin = Block.begin | ||
trycatch.guard = Block.guard | ||
} | ||
} | ||
function begin(tryFn, catchFn) { | ||
// Create origin _TOKEN_ for stack termination | ||
function _TOKEN_() { | ||
tryFn() | ||
} | ||
_TOKEN_.catchFn = catchFn | ||
_TOKEN_.error = new Error | ||
_TOKEN_.parent = 'trycatch' | ||
try { | ||
_TOKEN_() | ||
} catch (err) { | ||
catchFn(generateStack(err)) | ||
} | ||
} | ||
function generateStack(err, fn) { | ||
if (typeof fn !== 'function') { | ||
fn = function(error, structuredStackTrace) { | ||
return FormatStackTrace(error | ||
, structuredStackTrace | ||
, [filename1, filename2] | ||
, trycatch.colors) | ||
} | ||
} | ||
if (!err) err = new Error | ||
var old = Error.prepareStackTrace | ||
Error.prepareStackTrace = fn | ||
err.stack = err.stack | ||
Error.prepareStackTrace = old | ||
return err | ||
} | ||
// Generate a new callback wrapped in _TOKEN_ with Error to trace back | ||
function generateShim(next, name, location) { | ||
function longStackTracesGuard(next, name, location) { | ||
if (typeof next !== 'function') return next | ||
@@ -28,3 +83,3 @@ | ||
_TOKEN_.orig = name | ||
_TOKEN_.parent = name | ||
_TOKEN_.error = new Error | ||
@@ -36,3 +91,3 @@ | ||
function handleError(err, token, recursive) { | ||
var origin | ||
var parent | ||
@@ -43,4 +98,4 @@ if (!recursive) { | ||
err = err instanceof Error ? err : new Error(''+err) | ||
err = getFilteredError(err) | ||
err.originalStack = err.stack | ||
err = generateStack(err) | ||
err.parentalStack = err.stack | ||
} else { | ||
@@ -52,12 +107,12 @@ token = err.token | ||
while(token.error) { | ||
// stackSearch returns an object {token, stack} in place of error.stack String | ||
origin = getFilteredError(token.error, stackSearch).stack | ||
if (!origin) throw err | ||
// HACK: Use Error.prepareStackTrace = stackSearch to find parent | ||
parent = generateStack(token.error, stackSearch).stack | ||
if (!parent) throw err | ||
if (!token.catchFn && origin.stack) { | ||
if (!token.catchFn && parent.stack) { | ||
err.stack += '\n ----------------------------------------\n' + | ||
' at '+token.orig+'\n' + | ||
origin.stack.substring(origin.stack.indexOf("\n") + 1) | ||
' at '+token.parent+'\n' + | ||
parent.stack.substring(parent.stack.indexOf("\n") + 1) | ||
} | ||
token = origin.token | ||
token = parent.token | ||
if (token.catchFn) break | ||
@@ -76,32 +131,2 @@ } | ||
// Create origin _TOKEN_ for stack termination | ||
function trycatch(tryFn, catchFn) { | ||
function _TOKEN_() { | ||
tryFn() | ||
} | ||
_TOKEN_.catchFn = catchFn | ||
_TOKEN_.error = new Error | ||
_TOKEN_.orig = 'trycatch' | ||
try { | ||
_TOKEN_() | ||
} catch (err) { | ||
catchFn(getFilteredError(err)) | ||
} | ||
} | ||
function getFilteredError(err, fn) { | ||
if (typeof fn !== 'function') { | ||
fn = function(error, structuredStackTrace) { | ||
return FormatStackTrace(error, structuredStackTrace, [filename1, filename2], trycatch.colors) | ||
} | ||
} | ||
if (!err) err = new Error; | ||
var old = Error.prepareStackTrace | ||
Error.prepareStackTrace = fn | ||
err.stack = err.stack | ||
Error.prepareStackTrace = old | ||
return err | ||
} | ||
function stackSearch(error, structuredStackTrace) { | ||
@@ -120,1 +145,6 @@ if (!structuredStackTrace) return | ||
} | ||
// Replace built-in async functions, shim callbacks with generator | ||
require('./hook')(function generateShim(next, name, location) { | ||
return trycatch.guard(next, name, location) | ||
}) |
{ | ||
"name": "trycatch", | ||
"version": "0.1.4", | ||
"version": "0.1.5", | ||
"description": "An asynchronous exception handler with long stack traces for node.js", | ||
@@ -5,0 +5,0 @@ "homepage": "http://github.com/CrabDude/trycatch", |
@@ -1,3 +0,4 @@ | ||
var assert = require('assert'), | ||
trycatch = require('../lib/trycatch') | ||
var trycatch = require('../lib/trycatch') | ||
, assert = require('assert') | ||
@@ -8,30 +9,39 @@ /* | ||
describe('Basic Error catching', function() { | ||
it('should catch Error object thrown synchronously', function(done) { | ||
var onErrorCalled = false | ||
function run(longStackTraces) { | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('Basic Error catching' + str, function() { | ||
before(function() { | ||
trycatch.configure({ | ||
'long-stack-traces': !!longStackTraces | ||
}) | ||
}) | ||
trycatch(function() { | ||
(function foo() { | ||
throw new Error('Sync') | ||
})() | ||
}, function(err) { | ||
assert.equal(err.message, 'Sync') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
it('should catch Error object thrown synchronously', function(done) { | ||
trycatch(function() { | ||
(function foo() { | ||
throw new Error('Sync') | ||
})() | ||
}, function(err) { | ||
assert.equal(err.message, 'Sync') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
}) | ||
it('should catch Error object thrown asynchronously', function(done) { | ||
var onErrorCalled = false | ||
trycatch(function() { | ||
process.nextTick(function() { | ||
throw new Error('Async') | ||
it('should catch Error object thrown asynchronously', function(done) { | ||
trycatch(function() { | ||
process.nextTick(function() { | ||
throw new Error('Async') | ||
}) | ||
}, function(err) { | ||
assert.equal(err.message, 'Async') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}, function(err) { | ||
assert.equal(err.message, 'Async') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
}) | ||
} | ||
run(false) | ||
run(true) |
@@ -14,76 +14,88 @@ var trycatch = require('../lib/trycatch') | ||
describe('EventEmitter', function() { | ||
function EE() {} | ||
util.inherits(EE, events.EventEmitter) | ||
EE.prototype.sync = function() { | ||
this.emit('sync') | ||
} | ||
EE.prototype.onSync = function() { | ||
throw new Error('Sync') | ||
} | ||
EE.prototype.async = function() { | ||
var self = this | ||
function run(longStackTraces) { | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('EventEmitter' + str, function() { | ||
before(function() { | ||
trycatch.configure({ | ||
'long-stack-traces': !!longStackTraces | ||
}) | ||
}) | ||
setTimeout(function() { | ||
self.emit('async') | ||
}, 0) | ||
} | ||
EE.prototype.onAsync = function() { | ||
throw new Error('Async') | ||
} | ||
function EE() {} | ||
util.inherits(EE, events.EventEmitter) | ||
EE.prototype.sync = function() { | ||
this.emit('sync') | ||
} | ||
EE.prototype.onSync = function() { | ||
throw new Error('Sync') | ||
} | ||
EE.prototype.async = function() { | ||
var self = this | ||
it('should catch when emit called synchronously', function(done) { | ||
trycatch(function() { | ||
var ee = new EE | ||
ee.on('sync', ee.onSync) | ||
ee.sync() | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Sync') | ||
done() | ||
}) | ||
}) | ||
setTimeout(function() { | ||
self.emit('async') | ||
}, 0) | ||
} | ||
EE.prototype.onAsync = function() { | ||
throw new Error('Async') | ||
} | ||
it('should catch when emit called asynchronously', function(done) { | ||
trycatch(function() { | ||
var ee = new EE | ||
ee.on('async', ee.onAsync) | ||
ee.async() | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Async') | ||
done() | ||
}) | ||
}) | ||
it('should catch when emit called synchronously', function(done) { | ||
trycatch(function() { | ||
var ee = new EE | ||
ee.on('sync', ee.onSync) | ||
ee.sync() | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Sync') | ||
done() | ||
}) | ||
}) | ||
it('should catch when asynchronously called and emitted', function(done) { | ||
trycatch(function() { | ||
setTimeout(function() { | ||
it('should catch when emit called asynchronously', function(done) { | ||
trycatch(function() { | ||
var ee = new EE | ||
ee.on('async', ee.onAsync) | ||
ee.async() | ||
}, 0) | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Async') | ||
done() | ||
}) | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Async') | ||
done() | ||
}) | ||
}) | ||
it('should catch when asynchronously called and emitted', function(done) { | ||
trycatch(function() { | ||
setTimeout(function() { | ||
var ee = new EE | ||
ee.on('async', ee.onAsync) | ||
ee.async() | ||
}, 0) | ||
} | ||
, function(err) { | ||
assert.equal(err.message, 'Async') | ||
done() | ||
}) | ||
}) | ||
it('should removeListener if addListener called multiple times', function(done) { | ||
var ee = new EE | ||
function foo() { | ||
throw new Error('Event handler should have been removed') | ||
} | ||
ee.on('foo', foo) | ||
ee.on('foo', foo) | ||
ee.removeListener('foo', foo) | ||
ee.removeListener('foo', foo) | ||
assert.doesNotThrow(function() { | ||
ee.emit('foo') | ||
}) | ||
done() | ||
}) | ||
}) | ||
} | ||
it('should removeListener if addListener called multiple times', function(done) { | ||
var ee = new EE | ||
function foo() { | ||
throw new Error('Event handler should have been removed') | ||
} | ||
ee.on('foo', foo) | ||
ee.on('foo', foo) | ||
ee.removeListener('foo', foo) | ||
ee.removeListener('foo', foo) | ||
assert.doesNotThrow(function() { | ||
ee.emit('foo') | ||
}) | ||
done() | ||
}) | ||
}) | ||
run(false) | ||
run(true) |
@@ -10,80 +10,106 @@ var assert = require('assert'), | ||
describe('Nested trycatchs', function() { | ||
var delimitter = '----------------------------------------' | ||
it('should catch nested synchronously rethrown errors', function(done) { | ||
var count = 0 | ||
trycatch(function () { | ||
trycatch(function () { | ||
++count | ||
throw new Error('test 1') | ||
}, function(err) { | ||
++count | ||
assert.equal(err.stack.split(delimitter).length, 1) | ||
throw err; | ||
function run(longStackTraces) { | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('Nested trycatchs' + str, function() { | ||
before(function() { | ||
trycatch.configure({ | ||
'long-stack-traces': !!longStackTraces | ||
}) | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 1') | ||
assert.equal(count, 3) | ||
assert.equal(err.stack.split(delimitter).length, 1) | ||
done(); | ||
}) | ||
}) | ||
it('should catch asynchronously nested rethrown errors', function(done) { | ||
var count = 0 | ||
var delimitter = '----------------------------------------' | ||
trycatch(function () { | ||
setTimeout(function() { | ||
it('should catch nested synchronously rethrown errors', function(done) { | ||
var count = 0 | ||
trycatch(function () { | ||
trycatch(function () { | ||
setTimeout(function() { | ||
++count | ||
throw new Error('test 2') | ||
}, 0) | ||
++count | ||
throw new Error('test 1') | ||
}, function(err) { | ||
++count | ||
assert.equal(err.stack.split(delimitter).length, 2) | ||
// assert.equal(err.stack.split(delimitter).length, 1) | ||
throw err; | ||
}) | ||
}, 0) | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 2') | ||
assert.equal(count, 3) | ||
assert.equal(err.stack.split(delimitter).length, 3) | ||
done(); | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 1') | ||
assert.equal(count, 3) | ||
if (longStackTraces) { | ||
assert.equal(err.stack.split(delimitter).length, 1) | ||
} | ||
done(); | ||
}) | ||
}) | ||
}) | ||
it('should catch asynchronously nested asynchronously rethrown errors', function(done) { | ||
var count = 0 | ||
it('should catch asynchronously nested rethrown errors', function(done) { | ||
var count = 0 | ||
trycatch(function () { | ||
setTimeout(function() { | ||
trycatch(function () { | ||
setTimeout(function() { | ||
trycatch(function () { | ||
setTimeout(function() { | ||
trycatch(function () { | ||
setTimeout(function() { | ||
++count | ||
throw new Error('test 2') | ||
}, 0) | ||
}, function(err) { | ||
++count | ||
throw new Error('test 3') | ||
}, 0) | ||
}, function(err) { | ||
setTimeout(function() { | ||
++count | ||
assert.equal(err.stack.split(delimitter).length, 2) | ||
if (longStackTraces) { | ||
assert.equal(err.stack.split(delimitter).length, 2) | ||
} | ||
throw err; | ||
}, 0) | ||
}) | ||
}, 0) | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 3') | ||
assert.equal(count, 3) | ||
assert.equal(err.stack.split(delimitter).length, 3) | ||
done(); | ||
}) | ||
}, 0) | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 2') | ||
assert.equal(count, 3) | ||
if (longStackTraces) { | ||
assert.equal(err.stack.split(delimitter).length, 3) | ||
} | ||
done(); | ||
}) | ||
}) | ||
it('should catch asynchronously nested asynchronously rethrown errors', function(done) { | ||
var count = 0 | ||
trycatch(function () { | ||
setTimeout(function() { | ||
trycatch(function () { | ||
setTimeout(function() { | ||
++count | ||
throw new Error('test 3') | ||
}, 0) | ||
}, function(err) { | ||
setTimeout(function() { | ||
++count | ||
if (longStackTraces) { | ||
assert.equal(err.stack.split(delimitter).length, 2) | ||
} | ||
throw err; | ||
}, 0) | ||
}) | ||
}, 0) | ||
}, function(err) { | ||
++count | ||
assert.notEqual(err.name, 'AssertionError') | ||
assert.equal(err.message, 'test 3') | ||
assert.equal(count, 3) | ||
if (longStackTraces) { | ||
assert.equal(err.stack.split(delimitter).length, 3) | ||
} | ||
done(); | ||
}) | ||
}) | ||
}) | ||
}) | ||
} | ||
run(false) | ||
run(true) |
@@ -14,40 +14,53 @@ var assert = require('assert'), | ||
describe('non-Errors', function() { | ||
it('should catch Strings', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 'my-string being thrown' | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, 'my-string being thrown') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
function run(longStackTraces) { | ||
var str = longStackTraces ? ' (long-stack-traces)' : '' | ||
describe('non-Errors' + str, function() { | ||
before(function() { | ||
trycatch.configure({ | ||
'long-stack-traces': !!longStackTraces | ||
}) | ||
}) | ||
}) | ||
it('should catch Numbers', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 123 | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (123).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
it('should catch Strings', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 'my-string being thrown' | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, 'my-string being thrown') | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
}) | ||
it('should catch Booleans', function (done) { | ||
var onErrorCalled = false | ||
it('should catch Numbers', function (done) { | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw 123 | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (123).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw true | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (true).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
it('should catch Booleans', function (done) { | ||
var onErrorCalled = false | ||
trycatch(function () { | ||
setTimeout(function () { | ||
throw true | ||
}, 0) | ||
}, function onError(err) { | ||
assert.equal(err.message, (true).toString()) | ||
assert.notEqual(err.stack, undefined) | ||
done() | ||
}) | ||
}) | ||
}) | ||
}) | ||
} | ||
run(false) | ||
run(true) |
49586
16
807