Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

object: add templated property descriptors #610

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 64 additions & 9 deletions doc/property_descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,9 @@ Void Init(Env env) {
Object obj = Object::New(env);

// Accessor
PropertyDescriptor pd1 = PropertyDescriptor::Accessor(env,
obj,
"pd1",
TestGetter);
PropertyDescriptor pd2 = PropertyDescriptor::Accessor(env,
obj,
"pd2",
TestGetter,
TestSetter);
PropertyDescriptor pd1 = PropertyDescriptor::Accessor<TestGetter>("pd1");
PropertyDescriptor pd2 =
PropertyDescriptor::Accessor<TestGetter, TestSetter>("pd2");
// Function
PropertyDescriptor pd3 = PropertyDescriptor::Function(env,
"function",
Expand All @@ -51,6 +45,26 @@ Void Init(Env env) {
}
```

## Types

### PropertyDescriptor::GetterCallback

```cpp
typedef Napi::Value (*GetterCallback)(const Napi::CallbackInfo& info);
```

This is the signature of a getter function to be passed as a template parameter
to `PropertyDescriptor::Accessor`.

### PropertyDescriptor::SetterCallback

```cpp
typedef void (*SetterCallback)(const Napi::CallbackInfo& info);
```

This is the signature of a setter function to be passed as a template parameter
to `PropertyDescriptor::Accessor`.

## Methods

### Constructor
Expand All @@ -63,6 +77,47 @@ Napi::PropertyDescriptor::PropertyDescriptor (napi_property_descriptor desc);

### Accessor

```cpp
template <Napi::PropertyDescriptor::GetterCallback Getter>
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);
```

* `[template] Getter`: A getter function.
* `[in] attributes`: Potential attributes for the getter function.
* `[in] data`: A pointer to data of any type, default is a null pointer.

Returns a PropertyDescriptor that contains a read-only property.

The name of the property can be any of the following types:
- `const char*`
- `const std::string &`
- `napi_value value`
- `Napi::Name`
NickNaso marked this conversation as resolved.
Show resolved Hide resolved

```cpp
template <
Napi::PropertyDescriptor::GetterCallback Getter,
Napi::PropertyDescriptor::SetterCallback Setter>
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);
```

* `[template] Getter`: A getter function.
* `[template] Setter`: A setter function.
* `[in] attributes`: Potential attributes for the getter function.
* `[in] data`: A pointer to data of any type, default is a null pointer.

Returns a PropertyDescriptor that contains a read-write property.

The name of the property can be any of the following types:
- `const char*`
- `const std::string &`
- `napi_value value`
- `Napi::Name`

NickNaso marked this conversation as resolved.
Show resolved Hide resolved
```cpp
static Napi::PropertyDescriptor Napi::PropertyDescriptor::Accessor (___ name,
Getter getter,
Expand Down
102 changes: 102 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2732,6 +2732,108 @@ inline void CallbackInfo::SetData(void* data) {
// PropertyDescriptor class
////////////////////////////////////////////////////////////////////////////////

template <typename PropertyDescriptor::GetterCallback Getter>
PropertyDescriptor
PropertyDescriptor::Accessor(const char* utf8name,
napi_property_attributes attributes,
void* data) {
napi_property_descriptor desc = napi_property_descriptor();

desc.utf8name = utf8name;
desc.getter = &GetterCallbackWrapper<Getter>;
desc.attributes = attributes;
desc.data = data;

return desc;
}

template <typename PropertyDescriptor::GetterCallback Getter>
PropertyDescriptor
PropertyDescriptor::Accessor(const std::string& utf8name,
napi_property_attributes attributes,
void* data) {
return Accessor<Getter>(utf8name.c_str(), attributes, data);
}

template <typename PropertyDescriptor::GetterCallback Getter>
PropertyDescriptor
PropertyDescriptor::Accessor(Name name,
napi_property_attributes attributes,
void* data) {
napi_property_descriptor desc = napi_property_descriptor();

desc.name = name;
desc.getter = &GetterCallbackWrapper<Getter>;
desc.attributes = attributes;
desc.data = data;

return desc;
}

template <
typename PropertyDescriptor::GetterCallback Getter,
typename PropertyDescriptor::SetterCallback Setter>
PropertyDescriptor
PropertyDescriptor::Accessor(const char* utf8name,
napi_property_attributes attributes,
void* data) {

napi_property_descriptor desc = napi_property_descriptor();

desc.utf8name = utf8name;
desc.getter = &GetterCallbackWrapper<Getter>;
desc.setter = &SetterCallbackWrapper<Setter>;
desc.attributes = attributes;
desc.data = data;

return desc;
}

template <
typename PropertyDescriptor::GetterCallback Getter,
typename PropertyDescriptor::SetterCallback Setter>
PropertyDescriptor
PropertyDescriptor::Accessor(const std::string& utf8name,
napi_property_attributes attributes,
void* data) {
return Accessor<Getter, Setter>(utf8name.c_str(), attributes, data);
}

template <
typename PropertyDescriptor::GetterCallback Getter,
typename PropertyDescriptor::SetterCallback Setter>
PropertyDescriptor
PropertyDescriptor::Accessor(Name name,
napi_property_attributes attributes,
void* data) {
napi_property_descriptor desc = napi_property_descriptor();

desc.name = name;
desc.getter = &GetterCallbackWrapper<Getter>;
desc.setter = &SetterCallbackWrapper<Setter>;
desc.attributes = attributes;
desc.data = data;

return desc;
}

template <typename PropertyDescriptor::GetterCallback Getter>
napi_value
PropertyDescriptor::GetterCallbackWrapper(napi_env env,
napi_callback_info info) {
CallbackInfo cbInfo(env, info);
return Getter(cbInfo);
}

template <typename PropertyDescriptor::SetterCallback Setter>
napi_value
PropertyDescriptor::SetterCallbackWrapper(napi_env env,
napi_callback_info info) {
CallbackInfo cbInfo(env, info);
Setter(cbInfo);
return nullptr;
}

template <typename Getter>
inline PropertyDescriptor
PropertyDescriptor::Accessor(Napi::Env env,
Expand Down
37 changes: 37 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,9 @@ namespace Napi {

class PropertyDescriptor {
public:
typedef Napi::Value (*GetterCallback)(const Napi::CallbackInfo& info);
typedef void (*SetterCallback)(const Napi::CallbackInfo& info);

#ifndef NODE_ADDON_API_DISABLE_DEPRECATED
template <typename Getter>
static PropertyDescriptor Accessor(const char* utf8name,
Expand Down Expand Up @@ -1474,6 +1477,36 @@ namespace Napi {
void* data = nullptr);
#endif // !NODE_ADDON_API_DISABLE_DEPRECATED

template <GetterCallback Getter>
static PropertyDescriptor Accessor(const char* utf8name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <GetterCallback Getter>
static PropertyDescriptor Accessor(const std::string& utf8name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <GetterCallback Getter>
static PropertyDescriptor Accessor(Name name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <GetterCallback Getter, SetterCallback Setter>
static PropertyDescriptor Accessor(const char* utf8name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <GetterCallback Getter, SetterCallback Setter>
static PropertyDescriptor Accessor(const std::string& utf8name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <GetterCallback Getter, SetterCallback Setter>
static PropertyDescriptor Accessor(Name name,
napi_property_attributes attributes = napi_default,
void* data = nullptr);

template <typename Getter>
static PropertyDescriptor Accessor(Napi::Env env,
Napi::Object object,
Expand Down Expand Up @@ -1559,6 +1592,10 @@ namespace Napi {
operator const napi_property_descriptor&() const;

private:
template <GetterCallback Getter>
static napi_value GetterCallbackWrapper(napi_env env, napi_callback_info info);
template <SetterCallback Setter>
static napi_value SetterCallbackWrapper(napi_env env, napi_callback_info info);
napi_property_descriptor _desc;
};

Expand Down
47 changes: 47 additions & 0 deletions test/object/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<void*>(holder)),
PropertyDescriptor::Accessor(env, obj, "readwriteAccessorWithUserData", TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),

PropertyDescriptor::Accessor<TestGetter>("readonlyAccessorT"),
PropertyDescriptor::Accessor<TestGetter, TestSetter>(
"readwriteAccessorT"),
PropertyDescriptor::Accessor<TestGetterWithUserData>(
"readonlyAccessorWithUserDataT",
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),
PropertyDescriptor::Accessor<
TestGetterWithUserData,
TestSetterWithUserData>("readwriteAccessorWithUserDataT",
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),

PropertyDescriptor::Value("readonlyValue", trueValue),
PropertyDescriptor::Value("readwriteValue", trueValue, napi_writable),
PropertyDescriptor::Value("enumerableValue", trueValue, napi_enumerable),
Expand All @@ -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");
Expand All @@ -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<void*>(holder)),
PropertyDescriptor::Accessor(env, obj, str2a, TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),

PropertyDescriptor::Accessor<TestGetter>(str1t),
PropertyDescriptor::Accessor<TestGetter, TestSetter>(str2t),
PropertyDescriptor::Accessor<TestGetterWithUserData>(str1at,
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),
PropertyDescriptor::Accessor<
TestGetterWithUserData,
TestSetterWithUserData>(str2at,
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),

PropertyDescriptor::Value(str3, trueValue),
PropertyDescriptor::Value(str4, trueValue, napi_writable),
PropertyDescriptor::Value(str5, trueValue, napi_enumerable),
Expand All @@ -127,6 +159,21 @@ void DefineProperties(const CallbackInfo& info) {
Napi::String::New(env, "readonlyAccessorWithUserData"), TestGetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),
PropertyDescriptor::Accessor(env, obj,
Napi::String::New(env, "readwriteAccessorWithUserData"), TestGetterWithUserData, TestSetterWithUserData, napi_property_attributes::napi_default, reinterpret_cast<void*>(holder)),

PropertyDescriptor::Accessor<TestGetter>(
Napi::String::New(env, "readonlyAccessorT")),
PropertyDescriptor::Accessor<TestGetter, TestSetter>(
Napi::String::New(env, "readwriteAccessorT")),
PropertyDescriptor::Accessor<TestGetterWithUserData>(
Napi::String::New(env, "readonlyAccessorWithUserDataT"),
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),
PropertyDescriptor::Accessor<
TestGetterWithUserData, TestSetterWithUserData>(
Napi::String::New(env, "readwriteAccessorWithUserDataT"),
napi_property_attributes::napi_default,
reinterpret_cast<void*>(holder)),

PropertyDescriptor::Value(
Napi::String::New(env, "readonlyValue"), trueValue),
PropertyDescriptor::Value(
Expand Down
26 changes: 26 additions & 0 deletions test/object/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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.readonlyAccessorT, true);

assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'enumerable');
assertPropertyIsNot(obj, 'readonlyAccessorWithUserDataT', 'configurable');
assert.strictEqual(obj.readonlyAccessorWithUserDataT, -14, nameType);

assertPropertyIsNot(obj, 'readwriteAccessorT', 'enumerable');
assertPropertyIsNot(obj, 'readwriteAccessorT', 'configurable');
obj.readwriteAccessorT = false;
assert.strictEqual(obj.readwriteAccessorT, false);
obj.readwriteAccessorT = true;
assert.strictEqual(obj.readwriteAccessorT, true);

assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'enumerable');
assertPropertyIsNot(obj, 'readwriteAccessorWithUserDataT', 'configurable');
obj.readwriteAccessorWithUserDataT = 2;
assert.strictEqual(obj.readwriteAccessorWithUserDataT, 2);
obj.readwriteAccessorWithUserDataT = -14;
assert.strictEqual(obj.readwriteAccessorWithUserDataT, -14);

// values
assertPropertyIsNot(obj, 'readonlyValue', 'writable');
assertPropertyIsNot(obj, 'readonlyValue', 'enumerable');
assertPropertyIsNot(obj, 'readonlyValue', 'configurable');
Expand All @@ -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');
Expand Down