-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
node-api: get Node API version used by addon #45715
Conversation
Review requested:
|
We discussed this draft's approach in the 9 Dec 2022 Node-API meeting. One question that was brought up is discussion about using two functions for the register-by-symbol method (one for getting version and one for initializing the module) and if this is a good approach versus just using a single method. It was determined that using two methods would be more beneficial because we need to know the targeted runtime version before the module initialization code runs, since this code can run arbitrary JavaScript. |
87acd9b
to
09b349b
Compare
Thank you for working on this! I know this is still a draft but I'll continue reviewing. |
f5805cf
to
21c61e8
Compare
b327ee2
to
7273e06
Compare
7273e06
to
722dbc5
Compare
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.
Mostly LGTM. Can we combine the two tests into a single test that uses two addons? So, like,
{
"targets": [
{
"target_name": "test_reference_all_types",
"sources": [ "test_reference_by_node_api_version.c" ],
"defines": [ "NAPI_EXPERIMENTAL" ],
},
{
"target_name": "test_reference_obj_only",
"sources": [ "test_reference_by_node_api_version.c" ],
"defines": [ "NAPI_VERSION=8" ],
}
]
}
... and then do some #ifdef
-ing in the source file? The test JS can then require()
each add-on and perform a fixed set of tests with two different sets of expectations as passed-in parameters.
Let me see if it can be done. |
It may also be possible to distill common bits into one source file which is listed in the sources section of both targets and then have two source files, each of which has the version-specific changes, one for each target. That should reduce the need for |
830a9c0
to
a90d822
Compare
I combined both tests and found that the C code between them was exactly the same. So, there was no need for #ifdefs. I also noticed that the JS code was mostly the same and I combined them too with minimal changes. Now the test looks pretty cool because it shows how we can create two different addons from the same C code that have different behavior depending on the targeted Node-API version. Then we use these addons in the same JS code. |
a90d822
to
0cd1b0d
Compare
@legendecas , @mhdawson , the PR is ready to be merged. Could you please take a look at it? |
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.
I got partway through a review. Will look further.
// method cannot be weak references because they are never collected. | ||
// | ||
// Currently, V8 has no API to detect if a symbol is local or global. | ||
// Until we have a V8 API for it, we consider that all symbols can be weak. |
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.
This does match current behaviour but also means that we'll have a leak right? Since we can change behaviour in a new Node-API version without affecting existing applications maybe the right thing to do is to consider all symbols as not being week so that the leak does not occur?
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.
@mhdawson, there are no memory leaks. The only side effect is that for the global and well-known symbols we always return a valid symbol because they are global and never collected by GC (it is by design). The local symbols are behaving the same way as weak references to objects.
We say that when the napi_ref
ref count goes to zero we are converting values to weak references if they are supported, otherwise we release them. Symbols support weak references, but they do it partially: for some of them the weak reference will always return "live" value because GC never collects them.
There is also a difference in the JavaScript WeakRef
behavior. It throws an exception if a global symbol is given to it but succeeds for local and well-known symbols. We can align with this behavior in future when V8 API lets us to see the symbol type. Though we should probably avoid throwing an exception and thus resetting the global symbols instead of keeping them could be not that big of a deal.
In any case to avoid native memory leaks we require to call napi_delete_reference
. Changing the ref count was never controlling the memory lifetime as we see it in COM IUnknown
or in std::shared_ptr
. If the napi_delete_reference
is not called explicitly, then the native memory will be removed on napi_env
finalization. So, technically it is also not a memory leak, but rather delays the memory release.
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.
@vmoroz thanks for the clarification. I was thinking about the auto-delete we have in RefBase but looking more closely I see that won't be related to direct use of references.
@vmoroz just one question, otherwise all looks good to me. Will wait to add approval until we agree on the answer to that question. |
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.
LGTM
Sorry for going in circles about the PR. Thank you for pushing this forward! |
PR-URL: #45715 Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
PR-URL: #45715 Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
PR-URL: #45715 Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
PR-URL: nodejs#45715 Reviewed-By: Gabriel Schulhof <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
This is to address the issue #45657.
The main goal of the PR is to establish a new mechanism to implement Node-API changes that affect not only the API shape, but also its behavior.
We are going to associate Node-API functions and new Node-API behavior with new Node-API version.
Node-API modules can choose which version of Node-API they target.
By default, they target the current Node-API version 8.
To target newer versions of Node-API they must explicitly opt-in by defining
napi_module_get_api_version_v1
export function that returns targeted version.In this PR, the
NAPI_MODULE
andNAPI_MODULE_INITIALIZER
macros are changed to define thenapi_module_get_api_version_v1
function that reports the value of theNAPI_VERSION
macro. C++ developers can control the targeted Node-API version by defining theNAPI_VERSION
macro as they did before. The key difference is that now this macro not only affects the set of functions that module uses but also affects the Node-API implementation behavior.Node.js is going to validate the version requested by the addon.
NAPI_VERSION
the Node.js compiled for and it is notNAPI_VERSION_EXPERIMENTAL
, then Node.js will throw an error on module initialization.Note that modules targeting
NAPI_VERSION_EXPERIMENTAL
are compatible with any Node-API version the Node.js was compiled with. Node-API in that case will use the latest functions and behaviors. Such module should callnapi_get_version
function to get the latest stable Node-API version offered by current Node.js instance. Then it must adapt for the different behavior based on the version.In this PR, we change the behavior of the
napi_create_reference
function for Node-API versions higher than 8. The new behavior allows the creation of annapi_ref
for anynapi_valuetype
instead of onlynapi_object
,napi_function
,napi_external
, andnapi_symbol
. One of the main goals is to enable the use ofnapi_ref
fornapi_string
.The
napi_ref
for the new primitive types immediately releases them when the ref count becomes 0 because they cannot be weak references. For the previously supported object and symbol types, the behavior did not change: these values become weak references and can be collected at any time. Note that the globally registered symbols created with theSymbol.for()
function call and well-known symbols such asSymbol.iterator
are always kept alive because they are never collected by the garbage collector.