X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fnode.cc;h=434682fad6c9b15a2625c706d23772dbd6646c42;hb=c0d81f90996667a658aa4403123e02161262506a;hp=c88b0effbacf7b573bd3911e3cd603bc2cb985b6;hpb=aaf4f8d685fcb90a82d626e1218b67b3d5aa1c9b;p=platform%2Fupstream%2Fnodejs.git diff --git a/src/node.cc b/src/node.cc index c88b0ef..434682f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -35,20 +35,21 @@ #include "node_crypto.h" #endif -#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP +#if defined HAVE_DTRACE || defined HAVE_ETW #include "node_dtrace.h" #endif -#if HAVE_SYSTEMTAP -#include "node_provider.h" -#endif - #include "ares.h" +#include "async-wrap.h" +#include "async-wrap-inl.h" +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" #include "req_wrap.h" #include "string_bytes.h" #include "uv.h" #include "v8-debug.h" +#include "v8-profiler.h" #include "zlib.h" #include @@ -70,6 +71,7 @@ #define umask _umask typedef int mode_t; #else +#include // getrlimit, setrlimit #include // setuid, getuid #endif @@ -106,10 +108,7 @@ using v8::Message; using v8::Number; using v8::Object; using v8::ObjectTemplate; -using v8::Persistent; using v8::PropertyCallbackInfo; -using v8::ResourceConstraints; -using v8::SetResourceConstraints; using v8::String; using v8::ThrowException; using v8::TryCatch; @@ -118,43 +117,10 @@ using v8::V8; using v8::Value; using v8::kExternalUnsignedIntArray; +// FIXME(bnoordhuis) Make these per-context? QUEUE handle_wrap_queue = { &handle_wrap_queue, &handle_wrap_queue }; QUEUE req_wrap_queue = { &req_wrap_queue, &req_wrap_queue }; -// declared in req_wrap.h -Cached process_symbol; -Cached domain_symbol; - -// declared in node_internals.h -Persistent process_p; - -static Persistent process_tickCallback; -static Persistent binding_cache; -static Persistent module_load_list; -static Persistent p_domain_box; - -static Cached exports_symbol; - -static Cached errno_symbol; -static Cached syscall_symbol; -static Cached errpath_symbol; -static Cached code_symbol; - -static Cached rss_symbol; -static Cached heap_total_symbol; -static Cached heap_used_symbol; - -static Cached fatal_exception_symbol; - -static Cached enter_symbol; -static Cached exit_symbol; -static Cached disposed_symbol; - -// Essential for node_wrap.h -Persistent pipeConstructorTmpl; -Persistent tcpConstructorTmpl; -Persistent ttyConstructorTmpl; - static bool print_eval = false; static bool force_repl = false; static bool trace_deprecation = false; @@ -163,35 +129,18 @@ static const char* eval_string = NULL; static bool use_debug_agent = false; static bool debug_wait_connect = false; static int debug_port = 5858; -bool using_domains = false; +static bool v8_is_profiling = false; +static node_module* modpending; +static node_module* modlist_builtin; +static node_module* modlist_addon; // used by C++ modules as well bool no_deprecation = false; -static uv_check_t check_immediate_watcher; -static uv_idle_t idle_immediate_dummy; -static bool need_immediate_cb; -static Cached immediate_callback_sym; - -// for quick ref to tickCallback values -static struct { - uint32_t length; - uint32_t index; - uint32_t in_tick; - uint32_t last_threw; -} tick_infobox; - -// easily communicate domain depth -static struct { - uint32_t count; -} domain_flag; - // process-relative uptime base, initialized at start-up static double prog_start_time; - -static volatile bool debugger_running = false; +static bool debugger_running; static uv_async_t dispatch_debug_messages_async; -static uv_async_t emit_debug_enabled_async; // Declared in node_internals.h Isolate* node_isolate = NULL; @@ -205,7 +154,8 @@ class ArrayBufferAllocator : public ArrayBuffer::Allocator { static ArrayBufferAllocator the_singleton; virtual ~ArrayBufferAllocator() {} virtual void* Allocate(size_t length); - virtual void Free(void* data); + virtual void* AllocateUninitialized(size_t length); + virtual void Free(void* data, size_t length); private: ArrayBufferAllocator() {} ArrayBufferAllocator(const ArrayBufferAllocator&); @@ -216,35 +166,37 @@ ArrayBufferAllocator ArrayBufferAllocator::the_singleton; void* ArrayBufferAllocator::Allocate(size_t length) { - if (length > kMaxLength) return NULL; + if (length > kMaxLength) + return NULL; + char* data = new char[length]; + memset(data, 0, length); + return data; +} + + +void* ArrayBufferAllocator::AllocateUninitialized(size_t length) { + if (length > kMaxLength) + return NULL; return new char[length]; } -void ArrayBufferAllocator::Free(void* data) { +void ArrayBufferAllocator::Free(void* data, size_t length) { delete[] static_cast(data); } static void CheckImmediate(uv_check_t* handle, int status) { - assert(handle == &check_immediate_watcher); - assert(status == 0); - HandleScope scope(node_isolate); - - if (immediate_callback_sym.IsEmpty()) { - immediate_callback_sym = - FIXED_ONE_BYTE_STRING(node_isolate, "_immediateCallback"); - } - - MakeCallback(process_p, immediate_callback_sym, 0, NULL); + Environment* env = Environment::from_immediate_check_handle(handle); + Context::Scope context_scope(env->context()); + MakeCallback(env, env->process_object(), env->immediate_callback_string()); } -static void IdleImmediateDummy(uv_idle_t* handle, int status) { - // Do nothing. Only for maintaining event loop - assert(handle == &idle_immediate_dummy); - assert(status == 0); +static void IdleImmediateDummy(uv_idle_t*, int) { + // Do nothing. Only for maintaining event loop. + // TODO(bnoordhuis) Maybe make libuv accept NULL idle callbacks. } @@ -729,6 +681,8 @@ Local ErrnoException(int errorno, const char *syscall, const char *msg, const char *path) { + Environment* env = Environment::GetCurrent(node_isolate); + Local e; Local estring = OneByteString(node_isolate, errno_string(errorno)); if (msg == NULL || msg[0] == '\0') { @@ -740,13 +694,6 @@ Local ErrnoException(int errorno, String::Concat(estring, FIXED_ONE_BYTE_STRING(node_isolate, ", ")); Local cons2 = String::Concat(cons1, message); - if (syscall_symbol.IsEmpty()) { - syscall_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "syscall"); - errno_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "errno"); - errpath_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "path"); - code_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "code"); - } - if (path) { Local cons3 = String::Concat(cons2, FIXED_ONE_BYTE_STRING(node_isolate, " '")); @@ -760,11 +707,17 @@ Local ErrnoException(int errorno, } Local obj = e->ToObject(); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + obj->Set(env->code_string(), estring); + + if (path != NULL) { + obj->Set(env->path_string(), String::NewFromUtf8(node_isolate, path)); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - obj->Set(code_symbol, estring); - if (path) obj->Set(errpath_symbol, String::NewFromUtf8(node_isolate, path)); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); return e; } @@ -774,12 +727,7 @@ Local UVException(int errorno, const char *syscall, const char *msg, const char *path) { - if (syscall_symbol.IsEmpty()) { - syscall_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "syscall"); - errno_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "errno"); - errpath_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "path"); - code_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "code"); - } + Environment* env = Environment::GetCurrent(node_isolate); if (!msg || !msg[0]) msg = uv_strerror(errorno); @@ -820,12 +768,18 @@ Local UVException(int errorno, } Local obj = e->ToObject(); - // TODO(piscisaureus) errno should probably go - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - obj->Set(code_symbol, estring); - if (path) obj->Set(errpath_symbol, path_str); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + obj->Set(env->code_string(), estring); + + if (path != NULL) { + obj->Set(env->path_string(), path_str); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } + return e; } @@ -859,19 +813,14 @@ Local WinapiErrnoException(int errorno, const char* syscall, const char* msg, const char* path) { + Environment* env = Environment::GetCurrent(node_isolate); + Local e; if (!msg || !msg[0]) { msg = winapi_strerror(errorno); } Local message = OneByteString(node_isolate, msg); - if (syscall_symbol.IsEmpty()) { - syscall_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "syscall"); - errno_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "errno"); - errpath_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "path"); - code_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "code"); - } - if (path) { Local cons1 = String::Concat(message, FIXED_ONE_BYTE_STRING(node_isolate, " '")); @@ -885,107 +834,174 @@ Local WinapiErrnoException(int errorno, } Local obj = e->ToObject(); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + + if (path != NULL) { + obj->Set(env->path_string(), String::NewFromUtf8(node_isolate, path)); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - if (path) obj->Set(errpath_symbol, String::NewFromUtf8(node_isolate, path)); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); return e; } #endif +void SetupAsyncListener(const FunctionCallbackInfo& args) { + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + + assert(args[0]->IsObject()); + assert(args[1]->IsFunction()); + assert(args[2]->IsFunction()); + assert(args[3]->IsFunction()); + + env->set_async_listener_run_function(args[1].As()); + env->set_async_listener_load_function(args[2].As()); + env->set_async_listener_unload_function(args[3].As()); + + Local async_listener_flag_obj = args[0].As(); + Environment::AsyncListener* async_listener = env->async_listener(); + async_listener_flag_obj->SetIndexedPropertiesToExternalArrayData( + async_listener->fields(), + kExternalUnsignedIntArray, + async_listener->fields_count()); + + // Do a little housekeeping. + env->process_object()->Delete( + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupAsyncListener")); +} + + void SetupDomainUse(const FunctionCallbackInfo& args) { - if (using_domains) return; + Environment* env = Environment::GetCurrent(args.GetIsolate()); + + if (env->using_domains()) + return; + env->set_using_domains(true); + HandleScope scope(node_isolate); - using_domains = true; - Local process = PersistentToLocal(node_isolate, process_p); - Local tdc_v = - process->Get(FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback")); - if (!tdc_v->IsFunction()) { + Local process_object = env->process_object(); + + Local tick_callback_function_key = + FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback"); + Local tick_callback_function = + process_object->Get(tick_callback_function_key).As(); + + if (!tick_callback_function->IsFunction()) { fprintf(stderr, "process._tickDomainCallback assigned to non-function\n"); abort(); } - Local tdc = tdc_v.As(); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"), tdc); - process_tickCallback.Reset(node_isolate, tdc); - if (!args[0]->IsArray()) { - fprintf(stderr, "_setupDomainUse first argument must be an array\n"); - abort(); - } - p_domain_box.Reset(node_isolate, args[0].As()); - if (!args[1]->IsObject()) { - fprintf(stderr, "_setupDomainUse second argument must be an object\n"); - abort(); - } - Local flag = args[1].As(); - flag->SetIndexedPropertiesToExternalArrayData(&domain_flag, - kExternalUnsignedIntArray, - 1); -} + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"), + tick_callback_function); + env->set_tick_callback_function(tick_callback_function); -bool InDomain() { - return using_domains && domain_flag.count > 0; -} + assert(args[0]->IsArray()); + assert(args[1]->IsObject()); + env->set_domain_array(args[0].As()); -Handle GetDomain() { - // no domain can exist if no domain module has been loaded - if (!InDomain() || p_domain_box.IsEmpty()) - return Null(node_isolate); + Local domain_flag_obj = args[1].As(); + Environment::DomainFlag* domain_flag = env->domain_flag(); + domain_flag_obj->SetIndexedPropertiesToExternalArrayData( + domain_flag->fields(), + kExternalUnsignedIntArray, + domain_flag->fields_count()); - return PersistentToLocal(node_isolate, p_domain_box)->Get(0); + // Do a little housekeeping. + env->process_object()->Delete( + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")); } -Handle -MakeDomainCallback(const Handle object, - const Handle callback, - int argc, - Handle argv[]) { - // TODO(trevnorris) Hook for long stack traces to be made here. +void SetupNextTick(const FunctionCallbackInfo& args) { + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); - // lazy load domain specific symbols - if (enter_symbol.IsEmpty()) { - enter_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "enter"); - exit_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "exit"); - disposed_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "_disposed"); - } + assert(args[0]->IsObject()); + assert(args[1]->IsFunction()); - Local domain_v = object->Get(domain_symbol); - Local domain; - Local enter; - Local exit; + // Values use to cross communicate with processNextTick. + Local tick_info_obj = args[0].As(); + tick_info_obj->SetIndexedPropertiesToExternalArrayData( + env->tick_info()->fields(), + kExternalUnsignedIntArray, + env->tick_info()->fields_count()); + + env->set_tick_callback_function(args[1].As()); + + // Do a little housekeeping. + env->process_object()->Delete( + FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick")); +} + + +Handle MakeDomainCallback(Environment* env, + Handle recv, + const Handle callback, + int argc, + Handle argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); + + Local process = env->process_object(); + Local object, domain; + Local domain_v; TryCatch try_catch; try_catch.SetVerbose(true); - bool has_domain = domain_v->IsObject(); - if (has_domain) { - domain = domain_v->ToObject(); - assert(!domain.IsEmpty()); - if (domain->Get(disposed_symbol)->IsTrue()) { - // domain has been disposed of. - return Undefined(node_isolate); + bool has_async_queue = false; + + if (recv->IsObject()) { + object = recv.As(); + // TODO(trevnorris): This is sucky for performance. Fix it. + has_async_queue = object->Has(env->async_queue_string()); + if (has_async_queue) { + env->async_listener_load_function()->Call(process, 1, &recv); + + if (try_catch.HasCaught()) + return Undefined(node_isolate); } - enter = Local::Cast(domain->Get(enter_symbol)); - assert(!enter.IsEmpty()); - enter->Call(domain, 0, NULL); + } - if (try_catch.HasCaught()) { - return Undefined(node_isolate); + bool has_domain = false; + + if (!object.IsEmpty()) { + domain_v = object->Get(env->domain_string()); + has_domain = domain_v->IsObject(); + if (has_domain) { + domain = domain_v.As(); + + if (domain->Get(env->disposed_string())->IsTrue()) { + // domain has been disposed of. + return Undefined(node_isolate); + } + + Local enter = + domain->Get(env->enter_string()).As(); + assert(enter->IsFunction()); + enter->Call(domain, 0, NULL); + + if (try_catch.HasCaught()) { + return Undefined(node_isolate); + } } } - Local ret = callback->Call(object, argc, argv); + Local ret = callback->Call(recv, argc, argv); if (try_catch.HasCaught()) { return Undefined(node_isolate); } if (has_domain) { - exit = Local::Cast(domain->Get(exit_symbol)); - assert(!exit.IsEmpty()); + Local exit = + domain->Get(env->exit_string()).As(); + assert(exit->IsFunction()); exit->Call(domain, 0, NULL); if (try_catch.HasCaught()) { @@ -993,26 +1009,37 @@ MakeDomainCallback(const Handle object, } } - if (tick_infobox.last_threw == 1) { - tick_infobox.last_threw = 0; + if (has_async_queue) { + env->async_listener_unload_function()->Call(process, 1, &recv); + + if (try_catch.HasCaught()) + return Undefined(node_isolate); + } + + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->last_threw() == 1) { + tick_info->set_last_threw(0); return ret; } - if (tick_infobox.in_tick == 1) { + if (tick_info->in_tick()) { return ret; } - if (tick_infobox.length == 0) { - tick_infobox.index = 0; + if (tick_info->length() == 0) { + tick_info->set_index(0); return ret; } - // process nextTicks after call - Local process = PersistentToLocal(node_isolate, process_p); - Local fn = PersistentToLocal(node_isolate, process_tickCallback); - fn->Call(process, 0, NULL); + tick_info->set_in_tick(true); + + env->tick_callback_function()->Call(process, 0, NULL); + + tick_info->set_in_tick(false); if (try_catch.HasCaught()) { + tick_info->set_last_threw(true); return Undefined(node_isolate); } @@ -1020,51 +1047,64 @@ MakeDomainCallback(const Handle object, } -Handle -MakeCallback(const Handle object, - const Handle callback, - int argc, - Handle argv[]) { - // TODO(trevnorris) Hook for long stack traces to be made here. - Local process = PersistentToLocal(node_isolate, process_p); +Handle MakeCallback(Environment* env, + Handle recv, + const Handle callback, + int argc, + Handle argv[]) { + if (env->using_domains()) + return MakeDomainCallback(env, recv, callback, argc, argv); - if (using_domains) - return MakeDomainCallback(object, callback, argc, argv); + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); - // lazy load no domain next tick callbacks - if (process_tickCallback.IsEmpty()) { - Local cb_v = - process->Get(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback")); - if (!cb_v->IsFunction()) { - fprintf(stderr, "process._tickCallback assigned to non-function\n"); - abort(); - } - process_tickCallback.Reset(node_isolate, cb_v.As()); - } + Local process = env->process_object(); TryCatch try_catch; try_catch.SetVerbose(true); - Local ret = callback->Call(object, argc, argv); + // TODO(trevnorris): This is sucky for performance. Fix it. + bool has_async_queue = + recv->IsObject() && recv.As()->Has(env->async_queue_string()); + if (has_async_queue) { + env->async_listener_load_function()->Call(process, 1, &recv); + if (try_catch.HasCaught()) + return Undefined(node_isolate); + } + + Local ret = callback->Call(recv, argc, argv); if (try_catch.HasCaught()) { return Undefined(node_isolate); } - if (tick_infobox.in_tick == 1) { + if (has_async_queue) { + env->async_listener_unload_function()->Call(process, 1, &recv); + + if (try_catch.HasCaught()) + return Undefined(node_isolate); + } + + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->in_tick()) { return ret; } - if (tick_infobox.length == 0) { - tick_infobox.index = 0; + if (tick_info->length() == 0) { + tick_info->set_index(0); return ret; } + tick_info->set_in_tick(true); + // process nextTicks after call - Local fn = PersistentToLocal(node_isolate, process_tickCallback); - fn->Call(process, 0, NULL); + env->tick_callback_function()->Call(process, 0, NULL); + + tick_info->set_in_tick(false); if (try_catch.HasCaught()) { + tick_info->set_last_threw(true); return Undefined(node_isolate); } @@ -1073,56 +1113,94 @@ MakeCallback(const Handle object, // Internal only. -Handle -MakeCallback(const Handle object, - uint32_t index, - int argc, - Handle argv[]) { - HandleScope scope(node_isolate); +Handle MakeCallback(Environment* env, + Handle recv, + uint32_t index, + int argc, + Handle argv[]) { + Local callback = recv->Get(index).As(); + assert(callback->IsFunction()); - Local callback = object->Get(index).As(); + return MakeCallback(env, recv.As(), callback, argc, argv); +} + + +Handle MakeCallback(Environment* env, + Handle recv, + Handle symbol, + int argc, + Handle argv[]) { + Local callback = recv->Get(symbol).As(); assert(callback->IsFunction()); + return MakeCallback(env, recv.As(), callback, argc, argv); +} + - if (using_domains) - return scope.Close(MakeDomainCallback(object, callback, argc, argv)); - return scope.Close(MakeCallback(object, callback, argc, argv)); +Handle MakeCallback(Environment* env, + Handle recv, + const char* method, + int argc, + Handle argv[]) { + Local method_string = OneByteString(node_isolate, method); + return MakeCallback(env, recv, method_string, argc, argv); } -Handle -MakeCallback(const Handle object, - const Handle symbol, - int argc, - Handle argv[]) { - HandleScope scope(node_isolate); +Handle MakeCallback(Handle recv, + const char* method, + int argc, + Handle argv[]) { + HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify. + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + return handle_scope.Close(MakeCallback(env, recv, method, argc, argv)); +} - Local callback = object->Get(symbol).As(); - assert(callback->IsFunction()); - if (using_domains) - return scope.Close(MakeDomainCallback(object, callback, argc, argv)); - return scope.Close(MakeCallback(object, callback, argc, argv)); +Handle MakeCallback(Handle recv, + Handle symbol, + int argc, + Handle argv[]) { + HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify. + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + return handle_scope.Close(MakeCallback(env, recv, symbol, argc, argv)); } -Handle -MakeCallback(const Handle object, - const char* method, - int argc, - Handle argv[]) { - HandleScope scope(node_isolate); +Handle MakeCallback(Handle recv, + Handle callback, + int argc, + Handle argv[]) { + HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify. + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + return handle_scope.Close( + MakeCallback(env, recv.As(), callback, argc, argv)); +} - Local method_string = OneByteString(node_isolate, method); - Handle ret = MakeCallback(object, method_string, argc, argv); - return scope.Close(ret); +Handle MakeDomainCallback(Handle recv, + Handle callback, + int argc, + Handle argv[]) { + Local context = recv->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close( + MakeDomainCallback(env, recv, callback, argc, argv)); } enum encoding ParseEncoding(Handle encoding_v, enum encoding _default) { HandleScope scope(node_isolate); - if (!encoding_v->IsString()) return _default; + if (!encoding_v->IsString()) + return _default; String::Utf8Value encoding(encoding_v); @@ -1197,73 +1275,121 @@ ssize_t DecodeWrite(char *buf, return StringBytes::Write(buf, buflen, val, encoding, NULL); } -void DisplayExceptionLine(Handle message) { - // Prevent re-entry into this function. For example, if there is - // a throw from a program in vm.runInThisContext(code, filename, true), - // then we want to show the original failure, not the secondary one. - static bool displayed_error = false; - - if (displayed_error) return; - displayed_error = true; +void AppendExceptionLine(Environment* env, + Handle er, + Handle message) { + if (message.IsEmpty()) + return; - uv_tty_reset_mode(); + HandleScope scope(env->isolate()); + Local err_obj; + if (!er.IsEmpty() && er->IsObject()) { + err_obj = er.As(); - fprintf(stderr, "\n"); - - if (!message.IsEmpty()) { - // Print (filename):(line number): (message). - String::Utf8Value filename(message->GetScriptResourceName()); - const char* filename_string = *filename; - int linenum = message->GetLineNumber(); - fprintf(stderr, "%s:%i\n", filename_string, linenum); - // Print line of source code. - String::Utf8Value sourceline(message->GetSourceLine()); - const char* sourceline_string = *sourceline; - - // Because of how node modules work, all scripts are wrapped with a - // "function (module, exports, __filename, ...) {" - // to provide script local variables. - // - // When reporting errors on the first line of a script, this wrapper - // function is leaked to the user. There used to be a hack here to - // truncate off the first 62 characters, but it caused numerous other - // problems when vm.runIn*Context() methods were used for non-module - // code. - // - // If we ever decide to re-instate such a hack, the following steps - // must be taken: - // - // 1. Pass a flag around to say "this code was wrapped" - // 2. Update the stack frame output so that it is also correct. - // - // It would probably be simpler to add a line rather than add some - // number of characters to the first line, since V8 truncates the - // sourceline to 78 characters, and we end up not providing very much - // useful debugging info to the user if we remove 62 characters. - - int start = message->GetStartColumn(); - int end = message->GetEndColumn(); - - fprintf(stderr, "%s\n", sourceline_string); - // Print wavy underline (GetUnderline is deprecated). - for (int i = 0; i < start; i++) { - fputc((sourceline_string[i] == '\t') ? '\t' : ' ', stderr); - } - for (int i = start; i < end; i++) { - fputc('^', stderr); - } - fputc('\n', stderr); + // Do it only once per message + if (!err_obj->GetHiddenValue(env->processed_string()).IsEmpty()) + return; + err_obj->SetHiddenValue(env->processed_string(), True(env->isolate())); } + + static char arrow[1024]; + + // Print (filename):(line number): (message). + String::Utf8Value filename(message->GetScriptResourceName()); + const char* filename_string = *filename; + int linenum = message->GetLineNumber(); + // Print line of source code. + String::Utf8Value sourceline(message->GetSourceLine()); + const char* sourceline_string = *sourceline; + + // Because of how node modules work, all scripts are wrapped with a + // "function (module, exports, __filename, ...) {" + // to provide script local variables. + // + // When reporting errors on the first line of a script, this wrapper + // function is leaked to the user. There used to be a hack here to + // truncate off the first 62 characters, but it caused numerous other + // problems when vm.runIn*Context() methods were used for non-module + // code. + // + // If we ever decide to re-instate such a hack, the following steps + // must be taken: + // + // 1. Pass a flag around to say "this code was wrapped" + // 2. Update the stack frame output so that it is also correct. + // + // It would probably be simpler to add a line rather than add some + // number of characters to the first line, since V8 truncates the + // sourceline to 78 characters, and we end up not providing very much + // useful debugging info to the user if we remove 62 characters. + + int start = message->GetStartColumn(); + int end = message->GetEndColumn(); + + int off = snprintf(arrow, + sizeof(arrow), + "%s:%i\n%s\n", + filename_string, + linenum, + sourceline_string); + assert(off >= 0); + + // Print wavy underline (GetUnderline is deprecated). + for (int i = 0; i < start; i++) { + assert(static_cast(off) < sizeof(arrow)); + arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' '; + } + for (int i = start; i < end; i++) { + assert(static_cast(off) < sizeof(arrow)); + arrow[off++] = '^'; + } + assert(static_cast(off) < sizeof(arrow) - 1); + arrow[off++] = '\n'; + arrow[off] = '\0'; + + Local arrow_str = String::NewFromUtf8(env->isolate(), arrow); + Local msg; + Local stack; + + // Allocation failed, just print it out + if (arrow_str.IsEmpty() || err_obj.IsEmpty() || !err_obj->IsNativeError()) + goto print; + + msg = err_obj->Get(env->message_string()); + stack = err_obj->Get(env->stack_string()); + + if (msg.IsEmpty() || stack.IsEmpty()) + goto print; + + err_obj->Set(env->message_string(), + String::Concat(arrow_str, msg->ToString())); + err_obj->Set(env->stack_string(), + String::Concat(arrow_str, stack->ToString())); + return; + + print: + if (env->printed_error()) + return; + env->set_printed_error(true); + uv_tty_reset_mode(); + fprintf(stderr, "\n%s", arrow); } -static void ReportException(Handle er, Handle message) { - HandleScope scope(node_isolate); +static void ReportException(Environment* env, + Handle er, + Handle message) { + HandleScope scope(env->isolate()); + + AppendExceptionLine(env, er, message); + + Local trace_value; - DisplayExceptionLine(message); + if (er->IsUndefined() || er->IsNull()) + trace_value = Undefined(env->isolate()); + else + trace_value = er->ToObject()->Get(env->stack_string()); - Local trace_value( - er->ToObject()->Get(FIXED_ONE_BYTE_STRING(node_isolate, "stack"))); String::Utf8Value trace(trace_value); // range errors have a trace member set to undefined @@ -1278,8 +1404,8 @@ static void ReportException(Handle er, Handle message) { if (er->IsObject()) { Local err_obj = er.As(); - message = err_obj->Get(FIXED_ONE_BYTE_STRING(node_isolate, "message")); - name = err_obj->Get(FIXED_ONE_BYTE_STRING(node_isolate, "name")); + message = err_obj->Get(env->message_string()); + name = err_obj->Get(FIXED_ONE_BYTE_STRING(env->isolate(), "name")); } if (message.IsEmpty() || @@ -1300,14 +1426,16 @@ static void ReportException(Handle er, Handle message) { } -static void ReportException(const TryCatch& try_catch) { - ReportException(try_catch.Exception(), try_catch.Message()); +static void ReportException(Environment* env, const TryCatch& try_catch) { + ReportException(env, try_catch.Exception(), try_catch.Message()); } // Executes a str within the current v8 context. -Local ExecuteString(Handle source, Handle filename) { - HandleScope scope(node_isolate); +static Local ExecuteString(Environment* env, + Handle source, + Handle filename) { + HandleScope scope(env->isolate()); TryCatch try_catch; // try_catch must be nonverbose to disable FatalException() handler, @@ -1316,13 +1444,13 @@ Local ExecuteString(Handle source, Handle filename) { Local script = v8::Script::Compile(source, filename); if (script.IsEmpty()) { - ReportException(try_catch); + ReportException(env, try_catch); exit(3); } Local result = script->Run(); if (result.IsEmpty()) { - ReportException(try_catch); + ReportException(env, try_catch); exit(4); } @@ -1338,8 +1466,9 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { int i = 0; QUEUE_FOREACH(q, &req_wrap_queue) { - ReqWrap* w = container_of(q, ReqWrap, req_wrap_queue_); - if (w->persistent().IsEmpty()) continue; + ReqWrap* w = CONTAINER_OF(q, ReqWrap, req_wrap_queue_); + if (w->persistent().IsEmpty()) + continue; ary->Set(i++, w->object()); } @@ -1359,11 +1488,13 @@ void GetActiveHandles(const FunctionCallbackInfo& args) { Local owner_sym = FIXED_ONE_BYTE_STRING(node_isolate, "owner"); QUEUE_FOREACH(q, &handle_wrap_queue) { - HandleWrap* w = container_of(q, HandleWrap, handle_wrap_queue_); - if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref)) continue; + HandleWrap* w = CONTAINER_OF(q, HandleWrap, handle_wrap_queue_); + if (w->persistent().IsEmpty() || (w->flags_ & HandleWrap::kUnref)) + continue; Local object = w->object(); Local owner = object->Get(owner_sym); - if (owner->IsUndefined()) owner = object; + if (owner->IsUndefined()) + owner = object; ary->Set(i++, owner); } @@ -1622,7 +1753,8 @@ static void GetGroups(const FunctionCallbackInfo& args) { for (int i = 0; i < ngroups; i++) { groups_list->Set(i, Integer::New(groups[i], node_isolate)); - if (groups[i] == egid) seen_egid = true; + if (groups[i] == egid) + seen_egid = true; } delete[] groups; @@ -1697,7 +1829,8 @@ static void InitGroups(const FunctionCallbackInfo& args) { extra_group = gid_by_name(args[1]); if (extra_group == gid_not_found) { - if (must_free) free(user); + if (must_free) + free(user); return ThrowError("initgroups extra group not found"); } @@ -1724,40 +1857,35 @@ void Exit(const FunctionCallbackInfo& args) { static void Uptime(const FunctionCallbackInfo& args) { HandleScope scope(node_isolate); double uptime; - if (uv_uptime(&uptime)) return; + if (uv_uptime(&uptime)) + return; args.GetReturnValue().Set(uptime - prog_start_time); } void MemoryUsage(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); size_t rss; - int err = uv_resident_set_memory(&rss); if (err) { return ThrowUVException(err, "uv_resident_set_memory"); } - Local info = Object::New(); - - if (rss_symbol.IsEmpty()) { - rss_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "rss"); - heap_total_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "heapTotal"); - heap_used_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "heapUsed"); - } - - info->Set(rss_symbol, Number::New(rss)); - // V8 memory usage HeapStatistics v8_heap_stats; node_isolate->GetHeapStatistics(&v8_heap_stats); - info->Set(heap_total_symbol, - Integer::NewFromUnsigned(v8_heap_stats.total_heap_size(), - node_isolate)); - info->Set(heap_used_symbol, - Integer::NewFromUnsigned(v8_heap_stats.used_heap_size(), - node_isolate)); + + Local heap_total = + Integer::NewFromUnsigned(v8_heap_stats.total_heap_size(), node_isolate); + Local heap_used = + Integer::NewFromUnsigned(v8_heap_stats.used_heap_size(), node_isolate); + + Local info = Object::New(); + info->Set(env->rss_string(), Number::New(node_isolate, rss)); + info->Set(env->heap_total_string(), heap_total); + info->Set(env->heap_used_string(), heap_used); args.GetReturnValue().Set(info); } @@ -1806,31 +1934,57 @@ void Hrtime(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(tuple); } +extern "C" void node_module_register(void* m) { + struct node_module* mp = reinterpret_cast(m); + + if (mp->nm_flags & NM_F_BUILTIN) { + mp->nm_link = modlist_builtin; + modlist_builtin = mp; + } else { + assert(modpending == NULL); + modpending = mp; + } +} + +struct node_module* get_builtin_module(const char* name) { + struct node_module* mp; + + for (mp = modlist_builtin; mp != NULL; mp = mp->nm_link) { + if (strcmp(mp->nm_modname, name) == 0) + break; + } + + assert(mp == NULL || (mp->nm_flags & NM_F_BUILTIN) != 0); + return (mp); +} typedef void (UV_DYNAMIC* extInit)(Handle exports); // DLOpen is process.dlopen(module, filename). // Used to load 'module.node' dynamically shared objects. +// +// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict +// when two contexts try to load the same shared object. Maybe have a shadow +// cache that's a plain C list or hash table that's shared across contexts? void DLOpen(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); - char symbol[1024], *base, *pos; + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + struct node_module* mp; uv_lib_t lib; - int r; if (args.Length() < 2) { - return ThrowError("process.dlopen takes exactly 2 arguments."); + ThrowError("process.dlopen takes exactly 2 arguments."); + return; } Local module = args[0]->ToObject(); // Cast String::Utf8Value filename(args[1]); // Cast - if (exports_symbol.IsEmpty()) { - exports_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "exports"); - } - Local exports = module->Get(exports_symbol)->ToObject(); + Local exports_string = env->exports_string(); + Local exports = module->Get(exports_string)->ToObject(); if (uv_dlopen(*filename, &lib)) { - Local errmsg = OneByteString(node_isolate, uv_dlerror(&lib)); + Local errmsg = OneByteString(env->isolate(), uv_dlerror(&lib)); #ifdef _WIN32 // Windows needs to add the filename into the error message errmsg = String::Concat(errmsg, args[1]->ToString()); @@ -1839,62 +1993,44 @@ void DLOpen(const FunctionCallbackInfo& args) { return; } - String::Utf8Value path(args[1]); - base = *path; - - /* Find the shared library filename within the full path. */ -#ifdef __POSIX__ - pos = strrchr(base, '/'); - if (pos != NULL) { - base = pos + 1; - } -#else // Windows - for (;;) { - pos = strpbrk(base, "\\/:"); - if (pos == NULL) { - break; - } - base = pos + 1; - } -#endif // __POSIX__ - - /* Strip the .node extension. */ - pos = strrchr(base, '.'); - if (pos != NULL) { - *pos = '\0'; - } - - /* Add the `_module` suffix to the extension name. */ - r = snprintf(symbol, sizeof symbol, "%s_module", base); - if (r <= 0 || static_cast(r) >= sizeof symbol) { - return ThrowError("Out of memory."); - } - - /* Replace dashes with underscores. When loading foo-bar.node, - * look for foo_bar_module, not foo-bar_module. + /* + * Objects containing v14 or later modules will have registered themselves + * on the pending list. Activate all of them now. At present, only one + * module per object is supported. */ - for (pos = symbol; *pos != '\0'; ++pos) { - if (*pos == '-') *pos = '_'; - } + mp = modpending; + modpending = NULL; - node_module_struct *mod; - if (uv_dlsym(&lib, symbol, reinterpret_cast(&mod))) { - char errmsg[1024]; - snprintf(errmsg, sizeof(errmsg), "Symbol %s not found.", symbol); - return ThrowError(errmsg); + if (mp == NULL) { + ThrowError("Module did not self-register."); + return; } - - if (mod->version != NODE_MODULE_VERSION) { + if (mp->nm_version != NODE_MODULE_VERSION) { char errmsg[1024]; snprintf(errmsg, sizeof(errmsg), "Module version mismatch. Expected %d, got %d.", - NODE_MODULE_VERSION, mod->version); - return ThrowError(errmsg); + NODE_MODULE_VERSION, mp->nm_version); + ThrowError(errmsg); + return; + } + if (mp->nm_flags & NM_F_BUILTIN) { + ThrowError("Built-in module self-registered."); + return; } - // Execute the C++ module - mod->register_func(exports, module); + mp->nm_dso_handle = lib.handle; + mp->nm_link = modlist_addon; + modlist_addon = mp; + + if (mp->nm_context_register_func != NULL) { + mp->nm_context_register_func(exports, module, env->context(), mp->nm_priv); + } else if (mp->nm_register_func != NULL) { + mp->nm_register_func(exports, module, mp->nm_priv); + } else { + ThrowError("Module has no declared entry point."); + return; + } // Tell coverity that 'handle' should not be freed when we return. // coverity[leaked_storage] @@ -1908,10 +2044,7 @@ static void OnFatalError(const char* location, const char* message) { fprintf(stderr, "FATAL ERROR: %s\n", message); } fflush(stderr); -#if defined(DEBUG) abort(); -#endif - exit(5); } @@ -1925,40 +2058,37 @@ NO_RETURN void FatalError(const char* location, const char* message) { void FatalException(Handle error, Handle message) { HandleScope scope(node_isolate); - if (fatal_exception_symbol.IsEmpty()) { - fatal_exception_symbol = - FIXED_ONE_BYTE_STRING(node_isolate, "_fatalException"); - } - - Local process = PersistentToLocal(node_isolate, process_p); - Local fatal_v = process->Get(fatal_exception_symbol); + Environment* env = Environment::GetCurrent(node_isolate); + Local process_object = env->process_object(); + Local fatal_exception_string = env->fatal_exception_string(); + Local fatal_exception_function = + process_object->Get(fatal_exception_string).As(); - if (!fatal_v->IsFunction()) { + if (!fatal_exception_function->IsFunction()) { // failed before the process._fatalException function was added! // this is probably pretty bad. Nothing to do but report and exit. - ReportException(error, message); + ReportException(env, error, message); exit(6); } - Local fatal_f = Local::Cast(fatal_v); - TryCatch fatal_try_catch; // Do not call FatalException when _fatalException handler throws fatal_try_catch.SetVerbose(false); // this will return true if the JS layer handled it, false otherwise - Local caught = fatal_f->Call(process, 1, &error); + Local caught = + fatal_exception_function->Call(process_object, 1, &error); if (fatal_try_catch.HasCaught()) { // the fatal exception function threw, so we must exit - ReportException(fatal_try_catch); + ReportException(env, fatal_try_catch); exit(7); } if (false == caught->BooleanValue()) { - ReportException(error, message); - exit(8); + ReportException(env, error, message); + exit(1); } } @@ -1979,13 +2109,13 @@ void OnMessage(Handle message, Handle error) { static void Binding(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); Local module = args[0]->ToString(); String::Utf8Value module_v(module); - node_module_struct* modp; - Local cache = PersistentToLocal(node_isolate, binding_cache); + Local cache = env->binding_cache_object(); Local exports; if (cache->Has(module)) { @@ -1998,15 +2128,19 @@ static void Binding(const FunctionCallbackInfo& args) { char buf[1024]; snprintf(buf, sizeof(buf), "Binding %s", *module_v); - Local modules = PersistentToLocal(node_isolate, module_load_list); + Local modules = env->module_load_list_array(); uint32_t l = modules->Length(); modules->Set(l, OneByteString(node_isolate, buf)); - if ((modp = get_builtin_module(*module_v)) != NULL) { + node_module* mod = get_builtin_module(*module_v); + if (mod != NULL) { exports = Object::New(); - // Internal bindings don't have a "module" object, - // only exports. - modp->register_func(exports, Undefined(node_isolate)); + // Internal bindings don't have a "module" object, only exports. + assert(mod->nm_register_func == NULL); + assert(mod->nm_context_register_func != NULL); + Local unused = Undefined(env->isolate()); + mod->nm_context_register_func(exports, unused, + env->context(), mod->nm_priv); cache->Set(module, exports); } else if (!strcmp(*module_v, "constants")) { exports = Object::New(); @@ -2102,7 +2236,8 @@ static void EnvQuery(Local property, int32_t rc = -1; // Not found unless proven otherwise. #ifdef __POSIX__ String::Utf8Value key(property); - if (getenv(*key)) rc = 0; + if (getenv(*key)) + rc = 0; #else // _WIN32 String::Value key(property); WCHAR* key_ptr = reinterpret_cast(*key); @@ -2117,7 +2252,8 @@ static void EnvQuery(Local property, } } #endif - if (rc != -1) info.GetReturnValue().Set(rc); + if (rc != -1) + info.GetReturnValue().Set(rc); } @@ -2128,7 +2264,8 @@ static void EnvDeleter(Local property, #ifdef __POSIX__ String::Utf8Value key(property); rc = getenv(*key) != NULL; - if (rc) unsetenv(*key); + if (rc) + unsetenv(*key); #else String::Value key(property); WCHAR* key_ptr = reinterpret_cast(*key); @@ -2147,7 +2284,8 @@ static void EnvEnumerator(const PropertyCallbackInfo& info) { HandleScope scope(node_isolate); #ifdef __POSIX__ int size = 0; - while (environ[size]) size++; + while (environ[size]) + size++; Local env = Array::New(size); @@ -2163,7 +2301,8 @@ static void EnvEnumerator(const PropertyCallbackInfo& info) { } #else // _WIN32 WCHAR* environment = GetEnvironmentStringsW(); - if (environment == NULL) return; // This should not happen. + if (environment == NULL) + return; // This should not happen. Local env = Array::New(); WCHAR* p = environment; int i = 0; @@ -2254,55 +2393,94 @@ static void DebugEnd(const FunctionCallbackInfo& args); void NeedImmediateCallbackGetter(Local property, const PropertyCallbackInfo& info) { - info.GetReturnValue().Set(need_immediate_cb); + HandleScope handle_scope(info.GetIsolate()); + Environment* env = Environment::GetCurrent(info.GetIsolate()); + const uv_check_t* immediate_check_handle = env->immediate_check_handle(); + bool active = uv_is_active( + reinterpret_cast(immediate_check_handle)); + info.GetReturnValue().Set(active); } -static void NeedImmediateCallbackSetter(Local property, - Local value, - const PropertyCallbackInfo&) { - HandleScope scope(node_isolate); +static void NeedImmediateCallbackSetter( + Local property, + Local value, + const PropertyCallbackInfo& info) { + HandleScope handle_scope(info.GetIsolate()); + Environment* env = Environment::GetCurrent(info.GetIsolate()); - bool bool_value = value->BooleanValue(); + uv_check_t* immediate_check_handle = env->immediate_check_handle(); + bool active = uv_is_active( + reinterpret_cast(immediate_check_handle)); - if (need_immediate_cb == bool_value) return; + if (active == value->BooleanValue()) + return; - need_immediate_cb = bool_value; + uv_idle_t* immediate_idle_handle = env->immediate_idle_handle(); - if (need_immediate_cb) { - uv_check_start(&check_immediate_watcher, node::CheckImmediate); - // idle handle is needed only to maintain event loop - uv_idle_start(&idle_immediate_dummy, node::IdleImmediateDummy); + if (active) { + uv_check_stop(immediate_check_handle); + uv_idle_stop(immediate_idle_handle); } else { - uv_check_stop(&check_immediate_watcher); - uv_idle_stop(&idle_immediate_dummy); + uv_check_start(immediate_check_handle, CheckImmediate); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_start(immediate_idle_handle, IdleImmediateDummy); } } +void SetIdle(uv_prepare_t* handle, int) { + Environment* env = Environment::from_idle_prepare_handle(handle); + env->isolate()->GetCpuProfiler()->SetIdle(true); +} + + +void ClearIdle(uv_check_t* handle, int) { + Environment* env = Environment::from_idle_check_handle(handle); + env->isolate()->GetCpuProfiler()->SetIdle(false); +} + + +void StartProfilerIdleNotifier(Environment* env) { + uv_prepare_start(env->idle_prepare_handle(), SetIdle); + uv_check_start(env->idle_check_handle(), ClearIdle); +} + + +void StopProfilerIdleNotifier(Environment* env) { + uv_prepare_stop(env->idle_prepare_handle()); + uv_check_stop(env->idle_check_handle()); +} + + +void StartProfilerIdleNotifier(const FunctionCallbackInfo& args) { + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + StartProfilerIdleNotifier(env); +} + + +void StopProfilerIdleNotifier(const FunctionCallbackInfo& args) { + HandleScope handle_scope(args.GetIsolate()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + StopProfilerIdleNotifier(env); +} + + #define READONLY_PROPERTY(obj, str, var) \ do { \ obj->Set(OneByteString(node_isolate, str), var, v8::ReadOnly); \ } while (0) -Handle SetupProcessObject(int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv) { +void SetupProcessObject(Environment* env, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv) { HandleScope scope(node_isolate); - int i, j; - - Local process_template = FunctionTemplate::New(); - process_template->SetClassName( - FIXED_ONE_BYTE_STRING(node_isolate, "process")); - - Local process = process_template->GetFunction()->NewInstance(); - assert(process.IsEmpty() == false); - assert(process->IsObject() == true); - - process_p.Reset(node_isolate, process); + Local process = env->process_object(); process->SetAccessor(FIXED_ONE_BYTE_STRING(node_isolate, "title"), ProcessTitleGetter, @@ -2314,9 +2492,9 @@ Handle SetupProcessObject(int argc, FIXED_ONE_BYTE_STRING(node_isolate, NODE_VERSION)); // process.moduleLoadList - Local modules = Array::New(); - module_load_list.Reset(node_isolate, modules); - READONLY_PROPERTY(process, "moduleLoadList", modules); + READONLY_PROPERTY(process, + "moduleLoadList", + env->module_load_list_array()); // process.versions Local versions = Object::New(); @@ -2350,25 +2528,27 @@ Handle SetupProcessObject(int argc, #if HAVE_OPENSSL // Stupid code to slice out the version string. - int c, l = strlen(OPENSSL_VERSION_TEXT); - for (i = j = 0; i < l; i++) { - c = OPENSSL_VERSION_TEXT[i]; - if ('0' <= c && c <= '9') { - for (j = i + 1; j < l; j++) { - c = OPENSSL_VERSION_TEXT[j]; - if (c == ' ') break; + { // NOLINT(whitespace/braces) + size_t i, j, k; + int c; + for (i = j = 0, k = sizeof(OPENSSL_VERSION_TEXT) - 1; i < k; ++i) { + c = OPENSSL_VERSION_TEXT[i]; + if ('0' <= c && c <= '9') { + for (j = i + 1; j < k; ++j) { + c = OPENSSL_VERSION_TEXT[j]; + if (c == ' ') + break; + } + break; } - break; } + READONLY_PROPERTY( + versions, + "openssl", + OneByteString(node_isolate, &OPENSSL_VERSION_TEXT[i], j - i)); } - READONLY_PROPERTY( - versions, - "openssl", - OneByteString(node_isolate, &OPENSSL_VERSION_TEXT[i], j - i)); #endif - - // process.arch READONLY_PROPERTY(process, "arch", OneByteString(node_isolate, ARCH)); @@ -2392,15 +2572,15 @@ Handle SetupProcessObject(int argc, process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "execArgv"), exec_arguments); // create process.env - Local envTemplate = ObjectTemplate::New(); - envTemplate->SetNamedPropertyHandler(EnvGetter, - EnvSetter, - EnvQuery, - EnvDeleter, - EnvEnumerator, - Object::New()); - Local env = envTemplate->NewInstance(); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "env"), env); + Local process_env_template = ObjectTemplate::New(); + process_env_template->SetNamedPropertyHandler(EnvGetter, + EnvSetter, + EnvQuery, + EnvDeleter, + EnvEnumerator, + Object::New()); + Local process_env = process_env_template->NewInstance(); + process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "env"), process_env); READONLY_PROPERTY(process, "pid", Integer::New(getpid(), node_isolate)); READONLY_PROPERTY(process, "features", GetFeatures()); @@ -2461,6 +2641,12 @@ Handle SetupProcessObject(int argc, DebugPortSetter); // define various internal methods + NODE_SET_METHOD(process, + "_startProfilerIdleNotifier", + StartProfilerIdleNotifier); + NODE_SET_METHOD(process, + "_stopProfilerIdleNotifier", + StopProfilerIdleNotifier); NODE_SET_METHOD(process, "_getActiveRequests", GetActiveRequests); NODE_SET_METHOD(process, "_getActiveHandles", GetActiveHandles); NODE_SET_METHOD(process, "reallyExit", Exit); @@ -2497,19 +2683,20 @@ Handle SetupProcessObject(int argc, NODE_SET_METHOD(process, "binding", Binding); + NODE_SET_METHOD(process, "_setupAsyncListener", SetupAsyncListener); + NODE_SET_METHOD(process, "_setupNextTick", SetupNextTick); NODE_SET_METHOD(process, "_setupDomainUse", SetupDomainUse); // values use to cross communicate with processNextTick - Local info_box = Object::New(); - info_box->SetIndexedPropertiesToExternalArrayData(&tick_infobox, - kExternalUnsignedIntArray, - 4); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickInfoBox"), info_box); + Local tick_info_obj = Object::New(); + tick_info_obj->SetIndexedPropertiesToExternalArrayData( + env->tick_info()->fields(), + kExternalUnsignedIntArray, + env->tick_info()->fields_count()); + process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickInfo"), tick_info_obj); // pre-set _events object for faster emit checks process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_events"), Object::New()); - - return scope.Close(process); } @@ -2543,12 +2730,9 @@ static void RawDebug(const FunctionCallbackInfo& args) { } -void Load(Handle process_l) { +void Load(Environment* env) { HandleScope handle_scope(node_isolate); - process_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "process"); - domain_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "domain"); - // Compile, execute the src/node.js file. (Which was included as static C // string in node_natives.h. 'natve_node' is the string containing that // source code.) @@ -2563,10 +2747,10 @@ void Load(Handle process_l) { // are not safe to ignore. try_catch.SetVerbose(false); - Local script_name = FIXED_ONE_BYTE_STRING(node_isolate, "node.js"); - Local f_value = ExecuteString(MainSource(), script_name); + Local script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "node.js"); + Local f_value = ExecuteString(env, MainSource(), script_name); if (try_catch.HasCaught()) { - ReportException(try_catch); + ReportException(env, try_catch); exit(10); } assert(f_value->IsFunction()); @@ -2581,9 +2765,9 @@ void Load(Handle process_l) { // Node's I/O bindings may want to replace 'f' with their own function. // Add a reference to the global object - Local global = v8::Context::GetCurrent()->Global(); + Local global = env->context()->Global(); -#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP +#if defined HAVE_DTRACE || defined HAVE_ETW InitDTrace(global); #endif @@ -2599,44 +2783,45 @@ void Load(Handle process_l) { // thrown during process startup. try_catch.SetVerbose(true); - NODE_SET_METHOD(process_l, "_rawDebug", RawDebug); + NODE_SET_METHOD(env->process_object(), "_rawDebug", RawDebug); - Local arg = process_l; + Local arg = env->process_object(); f->Call(global, 1, &arg); } static void PrintHelp(); -static void ParseDebugOpt(const char* arg) { - const char *p = 0; +static bool ParseDebugOpt(const char* arg) { + const char* port = NULL; - if (strstr(arg, "--debug-port=") == arg) { - p = 1 + strchr(arg, '='); - debug_port = atoi(p); - } else { + if (!strcmp(arg, "--debug")) { use_debug_agent = true; - if (!strcmp(arg, "--debug-brk")) { - debug_wait_connect = true; - return; - } else if (!strcmp(arg, "--debug")) { - return; - } else if (strstr(arg, "--debug-brk=") == arg) { - debug_wait_connect = true; - p = 1 + strchr(arg, '='); - debug_port = atoi(p); - } else if (strstr(arg, "--debug=") == arg) { - p = 1 + strchr(arg, '='); - debug_port = atoi(p); - } + } else if (!strncmp(arg, "--debug=", sizeof("--debug=") - 1)) { + use_debug_agent = true; + port = arg + sizeof("--debug=") - 1; + } else if (!strcmp(arg, "--debug-brk")) { + use_debug_agent = true; + debug_wait_connect = true; + } else if (!strncmp(arg, "--debug-brk=", sizeof("--debug-brk=") - 1)) { + use_debug_agent = true; + debug_wait_connect = true; + port = arg + sizeof("--debug-brk=") - 1; + } else if (!strncmp(arg, "--debug-port=", sizeof("--debug-port=") - 1)) { + port = arg + sizeof("--debug-port=") - 1; + } else { + return false; } - if (p && debug_port > 1024 && debug_port < 65536) - return; - fprintf(stderr, "Bad debug option.\n"); - if (p) fprintf(stderr, "Debug port must be in range 1025 to 65535.\n"); + if (port != NULL) { + debug_port = atoi(port); + if (debug_port < 1024 || debug_port > 65535) { + fprintf(stderr, "Debug port must be in range 1024 to 65535.\n"); + PrintHelp(); + exit(12); + } + } - PrintHelp(); - exit(12); + return true; } static void PrintHelp() { @@ -2709,8 +2894,8 @@ static void ParseArgs(int* argc, const char* const arg = argv[index]; unsigned int args_consumed = 1; - if (strstr(arg, "--debug") == arg) { - ParseDebugOpt(arg); + if (ParseDebugOpt(arg)) { + // Done, consumed by ParseDebugOpt(). } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) { printf("%s\n", NODE_VERSION); exit(0); @@ -2731,9 +2916,11 @@ static void ParseArgs(int* argc, eval_string = argv[index + 1]; if (eval_string == NULL) { fprintf(stderr, "%s: %s requires an argument\n", argv[0], arg); - exit(1); + exit(9); } - } else if (argv[index + 1] != NULL && argv[index + 1][0] != '-') { + } else if ((index + 1 < nargs) && + argv[index + 1] != NULL && + argv[index + 1][0] != '-') { args_consumed += 1; eval_string = argv[index + 1]; if (strncmp(eval_string, "\\-", 2) == 0) { @@ -2783,84 +2970,84 @@ static void ParseArgs(int* argc, } -// Called from the main thread. -static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle, int status) { - v8::Debug::ProcessDebugMessages(); -} - - // Called from V8 Debug Agent TCP thread. static void DispatchMessagesDebugAgentCallback() { uv_async_send(&dispatch_debug_messages_async); } -// Called from the main thread -static void EmitDebugEnabledAsyncCallback(uv_async_t* handle, int status) { - HandleScope handle_scope(node_isolate); - Local obj = Object::New(); - obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "cmd"), - FIXED_ONE_BYTE_STRING(node_isolate, "NODE_DEBUG_ENABLED")); - Local args[] = { - FIXED_ONE_BYTE_STRING(node_isolate, "internalMessage"), - obj +// Called from the main thread. +static void EnableDebug(bool wait_connect) { + assert(debugger_running == false); + Isolate* isolate = node_isolate; // TODO(bnoordhuis) Multi-isolate support. + Isolate::Scope isolate_scope(isolate); + HandleScope handle_scope(isolate); + v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback, + false); + debugger_running = v8::Debug::EnableAgent("node " NODE_VERSION, + debug_port, + wait_connect); + if (debugger_running == false) { + fprintf(stderr, "Starting debugger on port %d failed\n", debug_port); + fflush(stderr); + return; + } + fprintf(stderr, "Debugger listening on port %d\n", debug_port); + fflush(stderr); + + Environment* env = Environment::GetCurrentChecked(isolate); + if (env == NULL) + return; // Still starting up. + + Context::Scope context_scope(env->context()); + Local message = Object::New(); + message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"), + FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED")); + Local argv[] = { + FIXED_ONE_BYTE_STRING(env->isolate(), "internalMessage"), + message }; - MakeCallback(process_p, "emit", ARRAY_SIZE(args), args); + MakeCallback(env, env->process_object(), "emit", ARRAY_SIZE(argv), argv); } -// Called from the signal watcher callback -static void EmitDebugEnabled() { - uv_async_send(&emit_debug_enabled_async); +// Called from the main thread. +static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle, int status) { + if (debugger_running == false) { + fprintf(stderr, "Starting debugger agent.\n"); + EnableDebug(false); + } + Isolate::Scope isolate_scope(node_isolate); + v8::Debug::ProcessDebugMessages(); } -static void EnableDebug(bool wait_connect) { - // If we're called from another thread, make sure to enter the right - // v8 isolate. - node_isolate->Enter(); - - v8::Debug::SetDebugMessageDispatchHandler(DispatchMessagesDebugAgentCallback, - false); - - // Start the debug thread and it's associated TCP server on port 5858. - bool r = v8::Debug::EnableAgent("node " NODE_VERSION, - debug_port, - wait_connect); - - // Crappy check that everything went well. FIXME - assert(r); +#ifdef __POSIX__ +static volatile sig_atomic_t caught_early_debug_signal; - // Print out some information. - fprintf(stderr, "debugger listening on port %d\n", debug_port); - fflush(stderr); - debugger_running = true; +static void EarlyDebugSignalHandler(int signo) { + caught_early_debug_signal = 1; +} - // Do not emit NODE_DEBUG_ENABLED when debugger is enabled before starting - // the main process (i.e. when called via `node --debug`) - if (!process_p.IsEmpty()) - EmitDebugEnabled(); - node_isolate->Exit(); +static void InstallEarlyDebugSignalHandler() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = EarlyDebugSignalHandler; + sigaction(SIGUSR1, &sa, NULL); } -#ifdef __POSIX__ -static void EnableDebugSignalHandler(uv_signal_t* handle, int) { - // Break once process will return execution to v8 - v8::Debug::DebugBreak(node_isolate); - - if (!debugger_running) { - fprintf(stderr, "Hit SIGUSR1 - starting debugger agent.\n"); - EnableDebug(false); - } +static void EnableDebugSignalHandler(int signo) { + // Call only async signal-safe functions here! + v8::Debug::DebugBreak(*static_cast(&node_isolate)); + uv_async_send(&dispatch_debug_messages_async); } static void RegisterSignalHandler(int signal, void (*handler)(int signal)) { struct sigaction sa; - memset(&sa, 0, sizeof(sa)); sa.sa_handler = handler; sigfillset(&sa.sa_mask); @@ -2884,22 +3071,24 @@ void DebugProcess(const FunctionCallbackInfo& args) { return ThrowErrnoException(errno, "kill"); } } + + +static int RegisterDebugSignalHandler() { + // FIXME(bnoordhuis) Should be per-isolate or per-context, not global. + RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler); + // If we caught a SIGUSR1 during the bootstrap process, re-raise it + // now that the debugger infrastructure is in place. + if (caught_early_debug_signal) + raise(SIGUSR1); + return 0; +} #endif // __POSIX__ #ifdef _WIN32 DWORD WINAPI EnableDebugThreadProc(void* arg) { - // Break once process will return execution to v8 - if (!debugger_running) { - for (int i = 0; i < 1; i++) { - fprintf(stderr, "Starting debugger agent.\r\n"); - fflush(stderr); - EnableDebug(false); - } - } - - v8::Debug::DebugBreak(node_isolate); - + v8::Debug::DebugBreak(*static_cast(&node_isolate)); + uv_async_send(&dispatch_debug_messages_async); return 0; } @@ -3058,22 +3247,28 @@ void Init(int* argc, uv_disable_stdio_inheritance(); // init async debug messages dispatching + // FIXME(bnoordhuis) Should be per-isolate or per-context, not global. uv_async_init(uv_default_loop(), &dispatch_debug_messages_async, DispatchDebugMessagesAsyncCallback); uv_unref(reinterpret_cast(&dispatch_debug_messages_async)); - // init async NODE_DEBUG_ENABLED emitter - uv_async_init(uv_default_loop(), - &emit_debug_enabled_async, - EmitDebugEnabledAsyncCallback); - uv_unref(reinterpret_cast(&emit_debug_enabled_async)); - // Parse a few arguments which are specific to Node. int v8_argc; const char** v8_argv; ParseArgs(argc, argv, exec_argc, exec_argv, &v8_argc, &v8_argv); + // TODO(bnoordhuis) Intercept --prof arguments and start the CPU profiler + // manually? That would give us a little more control over its runtime + // behavior but it could also interfere with the user's intentions in ways + // we fail to anticipate. Dillema. + for (int i = 1; i < v8_argc; ++i) { + if (strncmp(v8_argv[i], "--prof", sizeof("--prof") - 1) == 0) { + v8_is_profiling = true; + break; + } + } + // The const_cast doesn't violate conceptual const-ness. V8 doesn't modify // the argv array or the elements it points to. V8::SetFlagsFromCommandLine(&v8_argc, const_cast(v8_argv), true); @@ -3086,7 +3281,7 @@ void Init(int* argc, v8_argv = NULL; if (v8_argc > 1) { - exit(1); + exit(9); } if (debug_wait_connect) { @@ -3094,8 +3289,6 @@ void Init(int* argc, V8::SetFlagsFromString(expose_debug_as, sizeof(expose_debug_as) - 1); } - const char typed_arrays_flag[] = "--harmony_typed_arrays"; - V8::SetFlagsFromString(typed_arrays_flag, sizeof(typed_arrays_flag) - 1); V8::SetArrayBufferAllocator(&ArrayBufferAllocator::the_singleton); // Fetch a reference to the main isolate, so we have a reference to it @@ -3103,16 +3296,34 @@ void Init(int* argc, node_isolate = Isolate::GetCurrent(); #ifdef __POSIX__ + // Raise the open file descriptor limit. + { // NOLINT (whitespace/braces) + struct rlimit lim; + if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { + // Do a binary search for the limit. + rlim_t min = lim.rlim_cur; + rlim_t max = 1 << 20; + // But if there's a defined upper bound, don't search, just set it. + if (lim.rlim_max != RLIM_INFINITY) { + min = lim.rlim_max; + max = lim.rlim_max; + } + do { + lim.rlim_cur = min + (max - min) / 2; + if (setrlimit(RLIMIT_NOFILE, &lim)) { + max = lim.rlim_cur; + } else { + min = lim.rlim_cur; + } + } while (min + 1 < max); + } + } // Ignore SIGPIPE RegisterSignalHandler(SIGPIPE, SIG_IGN); RegisterSignalHandler(SIGINT, SignalExit); RegisterSignalHandler(SIGTERM, SignalExit); #endif // __POSIX__ - uv_check_init(uv_default_loop(), &check_immediate_watcher); - uv_unref(reinterpret_cast(&check_immediate_watcher)); - uv_idle_init(uv_default_loop(), &idle_immediate_dummy); - V8::SetFatalErrorHandler(node::OnFatalError); V8::AddMessageListener(OnMessage); @@ -3120,14 +3331,7 @@ void Init(int* argc, if (use_debug_agent) { EnableDebug(debug_wait_connect); } else { -#ifdef _WIN32 RegisterDebugSignalHandler(); -#else // Posix - static uv_signal_t signal_watcher; - uv_signal_init(uv_default_loop(), &signal_watcher); - uv_signal_start(&signal_watcher, EnableDebugSignalHandler, SIGUSR1); - uv_unref(reinterpret_cast(&signal_watcher)); -#endif // __POSIX__ } } @@ -3141,7 +3345,8 @@ struct AtExitCallback { static AtExitCallback* at_exit_functions_; -void RunAtExit() { +// TODO(bnoordhuis) Turn into per-context event. +void RunAtExit(Environment* env) { AtExitCallback* p = at_exit_functions_; at_exit_functions_ = NULL; @@ -3163,19 +3368,82 @@ void AtExit(void (*cb)(void* arg), void* arg) { } -void EmitExit(v8::Handle process_l) { +int EmitExit(Environment* env) { // process.emit('exit') - process_l->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"), - True(node_isolate)); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + Local process_object = env->process_object(); + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"), + True(node_isolate)); + + Handle exitCode = FIXED_ONE_BYTE_STRING(node_isolate, "exitCode"); + int code = process_object->Get(exitCode)->IntegerValue(); + Local args[] = { FIXED_ONE_BYTE_STRING(node_isolate, "exit"), - Integer::New(0, node_isolate) + Integer::New(code, node_isolate) }; - MakeCallback(process_l, "emit", ARRAY_SIZE(args), args); + + MakeCallback(env, process_object, "emit", ARRAY_SIZE(args), args); + + // Reload exit code, it may be changed by `emit('exit')` + return process_object->Get(exitCode)->IntegerValue(); +} + + +Environment* CreateEnvironment(Isolate* isolate, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv) { + HandleScope handle_scope(isolate); + + Local context = Context::New(isolate); + Context::Scope context_scope(context); + Environment* env = Environment::New(context); + + uv_check_init(env->event_loop(), env->immediate_check_handle()); + uv_unref( + reinterpret_cast(env->immediate_check_handle())); + uv_idle_init(env->event_loop(), env->immediate_idle_handle()); + + // Inform V8's CPU profiler when we're idle. The profiler is sampling-based + // but not all samples are created equal; mark the wall clock time spent in + // epoll_wait() and friends so profiling tools can filter it out. The samples + // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. + // TODO(bnoordhuis) Depends on a libuv implementation detail that we should + // probably fortify in the API contract, namely that the last started prepare + // or check watcher runs first. It's not 100% foolproof; if an add-on starts + // a prepare or check watcher after us, any samples attributed to its callback + // will be recorded with state=IDLE. + uv_prepare_init(env->event_loop(), env->idle_prepare_handle()); + uv_check_init(env->event_loop(), env->idle_check_handle()); + uv_unref(reinterpret_cast(env->idle_prepare_handle())); + uv_unref(reinterpret_cast(env->idle_check_handle())); + + if (v8_is_profiling) { + StartProfilerIdleNotifier(env); + } + + Local process_template = FunctionTemplate::New(); + process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); + + Local process_object = process_template->GetFunction()->NewInstance(); + env->set_process_object(process_object); + + SetupProcessObject(env, argc, argv, exec_argc, exec_argv); + Load(env); + + return env; } int Start(int argc, char** argv) { +#if !defined(_WIN32) + // Try hard not to lose SIGUSR1 signals during the bootstrap process. + InstallEarlyDebugSignalHandler(); +#endif + assert(argc > 0); // Hack around with the argv pointer. Used for process.title = "blah". @@ -3187,34 +3455,28 @@ int Start(int argc, char** argv) { const char** exec_argv; Init(&argc, const_cast(argv), &exec_argc, &exec_argv); +#if HAVE_OPENSSL + // V8 on Windows doesn't have a good source of entropy. Seed it from + // OpenSSL's pool. + V8::SetEntropySource(crypto::EntropySource); +#endif + + int code; V8::Initialize(); { Locker locker(node_isolate); - HandleScope handle_scope(node_isolate); - - // Create the one and only Context. - Local context = Context::New(node_isolate); - Context::Scope context_scope(context); - - binding_cache.Reset(node_isolate, Object::New()); - - // Use original argv, as we're just copying values out of it. - Local process_l = - SetupProcessObject(argc, argv, exec_argc, exec_argv); - - // Create all the objects, load modules, do everything. - // so your next reading stop should be node::Load()! - Load(process_l); - - // All our arguments are loaded. We've evaluated all of the scripts. We - // might even have created TCP servers. Now we enter the main eventloop. If - // there are no watchers on the loop (except for the ones that were - // uv_unref'd) then this function exits. As long as there are active - // watchers, it blocks. - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - - EmitExit(process_l); - RunAtExit(); + Environment* env = + CreateEnvironment(node_isolate, argc, argv, exec_argc, exec_argv); + // This Context::Scope is here so EnableDebug() can look up the current + // environment with Environment::GetCurrentChecked(). + // TODO(bnoordhuis) Reorder the debugger initialization logic so it can + // be removed. + Context::Scope context_scope(env->context()); + uv_run(env->event_loop(), UV_RUN_DEFAULT); + code = EmitExit(env); + RunAtExit(env); + env->Dispose(); + env = NULL; } #ifndef NDEBUG @@ -3225,7 +3487,7 @@ int Start(int argc, char** argv) { delete[] exec_argv; exec_argv = NULL; - return 0; + return code; }