Skip to content

Commit ecbb331

Browse files
himself65BethGriggs
authored andcommittedMar 2, 2020
n-api: add napi_get_all_property_names
Co-Authored-By: Gabriel Schulhof <gabriel.schulhof@intel.com> PR-URL: #30006 Backport-PR-URL: #31384 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 9bd1317 commit ecbb331

File tree

6 files changed

+243
-4
lines changed

6 files changed

+243
-4
lines changed
 

‎doc/api/n-api.md

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

13741374
### Enum types
1375+
#### napi_key_collection_mode
1376+
<!-- YAML
1377+
added: REPLACEME
1378+
-->
1379+
1380+
> Stability: 1 - Experimental
1381+
1382+
```C
1383+
typedef enum {
1384+
napi_key_include_prototypes,
1385+
napi_key_own_only
1386+
} napi_key_collection_mode;
1387+
```
1388+
1389+
Describes the `Keys/Properties` filter enums:
1390+
1391+
`napi_key_collection_mode` limits the range of collected properties.
1392+
1393+
`napi_key_own_only` limits the collected properties to the given
1394+
object only. `napi_key_include_prototypes` will include all keys
1395+
of the objects's prototype chain as well.
1396+
1397+
#### napi_key_filter
1398+
<!-- YAML
1399+
added: REPLACEME
1400+
-->
1401+
1402+
> Stability: 1 - Experimental
1403+
1404+
```C
1405+
typedef enum {
1406+
napi_key_all_properties = 0,
1407+
napi_key_writable = 1,
1408+
napi_key_enumerable = 1 << 1,
1409+
napi_key_configurable = 1 << 2,
1410+
napi_key_skip_strings = 1 << 3,
1411+
napi_key_skip_symbols = 1 << 4
1412+
} napi_key_filter;
1413+
```
1414+
1415+
Property filter bits. They can be or'ed to build a composite filter.
1416+
1417+
#### napi_key_conversion
1418+
<!-- YAML
1419+
added: REPLACEME
1420+
-->
1421+
1422+
> Stability: 1 - Experimental
1423+
1424+
```C
1425+
typedef enum {
1426+
napi_key_keep_numbers,
1427+
napi_key_numbers_to_strings
1428+
} napi_key_conversion;
1429+
```
1430+
1431+
`napi_key_numbers_to_strings` will convert integer indices to
1432+
strings. `napi_key_keep_numbers` will return numbers for integer
1433+
indices.
1434+
13751435
#### napi_valuetype
13761436
```C
13771437
typedef enum {
@@ -3126,6 +3186,37 @@ This API returns the names of the enumerable properties of `object` as an array
31263186
of strings. The properties of `object` whose key is a symbol will not be
31273187
included.
31283188

3189+
#### napi_get_all_property_names
3190+
<!-- YAML
3191+
added: REPLACEME
3192+
-->
3193+
3194+
> Stability: 1 - Experimental
3195+
3196+
```C
3197+
napi_get_all_property_names(napi_env env,
3198+
napi_value object,
3199+
napi_key_collection_mode key_mode,
3200+
napi_key_filter key_filter,
3201+
napi_key_conversion key_conversion,
3202+
napi_value* result);
3203+
```
3204+
3205+
* `[in] env`: The environment that the N-API call is invoked under.
3206+
* `[in] object`: The object from which to retrieve the properties.
3207+
* `[in] key_mode`: Whether to retrieve prototype properties as well.
3208+
* `[in] key_filter`: Which properties to retrieve
3209+
(enumerable/readable/writable).
3210+
* `[in] key_conversion`: Whether to convert numbered property keys to strings.
3211+
* `[out] result`: A `napi_value` representing an array of JavaScript values
3212+
that represent the property names of the object. [`napi_get_array_length`][] and
3213+
[`napi_get_element`][] can be used to iterate over `result`.
3214+
3215+
Returns `napi_ok` if the API succeeded.
3216+
3217+
This API returns an array containing the names of the available properties
3218+
of this object.
3219+
31293220
#### napi_set_property
31303221
<!-- YAML
31313222
added: v8.0.0

‎src/node_api.cc

+88-4
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,17 @@ struct napi_env__ {
192192
(out) = v8::type::New((buffer), (byte_offset), (length)); \
193193
} while (0)
194194

195+
#define RETURN_STATUS_IF_FALSE_WITH_PREAMBLE(env, condition, status) \
196+
do { \
197+
if (!(condition)) { \
198+
return napi_set_last_error( \
199+
(env), try_catch.HasCaught() ? napi_pending_exception : (status)); \
200+
} \
201+
} while (0)
202+
203+
#define CHECK_MAYBE_EMPTY_WITH_PREAMBLE(env, maybe, status) \
204+
RETURN_STATUS_IF_FALSE_WITH_PREAMBLE((env), !((maybe).IsEmpty()), (status))
205+
195206
namespace {
196207
namespace v8impl {
197208

@@ -1604,19 +1615,92 @@ napi_status napi_define_class(napi_env env,
16041615
napi_status napi_get_property_names(napi_env env,
16051616
napi_value object,
16061617
napi_value* result) {
1618+
return napi_get_all_property_names(
1619+
env,
1620+
object,
1621+
napi_key_include_prototypes,
1622+
static_cast<napi_key_filter>(napi_key_enumerable |
1623+
napi_key_skip_symbols),
1624+
napi_key_numbers_to_strings,
1625+
result);
1626+
}
1627+
1628+
napi_status napi_get_all_property_names(napi_env env,
1629+
napi_value object,
1630+
napi_key_collection_mode key_mode,
1631+
napi_key_filter key_filter,
1632+
napi_key_conversion key_conversion,
1633+
napi_value* result) {
16071634
NAPI_PREAMBLE(env);
16081635
CHECK_ARG(env, result);
16091636

16101637
v8::Local<v8::Context> context = env->context();
16111638
v8::Local<v8::Object> obj;
16121639
CHECK_TO_OBJECT(env, context, obj, object);
16131640

1614-
auto maybe_propertynames = obj->GetPropertyNames(context);
1641+
v8::PropertyFilter filter = v8::PropertyFilter::ALL_PROPERTIES;
1642+
if (key_filter & napi_key_writable) {
1643+
filter =
1644+
static_cast<v8::PropertyFilter>(filter |
1645+
v8::PropertyFilter::ONLY_WRITABLE);
1646+
}
1647+
if (key_filter & napi_key_enumerable) {
1648+
filter =
1649+
static_cast<v8::PropertyFilter>(filter |
1650+
v8::PropertyFilter::ONLY_ENUMERABLE);
1651+
}
1652+
if (key_filter & napi_key_configurable) {
1653+
filter =
1654+
static_cast<v8::PropertyFilter>(filter |
1655+
v8::PropertyFilter::ONLY_WRITABLE);
1656+
}
1657+
if (key_filter & napi_key_skip_strings) {
1658+
filter =
1659+
static_cast<v8::PropertyFilter>(filter |
1660+
v8::PropertyFilter::SKIP_STRINGS);
1661+
}
1662+
if (key_filter & napi_key_skip_symbols) {
1663+
filter =
1664+
static_cast<v8::PropertyFilter>(filter |
1665+
v8::PropertyFilter::SKIP_SYMBOLS);
1666+
}
1667+
v8::KeyCollectionMode collection_mode;
1668+
v8::KeyConversionMode conversion_mode;
1669+
1670+
switch (key_mode) {
1671+
case napi_key_include_prototypes:
1672+
collection_mode = v8::KeyCollectionMode::kIncludePrototypes;
1673+
break;
1674+
case napi_key_own_only:
1675+
collection_mode = v8::KeyCollectionMode::kOwnOnly;
1676+
break;
1677+
default:
1678+
return napi_set_last_error(env, napi_invalid_arg);
1679+
}
16151680

1616-
CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure);
1681+
switch (key_conversion) {
1682+
case napi_key_keep_numbers:
1683+
conversion_mode = v8::KeyConversionMode::kKeepNumbers;
1684+
break;
1685+
case napi_key_numbers_to_strings:
1686+
conversion_mode = v8::KeyConversionMode::kConvertToString;
1687+
break;
1688+
default:
1689+
return napi_set_last_error(env, napi_invalid_arg);
1690+
}
16171691

1618-
*result = v8impl::JsValueFromV8LocalValue(
1619-
maybe_propertynames.ToLocalChecked());
1692+
v8::MaybeLocal<v8::Array> maybe_all_propertynames =
1693+
obj->GetPropertyNames(context,
1694+
collection_mode,
1695+
filter,
1696+
v8::IndexFilter::kIncludeIndices,
1697+
conversion_mode);
1698+
1699+
CHECK_MAYBE_EMPTY_WITH_PREAMBLE(
1700+
env, maybe_all_propertynames, napi_generic_failure);
1701+
1702+
*result =
1703+
v8impl::JsValueFromV8LocalValue(maybe_all_propertynames.ToLocalChecked());
16201704
return GET_RETURN_STATUS(env);
16211705
}
16221706

‎src/node_api.h

+10
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,16 @@ NAPI_EXTERN napi_status napi_set_instance_data(napi_env env,
735735

736736
NAPI_EXTERN napi_status napi_get_instance_data(napi_env env,
737737
void** data);
738+
739+
// Object
740+
NAPI_EXTERN napi_status
741+
napi_get_all_property_names(napi_env env,
742+
napi_value object,
743+
napi_key_collection_mode key_mode,
744+
napi_key_filter key_filter,
745+
napi_key_conversion key_conversion,
746+
napi_value* result);
747+
738748
#endif // NAPI_EXPERIMENTAL
739749

740750
EXTERN_C_END

‎src/node_api_types.h

+21
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,25 @@ typedef struct {
147147
const char* release;
148148
} napi_node_version;
149149

150+
#ifdef NAPI_EXPERIMENTAL
151+
typedef enum {
152+
napi_key_include_prototypes,
153+
napi_key_own_only
154+
} napi_key_collection_mode;
155+
156+
typedef enum {
157+
napi_key_all_properties = 0,
158+
napi_key_writable = 1,
159+
napi_key_enumerable = 1 << 1,
160+
napi_key_configurable = 1 << 2,
161+
napi_key_skip_strings = 1 << 3,
162+
napi_key_skip_symbols = 1 << 4
163+
} napi_key_filter;
164+
165+
typedef enum {
166+
napi_key_keep_numbers,
167+
napi_key_numbers_to_strings
168+
} napi_key_conversion;
169+
#endif
170+
150171
#endif // SRC_NODE_API_TYPES_H_

‎test/addons-napi/test_object/test.js

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const assert = require('assert');
55
// Testing api calls for objects
66
const test_object = require(`./build/${common.buildType}/test_object`);
77

8+
const fooSymbol = Symbol('foo');
89

910
const object = {
1011
hello: 'world',
@@ -16,6 +17,8 @@ const object = {
1617
}
1718
};
1819

20+
object[fooSymbol] = 3;
21+
1922
assert.strictEqual(test_object.Get(object, 'hello'), 'world');
2023
assert.deepStrictEqual(test_object.Get(object, 'array'),
2124
[ 1, 94, 'str', 12.321, { test: 'obj in arr' } ]);
@@ -25,6 +28,7 @@ assert.deepStrictEqual(test_object.Get(object, 'newObject'),
2528
assert(test_object.Has(object, 'hello'));
2629
assert(test_object.Has(object, 'array'));
2730
assert(test_object.Has(object, 'newObject'));
31+
assert.deepStrictEqual(test_object.GetSymbolNames(object), [fooSymbol]);
2832

2933
const newObject = test_object.New();
3034
assert(test_object.Has(newObject, 'test_number'));

‎test/addons-napi/test_object/test_object.c

+29
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#define NAPI_EXPERIMENTAL
12
#include <node_api.h>
23
#include "../common.h"
34
#include <string.h>
@@ -193,6 +194,33 @@ static napi_value Inflate(napi_env env, napi_callback_info info) {
193194
return obj;
194195
}
195196

197+
static napi_value GetSymbolNames(napi_env env, napi_callback_info info) {
198+
size_t argc = 1;
199+
napi_value args[1];
200+
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL));
201+
202+
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
203+
204+
napi_valuetype value_type0;
205+
NAPI_CALL(env, napi_typeof(env, args[0], &value_type0));
206+
207+
NAPI_ASSERT(env,
208+
value_type0 == napi_object,
209+
"Wrong type of arguments. Expects an object as first argument.");
210+
211+
napi_value output;
212+
NAPI_CALL(env,
213+
napi_get_all_property_names(
214+
env,
215+
args[0],
216+
napi_key_include_prototypes,
217+
napi_key_skip_strings,
218+
napi_key_numbers_to_strings,
219+
&output));
220+
221+
return output;
222+
}
223+
196224
static napi_value Wrap(napi_env env, napi_callback_info info) {
197225
size_t argc = 1;
198226
napi_value arg;
@@ -220,6 +248,7 @@ static napi_value Init(napi_env env, napi_value exports) {
220248
napi_property_descriptor descriptors[] = {
221249
DECLARE_NAPI_PROPERTY("Get", Get),
222250
DECLARE_NAPI_PROPERTY("Set", Set),
251+
DECLARE_NAPI_PROPERTY("GetSymbolNames", GetSymbolNames),
223252
DECLARE_NAPI_PROPERTY("Has", Has),
224253
DECLARE_NAPI_PROPERTY("HasOwn", HasOwn),
225254
DECLARE_NAPI_PROPERTY("Delete", Delete),

0 commit comments

Comments
 (0)
Please sign in to comment.