diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 06dcbb4fb1d29d..f7a6d4e68dd483 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -123,8 +123,8 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { Local object = wrapper.As(); CHECK_GT(object->InternalFieldCount(), 0); - AsyncWrap* wrap = Unwrap(object); - if (wrap == nullptr) return nullptr; // ClearWrap() already called. + AsyncWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, object, nullptr); return new RetainedAsyncInfo(class_id, wrap); } @@ -231,7 +231,7 @@ class PromiseWrap : public AsyncWrap { public: PromiseWrap(Environment* env, Local object, bool silent) : AsyncWrap(env, object, PROVIDER_PROMISE, -1, silent) { - MakeWeak(this); + MakeWeak(); } size_t self_size() const override { return sizeof(*this); } diff --git a/src/base_object-inl.h b/src/base_object-inl.h index 5ff211f473b86b..11ba1c88da0486 100644 --- a/src/base_object-inl.h +++ b/src/base_object-inl.h @@ -31,54 +31,90 @@ namespace node { -inline BaseObject::BaseObject(Environment* env, v8::Local handle) +BaseObject::BaseObject(Environment* env, v8::Local handle) : persistent_handle_(env->isolate(), handle), env_(env) { CHECK_EQ(false, handle.IsEmpty()); - // The zero field holds a pointer to the handle. Immediately set it to - // nullptr in case it's accessed by the user before construction is complete. - if (handle->InternalFieldCount() > 0) - handle->SetAlignedPointerInInternalField(0, nullptr); + CHECK_GT(handle->InternalFieldCount(), 0); + handle->SetAlignedPointerInInternalField(0, static_cast(this)); } -inline Persistent& BaseObject::persistent() { +BaseObject::~BaseObject() { + if (persistent_handle_.IsEmpty()) { + // This most likely happened because the weak callback below cleared it. + return; + } + + { + v8::HandleScope handle_scope(env_->isolate()); + object()->SetAlignedPointerInInternalField(0, nullptr); + } +} + + +Persistent& BaseObject::persistent() { return persistent_handle_; } -inline v8::Local BaseObject::object() { +v8::Local BaseObject::object() { return PersistentToLocal(env_->isolate(), persistent_handle_); } -inline Environment* BaseObject::env() const { +Environment* BaseObject::env() const { return env_; } -template -inline void BaseObject::WeakCallback( - const v8::WeakCallbackInfo& data) { - delete data.GetParameter(); +BaseObject* BaseObject::FromJSObject(v8::Local obj) { + CHECK_GT(obj->InternalFieldCount(), 0); + return static_cast(obj->GetAlignedPointerFromInternalField(0)); } -template -inline void BaseObject::MakeWeak(Type* ptr) { - v8::HandleScope scope(env_->isolate()); - v8::Local handle = object(); - CHECK_GT(handle->InternalFieldCount(), 0); - Wrap(handle, ptr); - persistent_handle_.SetWeak(ptr, WeakCallback, - v8::WeakCallbackType::kParameter); +template +T* BaseObject::FromJSObject(v8::Local object) { + return static_cast(FromJSObject(object)); } -inline void BaseObject::ClearWeak() { +void BaseObject::MakeWeak() { + persistent_handle_.SetWeak( + this, + [](const v8::WeakCallbackInfo& data) { + BaseObject* obj = data.GetParameter(); + // Clear the persistent handle so that ~BaseObject() doesn't attempt + // to mess with internal fields, since the JS object may have + // transitioned into an invalid state. + // Refs: https://github.com/nodejs/node/issues/18897 + obj->persistent_handle_.Reset(); + delete obj; + }, v8::WeakCallbackType::kParameter); +} + + +void BaseObject::ClearWeak() { persistent_handle_.ClearWeak(); } + +v8::Local +BaseObject::MakeLazilyInitializedJSTemplate(Environment* env) { + auto constructor = [](const v8::FunctionCallbackInfo& args) { +#ifdef DEBUG + CHECK(args.IsConstructCall()); + CHECK_GT(args.This()->InternalFieldCount(), 0); +#endif + args.This()->SetAlignedPointerInInternalField(0, nullptr); + }; + + v8::Local t = env->NewFunctionTemplate(constructor); + t->InstanceTemplate()->SetInternalFieldCount(1); + return t; +} + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/base_object.h b/src/base_object.h index 478499bbfeb5b2..7d8281238b1c1d 100644 --- a/src/base_object.h +++ b/src/base_object.h @@ -26,6 +26,7 @@ #include "node_persistent.h" #include "v8.h" +#include // std::remove_reference namespace node { @@ -33,8 +34,10 @@ class Environment; class BaseObject { public: + // Associates this object with `handle`. It uses the 0th internal field for + // that, and in particular aborts if there is no such field. inline BaseObject(Environment* env, v8::Local handle); - virtual ~BaseObject() = default; + virtual inline ~BaseObject(); // Returns the wrapped object. Returns an empty handle when // persistent.IsEmpty() is true. @@ -44,23 +47,30 @@ class BaseObject { inline Environment* env() const; - // The handle_ must have an internal field count > 0, and the first - // index is reserved for a pointer to this class. This is an - // implicit requirement, but Node does not have a case where it's - // required that MakeWeak() be called and the internal field not - // be set. - template - inline void MakeWeak(Type* ptr); + // Get a BaseObject* pointer, or subclass pointer, for the JS object that + // was also passed to the `BaseObject()` constructor initially. + // This may return `nullptr` if the C++ object has not been constructed yet, + // e.g. when the JS object used `MakeLazilyInitializedJSTemplate`. + static inline BaseObject* FromJSObject(v8::Local object); + template + static inline T* FromJSObject(v8::Local object); + // Make the `Persistent` a weak reference and, `delete` this object once + // the JS object has been garbage collected. + inline void MakeWeak(); + + // Undo `MakeWeak()`, i.e. turn this into a strong reference. inline void ClearWeak(); + // Utility to create a FunctionTemplate with one internal field (used for + // the `BaseObject*` pointer) and a constructor that initializes that field + // to `nullptr`. + static inline v8::Local MakeLazilyInitializedJSTemplate( + Environment* env); + private: BaseObject(); - template - static inline void WeakCallback( - const v8::WeakCallbackInfo& data); - // persistent_handle_ needs to be at a fixed offset from the start of the // class because it is used by src/node_postmortem_metadata.cc to calculate // offsets and generate debug symbols for BaseObject, which assumes that the @@ -71,6 +81,22 @@ class BaseObject { Environment* env_; }; + +// Global alias for FromJSObject() to avoid churn. +template +inline T* Unwrap(v8::Local obj) { + return BaseObject::FromJSObject(obj); +} + + +#define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \ + do { \ + *ptr = static_cast::type>( \ + BaseObject::FromJSObject(obj)); \ + if (*ptr == nullptr) \ + return __VA_ARGS__; \ + } while (0) + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 0cc7ed1464fdb7..4df47d75d43ba8 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -187,7 +187,7 @@ ChannelWrap::ChannelWrap(Environment* env, is_servers_default_(true), library_inited_(false), active_query_count_(0) { - MakeWeak(this); + MakeWeak(); Setup(); } @@ -205,7 +205,6 @@ class GetAddrInfoReqWrap : public ReqWrap { GetAddrInfoReqWrap(Environment* env, Local req_wrap_obj, bool verbatim); - ~GetAddrInfoReqWrap(); size_t self_size() const override { return sizeof(*this); } bool verbatim() const { return verbatim_; } @@ -219,18 +218,12 @@ GetAddrInfoReqWrap::GetAddrInfoReqWrap(Environment* env, bool verbatim) : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETADDRINFOREQWRAP) , verbatim_(verbatim) { - 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); } }; @@ -238,11 +231,6 @@ class GetNameInfoReqWrap : public ReqWrap { GetNameInfoReqWrap::GetNameInfoReqWrap(Environment* env, Local req_wrap_obj) : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_GETNAMEINFOREQWRAP) { - Wrap(req_wrap_obj, this); -} - -GetNameInfoReqWrap::~GetNameInfoReqWrap() { - ClearWrap(object()); } @@ -587,8 +575,6 @@ class QueryWrap : public AsyncWrap { QueryWrap(ChannelWrap* channel, Local req_wrap_obj) : AsyncWrap(channel->env(), req_wrap_obj, AsyncWrap::PROVIDER_QUERYWRAP), channel_(channel) { - Wrap(req_wrap_obj, this); - // Make sure the channel object stays alive during the query lifetime. req_wrap_obj->Set(env()->context(), env()->channel_string(), @@ -597,7 +583,6 @@ class QueryWrap : public AsyncWrap { ~QueryWrap() override { CHECK_EQ(false, persistent().IsEmpty()); - ClearWrap(object()); } // Subclasses should implement the appropriate Send method. @@ -2143,14 +2128,8 @@ void Initialize(Local target, target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "AI_V4MAPPED"), Integer::New(env->isolate(), AI_V4MAPPED)); - 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); + BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, aiw); Local addrInfoWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "GetAddrInfoReqWrap"); @@ -2158,8 +2137,7 @@ void Initialize(Local target, target->Set(addrInfoWrapString, aiw->GetFunction()); Local niw = - FunctionTemplate::New(env->isolate(), is_construct_call_callback); - niw->InstanceTemplate()->SetInternalFieldCount(1); + BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, niw); Local nameInfoWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "GetNameInfoReqWrap"); @@ -2167,8 +2145,7 @@ void Initialize(Local target, target->Set(nameInfoWrapString, niw->GetFunction()); Local qrw = - FunctionTemplate::New(env->isolate(), is_construct_call_callback); - qrw->InstanceTemplate()->SetInternalFieldCount(1); + BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, qrw); Local queryWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "QueryReqWrap"); diff --git a/src/connect_wrap.cc b/src/connect_wrap.cc index f9ea987e05f5a3..dacdf72da7c494 100644 --- a/src/connect_wrap.cc +++ b/src/connect_wrap.cc @@ -13,12 +13,6 @@ using v8::Object; ConnectWrap::ConnectWrap(Environment* env, Local req_wrap_obj, AsyncWrap::ProviderType provider) : ReqWrap(env, req_wrap_obj, provider) { - Wrap(req_wrap_obj, this); -} - - -ConnectWrap::~ConnectWrap() { - ClearWrap(object()); } } // namespace node diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 6227542bcbee50..80eae7f9bb8290 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -15,7 +15,6 @@ 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/connection_wrap.h b/src/connection_wrap.h index 096672efddaae2..afb168c614aa97 100644 --- a/src/connection_wrap.h +++ b/src/connection_wrap.h @@ -29,7 +29,6 @@ class ConnectionWrap : public LibuvStreamWrap { UVType handle_; }; - } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 1e7505d6d3219a..ed74f36719db79 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -131,7 +131,7 @@ void FSEventWrap::New(const FunctionCallbackInfo& args) { void FSEventWrap::Start(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - FSEventWrap* wrap = Unwrap(args.Holder()); + FSEventWrap* wrap = Unwrap(args.This()); CHECK_NE(wrap, nullptr); CHECK(!wrap->initialized_); diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index a3b0209eb3121f..49bf0c55bea0a1 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -93,7 +93,6 @@ HandleWrap::HandleWrap(Environment* env, handle_(handle) { handle_->data = this; HandleScope scope(env->isolate()); - Wrap(object, this); env->handle_wrap_queue()->PushBack(this); } @@ -114,7 +113,6 @@ void HandleWrap::OnClose(uv_handle_t* handle) { if (have_close_callback) wrap->MakeCallback(env->onclose_string(), 0, nullptr); - ClearWrap(wrap->object()); delete wrap; } diff --git a/src/inspector_js_api.cc b/src/inspector_js_api.cc index 37cdcecd61dabb..ae353defe8079d 100644 --- a/src/inspector_js_api.cc +++ b/src/inspector_js_api.cc @@ -64,7 +64,6 @@ class JSBindingsConnection : public AsyncWrap { Local callback) : AsyncWrap(env, wrap, PROVIDER_INSPECTORJSBINDING), callback_(env->isolate(), callback) { - Wrap(wrap, this); Agent* inspector = env->inspector_agent(); session_ = inspector->Connect(std::unique_ptr( new JSBindingsSessionDelegate(env, this))); @@ -83,9 +82,6 @@ class JSBindingsConnection : public AsyncWrap { void Disconnect() { session_.reset(); - if (!persistent().IsEmpty()) { - ClearWrap(object()); - } delete this; } diff --git a/src/js_stream.cc b/src/js_stream.cc index ed6c6ee738e568..2293d8cf203d07 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -24,12 +24,7 @@ using v8::Value; JSStream::JSStream(Environment* env, Local obj) : AsyncWrap(env, obj, AsyncWrap::PROVIDER_JSSTREAM), StreamBase(env) { - node::Wrap(obj, this); - MakeWeak(this); -} - - -JSStream::~JSStream() { + MakeWeak(); } diff --git a/src/js_stream.h b/src/js_stream.h index 338cbe545630f1..b47a91a653ba7e 100644 --- a/src/js_stream.h +++ b/src/js_stream.h @@ -16,8 +16,6 @@ class JSStream : public AsyncWrap, public StreamBase { v8::Local unused, v8::Local context); - ~JSStream(); - bool IsAlive() override; bool IsClosing() override; int ReadStart() override; diff --git a/src/module_wrap.cc b/src/module_wrap.cc index f88c113ae0b93f..71f1acdaf67688 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -158,7 +158,6 @@ void ModuleWrap::New(const FunctionCallbackInfo& args) { obj->context_.Reset(isolate, context); env->module_map.emplace(module->GetIdentityHash(), obj); - Wrap(that, obj); that->SetIntegrityLevel(context, IntegrityLevel::kFrozen); args.GetReturnValue().Set(that); diff --git a/src/node_contextify.cc b/src/node_contextify.cc index 651e147617f436..4db82d4d7ca3e9 100644 --- a/src/node_contextify.cc +++ b/src/node_contextify.cc @@ -885,7 +885,7 @@ class ContextifyScript : public BaseObject { ContextifyScript(Environment* env, Local object) : BaseObject(env, object) { - MakeWeak(this); + MakeWeak(); } }; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 20a9e2ec4e1333..4b6eee55745f47 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4690,7 +4690,6 @@ class PBKDF2Request : public AsyncWrap { keylen_(keylen), key_(node::Malloc(keylen)), iter_(iter) { - Wrap(object, this); } ~PBKDF2Request() override { @@ -4705,8 +4704,6 @@ class PBKDF2Request : public AsyncWrap { free(key_); key_ = nullptr; keylen_ = 0; - - ClearWrap(object()); } uv_work_t* work_req() { @@ -4868,11 +4865,6 @@ class RandomBytesRequest : public AsyncWrap { size_(size), data_(data), free_mode_(free_mode) { - Wrap(object, this); - } - - ~RandomBytesRequest() override { - ClearWrap(object()); } uv_work_t* work_req() { diff --git a/src/node_crypto.h b/src/node_crypto.h index 3963f7050f2982..6f34eae3591252 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -175,7 +175,7 @@ class SecureContext : public BaseObject { ctx_(nullptr), cert_(nullptr), issuer_(nullptr) { - MakeWeak(this); + MakeWeak(); env->isolate()->AdjustAmountOfExternalAllocatedMemory(kExternalSize); } @@ -403,7 +403,7 @@ class CipherBase : public BaseObject { auth_tag_set_(false), auth_tag_len_(kNoAuthTagLength), pending_auth_failed_(false) { - MakeWeak(this); + MakeWeak(); } private: @@ -434,7 +434,7 @@ class Hmac : public BaseObject { Hmac(Environment* env, v8::Local wrap) : BaseObject(env, wrap), ctx_(nullptr) { - MakeWeak(this); + MakeWeak(); } private: @@ -459,7 +459,7 @@ class Hash : public BaseObject { : BaseObject(env, wrap), mdctx_(nullptr), finalized_(false) { - MakeWeak(this); + MakeWeak(); } private: @@ -514,7 +514,7 @@ class Sign : public SignBase { static void SignFinal(const v8::FunctionCallbackInfo& args); Sign(Environment* env, v8::Local wrap) : SignBase(env, wrap) { - MakeWeak(this); + MakeWeak(); } }; @@ -537,7 +537,7 @@ class Verify : public SignBase { static void VerifyFinal(const v8::FunctionCallbackInfo& args); Verify(Environment* env, v8::Local wrap) : SignBase(env, wrap) { - MakeWeak(this); + MakeWeak(); } }; @@ -605,7 +605,7 @@ class DiffieHellman : public BaseObject { initialised_(false), verifyError_(0), dh(nullptr) { - MakeWeak(this); + MakeWeak(); } private: @@ -641,7 +641,7 @@ class ECDH : public BaseObject { : BaseObject(env, wrap), key_(key), group_(EC_KEY_get0_group(key_)) { - MakeWeak(this); + MakeWeak(); CHECK_NE(group_, nullptr); } diff --git a/src/node_file.cc b/src/node_file.cc index 89c53afc5b197e..97b957eed66b31 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -99,7 +99,7 @@ FileHandle::FileHandle(Environment* env, int fd, Local obj) AsyncWrap::PROVIDER_FILEHANDLE), StreamBase(env), fd_(fd) { - MakeWeak(this); + MakeWeak(); v8::PropertyAttribute attr = static_cast(v8::ReadOnly | v8::DontDelete); object()->DefineOwnProperty(env->context(), diff --git a/src/node_file.h b/src/node_file.h index d6c8aa443c39f4..03e41097d5f12e 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -28,11 +28,6 @@ class FSReqBase : public ReqWrap { FSReqBase(Environment* env, Local req, AsyncWrap::ProviderType type) : ReqWrap(env, req, type) { - Wrap(object(), this); - } - - virtual ~FSReqBase() { - ClearWrap(object()); } void Init(const char* syscall, @@ -249,10 +244,10 @@ class FileHandle : public AsyncWrap, public StreamBase { env->fdclose_constructor_template() ->NewInstance(env->context()).ToLocalChecked(), AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { - Wrap(object(), this); promise_.Reset(env->isolate(), promise); ref_.Reset(env->isolate(), ref); } + ~CloseReq() { uv_fs_req_cleanup(req()); promise_.Reset(); diff --git a/src/node_http2.cc b/src/node_http2.cc index cb5c14a6faa20c..3563013da3e089 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -244,11 +244,6 @@ Http2Session::Http2Settings::Http2Settings( Init(); } -Http2Session::Http2Settings::~Http2Settings() { - if (!object().IsEmpty()) - ClearWrap(object()); -} - // Generates a Buffer that contains the serialized payload of a SETTINGS // frame. This can be used, for instance, to create the Base64-encoded // content of an Http2-Settings header field. @@ -458,7 +453,7 @@ Http2Session::Http2Session(Environment* env, nghttp2_session_type type) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION), session_type_(type) { - MakeWeak(this); + MakeWeak(); statistics_.start_time = uv_hrtime(); // Capture the configuration options for this session @@ -1668,7 +1663,7 @@ Http2Stream::Http2Stream( session_(session), id_(id), current_headers_category_(category) { - MakeWeak(this); + MakeWeak(); statistics_.start_time = uv_hrtime(); // Limit the number of header pairs @@ -2615,11 +2610,6 @@ Http2Session::Http2Ping::Http2Ping( session_(session), startTime_(uv_hrtime()) { } -Http2Session::Http2Ping::~Http2Ping() { - if (!object().IsEmpty()) - ClearWrap(object()); -} - void Http2Session::Http2Ping::Send(uint8_t* payload) { uint8_t data[8]; if (payload == nullptr) { diff --git a/src/node_http2.h b/src/node_http2.h index 8ee4fd450e578d..f4ac926bb54452 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -1148,7 +1148,6 @@ class Http2StreamPerformanceEntry : public PerformanceEntry { class Http2Session::Http2Ping : public AsyncWrap { public: explicit Http2Ping(Http2Session* session); - ~Http2Ping(); size_t self_size() const override { return sizeof(*this); } @@ -1169,7 +1168,6 @@ class Http2Session::Http2Settings : public AsyncWrap { public: explicit Http2Settings(Environment* env); explicit Http2Settings(Http2Session* session); - ~Http2Settings(); size_t self_size() const override { return sizeof(*this); } diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 085f4494e3484e..d6f9b110c3af34 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -151,16 +151,10 @@ class Parser : public AsyncWrap, public StreamListener { : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTPPARSER), current_buffer_len_(0), current_buffer_data_(nullptr) { - Wrap(object(), this); Init(type); } - ~Parser() override { - ClearWrap(object()); - } - - size_t self_size() const override { return sizeof(*this); } diff --git a/src/node_i18n.cc b/src/node_i18n.cc index f491d2191d7b55..5dd30a254ab969 100644 --- a/src/node_i18n.cc +++ b/src/node_i18n.cc @@ -259,7 +259,7 @@ class ConverterObject : public BaseObject, Converter { BaseObject(env, wrap), Converter(converter, sub), ignoreBOM_(ignoreBOM) { - MakeWeak(this); + MakeWeak(); switch (ucnv_getType(converter)) { case UCNV_UTF8: diff --git a/src/node_internals.h b/src/node_internals.h index b8acfa63c26ecf..e5ea575ebc5981 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -244,9 +244,10 @@ v8::Local AddressToJS( template void GetSockOrPeerName(const v8::FunctionCallbackInfo& args) { - T* const wrap = Unwrap(args.Holder()); - if (wrap == nullptr) - return args.GetReturnValue().Set(UV_EBADF); + T* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, + args.Holder(), + args.GetReturnValue().Set(UV_EBADF)); CHECK(args[0]->IsObject()); sockaddr_storage storage; int addrlen = sizeof(storage); diff --git a/src/node_serdes.cc b/src/node_serdes.cc index 6ace942c29fd56..520b350199245a 100644 --- a/src/node_serdes.cc +++ b/src/node_serdes.cc @@ -86,7 +86,7 @@ class DeserializerContext : public BaseObject, SerializerContext::SerializerContext(Environment* env, Local wrap) : BaseObject(env, wrap), serializer_(env->isolate(), this) { - MakeWeak(this); + MakeWeak(); } void SerializerContext::ThrowDataCloneError(Local message) { @@ -274,7 +274,7 @@ DeserializerContext::DeserializerContext(Environment* env, deserializer_(env->isolate(), data_, length_, this) { object()->Set(env->context(), env->buffer_string(), buffer).FromJust(); - MakeWeak(this); + MakeWeak(); } MaybeLocal DeserializerContext::ReadHostObject(Isolate* isolate) { diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index 41683a0dc1d36c..a2cfb1088c9d25 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -83,8 +83,7 @@ static void Delete(uv_handle_t* handle) { StatWatcher::StatWatcher(Environment* env, Local wrap) : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_STATWATCHER), watcher_(new uv_fs_poll_t) { - MakeWeak(this); - Wrap(wrap, this); + MakeWeak(); uv_fs_poll_init(env->event_loop(), watcher_); watcher_->data = static_cast(this); } @@ -191,7 +190,7 @@ void StatWatcher::Stop(const FunctionCallbackInfo& args) { void StatWatcher::Stop() { uv_fs_poll_stop(watcher_); - MakeWeak(this); + MakeWeak(); } diff --git a/src/node_trace_events.cc b/src/node_trace_events.cc index f37537d54544a2..0c0699f7be9e1f 100644 --- a/src/node_trace_events.cc +++ b/src/node_trace_events.cc @@ -37,7 +37,7 @@ class NodeCategorySet : public BaseObject { Local wrap, std::set categories) : BaseObject(env, wrap), categories_(categories) { - MakeWeak(this); + MakeWeak(); } bool enabled_ = false; diff --git a/src/node_wrap.h b/src/node_wrap.h index 843fa7151743de..67cea2e715f869 100644 --- a/src/node_wrap.h +++ b/src/node_wrap.h @@ -33,6 +33,8 @@ namespace node { +// TODO(addaleax): Use real inheritance for the JS object templates to avoid +// this unnecessary case switching. #define WITH_GENERIC_UV_STREAM(env, obj, BODY, ELSE) \ do { \ if (env->tcp_constructor_template().IsEmpty() == false && \ diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 67987baf8a375d..ec447638e2ae62 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -89,8 +89,6 @@ class ZCtx : public AsyncWrap { refs_(0), gzip_id_bytes_read_(0), write_result_(nullptr) { - MakeWeak(this); - Wrap(wrap, this); } @@ -662,7 +660,7 @@ class ZCtx : public AsyncWrap { void Unref() { CHECK_GT(refs_, 0); if (--refs_ == 0) { - MakeWeak(this); + MakeWeak(); } } diff --git a/src/pipe_wrap.cc b/src/pipe_wrap.cc index 0116051b3b6485..da7cb9e3ab55ba 100644 --- a/src/pipe_wrap.cc +++ b/src/pipe_wrap.cc @@ -102,12 +102,7 @@ void PipeWrap::Initialize(Local target, env->set_pipe_constructor_template(t); // 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); + auto cwt = BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, cwt); Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "PipeConnectWrap"); diff --git a/src/stream_base-inl.h b/src/stream_base-inl.h index 4f4252073d26b5..cfe0de0872df4a 100644 --- a/src/stream_base-inl.h +++ b/src/stream_base-inl.h @@ -243,12 +243,6 @@ SimpleShutdownWrap::SimpleShutdownWrap( OtherBase(stream->stream_env(), req_wrap_obj, AsyncWrap::PROVIDER_SHUTDOWNWRAP) { - Wrap(req_wrap_obj, static_cast(this)); -} - -template -SimpleShutdownWrap::~SimpleShutdownWrap() { - ClearWrap(static_cast(this)->object()); } inline ShutdownWrap* StreamBase::CreateShutdownWrap( @@ -264,12 +258,6 @@ SimpleWriteWrap::SimpleWriteWrap( OtherBase(stream->stream_env(), req_wrap_obj, AsyncWrap::PROVIDER_WRITEWRAP) { - Wrap(req_wrap_obj, static_cast(this)); -} - -template -SimpleWriteWrap::~SimpleWriteWrap() { - ClearWrap(static_cast(this)->object()); } inline WriteWrap* StreamBase::CreateWriteWrap( @@ -460,7 +448,7 @@ inline void StreamReq::ResetObject(v8::Local obj) { #ifdef DEBUG CHECK_GT(obj->InternalFieldCount(), StreamReq::kStreamReqField); #endif - ClearWrap(obj); + obj->SetAlignedPointerInInternalField(0, nullptr); // BaseObject field. obj->SetAlignedPointerInInternalField(StreamReq::kStreamReqField, nullptr); } diff --git a/src/stream_base.cc b/src/stream_base.cc index 801b7f4b2f4560..3708ffe7b65c0d 100644 --- a/src/stream_base.cc +++ b/src/stream_base.cc @@ -286,12 +286,6 @@ int StreamBase::WriteString(const FunctionCallbackInfo& args) { uv_stream_t* send_handle = nullptr; if (IsIPCPipe() && !send_handle_obj.IsEmpty()) { - // TODO(addaleax): This relies on the fact that HandleWrap comes first - // as a superclass of each individual subclass. - // There are similar assumptions in other places in the code base. - // A better idea would be having all BaseObject's internal pointers - // refer to the BaseObject* itself; this would require refactoring - // throughout the code base but makes Node rely much less on C++ quirks. HandleWrap* wrap; ASSIGN_OR_RETURN_UNWRAP(&wrap, send_handle_obj, UV_EINVAL); send_handle = reinterpret_cast(wrap->GetHandle()); diff --git a/src/stream_base.h b/src/stream_base.h index 94a091988cfcf9..b91cf7df6cf8c4 100644 --- a/src/stream_base.h +++ b/src/stream_base.h @@ -350,7 +350,6 @@ class SimpleShutdownWrap : public ShutdownWrap, public OtherBase { public: SimpleShutdownWrap(StreamBase* stream, v8::Local req_wrap_obj); - ~SimpleShutdownWrap(); AsyncWrap* GetAsyncWrap() override { return this; } size_t self_size() const override { return sizeof(*this); } @@ -361,7 +360,6 @@ class SimpleWriteWrap : public WriteWrap, public OtherBase { public: SimpleWriteWrap(StreamBase* stream, v8::Local req_wrap_obj); - ~SimpleWriteWrap(); AsyncWrap* GetAsyncWrap() override { return this; } size_t self_size() const override { return sizeof(*this) + StorageSize(); } diff --git a/src/stream_pipe.cc b/src/stream_pipe.cc index 8f0263cd9ae99b..617a0129cfea07 100644 --- a/src/stream_pipe.cc +++ b/src/stream_pipe.cc @@ -17,7 +17,7 @@ StreamPipe::StreamPipe(StreamBase* source, StreamBase* sink, Local obj) : AsyncWrap(source->stream_env(), obj, AsyncWrap::PROVIDER_STREAMPIPE) { - MakeWeak(this); + MakeWeak(); CHECK_NE(sink, nullptr); CHECK_NE(source, nullptr); diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 6158a1c4a424eb..3ccd157159c287 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -26,7 +26,6 @@ #include "handle_wrap.h" #include "node_buffer.h" #include "node_internals.h" -#include "node_wrap.h" #include "connect_wrap.h" #include "stream_base-inl.h" #include "stream_wrap.h" @@ -117,12 +116,8 @@ void TCPWrap::Initialize(Local target, env->set_tcp_constructor_template(t); // 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); + Local cwt = + BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, cwt); Local wrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "TCPConnectWrap"); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 02c0b8166981ab..88377a3e1b88cc 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -115,7 +115,8 @@ class TimerWrap : public HandleWrap { } static void Start(const FunctionCallbackInfo& args) { - TimerWrap* wrap = Unwrap(args.Holder()); + TimerWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); CHECK(HandleWrap::IsAlive(wrap)); @@ -125,7 +126,8 @@ class TimerWrap : public HandleWrap { } static void Stop(const FunctionCallbackInfo& args) { - TimerWrap* wrap = Unwrap(args.Holder()); + TimerWrap* wrap; + ASSIGN_OR_RETURN_UNWRAP(&wrap, args.Holder()); CHECK(HandleWrap::IsAlive(wrap)); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 71adbdfc52ca36..3764e2f3c93d4c 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -68,8 +68,7 @@ TLSWrap::TLSWrap(Environment* env, shutdown_(false), cycle_depth_(0), eof_(false) { - node::Wrap(object(), this); - MakeWeak(this); + MakeWeak(); // sc comes from an Unwrap. Make sure it was assigned. CHECK_NE(sc, nullptr); @@ -873,16 +872,9 @@ void TLSWrap::Initialize(Local target, env->SetMethod(target, "wrap", TLSWrap::Wrap); - auto constructor = [](const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - args.This()->SetAlignedPointerInInternalField(0, nullptr); - }; - + Local t = BaseObject::MakeLazilyInitializedJSTemplate(env); Local tlsWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "TLSWrap"); - - auto t = env->NewFunctionTemplate(constructor); - t->InstanceTemplate()->SetInternalFieldCount(1); t->SetClassName(tlsWrapString); Local get_write_queue_size = diff --git a/src/udp_wrap.cc b/src/udp_wrap.cc index e02220a8784d74..414fe07eab6da8 100644 --- a/src/udp_wrap.cc +++ b/src/udp_wrap.cc @@ -53,7 +53,6 @@ using AsyncHooks = Environment::AsyncHooks; 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); } @@ -67,12 +66,6 @@ SendWrap::SendWrap(Environment* env, bool have_callback) : ReqWrap(env, req_wrap_obj, AsyncWrap::PROVIDER_UDPSENDWRAP), have_callback_(have_callback) { - Wrap(req_wrap_obj, this); -} - - -SendWrap::~SendWrap() { - ClearWrap(object()); } @@ -81,12 +74,6 @@ inline bool SendWrap::have_callback() const { } -static void NewSendWrap(const FunctionCallbackInfo& args) { - CHECK(args.IsConstructCall()); - ClearWrap(args.This()); -} - - UDPWrap::UDPWrap(Environment* env, Local object) : HandleWrap(env, object, @@ -153,8 +140,7 @@ void UDPWrap::Initialize(Local target, // Create FunctionTemplate for SendWrap Local swt = - FunctionTemplate::New(env->isolate(), NewSendWrap); - swt->InstanceTemplate()->SetInternalFieldCount(1); + BaseObject::MakeLazilyInitializedJSTemplate(env); AsyncWrap::AddWrapMethods(env, swt); Local sendWrapString = FIXED_ONE_BYTE_STRING(env->isolate(), "SendWrap"); diff --git a/src/util-inl.h b/src/util-inl.h index d07cfea9227fbe..41a22c97efd9c0 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -217,25 +217,6 @@ inline v8::Local OneByteString(v8::Isolate* isolate, length).ToLocalChecked(); } -template -void Wrap(v8::Local object, TypeName* pointer) { - CHECK_EQ(false, object.IsEmpty()); - CHECK_GT(object->InternalFieldCount(), 0); - object->SetAlignedPointerInInternalField(0, pointer); -} - -void ClearWrap(v8::Local object) { - Wrap(object, nullptr); -} - -template -TypeName* Unwrap(v8::Local object) { - CHECK_EQ(false, object.IsEmpty()); - CHECK_GT(object->InternalFieldCount(), 0); - void* pointer = object->GetAlignedPointerFromInternalField(0); - return static_cast(pointer); -} - void SwapBytes16(char* data, size_t nbytes) { CHECK_EQ(nbytes % 2, 0); diff --git a/src/util.h b/src/util.h index c8bad8171e3bc1..133899008fb7ce 100644 --- a/src/util.h +++ b/src/util.h @@ -35,7 +35,6 @@ #include #include // std::function -#include // std::remove_reference namespace node { @@ -84,8 +83,6 @@ NO_RETURN void Abort(); NO_RETURN void Assert(const char* const (*args)[4]); void DumpBacktrace(FILE* fp); -template using remove_reference = std::remove_reference; - #define FIXED_ONE_BYTE_STRING(isolate, string) \ (node::OneByteString((isolate), (string), sizeof(string) - 1)) @@ -135,14 +132,6 @@ template using remove_reference = std::remove_reference; #define UNREACHABLE() ABORT() -#define ASSIGN_OR_RETURN_UNWRAP(ptr, obj, ...) \ - do { \ - *ptr = \ - Unwrap::type>(obj); \ - if (*ptr == nullptr) \ - return __VA_ARGS__; \ - } while (0) - // TAILQ-style intrusive list node. template class ListNode; @@ -250,13 +239,6 @@ inline v8::Local OneByteString(v8::Isolate* isolate, const unsigned char* data, int length = -1); -inline void Wrap(v8::Local object, void* pointer); - -inline void ClearWrap(v8::Local object); - -template -inline TypeName* Unwrap(v8::Local object); - // Swaps bytes in place. nbytes is the number of bytes to swap and must be a // multiple of the word size (checked by function). inline void SwapBytes16(char* data, size_t nbytes); diff --git a/test/cctest/test_node_postmortem_metadata.cc b/test/cctest/test_node_postmortem_metadata.cc index 335f3e85812547..f69df3ed227717 100644 --- a/test/cctest/test_node_postmortem_metadata.cc +++ b/test/cctest/test_node_postmortem_metadata.cc @@ -72,7 +72,11 @@ TEST_F(DebugSymbolsTest, BaseObjectPersistentHandle) { const Argv argv; Env env{handle_scope, argv}; - v8::Local object = v8::Object::New(isolate_); + v8::Local obj_templ = v8::ObjectTemplate::New(isolate_); + obj_templ->SetInternalFieldCount(1); + + v8::Local object = + obj_templ->NewInstance(env.context()).ToLocalChecked(); node::BaseObject obj(*env, object); auto expected = reinterpret_cast(&obj.persistent());