Skip to content

Commit d65a8f3

Browse files
vmoroztargos
authored andcommittedOct 2, 2024
node-api: remove RefBase and CallbackWrapper
PR-URL: #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>
1 parent 8422064 commit d65a8f3

File tree

3 files changed

+360
-350
lines changed

3 files changed

+360
-350
lines changed
 

‎src/js_native_api_v8.cc

+254-254
Original file line numberDiff line numberDiff line change
@@ -134,43 +134,38 @@ napi_status NewExternalString(napi_env env,
134134
return status;
135135
}
136136

137-
class TrackedStringResource : public Finalizer, RefTracker {
137+
class TrackedStringResource : private RefTracker {
138138
public:
139139
TrackedStringResource(napi_env env,
140140
napi_finalize finalize_callback,
141141
void* data,
142142
void* finalize_hint)
143-
: Finalizer(env, finalize_callback, data, finalize_hint) {
143+
: RefTracker(), finalizer_(env, finalize_callback, data, finalize_hint) {
144144
Link(finalize_callback == nullptr ? &env->reflist
145145
: &env->finalizing_reflist);
146146
}
147147

148148
protected:
149-
// The only time Finalize() gets called before Dispose() is if the
149+
// The only time Finalize() gets called before destructor is if the
150150
// environment is dying. Finalize() expects that the item will be unlinked,
151-
// so we do it here. V8 will still call Dispose() on us later, so we don't do
152-
// any deleting here. We just null out env_ to avoid passing a stale pointer
153-
// to the user's finalizer when V8 does finally call Dispose().
151+
// so we do it here. V8 will still call destructor on us later, so we don't do
152+
// any deleting here. We just null out env to avoid passing a stale pointer
153+
// to the user's finalizer when V8 does finally call destructor.
154154
void Finalize() override {
155155
Unlink();
156-
env_ = nullptr;
156+
finalizer_.ResetEnv();
157157
}
158158

159-
~TrackedStringResource() {
160-
if (finalize_callback_ == nullptr) return;
161-
if (env_ == nullptr) {
162-
// The environment is dead. Call the finalizer directly.
163-
finalize_callback_(nullptr, finalize_data_, finalize_hint_);
164-
} else {
165-
// The environment is still alive. Let's remove ourselves from its list
166-
// of references and call the user's finalizer.
167-
Unlink();
168-
env_->CallFinalizer(finalize_callback_, finalize_data_, finalize_hint_);
169-
}
159+
~TrackedStringResource() override {
160+
Unlink();
161+
finalizer_.CallFinalizer();
170162
}
163+
164+
private:
165+
Finalizer finalizer_;
171166
};
172167

173-
class ExternalOneByteStringResource
168+
class ExternalOneByteStringResource final
174169
: public v8::String::ExternalOneByteStringResource,
175170
TrackedStringResource {
176171
public:
@@ -191,8 +186,8 @@ class ExternalOneByteStringResource
191186
const size_t length_;
192187
};
193188

194-
class ExternalStringResource : public v8::String::ExternalStringResource,
195-
TrackedStringResource {
189+
class ExternalStringResource final : public v8::String::ExternalStringResource,
190+
TrackedStringResource {
196191
public:
197192
ExternalStringResource(napi_env env,
198193
char16_t* string,
@@ -368,7 +363,7 @@ inline napi_status Unwrap(napi_env env,
368363
if (action == RemoveWrap) {
369364
CHECK(obj->DeletePrivate(context, NAPI_PRIVATE_KEY(context, wrapper))
370365
.FromJust());
371-
if (reference->ownership() == Ownership::kUserland) {
366+
if (reference->ownership() == ReferenceOwnership::kUserland) {
372367
// When the wrap is been removed, the finalizer should be reset.
373368
reference->ResetFinalizer();
374369
} else {
@@ -400,10 +395,16 @@ class CallbackBundle {
400395
bundle->env = env;
401396

402397
v8::Local<v8::Value> cbdata = v8::External::New(env->isolate, bundle);
403-
Reference::New(
404-
env, cbdata, 0, Ownership::kRuntime, Delete, bundle, nullptr);
398+
ReferenceWithFinalizer::New(
399+
env, cbdata, 0, ReferenceOwnership::kRuntime, Delete, bundle, nullptr);
405400
return cbdata;
406401
}
402+
403+
static CallbackBundle* FromCallbackData(v8::Local<v8::Value> data) {
404+
return reinterpret_cast<CallbackBundle*>(data.As<v8::External>()->Value());
405+
}
406+
407+
public:
407408
napi_env env; // Necessary to invoke C++ NAPI callback
408409
void* cb_data; // The user provided callback data
409410
napi_callback cb;
@@ -415,71 +416,9 @@ class CallbackBundle {
415416
}
416417
};
417418

418-
// Base class extended by classes that wrap V8 function and property callback
419-
// info.
420-
class CallbackWrapper {
421-
public:
422-
inline CallbackWrapper(napi_value this_arg, size_t args_length, void* data)
423-
: _this(this_arg), _args_length(args_length), _data(data) {}
424-
425-
virtual napi_value GetNewTarget() = 0;
426-
virtual void Args(napi_value* buffer, size_t bufferlength) = 0;
427-
virtual void SetReturnValue(napi_value value) = 0;
428-
429-
napi_value This() { return _this; }
430-
431-
size_t ArgsLength() { return _args_length; }
432-
433-
void* Data() { return _data; }
434-
435-
protected:
436-
const napi_value _this;
437-
const size_t _args_length;
438-
void* _data;
439-
};
440-
441-
class CallbackWrapperBase : public CallbackWrapper {
442-
public:
443-
inline CallbackWrapperBase(const v8::FunctionCallbackInfo<v8::Value>& cbinfo,
444-
const size_t args_length)
445-
: CallbackWrapper(
446-
JsValueFromV8LocalValue(cbinfo.This()), args_length, nullptr),
447-
_cbinfo(cbinfo) {
448-
_bundle = reinterpret_cast<CallbackBundle*>(
449-
cbinfo.Data().As<v8::External>()->Value());
450-
_data = _bundle->cb_data;
451-
}
452-
453-
protected:
454-
inline void InvokeCallback() {
455-
napi_callback_info cbinfo_wrapper = reinterpret_cast<napi_callback_info>(
456-
static_cast<CallbackWrapper*>(this));
457-
458-
// All other pointers we need are stored in `_bundle`
459-
napi_env env = _bundle->env;
460-
napi_callback cb = _bundle->cb;
461-
462-
napi_value result = nullptr;
463-
bool exceptionOccurred = false;
464-
env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); },
465-
[&](napi_env env, v8::Local<v8::Value> value) {
466-
exceptionOccurred = true;
467-
if (env->terminatedOrTerminating()) {
468-
return;
469-
}
470-
env->isolate->ThrowException(value);
471-
});
472-
473-
if (!exceptionOccurred && (result != nullptr)) {
474-
this->SetReturnValue(result);
475-
}
476-
}
477-
478-
const v8::FunctionCallbackInfo<v8::Value>& _cbinfo;
479-
CallbackBundle* _bundle;
480-
};
481-
482-
class FunctionCallbackWrapper : public CallbackWrapperBase {
419+
// Wraps up v8::FunctionCallbackInfo.
420+
// The class must be stack allocated.
421+
class FunctionCallbackWrapper {
483422
public:
484423
static void Invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
485424
FunctionCallbackWrapper cbwrapper(info);
@@ -514,41 +453,70 @@ class FunctionCallbackWrapper : public CallbackWrapperBase {
514453
return napi_clear_last_error(env);
515454
}
516455

517-
explicit FunctionCallbackWrapper(
518-
const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
519-
: CallbackWrapperBase(cbinfo, cbinfo.Length()) {}
520-
521-
napi_value GetNewTarget() override {
522-
if (_cbinfo.IsConstructCall()) {
523-
return v8impl::JsValueFromV8LocalValue(_cbinfo.NewTarget());
456+
napi_value GetNewTarget() {
457+
if (cbinfo_.IsConstructCall()) {
458+
return v8impl::JsValueFromV8LocalValue(cbinfo_.NewTarget());
524459
} else {
525460
return nullptr;
526461
}
527462
}
528463

529-
/*virtual*/
530-
void Args(napi_value* buffer, size_t buffer_length) override {
464+
void Args(napi_value* buffer, size_t buffer_length) {
531465
size_t i = 0;
532-
size_t min = std::min(buffer_length, _args_length);
466+
size_t min_arg_count = std::min(buffer_length, ArgsLength());
533467

534-
for (; i < min; i += 1) {
535-
buffer[i] = v8impl::JsValueFromV8LocalValue(_cbinfo[i]);
468+
for (; i < min_arg_count; ++i) {
469+
buffer[i] = JsValueFromV8LocalValue(cbinfo_[i]);
536470
}
537471

538472
if (i < buffer_length) {
539473
napi_value undefined =
540-
v8impl::JsValueFromV8LocalValue(v8::Undefined(_cbinfo.GetIsolate()));
541-
for (; i < buffer_length; i += 1) {
474+
JsValueFromV8LocalValue(v8::Undefined(cbinfo_.GetIsolate()));
475+
for (; i < buffer_length; ++i) {
542476
buffer[i] = undefined;
543477
}
544478
}
545479
}
546480

547-
/*virtual*/
548-
void SetReturnValue(napi_value value) override {
549-
v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
550-
_cbinfo.GetReturnValue().Set(val);
481+
napi_value This() { return JsValueFromV8LocalValue(cbinfo_.This()); }
482+
483+
size_t ArgsLength() { return static_cast<size_t>(cbinfo_.Length()); }
484+
485+
void* Data() { return bundle_->cb_data; }
486+
487+
private:
488+
explicit FunctionCallbackWrapper(
489+
const v8::FunctionCallbackInfo<v8::Value>& cbinfo)
490+
: cbinfo_(cbinfo),
491+
bundle_(CallbackBundle::FromCallbackData(cbinfo.Data())) {}
492+
493+
void InvokeCallback() {
494+
napi_callback_info cbinfo_wrapper =
495+
reinterpret_cast<napi_callback_info>(this);
496+
497+
// All other pointers we need are stored in `_bundle`
498+
napi_env env = bundle_->env;
499+
napi_callback cb = bundle_->cb;
500+
501+
napi_value result = nullptr;
502+
bool exceptionOccurred = false;
503+
env->CallIntoModule([&](napi_env env) { result = cb(env, cbinfo_wrapper); },
504+
[&](napi_env env, v8::Local<v8::Value> value) {
505+
exceptionOccurred = true;
506+
if (env->terminatedOrTerminating()) {
507+
return;
508+
}
509+
env->isolate->ThrowException(value);
510+
});
511+
512+
if (!exceptionOccurred && (result != nullptr)) {
513+
cbinfo_.GetReturnValue().Set(V8LocalValueFromJsValue(result));
514+
}
551515
}
516+
517+
private:
518+
const v8::FunctionCallbackInfo<v8::Value>& cbinfo_;
519+
CallbackBundle* bundle_;
552520
};
553521

554522
inline napi_status Wrap(napi_env env,
@@ -579,24 +547,29 @@ inline napi_status Wrap(napi_env env,
579547
// before then, then the finalize callback will never be invoked.)
580548
// Therefore a finalize callback is required when returning a reference.
581549
CHECK_ARG(env, finalize_cb);
582-
reference = v8impl::Reference::New(env,
583-
obj,
584-
0,
585-
v8impl::Ownership::kUserland,
586-
finalize_cb,
587-
native_object,
588-
finalize_hint);
550+
reference = v8impl::ReferenceWithFinalizer::New(
551+
env,
552+
obj,
553+
0,
554+
v8impl::ReferenceOwnership::kUserland,
555+
finalize_cb,
556+
native_object,
557+
finalize_hint);
589558
*result = reinterpret_cast<napi_ref>(reference);
590-
} else {
559+
} else if (finalize_cb != nullptr) {
591560
// Create a self-deleting reference.
592-
reference = v8impl::Reference::New(
561+
reference = v8impl::ReferenceWithFinalizer::New(
593562
env,
594563
obj,
595564
0,
596-
v8impl::Ownership::kRuntime,
565+
v8impl::ReferenceOwnership::kRuntime,
597566
finalize_cb,
598567
native_object,
599-
finalize_cb == nullptr ? nullptr : finalize_hint);
568+
finalize_hint);
569+
} else {
570+
// Create a self-deleting reference.
571+
reference = v8impl::ReferenceWithData::New(
572+
env, obj, 0, v8impl::ReferenceOwnership::kRuntime, native_object);
600573
}
601574

602575
CHECK(obj->SetPrivate(context,
@@ -621,27 +594,46 @@ inline bool CanBeHeldWeakly(v8::Local<v8::Value> value) {
621594

622595
} // end of anonymous namespace
623596

597+
void Finalizer::ResetEnv() {
598+
env_ = nullptr;
599+
}
600+
624601
void Finalizer::ResetFinalizer() {
625602
finalize_callback_ = nullptr;
626603
finalize_data_ = nullptr;
627604
finalize_hint_ = nullptr;
628605
}
629606

607+
void Finalizer::CallFinalizer() {
608+
napi_finalize finalize_callback = finalize_callback_;
609+
void* finalize_data = finalize_data_;
610+
void* finalize_hint = finalize_hint_;
611+
ResetFinalizer();
612+
613+
if (finalize_callback == nullptr) return;
614+
if (env_ == nullptr) {
615+
// The environment is dead. Call the finalizer directly.
616+
finalize_callback(nullptr, finalize_data, finalize_hint);
617+
} else {
618+
env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint);
619+
}
620+
}
621+
630622
TrackedFinalizer::TrackedFinalizer(napi_env env,
631623
napi_finalize finalize_callback,
632624
void* finalize_data,
633625
void* finalize_hint)
634-
: Finalizer(env, finalize_callback, finalize_data, finalize_hint),
635-
RefTracker() {
636-
Link(finalize_callback == nullptr ? &env->reflist : &env->finalizing_reflist);
637-
}
626+
: RefTracker(),
627+
finalizer_(env, finalize_callback, finalize_data, finalize_hint) {}
638628

639629
TrackedFinalizer* TrackedFinalizer::New(napi_env env,
640630
napi_finalize finalize_callback,
641631
void* finalize_data,
642632
void* finalize_hint) {
643-
return new TrackedFinalizer(
633+
TrackedFinalizer* finalizer = new TrackedFinalizer(
644634
env, finalize_callback, finalize_data, finalize_hint);
635+
finalizer->Link(&env->finalizing_reflist);
636+
return finalizer;
645637
}
646638

647639
// When a TrackedFinalizer is being deleted, it may have been queued to call its
@@ -650,115 +642,44 @@ TrackedFinalizer::~TrackedFinalizer() {
650642
// Remove from the env's tracked list.
651643
Unlink();
652644
// Try to remove the finalizer from the scheduled second pass callback.
653-
env_->DequeueFinalizer(this);
645+
finalizer_.env()->DequeueFinalizer(this);
654646
}
655647

656648
void TrackedFinalizer::Finalize() {
657-
FinalizeCore(/*deleteMe:*/ true);
658-
}
659-
660-
void TrackedFinalizer::FinalizeCore(bool deleteMe) {
661-
// Swap out the field finalize_callback so that it can not be accidentally
662-
// called more than once.
663-
napi_finalize finalize_callback = finalize_callback_;
664-
void* finalize_data = finalize_data_;
665-
void* finalize_hint = finalize_hint_;
666-
ResetFinalizer();
667-
668-
// Either the RefBase is going to be deleted in the finalize_callback or not,
669-
// it should be removed from the tracked list.
670649
Unlink();
671-
// If the finalize_callback is present, it should either delete the
672-
// derived RefBase, or the RefBase ownership was set to Ownership::kRuntime
673-
// and the deleteMe parameter is true.
674-
if (finalize_callback != nullptr) {
675-
env_->CallFinalizer(finalize_callback, finalize_data, finalize_hint);
676-
}
677-
678-
if (deleteMe) {
679-
delete this;
680-
}
681-
}
682-
683-
// Wrapper around v8impl::Persistent that implements reference counting.
684-
RefBase::RefBase(napi_env env,
685-
uint32_t initial_refcount,
686-
Ownership ownership,
687-
napi_finalize finalize_callback,
688-
void* finalize_data,
689-
void* finalize_hint)
690-
: TrackedFinalizer(env, finalize_callback, finalize_data, finalize_hint),
691-
refcount_(initial_refcount),
692-
ownership_(ownership) {}
693-
694-
RefBase* RefBase::New(napi_env env,
695-
uint32_t initial_refcount,
696-
Ownership ownership,
697-
napi_finalize finalize_callback,
698-
void* finalize_data,
699-
void* finalize_hint) {
700-
return new RefBase(env,
701-
initial_refcount,
702-
ownership,
703-
finalize_callback,
704-
finalize_data,
705-
finalize_hint);
706-
}
707-
708-
void* RefBase::Data() {
709-
return finalize_data_;
710-
}
711-
712-
uint32_t RefBase::Ref() {
713-
return ++refcount_;
714-
}
715-
716-
uint32_t RefBase::Unref() {
717-
if (refcount_ == 0) {
718-
return 0;
719-
}
720-
return --refcount_;
650+
finalizer_.CallFinalizer();
651+
delete this;
721652
}
722653

723-
uint32_t RefBase::RefCount() {
724-
return refcount_;
725-
}
726-
727-
void RefBase::Finalize() {
728-
// If the RefBase is not Ownership::kRuntime, userland code should delete it.
729-
// Delete it if it is Ownership::kRuntime.
730-
FinalizeCore(/*deleteMe:*/ ownership_ == Ownership::kRuntime);
731-
}
732-
733-
template <typename... Args>
734-
Reference::Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args)
735-
: RefBase(env, std::forward<Args>(args)...),
654+
Reference::Reference(napi_env env,
655+
v8::Local<v8::Value> value,
656+
uint32_t initial_refcount,
657+
ReferenceOwnership ownership)
658+
: RefTracker(),
736659
persistent_(env->isolate, value),
660+
refcount_(initial_refcount),
661+
ownership_(ownership),
737662
can_be_weak_(CanBeHeldWeakly(value)) {
738-
if (RefCount() == 0) {
663+
if (refcount_ == 0) {
739664
SetWeak();
740665
}
741666
}
742667

743668
Reference::~Reference() {
744669
// Reset the handle. And no weak callback will be invoked.
745670
persistent_.Reset();
671+
672+
// Remove from the env's tracked list.
673+
Unlink();
746674
}
747675

748676
Reference* Reference::New(napi_env env,
749677
v8::Local<v8::Value> value,
750678
uint32_t initial_refcount,
751-
Ownership ownership,
752-
napi_finalize finalize_callback,
753-
void* finalize_data,
754-
void* finalize_hint) {
755-
return new Reference(env,
756-
value,
757-
initial_refcount,
758-
ownership,
759-
finalize_callback,
760-
finalize_data,
761-
finalize_hint);
679+
ReferenceOwnership ownership) {
680+
Reference* reference = new Reference(env, value, initial_refcount, ownership);
681+
reference->Link(&env->reflist);
682+
return reference;
762683
}
763684

764685
uint32_t Reference::Ref() {
@@ -767,32 +688,29 @@ uint32_t Reference::Ref() {
767688
if (persistent_.IsEmpty()) {
768689
return 0;
769690
}
770-
uint32_t refcount = RefBase::Ref();
771-
if (refcount == 1 && can_be_weak_) {
691+
if (++refcount_ == 1 && can_be_weak_) {
772692
persistent_.ClearWeak();
773693
}
774-
return refcount;
694+
return refcount_;
775695
}
776696

777697
uint32_t Reference::Unref() {
778698
// When the persistent_ is cleared in the WeakCallback, and a second pass
779699
// callback is pending, return 0 unconditionally.
780-
if (persistent_.IsEmpty()) {
700+
if (persistent_.IsEmpty() || refcount_ == 0) {
781701
return 0;
782702
}
783-
uint32_t old_refcount = RefCount();
784-
uint32_t refcount = RefBase::Unref();
785-
if (old_refcount == 1 && refcount == 0) {
703+
if (--refcount_ == 0) {
786704
SetWeak();
787705
}
788-
return refcount;
706+
return refcount_;
789707
}
790708

791-
v8::Local<v8::Value> Reference::Get() {
709+
v8::Local<v8::Value> Reference::Get(napi_env env) {
792710
if (persistent_.IsEmpty()) {
793711
return v8::Local<v8::Value>();
794712
} else {
795-
return v8::Local<v8::Value>::New(env_->isolate, persistent_);
713+
return v8::Local<v8::Value>::New(env->isolate, persistent_);
796714
}
797715
}
798716

@@ -801,12 +719,30 @@ void Reference::Finalize() {
801719
// be invoked again.
802720
persistent_.Reset();
803721

804-
// Chain up to perform the rest of the finalization.
805-
RefBase::Finalize();
722+
// If the Reference is not ReferenceOwnership::kRuntime, userland code should
723+
// delete it. Delete it if it is ReferenceOwnership::kRuntime.
724+
bool deleteMe = ownership_ == ReferenceOwnership::kRuntime;
725+
726+
// Whether the Reference is going to be deleted in the finalize_callback
727+
// or not, it should be removed from the tracked list.
728+
Unlink();
729+
730+
// If the finalize_callback is present, it should either delete the
731+
// derived Reference, or the Reference ownership was set to
732+
// ReferenceOwnership::kRuntime and the deleteMe parameter is true.
733+
CallUserFinalizer();
734+
735+
if (deleteMe) {
736+
delete this;
737+
}
806738
}
807739

808-
// Mark the reference as weak and eligible for collection
809-
// by the gc.
740+
// Call the Finalize immediately since there is no user finalizer to call.
741+
void Reference::InvokeFinalizerFromGC() {
742+
Finalize();
743+
}
744+
745+
// Mark the reference as weak and eligible for collection by the GC.
810746
void Reference::SetWeak() {
811747
if (can_be_weak_) {
812748
persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter);
@@ -815,14 +751,76 @@ void Reference::SetWeak() {
815751
}
816752
}
817753

818-
// The N-API finalizer callback may make calls into the engine. V8's heap is
819-
// not in a consistent state during the weak callback, and therefore it does
820-
// not support calls back into it. Enqueue the invocation of the finalizer.
754+
// Static function called by GC. Delegate the call to the reference instance.
821755
void Reference::WeakCallback(const v8::WeakCallbackInfo<Reference>& data) {
822756
Reference* reference = data.GetParameter();
823-
// The reference must be reset during the weak callback as the API protocol.
757+
// The reference must be reset during the weak callback per V8 API protocol.
824758
reference->persistent_.Reset();
825-
reference->env_->InvokeFinalizerFromGC(reference);
759+
reference->InvokeFinalizerFromGC();
760+
}
761+
762+
ReferenceWithData* ReferenceWithData::New(napi_env env,
763+
v8::Local<v8::Value> value,
764+
uint32_t initial_refcount,
765+
ReferenceOwnership ownership,
766+
void* data) {
767+
ReferenceWithData* reference =
768+
new ReferenceWithData(env, value, initial_refcount, ownership, data);
769+
reference->Link(&env->reflist);
770+
return reference;
771+
}
772+
773+
ReferenceWithData::ReferenceWithData(napi_env env,
774+
v8::Local<v8::Value> value,
775+
uint32_t initial_refcount,
776+
ReferenceOwnership ownership,
777+
void* data)
778+
: Reference(env, value, initial_refcount, ownership), data_(data) {}
779+
780+
ReferenceWithFinalizer* ReferenceWithFinalizer::New(
781+
napi_env env,
782+
v8::Local<v8::Value> value,
783+
uint32_t initial_refcount,
784+
ReferenceOwnership ownership,
785+
napi_finalize finalize_callback,
786+
void* finalize_data,
787+
void* finalize_hint) {
788+
ReferenceWithFinalizer* reference =
789+
new ReferenceWithFinalizer(env,
790+
value,
791+
initial_refcount,
792+
ownership,
793+
finalize_callback,
794+
finalize_data,
795+
finalize_hint);
796+
reference->Link(&env->finalizing_reflist);
797+
return reference;
798+
}
799+
800+
ReferenceWithFinalizer::ReferenceWithFinalizer(napi_env env,
801+
v8::Local<v8::Value> value,
802+
uint32_t initial_refcount,
803+
ReferenceOwnership ownership,
804+
napi_finalize finalize_callback,
805+
void* finalize_data,
806+
void* finalize_hint)
807+
: Reference(env, value, initial_refcount, ownership),
808+
finalizer_(env, finalize_callback, finalize_data, finalize_hint) {}
809+
810+
ReferenceWithFinalizer::~ReferenceWithFinalizer() {
811+
// Try to remove the finalizer from the scheduled second pass callback.
812+
finalizer_.env()->DequeueFinalizer(this);
813+
}
814+
815+
void ReferenceWithFinalizer::CallUserFinalizer() {
816+
finalizer_.CallFinalizer();
817+
}
818+
819+
// The Node-API finalizer callback may make calls into the engine. V8's heap is
820+
// not in a consistent state during the weak callback, and therefore it does
821+
// not support calls back into it. Enqueue the invocation of the finalizer.
822+
void ReferenceWithFinalizer::InvokeFinalizerFromGC() {
823+
finalizer_.env()->InvokeFinalizerFromGC(this);
826824
}
827825

828826
/**
@@ -2051,8 +2049,8 @@ napi_status NAPI_CDECL napi_get_cb_info(
20512049
CHECK_ENV(env);
20522050
CHECK_ARG(env, cbinfo);
20532051

2054-
v8impl::CallbackWrapper* info =
2055-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
2052+
v8impl::FunctionCallbackWrapper* info =
2053+
reinterpret_cast<v8impl::FunctionCallbackWrapper*>(cbinfo);
20562054

20572055
if (argv != nullptr) {
20582056
CHECK_ARG(env, argc);
@@ -2078,8 +2076,8 @@ napi_status NAPI_CDECL napi_get_new_target(napi_env env,
20782076
CHECK_ARG(env, cbinfo);
20792077
CHECK_ARG(env, result);
20802078

2081-
v8impl::CallbackWrapper* info =
2082-
reinterpret_cast<v8impl::CallbackWrapper*>(cbinfo);
2079+
v8impl::FunctionCallbackWrapper* info =
2080+
reinterpret_cast<v8impl::FunctionCallbackWrapper*>(cbinfo);
20832081

20842082
*result = info->GetNewTarget();
20852083
return napi_clear_last_error(env);
@@ -2605,13 +2603,13 @@ napi_create_external(napi_env env,
26052603
if (finalize_cb) {
26062604
// The Reference object will delete itself after invoking the finalizer
26072605
// callback.
2608-
v8impl::Reference::New(env,
2609-
external_value,
2610-
0,
2611-
v8impl::Ownership::kRuntime,
2612-
finalize_cb,
2613-
data,
2614-
finalize_hint);
2606+
v8impl::ReferenceWithFinalizer::New(env,
2607+
external_value,
2608+
0,
2609+
v8impl::ReferenceOwnership::kRuntime,
2610+
finalize_cb,
2611+
data,
2612+
finalize_hint);
26152613
}
26162614

26172615
*result = v8impl::JsValueFromV8LocalValue(external_value);
@@ -2746,7 +2744,7 @@ napi_status NAPI_CDECL napi_create_reference(napi_env env,
27462744
}
27472745

27482746
v8impl::Reference* reference = v8impl::Reference::New(
2749-
env, v8_value, initial_refcount, v8impl::Ownership::kUserland);
2747+
env, v8_value, initial_refcount, v8impl::ReferenceOwnership::kUserland);
27502748

27512749
*result = reinterpret_cast<napi_ref>(reference);
27522750
return napi_clear_last_error(env);
@@ -2802,7 +2800,7 @@ napi_status NAPI_CDECL napi_reference_unref(napi_env env,
28022800

28032801
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
28042802

2805-
if (reference->RefCount() == 0) {
2803+
if (reference->refcount() == 0) {
28062804
return napi_set_last_error(env, napi_generic_failure);
28072805
}
28082806

@@ -2828,7 +2826,7 @@ napi_status NAPI_CDECL napi_get_reference_value(napi_env env,
28282826
CHECK_ARG(env, result);
28292827

28302828
v8impl::Reference* reference = reinterpret_cast<v8impl::Reference*>(ref);
2831-
*result = v8impl::JsValueFromV8LocalValue(reference->Get());
2829+
*result = v8impl::JsValueFromV8LocalValue(reference->Get(env));
28322830

28332831
return napi_clear_last_error(env);
28342832
}
@@ -3439,10 +3437,10 @@ napi_add_finalizer(napi_env env,
34393437

34403438
// Create a self-deleting reference if the optional out-param result is not
34413439
// set.
3442-
v8impl::Ownership ownership = result == nullptr
3443-
? v8impl::Ownership::kRuntime
3444-
: v8impl::Ownership::kUserland;
3445-
v8impl::Reference* reference = v8impl::Reference::New(
3440+
v8impl::ReferenceOwnership ownership =
3441+
result == nullptr ? v8impl::ReferenceOwnership::kRuntime
3442+
: v8impl::ReferenceOwnership::kUserland;
3443+
v8impl::Reference* reference = v8impl::ReferenceWithFinalizer::New(
34463444
env, v8_value, 0, ownership, finalize_cb, finalize_data, finalize_hint);
34473445

34483446
if (result != nullptr) {
@@ -3485,15 +3483,16 @@ napi_status NAPI_CDECL napi_set_instance_data(node_api_basic_env basic_env,
34853483
napi_env env = const_cast<napi_env>(basic_env);
34863484
CHECK_ENV(env);
34873485

3488-
v8impl::RefBase* old_data = static_cast<v8impl::RefBase*>(env->instance_data);
3486+
v8impl::TrackedFinalizer* old_data =
3487+
static_cast<v8impl::TrackedFinalizer*>(env->instance_data);
34893488
if (old_data != nullptr) {
34903489
// Our contract so far has been to not finalize any old data there may be.
34913490
// So we simply delete it.
34923491
delete old_data;
34933492
}
34943493

3495-
env->instance_data = v8impl::RefBase::New(
3496-
env, 0, v8impl::Ownership::kRuntime, finalize_cb, data, finalize_hint);
3494+
env->instance_data =
3495+
v8impl::TrackedFinalizer::New(env, finalize_cb, data, finalize_hint);
34973496

34983497
return napi_clear_last_error(env);
34993498
}
@@ -3503,9 +3502,10 @@ napi_status NAPI_CDECL napi_get_instance_data(node_api_basic_env env,
35033502
CHECK_ENV(env);
35043503
CHECK_ARG(env, data);
35053504

3506-
v8impl::RefBase* idata = static_cast<v8impl::RefBase*>(env->instance_data);
3505+
v8impl::TrackedFinalizer* idata =
3506+
static_cast<v8impl::TrackedFinalizer*>(env->instance_data);
35073507

3508-
*data = (idata == nullptr ? nullptr : idata->Data());
3508+
*data = (idata == nullptr ? nullptr : idata->data());
35093509

35103510
return napi_clear_last_error(env);
35113511
}

‎src/js_native_api_v8.h

+99-84
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ inline napi_status napi_clear_last_error(node_api_basic_env env);
88

99
namespace v8impl {
1010

11+
// Base class to track references and finalizers in a doubly linked list.
1112
class RefTracker {
1213
public:
14+
using RefList = RefTracker;
15+
1316
RefTracker() = default;
1417
virtual ~RefTracker() = default;
1518
virtual void Finalize() {}
1619

17-
typedef RefTracker RefList;
18-
1920
inline void Link(RefList* list) {
2021
prev_ = list;
2122
next_ = list->next_;
@@ -47,7 +48,6 @@ class RefTracker {
4748
RefList* prev_ = nullptr;
4849
};
4950

50-
class Finalizer;
5151
} // end of namespace v8impl
5252

5353
struct napi_env__ {
@@ -99,11 +99,7 @@ struct napi_env__ {
9999
}
100100
}
101101

102-
// Call finalizer immediately.
103-
virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) {
104-
v8::HandleScope handle_scope(isolate);
105-
CallIntoModule([&](napi_env env) { cb(env, data, hint); });
106-
}
102+
virtual void CallFinalizer(napi_finalize cb, void* data, void* hint) = 0;
107103

108104
// Invoke finalizer from V8 garbage collector.
109105
void InvokeFinalizerFromGC(v8impl::RefTracker* finalizer);
@@ -323,7 +319,7 @@ inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
323319

324320
// Adapter for napi_finalize callbacks.
325321
class Finalizer {
326-
protected:
322+
public:
327323
Finalizer(napi_env env,
328324
napi_finalize finalize_callback,
329325
void* finalize_data,
@@ -333,23 +329,14 @@ class Finalizer {
333329
finalize_data_(finalize_data),
334330
finalize_hint_(finalize_hint) {}
335331

336-
virtual ~Finalizer() = default;
337-
338-
public:
339-
static Finalizer* New(napi_env env,
340-
napi_finalize finalize_callback = nullptr,
341-
void* finalize_data = nullptr,
342-
void* finalize_hint = nullptr) {
343-
return new Finalizer(env, finalize_callback, finalize_data, finalize_hint);
344-
}
345-
346-
napi_finalize callback() { return finalize_callback_; }
332+
napi_env env() { return env_; }
347333
void* data() { return finalize_data_; }
348-
void* hint() { return finalize_hint_; }
349334

335+
void ResetEnv();
350336
void ResetFinalizer();
337+
void CallFinalizer();
351338

352-
protected:
339+
private:
353340
napi_env env_;
354341
napi_finalize finalize_callback_;
355342
void* finalize_data_;
@@ -370,101 +357,129 @@ class TryCatch : public v8::TryCatch {
370357
napi_env _env;
371358
};
372359

373-
// Ownership of a reference.
374-
enum class Ownership {
375-
// The reference is owned by the runtime. No userland call is needed to
376-
// destruct the reference.
377-
kRuntime,
378-
// The reference is owned by the userland. User code is responsible to delete
379-
// the reference with appropriate node-api calls.
380-
kUserland,
381-
};
382-
383360
// Wrapper around Finalizer that can be tracked.
384-
class TrackedFinalizer : public Finalizer, public RefTracker {
385-
protected:
386-
TrackedFinalizer(napi_env env,
387-
napi_finalize finalize_callback,
388-
void* finalize_data,
389-
void* finalize_hint);
390-
361+
class TrackedFinalizer final : public RefTracker {
391362
public:
392363
static TrackedFinalizer* New(napi_env env,
393364
napi_finalize finalize_callback,
394365
void* finalize_data,
395366
void* finalize_hint);
396367
~TrackedFinalizer() override;
397368

398-
protected:
399-
void Finalize() override;
400-
void FinalizeCore(bool deleteMe);
401-
};
369+
void* data() { return finalizer_.data(); }
402370

403-
// Wrapper around TrackedFinalizer that implements reference counting.
404-
class RefBase : public TrackedFinalizer {
405-
protected:
406-
RefBase(napi_env env,
407-
uint32_t initial_refcount,
408-
Ownership ownership,
409-
napi_finalize finalize_callback,
410-
void* finalize_data,
411-
void* finalize_hint);
412-
413-
public:
414-
static RefBase* New(napi_env env,
415-
uint32_t initial_refcount,
416-
Ownership ownership,
417-
napi_finalize finalize_callback,
418-
void* finalize_data,
419-
void* finalize_hint);
420-
421-
void* Data();
422-
uint32_t Ref();
423-
uint32_t Unref();
424-
uint32_t RefCount();
425-
426-
Ownership ownership() { return ownership_; }
427-
428-
protected:
371+
private:
372+
TrackedFinalizer(napi_env env,
373+
napi_finalize finalize_callback,
374+
void* finalize_data,
375+
void* finalize_hint);
429376
void Finalize() override;
430377

431378
private:
432-
uint32_t refcount_;
433-
Ownership ownership_;
379+
Finalizer finalizer_;
434380
};
435381

436-
// Wrapper around v8impl::Persistent.
437-
class Reference : public RefBase {
438-
protected:
439-
template <typename... Args>
440-
Reference(napi_env env, v8::Local<v8::Value> value, Args&&... args);
382+
// Ownership of a reference.
383+
enum class ReferenceOwnership : uint8_t {
384+
// The reference is owned by the runtime. No userland call is needed to
385+
// destruct the reference.
386+
kRuntime,
387+
// The reference is owned by the userland. User code is responsible to delete
388+
// the reference with appropriate node-api calls.
389+
kUserland,
390+
};
441391

392+
// Wrapper around v8impl::Persistent.
393+
class Reference : public RefTracker {
442394
public:
443395
static Reference* New(napi_env env,
444396
v8::Local<v8::Value> value,
445397
uint32_t initial_refcount,
446-
Ownership ownership,
447-
napi_finalize finalize_callback = nullptr,
448-
void* finalize_data = nullptr,
449-
void* finalize_hint = nullptr);
398+
ReferenceOwnership ownership);
399+
~Reference() override;
450400

451-
virtual ~Reference();
452401
uint32_t Ref();
453402
uint32_t Unref();
454-
v8::Local<v8::Value> Get();
403+
v8::Local<v8::Value> Get(napi_env env);
404+
405+
virtual void ResetFinalizer() {}
406+
virtual void* Data() { return nullptr; }
407+
408+
uint32_t refcount() const { return refcount_; }
409+
ReferenceOwnership ownership() { return ownership_; }
455410

456411
protected:
457-
void Finalize() override;
412+
Reference(napi_env env,
413+
v8::Local<v8::Value> value,
414+
uint32_t initial_refcount,
415+
ReferenceOwnership ownership);
416+
virtual void CallUserFinalizer() {}
417+
virtual void InvokeFinalizerFromGC();
458418

459419
private:
460420
static void WeakCallback(const v8::WeakCallbackInfo<Reference>& data);
461-
462421
void SetWeak();
422+
void Finalize() override;
463423

424+
private:
464425
v8impl::Persistent<v8::Value> persistent_;
426+
uint32_t refcount_;
427+
ReferenceOwnership ownership_;
465428
bool can_be_weak_;
466429
};
467430

431+
// Reference that can store additional data.
432+
class ReferenceWithData final : public Reference {
433+
public:
434+
static ReferenceWithData* New(napi_env env,
435+
v8::Local<v8::Value> value,
436+
uint32_t initial_refcount,
437+
ReferenceOwnership ownership,
438+
void* data);
439+
440+
void* Data() override { return data_; }
441+
442+
private:
443+
ReferenceWithData(napi_env env,
444+
v8::Local<v8::Value> value,
445+
uint32_t initial_refcount,
446+
ReferenceOwnership ownership,
447+
void* data);
448+
449+
private:
450+
void* data_;
451+
};
452+
453+
// Reference that has a user finalizer callback.
454+
class ReferenceWithFinalizer final : public Reference {
455+
public:
456+
static ReferenceWithFinalizer* New(napi_env env,
457+
v8::Local<v8::Value> value,
458+
uint32_t initial_refcount,
459+
ReferenceOwnership ownership,
460+
napi_finalize finalize_callback,
461+
void* finalize_data,
462+
void* finalize_hint);
463+
~ReferenceWithFinalizer() override;
464+
465+
void ResetFinalizer() override { finalizer_.ResetFinalizer(); }
466+
void* Data() override { return finalizer_.data(); }
467+
468+
private:
469+
ReferenceWithFinalizer(napi_env env,
470+
v8::Local<v8::Value> value,
471+
uint32_t initial_refcount,
472+
ReferenceOwnership ownership,
473+
napi_finalize finalize_callback,
474+
void* finalize_data,
475+
void* finalize_hint);
476+
void CallUserFinalizer() override;
477+
void InvokeFinalizerFromGC() override;
478+
479+
private:
480+
Finalizer finalizer_;
481+
};
482+
468483
} // end of namespace v8impl
469484

470485
#endif // SRC_JS_NATIVE_API_V8_H_

‎src/node_api.cc

+7-12
Original file line numberDiff line numberDiff line change
@@ -121,23 +121,18 @@ namespace {
121121
class BufferFinalizer : private Finalizer {
122122
public:
123123
static BufferFinalizer* New(napi_env env,
124-
napi_finalize finalize_callback = nullptr,
125-
void* finalize_data = nullptr,
126-
void* finalize_hint = nullptr) {
124+
napi_finalize finalize_callback,
125+
void* finalize_data,
126+
void* finalize_hint) {
127127
return new BufferFinalizer(
128128
env, finalize_callback, finalize_data, finalize_hint);
129129
}
130130
// node::Buffer::FreeCallback
131131
static void FinalizeBufferCallback(char* data, void* hint) {
132132
std::unique_ptr<BufferFinalizer, Deleter> finalizer{
133133
static_cast<BufferFinalizer*>(hint)};
134-
finalizer->finalize_data_ = data;
135-
136134
// It is safe to call into JavaScript at this point.
137-
if (finalizer->finalize_callback_ == nullptr) return;
138-
finalizer->env_->CallFinalizer(finalizer->finalize_callback_,
139-
finalizer->finalize_data_,
140-
finalizer->finalize_hint_);
135+
finalizer->CallFinalizer();
141136
}
142137

143138
struct Deleter {
@@ -150,10 +145,10 @@ class BufferFinalizer : private Finalizer {
150145
void* finalize_data,
151146
void* finalize_hint)
152147
: Finalizer(env, finalize_callback, finalize_data, finalize_hint) {
153-
env_->Ref();
148+
env->Ref();
154149
}
155150

156-
~BufferFinalizer() { env_->Unref(); }
151+
~BufferFinalizer() { env()->Unref(); }
157152
};
158153

159154
void ThrowNodeApiVersionError(node::Environment* node_env,
@@ -1057,7 +1052,7 @@ napi_create_external_buffer(napi_env env,
10571052

10581053
// The finalizer object will delete itself after invoking the callback.
10591054
v8impl::BufferFinalizer* finalizer =
1060-
v8impl::BufferFinalizer::New(env, finalize_cb, nullptr, finalize_hint);
1055+
v8impl::BufferFinalizer::New(env, finalize_cb, data, finalize_hint);
10611056

10621057
v8::MaybeLocal<v8::Object> maybe =
10631058
node::Buffer::New(isolate,

0 commit comments

Comments
 (0)
Please sign in to comment.