diff options
author | Gabriel Schulhof <gabriel.schulhof@intel.com> | 2018-08-10 11:43:39 -0400 |
---|---|---|
committer | Gabriel Schulhof <gabriel.schulhof@intel.com> | 2018-09-12 22:37:42 -0400 |
commit | cf0e881b33c841d2f89a23be281e1aaf061a58a3 (patch) | |
tree | fd1ba7df5b6955559bd76eef9c1e276f891201e3 /src/node_api.cc | |
parent | 82b752a302f1bb1fb2e82baeabfb8627de0b159f (diff) | |
download | android-node-v8-cf0e881b33c841d2f89a23be281e1aaf061a58a3.tar.gz android-node-v8-cf0e881b33c841d2f89a23be281e1aaf061a58a3.tar.bz2 android-node-v8-cf0e881b33c841d2f89a23be281e1aaf061a58a3.zip |
n-api: add generic finalizer callback
Add `napi_add_finalizer()`, which provides the ability to attach data
to an arbitrary object and be notified when that object is garbage-
collected so as to have an opportunity to delete the data previously
attached.
This differs from `napi_wrap()` in that it does not use up the private
slot on the object, and is therefore neither removable, nor retrievable
after the call to `napi_add_finalizer()`. It is assumed that the data
is accessible by other means, yet it must be tied to the lifetime of
the object. This is the case for data passed to a dynamically created
function which is itself heap-allocated and must therefore be freed
along with the function.
Fixes: https://github.com/nodejs/abi-stable-node/issues/313
PR-URL: https://github.com/nodejs/node/pull/22244
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Diffstat (limited to 'src/node_api.cc')
-rw-r--r-- | src/node_api.cc | 112 |
1 files changed, 77 insertions, 35 deletions
diff --git a/src/node_api.cc b/src/node_api.cc index ef4ec29708..690579f573 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -1157,6 +1157,63 @@ class ThreadSafeFunction : public node::AsyncResource { bool handles_closing; }; +enum WrapType { + retrievable, + anonymous +}; + +template <WrapType wrap_type> static inline +napi_status Wrap(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, js_object); + + v8::Isolate* isolate = env->isolate; + v8::Local<v8::Context> context = isolate->GetCurrentContext(); + + v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object); + RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); + v8::Local<v8::Object> obj = value.As<v8::Object>(); + + if (wrap_type == retrievable) { + // If we've already wrapped this object, we error out. + RETURN_STATUS_IF_FALSE(env, + !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)) + .FromJust(), + napi_invalid_arg); + } else if (wrap_type == anonymous) { + // If no finalize callback is provided, we error out. + CHECK_ARG(env, finalize_cb); + } + + v8impl::Reference* reference = nullptr; + if (result != nullptr) { + // The returned reference should be deleted via napi_delete_reference() + // ONLY in response to the finalize callback invocation. (If it is deleted + // before then, then the finalize callback will never be invoked.) + // Therefore a finalize callback is required when returning a reference. + CHECK_ARG(env, finalize_cb); + reference = v8impl::Reference::New( + env, obj, 0, false, finalize_cb, native_object, finalize_hint); + *result = reinterpret_cast<napi_ref>(reference); + } else { + // Create a self-deleting reference. + reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, + native_object, finalize_cb == nullptr ? nullptr : finalize_hint); + } + + if (wrap_type == retrievable) { + CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), + v8::External::New(isolate, reference)).FromJust()); + } + + return GET_RETURN_STATUS(env); +} + } // end of namespace v8impl // Intercepts the Node-V8 module registration callback. Converts parameters @@ -2859,41 +2916,12 @@ napi_status napi_wrap(napi_env env, napi_finalize finalize_cb, void* finalize_hint, napi_ref* result) { - NAPI_PREAMBLE(env); - CHECK_ARG(env, js_object); - - v8::Isolate* isolate = env->isolate; - v8::Local<v8::Context> context = isolate->GetCurrentContext(); - - v8::Local<v8::Value> value = v8impl::V8LocalValueFromJsValue(js_object); - RETURN_STATUS_IF_FALSE(env, value->IsObject(), napi_invalid_arg); - v8::Local<v8::Object> obj = value.As<v8::Object>(); - - // If we've already wrapped this object, we error out. - RETURN_STATUS_IF_FALSE(env, - !obj->HasPrivate(context, NAPI_PRIVATE_KEY(context, wrapper)).FromJust(), - napi_invalid_arg); - - v8impl::Reference* reference = nullptr; - if (result != nullptr) { - // The returned reference should be deleted via napi_delete_reference() - // ONLY in response to the finalize callback invocation. (If it is deleted - // before then, then the finalize callback will never be invoked.) - // Therefore a finalize callback is required when returning a reference. - CHECK_ARG(env, finalize_cb); - reference = v8impl::Reference::New( - env, obj, 0, false, finalize_cb, native_object, finalize_hint); - *result = reinterpret_cast<napi_ref>(reference); - } else { - // Create a self-deleting reference. - reference = v8impl::Reference::New(env, obj, 0, true, finalize_cb, - native_object, finalize_cb == nullptr ? nullptr : finalize_hint); - } - - CHECK(obj->SetPrivate(context, NAPI_PRIVATE_KEY(context, wrapper), - v8::External::New(isolate, reference)).FromJust()); - - return GET_RETURN_STATUS(env); + return v8impl::Wrap<v8impl::retrievable>(env, + js_object, + native_object, + finalize_cb, + finalize_hint, + result); } napi_status napi_unwrap(napi_env env, napi_value obj, void** result) { @@ -4138,3 +4166,17 @@ napi_ref_threadsafe_function(napi_env env, napi_threadsafe_function func) { CHECK(func != nullptr); return reinterpret_cast<v8impl::ThreadSafeFunction*>(func)->Ref(); } + +napi_status napi_add_finalizer(napi_env env, + napi_value js_object, + void* native_object, + napi_finalize finalize_cb, + void* finalize_hint, + napi_ref* result) { + return v8impl::Wrap<v8impl::anonymous>(env, + js_object, + native_object, + finalize_cb, + finalize_hint, + result); +} |