#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 <assert.h>
#define umask _umask
typedef int mode_t;
#else
+#include <sys/resource.h> // getrlimit, setrlimit
#include <unistd.h> // setuid, getuid
#endif
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;
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<String> process_symbol;
-Cached<String> domain_symbol;
-
-// declared in node_internals.h
-Persistent<Object> process_p;
-
-static Persistent<Function> process_tickCallback;
-static Persistent<Object> binding_cache;
-static Persistent<Array> module_load_list;
-static Persistent<Array> p_domain_box;
-
-static Cached<String> exports_symbol;
-
-static Cached<String> errno_symbol;
-static Cached<String> syscall_symbol;
-static Cached<String> errpath_symbol;
-static Cached<String> code_symbol;
-
-static Cached<String> rss_symbol;
-static Cached<String> heap_total_symbol;
-static Cached<String> heap_used_symbol;
-
-static Cached<String> fatal_exception_symbol;
-
-static Cached<String> enter_symbol;
-static Cached<String> exit_symbol;
-static Cached<String> disposed_symbol;
-
-// Essential for node_wrap.h
-Persistent<FunctionTemplate> pipeConstructorTmpl;
-Persistent<FunctionTemplate> tcpConstructorTmpl;
-Persistent<FunctionTemplate> ttyConstructorTmpl;
-
static bool print_eval = false;
static bool force_repl = false;
static bool trace_deprecation = false;
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<String> 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;
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&);
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<char*>(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.
}
const char *syscall,
const char *msg,
const char *path) {
+ Environment* env = Environment::GetCurrent(node_isolate);
+
Local<Value> e;
Local<String> estring = OneByteString(node_isolate, errno_string(errorno));
if (msg == NULL || msg[0] == '\0') {
String::Concat(estring, FIXED_ONE_BYTE_STRING(node_isolate, ", "));
Local<String> 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<String> cons3 =
String::Concat(cons2, FIXED_ONE_BYTE_STRING(node_isolate, " '"));
}
Local<Object> 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;
}
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);
}
Local<Object> 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;
}
const char* syscall,
const char* msg,
const char* path) {
+ Environment* env = Environment::GetCurrent(node_isolate);
+
Local<Value> e;
if (!msg || !msg[0]) {
msg = winapi_strerror(errorno);
}
Local<String> 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<String> cons1 =
String::Concat(message, FIXED_ONE_BYTE_STRING(node_isolate, " '"));
}
Local<Object> 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<Value>& 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<Function>());
+ env->set_async_listener_load_function(args[2].As<Function>());
+ env->set_async_listener_unload_function(args[3].As<Function>());
+
+ Local<Object> async_listener_flag_obj = args[0].As<Object>();
+ 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<Value>& 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<Object> process = PersistentToLocal(node_isolate, process_p);
- Local<Value> tdc_v =
- process->Get(FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback"));
- if (!tdc_v->IsFunction()) {
+ Local<Object> process_object = env->process_object();
+
+ Local<String> tick_callback_function_key =
+ FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback");
+ Local<Function> tick_callback_function =
+ process_object->Get(tick_callback_function_key).As<Function>();
+
+ if (!tick_callback_function->IsFunction()) {
fprintf(stderr, "process._tickDomainCallback assigned to non-function\n");
abort();
}
- Local<Function> tdc = tdc_v.As<Function>();
- 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<Array>());
- if (!args[1]->IsObject()) {
- fprintf(stderr, "_setupDomainUse second argument must be an object\n");
- abort();
- }
- Local<Object> flag = args[1].As<Object>();
- 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<Array>());
-Handle<Value> GetDomain() {
- // no domain can exist if no domain module has been loaded
- if (!InDomain() || p_domain_box.IsEmpty())
- return Null(node_isolate);
+ Local<Object> domain_flag_obj = args[1].As<Object>();
+ 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<Value>
-MakeDomainCallback(const Handle<Object> object,
- const Handle<Function> callback,
- int argc,
- Handle<Value> argv[]) {
- // TODO(trevnorris) Hook for long stack traces to be made here.
+void SetupNextTick(const FunctionCallbackInfo<Value>& 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<Value> domain_v = object->Get(domain_symbol);
- Local<Object> domain;
- Local<Function> enter;
- Local<Function> exit;
+ // Values use to cross communicate with processNextTick.
+ Local<Object> tick_info_obj = args[0].As<Object>();
+ tick_info_obj->SetIndexedPropertiesToExternalArrayData(
+ env->tick_info()->fields(),
+ kExternalUnsignedIntArray,
+ env->tick_info()->fields_count());
+
+ env->set_tick_callback_function(args[1].As<Function>());
+
+ // Do a little housekeeping.
+ env->process_object()->Delete(
+ FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupNextTick"));
+}
+
+
+Handle<Value> MakeDomainCallback(Environment* env,
+ Handle<Value> recv,
+ const Handle<Function> callback,
+ int argc,
+ Handle<Value> argv[]) {
+ // If you hit this assertion, you forgot to enter the v8::Context first.
+ assert(env->context() == env->isolate()->GetCurrentContext());
+
+ Local<Object> process = env->process_object();
+ Local<Object> object, domain;
+ Local<Value> 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<Object>();
+ // 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<Function>::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<Object>();
+
+ if (domain->Get(env->disposed_string())->IsTrue()) {
+ // domain has been disposed of.
+ return Undefined(node_isolate);
+ }
+
+ Local<Function> enter =
+ domain->Get(env->enter_string()).As<Function>();
+ assert(enter->IsFunction());
+ enter->Call(domain, 0, NULL);
+
+ if (try_catch.HasCaught()) {
+ return Undefined(node_isolate);
+ }
}
}
- Local<Value> ret = callback->Call(object, argc, argv);
+ Local<Value> ret = callback->Call(recv, argc, argv);
if (try_catch.HasCaught()) {
return Undefined(node_isolate);
}
if (has_domain) {
- exit = Local<Function>::Cast(domain->Get(exit_symbol));
- assert(!exit.IsEmpty());
+ Local<Function> exit =
+ domain->Get(env->exit_string()).As<Function>();
+ assert(exit->IsFunction());
exit->Call(domain, 0, NULL);
if (try_catch.HasCaught()) {
}
}
- 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<Object> process = PersistentToLocal(node_isolate, process_p);
- Local<Function> 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);
}
}
-Handle<Value>
-MakeCallback(const Handle<Object> object,
- const Handle<Function> callback,
- int argc,
- Handle<Value> argv[]) {
- // TODO(trevnorris) Hook for long stack traces to be made here.
- Local<Object> process = PersistentToLocal(node_isolate, process_p);
+Handle<Value> MakeCallback(Environment* env,
+ Handle<Value> recv,
+ const Handle<Function> callback,
+ int argc,
+ Handle<Value> 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<Value> 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<Function>());
- }
+ Local<Object> process = env->process_object();
TryCatch try_catch;
try_catch.SetVerbose(true);
- Local<Value> ret = callback->Call(object, argc, argv);
+ // TODO(trevnorris): This is sucky for performance. Fix it.
+ bool has_async_queue =
+ recv->IsObject() && recv.As<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);
+ }
+
+ Local<Value> 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<Function> 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);
}
// Internal only.
-Handle<Value>
-MakeCallback(const Handle<Object> object,
- uint32_t index,
- int argc,
- Handle<Value> argv[]) {
- HandleScope scope(node_isolate);
+Handle<Value> MakeCallback(Environment* env,
+ Handle<Object> recv,
+ uint32_t index,
+ int argc,
+ Handle<Value> argv[]) {
+ Local<Function> callback = recv->Get(index).As<Function>();
+ assert(callback->IsFunction());
- Local<Function> callback = object->Get(index).As<Function>();
+ return MakeCallback(env, recv.As<Value>(), callback, argc, argv);
+}
+
+
+Handle<Value> MakeCallback(Environment* env,
+ Handle<Object> recv,
+ Handle<String> symbol,
+ int argc,
+ Handle<Value> argv[]) {
+ Local<Function> callback = recv->Get(symbol).As<Function>();
assert(callback->IsFunction());
+ return MakeCallback(env, recv.As<Value>(), callback, argc, argv);
+}
+
- if (using_domains)
- return scope.Close(MakeDomainCallback(object, callback, argc, argv));
- return scope.Close(MakeCallback(object, callback, argc, argv));
+Handle<Value> MakeCallback(Environment* env,
+ Handle<Object> recv,
+ const char* method,
+ int argc,
+ Handle<Value> argv[]) {
+ Local<String> method_string = OneByteString(node_isolate, method);
+ return MakeCallback(env, recv, method_string, argc, argv);
}
-Handle<Value>
-MakeCallback(const Handle<Object> object,
- const Handle<String> symbol,
- int argc,
- Handle<Value> argv[]) {
- HandleScope scope(node_isolate);
+Handle<Value> MakeCallback(Handle<Object> recv,
+ const char* method,
+ int argc,
+ Handle<Value> argv[]) {
+ HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify.
+ Local<Context> context = recv->CreationContext();
+ Environment* env = Environment::GetCurrent(context);
+ Context::Scope context_scope(context);
+ return handle_scope.Close(MakeCallback(env, recv, method, argc, argv));
+}
- Local<Function> callback = object->Get(symbol).As<Function>();
- assert(callback->IsFunction());
- if (using_domains)
- return scope.Close(MakeDomainCallback(object, callback, argc, argv));
- return scope.Close(MakeCallback(object, callback, argc, argv));
+Handle<Value> MakeCallback(Handle<Object> recv,
+ Handle<String> symbol,
+ int argc,
+ Handle<Value> argv[]) {
+ HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify.
+ Local<Context> context = recv->CreationContext();
+ Environment* env = Environment::GetCurrent(context);
+ Context::Scope context_scope(context);
+ return handle_scope.Close(MakeCallback(env, recv, symbol, argc, argv));
}
-Handle<Value>
-MakeCallback(const Handle<Object> object,
- const char* method,
- int argc,
- Handle<Value> argv[]) {
- HandleScope scope(node_isolate);
+Handle<Value> MakeCallback(Handle<Object> recv,
+ Handle<Function> callback,
+ int argc,
+ Handle<Value> argv[]) {
+ HandleScope handle_scope(node_isolate); // FIXME(bnoordhuis) Isolate-ify.
+ Local<Context> context = recv->CreationContext();
+ Environment* env = Environment::GetCurrent(context);
+ Context::Scope context_scope(context);
+ return handle_scope.Close(
+ MakeCallback(env, recv.As<Value>(), callback, argc, argv));
+}
- Local<String> method_string = OneByteString(node_isolate, method);
- Handle<Value> ret = MakeCallback(object, method_string, argc, argv);
- return scope.Close(ret);
+Handle<Value> MakeDomainCallback(Handle<Object> recv,
+ Handle<Function> callback,
+ int argc,
+ Handle<Value> argv[]) {
+ Local<Context> 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<Value> 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);
return StringBytes::Write(buf, buflen, val, encoding, NULL);
}
-void DisplayExceptionLine(Handle<Message> 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<Value> er,
+ Handle<Message> message) {
+ if (message.IsEmpty())
+ return;
- uv_tty_reset_mode();
+ HandleScope scope(env->isolate());
+ Local<Object> err_obj;
+ if (!er.IsEmpty() && er->IsObject()) {
+ err_obj = er.As<Object>();
- 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<size_t>(off) < sizeof(arrow));
+ arrow[off++] = (sourceline_string[i] == '\t') ? '\t' : ' ';
+ }
+ for (int i = start; i < end; i++) {
+ assert(static_cast<size_t>(off) < sizeof(arrow));
+ arrow[off++] = '^';
+ }
+ assert(static_cast<size_t>(off) < sizeof(arrow) - 1);
+ arrow[off++] = '\n';
+ arrow[off] = '\0';
+
+ Local<String> arrow_str = String::NewFromUtf8(env->isolate(), arrow);
+ Local<Value> msg;
+ Local<Value> 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<Value> er, Handle<Message> message) {
- HandleScope scope(node_isolate);
+static void ReportException(Environment* env,
+ Handle<Value> er,
+ Handle<Message> message) {
+ HandleScope scope(env->isolate());
+
+ AppendExceptionLine(env, er, message);
+
+ Local<Value> trace_value;
- DisplayExceptionLine(message);
+ if (er->IsUndefined() || er->IsNull())
+ trace_value = Undefined(env->isolate());
+ else
+ trace_value = er->ToObject()->Get(env->stack_string());
- Local<Value> 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
if (er->IsObject()) {
Local<Object> err_obj = er.As<Object>();
- 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() ||
}
-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<Value> ExecuteString(Handle<String> source, Handle<Value> filename) {
- HandleScope scope(node_isolate);
+static Local<Value> ExecuteString(Environment* env,
+ Handle<String> source,
+ Handle<Value> filename) {
+ HandleScope scope(env->isolate());
TryCatch try_catch;
// try_catch must be nonverbose to disable FatalException() handler,
Local<v8::Script> script = v8::Script::Compile(source, filename);
if (script.IsEmpty()) {
- ReportException(try_catch);
+ ReportException(env, try_catch);
exit(3);
}
Local<Value> result = script->Run();
if (result.IsEmpty()) {
- ReportException(try_catch);
+ ReportException(env, try_catch);
exit(4);
}
int i = 0;
QUEUE_FOREACH(q, &req_wrap_queue) {
- ReqWrap<uv_req_t>* w = container_of(q, ReqWrap<uv_req_t>, req_wrap_queue_);
- if (w->persistent().IsEmpty()) continue;
+ ReqWrap<uv_req_t>* w = CONTAINER_OF(q, ReqWrap<uv_req_t>, req_wrap_queue_);
+ if (w->persistent().IsEmpty())
+ continue;
ary->Set(i++, w->object());
}
Local<String> 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> object = w->object();
Local<Value> owner = object->Get(owner_sym);
- if (owner->IsUndefined()) owner = object;
+ if (owner->IsUndefined())
+ owner = object;
ary->Set(i++, owner);
}
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;
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");
}
static void Uptime(const FunctionCallbackInfo<Value>& 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<Value>& 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<Object> 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<Integer> heap_total =
+ Integer::NewFromUnsigned(v8_heap_stats.total_heap_size(), node_isolate);
+ Local<Integer> heap_used =
+ Integer::NewFromUnsigned(v8_heap_stats.used_heap_size(), node_isolate);
+
+ Local<Object> 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);
}
args.GetReturnValue().Set(tuple);
}
+extern "C" void node_module_register(void* m) {
+ struct node_module* mp = reinterpret_cast<struct node_module*>(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<Object> 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<Value>& 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<Object> 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<Object> exports = module->Get(exports_symbol)->ToObject();
+ Local<String> exports_string = env->exports_string();
+ Local<Object> exports = module->Get(exports_string)->ToObject();
if (uv_dlopen(*filename, &lib)) {
- Local<String> errmsg = OneByteString(node_isolate, uv_dlerror(&lib));
+ Local<String> 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());
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<size_t>(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<void**>(&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]
fprintf(stderr, "FATAL ERROR: %s\n", message);
}
fflush(stderr);
-#if defined(DEBUG)
abort();
-#endif
- exit(5);
}
void FatalException(Handle<Value> error, Handle<Message> message) {
HandleScope scope(node_isolate);
- if (fatal_exception_symbol.IsEmpty()) {
- fatal_exception_symbol =
- FIXED_ONE_BYTE_STRING(node_isolate, "_fatalException");
- }
-
- Local<Object> process = PersistentToLocal(node_isolate, process_p);
- Local<Value> fatal_v = process->Get(fatal_exception_symbol);
+ Environment* env = Environment::GetCurrent(node_isolate);
+ Local<Object> process_object = env->process_object();
+ Local<String> fatal_exception_string = env->fatal_exception_string();
+ Local<Function> fatal_exception_function =
+ process_object->Get(fatal_exception_string).As<Function>();
- 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<Function> fatal_f = Local<Function>::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<Value> caught = fatal_f->Call(process, 1, &error);
+ Local<Value> 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);
}
}
static void Binding(const FunctionCallbackInfo<Value>& args) {
- HandleScope scope(node_isolate);
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
Local<String> module = args[0]->ToString();
String::Utf8Value module_v(module);
- node_module_struct* modp;
- Local<Object> cache = PersistentToLocal(node_isolate, binding_cache);
+ Local<Object> cache = env->binding_cache_object();
Local<Object> exports;
if (cache->Has(module)) {
char buf[1024];
snprintf(buf, sizeof(buf), "Binding %s", *module_v);
- Local<Array> modules = PersistentToLocal(node_isolate, module_load_list);
+ Local<Array> 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<Value> 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();
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<WCHAR*>(*key);
}
}
#endif
- if (rc != -1) info.GetReturnValue().Set(rc);
+ if (rc != -1)
+ info.GetReturnValue().Set(rc);
}
#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<WCHAR*>(*key);
HandleScope scope(node_isolate);
#ifdef __POSIX__
int size = 0;
- while (environ[size]) size++;
+ while (environ[size])
+ size++;
Local<Array> env = Array::New(size);
}
#else // _WIN32
WCHAR* environment = GetEnvironmentStringsW();
- if (environment == NULL) return; // This should not happen.
+ if (environment == NULL)
+ return; // This should not happen.
Local<Array> env = Array::New();
WCHAR* p = environment;
int i = 0;
void NeedImmediateCallbackGetter(Local<String> property,
const PropertyCallbackInfo<Value>& 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<const uv_handle_t*>(immediate_check_handle));
+ info.GetReturnValue().Set(active);
}
-static void NeedImmediateCallbackSetter(Local<String> property,
- Local<Value> value,
- const PropertyCallbackInfo<void>&) {
- HandleScope scope(node_isolate);
+static void NeedImmediateCallbackSetter(
+ Local<String> property,
+ Local<Value> value,
+ const PropertyCallbackInfo<void>& 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<const uv_handle_t*>(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<Value>& args) {
+ HandleScope handle_scope(args.GetIsolate());
+ Environment* env = Environment::GetCurrent(args.GetIsolate());
+ StartProfilerIdleNotifier(env);
+}
+
+
+void StopProfilerIdleNotifier(const FunctionCallbackInfo<Value>& 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<Object> 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<FunctionTemplate> process_template = FunctionTemplate::New();
- process_template->SetClassName(
- FIXED_ONE_BYTE_STRING(node_isolate, "process"));
-
- Local<Object> process = process_template->GetFunction()->NewInstance();
- assert(process.IsEmpty() == false);
- assert(process->IsObject() == true);
-
- process_p.Reset(node_isolate, process);
+ Local<Object> process = env->process_object();
process->SetAccessor(FIXED_ONE_BYTE_STRING(node_isolate, "title"),
ProcessTitleGetter,
FIXED_ONE_BYTE_STRING(node_isolate, NODE_VERSION));
// process.moduleLoadList
- Local<Array> 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<Object> versions = Object::New();
#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));
process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "execArgv"), exec_arguments);
// create process.env
- Local<ObjectTemplate> envTemplate = ObjectTemplate::New();
- envTemplate->SetNamedPropertyHandler(EnvGetter,
- EnvSetter,
- EnvQuery,
- EnvDeleter,
- EnvEnumerator,
- Object::New());
- Local<Object> env = envTemplate->NewInstance();
- process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "env"), env);
+ Local<ObjectTemplate> process_env_template = ObjectTemplate::New();
+ process_env_template->SetNamedPropertyHandler(EnvGetter,
+ EnvSetter,
+ EnvQuery,
+ EnvDeleter,
+ EnvEnumerator,
+ Object::New());
+ Local<Object> 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());
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);
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<Object> info_box = Object::New();
- info_box->SetIndexedPropertiesToExternalArrayData(&tick_infobox,
- kExternalUnsignedIntArray,
- 4);
- process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickInfoBox"), info_box);
+ Local<Object> 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);
}
}
-void Load(Handle<Object> 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.)
// are not safe to ignore.
try_catch.SetVerbose(false);
- Local<String> script_name = FIXED_ONE_BYTE_STRING(node_isolate, "node.js");
- Local<Value> f_value = ExecuteString(MainSource(), script_name);
+ Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "node.js");
+ Local<Value> f_value = ExecuteString(env, MainSource(), script_name);
if (try_catch.HasCaught()) {
- ReportException(try_catch);
+ ReportException(env, try_catch);
exit(10);
}
assert(f_value->IsFunction());
// Node's I/O bindings may want to replace 'f' with their own function.
// Add a reference to the global object
- Local<Object> global = v8::Context::GetCurrent()->Global();
+ Local<Object> global = env->context()->Global();
-#if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP
+#if defined HAVE_DTRACE || defined HAVE_ETW
InitDTrace(global);
#endif
// thrown during process startup.
try_catch.SetVerbose(true);
- NODE_SET_METHOD(process_l, "_rawDebug", RawDebug);
+ NODE_SET_METHOD(env->process_object(), "_rawDebug", RawDebug);
- Local<Value> arg = process_l;
+ Local<Value> 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() {
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);
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) {
}
-// 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<Object> obj = Object::New();
- obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "cmd"),
- FIXED_ONE_BYTE_STRING(node_isolate, "NODE_DEBUG_ENABLED"));
- Local<Value> 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<Object> message = Object::New();
+ message->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "cmd"),
+ FIXED_ONE_BYTE_STRING(env->isolate(), "NODE_DEBUG_ENABLED"));
+ Local<Value> 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<Isolate* volatile*>(&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);
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<Isolate* volatile*>(&node_isolate));
+ uv_async_send(&dispatch_debug_messages_async);
return 0;
}
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<uv_handle_t*>(&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<uv_handle_t*>(&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<char**>(v8_argv), true);
v8_argv = NULL;
if (v8_argc > 1) {
- exit(1);
+ exit(9);
}
if (debug_wait_connect) {
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
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<uv_handle_t*>(&check_immediate_watcher));
- uv_idle_init(uv_default_loop(), &idle_immediate_dummy);
-
V8::SetFatalErrorHandler(node::OnFatalError);
V8::AddMessageListener(OnMessage);
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<uv_handle_t*>(&signal_watcher));
-#endif // __POSIX__
}
}
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;
}
-void EmitExit(v8::Handle<v8::Object> 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<Object> process_object = env->process_object();
+ process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"),
+ True(node_isolate));
+
+ Handle<String> exitCode = FIXED_ONE_BYTE_STRING(node_isolate, "exitCode");
+ int code = process_object->Get(exitCode)->IntegerValue();
+
Local<Value> 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 = 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<uv_handle_t*>(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<uv_handle_t*>(env->idle_prepare_handle()));
+ uv_unref(reinterpret_cast<uv_handle_t*>(env->idle_check_handle()));
+
+ if (v8_is_profiling) {
+ StartProfilerIdleNotifier(env);
+ }
+
+ Local<FunctionTemplate> process_template = FunctionTemplate::New();
+ process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process"));
+
+ Local<Object> 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".
const char** exec_argv;
Init(&argc, const_cast<const char**>(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 = 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<Object> 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
delete[] exec_argv;
exec_argv = NULL;
- return 0;
+ return code;
}