node-api: allow napi_delete_reference in finalizers

`napi_delete_reference` must be called immediately for a
`napi_reference` returned from `napi_wrap` in the corresponding
finalizer, in order to clean up the `napi_reference` timely.

`napi_delete_reference` is safe to be invoked during GC.
This commit is contained in:
Chengzhong Wu 2024-10-31 11:33:47 +00:00
parent 4b48f9b633
commit 139989e882
5 changed files with 37 additions and 5 deletions

View File

@ -2769,10 +2769,12 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
// Deletes a reference. The referenced value is released, and may be GC'd unless
// there are other references to it.
// For a napi_reference returned from `napi_wrap`, this must be called in the
// finalizer.
napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) {
// Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw
// JS exceptions.
CHECK_ENV_NOT_IN_GC(env);
CHECK_ENV(env);
CHECK_ARG(env, ref);
delete reinterpret_cast<v8impl::Reference*>(ref);

View File

@ -10,8 +10,9 @@ MyObject::MyObject(double value)
MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); }
void MyObject::Destructor(
napi_env env, void* nativeObject, void* /*finalize_hint*/) {
void MyObject::Destructor(node_api_basic_env env,
void* nativeObject,
void* /*finalize_hint*/) {
MyObject* obj = static_cast<MyObject*>(nativeObject);
delete obj;
}
@ -154,7 +155,7 @@ napi_value MyObject::Multiply(napi_env env, napi_callback_info info) {
}
// This finalizer should never be invoked.
void ObjectWrapDanglingReferenceFinalizer(napi_env env,
void ObjectWrapDanglingReferenceFinalizer(node_api_basic_env env,
void* finalize_data,
void* finalize_hint) {
assert(0 && "unreachable");

View File

@ -5,6 +5,13 @@
"sources": [
"6_object_wrap.cc"
]
},
{
"target_name": "6_object_wrap_basic_finalizer",
"defines": [ "NAPI_EXPERIMENTAL" ],
"sources": [
"6_object_wrap.cc"
]
}
]
}

View File

@ -6,7 +6,9 @@
class MyObject {
public:
static void Init(napi_env env, napi_value exports);
static void Destructor(napi_env env, void* nativeObject, void* finalize_hint);
static void Destructor(node_api_basic_env env,
void* nativeObject,
void* finalize_hint);
private:
explicit MyObject(double value_ = 0);

View File

@ -0,0 +1,20 @@
// Flags: --expose-gc
'use strict';
const common = require('../../common');
const addon = require(`./build/${common.buildType}/6_object_wrap_basic_finalizer`);
const { onGC } = require('../../common/gc');
/**
* This test verifies that an ObjectWrap can be correctly finalized with an NAPI_EXPERIMENTAL
* node_api_basic_finalizer.
*/
{
let obj = new addon.MyObject(9);
onGC(obj, {
ongc: common.mustCall(),
});
obj = null;
global.gc();
}