Skip to content

Commit dbf9e9f

Browse files
gabrielschulhofRafaelGSS
authored andcommitted
node-api: provide napi_define_properties fast path
Implement defining properties via V8's `v8::Object::CreateDataProperty()`, which is faster for data-valued, writable, configurable, and enumerable properties. Re: #45905 Signed-off-by: Gabriel Schulhof <[email protected]> PR-URL: #48440 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Tobias Nießen <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent 38bf290 commit dbf9e9f

File tree

6 files changed

+149
-9
lines changed

6 files changed

+149
-9
lines changed

‎Makefile‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,7 @@ FORMAT_CPP_FILES ?=
14221422
FORMAT_CPP_FILES += $(LINT_CPP_FILES)
14231423
# C source codes.
14241424
FORMAT_CPP_FILES += $(wildcard \
1425+
benchmark/napi/*/*.c \
14251426
test/js-native-api/*/*.c \
14261427
test/js-native-api/*/*.h \
14271428
test/node-api/*/*.c \
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build/
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#include<node_api.h>
2+
#include<stdio.h>
3+
#include<stdlib.h>
4+
5+
#defineNODE_API_CALL(call) \
6+
do{\
7+
napi_status status = call; \
8+
if (status != napi_ok){\
9+
fprintf(stderr, #call " failed: %d\n", status); \
10+
abort(); \
11+
} \
12+
} while (0)
13+
14+
#defineABORT_IF_FALSE(condition) \
15+
if (!(condition)){\
16+
fprintf(stderr, #condition " failed\n"); \
17+
abort(); \
18+
}
19+
20+
staticnapi_valueRunner(napi_envenv,
21+
napi_callback_infoinfo,
22+
napi_property_attributesattr){
23+
napi_valueargv[2], undefined, js_array_length, start, end;
24+
size_targc=2;
25+
napi_valuetypeval_type=napi_undefined;
26+
boolis_array= false;
27+
uint32_tarray_length=0;
28+
napi_value*native_array;
29+
30+
// Validate params and retrieve start and end function.
31+
NODE_API_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
32+
ABORT_IF_FALSE(argc==2);
33+
NODE_API_CALL(napi_typeof(env, argv[0], &val_type));
34+
ABORT_IF_FALSE(val_type==napi_object);
35+
NODE_API_CALL(napi_is_array(env, argv[1], &is_array));
36+
ABORT_IF_FALSE(is_array);
37+
NODE_API_CALL(napi_get_array_length(env, argv[1], &array_length));
38+
NODE_API_CALL(napi_get_named_property(env, argv[0], "start", &start));
39+
NODE_API_CALL(napi_typeof(env, start, &val_type));
40+
ABORT_IF_FALSE(val_type==napi_function);
41+
NODE_API_CALL(napi_get_named_property(env, argv[0], "end", &end));
42+
NODE_API_CALL(napi_typeof(env, end, &val_type));
43+
ABORT_IF_FALSE(val_type==napi_function);
44+
45+
NODE_API_CALL(napi_get_undefined(env, &undefined));
46+
NODE_API_CALL(napi_create_uint32(env, array_length, &js_array_length));
47+
48+
// Copy objects into a native array.
49+
native_array=malloc(array_length*sizeof(*native_array));
50+
for (uint32_tidx=0; idx<array_length; idx++){
51+
NODE_API_CALL(napi_get_element(env, argv[1], idx, &native_array[idx]));
52+
}
53+
54+
constnapi_property_descriptordesc={
55+
"prop", NULL, NULL, NULL, NULL, js_array_length, attr, NULL};
56+
57+
// Start the benchmark.
58+
napi_call_function(env, argv[0], start, 0, NULL, NULL);
59+
60+
for (uint32_tidx=0; idx<array_length; idx++){
61+
NODE_API_CALL(napi_define_properties(env, native_array[idx], 1, &desc));
62+
}
63+
64+
// Conclude the benchmark.
65+
NODE_API_CALL(
66+
napi_call_function(env, argv[0], end, 1, &js_array_length, NULL));
67+
68+
free(native_array);
69+
70+
returnundefined;
71+
}
72+
73+
staticnapi_valueRunFastPath(napi_envenv, napi_callback_infoinfo){
74+
returnRunner(env, info, napi_writable | napi_enumerable | napi_configurable);
75+
}
76+
77+
staticnapi_valueRunSlowPath(napi_envenv, napi_callback_infoinfo){
78+
returnRunner(env, info, napi_writable | napi_enumerable);
79+
}
80+
81+
NAPI_MODULE_INIT(){
82+
napi_property_descriptorprops[] ={
83+
{"runFastPath",
84+
NULL,
85+
RunFastPath,
86+
NULL,
87+
NULL,
88+
NULL,
89+
napi_writable | napi_configurable | napi_enumerable,
90+
NULL},
91+
{"runSlowPath",
92+
NULL,
93+
RunSlowPath,
94+
NULL,
95+
NULL,
96+
NULL,
97+
napi_writable | napi_configurable | napi_enumerable,
98+
NULL},
99+
};
100+
101+
NODE_API_CALL(napi_define_properties(
102+
env, exports, sizeof(props) / sizeof(*props), props));
103+
returnexports;
104+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.c' ]
6+
}
7+
]
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
constcommon=require('../../common.js');
4+
5+
constbinding=require(`./build/${common.buildType}/binding`);
6+
7+
constbench=common.createBenchmark(main,{
8+
n: [5e6],
9+
implem: ['runFastPath','runSlowPath'],
10+
});
11+
12+
functionmain({ n, implem }){
13+
constobjs=Array(n).fill(null).map((item)=>newObject());
14+
binding[implem](bench,objs);
15+
}

‎src/js_native_api_v8.cc‎

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,16 +1369,27 @@ napi_define_properties(napi_env env,
13691369
}
13701370
} else{
13711371
v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(p->value);
1372+
bool defined_successfully = false;
1373+
1374+
if ((p->attributes & napi_enumerable) &&
1375+
(p->attributes & napi_writable) &&
1376+
(p->attributes & napi_configurable)){
1377+
// Use a fast path for this type of data property.
1378+
auto define_maybe =
1379+
obj->CreateDataProperty(context, property_name, value);
1380+
defined_successfully = define_maybe.FromMaybe(false);
1381+
} else{
1382+
v8::PropertyDescriptor descriptor(value,
1383+
(p->attributes & napi_writable) != 0);
1384+
descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1385+
descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1386+
1387+
auto define_maybe =
1388+
obj->DefineProperty(context, property_name, descriptor);
1389+
defined_successfully = define_maybe.FromMaybe(false);
1390+
}
13721391

1373-
v8::PropertyDescriptor descriptor(value,
1374-
(p->attributes & napi_writable) != 0);
1375-
descriptor.set_enumerable((p->attributes & napi_enumerable) != 0);
1376-
descriptor.set_configurable((p->attributes & napi_configurable) != 0);
1377-
1378-
auto define_maybe =
1379-
obj->DefineProperty(context, property_name, descriptor);
1380-
1381-
if (!define_maybe.FromMaybe(false)){
1392+
if (!defined_successfully){
13821393
returnnapi_set_last_error(env, napi_invalid_arg);
13831394
}
13841395
}

0 commit comments

Comments
(0)