Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
5afd9a401d Implement RefCounted for napi_env
This change makes `napi_env__` extend `RefCounted<napi_env__>` and uses
`RefPtr<napi_env__>` throughout the codebase, following the RAII pattern
for automatic reference counting.

Changes:
- Make `struct napi_env__` extend `RefCounted<napi_env__>`
- Add `using NapiEnv = WTF::RefPtr<napi_env__>` typedef
- Update `NapiRef` to store `NapiEnv` instead of raw `napi_env`
- Update `ZigGlobalObject::m_napiEnvs` to use `Vector<NapiEnv>`
- Update `makeNapiEnv()` to use `adoptRef()` instead of `make_unique()`
- Update finalizer lambda to capture `NapiEnv` via RefPtr for automatic ref/unref
- Update all usages to call `.get()` when accessing the raw pointer

This enables automatic reference counting via RAII - RefPtr handles
ref/unref through constructors/destructors, eliminating the need for
manual reference management.
2025-10-15 23:12:33 +00:00
5 changed files with 19 additions and 12 deletions

View File

@@ -37,7 +37,7 @@ void NapiRef::unref()
void NapiRef::clear()
{
NAPI_LOG("ref clear %p", this);
finalizer.call(env, nativeObject);
finalizer.call(env.get(), nativeObject);
globalObject.clear();
weakValueRef.clear();
strongRef.clear();

View File

@@ -3519,7 +3519,7 @@ GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction h
napi_env GlobalObject::makeNapiEnv(const napi_module& mod)
{
m_napiEnvs.append(std::make_unique<napi_env__>(this, mod));
m_napiEnvs.append(adoptRef(new napi_env__(this, mod)));
return m_napiEnvs.last().get();
}

View File

@@ -60,6 +60,9 @@ class GlobalInternals;
#include "BakeAdditionsToGlobalObject.h"
#include "WriteBarrierList.h"
struct napi_env__;
using NapiEnv = WTF::RefPtr<napi_env__>;
namespace Bun {
class JSCommonJSExtensions;
class InternalModuleRegistry;
@@ -724,7 +727,7 @@ public:
// De-optimization once `require("module").runMain` is written to
bool hasOverriddenModuleRunMain = false;
WTF::Vector<std::unique_ptr<napi_env__>> m_napiEnvs;
WTF::Vector<NapiEnv> m_napiEnvs;
napi_env makeNapiEnv(const napi_module&);
napi_env makeNapiEnvForFFI();
bool hasNapiFinalizers() const;

View File

@@ -1126,9 +1126,11 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
*result = toNapi(ref);
} else {
// Otherwise, it's cheaper to just call .addFinalizer.
vm.heap.addFinalizer(object, [env, finalize_cb, native_object, finalize_hint](JSCell* cell) -> void {
// Capture env as RefPtr - automatically refs on capture, unrefs on destruction
NapiEnv envRef = env;
vm.heap.addFinalizer(object, [envRef = WTFMove(envRef), finalize_cb, native_object, finalize_hint](JSCell* cell) -> void {
NAPI_LOG("finalizer %p", finalize_hint);
env->doFinalizer(finalize_cb, native_object, finalize_hint);
envRef->doFinalizer(finalize_cb, native_object, finalize_hint);
});
}

View File

@@ -168,7 +168,7 @@ static bool equal(napi_async_cleanup_hook_handle one, napi_async_cleanup_hook_ha
} while (0)
// Named this way so we can manipulate napi_env values directly (since napi_env is defined as a pointer to struct napi_env__)
struct napi_env__ {
struct napi_env__ : public RefCounted<napi_env__> {
public:
napi_env__(Zig::GlobalObject* globalObject, const napi_module& napiModule)
: m_globalObject(globalObject)
@@ -506,6 +506,8 @@ private:
}
};
using NapiEnv = WTF::RefPtr<napi_env__>;
extern "C" void napi_internal_cleanup_env_cpp(napi_env);
extern "C" void napi_internal_remove_finalizer(napi_env, napi_finalize callback, void* hint, void* data);
@@ -659,9 +661,9 @@ public:
void unref();
void clear();
NapiRef(napi_env env, uint32_t count, Bun::NapiFinalizer finalizer)
: env(env)
, globalObject(JSC::Weak<JSC::JSGlobalObject>(env->globalObject()))
NapiRef(napi_env rawEnv, uint32_t count, Bun::NapiFinalizer finalizer)
: env(rawEnv)
, globalObject(JSC::Weak<JSC::JSGlobalObject>(rawEnv->globalObject()))
, finalizer(WTFMove(finalizer))
, refCount(count)
{
@@ -708,14 +710,14 @@ public:
// calling the finalizer
Bun::NapiFinalizer saved_finalizer = this->finalizer;
this->finalizer.clear();
saved_finalizer.call(env, nativeObject, !env->mustDeferFinalizers() || !env->inGC());
saved_finalizer.call(env.get(), nativeObject, !env->mustDeferFinalizers() || !env->inGC());
}
~NapiRef()
{
NAPI_LOG("destruct napi ref %p", this);
if (boundCleanup) {
boundCleanup->deactivate(env);
boundCleanup->deactivate(env.get());
boundCleanup = nullptr;
}
@@ -728,7 +730,7 @@ public:
weakValueRef.clear();
}
napi_env env = nullptr;
NapiEnv env = nullptr;
JSC::Weak<JSC::JSGlobalObject> globalObject;
NapiWeakValue weakValueRef;
JSC::Strong<JSC::Unknown> strongRef;