Skip to content

Instantly share code, notes, and snippets.

@jeancroy
Last active November 21, 2015 04:25
Show Gist options
  • Save jeancroy/5e0372dc4dbf03000e28 to your computer and use it in GitHub Desktop.
Save jeancroy/5e0372dc4dbf03000e28 to your computer and use it in GitHub Desktop.
// For use whithin node.js
referenceStack = null;
wrapperCache = null;
privilegiedCallback = null;
lastPrepareStackTrace = null;
//
// Each wrapper contain a link to their own callback
// And each of those will try to call the privileged callback before
//
// The test stack !== referenceStack is done so the privileged callback is
// called at most once per error.
//
// The privileged callback has to access the stack object before anyone else
// because other callback can modify the shared object.
//
// If holding on the stack object is detrimental, maybe it can be stored in a weakMap
//
function wrapperFactory(callback) {
lastPrepareStackTrace = callback;
return function (error, stack) {
if (!error) return stack;
if (stack !== referenceStack) {
referenceStack = stack;
if (typeof privilegiedCallback === 'function') {
privilegiedCallback.call(this, error, stack);
}
}
if (typeof callback === 'function') {
return callback.call(this, error, stack);
}
return stack
}
}
function hijackPrepareStackTrace() {
wrapperCache = wrapperFactory(Error.prepareStackTrace);
delete Error.prepareStackTrace;
Object.defineProperty(Error, "prepareStackTrace", {
get: function () {
return wrapperCache;
},
set: function (value) {
wrapperCache = wrapperFactory(value);
},
configurable: true,
enumerable: false
});
}
function restorePrepareStackTrace(){
delete Error.prepareStackTrace;
Error.prepareStackTrace = lastPrepareStackTrace;
privilegiedCallback = null;
referenceStack = null;
wrapperCache = null;
}
function setPrivilegiedCallback(value) {
privilegiedCallback = value
}
//---------------------------------------------------------------------
//
// Demo
//
// (A) Test that we support multiple consecutive. First Hijack 1 register itself, then hijack 2 register and call previous.
// (B) Test that we report the original object, even if modified by a callback.
// ------
// (C) Test that we support the restore pattern. (Hijack 2 remove itself from the chain without knowing there's a wrapper hook)
// ------
// (D) Test that we can remove the wrapper hook.
//Setup Hook
hijackPrepareStackTrace();
setPrivilegiedCallback(function(err,stack){
console.log("privilegied", stack);
return stack
});
// Hijack 1
Error.prepareStackTrace = function(err,stack){
console.log("hijack 1", stack);
return stack
};
// Hijack 2
beforeH2 = Error.prepareStackTrace;
Error.prepareStackTrace = function(err,stack){
stack[0] = {hello:"world"};
console.log("hijack 2", stack);
return beforeH2(err,stack);
};
// throw error to get stack.
dummy = {};
Error.captureStackTrace(dummy);
dummy.stack;
//---------------------------------------------
console.log("\n");
// Undo hicjack 2
Error.prepareStackTrace = beforeH2;
// throw error to get stack.
dummy = {};
Error.captureStackTrace(dummy);
dummy.stack;
//---------------------------------------------
console.log("\n");
// Release Wrapper Hook
restorePrepareStackTrace();
// throw error to get stack.
dummy = {};
Error.captureStackTrace(dummy);
dummy.stack;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment