node-api: remove RefBase and CallbackWrapper

PR-URL: https://github.com/nodejs/node/pull/53590
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Gabriel Schulhof <gabrielschulhof@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
This commit is contained in:
Vladimir Morozov 2024-08-27 02:30:19 -07:00 committed by GitHub
parent dd8eeec3f0
commit 431ac161e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 367 additions and 357 deletions

View File

@ -134,43 +134,38 @@ napi_status NewExternalString(napi_env env,
return status;
}
class TrackedStringResource : public Finalizer, RefTracker {
class TrackedStringResource : private RefTracker {
public:
TrackedStringResource(napi_env env,
napi_finalize finalize_callback,
void* data,
void* finalize_hint)
: Finalizer(env, finalize_callback, data, finalize_hint) {
: RefTracker(), finalizer_(env, finalize_callback, data, finalize_hint) {
Link(finalize_callback == nullptr ? &env->reflist
: &env->finalizing_reflist);
}
protected:
// The only time Finalize() gets called before Dispose() is if the
// The only time Finalize() gets called before destructor is if the
// environment is dying. Finalize() expects that the item will be unlinked,
// so we do it here. V8 will still call Dispose() on us later, so we don't do
// any deleting here. We just null out env_ to avoid passing a stale pointer
// to the user's finalizer when V8 does finally call Dispose().
// so we do it here. V8 will still call destructor on us later, so we don't do
// any deleting here. We just null out env to avoid passing a stale pointer
// to the user's finalizer when V8 does finally call destructor.
void Finalize() override {
Unlink();
env_ = nullptr;
finalizer_.ResetEnv();
}
~TrackedStringResource() {
if (finalize_callback_ == nullptr) return;
if (env_ == nullptr) {
// The environment is dead. Call the finalizer directly.
finalize_callback_(nullptr, finalize_data_, finalize_hint_);
} else {
// The environment is still alive. Let's remove ourselves from its list
// of references and call the user's finalizer.
Unlink();
env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
}
~TrackedStringResource() override {
Unlink();
finalizer_.CallFinalizer();
}
private:
Finalizer finalizer_;
};
class ExternalOneByteStringResource
class ExternalOneByteStringResource final
: public v8::String::ExternalOneByteStringResource,
TrackedStringResource {
public:
@ -191,8 +186,8 @@ class ExternalOneByteStringResource
const size_t length_;
};
class ExternalStringResource : public v8::String::ExternalStringResource,
TrackedStringResource {
class ExternalStringResource final : public v8::String::ExternalStringResource,
TrackedStringResource {
public:
ExternalStringResource(napi_env env,
char16_t* string,
@ -368,7 +363,7 @@ inline napi_status Unwrap(napi_env env,
if (action == RemoveWrap) {
CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
.FromJust());
if (reference->ownership() == Ownership::kUserland) {
if (reference->ownership() == ReferenceOwnership::kUserland) {
// When the wrap is been removed, the finalizer should be reset.
reference->ResetFinalizer();
} else {
@ -400,10 +395,16 @@ class CallbackBundle {
bundle->env = env;
v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
Reference::New(
env, cbdata, 0, Ownership::kRuntime, Delete, bundle, nullptr);
ReferenceWithFinalizer::New(
env, cbdata, 0, ReferenceOwnership::kRuntime, Delete, bundle, nullptr);
return cbdata;
}
static CallbackBundle* FromCallbackData(v8::Local<v8::Value> data) {
return reinterpret_cast<CallbackBundle*>(data.As<v8::External>()->Value());
}
public:
napi_env env; // Necessary to invoke C++ NAPI callback
void* cb_data; // The user provided callback data
napi_callback cb;
@ -415,71 +416,9 @@ class CallbackBundle {
}
};
// Base class extended by classes that wrap V8 function and property callback
// info.
class CallbackWrapper {
public:
inline CallbackWrapper(napi_value this_arg, size_t args_length, void* data)
: _this(this_arg), _args_length(args_length), _data(data) {}
virtual napi_value GetNewTarget() = 0;
virtual void Args(napi_value* buffer, size_t bufferlength) = 0;
virtual void SetReturnValue(napi_value value) = 0;
napi_value This() { return _this; }
size_t ArgsLength() { return _args_length; }
void* Data() { return _data; }
protected:
const napi_value _this;
const size_t _args_length;
void* _data;
};
class CallbackWrapperBase : public CallbackWrapper {
public:
inline CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value>& cbinfo,
const size_t args_length)
: CallbackWrapper(
JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr),
_cbinfo(cbinfo) {
_bundle = reinterpret_cast<CallbackBundle*>(
cbinfo.Data().As<v8::External>()->Value());
_data = _bundle->cb_data;
}
protected:
inline void InvokeCallback() {
napi_callback_info cbinfo_wrapper = reinterpret_cast<napi_callback_info>(
static_cast<CallbackWrapper*>(this));
// All other pointers we need are stored in `_bundle`
napi_env env = _bundle->env;
napi_callback cb = _bundle->cb;
napi_value result = nullptr;
bool exceptionOccurred = false;
env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); },
[&](napi_env env, v8::Local<v8::Value> value) {
exceptionOccurred = true;
if (env->terminatedOrTerminating()) {
return;
}
env->isolate->ThrowException(value);
});
if (!exceptionOccurred && (result != nullptr)) {
this->SetReturnValue(result);
}
}
const v8::FunctionCallbackInfo<v8::Value>& _cbinfo;
CallbackBundle* _bundle;
};
class FunctionCallbackWrapper : public CallbackWrapperBase {
// Wraps up v8::FunctionCallbackInfo.
// The class must be stack allocated.
class FunctionCallbackWrapper {
public:
static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
FunctionCallbackWrapper cbwrapper(info);
@ -514,41 +453,70 @@ class FunctionCallbackWrapper : public CallbackWrapperBase {
return napi_clear_last_error(env);
}
explicit FunctionCallbackWrapper(
const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
: CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
napi_value GetNewTarget() override {
if (_cbinfo.IsConstructCall()) {
return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
napi_value GetNewTarget() {
if (cbinfo_.IsConstructCall()) {
return v8impl::JsValueFromV8LocalValue(cbinfo_.NewTarget());
} else {
return nullptr;
}
}
/*virtual*/
void Args(napi_value* buffer, size_t buffer_length) override {
void Args(napi_value* buffer, size_t buffer_length) {
size_t i = 0;
size_t min = std::min(buffer_length, _args_length);
size_t min_arg_count = std::min(buffer_length, ArgsLength());
for (; i < min; i += 1) {
buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
for (; i < min_arg_count; ++i) {
buffer[i] = JsValueFromV8LocalValue(cbinfo_[i]);
}
if (i < buffer_length) {
napi_value undefined =
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
for (; i < buffer_length; i += 1) {
JsValueFromV8LocalValue(v8::Undefined(cbinfo_.GetIsolate()));
for (; i < buffer_length; ++i) {
buffer[i] = undefined;
}
}
}
/*virtual*/
void SetReturnValue(napi_value value) override {
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
_cbinfo.GetReturnValue().Set(val);
napi_value This() { return JsValueFromV8LocalValue(cbinfo_.This()); }
size_t ArgsLength() { return static_cast<size_t>(cbinfo_.Length()); }
void* Data() { return bundle_->cb_data; }
private:
explicit FunctionCallbackWrapper(
const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
: cbinfo_(cbinfo),
bundle_(CallbackBundle::FromCallbackData(cbinfo.Data())) {}
void InvokeCallback() {
napi_callback_info cbinfo_wrapper =
reinterpret_cast<napi_callback_info>(this);
// All other pointers we need are stored in `_bundle`
napi_env env = bundle_->env;
napi_callback cb = bundle_->cb;
napi_value result = nullptr;
bool exceptionOccurred = false;
env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); },
[&](napi_env env, v8::Local<v8::Value> value) {
exceptionOccurred = true;
if (env->terminatedOrTerminating()) {
return;
}
env->isolate->ThrowException(value);
});
if (!exceptionOccurred && (result != nullptr)) {
cbinfo_.GetReturnValue().Set(V8LocalValueFromJsValue(result));
}
}
private:
const v8::FunctionCallbackInfo<v8::Value>& cbinfo_;
CallbackBundle* bundle_;
};
inline napi_status Wrap(napi_env env,
@ -579,24 +547,29 @@ inline napi_status Wrap(napi_env env,
// 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,
v8impl::Ownership::kUserland,
finalize_cb,
native_object,
finalize_hint);
*result = reinterpret_cast<napi_ref>(reference);
} else {
// Create a self-deleting reference.
reference = v8impl::Reference::New(
reference = v8impl::ReferenceWithFinalizer::New(
env,
obj,
0,
v8impl::Ownership::kRuntime,
v8impl::ReferenceOwnership::kUserland,
finalize_cb,
native_object,
finalize_cb == nullptr ? nullptr : finalize_hint);
finalize_hint);
*result = reinterpret_cast<napi_ref>(reference);
} else if (finalize_cb != nullptr) {
// Create a self-deleting reference.
reference = v8impl::ReferenceWithFinalizer::New(
env,
obj,
0,
v8impl::ReferenceOwnership::kRuntime,
finalize_cb,
native_object,
finalize_hint);
} else {
// Create a self-deleting reference.
reference = v8impl::ReferenceWithData::New(
env, obj, 0, v8impl::ReferenceOwnership::kRuntime, native_object);
}
CHECK(obj->SetPrivate(context,
@ -621,27 +594,46 @@ inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) {
} // end of anonymous namespace
void Finalizer::ResetEnv() {
env_ = nullptr;
}
void Finalizer::ResetFinalizer() {
finalize_callback_ = nullptr;
finalize_data_ = nullptr;
finalize_hint_ = nullptr;
}
void Finalizer::CallFinalizer() {
napi_finalize finalize_callback = finalize_callback_;
void* finalize_data = finalize_data_;
void* finalize_hint = finalize_hint_;
ResetFinalizer();
if (finalize_callback == nullptr) return;
if (env_ == nullptr) {
// The environment is dead. Call the finalizer directly.
finalize_callback(nullptr, finalize_data, finalize_hint);
} else {
env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint);
}
}
TrackedFinalizer::TrackedFinalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
: Finalizer(env, finalize_callback, finalize_data, finalize_hint),
RefTracker() {
Link(finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist);
}
: RefTracker(),
finalizer_(env, finalize_callback, finalize_data, finalize_hint) {}
TrackedFinalizer* TrackedFinalizer::New(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
return new TrackedFinalizer(
TrackedFinalizer* finalizer = new TrackedFinalizer(
env, finalize_callback, finalize_data, finalize_hint);
finalizer->Link(&env->finalizing_reflist);
return finalizer;
}
// When a TrackedFinalizer is being deleted, it may have been queued to call its
@ -650,92 +642,25 @@ TrackedFinalizer::~TrackedFinalizer() {
// Remove from the env's tracked list.
Unlink();
// Try to remove the finalizer from the scheduled second pass callback.
env_->DequeueFinalizer(this);
finalizer_.env()->DequeueFinalizer(this);
}
void TrackedFinalizer::Finalize() {
FinalizeCore(/*deleteMe:*/ true);
}
void TrackedFinalizer::FinalizeCore(bool deleteMe) {
// Swap out the field finalize_callback so that it can not be accidentally
// called more than once.
napi_finalize finalize_callback = finalize_callback_;
void* finalize_data = finalize_data_;
void* finalize_hint = finalize_hint_;
ResetFinalizer();
// Either the RefBase is going to be deleted in the finalize_callback or not,
// it should be removed from the tracked list.
Unlink();
// If the finalize_callback is present, it should either delete the
// derived RefBase, or the RefBase ownership was set to Ownership::kRuntime
// and the deleteMe parameter is true.
if (finalize_callback != nullptr) {
env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint);
}
if (deleteMe) {
delete this;
}
finalizer_.CallFinalizer();
delete this;
}
// Wrapper around v8impl::Persistent that implements reference counting.
RefBase::RefBase(napi_env env,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
: TrackedFinalizer(env, finalize_callback, finalize_data, finalize_hint),
refcount_(initial_refcount),
ownership_(ownership) {}
RefBase* RefBase::New(napi_env env,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
return new RefBase(env,
initial_refcount,
ownership,
finalize_callback,
finalize_data,
finalize_hint);
}
void* RefBase::Data() {
return finalize_data_;
}
uint32_t RefBase::Ref() {
return ++refcount_;
}
uint32_t RefBase::Unref() {
if (refcount_ == 0) {
return 0;
}
return --refcount_;
}
uint32_t RefBase::RefCount() {
return refcount_;
}
void RefBase::Finalize() {
// If the RefBase is not Ownership::kRuntime, userland code should delete it.
// Delete it if it is Ownership::kRuntime.
FinalizeCore(/*deleteMe:*/ ownership_ == Ownership::kRuntime);
}
template <typename... Args>
Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args)
: RefBase(env, std::forward<Args>(args)...),
Reference::Reference(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership)
: RefTracker(),
persistent_(env->isolate, value),
refcount_(initial_refcount),
ownership_(ownership),
can_be_weak_(CanBeHeldWeakly(value)) {
if (RefCount() == 0) {
if (refcount_ == 0) {
SetWeak();
}
}
@ -743,22 +668,18 @@ Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args)
Reference::~Reference() {
// Reset the handle. And no weak callback will be invoked.
persistent_.Reset();
// Remove from the env's tracked list.
Unlink();
}
Reference* Reference::New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
return new Reference(env,
value,
initial_refcount,
ownership,
finalize_callback,
finalize_data,
finalize_hint);
ReferenceOwnership ownership) {
Reference* reference = new Reference(env, value, initial_refcount, ownership);
reference->Link(&env->reflist);
return reference;
}
uint32_t Reference::Ref() {
@ -767,32 +688,29 @@ uint32_t Reference::Ref() {
if (persistent_.IsEmpty()) {
return 0;
}
uint32_t refcount = RefBase::Ref();
if (refcount == 1 && can_be_weak_) {
if (++refcount_ == 1 && can_be_weak_) {
persistent_.ClearWeak();
}
return refcount;
return refcount_;
}
uint32_t Reference::Unref() {
// When the persistent_ is cleared in the WeakCallback, and a second pass
// callback is pending, return 0 unconditionally.
if (persistent_.IsEmpty()) {
if (persistent_.IsEmpty() || refcount_ == 0) {
return 0;
}
uint32_t old_refcount = RefCount();
uint32_t refcount = RefBase::Unref();
if (old_refcount == 1 && refcount == 0) {
if (--refcount_ == 0) {
SetWeak();
}
return refcount;
return refcount_;
}
v8::Local<v8::Value> Reference::Get() {
v8::Local<v8::Value> Reference::Get(napi_env env) {
if (persistent_.IsEmpty()) {
return v8::Local<v8::Value>();
} else {
return v8::Local<v8::Value>::New(env_->isolate, persistent_);
return v8::Local<v8::Value>::New(env->isolate, persistent_);
}
}
@ -801,12 +719,30 @@ void Reference::Finalize() {
// be invoked again.
persistent_.Reset();
// Chain up to perform the rest of the finalization.
RefBase::Finalize();
// If the Reference is not ReferenceOwnership::kRuntime, userland code should
// delete it. Delete it if it is ReferenceOwnership::kRuntime.
bool deleteMe = ownership_ == ReferenceOwnership::kRuntime;
// Whether the Reference is going to be deleted in the finalize_callback
// or not, it should be removed from the tracked list.
Unlink();
// If the finalize_callback is present, it should either delete the
// derived Reference, or the Reference ownership was set to
// ReferenceOwnership::kRuntime and the deleteMe parameter is true.
CallUserFinalizer();
if (deleteMe) {
delete this;
}
}
// Mark the reference as weak and eligible for collection
// by the gc.
// Call the Finalize immediately since there is no user finalizer to call.
void Reference::InvokeFinalizerFromGC() {
Finalize();
}
// Mark the reference as weak and eligible for collection by the GC.
void Reference::SetWeak() {
if (can_be_weak_) {
persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
@ -815,14 +751,76 @@ void Reference::SetWeak() {
}
}
// The N-API finalizer callback may make calls into the engine. V8's heap is
// not in a consistent state during the weak callback, and therefore it does
// not support calls back into it. Enqueue the invocation of the finalizer.
// Static function called by GC. Delegate the call to the reference instance.
void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) {
Reference* reference = data.GetParameter();
// The reference must be reset during the weak callback as the API protocol.
// The reference must be reset during the weak callback per V8 API protocol.
reference->persistent_.Reset();
reference->env_->InvokeFinalizerFromGC(reference);
reference->InvokeFinalizerFromGC();
}
ReferenceWithData* ReferenceWithData::New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
void* data) {
ReferenceWithData* reference =
new ReferenceWithData(env, value, initial_refcount, ownership, data);
reference->Link(&env->reflist);
return reference;
}
ReferenceWithData::ReferenceWithData(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
void* data)
: Reference(env, value, initial_refcount, ownership), data_(data) {}
ReferenceWithFinalizer* ReferenceWithFinalizer::New(
napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
ReferenceWithFinalizer* reference =
new ReferenceWithFinalizer(env,
value,
initial_refcount,
ownership,
finalize_callback,
finalize_data,
finalize_hint);
reference->Link(&env->finalizing_reflist);
return reference;
}
ReferenceWithFinalizer::ReferenceWithFinalizer(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint)
: Reference(env, value, initial_refcount, ownership),
finalizer_(env, finalize_callback, finalize_data, finalize_hint) {}
ReferenceWithFinalizer::~ReferenceWithFinalizer() {
// Try to remove the finalizer from the scheduled second pass callback.
finalizer_.env()->DequeueFinalizer(this);
}
void ReferenceWithFinalizer::CallUserFinalizer() {
finalizer_.CallFinalizer();
}
// The Node-API finalizer callback may make calls into the engine. V8's heap is
// not in a consistent state during the weak callback, and therefore it does
// not support calls back into it. Enqueue the invocation of the finalizer.
void ReferenceWithFinalizer::InvokeFinalizerFromGC() {
finalizer_.env()->InvokeFinalizerFromGC(this);
}
/**
@ -2048,8 +2046,8 @@ napi_status NAPI_CDECL napi_get_cb_info(
CHECK_ENV(env);
CHECK_ARG(env, cbinfo);
v8impl::CallbackWrapper* info =
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
v8impl::FunctionCallbackWrapper* info =
reinterpret_cast<v8impl::FunctionCallbackWrapper*>(cbinfo);
if (argv != nullptr) {
CHECK_ARG(env, argc);
@ -2075,8 +2073,8 @@ napi_status NAPI_CDECL napi_get_new_target(napi_env env,
CHECK_ARG(env, cbinfo);
CHECK_ARG(env, result);
v8impl::CallbackWrapper* info =
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
v8impl::FunctionCallbackWrapper* info =
reinterpret_cast<v8impl::FunctionCallbackWrapper*>(cbinfo);
*result = info->GetNewTarget();
return napi_clear_last_error(env);
@ -2598,13 +2596,13 @@ napi_create_external(napi_env env,
if (finalize_cb) {
// The Reference object will delete itself after invoking the finalizer
// callback.
v8impl::Reference::New(env,
external_value,
0,
v8impl::Ownership::kRuntime,
finalize_cb,
data,
finalize_hint);
v8impl::ReferenceWithFinalizer::New(env,
external_value,
0,
v8impl::ReferenceOwnership::kRuntime,
finalize_cb,
data,
finalize_hint);
}
*result = v8impl::JsValueFromV8LocalValue(external_value);
@ -2739,7 +2737,7 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
}
v8impl::Reference* reference = v8impl::Reference::New(
env, v8_value, initial_refcount, v8impl::Ownership::kUserland);
env, v8_value, initial_refcount, v8impl::ReferenceOwnership::kUserland);
*result = reinterpret_cast<napi_ref>(reference);
return napi_clear_last_error(env);
@ -2795,7 +2793,7 @@ napi_status NAPI_CDECL napi_reference_unref(napi_env env,
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
if (reference->RefCount() == 0) {
if (reference->refcount() == 0) {
return napi_set_last_error(env, napi_generic_failure);
}
@ -2821,7 +2819,7 @@ napi_status NAPI_CDECL napi_get_reference_value(napi_env env,
CHECK_ARG(env, result);
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
*result = v8impl::JsValueFromV8LocalValue(reference->Get());
*result = v8impl::JsValueFromV8LocalValue(reference->Get(env));
return napi_clear_last_error(env);
}
@ -3432,10 +3430,10 @@ napi_add_finalizer(napi_env env,
// Create a self-deleting reference if the optional out-param result is not
// set.
v8impl::Ownership ownership = result == nullptr
? v8impl::Ownership::kRuntime
: v8impl::Ownership::kUserland;
v8impl::Reference* reference = v8impl::Reference::New(
v8impl::ReferenceOwnership ownership =
result == nullptr ? v8impl::ReferenceOwnership::kRuntime
: v8impl::ReferenceOwnership::kUserland;
v8impl::Reference* reference = v8impl::ReferenceWithFinalizer::New(
env, v8_value, 0, ownership, finalize_cb, finalize_data, finalize_hint);
if (result != nullptr) {
@ -3478,15 +3476,16 @@ napi_status NAPI_CDECL napi_set_instance_data(node_api_basic_env basic_env,
napi_env env = const_cast<napi_env>(basic_env);
CHECK_ENV(env);
v8impl::RefBase* old_data = static_cast<v8impl::RefBase*>(env->instance_data);
v8impl::TrackedFinalizer* old_data =
static_cast<v8impl::TrackedFinalizer*>(env->instance_data);
if (old_data != nullptr) {
// Our contract so far has been to not finalize any old data there may be.
// So we simply delete it.
delete old_data;
}
env->instance_data = v8impl::RefBase::New(
env, 0, v8impl::Ownership::kRuntime, finalize_cb, data, finalize_hint);
env->instance_data =
v8impl::TrackedFinalizer::New(env, finalize_cb, data, finalize_hint);
return napi_clear_last_error(env);
}
@ -3496,9 +3495,10 @@ napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env env,
CHECK_ENV(env);
CHECK_ARG(env, data);
v8impl::RefBase* idata = static_cast<v8impl::RefBase*>(env->instance_data);
v8impl::TrackedFinalizer* idata =
static_cast<v8impl::TrackedFinalizer*>(env->instance_data);
*data = (idata == nullptr ? nullptr : idata->Data());
*data = (idata == nullptr ? nullptr : idata->data());
return napi_clear_last_error(env);
}

View File

@ -8,14 +8,15 @@ inline napi_status napi_clear_last_error(node_api_basic_env env);
namespace v8impl {
// Base class to track references and finalizers in a doubly linked list.
class RefTracker {
public:
using RefList = RefTracker;
RefTracker() = default;
virtual ~RefTracker() = default;
virtual void Finalize() {}
typedef RefTracker RefList;
inline void Link(RefList* list) {
prev_ = list;
next_ = list->next_;
@ -47,7 +48,6 @@ class RefTracker {
RefList* prev_ = nullptr;
};
class Finalizer;
} // end of namespace v8impl
struct napi_env__ {
@ -99,11 +99,7 @@ struct napi_env__ {
}
}
// Call finalizer immediately.
virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) {
v8::HandleScope handle_scope(isolate);
CallIntoModule([&](napi_env env) { cb(env, data, hint); });
}
virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) = 0;
// Invoke finalizer from V8 garbage collector.
void InvokeFinalizerFromGC(v8impl::RefTracker* finalizer);
@ -323,7 +319,7 @@ inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
// Adapter for napi_finalize callbacks.
class Finalizer {
protected:
public:
Finalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
@ -333,23 +329,14 @@ class Finalizer {
finalize_data_(finalize_data),
finalize_hint_(finalize_hint) {}
virtual ~Finalizer() = default;
public:
static Finalizer* New(napi_env env,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr) {
return new Finalizer(env, finalize_callback, finalize_data, finalize_hint);
}
napi_finalize callback() { return finalize_callback_; }
napi_env env() { return env_; }
void* data() { return finalize_data_; }
void* hint() { return finalize_hint_; }
void ResetEnv();
void ResetFinalizer();
void CallFinalizer();
protected:
private:
napi_env env_;
napi_finalize finalize_callback_;
void* finalize_data_;
@ -370,8 +357,30 @@ class TryCatch : public v8::TryCatch {
napi_env _env;
};
// Wrapper around Finalizer that can be tracked.
class TrackedFinalizer final : public RefTracker {
public:
static TrackedFinalizer* New(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
~TrackedFinalizer() override;
void* data() { return finalizer_.data(); }
private:
TrackedFinalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
void Finalize() override;
private:
Finalizer finalizer_;
};
// Ownership of a reference.
enum class Ownership {
enum class ReferenceOwnership : uint8_t {
// The reference is owned by the runtime. No userland call is needed to
// destruct the reference.
kRuntime,
@ -380,91 +389,97 @@ enum class Ownership {
kUserland,
};
// Wrapper around Finalizer that can be tracked.
class TrackedFinalizer : public Finalizer, public RefTracker {
protected:
TrackedFinalizer(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
public:
static TrackedFinalizer* New(napi_env env,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
~TrackedFinalizer() override;
protected:
void Finalize() override;
void FinalizeCore(bool deleteMe);
};
// Wrapper around TrackedFinalizer that implements reference counting.
class RefBase : public TrackedFinalizer {
protected:
RefBase(napi_env env,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
public:
static RefBase* New(napi_env env,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
void* Data();
uint32_t Ref();
uint32_t Unref();
uint32_t RefCount();
Ownership ownership() { return ownership_; }
protected:
void Finalize() override;
private:
uint32_t refcount_;
Ownership ownership_;
};
// Wrapper around v8impl::Persistent.
class Reference : public RefBase {
protected:
template <typename... Args>
Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args);
class Reference : public RefTracker {
public:
static Reference* New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
Ownership ownership,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr);
ReferenceOwnership ownership);
~Reference() override;
virtual ~Reference();
uint32_t Ref();
uint32_t Unref();
v8::Local<v8::Value> Get();
v8::Local<v8::Value> Get(napi_env env);
virtual void ResetFinalizer() {}
virtual void* Data() { return nullptr; }
uint32_t refcount() const { return refcount_; }
ReferenceOwnership ownership() { return ownership_; }
protected:
void Finalize() override;
Reference(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership);
virtual void CallUserFinalizer() {}
virtual void InvokeFinalizerFromGC();
private:
static void WeakCallback(const v8::WeakCallbackInfo<Reference>& data);
void SetWeak();
void Finalize() override;
private:
v8impl::Persistent<v8::Value> persistent_;
uint32_t refcount_;
ReferenceOwnership ownership_;
bool can_be_weak_;
};
// Reference that can store additional data.
class ReferenceWithData final : public Reference {
public:
static ReferenceWithData* New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
void* data);
void* Data() override { return data_; }
private:
ReferenceWithData(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
void* data);
private:
void* data_;
};
// Reference that has a user finalizer callback.
class ReferenceWithFinalizer final : public Reference {
public:
static ReferenceWithFinalizer* New(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
~ReferenceWithFinalizer() override;
void ResetFinalizer() override { finalizer_.ResetFinalizer(); }
void* Data() override { return finalizer_.data(); }
private:
ReferenceWithFinalizer(napi_env env,
v8::Local<v8::Value> value,
uint32_t initial_refcount,
ReferenceOwnership ownership,
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint);
void CallUserFinalizer() override;
void InvokeFinalizerFromGC() override;
private:
Finalizer finalizer_;
};
} // end of namespace v8impl
#endif // SRC_JS_NATIVE_API_V8_H_

View File

@ -122,9 +122,9 @@ namespace {
class BufferFinalizer : private Finalizer {
public:
static BufferFinalizer* New(napi_env env,
napi_finalize finalize_callback = nullptr,
void* finalize_data = nullptr,
void* finalize_hint = nullptr) {
napi_finalize finalize_callback,
void* finalize_data,
void* finalize_hint) {
return new BufferFinalizer(
env, finalize_callback, finalize_data, finalize_hint);
}
@ -132,13 +132,8 @@ class BufferFinalizer : private Finalizer {
static void FinalizeBufferCallback(char* data, void* hint) {
std::unique_ptr<BufferFinalizer, Deleter> finalizer{
static_cast<BufferFinalizer*>(hint)};
finalizer->finalize_data_ = data;
// It is safe to call into JavaScript at this point.
if (finalizer->finalize_callback_ == nullptr) return;
finalizer->env_->CallFinalizer(finalizer->finalize_callback_,
finalizer->finalize_data_,
finalizer->finalize_hint_);
finalizer->CallFinalizer();
}
struct Deleter {
@ -151,10 +146,10 @@ class BufferFinalizer : private Finalizer {
void* finalize_data,
void* finalize_hint)
: Finalizer(env, finalize_callback, finalize_data, finalize_hint) {
env_->Ref();
env->Ref();
}
~BufferFinalizer() { env_->Unref(); }
~BufferFinalizer() { env()->Unref(); }
};
void ThrowNodeApiVersionError(node::Environment* node_env,
@ -1064,7 +1059,7 @@ napi_create_external_buffer(napi_env env,
// The finalizer object will delete itself after invoking the callback.
v8impl::BufferFinalizer* finalizer =
v8impl::BufferFinalizer::New(env, finalize_cb, nullptr, finalize_hint);
v8impl::BufferFinalizer::New(env, finalize_cb, data, finalize_hint);
v8::MaybeLocal<v8::Object> maybe =
node::Buffer::New(isolate,