-
Notifications
You must be signed in to change notification settings - Fork 461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add napi_finalize override to ObjectWrap #515
Conversation
…lows for user defined napi_finalize finalzier callbacks; addressing advanced cleanup senarios with opt-in complexity closer to the N-API lib.
@raptor9g I'm wondering if simply making FinalizeCallback overridable versus private would be simpler and achieve the same goal? |
We could also provide a protected instance method that gets called before the implementation does diff --git a/napi-inl.h b/napi-inl.h
index 0db3c98..6223f41 100644
--- a/napi-inl.h
+++ b/napi-inl.h
@@ -3009,6 +3009,11 @@ inline Function ObjectWrap<T>::DefineClass(
data);
}
+template <typename T>
+inline void Finalize(napi_env env) {
+ (void) env;
+}
+
template <typename T>
inline ClassPropertyDescriptor<T> ObjectWrap<T>::StaticMethod(
const char* utf8name,
@@ -3400,8 +3405,9 @@ inline napi_value ObjectWrap<T>::InstanceSetterCallbackWrapper(
}
template <typename T>
-inline void ObjectWrap<T>::FinalizeCallback(napi_env /*env*/, void* data, void* /*hint*/) {
+inline void ObjectWrap<T>::FinalizeCallback(napi_env env, void* data, void* /*hint*/) {
T* instance = reinterpret_cast<T*>(data);
+ instance->Finalize(env);
delete instance;
}
diff --git a/napi.h b/napi.h
index c194641..72c3590 100644
--- a/napi.h
+++ b/napi.h
@@ -1658,6 +1658,9 @@ namespace Napi {
Napi::Value value,
napi_property_attributes attributes = napi_default);
+ protected:
+ virtual void Finalize(napi_env env);
+
private:
static napi_value ConstructorCallbackWrapper(napi_env env, napi_callback_info info);
static napi_value StaticVoidMethodCallbackWrapper(napi_env env, napi_callback_info info); |
@mhdawson Yeah that would work great! Since
Here's an implementation. |
@gabrielschulhof That would work for my specific instance. Given that the override is providing deeper access to the underlying N-API finalizer implementation, at some point should it give full access to the implementation? |
@raptor9g node-addon-api and N-API are fully mixable. Users wishing to fully control the connection between a native instance and the corresponding JS object can use Napi::Value SomeBinding(const Napi::CallbackInfo& info) {
Napi::Object obj = Napi::Object::New(info.Env());
NativeStuff* stuff = new NativeStuff();
NAPI_THROW_IF_FAILED(env,
napi_wrap(info.Env(),
obj,
stuff,
DeleteNativeStuff,
NULL,
NULL),
Napi::Value());
return obj;
} IMO the value of this PR is that users need not give up on the One kind of deeper access that we might want to provide in the future is to the allocator responsible for creating the native instance. Currently we hard-code that to be IIUC with your implementation as given in this PR as well as the implementation you point to in the link there is no opportunity to chain up to the parent class to actually perform the |
@gabrielschulhof This is a really good point. And I like your approach to adding a virtual instance method bc it tells you, "hey there something here you can implement." And because it's not a pure virtual, it also indicates that it is optional. As to the deletion, I noticed while I was reading the documentation in N-API for |
@raptor9g
Do you have some use cases in mind where this is useful? Other than that I like @gabrielschulhof 's rationale of better managing the lifecycle while allowing "additional" work if the subclass need to do it. |
@raptor9g @mhdawson I was thinking that maybe wrapping a static native instance while retaining the |
@mhdawson I do not have a specific use case for extending the native instance lifespan outside of a trivial "save native state/do work later." My specific use case only requires access to the |
@raptor9g would you then mind modifying the PR to add the |
nodejs#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
@gabrielschulhof Absolutely, just pushed it. I also cited you in the commit, as I basically just typed in what you proposed. I want to make sure I did that correctly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please add a test which performs some cleanup that requires N-API? For example, it could call a callback that is provided at construction time, and to which a reference is retained throughout the life cycle of the wrapped object.
formatting. Todo: Add ObjectWrap::Finalize test. Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
@gabrielschulhof Pushed a commit with a test. Let me know if that works? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM % nits.
Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@gabrielschulhof based on the failures
probably needs to be
|
@mhdawson ... and there also needs to be a destructor for |
@raptor9g could you please add these? |
ObjectWrap::Finalize env param outlinted here: nodejs#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>>
Yes sir. They have been pushed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you kindly, good sir!
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>
Landed in c3c8814. |
nodejs#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com>
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: nodejs/node-addon-api#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>> PR-URL: nodejs/node-addon-api#515 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
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: nodejs/node-addon-api#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>> PR-URL: nodejs/node-addon-api#515 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
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: nodejs/node-addon-api#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>> PR-URL: nodejs/node-addon-api#515 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
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: nodejs/node-addon-api#515 (comment) Co-authored-by: Gabriel Schulhof <gabriel.schulhof@intel.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com>> PR-URL: nodejs/node-addon-api#515 Reviewed-By: Gabriel Schulhof <gabriel.schulhof@intel.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
By implementing the release of the native instance,
ObjectWrap
takes an opinionated approach to thefinalize_cb
in N-APInapi_wrap
. This is arguably the correct default as the central theme of theObject Wrap
methods in N-API is encapsulation. Specifically tying the properties and lifetimes of a native instance to a JS object.However, because this default is not optional/overridable, it excludes access to more complex finalization scenarios allowed by N-API. Specifically when access to the
napi_env
is required. Because node-addon-api is meant to be a thin convenience wrapper, it follows that some method of access to the underlying N-API should exist where defaults are not merely a difference in syntax and style between C++ and C.I've added
ObjectWrap::OverrideFinalizeCallback
to accomplish this in regards to the defaultnapi_finalize
inObjectWrap
. I prefixed with "Override" as opposed to "Set" in order to make it clear that there is a default behavior that is being overridden. I also stated in the documentation that the responsibility of freeing the native instance is passed to the user definednapi_finalize
callback whenObjectWrap::OverrideFinalizeCallback
is used.