Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions benchmark/napi/create_object_with_properties/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
157 changes: 157 additions & 0 deletions benchmark/napi/create_object_with_properties/binding.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#include <assert.h>
#include <node_api.h>

// Creating with many options because complains are when ~20 properties
static void CreateTestProperties(napi_env env,
napi_value names[20],
napi_value values[20]) {
napi_create_string_utf8(env, "foo", NAPI_AUTO_LENGTH, &names[0]);
napi_create_string_utf8(env, "value1", NAPI_AUTO_LENGTH, &values[0]);
napi_create_string_utf8(env, "alpha", NAPI_AUTO_LENGTH, &names[1]);
napi_create_int32(env, 100, &values[1]);
napi_create_string_utf8(env, "beta", NAPI_AUTO_LENGTH, &names[2]);
napi_get_boolean(env, true, &values[2]);
napi_create_string_utf8(env, "gamma", NAPI_AUTO_LENGTH, &names[3]);
napi_create_double(env, 3.14159, &values[3]);
napi_create_string_utf8(env, "delta", NAPI_AUTO_LENGTH, &names[4]);
napi_create_int32(env, 42, &values[4]);
napi_create_string_utf8(env, "epsilon", NAPI_AUTO_LENGTH, &names[5]);
napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &values[5]);
napi_create_string_utf8(env, "zeta", NAPI_AUTO_LENGTH, &names[6]);
napi_create_string_utf8(env, "data", NAPI_AUTO_LENGTH, &values[6]);
napi_create_string_utf8(env, "eta", NAPI_AUTO_LENGTH, &names[7]);
napi_create_string_utf8(env, "info", NAPI_AUTO_LENGTH, &values[7]);
napi_create_string_utf8(env, "theta", NAPI_AUTO_LENGTH, &names[8]);
napi_create_string_utf8(env, "sample", NAPI_AUTO_LENGTH, &values[8]);
napi_create_string_utf8(env, "iota", NAPI_AUTO_LENGTH, &names[9]);
napi_create_double(env, 2.71828, &values[9]);
napi_create_string_utf8(env, "kappa", NAPI_AUTO_LENGTH, &names[10]);
napi_create_string_utf8(env, "benchmark", NAPI_AUTO_LENGTH, &values[10]);
napi_create_string_utf8(env, "lambda", NAPI_AUTO_LENGTH, &names[11]);
napi_create_string_utf8(env, "result", NAPI_AUTO_LENGTH, &values[11]);
napi_create_string_utf8(env, "mu", NAPI_AUTO_LENGTH, &names[12]);
napi_create_string_utf8(env, "output", NAPI_AUTO_LENGTH, &values[12]);
napi_create_string_utf8(env, "nu", NAPI_AUTO_LENGTH, &names[13]);
napi_get_boolean(env, false, &values[13]);
napi_create_string_utf8(env, "xi", NAPI_AUTO_LENGTH, &names[14]);
napi_create_int32(env, 7, &values[14]);
napi_create_string_utf8(env, "omicron", NAPI_AUTO_LENGTH, &names[15]);
napi_create_double(env, 1.618, &values[15]);
napi_create_string_utf8(env, "pi", NAPI_AUTO_LENGTH, &names[16]);
napi_create_string_utf8(env, "config", NAPI_AUTO_LENGTH, &values[16]);
napi_create_string_utf8(env, "rho", NAPI_AUTO_LENGTH, &names[17]);
napi_create_int32(env, 999, &values[17]);
napi_create_string_utf8(env, "sigma", NAPI_AUTO_LENGTH, &names[18]);
napi_create_double(env, 0.577, &values[18]);
napi_create_string_utf8(env, "tau", NAPI_AUTO_LENGTH, &names[19]);
napi_get_boolean(env, true, &values[19]);
}

static napi_value CreateObjectWithPropertiesNew(napi_env env,
napi_callback_info info) {
size_t argc = 4;
napi_value args[4];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

napi_value count_val = args[0];
napi_value bench_obj = args[1];
napi_value start_fn = args[2];
napi_value end_fn = args[3];

uint32_t count;
napi_get_value_uint32(env, count_val, &count);

napi_value names[20];
napi_value values[20];
napi_value null_prototype;

napi_get_null(env, &null_prototype);
CreateTestProperties(env, names, values);

napi_call_function(env, bench_obj, start_fn, 0, nullptr, nullptr);

for (uint32_t i = 0; i < count; i++) {
napi_value obj;
napi_create_object_with_properties(
env, null_prototype, names, values, 20, &obj);
}

napi_call_function(env, bench_obj, end_fn, 1, &count_val, nullptr);

return nullptr;
}

