Skip to content

Commit c3c8814

Browse files
mikepricedevGabriel SchulhofMichael Dawson
committed
implement virutal ObjectWrap::Finalize
Adds instance method `Finalize()` to `ObjectWrap()` which gets called immediately before the native instance is freed. It receives the `Napi::Env`, so classes that override it are able to perform cleanup that involves calls into N-API. Re: #515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>> PR-URL: #515 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent 5a7f8b2 commit c3c8814

File tree

5 files changed

+57
-1
lines changed

5 files changed

+57
-1
lines changed

doc/object_wrap.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,17 @@ property of the `Napi::CallbackInfo`.
190190

191191
Returns a `Napi::Function` representing the constructor function for the class.
192192

193+
### Finalize
194+
195+
Provides an opportunity to run cleanup code that requires access to the `Napi::Env`
196+
before the wrapped native object instance is freed. Override to implement.
197+
198+
```cpp
199+
virtual void Finalize(Napi::Env env);
200+
```
201+
202+
- `[in] env`: `Napi::Env`.
203+
193204
### StaticMethod
194205
195206
Creates property descriptor that represents a static method of a JavaScript class.

napi-inl.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2888,6 +2888,9 @@ inline ObjectWrap<T>::ObjectWrap(const Napi::CallbackInfo& callbackInfo) {
28882888
*instanceRef = Reference<Object>(env, ref);
28892889
}
28902890

2891+
template<typename T>
2892+
inline ObjectWrap<T>::~ObjectWrap() {}
2893+
28912894
template<typename T>
28922895
inline T* ObjectWrap<T>::Unwrap(Object wrapper) {
28932896
T* unwrapped;
@@ -3261,6 +3264,9 @@ inline ClassPropertyDescriptor<T> ObjectWrap<T>::InstanceValue(
32613264
return desc;
32623265
}
32633266

3267+
template <typename T>
3268+
inline void ObjectWrap<T>::Finalize(Napi::Env /*env*/) {}
3269+
32643270
template <typename T>
32653271
inline napi_value ObjectWrap<T>::ConstructorCallbackWrapper(
32663272
napi_env env,
@@ -3402,8 +3408,9 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
34023408
}
34033409

34043410
template <typename T>
3405-
inline void ObjectWrap<T>::FinalizeCallback(napi_env /*env*/, void* data, void* /*hint*/) {
3411+
inline void ObjectWrap<T>::FinalizeCallback(napi_env env, void* data, void* /*hint*/) {
34063412
T* instance = reinterpret_cast<T*>(data);
3413+
instance->Finalize(Napi::Env(env));
34073414
delete instance;
34083415
}
34093416

napi.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,7 @@ namespace Napi {
15701570
class ObjectWrap : public Reference<Object> {
15711571
public:
15721572
ObjectWrap(const CallbackInfo& callbackInfo);
1573+
virtual ~ObjectWrap();
15731574

15741575
static T* Unwrap(Object wrapper);
15751576

@@ -1657,6 +1658,7 @@ namespace Napi {
16571658
static PropertyDescriptor InstanceValue(Symbol name,
16581659
Napi::Value value,
16591660
napi_property_attributes attributes = napi_default);
1661+
virtual void Finalize(Napi::Env env);
16601662

16611663
private:
16621664
static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info);

test/objectwrap.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ class Test : public Napi::ObjectWrap<Test> {
2424
public:
2525
Test(const Napi::CallbackInfo& info) :
2626
Napi::ObjectWrap<Test>(info) {
27+
28+
if(info.Length() > 0) {
29+
finalizeCb_ = Napi::Persistent(info[0].As<Napi::Function>());
30+
}
31+
2732
}
2833

2934
void Setter(const Napi::CallbackInfo& /*info*/, const Napi::Value& value) {
@@ -105,8 +110,20 @@ class Test : public Napi::ObjectWrap<Test> {
105110
}));
106111
}
107112

113+
void Finalize(Napi::Env env) {
114+
115+
if(finalizeCb_.IsEmpty()) {
116+
return;
117+
}
118+
119+
finalizeCb_.Call(env.Global(), {Napi::Boolean::New(env, true)});
120+
finalizeCb_.Unref();
121+
122+
}
123+
108124
private:
109125
std::string value_;
126+
Napi::FunctionReference finalizeCb_;
110127
};
111128

112129
Napi::Object InitObjectWrap(Napi::Env env) {

test/objectwrap.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,24 @@ const test = (binding) => {
182182
}
183183
};
184184

185+
const testFinalize = (clazz) => {
186+
187+
let finalizeCalled = false;
188+
const finalizeCb = function(called) {
189+
finalizeCalled = called;
190+
};
191+
192+
//Scope Test instance so that it can be gc'd.
193+
(function() {
194+
new Test(finalizeCb);
195+
})();
196+
197+
global.gc();
198+
199+
assert.strictEqual(finalizeCalled, true);
200+
201+
};
202+
185203
const testObj = (obj, clazz) => {
186204
testValue(obj, clazz);
187205
testAccessor(obj, clazz);
@@ -198,6 +216,7 @@ const test = (binding) => {
198216
testStaticMethod(clazz);
199217

200218
testStaticEnumerables(clazz);
219+
testFinalize(clazz);
201220
};
202221

203222
// `Test` is needed for accessing exposed symbols

0 commit comments

Comments
 (0)