From 42063cf38503117efae836f3a43aba4dcb5e1ddf Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 1 May 2019 23:40:02 +0200 Subject: [PATCH] n-api: make napi_get_property_names return strings The documentation says that this method returns an array of strings. Currently, it does not do so for indices. Resolve that by telling V8 explicitly to convert to string. Fixes: https://github.com/nodejs/node/issues/27496 --- src/js_native_api_v8.cc | 9 +++++++- test/js-native-api/test_object/test.js | 23 ++++++++++++++++++++ test/js-native-api/test_object/test_object.c | 20 +++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index e05163c32f82..befef0af6585 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -870,7 +870,14 @@ napi_status napi_get_property_names(napi_env env, v8::Local obj; CHECK_TO_OBJECT(env, context, obj, object); - auto maybe_propertynames = obj->GetPropertyNames(context); + v8::MaybeLocal maybe_propertynames = obj->GetPropertyNames( + context, + v8::KeyCollectionMode::kIncludePrototypes, + static_cast( + v8::PropertyFilter::ONLY_ENUMERABLE | + v8::PropertyFilter::SKIP_SYMBOLS), + v8::IndexFilter::kIncludeIndices, + v8::KeyConversionMode::kConvertToString); CHECK_MAYBE_EMPTY(env, maybe_propertynames, napi_generic_failure); diff --git a/test/js-native-api/test_object/test.js b/test/js-native-api/test_object/test.js index 3047b1f58dc6..d6e15dcfcef0 100644 --- a/test/js-native-api/test_object/test.js +++ b/test/js-native-api/test_object/test.js @@ -202,3 +202,26 @@ assert.strictEqual(newObject.test_string, 'test string'); assert.strictEqual(test_object.Delete(obj, 'foo'), true); assert.strictEqual(obj.foo, 'baz'); } + +{ + // Verify that napi_get_property_names gets the right set of property names, + // i.e.: includes prototypes, only enumerable properties, skips symbols, + // and includes indices and converts them to strings. + + const object = Object.create({ + inherited: 1 + }); + + object.normal = 2; + object[Symbol('foo')] = 3; + Object.defineProperty(object, 'unenumerable', { + value: 4, + enumerable: false, + writable: true, + configurable: true + }); + object[5] = 5; + + assert.deepStrictEqual(test_object.GetPropertyNames(object), + ['5', 'normal', 'inherited']); +} diff --git a/test/js-native-api/test_object/test_object.c b/test/js-native-api/test_object/test_object.c index b6dbbd4dd33b..30768bdc8126 100644 --- a/test/js-native-api/test_object/test_object.c +++ b/test/js-native-api/test_object/test_object.c @@ -63,6 +63,25 @@ static napi_value GetNamed(napi_env env, napi_callback_info info) { return output; } +static napi_value GetPropertyNames(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments"); + + napi_valuetype value_type0; + NAPI_CALL(env, napi_typeof(env, args[0], &value_type0)); + + NAPI_ASSERT(env, value_type0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_value output; + NAPI_CALL(env, napi_get_property_names(env, args[0], &output)); + + return output; +} + static napi_value Set(napi_env env, napi_callback_info info) { size_t argc = 3; napi_value args[3]; @@ -325,6 +344,7 @@ napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Get", Get), DECLARE_NAPI_PROPERTY("GetNamed", GetNamed), + DECLARE_NAPI_PROPERTY("GetPropertyNames", GetPropertyNames), DECLARE_NAPI_PROPERTY("Set", Set), DECLARE_NAPI_PROPERTY("SetNamed", SetNamed), DECLARE_NAPI_PROPERTY("Has", Has),