diff --git a/src/node.cc b/src/node.cc index 8796047c6af..a69552431b5 100644 --- a/src/node.cc +++ b/src/node.cc @@ -57,6 +57,7 @@ static Persistent heap_used_symbol; static Persistent listeners_symbol; static Persistent uncaught_exception_symbol; static Persistent emit_symbol; +static Persistent exception_catcher_symbol; static int dash_dash_index = 0; static bool use_debug_agent = false; @@ -818,11 +819,40 @@ static void OnFatalError(const char* location, const char* message) { exit(1); } +Local GetProcessExceptionCatcher() { + return process->Get(exception_catcher_symbol); +} + +bool SetProcessExceptionCatcher(Handle value) { + return process->Set(exception_catcher_symbol, value); +} + +static int exception_catcher_counter = 0; static int uncaught_exception_counter = 0; void FatalException(TryCatch &try_catch) { HandleScope scope; + // First try process.exceptionCatcher -- + // if it's a function, and we're not already in it. + Local catcher_v = GetProcessExceptionCatcher(); + if (0 == exception_catcher_counter && catcher_v->IsFunction()) { + exception_catcher_counter++; + // Call process.exceptionCatcher(exception). If it throws, FatalException + // is reentered with nonzero exception_catcher_counter, and we drop + // through to the "uncaughtException" event below, or barf. + TryCatch inner_try_catch; + + Local catcher = Local::Cast(catcher_v); + Local argv[1] = { try_catch.Exception() }; + Local ret = catcher->Call(process, 1, argv); + if (inner_try_catch.HasCaught()) { + FatalException(inner_try_catch); + } + exception_catcher_counter--; + return; + } + // Check if uncaught_exception_counter indicates a recursion if (uncaught_exception_counter > 0) { ReportException(&try_catch); @@ -977,6 +1007,8 @@ static Local Load(int argc, char *argv[]) { process->Set(String::NewSymbol("EventEmitter"), EventEmitter::constructor_template->GetFunction()); + exception_catcher_symbol = NODE_PSYMBOL("exceptionCatcher"); + // Initialize the stats object Local stat_templ = FunctionTemplate::New(); stats_constructor_template = Persistent::New(stat_templ); diff --git a/src/node.h b/src/node.h index fd872a09607..5d921bd7c4c 100644 --- a/src/node.h +++ b/src/node.h @@ -42,6 +42,10 @@ enum encoding ParseEncoding(v8::Handle encoding_v, enum encoding _default = BINARY); void FatalException(v8::TryCatch &try_catch); +// Get and set process.exceptionCatcher +v8::Local GetProcessExceptionCatcher(); +bool SetProcessExceptionCatcher(v8::Handle value); + v8::Local Encode(const void *buf, size_t len, enum encoding encoding = BINARY); diff --git a/src/node.js b/src/node.js index 6cf17c14457..32cc9eb80fc 100644 --- a/src/node.js +++ b/src/node.js @@ -192,6 +192,18 @@ process.mixin = function() { return target; }; + +// This stub allows chaining to the previous process.exceptionCatcher +// without checking that it is a valid function. +// e.g.: +// var oldCatcher = process.exceptionCatcher; +// process.exceptionCatcher = function (e) { +// if (e instanceof MyError) { ... } +// else oldCatcher(e); +// } +process.exceptionCatcher = function (e) { throw e; } + + // Event var eventsModule = createInternalModule('events', function (exports) { @@ -207,6 +219,7 @@ var eventsModule = createInternalModule('events', function (exports) { // adding it to the listeners, first emit "newListeners". this.emit("newListener", type, listener); this._events[type].push(listener); + listener._exceptionCatcher = process.exceptionCatcher; } return this; }; diff --git a/src/node_events.cc b/src/node_events.cc index 27d07a46bed..437c3c8cfed 100644 --- a/src/node_events.cc +++ b/src/node_events.cc @@ -23,6 +23,7 @@ using namespace v8; Persistent EventEmitter::constructor_template; static Persistent events_symbol; +static Persistent exception_catcher_symbol; void EventEmitter::Initialize(Local ctemplate) { HandleScope scope; @@ -35,6 +36,7 @@ void EventEmitter::Initialize(Local ctemplate) { constructor_template->SetClassName(String::NewSymbol("EventEmitter")); events_symbol = NODE_PSYMBOL("_events"); + exception_catcher_symbol = NODE_PSYMBOL("_exceptionCatcher"); // All other prototype methods are defined in events.js } @@ -59,6 +61,7 @@ static bool ReallyEmit(Handle self, Local listener_v = listeners->Get(Integer::New(i)); if (!listener_v->IsFunction()) continue; Local listener = Local::Cast(listener_v); + SetProcessExceptionCatcher(listener->Get(exception_catcher_symbol)); TryCatch try_catch;