Skip to content

Commit f56c4dd

Browse files
himself65targos
authored andcommitted
n-api: add napi_get_all_property_names
Co-Authored-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Backport-PR-URL: #32482 PR-URL: #30006 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 907252d commit f56c4dd

File tree

7 files changed

+245
-12
lines changed

7 files changed

+245
-12
lines changed

doc/api/n-api.md

+91
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,66 @@ However, for better performance, it's better for the caller to make sure that
16591659
the `napi_value` in question is of the JavaScript type expected by the API.
16601660

16611661
### Enum types
1662+
#### napi_key_collection_mode
1663+
<!-- YAML
1664+
added: REPLACEME
1665+
-->
1666+
1667+
> Stability: 1 - Experimental
1668+
1669+
```C
1670+
typedef enum {
1671+
napi_key_include_prototypes,
1672+
napi_key_own_only
1673+
} napi_key_collection_mode;
1674+
```
1675+
1676+
Describes the `Keys/Properties` filter enums:
1677+
1678+
`napi_key_collection_mode` limits the range of collected properties.
1679+
1680+
`napi_key_own_only` limits the collected properties to the given
1681+
object only. `napi_key_include_prototypes` will include all keys
1682+
of the objects's prototype chain as well.
1683+
1684+
#### napi_key_filter
1685+
<!-- YAML
1686+
added: REPLACEME
1687+
-->
1688+
1689+
> Stability: 1 - Experimental
1690+
1691+
```C
1692+
typedef enum {
1693+
napi_key_all_properties = 0,
1694+
napi_key_writable = 1,
1695+
napi_key_enumerable = 1 << 1,
1696+
napi_key_configurable = 1 << 2,
1697+
napi_key_skip_strings = 1 << 3,
1698+
napi_key_skip_symbols = 1 << 4
1699+
} napi_key_filter;
1700+
```
1701+
1702+
Property filter bits. They can be or'ed to build a composite filter.
1703+
1704+
#### napi_key_conversion
1705+
<!-- YAML
1706+
added: REPLACEME
1707+
-->
1708+
1709+
> Stability: 1 - Experimental
1710+
1711+
```C
1712+
typedef enum {
1713+
napi_key_keep_numbers,
1714+
napi_key_numbers_to_strings
1715+
} napi_key_conversion;
1716+
```
1717+
1718+
`napi_key_numbers_to_strings` will convert integer indices to
1719+
strings. `napi_key_keep_numbers` will return numbers for integer
1720+
indices.
1721+
16621722
#### napi_valuetype
16631723

