Compare commits

...

9 Commits

Author SHA1 Message Date
Kai Tamkun
1a7a70ff9f More accurate signature for napi_register_module_v1 2024-12-02 14:05:00 -08:00
Kai Tamkun
fd0eb10b76 Merge branch 'main' into kai/napi-error-handling 2024-12-02 12:41:22 -08:00
Kai Tamkun
e848f7a85b Store last NAPI error info 2024-11-27 15:31:39 -08:00
Kai Tamkun
f99d1bce54 Don't undef dlsym on Windows too early 2024-11-26 17:56:02 -08:00
Kai Tamkun
57a3f839e5 Move instance data to napi envs 2024-11-26 17:47:20 -08:00
Kai Tamkun
b98fe402be Make constructors work 2024-11-26 17:35:22 -08:00
Kai Tamkun
85499c8709 Splitting napi envs mostly works now, save for one napi test 2024-11-26 17:22:29 -08:00
Kai Tamkun
bfc423eeb9 More work on splitting napi envs 2024-11-26 16:08:01 -08:00
Kai Tamkun
9e9d137ca2 Initial work on splitting up napi envs 2024-11-26 14:42:32 -08:00
13 changed files with 898 additions and 503 deletions

View File

@@ -73,6 +73,8 @@ const URL = @import("../../url.zig").URL;
const VirtualMachine = JSC.VirtualMachine;
const IOTask = JSC.IOTask;
const napi = @import("../../napi/napi.zig");
const TCC = @import("../../tcc.zig");
extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void;
@@ -446,7 +448,7 @@ pub const FFI = struct {
for (this.symbols.map.values()) |*symbol| {
if (symbol.needsNapiEnv()) {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalThis);
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalThis.makeNapiEnvForFFI());
break;
}
}
@@ -800,12 +802,14 @@ pub const FFI = struct {
// we are unable to free memory safely in certain cases here.
}
const napi_env = makeNapiEnvIfNeeded(compile_c.symbols.map.values(), globalThis);
var obj = JSC.JSValue.createEmptyObject(globalThis, compile_c.symbols.map.count());
for (compile_c.symbols.map.values()) |*function| {
const function_name = function.base_name.?;
const allocator = bun.default_allocator;
function.compile(allocator, globalThis) catch |err| {
function.compile(allocator, napi_env) catch |err| {
if (!globalThis.hasException()) {
const ret = JSC.toInvalidArguments("{s} when translating symbol \"{s}\"", .{
@errorName(err),
@@ -1126,6 +1130,9 @@ pub const FFI = struct {
var obj = JSC.JSValue.createEmptyObject(global, size);
obj.protect();
defer obj.unprotect();
const napi_env = makeNapiEnvIfNeeded(symbols.values(), global);
for (symbols.values()) |*function| {
const function_name = function.base_name.?;
@@ -1145,7 +1152,7 @@ pub const FFI = struct {
function.symbol_from_dynamic_library = resolved_symbol;
}
function.compile(allocator, global) catch |err| {
function.compile(allocator, napi_env) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\" in \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
@@ -1237,6 +1244,8 @@ pub const FFI = struct {
var obj = JSValue.createEmptyObject(global, symbols.count());
obj.ensureStillAlive();
defer obj.ensureStillAlive();
const napi_env = makeNapiEnvIfNeeded(symbols.values(), global);
for (symbols.values()) |*function| {
const function_name = function.base_name.?;
@@ -1250,7 +1259,7 @@ pub const FFI = struct {
return ret;
}
function.compile(allocator, global) catch |err| {
function.compile(allocator, napi_env) catch |err| {
const ret = JSC.toInvalidArguments("{s} when compiling symbol \"{s}\"", .{
bun.asByteSlice(@errorName(err)),
bun.asByteSlice(function_name),
@@ -1559,7 +1568,7 @@ pub const FFI = struct {
pub fn compile(
this: *Function,
allocator: std.mem.Allocator,
globalObject: *JSC.JSGlobalObject,
napiEnv: ?*napi.NapiEnv,
) !void {
var source_code = std.ArrayList(u8).init(allocator);
var source_code_writer = source_code.writer();
@@ -1582,7 +1591,9 @@ pub const FFI = struct {
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", globalObject);
if (napiEnv) |env| {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", env);
}
CompilerRT.define(state);
@@ -1691,7 +1702,9 @@ pub const FFI = struct {
_ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY);
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", js_context);
if (this.needsNapiEnv()) {
_ = TCC.tcc_add_symbol(state, "Bun__thisFFIModuleNapiEnv", js_context.makeNapiEnvForFFI());
}
CompilerRT.define(state);
@@ -2550,3 +2563,13 @@ const CompilerRT = struct {
};
pub const Bun__FFI__cc = FFI.Bun__FFI__cc;
fn makeNapiEnvIfNeeded(functions: []const FFI.Function, globalThis: *JSGlobalObject) ?*napi.NapiEnv {
for (functions) |function| {
if (function.needsNapiEnv()) {
return globalThis.makeNapiEnvForFFI();
}
}
return null;
}

View File

@@ -394,17 +394,14 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
return JSValue::encode(jsUndefined());
}
JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject,
JSC::EncodedJSValue exports);
#if OS(WINDOWS)
#define dlsym GetProcAddress
#endif
// TODO(@190n) look for node_register_module_vXYZ according to BuildOptions.reported_nodejs_version
// (bun/src/env.zig:36) and the table at https://github.com/nodejs/node/blob/main/doc/abi_version_registry.json
napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*,
JSC::EncodedJSValue)>(
dlsym(handle, "napi_register_module_v1"));
auto napi_register_module_v1 = reinterpret_cast<napi_value (*)(napi_env, napi_value)>(dlsym(handle, "napi_register_module_v1"));
auto node_api_module_get_api_version_v1 = reinterpret_cast<int32_t (*)()>(dlsym(handle, "node_api_module_get_api_version_v1"));
#if OS(WINDOWS)
#undef dlsym
@@ -420,10 +417,31 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen,
return {};
}
// TODO(@heimskr): get the API version without node_api_module_get_api_version_v1 a different way
int module_version = 8;
if (node_api_module_get_api_version_v1) {
module_version = node_api_module_get_api_version_v1();
}
NapiHandleScope handleScope(globalObject);
EncodedJSValue exportsValue = JSC::JSValue::encode(exports);
JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(globalObject, exportsValue));
napi_module nmodule {
.nm_version = module_version,
.nm_flags = 0,
.nm_filename = "file://",
.nm_register_func = nullptr,
.nm_modname = "[no modname]",
.nm_priv = nullptr,
.reserved = {},
};
static_assert(sizeof(napi_value) == sizeof(EncodedJSValue), "EncodedJSValue must be reinterpretable as a pointer");
auto env = globalObject->makeNapiEnv(nmodule);
JSC::JSValue resultValue = JSValue::decode(reinterpret_cast<EncodedJSValue>(napi_register_module_v1(env, reinterpret_cast<napi_value>(exportsValue))));
RETURN_IF_EXCEPTION(scope, {});

View File

@@ -1204,9 +1204,8 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::Scri
GlobalObject::~GlobalObject()
{
if (napiInstanceDataFinalizer) {
napi_finalize finalizer = reinterpret_cast<napi_finalize>(napiInstanceDataFinalizer);
finalizer(toNapi(this), napiInstanceData, napiInstanceDataFinalizerHint);
for (const auto& env : m_napiEnvs) {
env->cleanup();
}
if (auto* ctx = scriptExecutionContext()) {
@@ -2487,6 +2486,11 @@ extern "C" JSC__JSValue ZigGlobalObject__readableStreamToBlob(Zig::GlobalObject*
return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments));
}
extern "C" napi_env ZigGlobalObject__makeNapiEnvForFFI(Zig::GlobalObject* globalObject)
{
return globalObject->makeNapiEnvForFFI();
}
JSC_DECLARE_HOST_FUNCTION(functionReadableStreamToArrayBuffer);
JSC_DEFINE_HOST_FUNCTION(functionReadableStreamToArrayBuffer, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
@@ -4354,6 +4358,26 @@ 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));
return m_napiEnvs.last().get();
}
napi_env GlobalObject::makeNapiEnvForFFI()
{
auto out = makeNapiEnv(napi_module {
.nm_version = 9,
.nm_flags = 0,
.nm_filename = "ffi://",
.nm_register_func = nullptr,
.nm_modname = "[ffi]",
.nm_priv = nullptr,
.reserved = {},
});
return out;
}
#include "ZigGeneratedClasses+lazyStructureImpl.h"
#include "ZigGlobalObject.lut.h"

View File

@@ -54,6 +54,9 @@ class GlobalInternals;
#include "BunHttp2CommonStrings.h"
#include "BunGlobalScope.h"
typedef struct napi_env__* napi_env;
struct napi_module;
namespace WebCore {
class WorkerGlobalScope;
class SubtleCrypto;
@@ -446,13 +449,6 @@ public:
// To do that, we count the number of times we register a module.
int napiModuleRegisterCallCount = 0;
// NAPI instance data
// This is not a correct implementation
// Addon modules can override each other's data
void* napiInstanceData = nullptr;
void* napiInstanceDataFinalizer = nullptr;
void* napiInstanceDataFinalizerHint = nullptr;
// Used by napi_type_tag_object to associate a 128-bit type ID with JS objects.
// Should only use JSCell* keys and NapiTypeTag values.
LazyProperty<JSGlobalObject, JSC::JSWeakMap> m_napiTypeTags;
@@ -573,6 +569,10 @@ public:
bool hasOverridenModuleResolveFilenameFunction = false;
WTF::Vector<std::unique_ptr<napi_env__>> m_napiEnvs;
napi_env makeNapiEnv(const napi_module&);
napi_env makeNapiEnvForFFI();
private:
DOMGuardedObjectSet m_guardedObjects WTF_GUARDED_BY_LOCK(m_gcLock);
WebCore::SubtleCrypto* m_subtleCrypto = nullptr;

View File

@@ -23,6 +23,7 @@ const String = bun.String;
const ErrorableString = JSC.ErrorableString;
const JSError = bun.JSError;
const OOM = bun.OOM;
const napi = @import("../../napi/napi.zig");
pub const JSObject = extern struct {
pub const shim = Shimmer("JSC", "JSObject", @This());
@@ -3417,6 +3418,12 @@ pub const JSGlobalObject = opaque {
return ZigGlobalObject__readableStreamToFormData(this, value, content_type);
}
extern fn ZigGlobalObject__makeNapiEnvForFFI(*JSGlobalObject) *napi.NapiEnv;
pub fn makeNapiEnvForFFI(this: *JSGlobalObject) *napi.NapiEnv {
return ZigGlobalObject__makeNapiEnvForFFI(this);
}
pub inline fn assertOnJSThread(this: *JSGlobalObject) void {
if (bun.Environment.allow_assert) this.bunVM().assertOnJSThread();
}

View File

@@ -84,6 +84,26 @@ using namespace Zig;
#define NAPI_PREMABLE
#endif
// Usage: `return napi_set_last_error(napi_ok);`
//
// Sets the global extended error info to indicate the passed-in status, and then returns it.
// All NAPI functions should call this in all places where they return, even if there is no error,
// because the extended error info should always reflect the most recent API call. The only
// exception is napi_get_last_error_info, which should return napi_ok without overwriting the
// extended error info.
//
// Usually, you should use the above macros instead of this function.
//
// This is not part of Node-API, it's a convenience function for Bun.
extern "C" napi_status napi_set_last_error(napi_env env, napi_status status)
{
if (env) {
// napi_get_last_error_info will fill in the other fields if they are requested
env->m_lastNapiErrorInfo.error_code = status;
}
return status;
}
namespace Napi {
JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject)
@@ -135,7 +155,7 @@ public:
auto finalizer = weakValue->finalizer;
if (finalizer.finalize_cb) {
weakValue->finalizer.finalize_cb = nullptr;
finalizer.call(weakValue->globalObject.get(), weakValue->data);
finalizer.call(weakValue->env, weakValue->data);
}
}
};
@@ -146,11 +166,11 @@ static NapiRefWeakHandleOwner& weakValueHandleOwner()
return jscWeakValueHandleOwner;
}
void NapiFinalizer::call(JSC::JSGlobalObject* globalObject, void* data)
void NapiFinalizer::call(napi_env env, void* data)
{
if (this->finalize_cb) {
NAPI_PREMABLE
this->finalize_cb(toNapi(globalObject), data, this->finalize_hint);
this->finalize_cb(env, data, this->finalize_hint);
}
}
@@ -182,7 +202,7 @@ void NapiRef::unref()
void NapiRef::clear()
{
this->finalizer.call(this->globalObject.get(), this->data);
this->finalizer.call(this->env, this->data);
this->globalObject.clear();
this->weakValueRef.clear();
this->strongRef.clear();
@@ -432,7 +452,6 @@ public:
{
ASSERT(jsCast<NAPIFunction*>(callframe->jsCallee()));
auto* function = static_cast<NAPIFunction*>(callframe->jsCallee());
auto* env = toNapi(globalObject);
auto* callback = reinterpret_cast<napi_callback>(function->m_method);
JSC::VM& vm = globalObject->vm();
@@ -446,28 +465,29 @@ public:
auto scope = DECLARE_THROW_SCOPE(vm);
Bun::NapiHandleScope handleScope(jsCast<Zig::GlobalObject*>(globalObject));
auto result = callback(env, NAPICallFrame::toNapiCallbackInfo(frame));
auto result = callback(function->m_env, NAPICallFrame::toNapiCallbackInfo(frame));
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(toJS(result)));
}
NAPIFunction(JSC::VM& vm, JSC::NativeExecutable* exec, JSGlobalObject* globalObject, Structure* structure, Zig::CFFIFunction method, void* dataPtr)
NAPIFunction(JSC::VM& vm, JSC::NativeExecutable* exec, JSGlobalObject* globalObject, Structure* structure, Zig::CFFIFunction method, void* dataPtr, napi_env env)
: Base(vm, exec, globalObject, structure)
, m_method(method)
, m_env(env)
, m_dataPtr(dataPtr)
, m_method(method)
{
}
static NAPIFunction* create(JSC::VM& vm, Zig::GlobalObject* globalObject, unsigned length, const WTF::String& name, Zig::CFFIFunction method, void* dataPtr)
static NAPIFunction* create(JSC::VM& vm, Zig::GlobalObject* globalObject, unsigned length, const WTF::String& name, Zig::CFFIFunction method, void* dataPtr, napi_env env)
{
auto* structure = globalObject->NAPIFunctionStructure();
NativeExecutable* executable = vm.getHostFunction(&NAPIFunction::call, ImplementationVisibility::Public, &NAPIFunction::call, name);
NAPIFunction* functionObject = new (NotNull, JSC::allocateCell<NAPIFunction>(vm)) NAPIFunction(vm, executable, globalObject, structure, method, dataPtr);
NAPIFunction* functionObject = new (NotNull, JSC::allocateCell<NAPIFunction>(vm)) NAPIFunction(vm, executable, globalObject, structure, method, dataPtr, env);
functionObject->finishCreation(vm, executable, length, name);
return functionObject;
}
napi_env m_env = nullptr;
void* m_dataPtr = nullptr;
Zig::CFFIFunction m_method = nullptr;
@@ -501,8 +521,9 @@ Structure* Zig::createNAPIFunctionStructure(VM& vm, JSC::JSGlobalObject* globalO
return NAPIFunction::createStructure(vm, globalObject, prototype);
}
static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* to, void* inheritedDataPtr, napi_property_descriptor property, bool isInstance, JSC::ThrowScope& scope)
static void defineNapiProperty(napi_env env, JSC::JSObject* to, void* inheritedDataPtr, napi_property_descriptor property, bool isInstance, JSC::ThrowScope& scope)
{
Zig::GlobalObject* globalObject = env->globalObject();
JSC::VM& vm = globalObject->vm();
void* dataPtr = property.data;
if (!dataPtr) {
@@ -535,7 +556,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
JSC::JSValue value;
auto method = reinterpret_cast<Zig::CFFIFunction>(property.method);
auto* function = NAPIFunction::create(vm, globalObject, 1, propertyName.isSymbol() ? String() : propertyName.string(), method, dataPtr);
auto* function = NAPIFunction::create(vm, globalObject, 1, propertyName.isSymbol() ? String() : propertyName.string(), method, dataPtr, env);
value = JSC::JSValue(function);
to->putDirect(vm, propertyName, value, getPropertyAttributes(property));
@@ -550,7 +571,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
auto setterProperty = reinterpret_cast<CFFIFunction>(property.setter);
if (getterProperty) {
getter = NAPIFunction::create(vm, globalObject, 0, makeString("get "_s, propertyName.isSymbol() ? String() : propertyName.string()), getterProperty, dataPtr);
getter = NAPIFunction::create(vm, globalObject, 0, makeString("get "_s, propertyName.isSymbol() ? String() : propertyName.string()), getterProperty, dataPtr, env);
} else {
JSC::JSNativeStdFunction* getterFunction = JSC::JSNativeStdFunction::create(
globalObject->vm(), globalObject, 0, String(), [](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
@@ -560,7 +581,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t
}
if (setterProperty) {
setter = NAPIFunction::create(vm, globalObject, 1, makeString("set "_s, propertyName.isSymbol() ? String() : propertyName.string()), setterProperty, dataPtr);
setter = NAPIFunction::create(vm, globalObject, 1, makeString("set "_s, propertyName.isSymbol() ? String() : propertyName.string()), setterProperty, dataPtr, env);
} else {
JSC::JSNativeStdFunction* setterFunction = JSC::JSNativeStdFunction::create(
globalObject->vm(), globalObject, 1, String(), [](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue {
@@ -900,12 +921,12 @@ node_api_create_external_string_latin1(napi_env env,
}
length = length == NAPI_AUTO_LENGTH ? strlen(str) : length;
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const LChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) {
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const LChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback, env](void* hint, void* str, unsigned length) {
if (finalize_callback) {
#if NAPI_VERBOSE
printf("[napi] string finalize_callback\n");
#endif
finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint);
finalize_callback(env, nullptr, hint);
}
});
Zig::GlobalObject* globalObject = toJS(env);
@@ -937,13 +958,13 @@ node_api_create_external_string_utf16(napi_env env,
}
length = length == NAPI_AUTO_LENGTH ? std::char_traits<char16_t>::length(str) : length;
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const UChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) {
WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast<const UChar*>(str), static_cast<unsigned int>(length) }, finalize_hint, [finalize_callback, env](void* hint, void* str, unsigned length) {
#if NAPI_VERBOSE
printf("[napi] string finalize_callback\n");
#endif
if (finalize_callback) {
finalize_callback(toNapi(defaultGlobalObject()), nullptr, hint);
finalize_callback(env, nullptr, hint);
}
});
Zig::GlobalObject* globalObject = toJS(env);
@@ -959,6 +980,7 @@ extern "C" size_t Bun__napi_module_register_count;
extern "C" void napi_module_register(napi_module* mod)
{
auto* globalObject = defaultGlobalObject();
napi_env env = globalObject->makeNapiEnv(*mod);
JSC::VM& vm = globalObject->vm();
auto keyStr = WTF::String::fromUTF8(mod->nm_modname);
globalObject->napiModuleRegisterCallCount++;
@@ -988,7 +1010,7 @@ extern "C" void napi_module_register(napi_module* mod)
JSC::Strong<JSC::JSObject> strongObject = { vm, object };
Bun::NapiHandleScope handleScope(globalObject);
JSValue resultValue = toJS(mod->nm_register_func(toNapi(globalObject), toNapi(object, globalObject)));
JSValue resultValue = toJS(mod->nm_register_func(env, toNapi(object, globalObject)));
RETURN_IF_EXCEPTION(scope, void());
@@ -1033,8 +1055,6 @@ extern "C" napi_status napi_wrap(napi_env env,
return napi_object_expected;
}
auto* globalObject = toJS(env);
NapiRef** refPtr = nullptr;
if (auto* val = jsDynamicCast<NapiPrototype*>(value)) {
refPtr = &val->napiRef;
@@ -1053,7 +1073,7 @@ extern "C" napi_status napi_wrap(napi_env env,
return napi_invalid_arg;
}
auto* ref = new NapiRef(globalObject, 0);
auto* ref = new NapiRef(env, 0);
ref->weakValueRef.set(value, weakValueHandleOwner(), ref);
@@ -1158,7 +1178,7 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name,
}
auto method = reinterpret_cast<Zig::CFFIFunction>(cb);
auto* function = NAPIFunction::create(vm, globalObject, length, name, method, data);
auto* function = NAPIFunction::create(vm, globalObject, length, name, method, data, env);
ASSERT(function->isCallable());
*result = toNapi(JSC::JSValue(function), globalObject);
@@ -1280,7 +1300,7 @@ napi_define_properties(napi_env env, napi_value object, size_t property_count,
}
for (size_t i = 0; i < property_count; i++) {
defineNapiProperty(globalObject, objectObject, inheritedDataPtr, properties[i], true, throwScope);
defineNapiProperty(env, objectObject, inheritedDataPtr, properties[i], true, throwScope);
RETURN_IF_EXCEPTION(throwScope, napi_generic_failure);
}
@@ -1377,7 +1397,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value,
Zig::GlobalObject* globalObject = toJS(env);
auto* ref = new NapiRef(globalObject, initial_refcount);
auto* ref = new NapiRef(env, initial_refcount);
if (initial_refcount > 0) {
ref->strongRef.set(globalObject->vm(), val);
}
@@ -1814,8 +1834,7 @@ void NapiClass::visitChildrenImpl(JSCell* cell, Visitor& visitor)
DEFINE_VISIT_CHILDREN(NapiClass);
JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction,
(JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue NapiClass_ConstructorFunction(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame)
{
JSC::VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
@@ -1853,12 +1872,12 @@ JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction,
frame.newTarget = newTarget;
Bun::NapiHandleScope handleScope(jsCast<Zig::GlobalObject*>(globalObject));
napi->constructor()(globalObject, reinterpret_cast<JSC::CallFrame*>(NAPICallFrame::toNapiCallbackInfo(frame)));
napi->constructor()(napi->env(), NAPICallFrame::toNapiCallbackInfo(frame));
RETURN_IF_EXCEPTION(scope, {});
RELEASE_AND_RETURN(scope, JSValue::encode(frame.thisValue()));
}
NapiClass* NapiClass::create(VM& vm, Zig::GlobalObject* globalObject, const char* utf8name,
NapiClass* NapiClass::create(VM& vm, napi_env env, const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
@@ -1867,8 +1886,9 @@ NapiClass* NapiClass::create(VM& vm, Zig::GlobalObject* globalObject, const char
{
WTF::String name = WTF::String::fromUTF8({ utf8name, length }).isolatedCopy();
NativeExecutable* executable = vm.getHostFunction(NapiClass_ConstructorFunction, ImplementationVisibility::Public, NapiClass_ConstructorFunction, name);
auto* globalObject = toJS(env);
Structure* structure = globalObject->NapiClassStructure();
NapiClass* napiClass = new (NotNull, allocateCell<NapiClass>(vm)) NapiClass(vm, executable, globalObject, structure);
NapiClass* napiClass = new (NotNull, allocateCell<NapiClass>(vm)) NapiClass(vm, executable, env, structure);
napiClass->finishCreation(vm, executable, length, name, constructor, data, property_count, properties);
return napiClass;
}
@@ -1880,7 +1900,7 @@ void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned le
{
Base::finishCreation(vm, executable, length, name);
ASSERT(inherits(info()));
this->m_constructor = reinterpret_cast<CFFIFunction>(constructor);
this->m_constructor = constructor;
auto globalObject = reinterpret_cast<Zig::GlobalObject*>(this->globalObject());
this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | 0);
@@ -1893,9 +1913,9 @@ void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned le
const napi_property_descriptor& property = properties[i];
if (property.attributes & napi_static) {
defineNapiProperty(globalObject, this, nullptr, property, true, throwScope);
defineNapiProperty(m_env, this, nullptr, property, true, throwScope);
} else {
defineNapiProperty(globalObject, prototype, nullptr, property, false, throwScope);
defineNapiProperty(m_env, prototype, nullptr, property, false, throwScope);
}
if (throwScope.exception())
@@ -2004,7 +2024,7 @@ extern "C" napi_status napi_define_class(napi_env env,
if (len == NAPI_AUTO_LENGTH) {
len = strlen(utf8name);
}
NapiClass* napiClass = NapiClass::create(vm, globalObject, utf8name, len, constructor, data, property_count, properties);
NapiClass* napiClass = NapiClass::create(vm, env, utf8name, len, constructor, data, property_count, properties);
JSC::JSValue value = JSC::JSValue(napiClass);
JSC::EnsureStillAliveScope ensureStillAlive1(value);
if (data != nullptr) {
@@ -2088,12 +2108,12 @@ 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*)>([globalObject, finalize_hint, finalize_cb](void* p) {
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(data), length }, createSharedTask<void(void*)>([finalize_hint, finalize_cb, env](void* p) {
#if NAPI_VERBOSE
printf("[napi] buffer finalize_callback\n");
#endif
if (finalize_cb != nullptr) {
finalize_cb(toNapi(globalObject), p, finalize_hint);
finalize_cb(env, p, finalize_hint);
}
}));
auto* subclassStructure = globalObject->JSBufferSubclassStructure();
@@ -2116,12 +2136,12 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte
Zig::GlobalObject* globalObject = toJS(env);
JSC::VM& vm = globalObject->vm();
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(external_data), byte_length }, createSharedTask<void(void*)>([globalObject, finalize_hint, finalize_cb](void* p) {
auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast<const uint8_t*>(external_data), byte_length }, createSharedTask<void(void*)>([finalize_hint, finalize_cb, env](void* p) {
#if NAPI_VERBOSE
printf("[napi] arraybuffer finalize_callback\n");
#endif
if (finalize_cb != nullptr) {
finalize_cb(toNapi(globalObject), p, finalize_hint);
finalize_cb(env, p, finalize_hint);
}
}));
@@ -2393,7 +2413,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data,
JSC::VM& vm = globalObject->vm();
auto* structure = globalObject->NapiExternalStructure();
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, reinterpret_cast<void*>(finalize_cb));
JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, env, reinterpret_cast<void*>(finalize_cb));
JSC::EnsureStillAliveScope ensureStillAlive(value);
*result = toNapi(value, globalObject);
return napi_ok;
@@ -2556,12 +2576,11 @@ extern "C" napi_status napi_get_instance_data(napi_env env,
{
NAPI_PREMABLE
Zig::GlobalObject* globalObject = toJS(env);
if (UNLIKELY(data == nullptr)) {
return napi_invalid_arg;
}
*data = globalObject->napiInstanceData;
*data = env->getInstanceData();
return napi_ok;
}
@@ -2616,13 +2635,7 @@ extern "C" napi_status napi_set_instance_data(napi_env env,
void* finalize_hint)
{
NAPI_PREMABLE
Zig::GlobalObject* globalObject = toJS(env);
globalObject->napiInstanceData = data;
globalObject->napiInstanceDataFinalizer = reinterpret_cast<void*>(finalize_cb);
globalObject->napiInstanceDataFinalizerHint = finalize_hint;
env->setInstanceData(data, finalize_cb, finalize_hint);
return napi_ok;
}
@@ -2824,3 +2837,8 @@ extern "C" napi_status napi_check_object_type_tag(napi_env env, napi_value value
}
return napi_ok;
}
extern "C" JSGlobalObject* NapiEnv__globalObject(napi_env env)
{
return env->globalObject();
}

View File

@@ -7,7 +7,7 @@
#include "headers-handwritten.h"
#include "BunClientData.h"
#include <JavaScriptCore/CallFrame.h>
#include "js_native_api.h"
#include "node_api.h"
#include <JavaScriptCore/JSWeakValue.h>
#include "JSFFIFunction.h"
#include "ZigGlobalObject.h"
@@ -22,6 +22,56 @@ namespace Napi {
JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSObject* object, JSC::JSGlobalObject* globalObject);
}
struct napi_env__ {
public:
napi_env__(Zig::GlobalObject* globalObject, const napi_module& napiModule)
: m_globalObject(globalObject)
, m_napiModule(napiModule)
{
}
inline Zig::GlobalObject* globalObject() const { return m_globalObject; }
inline const napi_module& napiModule() const { return m_napiModule; }
void cleanup()
{
if (m_instanceDataFinalizer) {
m_instanceDataFinalizer(this, m_instanceData, m_instanceDataFinalizerHint);
}
}
void setInstanceData(void* data, napi_finalize finalizer, void* hint)
{
m_instanceData = data;
m_instanceDataFinalizer = finalizer;
m_instanceDataFinalizerHint = hint;
}
inline void* getInstanceData() const
{
return m_instanceData;
}
// Almost all NAPI functions should set error_code to the status they're returning right before
// they return it
napi_extended_error_info m_lastNapiErrorInfo = {
.error_message = "",
// Not currently used by Bun -- always nullptr
.engine_reserved = nullptr,
// Not currently used by Bun -- always zero
.engine_error_code = 0,
.error_code = napi_ok,
};
private:
Zig::GlobalObject* m_globalObject = nullptr;
napi_module m_napiModule;
void* m_instanceData = nullptr;
napi_finalize m_instanceDataFinalizer = nullptr;
void* m_instanceDataFinalizerHint = nullptr;
};
namespace Zig {
using namespace JSC;
@@ -33,7 +83,7 @@ static inline JSValue toJS(napi_value val)
static inline Zig::GlobalObject* toJS(napi_env val)
{
return reinterpret_cast<Zig::GlobalObject*>(val);
return val->globalObject();
}
static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObject)
@@ -46,17 +96,12 @@ static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObjec
return reinterpret_cast<napi_value>(JSC::JSValue::encode(val));
}
static inline napi_env toNapi(JSC::JSGlobalObject* val)
{
return reinterpret_cast<napi_env>(val);
}
class NapiFinalizer {
public:
void* finalize_hint = nullptr;
napi_finalize finalize_cb;
void call(JSC::JSGlobalObject* globalObject, void* data);
void call(napi_env env, void* data);
};
// This is essentially JSC::JSWeakValue, except with a JSCell* instead of a
@@ -147,12 +192,11 @@ public:
void unref();
void clear();
NapiRef(JSC::JSGlobalObject* global, uint32_t count)
NapiRef(napi_env env, uint32_t count)
: env(env)
, globalObject(JSC::Weak<JSC::JSGlobalObject>(env->globalObject()))
, refCount(count)
{
globalObject = JSC::Weak<JSC::JSGlobalObject>(global);
strongRef = {};
weakValueRef.clear();
refCount = count;
}
JSC::JSValue value() const
@@ -172,6 +216,7 @@ public:
weakValueRef.clear();
}
napi_env env = nullptr;
JSC::Weak<JSC::JSGlobalObject> globalObject;
NapiWeakValue weakValueRef;
JSC::Strong<JSC::Unknown> strongRef;
@@ -210,7 +255,7 @@ public:
DECLARE_EXPORT_INFO;
JS_EXPORT_PRIVATE static NapiClass* create(VM&, Zig::GlobalObject*, const char* utf8name,
JS_EXPORT_PRIVATE static NapiClass* create(VM&, napi_env, const char* utf8name,
size_t length,
napi_callback constructor,
void* data,
@@ -223,18 +268,22 @@ public:
return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
}
CFFIFunction constructor()
napi_callback constructor()
{
return m_constructor;
}
void* dataPtr = nullptr;
CFFIFunction m_constructor = nullptr;
napi_callback m_constructor = nullptr;
NapiRef* napiRef = nullptr;
napi_env m_env = nullptr;
inline napi_env env() const { return m_env; }
private:
NapiClass(VM& vm, NativeExecutable* executable, JSC::JSGlobalObject* global, Structure* structure)
: Base(vm, executable, global, structure)
NapiClass(VM& vm, NativeExecutable* executable, napi_env env, Structure* structure)
: Base(vm, executable, env->globalObject(), structure)
, m_env(env)
{
}
void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, napi_callback constructor,

View File

@@ -5,10 +5,10 @@ namespace Bun {
NapiExternal::~NapiExternal()
{
if (finalizer) {
if (m_finalizer) {
// We cannot call globalObject() here because it is in a finalizer.
// https://github.com/oven-sh/bun/issues/13001#issuecomment-2290022312
reinterpret_cast<napi_finalize>(finalizer)(toNapi(this->napi_env), m_value, m_finalizerHint);
reinterpret_cast<napi_finalize>(m_finalizer)(m_env, m_value, m_finalizerHint);
}
}

View File

@@ -1,11 +1,10 @@
#pragma once
#include "root.h"
#include "BunBuiltinNames.h"
#include "BunClientData.h"
#include "napi.h"
namespace Bun {
@@ -47,11 +46,11 @@ public:
JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, void* finalizer)
static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, napi_env env, void* finalizer)
{
NapiExternal* accessor = new (NotNull, JSC::allocateCell<NapiExternal>(vm)) NapiExternal(vm, structure);
accessor->finishCreation(vm, value, finalizer_hint, finalizer);
accessor->finishCreation(vm, value, finalizer_hint, env, finalizer);
#if BUN_DEBUG
if (auto* callFrame = vm.topCallFrame) {
@@ -75,23 +74,23 @@ public:
return accessor;
}
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, void* finalizer)
void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, napi_env env, void* finalizer)
{
Base::finishCreation(vm);
m_value = value;
m_finalizerHint = finalizer_hint;
napi_env = this->globalObject();
this->finalizer = finalizer;
m_env = env;
m_finalizer = finalizer;
}
static void destroy(JSC::JSCell* cell);
void* value() const { return m_value; }
void* m_value;
void* m_finalizerHint;
void* finalizer;
JSGlobalObject* napi_env;
void* m_value = nullptr;
void* m_finalizerHint = nullptr;
void* m_finalizer = nullptr;
napi_env m_env = nullptr;
#if BUN_DEBUG
String sourceOriginURL = String();

View File

@@ -1,6 +1,7 @@
#include "napi_handle_scope.h"
#include "ZigGlobalObject.h"
#include "napi.h"
namespace Bun {
@@ -127,19 +128,19 @@ NapiHandleScope::~NapiHandleScope()
NapiHandleScope::close(m_globalObject, m_impl);
}
extern "C" NapiHandleScopeImpl* NapiHandleScope__open(Zig::GlobalObject* globalObject, bool escapable)
extern "C" NapiHandleScopeImpl* NapiHandleScope__open(napi_env env, bool escapable)
{
return NapiHandleScope::open(globalObject, escapable);
return NapiHandleScope::open(env->globalObject(), escapable);
}
extern "C" void NapiHandleScope__close(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current)
extern "C" void NapiHandleScope__close(napi_env env, NapiHandleScopeImpl* current)
{
return NapiHandleScope::close(globalObject, current);
return NapiHandleScope::close(env->globalObject(), current);
}
extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value)
extern "C" void NapiHandleScope__append(napi_env env, JSC::EncodedJSValue value)
{
globalObject->m_currentNapiHandleScopeImpl.get()->append(JSC::JSValue::decode(value));
env->globalObject()->m_currentNapiHandleScopeImpl.get()->append(JSC::JSValue::decode(value));
}
extern "C" bool NapiHandleScope__escape(NapiHandleScopeImpl* handleScope, JSC::EncodedJSValue value)

View File

@@ -3,6 +3,8 @@
#include "BunClientData.h"
#include "root.h"
typedef struct napi_env__* napi_env;
namespace Bun {
// An array of write barriers (so that newly-added objects are not lost by GC) to JSValues. Unlike
@@ -80,14 +82,14 @@ private:
};
// Create a new handle scope in the given environment
extern "C" NapiHandleScopeImpl* NapiHandleScope__open(Zig::GlobalObject* globalObject, bool escapable);
extern "C" NapiHandleScopeImpl* NapiHandleScope__open(napi_env env, bool escapable);
// Pop the most recently created handle scope in the given environment and restore the old one.
// Asserts that `current` is the active handle scope.
extern "C" void NapiHandleScope__close(Zig::GlobalObject* globalObject, NapiHandleScopeImpl* current);
extern "C" void NapiHandleScope__close(napi_env env, NapiHandleScopeImpl* current);
// Store a value in the active handle scope in the given environment
extern "C" void NapiHandleScope__append(Zig::GlobalObject* globalObject, JSC::EncodedJSValue value);
extern "C" void NapiHandleScope__append(napi_env env, JSC::EncodedJSValue value);
// Put a value from the current handle scope into its escape slot reserved in the outer handle
// scope. Returns false if the current handle scope is not escapable or if escape has already been

View File

@@ -12,7 +12,7 @@ Local<External> External::New(Isolate* isolate, void* value)
auto globalObject = isolate->globalObject();
auto& vm = globalObject->vm();
auto structure = globalObject->NapiExternalStructure();
Bun::NapiExternal* val = Bun::NapiExternal::create(vm, structure, value, nullptr, nullptr);
Bun::NapiExternal* val = Bun::NapiExternal::create(vm, structure, value, nullptr, nullptr, nullptr);
return isolate->currentHandleScope()->createLocal<External>(vm, val);
}

File diff suppressed because it is too large Load Diff