Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 36.4k
n-api: add generic finalizer callback#22244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -3208,6 +3208,11 @@ JavaScript functions from native code. One can either call a function | ||
| like a regular JavaScript function call, or as a constructor | ||
| function. | ||
| Any non-`NULL` data which is passed to this API via the `data` field of the | ||
| `napi_property_descriptor` items can be associated with `object` and freed | ||
| whenever `object` is garbage-collected by passing both `object` and the data to | ||
| [`napi_add_finalizer`][]. | ||
| ### napi_call_function | ||
| <!-- YAML | ||
| added: v8.0.0 | ||
| @@ -3345,6 +3350,11 @@ myaddon.sayHello(); | ||
| The string passed to `require()` is the name of the target in `binding.gyp` | ||
| responsible for creating the `.node` file. | ||
| Any non-`NULL` data which is passed to this API via the `data` parameter can | ||
| be associated with the resulting JavaScript function (which is returned in the | ||
| `result` parameter) and freed whenever the function is garbage-collected by | ||
| passing both the JavaScript function and the data to [`napi_add_finalizer`][]. | ||
| JavaScript `Function`s are described in | ||
| [Section 19.2](https://tc39.github.io/ecma262/#sec-function-objects) | ||
| of the ECMAScript Language Specification. | ||
| @@ -3551,6 +3561,12 @@ case, to prevent the function value from being garbage-collected, create a | ||
| persistent reference to it using [`napi_create_reference`][] and ensure the | ||
| reference count is kept >= 1. | ||
| Any non-`NULL` data which is passed to this API via the `data` parameter or via | ||
| the `data` field of the `napi_property_descriptor` array items can be associated | ||
| with the resulting JavaScript constructor (which is returned in the `result` | ||
| parameter) and freed whenever the class is garbage-collected by passing both | ||
| the JavaScript function and the data to [`napi_add_finalizer`][]. | ||
| ### napi_wrap | ||
| <!-- YAML | ||
| added: v8.0.0 | ||
| @@ -3655,6 +3671,47 @@ object `js_object` using `napi_wrap()` and removes the wrapping. If a finalize | ||
| callback was associated with the wrapping, it will no longer be called when the | ||
| JavaScript object becomes garbage-collected. | ||
| ### napi_add_finalizer | ||
| <!-- YAML | ||
| added: v8.0.0 | ||
| napiVersion: 1 | ||
| --> | ||
| ```C | ||
| napi_status napi_add_finalizer(napi_env env, | ||
| napi_value js_object, | ||
| void* native_object, | ||
| napi_finalize finalize_cb, | ||
| void* finalize_hint, | ||
| napi_ref* result); | ||
| ``` | ||
| - `[in] env`: The environment that the API is invoked under. | ||
| - `[in] js_object`: The JavaScript object to which the native data will be | ||
| attached. | ||
| - `[in] native_object`: The native data that will be attached to the JavaScript | ||
| object. | ||
| - `[in] finalize_cb`: Native callback that will be used to free the | ||
| native data when the JavaScript object is ready for garbage-collection. | ||
| - `[in] finalize_hint`: Optional contextual hint that is passed to the | ||
| finalize callback. | ||
| - `[out] result`: Optional reference to the JavaScript object. | ||
| Returns `napi_ok` if the API succeeded. | ||
| Adds a `napi_finalize` callback which will be called when the JavaScript object | ||
| in `js_object` is ready for garbage collection. This API is similar to | ||
| `napi_wrap()` except that | ||
| * the native data cannot be retrieved later using `napi_unwrap()`, | ||
| * nor can it be removed later using `napi_remove_wrap()`, and | ||
| * the API can be called multiple times with different data items in order to | ||
| attach each of them to the JavaScript object. | ||
| *Caution*: The optional returned reference (if obtained) should be deleted via | ||
| [`napi_delete_reference`][] ONLY in response to the finalize callback | ||
| invocation. If it is deleted before then, then the finalize callback may never | ||
| be invoked. Therefore, when obtaining a reference a finalize callback is also | ||
| required in order to enable correct disposal of the reference. | ||
| ## Simple Asynchronous Operations | ||
| Addon modules often need to leverage async helpers from libuv as part of their | ||
| @@ -4529,6 +4586,7 @@ This API may only be called from the main thread. | ||
| [Working with JavaScript Values]: #n_api_working_with_javascript_values | ||
| [Working with JavaScript Values - Abstract Operations]: #n_api_working_with_javascript_values_abstract_operations | ||
| [`napi_add_finalizer`]: #n_api_napi_add_finalizer | ||
BridgeAR marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| [`napi_async_init`]: #n_api_napi_async_init | ||
| [`napi_cancel_async_work`]: #n_api_napi_cancel_async_work | ||
| [`napi_close_escapable_handle_scope`]: #n_api_napi_close_escapable_handle_scope | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| 'use strict'; | ||
| // Flags: --expose-gc | ||
| const common = require('../../common'); | ||
| const test_general = require(`./build/${common.buildType}/test_general`); | ||
| const assert = require('assert'); | ||
| let finalized = {}; | ||
| const callback = common.mustCall(2); | ||
| // Add two items to be finalized and ensure the callback is called for each. | ||
| test_general.addFinalizerOnly(finalized, callback); | ||
| test_general.addFinalizerOnly(finalized, callback); | ||
| // Ensure attached items cannot be retrieved. | ||
| common.expectsError(() => test_general.unwrap(finalized), | ||
| { type: Error, message: 'Invalid argument' }); | ||
| // Ensure attached items cannot be removed. | ||
| common.expectsError(() => test_general.removeWrap(finalized), | ||
| { type: Error, message: 'Invalid argument' }); | ||
| finalized = null; | ||
| global.gc(); | ||
BridgeAR marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| // Add an item to an object that is already wrapped, and ensure that its | ||
| // finalizer as well as the wrap finalizer gets called. | ||
| let finalizeAndWrap = {}; | ||
| test_general.wrap(finalizeAndWrap); | ||
| test_general.addFinalizerOnly(finalizeAndWrap, common.mustCall()); | ||
| finalizeAndWrap = null; | ||
| global.gc(); | ||
| assert.strictEqual(test_general.derefItemWasCalled(), true, | ||
| 'finalize callback was called'); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| #define NAPI_EXPERIMENTAL | ||
| #include <node_api.h> | ||
| #include <stdlib.h> | ||
| #include "../common.h" | ||
| @@ -177,6 +178,17 @@ static napi_value wrap(napi_env env, napi_callback_info info) { | ||
| return NULL; | ||
| } | ||
| static napi_value unwrap(napi_env env, napi_callback_info info) { | ||
| size_t argc = 1; | ||
| napi_value wrapped; | ||
| void* data; | ||
| NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &wrapped, NULL, NULL)); | ||
| NAPI_CALL(env, napi_unwrap(env, wrapped, &data)); | ||
| return NULL; | ||
| } | ||
| static napi_value remove_wrap(napi_env env, napi_callback_info info) { | ||
| size_t argc = 1; | ||
| napi_value wrapped; | ||
| @@ -232,6 +244,33 @@ static napi_value testNapiRun(napi_env env, napi_callback_info info) { | ||
| return result; | ||
| } | ||
| static void finalizer_only_callback(napi_env env, void* data, void* hint) { | ||
| napi_ref js_cb_ref = data; | ||
| napi_value js_cb, undefined; | ||
| NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, js_cb_ref, &js_cb)); | ||
| NAPI_CALL_RETURN_VOID(env, napi_get_undefined(env, &undefined)); | ||
| NAPI_CALL_RETURN_VOID(env, | ||
| napi_call_function(env, undefined, js_cb, 0, NULL, NULL)); | ||
| NAPI_CALL_RETURN_VOID(env, napi_delete_reference(env, js_cb_ref)); | ||
| } | ||
| static napi_value add_finalizer_only(napi_env env, napi_callback_info info) { | ||
| size_t argc = 2; | ||
| napi_value argv[2]; | ||
| napi_ref js_cb_ref; | ||
| NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); | ||
| NAPI_CALL(env, napi_create_reference(env, argv[1], 1, &js_cb_ref)); | ||
| NAPI_CALL(env, | ||
| napi_add_finalizer(env, | ||
| argv[0], | ||
| js_cb_ref, | ||
| finalizer_only_callback, | ||
| NULL, | ||
| NULL)); | ||
| return NULL; | ||
| } | ||
BridgeAR marked this conversation as resolved.
Outdated
Uh oh!There was an error while loading. Please reload this page. | ||
| static napi_value Init(napi_env env, napi_value exports) { | ||
| napi_property_descriptor descriptors[] = { | ||
| DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), | ||
| @@ -246,7 +285,9 @@ static napi_value Init(napi_env env, napi_value exports) { | ||
| DECLARE_NAPI_PROPERTY("testNapiErrorCleanup", testNapiErrorCleanup), | ||
| DECLARE_NAPI_PROPERTY("testNapiTypeof", testNapiTypeof), | ||
| DECLARE_NAPI_PROPERTY("wrap", wrap), | ||
| DECLARE_NAPI_PROPERTY("unwrap", unwrap), | ||
| DECLARE_NAPI_PROPERTY("removeWrap", remove_wrap), | ||
| DECLARE_NAPI_PROPERTY("addFinalizerOnly", add_finalizer_only), | ||
| DECLARE_NAPI_PROPERTY("testFinalizeWrap", test_finalize_wrap), | ||
| DECLARE_NAPI_PROPERTY("finalizeWasCalled", finalize_was_called), | ||
| DECLARE_NAPI_PROPERTY("derefItemWasCalled", deref_item_was_called), | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is v8.0.0 right? Also, should it be marked as experimental, since it needs
NAPI_EXPERIMENTAL?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does need the indicator that it is experimental. bit confusing that it says N-API version 1 even in the existing docs.https://nodejs.org/docs/latest/api/n-api.html#n_api_napi_add_finalizer. That seems wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few like that in the master docs, and some that don't likst the N-API version at all for the ones that are experimental. Leaving out I think is the right answer. I'll create a PR to do that in master.