Skip to content

Commit 0911813

Browse files
himself65MylesBorins
authored andcommitted
n-api: add napi_get_all_property_names
Co-Authored-By: Gabriel Schulhof <[email protected]> PR-URL: #30006 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]> Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 7a5d4fa commit 0911813

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
@@ -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

+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
@@ -898,26 +898,92 @@ napi_status napi_define_class(napi_env env,
898898
napi_status napi_get_property_names(napi_env env,
899899
napi_value object,
900900
napi_value* result) {
901+
return napi_get_all_property_names(
902+
env,
903+
object,
904+
napi_key_include_prototypes,
905+
static_cast<napi_key_filter>(napi_key_enumerable |
906+
napi_key_skip_symbols),
907+
napi_key_numbers_to_strings,
908+
result);
909+
}
910+
911+
napi_status napi_get_all_property_names(napi_env env,
912+
napi_value object,
913+
napi_key_collection_mode key_mode,
914+
napi_key_filter key_filter,
915+
napi_key_conversion key_conversion,
916+
napi_value* result) {
901917
NAPI_PREAMBLE(env);
902918
CHECK_ARG(env, result);
903919

904920
v8::Local<v8::Context> context = env->context();
905921
v8::Local<v8::Object> obj;
906922
CHECK_TO_OBJECT(env, context, obj, object);
907923

908-
v8::MaybeLocal<v8::Array> maybe_propertynames = obj->GetPropertyNames(
909-
context,
910-
v8::KeyCollectionMode::kIncludePrototypes,
911-
static_cast<v8::PropertyFilter>(
912-
v8::PropertyFilter::ONLY_ENUMERABLE |
913-
v8::PropertyFilter::SKIP_SYMBOLS),
914-
v8::IndexFilter::kIncludeIndices,
915-
v8::KeyConversionMode::kConvertToString);
924+
v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES;
925+
if (key_filter & napi_key_writable) {
926+
filter =
927+
static_cast<v8::PropertyFilter>(filter |
928+
v8::PropertyFilter::ONLY_WRITABLE);
929+
}
930+
if (key_filter & napi_key_enumerable) {
931+
filter =
932+
static_cast<v8::PropertyFilter>(filter |
933+
v8::PropertyFilter::ONLY_ENUMERABLE);
934+
}
935+
if (key_filter & napi_key_configurable) {
936+
filter =
937+
static_cast<v8::PropertyFilter>(filter |
938+
v8::PropertyFilter::ONLY_WRITABLE);
939+
}
940+
if (key_filter & napi_key_skip_strings) {
941+
filter =
942+
static_cast<v8::PropertyFilter>(filter |
943+
v8::PropertyFilter::SKIP_STRINGS);
944+
}
945+
if (key_filter & napi_key_skip_symbols) {
946+
filter =
947+
static_cast<v8::PropertyFilter>(filter |
948+
v8::PropertyFilter::SKIP_SYMBOLS);
949+
}
950+
v8::KeyCollectionMode collection_mode;
951+
v8::KeyConversionMode conversion_mode;
952+
953+
switch (key_mode) {
954+
case napi_key_include_prototypes:
955+
collection_mode = v8::KeyCollectionMode::kIncludePrototypes;
956+
break;
957+
case napi_key_own_only:
958+
collection_mode = v8::KeyCollectionMode::kOwnOnly;
959+
break;
960+
default:
961+
return napi_set_last_error(env, napi_invalid_arg);
962+
}
916963

917-
CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure);
964+
switch (key_conversion) {
965+
case napi_key_keep_numbers:
966+
conversion_mode = v8::KeyConversionMode::kKeepNumbers;
967+
break;
968+
case napi_key_numbers_to_strings:
969+
conversion_mode = v8::KeyConversionMode::kConvertToString;
970+
break;
971+
default:
972+
return napi_set_last_error(env, napi_invalid_arg);
973+
}
918974

919-
*result = v8impl::JsValueFromV8LocalValue(
920-
maybe_propertynames.ToLocalChecked());
975+
v8::MaybeLocal<v8::Array> maybe_all_propertynames =
976+
obj->GetPropertyNames(context,
977+
collection_mode,
978+
filter,
979+
v8::IndexFilter::kIncludeIndices,
980+
conversion_mode);
981+
982+
CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
983+
env, maybe_all_propertynames, napi_generic_failure);
984+
985+
*result =
986+
v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
921987
return GET_RETURN_STATUS(env);
922988
}
923989

src/js_native_api_v8.h

+11
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ napi_status napi_set_last_error(napi_env env, napi_status error_code,
165165
} \
166166
} while (0)
167167

168+
#define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \
169+
do { \
170+
if (!(condition)) { \
171+
return napi_set_last_error( \
172+
(env), try_catch.HasCaught() ? napi_pending_exception : (status)); \
173+
} \
174+
} while (0)
175+
176+
#define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \
177+
RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status))
178+
168179
namespace v8impl {
169180

170181
//=== 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)