From 89fa0f343945e61d5e4a0077cc7e93a802ed56e7 Mon Sep 17 00:00:00 2001 From: Dylan Conway Date: Tue, 21 Oct 2025 22:58:46 -0700 Subject: [PATCH] Refactor napi_env to use Ref-counted NapiEnv (#23940) ### What does this PR do? Replaces raw napi_env pointers with WTF::Ref 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? --- src/bun.js/api/FFI.h | 4 +- src/bun.js/bindings/BunProcess.cpp | 4 +- src/bun.js/bindings/NapiRef.cpp | 2 +- src/bun.js/bindings/ZigGlobalObject.cpp | 8 +- src/bun.js/bindings/ZigGlobalObject.h | 4 +- src/bun.js/bindings/napi.cpp | 32 +++-- src/bun.js/bindings/napi.h | 29 ++-- src/bun.js/bindings/napi_external.cpp | 6 +- src/bun.js/bindings/napi_external.h | 14 +- src/bun.js/bindings/napi_finalizer.cpp | 6 +- src/bun.js/bindings/napi_finalizer.h | 2 +- src/bun.js/bindings/napi_handle_scope.h | 2 +- src/napi/js_native_api_types.h | 182 ++++++++++++------------ src/napi/napi.zig | 12 ++ 14 files changed, 168 insertions(+), 139 deletions(-) diff --git a/src/bun.js/api/FFI.h b/src/bun.js/api/FFI.h index 6ca644a1e2..c3796712aa 100644 --- a/src/bun.js/api/FFI.h +++ b/src/bun.js/api/FFI.h @@ -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 diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 9ce22609dc..11ccf98275 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -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(napi_register_module_v1(env, reinterpret_cast(exportsValue))); + auto encoded = reinterpret_cast(napi_register_module_v1(env.ptr(), reinterpret_cast(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, {}); diff --git a/src/bun.js/bindings/NapiRef.cpp b/src/bun.js/bindings/NapiRef.cpp index d33ac46cef..03660630b9 100644 --- a/src/bun.js/bindings/NapiRef.cpp +++ b/src/bun.js/bindings/NapiRef.cpp @@ -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(); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 0311fd6f3e..a2f8b35b4c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -3517,10 +3517,10 @@ GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction h } } -napi_env GlobalObject::makeNapiEnv(const napi_module& mod) +Ref GlobalObject::makeNapiEnv(const napi_module& mod) { - m_napiEnvs.append(std::make_unique(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 diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 6366c7ba92..937da8c906 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -724,8 +724,8 @@ public: // De-optimization once `require("module").runMain` is written to bool hasOverriddenModuleRunMain = false; - WTF::Vector> m_napiEnvs; - napi_env makeNapiEnv(const napi_module&); + WTF::Vector> m_napiEnvs; + Ref makeNapiEnv(const napi_module&); napi_env makeNapiEnvForFFI(); bool hasNapiFinalizers() const; diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index b06b4a2b6f..cc8f63465f 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -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 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(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(*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(data), length }, createSharedTask([env, finalize_hint, finalize_cb](void* p) { + auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(data), length }, createSharedTask([env = WTF::Ref(*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(); +} + } diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index f5a54314fe..56346cd019 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -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 { + 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 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 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&& env, uint32_t count, Bun::NapiFinalizer finalizer) : env(env) , globalObject(JSC::Weak(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 env; JSC::Weak globalObject; NapiWeakValue weakValueRef; JSC::Strong 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; diff --git a/src/bun.js/bindings/napi_external.cpp b/src/bun.js/bindings/napi_external.cpp index c303c85c4b..239ba8c2fe 100644 --- a/src/bun.js/bindings/napi_external.cpp +++ b/src/bun.js/bindings/napi_external.cpp @@ -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(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) }; } diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index c34a4272a7..2d104fceb8 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -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 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 env = nullptr) { - NapiExternal* accessor = new (NotNull, JSC::allocateCell(vm)) NapiExternal(vm, structure); + NapiExternal* accessor = new (NotNull, JSC::allocateCell(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 m_env; #if ASSERT_ENABLED String sourceOriginURL = String(); diff --git a/src/bun.js/bindings/napi_finalizer.cpp b/src/bun.js/bindings/napi_finalizer.cpp index cc2c25ea09..afc566f8b2 100644 --- a/src/bun.js/bindings/napi_finalizer.cpp +++ b/src/bun.js/bindings/napi_finalizer.cpp @@ -5,14 +5,14 @@ namespace Bun { -void NapiFinalizer::call(napi_env env, void* data, bool immediate) +void NapiFinalizer::call(WTF::RefPtr 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); } } } diff --git a/src/bun.js/bindings/napi_finalizer.h b/src/bun.js/bindings/napi_finalizer.h index 65d4bbccfa..4bf6e08382 100644 --- a/src/bun.js/bindings/napi_finalizer.h +++ b/src/bun.js/bindings/napi_finalizer.h @@ -17,7 +17,7 @@ public: NapiFinalizer() = default; - void call(napi_env env, void* data, bool immediate = false); + void call(WTF::RefPtr env, void* data, bool immediate = false); void clear(); inline napi_finalize callback() const { return m_callback; } diff --git a/src/bun.js/bindings/napi_handle_scope.h b/src/bun.js/bindings/napi_handle_scope.h index a8dcc14a6f..a004716637 100644 --- a/src/bun.js/bindings/napi_handle_scope.h +++ b/src/bun.js/bindings/napi_handle_scope.h @@ -3,7 +3,7 @@ #include "BunClientData.h" #include "root.h" -typedef struct napi_env__* napi_env; +typedef struct NapiEnv* napi_env; namespace Bun { diff --git a/src/napi/js_native_api_types.h b/src/napi/js_native_api_types.h index 16f09afe13..9341e7b91a 100644 --- a/src/napi/js_native_api_types.h +++ b/src/napi/js_native_api_types.h @@ -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 diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 708b453b1c..1be2beb975 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -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();