From 756b6222956b5d25b2e7db81f4e79033a3a4d20e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 11 Aug 2013 00:26:11 +0200 Subject: [PATCH] src: add multi-context support This commit makes it possible to use multiple V8 execution contexts within a single event loop. Put another way, handle and request wrap objects now "remember" the context they belong to and switch back to that context when the time comes to call into JS land. This could have been done in a quick and hacky way by calling v8::Object::GetCreationContext() on the wrap object right before making a callback but that leaves a fairly wide margin for bugs. Instead, we make the context explicit through a new Environment class that encapsulates everything (or almost everything) that belongs to the context. Variables that used to be a static or a global are now members of the aforementioned class. An additional benefit is that this approach should make it relatively straightforward to add full isolate support in due course. There is no JavaScript API yet but that will be added in the near future. This work was graciously sponsored by GitHub, Inc. --- node.gyp | 5 + src/cares_wrap.cc | 236 ++++++++++------ src/env-inl.h | 289 +++++++++++++++++++ src/env.h | 324 +++++++++++++++++++++ src/fs_event_wrap.cc | 60 ++-- src/handle_wrap.cc | 29 +- src/handle_wrap.h | 14 +- src/node.cc | 720 ++++++++++++++++++++++++----------------------- src/node.h | 32 ++- src/node.js | 53 ++-- src/node_buffer.cc | 67 +++-- src/node_buffer.h | 18 ++ src/node_contextify.cc | 127 ++++----- src/node_crypto.cc | 277 +++++++++--------- src/node_crypto.h | 56 ++-- src/node_file.cc | 162 +++++------ src/node_file.h | 5 - src/node_http_parser.cc | 192 ++++++------- src/node_internals.h | 230 +++------------ src/node_os.cc | 9 +- src/node_stat_watcher.cc | 49 ++-- src/node_stat_watcher.h | 9 +- src/node_version.h | 2 +- src/node_wrap.h | 45 +-- src/node_zlib.cc | 111 ++++---- src/pipe_wrap.cc | 96 ++++--- src/pipe_wrap.h | 10 +- src/process_wrap.cc | 55 ++-- src/req_wrap.h | 40 ++- src/signal_wrap.cc | 43 ++- src/smalloc.cc | 43 ++- src/stream_wrap.cc | 129 +++++---- src/stream_wrap.h | 14 +- src/tcp_wrap.cc | 135 +++++---- src/tcp_wrap.h | 10 +- src/timer_wrap.cc | 39 +-- src/tls_wrap.cc | 166 ++++++----- src/tls_wrap.h | 13 +- src/tty_wrap.cc | 31 +- src/tty_wrap.h | 10 +- src/udp_wrap.cc | 82 +++--- src/udp_wrap.h | 14 +- src/util-inl.h | 83 ++++++ src/util.h | 82 ++++++ src/uv.cc | 8 +- 45 files changed, 2520 insertions(+), 1704 deletions(-) create mode 100644 src/env-inl.h create mode 100644 src/env.h create mode 100644 src/util-inl.h create mode 100644 src/util.h diff --git a/node.gyp b/node.gyp index 9e29ed1..3ff177a 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 f09590e..0023878 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 0000000..a8fcc58 --- /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 0000000..db8d660 --- /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 8906f43..719ee8c 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 858b55c..05d4c4d 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 b5918f5..73a43c3 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 c88b0ef..4670151 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); -} - -bool InDomain() { - return using_domains && domain_flag.count > 0; + 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()); } -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(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 -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 527ac25..00f60d3 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 3c7ede8..9ba1b3b 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 519fddf..2e73749 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 ca9a135..78e6e42 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 331dbdb..132fa91 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