diff options
45 files changed, 2520 insertions, 1704 deletions
@@ -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 f09590e88e..00238788ad 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<uv_getaddrinfo_t> 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<String> 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<ares_task_t*>(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<uv_loop_t*>(data); + Environment* env = static_cast<Environment*>(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<uv_handle_t*>(&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<uv_handle_t*>(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<uv_handle_t*>(&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<Array> HostentToNames(struct hostent* host) { class QueryWrap { public: - explicit QueryWrap(Local<Object> req_wrap_obj) { + QueryWrap(Environment* env, Local<Object> 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<Value> answer) { - HandleScope scope(node_isolate); - Local<Value> 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<Value> argv[] = { + Integer::New(0, env()->isolate()), + answer + }; + MakeCallback(env(), + object(), + env()->oncomplete_string(), + ARRAY_SIZE(argv), + argv); } void CallOnComplete(Local<Value> answer, Local<Value> family) { - HandleScope scope(node_isolate); - Local<Value> 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<Value> 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<Value> 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> object_; + Environment* const env_; }; class QueryAWrap: public QueryWrap { public: - explicit QueryAWrap(Local<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryAWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryAaaaWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) - : QueryWrap(req_wrap_obj) { + QueryCnameWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryMxWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryNsWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + QueryTxtWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) : QueryWrap(req_wrap_obj) { + explicit QuerySrvWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit QueryNaptrWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit GetHostByAddrWrap(Environment* env, Local<Object> 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<Object> req_wrap_obj) - : QueryWrap(req_wrap_obj) { + explicit GetHostByNameWrap(Environment* env, Local<Object> 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 <class Wrap> static void Query(const FunctionCallbackInfo<Value>& 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<Value>& args) { Local<Object> req_wrap_obj = args[0].As<Object>(); Local<String> string = args[1].As<String>(); - 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<Value>& args) { void AfterGetAddrInfo(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { - HandleScope scope(node_isolate); - GetAddrInfoReqWrap* req_wrap = static_cast<GetAddrInfoReqWrap*>(req->data); + Environment* env = req_wrap->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Value> 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<Value>& args) { static void GetAddrInfo(const FunctionCallbackInfo<Value>& 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<Value>& 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<Value>& args) { static void GetServers(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); Local<Array> 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<Value>& args) { static void SetServers(const FunctionCallbackInfo<Value>& 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<Value>& 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<Value>& 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<Value>& args) { } -static void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - int r; +static void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> 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<QueryAWrap>); NODE_SET_METHOD(target, "queryAaaa", Query<QueryAaaaWrap>); @@ -1045,11 +1105,9 @@ static void Initialize(Handle<Object> 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 0000000000..a8fcc58603 --- /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 <stddef.h> +#include <stdint.h> + +namespace node { + +inline Environment::IsolateData* Environment::IsolateData::GetOrCreate( + v8::Isolate* isolate) { + IsolateData* isolate_data = static_cast<IsolateData*>(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<v8::Context> 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<v8::Context> context) { + return static_cast<Environment*>( + 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<v8::Context> context) { + if (context.IsEmpty()) { + return NULL; + } else { + return GetCurrent(context); + } +} + +inline Environment::Environment(v8::Local<v8::Context> 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<Environment*>(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<v8::String> Environment::IsolateData::PropertyName() const { \ + return v8::Local<v8::String>::GetEternal(isolate(), \ + PropertyName ## _index_); \ + } + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, StringValue) \ + inline v8::Local<v8::String> Environment::PropertyName() const { \ + return isolate_data()->PropertyName(); \ + } + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, TypeName) \ + inline v8::Local<TypeName> Environment::PropertyName() const { \ + return StrongPersistentToLocal(PropertyName ## _); \ + } \ + inline void Environment::set_ ## PropertyName(v8::Local<TypeName> 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 0000000000..db8d660c30 --- /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 <stdint.h> + +// 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<v8::Context> context); + static inline Environment* GetCurrentChecked(v8::Isolate* isolate); + static inline Environment* GetCurrentChecked(v8::Local<v8::Context> context); + + // See CreateEnvironment() in src/node.cc. + static inline Environment* New(v8::Local<v8::Context> 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<v8::String> PropertyName() const; + PER_ISOLATE_STRING_PROPERTIES(V) +#undef V + +#define V(PropertyName, TypeName) \ + inline v8::Local<TypeName> PropertyName() const; \ + inline void set_ ## PropertyName(v8::Local<TypeName> value); + ENVIRONMENT_STRONG_PERSISTENT_PROPERTIES(V) +#undef V + + private: + class IsolateData; + inline explicit Environment(v8::Local<v8::Context> 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<TypeName> 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<v8::String> 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 8906f43ee3..719ee8c7eb 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<String> change_sym; -static Cached<String> onchange_sym; -static Cached<String> rename_sym; - class FSEventWrap: public HandleWrap { public: - static void Initialize(Handle<Object> target); + static void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context); static void New(const FunctionCallbackInfo<Value>& args); static void Start(const FunctionCallbackInfo<Value>& args); static void Close(const FunctionCallbackInfo<Value>& args); private: - explicit FSEventWrap(Handle<Object> object); + FSEventWrap(Environment* env, Handle<Object> 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> object) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&handle_)) { +FSEventWrap::FSEventWrap(Environment* env, Handle<Object> object) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) { initialized_ = false; } @@ -70,8 +71,11 @@ FSEventWrap::~FSEventWrap() { } -void FSEventWrap::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); +void FSEventWrap::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); + HandleScope handle_scope(env->isolate()); Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -81,17 +85,13 @@ void FSEventWrap::Initialize(Handle<Object> 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<Value>& 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<Value>& 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<Value>& args) { void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, int events, int status) { - HandleScope scope(node_isolate); - Handle<String> eventStr; - FSEventWrap* wrap = static_cast<FSEventWrap*>(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<String> 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<Value> argv[3] = { + Local<Value> 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<Value>& 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 858b55cdd4..05d4c4dddc 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<String> close_sym; void HandleWrap::Ref(const FunctionCallbackInfo<Value>& args) { @@ -73,27 +74,26 @@ void HandleWrap::Close(const FunctionCallbackInfo<Value>& 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> object, uv_handle_t* h) { - flags_ = 0; - handle__ = h; +HandleWrap::HandleWrap(Environment* env, + Handle<Object> 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<HandleWrap*>(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> 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 b5918f5dfe..73a43c3a3f 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<v8::Object> object, uv_handle_t* handle); + HandleWrap(Environment* env, + v8::Handle<v8::Object> object, + uv_handle_t* handle); virtual ~HandleWrap(); + inline Environment* env() const { + return env_; + } + inline v8::Local<v8::Object> object() { - return PersistentToLocal(node_isolate, persistent()); + return PersistentToLocal(env()->isolate(), persistent()); } inline v8::Persistent<v8::Object>& persistent() { @@ -74,10 +81,11 @@ class HandleWrap { static void OnClose(uv_handle_t* handle); v8::Persistent<v8::Object> 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 c88b0effba..4670151a8a 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<String> process_symbol; -Cached<String> domain_symbol; - -// declared in node_internals.h -Persistent<Object> process_p; - -static Persistent<Function> process_tickCallback; -static Persistent<Object> binding_cache; -static Persistent<Array> module_load_list; -static Persistent<Array> p_domain_box; - -static Cached<String> exports_symbol; - -static Cached<String> errno_symbol; -static Cached<String> syscall_symbol; -static Cached<String> errpath_symbol; -static Cached<String> code_symbol; - -static Cached<String> rss_symbol; -static Cached<String> heap_total_symbol; -static Cached<String> heap_used_symbol; - -static Cached<String> fatal_exception_symbol; - -static Cached<String> enter_symbol; -static Cached<String> exit_symbol; -static Cached<String> disposed_symbol; - -// Essential for node_wrap.h -Persistent<FunctionTemplate> pipeConstructorTmpl; -Persistent<FunctionTemplate> tcpConstructorTmpl; -Persistent<FunctionTemplate> ttyConstructorTmpl; - static bool print_eval = false; static bool force_repl = false; static bool trace_deprecation = false; @@ -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<String> immediate_callback_sym; - -// for quick ref to tickCallback values -static struct { - uint32_t length; - uint32_t index; - uint32_t in_tick; - uint32_t last_threw; -} tick_infobox; - -// easily communicate domain depth -static struct { - uint32_t count; -} domain_flag; - // process-relative uptime base, initialized at start-up static double prog_start_time; @@ -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<Value> ErrnoException(int errorno, const char *syscall, const char *msg, const char *path) { + Environment* env = Environment::GetCurrent(node_isolate); + Local<Value> e; Local<String> estring = OneByteString(node_isolate, errno_string(errorno)); if (msg == NULL || msg[0] == '\0') { @@ -740,13 +680,6 @@ Local<Value> ErrnoException(int errorno, String::Concat(estring, FIXED_ONE_BYTE_STRING(node_isolate, ", ")); Local<String> cons2 = String::Concat(cons1, message); - if (syscall_symbol.IsEmpty()) { - syscall_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "syscall"); - errno_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "errno"); - errpath_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "path"); - code_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "code"); - } - if (path) { Local<String> cons3 = String::Concat(cons2, FIXED_ONE_BYTE_STRING(node_isolate, " '")); @@ -760,11 +693,17 @@ Local<Value> ErrnoException(int errorno, } Local<Object> obj = e->ToObject(); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + obj->Set(env->code_string(), estring); + + if (path != NULL) { + obj->Set(env->path_string(), String::NewFromUtf8(node_isolate, path)); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - obj->Set(code_symbol, estring); - if (path) obj->Set(errpath_symbol, String::NewFromUtf8(node_isolate, path)); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); return e; } @@ -774,12 +713,7 @@ Local<Value> 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<Value> UVException(int errorno, } Local<Object> obj = e->ToObject(); - // TODO(piscisaureus) errno should probably go - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - obj->Set(code_symbol, estring); - if (path) obj->Set(errpath_symbol, path_str); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + obj->Set(env->code_string(), estring); + + if (path != NULL) { + obj->Set(env->path_string(), path_str); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } + return e; } @@ -859,19 +799,14 @@ Local<Value> WinapiErrnoException(int errorno, const char* syscall, const char* msg, const char* path) { + Environment* env = Environment::GetCurrent(node_isolate); + Local<Value> e; if (!msg || !msg[0]) { msg = winapi_strerror(errorno); } Local<String> message = OneByteString(node_isolate, msg); - if (syscall_symbol.IsEmpty()) { - syscall_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "syscall"); - errno_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "errno"); - errpath_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "path"); - code_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "code"); - } - if (path) { Local<String> cons1 = String::Concat(message, FIXED_ONE_BYTE_STRING(node_isolate, " '")); @@ -885,91 +820,92 @@ Local<Value> WinapiErrnoException(int errorno, } Local<Object> obj = e->ToObject(); + obj->Set(env->errno_string(), Integer::New(errorno, node_isolate)); + + if (path != NULL) { + obj->Set(env->path_string(), String::NewFromUtf8(node_isolate, path)); + } + + if (syscall != NULL) { + obj->Set(env->syscall_string(), OneByteString(node_isolate, syscall)); + } - obj->Set(errno_symbol, Integer::New(errorno, node_isolate)); - if (path) obj->Set(errpath_symbol, String::NewFromUtf8(node_isolate, path)); - if (syscall) obj->Set(syscall_symbol, OneByteString(node_isolate, syscall)); return e; } #endif void SetupDomainUse(const FunctionCallbackInfo<Value>& args) { - if (using_domains) return; + Environment* env = Environment::GetCurrent(args.GetIsolate()); + + if (env->using_domains()) return; + env->set_using_domains(true); + HandleScope scope(node_isolate); - using_domains = true; - Local<Object> process = PersistentToLocal(node_isolate, process_p); - Local<Value> tdc_v = - process->Get(FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback")); - if (!tdc_v->IsFunction()) { + Local<Object> process_object = env->process_object(); + + Local<String> tick_callback_function_key = + FIXED_ONE_BYTE_STRING(node_isolate, "_tickDomainCallback"); + Local<Function> tick_callback_function = + process_object->Get(tick_callback_function_key).As<Function>(); + + if (!tick_callback_function->IsFunction()) { fprintf(stderr, "process._tickDomainCallback assigned to non-function\n"); abort(); } - Local<Function> tdc = tdc_v.As<Function>(); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"), tdc); - process_tickCallback.Reset(node_isolate, tdc); + + 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<Array>()); + env->set_domain_array(args[0].As<Array>()); + if (!args[1]->IsObject()) { fprintf(stderr, "_setupDomainUse second argument must be an object\n"); abort(); } - Local<Object> flag = args[1].As<Object>(); - flag->SetIndexedPropertiesToExternalArrayData(&domain_flag, - kExternalUnsignedIntArray, - 1); -} - -bool InDomain() { - return using_domains && domain_flag.count > 0; + Local<Object> domain_flag_obj = args[1].As<Object>(); + Environment::DomainFlag* domain_flag = env->domain_flag(); + domain_flag_obj->SetIndexedPropertiesToExternalArrayData( + domain_flag->fields(), + kExternalUnsignedIntArray, + domain_flag->fields_count()); } -Handle<Value> 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<Value> MakeDomainCallback(Environment* env, + const Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); -Handle<Value> -MakeDomainCallback(const Handle<Object> object, - const Handle<Function> callback, - int argc, - Handle<Value> argv[]) { // TODO(trevnorris) Hook for long stack traces to be made here. - // 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<Value> domain_v = object->Get(domain_symbol); + Local<Value> domain_v = object->Get(env->domain_string()); Local<Object> domain; - Local<Function> enter; - Local<Function> 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<Object>(); + + if (domain->Get(env->disposed_string())->IsTrue()) { // domain has been disposed of. return Undefined(node_isolate); } - enter = Local<Function>::Cast(domain->Get(enter_symbol)); - assert(!enter.IsEmpty()); + + Local<Function> enter = + domain->Get(env->enter_string()).As<Function>(); + assert(enter->IsFunction()); enter->Call(domain, 0, NULL); if (try_catch.HasCaught()) { @@ -984,8 +920,9 @@ MakeDomainCallback(const Handle<Object> object, } if (has_domain) { - exit = Local<Function>::Cast(domain->Get(exit_symbol)); - assert(!exit.IsEmpty()); + Local<Function> exit = + domain->Get(env->exit_string()).As<Function>(); + assert(exit->IsFunction()); exit->Call(domain, 0, NULL); if (try_catch.HasCaught()) { @@ -993,24 +930,26 @@ MakeDomainCallback(const Handle<Object> 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<Object> process = PersistentToLocal(node_isolate, process_p); - Local<Function> fn = PersistentToLocal(node_isolate, process_tickCallback); - fn->Call(process, 0, NULL); + Local<Object> process_object = env->process_object(); + Local<Function> 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> object, } -Handle<Value> -MakeCallback(const Handle<Object> object, - const Handle<Function> callback, - int argc, - Handle<Value> argv[]) { +Handle<Value> MakeCallback(Environment* env, + const Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); + // TODO(trevnorris) Hook for long stack traces to be made here. - Local<Object> process = PersistentToLocal(node_isolate, process_p); + Local<Object> 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<Value> cb_v = - process->Get(FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback")); - if (!cb_v->IsFunction()) { - fprintf(stderr, "process._tickCallback assigned to non-function\n"); - abort(); - } - process_tickCallback.Reset(node_isolate, cb_v.As<Function>()); - } + 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> 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<Function> tick_callback_function = env->tick_callback_function(); + if (tick_callback_function.IsEmpty()) { + Local<String> tick_callback_function_key = + FIXED_ONE_BYTE_STRING(node_isolate, "_tickCallback"); + tick_callback_function = + process_object->Get(tick_callback_function_key).As<Function>(); + 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<Function> 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> object, // Internal only. -Handle<Value> -MakeCallback(const Handle<Object> object, - uint32_t index, - int argc, - Handle<Value> argv[]) { - HandleScope scope(node_isolate); +Handle<Value> MakeCallback(Environment* env, + const Handle<Object> object, + uint32_t index, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); Local<Function> callback = object->Get(index).As<Function>(); 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<Value> -MakeCallback(const Handle<Object> object, - const Handle<String> symbol, - int argc, - Handle<Value> argv[]) { - HandleScope scope(node_isolate); +Handle<Value> MakeCallback(Environment* env, + const Handle<Object> object, + const Handle<String> symbol, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); Local<Function> callback = object->Get(symbol).As<Function>(); assert(callback->IsFunction()); - if (using_domains) - return scope.Close(MakeDomainCallback(object, callback, argc, argv)); - return scope.Close(MakeCallback(object, callback, argc, argv)); -} + if (env->using_domains()) { + return MakeDomainCallback(env, object, callback, argc, argv); + } + return MakeCallback(env, object, callback, argc, argv); +} -Handle<Value> -MakeCallback(const Handle<Object> object, - const char* method, - int argc, - Handle<Value> argv[]) { - HandleScope scope(node_isolate); +Handle<Value> MakeCallback(Environment* env, + const Handle<Object> object, + const char* method, + int argc, + Handle<Value> argv[]) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); Local<String> method_string = OneByteString(node_isolate, method); - Handle<Value> ret = MakeCallback(object, method_string, argc, argv); + return MakeCallback(env, object, method_string, argc, argv); +} - return scope.Close(ret); + +Handle<Value> MakeCallback(const Handle<Object> object, + const char* method, + int argc, + Handle<Value> argv[]) { + Local<Context> 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<Value> MakeCallback(const Handle<Object> object, + const Handle<String> symbol, + int argc, + Handle<Value> argv[]) { + Local<Context> 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<Value> MakeCallback(const Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + Local<Context> 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<Value> MakeDomainCallback(const Handle<Object> object, + const Handle<Function> callback, + int argc, + Handle<Value> argv[]) { + Local<Context> 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<Value>& args) { void MemoryUsage(const FunctionCallbackInfo<Value>& 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<Object> info = Object::New(); - - if (rss_symbol.IsEmpty()) { - rss_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "rss"); - heap_total_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "heapTotal"); - heap_used_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "heapUsed"); - } - - info->Set(rss_symbol, Number::New(rss)); - // V8 memory usage HeapStatistics v8_heap_stats; node_isolate->GetHeapStatistics(&v8_heap_stats); - info->Set(heap_total_symbol, - Integer::NewFromUnsigned(v8_heap_stats.total_heap_size(), - node_isolate)); - info->Set(heap_used_symbol, - Integer::NewFromUnsigned(v8_heap_stats.used_heap_size(), - node_isolate)); + + Local<Integer> heap_total = + Integer::NewFromUnsigned(v8_heap_stats.total_heap_size(), node_isolate); + Local<Integer> heap_used = + Integer::NewFromUnsigned(v8_heap_stats.used_heap_size(), node_isolate); + + Local<Object> info = Object::New(); + info->Set(env->rss_string(), Number::New(node_isolate, rss)); + info->Set(env->heap_total_string(), heap_total); + info->Set(env->heap_used_string(), heap_used); args.GetReturnValue().Set(info); } @@ -1811,8 +1804,13 @@ typedef void (UV_DYNAMIC* extInit)(Handle<Object> exports); // DLOpen is process.dlopen(module, filename). // Used to load 'module.node' dynamically shared objects. +// +// FIXME(bnoordhuis) Not multi-context ready. TBD how to resolve the conflict +// when two contexts try to load the same shared object. Maybe have a shadow +// cache that's a plain C list or hash table that's shared across contexts? void DLOpen(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + 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<Value>& args) { Local<Object> module = args[0]->ToObject(); // Cast String::Utf8Value filename(args[1]); // Cast - if (exports_symbol.IsEmpty()) { - exports_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "exports"); - } - Local<Object> exports = module->Get(exports_symbol)->ToObject(); + Local<String> exports_string = env->exports_string(); + Local<Object> exports = module->Get(exports_string)->ToObject(); if (uv_dlopen(*filename, &lib)) { - Local<String> errmsg = OneByteString(node_isolate, uv_dlerror(&lib)); + Local<String> errmsg = OneByteString(env->isolate(), uv_dlerror(&lib)); #ifdef _WIN32 // Windows needs to add the filename into the error message errmsg = String::Concat(errmsg, args[1]->ToString()); @@ -1894,7 +1890,13 @@ void DLOpen(const FunctionCallbackInfo<Value>& 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<Value> error, Handle<Message> 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<Object> process_object = env->process_object(); + Local<String> fatal_exception_string = env->fatal_exception_string(); + Local<Function> fatal_exception_function = + process_object->Get(fatal_exception_string).As<Function>(); - Local<Object> process = PersistentToLocal(node_isolate, process_p); - Local<Value> 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<Function> fatal_f = Local<Function>::Cast(fatal_v); - TryCatch fatal_try_catch; // Do not call FatalException when _fatalException handler throws fatal_try_catch.SetVerbose(false); // this will return true if the JS layer handled it, false otherwise - Local<Value> caught = fatal_f->Call(process, 1, &error); + Local<Value> caught = + fatal_exception_function->Call(process_object, 1, &error); if (fatal_try_catch.HasCaught()) { // the fatal exception function threw, so we must exit @@ -1979,13 +1978,13 @@ void OnMessage(Handle<Message> message, Handle<Value> error) { static void Binding(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); Local<String> module = args[0]->ToString(); String::Utf8Value module_v(module); - node_module_struct* modp; - Local<Object> cache = PersistentToLocal(node_isolate, binding_cache); + Local<Object> cache = env->binding_cache_object(); Local<Object> exports; if (cache->Has(module)) { @@ -1998,15 +1997,18 @@ static void Binding(const FunctionCallbackInfo<Value>& args) { char buf[1024]; snprintf(buf, sizeof(buf), "Binding %s", *module_v); - Local<Array> modules = PersistentToLocal(node_isolate, module_load_list); + Local<Array> modules = env->module_load_list_array(); uint32_t l = modules->Length(); modules->Set(l, OneByteString(node_isolate, buf)); - if ((modp = get_builtin_module(*module_v)) != NULL) { + node_module_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<Value> 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<Value>& args); void NeedImmediateCallbackGetter(Local<String> property, const PropertyCallbackInfo<Value>& 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<const uv_handle_t*>(immediate_check_handle)); + info.GetReturnValue().Set(active); } -static void NeedImmediateCallbackSetter(Local<String> property, - Local<Value> value, - const PropertyCallbackInfo<void>&) { - HandleScope scope(node_isolate); +static void NeedImmediateCallbackSetter( + Local<String> property, + Local<Value> value, + const PropertyCallbackInfo<void>& info) { + 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<const uv_handle_t*>(immediate_check_handle)); - if (need_immediate_cb == bool_value) return; + if (active == value->BooleanValue()) + return; - need_immediate_cb = bool_value; + uv_idle_t* immediate_idle_handle = env->immediate_idle_handle(); - if (need_immediate_cb) { - uv_check_start(&check_immediate_watcher, node::CheckImmediate); - // idle handle is needed only to maintain event loop - uv_idle_start(&idle_immediate_dummy, node::IdleImmediateDummy); + if (active) { + uv_check_stop(immediate_check_handle); + uv_idle_stop(immediate_idle_handle); } else { - uv_check_stop(&check_immediate_watcher); - uv_idle_stop(&idle_immediate_dummy); + uv_check_start(immediate_check_handle, CheckImmediate); + // Idle handle is needed only to stop the event loop from blocking in poll. + uv_idle_start(immediate_idle_handle, IdleImmediateDummy); } } @@ -2286,23 +2297,15 @@ static void NeedImmediateCallbackSetter(Local<String> property, } while (0) -Handle<Object> SetupProcessObject(int argc, - const char* const* argv, - int exec_argc, - const char* const* exec_argv) { +void SetupProcessObject(Environment* env, + int argc, + const char* const* argv, + int exec_argc, + const char* const* exec_argv) { HandleScope scope(node_isolate); - int i, j; - Local<FunctionTemplate> process_template = FunctionTemplate::New(); - process_template->SetClassName( - FIXED_ONE_BYTE_STRING(node_isolate, "process")); - - Local<Object> process = process_template->GetFunction()->NewInstance(); - assert(process.IsEmpty() == false); - assert(process->IsObject() == true); - - process_p.Reset(node_isolate, process); + Local<Object> process = env->process_object(); process->SetAccessor(FIXED_ONE_BYTE_STRING(node_isolate, "title"), ProcessTitleGetter, @@ -2314,9 +2317,9 @@ Handle<Object> SetupProcessObject(int argc, FIXED_ONE_BYTE_STRING(node_isolate, NODE_VERSION)); // process.moduleLoadList - Local<Array> modules = Array::New(); - module_load_list.Reset(node_isolate, modules); - READONLY_PROPERTY(process, "moduleLoadList", modules); + READONLY_PROPERTY(process, + "moduleLoadList", + env->module_load_list_array()); // process.versions Local<Object> versions = Object::New(); @@ -2367,8 +2370,6 @@ Handle<Object> 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<Object> SetupProcessObject(int argc, process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "execArgv"), exec_arguments); // create process.env - Local<ObjectTemplate> envTemplate = ObjectTemplate::New(); - envTemplate->SetNamedPropertyHandler(EnvGetter, - EnvSetter, - EnvQuery, - EnvDeleter, - EnvEnumerator, - Object::New()); - Local<Object> env = envTemplate->NewInstance(); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "env"), env); + Local<ObjectTemplate> process_env_template = ObjectTemplate::New(); + process_env_template->SetNamedPropertyHandler(EnvGetter, + EnvSetter, + EnvQuery, + EnvDeleter, + EnvEnumerator, + Object::New()); + Local<Object> process_env = process_env_template->NewInstance(); + process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "env"), process_env); READONLY_PROPERTY(process, "pid", Integer::New(getpid(), node_isolate)); READONLY_PROPERTY(process, "features", GetFeatures()); @@ -2500,16 +2501,15 @@ Handle<Object> SetupProcessObject(int argc, NODE_SET_METHOD(process, "_setupDomainUse", SetupDomainUse); // values use to cross communicate with processNextTick - Local<Object> info_box = Object::New(); - info_box->SetIndexedPropertiesToExternalArrayData(&tick_infobox, - kExternalUnsignedIntArray, - 4); - process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickInfoBox"), info_box); + Local<Object> tick_info_obj = Object::New(); + tick_info_obj->SetIndexedPropertiesToExternalArrayData( + env->tick_info()->fields(), + kExternalUnsignedIntArray, + env->tick_info()->fields_count()); + process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_tickInfo"), tick_info_obj); // pre-set _events object for faster emit checks process->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_events"), Object::New()); - - return scope.Close(process); } @@ -2543,12 +2543,9 @@ static void RawDebug(const FunctionCallbackInfo<Value>& args) { } -void Load(Handle<Object> process_l) { +void Load(Environment* env) { HandleScope handle_scope(node_isolate); - process_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "process"); - domain_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "domain"); - // Compile, execute the src/node.js file. (Which was included as static C // string in node_natives.h. 'natve_node' is the string containing that // source code.) @@ -2581,7 +2578,7 @@ void Load(Handle<Object> process_l) { // Node's I/O bindings may want to replace 'f' with their own function. // Add a reference to the global object - Local<Object> global = v8::Context::GetCurrent()->Global(); + Local<Object> global = env->context()->Global(); #if defined HAVE_DTRACE || defined HAVE_ETW || defined HAVE_SYSTEMTAP InitDTrace(global); @@ -2599,9 +2596,9 @@ void Load(Handle<Object> 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<Value> arg = process_l; + Local<Value> 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<Object> 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<Object> message = Object::New(); + message->Set(FIXED_ONE_BYTE_STRING(node_isolate, "cmd"), + FIXED_ONE_BYTE_STRING(node_isolate, "NODE_DEBUG_ENABLED")); Local<Value> args[] = { FIXED_ONE_BYTE_STRING(node_isolate, "internalMessage"), - obj + 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<uv_handle_t*>(&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<uv_handle_t*>(&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<v8::Object> 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<Object> process_object = env->process_object(); + process_object->Set(FIXED_ONE_BYTE_STRING(node_isolate, "_exiting"), + True(node_isolate)); Local<Value> 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 = Context::New(isolate); + Context::Scope context_scope(context); + Environment* env = Environment::New(context); + + uv_check_init(env->event_loop(), env->immediate_check_handle()); + uv_unref( + reinterpret_cast<uv_handle_t*>(env->immediate_check_handle())); + uv_idle_init(env->event_loop(), env->immediate_idle_handle()); + + Local<FunctionTemplate> process_template = FunctionTemplate::New(); + process_template->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "process")); + + Local<Object> process_object = process_template->GetFunction()->NewInstance(); + env->set_process_object(process_object); + + SetupProcessObject(env, argc, argv, exec_argc, exec_argv); + Load(env); + + return env; } @@ -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 = Context::New(node_isolate); - Context::Scope context_scope(context); - - binding_cache.Reset(node_isolate, Object::New()); - - // Use original argv, as we're just copying values out of it. - Local<Object> process_l = - SetupProcessObject(argc, argv, exec_argc, exec_argv); - - // Create all the objects, load modules, do everything. - // so your next reading stop should be node::Load()! - Load(process_l); - - // All our arguments are loaded. We've evaluated all of the scripts. We - // might even have created TCP servers. Now we enter the main eventloop. If - // there are no watchers on the loop (except for the ones that were - // uv_unref'd) then this function exits. As long as there are active - // watchers, it blocks. - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - - EmitExit(process_l); - RunAtExit(); + Environment* env = + CreateEnvironment(node_isolate, argc, argv, exec_argc, exec_argv); + 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 527ac25a35..00f60d3fb1 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<v8::Value> MakeCallback( #include "node_internals.h" #endif -#include "uv.h" -#include "v8.h" - #include <assert.h> #ifndef NODE_STRINGIFY @@ -185,9 +183,6 @@ NODE_EXTERN ssize_t DecodeWrite(char *buf, v8::Handle<v8::Value>, enum encoding encoding = BINARY); -v8::Local<v8::Object> BuildStatsObject(const uv_stat_t* s); - - #ifdef _WIN32 NODE_EXTERN v8::Local<v8::Value> WinapiErrnoException(int errorno, const char *syscall = NULL, const char *msg = "", @@ -197,14 +192,21 @@ NODE_EXTERN v8::Local<v8::Value> WinapiErrnoException(int errorno, const char *signo_string(int errorno); -NODE_EXTERN typedef void (* addon_register_func)( - v8::Handle<v8::Object> exports, v8::Handle<v8::Value> module); +NODE_EXTERN typedef void (*addon_register_func)( + v8::Handle<v8::Object> exports, + v8::Handle<v8::Value> module); + +NODE_EXTERN typedef void (*addon_context_register_func)( + v8::Handle<v8::Object> exports, + v8::Handle<v8::Value> module, + v8::Handle<v8::Context> 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 3c7ede82b8..9ba1b3b4a0 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 519fddf4c1..2e7374971c 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 <assert.h> #include <string.h> @@ -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<Function> p_buffer_fn; - bool HasInstance(Handle<Value> val) { return val->IsObject() && HasInstance(val.As<Object>()); @@ -124,12 +124,20 @@ Local<Object> New(Handle<String> string, enum encoding enc) { Local<Object> 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<Object> New(Environment* env, size_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate); - Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg); + Local<Object> 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<Object> New(size_t length) { } +Local<Object> 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<Object> New(const char* data, size_t length) { +Local<Object> New(Environment* env, const char* data, size_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate); - Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg); + Local<Object> 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<Object> 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<Object> New(Environment* env, + char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate); - Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg); + Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); smalloc::Alloc(obj, data, length, callback, hint); @@ -196,12 +220,18 @@ Local<Object> New(char* data, Local<Object> Use(char* data, uint32_t length) { + Environment* env = Environment::GetCurrent(node_isolate); + return Buffer::Use(env, data, length); +} + + +Local<Object> Use(Environment* env, char* data, uint32_t length) { HandleScope scope(node_isolate); assert(length <= kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(length, node_isolate); - Local<Object> obj = NewInstance(p_buffer_fn, 1, &arg); + Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); smalloc::Alloc(obj, data, length); @@ -534,12 +564,13 @@ void ByteLength(const FunctionCallbackInfo<Value> &args) { // pass Buffer object to load prototype methods void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); assert(args[0]->IsFunction()); Local<Function> bv = args[0].As<Function>(); - p_buffer_fn.Reset(node_isolate, bv); + env->set_buffer_constructor_function(bv); Local<Value> proto_v = bv->Get(FIXED_ONE_BYTE_STRING(node_isolate, "prototype")); @@ -588,10 +619,12 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { } -void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - - target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "setupBufferJS"), +void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> 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<Object> 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 ca9a135e9d..78e6e42f6c 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<v8::Object> New(char* data, // TODO(trevnorris): should be New() for consistency NODE_EXTERN v8::Local<v8::Object> 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<v8::Object> New(Environment* env, size_t size); +v8::Local<v8::Object> New(Environment* env, const char* data, size_t len); +v8::Local<v8::Object> New(Environment* env, + char* data, + size_t length, + smalloc::FreeCallback callback, + void* hint); +v8::Local<v8::Object> 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 331dbdb6e9..132fa911de 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<Object> sandbox_; - Persistent<Object> proxy_global_; Persistent<Context> context_; - static Persistent<Function> data_wrapper_ctor; + Persistent<Object> proxy_global_; public: - explicit ContextifyContext(Local<Object> sandbox) : - sandbox_(node_isolate, sandbox) { - HandleScope scope(node_isolate); - Local<Context> v8_context = CreateV8Context(); - context_.Reset(node_isolate, v8_context); - proxy_global_.Reset(node_isolate, v8_context->Global()); + explicit ContextifyContext(Environment* env, Local<Object> 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> 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<Value> CreateDataWrapper() { + Local<Value> CreateDataWrapper(Environment* env) { HandleScope scope(node_isolate); - Local<Function> ctor = PersistentToLocal(node_isolate, data_wrapper_ctor); - Local<Object> wrapper = ctor->NewInstance(); + Local<Object> wrapper = + env->script_data_constructor_function()->NewInstance(); NODE_WRAP(wrapper, this); return scope.Close(wrapper); } - Local<Context> CreateV8Context() { + Local<Context> CreateV8Context(Environment* env) { HandleScope scope(node_isolate); Local<FunctionTemplate> 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<Object> target) { - HandleScope scope(node_isolate); - + static void Init(Environment* env, Local<Object> target) { Local<FunctionTemplate> 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<Value>& 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<External> hidden_context = External::New(context); sandbox->SetHiddenValue(hidden_name, hidden_context); } @@ -179,18 +190,6 @@ class ContextifyContext { return static_cast<ContextifyContext*>(context_external->Value()); } - static Local<Context> V8ContextFromContextifiedSandbox( - const Local<Object>& sandbox) { - ContextifyContext* contextify_context = - ContextFromContextifiedSandbox(sandbox); - if (contextify_context == NULL) { - ThrowTypeError("sandbox argument must have been converted to a context."); - return Local<Context>(); - } - - return PersistentToLocal(node_isolate, contextify_context->context_); - } - static bool GlobalPropertyNamedAccessCheck(Local<Object> host, Local<Value> key, @@ -300,24 +299,21 @@ class ContextifyScript : ObjectWrap { Persistent<Script> script_; public: - static Persistent<FunctionTemplate> script_tmpl; - - static void Init(Local<Object> target) { + static void Init(Environment* env, Local<Object> target) { HandleScope scope(node_isolate); Local<String> class_name = FIXED_ONE_BYTE_STRING(node_isolate, "ContextifyScript"); - script_tmpl.Reset(node_isolate, FunctionTemplate::New(New)); - Local<FunctionTemplate> lscript_tmpl = - PersistentToLocal(node_isolate, script_tmpl); - lscript_tmpl->InstanceTemplate()->SetInternalFieldCount(1); - lscript_tmpl->SetClassName(class_name); - NODE_SET_PROTOTYPE_METHOD(lscript_tmpl, "runInContext", RunInContext); - NODE_SET_PROTOTYPE_METHOD(lscript_tmpl, + Local<FunctionTemplate> script_tmpl = FunctionTemplate::New(New); + script_tmpl->InstanceTemplate()->SetInternalFieldCount(1); + script_tmpl->SetClassName(class_name); + NODE_SET_PROTOTYPE_METHOD(script_tmpl, "runInContext", RunInContext); + NODE_SET_PROTOTYPE_METHOD(script_tmpl, "runInThisContext", RunInThisContext); - target->Set(class_name, lscript_tmpl->GetFunction()); + target->Set(class_name, script_tmpl->GetFunction()); + env->set_script_context_constructor_template(script_tmpl); } @@ -357,15 +353,16 @@ class ContextifyScript : ObjectWrap { } - static bool InstanceOf(const Local<Value>& value) { + static bool InstanceOf(Environment* env, const Local<Value>& value) { return !value.IsEmpty() && - PersistentToLocal(node_isolate, script_tmpl)->HasInstance(value); + env->script_context_constructor_template()->HasInstance(value); } // args: [options] static void RunInThisContext(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); // Assemble arguments TryCatch try_catch; @@ -377,7 +374,7 @@ class ContextifyScript : ObjectWrap { } // Do the eval within this context - EvalMachine(timeout, display_errors, args, try_catch); + EvalMachine(env, timeout, display_errors, args, try_catch); } // args: sandbox, [options] @@ -398,16 +395,20 @@ class ContextifyScript : ObjectWrap { } // Get the context from the sandbox - Local<Context> context = - ContextifyContext::V8ContextFromContextifiedSandbox(sandbox); - if (try_catch.HasCaught()) { - try_catch.ReThrow(); - return; + ContextifyContext* contextify_context = + ContextifyContext::ContextFromContextifiedSandbox(sandbox); + if (contextify_context == NULL) { + return ThrowTypeError( + "sandbox argument must have been converted to a context."); } // Do the eval within the context - Context::Scope context_scope(context); - EvalMachine(timeout, display_errors, args, try_catch); + Context::Scope context_scope(contextify_context->context()); + EvalMachine(contextify_context->env(), + timeout, + display_errors, + args, + try_catch); } static int64_t GetTimeoutArg(const FunctionCallbackInfo<Value>& args, @@ -475,11 +476,12 @@ class ContextifyScript : ObjectWrap { } - static void EvalMachine(const int64_t timeout, + static void EvalMachine(Environment* env, + const int64_t timeout, const bool display_errors, const FunctionCallbackInfo<Value>& args, TryCatch& try_catch) { - if (!ContextifyScript::InstanceOf(args.This())) { + if (!ContextifyScript::InstanceOf(env, args.This())) { return ThrowTypeError( "Script methods can only be called on script instances."); } @@ -521,15 +523,14 @@ class ContextifyScript : ObjectWrap { }; -Persistent<Function> ContextifyContext::data_wrapper_ctor; -Persistent<FunctionTemplate> ContextifyScript::script_tmpl; - -void InitContextify(Local<Object> target) { - HandleScope scope(node_isolate); - ContextifyContext::Init(target); - ContextifyScript::Init(target); +void InitContextify(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); + ContextifyContext::Init(env, target); + ContextifyScript::Init(env, target); } } // namespace node -NODE_MODULE(node_contextify, node::InitContextify); +NODE_MODULE_CONTEXT_AWARE(node_contextify, node::InitContextify); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index ff90e91a46..9a3cfe9390 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -27,6 +27,8 @@ #include "node_root_certs.h" #include "tls_wrap.h" // TLSCallbacks +#include "env.h" +#include "env-inl.h" #include "string_bytes.h" #include "v8.h" @@ -70,6 +72,7 @@ namespace crypto { using v8::Array; using v8::Boolean; +using v8::Context; using v8::Exception; using v8::False; using v8::FunctionCallbackInfo; @@ -95,27 +98,6 @@ struct ClearErrorOnReturn { ~ClearErrorOnReturn() { ERR_clear_error(); } }; -static Cached<String> subject_symbol; -static Cached<String> subjectaltname_symbol; -static Cached<String> modulus_symbol; -static Cached<String> exponent_symbol; -static Cached<String> issuer_symbol; -static Cached<String> valid_from_symbol; -static Cached<String> valid_to_symbol; -static Cached<String> fingerprint_symbol; -static Cached<String> name_symbol; -static Cached<String> version_symbol; -static Cached<String> ext_key_usage_symbol; -static Cached<String> onhandshakestart_sym; -static Cached<String> onhandshakedone_sym; -static Cached<String> onclienthello_sym; -static Cached<String> onnewsession_sym; -static Cached<String> sessionid_sym; -static Cached<String> servername_sym; -static Cached<String> tls_ticket_sym; - -static Persistent<FunctionTemplate> secure_context_constructor; - static uv_rwlock_t* locks; X509_STORE* root_cert_store; @@ -203,12 +185,8 @@ void ThrowCryptoTypeError(unsigned long err) { } -void SecureContext::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - +void SecureContext::Initialize(Environment* env, Handle<Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(SecureContext::New); - secure_context_constructor.Reset(node_isolate, t); - t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "SecureContext")); @@ -231,12 +209,14 @@ void SecureContext::Initialize(Handle<Object> target) { target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "SecureContext"), t->GetFunction()); + env->set_secure_context_constructor_template(t); } void SecureContext::New(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); - SecureContext* sc = new SecureContext(); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + SecureContext* sc = new SecureContext(env); sc->Wrap(args.This()); } @@ -750,12 +730,12 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo<Value>& args) { void SecureContext::GetTicketKeys(const FunctionCallbackInfo<Value>& args) { #if !defined(OPENSSL_NO_TLSEXT) && defined(SSL_CTX_get_tlsext_ticket_keys) - HandleScope scope(node_isolate); + HandleScope handle_scope(args.GetIsolate()); SecureContext* wrap; NODE_UNWRAP(args.This(), SecureContext, wrap); - Local<Object> buff = Buffer::New(48); + Local<Object> buff = Buffer::New(wrap->env(), 48); if (SSL_CTX_get_tlsext_ticket_keys(wrap->ctx_, Buffer::Data(buff), Buffer::Length(buff)) != 1) { @@ -836,6 +816,7 @@ int SSLWrap<Base>::NewSessionCallback(SSL* s, SSL_SESSION* sess) { HandleScope scope(node_isolate); Base* w = static_cast<Base*>(SSL_get_app_data(s)); + Environment* env = w->env(); if (!w->session_callbacks_) return 0; @@ -846,19 +827,19 @@ int SSLWrap<Base>::NewSessionCallback(SSL* s, SSL_SESSION* sess) { return 0; // Serialize session - Local<Object> buff = Buffer::New(size); + Local<Object> buff = Buffer::New(env, size); unsigned char* serialized = reinterpret_cast<unsigned char*>( Buffer::Data(buff)); memset(serialized, 0, size); i2d_SSL_SESSION(sess, &serialized); - Local<Object> session = Buffer::New(reinterpret_cast<char*>(sess->session_id), + Local<Object> session = Buffer::New(env, + reinterpret_cast<char*>(sess->session_id), sess->session_id_length); Local<Value> argv[] = { session, buff }; - if (onnewsession_sym.IsEmpty()) - onnewsession_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onnewsession"); - MakeCallback(w->handle(node_isolate), - onnewsession_sym, + MakeCallback(env, + w->handle(node_isolate), + env->onnewsession_string(), ARRAY_SIZE(argv), argv); @@ -872,34 +853,28 @@ void SSLWrap<Base>::OnClientHello(void* arg, HandleScope scope(node_isolate); Base* w = static_cast<Base*>(arg); - - if (onclienthello_sym.IsEmpty()) - onclienthello_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onclienthello"); - if (sessionid_sym.IsEmpty()) - sessionid_sym = FIXED_ONE_BYTE_STRING(node_isolate, "sessionId"); - if (servername_sym.IsEmpty()) - servername_sym = FIXED_ONE_BYTE_STRING(node_isolate, "servername"); - if (tls_ticket_sym.IsEmpty()) - tls_ticket_sym = FIXED_ONE_BYTE_STRING(node_isolate, "tlsTicket"); + Environment* env = w->env(); Local<Object> hello_obj = Object::New(); Local<Object> buff = Buffer::New( + env, reinterpret_cast<const char*>(hello.session_id()), - hello.session_size()); - hello_obj->Set(sessionid_sym, buff); + hello.session_size()); + hello_obj->Set(env->session_id_string(), buff); if (hello.servername() == NULL) { - hello_obj->Set(servername_sym, String::Empty(node_isolate)); + hello_obj->Set(env->servername_string(), String::Empty(node_isolate)); } else { Local<String> servername = OneByteString(node_isolate, hello.servername(), hello.servername_size()); - hello_obj->Set(servername_sym, servername); + hello_obj->Set(env->servername_string(), servername); } - hello_obj->Set(tls_ticket_sym, Boolean::New(hello.has_ticket())); + hello_obj->Set(env->tls_ticket_string(), Boolean::New(hello.has_ticket())); Local<Value> argv[] = { hello_obj }; - MakeCallback(w->handle(node_isolate), - onclienthello_sym, + MakeCallback(env, + w->handle(node_isolate), + env->onclienthello_string(), ARRAY_SIZE(argv), argv); } @@ -912,6 +887,7 @@ void SSLWrap<Base>::GetPeerCertificate( HandleScope scope(node_isolate); Base* w = ObjectWrap::Unwrap<Base>(args.This()); + Environment* env = w->env(); Local<Object> info = Object::New(); X509* peer_cert = SSL_get_peer_certificate(w->ssl_); @@ -923,7 +899,7 @@ void SSLWrap<Base>::GetPeerCertificate( 0, X509_NAME_FLAGS) > 0) { BIO_get_mem_ptr(bio, &mem); - info->Set(subject_symbol, + info->Set(env->subject_string(), OneByteString(node_isolate, mem->data, mem->length)); } (void) BIO_reset(bio); @@ -931,7 +907,7 @@ void SSLWrap<Base>::GetPeerCertificate( X509_NAME* issuer_name = X509_get_issuer_name(peer_cert); if (X509_NAME_print_ex(bio, issuer_name, 0, X509_NAME_FLAGS) > 0) { BIO_get_mem_ptr(bio, &mem); - info->Set(issuer_symbol, + info->Set(env->issuer_string(), OneByteString(node_isolate, mem->data, mem->length)); } (void) BIO_reset(bio); @@ -948,7 +924,7 @@ void SSLWrap<Base>::GetPeerCertificate( assert(rv == 1); BIO_get_mem_ptr(bio, &mem); - info->Set(subjectaltname_symbol, + info->Set(env->subjectaltname_string(), OneByteString(node_isolate, mem->data, mem->length)); (void) BIO_reset(bio); @@ -962,13 +938,13 @@ void SSLWrap<Base>::GetPeerCertificate( if (rsa != NULL) { BN_print(bio, rsa->n); BIO_get_mem_ptr(bio, &mem); - info->Set(modulus_symbol, + info->Set(env->modulus_string(), OneByteString(node_isolate, mem->data, mem->length)); (void) BIO_reset(bio); BN_print(bio, rsa->e); BIO_get_mem_ptr(bio, &mem); - info->Set(exponent_symbol, + info->Set(env->exponent_string(), OneByteString(node_isolate, mem->data, mem->length)); (void) BIO_reset(bio); } @@ -984,13 +960,13 @@ void SSLWrap<Base>::GetPeerCertificate( ASN1_TIME_print(bio, X509_get_notBefore(peer_cert)); BIO_get_mem_ptr(bio, &mem); - info->Set(valid_from_symbol, + info->Set(env->valid_from_string(), OneByteString(node_isolate, mem->data, mem->length)); (void) BIO_reset(bio); ASN1_TIME_print(bio, X509_get_notAfter(peer_cert)); BIO_get_mem_ptr(bio, &mem); - info->Set(valid_to_symbol, + info->Set(env->valid_to_string(), OneByteString(node_isolate, mem->data, mem->length)); BIO_free_all(bio); @@ -1013,7 +989,8 @@ void SSLWrap<Base>::GetPeerCertificate( fingerprint[0] = '\0'; } - info->Set(fingerprint_symbol, OneByteString(node_isolate, fingerprint)); + info->Set(env->fingerprint_string(), + OneByteString(node_isolate, fingerprint)); } STACK_OF(ASN1_OBJECT)* eku = static_cast<STACK_OF(ASN1_OBJECT)*>( @@ -1029,7 +1006,7 @@ void SSLWrap<Base>::GetPeerCertificate( } sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free); - info->Set(ext_key_usage_symbol, ext_key_usage); + info->Set(env->ext_key_usage_string(), ext_key_usage); } X509_free(peer_cert); @@ -1103,6 +1080,7 @@ void SSLWrap<Base>::LoadSession(const FunctionCallbackInfo<Value>& args) { HandleScope scope(node_isolate); Base* w = ObjectWrap::Unwrap<Base>(args.This()); + Environment* env = w->env(); if (args.Length() >= 1 && Buffer::HasInstance(args[0])) { ssize_t slen = Buffer::Length(args[0]); @@ -1118,12 +1096,10 @@ void SSLWrap<Base>::LoadSession(const FunctionCallbackInfo<Value>& args) { Local<Object> info = Object::New(); #ifndef OPENSSL_NO_TLSEXT - if (servername_sym.IsEmpty()) - servername_sym = FIXED_ONE_BYTE_STRING(node_isolate, "servername"); if (sess->tlsext_hostname == NULL) { - info->Set(servername_sym, False(node_isolate)); + info->Set(env->servername_string(), False(node_isolate)); } else { - info->Set(servername_sym, + info->Set(env->servername_string(), OneByteString(node_isolate, sess->tlsext_hostname)); } #endif @@ -1255,6 +1231,7 @@ void SSLWrap<Base>::GetCurrentCipher(const FunctionCallbackInfo<Value>& args) { HandleScope scope(node_isolate); Base* w = ObjectWrap::Unwrap<Base>(args.This()); + Environment* env = w->env(); OPENSSL_CONST SSL_CIPHER* c = SSL_get_current_cipher(w->ssl_); if (c == NULL) @@ -1262,9 +1239,9 @@ void SSLWrap<Base>::GetCurrentCipher(const FunctionCallbackInfo<Value>& args) { Local<Object> info = Object::New(); const char* cipher_name = SSL_CIPHER_get_name(c); - info->Set(name_symbol, OneByteString(node_isolate, cipher_name)); + info->Set(env->name_string(), OneByteString(node_isolate, cipher_name)); const char* cipher_version = SSL_CIPHER_get_version(c); - info->Set(version_symbol, OneByteString(node_isolate, cipher_version)); + info->Set(env->version_string(), OneByteString(node_isolate, cipher_version)); args.GetReturnValue().Set(info); } @@ -1539,9 +1516,7 @@ void Connection::SetShutdownFlags() { } -void Connection::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - +void Connection::Initialize(Environment* env, Handle<Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(Connection::New); t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Connection")); @@ -1629,6 +1604,7 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) { HandleScope scope(node_isolate); Connection* conn = static_cast<Connection*>(SSL_get_app_data(s)); + Environment* env = conn->env(); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); @@ -1640,11 +1616,15 @@ int Connection::SelectSNIContextCallback_(SSL *s, int *ad, void* arg) { if (!conn->sniObject_.IsEmpty()) { conn->sniContext_.Dispose(); + Local<Object> sni_object = + PersistentToLocal(node_isolate, conn->sniObject_); Local<Value> arg = PersistentToLocal(node_isolate, conn->servername_); - Local<Value> ret = MakeCallback(conn->sniObject_, "onselect", 1, &arg); + Local<Value> ret = MakeCallback(env, sni_object, "onselect", 1, &arg); // If ret is SecureContext - if (HasInstance(secure_context_constructor, ret)) { + Local<FunctionTemplate> secure_context_constructor_template = + env->secure_context_constructor_template(); + if (secure_context_constructor_template->HasInstance(ret)) { conn->sniContext_.Reset(node_isolate, ret); SecureContext* sc = ObjectWrap::Unwrap<SecureContext>(ret.As<Object>()); SSL_set_SSL_CTX(s, sc->ctx_); @@ -1666,12 +1646,13 @@ void Connection::New(const FunctionCallbackInfo<Value>& args) { } SecureContext* sc = ObjectWrap::Unwrap<SecureContext>(args[0]->ToObject()); + Environment* env = sc->env(); bool is_server = args[1]->BooleanValue(); - Connection* conn = new Connection( - sc, - is_server ? SSLWrap<Connection>::kServer : SSLWrap<Connection>::kClient); + SSLWrap<Connection>::Kind kind = + is_server ? SSLWrap<Connection>::kServer : SSLWrap<Connection>::kClient; + Connection* conn = new Connection(env, sc, kind); conn->Wrap(args.This()); conn->ssl_ = SSL_new(sc->ctx_); @@ -1746,26 +1727,27 @@ void Connection::New(const FunctionCallbackInfo<Value>& args) { void Connection::SSLInfoCallback(const SSL *ssl_, int where, int ret) { + if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))) + return; + // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants // a non-const SSL* in OpenSSL <= 0.9.7e. SSL* ssl = const_cast<SSL*>(ssl_); + Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl)); + Environment* env = conn->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + if (where & SSL_CB_HANDSHAKE_START) { - HandleScope scope(node_isolate); - Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl)); - if (onhandshakestart_sym.IsEmpty()) { - onhandshakestart_sym = - FIXED_ONE_BYTE_STRING(node_isolate, "onhandshakestart"); - } - MakeCallback(conn->handle(node_isolate), onhandshakestart_sym, 0, NULL); + MakeCallback(env, + conn->handle(node_isolate), + env->onhandshakestart_string()); } + if (where & SSL_CB_HANDSHAKE_DONE) { - HandleScope scope(node_isolate); - Connection* conn = static_cast<Connection*>(SSL_get_app_data(ssl)); - if (onhandshakedone_sym.IsEmpty()) { - onhandshakedone_sym = - FIXED_ONE_BYTE_STRING(node_isolate, "onhandshakedone"); - } - MakeCallback(conn->handle(node_isolate), onhandshakedone_sym, 0, NULL); + MakeCallback(env, + conn->handle(node_isolate), + env->onhandshakedone_string()); } } @@ -2060,9 +2042,7 @@ void Connection::SetSNICallback(const FunctionCallbackInfo<Value>& args) { #endif -void CipherBase::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - +void CipherBase::Initialize(Environment* env, Handle<Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -2215,7 +2195,8 @@ bool CipherBase::Update(const char* data, void CipherBase::Update(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This()); @@ -2247,7 +2228,7 @@ void CipherBase::Update(const FunctionCallbackInfo<Value>& args) { return ThrowCryptoTypeError(ERR_get_error()); } - Local<Object> buf = Buffer::New(reinterpret_cast<char*>(out), out_len); + Local<Object> buf = Buffer::New(env, reinterpret_cast<char*>(out), out_len); if (out) delete[] out; args.GetReturnValue().Set(buf); @@ -2280,7 +2261,8 @@ bool CipherBase::Final(unsigned char** out, int *out_len) { void CipherBase::Final(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); CipherBase* cipher = ObjectWrap::Unwrap<CipherBase>(args.This()); @@ -2298,13 +2280,11 @@ void CipherBase::Final(const FunctionCallbackInfo<Value>& args) { } args.GetReturnValue().Set( - Buffer::New(reinterpret_cast<char*>(out_value), out_len)); + Buffer::New(env, reinterpret_cast<char*>(out_value), out_len)); } -void Hmac::Initialize(v8::Handle<v8::Object> target) { - HandleScope scope(node_isolate); - +void Hmac::Initialize(Environment* env, v8::Handle<v8::Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -2434,9 +2414,7 @@ void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) { } -void Hash::Initialize(v8::Handle<v8::Object> target) { - HandleScope scope(node_isolate); - +void Hash::Initialize(Environment* env, v8::Handle<v8::Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -2543,9 +2521,7 @@ void Hash::HashDigest(const FunctionCallbackInfo<Value>& args) { } -void Sign::Initialize(v8::Handle<v8::Object> target) { - HandleScope scope(node_isolate); - +void Sign::Initialize(Environment* env, v8::Handle<v8::Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -2688,9 +2664,7 @@ void Sign::SignFinal(const FunctionCallbackInfo<Value>& args) { } -void Verify::Initialize(v8::Handle<v8::Object> target) { - HandleScope scope(node_isolate); - +void Verify::Initialize(Environment* env, v8::Handle<v8::Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -2891,9 +2865,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo<Value>& args) { } -void DiffieHellman::Initialize(v8::Handle<v8::Object> target) { - HandleScope scope(node_isolate); - +void DiffieHellman::Initialize(Environment* env, Handle<Object> target) { Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -3244,8 +3216,10 @@ bool DiffieHellman::VerifyContext() { } +// TODO(bnoordhuis) Turn into proper RAII class. struct pbkdf2_req { uv_work_t work_req; + Environment* env; int err; char* pass; size_t passlen; @@ -3299,7 +3273,9 @@ void EIO_PBKDF2After(pbkdf2_req* req, Local<Value> argv[2]) { void EIO_PBKDF2After(uv_work_t* work_req, int status) { assert(status == 0); pbkdf2_req* req = container_of(work_req, pbkdf2_req, work_req); - HandleScope scope(node_isolate); + Environment* env = req->env; + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // Create a new Local that's associated with the current HandleScope. // PersistentToLocal() returns a handle that gets zeroed when we call // Dispose() so don't use that. @@ -3307,12 +3283,13 @@ void EIO_PBKDF2After(uv_work_t* work_req, int status) { req->obj.Dispose(); Local<Value> argv[2]; EIO_PBKDF2After(req, argv); - MakeCallback(obj, "ondone", ARRAY_SIZE(argv), argv); + MakeCallback(env, obj, "ondone", ARRAY_SIZE(argv), argv); } void PBKDF2(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); const char* type_error = NULL; char* pass = NULL; @@ -3375,6 +3352,7 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) { } req = new pbkdf2_req; + req->env = env; req->err = 0; req->pass = pass; req->passlen = passlen; @@ -3387,9 +3365,11 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) { if (args[4]->IsFunction()) { Local<Object> obj = Object::New(); obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "ondone"), args[4]); - obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "domain"), GetDomain()); + if (env->in_domain()) { + obj->Set(env->domain_string(), env->domain_array()->Get(0)); + } req->obj.Reset(node_isolate, obj); - uv_queue_work(uv_default_loop(), + uv_queue_work(env->event_loop(), &req->work_req, EIO_PBKDF2, EIO_PBKDF2After); @@ -3411,8 +3391,10 @@ void PBKDF2(const FunctionCallbackInfo<Value>& args) { } +// TODO(bnoordhuis) Turn into proper RAII class. struct RandomBytesRequest { ~RandomBytesRequest(); + Environment* env_; Persistent<Object> obj_; unsigned long error_; // openssl error code or zero uv_work_t work_req_; @@ -3474,17 +3456,21 @@ void RandomBytesAfter(uv_work_t* work_req, int status) { RandomBytesRequest* req = container_of(work_req, RandomBytesRequest, work_req_); - HandleScope scope(node_isolate); + Environment* env = req->env_; + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Value> argv[2]; RandomBytesCheck(req, argv); - MakeCallback(req->obj_, "ondone", ARRAY_SIZE(argv), argv); + Local<Object> obj = PersistentToLocal(node_isolate, req->obj_); + MakeCallback(env, obj, "ondone", ARRAY_SIZE(argv), argv); delete req; } template <bool pseudoRandom> void RandomBytes(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); // maybe allow a buffer to write to? cuts down on object creation // when generating random data in a loop @@ -3498,6 +3484,7 @@ void RandomBytes(const FunctionCallbackInfo<Value>& args) { } RandomBytesRequest* req = new RandomBytesRequest(); + req->env_ = env; req->error_ = 0; req->size_ = size; req->data_ = static_cast<char*>(malloc(size)); @@ -3511,10 +3498,12 @@ void RandomBytes(const FunctionCallbackInfo<Value>& args) { if (args[1]->IsFunction()) { Local<Object> obj = Object::New(); obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "ondone"), args[1]); - obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "domain"), GetDomain()); + if (env->in_domain()) { + obj->Set(env->domain_string(), env->domain_array()->Get(0)); + } req->obj_.Reset(node_isolate, obj); - uv_queue_work(uv_default_loop(), + uv_queue_work(env->event_loop(), &req->work_req_, RandomBytesWork<pseudoRandom>, RandomBytesAfter); @@ -3588,9 +3577,7 @@ void GetHashes(const FunctionCallbackInfo<Value>& args) { } -void InitCrypto(Handle<Object> target) { - HandleScope scope(node_isolate); - +void InitCryptoOnce() { SSL_library_init(); OpenSSL_add_all_algorithms(); OpenSSL_add_all_digests(); @@ -3601,7 +3588,7 @@ void InitCrypto(Handle<Object> target) { CRYPTO_set_locking_callback(crypto_lock_cb); CRYPTO_THREADID_set_callback(crypto_threadid_cb); - // Turn off compression. Saves memory - do it in userland. + // Turn off compression. Saves memory and protects against BEAST attacks. #if !defined(OPENSSL_NO_COMP) #if OPENSSL_VERSION_NUMBER < 0x00908000L STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_method(); @@ -3611,15 +3598,25 @@ void InitCrypto(Handle<Object> target) { sk_SSL_COMP_zero(comp_methods); assert(sk_SSL_COMP_num(comp_methods) == 0); #endif +} + - SecureContext::Initialize(target); - Connection::Initialize(target); - CipherBase::Initialize(target); - DiffieHellman::Initialize(target); - Hmac::Initialize(target); - Hash::Initialize(target); - Sign::Initialize(target); - Verify::Initialize(target); +// FIXME(bnoordhuis) Handle global init correctly. +void InitCrypto(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + static uv_once_t init_once = UV_ONCE_INIT; + uv_once(&init_once, InitCryptoOnce); + + Environment* env = Environment::GetCurrent(context); + SecureContext::Initialize(env, target); + Connection::Initialize(env, target); + CipherBase::Initialize(env, target); + DiffieHellman::Initialize(env, target); + Hmac::Initialize(env, target); + Hash::Initialize(env, target); + Sign::Initialize(env, target); + Verify::Initialize(env, target); NODE_SET_METHOD(target, "PBKDF2", PBKDF2); NODE_SET_METHOD(target, "randomBytes", RandomBytes<false>); @@ -3627,21 +3624,9 @@ void InitCrypto(Handle<Object> target) { NODE_SET_METHOD(target, "getSSLCiphers", GetSSLCiphers); NODE_SET_METHOD(target, "getCiphers", GetCiphers); NODE_SET_METHOD(target, "getHashes", GetHashes); - - subject_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "subject"); - issuer_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "issuer"); - valid_from_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "valid_from"); - valid_to_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "valid_to"); - subjectaltname_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "subjectaltname"); - modulus_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "modulus"); - exponent_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "exponent"); - fingerprint_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "fingerprint"); - name_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "name"); - version_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "version"); - ext_key_usage_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "ext_key_usage"); } } // namespace crypto } // namespace node -NODE_MODULE(node_crypto, node::crypto::InitCrypto) +NODE_MODULE_CONTEXT_AWARE(node_crypto, node::crypto::InitCrypto) diff --git a/src/node_crypto.h b/src/node_crypto.h index b1c2c7fb07..2ae8794031 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -31,6 +31,7 @@ #include "node_buffer.h" #endif +#include "env.h" #include "v8.h" #include <openssl/ssl.h> @@ -58,10 +59,14 @@ class Connection; class SecureContext : ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); + + inline Environment* env() const { + return env_; + } - SSL_CTX* ctx_; X509_STORE* ca_store_; + SSL_CTX* ctx_; static const int kMaxSessionSize = 10 * 1024; @@ -85,9 +90,11 @@ class SecureContext : ObjectWrap { static void GetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args); static void SetTicketKeys(const v8::FunctionCallbackInfo<v8::Value>& args); - SecureContext() : ObjectWrap() { - ctx_ = NULL; - ca_store_ = NULL; + explicit SecureContext(Environment* env) + : ObjectWrap() + , ca_store_(NULL) + , ctx_(NULL) + , env_(env) { } void FreeCTXMem() { @@ -112,6 +119,7 @@ class SecureContext : ObjectWrap { } private: + Environment* const env_; }; template <class Base> @@ -122,9 +130,11 @@ class SSLWrap { kServer }; - SSLWrap(SecureContext* sc, Kind kind) : kind_(kind), - next_sess_(NULL), - session_callbacks_(false) { + SSLWrap(Environment* env, SecureContext* sc, Kind kind) + : env_(env) + , kind_(kind) + , next_sess_(NULL) + , session_callbacks_(false) { ssl_ = SSL_new(sc->ctx_); assert(ssl_ != NULL); } @@ -190,6 +200,11 @@ class SSLWrap { void* arg); #endif // OPENSSL_NPN_NEGOTIATED + inline Environment* env() const { + return env_; + } + + Environment* const env_; Kind kind_; SSL_SESSION* next_sess_; SSL* ssl_; @@ -206,7 +221,7 @@ class SSLWrap { class Connection : public SSLWrap<Connection>, public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); #ifdef OPENSSL_NPN_NEGOTIATED v8::Persistent<v8::Object> npnProtos_; @@ -263,10 +278,13 @@ class Connection : public SSLWrap<Connection>, public ObjectWrap { return conn; } - Connection(SecureContext* sc, SSLWrap<Connection>::Kind kind) - : SSLWrap<Connection>(sc, kind), - hello_offset_(0) { - bio_read_ = bio_write_ = NULL; + Connection(Environment* env, + SecureContext* sc, + SSLWrap<Connection>::Kind kind) + : SSLWrap<Connection>(env, sc, kind) + , bio_read_(NULL) + , bio_write_(NULL) + , hello_offset_(0) { hello_parser_.Start(SSLWrap<Connection>::OnClientHello, OnClientHelloParseEnd, this); @@ -296,7 +314,7 @@ class Connection : public SSLWrap<Connection>, public ObjectWrap { class CipherBase : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); protected: enum CipherKind { @@ -340,7 +358,7 @@ class CipherBase : public ObjectWrap { class Hmac : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); protected: void HmacInit(const char* hash_type, const char* key, int key_len); @@ -368,7 +386,7 @@ class Hmac : public ObjectWrap { class Hash : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); bool HashInit(const char* hash_type); bool HashUpdate(const char* data, int len); @@ -394,7 +412,7 @@ class Hash : public ObjectWrap { class Sign : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); void SignInit(const char* sign_type); bool SignUpdate(const char* data, int len); @@ -425,7 +443,7 @@ class Sign : public ObjectWrap { class Verify : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); void VerifyInit(const char* verify_type); bool VerifyUpdate(const char* data, int len); @@ -456,7 +474,7 @@ class Verify : public ObjectWrap { class DiffieHellman : public ObjectWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(Environment* env, v8::Handle<v8::Object> target); bool Init(int primeLength); bool Init(const char* p, int p_len); diff --git a/src/node_file.cc b/src/node_file.cc index c29c0fda50..fa49dd4bcc 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -24,6 +24,9 @@ #include "node_buffer.h" #include "node_internals.h" #include "node_stat_watcher.h" + +#include "env.h" +#include "env-inl.h" #include "req_wrap.h" #include "string_bytes.h" @@ -42,6 +45,7 @@ namespace node { using v8::Array; +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -51,7 +55,6 @@ using v8::Integer; using v8::Local; using v8::Number; using v8::Object; -using v8::Persistent; using v8::String; using v8::Value; @@ -63,8 +66,9 @@ using v8::Value; class FSReqWrap: public ReqWrap<uv_fs_t> { public: - explicit FSReqWrap(const char* syscall, char* data = NULL) - : syscall_(syscall) + FSReqWrap(Environment* env, const char* syscall, char* data = NULL) + : ReqWrap<uv_fs_t>(env) + , syscall_(syscall) , data_(data) { } @@ -82,9 +86,6 @@ class FSReqWrap: public ReqWrap<uv_fs_t> { }; -static Cached<String> oncomplete_sym; - - #define ASSERT_OFFSET(a) \ if (!(a)->IsUndefined() && !(a)->IsNull() && !IsInt64((a)->NumberValue())) { \ return ThrowTypeError("Not an integer"); \ @@ -102,12 +103,14 @@ static inline int IsInt64(double x) { static void After(uv_fs_t *req) { - HandleScope scope(node_isolate); - FSReqWrap* req_wrap = static_cast<FSReqWrap*>(req->data); assert(&req_wrap->req_ == req); req_wrap->ReleaseEarly(); // Free memory that's no longer used now. + Environment* env = req_wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + // there is always at least one argument. "error" int argc = 1; @@ -168,7 +171,8 @@ static void After(uv_fs_t *req) { case UV_FS_STAT: case UV_FS_LSTAT: case UV_FS_FSTAT: - argv[1] = BuildStatsObject(static_cast<const uv_stat_t*>(req->ptr)); + argv[1] = BuildStatsObject(env, + static_cast<const uv_stat_t*>(req->ptr)); break; case UV_FS_READLINK: @@ -209,7 +213,7 @@ static void After(uv_fs_t *req) { } } - MakeCallback(req_wrap->object(), oncomplete_sym, argc, argv); + MakeCallback(env, req_wrap->object(), env->oncomplete_string(), argc, argv); uv_fs_req_cleanup(&req_wrap->req_); delete req_wrap; @@ -227,27 +231,31 @@ struct fs_req_wrap { }; -#define ASYNC_CALL(func, callback, ...) \ - FSReqWrap* req_wrap = new FSReqWrap(#func); \ - int err = uv_fs_ ## func(uv_default_loop(), &req_wrap->req_, \ - __VA_ARGS__, After); \ - req_wrap->object()->Set(oncomplete_sym, callback); \ - req_wrap->Dispatched(); \ - if (err < 0) { \ - uv_fs_t* req = &req_wrap->req_; \ - req->result = err; \ - req->path = NULL; \ - After(req); \ - } \ +#define ASYNC_CALL(func, callback, ...) \ + Environment* env = Environment::GetCurrent(args.GetIsolate()); \ + FSReqWrap* req_wrap = new FSReqWrap(env, #func); \ + int err = uv_fs_ ## func(env->event_loop(), \ + &req_wrap->req_, \ + __VA_ARGS__, \ + After); \ + req_wrap->object()->Set(env->oncomplete_string(), callback); \ + req_wrap->Dispatched(); \ + if (err < 0) { \ + uv_fs_t* req = &req_wrap->req_; \ + req->result = err; \ + req->path = NULL; \ + After(req); \ + } \ args.GetReturnValue().Set(req_wrap->persistent()); -#define SYNC_CALL(func, path, ...) \ - fs_req_wrap req_wrap; \ - int err = uv_fs_ ## func(uv_default_loop(), \ - &req_wrap.req, \ - __VA_ARGS__, \ - NULL); \ - if (err < 0) return ThrowUVException(err, #func, NULL, path); \ +#define SYNC_CALL(func, path, ...) \ + fs_req_wrap req_wrap; \ + Environment* env = Environment::GetCurrent(args.GetIsolate()); \ + int err = uv_fs_ ## func(env->event_loop(), \ + &req_wrap.req, \ + __VA_ARGS__, \ + NULL); \ + if (err < 0) return ThrowUVException(err, #func, NULL, path); \ #define SYNC_REQ req_wrap.req @@ -271,47 +279,16 @@ static void Close(const FunctionCallbackInfo<Value>& args) { } -static Persistent<Function> stats_constructor; - -static Cached<String> dev_symbol; -static Cached<String> ino_symbol; -static Cached<String> mode_symbol; -static Cached<String> nlink_symbol; -static Cached<String> uid_symbol; -static Cached<String> gid_symbol; -static Cached<String> rdev_symbol; -static Cached<String> size_symbol; -static Cached<String> blksize_symbol; -static Cached<String> blocks_symbol; -static Cached<String> atime_symbol; -static Cached<String> mtime_symbol; -static Cached<String> ctime_symbol; -static Cached<String> birthtime_symbol; - -Local<Object> BuildStatsObject(const uv_stat_t* s) { - HandleScope scope(node_isolate); +Local<Object> BuildStatsObject(Environment* env, const uv_stat_t* s) { + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); - if (dev_symbol.IsEmpty()) { - dev_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "dev"); - ino_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "ino"); - mode_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "mode"); - nlink_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "nlink"); - uid_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "uid"); - gid_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "gid"); - rdev_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "rdev"); - size_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "size"); - blksize_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "blksize"); - blocks_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "blocks"); - atime_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "atime"); - mtime_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "mtime"); - ctime_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "ctime"); - birthtime_symbol = FIXED_ONE_BYTE_STRING(node_isolate, "birthtime"); - } + HandleScope handle_scope(env->isolate()); - Local<Function> constructor = - PersistentToLocal(node_isolate, stats_constructor); - Local<Object> stats = constructor->NewInstance(); - if (stats.IsEmpty()) return Local<Object>(); + Local<Object> stats = env->stats_constructor_function()->NewInstance(); + if (stats.IsEmpty()) { + return Local<Object>(); + } // The code below is very nasty-looking but it prevents a segmentation fault // when people run JS code like the snippet below. It's apparently more @@ -328,7 +305,7 @@ Local<Object> BuildStatsObject(const uv_stat_t* s) { { \ Local<Value> val = Integer::New(s->st_##name, node_isolate); \ if (val.IsEmpty()) return Local<Object>(); \ - stats->Set(name##_symbol, val); \ + stats->Set(env->name ## _string(), val); \ } X(dev) X(mode) @@ -345,7 +322,7 @@ Local<Object> BuildStatsObject(const uv_stat_t* s) { { \ Local<Value> val = Number::New(static_cast<double>(s->st_##name)); \ if (val.IsEmpty()) return Local<Object>(); \ - stats->Set(name##_symbol, val); \ + stats->Set(env->name ## _string(), val); \ } X(ino) X(size) @@ -360,7 +337,7 @@ Local<Object> BuildStatsObject(const uv_stat_t* s) { msecs += static_cast<double>(s->st_##rec.tv_nsec / 1000000); \ Local<Value> val = v8::Date::New(msecs); \ if (val.IsEmpty()) return Local<Object>(); \ - stats->Set(name##_symbol, val); \ + stats->Set(env->name ## _string(), val); \ } X(atime, atim) X(mtime, mtim) @@ -368,7 +345,7 @@ Local<Object> BuildStatsObject(const uv_stat_t* s) { X(birthtime, birthtim) #undef X - return scope.Close(stats); + return handle_scope.Close(stats); } static void Stat(const FunctionCallbackInfo<Value>& args) { @@ -384,7 +361,7 @@ static void Stat(const FunctionCallbackInfo<Value>& args) { } else { SYNC_CALL(stat, *path, *path) args.GetReturnValue().Set( - BuildStatsObject(static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); + BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); } } @@ -401,7 +378,7 @@ static void LStat(const FunctionCallbackInfo<Value>& args) { } else { SYNC_CALL(lstat, *path, *path) args.GetReturnValue().Set( - BuildStatsObject(static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); + BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); } } @@ -419,7 +396,7 @@ static void FStat(const FunctionCallbackInfo<Value>& args) { } else { SYNC_CALL(fstat, 0, fd) args.GetReturnValue().Set( - BuildStatsObject(static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); + BuildStatsObject(env, static_cast<const uv_stat_t*>(SYNC_REQ.ptr))); } } @@ -719,7 +696,8 @@ static void WriteBuffer(const FunctionCallbackInfo<Value>& args) { // if null, write from the current position // 3 enc encoding of string static void WriteString(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); if (!args[0]->IsInt32()) return ThrowTypeError("First argument must be file descriptor"); @@ -753,15 +731,15 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) { return args.GetReturnValue().Set(SYNC_RESULT); } - FSReqWrap* req_wrap = new FSReqWrap("write", must_free ? buf : NULL); - int err = uv_fs_write(uv_default_loop(), + FSReqWrap* req_wrap = new FSReqWrap(env, "write", must_free ? buf : NULL); + int err = uv_fs_write(env->event_loop(), &req_wrap->req_, fd, buf, len, pos, After); - req_wrap->object()->Set(oncomplete_sym, cb); + req_wrap->object()->Set(env->oncomplete_string(), cb); req_wrap->Dispatched(); if (err < 0) { uv_fs_t* req = &req_wrap->req_; @@ -972,8 +950,15 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) { } -void File::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); +void InitFs(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); + + // Initialize the stats object + Local<Function> constructor = FunctionTemplate::New()->GetFunction(); + target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Stats"), constructor); + env->set_stats_constructor_function(constructor); NODE_SET_METHOD(target, "close", Close); NODE_SET_METHOD(target, "open", Open); @@ -1005,23 +990,10 @@ void File::Initialize(Handle<Object> target) { NODE_SET_METHOD(target, "utimes", UTimes); NODE_SET_METHOD(target, "futimes", FUTimes); -} - -void InitFs(Handle<Object> target) { - HandleScope scope(node_isolate); - - // Initialize the stats object - Local<Function> constructor = FunctionTemplate::New()->GetFunction(); - target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Stats"), constructor); - stats_constructor.Reset(node_isolate, constructor); - - File::Initialize(target); - - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); StatWatcher::Initialize(target); } } // end namespace node -NODE_MODULE(node_fs, node::InitFs) +NODE_MODULE_CONTEXT_AWARE(node_fs, node::InitFs) diff --git a/src/node_file.h b/src/node_file.h index 0a5d270101..dc5deedb0a 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -27,11 +27,6 @@ namespace node { -class File { - public: - static void Initialize(v8::Handle<v8::Object> target); -}; - void InitFs(v8::Handle<v8::Object> target); } // namespace node diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 80485ec05f..80046a9b7b 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -22,6 +22,9 @@ #include "node.h" #include "node_buffer.h" #include "node_http_parser.h" + +#include "env.h" +#include "env-inl.h" #include "v8.h" #include <stdlib.h> // free() @@ -48,6 +51,7 @@ namespace node { using v8::Array; +using v8::Context; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; @@ -65,32 +69,6 @@ const uint32_t kOnHeadersComplete = 1; const uint32_t kOnBody = 2; const uint32_t kOnMessageComplete = 3; -static Cached<String> method_sym; -static Cached<String> status_code_sym; -static Cached<String> http_version_sym; -static Cached<String> version_major_sym; -static Cached<String> version_minor_sym; -static Cached<String> should_keep_alive_sym; -static Cached<String> upgrade_sym; -static Cached<String> headers_sym; -static Cached<String> url_sym; - -static Cached<String> unknown_method_sym; - -#define X(num, name, string) static Cached<String> name##_sym; -HTTP_METHOD_MAP(X) -#undef X - -static struct http_parser_settings settings; - - -// This is a hack to get the current_buffer to the callbacks with the least -// amount of overhead. Nothing else will run while http_parser_execute() -// runs, therefore this pointer can be set and used for the execution. -static Local<Value>* current_buffer; -static char* current_buffer_data; -static size_t current_buffer_len; - #define HTTP_CB(name) \ static int name(http_parser* p_) { \ @@ -108,14 +86,30 @@ static size_t current_buffer_len; int name##_(const char* at, size_t length) -static inline Handle<String> -method_to_str(unsigned short m) { - switch (m) { -#define X(num, name, string) case HTTP_##name: return name##_sym; - HTTP_METHOD_MAP(X) -#undef X +// Call this function only when there is a valid HandleScope on the stack +// somewhere. +inline Local<String> MethodToString(Environment* env, uint32_t method) { + // XXX(bnoordhuis) Predicated on the observation that 99.9% of all HTTP + // requests are either GET, HEAD or POST. I threw in DELETE and PUT for + // good measure. + switch (method) { + case HTTP_DELETE: return env->DELETE_string(); + case HTTP_GET: return env->GET_string(); + case HTTP_HEAD: return env->HEAD_string(); + case HTTP_POST: return env->POST_string(); + case HTTP_PUT: return env->PUT_string(); } - return unknown_method_sym; + + switch (method) { +#define V(num, name, string) \ + case HTTP_ ## name: \ + return FIXED_ONE_BYTE_STRING(node_isolate, #string); + HTTP_METHOD_MAP(V) +#undef V + } + + // Unreachable, http_parser parses only a restricted set of request methods. + return FIXED_ONE_BYTE_STRING(node_isolate, "UNKNOWN_METHOD"); } @@ -193,7 +187,11 @@ struct StringPtr { class Parser : public ObjectWrap { public: - explicit Parser(enum http_parser_type type) : ObjectWrap() { + Parser(Environment* env, enum http_parser_type type) + : ObjectWrap() + , env_(env) + , current_buffer_len_(0) + , current_buffer_data_(NULL) { Init(type); } @@ -267,34 +265,35 @@ class Parser : public ObjectWrap { Flush(); } else { // Fast case, pass headers and URL to JS land. - message_info->Set(headers_sym, CreateHeaders()); + message_info->Set(env()->headers_string(), CreateHeaders()); if (parser_.type == HTTP_REQUEST) - message_info->Set(url_sym, url_.ToString()); + message_info->Set(env()->url_string(), url_.ToString()); } num_fields_ = num_values_ = 0; // METHOD if (parser_.type == HTTP_REQUEST) { - message_info->Set(method_sym, method_to_str(parser_.method)); + message_info->Set(env()->method_string(), + MethodToString(env(), parser_.method)); } // STATUS if (parser_.type == HTTP_RESPONSE) { - message_info->Set(status_code_sym, + message_info->Set(env()->status_code_string(), Integer::New(parser_.status_code, node_isolate)); } // VERSION - message_info->Set(version_major_sym, + message_info->Set(env()->version_major_string(), Integer::New(parser_.http_major, node_isolate)); - message_info->Set(version_minor_sym, + message_info->Set(env()->version_minor_string(), Integer::New(parser_.http_minor, node_isolate)); - message_info->Set(should_keep_alive_sym, + message_info->Set(env()->should_keep_alive_string(), http_should_keep_alive(&parser_) ? True(node_isolate) : False(node_isolate)); - message_info->Set(upgrade_sym, + message_info->Set(env()->upgrade_string(), parser_.upgrade ? True(node_isolate) : False(node_isolate)); @@ -321,9 +320,9 @@ class Parser : public ObjectWrap { return 0; Local<Value> argv[3] = { - *current_buffer, - Integer::New(at - current_buffer_data, node_isolate), - Integer::New(length, node_isolate) + current_buffer_, + Integer::NewFromUnsigned(at - current_buffer_data_, node_isolate), + Integer::NewFromUnsigned(length, node_isolate) }; Local<Value> r = cb.As<Function>()->Call(obj, ARRAY_SIZE(argv), argv); @@ -361,13 +360,14 @@ class Parser : public ObjectWrap { static void New(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); http_parser_type type = static_cast<http_parser_type>(args[0]->Int32Value()); assert(type == HTTP_REQUEST || type == HTTP_RESPONSE); - Parser* parser = new Parser(type); + Parser* parser = new Parser(env, type); parser->Wrap(args.This()); } @@ -390,28 +390,21 @@ class Parser : public ObjectWrap { HandleScope scope(node_isolate); Parser* parser = ObjectWrap::Unwrap<Parser>(args.This()); + assert(parser->current_buffer_.IsEmpty()); + assert(parser->current_buffer_len_ == 0); + assert(parser->current_buffer_data_ == NULL); + assert(Buffer::HasInstance(args[0]) == true); - assert(!current_buffer); - assert(!current_buffer_data); - - if (current_buffer) { - return ThrowTypeError("Already parsing a buffer"); - } - - Local<Value> buffer_v = args[0]; - - if (!Buffer::HasInstance(buffer_v)) { - return ThrowTypeError("Argument should be a buffer"); - } - - Local<Object> buffer_obj = buffer_v->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); + Local<Object> buffer_obj = args[0].As<Object>(); + char* buffer_data = Buffer::Data(buffer_obj); size_t buffer_len = Buffer::Length(buffer_obj); - // Assign 'buffer_' while we parse. The callbacks will access that varible. - current_buffer = &buffer_v; - current_buffer_data = buffer_data; - current_buffer_len = buffer_len; + // This is a hack to get the current_buffer to the callbacks with the least + // amount of overhead. Nothing else will run while http_parser_execute() + // runs, therefore this pointer can be set and used for the execution. + parser->current_buffer_ = buffer_obj; + parser->current_buffer_len_ = buffer_len; + parser->current_buffer_data_ = buffer_data; parser->got_exception_ = false; size_t nparsed = @@ -420,9 +413,9 @@ class Parser : public ObjectWrap { parser->Save(); // Unassign the 'buffer_' variable - assert(current_buffer); - current_buffer = NULL; - current_buffer_data = NULL; + parser->current_buffer_.Clear(); + parser->current_buffer_len_ = 0; + parser->current_buffer_data_ = NULL; // If there was an exception in one of the callbacks if (parser->got_exception_) return; @@ -439,6 +432,7 @@ class Parser : public ObjectWrap { obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "bytesParsed"), nparsed_obj); obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "code"), OneByteString(node_isolate, http_errno_name(err))); + args.GetReturnValue().Set(e); } else { args.GetReturnValue().Set(nparsed_obj); @@ -451,7 +445,7 @@ class Parser : public ObjectWrap { Parser* parser = ObjectWrap::Unwrap<Parser>(args.This()); - assert(!current_buffer); + assert(parser->current_buffer_.IsEmpty()); parser->got_exception_ = false; int rv = http_parser_execute(&(parser->parser_), &settings, NULL, 0); @@ -468,19 +462,23 @@ class Parser : public ObjectWrap { Integer::New(0, node_isolate)); obj->Set(FIXED_ONE_BYTE_STRING(node_isolate, "code"), OneByteString(node_isolate, http_errno_name(err))); + args.GetReturnValue().Set(e); } } static void Reinitialize(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); http_parser_type type = static_cast<http_parser_type>(args[0]->Int32Value()); assert(type == HTTP_REQUEST || type == HTTP_RESPONSE); Parser* parser = ObjectWrap::Unwrap<Parser>(args.This()); + // Should always be called from the same context. + assert(env == parser->env()); parser->Init(type); } @@ -536,6 +534,12 @@ class Parser : public ObjectWrap { } + inline Environment* env() const { + return env_; + } + + + Environment* const env_; http_parser parser_; StringPtr fields_[32]; // header fields StringPtr values_[32]; // header values @@ -544,12 +548,27 @@ class Parser : public ObjectWrap { int num_values_; bool have_flushed_; bool got_exception_; + Local<Object> current_buffer_; + size_t current_buffer_len_; + char* current_buffer_data_; + static const struct http_parser_settings settings; }; -void InitHttpParser(Handle<Object> target) { - HandleScope scope(node_isolate); +const struct http_parser_settings Parser::settings = { + Parser::on_message_begin, + Parser::on_url, + Parser::on_header_field, + Parser::on_header_value, + Parser::on_headers_complete, + Parser::on_body, + Parser::on_message_complete +}; + +void InitHttpParser(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { Local<FunctionTemplate> t = FunctionTemplate::New(Parser::New); t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "HTTPParser")); @@ -573,33 +592,8 @@ void InitHttpParser(Handle<Object> target) { target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "HTTPParser"), t->GetFunction()); - -#define X(num, name, string) \ - name ## _sym = OneByteString(node_isolate, #string); - HTTP_METHOD_MAP(X) -#undef X - unknown_method_sym = FIXED_ONE_BYTE_STRING(node_isolate, "UNKNOWN_METHOD"); - - method_sym = FIXED_ONE_BYTE_STRING(node_isolate, "method"); - status_code_sym = FIXED_ONE_BYTE_STRING(node_isolate, "statusCode"); - http_version_sym = FIXED_ONE_BYTE_STRING(node_isolate, "httpVersion"); - version_major_sym = FIXED_ONE_BYTE_STRING(node_isolate, "versionMajor"); - version_minor_sym = FIXED_ONE_BYTE_STRING(node_isolate, "versionMinor"); - should_keep_alive_sym = - FIXED_ONE_BYTE_STRING(node_isolate, "shouldKeepAlive"); - upgrade_sym = FIXED_ONE_BYTE_STRING(node_isolate, "upgrade"); - headers_sym = FIXED_ONE_BYTE_STRING(node_isolate, "headers"); - url_sym = FIXED_ONE_BYTE_STRING(node_isolate, "url"); - - settings.on_message_begin = Parser::on_message_begin; - settings.on_url = Parser::on_url; - settings.on_header_field = Parser::on_header_field; - settings.on_header_value = Parser::on_header_value; - settings.on_headers_complete = Parser::on_headers_complete; - settings.on_body = Parser::on_body; - settings.on_message_complete = Parser::on_message_complete; } } // namespace node -NODE_MODULE(node_http_parser, node::InitHttpParser) +NODE_MODULE_CONTEXT_AWARE(node_http_parser, node::InitHttpParser) diff --git a/src/node_internals.h b/src/node_internals.h index ba110a51c8..141bd92ff0 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -22,15 +22,16 @@ #ifndef SRC_NODE_INTERNALS_H_ #define SRC_NODE_INTERNALS_H_ +#include "env.h" +#include "util.h" +#include "util-inl.h" +#include "uv.h" #include "v8.h" #include <assert.h> #include <stdint.h> #include <stdlib.h> -#define FIXED_ONE_BYTE_STRING(isolate, string) \ - (node::OneByteString((isolate), (string), sizeof(string) - 1)) - struct sockaddr; namespace node { @@ -38,36 +39,6 @@ namespace node { // Defined in node.cc extern v8::Isolate* node_isolate; -// Defined in node.cc at startup. -extern v8::Persistent<v8::Object> process_p; - -template <typename TypeName> -class CachedBase { - public: - CachedBase(); - operator v8::Handle<TypeName>() const; - void operator=(v8::Handle<TypeName> that); // Can only assign once. - bool IsEmpty() const; - private: - CachedBase(const CachedBase&); - void operator=(const CachedBase&); - v8::Persistent<TypeName> handle_; -}; - -template <typename TypeName> -class Cached : public CachedBase<TypeName> { - public: - operator v8::Handle<v8::Value>() const; - void operator=(v8::Handle<TypeName> that); -}; - -template <> -class Cached<v8::Value> : public CachedBase<v8::Value> { - public: - operator v8::Handle<v8::Value>() const; - void operator=(v8::Handle<v8::Value> that); -}; - // If persistent.IsWeak() == false, then do not call persistent.Dispose() // while the returned Local<T> is still in scope, it will destroy the // reference to the object. @@ -76,55 +47,41 @@ inline v8::Local<TypeName> PersistentToLocal( v8::Isolate* isolate, const v8::Persistent<TypeName>& persistent); -v8::Handle<v8::Value> MakeCallback( - const v8::Handle<v8::Object> recv, - uint32_t index, - int argc, - v8::Handle<v8::Value>* argv); - -template <typename TypeName> -v8::Handle<v8::Value> MakeCallback( - const v8::Persistent<v8::Object>& recv, - const TypeName method, - int argc, - v8::Handle<v8::Value>* argv); - -template <typename TypeName> -v8::Handle<v8::Value> MakeCallback( - const v8::Persistent<v8::Object>& recv, - const Cached<TypeName>& method, - int argc, - v8::Handle<v8::Value>* argv); - -inline bool HasInstance( - const v8::Persistent<v8::FunctionTemplate>& function_template, - v8::Handle<v8::Value> value); - -inline v8::Local<v8::Object> NewInstance( - const v8::Persistent<v8::Function>& ctor, - int argc = 0, - v8::Handle<v8::Value>* argv = NULL); - -// Convenience wrapper around v8::String::NewFromOneByte(). -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const char* data, - int length = -1); - -// For the people that compile with -funsigned-char. -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const signed char* data, - int length = -1); - -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const unsigned char* data, - int length = -1); +// Call with valid HandleScope and while inside Context scope. +v8::Handle<v8::Value> MakeCallback(Environment* env, + const v8::Handle<v8::Object> object, + const char* method, + int argc = 0, + v8::Handle<v8::Value>* argv = NULL); + +// Call with valid HandleScope and while inside Context scope. +v8::Handle<v8::Value> MakeCallback(Environment* env, + const v8::Handle<v8::Object> object, + uint32_t index, + int argc = 0, + v8::Handle<v8::Value>* argv = NULL); + +// Call with valid HandleScope and while inside Context scope. +v8::Handle<v8::Value> MakeCallback(Environment* env, + const v8::Handle<v8::Object> object, + const v8::Handle<v8::String> symbol, + int argc = 0, + v8::Handle<v8::Value>* argv = NULL); + +// Call with valid HandleScope and while inside Context scope. +v8::Handle<v8::Value> MakeCallback(Environment* env, + const v8::Handle<v8::Object> object, + const v8::Handle<v8::Function> callback, + int argc = 0, + v8::Handle<v8::Value>* argv = NULL); // Convert a struct sockaddr to a { address: '1.2.3.4', port: 1234 } JS object. // Sets address and port properties on the info object and returns it. // If |info| is omitted, a new object is returned. v8::Local<v8::Object> AddressToJS( + Environment* env, const sockaddr* addr, - v8::Handle<v8::Object> info = v8::Handle<v8::Object>()); + v8::Local<v8::Object> info = v8::Handle<v8::Object>()); #ifdef _WIN32 // emulate snprintf() on windows, _snprintf() doesn't zero-terminate the buffer @@ -213,6 +170,8 @@ inline static void ThrowUVException(int errorno, NO_RETURN void FatalError(const char* location, const char* message); +v8::Local<v8::Object> BuildStatsObject(Environment* env, const uv_stat_t* s); + #define NODE_WRAP(Object, Pointer) \ do { \ assert(!Object.IsEmpty()); \ @@ -286,125 +245,6 @@ inline MUST_USE_RESULT bool ParseArrayIndex(v8::Handle<v8::Value> arg, return true; } -template <class TypeName> -inline v8::Local<TypeName> PersistentToLocal( - v8::Isolate* isolate, - const v8::Persistent<TypeName>& persistent) { - if (persistent.IsWeak()) { - return v8::Local<TypeName>::New(isolate, persistent); - } else { - return *reinterpret_cast<v8::Local<TypeName>*>( - const_cast<v8::Persistent<TypeName>*>(&persistent)); - } -} - -template <typename TypeName> -CachedBase<TypeName>::CachedBase() { -} - -template <typename TypeName> -CachedBase<TypeName>::operator v8::Handle<TypeName>() const { - return PersistentToLocal(node_isolate, handle_); -} - -template <typename TypeName> -void CachedBase<TypeName>::operator=(v8::Handle<TypeName> that) { - assert(handle_.IsEmpty() == true); // Can only assign once. - handle_.Reset(node_isolate, that); -} - -template <typename TypeName> -bool CachedBase<TypeName>::IsEmpty() const { - return handle_.IsEmpty(); -} - -template <typename TypeName> -Cached<TypeName>::operator v8::Handle<v8::Value>() const { - return CachedBase<TypeName>::operator v8::Handle<TypeName>(); -} - -template <typename TypeName> -void Cached<TypeName>::operator=(v8::Handle<TypeName> that) { - CachedBase<TypeName>::operator=(that); -} - -inline Cached<v8::Value>::operator v8::Handle<v8::Value>() const { - return CachedBase<v8::Value>::operator v8::Handle<v8::Value>(); -} - -inline void Cached<v8::Value>::operator=(v8::Handle<v8::Value> that) { - CachedBase<v8::Value>::operator=(that); -} - -template <typename TypeName> -v8::Handle<v8::Value> MakeCallback( - const v8::Persistent<v8::Object>& recv, - const TypeName method, - int argc, - v8::Handle<v8::Value>* argv) { - v8::Local<v8::Object> recv_obj = PersistentToLocal(node_isolate, recv); - return MakeCallback(recv_obj, method, argc, argv); -} - -template <typename TypeName> -v8::Handle<v8::Value> MakeCallback( - const v8::Persistent<v8::Object>& recv, - const Cached<TypeName>& method, - int argc, - v8::Handle<v8::Value>* argv) { - const v8::Handle<TypeName> handle = method; - return MakeCallback(recv, handle, argc, argv); -} - -inline bool HasInstance( - const v8::Persistent<v8::FunctionTemplate>& function_template, - v8::Handle<v8::Value> value) { - if (function_template.IsEmpty()) return false; - v8::Local<v8::FunctionTemplate> function_template_handle = - PersistentToLocal(node_isolate, function_template); - return function_template_handle->HasInstance(value); -} - -inline v8::Local<v8::Object> NewInstance( - const v8::Persistent<v8::Function>& ctor, - int argc, - v8::Handle<v8::Value>* argv) { - v8::Local<v8::Function> constructor_handle = - PersistentToLocal(node_isolate, ctor); - return constructor_handle->NewInstance(argc, argv); -} - -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const char* data, - int length) { - return v8::String::NewFromOneByte(isolate, - reinterpret_cast<const uint8_t*>(data), - v8::String::kNormalString, - length); -} - -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const signed char* data, - int length) { - return v8::String::NewFromOneByte(isolate, - reinterpret_cast<const uint8_t*>(data), - v8::String::kNormalString, - length); -} - -inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, - const unsigned char* data, - int length) { - return v8::String::NewFromOneByte(isolate, - reinterpret_cast<const uint8_t*>(data), - v8::String::kNormalString, - length); -} - -bool InDomain(); - -v8::Handle<v8::Value> GetDomain(); - } // namespace node #endif // SRC_NODE_INTERNALS_H_ diff --git a/src/node_os.cc b/src/node_os.cc index 36506fc6ed..e3d9971e3e 100644 --- a/src/node_os.cc +++ b/src/node_os.cc @@ -46,6 +46,7 @@ namespace node { namespace os { using v8::Array; +using v8::Context; using v8::FunctionCallbackInfo; using v8::Handle; using v8::HandleScope; @@ -275,9 +276,9 @@ static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) { } -void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - +void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { NODE_SET_METHOD(target, "getEndianness", GetEndianness); NODE_SET_METHOD(target, "getHostname", GetHostname); NODE_SET_METHOD(target, "getLoadAvg", GetLoadAvg); @@ -293,4 +294,4 @@ void Initialize(Handle<Object> target) { } // namespace os } // namespace node -NODE_MODULE(node_os, node::os::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_os, node::os::Initialize) diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index fb3cf4f8aa..1cd13c0847 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -20,6 +20,8 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "node_stat_watcher.h" +#include "env.h" +#include "env-inl.h" #include <assert.h> #include <string.h> @@ -27,6 +29,7 @@ namespace node { +using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; @@ -37,9 +40,6 @@ using v8::Object; using v8::String; using v8::Value; -static Cached<String> onchange_sym; -static Cached<String> onstop_sym; - void StatWatcher::Initialize(Handle<Object> target) { HandleScope scope(node_isolate); @@ -61,9 +61,11 @@ static void Delete(uv_handle_t* handle) { } -StatWatcher::StatWatcher() : ObjectWrap(), - watcher_(new uv_fs_poll_t) { - uv_fs_poll_init(uv_default_loop(), watcher_); +StatWatcher::StatWatcher(Environment* env) + : ObjectWrap() + , watcher_(new uv_fs_poll_t) + , env_(env) { + uv_fs_poll_init(env->event_loop(), watcher_); watcher_->data = static_cast<void*>(this); } @@ -80,16 +82,17 @@ void StatWatcher::Callback(uv_fs_poll_t* handle, const uv_stat_t* curr) { StatWatcher* wrap = static_cast<StatWatcher*>(handle->data); assert(wrap->watcher_ == handle); - HandleScope scope(node_isolate); - Local<Value> argv[3]; - argv[0] = BuildStatsObject(curr); - argv[1] = BuildStatsObject(prev); - argv[2] = Integer::New(status, node_isolate); - if (onchange_sym.IsEmpty()) { - onchange_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onchange"); - } - MakeCallback(wrap->handle(node_isolate), - onchange_sym, + Environment* env = wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + Local<Value> argv[] = { + BuildStatsObject(env, curr), + BuildStatsObject(env, prev), + Integer::New(status, node_isolate) + }; + MakeCallback(env, + wrap->handle(node_isolate), + env->onchange_string(), ARRAY_SIZE(argv), argv); } @@ -97,8 +100,9 @@ void StatWatcher::Callback(uv_fs_poll_t* handle, void StatWatcher::New(const FunctionCallbackInfo<Value>& args) { assert(args.IsConstructCall()); - HandleScope scope(node_isolate); - StatWatcher* s = new StatWatcher(); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + StatWatcher* s = new StatWatcher(env); s->Wrap(args.This()); } @@ -119,12 +123,11 @@ void StatWatcher::Start(const FunctionCallbackInfo<Value>& args) { void StatWatcher::Stop(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); StatWatcher* wrap = ObjectWrap::Unwrap<StatWatcher>(args.This()); - if (onstop_sym.IsEmpty()) { - onstop_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onstop"); - } - MakeCallback(wrap->handle(node_isolate), onstop_sym, 0, NULL); + Environment* env = wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + MakeCallback(env, wrap->handle(node_isolate), env->onstop_string()); wrap->Stop(); } diff --git a/src/node_stat_watcher.h b/src/node_stat_watcher.h index 082608f76a..fd38c8b42f 100644 --- a/src/node_stat_watcher.h +++ b/src/node_stat_watcher.h @@ -22,17 +22,21 @@ #ifndef SRC_NODE_STAT_WATCHER_H_ #define SRC_NODE_STAT_WATCHER_H_ -#include "node.h" +#include "node_object_wrap.h" + +#include "env.h" #include "uv.h" +#include "v8.h" namespace node { class StatWatcher : ObjectWrap { public: static void Initialize(v8::Handle<v8::Object> target); + inline Environment* env() const { return env_; } protected: - StatWatcher(); + explicit StatWatcher(Environment* env); virtual ~StatWatcher(); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); @@ -47,6 +51,7 @@ class StatWatcher : ObjectWrap { void Stop(); uv_fs_poll_t* watcher_; + Environment* const env_; }; } // namespace node diff --git a/src/node_version.h b/src/node_version.h index e02b932523..a355cfac53 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -64,6 +64,6 @@ * an API is broken in the C++ side, including in v8 or * other dependencies. */ -#define NODE_MODULE_VERSION 0x000C /* v0.12 */ +#define NODE_MODULE_VERSION 13 /* v0.12 */ #endif /* SRC_NODE_VERSION_H_ */ diff --git a/src/node_wrap.h b/src/node_wrap.h index 8839a7a50b..0f56f6ddf7 100644 --- a/src/node_wrap.h +++ b/src/node_wrap.h @@ -22,38 +22,39 @@ #ifndef SRC_NODE_WRAP_H_ #define SRC_NODE_WRAP_H_ -#include "v8.h" -#include "uv.h" - +#include "env.h" +#include "env-inl.h" #include "pipe_wrap.h" -#include "tty_wrap.h" #include "tcp_wrap.h" +#include "tty_wrap.h" #include "udp_wrap.h" +#include "uv.h" +#include "v8.h" namespace node { -extern v8::Persistent<v8::FunctionTemplate> pipeConstructorTmpl; -extern v8::Persistent<v8::FunctionTemplate> ttyConstructorTmpl; -extern v8::Persistent<v8::FunctionTemplate> tcpConstructorTmpl; - -#define WITH_GENERIC_STREAM(obj, BODY) \ - do { \ - if (HasInstance(tcpConstructorTmpl, obj)) { \ - TCPWrap* const wrap = TCPWrap::Unwrap(obj); \ - BODY \ - } else if (HasInstance(ttyConstructorTmpl, obj)) { \ - TTYWrap* const wrap = TTYWrap::Unwrap(obj); \ - BODY \ - } else if (HasInstance(pipeConstructorTmpl, obj)) { \ - PipeWrap* const wrap = PipeWrap::Unwrap(obj); \ - BODY \ - } \ +#define WITH_GENERIC_STREAM(env, obj, BODY) \ + do { \ + if (env->tcp_constructor_template().IsEmpty() == false && \ + env->tcp_constructor_template()->HasInstance(obj)) { \ + TCPWrap* const wrap = TCPWrap::Unwrap(obj); \ + BODY \ + } else if (env->tty_constructor_template().IsEmpty() == false && \ + env->tty_constructor_template()->HasInstance(obj)) { \ + TTYWrap* const wrap = TTYWrap::Unwrap(obj); \ + BODY \ + } else if (env->pipe_constructor_template().IsEmpty() == false && \ + env->pipe_constructor_template()->HasInstance(obj)) { \ + PipeWrap* const wrap = PipeWrap::Unwrap(obj); \ + BODY \ + } \ } while (0) -inline uv_stream_t* HandleToStream(v8::Local<v8::Object> obj) { +inline uv_stream_t* HandleToStream(Environment* env, + v8::Local<v8::Object> obj) { v8::HandleScope scope(node_isolate); - WITH_GENERIC_STREAM(obj, { + WITH_GENERIC_STREAM(env, obj, { return reinterpret_cast<uv_stream_t*>(wrap->UVHandle()); }); diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 91bfb0dd3d..ebc9d2b916 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -21,6 +21,9 @@ #include "node.h" #include "node_buffer.h" + +#include "env.h" +#include "env-inl.h" #include "v8.h" #include "zlib.h" @@ -31,6 +34,7 @@ namespace node { +using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; @@ -42,9 +46,6 @@ using v8::Object; using v8::String; using v8::Value; -static Cached<String> callback_sym; -static Cached<String> onerror_sym; - enum node_zlib_mode { NONE, DEFLATE, @@ -66,19 +67,21 @@ void InitZlib(v8::Handle<v8::Object> target); class ZCtx : public ObjectWrap { public: - explicit ZCtx(node_zlib_mode mode) : ObjectWrap(), - init_done_(false), - level_(0), - windowBits_(0), - memLevel_(0), - strategy_(0), - err_(0), - dictionary_(NULL), - dictionary_len_(0), - flush_(0), - chunk_size_(0), - write_in_progress_(false), - mode_(mode) { + ZCtx(Environment* env, node_zlib_mode mode) + : ObjectWrap() + , chunk_size_(0) + , dictionary_(NULL) + , dictionary_len_(0) + , env_(env) + , err_(0) + , flush_(0) + , init_done_(false) + , level_(0) + , memLevel_(0) + , mode_(mode) + , strategy_(0) + , windowBits_(0) + , write_in_progress_(false) { } @@ -87,6 +90,10 @@ class ZCtx : public ObjectWrap { } + inline Environment* env() const { + return env_; + } + void Close() { assert(!write_in_progress_ && "write in progress"); assert(init_done_ && "close before init"); @@ -182,7 +189,7 @@ class ZCtx : public ObjectWrap { // set this so that later on, I can easily tell how much was written. ctx->chunk_size_ = out_len; - uv_queue_work(uv_default_loop(), + uv_queue_work(ctx->env()->event_loop(), work_req, ZCtx::Process, ZCtx::After); @@ -245,8 +252,11 @@ class ZCtx : public ObjectWrap { static void After(uv_work_t* work_req, int status) { assert(status == 0); - HandleScope scope(node_isolate); - ZCtx *ctx = container_of(work_req, ZCtx, work_req_); + ZCtx* ctx = container_of(work_req, ZCtx, work_req_); + Environment* env = ctx->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // Acceptable error states depend on the type of zlib stream. switch (ctx->err_) { @@ -275,29 +285,29 @@ class ZCtx : public ObjectWrap { // call the write() cb Local<Object> handle = ctx->handle(node_isolate); - assert(handle->Get(callback_sym)->IsFunction() && "Invalid callback"); Local<Value> args[2] = { avail_in, avail_out }; - MakeCallback(handle, callback_sym, ARRAY_SIZE(args), args); + MakeCallback(env, handle, env->callback_string(), ARRAY_SIZE(args), args); ctx->Unref(); } - static void Error(ZCtx *ctx, const char *msg_) { - const char *msg; + static void Error(ZCtx* ctx, const char* message) { + Environment* env = ctx->env(); + + // If you hit this assertion, you forgot to enter the v8::Context first. + assert(env->context() == env->isolate()->GetCurrentContext()); + if (ctx->strm_.msg != NULL) { - msg = ctx->strm_.msg; - } else { - msg = msg_; + message = ctx->strm_.msg; } Local<Object> handle = ctx->handle(node_isolate); - assert(handle->Get(onerror_sym)->IsFunction() && "Invalid error handler"); HandleScope scope(node_isolate); Local<Value> args[2] = { - OneByteString(node_isolate, msg), + OneByteString(node_isolate, message), Number::New(ctx->err_) }; - MakeCallback(handle, onerror_sym, ARRAY_SIZE(args), args); + MakeCallback(env, handle, env->onerror_string(), ARRAY_SIZE(args), args); // no hope of rescue. ctx->write_in_progress_ = false; @@ -305,7 +315,9 @@ class ZCtx : public ObjectWrap { } static void New(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + if (args.Length() < 1 || !args[0]->IsInt32()) { return ThrowTypeError("Bad argument"); } @@ -315,7 +327,7 @@ class ZCtx : public ObjectWrap { return ThrowTypeError("Bad argument"); } - ZCtx *ctx = new ZCtx(mode); + ZCtx* ctx = new ZCtx(env, mode); ctx->Wrap(args.This()); } @@ -506,33 +518,27 @@ class ZCtx : public ObjectWrap { static const int kDeflateContextSize = 16384; // approximate static const int kInflateContextSize = 10240; // approximate + int chunk_size_; + Bytef* dictionary_; + size_t dictionary_len_; + Environment* const env_; + int err_; + int flush_; bool init_done_; - - z_stream strm_; int level_; - int windowBits_; int memLevel_; + node_zlib_mode mode_; int strategy_; - - int err_; - - Bytef* dictionary_; - size_t dictionary_len_; - - int flush_; - - int chunk_size_; - - bool write_in_progress_; - + z_stream strm_; + int windowBits_; uv_work_t work_req_; - node_zlib_mode mode_; + bool write_in_progress_; }; -void InitZlib(Handle<Object> target) { - HandleScope scope(node_isolate); - +void InitZlib(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { Local<FunctionTemplate> z = FunctionTemplate::New(ZCtx::New); z->InstanceTemplate()->SetInternalFieldCount(1); @@ -546,9 +552,6 @@ void InitZlib(Handle<Object> target) { z->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Zlib")); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Zlib"), z->GetFunction()); - callback_sym = FIXED_ONE_BYTE_STRING(node_isolate, "callback"); - onerror_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onerror"); - // valid flush values. NODE_DEFINE_CONSTANT(target, Z_NO_FLUSH); NODE_DEFINE_CONSTANT(target, Z_PARTIAL_FLUSH); @@ -593,4 +596,4 @@ void InitZlib(Handle<Object> target) { } // namespace node -NODE_MODULE(node_zlib, node::InitZlib) +NODE_MODULE_CONTEXT_AWARE(node_zlib, node::InitZlib) diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 97375ab847..805e69b668 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -20,16 +20,22 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "pipe_wrap.h" + +#include "env.h" +#include "env-inl.h" +#include "handle_wrap.h" #include "node.h" #include "node_buffer.h" -#include "handle_wrap.h" #include "node_wrap.h" #include "req_wrap.h" #include "stream_wrap.h" +#include "util-inl.h" +#include "util.h" namespace node { using v8::Boolean; +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -38,17 +44,11 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; -using v8::Persistent; using v8::PropertyAttribute; using v8::String; using v8::Undefined; using v8::Value; -static Persistent<Function> pipeConstructor; -static Cached<String> onconnection_sym; -static Cached<String> oncomplete_sym; - - // TODO(bnoordhuis) share with TCPWrap? typedef class ReqWrap<uv_connect_t> ConnectWrap; @@ -58,10 +58,14 @@ uv_pipe_t* PipeWrap::UVHandle() { } -Local<Object> PipeWrap::Instantiate() { - HandleScope scope(node_isolate); - assert(!pipeConstructor.IsEmpty()); - return scope.Close(NewInstance(pipeConstructor)); +Local<Object> PipeWrap::Instantiate(Environment* env) { + HandleScope handle_scope(env->isolate()); + assert(!env->pipe_constructor_template().IsEmpty()); + Local<Function> constructor = env->pipe_constructor_template()->GetFunction(); + assert(!constructor.IsEmpty()); + Local<Object> instance = constructor->NewInstance(); + assert(!instance.IsEmpty()); + return handle_scope.Close(instance); } @@ -72,14 +76,13 @@ PipeWrap* PipeWrap::Unwrap(Local<Object> obj) { } -void PipeWrap::Initialize(Handle<Object> target) { - StreamWrap::Initialize(target); - - HandleScope scope(node_isolate); +void PipeWrap::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); Local<FunctionTemplate> t = FunctionTemplate::New(New); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Pipe")); - t->InstanceTemplate()->SetInternalFieldCount(1); enum PropertyAttribute attributes = @@ -115,9 +118,8 @@ void PipeWrap::Initialize(Handle<Object> target) { NODE_SET_PROTOTYPE_METHOD(t, "setPendingInstances", SetPendingInstances); #endif - pipeConstructorTmpl.Reset(node_isolate, t); - pipeConstructor.Reset(node_isolate, t->GetFunction()); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Pipe"), t->GetFunction()); + env->set_pipe_constructor_template(t); } @@ -126,16 +128,15 @@ void PipeWrap::New(const FunctionCallbackInfo<Value>& args) { // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); - - HandleScope scope(node_isolate); - PipeWrap* wrap = new PipeWrap(args.This(), args[0]->IsTrue()); - assert(wrap); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + new PipeWrap(env, args.This(), args[0]->IsTrue()); } -PipeWrap::PipeWrap(Handle<Object> object, bool ipc) - : StreamWrap(object, reinterpret_cast<uv_stream_t*>(&handle_)) { - int r = uv_pipe_init(uv_default_loop(), &handle_, ipc); +PipeWrap::PipeWrap(Environment* env, Handle<Object> object, bool ipc) + : StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) { + int r = uv_pipe_init(env->event_loop(), &handle_, ipc); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_pipe_init() returns void. UpdateWriteQueueSize(); @@ -184,11 +185,13 @@ void PipeWrap::Listen(const FunctionCallbackInfo<Value>& args) { // TODO(bnoordhuis) maybe share with TCPWrap? void PipeWrap::OnConnection(uv_stream_t* handle, int status) { - HandleScope scope(node_isolate); - PipeWrap* pipe_wrap = static_cast<PipeWrap*>(handle->data); assert(&pipe_wrap->handle_ == reinterpret_cast<uv_pipe_t*>(handle)); + Environment* env = pipe_wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + // We should not be getting this callback if someone as already called // uv_close() on the handle. assert(pipe_wrap->persistent().IsEmpty() == false); @@ -199,12 +202,17 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) { }; if (status != 0) { - MakeCallback(pipe_wrap->object(), "onconnection", ARRAY_SIZE(argv), argv); + MakeCallback(env, + pipe_wrap->object(), + env->onconnection_string(), + ARRAY_SIZE(argv), + argv); return; } // Instanciate the client javascript object and handle. - Local<Object> client_obj = NewInstance(pipeConstructor); + Local<Object> client_obj = + env->pipe_constructor_template()->GetFunction()->NewInstance(); // Unwrap the client javascript object. PipeWrap* wrap; @@ -215,18 +223,22 @@ void PipeWrap::OnConnection(uv_stream_t* handle, int status) { // Successful accept. Call the onconnection callback in JavaScript land. argv[1] = client_obj; - if (onconnection_sym.IsEmpty()) { - onconnection_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onconnection"); - } - MakeCallback(pipe_wrap->object(), onconnection_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + pipe_wrap->object(), + env->onconnection_string(), + ARRAY_SIZE(argv), + argv); } // TODO(bnoordhuis) Maybe share this with TCPWrap? void PipeWrap::AfterConnect(uv_connect_t* req, int status) { ConnectWrap* req_wrap = static_cast<ConnectWrap*>(req->data); PipeWrap* wrap = static_cast<PipeWrap*>(req->handle->data); + assert(req_wrap->env() == wrap->env()); + Environment* env = wrap->env(); - HandleScope scope(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // The wrap and request objects should still be there. assert(req_wrap->persistent().IsEmpty() == false); @@ -250,10 +262,11 @@ void PipeWrap::AfterConnect(uv_connect_t* req, int status) { Boolean::New(writable) }; - if (oncomplete_sym.IsEmpty()) { - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); - } - MakeCallback(req_wrap_obj, oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + req_wrap_obj, + env->oncomplete_string(), + ARRAY_SIZE(argv), + argv); delete req_wrap; } @@ -272,7 +285,8 @@ void PipeWrap::Open(const FunctionCallbackInfo<Value>& args) { void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope scope(args.GetIsolate()); PipeWrap* wrap; NODE_UNWRAP(args.This(), PipeWrap, wrap); @@ -283,7 +297,7 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) { Local<Object> req_wrap_obj = args[0].As<Object>(); String::AsciiValue name(args[1]); - ConnectWrap* req_wrap = new ConnectWrap(req_wrap_obj); + ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj); uv_pipe_connect(&req_wrap->req_, &wrap->handle_, *name, @@ -296,4 +310,4 @@ void PipeWrap::Connect(const FunctionCallbackInfo<Value>& args) { } // namespace node -NODE_MODULE(node_pipe_wrap, node::PipeWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_pipe_wrap, node::PipeWrap::Initialize) diff --git a/src/pipe_wrap.h b/src/pipe_wrap.h index 944835774d..89330fc724 100644 --- a/src/pipe_wrap.h +++ b/src/pipe_wrap.h @@ -21,6 +21,8 @@ #ifndef SRC_PIPE_WRAP_H_ #define SRC_PIPE_WRAP_H_ + +#include "env.h" #include "stream_wrap.h" namespace node { @@ -29,12 +31,14 @@ class PipeWrap : public StreamWrap { public: uv_pipe_t* UVHandle(); - static v8::Local<v8::Object> Instantiate(); + static v8::Local<v8::Object> Instantiate(Environment* env); static PipeWrap* Unwrap(v8::Local<v8::Object> obj); - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(v8::Handle<v8::Object> target, + v8::Handle<v8::Value> unused, + v8::Handle<v8::Context> context); private: - PipeWrap(v8::Handle<v8::Object> object, bool ipc); + PipeWrap(Environment* env, v8::Handle<v8::Object> object, bool ipc); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); static void Bind(const v8::FunctionCallbackInfo<v8::Value>& args); diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 1784a163da..bff4e0ba86 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -19,7 +19,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node.h" +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" #include "node_wrap.h" @@ -29,6 +30,7 @@ namespace node { using v8::Array; +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -41,13 +43,11 @@ using v8::Object; using v8::String; using v8::Value; -static Cached<String> onexit_sym; - class ProcessWrap : public HandleWrap { public: - static void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - + static void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { Local<FunctionTemplate> constructor = FunctionTemplate::New(New); constructor->InstanceTemplate()->SetInternalFieldCount(1); constructor->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Process")); @@ -70,22 +70,25 @@ class ProcessWrap : public HandleWrap { // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); - HandleScope scope(node_isolate); - new ProcessWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + new ProcessWrap(env, args.This()); } - explicit ProcessWrap(Handle<Object> object) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&process_)) { + ProcessWrap(Environment* env, Handle<Object> object) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&process_)) { } ~ProcessWrap() { } - static void ParseStdioOptions(Local<Object> js_options, + static void ParseStdioOptions(Environment* env, + Local<Object> js_options, uv_process_options_t* options) { Local<String> stdio_key = FIXED_ONE_BYTE_STRING(node_isolate, "stdio"); Local<Array> stdios = js_options->Get(stdio_key).As<Array>(); + uint32_t len = stdios->Length(); options->stdio = new uv_stdio_container_t[len]; options->stdio_count = len; @@ -110,7 +113,7 @@ class ProcessWrap : public HandleWrap { Local<String> handle_key = FIXED_ONE_BYTE_STRING(node_isolate, "handle"); Local<Object> handle = stdio->Get(handle_key).As<Object>(); - uv_stream_t* stream = HandleToStream(handle); + uv_stream_t* stream = HandleToStream(env, handle); assert(stream != NULL); options->stdio[i].flags = UV_INHERIT_STREAM; @@ -118,7 +121,6 @@ class ProcessWrap : public HandleWrap { } else { Local<String> fd_key = FIXED_ONE_BYTE_STRING(node_isolate, "fd"); int fd = static_cast<int>(stdio->Get(fd_key)->IntegerValue()); - options->stdio[i].flags = UV_INHERIT_FD; options->stdio[i].data.fd = fd; } @@ -126,7 +128,8 @@ class ProcessWrap : public HandleWrap { } static void Spawn(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); ProcessWrap* wrap; NODE_UNWRAP(args.This(), ProcessWrap, wrap); @@ -216,7 +219,7 @@ class ProcessWrap : public HandleWrap { } // options.stdio - ParseStdioOptions(js_options, &options); + ParseStdioOptions(env, js_options, &options); // options.windows_verbatim_arguments Local<String> windows_verbatim_arguments_key = @@ -232,7 +235,7 @@ class ProcessWrap : public HandleWrap { options.flags |= UV_PROCESS_DETACHED; } - int err = uv_spawn(uv_default_loop(), &wrap->process_, &options); + int err = uv_spawn(env->event_loop(), &wrap->process_, &options); if (err == 0) { assert(wrap->process_.data == wrap); @@ -268,22 +271,24 @@ class ProcessWrap : public HandleWrap { static void OnExit(uv_process_t* handle, int64_t exit_status, int term_signal) { - HandleScope scope(node_isolate); - ProcessWrap* wrap = static_cast<ProcessWrap*>(handle->data); - assert(wrap); + assert(wrap != NULL); assert(&wrap->process_ == handle); + Environment* env = wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + Local<Value> argv[] = { Number::New(node_isolate, static_cast<double>(exit_status)), OneByteString(node_isolate, signo_string(term_signal)) }; - if (onexit_sym.IsEmpty()) { - onexit_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onexit"); - } - - MakeCallback(wrap->object(), onexit_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + wrap->object(), + env->onexit_string(), + ARRAY_SIZE(argv), + argv); } uv_process_t process_; @@ -292,4 +297,4 @@ class ProcessWrap : public HandleWrap { } // namespace node -NODE_MODULE(node_process_wrap, node::ProcessWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_process_wrap, node::ProcessWrap::Initialize) diff --git a/src/req_wrap.h b/src/req_wrap.h index 44749a3004..8a701e5158 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -22,29 +22,31 @@ #ifndef SRC_REQ_WRAP_H_ #define SRC_REQ_WRAP_H_ -#include "node.h" -#include "node_internals.h" +#include "env.h" +#include "env-inl.h" #include "queue.h" +#include "util.h" namespace node { // defined in node.cc -extern Cached<v8::String> process_symbol; -extern Cached<v8::String> domain_symbol; extern QUEUE req_wrap_queue; template <typename T> class ReqWrap { public: - ReqWrap(v8::Handle<v8::Object> object = v8::Handle<v8::Object>()) { - v8::HandleScope scope(node_isolate); - if (object.IsEmpty()) object = v8::Object::New(); - persistent().Reset(node_isolate, object); - - if (InDomain()) { - v8::Local<v8::Value> domain = GetDomain(); - if (domain->IsObject()) - object->Set(domain_symbol, domain); + ReqWrap(Environment* env, + v8::Handle<v8::Object> object = v8::Handle<v8::Object>()) + : env_(env) { + v8::HandleScope handle_scope(env->isolate()); + + if (object.IsEmpty()) { + object = v8::Object::New(); + } + persistent().Reset(env->isolate(), object); + + if (env->in_domain()) { + object->Set(env->domain_string(), env->domain_array()->Get(0)); } QUEUE_INSERT_TAIL(&req_wrap_queue, &req_wrap_queue_); @@ -64,17 +66,25 @@ class ReqWrap { req_.data = this; } + inline Environment* env() const { + return env_; + } + inline v8::Local<v8::Object> object() { - return PersistentToLocal(node_isolate, persistent()); + return PersistentToLocal(env()->isolate(), persistent()); } inline v8::Persistent<v8::Object>& persistent() { return object_; } - v8::Persistent<v8::Object> object_; + // TODO(bnoordhuis) Make these private. QUEUE req_wrap_queue_; T req_; // *must* be last, GetActiveRequests() in node.cc depends on it + + private: + v8::Persistent<v8::Object> object_; + Environment* const env_; }; diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index b9f6d49ff1..a0d62e84a9 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -19,12 +19,14 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node.h" +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" - +#include "v8.h" namespace node { +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -33,17 +35,13 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; -using v8::String; using v8::Value; -static Cached<String> onsignal_sym; - - class SignalWrap : public HandleWrap { public: - static void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - + static void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { Local<FunctionTemplate> constructor = FunctionTemplate::New(New); constructor->InstanceTemplate()->SetInternalFieldCount(1); constructor->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Signal")); @@ -54,8 +52,6 @@ class SignalWrap : public HandleWrap { NODE_SET_PROTOTYPE_METHOD(constructor, "start", Start); NODE_SET_PROTOTYPE_METHOD(constructor, "stop", Stop); - onsignal_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onsignal"); - target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "Signal"), constructor->GetFunction()); } @@ -66,14 +62,14 @@ class SignalWrap : public HandleWrap { // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); - - HandleScope scope(node_isolate); - new SignalWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + new SignalWrap(env, args.This()); } - explicit SignalWrap(Handle<Object> object) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&handle_)) { - int r = uv_signal_init(uv_default_loop(), &handle_); + SignalWrap(Environment* env, Handle<Object> object) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) { + int r = uv_signal_init(env->event_loop(), &handle_); assert(r == 0); } @@ -100,13 +96,12 @@ class SignalWrap : public HandleWrap { } static void OnSignal(uv_signal_t* handle, int signum) { - HandleScope scope(node_isolate); - SignalWrap* wrap = container_of(handle, SignalWrap, handle_); - assert(wrap); - - Local<Value> argv[1] = { Integer::New(signum, node_isolate) }; - MakeCallback(wrap->object(), onsignal_sym, ARRAY_SIZE(argv), argv); + Environment* env = wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); + Local<Value> arg = Integer::New(signum, env->isolate()); + MakeCallback(env, wrap->object(), env->onsignal_string(), 1, &arg); } uv_signal_t handle_; @@ -116,4 +111,4 @@ class SignalWrap : public HandleWrap { } // namespace node -NODE_MODULE(node_signal_wrap, node::SignalWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_signal_wrap, node::SignalWrap::Initialize) diff --git a/src/smalloc.cc b/src/smalloc.cc index 07eecb9427..62a57fada9 100644 --- a/src/smalloc.cc +++ b/src/smalloc.cc @@ -20,11 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "smalloc.h" -#include "node.h" -#include "node_internals.h" -#include "v8.h" +#include "env.h" +#include "env-inl.h" +#include "node_internals.h" #include "v8-profiler.h" +#include "v8.h" #include <string.h> #include <assert.h> @@ -34,6 +35,7 @@ namespace node { namespace smalloc { +using v8::Context; using v8::External; using v8::ExternalArrayType; using v8::FunctionCallbackInfo; @@ -45,7 +47,6 @@ using v8::Local; using v8::Object; using v8::Persistent; using v8::RetainedObjectInfo; -using v8::String; using v8::Uint32; using v8::Value; using v8::kExternalUnsignedByteArray; @@ -64,9 +65,6 @@ void TargetFreeCallback(Isolate* isolate, Persistent<Object>* target, CallbackInfo* arg); -Cached<String> smalloc_sym; -static bool using_alloc_cb; - // return size of external array type, or 0 if unrecognized size_t ExternalArraySize(enum ExternalArrayType type) { @@ -299,10 +297,11 @@ void AllocDispose(const FunctionCallbackInfo<Value>& args) { void AllocDispose(Handle<Object> obj) { - HandleScope handle_scope(node_isolate); + Environment* env = Environment::GetCurrent(node_isolate); + HandleScope handle_scope(env->isolate()); - if (using_alloc_cb) { - Local<Value> ext_v = obj->GetHiddenValue(smalloc_sym); + if (env->using_smalloc_alloc_cb()) { + Local<Value> ext_v = obj->GetHiddenValue(env->smalloc_p_string()); if (ext_v->IsExternal()) { Local<External> ext = ext_v.As<External>(); CallbackInfo* cb_info = static_cast<CallbackInfo*>(ext->Value()); @@ -361,16 +360,14 @@ void Alloc(Handle<Object> obj, enum ExternalArrayType type) { assert(!obj->HasIndexedPropertiesInExternalArrayData()); - if (smalloc_sym.IsEmpty()) { - smalloc_sym = FIXED_ONE_BYTE_STRING(node_isolate, "_smalloc_p"); - using_alloc_cb = true; - } + Environment* env = Environment::GetCurrent(node_isolate); + env->set_using_smalloc_alloc_cb(true); CallbackInfo* cb_info = new CallbackInfo; cb_info->cb = fn; cb_info->hint = hint; cb_info->p_obj.Reset(node_isolate, obj); - obj->SetHiddenValue(smalloc_sym, External::New(cb_info)); + obj->SetHiddenValue(env->smalloc_p_string(), External::New(cb_info)); node_isolate->AdjustAmountOfExternalAllocatedMemory(length + sizeof(*cb_info)); @@ -462,7 +459,11 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Handle<Value> wrapper) { } -void Initialize(Handle<Object> exports) { +void Initialize(Handle<Object> exports, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); + NODE_SET_METHOD(exports, "copyOnto", CopyOnto); NODE_SET_METHOD(exports, "sliceOnto", SliceOnto); @@ -470,13 +471,9 @@ void Initialize(Handle<Object> exports) { NODE_SET_METHOD(exports, "dispose", AllocDispose); exports->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kMaxLength"), - Uint32::NewFromUnsigned(kMaxLength, node_isolate)); - - // for performance, begin checking if allocation object may contain - // callbacks if at least one has been set. - using_alloc_cb = false; + Uint32::NewFromUnsigned(kMaxLength, env->isolate())); - HeapProfiler* heap_profiler = node_isolate->GetHeapProfiler(); + HeapProfiler* heap_profiler = env->isolate()->GetHeapProfiler(); heap_profiler->SetWrapperClassInfoProvider(ALLOC_ID, WrapperInfo); } @@ -484,4 +481,4 @@ void Initialize(Handle<Object> exports) { } // namespace smalloc } // namespace node -NODE_MODULE(node_smalloc, node::smalloc::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_smalloc, node::smalloc::Initialize) diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 6a5ff168d4..49a2f90f46 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -20,10 +20,11 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "stream_wrap.h" -#include "node.h" +#include "env-inl.h" +#include "env.h" +#include "handle_wrap.h" #include "node_buffer.h" #include "node_counters.h" -#include "handle_wrap.h" #include "pipe_wrap.h" #include "req_wrap.h" #include "tcp_wrap.h" @@ -36,6 +37,7 @@ namespace node { using v8::Array; +using v8::Context; using v8::FunctionCallbackInfo; using v8::Handle; using v8::HandleScope; @@ -49,31 +51,13 @@ using v8::Undefined; using v8::Value; -static Cached<String> bytes_sym; -static Cached<String> write_queue_size_sym; -static Cached<String> onread_sym; -static Cached<String> oncomplete_sym; -static Cached<String> handle_sym; -static bool initialized; - - -void StreamWrap::Initialize(Handle<Object> target) { - if (initialized) return; - initialized = true; - - HandleScope scope(node_isolate); - bytes_sym = FIXED_ONE_BYTE_STRING(node_isolate, "bytes"); - write_queue_size_sym = FIXED_ONE_BYTE_STRING(node_isolate, "writeQueueSize"); - onread_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onread"); - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); -} - - -StreamWrap::StreamWrap(Handle<Object> object, uv_stream_t* stream) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(stream)), - stream_(stream), - default_callbacks_(this), - callbacks_(&default_callbacks_) { +StreamWrap::StreamWrap(Environment* env, + Local<Object> object, + uv_stream_t* stream) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(stream)) + , stream_(stream) + , default_callbacks_(this) + , callbacks_(&default_callbacks_) { } @@ -95,7 +79,7 @@ void StreamWrap::UpdateWriteQueueSize() { HandleScope scope(node_isolate); Local<Integer> write_queue_size = Integer::NewFromUnsigned(stream()->write_queue_size, node_isolate); - object()->Set(write_queue_size_sym, write_queue_size); + object()->Set(env()->write_queue_size_string(), write_queue_size); } @@ -137,12 +121,12 @@ void StreamWrap::OnAlloc(uv_handle_t* handle, template <class WrapType, class UVType> -static Local<Object> AcceptHandle(uv_stream_t* pipe) { +static Local<Object> AcceptHandle(Environment* env, uv_stream_t* pipe) { HandleScope scope(node_isolate); Local<Object> wrap_obj; UVType* handle; - wrap_obj = WrapType::Instantiate(); + wrap_obj = WrapType::Instantiate(env); if (wrap_obj.IsEmpty()) return Local<Object>(); @@ -208,7 +192,8 @@ size_t StreamWrap::WriteBuffer(Handle<Value> val, uv_buf_t* buf) { void StreamWrap::WriteBuffer(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); StreamWrap* wrap; NODE_UNWRAP(args.This(), StreamWrap, wrap); @@ -221,7 +206,8 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo<Value>& args) { size_t length = Buffer::Length(buf_obj); char* storage = new char[sizeof(WriteWrap)]; - WriteWrap* req_wrap = new(storage) WriteWrap(req_wrap_obj, wrap); + WriteWrap* req_wrap = + new(storage) WriteWrap(env, req_wrap_obj, wrap); uv_buf_t buf; WriteBuffer(buf_obj, &buf); @@ -232,7 +218,8 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo<Value>& args) { NULL, StreamWrap::AfterWrite); req_wrap->Dispatched(); - req_wrap_obj->Set(bytes_sym, Integer::NewFromUnsigned(length, node_isolate)); + req_wrap_obj->Set(env->bytes_string(), + Integer::NewFromUnsigned(length, node_isolate)); if (err) { req_wrap->~WriteWrap(); @@ -245,7 +232,8 @@ void StreamWrap::WriteBuffer(const FunctionCallbackInfo<Value>& args) { template <enum encoding encoding> void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); int err; StreamWrap* wrap; @@ -272,7 +260,8 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) { } char* storage = new char[sizeof(WriteWrap) + storage_size + 15]; - WriteWrap* req_wrap = new(storage) WriteWrap(req_wrap_obj, wrap); + WriteWrap* req_wrap = + new(storage) WriteWrap(env, req_wrap_obj, wrap); char* data = reinterpret_cast<char*>(ROUND_UP( reinterpret_cast<uintptr_t>(storage) + sizeof(WriteWrap), 16)); @@ -301,14 +290,10 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) { HandleWrap* wrap; NODE_UNWRAP(send_handle_obj, HandleWrap, wrap); send_handle = wrap->GetHandle(); - // Reference StreamWrap instance to prevent it from being garbage // collected before `AfterWrite` is called. - if (handle_sym.IsEmpty()) { - handle_sym = FIXED_ONE_BYTE_STRING(node_isolate, "handle"); - } assert(!req_wrap->persistent().IsEmpty()); - req_wrap->object()->Set(handle_sym, send_handle_obj); + req_wrap->object()->Set(env->handle_string(), send_handle_obj); } err = wrap->callbacks()->DoWrite( @@ -320,7 +305,8 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) { } req_wrap->Dispatched(); - req_wrap->object()->Set(bytes_sym, Number::New(node_isolate, data_size)); + req_wrap->object()->Set(env->bytes_string(), + Number::New(node_isolate, data_size)); if (err) { req_wrap->~WriteWrap(); @@ -332,7 +318,8 @@ void StreamWrap::WriteStringImpl(const FunctionCallbackInfo<Value>& args) { void StreamWrap::Writev(const FunctionCallbackInfo<Value>& args) { - HandleScope scope; + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); StreamWrap* wrap; NODE_UNWRAP(args.This(), StreamWrap, wrap); @@ -378,7 +365,8 @@ void StreamWrap::Writev(const FunctionCallbackInfo<Value>& args) { storage_size += sizeof(WriteWrap); char* storage = new char[storage_size]; - WriteWrap* req_wrap = new(storage) WriteWrap(req_wrap_obj, wrap); + WriteWrap* req_wrap = + new(storage) WriteWrap(env, req_wrap_obj, wrap); uint32_t bytes = 0; size_t offset = sizeof(WriteWrap); @@ -419,7 +407,8 @@ void StreamWrap::Writev(const FunctionCallbackInfo<Value>& args) { delete[] bufs; req_wrap->Dispatched(); - req_wrap->object()->Set(bytes_sym, Number::New(node_isolate, bytes)); + req_wrap->object()->Set(env->bytes_string(), + Number::New(node_isolate, bytes)); if (err) { req_wrap->~WriteWrap(); @@ -448,8 +437,10 @@ void StreamWrap::WriteUcs2String(const FunctionCallbackInfo<Value>& args) { void StreamWrap::AfterWrite(uv_write_t* req, int status) { WriteWrap* req_wrap = container_of(req, WriteWrap, req_); StreamWrap* wrap = req_wrap->wrap(); + Environment* env = wrap->env(); - HandleScope scope(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // The wrap and request objects should still be there. assert(req_wrap->persistent().IsEmpty() == false); @@ -457,11 +448,8 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { // Unref handle property Local<Object> req_wrap_obj = req_wrap->object(); - if (!handle_sym.IsEmpty()) { - req_wrap_obj->Delete(handle_sym); - } - - wrap->callbacks()->AfterWrite(req_wrap); + req_wrap_obj->Delete(env->handle_string()); + wrap->callbacks_->AfterWrite(req_wrap); Local<Value> argv[] = { Integer::New(status, node_isolate), @@ -469,7 +457,11 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { req_wrap_obj }; - MakeCallback(req_wrap_obj, oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + req_wrap_obj, + env->oncomplete_string(), + ARRAY_SIZE(argv), + argv); req_wrap->~WriteWrap(); delete[] reinterpret_cast<char*>(req_wrap); @@ -477,7 +469,8 @@ void StreamWrap::AfterWrite(uv_write_t* req, int status) { void StreamWrap::Shutdown(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); StreamWrap* wrap; NODE_UNWRAP(args.This(), StreamWrap, wrap); @@ -485,7 +478,7 @@ void StreamWrap::Shutdown(const FunctionCallbackInfo<Value>& args) { assert(args[0]->IsObject()); Local<Object> req_wrap_obj = args[0].As<Object>(); - ShutdownWrap* req_wrap = new ShutdownWrap(req_wrap_obj); + ShutdownWrap* req_wrap = new ShutdownWrap(env, req_wrap_obj); int err = wrap->callbacks()->DoShutdown(req_wrap, AfterShutdown); req_wrap->Dispatched(); if (err) delete req_wrap; @@ -496,12 +489,14 @@ void StreamWrap::Shutdown(const FunctionCallbackInfo<Value>& args) { void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { ShutdownWrap* req_wrap = static_cast<ShutdownWrap*>(req->data); StreamWrap* wrap = static_cast<StreamWrap*>(req->handle->data); + Environment* env = wrap->env(); // The wrap and request objects should still be there. assert(req_wrap->persistent().IsEmpty() == false); assert(wrap->persistent().IsEmpty() == false); - HandleScope scope(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Object> req_wrap_obj = req_wrap->object(); Local<Value> argv[3] = { @@ -510,7 +505,11 @@ void StreamWrap::AfterShutdown(uv_shutdown_t* req, int status) { req_wrap_obj }; - MakeCallback(req_wrap_obj, oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + req_wrap_obj, + env->oncomplete_string(), + ARRAY_SIZE(argv), + argv); delete req_wrap; } @@ -568,7 +567,9 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf, uv_handle_type pending) { - HandleScope scope(node_isolate); + Environment* env = wrap()->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Value> argv[] = { Integer::New(nread, node_isolate), @@ -579,7 +580,7 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle, if (nread < 0) { if (buf->base != NULL) free(buf->base); - MakeCallback(Self(), onread_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, Self(), env->onread_string(), ARRAY_SIZE(argv), argv); return; } @@ -591,15 +592,15 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle, char* base = static_cast<char*>(realloc(buf->base, nread)); assert(static_cast<size_t>(nread) <= buf->len); - argv[1] = Buffer::Use(base, nread); + argv[1] = Buffer::Use(env, base, nread); Local<Object> pending_obj; if (pending == UV_TCP) { - pending_obj = AcceptHandle<TCPWrap, uv_tcp_t>(handle); + pending_obj = AcceptHandle<TCPWrap, uv_tcp_t>(env, handle); } else if (pending == UV_NAMED_PIPE) { - pending_obj = AcceptHandle<PipeWrap, uv_pipe_t>(handle); + pending_obj = AcceptHandle<PipeWrap, uv_pipe_t>(env, handle); } else if (pending == UV_UDP) { - pending_obj = AcceptHandle<UDPWrap, uv_udp_t>(handle); + pending_obj = AcceptHandle<UDPWrap, uv_udp_t>(env, handle); } else { assert(pending == UV_UNKNOWN_HANDLE); } @@ -608,7 +609,11 @@ void StreamWrapCallbacks::DoRead(uv_stream_t* handle, argv[2] = pending_obj; } - MakeCallback(wrap()->object(), onread_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + wrap()->object(), + env->onread_string(), + ARRAY_SIZE(argv), + argv); } diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 6731fdbf75..26fb5d5ef6 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -22,11 +22,11 @@ #ifndef SRC_STREAM_WRAP_H_ #define SRC_STREAM_WRAP_H_ -#include "v8.h" -#include "node.h" +#include "env.h" #include "handle_wrap.h" #include "req_wrap.h" #include "string_bytes.h" +#include "v8.h" namespace node { @@ -37,8 +37,8 @@ typedef class ReqWrap<uv_shutdown_t> ShutdownWrap; class WriteWrap: public ReqWrap<uv_write_t> { public: - WriteWrap(v8::Local<v8::Object> obj, StreamWrap* wrap) - : ReqWrap<uv_write_t>(obj) + WriteWrap(Environment* env, v8::Local<v8::Object> obj, StreamWrap* wrap) + : ReqWrap<uv_write_t>(env, obj) , wrap_(wrap) { } @@ -108,8 +108,6 @@ class StreamWrap : public HandleWrap { delete old; } - static void Initialize(v8::Handle<v8::Object> target); - static void GetFD(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>&); @@ -148,7 +146,9 @@ class StreamWrap : public HandleWrap { protected: static size_t WriteBuffer(v8::Handle<v8::Value> val, uv_buf_t* buf); - StreamWrap(v8::Handle<v8::Object> object, uv_stream_t* stream); + StreamWrap(Environment* env, + v8::Local<v8::Object> object, + uv_stream_t* stream); ~StreamWrap() { if (callbacks_ != &default_callbacks_) { diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index e007056296..305ee1db2b 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -20,10 +20,12 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "tcp_wrap.h" -#include "node.h" + +#include "env.h" +#include "env-inl.h" +#include "handle_wrap.h" #include "node_buffer.h" #include "node_wrap.h" -#include "handle_wrap.h" #include "req_wrap.h" #include "stream_wrap.h" @@ -32,6 +34,7 @@ namespace node { +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -40,40 +43,32 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; -using v8::Persistent; using v8::PropertyAttribute; using v8::String; using v8::Undefined; using v8::Value; -static Persistent<Function> tcpConstructor; -static Cached<String> oncomplete_sym; -static Cached<String> onconnection_sym; - - typedef class ReqWrap<uv_connect_t> ConnectWrap; -Local<Object> TCPWrap::Instantiate() { - // If this assert fire then process.binding('tcp_wrap') hasn't been - // called yet. - assert(tcpConstructor.IsEmpty() == false); - - HandleScope scope(node_isolate); - Local<Object> obj = NewInstance(tcpConstructor); - - return scope.Close(obj); +Local<Object> TCPWrap::Instantiate(Environment* env) { + HandleScope handle_scope(env->isolate()); + assert(env->tcp_constructor_template().IsEmpty() == false); + Local<Function> constructor = env->tcp_constructor_template()->GetFunction(); + assert(constructor.IsEmpty() == false); + Local<Object> instance = constructor->NewInstance(); + assert(instance.IsEmpty() == false); + return handle_scope.Close(instance); } -void TCPWrap::Initialize(Handle<Object> target) { - StreamWrap::Initialize(target); - - HandleScope scope(node_isolate); +void TCPWrap::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); Local<FunctionTemplate> t = FunctionTemplate::New(New); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "TCP")); - t->InstanceTemplate()->SetInternalFieldCount(1); enum PropertyAttribute attributes = @@ -119,12 +114,8 @@ void TCPWrap::Initialize(Handle<Object> target) { SetSimultaneousAccepts); #endif - onconnection_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onconnection"); - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); - - tcpConstructorTmpl.Reset(node_isolate, t); - tcpConstructor.Reset(node_isolate, t->GetFunction()); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "TCP"), t->GetFunction()); + env->set_tcp_constructor_template(t); } @@ -145,15 +136,16 @@ void TCPWrap::New(const FunctionCallbackInfo<Value>& args) { // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); - HandleScope scope(node_isolate); - TCPWrap* wrap = new TCPWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + TCPWrap* wrap = new TCPWrap(env, args.This()); assert(wrap); } -TCPWrap::TCPWrap(Handle<Object> object) - : StreamWrap(object, reinterpret_cast<uv_stream_t*>(&handle_)) { - int r = uv_tcp_init(uv_default_loop(), &handle_); +TCPWrap::TCPWrap(Environment* env, Handle<Object> object) + : StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) { + int r = uv_tcp_init(env->event_loop(), &handle_); assert(r == 0); // How do we proxy this error up to javascript? // Suggestion: uv_tcp_init() returns void. UpdateWriteQueueSize(); @@ -166,7 +158,8 @@ TCPWrap::~TCPWrap() { void TCPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); struct sockaddr_storage address; TCPWrap* wrap; @@ -181,7 +174,7 @@ void TCPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { &addrlen); if (err == 0) { const sockaddr* addr = reinterpret_cast<const sockaddr*>(&address); - AddressToJS(addr, out); + AddressToJS(env, addr, out); } args.GetReturnValue().Set(err); @@ -189,7 +182,8 @@ void TCPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { void TCPWrap::GetPeerName(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); struct sockaddr_storage address; TCPWrap* wrap; @@ -204,7 +198,7 @@ void TCPWrap::GetPeerName(const FunctionCallbackInfo<Value>& args) { &addrlen); if (err == 0) { const sockaddr* addr = reinterpret_cast<const sockaddr*>(&address); - AddressToJS(addr, out); + AddressToJS(env, addr, out); } args.GetReturnValue().Set(err); @@ -311,10 +305,12 @@ void TCPWrap::Listen(const FunctionCallbackInfo<Value>& args) { void TCPWrap::OnConnection(uv_stream_t* handle, int status) { - HandleScope scope(node_isolate); - TCPWrap* tcp_wrap = static_cast<TCPWrap*>(handle->data); assert(&tcp_wrap->handle_ == reinterpret_cast<uv_tcp_t*>(handle)); + Environment* env = tcp_wrap->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // We should not be getting this callback if someone as already called // uv_close() on the handle. @@ -327,7 +323,7 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) { if (status == 0) { // Instantiate the client javascript object and handle. - Local<Object> client_obj = Instantiate(); + Local<Object> client_obj = Instantiate(env); // Unwrap the client javascript object. TCPWrap* wrap; @@ -340,15 +336,22 @@ void TCPWrap::OnConnection(uv_stream_t* handle, int status) { argv[1] = client_obj; } - MakeCallback(tcp_wrap->object(), onconnection_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + tcp_wrap->object(), + env->onconnection_string(), + ARRAY_SIZE(argv), + argv); } void TCPWrap::AfterConnect(uv_connect_t* req, int status) { ConnectWrap* req_wrap = static_cast<ConnectWrap*>(req->data); TCPWrap* wrap = static_cast<TCPWrap*>(req->handle->data); + assert(req_wrap->env() == wrap->env()); + Environment* env = wrap->env(); - HandleScope scope(node_isolate); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); // The wrap and request objects should still be there. assert(req_wrap->persistent().IsEmpty() == false); @@ -362,14 +365,19 @@ void TCPWrap::AfterConnect(uv_connect_t* req, int status) { v8::True(node_isolate), v8::True(node_isolate) }; - MakeCallback(req_wrap_obj, oncomplete_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + req_wrap_obj, + env->oncomplete_string(), + ARRAY_SIZE(argv), + argv); delete req_wrap; } void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); TCPWrap* wrap; NODE_UNWRAP(args.This(), TCPWrap, wrap); @@ -386,7 +394,7 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) { int err = uv_ip4_addr(*ip_address, port, &addr); if (err == 0) { - ConnectWrap* req_wrap = new ConnectWrap(req_wrap_obj); + ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj); err = uv_tcp_connect(&req_wrap->req_, &wrap->handle_, reinterpret_cast<const sockaddr*>(&addr), @@ -400,7 +408,8 @@ void TCPWrap::Connect(const FunctionCallbackInfo<Value>& args) { void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); TCPWrap* wrap; NODE_UNWRAP(args.This(), TCPWrap, wrap); @@ -417,7 +426,7 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) { int err = uv_ip6_addr(*ip_address, port, &addr); if (err == 0) { - ConnectWrap* req_wrap = new ConnectWrap(req_wrap_obj); + ConnectWrap* req_wrap = new ConnectWrap(env, req_wrap_obj); err = uv_tcp_connect(&req_wrap->req_, &wrap->handle_, reinterpret_cast<const sockaddr*>(&addr), @@ -431,27 +440,15 @@ void TCPWrap::Connect6(const FunctionCallbackInfo<Value>& args) { // also used by udp_wrap.cc -Local<Object> AddressToJS(const sockaddr* addr, Handle<Object> info) { - static Cached<String> address_sym; - static Cached<String> family_sym; - static Cached<String> port_sym; - static Cached<String> ipv4_sym; - static Cached<String> ipv6_sym; - +Local<Object> AddressToJS(Environment* env, + const sockaddr* addr, + Local<Object> info) { HandleScope scope(node_isolate); char ip[INET6_ADDRSTRLEN]; const sockaddr_in *a4; const sockaddr_in6 *a6; int port; - if (address_sym.IsEmpty()) { - address_sym = FIXED_ONE_BYTE_STRING(node_isolate, "address"); - family_sym = FIXED_ONE_BYTE_STRING(node_isolate, "family"); - port_sym = FIXED_ONE_BYTE_STRING(node_isolate, "port"); - ipv4_sym = FIXED_ONE_BYTE_STRING(node_isolate, "IPv4"); - ipv6_sym = FIXED_ONE_BYTE_STRING(node_isolate, "IPv6"); - } - if (info.IsEmpty()) info = Object::New(); switch (addr->sa_family) { @@ -459,22 +456,22 @@ Local<Object> AddressToJS(const sockaddr* addr, Handle<Object> info) { a6 = reinterpret_cast<const sockaddr_in6*>(addr); uv_inet_ntop(AF_INET6, &a6->sin6_addr, ip, sizeof ip); port = ntohs(a6->sin6_port); - info->Set(address_sym, OneByteString(node_isolate, ip)); - info->Set(family_sym, ipv6_sym); - info->Set(port_sym, Integer::New(port, node_isolate)); + info->Set(env->address_string(), OneByteString(node_isolate, ip)); + info->Set(env->family_string(), env->ipv6_string()); + info->Set(env->port_string(), Integer::New(port, node_isolate)); break; case AF_INET: a4 = reinterpret_cast<const sockaddr_in*>(addr); uv_inet_ntop(AF_INET, &a4->sin_addr, ip, sizeof ip); port = ntohs(a4->sin_port); - info->Set(address_sym, OneByteString(node_isolate, ip)); - info->Set(family_sym, ipv4_sym); - info->Set(port_sym, Integer::New(port, node_isolate)); + info->Set(env->address_string(), OneByteString(node_isolate, ip)); + info->Set(env->family_string(), env->ipv4_string()); + info->Set(env->port_string(), Integer::New(port, node_isolate)); break; default: - info->Set(address_sym, String::Empty(node_isolate)); + info->Set(env->address_string(), String::Empty(node_isolate)); } return scope.Close(info); @@ -483,4 +480,4 @@ Local<Object> AddressToJS(const sockaddr* addr, Handle<Object> info) { } // namespace node -NODE_MODULE(node_tcp_wrap, node::TCPWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_tcp_wrap, node::TCPWrap::Initialize) diff --git a/src/tcp_wrap.h b/src/tcp_wrap.h index 374458b78d..b1fa4c7f8f 100644 --- a/src/tcp_wrap.h +++ b/src/tcp_wrap.h @@ -21,20 +21,24 @@ #ifndef SRC_TCP_WRAP_H_ #define SRC_TCP_WRAP_H_ + +#include "env.h" #include "stream_wrap.h" namespace node { class TCPWrap : public StreamWrap { public: - static v8::Local<v8::Object> Instantiate(); + static v8::Local<v8::Object> Instantiate(Environment* env); static TCPWrap* Unwrap(v8::Local<v8::Object> obj); - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(v8::Handle<v8::Object> target, + v8::Handle<v8::Value> unused, + v8::Handle<v8::Context> context); uv_tcp_t* UVHandle(); private: - explicit TCPWrap(v8::Handle<v8::Object> object); + TCPWrap(Environment* env, v8::Handle<v8::Object> object); ~TCPWrap(); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index c9782d8713..76b95540f2 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -19,13 +19,15 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node.h" +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" #include <stdint.h> namespace node { +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -40,9 +42,9 @@ const uint32_t kOnTimeout = 0; class TimerWrap : public HandleWrap { public: - static void Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - + static void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { Local<FunctionTemplate> constructor = FunctionTemplate::New(New); constructor->InstanceTemplate()->SetInternalFieldCount(1); constructor->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "Timer")); @@ -71,13 +73,14 @@ class TimerWrap : public HandleWrap { // Therefore we assert that we are not trying to call this as a // normal function. assert(args.IsConstructCall()); - HandleScope scope(node_isolate); - new TimerWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + new TimerWrap(env, args.This()); } - explicit TimerWrap(Handle<Object> object) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&handle_)) { - int r = uv_timer_init(uv_default_loop(), &handle_); + TimerWrap(Environment* env, Handle<Object> object) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) { + int r = uv_timer_init(env->event_loop(), &handle_); assert(r == 0); } @@ -133,19 +136,19 @@ class TimerWrap : public HandleWrap { } static void OnTimeout(uv_timer_t* handle, int status) { - HandleScope scope(node_isolate); - TimerWrap* wrap = static_cast<TimerWrap*>(handle->data); - assert(wrap); - + Environment* env = wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Value> argv[1] = { Integer::New(status, node_isolate) }; - MakeCallback(wrap->object(), kOnTimeout, ARRAY_SIZE(argv), argv); + MakeCallback(env, wrap->object(), kOnTimeout, ARRAY_SIZE(argv), argv); } static void Now(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); - uv_update_time(uv_default_loop()); - double now = static_cast<double>(uv_now(uv_default_loop())); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + uv_update_time(env->event_loop()); + double now = static_cast<double>(uv_now(env->event_loop())); args.GetReturnValue().Set(now); } @@ -155,4 +158,4 @@ class TimerWrap : public HandleWrap { } // namespace node -NODE_MODULE(node_timer_wrap, node::TimerWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_timer_wrap, node::TimerWrap::Initialize) diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index cff2cded8c..e6afda6c0c 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -34,6 +34,7 @@ namespace node { using crypto::SSLWrap; using crypto::SecureContext; using v8::Boolean; +using v8::Context; using v8::Exception; using v8::Function; using v8::FunctionCallbackInfo; @@ -44,40 +45,20 @@ using v8::Integer; using v8::Local; using v8::Null; using v8::Object; -using v8::Persistent; using v8::String; using v8::Value; -static Cached<String> onread_sym; -static Cached<String> onerror_sym; -static Cached<String> onhandshakestart_sym; -static Cached<String> onhandshakedone_sym; -static Cached<String> onclienthello_sym; -static Cached<String> subject_sym; -static Cached<String> subjectaltname_sym; -static Cached<String> modulus_sym; -static Cached<String> exponent_sym; -static Cached<String> issuer_sym; -static Cached<String> valid_from_sym; -static Cached<String> valid_to_sym; -static Cached<String> fingerprint_sym; -static Cached<String> name_sym; -static Cached<String> version_sym; -static Cached<String> ext_key_usage_sym; -static Cached<String> sni_context_sym; - -static Persistent<Function> tlsWrap; - static const int X509_NAME_FLAGS = ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | XN_FLAG_SEP_MULTILINE | XN_FLAG_FN_SN; -TLSCallbacks::TLSCallbacks(Kind kind, +TLSCallbacks::TLSCallbacks(Environment* env, + Kind kind, Handle<Object> sc, StreamWrapCallbacks* old) - : SSLWrap<TLSCallbacks>(ObjectWrap::Unwrap<SecureContext>(sc), kind), + : SSLWrap<TLSCallbacks>(env, ObjectWrap::Unwrap<SecureContext>(sc), kind), StreamWrapCallbacks(old), enc_in_(NULL), enc_out_(NULL), @@ -92,7 +73,7 @@ TLSCallbacks::TLSCallbacks(Kind kind, sc_ = ObjectWrap::Unwrap<SecureContext>(sc); sc_handle_.Reset(node_isolate, sc); - Local<Object> object = NewInstance(tlsWrap); + Local<Object> object = env->tls_wrap_constructor_function()->NewInstance(); NODE_WRAP(object, this); persistent().Reset(node_isolate, object); @@ -197,7 +178,8 @@ void TLSCallbacks::InitSSL() { void TLSCallbacks::Wrap(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); if (args.Length() < 1 || !args[0]->IsObject()) return ThrowTypeError("First argument should be a StreamWrap instance"); @@ -212,8 +194,8 @@ void TLSCallbacks::Wrap(const FunctionCallbackInfo<Value>& args) { SSLWrap<TLSCallbacks>::kClient; TLSCallbacks* callbacks = NULL; - WITH_GENERIC_STREAM(stream, { - callbacks = new TLSCallbacks(kind, sc, wrap->callbacks()); + WITH_GENERIC_STREAM(env, stream, { + callbacks = new TLSCallbacks(env, kind, sc, wrap->callbacks()); wrap->OverrideCallbacks(callbacks); }); @@ -243,23 +225,32 @@ void TLSCallbacks::Start(const FunctionCallbackInfo<Value>& args) { void TLSCallbacks::SSLInfoCallback(const SSL* ssl_, int where, int ret) { + if (!(where & (SSL_CB_HANDSHAKE_START | SSL_CB_HANDSHAKE_DONE))) + return; + // Be compatible with older versions of OpenSSL. SSL_get_app_data() wants // a non-const SSL* in OpenSSL <= 0.9.7e. SSL* ssl = const_cast<SSL*>(ssl_); + TLSCallbacks* c = static_cast<TLSCallbacks*>(SSL_get_app_data(ssl)); + Environment* env = c->env(); + // There should be a Context::Scope a few stack frames down. + assert(env->context() == env->isolate()->GetCurrentContext()); + HandleScope handle_scope(env->isolate()); + Local<Object> object = c->object(env->isolate()); + if (where & SSL_CB_HANDSHAKE_START) { - HandleScope scope(node_isolate); - TLSCallbacks* c = static_cast<TLSCallbacks*>(SSL_get_app_data(ssl)); - Local<Object> object = c->object(node_isolate); - if (object->Has(onhandshakestart_sym)) - MakeCallback(object, onhandshakestart_sym, 0, NULL); + Local<Value> callback = object->Get(env->onhandshakestart_string()); + if (callback->IsFunction()) { + MakeCallback(env, object, callback.As<Function>()); + } } + if (where & SSL_CB_HANDSHAKE_DONE) { - HandleScope scope(node_isolate); - TLSCallbacks* c = static_cast<TLSCallbacks*>(SSL_get_app_data(ssl)); c->established_ = true; - Local<Object> object = c->object(node_isolate); - if (object->Has(onhandshakedone_sym)) - MakeCallback(object, onhandshakedone_sym, 0, NULL); + Local<Value> callback = object->Get(env->onhandshakedone_string()); + if (callback->IsFunction()) { + MakeCallback(env, object, callback.As<Function>()); + } } } @@ -306,9 +297,8 @@ void TLSCallbacks::EncOut() { void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { - HandleScope scope(node_isolate); - TLSCallbacks* callbacks = static_cast<TLSCallbacks*>(req->data); + Environment* env = callbacks->env(); // Handle error if (status) { @@ -317,10 +307,16 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { return; // Notify about error + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Value> arg = String::Concat( FIXED_ONE_BYTE_STRING(node_isolate, "write cb error, status: "), Integer::New(status, node_isolate)->ToString()); - MakeCallback(callbacks->object(node_isolate), onerror_sym, 1, &arg); + MakeCallback(env, + callbacks->object(node_isolate), + env->onerror_string(), + 1, + &arg); callbacks->InvokeQueued(status); return; } @@ -334,7 +330,7 @@ void TLSCallbacks::EncOutCb(uv_write_t* req, int status) { } -Handle<Value> TLSCallbacks::GetSSLError(int status, int* err) { +Local<Value> TLSCallbacks::GetSSLError(int status, int* err) { HandleScope scope(node_isolate); *err = SSL_get_error(ssl_, status); @@ -365,7 +361,7 @@ Handle<Value> TLSCallbacks::GetSSLError(int status, int* err) { return scope.Close(exception); } } - return Handle<Value>(); + return Local<Value>(); } @@ -374,7 +370,8 @@ void TLSCallbacks::ClearOut() { if (!hello_parser_.IsEnded()) return; - HandleScope scope(node_isolate); + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); assert(ssl_ != NULL); @@ -385,9 +382,13 @@ void TLSCallbacks::ClearOut() { if (read > 0) { Local<Value> argv[] = { Integer::New(read, node_isolate), - Buffer::New(out, read) + Buffer::New(env(), out, read) }; - MakeCallback(Self(), onread_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env(), + Self(), + env()->onread_string(), + ARRAY_SIZE(argv), + argv); } } while (read > 0); @@ -395,8 +396,13 @@ void TLSCallbacks::ClearOut() { int err; Handle<Value> argv = GetSSLError(read, &err); - if (!argv.IsEmpty()) - MakeCallback(object(node_isolate), onerror_sym, 1, &argv); + if (!argv.IsEmpty()) { + MakeCallback(env(), + object(node_isolate), + env()->onerror_string(), + 1, + &argv); + } } } @@ -406,8 +412,6 @@ bool TLSCallbacks::ClearIn() { if (!hello_parser_.IsEnded()) return false; - HandleScope scope(node_isolate); - int written = 0; while (clear_in_->Length() > 0) { size_t avail = 0; @@ -425,11 +429,19 @@ bool TLSCallbacks::ClearIn() { return true; } + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); + // Error or partial write int err; Handle<Value> argv = GetSSLError(written, &err); - if (!argv.IsEmpty()) - MakeCallback(object(node_isolate), onerror_sym, 1, &argv); + if (!argv.IsEmpty()) { + MakeCallback(env(), + object(node_isolate), + env()->onerror_string(), + 1, + &argv); + } return false; } @@ -440,8 +452,6 @@ int TLSCallbacks::DoWrite(WriteWrap* w, size_t count, uv_stream_t* send_handle, uv_write_cb cb) { - HandleScope scope(node_isolate); - assert(send_handle == NULL); // Queue callback to execute it on next tick @@ -489,9 +499,15 @@ int TLSCallbacks::DoWrite(WriteWrap* w, if (i != count) { int err; + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); Handle<Value> argv = GetSSLError(written, &err); if (!argv.IsEmpty()) { - MakeCallback(object(node_isolate), onerror_sym, 1, &argv); + MakeCallback(env(), + object(node_isolate), + env()->onerror_string(), + 1, + &argv); return -1; } @@ -527,8 +543,10 @@ void TLSCallbacks::DoRead(uv_stream_t* handle, if (nread < 0) { // Error should be emitted only after all data was read ClearOut(); + Context::Scope context_scope(env()->context()); + HandleScope handle_scope(env()->isolate()); Local<Value> arg = Integer::New(nread, node_isolate); - MakeCallback(Self(), onread_sym, 1, &arg); + MakeCallback(env(), Self(), env()->onread_string(), 1, &arg); return; } @@ -664,18 +682,16 @@ int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { HandleScope scope(node_isolate); TLSCallbacks* p = static_cast<TLSCallbacks*>(arg); + Environment* env = p->env(); const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); if (servername != NULL) { // Call the SNI callback and use its return value as context Local<Object> object = p->object(node_isolate); - Local<Value> ctx; - if (object->Has(sni_context_sym)) { - ctx = object->Get(sni_context_sym); - } + Local<Value> ctx = object->Get(env->sni_context_string()); - if (ctx.IsEmpty() || ctx->IsUndefined()) + if (!ctx->IsObject()) return SSL_TLSEXT_ERR_NOACK; p->sni_context_.Dispose(); @@ -690,8 +706,10 @@ int TLSCallbacks::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { #endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB -void TLSCallbacks::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); +void TLSCallbacks::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); NODE_SET_METHOD(target, "wrap", TLSCallbacks::Wrap); @@ -715,29 +733,9 @@ void TLSCallbacks::Initialize(Handle<Object> target) { NODE_SET_PROTOTYPE_METHOD(t, "setServername", SetServername); #endif // SSL_CRT_SET_TLSEXT_SERVERNAME_CB - tlsWrap.Reset(node_isolate, t->GetFunction()); - - onread_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onread"); - onerror_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onerror"); - onhandshakestart_sym = - FIXED_ONE_BYTE_STRING(node_isolate, "onhandshakestart"); - onhandshakedone_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onhandshakedone"); - onclienthello_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onclienthello"); - - subject_sym = FIXED_ONE_BYTE_STRING(node_isolate, "subject"); - issuer_sym = FIXED_ONE_BYTE_STRING(node_isolate, "issuer"); - valid_from_sym = FIXED_ONE_BYTE_STRING(node_isolate, "valid_from"); - valid_to_sym = FIXED_ONE_BYTE_STRING(node_isolate, "valid_to"); - subjectaltname_sym = FIXED_ONE_BYTE_STRING(node_isolate, "subjectaltname"); - modulus_sym = FIXED_ONE_BYTE_STRING(node_isolate, "modulus"); - exponent_sym = FIXED_ONE_BYTE_STRING(node_isolate, "exponent"); - fingerprint_sym = FIXED_ONE_BYTE_STRING(node_isolate, "fingerprint"); - name_sym = FIXED_ONE_BYTE_STRING(node_isolate, "name"); - version_sym = FIXED_ONE_BYTE_STRING(node_isolate, "version"); - ext_key_usage_sym = FIXED_ONE_BYTE_STRING(node_isolate, "ext_key_usage"); - sni_context_sym = FIXED_ONE_BYTE_STRING(node_isolate, "sni_context"); + env->set_tls_wrap_constructor_function(t->GetFunction()); } } // namespace node -NODE_MODULE(node_tls_wrap, node::TLSCallbacks::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_tls_wrap, node::TLSCallbacks::Initialize) diff --git a/src/tls_wrap.h b/src/tls_wrap.h index 83f54c3b92..d8f3997923 100644 --- a/src/tls_wrap.h +++ b/src/tls_wrap.h @@ -24,6 +24,8 @@ #include "node.h" #include "node_crypto.h" // SSLWrap + +#include "env.h" #include "queue.h" #include "stream_wrap.h" #include "v8.h" @@ -42,7 +44,9 @@ namespace crypto { class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>, public StreamWrapCallbacks { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(v8::Handle<v8::Object> target, + v8::Handle<v8::Value> unused, + v8::Handle<v8::Context> context); int DoWrite(WriteWrap* w, uv_buf_t* bufs, @@ -82,7 +86,10 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>, QUEUE member_; }; - TLSCallbacks(Kind kind, v8::Handle<v8::Object> sc, StreamWrapCallbacks* old); + TLSCallbacks(Environment* env, + Kind kind, + v8::Handle<v8::Object> sc, + StreamWrapCallbacks* old); ~TLSCallbacks(); static void SSLInfoCallback(const SSL* ssl_, int where, int ret); @@ -99,7 +106,7 @@ class TLSCallbacks : public crypto::SSLWrap<TLSCallbacks>, EncOut(); } - v8::Handle<v8::Value> GetSSLError(int status, int* err); + v8::Local<v8::Value> GetSSLError(int status, int* err); static void OnClientHelloParseEnd(void* arg); static void Wrap(const v8::FunctionCallbackInfo<v8::Value>& args); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index 0b675e1bb8..aad19a7e60 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -20,9 +20,11 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "tty_wrap.h" -#include "node.h" -#include "node_buffer.h" + +#include "env.h" +#include "env-inl.h" #include "handle_wrap.h" +#include "node_buffer.h" #include "node_wrap.h" #include "req_wrap.h" #include "stream_wrap.h" @@ -30,6 +32,7 @@ namespace node { using v8::Array; +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -43,14 +46,13 @@ using v8::String; using v8::Value; -void TTYWrap::Initialize(Handle<Object> target) { - StreamWrap::Initialize(target); - - HandleScope scope(node_isolate); +void TTYWrap::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); Local<FunctionTemplate> t = FunctionTemplate::New(New); t->SetClassName(FIXED_ONE_BYTE_STRING(node_isolate, "TTY")); - t->InstanceTemplate()->SetInternalFieldCount(1); enum PropertyAttribute attributes = @@ -81,8 +83,8 @@ void TTYWrap::Initialize(Handle<Object> target) { NODE_SET_METHOD(target, "isTTY", IsTTY); NODE_SET_METHOD(target, "guessHandleType", GuessHandleType); - ttyConstructorTmpl.Reset(node_isolate, t); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "TTY"), t->GetFunction()); + env->set_tty_constructor_template(t); } @@ -162,7 +164,8 @@ void TTYWrap::SetRawMode(const FunctionCallbackInfo<Value>& args) { void TTYWrap::New(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); // This constructor should not be exposed to public javascript. // Therefore we assert that we are not trying to call this as a @@ -172,16 +175,16 @@ void TTYWrap::New(const FunctionCallbackInfo<Value>& args) { int fd = args[0]->Int32Value(); assert(fd >= 0); - TTYWrap* wrap = new TTYWrap(args.This(), fd, args[1]->IsTrue()); + TTYWrap* wrap = new TTYWrap(env, args.This(), fd, args[1]->IsTrue()); wrap->UpdateWriteQueueSize(); } -TTYWrap::TTYWrap(Handle<Object> object, int fd, bool readable) - : StreamWrap(object, reinterpret_cast<uv_stream_t*>(&handle_)) { - uv_tty_init(uv_default_loop(), &handle_, fd, readable); +TTYWrap::TTYWrap(Environment* env, Handle<Object> object, int fd, bool readable) + : StreamWrap(env, object, reinterpret_cast<uv_stream_t*>(&handle_)) { + uv_tty_init(env->event_loop(), &handle_, fd, readable); } } // namespace node -NODE_MODULE(node_tty_wrap, node::TTYWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_tty_wrap, node::TTYWrap::Initialize) diff --git a/src/tty_wrap.h b/src/tty_wrap.h index 0475073fdc..4c69b4c88a 100644 --- a/src/tty_wrap.h +++ b/src/tty_wrap.h @@ -22,6 +22,7 @@ #ifndef SRC_TTY_WRAP_H_ #define SRC_TTY_WRAP_H_ +#include "env.h" #include "handle_wrap.h" #include "stream_wrap.h" @@ -29,13 +30,18 @@ namespace node { class TTYWrap : public StreamWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(v8::Handle<v8::Object> target, + v8::Handle<v8::Value> unused, + v8::Handle<v8::Context> context); static TTYWrap* Unwrap(v8::Local<v8::Object> obj); uv_tty_t* UVHandle(); private: - TTYWrap(v8::Handle<v8::Object> object, int fd, bool readable); + TTYWrap(Environment* env, + v8::Handle<v8::Object> object, + int fd, + bool readable); static void GuessHandleType(const v8::FunctionCallbackInfo<v8::Value>& args); static void IsTTY(const v8::FunctionCallbackInfo<v8::Value>& args); diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 1e5d32269b..fd679ab4d8 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -20,7 +20,8 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. #include "udp_wrap.h" -#include "node.h" +#include "env.h" +#include "env-inl.h" #include "node_buffer.h" #include "handle_wrap.h" #include "req_wrap.h" @@ -30,6 +31,7 @@ namespace node { +using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -38,7 +40,6 @@ using v8::HandleScope; using v8::Integer; using v8::Local; using v8::Object; -using v8::Persistent; using v8::PropertyAttribute; using v8::PropertyCallbackInfo; using v8::String; @@ -49,20 +50,17 @@ using v8::Value; class SendWrap : public ReqWrap<uv_udp_send_t> { public: - SendWrap(Local<Object> req_wrap_obj, bool have_callback); + SendWrap(Environment* env, Local<Object> req_wrap_obj, bool have_callback); inline bool have_callback() const; private: const bool have_callback_; }; -static Persistent<Function> constructor; -static Cached<String> oncomplete_sym; -static Cached<String> onmessage_sym; - - -SendWrap::SendWrap(Local<Object> req_wrap_obj, bool have_callback) - : ReqWrap<uv_udp_send_t>(req_wrap_obj) +SendWrap::SendWrap(Environment* env, + Local<Object> req_wrap_obj, + bool have_callback) + : ReqWrap<uv_udp_send_t>(env, req_wrap_obj) , have_callback_(have_callback) { } @@ -72,9 +70,9 @@ inline bool SendWrap::have_callback() const { } -UDPWrap::UDPWrap(Handle<Object> object) - : HandleWrap(object, reinterpret_cast<uv_handle_t*>(&handle_)) { - int r = uv_udp_init(uv_default_loop(), &handle_); +UDPWrap::UDPWrap(Environment* env, Handle<Object> object) + : HandleWrap(env, object, reinterpret_cast<uv_handle_t*>(&handle_)) { + int r = uv_udp_init(env->event_loop(), &handle_); assert(r == 0); // can't fail anyway } @@ -83,11 +81,10 @@ UDPWrap::~UDPWrap() { } -void UDPWrap::Initialize(Handle<Object> target) { - HandleScope scope(node_isolate); - - oncomplete_sym = FIXED_ONE_BYTE_STRING(node_isolate, "oncomplete"); - onmessage_sym = FIXED_ONE_BYTE_STRING(node_isolate, "onmessage"); +void UDPWrap::Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { + Environment* env = Environment::GetCurrent(context); Local<FunctionTemplate> t = FunctionTemplate::New(New); t->InstanceTemplate()->SetInternalFieldCount(1); @@ -120,15 +117,16 @@ void UDPWrap::Initialize(Handle<Object> target) { NODE_SET_PROTOTYPE_METHOD(t, "ref", HandleWrap::Ref); NODE_SET_PROTOTYPE_METHOD(t, "unref", HandleWrap::Unref); - constructor.Reset(node_isolate, t->GetFunction()); target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "UDP"), t->GetFunction()); + env->set_udp_constructor_function(t->GetFunction()); } void UDPWrap::New(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); assert(args.IsConstructCall()); - new UDPWrap(args.This()); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + new UDPWrap(env, args.This()); } @@ -244,7 +242,8 @@ void UDPWrap::DropMembership(const FunctionCallbackInfo<Value>& args) { void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); UDPWrap* wrap; NODE_UNWRAP(args.This(), UDPWrap, wrap); @@ -269,7 +268,7 @@ void UDPWrap::DoSend(const FunctionCallbackInfo<Value>& args, int family) { assert(offset < Buffer::Length(buffer_obj)); assert(length <= Buffer::Length(buffer_obj) - offset); - SendWrap* req_wrap = new SendWrap(req_wrap_obj, have_callback); + SendWrap* req_wrap = new SendWrap(env, req_wrap_obj, have_callback); uv_buf_t buf = uv_buf_init(Buffer::Data(buffer_obj) + offset, length); @@ -337,7 +336,9 @@ void UDPWrap::RecvStop(const FunctionCallbackInfo<Value>& args) { void UDPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { - HandleScope scope(node_isolate); + Environment* env = Environment::GetCurrent(args.GetIsolate()); + HandleScope handle_scope(args.GetIsolate()); + struct sockaddr_storage address; UDPWrap* wrap; NODE_UNWRAP(args.This(), UDPWrap, wrap); @@ -352,7 +353,7 @@ void UDPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { if (err == 0) { const sockaddr* addr = reinterpret_cast<const sockaddr*>(&address); - AddressToJS(addr, obj); + AddressToJS(env, addr, obj); } args.GetReturnValue().Set(err); @@ -363,10 +364,12 @@ void UDPWrap::GetSockName(const FunctionCallbackInfo<Value>& args) { void UDPWrap::OnSend(uv_udp_send_t* req, int status) { SendWrap* req_wrap = static_cast<SendWrap*>(req->data); if (req_wrap->have_callback()) { - HandleScope scope(node_isolate); + Environment* env = req_wrap->env(); + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); Local<Object> req_wrap_obj = req_wrap->object(); Local<Value> arg = Integer::New(status, node_isolate); - MakeCallback(req_wrap_obj, oncomplete_sym, 1, &arg); + MakeCallback(env, req_wrap_obj, env->oncomplete_string(), 1, &arg); } delete req_wrap; } @@ -397,8 +400,11 @@ void UDPWrap::OnRecv(uv_udp_t* handle, } UDPWrap* wrap = static_cast<UDPWrap*>(handle->data); + Environment* env = wrap->env(); + + Context::Scope context_scope(env->context()); + HandleScope handle_scope(env->isolate()); - HandleScope scope(node_isolate); Local<Object> wrap_obj = wrap->object(); Local<Value> argv[] = { Integer::New(nread, node_isolate), @@ -410,14 +416,18 @@ void UDPWrap::OnRecv(uv_udp_t* handle, if (nread < 0) { if (buf->base != NULL) free(buf->base); - MakeCallback(wrap_obj, onmessage_sym, ARRAY_SIZE(argv), argv); + MakeCallback(env, + wrap_obj, + env->onmessage_string(), + ARRAY_SIZE(argv), + argv); return; } char* base = static_cast<char*>(realloc(buf->base, nread)); - argv[2] = Buffer::Use(base, nread); - argv[3] = AddressToJS(addr); - MakeCallback(wrap_obj, onmessage_sym, ARRAY_SIZE(argv), argv); + argv[2] = Buffer::Use(env, base, nread); + argv[3] = AddressToJS(env, addr); + MakeCallback(env, wrap_obj, env->onmessage_string(), ARRAY_SIZE(argv), argv); } @@ -428,10 +438,10 @@ UDPWrap* UDPWrap::Unwrap(Local<Object> obj) { } -Local<Object> UDPWrap::Instantiate() { +Local<Object> UDPWrap::Instantiate(Environment* env) { // If this assert fires then Initialize hasn't been called yet. - assert(constructor.IsEmpty() == false); - return NewInstance(constructor); + assert(env->udp_constructor_function().IsEmpty() == false); + return env->udp_constructor_function()->NewInstance(); } @@ -442,4 +452,4 @@ uv_udp_t* UDPWrap::UVHandle() { } // namespace node -NODE_MODULE(node_udp_wrap, node::UDPWrap::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_udp_wrap, node::UDPWrap::Initialize) diff --git a/src/udp_wrap.h b/src/udp_wrap.h index e0cfc7ba26..b9aa566be2 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -22,15 +22,19 @@ #ifndef SRC_UDP_WRAP_H_ #define SRC_UDP_WRAP_H_ -#include "node.h" -#include "req_wrap.h" +#include "env.h" #include "handle_wrap.h" +#include "req_wrap.h" +#include "uv.h" +#include "v8.h" namespace node { class UDPWrap: public HandleWrap { public: - static void Initialize(v8::Handle<v8::Object> target); + static void Initialize(v8::Handle<v8::Object> target, + v8::Handle<v8::Value> unused, + v8::Handle<v8::Context> context); static void GetFD(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>&); static void New(const v8::FunctionCallbackInfo<v8::Value>& args); @@ -50,11 +54,11 @@ class UDPWrap: public HandleWrap { static void SetTTL(const v8::FunctionCallbackInfo<v8::Value>& args); static UDPWrap* Unwrap(v8::Local<v8::Object> obj); - static v8::Local<v8::Object> Instantiate(); + static v8::Local<v8::Object> Instantiate(Environment* env); uv_udp_t* UVHandle(); private: - explicit UDPWrap(v8::Handle<v8::Object> object); + UDPWrap(Environment* env, v8::Handle<v8::Object> object); virtual ~UDPWrap(); static void DoBind(const v8::FunctionCallbackInfo<v8::Value>& args, diff --git a/src/util-inl.h b/src/util-inl.h new file mode 100644 index 0000000000..1ea31d94db --- /dev/null +++ b/src/util-inl.h @@ -0,0 +1,83 @@ +// 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_UTIL_INL_H_ +#define SRC_UTIL_INL_H_ + +#include "util.h" + +namespace node { + +template <class TypeName> +inline v8::Local<TypeName> PersistentToLocal( + v8::Isolate* isolate, + const v8::Persistent<TypeName>& persistent) { + if (persistent.IsWeak()) { + return WeakPersistentToLocal(isolate, persistent); + } else { + return StrongPersistentToLocal(persistent); + } +} + +template <class TypeName> +inline v8::Local<TypeName> StrongPersistentToLocal( + const v8::Persistent<TypeName>& persistent) { + return *reinterpret_cast<v8::Local<TypeName>*>( + const_cast<v8::Persistent<TypeName>*>(&persistent)); +} + +template <class TypeName> +inline v8::Local<TypeName> WeakPersistentToLocal( + v8::Isolate* isolate, + const v8::Persistent<TypeName>& persistent) { + return v8::Local<TypeName>::New(isolate, persistent); +} + +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const char* data, + int length) { + return v8::String::NewFromOneByte(isolate, + reinterpret_cast<const uint8_t*>(data), + v8::String::kNormalString, + length); +} + +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const signed char* data, + int length) { + return v8::String::NewFromOneByte(isolate, + reinterpret_cast<const uint8_t*>(data), + v8::String::kNormalString, + length); +} + +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const unsigned char* data, + int length) { + return v8::String::NewFromOneByte(isolate, + reinterpret_cast<const uint8_t*>(data), + v8::String::kNormalString, + length); +} + +} // namespace node + +#endif // SRC_UTIL_INL_H_ diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000000..7e71f36c70 --- /dev/null +++ b/src/util.h @@ -0,0 +1,82 @@ +// 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_UTIL_H_ +#define SRC_UTIL_H_ + +#include "v8.h" +#include <stddef.h> + +namespace node { + +#define OFFSET_OF(TypeName, Field) \ + (reinterpret_cast<uintptr_t>(&(reinterpret_cast<TypeName*>(8)->Field)) - 8) + +#define CONTAINER_OF(Pointer, TypeName, Field) \ + reinterpret_cast<TypeName*>( \ + reinterpret_cast<uintptr_t>(Pointer) - OFFSET_OF(TypeName, Field)) + +#define FIXED_ONE_BYTE_STRING(isolate, string) \ + (node::OneByteString((isolate), (string), sizeof(string) - 1)) + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + void operator=(const TypeName&); \ + TypeName(const TypeName&) + +// If persistent.IsWeak() == false, then do not call persistent.Dispose() +// while the returned Local<T> is still in scope, it will destroy the +// reference to the object. +template <class TypeName> +inline v8::Local<TypeName> PersistentToLocal( + v8::Isolate* isolate, + const v8::Persistent<TypeName>& persistent); + +// Unchecked conversion from a non-weak Persistent<T> to Local<TLocal<T>, +// use with care! +// +// Do not call persistent.Dispose() while the returned Local<T> is still in +// scope, it will destroy the reference to the object. +template <class TypeName> +inline v8::Local<TypeName> StrongPersistentToLocal( + const v8::Persistent<TypeName>& persistent); + +template <class TypeName> +inline v8::Local<TypeName> WeakPersistentToLocal( + v8::Isolate* isolate, + const v8::Persistent<TypeName>& persistent); + +// Convenience wrapper around v8::String::NewFromOneByte(). +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const char* data, + int length = -1); + +// For the people that compile with -funsigned-char. +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const signed char* data, + int length = -1); + +inline v8::Local<v8::String> OneByteString(v8::Isolate* isolate, + const unsigned char* data, + int length = -1); + +} // namespace node + +#endif // SRC_UTIL_H_ @@ -25,6 +25,7 @@ namespace node { namespace uv { +using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::Handle; @@ -44,8 +45,9 @@ void ErrName(const FunctionCallbackInfo<Value>& args) { } -void Initialize(Handle<Object> target) { - v8::HandleScope handle_scope(node_isolate); +void Initialize(Handle<Object> target, + Handle<Value> unused, + Handle<Context> context) { target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "errname"), FunctionTemplate::New(ErrName)->GetFunction()); #define V(name, _) \ @@ -59,4 +61,4 @@ void Initialize(Handle<Object> target) { } // namespace uv } // namespace node -NODE_MODULE(node_uv, node::uv::Initialize) +NODE_MODULE_CONTEXT_AWARE(node_uv, node::uv::Initialize) |