10
10
// Note: Do not include this file directly! Include "napi.h" instead.
11
11
12
12
#include < algorithm>
13
+ #include < atomic>
13
14
#include < cstring>
14
15
#include < mutex>
15
16
#include < type_traits>
@@ -19,6 +20,8 @@ namespace Napi {
19
20
// Helpers to handle functions exposed from C++.
20
21
namespace details {
21
22
23
+ extern std::atomic_bool needs_objectwrap_destructor_fix;
24
+
22
25
// Attach a data item to an object and delete it when the object gets
23
26
// garbage-collected.
24
27
// TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
@@ -251,11 +254,16 @@ struct AccessorCallbackData {
251
254
// Module registration
252
255
// //////////////////////////////////////////////////////////////////////////////
253
256
254
- #define NODE_API_MODULE (modname, regfunc ) \
255
- napi_value __napi_ ## regfunc(napi_env env, \
256
- napi_value exports) { \
257
- return Napi::RegisterModule (env, exports, regfunc); \
258
- } \
257
+ #define NODE_API_MODULE (modname, regfunc ) \
258
+ namespace Napi { \
259
+ namespace details { \
260
+ std::atomic_bool needs_objectwrap_destructor_fix (false ); \
261
+ } \
262
+ } \
263
+ napi_value __napi_ ## regfunc(napi_env env, \
264
+ napi_value exports) { \
265
+ return Napi::RegisterModule (env, exports, regfunc); \
266
+ } \
259
267
NAPI_MODULE (modname, __napi_ ## regfunc)
260
268
261
269
// Adapt the NAPI_MODULE registration function:
@@ -264,6 +272,12 @@ struct AccessorCallbackData {
264
272
inline napi_value RegisterModule (napi_env env,
265
273
napi_value exports,
266
274
ModuleRegisterCallback registerCallback) {
275
+ const napi_node_version* nver = Napi::VersionManagement::GetNodeVersion (env);
276
+ Napi::details::needs_objectwrap_destructor_fix =
277
+ (nver->major < 10 ||
278
+ (nver->major == 10 && nver->minor < 15 ) ||
279
+ (nver->major == 10 && nver->minor == 15 && nver->patch < 3 ));
280
+
267
281
return details::WrapCallback ([&] {
268
282
return napi_value (registerCallback (Napi::Env (env),
269
283
Napi::Object (env, exports)));
@@ -2968,16 +2982,36 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
2968
2982
napi_value wrapper = callbackInfo.This ();
2969
2983
napi_status status;
2970
2984
napi_ref ref;
2971
- T* instance = static_cast <T*>(this );
2972
- status = napi_wrap (env, wrapper, instance, FinalizeCallback, nullptr , &ref);
2985
+ status = napi_wrap (env, wrapper, this , FinalizeCallback, nullptr , &ref);
2973
2986
NAPI_THROW_IF_FAILED_VOID (env, status);
2974
2987
2975
- Reference<Object>* instanceRef = instance ;
2988
+ Reference<Object>* instanceRef = this ;
2976
2989
*instanceRef = Reference<Object>(env, ref);
2977
2990
}
2978
2991
2979
- template <typename T>
2980
- inline ObjectWrap<T>::~ObjectWrap () {}
2992
+ template <typename T>
2993
+ inline ObjectWrap<T>::~ObjectWrap () {
2994
+ // If the JS object still exists at this point, remove the finalizer added
2995
+ // through `napi_wrap()`.
2996
+ if (!IsEmpty ()) {
2997
+ Object object = Value ();
2998
+ // It is not valid to call `napi_remove_wrap()` with an empty `object`.
2999
+ // This happens e.g. during garbage collection.
3000
+ if (!object.IsEmpty () && _construction_failed) {
3001
+ napi_remove_wrap (Env (), object, nullptr );
3002
+
3003
+ if (Napi::details::needs_objectwrap_destructor_fix) {
3004
+ // If construction failed we delete the reference via
3005
+ // `napi_remove_wrap()`, not via `napi_delete_reference()` in the
3006
+ // `Reference<Object>` destructor. This will prevent the
3007
+ // `Reference<Object>` destructor from doing a double delete of this
3008
+ // reference.
3009
+ _ref = nullptr ;
3010
+ _env = nullptr ;
3011
+ }
3012
+ }
3013
+ }
3014
+ }
2981
3015
2982
3016
template <typename T>
2983
3017
inline T* ObjectWrap<T>::Unwrap(Object wrapper) {
@@ -3369,10 +3403,21 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
3369
3403
return nullptr ;
3370
3404
}
3371
3405
3372
- T* instance;
3373
3406
napi_value wrapper = details::WrapCallback ([&] {
3374
3407
CallbackInfo callbackInfo (env, info);
3375
- instance = new T (callbackInfo);
3408
+ T* instance = new T (callbackInfo);
3409
+ #ifdef NAPI_CPP_EXCEPTIONS
3410
+ instance->_construction_failed = false ;
3411
+ #else
3412
+ if (callbackInfo.Env ().IsExceptionPending ()) {
3413
+ // We need to clear the exception so that removing the wrap might work.
3414
+ Error e = callbackInfo.Env ().GetAndClearPendingException ();
3415
+ delete instance;
3416
+ e.ThrowAsJavaScriptException ();
3417
+ } else {
3418
+ instance->_construction_failed = false ;
3419
+ }
3420
+ # endif // NAPI_CPP_EXCEPTIONS
3376
3421
return callbackInfo.This ();
3377
3422
});
3378
3423
@@ -3497,7 +3542,7 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
3497
3542
3498
3543
template <typename T>
3499
3544
inline void ObjectWrap<T>::FinalizeCallback(napi_env env, void * data, void * /* hint*/ ) {
3500
- T * instance = reinterpret_cast <T *>(data);
3545
+ ObjectWrap<T> * instance = static_cast <ObjectWrap<T> *>(data);
3501
3546
instance->Finalize (Napi::Env (env));
3502
3547
delete instance;
3503
3548
}
0 commit comments