1010// Note: Do not include this file directly! Include "napi.h" instead.
1111
1212#include < algorithm>
13+ #include < atomic>
1314#include < cstring>
1415#include < mutex>
1516#include < type_traits>
@@ -19,6 +20,8 @@ namespace Napi {
1920// Helpers to handle functions exposed from C++.
2021namespace details {
2122
23+ extern std::atomic_bool needs_objectwrap_destructor_fix;
24+
2225// Attach a data item to an object and delete it when the object gets
2326// garbage-collected.
2427// TODO: Replace this code with `napi_add_finalizer()` whenever it becomes
@@ -251,11 +254,16 @@ struct AccessorCallbackData {
251254// Module registration
252255// //////////////////////////////////////////////////////////////////////////////
253256
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+ } \
259267 NAPI_MODULE (modname, __napi_ ## regfunc)
260268
261269// Adapt the NAPI_MODULE registration function:
@@ -264,6 +272,12 @@ struct AccessorCallbackData {
264272inline napi_value RegisterModule (napi_env env,
265273 napi_value exports,
266274 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+
267281 return details::WrapCallback ([&] {
268282 return napi_value (registerCallback (Napi::Env (env),
269283 Napi::Object (env, exports)));
@@ -2968,16 +2982,36 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
29682982 napi_value wrapper = callbackInfo.This ();
29692983 napi_status status;
29702984 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);
29732986 NAPI_THROW_IF_FAILED_VOID (env, status);
29742987
2975- Reference<Object>* instanceRef = instance ;
2988+ Reference<Object>* instanceRef = this ;
29762989 *instanceRef = Reference<Object>(env, ref);
29772990}
29782991
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+ }
29813015
29823016template <typename T>
29833017inline T* ObjectWrap<T>::Unwrap(Object wrapper) {
@@ -3369,10 +3403,21 @@ inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
33693403 return nullptr ;
33703404 }
33713405
3372- T* instance;
33733406 napi_value wrapper = details::WrapCallback ([&] {
33743407 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
33763421 return callbackInfo.This ();
33773422 });
33783423
@@ -3497,7 +3542,7 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
34973542
34983543template <typename T>
34993544inline 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);
35013546 instance->Finalize (Napi::Env (env));
35023547 delete instance;
35033548}
0 commit comments