From b88e7979babc9dc103122e5d611af1ab6f90ec01 Mon Sep 17 00:00:00 2001 From: Dmitry Ashkadov Date: Fri, 22 Nov 2019 23:46:24 +0300 Subject: [PATCH 1/2] Issue 602: Add template methods to ObjectWrap ObjectWrap was enhanced to support template methods for defining properties and methods of JS class. Now C++ methods and functions may be passed as template parameters for ObjectWrap::InstanceMethod, ObjectWrap::StaticAccessor, etc. There are several benefits: - no need to allocate extra memory for passing C++ function napi callback and use add_finalizer() to free memory; - a compiler can see whole chain of calls up to napi callback that may allow better optimisation. Some examples: ```cpp // Method InstanceMethod<&MyClass::method>("method"); // Read-write property InstanceAccessor<&MyClass::get, &MyClass::set>("rw_prop"); // Read-only property InstanceAccessor<&MyClass::get>("ro_prop"); ``` Closes #602. --- doc/class_property_descriptor.md | 6 +- doc/object_wrap.md | 280 ++++++++++++++++++++++++++++++- napi-inl.h | 235 ++++++++++++++++++++++++++ napi.h | 93 +++++++++- test/objectwrap.cc | 46 +++++ test/objectwrap.js | 37 ++++ tools/README.md | 8 +- 7 files changed, 694 insertions(+), 11 deletions(-) diff --git a/doc/class_property_descriptor.md b/doc/class_property_descriptor.md index bb492de7d..92336e7ea 100644 --- a/doc/class_property_descriptor.md +++ b/doc/class_property_descriptor.md @@ -26,9 +26,9 @@ class Example : public Napi::ObjectWrap { Napi::Object Example::Init(Napi::Env env, Napi::Object exports) { Napi::Function func = DefineClass(env, "Example", { // Register a class instance accessor with getter and setter functions. - InstanceAccessor("value", &Example::GetValue, &Example::SetValue), - // We can also register a readonly accessor by passing nullptr as the setter. - InstanceAccessor("readOnlyProp", &Example::GetValue, nullptr) + InstanceAccessor<&Example::GetValue, &Example::SetValue>("value"), + // We can also register a readonly accessor by omitting the setter. + InstanceAccessor<&Example::GetValue>("readOnlyProp") }); constructor = Napi::Persistent(func); diff --git a/doc/object_wrap.md b/doc/object_wrap.md index 1db658a91..5a0ec1036 100644 --- a/doc/object_wrap.md +++ b/doc/object_wrap.md @@ -34,8 +34,8 @@ class Example : public Napi::ObjectWrap { Napi::Object Example::Init(Napi::Env env, Napi::Object exports) { // This method is used to hook the accessor and method callbacks Napi::Function func = DefineClass(env, "Example", { - InstanceMethod("GetValue", &Example::GetValue), - InstanceMethod("SetValue", &Example::SetValue) + InstanceMethod<&Example::GetValue>("GetValue"), + InstanceMethod<&Example::SetValue>("SetValue") }); // Create a peristent reference to the class constructor. This will allow @@ -289,6 +289,93 @@ One or more of `napi_property_attributes`. Returns `Napi::PropertyDescriptor` object that represents a static method of a JavaScript class. +### StaticMethod + +Creates property descriptor that represents a static method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents a static method of a +JavaScript class. This function returns nothing. +- `[in] utf8name`: Null-terminated string that represents the name of a static +method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents the static method of a +JavaScript class. + +### StaticMethod + +Creates property descriptor that represents a static method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents a static method of a +JavaScript class. +- `[in] utf8name`: Null-terminated string that represents the name of a static +method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents a static method of a +JavaScript class. + +### StaticMethod + +Creates property descriptor that represents a static method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents a static method of a +JavaScript class. +- `[in] name`: Napi:Symbol that represents the name of a static +method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents the static method of a +JavaScript class. + +### StaticMethod + +Creates property descriptor that represents a static method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents a static method of a +JavaScript class. +- `[in] name`: Napi:Symbol that represents the name of a static. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents a static method of a +JavaScript class. + ### StaticAccessor Creates property descriptor that represents a static accessor property of a @@ -342,6 +429,57 @@ is invoked. Returns `Napi::PropertyDescriptor` object that represents a static accessor property of a JavaScript class. +### StaticAccessor + +Creates property descriptor that represents a static accessor property of a +JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticAccessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] getter`: The native function to call when a get access to the property of +a JavaScript class is performed. +- `[in] setter`: The native function to call when a set access to the property of +a JavaScript class is performed. +- `[in] utf8name`: Null-terminated string that represents the name of a static +accessor property for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into getter or setter when +is invoked. + +Returns `Napi::PropertyDescriptor` object that represents a static accessor +property of a JavaScript class. + +### StaticAccessor + +Creates property descriptor that represents a static accessor property of a +JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::StaticAccessor(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] getter`: The native function to call when a get access to the property of +a JavaScript class is performed. +- `[in] setter`: The native function to call when a set access to the property of +a JavaScript class is performed. +- `[in] name`: Napi:Symbol that represents the name of a static accessor. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into getter or setter when +is invoked. + +Returns `Napi::PropertyDescriptor` object that represents a static accessor +property of a JavaScript class. + ### InstanceMethod Creates property descriptor that represents an instance method of a JavaScript class. @@ -430,6 +568,94 @@ One or more of `napi_property_attributes`. Returns `Napi::PropertyDescriptor` object that represents an instance method of a JavaScript class. +### InstanceMethod + +Creates property descriptor that represents an instance method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents an instance method of a +JavaScript class. +- `[in] utf8name`: Null-terminated string that represents the name of an instance +method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance method of a +JavaScript class. + +### InstanceMethod + +Creates property descriptor that represents an instance method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents an instance method of a +JavaScript class. +- `[in] utf8name`: Null-terminated string that represents the name of an instance +method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance method of a +JavaScript class. + +### InstanceMethod + +Creates property descriptor that represents an instance method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceMethod(Napi::Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents an instance method of a +JavaScript class. +- `[in] name`: The `Napi::Symbol` object whose value is used to identify the +instance method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance method of a +JavaScript class. + +### InstanceMethod + +Creates property descriptor that represents an instance method of a JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceMethod(Napi::Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] method`: The native function that represents an instance method of a +JavaScript class. +- `[in] name`: The `Napi::Symbol` object whose value is used to identify the +instance method for the class. +- `[in] attributes`: The attributes associated with a particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into method when it is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance method of a +JavaScript class. + ### InstanceAccessor Creates property descriptor that represents an instance accessor property of a @@ -482,6 +708,56 @@ One or more of `napi_property_attributes`. Returns `Napi::PropertyDescriptor` object that represents an instance accessor property of a JavaScript class. +### InstanceAccessor + +Creates property descriptor that represents an instance accessor property of a +JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceAccessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] getter`: The native function to call when a get access to the property of +a JavaScript class is performed. +- `[in] setter`: The native function to call when a set access to the property of +a JavaScript class is performed. +- `[in] utf8name`: Null-terminated string that represents the name of an instance +accessor property for the class. +- `[in] attributes`: The attributes associated with the particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into getter or setter when this is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance accessor +property of a JavaScript class. + +### InstanceAccessor + +Creates property descriptor that represents an instance accessor property of a +JavaScript class. + +```cpp +template +static Napi::PropertyDescriptor Napi::ObjectWrap::InstanceAccessor(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); +``` + +- `[in] getter`: The native function to call when a get access to the property of +a JavaScript class is performed. +- `[in] setter`: The native function to call when a set access to the property of +a JavaScript class is performed. +- `[in] name`: The `Napi::Symbol` object whose value is used to identify the +instance accessor. +- `[in] attributes`: The attributes associated with the particular property. +One or more of `napi_property_attributes`. +- `[in] data`: User-provided data passed into getter or setter when this is invoked. + +Returns `Napi::PropertyDescriptor` object that represents an instance accessor +property of a JavaScript class. + ### StaticValue Creates property descriptor that represents an static value property of a diff --git a/napi-inl.h b/napi-inl.h index 98c578ed1..e6dfa3f7a 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3166,6 +3166,62 @@ inline ClassPropertyDescriptor ObjectWrap::StaticMethod( return desc; } +template +template ::StaticVoidMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.utf8name = utf8name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + +template +template ::StaticVoidMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.name = name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + +template +template ::StaticMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.utf8name = utf8name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + +template +template ::StaticMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::StaticMethod( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.name = name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + template inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( const char* utf8name, @@ -3204,6 +3260,38 @@ inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( return desc; } +template +template ::StaticGetterCallback getter, + typename ObjectWrap::StaticSetterCallback setter> +inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = napi_property_descriptor(); + desc.utf8name = utf8name; + desc.getter = This::WrapStaticGetter(This::StaticGetterTag()); + desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + +template +template ::StaticGetterCallback getter, + typename ObjectWrap::StaticSetterCallback setter> +inline ClassPropertyDescriptor ObjectWrap::StaticAccessor( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = napi_property_descriptor(); + desc.name = name; + desc.getter = This::WrapStaticGetter(This::StaticGetterTag()); + desc.setter = This::WrapStaticSetter(This::StaticSetterTag()); + desc.data = data; + desc.attributes = static_cast(attributes | napi_static); + return desc; +} + template inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( const char* utf8name, @@ -3270,6 +3358,62 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( return desc; } +template +template ::InstanceVoidMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.utf8name = utf8name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = attributes; + return desc; +} + +template +template ::InstanceMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.utf8name = utf8name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = attributes; + return desc; +} + +template +template ::InstanceVoidMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.name = name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = attributes; + return desc; +} + +template +template ::InstanceMethodCallback method> +inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.name = name; + desc.method = &ObjectWrap::WrappedMethod; + desc.data = data; + desc.attributes = attributes; + return desc; +} + template inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( const char* utf8name, @@ -3308,6 +3452,38 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( return desc; } +template +template ::InstanceGetterCallback getter, + typename ObjectWrap::InstanceSetterCallback setter> +inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( + const char* utf8name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.utf8name = utf8name; + desc.getter = This::WrapGetter(This::GetterTag()); + desc.setter = This::WrapSetter(This::SetterTag()); + desc.data = data; + desc.attributes = attributes; + return desc; +} + +template +template ::InstanceGetterCallback getter, + typename ObjectWrap::InstanceSetterCallback setter> +inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( + Symbol name, + napi_property_attributes attributes, + void* data) { + napi_property_descriptor desc = {}; + desc.name = name; + desc.getter = This::WrapGetter(This::GetterTag()); + desc.setter = This::WrapSetter(This::SetterTag()); + desc.data = data; + desc.attributes = attributes; + return desc; +} + template inline ClassPropertyDescriptor ObjectWrap::StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes) { @@ -3502,6 +3678,65 @@ inline void ObjectWrap::FinalizeCallback(napi_env env, void* data, void* /*hi delete instance; } +template +template ::StaticVoidMethodCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + method(CallbackInfo(env, info)); + return nullptr; + }); +} + +template +template ::StaticMethodCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + return method(CallbackInfo(env, info)); + }); +} + +template +template ::InstanceVoidMethodCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + const CallbackInfo cbInfo(env, info); + T* instance = Unwrap(cbInfo.This().As()); + (instance->*method)(cbInfo); + return nullptr; + }); +} + +template +template ::InstanceMethodCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + const CallbackInfo cbInfo(env, info); + T* instance = Unwrap(cbInfo.This().As()); + return (instance->*method)(cbInfo); + }); +} + +template +template ::StaticSetterCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + const CallbackInfo cbInfo(env, info); + method(cbInfo, cbInfo[0]); + return nullptr; + }); +} + +template +template ::InstanceSetterCallback method> +inline napi_value ObjectWrap::WrappedMethod(napi_env env, napi_callback_info info) noexcept { + return details::WrapCallback([&] { + const CallbackInfo cbInfo(env, info); + T* instance = Unwrap(cbInfo.This().As()); + (instance->*method)(cbInfo, cbInfo[0]); + return nullptr; + }); +} + //////////////////////////////////////////////////////////////////////////////// // HandleScope class //////////////////////////////////////////////////////////////////////////////// diff --git a/napi.h b/napi.h index 2fc9b10f3..881176e04 100644 --- a/napi.h +++ b/napi.h @@ -1593,8 +1593,8 @@ namespace Napi { /// public: /// static void Initialize(Napi::Env& env, Napi::Object& target) { /// Napi::Function constructor = DefineClass(env, "Example", { - /// InstanceAccessor("value", &Example::GetSomething, &Example::SetSomething), - /// InstanceMethod("doSomething", &Example::DoSomething), + /// InstanceAccessor<&Example::GetSomething, &Example::SetSomething>("value"), + /// InstanceMethod<&Example::DoSomething>("doSomething"), /// }); /// target.Set("Example", constructor); /// } @@ -1648,6 +1648,22 @@ namespace Napi { StaticMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); + template + static PropertyDescriptor StaticMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor StaticMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor StaticMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor StaticMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); static PropertyDescriptor StaticAccessor(const char* utf8name, StaticGetterCallback getter, StaticSetterCallback setter, @@ -1658,6 +1674,14 @@ namespace Napi { StaticSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); + template + static PropertyDescriptor StaticAccessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor StaticAccessor(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); static PropertyDescriptor InstanceMethod(const char* utf8name, InstanceVoidMethodCallback method, napi_property_attributes attributes = napi_default, @@ -1674,6 +1698,22 @@ namespace Napi { InstanceMethodCallback method, napi_property_attributes attributes = napi_default, void* data = nullptr); + template + static PropertyDescriptor InstanceMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor InstanceMethod(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor InstanceMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor InstanceMethod(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); static PropertyDescriptor InstanceAccessor(const char* utf8name, InstanceGetterCallback getter, InstanceSetterCallback setter, @@ -1684,6 +1724,14 @@ namespace Napi { InstanceSetterCallback setter, napi_property_attributes attributes = napi_default, void* data = nullptr); + template + static PropertyDescriptor InstanceAccessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template + static PropertyDescriptor InstanceAccessor(Symbol name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); static PropertyDescriptor StaticValue(const char* utf8name, Napi::Value value, napi_property_attributes attributes = napi_default); @@ -1699,6 +1747,8 @@ namespace Napi { virtual void Finalize(Napi::Env env); private: + using This = ObjectWrap; + static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); static napi_value StaticMethodCallbackWrapper(napi_env env, napi_callback_info info); @@ -1735,6 +1785,45 @@ namespace Napi { StaticAccessorCallbackData; typedef AccessorCallbackData InstanceAccessorCallbackData; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template + static napi_value WrappedMethod(napi_env env, napi_callback_info info) noexcept; + + template struct StaticGetterTag {}; + template struct StaticSetterTag {}; + template struct GetterTag {}; + template struct SetterTag {}; + + template + static napi_callback WrapStaticGetter(StaticGetterTag) noexcept { return &This::WrappedMethod; } + static napi_callback WrapStaticGetter(StaticGetterTag) noexcept { return nullptr; } + + template + static napi_callback WrapStaticSetter(StaticSetterTag) noexcept { return &This::WrappedMethod; } + static napi_callback WrapStaticSetter(StaticSetterTag) noexcept { return nullptr; } + + template + static napi_callback WrapGetter(GetterTag) noexcept { return &This::WrappedMethod; } + static napi_callback WrapGetter(GetterTag) noexcept { return nullptr; } + + template + static napi_callback WrapSetter(SetterTag) noexcept { return &This::WrappedMethod; } + static napi_callback WrapSetter(SetterTag) noexcept { return nullptr; } }; class HandleScope { diff --git a/test/objectwrap.cc b/test/objectwrap.cc index 4a89530e5..0d61171ca 100644 --- a/test/objectwrap.cc +++ b/test/objectwrap.cc @@ -60,25 +60,53 @@ class Test : public Napi::ObjectWrap { return array.Get(Napi::Symbol::WellKnown(info.Env(), "iterator")).As().Call(array, {}); } + void TestVoidMethodT(const Napi::CallbackInfo &info) { + value_ = info[0].ToString(); + } + + Napi::Value TestMethodT(const Napi::CallbackInfo &info) { + return Napi::String::New(info.Env(), value_); + } + + static Napi::Value TestStaticMethodT(const Napi::CallbackInfo& info) { + return Napi::String::New(info.Env(), s_staticMethodText); + } + + static void TestStaticVoidMethodT(const Napi::CallbackInfo& info) { + s_staticMethodText = info[0].ToString(); + } + static void Initialize(Napi::Env env, Napi::Object exports) { Napi::Symbol kTestStaticValueInternal = Napi::Symbol::New(env, "kTestStaticValueInternal"); Napi::Symbol kTestStaticAccessorInternal = Napi::Symbol::New(env, "kTestStaticAccessorInternal"); + Napi::Symbol kTestStaticAccessorTInternal = Napi::Symbol::New(env, "kTestStaticAccessorTInternal"); Napi::Symbol kTestStaticMethodInternal = Napi::Symbol::New(env, "kTestStaticMethodInternal"); + Napi::Symbol kTestStaticMethodTInternal = Napi::Symbol::New(env, "kTestStaticMethodTInternal"); + Napi::Symbol kTestStaticVoidMethodTInternal = Napi::Symbol::New(env, "kTestStaticVoidMethodTInternal"); Napi::Symbol kTestValueInternal = Napi::Symbol::New(env, "kTestValueInternal"); Napi::Symbol kTestAccessorInternal = Napi::Symbol::New(env, "kTestAccessorInternal"); + Napi::Symbol kTestAccessorTInternal = Napi::Symbol::New(env, "kTestAccessorTInternal"); Napi::Symbol kTestMethodInternal = Napi::Symbol::New(env, "kTestMethodInternal"); + Napi::Symbol kTestMethodTInternal = Napi::Symbol::New(env, "kTestMethodTInternal"); + Napi::Symbol kTestVoidMethodTInternal = Napi::Symbol::New(env, "kTestVoidMethodTInternal"); exports.Set("Test", DefineClass(env, "Test", { // expose symbols for testing StaticValue("kTestStaticValueInternal", kTestStaticValueInternal), StaticValue("kTestStaticAccessorInternal", kTestStaticAccessorInternal), + StaticValue("kTestStaticAccessorTInternal", kTestStaticAccessorTInternal), StaticValue("kTestStaticMethodInternal", kTestStaticMethodInternal), + StaticValue("kTestStaticMethodTInternal", kTestStaticMethodTInternal), + StaticValue("kTestStaticVoidMethodTInternal", kTestStaticVoidMethodTInternal), StaticValue("kTestValueInternal", kTestValueInternal), StaticValue("kTestAccessorInternal", kTestAccessorInternal), + StaticValue("kTestAccessorTInternal", kTestAccessorTInternal), StaticValue("kTestMethodInternal", kTestMethodInternal), + StaticValue("kTestMethodTInternal", kTestMethodTInternal), + StaticValue("kTestVoidMethodTInternal", kTestVoidMethodTInternal), // test data StaticValue("testStaticValue", Napi::String::New(env, "value"), napi_enumerable), @@ -88,9 +116,16 @@ class Test : public Napi::ObjectWrap { StaticAccessor("testStaticSetter", nullptr, &StaticSetter, napi_default), StaticAccessor("testStaticGetSet", &StaticGetter, &StaticSetter, napi_enumerable), StaticAccessor(kTestStaticAccessorInternal, &StaticGetter, &StaticSetter, napi_enumerable), + StaticAccessor<&StaticGetter>("testStaticGetterT"), + StaticAccessor<&StaticGetter, &StaticSetter>("testStaticGetSetT"), + StaticAccessor<&StaticGetter, &StaticSetter>(kTestStaticAccessorTInternal), StaticMethod("testStaticMethod", &TestStaticMethod, napi_enumerable), StaticMethod(kTestStaticMethodInternal, &TestStaticMethodInternal, napi_default), + StaticMethod<&TestStaticVoidMethodT>("testStaticVoidMethodT"), + StaticMethod<&TestStaticMethodT>("testStaticMethodT"), + StaticMethod<&TestStaticVoidMethodT>(kTestStaticVoidMethodTInternal), + StaticMethod<&TestStaticMethodT>(kTestStaticMethodTInternal), InstanceValue("testValue", Napi::Boolean::New(env, true), napi_enumerable), InstanceValue(kTestValueInternal, Napi::Boolean::New(env, false), napi_enumerable), @@ -99,9 +134,16 @@ class Test : public Napi::ObjectWrap { InstanceAccessor("testSetter", nullptr, &Test::Setter, napi_default), InstanceAccessor("testGetSet", &Test::Getter, &Test::Setter, napi_enumerable), InstanceAccessor(kTestAccessorInternal, &Test::Getter, &Test::Setter, napi_enumerable), + InstanceAccessor<&Test::Getter>("testGetterT"), + InstanceAccessor<&Test::Getter, &Test::Setter>("testGetSetT"), + InstanceAccessor<&Test::Getter, &Test::Setter>(kTestAccessorInternal), InstanceMethod("testMethod", &Test::TestMethod, napi_enumerable), InstanceMethod(kTestMethodInternal, &Test::TestMethodInternal, napi_default), + InstanceMethod<&Test::TestMethodT>("testMethodT"), + InstanceMethod<&Test::TestVoidMethodT>("testVoidMethodT"), + InstanceMethod<&Test::TestMethodT>(kTestMethodTInternal), + InstanceMethod<&Test::TestVoidMethodT>(kTestVoidMethodTInternal), // conventions InstanceAccessor(Napi::Symbol::WellKnown(env, "toStringTag"), &Test::ToStringTag, nullptr, napi_enumerable), @@ -124,8 +166,12 @@ class Test : public Napi::ObjectWrap { private: std::string value_; Napi::FunctionReference finalizeCb_; + + static std::string s_staticMethodText; }; +std::string Test::s_staticMethodText; + Napi::Object InitObjectWrap(Napi::Env env) { testStaticContextRef = Napi::Persistent(Napi::Object::New(env)); testStaticContextRef.SuppressDestruct(); diff --git a/test/objectwrap.js b/test/objectwrap.js index 533c05f75..de16a6067 100644 --- a/test/objectwrap.js +++ b/test/objectwrap.js @@ -15,9 +15,11 @@ const test = (binding) => { { obj.testSetter = 'instance getter'; assert.strictEqual(obj.testGetter, 'instance getter'); + assert.strictEqual(obj.testGetterT, 'instance getter'); obj.testSetter = 'instance getter 2'; assert.strictEqual(obj.testGetter, 'instance getter 2'); + assert.strictEqual(obj.testGetterT, 'instance getter 2'); } // read write-only @@ -36,6 +38,9 @@ const test = (binding) => { let error; try { obj.testGetter = 'write'; } catch (e) { error = e; } assert.strictEqual(error.name, 'TypeError'); + + try { obj.testGetterT = 'write'; } catch (e) { error = e; } + assert.strictEqual(error.name, 'TypeError'); } // rw @@ -45,6 +50,12 @@ const test = (binding) => { obj.testGetSet = 'instance getset 2'; assert.strictEqual(obj.testGetSet, 'instance getset 2'); + + obj.testGetSetT = 'instance getset 3'; + assert.strictEqual(obj.testGetSetT, 'instance getset 3'); + + obj.testGetSetT = 'instance getset 4'; + assert.strictEqual(obj.testGetSetT, 'instance getset 4'); } // rw symbol @@ -54,12 +65,22 @@ const test = (binding) => { obj[clazz.kTestAccessorInternal] = 'instance internal getset 2'; assert.strictEqual(obj[clazz.kTestAccessorInternal], 'instance internal getset 2'); + + obj[clazz.kTestAccessorTInternal] = 'instance internal getset 3'; + assert.strictEqual(obj[clazz.kTestAccessorTInternal], 'instance internal getset 3'); + + obj[clazz.kTestAccessorTInternal] = 'instance internal getset 4'; + assert.strictEqual(obj[clazz.kTestAccessorTInternal], 'instance internal getset 4'); } }; const testMethod = (obj, clazz) => { assert.strictEqual(obj.testMethod('method'), 'method instance'); assert.strictEqual(obj[clazz.kTestMethodInternal]('method'), 'method instance internal'); + obj.testVoidMethodT('method<>(const char*)'); + assert.strictEqual(obj.testMethodT(), 'method<>(const char*)'); + obj[clazz.kTestVoidMethodTInternal]('method<>(Symbol)'); + assert.strictEqual(obj[clazz.kTestMethodTInternal](), 'method<>(Symbol)'); }; const testEnumerables = (obj, clazz) => { @@ -112,10 +133,12 @@ const test = (binding) => { const tempObj = {}; clazz.testStaticSetter = tempObj; assert.strictEqual(clazz.testStaticGetter, tempObj); + assert.strictEqual(clazz.testStaticGetterT, tempObj); const tempArray = []; clazz.testStaticSetter = tempArray; assert.strictEqual(clazz.testStaticGetter, tempArray); + assert.strictEqual(clazz.testStaticGetterT, tempArray); } // read write-only @@ -134,6 +157,8 @@ const test = (binding) => { let error; try { clazz.testStaticGetter = 'write'; } catch (e) { error = e; } assert.strictEqual(error.name, 'TypeError'); + try { clazz.testStaticGetterT = 'write'; } catch (e) { error = e; } + assert.strictEqual(error.name, 'TypeError'); } // rw @@ -143,18 +168,30 @@ const test = (binding) => { clazz.testStaticGetSet = 4; assert.strictEqual(clazz.testStaticGetSet, 4); + + clazz.testStaticGetSetT = -9; + assert.strictEqual(clazz.testStaticGetSetT, -9); + + clazz.testStaticGetSetT = -4; + assert.strictEqual(clazz.testStaticGetSetT, -4); } // rw symbol { clazz[clazz.kTestStaticAccessorInternal] = 'static internal getset'; assert.strictEqual(clazz[clazz.kTestStaticAccessorInternal], 'static internal getset'); + clazz[clazz.kTestStaticAccessorTInternal] = 'static internal getset <>'; + assert.strictEqual(clazz[clazz.kTestStaticAccessorTInternal], 'static internal getset <>'); } }; const testStaticMethod = (clazz) => { assert.strictEqual(clazz.testStaticMethod('method'), 'method static'); assert.strictEqual(clazz[clazz.kTestStaticMethodInternal]('method'), 'method static internal'); + clazz.testStaticVoidMethodT('static method<>(const char*)'); + assert.strictEqual(clazz.testStaticMethodT(), 'static method<>(const char*)'); + clazz[clazz.kTestStaticVoidMethodTInternal]('static method<>(Symbol)'); + assert.strictEqual(clazz[clazz.kTestStaticMethodTInternal](), 'static method<>(Symbol)'); }; const testStaticEnumerables = (clazz) => { diff --git a/tools/README.md b/tools/README.md index b71e5d92c..8d110609c 100644 --- a/tools/README.md +++ b/tools/README.md @@ -49,10 +49,10 @@ Napi::FunctionReference constructor; void [ClassName]::Init(Napi::Env env, Napi::Object exports, Napi::Object module) { Napi::HandleScope scope(env); Napi::Function ctor = DefineClass(env, "Canvas", { - InstanceMethod("Func1", &[ClassName]::Func1), - InstanceMethod("Func2", &[ClassName]::Func2), - InstanceAccessor("Value", &[ClassName]::ValueGetter), - StaticMethod("MethodName", &[ClassName]::StaticMethod), + InstanceMethod<&[ClassName]::Func1>("Func1"), + InstanceMethod<&[ClassName]::Func2>("Func2"), + InstanceAccessor<&[ClassName]::ValueGetter>("Value"), + StaticMethod<&[ClassName]::StaticMethod>("MethodName"), InstanceValue("Value", Napi::[Type]::New(env, value)), }); From 3ff5c03b321f5d80829ec035c964a5e1a080aa5e Mon Sep 17 00:00:00 2001 From: Dmitry Ashkadov Date: Sat, 23 Nov 2019 00:15:23 +0300 Subject: [PATCH 2/2] workaround for missing initializer warning (old gcc) --- napi-inl.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/napi-inl.h b/napi-inl.h index e6dfa3f7a..921c1c858 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -3172,7 +3172,7 @@ inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3186,7 +3186,7 @@ inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3200,7 +3200,7 @@ inline ClassPropertyDescriptor ObjectWrap::StaticMethod( const char* utf8name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3214,7 +3214,7 @@ inline ClassPropertyDescriptor ObjectWrap::StaticMethod( Symbol name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3364,7 +3364,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3378,7 +3378,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( const char* utf8name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3392,7 +3392,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3406,7 +3406,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceMethod( Symbol name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.method = &ObjectWrap::WrappedMethod; desc.data = data; @@ -3459,7 +3459,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( const char* utf8name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.utf8name = utf8name; desc.getter = This::WrapGetter(This::GetterTag()); desc.setter = This::WrapSetter(This::SetterTag()); @@ -3475,7 +3475,7 @@ inline ClassPropertyDescriptor ObjectWrap::InstanceAccessor( Symbol name, napi_property_attributes attributes, void* data) { - napi_property_descriptor desc = {}; + napi_property_descriptor desc = napi_property_descriptor(); desc.name = name; desc.getter = This::WrapGetter(This::GetterTag()); desc.setter = This::WrapSetter(This::SetterTag());