From 8ec7f3460ffdabcb21695fe52079ac42699f6121 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Wed, 27 Nov 2019 11:53:46 -0800 Subject: [PATCH] object: add templated property descriptors --- napi-inl.h | 107 ++++++++++++++++++++++++++++++++++++++++++ napi.h | 37 +++++++++++++++ test/object/object.cc | 47 +++++++++++++++++++ test/object/object.js | 26 ++++++++++ 4 files changed, 217 insertions(+) diff --git a/napi-inl.h b/napi-inl.h index f72e1daea..9bcc529e5 100644 --- a/napi-inl.h +++ b/napi-inl.h @@ -2732,6 +2732,113 @@ inline void CallbackInfo::SetData(void* data) { // PropertyDescriptor class //////////////////////////////////////////////////////////////////////////////// +template +PropertyDescriptor +PropertyDescriptor::Accessor(const char* utf8name, + napi_property_attributes attributes, + void* data) { + return PropertyDescriptor({ + utf8name, + nullptr, + nullptr, + GetterCallbackWrapper, + nullptr, + nullptr, + attributes, + data + }); +} + +template +PropertyDescriptor +PropertyDescriptor::Accessor(const std::string& utf8name, + napi_property_attributes attributes, + void* data) { + return Accessor(utf8name.c_str(), attributes, data); +} + +template +PropertyDescriptor +PropertyDescriptor::Accessor(Name name, + napi_property_attributes attributes, + void* data) { + return PropertyDescriptor({ + nullptr, + name, + nullptr, + GetterCallbackWrapper, + nullptr, + nullptr, + attributes, + data + }); +} + +template < +PropertyDescriptor::GetterCallback Getter, +PropertyDescriptor::SetterCallback Setter> +PropertyDescriptor +PropertyDescriptor::Accessor(const char* utf8name, + napi_property_attributes attributes, + void* data) { + return PropertyDescriptor({ + utf8name, + nullptr, + nullptr, + GetterCallbackWrapper, + SetterCallbackWrapper, + nullptr, + attributes, + data + }); +} + +template < +PropertyDescriptor::GetterCallback Getter, +PropertyDescriptor::SetterCallback Setter> +PropertyDescriptor +PropertyDescriptor::Accessor(const std::string& utf8name, + napi_property_attributes attributes, + void* data) { + return Accessor(utf8name.c_str(), attributes, data); +} + +template < +PropertyDescriptor::GetterCallback Getter, +PropertyDescriptor::SetterCallback Setter> +PropertyDescriptor +PropertyDescriptor::Accessor(Name name, + napi_property_attributes attributes, + void* data) { + return PropertyDescriptor({ + nullptr, + name, + nullptr, + GetterCallbackWrapper, + SetterCallbackWrapper, + nullptr, + attributes, + data + }); +} + +template +napi_value +PropertyDescriptor::GetterCallbackWrapper(napi_env env, + napi_callback_info info) { + CallbackInfo cbInfo(env, info); + return Getter(cbInfo); +} + +template +napi_value +PropertyDescriptor::SetterCallbackWrapper(napi_env env, + napi_callback_info info) { + CallbackInfo cbInfo(env, info); + Setter(cbInfo, cbInfo[0]); + return nullptr; +} + template inline PropertyDescriptor PropertyDescriptor::Accessor(Napi::Env env, diff --git a/napi.h b/napi.h index 2fc9b10f3..4d3137538 100644 --- a/napi.h +++ b/napi.h @@ -1407,6 +1407,9 @@ namespace Napi { class PropertyDescriptor { public: + typedef Napi::Value (*GetterCallback)(const CallbackInfo& info); + typedef void (*SetterCallback)(const CallbackInfo& info, const Napi::Value& value); + #ifndef NODE_ADDON_API_DISABLE_DEPRECATED template static PropertyDescriptor Accessor(const char* utf8name, @@ -1474,6 +1477,36 @@ namespace Napi { void* data = nullptr); #endif // !NODE_ADDON_API_DISABLE_DEPRECATED + template + static PropertyDescriptor Accessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + + template + static PropertyDescriptor Accessor(const std::string& utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + + template + static PropertyDescriptor Accessor(Name name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + + template + static PropertyDescriptor Accessor(const char* utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + + template + static PropertyDescriptor Accessor(const std::string& utf8name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + + template + static PropertyDescriptor Accessor(Name name, + napi_property_attributes attributes = napi_default, + void* data = nullptr); + template static PropertyDescriptor Accessor(Napi::Env env, Napi::Object object, @@ -1559,6 +1592,10 @@ namespace Napi { operator const napi_property_descriptor&() const; private: + template + static napi_value GetterCallbackWrapper(napi_env env, napi_callback_info info); + template + static napi_value SetterCallbackWrapper(napi_env env, napi_callback_info info); napi_property_descriptor _desc; }; diff --git a/test/object/object.cc b/test/object/object.cc index 32f2c237a..1c9ce59f9 100644 --- a/test/object/object.cc +++ b/test/object/object.cc @@ -85,6 +85,20 @@ void DefineProperties(const CallbackInfo& info) { PropertyDescriptor::Accessor(env, obj, "readwriteAccessor", TestGetter, TestSetter), PropertyDescriptor::Accessor(env, obj, "readonlyAccessorWithUserData", TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), PropertyDescriptor::Accessor(env, obj, "readwriteAccessorWithUserData", TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), + + PropertyDescriptor::Accessor("readonlyAccessorT"), + PropertyDescriptor::Accessor( + "readwriteAccessorT"), + PropertyDescriptor::Accessor( + "readonlyAccessorWithUserDataT", + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Accessor< + TestGetterWithUserData, + TestSetterWithUserData>("readwriteAccessorWithUserDataT", + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Value("readonlyValue", trueValue), PropertyDescriptor::Value("readwriteValue", trueValue, napi_writable), PropertyDescriptor::Value("enumerableValue", trueValue, napi_enumerable), @@ -100,6 +114,12 @@ void DefineProperties(const CallbackInfo& info) { std::string str2("readwriteAccessor"); std::string str1a("readonlyAccessorWithUserData"); std::string str2a("readwriteAccessorWithUserData"); + + std::string str1t("readonlyAccessorT"); + std::string str2t("readwriteAccessorT"); + std::string str1at("readonlyAccessorWithUserDataT"); + std::string str2at("readwriteAccessorWithUserDataT"); + std::string str3("readonlyValue"); std::string str4("readwriteValue"); std::string str5("enumerableValue"); @@ -111,6 +131,18 @@ void DefineProperties(const CallbackInfo& info) { PropertyDescriptor::Accessor(env, obj, str2, TestGetter, TestSetter), PropertyDescriptor::Accessor(env, obj, str1a, TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), PropertyDescriptor::Accessor(env, obj, str2a, TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), + + PropertyDescriptor::Accessor(str1t), + PropertyDescriptor::Accessor(str2t), + PropertyDescriptor::Accessor(str1at, + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Accessor< + TestGetterWithUserData, + TestSetterWithUserData>(str2at, + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Value(str3, trueValue), PropertyDescriptor::Value(str4, trueValue, napi_writable), PropertyDescriptor::Value(str5, trueValue, napi_enumerable), @@ -127,6 +159,21 @@ void DefineProperties(const CallbackInfo& info) { Napi::String::New(env, "readonlyAccessorWithUserData"), TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), PropertyDescriptor::Accessor(env, obj, Napi::String::New(env, "readwriteAccessorWithUserData"), TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast(holder)), + + PropertyDescriptor::Accessor( + Napi::String::New(env, "readonlyAccessorT")), + PropertyDescriptor::Accessor( + Napi::String::New(env, "readwriteAccessorT")), + PropertyDescriptor::Accessor( + Napi::String::New(env, "readonlyAccessorWithUserDataT"), + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Accessor< + TestGetterWithUserData, TestSetterWithUserData>( + Napi::String::New(env, "readwriteAccessorWithUserDataT"), + napi_property_attributes::napi_default, + reinterpret_cast(holder)), + PropertyDescriptor::Value( Napi::String::New(env, "readonlyValue"), trueValue), PropertyDescriptor::Value( diff --git a/test/object/object.js b/test/object/object.js index b4fe57dff..d75f1859f 100644 --- a/test/object/object.js +++ b/test/object/object.js @@ -22,6 +22,7 @@ function test(binding) { const obj = {}; binding.object.defineProperties(obj, nameType); + // accessors assertPropertyIsNot(obj, 'readonlyAccessor', 'enumerable'); assertPropertyIsNot(obj, 'readonlyAccessor', 'configurable'); assert.strictEqual(obj.readonlyAccessor, true); @@ -44,6 +45,30 @@ function test(binding) { obj.readwriteAccessorWithUserData = -14; assert.strictEqual(obj.readwriteAccessorWithUserData, -14); + // templated accessors + assertPropertyIsNot(obj, 'readonlyAccessorT', 'enumerable'); + assertPropertyIsNot(obj, 'readonlyAccessorT', 'configurable'); + assert.strictEqual(obj.readonlyAccessor, true); + + assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'enumerable'); + assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'configurable'); + assert.strictEqual(obj.readonlyAccessorWithUserData, 1234, nameType); + + assertPropertyIsNot(obj, 'readwriteAccessorT', 'enumerable'); + assertPropertyIsNot(obj, 'readwriteAccessorT', 'configurable'); + obj.readwriteAccessor = false; + assert.strictEqual(obj.readwriteAccessor, false); + obj.readwriteAccessor = true; + assert.strictEqual(obj.readwriteAccessor, true); + + assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'enumerable'); + assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'configurable'); + obj.readwriteAccessorWithUserData = 2; + assert.strictEqual(obj.readwriteAccessorWithUserData, 2); + obj.readwriteAccessorWithUserData = -14; + assert.strictEqual(obj.readwriteAccessorWithUserData, -14); + + // values assertPropertyIsNot(obj, 'readonlyValue', 'writable'); assertPropertyIsNot(obj, 'readonlyValue', 'enumerable'); assertPropertyIsNot(obj, 'readonlyValue', 'configurable'); @@ -65,6 +90,7 @@ function test(binding) { assertPropertyIsNot(obj, 'configurableValue', 'enumerable'); assertPropertyIs(obj, 'configurableValue', 'configurable'); + // functions assertPropertyIsNot(obj, 'function', 'writable'); assertPropertyIsNot(obj, 'function', 'enumerable'); assertPropertyIsNot(obj, 'function', 'configurable');