Skip to content

Commit 5c7b75f

Browse files
committed
object: add templated property descriptors
Add static methods to `PropertyDescriptor` that allows the definition of accessors where the getter/setter is specified as a template parameter rather than a function parameter. This allows us to avoid heap-allocating callback data. PR-URL: nodejs/node-addon-api#610 Reviewed-By: NickNaso <nicoladelgobbo@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent f400b57 commit 5c7b75f

File tree

5 files changed

+276
-9
lines changed

5 files changed

+276
-9
lines changed

doc/property_descriptor.md

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ Void Init(Env env) {
2626
Object obj = Object::New(env);
2727

2828
// Accessor
29-
PropertyDescriptor pd1 = PropertyDescriptor::Accessor(env,
30-
obj,
31-
"pd1",
32-
TestGetter);
33-
PropertyDescriptor pd2 = PropertyDescriptor::Accessor(env,
34-
obj,
35-
"pd2",
36-
TestGetter,
37-
TestSetter);
29+
PropertyDescriptor pd1 = PropertyDescriptor::Accessor<TestGetter>("pd1");
30+
PropertyDescriptor pd2 =
31+
PropertyDescriptor::Accessor<TestGetter, TestSetter>("pd2");
3832
// Function
3933
PropertyDescriptor pd3 = PropertyDescriptor::Function(env,
4034
"function",
@@ -51,6 +45,26 @@ Void Init(Env env) {
5145
}
5246
```
5347
48+
## Types
49+
50+
### PropertyDescriptor::GetterCallback
51+
52+
```cpp
53+
typedef Napi::Value (*GetterCallback)(const Napi::CallbackInfo& info);
54+
```
55+
56+
This is the signature of a getter function to be passed as a template parameter
57+
to `PropertyDescriptor::Accessor`.
58+
59+
### PropertyDescriptor::SetterCallback
60+
61+
```cpp
62+
typedef void (*SetterCallback)(const Napi::CallbackInfo& info);
63+
```
64+
65+
This is the signature of a setter function to be passed as a template parameter
66+
to `PropertyDescriptor::Accessor`.
67+
5468
## Methods
5569
5670
### Constructor
@@ -63,6 +77,47 @@ Napi::PropertyDescriptor::PropertyDescriptor (napi_property_descriptor desc);
6377

6478
### Accessor
6579

80+
```cpp
81+
template <Napi::PropertyDescriptor::GetterCallback Getter>
82+
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
83+
napi_property_attributes attributes = napi_default,
84+
void* data = nullptr);
85+
```
86+
87+
* `[template] Getter`: A getter function.
88+
* `[in] attributes`: Potential attributes for the getter function.
89+
* `[in] data`: A pointer to data of any type, default is a null pointer.
90+
91+
Returns a PropertyDescriptor that contains a read-only property.
92+
93+
The name of the property can be any of the following types:
94+
- `const char*`
95+
- `const std::string &`
96+
- `napi_value value`
97+
- `Napi::Name`
98+
99+
```cpp
100+
template <
101+
Napi::PropertyDescriptor::GetterCallback Getter,
102+
Napi::PropertyDescriptor::SetterCallback Setter>
103+
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
104+
napi_property_attributes attributes = napi_default,
105+
void* data = nullptr);
106+
```
107+
108+
* `[template] Getter`: A getter function.
109+
* `[template] Setter`: A setter function.
110+
* `[in] attributes`: Potential attributes for the getter function.
111+
* `[in] data`: A pointer to data of any type, default is a null pointer.
112+
113+
Returns a PropertyDescriptor that contains a read-write property.
114+
115+
The name of the property can be any of the following types:
116+
- `const char*`
117+
- `const std::string &`
118+
- `napi_value value`
119+
- `Napi::Name`
120+
66121
```cpp
67122
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
68123
Getter getter,