static napi_value CreateObjectWithPropertiesOld(napi_env env,
napi_callback_info info) {
size_t argc = 4;
napi_value args[4];
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

napi_value count_val = args[0];
napi_value bench_obj = args[1];
napi_value start_fn = args[2];
napi_value end_fn = args[3];

uint32_t count;
napi_get_value_uint32(env, count_val, &count);

napi_value names[20];
napi_value values[20];

CreateTestProperties(env, names, values);

napi_call_function(env, bench_obj, start_fn, 0, nullptr, nullptr);

for (uint32_t i = 0; i < count; i++) {
napi_value obj;
napi_create_object(env, &obj);
napi_set_property(env, obj, names[0], values[0]);
napi_set_property(env, obj, names[1], values[1]);
napi_set_property(env, obj, names[2], values[2]);
napi_set_property(env, obj, names[3], values[3]);
napi_set_property(env, obj, names[4], values[4]);
napi_set_property(env, obj, names[5], values[5]);
napi_set_property(env, obj, names[6], values[6]);
napi_set_property(env, obj, names[7], values[7]);
napi_set_property(env, obj, names[8], values[8]);
napi_set_property(env, obj, names[9], values[9]);
napi_set_property(env, obj, names[10], values[10]);
napi_set_property(env, obj, names[11], values[11]);
napi_set_property(env, obj, names[12], values[12]);
napi_set_property(env, obj, names[13], values[13]);
napi_set_property(env, obj, names[14], values[14]);
napi_set_property(env, obj, names[15], values[15]);
napi_set_property(env, obj, names[16], values[16]);
napi_set_property(env, obj, names[17], values[17]);
napi_set_property(env, obj, names[18], values[18]);
napi_set_property(env, obj, names[19], values[19]);
}

napi_call_function(env, bench_obj, end_fn, 1, &count_val, nullptr);

return nullptr;
}

NAPI_MODULE_INIT() {
napi_property_descriptor desc[] = {
{"createObjectWithPropertiesNew",
0,
CreateObjectWithPropertiesNew,
0,
0,
0,
napi_default,
0},
{"createObjectWithPropertiesOld",
0,
CreateObjectWithPropertiesOld,
0,
0,
0,
napi_default,
0},
};

napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
9 changes: 9 additions & 0 deletions benchmark/napi/create_object_with_properties/binding.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
'targets': [
{
'target_name': 'binding',
'sources': [ 'binding.cc' ],
'defines': ['NAPI_VERSION=8']
}
]
}
24 changes: 24 additions & 0 deletions benchmark/napi/create_object_with_properties/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const common = require('../../common.js');

let binding;
try {
binding = require(`./build/${common.buildType}/binding`);
} catch {
console.error(`${__filename}: Binding failed to load`);
process.exit(0);
}

const bench = common.createBenchmark(main, {
n: [1e2, 1e3, 1e4, 1e5, 1e6],
method: ['new', 'old'],
});

function main({ n, method }) {
if (method === 'new') {
binding.createObjectWithPropertiesNew(n, bench, bench.start, bench.end);
} else {
binding.createObjectWithPropertiesOld(n, bench, bench.start, bench.end);
}
}
35 changes: 35 additions & 0 deletions doc/api/n-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2637,6 +2637,41 @@ It is the equivalent of doing `new Object()` in JavaScript.
The JavaScript `Object` type is described in [Section object type][] of the
ECMAScript Language Specification.

#### `napi_create_object_with_properties`

<!-- YAML
added: REPLACEME
napiVersion: 8
-->

```c
napi_status napi_create_object_with_properties(napi_env env,
napi_value prototype_or_null,
const napi_value* property_names,
const napi_value* property_values,
size_t property_count,
napi_value* result)
```

* `[in] env`: The environment that the API is invoked under.
* `[in] prototype_or_null`: The prototype object for the new object, or `null`
for no prototype.
* `[in] property_names`: Array of `napi_value` representing the property names.
* `[in] property_values`: Array of `napi_value` representing the property values.
* `[in] property_count`: Number of properties in the arrays.
* `[out] result`: A `napi_value` representing a JavaScript `Object`.

