mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
node-api: add napi_create_object_with_properties
PR-URL: https://github.com/nodejs/node/pull/59953 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Vladimir Morozov <vmorozov@microsoft.com>
This commit is contained in:
parent
515ca295fb
commit
a556db4782
1
benchmark/napi/create_object_with_properties/.gitignore
vendored
Normal file
1
benchmark/napi/create_object_with_properties/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
||||||
116
benchmark/napi/create_object_with_properties/binding.cc
Normal file
116
benchmark/napi/create_object_with_properties/binding.cc
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <node_api.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct BenchmarkParams {
|
||||||
|
napi_value count_val;
|
||||||
|
napi_value bench_obj;
|
||||||
|
napi_value start_fn;
|
||||||
|
napi_value end_fn;
|
||||||
|
uint32_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
static BenchmarkParams ParseBenchmarkArgs(napi_env env,
|
||||||
|
const napi_callback_info info) {
|
||||||
|
BenchmarkParams params;
|
||||||
|
size_t argc = 4;
|
||||||
|
napi_value args[4];
|
||||||
|
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||||||
|
|
||||||
|
params.count_val = args[0];
|
||||||
|
params.bench_obj = args[1];
|
||||||
|
params.start_fn = args[2];
|
||||||
|
params.end_fn = args[3];
|
||||||
|
|
||||||
|
napi_get_value_uint32(env, params.count_val, ¶ms.count);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
static napi_value global_names[20];
|
||||||
|
static napi_value global_values[20];
|
||||||
|
static bool global_properties_initialized = false;
|
||||||
|
|
||||||
|
// Creating with many options because complains are when ~20 properties
|
||||||
|
static void InitializeTestProperties(napi_env env) {
|
||||||
|
if (global_properties_initialized) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
std::string name = "foo" + std::to_string(i);
|
||||||
|
napi_create_string_utf8(
|
||||||
|
env, name.c_str(), NAPI_AUTO_LENGTH, &global_names[i]);
|
||||||
|
napi_create_string_utf8(
|
||||||
|
env, name.c_str(), NAPI_AUTO_LENGTH, &global_values[i]);
|
||||||
|
}
|
||||||
|
global_properties_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static napi_value CreateObjectWithPropertiesNew(napi_env env,
|
||||||
|
napi_callback_info info) {
|
||||||
|
BenchmarkParams params = ParseBenchmarkArgs(env, info);
|
||||||
|
|
||||||
|
InitializeTestProperties(env);
|
||||||
|
|
||||||
|
napi_value null_prototype;
|
||||||
|
napi_get_null(env, &null_prototype);
|
||||||
|
|
||||||
|
napi_call_function(
|
||||||
|
env, params.bench_obj, params.start_fn, 0, nullptr, nullptr);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < params.count; i++) {
|
||||||
|
napi_value obj;
|
||||||
|
napi_create_object_with_properties(
|
||||||
|
env, null_prototype, global_names, global_values, 20, &obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
napi_call_function(
|
||||||
|
env, params.bench_obj, params.end_fn, 1, ¶ms.count_val, nullptr);
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static napi_value CreateObjectWithPropertiesOld(napi_env env,
|
||||||
|
napi_callback_info info) {
|
||||||
|
BenchmarkParams params = ParseBenchmarkArgs(env, info);
|
||||||
|
|
||||||
|
InitializeTestProperties(env);
|
||||||
|
|
||||||
|
napi_call_function(
|
||||||
|
env, params.bench_obj, params.start_fn, 0, nullptr, nullptr);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < params.count; i++) {
|
||||||
|
napi_value obj;
|
||||||
|
napi_create_object(env, &obj);
|
||||||
|
for (int j = 0; j < 20; j++) {
|
||||||
|
napi_set_property(env, obj, global_names[j], global_values[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
napi_call_function(
|
||||||
|
env, params.bench_obj, params.end_fn, 1, ¶ms.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
benchmark/napi/create_object_with_properties/binding.gyp
Normal file
9
benchmark/napi/create_object_with_properties/binding.gyp
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'binding',
|
||||||
|
'sources': [ 'binding.cc' ],
|
||||||
|
'defines': ['NAPI_EXPERIMENTAL']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
24
benchmark/napi/create_object_with_properties/index.js
Normal file
24
benchmark/napi/create_object_with_properties/index.js
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2637,6 +2637,43 @@ It is the equivalent of doing `new Object()` in JavaScript.
|
||||||
The JavaScript `Object` type is described in [Section object type][] of the
|
The JavaScript `Object` type is described in [Section object type][] of the
|
||||||
ECMAScript Language Specification.
|
ECMAScript Language Specification.
|
||||||
|
|
||||||
|
#### `napi_create_object_with_properties`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
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. Can be a
|
||||||
|
`napi_value` representing a JavaScript object to use as the prototype, a
|
||||||
|
`napi_value` representing JavaScript `null`, or a `nullptr` that will be converted to `null`.
|
||||||
|
* `[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`
|
#### `napi_create_symbol`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,17 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_boolean(napi_env env,
|
||||||
// Methods to create Primitive types/Objects
|
// Methods to create Primitive types/Objects
|
||||||
NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env,
|
NAPI_EXTERN napi_status NAPI_CDECL napi_create_object(napi_env env,
|
||||||
napi_value* result);
|
napi_value* result);
|
||||||
|
#ifdef NAPI_EXPERIMENTAL
|
||||||
|
#define NODE_API_EXPERIMENTAL_HAS_CREATE_OBJECT_WITH_PROPERTIES
|
||||||
|
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_EXPERIMENTAL
|
||||||
|
|
||||||
NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env,
|
NAPI_EXTERN napi_status NAPI_CDECL napi_create_array(napi_env env,
|
||||||
napi_value* result);
|
napi_value* result);
|
||||||
NAPI_EXTERN napi_status NAPI_CDECL
|
NAPI_EXTERN napi_status NAPI_CDECL
|
||||||
|
|
|
||||||
|
|
@ -1593,6 +1593,50 @@ napi_status NAPI_CDECL napi_create_object(napi_env env, napi_value* result) {
|
||||||
return napi_clear_last_error(env);
|
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;
|
||||||
|
if (prototype_or_null == nullptr) {
|
||||||
|
v8_prototype_or_null = v8::Null(env->isolate);
|
||||||
|
} else {
|
||||||
|
v8_prototype_or_null = v8impl::V8LocalValueFromJsValue(prototype_or_null);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Object> obj = v8::Object::New(env->isolate,
|
||||||
|
v8_prototype_or_null,
|
||||||
|
v8_names.data(),
|
||||||
|
v8_values.data(),
|
||||||
|
property_count);
|
||||||
|
|
||||||
|
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) {
|
napi_status NAPI_CDECL napi_create_array(napi_env env, napi_value* result) {
|
||||||
CHECK_ENV_NOT_IN_GC(env);
|
CHECK_ENV_NOT_IN_GC(env);
|
||||||
CHECK_ARG(env, result);
|
CHECK_ARG(env, result);
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
"sources": [
|
"sources": [
|
||||||
"test_null.c",
|
"test_null.c",
|
||||||
"test_object.c"
|
"test_object.c"
|
||||||
]
|
],
|
||||||
|
"defines": [
|
||||||
|
"NAPI_EXPERIMENTAL"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"target_name": "test_exceptions",
|
"target_name": "test_exceptions",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ const assert = require('assert');
|
||||||
// Testing api calls for objects
|
// Testing api calls for objects
|
||||||
const test_object = require(`./build/${common.buildType}/test_object`);
|
const test_object = require(`./build/${common.buildType}/test_object`);
|
||||||
|
|
||||||
|
|
||||||
const object = {
|
const object = {
|
||||||
hello: 'world',
|
hello: 'world',
|
||||||
array: [
|
array: [
|
||||||
|
|
@ -391,3 +390,21 @@ assert.deepStrictEqual(test_object.TestGetProperty(), {
|
||||||
delete obj.x;
|
delete obj.x;
|
||||||
}, /Cannot delete property 'x' of #<Object>/);
|
}, /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.deepStrictEqual(Object.getOwnPropertyNames(objectWithCustomPrototype), ['value']);
|
||||||
|
assert.strictEqual(objectWithCustomPrototype.value, 42);
|
||||||
|
assert.strictEqual(typeof objectWithCustomPrototype.test, 'function');
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -710,6 +710,79 @@ CheckTypeTag(napi_env env, napi_callback_info info) {
|
||||||
return js_result;
|
return js_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static napi_value TestCreateObjectWithProperties(napi_env env,
|
||||||
|
napi_callback_info info) {
|
||||||
|
napi_value names[3];
|
||||||
|
napi_value values[3];
|
||||||
|
napi_value result;
|
||||||
|
|
||||||
|
NODE_API_CALL(
|
||||||
|
env, napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &names[0]));
|
||||||
|
NODE_API_CALL(
|
||||||
|
env, napi_create_string_utf8(env, "Foo", NAPI_AUTO_LENGTH, &values[0]));
|
||||||
|
|
||||||
|
NODE_API_CALL(
|
||||||
|
env, napi_create_string_utf8(env, "age", NAPI_AUTO_LENGTH, &names[1]));
|
||||||
|
NODE_API_CALL(env, napi_create_int32(env, 42, &values[1]));
|
||||||
|
|
||||||
|
NODE_API_CALL(
|
||||||
|
env, napi_create_string_utf8(env, "active", NAPI_AUTO_LENGTH, &names[2]));
|
||||||
|
NODE_API_CALL(env, napi_get_boolean(env, true, &values[2]));
|
||||||
|
|
||||||
|
napi_value null_prototype;
|
||||||
|
NODE_API_CALL(env, napi_get_null(env, &null_prototype));
|
||||||
|
NODE_API_CALL(env,
|
||||||
|
napi_create_object_with_properties(
|
||||||
|
env, null_prototype, names, values, 3, &result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static napi_value TestCreateObjectWithPropertiesEmpty(napi_env env,
|
||||||
|
napi_callback_info info) {
|
||||||
|
napi_value result;
|
||||||
|
|
||||||
|
NODE_API_CALL(
|
||||||
|
env,
|
||||||
|
napi_create_object_with_properties(env, NULL, NULL, NULL, 0, &result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static napi_value TestCreateObjectWithCustomPrototype(napi_env env,
|
||||||
|
napi_callback_info info) {
|
||||||
|
napi_value prototype;
|
||||||
|
napi_value method_name;
|
||||||
|
napi_value method_func;
|
||||||
|
napi_value names[1];
|
||||||
|
napi_value values[1];
|
||||||
|
napi_value result;
|
||||||
|
|
||||||
|
NODE_API_CALL(env, napi_create_object(env, &prototype));
|
||||||
|
NODE_API_CALL(
|
||||||
|
env,
|
||||||
|
napi_create_string_utf8(env, "test", NAPI_AUTO_LENGTH, &method_name));
|
||||||
|
NODE_API_CALL(env,
|
||||||
|
napi_create_function(env,
|
||||||
|
"test",
|
||||||
|
NAPI_AUTO_LENGTH,
|
||||||
|
TestCreateObjectWithProperties,
|
||||||
|
NULL,
|
||||||
|
&method_func));
|
||||||
|
NODE_API_CALL(env,
|
||||||
|
napi_set_property(env, prototype, method_name, method_func));
|
||||||
|
|
||||||
|
NODE_API_CALL(
|
||||||
|
env, napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &names[0]));
|
||||||
|
NODE_API_CALL(env, napi_create_int32(env, 42, &values[0]));
|
||||||
|
|
||||||
|
NODE_API_CALL(env,
|
||||||
|
napi_create_object_with_properties(
|
||||||
|
env, prototype, names, values, 1, &result));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
EXTERN_C_START
|
EXTERN_C_START
|
||||||
napi_value Init(napi_env env, napi_value exports) {
|
napi_value Init(napi_env env, napi_value exports) {
|
||||||
napi_property_descriptor descriptors[] = {
|
napi_property_descriptor descriptors[] = {
|
||||||
|
|
@ -743,6 +816,12 @@ napi_value Init(napi_env env, napi_value exports) {
|
||||||
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
|
DECLARE_NODE_API_PROPERTY("TestGetProperty", TestGetProperty),
|
||||||
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
|
DECLARE_NODE_API_PROPERTY("TestFreeze", TestFreeze),
|
||||||
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
|
DECLARE_NODE_API_PROPERTY("TestSeal", TestSeal),
|
||||||
|
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithProperties",
|
||||||
|
TestCreateObjectWithProperties),
|
||||||
|
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithPropertiesEmpty",
|
||||||
|
TestCreateObjectWithPropertiesEmpty),
|
||||||
|
DECLARE_NODE_API_PROPERTY("TestCreateObjectWithCustomPrototype",
|
||||||
|
TestCreateObjectWithCustomPrototype),
|
||||||
};
|
};
|
||||||
|
|
||||||
init_test_null(env, exports);
|
init_test_null(env, exports);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user