Skip to content

Commit 96cb864

Browse files
himself65Gabriel Schulhof
authored andcommitted
n-api: add napi_get_all_property_names
Co-Authored-By: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: nodejs#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 3cb02e5 commit 96cb864

7 files changed

Lines changed: 245 additions & 12 deletions

File tree

doc/api/n-api.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,66 @@ However, for better performance, it's better for the caller to make sure that
16601660
the `napi_value` in question is of the JavaScript type expected by the API.
16611661

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

16651725
```C
@@ -3532,6 +3592,37 @@ This API returns the names of the enumerable properties of `object` as an array
35323592
of strings. The properties of `object` whose key is a symbol will not be
35333593
included.
35343594

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

src/js_native_api.h

Lines changed: 9 additions & 0 deletions
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

Lines changed: 21 additions & 0 deletions
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

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -933,26 +933,92 @@ napi_status napi_define_class(napi_env env,
933933
napi_status napi_get_property_names(napi_env env,
934934
napi_value object,
935935
napi_value* result) {
936+
return napi_get_all_property_names(
937+
env,
938+
object,
939+
napi_key_include_prototypes,
940+
static_cast<napi_key_filter>(napi_key_enumerable |
941+
napi_key_skip_symbols),
942+
napi_key_numbers_to_strings,
943+
result);
944+
}
945+
946+
napi_status napi_get_all_property_names(napi_env env,
947+
napi_value object,
948+
napi_key_collection_mode key_mode,
949+
napi_key_filter key_filter,
950+
napi_key_conversion key_conversion,
951+
napi_value* result) {
936952
NAPI_PREAMBLE(env);
937953
CHECK_ARG(env, result);
938954

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

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

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

954-
*result = v8impl::JsValueFromV8LocalValue(
955-
maybe_propertynames.ToLocalChecked());
1010+
v8::MaybeLocal<v8::Array> maybe_all_propertynames =
1011+
obj->GetPropertyNames(context,
1012+
collection_mode,
1013+
filter,
1014+
v8::IndexFilter::kIncludeIndices,
1015+
conversion_mode);
1016+
1017+
CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
1018+
env, maybe_all_propertynames, napi_generic_failure);
1019+
1020+
*result =
1021+
v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
9561022
return GET_RETURN_STATUS(env);
9571023
}
9581024

src/js_native_api_v8.h

Lines changed: 11 additions & 0 deletions
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

Lines changed: 6 additions & 1 deletion
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

Lines changed: 30 additions & 0 deletions
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>
@@ -83,6 +85,33 @@ static napi_value GetPropertyNames(napi_env env, napi_callback_info info) {
8385
return output;
8486
}
8587

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

0 commit comments

Comments
 (0)