16641724
```C
@@ -3535,6 +3595,37 @@ This API returns the names of the enumerable properties of `object` as an array
35353595
of strings. The properties of `object` whose key is a symbol will not be
35363596
included.
35373597

3598+
#### napi_get_all_property_names
3599+
<!-- YAML
3600+
added: REPLACEME
3601+
-->
3602+
3603+
> Stability: 1 - Experimental
3604+
3605+
```C
3606+
napi_get_all_property_names(napi_env env,
3607+
napi_value object,
3608+
napi_key_collection_mode key_mode,
3609+
napi_key_filter key_filter,
3610+
napi_key_conversion key_conversion,
3611+
napi_value* result);
3612+
```
3613+
3614+
* `[in] env`: The environment that the N-API call is invoked under.
3615+
* `[in] object`: The object from which to retrieve the properties.
3616+
* `[in] key_mode`: Whether to retrieve prototype properties as well.
3617+
* `[in] key_filter`: Which properties to retrieve
3618+
(enumerable/readable/writable).
3619+
* `[in] key_conversion`: Whether to convert numbered property keys to strings.
3620+
* `[out] result`: A `napi_value` representing an array of JavaScript values
3621+
that represent the property names of the object. [`napi_get_array_length`][] and
3622+
[`napi_get_element`][] can be used to iterate over `result`.
3623+
3624+
Returns `napi_ok` if the API succeeded.
3625+
3626+
This API returns an array containing the names of the available properties
3627+
of this object.
3628+
35383629
#### napi_set_property
35393630
<!-- YAML
35403631
added: v8.0.0

src/js_native_api.h

+9
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,15 @@ NAPI_EXTERN napi_status napi_get_value_bigint_words(napi_env env,
506506
size_t* word_count,
507507
uint64_t* words);
508508

509+
// Object
510+
NAPI_EXTERN napi_status
511+
napi_get_all_property_names(napi_env env,
512+
napi_value object,
513+
napi_key_collection_mode key_mode,
514+
napi_key_filter key_filter,
515+
napi_key_conversion key_conversion,
516+
napi_value* result);
517+
509518
// Instance data
510519
NAPI_EXTERN napi_status napi_set_instance_data(napi_env env,
511520
void* data,

src/js_native_api_types.h

+21
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,25 @@ typedef struct {
115115
napi_status error_code;
116116
} napi_extended_error_info;
117117

118+
#ifdef NAPI_EXPERIMENTAL
119+
typedef enum {
120+
napi_key_include_prototypes,
121+
napi_key_own_only
122+
} napi_key_collection_mode;
123+
124+
typedef enum {
125+
napi_key_all_properties = 0,
126+
napi_key_writable = 1,
127+
napi_key_enumerable = 1 << 1,
128+
napi_key_configurable = 1 << 2,
129+
napi_key_skip_strings = 1 << 3,
130+
napi_key_skip_symbols = 1 << 4
131+
} napi_key_filter;
132+
133+
typedef enum {
134+
napi_key_keep_numbers,
135+
napi_key_numbers_to_strings
136+
} napi_key_conversion;
137+
#endif
138+
118139
#endif // SRC_JS_NATIVE_API_TYPES_H_

src/js_native_api_v8.cc

+77-11
Original file line numberDiff line numberDiff line change
@@ -935,26 +935,92 @@ napi_status napi_define_class(napi_env env,
935935
napi_status napi_get_property_names(napi_env env,
936936
napi_value object,
937937
napi_value* result) {
938+
return napi_get_all_property_names(
939+
env,
940+
object,
941+
napi_key_include_prototypes,
942+
static_cast<napi_key_filter>(napi_key_enumerable |
943+
napi_key_skip_symbols),
944+
napi_key_numbers_to_strings,
945+
result);
946+
}
947+
948+
napi_status napi_get_all_property_names(napi_env env,
949+
napi_value object,
950+
napi_key_collection_mode key_mode,
951+
napi_key_filter key_filter,
952+
napi_key_conversion key_conversion,
953+
napi_value* result) {
938954
NAPI_PREAMBLE(env);
939955
CHECK_ARG(env, result);
940956

941957
v8::Local<v8::Context> context = env->context();
942958
v8::Local<v8::Object> obj;
943959
CHECK_TO_OBJECT(env, context, obj, object);
944960

945-
v8::MaybeLocal<v8::Array> maybe_propertynames = obj->GetPropertyNames(
946-
context,
947-
v8::KeyCollectionMode::kIncludePrototypes,
948-
static_cast<v8::PropertyFilter>(
949-
v8::PropertyFilter::ONLY_ENUMERABLE |
950-
v8::PropertyFilter::SKIP_SYMBOLS),
951-
v8::IndexFilter::kIncludeIndices,
952-
v8::KeyConversionMode::kConvertToString);
961+
v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES;
962+
if (key_filter & napi_key_writable) {
963+
filter =
964+
static_cast<v8::PropertyFilter>(filter |
965+
v8::PropertyFilter::ONLY_WRITABLE);
966+
}
967+
if (key_filter & napi_key_enumerable) {
968+
filter =
969+
static_cast<v8::PropertyFilter>(filter |
970+
v8::PropertyFilter::ONLY_ENUMERABLE);
971+
}
972+
if (key_filter & napi_key_configurable) {
973+
filter =
974+
static_cast<v8::PropertyFilter>(filter |
975+
v8::PropertyFilter::ONLY_WRITABLE);
976+
}
977+
if (key_filter & napi_key_skip_strings) {
978+
filter =
979+
static_cast<v8::PropertyFilter>(filter |
980+
v8::PropertyFilter::SKIP_STRINGS);
981+
}
982+
if (key_filter & napi_key_skip_symbols) {
983+
filter =
984+
static_cast<v8::PropertyFilter>(filter |
985+
v8::PropertyFilter::SKIP_SYMBOLS);
986+
}
987+
v8::KeyCollectionMode collection_mode;
988+
v8::KeyConversionMode conversion_mode;
989+
990+
switch (key_mode) {
991+
case napi_key_include_prototypes:
992+
collection_mode = v8::KeyCollectionMode::kIncludePrototypes;
993+
break;
994+
case napi_key_own_only:
995+
collection_mode = v8::KeyCollectionMode::kOwnOnly;
996+
break;
997+
default:
998+
return napi_set_last_error(env, napi_invalid_arg);
999+
}
9531000

954-
CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure);
1001+
switch (key_conversion) {
1002+
case napi_key_keep_numbers:
1003+
conversion_mode = v8::KeyConversionMode::kKeepNumbers;
1004+
break;
1005+
case napi_key_numbers_to_strings:
1006+
conversion_mode = v8::KeyConversionMode::kConvertToString;
1007+
break;
1008+
default:
1009+
return napi_set_last_error(env, napi_invalid_arg);
1010+
}
9551011

956-
*result = v8impl::JsValueFromV8LocalValue(
957-
maybe_propertynames.ToLocalChecked());
1012+
v8::MaybeLocal<v8::Array> maybe_all_propertynames =
1013+
obj->GetPropertyNames(context,
1014+
collection_mode,
1015+
filter,
1016+
v8::IndexFilter::kIncludeIndices,
1017+
conversion_mode);
1018+
1019+
CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
1020+
env, maybe_all_propertynames, napi_generic_failure);
1021+
1022+
*result =
1023+
v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
9581024
return GET_RETURN_STATUS(env);
9591025
}
9601026

src/js_native_api_v8.h

+11
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code,
199199
} \
200200
} while (0)
201201

202+
#define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \
203+
do { \
204+
if (!(condition)) { \
205+
return napi_set_last_error( \
206+
(env), try_catch.HasCaught() ? napi_pending_exception : (status)); \
207+
} \
208+
} while (0)
209+
210+
#define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \
211+
RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status))
212+
202213
namespace v8impl {
203214

204215
//=== Conversion between V8 Handles and napi_value ========================

test/js-native-api/test_object/test.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,10 @@ assert.strictEqual(newObject.test_string, 'test string');
212212
inherited: 1
213213
});
214214

215+
const fooSymbol = Symbol('foo');
216+
215217
object.normal = 2;
216-
object[Symbol('foo')] = 3;
218+
object[fooSymbol] = 3;
217219
Object.defineProperty(object, 'unenumerable', {
218220
value: 4,
219221
enumerable: false,
@@ -224,6 +226,9 @@ assert.strictEqual(newObject.test_string, 'test string');
224226

225227
assert.deepStrictEqual(test_object.GetPropertyNames(object),
226228
['5', 'normal', 'inherited']);
229+
230+
assert.deepStrictEqual(test_object.GetSymbolNames(object),
231+
[fooSymbol]);
227232
}
228233

229234
// Verify that passing NULL to napi_set_property() results in the correct

test/js-native-api/test_object/test_object.c

+30
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#define NAPI_EXPERIMENTAL
2+
13
#include <js_native_api.h>
24
#include "../common.h"
35
#include <string.h>
@@ -82,6 +84,33 @@ static napi_value GetPropertyNames(napi_env env, napi_callback_info info) {
8284
return output;
8385
}
8486

87+
static napi_value GetSymbolNames(napi_env env, napi_callback_info info) {
88+
size_t argc = 1;
89+
napi_value args[1];
90+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
91+
92+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
93+
94+
napi_valuetype value_type0;
95+
NAPI_CALL(env, napi_typeof(env, args[0], &value_type0));
96+
97+
NAPI_ASSERT(env,
98+
value_type0 == napi_object,
99+
"Wrong type of arguments. Expects an object as first argument.");
100+
101+
napi_value output;
102+
NAPI_CALL(env,
103+
napi_get_all_property_names(
104+
env,
105+
args[0],
106+
napi_key_include_prototypes,
107+
napi_key_skip_strings,
108+
napi_key_numbers_to_strings,
109+
&output));
110+
111+
return output;
112+
}
113+
85114
static napi_value Set(napi_env env, napi_callback_info info) {
86115
size_t argc = 3;
87116
napi_value args[3];
@@ -449,6 +478,7 @@ napi_value Init(napi_env env, napi_value exports) {
449478
DECLARE_NAPI_PROPERTY("Get", Get),
450479
DECLARE_NAPI_PROPERTY("GetNamed", GetNamed),
451480
DECLARE_NAPI_PROPERTY("GetPropertyNames", GetPropertyNames),
481+
DECLARE_NAPI_PROPERTY("GetSymbolNames", GetSymbolNames),
452482
DECLARE_NAPI_PROPERTY("Set", Set),
453483
DECLARE_NAPI_PROPERTY("SetNamed", SetNamed),
454484
DECLARE_NAPI_PROPERTY("Has", Has),

0 commit comments

Comments
 (0)