diff --git a/doc/api/n-api.md b/doc/api/n-api.md index b2032f2c08f730..c3dd96cca36b97 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -5049,6 +5049,28 @@ added to it, as well as marking all existing properties as non-configurable. This is described in [Section 19.1.2.20](https://tc39.es/ecma262/#sec-object.seal) of the ECMA-262 specification. +#### `node_api_set_prototype` + + + +> Stability: 1 - Experimental + +```c +napi_status node_api_set_prototype(napi_env env, + napi_value object, + napi_value value); +``` + +* `[in] env`: The environment that the Node-API call is invoked under. +* `[in] object`: The object on which to set the prototype. +* `[in] value`: The prototype value. + +Returns `napi_ok` if the API succeeded. + +This API sets the prototype of the `Object` passed in. + ## Working with JavaScript functions Node-API provides a set of APIs that allow JavaScript code to diff --git a/src/js_native_api.h b/src/js_native_api.h index f979720b0caeab..0bc8f1810d2872 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -197,6 +197,12 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_coerce_to_string(napi_env env, napi_value* result); // Methods to work with Objects +#ifdef NAPI_EXPERIMENTAL +#define NODE_API_EXPERIMENTAL_HAS_SET_PROTOTYPE +NAPI_EXTERN napi_status NAPI_CDECL node_api_set_prototype(napi_env env, + napi_value object, + napi_value value); +#endif NAPI_EXTERN napi_status NAPI_CDECL napi_get_prototype(napi_env env, napi_value object, napi_value* result); diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index a5e30895da68b0..478bbd4eb03cf4 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -1567,6 +1567,26 @@ napi_status NAPI_CDECL napi_strict_equals(napi_env env, return GET_RETURN_STATUS(env); } +napi_status NAPI_CDECL node_api_set_prototype(napi_env env, + napi_value object, + napi_value value) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, value); + + v8::Local context = env->context(); + v8::Local obj; + + CHECK_TO_OBJECT(env, context, obj, object); + + v8::Local val = v8impl::V8LocalValueFromJsValue(value); + + v8::Maybe set_maybe = obj->SetPrototypeV2(context, val); + + RETURN_STATUS_IF_FALSE_WITH_PREAMBLE( + env, set_maybe.FromMaybe(false), napi_generic_failure); + return GET_RETURN_STATUS(env); +} + napi_status NAPI_CDECL napi_get_prototype(napi_env env, napi_value object, napi_value* result) { diff --git a/test/js-native-api/test_general/binding.gyp b/test/js-native-api/test_general/binding.gyp index 577a506f7fad73..cd261547de149f 100644 --- a/test/js-native-api/test_general/binding.gyp +++ b/test/js-native-api/test_general/binding.gyp @@ -4,7 +4,10 @@ "target_name": "test_general", "sources": [ "test_general.c" - ] + ], + "defines": [ + "NAPI_EXPERIMENTAL" + ], } ] } diff --git a/test/js-native-api/test_general/test.js b/test/js-native-api/test_general/test.js index 843c6aee3af47f..7bb40f7ae74eb8 100644 --- a/test/js-native-api/test_general/test.js +++ b/test/js-native-api/test_general/test.js @@ -18,12 +18,18 @@ class ExtendedClass extends BaseClass { const baseObject = new BaseClass(); const extendedObject = new ExtendedClass(); +const nullProtoObject = { __proto__: null }; // Test napi_strict_equals assert.ok(test_general.testStrictEquals(val1, val1)); assert.strictEqual(test_general.testStrictEquals(val1, val2), false); assert.ok(test_general.testStrictEquals(val2, val3)); +// Test napi_set_prototype +test_general.testSetPrototype(nullProtoObject, Object.prototype); +assert.strictEqual(Object.getPrototypeOf(nullProtoObject), + Object.prototype); + // Test napi_get_prototype assert.strictEqual(test_general.testGetPrototype(baseObject), Object.getPrototypeOf(baseObject)); diff --git a/test/js-native-api/test_general/test_general.c b/test/js-native-api/test_general/test_general.c index 0cd1c54ee142f1..6249c55a7cbd54 100644 --- a/test/js-native-api/test_general/test_general.c +++ b/test/js-native-api/test_general/test_general.c @@ -23,6 +23,16 @@ static napi_value testStrictEquals(napi_env env, napi_callback_info info) { return result; } +static napi_value testSetPrototype(napi_env env, napi_callback_info info) { + size_t argc = 2; + napi_value args[2]; + NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + + NODE_API_CALL(env, node_api_set_prototype(env, args[0], args[1])); + + return NULL; +} + static napi_value testGetPrototype(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1]; @@ -137,7 +147,7 @@ static napi_value testNapiTypeof(napi_env env, napi_callback_info info) { } static bool deref_item_called = false; -static void deref_item(napi_env env, void* data, void* hint) { +static void deref_item(node_api_nogc_env env, void* data, void* hint) { (void) hint; NODE_API_ASSERT_RETURN_VOID(env, data == &deref_item_called, @@ -156,7 +166,7 @@ static napi_value deref_item_was_called(napi_env env, napi_callback_info info) { static napi_value wrap_first_arg(napi_env env, napi_callback_info info, - napi_finalize finalizer, + node_api_basic_finalize finalizer, void* data) { size_t argc = 1; napi_value to_wrap; @@ -195,7 +205,7 @@ static napi_value remove_wrap(napi_env env, napi_callback_info info) { } static bool finalize_called = false; -static void test_finalize(napi_env env, void* data, void* hint) { +static void test_finalize(node_api_nogc_env env, void* data, void* hint) { finalize_called = true; } @@ -242,6 +252,15 @@ static void finalizer_only_callback(napi_env env, void* data, void* hint) { NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, js_cb_ref)); } +static void schedule_finalizer_only_callback(node_api_nogc_env env, + void* data, + void* hint) { + NODE_API_CALL_RETURN_VOID( + (napi_env)env, + node_api_post_finalizer( + (napi_env)env, finalizer_only_callback, data, NULL)); +} + static napi_value add_finalizer_only(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value argv[2]; @@ -250,8 +269,12 @@ static napi_value add_finalizer_only(napi_env env, napi_callback_info info) { NODE_API_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); NODE_API_CALL(env, napi_create_reference(env, argv[1], 1, &js_cb_ref)); NODE_API_CALL(env, - napi_add_finalizer( - env, argv[0], js_cb_ref, finalizer_only_callback, NULL, NULL)); + napi_add_finalizer(env, + argv[0], + js_cb_ref, + schedule_finalizer_only_callback, + NULL, + NULL)); return NULL; } @@ -262,7 +285,9 @@ static const char* env_cleanup_finalizer_messages[] = { "second wrap" }; -static void cleanup_env_finalizer(napi_env env, void* data, void* hint) { +static void cleanup_env_finalizer(node_api_nogc_env env, + void* data, + void* hint) { (void) env; (void) hint; @@ -286,26 +311,27 @@ static napi_value env_cleanup_wrap(napi_env env, napi_callback_info info) { EXTERN_C_START napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { - DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals), - DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype), - DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion), - DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun), - DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf), - DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined), - DECLARE_NODE_API_PROPERTY("getNull", getNull), - DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError), - DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup), - DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof), - DECLARE_NODE_API_PROPERTY("wrap", wrap), - DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap), - DECLARE_NODE_API_PROPERTY("unwrap", unwrap), - DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap), - DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only), - DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap), - DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called), - DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called), - DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory", testAdjustExternalMemory) - }; + DECLARE_NODE_API_PROPERTY("testStrictEquals", testStrictEquals), + DECLARE_NODE_API_PROPERTY("testSetPrototype", testSetPrototype), + DECLARE_NODE_API_PROPERTY("testGetPrototype", testGetPrototype), + DECLARE_NODE_API_PROPERTY("testGetVersion", testGetVersion), + DECLARE_NODE_API_PROPERTY("testNapiRun", testNapiRun), + DECLARE_NODE_API_PROPERTY("doInstanceOf", doInstanceOf), + DECLARE_NODE_API_PROPERTY("getUndefined", getUndefined), + DECLARE_NODE_API_PROPERTY("getNull", getNull), + DECLARE_NODE_API_PROPERTY("createNapiError", createNapiError), + DECLARE_NODE_API_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup), + DECLARE_NODE_API_PROPERTY("testNapiTypeof", testNapiTypeof), + DECLARE_NODE_API_PROPERTY("wrap", wrap), + DECLARE_NODE_API_PROPERTY("envCleanupWrap", env_cleanup_wrap), + DECLARE_NODE_API_PROPERTY("unwrap", unwrap), + DECLARE_NODE_API_PROPERTY("removeWrap", remove_wrap), + DECLARE_NODE_API_PROPERTY("addFinalizerOnly", add_finalizer_only), + DECLARE_NODE_API_PROPERTY("testFinalizeWrap", test_finalize_wrap), + DECLARE_NODE_API_PROPERTY("finalizeWasCalled", finalize_was_called), + DECLARE_NODE_API_PROPERTY("derefItemWasCalled", deref_item_was_called), + DECLARE_NODE_API_PROPERTY("testAdjustExternalMemory", + testAdjustExternalMemory)}; NODE_API_CALL(env, napi_define_properties( env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors));