Skip to content

Commit e7e1dfd

Browse files
author
Julien Gilli
committed
domains: fix no error handler & abort-on-uncaught
Make the process abort if an error is thrown within a domain with no error handler and `--abort-on-uncaught-exception` is used. If the domain within which the error is thrown has no error handler, but a domain further down the domains stack has one, the process will not abort. Fixes #3653.
1 parent 04b1302 commit e7e1dfd

File tree

5 files changed

+508
-15
lines changed

5 files changed

+508
-15
lines changed

lib/domain.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,20 @@ var endMethods = ['end', 'abort', 'destroy', 'destroySoon'];
3232
// a few side effects.
3333
events.usingDomains = true;
3434

35+
// it's possible to enter one domain while already inside
36+
// another one. the stack is each entered domain.
37+
var stack = [];
38+
exports._stack = stack;
39+
3540
// let the process know we're using domains
36-
process._usingDomains();
41+
process._usingDomains(stack);
3742

3843
exports.Domain = Domain;
3944

4045
exports.create = exports.createDomain = function(cb) {
4146
return new Domain(cb);
4247
};
4348

44-
// it's possible to enter one domain while already inside
45-
// another one. the stack is each entered domain.
46-
var stack = [];
47-
exports._stack = stack;
4849
// the active domain is always the one that we're currently in.
4950
exports.active = null;
5051

src/node.cc

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ Persistent<String> domain_symbol;
105105
// declared in node_internals.h
106106
Persistent<Object> process;
107107

108+
static Persistent<Array> domains_stack;
109+
108110
static Persistent<Function> process_tickFromSpinner;
109111
static Persistent<Function> process_tickCallback;
110112

@@ -126,6 +128,8 @@ static Persistent<String> exit_symbol;
126128
static Persistent<String> disposed_symbol;
127129

128130
static Persistent<String> emitting_toplevel_domain_error_symbol;
131+
static Persistent<String> _events_symbol;
132+
static Persistent<String> error_symbol;
129133

130134
static bool print_eval = false;
131135
static bool force_repl = false;
@@ -904,25 +908,82 @@ Handle<Value> FromConstructorTemplate(Persistent<FunctionTemplate> t,
904908
return scope.Close(t->GetFunction()->NewInstance(argc, argv));
905909
}
906910

907-
static bool IsDomainActive() {
908-
if (domain_symbol.IsEmpty())
909-
domain_symbol = NODE_PSYMBOL("domain");
911+
static bool domainHasErrorHandler(const Local<Object>& domain) {
912+
HandleScope scope;
913+
914+
if (_events_symbol.IsEmpty())
915+
_events_symbol = NODE_PSYMBOL("_events");
916+
917+
Local<Value> domain_event_listeners_v = domain->Get(_events_symbol);
918+
if (!domain_event_listeners_v->IsObject())
919+
return false;
920+
921+
Local<Object> domain_event_listeners_o =
922+
domain_event_listeners_v->ToObject();
923+
924+
if (domain_event_listeners_o->IsNull())
925+
return false;
926+
927+
if (error_symbol.IsEmpty())
928+
error_symbol = NODE_PSYMBOL("error");
929+
930+
Local<Value> domain_error_listeners_v =
931+
domain_event_listeners_o->Get(error_symbol);
932+
933+
if (domain_error_listeners_v->IsFunction() ||
934+
(domain_error_listeners_v->IsArray() &&
935+
domain_error_listeners_v.As<Array>()->Length() > 0))
936+
return true;
937+
938+
return false;
939+
}
940+
941+
static bool domainsStackHasErrorHandler() {
942+
HandleScope scope;
943+
944+
if (!using_domains)
945+
return false;
910946

911-
Local<Value> domain_v = process->Get(domain_symbol);
947+
Local<Array> domains_stack_array = Local<Array>::New(domains_stack);
948+
if (domains_stack_array->Length() == 0)
949+
return false;
912950

913-
return domain_v->IsObject();
951+
uint32_t domains_stack_length = domains_stack_array->Length();
952+
for (int i = domains_stack_length - 1; i >= 0; --i) {
953+
Local<Value> domain_v = domains_stack_array->Get(i);
954+
if (domain_v->IsNull())
955+
return false;
956+
957+
Local<Object> domain = domain_v->ToObject();
958+
if (domain->IsNull())
959+
return false;
960+
961+
if (domainHasErrorHandler(domain))
962+
return true;
963+
}
964+
965+
return false;
914966
}
915967

916968
bool ShouldAbortOnUncaughtException() {
917969
Local<Value> emitting_toplevel_domain_error_v =
918970
process->Get(emitting_toplevel_domain_error_symbol);
919-
return !IsDomainActive() || emitting_toplevel_domain_error_v->BooleanValue();
971+
972+
return emitting_toplevel_domain_error_v->BooleanValue() ||
973+
!domainsStackHasErrorHandler();
920974
}
921975

922976
Handle<Value> UsingDomains(const Arguments& args) {
923977
HandleScope scope;
978+
924979
if (using_domains)
925980
return scope.Close(Undefined());
981+
982+
if (!args[0]->IsArray()) {
983+
fprintf(stderr, "domains stack must be an array\n");
984+
abort();
985+
}
986+
926987
using_domains = true;
927988
Local<Value> tdc_v = process->Get(String::New("_tickDomainCallback"));
928989
Local<Value> ndt_v = process->Get(String::New("_nextDomainTick"));
@@ -934,6 +995,9 @@ Handle<Value> UsingDomains(const Arguments& args) {
934995
fprintf(stderr, "process._nextDomainTick assigned to non-function\n");
935996
abort();
936997
}
998+
999+
domains_stack = Persistent<Array>::New(args[0].As<Array>());
1000+
9371001
Local<Function> tdc = tdc_v.As<Function>();
9381002
Local<Function> ndt = ndt_v.As<Function>();
9391003
process->Set(String::New("_tickCallback"), tdc);
@@ -2449,7 +2513,10 @@ Handle<Object> SetupProcessObject(int argc, char *argv[]) {
24492513
process->Set(String::NewSymbol("_tickInfoBox"), info_box);
24502514

24512515
// pre-set _events object for faster emit checks
2452-
process->Set(String::NewSymbol("_events"), Object::New());
2516+
if (_events_symbol.IsEmpty())
2517+
_events_symbol = NODE_PSYMBOL("_events");
2518+
2519+
process->Set(_events_symbol, Object::New());
24532520

24542521
if (emitting_toplevel_domain_error_symbol.IsEmpty())
24552522
emitting_toplevel_domain_error_symbol =

0 commit comments

Comments
 (0)