From 22b9a87845a17492a955ea2d3ab1a0558429b509 Mon Sep 17 00:00:00 2001 From: cjihrig <cjihrig@gmail.com> Date: Mon, 26 Jun 2017 14:31:55 -0400 Subject: [PATCH] n-api: add napi_delete_property() Fixes: https://github.com/nodejs/node/issues/13924 PR-URL: https://github.com/nodejs/node/pull/13934 Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jason Ginchereau <jasongin@microsoft.com> --- doc/api/n-api.md | 23 +++++++++++ src/node_api.cc | 22 +++++++++++ src/node_api.h | 4 ++ test/addons-napi/test_object/test.js | 44 ++++++++++++++++++++++ test/addons-napi/test_object/test_object.c | 26 +++++++++++++ 5 files changed, 119 insertions(+) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 5c6654f97cfdd0..831a02cf3dfa82 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -2276,6 +2276,28 @@ Returns `napi_ok` if the API succeeded. This API checks if the Object passed in has the named property. +#### *napi_delete_property* +<!-- YAML +added: REPLACEME +--> +```C +napi_status napi_delete_property(napi_env env, + napi_value object, + napi_value key, + bool* result); +``` + +- `[in] env`: The environment that the N-API call is invoked under. +- `[in] object`: The object to query. +- `[in] key`: The name of the property to delete. +- `[out] result`: Whether the property deletion succeeded or not. `result` can +optionally be ignored by passing `NULL`. + +Returns `napi_ok` if the API succeeded. + +This API attempts to delete the `key` own property from `object`. + + #### *napi_set_named_property* <!-- YAML added: v8.0.0 @@ -3074,6 +3096,7 @@ support it: [`napi_delete_async_work`]: #n_api_napi_delete_async_work [`napi_define_class`]: #n_api_napi_define_class [`napi_delete_element`]: #n_api_napi_delete_element +[`napi_delete_property`]: #n_api_napi_delete_property [`napi_delete_reference`]: #n_api_napi_delete_reference [`napi_escape_handle`]: #n_api_napi_escape_handle [`napi_get_array_length`]: #n_api_napi_get_array_length diff --git a/src/node_api.cc b/src/node_api.cc index e182459cb8c5f4..ef02bd47f6e549 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1005,6 +1005,28 @@ napi_status napi_get_property(napi_env env, return GET_RETURN_STATUS(env); } +napi_status napi_delete_property(napi_env env, + napi_value object, + napi_value key, + bool* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, key); + + v8::Isolate* isolate = env->isolate; + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + v8::Local<v8::Value> k = v8impl::V8LocalValueFromJsValue(key); + v8::Local<v8::Object> obj; + + CHECK_TO_OBJECT(env, context, obj, object); + v8::Maybe<bool> delete_maybe = obj->Delete(context, k); + CHECK_MAYBE_NOTHING(env, delete_maybe, napi_generic_failure); + + if (result != NULL) + *result = delete_maybe.FromMaybe(false); + + return GET_RETURN_STATUS(env); +} + napi_status napi_set_named_property(napi_env env, napi_value object, const char* utf8name, diff --git a/src/node_api.h b/src/node_api.h index 58222e74318fe2..503ea258881cad 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -226,6 +226,10 @@ NAPI_EXTERN napi_status napi_get_property(napi_env env, napi_value object, napi_value key, napi_value* result); +NAPI_EXTERN napi_status napi_delete_property(napi_env env, + napi_value object, + napi_value key, + bool* result); NAPI_EXTERN napi_status napi_set_named_property(napi_env env, napi_value object, const char* utf8name, diff --git a/test/addons-napi/test_object/test.js b/test/addons-napi/test_object/test.js index c9c549c827409f..fe6fb169b6c701 100644 --- a/test/addons-napi/test_object/test.js +++ b/test/addons-napi/test_object/test.js @@ -120,3 +120,47 @@ assert.strictEqual(newObject.test_string, 'test string'); assert(wrapper.protoA, true); assert(wrapper.protoB, true); } + +{ + // Verify that normal and nonexistent properties can be deleted. + const sym = Symbol(); + const obj = { foo: 'bar', [sym]: 'baz' }; + + assert.strictEqual('foo' in obj, true); + assert.strictEqual(sym in obj, true); + assert.strictEqual('does_not_exist' in obj, false); + assert.strictEqual(test_object.Delete(obj, 'foo'), true); + assert.strictEqual('foo' in obj, false); + assert.strictEqual(sym in obj, true); + assert.strictEqual('does_not_exist' in obj, false); + assert.strictEqual(test_object.Delete(obj, sym), true); + assert.strictEqual('foo' in obj, false); + assert.strictEqual(sym in obj, false); + assert.strictEqual('does_not_exist' in obj, false); +} + +{ + // Verify that non-configurable properties are not deleted. + const obj = {}; + + Object.defineProperty(obj, 'foo', { configurable: false }); + assert.strictEqual(test_object.Delete(obj, 'foo'), false); + assert.strictEqual('foo' in obj, true); +} + +{ + // Verify that prototype properties are not deleted. + function Foo() { + this.foo = 'bar'; + } + + Foo.prototype.foo = 'baz'; + + const obj = new Foo(); + + assert.strictEqual(obj.foo, 'bar'); + assert.strictEqual(test_object.Delete(obj, 'foo'), true); + assert.strictEqual(obj.foo, 'baz'); + assert.strictEqual(test_object.Delete(obj, 'foo'), true); + assert.strictEqual(obj.foo, 'baz'); +} diff --git a/test/addons-napi/test_object/test_object.c b/test/addons-napi/test_object/test_object.c index 383fe46342a340..ad24d61ca787e0 100644 --- a/test/addons-napi/test_object/test_object.c +++ b/test/addons-napi/test_object/test_object.c @@ -86,6 +86,31 @@ napi_value Has(napi_env env, napi_callback_info info) { return ret; } +napi_value Delete(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NAPI_ASSERT(env, argc == 2, "Wrong number of arguments"); + + napi_valuetype valuetype0; + NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0)); + NAPI_ASSERT(env, valuetype0 == napi_object, + "Wrong type of arguments. Expects an object as first argument."); + + napi_valuetype valuetype1; + NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1)); + NAPI_ASSERT(env, valuetype1 == napi_string || valuetype1 == napi_symbol, + "Wrong type of arguments. Expects a string or symbol as second."); + + bool result; + napi_value ret; + NAPI_CALL(env, napi_delete_property(env, args[0], args[1], &result)); + NAPI_CALL(env, napi_get_boolean(env, result, &ret)); + + return ret; +} + napi_value New(napi_env env, napi_callback_info info) { napi_value ret; NAPI_CALL(env, napi_create_object(env, &ret)); @@ -171,6 +196,7 @@ void Init(napi_env env, napi_value exports, napi_value module, void* priv) { DECLARE_NAPI_PROPERTY("Get", Get), DECLARE_NAPI_PROPERTY("Set", Set), DECLARE_NAPI_PROPERTY("Has", Has), + DECLARE_NAPI_PROPERTY("Delete", Delete), DECLARE_NAPI_PROPERTY("New", New), DECLARE_NAPI_PROPERTY("Inflate", Inflate), DECLARE_NAPI_PROPERTY("Wrap", Wrap),