Skip to content
Open
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
27 changes: 27 additions & 0 deletions doc/object.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,33 @@ Napi::Object Napi::Object::New(napi_env env);

Creates a new `Napi::Object` value.

### New()

```cpp
Napi::Object Napi::Object::New(
napi_env env,
napi_value prototypeOrNull,
std::vector<napi_value>& propertyNames,
std::vector<napi_value>& propertyValues);
```
- `[in] env`: The `napi_env` environment in which to construct the `Napi::Value`
object.
- `[in] prototypeOrNull`: The prototype object for the new object. Can be an
`napi_value` representing a JavaScript object to use as the prototype, an
`napi_value` representing JavaScript `null`, or `nullptr` that will be
converted to `null`.
- `[in] propertyNames`: Array of `napi_value`s representing the property names.
- `[in] propertyValues`: Array of `napi_value`s representing the property
values.

Creates a new `Napi::Object` with the specified prototype and properties. This
is more efficient than calling `Napi::Object::New()` followed by multiple
`Set()` calls, as it can create the object with all properties atomically.

**NOTE**: The support for this overload of `Napi::Object::New()` is only
available when using `NAPI_EXPERIMENTAL` and building against Node.js headers
that supports this feature.

### Set()

```cpp
Expand Down
25 changes: 25 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,31 @@ inline Object Object::New(napi_env env) {
return Object(env, value);
}

#ifdef NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
inline Object Object::New(napi_env env,
napi_value prototypeOrNull,
std::vector<napi_value>& propertyNames,
std::vector<napi_value>& propertyValues) {
if (propertyNames.size() != propertyValues.size()) {
Napi::Error::New(env, "Mismatch in size of property names and values")
.ThrowAsJavaScriptException();
return Object();
}

napi_value value;
napi_status status =
node_api_create_object_with_properties(env,
prototypeOrNull,
propertyNames.data(),
propertyValues.data(),
propertyNames.size(),
&value);

NAPI_THROW_IF_FAILED(env, status, Object());
return Object(env, value);
}
#endif

inline void Object::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Object::CheckCast", "empty value");

Expand Down
10 changes: 10 additions & 0 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,16 @@ class Object : public TypeTaggable {
static Object New(napi_env env ///< Node-API environment
);

#ifdef NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
/// Creates a new Object value with the given property names and values.
static Object New(
napi_env env, ///< Node-API environment
napi_value prototypeOrNull, ///< Prototype (Object) or null / empty Value
std::vector<napi_value>& propertyNames, ///< Property names
std::vector<napi_value>& propertyValues ///< Property values
);
#endif

static void CheckCast(napi_env env, napi_value value);

Object(); ///< Creates a new _empty_ Object instance.
Expand Down
39 changes: 39 additions & 0 deletions test/object/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,40 @@ Value SetPrototype(const CallbackInfo& info) {
}
#endif // NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE

#ifdef NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
Value CreateObjectWithProperties(const CallbackInfo& info) {
Env env = info.Env();
Value prototype = info.Length() > 0 ? info[0] : Value();
Array propertyNames =
info.Length() > 1 ? info[1].As<Array>() : Array::New(env);
Array propertyValues =
info.Length() > 2 ? info[2].As<Array>() : Array::New(env);

std::vector<napi_value> names;
std::vector<napi_value> values;

#ifdef NODE_ADDON_API_ENABLE_MAYBE
for (uint32_t i = 0; i < propertyNames.Length(); ++i) {
names.push_back(propertyNames.Get(i).Unwrap());
}

for (uint32_t i = 0; i < propertyValues.Length(); ++i) {
values.push_back(propertyValues.Get(i).Unwrap());
}
#else
for (uint32_t i = 0; i < propertyNames.Length(); ++i) {
names.push_back(propertyNames.Get(i));
}

for (uint32_t i = 0; i < propertyValues.Length(); ++i) {
values.push_back(propertyValues.Get(i));
}
#endif

return Object::New(env, prototype, names, values);
}
#endif

Object InitObject(Env env) {
Object exports = Object::New(env);

Expand Down Expand Up @@ -444,5 +478,10 @@ Object InitObject(Env env) {
exports["setPrototype"] = Function::New(env, SetPrototype);
#endif // NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE

#ifdef NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
exports["createObjectWithProperties"] =
Function::New(env, CreateObjectWithProperties);
#endif // NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES

return exports;
}
49 changes: 49 additions & 0 deletions test/object/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,53 @@ function test (binding) {
assert.strictEqual(binding.object.setPrototype(obj, prototype), true);
assert.strictEqual(Object.getPrototypeOf(obj), prototype);
}

if ('createObjectWithProperties' in binding.object) {
{
const prototype = {};
const names = ['name', 'age', 'active', Symbol.for('id')];
const values = ['Foo', 42, true, 12345];
const obj = binding.object.createObjectWithProperties(prototype, names, values);
const descriptors = Object.getOwnPropertyDescriptors(obj);

assert.strictEqual(Object.getPrototypeOf(obj), prototype);

assert.equal(Reflect.ownKeys(descriptors).length, names.length);

for (let i = 0; i < names.length; i++) {
const expectedName = names[i];
const expectedValue = values[i];

assert.ok(expectedName in descriptors);
assert.strictEqual(descriptors[expectedName].value, expectedValue);
}
}

{
// Test `null` Napi::Value passed as prototype
const prototype = null;
const obj = binding.object.createObjectWithProperties(prototype);
assert.strictEqual(Object.getPrototypeOf(obj), prototype);
}

{
// Test empty Napi::Value passed as prototype
const obj = binding.object.createObjectWithProperties();
assert.strictEqual(Object.getPrototypeOf(obj), null);
}

{
// Test mismatch in length between property names and values
const expectedErrorMessage = 'Mismatch in size of property names and values';

try {
binding.object.createObjectWithProperties(null, ['foo'], []);
throw new Error(`Expected error "${expectedErrorMessage}" was not thrown`);
} catch (e) {
if (e.message !== expectedErrorMessage) {
throw e;
}
}
}
}
}
Loading