GeezerVison is showing github.com — node-api: allow napi_delete_reference in finalizers · nodejs/node@f883bed
File treeExpand file treeCollapse file tree Open diff view settings test/js-native-api/6_object_wrap
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: src/js_native_api_v8.cc +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -2769,10 +2769,12 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env, | 2769 | 2769 |
| 2770 | 2770 | // Deletes a reference. The referenced value is released, and may be GC'd unless | 2771 | 2771 | // there are other references to it. | | 2772 | +// For a napi_reference returned from `napi_wrap`, this must be called in the | | 2773 | +// finalizer. | 2772 | 2774 | napi_status NAPI_CDECL napi_delete_reference(napi_env env, napi_ref ref) { | 2773 | 2775 | // Omit NAPI_PREAMBLE and GET_RETURN_STATUS because V8 calls here cannot throw | 2774 | 2776 | // JS exceptions. | 2775 | | - CHECK_ENV_NOT_IN_GC(env); | | 2777 | + CHECK_ENV(env); | 2776 | 2778 | CHECK_ARG(env, ref); | 2777 | 2779 |
| 2778 | 2780 | delete reinterpret_cast<v8impl::Reference*>(ref); | |
Collapse file Copy file name to clipboardExpand all lines: test/js-native-api/6_object_wrap/6_object_wrap.cc +34-3Lines changed: 34 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 3 | 3 | #include "assert.h" | 4 | 4 | #include "myobject.h" | 5 | 5 |
| | 6 | +typedef int32_t FinalizerData; | | 7 | + | 6 | 8 | napi_ref MyObject::constructor; | 7 | 9 |
| 8 | 10 | MyObject::MyObject(double value) | 9 | 11 | : value_(value), env_(nullptr), wrapper_(nullptr) {} | 10 | 12 |
| 11 | 13 | MyObject::~MyObject() { napi_delete_reference(env_, wrapper_); } | 12 | 14 |
| 13 | | -void MyObject::Destructor( | 14 | | - napi_env env, void* nativeObject, void* /*finalize_hint*/) { | | 15 | +void MyObject::Destructor(node_api_basic_env env, | | 16 | + void* nativeObject, | | 17 | + void* /*finalize_hint*/) { | 15 | 18 | MyObject* obj = static_cast<MyObject*>(nativeObject); | 16 | 19 | delete obj; | | 20 | + | | 21 | + FinalizerData* data; | | 22 | + NODE_API_BASIC_CALL_RETURN_VOID( | | 23 | + env, napi_get_instance_data(env, reinterpret_cast<void**>(&data))); | | 24 | + *data += 1; | 17 | 25 | } | 18 | 26 |
| 19 | 27 | void MyObject::Init(napi_env env, napi_value exports) { | @@ -154,7 +162,7 @@ napi_value MyObject::Multiply(napi_env env, napi_callback_info info) { | 154 | 162 | } | 155 | 163 |
| 156 | 164 | // This finalizer should never be invoked. | 157 | | -void ObjectWrapDanglingReferenceFinalizer(napi_env env, | | 165 | +void ObjectWrapDanglingReferenceFinalizer(node_api_basic_env env, | 158 | 166 | void* finalize_data, | 159 | 167 | void* finalize_hint) { | 160 | 168 | assert(0 && "unreachable"); | @@ -198,15 +206,38 @@ napi_value ObjectWrapDanglingReferenceTest(napi_env env, | 198 | 206 | return ret; | 199 | 207 | } | 200 | 208 |
| | 209 | +static napi_value GetFinalizerCallCount(napi_env env, napi_callback_info info) { | | 210 | + size_t argc = 1; | | 211 | + napi_value argv[1]; | | 212 | + FinalizerData* data; | | 213 | + napi_value result; | | 214 | + | | 215 | + NODE_API_CALL(env, | | 216 | + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); | | 217 | + NODE_API_CALL(env, | | 218 | + napi_get_instance_data(env, reinterpret_cast<void**>(&data))); | | 219 | + NODE_API_CALL(env, napi_create_int32(env, *data, &result)); | | 220 | + return result; | | 221 | +} | | 222 | + | | 223 | +static void finalizeData(napi_env env, void* data, void* hint) { | | 224 | + delete reinterpret_cast<FinalizerData*>(data); | | 225 | +} | | 226 | + | 201 | 227 | EXTERN_C_START | 202 | 228 | napi_value Init(napi_env env, napi_value exports) { | | 229 | + FinalizerData* data = new FinalizerData; | | 230 | + *data = 0; | | 231 | + NODE_API_CALL(env, napi_set_instance_data(env, data, finalizeData, nullptr)); | | 232 | + | 203 | 233 | MyObject::Init(env, exports); | 204 | 234 |
| 205 | 235 | napi_property_descriptor descriptors[] = { | 206 | 236 | DECLARE_NODE_API_PROPERTY("objectWrapDanglingReference", | 207 | 237 | ObjectWrapDanglingReference), | 208 | 238 | DECLARE_NODE_API_PROPERTY("objectWrapDanglingReferenceTest", | 209 | 239 | ObjectWrapDanglingReferenceTest), | | 240 | + DECLARE_NODE_API_PROPERTY("getFinalizerCallCount", GetFinalizerCallCount), | 210 | 241 | }; | 211 | 242 |
| 212 | 243 | NODE_API_CALL( | |
Collapse file Copy file name to clipboardExpand all lines: test/js-native-api/6_object_wrap/binding.gyp +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 5 | 5 | "sources": [ | 6 | 6 | "6_object_wrap.cc" | 7 | 7 | ] | | 8 | + }, | | 9 | + { | | 10 | + "target_name": "6_object_wrap_basic_finalizer", | | 11 | + "defines": [ "NAPI_EXPERIMENTAL" ], | | 12 | + "sources": [ | | 13 | + "6_object_wrap.cc" | | 14 | + ] | 8 | 15 | } | 9 | 16 | ] | 10 | 17 | } |
Collapse file Copy file name to clipboardExpand all lines: test/js-native-api/6_object_wrap/myobject.h +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
| 6 | 6 | class MyObject { | 7 | 7 | public: | 8 | 8 | static void Init(napi_env env, napi_value exports); | 9 | | - static void Destructor(napi_env env, void* nativeObject, void* finalize_hint); | | 9 | + static void Destructor(node_api_basic_env env, | | 10 | + void* nativeObject, | | 11 | + void* finalize_hint); | 10 | 12 |
| 11 | 13 | private: | 12 | 14 | explicit MyObject(double value_ = 0); | |
Collapse file Copy file name to clipboard +24Lines changed: 24 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +// Flags: --expose-gc | | 2 | + | | 3 | +'use strict'; | | 4 | +const common = require('../../common'); | | 5 | +const assert = require('assert'); | | 6 | +const addon = require(`./build/${common.buildType}/6_object_wrap_basic_finalizer`); | | 7 | + | | 8 | +// This test verifies that ObjectWrap can be correctly finalized with a node_api_basic_finalizer | | 9 | +// in the current JS loop tick | | 10 | +(() => { | | 11 | + let obj = new addon.MyObject(9); | | 12 | + obj = null; | | 13 | + // Silent eslint about unused variables. | | 14 | + assert.strictEqual(obj, null); | | 15 | +})(); | | 16 | + | | 17 | +for (let i = 0; i < 10; ++i) { | | 18 | + global.gc(); | | 19 | + if (addon.getFinalizerCallCount() === 1) { | | 20 | + break; | | 21 | + } | | 22 | +} | | 23 | + | | 24 | +assert.strictEqual(addon.getFinalizerCallCount(), 1); |
|