Skip to content

Instantly share code, notes, and snippets.

@pmuellr
Created December 2, 2009 20:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pmuellr/247514 to your computer and use it in GitHub Desktop.
Save pmuellr/247514 to your computer and use it in GitHub Desktop.
JavaScript traceback sample
// sample code
function legend(message) {
var dashes = new Array(60).join("-")
console.log(dashes)
console.log(message)
console.log(dashes)
console.log("")
}
// tests with user-land function
function factorial(n) {
if (n <= 0) return 1
return n * factorial(n-1)
}
function printFactorial(n) {
console.log(n + "! == " + factorial(n))
console.log("")
}
function a() { b() }
function b() { c() }
function c() {
printFactorial(0)
printFactorial(5)
}
legend("calling factorial() before wrapping")
a()
factorial = wrapWithBackTrace(factorial)
legend("calling factorial() after wrapping")
a()
legend("calling factorial() at top level")
factorial(0)
// tests with built-in function
setTimeout = wrapWithBackTrace(setTimeout)
function doNothing() {
console.log("in doNothing()")
console.log("")
}
function x() { y() }
function y() { z() }
function z() { setTimeout(doNothing, 100) }
legend("calling setTimeout() after wrapping")
x()
legend("calling setTimeout() at top level")
setTimeout(doNothing, 200)
// requires Array.indexOf, Array.map, and Array.forEach
// if you don't have these, you can get portable implementations here:
// https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Objects/Array
//---------------------------------------------------------------------
// wrap a function with a backtrace printer
//
// parameters:
// func: the function to wrap
// receiver: the (optional) receiver to call func with
//
// returns:
// a new function to use in replace of the original function
//---------------------------------------------------------------------
function wrapWithBackTrace(func, receiver) {
// get the name of a function
function funcName(func) {
return func.name || "{anonymous}"
}
// generate the actual backtrace as an array of strings
function getBackTrace() {
var stack = []
var func = arguments.callee.caller.caller
// iterate through the stack
while (func) {
// check for recursion!
if (stack.indexOf(func) != -1) {
stack.push({name: "... recursion on " + funcName(func)})
break
}
// collect results, move to next entry
stack.push(func)
func = func.caller
}
// convert results to function names
var result = stack.map(function(element) {
return funcName(element) + "()"
})
// if nothing on stack, was a top level call
if (!result.length) result = ["{top level call}"]
return result
}
// returns the function to use as a replacement
return function wrappedWithBackTrace() {
// get the calling arguments
var args = Array.prototype.slice.call(arguments)
// call the original function
var result = func.apply(receiver, args)
// get the back trace
var trace = getBackTrace()
// get our function name
var name = funcName(func)
// print results
console.log("backtrace for " + name + "()")
trace.forEach(function (element) {
console.log(" - " + element)
})
console.log("")
return result
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment