Refactor napi_env to use Ref-counted NapiEnv (#23940)

### What does this PR do?

Replaces raw napi_env pointers with WTF::Ref<NapiEnv> for improved
memory management and safety. Updates related classes, function
signatures, and finalizer handling to use reference counting. Adds
ref/deref methods to NapiEnv and integrates them in Zig and C++ code
paths, ensuring proper lifecycle management for N-API environments.

### How did you verify your code works?
This commit is contained in:
Dylan Conway
2025-10-21 22:58:46 -07:00
committed by GitHub
parent 72f1ffdaf7
commit 89fa0f3439
14 changed files with 168 additions and 139 deletions

View File

@@ -39,7 +39,7 @@ typedef _Bool bool;
#define false 0
#ifndef SRC_JS_NATIVE_API_TYPES_H_
typedef struct napi_env__ *napi_env;
typedef struct NapiEnv *napi_env;
typedef int64_t napi_value;
typedef enum {
napi_ok,
@@ -67,7 +67,7 @@ typedef enum {
} napi_status;
BUN_FFI_IMPORT void* NapiHandleScope__open(void* napi_env, bool detached);
BUN_FFI_IMPORT void NapiHandleScope__close(void* napi_env, void* handleScope);
BUN_FFI_IMPORT extern struct napi_env__ Bun__thisFFIModuleNapiEnv;
BUN_FFI_IMPORT extern struct NapiEnv Bun__thisFFIModuleNapiEnv;
#endif

View File

@@ -637,7 +637,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
auto env = globalObject->makeNapiEnv(nmodule);
env->filename = filename_cstr;
auto encoded = reinterpret_cast<EncodedJSValue>(napi_register_module_v1(env, reinterpret_cast<napi_value>(exportsValue)));
auto encoded = reinterpret_cast<EncodedJSValue>(napi_register_module_v1(env.ptr(), reinterpret_cast<napi_value>(exportsValue)));
if (env->throwPendingException()) {
return {};
}
@@ -656,7 +656,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
// TODO: think about the finalizer here
// currently we do not dealloc napi modules so we don't have to worry about it right now
auto* meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle);
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, env, nullptr);
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr, env.ptr());
bool success = resultObject->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
ASSERT(success);
RETURN_IF_EXCEPTION(scope, {});

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.ptr(), nativeObject);
globalObject.clear();
weakValueRef.clear();
strongRef.clear();

View File

@@ -3517,10 +3517,10 @@ GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction h
}
}
napi_env GlobalObject::makeNapiEnv(const napi_module& mod)
Ref<NapiEnv> GlobalObject::makeNapiEnv(const napi_module& mod)
{
m_napiEnvs.append(std::make_unique<napi_env__>(this, mod));
return m_napiEnvs.last().get();
m_napiEnvs.append(NapiEnv::create(this, mod));
return m_napiEnvs.last();
}
napi_env GlobalObject::makeNapiEnvForFFI()
@@ -3534,7 +3534,7 @@ napi_env GlobalObject::makeNapiEnvForFFI()
.nm_priv = nullptr,
.reserved = {},
});
return out;
return &out.leakRef();
}
bool GlobalObject::hasNapiFinalizers() const

View File

@@ -724,8 +724,8 @@ public:
// De-optimization once `require("module").runMain` is written to
bool hasOverriddenModuleRunMain = false;
WTF::Vector<std::unique_ptr<napi_env__>> m_napiEnvs;
napi_env makeNapiEnv(const napi_module&);
WTF::Vector<WTF::Ref<NapiEnv>> m_napiEnvs;
Ref<NapiEnv> makeNapiEnv(const napi_module&);
napi_env makeNapiEnvForFFI();
bool hasNapiFinalizers() const;

View File

@@ -698,7 +698,7 @@ void Napi::executePendingNapiModule(Zig::GlobalObject* globalObject)
ASSERT(globalObject->m_pendingNapiModule);
auto& mod = *globalObject->m_pendingNapiModule;
napi_env env = globalObject->makeNapiEnv(mod);
Ref<NapiEnv> env = globalObject->makeNapiEnv(mod);
auto keyStr = WTF::String::fromUTF8(mod.nm_modname);
JSValue pendingNapiModule = globalObject->m_pendingNapiModuleAndExports[0].get();
JSObject* object = (pendingNapiModule && pendingNapiModule.isObject()) ? pendingNapiModule.getObject()
@@ -727,7 +727,7 @@ void Napi::executePendingNapiModule(Zig::GlobalObject* globalObject)
JSValue resultValue;
if (mod.nm_register_func) {
resultValue = toJS(mod.nm_register_func(env, toNapi(object, globalObject)));
resultValue = toJS(mod.nm_register_func(env.ptr(), toNapi(object, globalObject)));
} else {
JSValue errorInstance = createError(globalObject, makeString("Module has no declared entry point."_s));
globalObject->m_pendingNapiModuleAndExports[0].set(vm, globalObject, errorInstance);
@@ -751,7 +751,7 @@ void Napi::executePendingNapiModule(Zig::GlobalObject* globalObject)
auto* meta = new Bun::NapiModuleMeta(globalObject->m_pendingNapiModuleDlopenHandle);
// TODO: think about the finalizer here
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, env, nullptr);
Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), meta, nullptr, nullptr, env.ptr());
bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
ASSERT(success);
@@ -791,7 +791,7 @@ static void wrap_cleanup(napi_env env, void* data, void* hint)
{
auto* ref = reinterpret_cast<NapiRef*>(data);
ASSERT(ref->boundCleanup != nullptr);
ref->boundCleanup->deactivate(env);
ref->boundCleanup->deactivate(*env);
ref->boundCleanup = nullptr;
ref->callFinalizer();
}
@@ -842,7 +842,7 @@ extern "C" napi_status napi_wrap(napi_env env,
NAPI_RETURN_EARLY_IF_FALSE(env, existing_wrap == nullptr, napi_invalid_arg);
// create a new weak reference (refcount 0)
auto* ref = new NapiRef(env, 0, Bun::NapiFinalizer { finalize_cb, finalize_hint });
auto* ref = new NapiRef(*env, 0, Bun::NapiFinalizer { finalize_cb, finalize_hint });
// In case the ref's finalizer is never called, we'll add a finalizer to execute on exit.
const auto& bound_cleanup = env->addFinalizer(wrap_cleanup, native_object, ref);
ref->boundCleanup = &bound_cleanup;
@@ -852,7 +852,7 @@ extern "C" napi_status napi_wrap(napi_env env,
napi_instance->napiRef = ref;
} else {
// wrap the ref in an external so that it can serve as a JSValue
auto* external = Bun::NapiExternal::create(JSC::getVM(globalObject), globalObject->NapiExternalStructure(), ref, nullptr, env, nullptr);
auto* external = Bun::NapiExternal::create(JSC::getVM(globalObject), globalObject->NapiExternalStructure(), ref, nullptr, nullptr, env);
jsc_object->putDirect(vm, propertyName, JSValue(external));
}
@@ -1082,7 +1082,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
can_be_weak = false;
}
auto* ref = new NapiRef(env, initial_refcount, Bun::NapiFinalizer {});
auto* ref = new NapiRef(*env, initial_refcount, Bun::NapiFinalizer {});
ref->setValueInitial(val, can_be_weak);
*result = toNapi(ref);
@@ -1119,14 +1119,14 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object,
if (result) {
// If they're expecting a Ref, use the ref.
auto* ref = new NapiRef(env, 0, Bun::NapiFinalizer { finalize_cb, finalize_hint });
auto* ref = new NapiRef(*env, 0, Bun::NapiFinalizer { finalize_cb, finalize_hint });
// TODO(@heimskr): consider detecting whether the value can't be weak, as we do in napi_create_reference.
ref->setValueInitial(object, true);
ref->nativeObject = native_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 {
vm.heap.addFinalizer(object, [env = WTF::Ref<NapiEnv>(*env), finalize_cb, native_object, finalize_hint](JSCell* cell) -> void {
NAPI_LOG("finalizer %p", finalize_hint);
env->doFinalizer(finalize_cb, native_object, finalize_hint);
});
@@ -1991,7 +1991,7 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length,
Zig::GlobalObject* globalObject = toJS(env);
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(data), length }, createSharedTask<void(void*)>([env, finalize_hint, finalize_cb](void* p) {
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(data), length }, createSharedTask<void(void*)>([env = WTF::Ref<NapiEnv>(*env), finalize_hint, finalize_cb](void* p) {
NAPI_LOG("external buffer finalizer");
env->doFinalizer(finalize_cb, p, finalize_hint);
}));
@@ -2303,7 +2303,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
JSC::VM& vm = JSC::getVM(globalObject);
auto* structure = globalObject->NapiExternalStructure();
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, env, finalize_cb);
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, finalize_cb, env);
JSC::EnsureStillAliveScope ensureStillAlive(value);
*result = toNapi(value, globalObject);
NAPI_RETURN_SUCCESS(env);
@@ -2902,4 +2902,14 @@ extern "C" bool NapiEnv__getAndClearPendingException(napi_env env, JSC::EncodedJ
return false;
}
extern "C" void NapiEnv__ref(napi_env env)
{
env->ref();
}
extern "C" void NapiEnv__deref(napi_env env)
{
env->deref();
}
}

View File

@@ -168,9 +168,11 @@ 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 NapiEnv : public WTF::RefCounted<NapiEnv> {
WTF_MAKE_STRUCT_TZONE_ALLOCATED(NapiEnv);
public:
napi_env__(Zig::GlobalObject* globalObject, const napi_module& napiModule)
NapiEnv(Zig::GlobalObject* globalObject, const napi_module& napiModule)
: m_globalObject(globalObject)
, m_napiModule(napiModule)
, m_vm(JSC::getVM(globalObject))
@@ -178,7 +180,12 @@ public:
napi_internal_register_cleanup_zig(this);
}
~napi_env__()
static Ref<NapiEnv> create(Zig::GlobalObject* globalObject, const napi_module& napiModule)
{
return adoptRef(*new NapiEnv(globalObject, napiModule));
}
~NapiEnv()
{
delete[] filename;
}
@@ -434,12 +441,12 @@ public:
}
}
void deactivate(napi_env env) const
void deactivate(NapiEnv& env) const
{
if (env->isFinishingFinalizers()) {
if (env.isFinishingFinalizers()) {
active = false;
} else {
env->removeFinalizer(*this);
env.removeFinalizer(*this);
// At this point the BoundFinalizer has been destroyed, but because we're not doing anything else here it's safe.
// https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
}
@@ -451,7 +458,7 @@ public:
}
struct Hash {
std::size_t operator()(const napi_env__::BoundFinalizer& bound) const
std::size_t operator()(const NapiEnv::BoundFinalizer& bound) const
{
constexpr std::hash<void*> hasher;
constexpr std::ptrdiff_t magic = 0x9e3779b9;
@@ -659,7 +666,7 @@ public:
void unref();
void clear();
NapiRef(napi_env env, uint32_t count, Bun::NapiFinalizer finalizer)
NapiRef(Ref<NapiEnv>&& env, uint32_t count, Bun::NapiFinalizer finalizer)
: env(env)
, globalObject(JSC::Weak<JSC::JSGlobalObject>(env->globalObject()))
, finalizer(WTFMove(finalizer))
@@ -708,7 +715,7 @@ 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.ptr(), nativeObject, !env->mustDeferFinalizers() || !env->inGC());
}
~NapiRef()
@@ -728,12 +735,12 @@ public:
weakValueRef.clear();
}
napi_env env = nullptr;
WTF::Ref<NapiEnv> env;
JSC::Weak<JSC::JSGlobalObject> globalObject;
NapiWeakValue weakValueRef;
JSC::Strong<JSC::Unknown> strongRef;
Bun::NapiFinalizer finalizer;
const napi_env__::BoundFinalizer* boundCleanup = nullptr;
const NapiEnv::BoundFinalizer* boundCleanup = nullptr;
void* nativeObject = nullptr;
uint32_t refCount = 0;
bool releaseOnWeaken = false;

View File

@@ -5,8 +5,8 @@ namespace Bun {
NapiExternal::~NapiExternal()
{
ASSERT(m_env);
m_finalizer.call(m_env, m_value, !m_env->mustDeferFinalizers());
auto* env = m_env.get();
m_finalizer.call(env, m_value, env && !env->mustDeferFinalizers());
}
void NapiExternal::destroy(JSC::JSCell* cell)
@@ -14,6 +14,6 @@ void NapiExternal::destroy(JSC::JSCell* cell)
static_cast<NapiExternal*>(cell)->~NapiExternal();
}
const ClassInfo NapiExternal::s_info = { "External"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NapiExternal) };
const ClassInfo NapiExternal::s_info = { "NapiExternal"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(NapiExternal) };
}

View File

@@ -22,8 +22,9 @@ class NapiExternal : public JSC::JSDestructibleObject {
using Base = JSC::JSDestructibleObject;
public:
NapiExternal(JSC::VM& vm, JSC::Structure* structure)
NapiExternal(JSC::VM& vm, JSC::Structure* structure, WTF::RefPtr<NapiEnv> env)
: Base(vm, structure)
, m_env(env)
{
}
@@ -53,11 +54,11 @@ public:
JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, napi_env env, napi_finalize callback)
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, napi_finalize callback, WTF::RefPtr<NapiEnv> env = nullptr)
{
NapiExternal* accessor = new (NotNull, JSC::allocateCell<NapiExternal>(vm)) NapiExternal(vm, structure);
NapiExternal* accessor = new (NotNull, JSC::allocateCell<NapiExternal>(vm)) NapiExternal(vm, structure, env);
accessor->finishCreation(vm, value, finalizer_hint, env, callback);
accessor->finishCreation(vm, value, finalizer_hint, callback);
#if ASSERT_ENABLED
if (auto* callFrame = vm.topCallFrame) {
@@ -81,11 +82,10 @@ public:
return accessor;
}
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, napi_env env, napi_finalize callback)
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, napi_finalize callback)
{
Base::finishCreation(vm);
m_value = value;
m_env = env;
m_finalizer = NapiFinalizer { callback, finalizer_hint };
}
@@ -95,7 +95,7 @@ public:
void* m_value;
NapiFinalizer m_finalizer;
napi_env m_env;
WTF::RefPtr<NapiEnv> m_env;
#if ASSERT_ENABLED
String sourceOriginURL = String();

View File

@@ -5,14 +5,14 @@
namespace Bun {
void NapiFinalizer::call(napi_env env, void* data, bool immediate)
void NapiFinalizer::call(WTF::RefPtr<NapiEnv> env, void* data, bool immediate)
{
if (m_callback) {
NAPI_LOG_CURRENT_FUNCTION;
if (immediate) {
m_callback(env, data, m_hint);
m_callback(env.get(), data, m_hint);
} else {
napi_internal_enqueue_finalizer(env, m_callback, data, m_hint);
napi_internal_enqueue_finalizer(env.get(), m_callback, data, m_hint);
}
}
}

View File

@@ -17,7 +17,7 @@ public:
NapiFinalizer() = default;
void call(napi_env env, void* data, bool immediate = false);
void call(WTF::RefPtr<NapiEnv> env, void* data, bool immediate = false);
void clear();
inline napi_finalize callback() const { return m_callback; }

View File

@@ -3,7 +3,7 @@
#include "BunClientData.h"
#include "root.h"
typedef struct napi_env__* napi_env;
typedef struct NapiEnv* napi_env;
namespace Bun {

View File

@@ -13,86 +13,86 @@ typedef uint16_t char16_t;
// JSVM API types are all opaque pointers for ABI stability
// typedef undefined structs instead of void* for compile time type safety
typedef struct napi_env__ *napi_env;
typedef struct napi_value__ *napi_value;
typedef struct napi_ref__ *napi_ref;
typedef struct napi_handle_scope__ *napi_handle_scope;
typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope;
typedef struct napi_callback_info__ *napi_callback_info;
typedef struct napi_deferred__ *napi_deferred;
typedef struct NapiEnv* napi_env;
typedef struct napi_value__* napi_value;
typedef struct napi_ref__* napi_ref;
typedef struct napi_handle_scope__* napi_handle_scope;
typedef struct napi_escapable_handle_scope__* napi_escapable_handle_scope;
typedef struct napi_callback_info__* napi_callback_info;
typedef struct napi_deferred__* napi_deferred;
typedef enum {
napi_default = 0,
napi_writable = 1 << 0,
napi_enumerable = 1 << 1,
napi_configurable = 1 << 2,
napi_default = 0,
napi_writable = 1 << 0,
napi_enumerable = 1 << 1,
napi_configurable = 1 << 2,
// Used with napi_define_class to distinguish static properties
// from instance properties. Ignored by napi_define_properties.
napi_static = 1 << 10,
// Used with napi_define_class to distinguish static properties
// from instance properties. Ignored by napi_define_properties.
napi_static = 1 << 10,
#if NAPI_VERSION >= 8
// Default for class methods.
napi_default_method = napi_writable | napi_configurable,
// Default for class methods.
napi_default_method = napi_writable | napi_configurable,
// Default for object properties, like in JS obj[prop].
napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable,
// Default for object properties, like in JS obj[prop].
napi_default_jsproperty = napi_writable | napi_enumerable | napi_configurable,
#endif // NAPI_VERSION >= 8
} napi_property_attributes;
typedef enum {
// ES6 types (corresponds to typeof)
napi_undefined,
napi_null,
napi_boolean,
napi_number,
napi_string,
napi_symbol,
napi_object,
napi_function,
napi_external,
napi_bigint,
// ES6 types (corresponds to typeof)
napi_undefined,
napi_null,
napi_boolean,
napi_number,
napi_string,
napi_symbol,
napi_object,
napi_function,
napi_external,
napi_bigint,
} napi_valuetype;
typedef enum {
napi_int8_array,
napi_uint8_array,
napi_uint8_clamped_array,
napi_int16_array,
napi_uint16_array,
napi_int32_array,
napi_uint32_array,
napi_float32_array,
napi_float64_array,
napi_bigint64_array,
napi_biguint64_array,
napi_int8_array,
napi_uint8_array,
napi_uint8_clamped_array,
napi_int16_array,
napi_uint16_array,
napi_int32_array,
napi_uint32_array,
napi_float32_array,
napi_float64_array,
napi_bigint64_array,
napi_biguint64_array,
} napi_typedarray_type;
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock, // unused
napi_no_external_buffers_allowed,
napi_cannot_run_js,
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
napi_date_expected,
napi_arraybuffer_expected,
napi_detachable_arraybuffer_expected,
napi_would_deadlock, // unused
napi_no_external_buffers_allowed,
napi_cannot_run_js,
} napi_status;
// Note: when adding a new enum value to `napi_status`, please also update
// * `constexpr int last_status` in the definition of `napi_get_last_error_info()'
@@ -101,55 +101,55 @@ typedef enum {
// message explaining the error.
typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info);
typedef void (*napi_finalize)(napi_env env, void *finalize_data,
void *finalize_hint);
typedef void (*napi_finalize)(napi_env env, void* finalize_data,
void* finalize_hint);
typedef struct {
// One of utf8name or name should be NULL.
const char *utf8name;
napi_value name;
// One of utf8name or name should be NULL.
const char* utf8name;
napi_value name;
napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;
napi_callback method;
napi_callback getter;
napi_callback setter;
napi_value value;
napi_property_attributes attributes;
void *data;
napi_property_attributes attributes;
void* data;
} napi_property_descriptor;
typedef struct {
const char *error_message;
void *engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
const char* error_message;
void* engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
} napi_extended_error_info;
#if NAPI_VERSION >= 6
typedef enum {
napi_key_include_prototypes,
napi_key_own_only
napi_key_include_prototypes,
napi_key_own_only
} napi_key_collection_mode;
typedef enum {
napi_key_all_properties = 0,
napi_key_writable = 1,
napi_key_enumerable = 1 << 1,
napi_key_configurable = 1 << 2,
napi_key_skip_strings = 1 << 3,
napi_key_skip_symbols = 1 << 4
napi_key_all_properties = 0,
napi_key_writable = 1,
napi_key_enumerable = 1 << 1,
napi_key_configurable = 1 << 2,
napi_key_skip_strings = 1 << 3,
napi_key_skip_symbols = 1 << 4
} napi_key_filter;
typedef enum {
napi_key_keep_numbers,
napi_key_numbers_to_strings
napi_key_keep_numbers,
napi_key_numbers_to_strings
} napi_key_conversion;
#endif // NAPI_VERSION >= 6
#if NAPI_VERSION >= 8
typedef struct {
uint64_t lower;
uint64_t upper;
uint64_t lower;
uint64_t upper;
} napi_type_tag;
#endif // NAPI_VERSION >= 8

View File

@@ -55,9 +55,19 @@ pub const NapiEnv = opaque {
return null;
}
pub fn ref(self: *NapiEnv) void {
NapiEnv__ref(self);
}
pub fn deref(self: *NapiEnv) void {
NapiEnv__deref(self);
}
extern fn NapiEnv__globalObject(*NapiEnv) *jsc.JSGlobalObject;
extern fn NapiEnv__getAndClearPendingException(*NapiEnv, *JSValue) bool;
extern fn napi_internal_get_version(*NapiEnv) u32;
extern fn NapiEnv__deref(*NapiEnv) void;
extern fn NapiEnv__ref(*NapiEnv) void;
};
fn envIsNull() napi_status {
@@ -1660,6 +1670,7 @@ pub const ThreadSafeFunction = struct {
this.callback.deinit();
this.queue.deinit();
this.env.deref();
bun.destroy(this);
}
@@ -1757,6 +1768,7 @@ pub export fn napi_create_threadsafe_function(
// nodejs by default keeps the event loop alive until the thread-safe function is unref'd
function.ref();
function.tracker.didSchedule(vm.global);
function.env.ref();
result.* = function;
return env.ok();