napi-inl.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2732,6 +2732,108 @@ inline void CallbackInfo::SetData(void* data) {
27322732
// PropertyDescriptor class
27332733
////////////////////////////////////////////////////////////////////////////////
27342734

2735+
template <typename PropertyDescriptor::GetterCallback Getter>
2736+
PropertyDescriptor
2737+
PropertyDescriptor::Accessor(const char* utf8name,
2738+
napi_property_attributes attributes,
2739+
void* data) {
2740+
napi_property_descriptor desc = napi_property_descriptor();
2741+
2742+
desc.utf8name = utf8name;
2743+
desc.getter = &GetterCallbackWrapper<Getter>;
2744+
desc.attributes = attributes;
2745+
desc.data = data;
2746+
2747+
return desc;
2748+
}
2749+
2750+
template <typename PropertyDescriptor::GetterCallback Getter>
2751+
PropertyDescriptor
2752+
PropertyDescriptor::Accessor(const std::string& utf8name,
2753+
napi_property_attributes attributes,
2754+
void* data) {
2755+
return Accessor<Getter>(utf8name.c_str(), attributes, data);
2756+
}
2757+
2758+
template <typename PropertyDescriptor::GetterCallback Getter>
2759+
PropertyDescriptor
2760+
PropertyDescriptor::Accessor(Name name,
2761+
napi_property_attributes attributes,
2762+
void* data) {
2763+
napi_property_descriptor desc = napi_property_descriptor();
2764+
2765+
desc.name = name;
2766+
desc.getter = &GetterCallbackWrapper<Getter>;
2767+
desc.attributes = attributes;
2768+
desc.data = data;
2769+
2770+
return desc;
2771+
}
2772+
2773+
template <
2774+
typename PropertyDescriptor::GetterCallback Getter,
2775+
typename PropertyDescriptor::SetterCallback Setter>
2776+
PropertyDescriptor
2777+
PropertyDescriptor::Accessor(const char* utf8name,
2778+
napi_property_attributes attributes,
2779+
void* data) {
2780+
2781+
napi_property_descriptor desc = napi_property_descriptor();
2782+
2783+
desc.utf8name = utf8name;
2784+
desc.getter = &GetterCallbackWrapper<Getter>;
2785+
desc.setter = &SetterCallbackWrapper<Setter>;
2786+
desc.attributes = attributes;
2787+
desc.data = data;
2788+
2789+
return desc;
2790+
}
2791+
2792+
template <
2793+
typename PropertyDescriptor::GetterCallback Getter,
2794+
typename PropertyDescriptor::SetterCallback Setter>
2795+
PropertyDescriptor
2796+
PropertyDescriptor::Accessor(const std::string& utf8name,
2797+
napi_property_attributes attributes,
2798+
void* data) {
2799+
return Accessor<Getter, Setter>(utf8name.c_str(), attributes, data);
2800+
}
2801+
2802+
template <
2803+
typename PropertyDescriptor::GetterCallback Getter,
2804+
typename PropertyDescriptor::SetterCallback Setter>
2805+
PropertyDescriptor
2806+
PropertyDescriptor::Accessor(Name name,
2807+
napi_property_attributes attributes,
2808+
void* data) {
2809+
napi_property_descriptor desc = napi_property_descriptor();
2810+
2811+
desc.name = name;
2812+
desc.getter = &GetterCallbackWrapper<Getter>;
2813+
desc.setter = &SetterCallbackWrapper<Setter>;
2814+
desc.attributes = attributes;
2815+
desc.data = data;
2816+
2817+
return desc;
2818+
}
2819+
2820+
template <typename PropertyDescriptor::GetterCallback Getter>
2821+
napi_value
2822+
PropertyDescriptor::GetterCallbackWrapper(napi_env env,
2823+
napi_callback_info info) {
2824+
CallbackInfo cbInfo(env, info);
2825+
return Getter(cbInfo);
2826+
}
2827+
2828+
template <typename PropertyDescriptor::SetterCallback Setter>
2829+
napi_value
2830+
PropertyDescriptor::SetterCallbackWrapper(napi_env env,
2831+
napi_callback_info info) {
2832+
CallbackInfo cbInfo(env, info);
2833+
Setter(cbInfo);
2834+
return nullptr;
2835+
}
2836+
27352837
template <typename Getter>
27362838
inline PropertyDescriptor
27372839
PropertyDescriptor::Accessor(Napi::Env env,

napi.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,9 @@ namespace Napi {
14071407

14081408
class PropertyDescriptor {
14091409
public:
1410+
typedef Napi::Value (*GetterCallback)(const Napi::CallbackInfo& info);
1411+
typedef void (*SetterCallback)(const Napi::CallbackInfo& info);
1412+
14101413
#ifndef NODE_ADDON_API_DISABLE_DEPRECATED
14111414
template <typename Getter>
14121415
static PropertyDescriptor Accessor(const char* utf8name,
@@ -1474,6 +1477,36 @@ namespace Napi {
14741477
void* data = nullptr);
14751478
#endif // !NODE_ADDON_API_DISABLE_DEPRECATED
14761479

1480+
template <GetterCallback Getter>
1481+
static PropertyDescriptor Accessor(const char* utf8name,
1482+
napi_property_attributes attributes = napi_default,
1483+
void* data = nullptr);
1484+
1485+
template <GetterCallback Getter>
1486+
static PropertyDescriptor Accessor(const std::string& utf8name,
1487+
napi_property_attributes attributes = napi_default,
1488+
void* data = nullptr);
1489+
1490+
template <GetterCallback Getter>
1491+
static PropertyDescriptor Accessor(Name name,
1492+
napi_property_attributes attributes = napi_default,
1493+
void* data = nullptr);
1494+
1495+
template <GetterCallback Getter, SetterCallback Setter>
1496+
static PropertyDescriptor Accessor(const char* utf8name,
1497+
napi_property_attributes attributes = napi_default,
1498+
void* data = nullptr);
1499+
1500+
template <GetterCallback Getter, SetterCallback Setter>
1501+
static PropertyDescriptor Accessor(const std::string& utf8name,
1502+
napi_property_attributes attributes = napi_default,
1503+
void* data = nullptr);
1504+
1505+
template <GetterCallback Getter, SetterCallback Setter>
1506+
static PropertyDescriptor Accessor(Name name,
1507+
napi_property_attributes attributes = napi_default,
1508+
void* data = nullptr);
1509+
14771510
template <typename Getter>
14781511
static PropertyDescriptor Accessor(Napi::Env env,
14791512
Napi::Object object,
@@ -1559,6 +1592,10 @@ namespace Napi {
15591592
operator const napi_property_descriptor&() const;
15601593

15611594
private:
1595+
template <GetterCallback Getter>
1596+
static napi_value GetterCallbackWrapper(napi_env env, napi_callback_info info);
1597+
template <SetterCallback Setter>
1598+
static napi_value SetterCallbackWrapper(napi_env env, napi_callback_info info);
15621599
napi_property_descriptor _desc;
15631600
};
15641601

test/object/object.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@ void DefineProperties(const CallbackInfo& info) {
8585
PropertyDescriptor::Accessor(env, obj, "readwriteAccessor", TestGetter, TestSetter),
8686
PropertyDescriptor::Accessor(env, obj, "readonlyAccessorWithUserData", TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
8787
PropertyDescriptor::Accessor(env, obj, "readwriteAccessorWithUserData", TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
88+
89+
PropertyDescriptor::Accessor<TestGetter>("readonlyAccessorT"),
90+
PropertyDescriptor::Accessor<TestGetter, TestSetter>(
91+
"readwriteAccessorT"),
92+
PropertyDescriptor::Accessor<TestGetterWithUserData>(
93+
"readonlyAccessorWithUserDataT",
94+
napi_property_attributes::napi_default,
95+
reinterpret_cast<void*>(holder)),
96+
PropertyDescriptor::Accessor<
97+
TestGetterWithUserData,
98+
TestSetterWithUserData>("readwriteAccessorWithUserDataT",
99+
napi_property_attributes::napi_default,
100+
reinterpret_cast<void*>(holder)),
101+
88102
PropertyDescriptor::Value("readonlyValue", trueValue),
89103
PropertyDescriptor::Value("readwriteValue", trueValue, napi_writable),
90104
PropertyDescriptor::Value("enumerableValue", trueValue, napi_enumerable),
@@ -100,6 +114,12 @@ void DefineProperties(const CallbackInfo& info) {
100114
std::string str2("readwriteAccessor");
101115
std::string str1a("readonlyAccessorWithUserData");
102116
std::string str2a("readwriteAccessorWithUserData");
117+
118+
std::string str1t("readonlyAccessorT");
119+
std::string str2t("readwriteAccessorT");
120+
std::string str1at("readonlyAccessorWithUserDataT");
121+
std::string str2at("readwriteAccessorWithUserDataT");
122+
103123
std::string str3("readonlyValue");
104124
std::string str4("readwriteValue");
105125
std::string str5("enumerableValue");
@@ -111,6 +131,18 @@ void DefineProperties(const CallbackInfo& info) {
111131
PropertyDescriptor::Accessor(env, obj, str2, TestGetter, TestSetter),
112132
PropertyDescriptor::Accessor(env, obj, str1a, TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
113133
PropertyDescriptor::Accessor(env, obj, str2a, TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
134+
135+
PropertyDescriptor::Accessor<TestGetter>(str1t),
136+
PropertyDescriptor::Accessor<TestGetter, TestSetter>(str2t),
137+
PropertyDescriptor::Accessor<TestGetterWithUserData>(str1at,
138+
napi_property_attributes::napi_default,
139+
reinterpret_cast<void*>(holder)),
140+
PropertyDescriptor::Accessor<
141+
TestGetterWithUserData,
142+
TestSetterWithUserData>(str2at,
143+
napi_property_attributes::napi_default,
144+
reinterpret_cast<void*>(holder)),
145+
114146
PropertyDescriptor::Value(str3, trueValue),
115147
PropertyDescriptor::Value(str4, trueValue, napi_writable),
116148
PropertyDescriptor::Value(str5, trueValue, napi_enumerable),
@@ -127,6 +159,21 @@ void DefineProperties(const CallbackInfo& info) {
127159
Napi::String::New(env, "readonlyAccessorWithUserData"), TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
128160
PropertyDescriptor::Accessor(env, obj,
129161
Napi::String::New(env, "readwriteAccessorWithUserData"), TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
162+
163+
PropertyDescriptor::Accessor<TestGetter>(
164+
Napi::String::New(env, "readonlyAccessorT")),
165+
PropertyDescriptor::Accessor<TestGetter, TestSetter>(
166+
Napi::String::New(env, "readwriteAccessorT")),
167+
PropertyDescriptor::Accessor<TestGetterWithUserData>(
168+
Napi::String::New(env, "readonlyAccessorWithUserDataT"),
169+
napi_property_attributes::napi_default,
170+
reinterpret_cast<void*>(holder)),
171+
PropertyDescriptor::Accessor<
172+
TestGetterWithUserData, TestSetterWithUserData>(
173+
Napi::String::New(env, "readwriteAccessorWithUserDataT"),
174+
napi_property_attributes::napi_default,
175+
reinterpret_cast<void*>(holder)),
176+
130177
PropertyDescriptor::Value(
131178
Napi::String::New(env, "readonlyValue"), trueValue),
132179
PropertyDescriptor::Value(

test/object/object.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function test(binding) {
2222
const obj = {};
2323
binding.object.defineProperties(obj, nameType);
2424

25+
// accessors
2526
assertPropertyIsNot(obj, 'readonlyAccessor', 'enumerable');
2627
assertPropertyIsNot(obj, 'readonlyAccessor', 'configurable');
2728
assert.strictEqual(obj.readonlyAccessor, true);
@@ -44,6 +45,30 @@ function test(binding) {
4445
obj.readwriteAccessorWithUserData = -14;
4546
assert.strictEqual(obj.readwriteAccessorWithUserData, -14);
4647

48+
// templated accessors
49+
assertPropertyIsNot(obj, 'readonlyAccessorT', 'enumerable');
50+
assertPropertyIsNot(obj, 'readonlyAccessorT', 'configurable');
51+
assert.strictEqual(obj.readonlyAccessorT, true);
52+
53+
assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'enumerable');
54+
assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'configurable');
55+
assert.strictEqual(obj.readonlyAccessorWithUserDataT, -14, nameType);
56+
57+
assertPropertyIsNot(obj, 'readwriteAccessorT', 'enumerable');
58+
assertPropertyIsNot(obj, 'readwriteAccessorT', 'configurable');
59+
obj.readwriteAccessorT = false;
60+
assert.strictEqual(obj.readwriteAccessorT, false);
61+
obj.readwriteAccessorT = true;
62+
assert.strictEqual(obj.readwriteAccessorT, true);
63+
64+
assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'enumerable');
65+
assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'configurable');
66+
obj.readwriteAccessorWithUserDataT = 2;
67+
assert.strictEqual(obj.readwriteAccessorWithUserDataT, 2);
68+
obj.readwriteAccessorWithUserDataT = -14;
69+
assert.strictEqual(obj.readwriteAccessorWithUserDataT, -14);
70+
71+
// values
4772
assertPropertyIsNot(obj, 'readonlyValue', 'writable');
4873
assertPropertyIsNot(obj, 'readonlyValue', 'enumerable');
4974
assertPropertyIsNot(obj, 'readonlyValue', 'configurable');
@@ -65,6 +90,7 @@ function test(binding) {
6590
assertPropertyIsNot(obj, 'configurableValue', 'enumerable');
6691
assertPropertyIs(obj, 'configurableValue', 'configurable');
6792

93+
// functions
6894
assertPropertyIsNot(obj, 'function', 'writable');
6995
assertPropertyIsNot(obj, 'function', 'enumerable');
7096
assertPropertyIsNot(obj, 'function', 'configurable');

0 commit comments

Comments
 (0)