From 81301991b6010727ddaba118b2c44aca58d0efa0 Mon Sep 17 00:00:00 2001 From: Trevor Norris Date: Wed, 2 Nov 2016 13:55:00 -0600 Subject: [PATCH] async_wrap,src: add GetAsyncId() method Allow handles to retrieve their own uid's by adding a new method on the FunctionTemplates. Implementation of these into all other classes will come in a future commit. Add the method AsyncWrap::GetAsyncId() to all inheriting class objects so the uid of the handle can be retrieved from JS. In all applicable locations, run ClearWrap() on the object holding the pointer so that it never points to invalid memory and make sure Wrap() is always run so the class pointer is correctly attached to the object and can be retrieved so GetAsyncId() can be run. In many places a class instance was not removing its own pointer from object() in the destructor. This left an invalid pointer in the JS object that could cause the application to segfault under certain conditions. Remove ClearWrap() from ReqWrap for continuity. The ReqWrap constructor was not the one to call Wrap(), so it shouldn't be the one to call ClearWrap(). Wrap() has been added to all constructors that inherit from AsyncWrap. Normally it's the child most class. Except in the case of HandleWrap. Which must be the constructor that runs Wrap() because the class pointer is retrieved for certain calls and because other child classes have multiple inheritance to pointer to the HandleWrap needs to be stored. ClearWrap() has been placed in all FunctionTemplate constructors so that no random values are returned when running getAsyncId(). ClearWrap() has also been placed in all class destructors, except in those that use MakeWeak() because the destructor will run during GC. Making the object() inaccessible. It could be simplified to where AsyncWrap sets the internal pointer, then if an inheriting class needs one of it's own it could set it again. But the inverse would need to be true also, where AsyncWrap then also runs ClearWeak. Unforunately because some of the handles are cleaned up during GC that's impossible. Also in the case of ReqWrap it runs Reset() in the destructor, making the object() inaccessible. Meaning, ClearWrap() must be run by the class that runs Wrap(). There's currently no generalized way of taking care of this across all instances of AsyncWrap. I'd prefer that there be checks in there for these things, but haven't found a way to place them that wouldn't be just as unreliable. Add test that checks all resources that can run getAsyncId(). Would like a way to enforce that any new classes that can also run getAsyncId() are tested, but don't have one. PR-URL: https://github.com/nodejs/node/pull/12892 Ref: https://github.com/nodejs/node/pull/11883 Ref: https://github.com/nodejs/node/pull/8531 Reviewed-By: Andreas Madsen Reviewed-By: Anna Henningsen Reviewed-By: Sam Roberts Reviewed-By: Matteo Collina Reviewed-By: Refael Ackermann Reviewed-By: James M Snell Reviewed-By: Jeremiah Senkpiel --- src/async-wrap.cc | 8 + src/async-wrap.h | 2 + src/cares_wrap.cc | 15 ++ src/connect_wrap.cc | 5 + src/connect_wrap.h | 1 + src/fs_event_wrap.cc | 1 + src/js_stream.cc | 2 + src/node_crypto.cc | 3 + src/node_crypto.h | 1 + src/node_file.cc | 7 +- src/node_http_parser.cc | 1 + src/node_stat_watcher.cc | 2 + src/node_zlib.cc | 2 + src/pipe_wrap.cc | 4 + src/process_wrap.cc | 2 + src/req-wrap-inl.h | 1 - src/signal_wrap.cc | 1 + src/stream_base.h | 8 + src/stream_wrap.cc | 3 + src/tcp_wrap.cc | 3 + src/timer_wrap.cc | 2 + src/tls_wrap.cc | 1 + src/tty_wrap.cc | 2 + src/udp_wrap.cc | 10 + test/parallel/test-async-wrap-getasyncid.js | 241 ++++++++++++++++++++ 25 files changed, 326 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-async-wrap-getasyncid.js diff --git a/src/async-wrap.cc b/src/async-wrap.cc index 304e5d0c956490..11ed67d24207c0 100644 --- a/src/async-wrap.cc +++ b/src/async-wrap.cc @@ -180,6 +180,14 @@ static void SetupHooks(const FunctionCallbackInfo& args) { } +void AsyncWrap::GetAsyncId(const FunctionCallbackInfo& args) { + AsyncWrap* wrap; + args.GetReturnValue().Set(-1); + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); + args.GetReturnValue().Set(wrap->get_id()); +} + + void AsyncWrap::Initialize(Local target, Local unused, Local context) { diff --git a/src/async-wrap.h b/src/async-wrap.h index ecf8db9fc5eea9..7ccae02cced05b 100644 --- a/src/async-wrap.h +++ b/src/async-wrap.h @@ -85,6 +85,8 @@ class AsyncWrap : public BaseObject { v8::Local unused, v8::Local context); + static void GetAsyncId(const v8::FunctionCallbackInfo& args); + static void DestroyIdsCb(uv_idle_t* handle); inline ProviderType provider_type() const; diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 7a9102ff3dc529..15c261b6f165f5 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -104,6 +104,7 @@ inline const char* ToErrorCodeString(int status) { class GetAddrInfoReqWrap : public ReqWrap { public: GetAddrInfoReqWrap(Environment* env, Local req_wrap_obj); + ~GetAddrInfoReqWrap(); size_t self_size() const override { return sizeof(*this); } }; @@ -114,10 +115,15 @@ GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, Wrap(req_wrap_obj, this); } +GetAddrInfoReqWrap::~GetAddrInfoReqWrap() { + ClearWrap(object()); +} + class GetNameInfoReqWrap : public ReqWrap { public: GetNameInfoReqWrap(Environment* env, Local req_wrap_obj); + ~GetNameInfoReqWrap(); size_t self_size() const override { return sizeof(*this); } }; @@ -128,6 +134,10 @@ GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, Wrap(req_wrap_obj, this); } +GetNameInfoReqWrap::~GetNameInfoReqWrap() { + ClearWrap(object()); +} + int cmp_ares_tasks(const node_ares_task* a, const node_ares_task* b) { if (a->sock < b->sock) @@ -293,6 +303,7 @@ class QueryWrap : public AsyncWrap { : AsyncWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP) { if (env->in_domain()) req_wrap_obj->Set(env->domain_string(), env->domain_array()->Get(0)); + Wrap(req_wrap_obj, this); } ~QueryWrap() override { @@ -1388,10 +1399,12 @@ void Initialize(Local target, auto is_construct_call_callback = [](const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); }; Local aiw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); aiw->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(aiw, "getAsyncId", AsyncWrap::GetAsyncId); aiw->SetClassName( FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"), @@ -1400,6 +1413,7 @@ void Initialize(Local target, Local niw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); niw->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(niw, "getAsyncId", AsyncWrap::GetAsyncId); niw->SetClassName( FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"), @@ -1408,6 +1422,7 @@ void Initialize(Local target, Local qrw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); qrw->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(qrw, "getAsyncId", AsyncWrap::GetAsyncId); qrw->SetClassName( FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"), diff --git a/src/connect_wrap.cc b/src/connect_wrap.cc index df3f093e732972..e373b5a36e33e6 100644 --- a/src/connect_wrap.cc +++ b/src/connect_wrap.cc @@ -19,4 +19,9 @@ ConnectWrap::ConnectWrap(Environment* env, Wrap(req_wrap_obj, this); } + +ConnectWrap::~ConnectWrap() { + ClearWrap(object()); +} + } // namespace node diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 28d4872d7ed416..7b16a5448745aa 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -15,6 +15,7 @@ class ConnectWrap : public ReqWrap { ConnectWrap(Environment* env, v8::Local req_wrap_obj, AsyncWrap::ProviderType provider); + ~ConnectWrap(); size_t self_size() const override { return sizeof(*this); } }; diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index bc3b33027ac3a6..228c3a344edf3c 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -94,6 +94,7 @@ void FSEventWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(fsevent_string); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "close", Close); diff --git a/src/js_stream.cc b/src/js_stream.cc index e51c4ae9b35084..1d20e1c6d77dfb 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -221,6 +221,8 @@ void JSStream::Initialize(Local target, t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "JSStream")); t->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + env->SetProtoMethod(t, "doAlloc", DoAlloc); env->SetProtoMethod(t, "doRead", DoRead); env->SetProtoMethod(t, "doAfterWrite", DoAfterWrite); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 87e008acd97e10..dac4a2e76bfe76 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2737,6 +2737,7 @@ void Connection::Initialize(Environment* env, Local target) { t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Connection")); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "encIn", Connection::EncIn); env->SetProtoMethod(t, "clearOut", Connection::ClearOut); env->SetProtoMethod(t, "clearIn", Connection::ClearIn); @@ -6258,12 +6259,14 @@ void InitCrypto(Local target, Local pb = FunctionTemplate::New(env->isolate()); pb->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PBKDF2")); + env->SetProtoMethod(pb, "getAsyncId", AsyncWrap::GetAsyncId); Local pbt = pb->InstanceTemplate(); pbt->SetInternalFieldCount(1); env->set_pbkdf2_constructor_template(pbt); Local rb = FunctionTemplate::New(env->isolate()); rb->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "RandomBytes")); + env->SetProtoMethod(rb, "getAsyncId", AsyncWrap::GetAsyncId); Local rbt = rb->InstanceTemplate(); rbt->SetInternalFieldCount(1); env->set_randombytes_constructor_template(rbt); diff --git a/src/node_crypto.h b/src/node_crypto.h index 90e268456a4f12..ad1b493596aa7a 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -408,6 +408,7 @@ class Connection : public AsyncWrap, public SSLWrap { bio_write_(nullptr), hello_offset_(0) { MakeWeak(this); + Wrap(wrap, this); hello_parser_.Start(SSLWrap::OnClientHello, OnClientHelloParseEnd, this); diff --git a/src/node_file.cc b/src/node_file.cc index 4a0b1527d6aca4..7a3be9db541a16 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -110,7 +110,10 @@ class FSReqWrap: public ReqWrap { Wrap(object(), this); } - ~FSReqWrap() { ReleaseEarly(); } + ~FSReqWrap() { + ReleaseEarly(); + ClearWrap(object()); + } void* operator new(size_t size) = delete; void* operator new(size_t size, char* storage) { return storage; } @@ -151,6 +154,7 @@ void FSReqWrap::Dispose() { void NewFSReqWrap(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); } @@ -1474,6 +1478,7 @@ void InitFs(Local target, Local fst = FunctionTemplate::New(env->isolate(), NewFSReqWrap); fst->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(fst, "getAsyncId", AsyncWrap::GetAsyncId); fst->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "FSReqWrap"), fst->GetFunction()); diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index d504f42f35107b..531a83392c291f 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -785,6 +785,7 @@ void InitHttpParser(Local target, #undef V target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "methods"), methods); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "close", Parser::Close); env->SetProtoMethod(t, "execute", Parser::Execute); env->SetProtoMethod(t, "finish", Parser::Finish); diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 9eeed77476be56..18bf2c54193d7b 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -49,6 +49,7 @@ void StatWatcher::Initialize(Environment* env, Local target) { t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "StatWatcher")); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "start", StatWatcher::Start); env->SetProtoMethod(t, "stop", StatWatcher::Stop); @@ -66,6 +67,7 @@ StatWatcher::StatWatcher(Environment* env, Local wrap) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_STATWATCHER), watcher_(new uv_fs_poll_t) { MakeWeak(this); + Wrap(wrap, this); uv_fs_poll_init(env->event_loop(), watcher_); watcher_->data = static_cast(this); } diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 054023ca10360f..e4adda52026c87 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -88,6 +88,7 @@ class ZCtx : public AsyncWrap { refs_(0), gzip_id_bytes_read_(0) { MakeWeak(this); + Wrap(wrap, this); } @@ -678,6 +679,7 @@ void InitZlib(Local target, z->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(z, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(z, "write", ZCtx::Write); env->SetProtoMethod(z, "writeSync", ZCtx::Write); env->SetProtoMethod(z, "init", ZCtx::Init); diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 132b2662f516f3..8c251f1f741461 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -69,6 +69,8 @@ void PipeWrap::Initialize(Local target, t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Pipe")); t->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "ref", HandleWrap::Ref); @@ -95,9 +97,11 @@ void PipeWrap::Initialize(Local target, // Create FunctionTemplate for PipeConnectWrap. auto constructor = [](const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); }; auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); cwt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap"), cwt->GetFunction()); diff --git a/src/process_wrap.cc b/src/process_wrap.cc index 2780adad74afca..cae0788927bc89 100644 --- a/src/process_wrap.cc +++ b/src/process_wrap.cc @@ -55,6 +55,8 @@ class ProcessWrap : public HandleWrap { constructor->InstanceTemplate()->SetInternalFieldCount(1); constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Process")); + env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + env->SetProtoMethod(constructor, "close", HandleWrap::Close); env->SetProtoMethod(constructor, "spawn", Spawn); diff --git a/src/req-wrap-inl.h b/src/req-wrap-inl.h index 84af22023dc3b9..e21fb1bdad9363 100644 --- a/src/req-wrap-inl.h +++ b/src/req-wrap-inl.h @@ -30,7 +30,6 @@ template ReqWrap::~ReqWrap() { CHECK_EQ(req_.data, this); // Assert that someone has called Dispatched(). CHECK_EQ(false, persistent().IsEmpty()); - ClearWrap(object()); persistent().Reset(); } diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index 36b862ce82523f..ccd1b0ec411dc8 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -51,6 +51,7 @@ class SignalWrap : public HandleWrap { constructor->InstanceTemplate()->SetInternalFieldCount(1); constructor->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "Signal")); + env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(constructor, "close", HandleWrap::Close); env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); diff --git a/src/stream_base.h b/src/stream_base.h index e2ef8d8d396b87..581c5405aaf594 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -53,6 +53,10 @@ class ShutdownWrap : public ReqWrap, Wrap(req_wrap_obj, this); } + ~ShutdownWrap() { + ClearWrap(object()); + } + static ShutdownWrap* from_req(uv_shutdown_t* req) { return ContainerOf(&ShutdownWrap::req_, req); } @@ -98,6 +102,10 @@ class WriteWrap: public ReqWrap, Wrap(obj, this); } + ~WriteWrap() { + ClearWrap(object()); + } + void* operator new(size_t size) = delete; void* operator new(size_t size, char* storage) { return storage; } diff --git a/src/stream_wrap.cc b/src/stream_wrap.cc index 83c375b54b8a75..065505af1971ac 100644 --- a/src/stream_wrap.cc +++ b/src/stream_wrap.cc @@ -62,11 +62,13 @@ void StreamWrap::Initialize(Local target, auto is_construct_call_callback = [](const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); }; Local sw = FunctionTemplate::New(env->isolate(), is_construct_call_callback); sw->InstanceTemplate()->SetInternalFieldCount(1); sw->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap")); + env->SetProtoMethod(sw, "getAsyncId", AsyncWrap::GetAsyncId); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ShutdownWrap"), sw->GetFunction()); @@ -74,6 +76,7 @@ void StreamWrap::Initialize(Local target, FunctionTemplate::New(env->isolate(), is_construct_call_callback); ww->InstanceTemplate()->SetInternalFieldCount(1); ww->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap")); + env->SetProtoMethod(ww, "getAsyncId", AsyncWrap::GetAsyncId); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "WriteWrap"), ww->GetFunction()); env->set_write_wrap_constructor_function(ww->GetFunction()); diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index f2525b1fb1b22a..931b637751e610 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -84,6 +84,7 @@ void TCPWrap::Initialize(Local target, "onconnection"), Null(env->isolate())); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "close", HandleWrap::Close); @@ -116,9 +117,11 @@ void TCPWrap::Initialize(Local target, // Create FunctionTemplate for TCPConnectWrap. auto constructor = [](const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); }; auto cwt = FunctionTemplate::New(env->isolate(), constructor); cwt->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(cwt, "getAsyncId", AsyncWrap::GetAsyncId); cwt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap"), cwt->GetFunction()); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 382bcaacb6c02d..609d087e334f72 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -57,6 +57,8 @@ class TimerWrap : public HandleWrap { env->SetTemplateMethod(constructor, "now", Now); + env->SetProtoMethod(constructor, "getAsyncId", AsyncWrap::GetAsyncId); + env->SetProtoMethod(constructor, "close", HandleWrap::Close); env->SetProtoMethod(constructor, "ref", HandleWrap::Ref); env->SetProtoMethod(constructor, "unref", HandleWrap::Unref); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 6f2d0e4c16d576..05349b2f55230d 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -939,6 +939,7 @@ void TLSWrap::Initialize(Local target, t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap")); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); env->SetProtoMethod(t, "receive", Receive); env->SetProtoMethod(t, "start", Start); env->SetProtoMethod(t, "setVerifyMode", SetVerifyMode); diff --git a/src/tty_wrap.cc b/src/tty_wrap.cc index b6e3efcc100a34..f3f1edfe5d3248 100644 --- a/src/tty_wrap.cc +++ b/src/tty_wrap.cc @@ -53,6 +53,8 @@ void TTYWrap::Initialize(Local target, t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TTY")); t->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + env->SetProtoMethod(t, "close", HandleWrap::Close); env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "ref", HandleWrap::Ref); diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index 4f5388080e2bfe..fe2b10661fd7c3 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -54,6 +54,7 @@ using v8::Value; class SendWrap : public ReqWrap { public: SendWrap(Environment* env, Local req_wrap_obj, bool have_callback); + ~SendWrap(); inline bool have_callback() const; size_t msg_size; size_t self_size() const override { return sizeof(*this); } @@ -71,6 +72,11 @@ SendWrap::SendWrap(Environment* env, } +SendWrap::~SendWrap() { + ClearWrap(object()); +} + + inline bool SendWrap::have_callback() const { return have_callback_; } @@ -78,6 +84,7 @@ inline bool SendWrap::have_callback() const { static void NewSendWrap(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); + ClearWrap(args.This()); } @@ -129,6 +136,8 @@ void UDPWrap::Initialize(Local target, env->SetProtoMethod(t, "unref", HandleWrap::Unref); env->SetProtoMethod(t, "hasRef", HandleWrap::HasRef); + env->SetProtoMethod(t, "getAsyncId", AsyncWrap::GetAsyncId); + target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "UDP"), t->GetFunction()); env->set_udp_constructor_function(t->GetFunction()); @@ -136,6 +145,7 @@ void UDPWrap::Initialize(Local target, Local swt = FunctionTemplate::New(env->isolate(), NewSendWrap); swt->InstanceTemplate()->SetInternalFieldCount(1); + env->SetProtoMethod(swt, "getAsyncId", AsyncWrap::GetAsyncId); swt->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap")); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap"), swt->GetFunction()); diff --git a/test/parallel/test-async-wrap-getasyncid.js b/test/parallel/test-async-wrap-getasyncid.js new file mode 100644 index 00000000000000..5e2ce3a820381c --- /dev/null +++ b/test/parallel/test-async-wrap-getasyncid.js @@ -0,0 +1,241 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const net = require('net'); +const providers = Object.assign({}, process.binding('async_wrap').Providers); + +// Make sure that all Providers are tested. +{ + const hooks = require('async_hooks').createHook({ + init(id, type) { + if (type === 'NONE') + throw new Error('received a provider type of NONE'); + delete providers[type]; + }, + }).enable(); + process.on('beforeExit', common.mustCall(() => { + process.removeAllListeners('uncaughtException'); + hooks.disable(); + delete providers.NONE; // Should never be used. + const obj_keys = Object.keys(providers); + if (obj_keys.length > 0) + process._rawDebug(obj_keys); + assert.strictEqual(obj_keys.length, 0); + })); +} + +function testUninitialized(req, ctor_name) { + assert.strictEqual(typeof req.getAsyncId, 'function'); + assert.strictEqual(req.getAsyncId(), -1); + assert.strictEqual(req.constructor.name, ctor_name); +} + +function testInitialized(req, ctor_name) { + assert.strictEqual(typeof req.getAsyncId, 'function'); + assert(Number.isSafeInteger(req.getAsyncId())); + assert(req.getAsyncId() > 0); + assert.strictEqual(req.constructor.name, ctor_name); +} + + +{ + const cares = process.binding('cares_wrap'); + const dns = require('dns'); + + testUninitialized(new cares.GetAddrInfoReqWrap(), 'GetAddrInfoReqWrap'); + testUninitialized(new cares.GetNameInfoReqWrap(), 'GetNameInfoReqWrap'); + testUninitialized(new cares.QueryReqWrap(), 'QueryReqWrap'); + + testInitialized(dns.lookup('www.google.com', () => {}), 'GetAddrInfoReqWrap'); + testInitialized(dns.lookupService('::1', 22, () => {}), 'GetNameInfoReqWrap'); + testInitialized(dns.resolve6('::1', () => {}), 'QueryReqWrap'); +} + + +{ + const FSEvent = process.binding('fs_event_wrap').FSEvent; + testInitialized(new FSEvent(), 'FSEvent'); +} + + +{ + const JSStream = process.binding('js_stream').JSStream; + testInitialized(new JSStream(), 'JSStream'); +} + + +if (common.hasCrypto) { + const tls = require('tls'); + // SecurePair + testInitialized(tls.createSecurePair().ssl, 'Connection'); +} + + +if (common.hasCrypto) { + const crypto = require('crypto'); + + // The handle for PBKDF2 and RandomBytes isn't returned by the function call, + // so need to check it from the callback. + + const mc = common.mustCall(function pb() { + testInitialized(this, 'PBKDF2'); + }); + crypto.pbkdf2('password', 'salt', 1, 20, 'sha256', mc); + + crypto.randomBytes(1, common.mustCall(function rb() { + testInitialized(this, 'RandomBytes'); + })); +} + + +{ + const binding = process.binding('fs'); + const path = require('path'); + + const FSReqWrap = binding.FSReqWrap; + const req = new FSReqWrap(); + req.oncomplete = () => { }; + + testUninitialized(req, 'FSReqWrap'); + binding.access(path._makeLong('../'), fs.F_OK, req); + testInitialized(req, 'FSReqWrap'); + + const StatWatcher = binding.StatWatcher; + testInitialized(new StatWatcher(), 'StatWatcher'); +} + + +{ + const HTTPParser = process.binding('http_parser').HTTPParser; + testInitialized(new HTTPParser(), 'HTTPParser'); +} + + +{ + const Gzip = require('zlib').Gzip; + testInitialized(new Gzip()._handle, 'Zlib'); +} + + +{ + const binding = process.binding('pipe_wrap'); + const handle = new binding.Pipe(); + testInitialized(handle, 'Pipe'); + const req = new binding.PipeConnectWrap(); + testUninitialized(req, 'PipeConnectWrap'); + req.address = common.PIPE; + req.oncomplete = common.mustCall(() => handle.close()); + handle.connect(req, req.address, req.oncomplete); + testInitialized(req, 'PipeConnectWrap'); +} + + +{ + const Process = process.binding('process_wrap').Process; + testInitialized(new Process(), 'Process'); +} + + +{ + const Signal = process.binding('signal_wrap').Signal; + testInitialized(new Signal(), 'Signal'); +} + + +{ + const binding = process.binding('stream_wrap'); + testUninitialized(new binding.WriteWrap(), 'WriteWrap'); +} + +{ + const stream_wrap = process.binding('stream_wrap'); + const tcp_wrap = process.binding('tcp_wrap'); + const server = net.createServer(common.mustCall((socket) => { + socket.on('data', (x) => { + socket.end(); + socket.destroy(); + }); + socket.resume(); + })).listen(0, common.localhostIPv4, common.mustCall(() => { + const handle = new tcp_wrap.TCP(); + const req = new tcp_wrap.TCPConnectWrap(); + const sreq = new stream_wrap.ShutdownWrap(); + const wreq = new stream_wrap.WriteWrap(); + testInitialized(handle, 'TCP'); + testUninitialized(req, 'TCPConnectWrap'); + testUninitialized(sreq, 'ShutdownWrap'); + + sreq.oncomplete = common.mustCall(() => { + handle.close(); + server.close(); + }); + + wreq.handle = handle; + wreq.oncomplete = common.mustCall(() => { + handle.shutdown(sreq); + testInitialized(sreq, 'ShutdownWrap'); + }); + wreq.async = true; + + req.oncomplete = common.mustCall(() => { + // Use a long string to make sure the write happens asynchronously. + const err = handle.writeLatin1String(wreq, 'hi'.repeat(100000)); + if (err) + throw new Error(`write failed: ${process.binding('uv').errname(err)}`); + testInitialized(wreq, 'WriteWrap'); + }); + req.address = common.localhostIPv4; + req.port = server.address().port; + const err = handle.connect(req, req.address, req.port); + assert.strictEqual(err, 0); + testInitialized(req, 'TCPConnectWrap'); + })); +} + + +{ + const TimerWrap = process.binding('timer_wrap').Timer; + testInitialized(new TimerWrap(), 'Timer'); +} + + +if (common.hasCrypto) { + const TCP = process.binding('tcp_wrap').TCP; + const tcp = new TCP(); + + const ca = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); + const cert = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); + const key = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); + const credentials = require('tls').createSecureContext({ ca, cert, key }); + + // TLSWrap is exposed, but needs to be instantiated via tls_wrap.wrap(). + const tls_wrap = process.binding('tls_wrap'); + testInitialized( + tls_wrap.wrap(tcp._externalStream, credentials.context, true), 'TLSWrap'); +} + + +{ + const tty_wrap = process.binding('tty_wrap'); + if (tty_wrap.isTTY(0)) { + testInitialized(new tty_wrap.TTY(0, false), 'TTY'); + } +} + + +{ + const binding = process.binding('udp_wrap'); + const handle = new binding.UDP(); + const req = new binding.SendWrap(); + testInitialized(handle, 'UDP'); + testUninitialized(req, 'SendWrap'); + + handle.bind('0.0.0.0', common.PORT, undefined); + req.address = '127.0.0.1'; + req.port = common.PORT; + req.oncomplete = () => handle.close(); + handle.send(req, [Buffer.alloc(1)], 1, req.port, req.address, true); + testInitialized(req, 'SendWrap'); +}