Returns `napi_ok` if the API succeeded.

This API creates a JavaScript `Object` with the specified prototype and
properties. This is more efficient than calling `napi_create_object` followed
by multiple `napi_set_property` calls, as it can create the object with all
properties atomically, avoiding potential V8 map transitions.

The arrays `property_names` and `property_values` must have the same length
specified by `property_count`. The properties are added to the object in the
order they appear in the arrays.

#### `napi_create_symbol`

<!-- YAML
Expand Down
7 changes: 7 additions & 0 deletions src/js_native_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,13 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_object_freeze(napi_env env,
napi_value object);
NAPI_EXTERN napi_status NAPI_CDECL napi_object_seal(napi_env env,
napi_value object);
NAPI_EXTERN napi_status NAPI_CDECL
napi_create_object_with_properties(napi_env env,
napi_value prototype_or_null,
napi_value* property_names,
napi_value* property_values,
size_t property_count,
napi_value* result);
#endif // NAPI_VERSION >= 8

EXTERN_C_END
Expand Down
47 changes: 47 additions & 0 deletions src/js_native_api_v8.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,53 @@ napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) {
return napi_clear_last_error(env);
}

napi_status NAPI_CDECL
napi_create_object_with_properties(napi_env env,
napi_value prototype_or_null,
napi_value* property_names,
napi_value* property_values,
size_t property_count,
napi_value* result) {
CHECK_ENV_NOT_IN_GC(env);
CHECK_ARG(env, result);

if (property_count > 0) {
CHECK_ARG(env, property_names);
CHECK_ARG(env, property_values);
}

v8::Local<v8::Value> v8_prototype_or_null =
v8impl::V8LocalValueFromJsValue(prototype_or_null);

v8::Local<v8::Object> obj;

if (property_count > 0) {
v8::LocalVector<v8::Name> v8_names(env->isolate, property_count);
v8::LocalVector<v8::Value> v8_values(env->isolate, property_count);

for (size_t i = 0; i < property_count; i++) {
v8::Local<v8::Value> name_value =
v8impl::V8LocalValueFromJsValue(property_names[i]);
RETURN_STATUS_IF_FALSE(env, name_value->IsName(), napi_name_expected);
v8_names[i] = name_value.As<v8::Name>();
v8_values[i] = v8impl::V8LocalValueFromJsValue(property_values[i]);
}

obj = v8::Object::New(env->isolate,
v8_prototype_or_null,
v8_names.data(),
v8_values.data(),
property_count);
} else {
obj = v8::Object::New(
env->isolate, v8_prototype_or_null, nullptr, nullptr, 0);
}

RETURN_STATUS_IF_FALSE(env, !obj.IsEmpty(), napi_generic_failure);
*result = v8impl::JsValueFromV8LocalValue(obj);
return napi_clear_last_error(env);
}

napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) {
CHECK_ENV_NOT_IN_GC(env);
CHECK_ARG(env, result);
Expand Down
18 changes: 17 additions & 1 deletion test/js-native-api/test_object/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const assert = require('assert');
// Testing api calls for objects
const test_object = require(`./build/${common.buildType}/test_object`);


const object = {
hello: 'world',
array: [
Expand Down Expand Up @@ -391,3 +390,20 @@ assert.deepStrictEqual(test_object.TestGetProperty(), {
delete obj.x;
}, /Cannot delete property 'x' of #<Object>/);
}

{
const objectWithProperties = test_object.TestCreateObjectWithProperties();
assert.strictEqual(typeof objectWithProperties, 'object');
assert.strictEqual(objectWithProperties.name, 'Foo');
assert.strictEqual(objectWithProperties.age, 42);
assert.strictEqual(objectWithProperties.active, true);

const emptyObject = test_object.TestCreateObjectWithPropertiesEmpty();
assert.strictEqual(typeof emptyObject, 'object');
assert.strictEqual(Object.keys(emptyObject).length, 0);

const objectWithCustomPrototype = test_object.TestCreateObjectWithCustomPrototype();
assert.strictEqual(typeof objectWithCustomPrototype, 'object');
assert.strictEqual(objectWithCustomPrototype.value, 42);
assert.strictEqual(typeof objectWithCustomPrototype.test, 'function');
}
Loading
Loading