diff --git a/node.gyp b/node.gyp index 9e29ed12a45..3ff177ad98b 100644 --- a/node.gyp +++ b/node.gyp @@ -116,6 +116,8 @@ 'src/udp_wrap.cc', 'src/uv.cc', # headers to make for a more pleasant IDE experience + 'src/env.h', + 'src/env-inl.h', 'src/handle_wrap.h', 'src/node.h', 'src/node_buffer.h', @@ -124,6 +126,7 @@ 'src/node_extensions.h', 'src/node_file.h', 'src/node_http_parser.h', + 'src/node_internals.h', 'src/node_javascript.h', 'src/node_root_certs.h', 'src/node_version.h', @@ -139,6 +142,8 @@ 'src/string_bytes.h', 'src/stream_wrap.h', 'src/tree.h', + 'src/util.h', + 'src/util-inl.h', 'deps/http_parser/http_parser.h', '<(SHARED_INTERMEDIATE_DIR)/node_natives.h', # javascript files to make for an even more pleasant IDE experience diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index f09590e88e3..00238788ade 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -21,6 +21,8 @@ #define CARES_STATICLIB #include "ares.h" +#include "env.h" +#include "env-inl.h" #include "node.h" #include "req_wrap.h" #include "tree.h" @@ -45,6 +47,7 @@ namespace node { namespace cares_wrap { using v8::Array; +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::Handle; @@ -57,22 +60,8 @@ using v8::Persistent; using v8::String; using v8::Value; - typedef class ReqWrap GetAddrInfoReqWrap; -struct ares_task_t { - uv_loop_t* loop; - ares_socket_t sock; - uv_poll_t poll_watcher; - RB_ENTRY(ares_task_t) node; -}; - - -static Cached oncomplete_sym; -static ares_channel ares_channel; -static uv_timer_t ares_timer; -static RB_HEAD(ares_task_list, ares_task_t) ares_tasks; - static int cmp_ares_tasks(const ares_task_t* a, const ares_task_t* b) { if (a->sock < b->sock) return -1; @@ -88,26 +77,28 @@ RB_GENERATE_STATIC(ares_task_list, ares_task_t, node, cmp_ares_tasks) /* This is called once per second by loop->timer. It is used to constantly */ /* call back into c-ares for possibly processing timeouts. */ static void ares_timeout(uv_timer_t* handle, int status) { - assert(!RB_EMPTY(&ares_tasks)); - ares_process_fd(ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + Environment* env = Environment::from_cares_timer_handle(handle); + assert(!RB_EMPTY(env->cares_task_list())); + ares_process_fd(env->cares_channel(), ARES_SOCKET_BAD, ARES_SOCKET_BAD); } static void ares_poll_cb(uv_poll_t* watcher, int status, int events) { ares_task_t* task = container_of(watcher, ares_task_t, poll_watcher); + Environment* env = task->env; /* Reset the idle timer */ - uv_timer_again(&ares_timer); + uv_timer_again(env->cares_timer_handle()); if (status < 0) { /* An error happened. Just pretend that the socket is both readable and */ /* writable. */ - ares_process_fd(ares_channel, task->sock, task->sock); + ares_process_fd(env->cares_channel(), task->sock, task->sock); return; } /* Process DNS responses */ - ares_process_fd(ares_channel, + ares_process_fd(env->cares_channel(), events & UV_READABLE ? task->sock : ARES_SOCKET_BAD, events & UV_WRITABLE ? task->sock : ARES_SOCKET_BAD); } @@ -120,7 +111,7 @@ static void ares_poll_close_cb(uv_handle_t* watcher) { /* Allocates and returns a new ares_task_t */ -static ares_task_t* ares_task_create(uv_loop_t* loop, ares_socket_t sock) { +static ares_task_t* ares_task_create(Environment* env, ares_socket_t sock) { ares_task_t* task = static_cast(malloc(sizeof(*task))); if (task == NULL) { @@ -128,10 +119,10 @@ static ares_task_t* ares_task_create(uv_loop_t* loop, ares_socket_t sock) { return NULL; } - task->loop = loop; + task->env = env; task->sock = sock; - if (uv_poll_init_socket(loop, &task->poll_watcher, sock) < 0) { + if (uv_poll_init_socket(env->event_loop(), &task->poll_watcher, sock) < 0) { /* This should never happen. */ free(task); return NULL; @@ -146,24 +137,25 @@ static void ares_sockstate_cb(void* data, ares_socket_t sock, int read, int write) { - uv_loop_t* loop = static_cast(data); + Environment* env = static_cast(data); ares_task_t* task; ares_task_t lookup_task; lookup_task.sock = sock; - task = RB_FIND(ares_task_list, &ares_tasks, &lookup_task); + task = RB_FIND(ares_task_list, env->cares_task_list(), &lookup_task); if (read || write) { if (!task) { /* New socket */ /* If this is the first socket then start the timer. */ - if (!uv_is_active(reinterpret_cast(&ares_timer))) { - assert(RB_EMPTY(&ares_tasks)); - uv_timer_start(&ares_timer, ares_timeout, 1000, 1000); + uv_timer_t* timer_handle = env->cares_timer_handle(); + if (!uv_is_active(reinterpret_cast(timer_handle))) { + assert(RB_EMPTY(env->cares_task_list())); + uv_timer_start(timer_handle, ares_timeout, 1000, 1000); } - task = ares_task_create(loop, sock); + task = ares_task_create(env, sock); if (task == NULL) { /* This should never happen unless we're out of memory or something */ /* is seriously wrong. The socket won't be polled, but the the query */ @@ -171,7 +163,7 @@ static void ares_sockstate_cb(void* data, return; } - RB_INSERT(ares_task_list, &ares_tasks, task); + RB_INSERT(ares_task_list, env->cares_task_list(), task); } /* This should never fail. If it fails anyway, the query will eventually */ @@ -187,12 +179,12 @@ static void ares_sockstate_cb(void* data, assert(task && "When an ares socket is closed we should have a handle for it"); - RB_REMOVE(ares_task_list, &ares_tasks, task); + RB_REMOVE(ares_task_list, env->cares_task_list(), task); uv_close(reinterpret_cast(&task->poll_watcher), ares_poll_close_cb); - if (RB_EMPTY(&ares_tasks)) { - uv_timer_stop(&ares_timer); + if (RB_EMPTY(env->cares_task_list())) { + uv_timer_stop(env->cares_timer_handle()); } } } @@ -228,7 +220,7 @@ static Local HostentToNames(struct hostent* host) { class QueryWrap { public: - explicit QueryWrap(Local req_wrap_obj) { + QueryWrap(Environment* env, Local req_wrap_obj) : env_(env) { HandleScope scope(node_isolate); persistent().Reset(node_isolate, req_wrap_obj); } @@ -289,24 +281,46 @@ class QueryWrap { } void CallOnComplete(Local answer) { - HandleScope scope(node_isolate); - Local argv[2] = { Integer::New(0, node_isolate), answer }; - MakeCallback(object(), oncomplete_sym, ARRAY_SIZE(argv), argv); + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); + Local argv[] = { + Integer::New(0, env()->isolate()), + answer + }; + MakeCallback(env(), + object(), + env()->oncomplete_string(), + ARRAY_SIZE(argv), + argv); } void CallOnComplete(Local answer, Local family) { - HandleScope scope(node_isolate); - Local argv[3] = { Integer::New(0, node_isolate), answer, family }; - MakeCallback(object(), oncomplete_sym, ARRAY_SIZE(argv), argv); + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); + Local argv[] = { + Integer::New(0, env()->isolate()), + answer, + family + }; + MakeCallback(env(), + object(), + env()->oncomplete_string(), + ARRAY_SIZE(argv), + argv); } void ParseError(int status) { assert(status != ARES_SUCCESS); - HandleScope scope(node_isolate); + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); Local argv[] = { - Integer::New(status, node_isolate) + Integer::New(status, env()->isolate()) }; - MakeCallback(object(), oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env(), + object(), + env()->oncomplete_string(), + ARRAY_SIZE(argv), + argv); } // Subclasses should implement the appropriate Parse method. @@ -318,18 +332,29 @@ class QueryWrap { assert(0); }; + inline Environment* env() const { + return env_; + } + private: Persistent object_; + Environment* const env_; }; class QueryAWrap: public QueryWrap { public: - explicit QueryAWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryAWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, name, ns_c_in, ns_t_a, Callback, GetQueryArg()); + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_a, + Callback, + GetQueryArg()); return 0; } @@ -355,11 +380,12 @@ class QueryAWrap: public QueryWrap { class QueryAaaaWrap: public QueryWrap { public: - explicit QueryAaaaWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryAaaaWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, + ares_query(env()->cares_channel(), name, ns_c_in, ns_t_aaaa, @@ -390,12 +416,12 @@ class QueryAaaaWrap: public QueryWrap { class QueryCnameWrap: public QueryWrap { public: - explicit QueryCnameWrap(Local req_wrap_obj) - : QueryWrap(req_wrap_obj) { + QueryCnameWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, + ares_query(env()->cares_channel(), name, ns_c_in, ns_t_cname, @@ -429,11 +455,17 @@ class QueryCnameWrap: public QueryWrap { class QueryMxWrap: public QueryWrap { public: - explicit QueryMxWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryMxWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, name, ns_c_in, ns_t_mx, Callback, GetQueryArg()); + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_mx, + Callback, + GetQueryArg()); return 0; } @@ -473,11 +505,17 @@ class QueryMxWrap: public QueryWrap { class QueryNsWrap: public QueryWrap { public: - explicit QueryNsWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryNsWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, name, ns_c_in, ns_t_ns, Callback, GetQueryArg()); + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_ns, + Callback, + GetQueryArg()); return 0; } @@ -501,11 +539,17 @@ class QueryNsWrap: public QueryWrap { class QueryTxtWrap: public QueryWrap { public: - explicit QueryTxtWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryTxtWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, name, ns_c_in, ns_t_txt, Callback, GetQueryArg()); + ares_query(env()->cares_channel(), + name, + ns_c_in, + ns_t_txt, + Callback, + GetQueryArg()); return 0; } @@ -536,11 +580,12 @@ class QueryTxtWrap: public QueryWrap { class QuerySrvWrap: public QueryWrap { public: - explicit QuerySrvWrap(Local req_wrap_obj) : QueryWrap(req_wrap_obj) { + explicit QuerySrvWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, + ares_query(env()->cares_channel(), name, ns_c_in, ns_t_srv, @@ -592,12 +637,12 @@ class QuerySrvWrap: public QueryWrap { class QueryNaptrWrap: public QueryWrap { public: - explicit QueryNaptrWrap(Local req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit QueryNaptrWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { - ares_query(ares_channel, + ares_query(env()->cares_channel(), name, ns_c_in, ns_t_naptr, @@ -659,8 +704,8 @@ class QueryNaptrWrap: public QueryWrap { class GetHostByAddrWrap: public QueryWrap { public: - explicit GetHostByAddrWrap(Local req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit GetHostByAddrWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name) { @@ -677,7 +722,7 @@ class GetHostByAddrWrap: public QueryWrap { return UV_EINVAL; // So errnoException() reports a proper error. } - ares_gethostbyaddr(ares_channel, + ares_gethostbyaddr(env()->cares_channel(), address_buffer, length, family, @@ -697,12 +742,16 @@ class GetHostByAddrWrap: public QueryWrap { class GetHostByNameWrap: public QueryWrap { public: - explicit GetHostByNameWrap(Local req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit GetHostByNameWrap(Environment* env, Local req_wrap_obj) + : QueryWrap(env, req_wrap_obj) { } int Send(const char* name, int family) { - ares_gethostbyname(ares_channel, name, family, Callback, GetQueryArg()); + ares_gethostbyname(env()->cares_channel(), + name, + family, + Callback, + GetQueryArg()); return 0; } @@ -720,7 +769,8 @@ class GetHostByNameWrap: public QueryWrap { template static void Query(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); assert(!args.IsConstructCall()); assert(args[0]->IsObject()); @@ -728,7 +778,7 @@ static void Query(const FunctionCallbackInfo& args) { Local req_wrap_obj = args[0].As(); Local string = args[1].As(); - Wrap* wrap = new Wrap(req_wrap_obj); + Wrap* wrap = new Wrap(env, req_wrap_obj); String::Utf8Value name(string); int err = wrap->Send(*name); @@ -739,9 +789,11 @@ static void Query(const FunctionCallbackInfo& args) { void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { - HandleScope scope(node_isolate); - GetAddrInfoReqWrap* req_wrap = static_cast(req->data); + Environment* env = req_wrap->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local argv[] = { Integer::New(status, node_isolate), @@ -828,7 +880,11 @@ void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { uv_freeaddrinfo(res); // Make the callback into JavaScript - MakeCallback(req_wrap->object(), oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + req_wrap->object(), + env->oncomplete_string(), + ARRAY_SIZE(argv), + argv); delete req_wrap; } @@ -851,7 +907,8 @@ static void IsIP(const FunctionCallbackInfo& args) { static void GetAddrInfo(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); assert(args[0]->IsObject()); assert(args[1]->IsString()); @@ -875,14 +932,14 @@ static void GetAddrInfo(const FunctionCallbackInfo& args) { abort(); } - GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(req_wrap_obj); + GetAddrInfoReqWrap* req_wrap = new GetAddrInfoReqWrap(env, req_wrap_obj); struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; - int err = uv_getaddrinfo(uv_default_loop(), + int err = uv_getaddrinfo(env->event_loop(), &req_wrap->req_, AfterGetAddrInfo, *hostname, @@ -896,13 +953,14 @@ static void GetAddrInfo(const FunctionCallbackInfo& args) { static void GetServers(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); Local server_array = Array::New(); ares_addr_node* servers; - int r = ares_get_servers(ares_channel, &servers); + int r = ares_get_servers(env->cares_channel(), &servers); assert(r == ARES_SUCCESS); ares_addr_node* cur = servers; @@ -925,7 +983,8 @@ static void GetServers(const FunctionCallbackInfo& args) { static void SetServers(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); assert(args[0]->IsArray()); @@ -934,7 +993,7 @@ static void SetServers(const FunctionCallbackInfo& args) { uint32_t len = arr->Length(); if (len == 0) { - int rv = ares_set_servers(ares_channel, NULL); + int rv = ares_set_servers(env->cares_channel(), NULL); return args.GetReturnValue().Set(rv); } @@ -982,7 +1041,7 @@ static void SetServers(const FunctionCallbackInfo& args) { } if (err == 0) - err = ares_set_servers(ares_channel, &servers[0]); + err = ares_set_servers(env->cares_channel(), &servers[0]); else err = ARES_EBADSTR; @@ -999,28 +1058,29 @@ static void StrError(const FunctionCallbackInfo& args) { } -static void Initialize(Handle target) { - HandleScope scope(node_isolate); - int r; +static void Initialize(Handle target, + Handle unused, + Handle context) { + Environment* env = Environment::GetCurrent(context); - r = ares_library_init(ARES_LIB_INIT_ALL); + int r = ares_library_init(ARES_LIB_INIT_ALL); assert(r == ARES_SUCCESS); struct ares_options options; memset(&options, 0, sizeof(options)); options.flags = ARES_FLAG_NOCHECKRESP; options.sock_state_cb = ares_sockstate_cb; - options.sock_state_cb_data = uv_default_loop(); + options.sock_state_cb_data = env; /* We do the call to ares_init_option for caller. */ - r = ares_init_options(&ares_channel, + r = ares_init_options(env->cares_channel_ptr(), &options, ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB); assert(r == ARES_SUCCESS); /* Initialize the timeout timer. The timer won't be started until the */ /* first socket is opened. */ - uv_timer_init(uv_default_loop(), &ares_timer); + uv_timer_init(env->event_loop(), env->cares_timer_handle()); NODE_SET_METHOD(target, "queryA", Query); NODE_SET_METHOD(target, "queryAaaa", Query); @@ -1045,11 +1105,9 @@ static void Initialize(Handle target) { Integer::New(AF_INET6, node_isolate)); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "AF_UNSPEC"), Integer::New(AF_UNSPEC, node_isolate)); - - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); } } // namespace cares_wrap } // namespace node -NODE_MODULE(node_cares_wrap, node::cares_wrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_cares_wrap, node::cares_wrap::Initialize) diff --git a/src/env-inl.h b/src/env-inl.h new file mode 100644 index 00000000000..a8fcc586034 --- /dev/null +++ b/src/env-inl.h @@ -0,0 +1,289 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SRC_ENV_INL_H_ +#define SRC_ENV_INL_H_ + +#include "env.h" +#include "util.h" +#include "util-inl.h" +#include "uv.h" +#include "v8.h" + +#include +#include + +namespace node { + +inline Environment::IsolateData* Environment::IsolateData::GetOrCreate( + v8::Isolate* isolate) { + IsolateData* isolate_data = static_cast(isolate->GetData()); + if (isolate_data == NULL) { + isolate_data = new IsolateData(isolate); + isolate->SetData(isolate_data); + } + isolate_data->ref_count_ += 1; + return isolate_data; +} + +inline void Environment::IsolateData::Put() { + if (--ref_count_ == 0) { + isolate()->SetData(NULL); + delete this; + } +} + +inline Environment::IsolateData::IsolateData(v8::Isolate* isolate) + : event_loop_(uv_default_loop()) + , isolate_(isolate) +#define V(PropertyName, StringValue) \ + , PropertyName ## _index_( \ + FIXED_ONE_BYTE_STRING(isolate, StringValue).Eternalize(isolate)) + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + , ref_count_(0) { +} + +inline uv_loop_t* Environment::IsolateData::event_loop() const { + return event_loop_; +} + +inline v8::Isolate* Environment::IsolateData::isolate() const { + return isolate_; +} + +inline Environment::DomainFlag::DomainFlag() { + for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; +} + +inline uint32_t* Environment::DomainFlag::fields() { + return fields_; +} + +inline int Environment::DomainFlag::fields_count() const { + return kFieldsCount; +} + +inline uint32_t Environment::DomainFlag::count() const { + return fields_[kCount]; +} + +inline Environment::TickInfo::TickInfo() { + for (int i = 0; i < kFieldsCount; ++i) fields_[i] = 0; +} + +inline uint32_t* Environment::TickInfo::fields() { + return fields_; +} + +inline int Environment::TickInfo::fields_count() const { + return kFieldsCount; +} + +inline uint32_t Environment::TickInfo::in_tick() const { + return fields_[kInTick]; +} + +inline uint32_t Environment::TickInfo::index() const { + return fields_[kIndex]; +} + +inline uint32_t Environment::TickInfo::last_threw() const { + return fields_[kLastThrew]; +} + +inline uint32_t Environment::TickInfo::length() const { + return fields_[kLength]; +} + +inline void Environment::TickInfo::set_index(uint32_t value) { + fields_[kIndex] = value; +} + +inline void Environment::TickInfo::set_last_threw(uint32_t value) { + fields_[kLastThrew] = value; +} + +inline Environment* Environment::New(v8::Local context) { + Environment* env = new Environment(context); + context->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, env); + return env; +} + +inline Environment* Environment::GetCurrent(v8::Isolate* isolate) { + return GetCurrent(isolate->GetCurrentContext()); +} + +inline Environment* Environment::GetCurrent(v8::Local context) { + return static_cast( + context->GetAlignedPointerFromEmbedderData(kContextEmbedderDataIndex)); +} + +inline Environment* Environment::GetCurrentChecked(v8::Isolate* isolate) { + if (isolate == NULL) { + return NULL; + } else { + return GetCurrentChecked(isolate->GetCurrentContext()); + } +} + +inline Environment* Environment::GetCurrentChecked( + v8::Local context) { + if (context.IsEmpty()) { + return NULL; + } else { + return GetCurrent(context); + } +} + +inline Environment::Environment(v8::Local context) + : isolate_(context->GetIsolate()) + , isolate_data_(IsolateData::GetOrCreate(context->GetIsolate())) + , using_smalloc_alloc_cb_(false) + , using_domains_(false) + , context_(context->GetIsolate(), context) { + // We'll be creating new objects so make sure we've entered the context. + v8::Context::Scope context_scope(context); + v8::HandleScope handle_scope(isolate()); + set_binding_cache_object(v8::Object::New()); + set_module_load_list_array(v8::Array::New()); +} + +inline Environment::~Environment() { + context()->SetAlignedPointerInEmbedderData(kContextEmbedderDataIndex, NULL); +#define V(PropertyName, TypeName) PropertyName ## _.Dispose(); + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#undef V + isolate_data()->Put(); +} + +inline void Environment::Dispose() { + delete this; +} + +inline v8::Isolate* Environment::isolate() const { + return isolate_; +} + +inline bool Environment::in_domain() const { + // The const_cast is okay, it doesn't violate conceptual const-ness. + return using_domains() && + const_cast(this)->domain_flag()->count() > 0; +} + +inline Environment* Environment::from_immediate_check_handle( + uv_check_t* handle) { + return CONTAINER_OF(handle, Environment, immediate_check_handle_); +} + +inline uv_check_t* Environment::immediate_check_handle() { + return &immediate_check_handle_; +} + +inline uv_idle_t* Environment::immediate_idle_handle() { + return &immediate_idle_handle_; +} + +inline uv_loop_t* Environment::event_loop() const { + return isolate_data()->event_loop(); +} + +inline Environment::DomainFlag* Environment::domain_flag() { + return &domain_flag_; +} + +inline Environment::TickInfo* Environment::tick_info() { + return &tick_info_; +} + +inline bool Environment::using_smalloc_alloc_cb() const { + return using_smalloc_alloc_cb_; +} + +inline void Environment::set_using_smalloc_alloc_cb(bool value) { + using_smalloc_alloc_cb_ = value; +} + +inline bool Environment::using_domains() const { + return using_domains_; +} + +inline void Environment::set_using_domains(bool value) { + using_domains_ = value; +} + +inline Environment* Environment::from_cares_timer_handle(uv_timer_t* handle) { + return CONTAINER_OF(handle, Environment, cares_timer_handle_); +} + +inline uv_timer_t* Environment::cares_timer_handle() { + return &cares_timer_handle_; +} + +inline ares_channel Environment::cares_channel() { + return cares_channel_; +} + +// Only used in the call to ares_init_options(). +inline ares_channel* Environment::cares_channel_ptr() { + return &cares_channel_; +} + +inline ares_task_list* Environment::cares_task_list() { + return &cares_task_list_; +} + +inline Environment::IsolateData* Environment::isolate_data() const { + return isolate_data_; +} + +#define V(PropertyName, StringValue) \ + inline \ + v8::Local Environment::IsolateData::PropertyName() const { \ + return v8::Local::GetEternal(isolate(), \ + PropertyName ## _index_); \ + } + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, StringValue) \ + inline v8::Local Environment::PropertyName() const { \ + return isolate_data()->PropertyName(); \ + } + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, TypeName) \ + inline v8::Local Environment::PropertyName() const { \ + return StrongPersistentToLocal(PropertyName ## _); \ + } \ + inline void Environment::set_ ## PropertyName(v8::Local value) { \ + PropertyName ## _.Reset(isolate(), value); \ + } + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#undef V + +#undef ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES +#undef PER_ISOLATE_STRING_PROPERTIES + +} // namespace node + +#endif // SRC_ENV_INL_H_ diff --git a/src/env.h b/src/env.h new file mode 100644 index 00000000000..db8d660c30f --- /dev/null +++ b/src/env.h @@ -0,0 +1,324 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SRC_ENV_H_ +#define SRC_ENV_H_ + +#include "ares.h" +#include "tree.h" +#include "util.h" +#include "uv.h" +#include "v8.h" + +#include + +// Caveat emptor: we're going slightly crazy with macros here but the end +// hopefully justifies the means. We have a lot of per-context properties +// and adding and maintaining their getters and setters by hand would be +// a nightmare so let's make the preprocessor generate them for us. +// +// Make sure that any macros defined here are undefined again at the bottom +// of context-inl.h. The sole exception is NODE_CONTEXT_EMBEDDER_DATA_INDEX, +// it may have been defined externally. +namespace node { + +// Pick an index that's hopefully out of the way when we're embedded inside +// another application. Performance-wise or memory-wise it doesn't matter: +// Context::SetAlignedPointerInEmbedderData() is backed by a FixedArray, +// worst case we pay a one-time penalty for resizing the array. +#ifndef NODE_CONTEXT_EMBEDDER_DATA_INDEX +#define NODE_CONTEXT_EMBEDDER_DATA_INDEX 32 +#endif + +// Strings are per-isolate primitives but Environment proxies them +// for the sake of convenience. +#define PER_ISOLATE_STRING_PROPERTIES(V) \ + V(DELETE_string, "DELETE") \ + V(GET_string, "GET") \ + V(HEAD_string, "HEAD") \ + V(POST_string, "POST") \ + V(PUT_string, "PUT") \ + V(address_string, "address") \ + V(atime_string, "atime") \ + V(birthtime_string, "birthtime") \ + V(blksize_string, "blksize") \ + V(blocks_string, "blocks") \ + V(buffer_string, "buffer") \ + V(bytes_string, "bytes") \ + V(callback_string, "callback") \ + V(change_string, "change") \ + V(close_string, "close") \ + V(code_string, "code") \ + V(ctime_string, "ctime") \ + V(dev_string, "dev") \ + V(disposed_string, "_disposed") \ + V(domain_string, "domain") \ + V(enter_string, "enter") \ + V(errno_string, "errno") \ + V(exit_string, "exit") \ + V(exponent_string, "exponent") \ + V(exports_string, "exports") \ + V(ext_key_usage_string, "ext_key_usage") \ + V(family_string, "family") \ + V(fatal_exception_string, "_fatalException") \ + V(fingerprint_string, "fingerprint") \ + V(gid_string, "gid") \ + V(handle_string, "handle") \ + V(headers_string, "headers") \ + V(heap_total_string, "heapTotal") \ + V(heap_used_string, "heapUsed") \ + V(immediate_callback_string, "_immediateCallback") \ + V(ino_string, "ino") \ + V(ipv4_string, "IPv4") \ + V(ipv6_string, "IPv6") \ + V(issuer_string, "issuer") \ + V(method_string, "method") \ + V(mode_string, "mode") \ + V(modulus_string, "modulus") \ + V(mtime_string, "mtime") \ + V(name_string, "name") \ + V(nlink_string, "nlink") \ + V(onchange_string, "onchange") \ + V(onclienthello_string, "onclienthello") \ + V(oncomplete_string, "oncomplete") \ + V(onconnection_string, "onconnection") \ + V(onerror_string, "onerror") \ + V(onexit_string, "onexit") \ + V(onhandshakedone_string, "onhandshakedone") \ + V(onhandshakestart_string, "onhandshakestart") \ + V(onmessage_string, "onmessage") \ + V(onnewsession_string, "onnewsession") \ + V(onread_string, "onread") \ + V(onsignal_string, "onsignal") \ + V(onstop_string, "onstop") \ + V(path_string, "path") \ + V(port_string, "port") \ + V(rdev_string, "rdev") \ + V(rename_string, "rename") \ + V(rss_string, "rss") \ + V(servername_string, "servername") \ + V(session_id_string, "sessionId") \ + V(should_keep_alive_string, "shouldKeepAlive") \ + V(size_string, "size") \ + V(smalloc_p_string, "_smalloc_p") \ + V(sni_context_string, "sni_context") \ + V(status_code_string, "statusCode") \ + V(subject_string, "subject") \ + V(subjectaltname_string, "subjectaltname") \ + V(syscall_string, "syscall") \ + V(tls_ticket_string, "tlsTicket") \ + V(uid_string, "uid") \ + V(upgrade_string, "upgrade") \ + V(url_string, "url") \ + V(valid_from_string, "valid_from") \ + V(valid_to_string, "valid_to") \ + V(version_major_string, "versionMajor") \ + V(version_minor_string, "versionMinor") \ + V(version_string, "version") \ + V(write_queue_size_string, "writeQueueSize") \ + +#define ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) \ + V(binding_cache_object, v8::Object) \ + V(buffer_constructor_function, v8::Function) \ + V(context, v8::Context) \ + V(domain_array, v8::Array) \ + V(module_load_list_array, v8::Array) \ + V(pipe_constructor_template, v8::FunctionTemplate) \ + V(process_object, v8::Object) \ + V(script_context_constructor_template, v8::FunctionTemplate) \ + V(script_data_constructor_function, v8::Function) \ + V(secure_context_constructor_template, v8::FunctionTemplate) \ + V(stats_constructor_function, v8::Function) \ + V(tcp_constructor_template, v8::FunctionTemplate) \ + V(tick_callback_function, v8::Function) \ + V(tls_wrap_constructor_function, v8::Function) \ + V(tty_constructor_template, v8::FunctionTemplate) \ + V(udp_constructor_function, v8::Function) \ + +class Environment; + +// TODO(bnoordhuis) Rename struct, the ares_ prefix implies it's part +// of the c-ares API while the _t suffix implies it's a typedef. +struct ares_task_t { + Environment* env; + ares_socket_t sock; + uv_poll_t poll_watcher; + RB_ENTRY(ares_task_t) node; +}; + +RB_HEAD(ares_task_list, ares_task_t); + +class Environment { + public: + class DomainFlag { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline uint32_t count() const; + + private: + friend class Environment; // So we can call the constructor. + inline DomainFlag(); + + enum Fields { + kCount, + kFieldsCount + }; + + uint32_t fields_[kFieldsCount]; + + DISALLOW_COPY_AND_ASSIGN(DomainFlag); + }; + + class TickInfo { + public: + inline uint32_t* fields(); + inline int fields_count() const; + inline uint32_t in_tick() const; + inline uint32_t index() const; + inline uint32_t last_threw() const; + inline uint32_t length() const; + inline void set_index(uint32_t value); + inline void set_last_threw(uint32_t value); + + private: + friend class Environment; // So we can call the constructor. + inline TickInfo(); + + enum Fields { + kInTick, + kIndex, + kLastThrew, + kLength, + kFieldsCount + }; + + uint32_t fields_[kFieldsCount]; + + DISALLOW_COPY_AND_ASSIGN(TickInfo); + }; + + static inline Environment* GetCurrent(v8::Isolate* isolate); + static inline Environment* GetCurrent(v8::Local context); + static inline Environment* GetCurrentChecked(v8::Isolate* isolate); + static inline Environment* GetCurrentChecked(v8::Local context); + + // See CreateEnvironment() in src/node.cc. + static inline Environment* New(v8::Local context); + inline void Dispose(); + + inline v8::Isolate* isolate() const; + inline uv_loop_t* event_loop() const; + inline bool in_domain() const; + + static inline Environment* from_immediate_check_handle(uv_check_t* handle); + inline uv_check_t* immediate_check_handle(); + inline uv_idle_t* immediate_idle_handle(); + inline DomainFlag* domain_flag(); + inline TickInfo* tick_info(); + + static inline Environment* from_cares_timer_handle(uv_timer_t* handle); + inline uv_timer_t* cares_timer_handle(); + inline ares_channel cares_channel(); + inline ares_channel* cares_channel_ptr(); + inline ares_task_list* cares_task_list(); + + inline bool using_smalloc_alloc_cb() const; + inline void set_using_smalloc_alloc_cb(bool value); + + inline bool using_domains() const; + inline void set_using_domains(bool value); + + // Strings are shared across shared contexts. The getters simply proxy to + // the per-isolate primitive. +#define V(PropertyName, StringValue) \ + inline v8::Local PropertyName() const; + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, TypeName) \ + inline v8::Local PropertyName() const; \ + inline void set_ ## PropertyName(v8::Local value); + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#undef V + + private: + class IsolateData; + inline explicit Environment(v8::Local context); + inline ~Environment(); + inline IsolateData* isolate_data() const; + + enum ContextEmbedderDataIndex { + kContextEmbedderDataIndex = NODE_CONTEXT_EMBEDDER_DATA_INDEX + }; + + v8::Isolate* const isolate_; + IsolateData* const isolate_data_; + uv_check_t immediate_check_handle_; + uv_idle_t immediate_idle_handle_; + DomainFlag domain_flag_; + TickInfo tick_info_; + uv_timer_t cares_timer_handle_; + ares_channel cares_channel_; + ares_task_list cares_task_list_; + bool using_smalloc_alloc_cb_; + bool using_domains_; + +#define V(PropertyName, TypeName) \ + v8::Persistent PropertyName ## _; + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#undef V + + // Per-thread, reference-counted singleton. + class IsolateData { + public: + static inline IsolateData* GetOrCreate(v8::Isolate* isolate); + inline void Put(); + inline uv_loop_t* event_loop() const; + +#define V(PropertyName, StringValue) \ + inline v8::Local PropertyName() const; + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + + private: + inline explicit IsolateData(v8::Isolate* isolate); + inline v8::Isolate* isolate() const; + + uv_loop_t* const event_loop_; + v8::Isolate* const isolate_; + +#define V(PropertyName, StringValue) \ + const int PropertyName ## _index_; + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + + unsigned int ref_count_; + + DISALLOW_COPY_AND_ASSIGN(IsolateData); + }; + + DISALLOW_COPY_AND_ASSIGN(Environment); +}; + +} // namespace node + +#endif // SRC_ENV_H_ diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 8906f43ee32..719ee8c7eb0 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -19,6 +19,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. +#include "env.h" +#include "env-inl.h" #include "node.h" #include "handle_wrap.h" @@ -26,6 +28,7 @@ namespace node { +using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; @@ -36,19 +39,17 @@ using v8::Object; using v8::String; using v8::Value; -static Cached change_sym; -static Cached onchange_sym; -static Cached rename_sym; - class FSEventWrap: public HandleWrap { public: - static void Initialize(Handle target); + static void Initialize(Handle target, + Handle unused, + Handle context); static void New(const FunctionCallbackInfo& args); static void Start(const FunctionCallbackInfo& args); static void Close(const FunctionCallbackInfo& args); private: - explicit FSEventWrap(Handle object); + FSEventWrap(Environment* env, Handle object); virtual ~FSEventWrap(); static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, @@ -59,8 +60,8 @@ class FSEventWrap: public HandleWrap { }; -FSEventWrap::FSEventWrap(Handle object) - : HandleWrap(object, reinterpret_cast(&handle_)) { +FSEventWrap::FSEventWrap(Environment* env, Handle object) + : HandleWrap(env, object, reinterpret_cast(&handle_)) { initialized_ = false; } @@ -70,8 +71,11 @@ FSEventWrap::~FSEventWrap() { } -void FSEventWrap::Initialize(Handle target) { - HandleScope scope(node_isolate); +void FSEventWrap::Initialize(Handle target, + Handle unused, + Handle context) { + Environment* env = Environment::GetCurrent(context); + HandleScope handle_scope(env->isolate()); Local t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -81,17 +85,13 @@ void FSEventWrap::Initialize(Handle target) { NODE_SET_PROTOTYPE_METHOD(t, "close", Close); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "FSEvent"), t->GetFunction()); - - change_sym = FIXED_ONE_BYTE_STRING(node_isolate, "change"); - onchange_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onchange"); - rename_sym = FIXED_ONE_BYTE_STRING(node_isolate, "rename"); } void FSEventWrap::New(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); assert(args.IsConstructCall()); - new FSEventWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + new FSEventWrap(env, args.This()); } @@ -107,7 +107,7 @@ void FSEventWrap::Start(const FunctionCallbackInfo& args) { String::Utf8Value path(args[0]); - int err = uv_fs_event_init(uv_default_loop(), + int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_, *path, OnEvent, @@ -126,10 +126,11 @@ void FSEventWrap::Start(const FunctionCallbackInfo& args) { void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, int events, int status) { - HandleScope scope(node_isolate); - Handle eventStr; - FSEventWrap* wrap = static_cast(handle->data); + Environment* env = wrap->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); assert(wrap->persistent().IsEmpty() == false); @@ -144,20 +145,21 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the // assumption that a rename implicitly means an attribute change. Not too // unreasonable, right? Still, we should revisit this before v1.0. + Local event_string; if (status) { - eventStr = String::Empty(node_isolate); + event_string = String::Empty(node_isolate); } else if (events & UV_RENAME) { - eventStr = rename_sym; + event_string = env->rename_string(); } else if (events & UV_CHANGE) { - eventStr = change_sym; + event_string = env->change_string(); } else { assert(0 && "bad fs events flag"); abort(); } - Handle argv[3] = { + Local argv[] = { Integer::New(status, node_isolate), - eventStr, + event_string, Null(node_isolate) }; @@ -165,7 +167,11 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, argv[2] = OneByteString(node_isolate, filename); } - MakeCallback(wrap->object(), onchange_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + wrap->object(), + env->onchange_string(), + ARRAY_SIZE(argv), + argv); } @@ -183,4 +189,4 @@ void FSEventWrap::Close(const FunctionCallbackInfo& args) { } // namespace node -NODE_MODULE(node_fs_event_wrap, node::FSEventWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_fs_event_wrap, node::FSEventWrap::Initialize) diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 858b55cdd4e..05d4c4dddc7 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -20,22 +20,23 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "handle_wrap.h" +#include "env.h" +#include "env-inl.h" #include "node.h" #include "queue.h" namespace node { +using v8::Context; using v8::FunctionCallbackInfo; using v8::Handle; using v8::HandleScope; using v8::Local; using v8::Object; -using v8::String; using v8::Value; // defined in node.cc extern QUEUE handle_wrap_queue; -static Cached close_sym; void HandleWrap::Ref(const FunctionCallbackInfo& args) { @@ -73,27 +74,26 @@ void HandleWrap::Close(const FunctionCallbackInfo& args) { // guard against uninitialized handle or double close if (wrap == NULL || wrap->handle__ == NULL) return; + Environment* env = wrap->env(); assert(!wrap->persistent().IsEmpty()); uv_close(wrap->handle__, OnClose); wrap->handle__ = NULL; if (args[0]->IsFunction()) { - if (close_sym.IsEmpty() == true) { - close_sym = FIXED_ONE_BYTE_STRING(node_isolate, "close"); - } - wrap->object()->Set(close_sym, args[0]); + wrap->object()->Set(env->close_string(), args[0]); wrap->flags_ |= kCloseCallback; } } -HandleWrap::HandleWrap(Handle object, uv_handle_t* h) { - flags_ = 0; - handle__ = h; +HandleWrap::HandleWrap(Environment* env, + Handle object, + uv_handle_t* handle) + : env_(env) + , flags_(0) + , handle__(handle) { handle__->data = this; - HandleScope scope(node_isolate); - assert(persistent().IsEmpty()); persistent().Reset(node_isolate, object); NODE_WRAP(object, this); QUEUE_INSERT_TAIL(&handle_wrap_queue, &handle_wrap_queue_); @@ -108,6 +108,7 @@ HandleWrap::~HandleWrap() { void HandleWrap::OnClose(uv_handle_t* handle) { HandleWrap* wrap = static_cast(handle->data); + Environment* env = wrap->env(); // The wrap object should still be there. assert(wrap->persistent().IsEmpty() == false); @@ -115,12 +116,12 @@ void HandleWrap::OnClose(uv_handle_t* handle) { // But the handle pointer should be gone. assert(wrap->handle__ == NULL); - HandleScope scope(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local object = wrap->object(); if (wrap->flags_ & kCloseCallback) { - assert(close_sym.IsEmpty() == false); - MakeCallback(object, close_sym, 0, NULL); + MakeCallback(env, object, env->close_string()); } object->SetAlignedPointerInInternalField(0, NULL); diff --git a/src/handle_wrap.h b/src/handle_wrap.h index b5918f5dfe9..73a43c3a3f2 100644 --- a/src/handle_wrap.h +++ b/src/handle_wrap.h @@ -22,6 +22,7 @@ #ifndef SRC_HANDLE_WRAP_H_ #define SRC_HANDLE_WRAP_H_ +#include "env.h" #include "node.h" #include "queue.h" #include "uv.h" @@ -58,11 +59,17 @@ class HandleWrap { inline uv_handle_t* GetHandle() { return handle__; } protected: - explicit HandleWrap(v8::Handle object, uv_handle_t* handle); + HandleWrap(Environment* env, + v8::Handle object, + uv_handle_t* handle); virtual ~HandleWrap(); + inline Environment* env() const { + return env_; + } + inline v8::Local object() { - return PersistentToLocal(node_isolate, persistent()); + return PersistentToLocal(env()->isolate(), persistent()); } inline v8::Persistent& persistent() { @@ -74,10 +81,11 @@ class HandleWrap { static void OnClose(uv_handle_t* handle); v8::Persistent object_; QUEUE handle_wrap_queue_; + Environment* const env_; + unsigned int flags_; // Using double underscore due to handle_ member in tcp_wrap. Probably // tcp_wrap should rename it's member to 'handle'. uv_handle_t* handle__; - unsigned int flags_; static const unsigned int kUnref = 1; static const unsigned int kCloseCallback = 2; diff --git a/src/node.cc b/src/node.cc index c88b0effbac..4670151a8a3 100644 --- a/src/node.cc +++ b/src/node.cc @@ -44,6 +44,8 @@ #endif #include "ares.h" +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" #include "req_wrap.h" #include "string_bytes.h" @@ -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,29 +129,10 @@ 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; // 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; @@ -227,24 +174,15 @@ void ArrayBufferAllocator::Free(void* 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 +667,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 +680,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 +693,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 +713,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 +754,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 +799,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,91 +820,92 @@ 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 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); + + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"), + tick_callback_function); + env->set_tick_callback_function(tick_callback_function); + if (!args[0]->IsArray()) { fprintf(stderr, "_setupDomainUse first argument must be an array\n"); abort(); } - p_domain_box.Reset(node_isolate, args[0].As()); + env->set_domain_array(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); + + 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()); } -bool InDomain() { - return using_domains && domain_flag.count > 0; -} +Handle MakeDomainCallback(Environment* env, + const Handle object, + 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()); - -Handle GetDomain() { - // no domain can exist if no domain module has been loaded - if (!InDomain() || p_domain_box.IsEmpty()) - return Null(node_isolate); - - return PersistentToLocal(node_isolate, p_domain_box)->Get(0); -} - - -Handle -MakeDomainCallback(const Handle object, - const Handle callback, - int argc, - Handle argv[]) { // TODO(trevnorris) Hook for long stack traces to be made here. - // 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"); - } - - Local domain_v = object->Get(domain_symbol); + Local domain_v = object->Get(env->domain_string()); Local domain; - Local enter; - Local exit; 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 = domain_v.As(); + + if (domain->Get(env->disposed_string())->IsTrue()) { // domain has been disposed of. return Undefined(node_isolate); } - enter = Local::Cast(domain->Get(enter_symbol)); - assert(!enter.IsEmpty()); + + Local enter = + domain->Get(env->enter_string()).As(); + assert(enter->IsFunction()); enter->Call(domain, 0, NULL); if (try_catch.HasCaught()) { @@ -984,8 +920,9 @@ MakeDomainCallback(const Handle object, } 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,24 +930,26 @@ MakeDomainCallback(const Handle object, } } - if (tick_infobox.last_threw == 1) { - tick_infobox.last_threw = 0; + 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() == 1) { 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); + Local process_object = env->process_object(); + Local tick_callback_function = env->tick_callback_function(); + tick_callback_function->Call(process_object, 0, NULL); if (try_catch.HasCaught()) { return Undefined(node_isolate); @@ -1020,27 +959,19 @@ MakeDomainCallback(const Handle object, } -Handle -MakeCallback(const Handle object, - const Handle callback, - int argc, - Handle argv[]) { +Handle MakeCallback(Environment* env, + const Handle object, + 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()); + // TODO(trevnorris) Hook for long stack traces to be made here. - Local process = PersistentToLocal(node_isolate, process_p); + Local process_object = env->process_object(); - if (using_domains) - return MakeDomainCallback(object, callback, argc, argv); - - // 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()); - } + if (env->using_domains()) + return MakeDomainCallback(env, object, callback, argc, argv); TryCatch try_catch; try_catch.SetVerbose(true); @@ -1051,18 +982,33 @@ MakeCallback(const Handle object, return Undefined(node_isolate); } - if (tick_infobox.in_tick == 1) { + Environment::TickInfo* tick_info = env->tick_info(); + + if (tick_info->in_tick() == 1) { return ret; } - if (tick_infobox.length == 0) { - tick_infobox.index = 0; + if (tick_info->length() == 0) { + tick_info->set_index(0); return ret; } + // lazy load no domain next tick callbacks + Local tick_callback_function = env->tick_callback_function(); + if (tick_callback_function.IsEmpty()) { + Local tick_callback_function_key = + FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"); + tick_callback_function = + process_object->Get(tick_callback_function_key).As(); + if (!tick_callback_function->IsFunction()) { + fprintf(stderr, "process._tickCallback assigned to non-function\n"); + abort(); + } + env->set_tick_callback_function(tick_callback_function); + } + // process nextTicks after call - Local fn = PersistentToLocal(node_isolate, process_tickCallback); - fn->Call(process, 0, NULL); + tick_callback_function->Call(process_object, 0, NULL); if (try_catch.HasCaught()) { return Undefined(node_isolate); @@ -1073,49 +1019,102 @@ 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, + const Handle object, + uint32_t index, + int argc, + Handle argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); Local callback = object->Get(index).As(); assert(callback->IsFunction()); - if (using_domains) - return scope.Close(MakeDomainCallback(object, callback, argc, argv)); - return scope.Close(MakeCallback(object, callback, argc, argv)); + if (env->using_domains()) { + return MakeDomainCallback(env, object, callback, argc, argv); + } + + return MakeCallback(env, object, callback, argc, argv); } -Handle -MakeCallback(const Handle object, - const Handle symbol, - int argc, - Handle argv[]) { - HandleScope scope(node_isolate); +Handle MakeCallback(Environment* env, + const Handle object, + const Handle symbol, + int argc, + Handle argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); 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)); + if (env->using_domains()) { + return MakeDomainCallback(env, object, callback, argc, argv); + } + + return MakeCallback(env, object, callback, argc, argv); } -Handle -MakeCallback(const Handle object, - const char* method, - int argc, - Handle argv[]) { - HandleScope scope(node_isolate); - +Handle MakeCallback(Environment* env, + const Handle object, + const char* method, + int argc, + Handle argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); Local method_string = OneByteString(node_isolate, method); - Handle ret = MakeCallback(object, method_string, argc, argv); + return MakeCallback(env, object, method_string, argc, argv); +} - return scope.Close(ret); + +Handle MakeCallback(const Handle object, + const char* method, + int argc, + Handle argv[]) { + Local context = object->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close(MakeCallback(env, object, method, argc, argv)); +} + + +Handle MakeCallback(const Handle object, + const Handle symbol, + int argc, + Handle argv[]) { + Local context = object->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close(MakeCallback(env, object, symbol, argc, argv)); +} + + +Handle MakeCallback(const Handle object, + const Handle callback, + int argc, + Handle argv[]) { + Local context = object->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close(MakeCallback(env, object, callback, argc, argv)); +} + + +Handle MakeDomainCallback(const Handle object, + const Handle callback, + int argc, + Handle argv[]) { + Local context = object->CreationContext(); + Environment* env = Environment::GetCurrent(context); + Context::Scope context_scope(context); + HandleScope handle_scope(env->isolate()); + return handle_scope.Close( + MakeDomainCallback(env, object, callback, argc, argv)); } @@ -1730,34 +1729,28 @@ static void Uptime(const FunctionCallbackInfo& args) { void MemoryUsage(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(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); } @@ -1811,8 +1804,13 @@ 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); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); char symbol[1024], *base, *pos; uv_lib_t lib; int r; @@ -1824,13 +1822,11 @@ void DLOpen(const FunctionCallbackInfo& args) { 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()); @@ -1894,7 +1890,13 @@ void DLOpen(const FunctionCallbackInfo& args) { } // Execute the C++ module - mod->register_func(exports, module); + if (mod->register_context_func != NULL) { + mod->register_context_func(exports, module, env->context()); + } else if (mod->register_func != NULL) { + mod->register_func(exports, module); + } else { + return ThrowError("Module has no declared entry point."); + } // Tell coverity that 'handle' should not be freed when we return. // coverity[leaked_storage] @@ -1925,30 +1927,27 @@ 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"); - } + 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(); - Local process = PersistentToLocal(node_isolate, process_p); - Local fatal_v = process->Get(fatal_exception_symbol); - - 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); 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 @@ -1979,13 +1978,13 @@ void OnMessage(Handle message, Handle error) { static void Binding(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(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 +1997,18 @@ 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_struct* 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->register_func == NULL); + assert(mod->register_context_func != NULL); + Local unused = Undefined(env->isolate()); + mod->register_context_func(exports, unused, env->context()); cache->Set(module, exports); } else if (!strcmp(*module_v, "constants")) { exports = Object::New(); @@ -2254,28 +2256,37 @@ static void DebugEnd(const FunctionCallbackInfo& args); void NeedImmediateCallbackGetter(Local property, const PropertyCallbackInfo& info) { - info.GetReturnValue().Set(need_immediate_cb); + 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) { + Environment* env = Environment::GetCurrent(info.GetIsolate()); + HandleScope handle_scope(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); } } @@ -2286,23 +2297,15 @@ static void NeedImmediateCallbackSetter(Local property, } 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 +2317,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(); @@ -2367,8 +2370,6 @@ Handle SetupProcessObject(int argc, OneByteString(node_isolate, &OPENSSL_VERSION_TEXT[i], j - i)); #endif - - // process.arch READONLY_PROPERTY(process, "arch", OneByteString(node_isolate, ARCH)); @@ -2392,15 +2393,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()); @@ -2500,16 +2501,15 @@ Handle SetupProcessObject(int argc, 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 +2543,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.) @@ -2581,7 +2578,7 @@ 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 InitDTrace(global); @@ -2599,9 +2596,9 @@ 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); } @@ -2797,15 +2794,17 @@ static void DispatchMessagesDebugAgentCallback() { // 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")); + Environment* env = Environment::GetCurrent(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + Local message = Object::New(); + message->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 + message }; - MakeCallback(process_p, "emit", ARRAY_SIZE(args), args); + MakeCallback(env, env->process_object(), "emit", ARRAY_SIZE(args), args); } @@ -2837,10 +2836,9 @@ static void EnableDebug(bool wait_connect) { debugger_running = true; - // 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()) + if (Environment::GetCurrentChecked(node_isolate) != NULL) { EmitDebugEnabled(); + } node_isolate->Exit(); } @@ -3058,12 +3056,14 @@ 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 + // FIXME(bnoordhuis) Should be per-isolate or per-context, not global. uv_async_init(uv_default_loop(), &emit_debug_enabled_async, EmitDebugEnabledAsyncCallback); @@ -3109,10 +3109,6 @@ void Init(int* argc, 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); @@ -3123,6 +3119,7 @@ void Init(int* argc, #ifdef _WIN32 RegisterDebugSignalHandler(); #else // Posix + // FIXME(bnoordhuis) Should be per-isolate or per-context, not global. static uv_signal_t signal_watcher; uv_signal_init(uv_default_loop(), &signal_watcher); uv_signal_start(&signal_watcher, EnableDebugSignalHandler, SIGUSR1); @@ -3141,7 +3138,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,15 +3161,47 @@ void AtExit(void (*cb)(void* arg), void* arg) { } -void EmitExit(v8::Handle process_l) { +void EmitExit(Environment* env) { // process.emit('exit') - process_l->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"), - True(node_isolate)); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + Local process_object = env->process_object(); + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"), + True(node_isolate)); Local args[] = { FIXED_ONE_BYTE_STRING(node_isolate, "exit"), Integer::New(0, node_isolate) }; - MakeCallback(process_l, "emit", ARRAY_SIZE(args), args); + MakeCallback(env, process_object, "emit", ARRAY_SIZE(args), args); +} + + +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()); + + 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; } @@ -3190,31 +3220,15 @@ int Start(int argc, char** argv) { 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); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + uv_run(env->event_loop(), UV_RUN_DEFAULT); + EmitExit(env); + RunAtExit(env); + env->Dispose(); + env = NULL; } #ifndef NDEBUG diff --git a/src/node.h b/src/node.h index 527ac25a357..00f60d3fb17 100644 --- a/src/node.h +++ b/src/node.h @@ -58,6 +58,7 @@ # define SIGKILL 9 #endif +#include "v8.h" // NOLINT(build/include_order) #include "node_version.h" // NODE_MODULE_VERSION #include "node_object_wrap.h" @@ -95,9 +96,6 @@ NODE_EXTERN v8::Handle MakeCallback( #include "node_internals.h" #endif -#include "uv.h" -#include "v8.h" - #include #ifndef NODE_STRINGIFY @@ -185,9 +183,6 @@ NODE_EXTERN ssize_t DecodeWrite(char *buf, v8::Handle, enum encoding encoding = BINARY); -v8::Local BuildStatsObject(const uv_stat_t* s); - - #ifdef _WIN32 NODE_EXTERN v8::Local WinapiErrnoException(int errorno, const char *syscall = NULL, const char *msg = "", @@ -197,14 +192,21 @@ NODE_EXTERN v8::Local WinapiErrnoException(int errorno, const char *signo_string(int errorno); -NODE_EXTERN typedef void (* addon_register_func)( - v8::Handle exports, v8::Handle module); +NODE_EXTERN typedef void (*addon_register_func)( + v8::Handle exports, + v8::Handle module); + +NODE_EXTERN typedef void (*addon_context_register_func)( + v8::Handle exports, + v8::Handle module, + v8::Handle context); struct node_module_struct { int version; void *dso_handle; const char *filename; node::addon_register_func register_func; + node::addon_context_register_func register_context_func; const char *modname; }; @@ -226,7 +228,19 @@ node_module_struct* get_builtin_module(const char *name); NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \ { \ NODE_STANDARD_MODULE_STUFF, \ - (node::addon_register_func)regfunc, \ + (node::addon_register_func) (regfunc), \ + NULL, \ + NODE_STRINGIFY(modname) \ + }; \ + } + +#define NODE_MODULE_CONTEXT_AWARE(modname, regfunc) \ + extern "C" { \ + NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \ + { \ + NODE_STANDARD_MODULE_STUFF, \ + NULL, \ + (regfunc), \ NODE_STRINGIFY(modname) \ }; \ } diff --git a/src/node.js b/src/node.js index 3c7ede82b86..9ba1b3b4a0f 100644 --- a/src/node.js +++ b/src/node.js @@ -273,16 +273,15 @@ startup.processNextTick = function() { var nextTickQueue = []; - // this infoBox thing is used so that the C++ code in src/node.cc - // can have easy access to our nextTick state, and avoid unnecessary - // calls into process._tickCallback. - // order is [length, index, inTick, lastThrew] - // Never write code like this without very good reason! - var infoBox = process._tickInfoBox; - var length = 0; - var index = 1; - var inTick = 2; - var lastThrew = 3; + // This tickInfo thing is used so that the C++ code in src/node.cc + // can have easy accesss to our nextTick state, and avoid unnecessary + var tickInfo = process._tickInfo; + + // *Must* match Environment::TickInfo::Fields in src/env.h. + var kInTick = 0; + var kIndex = 1; + var kLastThrew = 2; + var kLength = 3; process.nextTick = nextTick; // needs to be accessible from cc land @@ -290,17 +289,17 @@ process._tickDomainCallback = _tickDomainCallback; function tickDone() { - if (infoBox[length] !== 0) { - if (infoBox[length] <= infoBox[index]) { + if (tickInfo[kLength] !== 0) { + if (tickInfo[kLength] <= tickInfo[kIndex]) { nextTickQueue = []; - infoBox[length] = 0; + tickInfo[kLength] = 0; } else { - nextTickQueue.splice(0, infoBox[index]); - infoBox[length] = nextTickQueue.length; + nextTickQueue.splice(0, tickInfo[kIndex]); + tickInfo[kLength] = nextTickQueue.length; } } - infoBox[inTick] = 0; - infoBox[index] = 0; + tickInfo[kInTick] = 0; + tickInfo[kIndex] = 0; } // run callbacks that have no domain @@ -308,10 +307,10 @@ function _tickCallback() { var callback, threw; - infoBox[inTick] = 1; + tickInfo[kInTick] = 1; - while (infoBox[index] < infoBox[length]) { - callback = nextTickQueue[infoBox[index]++].callback; + while (tickInfo[kIndex] < tickInfo[kLength]) { + callback = nextTickQueue[tickInfo[kIndex]++].callback; threw = true; try { callback(); @@ -327,22 +326,22 @@ function _tickDomainCallback() { var tock, callback, domain; - infoBox[inTick] = 1; + tickInfo[kInTick] = 1; - while (infoBox[index] < infoBox[length]) { - tock = nextTickQueue[infoBox[index]++]; + while (tickInfo[kIndex] < tickInfo[kLength]) { + tock = nextTickQueue[tickInfo[kIndex]++]; callback = tock.callback; domain = tock.domain; if (domain) { if (domain._disposed) continue; domain.enter(); } - infoBox[lastThrew] = 1; + tickInfo[kLastThrew] = 1; try { callback(); - infoBox[lastThrew] = 0; + tickInfo[kLastThrew] = 0; } finally { - if (infoBox[lastThrew] === 1) tickDone(); + if (tickInfo[kLastThrew] === 1) tickDone(); } if (domain) domain.exit(); @@ -360,7 +359,7 @@ callback: callback, domain: process.domain || null }); - infoBox[length]++; + tickInfo[kLength]++; } }; diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 519fddf4c1b..2e7374971c1 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -22,11 +22,13 @@ #include "node.h" #include "node_buffer.h" + +#include "env.h" +#include "env-inl.h" #include "smalloc.h" #include "string_bytes.h" - -#include "v8.h" #include "v8-profiler.h" +#include "v8.h" #include #include @@ -55,6 +57,7 @@ namespace node { namespace Buffer { +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -63,13 +66,10 @@ using v8::HandleScope; using v8::Local; using v8::Number; using v8::Object; -using v8::Persistent; using v8::String; using v8::Uint32; using v8::Value; -static Persistent p_buffer_fn; - bool HasInstance(Handle val) { return val->IsObject() && HasInstance(val.As()); @@ -124,12 +124,20 @@ Local New(Handle string, enum encoding enc) { Local New(size_t length) { + Environment* env = Environment::GetCurrent(node_isolate); + return Buffer::New(env, length); +} + + +// TODO(trevnorris): these have a flaw by needing to call the Buffer inst then +// Alloc. continue to look for a better architecture. +Local New(Environment* env, size_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local arg = Uint32::NewFromUnsigned(length, node_isolate); - Local obj = NewInstance(p_buffer_fn, 1, &arg); + Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); // TODO(trevnorris): done like this to handle HasInstance since only checks // if external array data has been set, but would like to use a better @@ -148,16 +156,22 @@ Local New(size_t length) { } +Local New(const char* data, size_t length) { + Environment* env = Environment::GetCurrent(node_isolate); + return Buffer::New(env, data, length); +} + + // TODO(trevnorris): for backwards compatibility this is left to copy the data, // but for consistency w/ the other should use data. And a copy version renamed // to something else. -Local New(const char* data, size_t length) { +Local New(Environment* env, const char* data, size_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local arg = Uint32::NewFromUnsigned(length, node_isolate); - Local obj = NewInstance(p_buffer_fn, 1, &arg); + Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); // TODO(trevnorris): done like this to handle HasInstance since only checks // if external array data has been set, but would like to use a better @@ -182,12 +196,22 @@ Local New(char* data, size_t length, smalloc::FreeCallback callback, void* hint) { + Environment* env = Environment::GetCurrent(node_isolate); + return Buffer::New(env, data, length, callback, hint); +} + + +Local New(Environment* env, + char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local arg = Uint32::NewFromUnsigned(length, node_isolate); - Local obj = NewInstance(p_buffer_fn, 1, &arg); + Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); smalloc::Alloc(obj, data, length, callback, hint); @@ -196,12 +220,18 @@ Local New(char* data, Local Use(char* data, uint32_t length) { + Environment* env = Environment::GetCurrent(node_isolate); + return Buffer::Use(env, data, length); +} + + +Local Use(Environment* env, char* data, uint32_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local arg = Uint32::NewFromUnsigned(length, node_isolate); - Local obj = NewInstance(p_buffer_fn, 1, &arg); + Local obj = env->buffer_constructor_function()->NewInstance(1, &arg); smalloc::Alloc(obj, data, length); @@ -534,12 +564,13 @@ void ByteLength(const FunctionCallbackInfo &args) { // pass Buffer object to load prototype methods void SetupBufferJS(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); assert(args[0]->IsFunction()); Local bv = args[0].As(); - p_buffer_fn.Reset(node_isolate, bv); + env->set_buffer_constructor_function(bv); Local proto_v = bv->Get(FIXED_ONE_BYTE_STRING(node_isolate, "prototype")); @@ -588,10 +619,12 @@ void SetupBufferJS(const FunctionCallbackInfo& args) { } -void Initialize(Handle target) { - HandleScope scope(node_isolate); - - target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "setupBufferJS"), +void Initialize(Handle target, + Handle unused, + Handle context) { + Environment* env = Environment::GetCurrent(context); + HandleScope handle_scope(env->isolate()); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "setupBufferJS"), FunctionTemplate::New(SetupBufferJS)->GetFunction()); } @@ -599,4 +632,4 @@ void Initialize(Handle target) { } // namespace Buffer } // namespace node -NODE_MODULE(node_buffer, node::Buffer::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_buffer, node::Buffer::Initialize) diff --git a/src/node_buffer.h b/src/node_buffer.h index ca9a135e9df..78e6e42f6cb 100644 --- a/src/node_buffer.h +++ b/src/node_buffer.h @@ -26,6 +26,10 @@ #include "smalloc.h" #include "v8.h" +#if defined(NODE_WANT_INTERNALS) +#include "env.h" +#endif // defined(NODE_WANT_INTERNALS) + namespace node { namespace Buffer { @@ -56,6 +60,20 @@ NODE_EXTERN v8::Local New(char* data, // TODO(trevnorris): should be New() for consistency NODE_EXTERN v8::Local Use(char* data, uint32_t len); +// Internal. Not for public consumption. We can't define these in +// src/node_internals.h due to a circular dependency issue with +// the smalloc.h and node_internals.h headers. +#if defined(NODE_WANT_INTERNALS) +v8::Local New(Environment* env, size_t size); +v8::Local New(Environment* env, const char* data, size_t len); +v8::Local New(Environment* env, + char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint); +v8::Local Use(Environment* env, char* data, uint32_t length); +#endif // defined(NODE_WANT_INTERNALS) + } // namespace Buffer } // namespace node diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 331dbdb6e99..132fa911dea 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -22,6 +22,8 @@ #include "node.h" #include "node_internals.h" #include "node_watchdog.h" +#include "env.h" +#include "env-inl.h" namespace node { @@ -33,6 +35,7 @@ using v8::External; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; +using v8::Handle; using v8::HandleScope; using v8::Integer; using v8::Isolate; @@ -51,18 +54,17 @@ using v8::Value; class ContextifyContext { private: + Environment* const env_; Persistent sandbox_; - Persistent proxy_global_; Persistent context_; - static Persistent data_wrapper_ctor; + Persistent proxy_global_; public: - explicit ContextifyContext(Local sandbox) : - sandbox_(node_isolate, sandbox) { - HandleScope scope(node_isolate); - Local v8_context = CreateV8Context(); - context_.Reset(node_isolate, v8_context); - proxy_global_.Reset(node_isolate, v8_context->Global()); + explicit ContextifyContext(Environment* env, Local sandbox) + : env_(env) + , sandbox_(env->isolate(), sandbox) + , context_(env->isolate(), CreateV8Context(env)) + , proxy_global_(env->isolate(), context()->Global()) { sandbox_.MakeWeak(this, SandboxFreeCallback); sandbox_.MarkIndependent(); } @@ -75,21 +77,31 @@ class ContextifyContext { } + inline Environment* env() const { + return env_; + } + + + inline Local context() const { + return PersistentToLocal(env()->isolate(), context_); + } + + // This is an object that just keeps an internal pointer to this // ContextifyContext. It's passed to the NamedPropertyHandler. If we // pass the main JavaScript context object we're embedded in, then the // NamedPropertyHandler will store a reference to it forever and keep it // from getting gc'd. - Local CreateDataWrapper() { + Local CreateDataWrapper(Environment* env) { HandleScope scope(node_isolate); - Local ctor = PersistentToLocal(node_isolate, data_wrapper_ctor); - Local wrapper = ctor->NewInstance(); + Local wrapper = + env->script_data_constructor_function()->NewInstance(); NODE_WRAP(wrapper, this); return scope.Close(wrapper); } - Local CreateV8Context() { + Local CreateV8Context(Environment* env) { HandleScope scope(node_isolate); Local function_template = FunctionTemplate::New(); function_template->SetHiddenPrototype(true); @@ -104,19 +116,17 @@ class ContextifyContext { GlobalPropertyQueryCallback, GlobalPropertyDeleterCallback, GlobalPropertyEnumeratorCallback, - CreateDataWrapper()); + CreateDataWrapper(env)); object_template->SetAccessCheckCallbacks(GlobalPropertyNamedAccessCheck, GlobalPropertyIndexedAccessCheck); return scope.Close(Context::New(node_isolate, NULL, object_template)); } - static void Init(Local target) { - HandleScope scope(node_isolate); - + static void Init(Environment* env, Local target) { Local function_template = FunctionTemplate::New(); function_template->InstanceTemplate()->SetInternalFieldCount(1); - data_wrapper_ctor.Reset(node_isolate, function_template->GetFunction()); + env->set_script_data_constructor_function(function_template->GetFunction()); NODE_SET_METHOD(target, "makeContext", MakeContext); NODE_SET_METHOD(target, "isContext", IsContext); @@ -124,7 +134,8 @@ class ContextifyContext { static void MakeContext(const FunctionCallbackInfo& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); if (!args[0]->IsObject()) { return ThrowTypeError("sandbox argument must be an object."); @@ -137,7 +148,7 @@ class ContextifyContext { // Don't allow contextifying a sandbox multiple times. assert(sandbox->GetHiddenValue(hidden_name).IsEmpty()); - ContextifyContext* context = new ContextifyContext(sandbox); + ContextifyContext* context = new ContextifyContext(env, sandbox); Local hidden_context = External::New(context); sandbox->SetHiddenValue(hidden_name, hidden_context); } @@ -179,18 +190,6 @@ class ContextifyContext { return static_cast(context_external->Value()); } - static Local V8ContextFromContextifiedSandbox( - const Local& sandbox) { - ContextifyContext* contextify_context = - ContextFromContextifiedSandbox(sandbox); - if (contextify_context == NULL) { - ThrowTypeError("sandbox argument must have been converted to a context."); - return Local(); - } - - return PersistentToLocal(node_isolate, contextify_context->context_); - } - static bool GlobalPropertyNamedAccessCheck(Local host, Local key, @@ -300,24 +299,21 @@ class ContextifyScript : ObjectWrap { Persistent