diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2013-08-11 00:26:11 +0200 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2013-09-06 05:51:42 +0200 |
commit | 756b6222956b5d25b2e7db81f4e79033a3a4d20e (patch) | |
tree | eb03b6f0bf33c36e011858c4a31b10a6eaadfc19 | |
parent | 81655a224a36948291e1f21f03273b5b604b7e6a (diff) | |
download | node-756b6222956b5d25b2e7db81f4e79033a3a4d20e.tar.gz |
src: add multi-context support
This commit makes it possible to use multiple V8 execution contexts
within a single event loop. Put another way, handle and request wrap
objects now "remember" the context they belong to and switch back to
that context when the time comes to call into JS land.
This could have been done in a quick and hacky way by calling
v8::Object::GetCreationContext() on the wrap object right before
making a callback but that leaves a fairly wide margin for bugs.
Instead, we make the context explicit through a new Environment class
that encapsulates everything (or almost everything) that belongs to
the context. Variables that used to be a static or a global are now
members of the aforementioned class. An additional benefit is that
this approach should make it relatively straightforward to add full
isolate support in due course.
There is no JavaScript API yet but that will be added in the near
future.
This work was graciously sponsored by GitHub, Inc.
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 f09590e88..00238788a 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 000000000..a8fcc5860 --- /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 000000000..db8d660c3 --- /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 8906f43ee..719ee8c7e 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 858b55cdd..05d4c4ddd 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 b5918f5df..73a43c3a3 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 c88b0effb..4670151a8 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 527ac25a3..00f60d3fb 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 3c7ede82b..9ba1b3b4a 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 519fddf4c..2e7374971 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 ca9a135e9..78e6e42f6 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 331dbdb6e..132fa911d 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 ff90e91a4..9a3cfe939 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 b1c2c7fb0..2ae879403 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 c29c0fda5..fa49dd4bc 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 0a5d27010..dc5deedb0 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 80485ec05..80046a9b7 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 ba110a51c..141bd92ff 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 36506fc6e..e3d9971e3 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 fb3cf4f8a..1cd13c084 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 082608f76..fd38c8b42 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 e02b93252..a355cfac5 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 8839a7a50..0f56f6ddf 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 91bfb0dd3..ebc9d2b91 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 97375ab84..805e69b66 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 944835774..89330fc72 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 1784a163d..bff4e0ba8 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 44749a300..8a701e515 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 b9f6d49ff..a0d62e84a 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 07eecb942..62a57fada 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 6a5ff168d..49a2f90f4 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 6731fdbf7..26fb5d5ef 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 e00705629..305ee1db2 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 374458b78..b1fa4c7f8 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 c9782d871..76b95540f 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 cff2cded8..e6afda6c0 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 83f54c3b9..d8f399792 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 0b675e1bb..aad19a7e6 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 0475073fd..4c69b4c88 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 1e5d32269..fd679ab4d 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 e0cfc7ba2..b9aa566be 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 000000000..1ea31d94d --- /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 000000000..7e71f36c7 --- /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) |