From 9e9d137ca2e9c19826f495845273a803547b202f Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 14:42:32 -0800 Subject: [PATCH 01/11] Initial work on splitting up napi envs --- src/bun.js/api/ffi.zig | 37 ++++++++++++++++++++----- src/bun.js/bindings/ZigGlobalObject.cpp | 25 +++++++++++++++++ src/bun.js/bindings/ZigGlobalObject.h | 7 +++++ src/bun.js/bindings/bindings.zig | 7 +++++ src/bun.js/bindings/napi.cpp | 5 ++++ src/bun.js/bindings/napi.h | 20 +++++++++++-- src/napi/napi.zig | 33 +++++++++++++++++++--- 7 files changed, 121 insertions(+), 13 deletions(-) diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index 262ff8357e7b6d..d044856ecc0916 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -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; } } @@ -803,12 +805,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), @@ -1129,6 +1133,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.?; @@ -1148,7 +1155,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), @@ -1240,6 +1247,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.?; @@ -1253,7 +1262,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), @@ -1562,7 +1571,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(); @@ -1585,7 +1594,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); @@ -1694,7 +1705,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); @@ -2553,3 +2566,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; +} diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index b241674415f816..2f3e0b2323aee3 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2487,6 +2487,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 +4359,26 @@ GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction h } } +napi_env GlobalObject::makeNapiEnv(const napi_module& mod) +{ + m_napiEnvs.append(std::make_unique(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" diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 57ba7dd8fcd7d3..022214a9fb8a85 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -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; @@ -573,6 +576,10 @@ class GlobalObject : public Bun::GlobalScope { bool hasOverridenModuleResolveFilenameFunction = false; + WTF::Vector> 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; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 63f5715076d2c8..d6dac04a924362 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -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()); @@ -3519,6 +3520,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(); } diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 71a42eab87aa29..cb25716cd4f4dc 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -2824,3 +2824,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(); +} diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index a26022c1e8b9c8..8668fab6c56678 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -7,7 +7,7 @@ #include "headers-handwritten.h" #include "BunClientData.h" #include -#include "js_native_api.h" +#include "node_api.h" #include #include "JSFFIFunction.h" #include "ZigGlobalObject.h" @@ -22,6 +22,22 @@ 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; } + +private: + Zig::GlobalObject* m_globalObject = nullptr; + napi_module m_napiModule; +}; + namespace Zig { using namespace JSC; @@ -33,7 +49,7 @@ static inline JSValue toJS(napi_value val) static inline Zig::GlobalObject* toJS(napi_env val) { - return reinterpret_cast(val); + return val->globalObject(); } static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObject) diff --git a/src/napi/napi.zig b/src/napi/napi.zig index d329b68c89557f..32cc042597a82f 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -27,7 +27,26 @@ fn genericFailure() napi_status { } const Async = bun.Async; -pub const napi_env = *JSC.JSGlobalObject; +pub const NapiEnv = opaque { + pub fn toJS(self: *NapiEnv) *JSC.JSGlobalObject { + return NapiEnv__globalObject(self); + } + + extern fn NapiEnv__globalObject(*NapiEnv) *JSC.JSGlobalObject; +}; + +fn envIsNull() napi_status { + // in this case we don't actually have an environment to set the last error on, so it doesn't + // make sense to call napi_set_last_error + @setCold(true); + return @intFromEnum(napi_status.invalid_arg); +} + +/// This is nullable because native modules may pass null pointers for the NAPI environment, which +/// is an error that our NAPI functions need to handle (by returning napi_invalid_arg). To specify +/// a Zig API that uses a never-null napi_env, use `*NapiEnv`. +pub const napi_env = ?*NapiEnv; + pub const Ref = opaque { pub fn create(globalThis: *JSC.JSGlobalObject, value: JSValue) *Ref { JSC.markBinding(@src()); @@ -262,20 +281,26 @@ pub export fn napi_get_null(env: napi_env, result_: ?*napi_value) napi_status { return .ok; } pub extern fn napi_get_global(env: napi_env, result: *napi_value) napi_status; -pub export fn napi_get_boolean(env: napi_env, value: bool, result_: ?*napi_value) napi_status { +pub export fn napi_get_boolean(env_: napi_env, value: bool, result_: ?*napi_value) napi_status { log("napi_get_boolean", .{}); + const env = env_ orelse { + return invalidArg(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsBoolean(value)); return .ok; } -pub export fn napi_create_array(env: napi_env, result_: ?*napi_value) napi_status { +pub export fn napi_create_array(env_: napi_env, result_: ?*napi_value) napi_status { log("napi_create_array", .{}); + const env = env_ orelse { + return invalidArg(); + }; const result = result_ orelse { return invalidArg(); }; - result.set(env, JSValue.createEmptyArray(env, 0)); + result.set(env, JSValue.createEmptyArray(env.toJS(), 0)); return .ok; } pub export fn napi_create_array_with_length(env: napi_env, length: usize, result_: ?*napi_value) napi_status { From bfc423eeb9b234da5c3912b9d774c5b6bb5bd8bb Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 16:08:01 -0800 Subject: [PATCH 02/11] More work on splitting napi envs --- src/napi/napi.zig | 525 ++++++++++++++++++++++++++++++---------------- 1 file changed, 342 insertions(+), 183 deletions(-) diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 32cc042597a82f..c99b9cce1e208e 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -39,7 +39,7 @@ fn envIsNull() napi_status { // in this case we don't actually have an environment to set the last error on, so it doesn't // make sense to call napi_set_last_error @setCold(true); - return @intFromEnum(napi_status.invalid_arg); + return napi_status.invalid_arg; } /// This is nullable because native modules may pass null pointers for the NAPI environment, which @@ -83,36 +83,37 @@ pub const Ref = opaque { extern fn napi_delete_reference_internal(ref: *Ref) void; extern fn napi_set_ref(ref: *Ref, value: JSC.JSValue) void; }; + pub const NapiHandleScope = opaque { - pub extern fn NapiHandleScope__open(globalObject: *JSC.JSGlobalObject, escapable: bool) ?*NapiHandleScope; - pub extern fn NapiHandleScope__close(globalObject: *JSC.JSGlobalObject, current: ?*NapiHandleScope) void; - extern fn NapiHandleScope__append(globalObject: *JSC.JSGlobalObject, value: JSValue) void; - extern fn NapiHandleScope__escape(handleScope: *NapiHandleScope, value: JSValue) bool; + pub extern fn NapiHandleScope__open(env: *NapiEnv, escapable: bool) ?*NapiHandleScope; + pub extern fn NapiHandleScope__close(env: *NapiEnv, current: ?*NapiHandleScope) void; + extern fn NapiHandleScope__append(env: *NapiEnv, value: JSC.JSValueReprInt) void; + extern fn NapiHandleScope__escape(handleScope: *NapiHandleScope, value: JSC.JSValueReprInt) bool; /// Create a new handle scope in the given environment, or return null if creating one now is /// unsafe (i.e. inside a finalizer) - pub fn open(env: napi_env, escapable: bool) ?*NapiHandleScope { + pub fn open(env: *NapiEnv, escapable: bool) ?*NapiHandleScope { return NapiHandleScope__open(env, escapable); } /// Closes the given handle scope, releasing all values inside it, if it is safe to do so. /// Asserts that self is the current handle scope in env. - pub fn close(self: ?*NapiHandleScope, env: napi_env) void { + pub fn close(self: ?*NapiHandleScope, env: *NapiEnv) void { NapiHandleScope__close(env, self); } /// Place a value in the handle scope. Must be done while returning any JS value into NAPI /// callbacks, as the value must remain alive as long as the handle scope is active, even if the /// native module doesn't keep it visible on the stack. - pub fn append(env: napi_env, value: JSC.JSValue) void { - NapiHandleScope__append(env, value); + pub fn append(env: *NapiEnv, value: JSC.JSValue) void { + NapiHandleScope__append(env, @intFromEnum(value)); } /// Move a value from the current handle scope (which must be escapable) to the reserved escape /// slot in the parent handle scope, allowing that value to outlive the current handle scope. /// Returns an error if escape() has already been called on this handle scope. pub fn escape(self: *NapiHandleScope, value: JSC.JSValue) error{EscapeCalledTwice}!void { - if (!NapiHandleScope__escape(self, value)) { + if (!NapiHandleScope__escape(self, @intFromEnum(value))) { return error.EscapeCalledTwice; } } @@ -130,7 +131,7 @@ pub const napi_value = enum(i64) { pub fn set( self: *napi_value, - env: napi_env, + env: *NapiEnv, val: JSC.JSValue, ) void { NapiHandleScope.append(env, val); @@ -141,7 +142,7 @@ pub const napi_value = enum(i64) { return @enumFromInt(@intFromEnum(self.*)); } - pub fn create(env: napi_env, val: JSC.JSValue) napi_value { + pub fn create(env: *NapiEnv, val: JSC.JSValue) napi_value { NapiHandleScope.append(env, val); return @enumFromInt(@intFromEnum(val)); } @@ -263,28 +264,34 @@ const napi_type_tag = extern struct { lower: u64, upper: u64, }; -pub extern fn napi_get_last_error_info(env: napi_env, result: [*c][*c]const napi_extended_error_info) napi_status; -pub export fn napi_get_undefined(env: napi_env, result_: ?*napi_value) napi_status { +pub extern fn napi_get_last_error_info(env_: napi_env, result: [*c][*c]const napi_extended_error_info) napi_status; +pub export fn napi_get_undefined(env_: napi_env, result_: ?*napi_value) napi_status { log("napi_get_undefined", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsUndefined()); return .ok; } -pub export fn napi_get_null(env: napi_env, result_: ?*napi_value) napi_status { +pub export fn napi_get_null(env_: napi_env, result_: ?*napi_value) napi_status { log("napi_get_null", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsNull()); return .ok; } -pub extern fn napi_get_global(env: napi_env, result: *napi_value) napi_status; +pub extern fn napi_get_global(env_: napi_env, result: *napi_value) napi_status; pub export fn napi_get_boolean(env_: napi_env, value: bool, result_: ?*napi_value) napi_status { log("napi_get_boolean", .{}); const env = env_ orelse { - return invalidArg(); + return envIsNull(); }; const result = result_ orelse { return invalidArg(); @@ -295,7 +302,7 @@ pub export fn napi_get_boolean(env_: napi_env, value: bool, result_: ?*napi_valu pub export fn napi_create_array(env_: napi_env, result_: ?*napi_value) napi_status { log("napi_create_array", .{}); const env = env_ orelse { - return invalidArg(); + return envIsNull(); }; const result = result_ orelse { return invalidArg(); @@ -303,8 +310,11 @@ pub export fn napi_create_array(env_: napi_env, result_: ?*napi_value) napi_stat result.set(env, JSValue.createEmptyArray(env.toJS(), 0)); return .ok; } -pub export fn napi_create_array_with_length(env: napi_env, length: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_array_with_length(env_: napi_env, length: usize, result_: ?*napi_value) napi_status { log("napi_create_array_with_length", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -313,37 +323,49 @@ pub export fn napi_create_array_with_length(env: napi_env, length: usize, result // Node and V8 convert out-of-bounds array sizes to 0 const len = std.math.cast(u32, length) orelse 0; - const array = JSC.JSValue.createEmptyArray(env, len); + const array = JSC.JSValue.createEmptyArray(env.toJS(), len); array.ensureStillAlive(); result.set(env, array); return .ok; } pub extern fn napi_create_double(_: napi_env, value: f64, result: *napi_value) napi_status; -pub export fn napi_create_int32(env: napi_env, value: i32, result_: ?*napi_value) napi_status { +pub export fn napi_create_int32(env_: napi_env, value: i32, result_: ?*napi_value) napi_status { log("napi_create_int32", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsNumber(value)); return .ok; } -pub export fn napi_create_uint32(env: napi_env, value: u32, result_: ?*napi_value) napi_status { +pub export fn napi_create_uint32(env_: napi_env, value: u32, result_: ?*napi_value) napi_status { log("napi_create_uint32", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsNumber(value)); return .ok; } -pub export fn napi_create_int64(env: napi_env, value: i64, result_: ?*napi_value) napi_status { +pub export fn napi_create_int64(env_: napi_env, value: i64, result_: ?*napi_value) napi_status { log("napi_create_int64", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.jsNumber(value)); return .ok; } -pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_string_latin1(env_: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { + const env = env_ orelse { + return envIsNull(); + }; const result: *napi_value = result_ orelse { return invalidArg(); }; @@ -364,7 +386,7 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length log("napi_create_string_latin1: {s}", .{slice}); if (slice.len == 0) { - result.set(env, bun.String.empty.toJS(env)); + result.set(env, bun.String.empty.toJS(env.toJS())); return .ok; } @@ -373,10 +395,13 @@ pub export fn napi_create_string_latin1(env: napi_env, str: ?[*]const u8, length @memcpy(bytes, slice); - result.set(env, string.toJS(env)); + result.set(env, string.toJS(env.toJS())); return .ok; } -pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_string_utf8(env_: napi_env, str: ?[*]const u8, length: usize, result_: ?*napi_value) napi_status { + const env = env_ orelse { + return envIsNull(); + }; const result: *napi_value = result_ orelse { return invalidArg(); }; @@ -401,10 +426,13 @@ pub export fn napi_create_string_utf8(env: napi_env, str: ?[*]const u8, length: } defer string.deref(); - result.set(env, string.toJS(env)); + result.set(env, string.toJS(env.toJS())); return .ok; } -pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, length: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_string_utf16(env_: napi_env, str: ?[*]const char16_t, length: usize, result_: ?*napi_value) napi_status { + const env = env_ orelse { + return envIsNull(); + }; const result: *napi_value = result_ orelse { return invalidArg(); }; @@ -426,7 +454,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l log("napi_create_string_utf16: {d} {any}", .{ slice.len, bun.fmt.FormatUTF16{ .buf = slice[0..@min(slice.len, 512)] } }); if (slice.len == 0) { - result.set(env, bun.String.empty.toJS(env)); + result.set(env, bun.String.empty.toJS(env.toJS())); } var string, const chars = bun.String.createUninitialized(.utf16, slice.len); @@ -434,15 +462,15 @@ pub export fn napi_create_string_utf16(env: napi_env, str: ?[*]const char16_t, l @memcpy(chars, slice); - result.set(env, string.toJS(env)); + result.set(env, string.toJS(env.toJS())); return .ok; } -pub extern fn napi_create_symbol(env: napi_env, description: napi_value, result: *napi_value) napi_status; -pub extern fn napi_create_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; -pub extern fn napi_create_type_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; -pub extern fn napi_create_range_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; -pub extern fn napi_typeof(env: napi_env, value: napi_value, result: *napi_valuetype) napi_status; -pub extern fn napi_get_value_double(env: napi_env, value: napi_value, result: *f64) napi_status; +pub extern fn napi_create_symbol(env_: napi_env, description: napi_value, result: *napi_value) napi_status; +pub extern fn napi_create_error(env_: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; +pub extern fn napi_create_type_error(env_: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; +pub extern fn napi_create_range_error(env_: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; +pub extern fn napi_typeof(env_: napi_env, value: napi_value, result: *napi_valuetype) napi_status; +pub extern fn napi_get_value_double(env_: napi_env, value: napi_value, result: *f64) napi_status; pub extern fn napi_get_value_int32(_: napi_env, value_: napi_value, result: ?*i32) napi_status; pub extern fn napi_get_value_uint32(_: napi_env, value_: napi_value, result_: ?*u32) napi_status; pub extern fn napi_get_value_int64(_: napi_env, value_: napi_value, result_: ?*i64) napi_status; @@ -461,13 +489,16 @@ inline fn maybeAppendNull(ptr: anytype, doit: bool) void { ptr.* = 0; } } -pub export fn napi_get_value_string_latin1(env: napi_env, value_: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status { +pub export fn napi_get_value_string_latin1(env_: napi_env, value_: napi_value, buf_ptr_: ?[*:0]c_char, bufsize: usize, result_ptr: ?*usize) napi_status { log("napi_get_value_string_latin1", .{}); + const env = env_ orelse { + return envIsNull(); + }; const value = value_.get(); defer value.ensureStillAlive(); const buf_ptr = @as(?[*:0]u8, @ptrCast(buf_ptr_)); - const str = value.toBunString(env); + const str = value.toBunString(env.toJS()); defer str.deref(); var buf = buf_ptr orelse { @@ -518,12 +549,15 @@ pub export fn napi_get_value_string_latin1(env: napi_env, value_: napi_value, bu /// If buf is NULL, this method returns the length of the string (in bytes) /// via the result parameter. /// The result argument is optional unless buf is NULL. -pub extern fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_ptr: [*c]u8, bufsize: usize, result_ptr: ?*usize) napi_status; -pub export fn napi_get_value_string_utf16(env: napi_env, value_: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status { +pub extern fn napi_get_value_string_utf8(env_: napi_env, value: napi_value, buf_ptr: [*c]u8, bufsize: usize, result_ptr: ?*usize) napi_status; +pub export fn napi_get_value_string_utf16(env_: napi_env, value_: napi_value, buf_ptr: ?[*]char16_t, bufsize: usize, result_ptr: ?*usize) napi_status { log("napi_get_value_string_utf16", .{}); + const env = env_ orelse { + return envIsNull(); + }; const value = value_.get(); defer value.ensureStillAlive(); - const str = value.toBunString(env); + const str = value.toBunString(env.toJS()); defer str.deref(); var buf = buf_ptr orelse { @@ -568,35 +602,47 @@ pub export fn napi_get_value_string_utf16(env: napi_env, value_: napi_value, buf return .ok; } -pub export fn napi_coerce_to_bool(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_bool(env_: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_bool", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const value = value_.get(); - result.set(env, JSValue.jsBoolean(value.coerce(bool, env))); + result.set(env, JSValue.jsBoolean(value.coerce(bool, env.toJS()))); return .ok; } -pub export fn napi_coerce_to_number(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_number(env_: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_number", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const value = value_.get(); - result.set(env, JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.ref(), value.asObjectRef(), TODO_EXCEPTION))); + result.set(env, JSC.JSValue.jsNumber(JSC.C.JSValueToNumber(env.toJS().ref(), value.asObjectRef(), TODO_EXCEPTION))); return .ok; } -pub export fn napi_coerce_to_object(env: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_coerce_to_object(env_: napi_env, value_: napi_value, result_: ?*napi_value) napi_status { log("napi_coerce_to_object", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const value = value_.get(); - result.set(env, JSValue.c(JSC.C.JSValueToObject(env.ref(), value.asObjectRef(), TODO_EXCEPTION))); + result.set(env, JSValue.c(JSC.C.JSValueToObject(env.toJS().ref(), value.asObjectRef(), TODO_EXCEPTION))); return .ok; } -pub export fn napi_get_prototype(env: napi_env, object_: napi_value, result_: ?*napi_value) napi_status { +pub export fn napi_get_prototype(env_: napi_env, object_: napi_value, result_: ?*napi_value) napi_status { log("napi_get_prototype", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -605,11 +651,11 @@ pub export fn napi_get_prototype(env: napi_env, object_: napi_value, result_: ?* return .object_expected; } - result.set(env, JSValue.c(JSC.C.JSObjectGetPrototype(env.ref(), object.asObjectRef()))); + result.set(env, JSValue.c(JSC.C.JSObjectGetPrototype(env.toJS().ref(), object.asObjectRef()))); return .ok; } // TODO: bind JSC::ownKeys -// pub export fn napi_get_property_names(env: napi_env, object: napi_value, result: *napi_value) napi_status { +// pub export fn napi_get_property_names(env_: napi_env, object: napi_value, result: *napi_value) napi_status { // log("napi_get_property_names ", .{}); // if (!object.isObject()) { // return .object_expected; @@ -617,8 +663,11 @@ pub export fn napi_get_prototype(env: napi_env, object_: napi_value, result_: ?* // result.* = // } -pub export fn napi_set_element(env: napi_env, object_: napi_value, index: c_uint, value_: napi_value) napi_status { +pub export fn napi_set_element(env_: napi_env, object_: napi_value, index: c_uint, value_: napi_value) napi_status { log("napi_set_element", .{}); + const env = env_ orelse { + return envIsNull(); + }; const object = object_.get(); const value = value_.get(); if (!object.jsType().isIndexable()) { @@ -626,11 +675,14 @@ pub export fn napi_set_element(env: napi_env, object_: napi_value, index: c_uint } if (value == .zero) return invalidArg(); - JSC.C.JSObjectSetPropertyAtIndex(env.ref(), object.asObjectRef(), index, value.asObjectRef(), TODO_EXCEPTION); + JSC.C.JSObjectSetPropertyAtIndex(env.toJS().ref(), object.asObjectRef(), index, value.asObjectRef(), TODO_EXCEPTION); return .ok; } -pub export fn napi_has_element(env: napi_env, object_: napi_value, index: c_uint, result_: ?*bool) napi_status { +pub export fn napi_has_element(env_: napi_env, object_: napi_value, index: c_uint, result_: ?*bool) napi_status { log("napi_has_element", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -640,12 +692,12 @@ pub export fn napi_has_element(env: napi_env, object_: napi_value, index: c_uint return .array_expected; } - result.* = object.getLength(env) > index; + result.* = object.getLength(env.toJS()) > index; return .ok; } -pub extern fn napi_get_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; -pub extern fn napi_delete_element(env: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; -pub extern fn napi_define_properties(env: napi_env, object: napi_value, property_count: usize, properties: [*c]const napi_property_descriptor) napi_status; +pub extern fn napi_get_element(env_: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; +pub extern fn napi_delete_element(env_: napi_env, object: napi_value, index: u32, result: *napi_value) napi_status; +pub extern fn napi_define_properties(env_: napi_env, object: napi_value, property_count: usize, properties: [*c]const napi_property_descriptor) napi_status; pub export fn napi_is_array(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_array", .{}); const result = result_ orelse { @@ -655,8 +707,11 @@ pub export fn napi_is_array(_: napi_env, value_: napi_value, result_: ?*bool) na result.* = value.jsType().isArray(); return .ok; } -pub export fn napi_get_array_length(env: napi_env, value_: napi_value, result_: [*c]u32) napi_status { +pub export fn napi_get_array_length(env_: napi_env, value_: napi_value, result_: [*c]u32) napi_status { log("napi_get_array_length", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -666,35 +721,41 @@ pub export fn napi_get_array_length(env: napi_env, value_: napi_value, result_: return .array_expected; } - result.* = @as(u32, @truncate(value.getLength(env))); + result.* = @as(u32, @truncate(value.getLength(env.toJS()))); return .ok; } -pub export fn napi_strict_equals(env: napi_env, lhs_: napi_value, rhs_: napi_value, result_: ?*bool) napi_status { +pub export fn napi_strict_equals(env_: napi_env, lhs_: napi_value, rhs_: napi_value, result_: ?*bool) napi_status { log("napi_strict_equals", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const lhs, const rhs = .{ lhs_.get(), rhs_.get() }; // there is some nuance with NaN here i'm not sure about - result.* = lhs.isSameValue(rhs, env); + result.* = lhs.isSameValue(rhs, env.toJS()); return .ok; } -pub extern fn napi_call_function(env: napi_env, recv: napi_value, func: napi_value, argc: usize, argv: [*c]const napi_value, result: *napi_value) napi_status; -pub extern fn napi_new_instance(env: napi_env, constructor: napi_value, argc: usize, argv: [*c]const napi_value, result_: ?*napi_value) napi_status; -pub export fn napi_instanceof(env: napi_env, object_: napi_value, constructor_: napi_value, result_: ?*bool) napi_status { +pub extern fn napi_call_function(env_: napi_env, recv: napi_value, func: napi_value, argc: usize, argv: [*c]const napi_value, result: *napi_value) napi_status; +pub extern fn napi_new_instance(env_: napi_env, constructor: napi_value, argc: usize, argv: [*c]const napi_value, result_: ?*napi_value) napi_status; +pub export fn napi_instanceof(env_: napi_env, object_: napi_value, constructor_: napi_value, result_: ?*bool) napi_status { log("napi_instanceof", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const object, const constructor = .{ object_.get(), constructor_.get() }; // TODO: does this throw object_expected in node? - result.* = object.isObject() and object.isInstanceOf(env, constructor); + result.* = object.isObject() and object.isInstanceOf(env.toJS(), constructor); return .ok; } -pub extern fn napi_get_cb_info(env: napi_env, cbinfo: napi_callback_info, argc: [*c]usize, argv: *napi_value, this_arg: *napi_value, data: [*]*anyopaque) napi_status; -pub extern fn napi_get_new_target(env: napi_env, cbinfo: napi_callback_info, result: *napi_value) napi_status; +pub extern fn napi_get_cb_info(env_: napi_env, cbinfo: napi_callback_info, argc: [*c]usize, argv: *napi_value, this_arg: *napi_value, data: [*]*anyopaque) napi_status; +pub extern fn napi_get_new_target(env_: napi_env, cbinfo: napi_callback_info, result: *napi_value) napi_status; pub extern fn napi_define_class( - env: napi_env, + env_: napi_env, utf8name: [*c]const u8, length: usize, constructor: napi_callback, @@ -703,21 +764,24 @@ pub extern fn napi_define_class( properties: [*c]const napi_property_descriptor, result: *napi_value, ) napi_status; -pub extern fn napi_wrap(env: napi_env, js_object: napi_value, native_object: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: [*]*Ref) napi_status; -pub extern fn napi_unwrap(env: napi_env, js_object: napi_value, result: [*]*anyopaque) napi_status; -pub extern fn napi_remove_wrap(env: napi_env, js_object: napi_value, result: [*]*anyopaque) napi_status; -pub extern fn napi_create_object(env: napi_env, result: *napi_value) napi_status; -pub extern fn napi_create_external(env: napi_env, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; -pub extern fn napi_get_value_external(env: napi_env, value: napi_value, result: [*]*anyopaque) napi_status; -pub extern fn napi_create_reference(env: napi_env, value: napi_value, initial_refcount: u32, result: **Ref) napi_status; -pub extern fn napi_delete_reference(env: napi_env, ref: *Ref) napi_status; -pub extern fn napi_reference_ref(env: napi_env, ref: *Ref, result: [*c]u32) napi_status; -pub extern fn napi_reference_unref(env: napi_env, ref: *Ref, result: [*c]u32) napi_status; -pub extern fn napi_get_reference_value(env: napi_env, ref: *Ref, result: *napi_value) napi_status; +pub extern fn napi_wrap(env_: napi_env, js_object: napi_value, native_object: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: [*]*Ref) napi_status; +pub extern fn napi_unwrap(env_: napi_env, js_object: napi_value, result: [*]*anyopaque) napi_status; +pub extern fn napi_remove_wrap(env_: napi_env, js_object: napi_value, result: [*]*anyopaque) napi_status; +pub extern fn napi_create_object(env_: napi_env, result: *napi_value) napi_status; +pub extern fn napi_create_external(env_: napi_env, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; +pub extern fn napi_get_value_external(env_: napi_env, value: napi_value, result: [*]*anyopaque) napi_status; +pub extern fn napi_create_reference(env_: napi_env, value: napi_value, initial_refcount: u32, result: **Ref) napi_status; +pub extern fn napi_delete_reference(env_: napi_env, ref: *Ref) napi_status; +pub extern fn napi_reference_ref(env_: napi_env, ref: *Ref, result: [*c]u32) napi_status; +pub extern fn napi_reference_unref(env_: napi_env, ref: *Ref, result: [*c]u32) napi_status; +pub extern fn napi_get_reference_value(env_: napi_env, ref: *Ref, result: *napi_value) napi_status; pub extern fn napi_get_reference_value_internal(ref: *Ref) JSC.JSValue; -pub export fn napi_open_handle_scope(env: napi_env, result_: ?*napi_handle_scope) napi_status { +pub export fn napi_open_handle_scope(env_: napi_env, result_: ?*napi_handle_scope) napi_status { log("napi_open_handle_scope", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -725,8 +789,11 @@ pub export fn napi_open_handle_scope(env: napi_env, result_: ?*napi_handle_scope return .ok; } -pub export fn napi_close_handle_scope(env: napi_env, handle_scope: napi_handle_scope) napi_status { +pub export fn napi_close_handle_scope(env_: napi_env, handle_scope: napi_handle_scope) napi_status { log("napi_close_handle_scope", .{}); + const env = env_ orelse { + return envIsNull(); + }; if (handle_scope) |scope| { scope.close(env); } @@ -735,8 +802,11 @@ pub export fn napi_close_handle_scope(env: napi_env, handle_scope: napi_handle_s } // we don't support async contexts -pub export fn napi_async_init(env: napi_env, _: napi_value, _: napi_value, async_ctx: **anyopaque) napi_status { +pub export fn napi_async_init(env_: napi_env, _: napi_value, _: napi_value, async_ctx: **anyopaque) napi_status { log("napi_async_init", .{}); + const env = env_ orelse { + return envIsNull(); + }; async_ctx.* = env; return .ok; } @@ -748,15 +818,18 @@ pub export fn napi_async_destroy(_: napi_env, _: *anyopaque) napi_status { } // this is just a regular function call -pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv_: napi_value, func_: napi_value, arg_count: usize, args: ?[*]const napi_value, maybe_result: ?*napi_value) napi_status { +pub export fn napi_make_callback(env_: napi_env, _: *anyopaque, recv_: napi_value, func_: napi_value, arg_count: usize, args: ?[*]const napi_value, maybe_result: ?*napi_value) napi_status { log("napi_make_callback", .{}); + const env = env_ orelse { + return envIsNull(); + }; const recv, const func = .{ recv_.get(), func_.get() }; - if (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm())) { + if (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.toJS().vm())) { return .function_expected; } const res = func.call( - env, + env.toJS(), if (recv != .zero) recv else @@ -766,7 +839,7 @@ pub export fn napi_make_callback(env: napi_env, _: *anyopaque, recv_: napi_value else &.{}, ) catch |err| // TODO: handle errors correctly - env.takeException(err); + env.toJS().takeException(err); if (maybe_result) |result| { result.set(env, res); @@ -797,16 +870,22 @@ fn notImplementedYet(comptime name: []const u8) void { ); } -pub export fn napi_open_escapable_handle_scope(env: napi_env, result_: ?*napi_escapable_handle_scope) napi_status { +pub export fn napi_open_escapable_handle_scope(env_: napi_env, result_: ?*napi_escapable_handle_scope) napi_status { log("napi_open_escapable_handle_scope", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; result.* = NapiHandleScope.open(env, true); return .ok; } -pub export fn napi_close_escapable_handle_scope(env: napi_env, scope: napi_escapable_handle_scope) napi_status { +pub export fn napi_close_escapable_handle_scope(env_: napi_env, scope: napi_escapable_handle_scope) napi_status { log("napi_close_escapable_handle_scope", .{}); + const env = env_ orelse { + return envIsNull(); + }; if (scope) |s| { s.close(env); } @@ -836,18 +915,18 @@ pub export fn napi_close_callback_scope(_: napi_env, _: *anyopaque) napi_status log("napi_close_callback_scope", .{}); return .ok; } -pub extern fn napi_throw(env: napi_env, @"error": napi_value) napi_status; -pub extern fn napi_throw_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; -pub extern fn napi_throw_type_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; -pub extern fn napi_throw_range_error(env: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; +pub extern fn napi_throw(env_: napi_env, @"error": napi_value) napi_status; +pub extern fn napi_throw_error(env_: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; +pub extern fn napi_throw_type_error(env_: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; +pub extern fn napi_throw_range_error(env_: napi_env, code: [*c]const u8, msg: [*c]const u8) napi_status; pub export fn napi_is_error(_: napi_env, value_: napi_value, result: *bool) napi_status { log("napi_is_error", .{}); const value = value_.get(); result.* = value.isAnyError(); return .ok; } -pub extern fn napi_is_exception_pending(env: napi_env, result: *bool) napi_status; -pub extern fn napi_get_and_clear_last_exception(env: napi_env, result: *napi_value) napi_status; +pub extern fn napi_is_exception_pending(env_: napi_env, result: *bool) napi_status; +pub extern fn napi_get_and_clear_last_exception(env_: napi_env, result: *napi_value) napi_status; pub export fn napi_is_arraybuffer(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_arraybuffer", .{}); const result = result_ orelse { @@ -857,14 +936,17 @@ pub export fn napi_is_arraybuffer(_: napi_env, value_: napi_value, result_: ?*bo result.* = !value.isNumber() and value.jsTypeLoose() == .ArrayBuffer; return .ok; } -pub extern fn napi_create_arraybuffer(env: napi_env, byte_length: usize, data: [*]const u8, result: *napi_value) napi_status; +pub extern fn napi_create_arraybuffer(env_: napi_env, byte_length: usize, data: [*]const u8, result: *napi_value) napi_status; -pub extern fn napi_create_external_arraybuffer(env: napi_env, external_data: ?*anyopaque, byte_length: usize, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; +pub extern fn napi_create_external_arraybuffer(env_: napi_env, external_data: ?*anyopaque, byte_length: usize, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; -pub export fn napi_get_arraybuffer_info(env: napi_env, arraybuffer_: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status { +pub export fn napi_get_arraybuffer_info(env_: napi_env, arraybuffer_: napi_value, data: ?*[*]u8, byte_length: ?*usize) napi_status { log("napi_get_arraybuffer_info", .{}); + const env = env_ orelse { + return envIsNull(); + }; const arraybuffer = arraybuffer_.get(); - const array_buffer = arraybuffer.asArrayBuffer(env) orelse return .arraybuffer_expected; + const array_buffer = arraybuffer.asArrayBuffer(env.toJS()) orelse return .arraybuffer_expected; const slice = array_buffer.slice(); if (data) |dat| dat.* = slice.ptr; @@ -879,15 +961,18 @@ pub export fn napi_is_typedarray(_: napi_env, value_: napi_value, result: ?*bool result.?.* = value.jsTypeLoose().isTypedArray(); return if (result != null) .ok else invalidArg(); } -pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer_: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status { +pub export fn napi_create_typedarray(env_: napi_env, @"type": napi_typedarray_type, length: usize, arraybuffer_: napi_value, byte_offset: usize, result_: ?*napi_value) napi_status { log("napi_create_typedarray", .{}); + const env = env_ orelse { + return envIsNull(); + }; const arraybuffer = arraybuffer_.get(); const result = result_ orelse { return invalidArg(); }; result.set(env, JSValue.c( JSC.C.JSObjectMakeTypedArrayWithArrayBufferAndOffset( - env.ref(), + env.toJS().ref(), @"type".toC(), arraybuffer.asObjectRef(), byte_offset, @@ -898,7 +983,7 @@ pub export fn napi_create_typedarray(env: napi_env, @"type": napi_typedarray_typ return .ok; } pub export fn napi_get_typedarray_info( - env: napi_env, + env_: napi_env, typedarray_: napi_value, maybe_type: ?*napi_typedarray_type, maybe_length: ?*usize, @@ -907,12 +992,15 @@ pub export fn napi_get_typedarray_info( maybe_byte_offset: ?*usize, ) napi_status { log("napi_get_typedarray_info", .{}); + const env = env_ orelse { + return envIsNull(); + }; const typedarray = typedarray_.get(); if (typedarray.isEmptyOrUndefinedOrNull()) return invalidArg(); defer typedarray.ensureStillAlive(); - const array_buffer = typedarray.asArrayBuffer(env) orelse return invalidArg(); + const array_buffer = typedarray.asArrayBuffer(env.toJS()) orelse return invalidArg(); if (maybe_type) |@"type"| @"type".* = napi_typedarray_type.fromJSType(array_buffer.typed_array_type) orelse return invalidArg(); @@ -924,13 +1012,13 @@ pub export fn napi_get_typedarray_info( length.* = array_buffer.len; if (maybe_arraybuffer) |arraybuffer| - arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), typedarray.asObjectRef(), null))); + arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.toJS().ref(), typedarray.asObjectRef(), null))); if (maybe_byte_offset) |byte_offset| byte_offset.* = array_buffer.offset; return .ok; } -pub extern fn napi_create_dataview(env: napi_env, length: usize, arraybuffer: napi_value, byte_offset: usize, result: *napi_value) napi_status; +pub extern fn napi_create_dataview(env_: napi_env, length: usize, arraybuffer: napi_value, byte_offset: usize, result: *napi_value) napi_status; pub export fn napi_is_dataview(_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_dataview", .{}); const result = result_ orelse { @@ -941,7 +1029,7 @@ pub export fn napi_is_dataview(_: napi_env, value_: napi_value, result_: ?*bool) return .ok; } pub export fn napi_get_dataview_info( - env: napi_env, + env_: napi_env, dataview_: napi_value, maybe_bytelength: ?*usize, maybe_data: ?*[*]u8, @@ -949,8 +1037,11 @@ pub export fn napi_get_dataview_info( maybe_byte_offset: ?*usize, ) napi_status { log("napi_get_dataview_info", .{}); + const env = env_ orelse { + return envIsNull(); + }; const dataview = dataview_.get(); - const array_buffer = dataview.asArrayBuffer(env) orelse return .object_expected; + const array_buffer = dataview.asArrayBuffer(env.toJS()) orelse return .object_expected; if (maybe_bytelength) |bytelength| bytelength.* = array_buffer.byte_len; @@ -958,7 +1049,7 @@ pub export fn napi_get_dataview_info( data.* = array_buffer.ptr; if (maybe_arraybuffer) |arraybuffer| - arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.ref(), dataview.asObjectRef(), null))); + arraybuffer.set(env, JSValue.c(JSC.C.JSObjectGetTypedArrayBuffer(env.toJS().ref(), dataview.asObjectRef(), null))); if (maybe_byte_offset) |byte_offset| byte_offset.* = array_buffer.offset; @@ -973,8 +1064,11 @@ pub export fn napi_get_version(_: napi_env, result_: ?*u32) napi_status { result.* = NAPI_VERSION; return .ok; } -pub export fn napi_create_promise(env: napi_env, deferred_: ?*napi_deferred, promise_: ?*napi_value) napi_status { +pub export fn napi_create_promise(env_: napi_env, deferred_: ?*napi_deferred, promise_: ?*napi_value) napi_status { log("napi_create_promise", .{}); + const env = env_ orelse { + return envIsNull(); + }; const deferred = deferred_ orelse { return invalidArg(); }; @@ -982,24 +1076,30 @@ pub export fn napi_create_promise(env: napi_env, deferred_: ?*napi_deferred, pro return invalidArg(); }; deferred.* = bun.default_allocator.create(JSC.JSPromise.Strong) catch @panic("failed to allocate napi_deferred"); - deferred.*.* = JSC.JSPromise.Strong.init(env); - promise.set(env, deferred.*.get().asValue(env)); + deferred.*.* = JSC.JSPromise.Strong.init(env.toJS()); + promise.set(env, deferred.*.get().asValue(env.toJS())); return .ok; } -pub export fn napi_resolve_deferred(env: napi_env, deferred: napi_deferred, resolution_: napi_value) napi_status { +pub export fn napi_resolve_deferred(env_: napi_env, deferred: napi_deferred, resolution_: napi_value) napi_status { log("napi_resolve_deferred", .{}); + const env = env_ orelse { + return envIsNull(); + }; const resolution = resolution_.get(); var prom = deferred.get(); - prom.resolve(env, resolution); + prom.resolve(env.toJS(), resolution); deferred.deinit(); bun.default_allocator.destroy(deferred); return .ok; } -pub export fn napi_reject_deferred(env: napi_env, deferred: napi_deferred, rejection_: napi_value) napi_status { +pub export fn napi_reject_deferred(env_: napi_env, deferred: napi_deferred, rejection_: napi_value) napi_status { log("napi_reject_deferred", .{}); + const env = env_ orelse { + return envIsNull(); + }; const rejection = rejection_.get(); var prom = deferred.get(); - prom.reject(env, rejection); + prom.reject(env.toJS(), rejection); deferred.deinit(); bun.default_allocator.destroy(deferred); return .ok; @@ -1018,15 +1118,18 @@ pub export fn napi_is_promise(_: napi_env, value_: napi_value, is_promise_: ?*bo is_promise.* = value.asAnyPromise() != null; return .ok; } -pub extern fn napi_run_script(env: napi_env, script: napi_value, result: *napi_value) napi_status; -pub extern fn napi_adjust_external_memory(env: napi_env, change_in_bytes: i64, adjusted_value: [*c]i64) napi_status; -pub export fn napi_create_date(env: napi_env, time: f64, result_: ?*napi_value) napi_status { +pub extern fn napi_run_script(env_: napi_env, script: napi_value, result: *napi_value) napi_status; +pub extern fn napi_adjust_external_memory(env_: napi_env, change_in_bytes: i64, adjusted_value: [*c]i64) napi_status; +pub export fn napi_create_date(env_: napi_env, time: f64, result_: ?*napi_value) napi_status { log("napi_create_date", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; var args = [_]JSC.C.JSValueRef{JSC.JSValue.jsNumber(time).asObjectRef()}; - result.set(env, JSValue.c(JSC.C.JSObjectMakeDate(env.ref(), 1, &args, TODO_EXCEPTION))); + result.set(env, JSValue.c(JSC.C.JSObjectMakeDate(env.toJS().ref(), 1, &args, TODO_EXCEPTION))); return .ok; } pub export fn napi_is_date(_: napi_env, value_: napi_value, is_date_: ?*bool) napi_status { @@ -1038,25 +1141,31 @@ pub export fn napi_is_date(_: napi_env, value_: napi_value, is_date_: ?*bool) na is_date.* = value.jsTypeLoose() == .JSDate; return .ok; } -pub extern fn napi_get_date_value(env: napi_env, value: napi_value, result: *f64) napi_status; -pub extern fn napi_add_finalizer(env: napi_env, js_object: napi_value, native_object: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *Ref) napi_status; -pub export fn napi_create_bigint_int64(env: napi_env, value: i64, result_: ?*napi_value) napi_status { +pub extern fn napi_get_date_value(env_: napi_env, value: napi_value, result: *f64) napi_status; +pub extern fn napi_add_finalizer(env_: napi_env, js_object: napi_value, native_object: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *Ref) napi_status; +pub export fn napi_create_bigint_int64(env_: napi_env, value: i64, result_: ?*napi_value) napi_status { log("napi_create_bigint_int64", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; - result.set(env, JSC.JSValue.fromInt64NoTruncate(env, value)); + result.set(env, JSC.JSValue.fromInt64NoTruncate(env.toJS(), value)); return .ok; } -pub export fn napi_create_bigint_uint64(env: napi_env, value: u64, result_: ?*napi_value) napi_status { +pub export fn napi_create_bigint_uint64(env_: napi_env, value: u64, result_: ?*napi_value) napi_status { log("napi_create_bigint_uint64", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; - result.set(env, JSC.JSValue.fromUInt64NoTruncate(env, value)); + result.set(env, JSC.JSValue.fromUInt64NoTruncate(env.toJS(), value)); return .ok; } -pub extern fn napi_create_bigint_words(env: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status; +pub extern fn napi_create_bigint_words(env_: napi_env, sign_bit: c_int, word_count: usize, words: [*c]const u64, result: *napi_value) napi_status; // TODO: lossless pub export fn napi_get_value_bigint_int64(_: napi_env, value_: napi_value, result_: ?*i64, _: *bool) napi_status { log("napi_get_value_bigint_int64", .{}); @@ -1078,12 +1187,12 @@ pub export fn napi_get_value_bigint_uint64(_: napi_env, value_: napi_value, resu return .ok; } -pub extern fn napi_get_value_bigint_words(env: napi_env, value: napi_value, sign_bit: [*c]c_int, word_count: [*c]usize, words: [*c]u64) napi_status; -pub extern fn napi_get_all_property_names(env: napi_env, object: napi_value, key_mode: napi_key_collection_mode, key_filter: napi_key_filter, key_conversion: napi_key_conversion, result: *napi_value) napi_status; -pub extern fn napi_set_instance_data(env: napi_env, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque) napi_status; -pub extern fn napi_get_instance_data(env: napi_env, data: [*]*anyopaque) napi_status; -pub extern fn napi_detach_arraybuffer(env: napi_env, arraybuffer: napi_value) napi_status; -pub extern fn napi_is_detached_arraybuffer(env: napi_env, value: napi_value, result: *bool) napi_status; +pub extern fn napi_get_value_bigint_words(env_: napi_env, value: napi_value, sign_bit: [*c]c_int, word_count: [*c]usize, words: [*c]u64) napi_status; +pub extern fn napi_get_all_property_names(env_: napi_env, object: napi_value, key_mode: napi_key_collection_mode, key_filter: napi_key_filter, key_conversion: napi_key_conversion, result: *napi_value) napi_status; +pub extern fn napi_set_instance_data(env_: napi_env, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque) napi_status; +pub extern fn napi_get_instance_data(env_: napi_env, data: [*]*anyopaque) napi_status; +pub extern fn napi_detach_arraybuffer(env_: napi_env, arraybuffer: napi_value) napi_status; +pub extern fn napi_is_detached_arraybuffer(env_: napi_env, value: napi_value, result: *bool) napi_status; pub const struct_napi_async_work__ = opaque {}; const WorkPool = @import("../work_pool.zig").WorkPool; @@ -1095,7 +1204,8 @@ pub const napi_async_work = struct { concurrent_task: JSC.ConcurrentTask = .{}, completion_task: ?*anyopaque = null, event_loop: *JSC.EventLoop, - global: napi_env, + global: *JSC.JSGlobalObject, + env: *NapiEnv, execute: napi_async_execute_callback = null, complete: napi_async_complete_callback = null, ctx: ?*anyopaque = null, @@ -1111,10 +1221,12 @@ pub const napi_async_work = struct { cancelled = 3, }; - pub fn create(global: napi_env, execute: napi_async_execute_callback, complete: napi_async_complete_callback, ctx: ?*anyopaque) !*napi_async_work { + pub fn create(env: *NapiEnv, execute: napi_async_execute_callback, complete: napi_async_complete_callback, ctx: ?*anyopaque) !*napi_async_work { const work = try bun.default_allocator.create(napi_async_work); + const global = env.toJS(); work.* = .{ .global = global, + .env = env, .execute = execute, .event_loop = global.bunVM().eventLoop(), .complete = complete, @@ -1138,7 +1250,7 @@ pub const napi_async_work = struct { } return; } - this.execute.?(this.global, this.ctx); + this.execute.?(this.env, this.ctx); this.status.store(@intFromEnum(Status.completed), .seq_cst); this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); @@ -1167,10 +1279,10 @@ pub const napi_async_work = struct { } fn runFromJSWithError(this: *napi_async_work) bun.JSError!void { - const handle_scope = NapiHandleScope.open(this.global, false); - defer if (handle_scope) |scope| scope.close(this.global); + const handle_scope = NapiHandleScope.open(this.env, false); + defer if (handle_scope) |scope| scope.close(this.env); this.complete.?( - this.global, + this.env, if (this.status.load(.seq_cst) == @intFromEnum(Status.cancelled)) napi_status.cancelled else @@ -1253,25 +1365,31 @@ pub export fn napi_fatal_error(location_ptr: ?[*:0]const u8, location_len: usize bun.Output.panic("napi: {s}", .{message}); } -pub export fn napi_create_buffer(env: napi_env, length: usize, data: ?**anyopaque, result: *napi_value) napi_status { +pub export fn napi_create_buffer(env_: napi_env, length: usize, data: ?**anyopaque, result: *napi_value) napi_status { log("napi_create_buffer: {d}", .{length}); - var buffer = JSC.JSValue.createBufferFromLength(env, length); + const env = env_ orelse { + return envIsNull(); + }; + var buffer = JSC.JSValue.createBufferFromLength(env.toJS(), length); if (length > 0) { if (data) |ptr| { - ptr.* = buffer.asArrayBuffer(env).?.ptr; + ptr.* = buffer.asArrayBuffer(env.toJS()).?.ptr; } } result.set(env, buffer); return .ok; } -pub extern fn napi_create_external_buffer(env: napi_env, length: usize, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; -pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8, result_data: ?*?*anyopaque, result_: ?*napi_value) napi_status { +pub extern fn napi_create_external_buffer(env_: napi_env, length: usize, data: ?*anyopaque, finalize_cb: napi_finalize, finalize_hint: ?*anyopaque, result: *napi_value) napi_status; +pub export fn napi_create_buffer_copy(env_: napi_env, length: usize, data: [*]u8, result_data: ?*?*anyopaque, result_: ?*napi_value) napi_status { log("napi_create_buffer_copy: {d}", .{length}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; - var buffer = JSC.JSValue.createBufferFromLength(env, length); - if (buffer.asArrayBuffer(env)) |array_buf| { + var buffer = JSC.JSValue.createBufferFromLength(env.toJS(), length); + if (buffer.asArrayBuffer(env.toJS())) |array_buf| { if (length > 0) { @memcpy(array_buf.slice()[0..length], data[0..length]); } @@ -1284,19 +1402,25 @@ pub export fn napi_create_buffer_copy(env: napi_env, length: usize, data: [*]u8, return .ok; } -pub export fn napi_is_buffer(env: napi_env, value_: napi_value, result_: ?*bool) napi_status { +pub export fn napi_is_buffer(env_: napi_env, value_: napi_value, result_: ?*bool) napi_status { log("napi_is_buffer", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const value = value_.get(); - result.* = value.isBuffer(env); + result.* = value.isBuffer(env.toJS()); return .ok; } -pub export fn napi_get_buffer_info(env: napi_env, value_: napi_value, data: ?*[*]u8, length: ?*usize) napi_status { +pub export fn napi_get_buffer_info(env_: napi_env, value_: napi_value, data: ?*[*]u8, length: ?*usize) napi_status { log("napi_get_buffer_info", .{}); + const env = env_ orelse { + return envIsNull(); + }; const value = value_.get(); - const array_buf = value.asArrayBuffer(env) orelse { + const array_buf = value.asArrayBuffer(env.toJS()) orelse { // TODO: is invalid_arg what to return here? return .arraybuffer_expected; }; @@ -1317,7 +1441,7 @@ extern fn node_api_create_external_string_latin1(napi_env, [*:0]u8, usize, napi_ extern fn node_api_create_external_string_utf16(napi_env, [*:0]u16, usize, napi_finalize, ?*anyopaque, *JSValue, *bool) napi_status; pub export fn napi_create_async_work( - env: napi_env, + env_: napi_env, _: napi_value, _: [*:0]const u8, execute: napi_async_execute_callback, @@ -1326,6 +1450,9 @@ pub export fn napi_create_async_work( result_: ?**napi_async_work, ) napi_status { log("napi_create_async_work", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; @@ -1334,30 +1461,39 @@ pub export fn napi_create_async_work( }; return .ok; } -pub export fn napi_delete_async_work(env: napi_env, work_: ?*napi_async_work) napi_status { +pub export fn napi_delete_async_work(env_: napi_env, work_: ?*napi_async_work) napi_status { log("napi_delete_async_work", .{}); + const env = env_ orelse { + return envIsNull(); + }; const work = work_ orelse { return invalidArg(); }; - bun.assert(env == work.global); + bun.assert(env.toJS() == work.global); work.deinit(); return .ok; } -pub export fn napi_queue_async_work(env: napi_env, work_: ?*napi_async_work) napi_status { +pub export fn napi_queue_async_work(env_: napi_env, work_: ?*napi_async_work) napi_status { log("napi_queue_async_work", .{}); + const env = env_ orelse { + return envIsNull(); + }; const work = work_ orelse { return invalidArg(); }; - bun.assert(env == work.global); + bun.assert(env.toJS() == work.global); work.schedule(); return .ok; } -pub export fn napi_cancel_async_work(env: napi_env, work_: ?*napi_async_work) napi_status { +pub export fn napi_cancel_async_work(env_: napi_env, work_: ?*napi_async_work) napi_status { log("napi_cancel_async_work", .{}); + const env = env_ orelse { + return envIsNull(); + }; const work = work_ orelse { return invalidArg(); }; - bun.assert(env == work.global); + bun.assert(env.toJS() == work.global); if (work.cancel()) { return .ok; } @@ -1373,8 +1509,11 @@ pub export fn napi_get_node_version(_: napi_env, version_: ?**const napi_node_ve return .ok; } const napi_event_loop = if (bun.Environment.isWindows) *bun.windows.libuv.Loop else *JSC.EventLoop; -pub export fn napi_get_uv_event_loop(env: napi_env, loop_: ?*napi_event_loop) napi_status { +pub export fn napi_get_uv_event_loop(env_: napi_env, loop_: ?*napi_event_loop) napi_status { log("napi_get_uv_event_loop", .{}); + const env = env_ orelse { + return envIsNull(); + }; const loop = loop_ orelse { return invalidArg(); }; @@ -1384,37 +1523,45 @@ pub export fn napi_get_uv_event_loop(env: napi_env, loop_: ?*napi_event_loop) na loop.* = JSC.VirtualMachine.get().uvLoop(); } else { // there is no uv event loop on posix, we use our event loop handle. - loop.* = env.bunVM().eventLoop(); + loop.* = env.toJS().bunVM().eventLoop(); } return .ok; } -pub extern fn napi_fatal_exception(env: napi_env, err: napi_value) napi_status; +pub extern fn napi_fatal_exception(env_: napi_env, err: napi_value) napi_status; // We use a linked list here because we assume removing these is relatively rare // and array reallocations are relatively expensive. -pub export fn napi_add_env_cleanup_hook(env: napi_env, fun: ?*const fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { +pub export fn napi_add_env_cleanup_hook(env_: napi_env, fun: ?*const fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { log("napi_add_env_cleanup_hook", .{}); - if (fun == null) + const env = env_ orelse { + return envIsNull(); + }; + if (fun == null) { return .ok; + } - env.bunVM().rareData().pushCleanupHook(env, arg, fun.?); + env.toJS().bunVM().rareData().pushCleanupHook(env.toJS(), arg, fun.?); return .ok; } -pub export fn napi_remove_env_cleanup_hook(env: napi_env, fun: ?*const fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { +pub export fn napi_remove_env_cleanup_hook(env_: napi_env, fun: ?*const fn (?*anyopaque) callconv(.C) void, arg: ?*anyopaque) napi_status { log("napi_remove_env_cleanup_hook", .{}); + const env = env_ orelse { + return envIsNull(); + }; - // Avoid looking up env.bunVM(). + // Avoid looking up env.toJS().bunVM(). if (bun.Global.isExiting()) { return .ok; } const vm = JSC.VirtualMachine.get(); - if (vm.rare_data == null or fun == null or vm.isShuttingDown()) + if (vm.rare_data == null or fun == null or vm.isShuttingDown()) { return .ok; + } var rare_data = vm.rare_data.?; - const cmp = JSC.RareData.CleanupHook.init(env, arg, fun.?); + const cmp = JSC.RareData.CleanupHook.init(env.toJS(), arg, fun.?); for (rare_data.cleanup_hooks.items, 0..) |*hook, i| { if (hook.eql(cmp)) { _ = rare_data.cleanup_hooks.orderedRemove(i); @@ -1426,8 +1573,10 @@ pub export fn napi_remove_env_cleanup_hook(env: napi_env, fun: ?*const fn (?*any } pub const Finalizer = struct { + env: napi_env, fun: napi_finalize, data: ?*anyopaque = null, + hint: ?*anyopaque = null, }; // TODO: generate comptime version of this instead of runtime checking @@ -1465,9 +1614,9 @@ pub const ThreadSafeFunction = struct { event_loop: *JSC.EventLoop, tracker: JSC.AsyncTaskTracker, - env: napi_env, + env: *NapiEnv, - finalizer: Finalizer = Finalizer{ .fun = null, .data = null }, + finalizer: Finalizer = Finalizer{ .env = null, .fun = null, .data = null, .hint = null }, has_queued_finalizer: bool = false, queue: Queue = .{ .data = std.fifo.LinearFifo(?*anyopaque, .Dynamic).init(bun.default_allocator), @@ -1616,7 +1765,8 @@ pub const ThreadSafeFunction = struct { /// See: https://github.com/nodejs/node/pull/38506 /// In that case, we need to drain microtasks. fn call(this: *ThreadSafeFunction, task: ?*anyopaque, is_first: bool) void { - const globalObject = this.env; + const env = this.env; + const globalObject = env.toJS(); if (!is_first) { this.event_loop.drainMicrotasks(); } @@ -1637,9 +1787,9 @@ pub const ThreadSafeFunction = struct { .c => |cb| { const js = cb.js.get() orelse .undefined; - const handle_scope = NapiHandleScope.open(globalObject, false); - defer if (handle_scope) |scope| scope.close(globalObject); - cb.napi_threadsafe_function_call_js(globalObject, napi_value.create(globalObject, js), this.ctx, task); + const handle_scope = NapiHandleScope.open(env, false); + defer if (handle_scope) |scope| scope.close(env); + cb.napi_threadsafe_function_call_js(env, napi_value.create(env, js), this.ctx, task); }, } } @@ -1691,7 +1841,7 @@ pub const ThreadSafeFunction = struct { if (this.finalizer.fun) |fun| { const handle_scope = NapiHandleScope.open(this.env, false); defer if (handle_scope) |scope| scope.close(this.env); - fun(this.event_loop.global, this.finalizer.data, this.ctx); + fun(this.env, this.finalizer.data, this.ctx); } this.callback.deinit(); @@ -1745,7 +1895,7 @@ pub const ThreadSafeFunction = struct { }; pub export fn napi_create_threadsafe_function( - env: napi_env, + env_: napi_env, func_: napi_value, _: napi_value, _: napi_value, @@ -1758,26 +1908,29 @@ pub export fn napi_create_threadsafe_function( result_: ?*napi_threadsafe_function, ) napi_status { log("napi_create_threadsafe_function", .{}); + const env = env_ orelse { + return envIsNull(); + }; const result = result_ orelse { return invalidArg(); }; const func = func_.get(); - if (call_js_cb == null and (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.vm()))) { + if (call_js_cb == null and (func.isEmptyOrUndefinedOrNull() or !func.isCallable(env.toJS().vm()))) { return napi_status.function_expected; } - const vm = env.bunVM(); + const vm = env.toJS().bunVM(); var function = ThreadSafeFunction.new(.{ .event_loop = vm.eventLoop(), .env = env, .callback = if (call_js_cb) |c| .{ .c = .{ .napi_threadsafe_function_call_js = c, - .js = if (func == .zero) .{} else JSC.Strong.create(func.withAsyncContextIfNeeded(env), vm.global), + .js = if (func == .zero) .{} else JSC.Strong.create(func.withAsyncContextIfNeeded(env.toJS()), vm.global), }, } else .{ - .js = if (func == .zero) .{} else JSC.Strong.create(func.withAsyncContextIfNeeded(env), vm.global), + .js = if (func == .zero) .{} else JSC.Strong.create(func.withAsyncContextIfNeeded(env.toJS()), vm.global), }, .ctx = context, .queue = ThreadSafeFunction.Queue.init(max_queue_size, bun.default_allocator), @@ -1786,7 +1939,7 @@ pub export fn napi_create_threadsafe_function( .tracker = JSC.AsyncTaskTracker.init(vm), }); - function.finalizer = .{ .data = thread_finalize_data, .fun = thread_finalize_cb }; + function.finalizer = .{ .env = env, .data = thread_finalize_data, .fun = thread_finalize_cb, .hint = null }; // nodejs by default keeps the event loop alive until the thread-safe function is unref'd function.ref(); function.tracker.didSchedule(vm.global); @@ -1811,15 +1964,21 @@ pub export fn napi_release_threadsafe_function(func: napi_threadsafe_function, m log("napi_release_threadsafe_function", .{}); return func.release(mode, false); } -pub export fn napi_unref_threadsafe_function(env: napi_env, func: napi_threadsafe_function) napi_status { +pub export fn napi_unref_threadsafe_function(env_: napi_env, func: napi_threadsafe_function) napi_status { log("napi_unref_threadsafe_function", .{}); - bun.assert(func.event_loop.global == env); + const env = env_ orelse { + return envIsNull(); + }; + bun.assert(func.event_loop.global == env.toJS()); func.unref(); return .ok; } -pub export fn napi_ref_threadsafe_function(env: napi_env, func: napi_threadsafe_function) napi_status { +pub export fn napi_ref_threadsafe_function(env_: napi_env, func: napi_threadsafe_function) napi_status { log("napi_ref_threadsafe_function", .{}); - bun.assert(func.event_loop.global == env); + const env = env_ orelse { + return envIsNull(); + }; + bun.assert(func.event_loop.global == env.toJS()); func.ref(); return .ok; } From 85499c8709131cf53d9a6e1ff22798a6277ad717 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 17:22:21 -0800 Subject: [PATCH 03/11] Splitting napi envs mostly works now, save for one napi test --- src/bun.js/bindings/BunProcess.cpp | 29 ++++++-- src/bun.js/bindings/ZigGlobalObject.cpp | 2 +- src/bun.js/bindings/ZigGlobalObject.h | 1 + src/bun.js/bindings/napi.cpp | 87 ++++++++++++----------- src/bun.js/bindings/napi.h | 31 ++++---- src/bun.js/bindings/napi_external.cpp | 4 +- src/bun.js/bindings/napi_external.h | 21 +++--- src/bun.js/bindings/napi_handle_scope.cpp | 13 ++-- src/bun.js/bindings/napi_handle_scope.h | 8 ++- src/bun.js/bindings/v8/V8External.cpp | 2 +- 10 files changed, 112 insertions(+), 86 deletions(-) diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index f0d82411b20e2a..832b3d61540f80 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -394,16 +394,14 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return JSValue::encode(jsUndefined()); } - JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject, - JSC::EncodedJSValue exports); + JSC::EncodedJSValue (*napi_register_module_v1)(napi_env env, 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( + napi_register_module_v1 = reinterpret_cast( dlsym(handle, "napi_register_module_v1")); #if OS(WINDOWS) @@ -420,10 +418,31 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return {}; } + auto node_api_module_get_api_version_v1 = reinterpret_cast(dlsym(handle, "node_api_module_get_api_version_v1")); + NapiHandleScope handleScope(globalObject); + int module_version = 8; + if (node_api_module_get_api_version_v1) { + module_version = node_api_module_get_api_version_v1(); + } + + 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); + EncodedJSValue exportsValue = JSC::JSValue::encode(exports); - JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(globalObject, exportsValue)); + JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(env, exportsValue)); RETURN_IF_EXCEPTION(scope, {}); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 2f3e0b2323aee3..4638d82c724e54 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1206,7 +1206,7 @@ GlobalObject::~GlobalObject() { if (napiInstanceDataFinalizer) { napi_finalize finalizer = reinterpret_cast(napiInstanceDataFinalizer); - finalizer(toNapi(this), napiInstanceData, napiInstanceDataFinalizerHint); + finalizer(napiInstanceDataEnv, napiInstanceData, napiInstanceDataFinalizerHint); } if (auto* ctx = scriptExecutionContext()) { diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 022214a9fb8a85..7d80c81a97738b 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -453,6 +453,7 @@ class GlobalObject : public Bun::GlobalScope { // This is not a correct implementation // Addon modules can override each other's data void* napiInstanceData = nullptr; + struct napi_env__* napiInstanceDataEnv = nullptr; void* napiInstanceDataFinalizer = nullptr; void* napiInstanceDataFinalizerHint = nullptr; diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index cb25716cd4f4dc..23dffaa54ceaf3 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -135,7 +135,7 @@ class NapiRefWeakHandleOwner final : public JSC::WeakHandleOwner { 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 +146,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 +182,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 +432,6 @@ class NAPIFunction : public JSC::JSFunction { { ASSERT(jsCast(callframe->jsCallee())); auto* function = static_cast(callframe->jsCallee()); - auto* env = toNapi(globalObject); auto* callback = reinterpret_cast(function->m_method); JSC::VM& vm = globalObject->vm(); @@ -446,28 +445,29 @@ class NAPIFunction : public JSC::JSFunction { auto scope = DECLARE_THROW_SCOPE(vm); Bun::NapiHandleScope handleScope(jsCast(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(vm)) NAPIFunction(vm, executable, globalObject, structure, method, dataPtr); + NAPIFunction* functionObject = new (NotNull, JSC::allocateCell(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 +501,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 +536,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t JSC::JSValue value; auto method = reinterpret_cast(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 +551,7 @@ static void defineNapiProperty(Zig::GlobalObject* globalObject, JSC::JSObject* t auto setterProperty = reinterpret_cast(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 +561,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 +901,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(str), static_cast(length) }, finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) { + WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast(str), static_cast(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 +938,13 @@ node_api_create_external_string_utf16(napi_env env, } length = length == NAPI_AUTO_LENGTH ? std::char_traits::length(str) : length; - WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast(str), static_cast(length) }, finalize_hint, [finalize_callback](void* hint, void* str, unsigned length) { + WTF::ExternalStringImpl& impl = WTF::ExternalStringImpl::create({ reinterpret_cast(str), static_cast(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 +960,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 +990,7 @@ extern "C" void napi_module_register(napi_module* mod) JSC::Strong 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 +1035,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(value)) { refPtr = &val->napiRef; @@ -1053,7 +1053,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 +1158,7 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name, } auto method = reinterpret_cast(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 +1280,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 +1377,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 +1814,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 +1852,15 @@ JSC_DEFINE_HOST_FUNCTION(NapiClass_ConstructorFunction, frame.newTarget = newTarget; Bun::NapiHandleScope handleScope(jsCast(globalObject)); - napi->constructor()(globalObject, reinterpret_cast(NAPICallFrame::toNapiCallbackInfo(frame))); + JSValue ret = toJS(napi->constructor()(napi->env(), NAPICallFrame::toNapiCallbackInfo(frame))); RETURN_IF_EXCEPTION(scope, {}); - RELEASE_AND_RETURN(scope, JSValue::encode(frame.thisValue())); + if (ret.isEmpty()) { + ret = jsUndefined(); + } + RELEASE_AND_RETURN(scope, JSValue::encode(ret)); } -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 +1869,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(vm)) NapiClass(vm, executable, globalObject, structure); + NapiClass* napiClass = new (NotNull, allocateCell(vm)) NapiClass(vm, executable, env, structure); napiClass->finishCreation(vm, executable, length, name, constructor, data, property_count, properties); return napiClass; } @@ -1880,7 +1883,7 @@ void NapiClass::finishCreation(VM& vm, NativeExecutable* executable, unsigned le { Base::finishCreation(vm, executable, length, name); ASSERT(inherits(info())); - this->m_constructor = reinterpret_cast(constructor); + this->m_constructor = constructor; auto globalObject = reinterpret_cast(this->globalObject()); this->putDirect(vm, vm.propertyNames->name, jsString(vm, name), JSC::PropertyAttribute::DontEnum | 0); @@ -1893,9 +1896,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 +2007,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 +2091,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(data), length }, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { + auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(data), length }, createSharedTask([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 +2119,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(external_data), byte_length }, createSharedTask([globalObject, finalize_hint, finalize_cb](void* p) { + auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(external_data), byte_length }, createSharedTask([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 +2396,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(finalize_cb)); + JSValue value = Bun::NapiExternal::create(vm, structure, data, finalize_hint, env, reinterpret_cast(finalize_cb)); JSC::EnsureStillAliveScope ensureStillAlive(value); *result = toNapi(value, globalObject); return napi_ok; @@ -2561,6 +2564,7 @@ extern "C" napi_status napi_get_instance_data(napi_env env, return napi_invalid_arg; } + ASSERT(globalObject->napiInstanceDataEnv == env); *data = globalObject->napiInstanceData; return napi_ok; } @@ -2619,6 +2623,7 @@ extern "C" napi_status napi_set_instance_data(napi_env env, Zig::GlobalObject* globalObject = toJS(env); globalObject->napiInstanceData = data; + globalObject->napiInstanceDataEnv = env; globalObject->napiInstanceDataFinalizer = reinterpret_cast(finalize_cb); globalObject->napiInstanceDataFinalizerHint = finalize_hint; diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index 8668fab6c56678..83018b7cacfb78 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -62,17 +62,12 @@ static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObjec return reinterpret_cast(JSC::JSValue::encode(val)); } -static inline napi_env toNapi(JSC::JSGlobalObject* val) -{ - return reinterpret_cast(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 @@ -163,12 +158,11 @@ class NapiRef { void unref(); void clear(); - NapiRef(JSC::JSGlobalObject* global, uint32_t count) + NapiRef(napi_env env, uint32_t count) + : env(env) + , globalObject(JSC::Weak(env->globalObject())) + , refCount(count) { - globalObject = JSC::Weak(global); - strongRef = {}; - weakValueRef.clear(); - refCount = count; } JSC::JSValue value() const @@ -188,6 +182,7 @@ class NapiRef { weakValueRef.clear(); } + napi_env env = nullptr; JSC::Weak globalObject; NapiWeakValue weakValueRef; JSC::Strong strongRef; @@ -226,7 +221,7 @@ class NapiClass final : public JSC::JSFunction { 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, @@ -239,18 +234,22 @@ class NapiClass final : public JSC::JSFunction { 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, diff --git a/src/bun.js/bindings/napi_external.cpp b/src/bun.js/bindings/napi_external.cpp index f6925d999297ca..874d088ab639b6 100644 --- a/src/bun.js/bindings/napi_external.cpp +++ b/src/bun.js/bindings/napi_external.cpp @@ -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(finalizer)(toNapi(this->napi_env), m_value, m_finalizerHint); + reinterpret_cast(m_finalizer)(m_env, m_value, m_finalizerHint); } } diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index 99a50648dc4dc0..f6a41674db2d50 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -1,11 +1,10 @@ - - #pragma once #include "root.h" #include "BunBuiltinNames.h" #include "BunClientData.h" +#include "napi.h" namespace Bun { @@ -47,11 +46,11 @@ class NapiExternal : public JSC::JSDestructibleObject { 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(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 @@ class NapiExternal : public JSC::JSDestructibleObject { 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(); diff --git a/src/bun.js/bindings/napi_handle_scope.cpp b/src/bun.js/bindings/napi_handle_scope.cpp index ed3af6e6182bfe..cd501c733378a0 100644 --- a/src/bun.js/bindings/napi_handle_scope.cpp +++ b/src/bun.js/bindings/napi_handle_scope.cpp @@ -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) diff --git a/src/bun.js/bindings/napi_handle_scope.h b/src/bun.js/bindings/napi_handle_scope.h index 851d5006a5f923..b6787700384700 100644 --- a/src/bun.js/bindings/napi_handle_scope.h +++ b/src/bun.js/bindings/napi_handle_scope.h @@ -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 @@ class NapiHandleScope { }; // 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 diff --git a/src/bun.js/bindings/v8/V8External.cpp b/src/bun.js/bindings/v8/V8External.cpp index 51023c986356d4..1a003d8509a974 100644 --- a/src/bun.js/bindings/v8/V8External.cpp +++ b/src/bun.js/bindings/v8/V8External.cpp @@ -12,7 +12,7 @@ Local 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(vm, val); } From b98fe402be60c1d18919b2b137a5b46b101f6440 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 17:35:22 -0800 Subject: [PATCH 04/11] Make constructors work --- src/bun.js/bindings/napi.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index 23dffaa54ceaf3..e7dc9d14cee25e 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -1852,12 +1852,9 @@ JSC_HOST_CALL_ATTRIBUTES JSC::EncodedJSValue NapiClass_ConstructorFunction(JSC:: frame.newTarget = newTarget; Bun::NapiHandleScope handleScope(jsCast(globalObject)); - JSValue ret = toJS(napi->constructor()(napi->env(), NAPICallFrame::toNapiCallbackInfo(frame))); + napi->constructor()(napi->env(), NAPICallFrame::toNapiCallbackInfo(frame)); RETURN_IF_EXCEPTION(scope, {}); - if (ret.isEmpty()) { - ret = jsUndefined(); - } - RELEASE_AND_RETURN(scope, JSValue::encode(ret)); + RELEASE_AND_RETURN(scope, JSValue::encode(frame.thisValue())); } NapiClass* NapiClass::create(VM& vm, napi_env env, const char* utf8name, From 57a3f839e5341d73f1f79596abaaeacadd7fe3c5 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 17:47:20 -0800 Subject: [PATCH 05/11] Move instance data to napi envs --- src/bun.js/bindings/ZigGlobalObject.cpp | 5 ++--- src/bun.js/bindings/ZigGlobalObject.h | 8 -------- src/bun.js/bindings/napi.cpp | 13 ++----------- src/bun.js/bindings/napi.h | 23 +++++++++++++++++++++++ 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 4638d82c724e54..c40b40afc98b96 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -1204,9 +1204,8 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::Scri GlobalObject::~GlobalObject() { - if (napiInstanceDataFinalizer) { - napi_finalize finalizer = reinterpret_cast(napiInstanceDataFinalizer); - finalizer(napiInstanceDataEnv, napiInstanceData, napiInstanceDataFinalizerHint); + for (const auto& env : m_napiEnvs) { + env->cleanup(); } if (auto* ctx = scriptExecutionContext()) { diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 7d80c81a97738b..a255801ffdd39c 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -449,14 +449,6 @@ class GlobalObject : public Bun::GlobalScope { // 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; - struct napi_env__* napiInstanceDataEnv = 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 m_napiTypeTags; diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index e7dc9d14cee25e..ef731064a06af9 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -2556,13 +2556,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; } - ASSERT(globalObject->napiInstanceDataEnv == env); - *data = globalObject->napiInstanceData; + *data = env->getInstanceData(); return napi_ok; } @@ -2617,14 +2615,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->napiInstanceDataEnv = env; - - globalObject->napiInstanceDataFinalizer = reinterpret_cast(finalize_cb); - globalObject->napiInstanceDataFinalizerHint = finalize_hint; - + env->setInstanceData(data, finalize_cb, finalize_hint); return napi_ok; } diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index 83018b7cacfb78..066b50e7621046 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -33,9 +33,32 @@ struct napi_env__ { 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; + } + 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 { From f99d1bce54fb8c14860042812891ae8e00cfe0bf Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Tue, 26 Nov 2024 17:56:02 -0800 Subject: [PATCH 06/11] Don't undef dlsym on Windows too early --- src/bun.js/bindings/BunProcess.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 832b3d61540f80..05e34dcf064a70 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -404,10 +404,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, napi_register_module_v1 = reinterpret_cast( dlsym(handle, "napi_register_module_v1")); -#if OS(WINDOWS) -#undef dlsym -#endif - if (!napi_register_module_v1) { #if OS(WINDOWS) FreeLibrary(handle); @@ -420,6 +416,10 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, auto node_api_module_get_api_version_v1 = reinterpret_cast(dlsym(handle, "node_api_module_get_api_version_v1")); +#if OS(WINDOWS) +#undef dlsym +#endif + NapiHandleScope handleScope(globalObject); int module_version = 8; From 890fb04a09f12dc941a4b19829f2c81ad64c04f1 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Fri, 6 Dec 2024 18:56:30 -0800 Subject: [PATCH 07/11] Implement feedback --- src/bun.js/bindings/BunProcess.cpp | 6 +- src/bun.js/bindings/ZigGlobalObject.h | 2 +- src/bun.js/bindings/napi.cpp | 120 +++++++++++++------------- src/bun.js/bindings/napi.h | 5 -- src/bun.js/bindings/napi_external.h | 1 + 5 files changed, 65 insertions(+), 69 deletions(-) diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 05e34dcf064a70..aa19aa72d78ba4 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -394,14 +394,14 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, return JSValue::encode(jsUndefined()); } - JSC::EncodedJSValue (*napi_register_module_v1)(napi_env env, JSC::EncodedJSValue exports); + napi_value (*napi_register_module_v1)(napi_env env, napi_value 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( + napi_register_module_v1 = reinterpret_cast( dlsym(handle, "napi_register_module_v1")); if (!napi_register_module_v1) { @@ -442,7 +442,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, auto env = globalObject->makeNapiEnv(nmodule); EncodedJSValue exportsValue = JSC::JSValue::encode(exports); - JSC::JSValue resultValue = JSValue::decode(napi_register_module_v1(env, exportsValue)); + JSC::JSValue resultValue = JSValue::decode(reinterpret_cast(napi_register_module_v1(env, reinterpret_cast(exportsValue)))); RETURN_IF_EXCEPTION(scope, {}); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index a255801ffdd39c..56540e6af2f0fe 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -569,7 +569,7 @@ class GlobalObject : public Bun::GlobalScope { bool hasOverridenModuleResolveFilenameFunction = false; - WTF::Vector> m_napiEnvs; + WTF::Vector, 0> m_napiEnvs; napi_env makeNapiEnv(const napi_module&); napi_env makeNapiEnvForFFI(); diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index ef731064a06af9..c62f6ece8d0248 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -120,7 +120,7 @@ JSC::SourceCode generateSourceCode(WTF::String keyString, JSC::VM& vm, JSC::JSOb static inline Zig::GlobalObject* defaultGlobalObject(napi_env env) { if (env) { - return defaultGlobalObject(toJS(env)); + return defaultGlobalObject(env->globalObject()); } return defaultGlobalObject(); @@ -597,7 +597,7 @@ extern "C" napi_status napi_set_property(napi_env env, napi_value target, return napi_object_expected; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); auto* object = targetValue.toObject(globalObject); @@ -637,7 +637,7 @@ extern "C" napi_status napi_has_property(napi_env env, napi_value object, return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); auto* target = toJS(object).toObject(globalObject); @@ -691,7 +691,7 @@ extern "C" napi_status napi_get_property(napi_env env, napi_value object, return napi_object_expected; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -713,7 +713,7 @@ extern "C" napi_status napi_delete_property(napi_env env, napi_value object, { NAPI_PREMABLE - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -740,7 +740,7 @@ extern "C" napi_status napi_has_own_property(napi_env env, napi_value object, return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -761,7 +761,7 @@ extern "C" napi_status napi_set_named_property(napi_env env, napi_value object, { NAPI_PREMABLE - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); auto target = toJS(object).toObject(globalObject); @@ -793,7 +793,7 @@ extern "C" napi_status napi_create_arraybuffer(napi_env env, { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); if (UNLIKELY(!globalObject || !result)) { return napi_invalid_arg; } @@ -841,7 +841,7 @@ extern "C" napi_status napi_has_named_property(napi_env env, napi_value object, return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -867,7 +867,7 @@ extern "C" napi_status napi_get_named_property(napi_env env, napi_value object, return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_CATCH_SCOPE(vm); @@ -909,7 +909,7 @@ node_api_create_external_string_latin1(napi_env env, finalize_callback(env, nullptr, hint); } }); - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl)); ensureStillAliveHere(out); @@ -947,7 +947,7 @@ node_api_create_external_string_utf16(napi_env env, finalize_callback(env, nullptr, hint); } }); - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSString* out = JSC::jsString(globalObject->vm(), WTF::String(impl)); ensureStillAliveHere(out); @@ -1149,7 +1149,7 @@ extern "C" napi_status napi_create_function(napi_env env, const char* utf8name, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto name = WTF::String(); @@ -1178,7 +1178,7 @@ extern "C" napi_status napi_get_cb_info( NAPI_PREMABLE JSC::CallFrame* callFrame = reinterpret_cast(cbinfo); - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) { NAPICallFrame::extract(*frame, argc, argv, this_arg, data, globalObject); @@ -1261,7 +1261,7 @@ napi_define_properties(napi_env env, napi_value object, size_t property_count, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSC::JSValue objectValue = toJS(object); @@ -1310,7 +1310,7 @@ static JSC::ErrorInstance* createErrorWithCode(JSC::JSGlobalObject* globalObject // used to implement napi_throw_*_error static napi_status throwErrorWithCStrings(napi_env env, const char* code_utf8, const char* msg_utf8, JSC::ErrorType type) { - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -1340,7 +1340,7 @@ static napi_status createErrorWithNapiValues(napi_env env, napi_value code, napi return napi_string_expected; } - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); auto wtf_code = js_code.isEmpty() ? WTF::String() : js_code.getString(globalObject); auto wtf_message = js_message.getString(globalObject); @@ -1375,7 +1375,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value, return napi_object_expected; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); auto* ref = new NapiRef(env, initial_refcount); if (initial_refcount > 0) { @@ -1409,7 +1409,7 @@ extern "C" napi_status napi_add_finalizer(napi_env env, napi_value js_object, if (UNLIKELY(!env)) { return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSC::JSValue objectValue = toJS(js_object); @@ -1455,7 +1455,7 @@ extern "C" napi_status napi_get_reference_value(napi_env env, napi_ref ref, return napi_invalid_arg; } NapiRef* napiRef = toJS(ref); - *result = toNapi(napiRef->value(), toJS(env)); + *result = toNapi(napiRef->value(), env->globalObject()); return napi_ok; } @@ -1523,7 +1523,7 @@ extern "C" napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSC::JSArrayBuffer* jsArrayBuffer = JSC::jsDynamicCast(toJS(arraybuffer)); @@ -1553,9 +1553,9 @@ extern "C" napi_status napi_adjust_external_memory(napi_env env, } if (change_in_bytes > 0) { - toJS(env)->vm().heap.deprecatedReportExtraMemory(change_in_bytes); + env->globalObject()->vm().heap.deprecatedReportExtraMemory(change_in_bytes); } - *adjusted_value = toJS(env)->vm().heap.extraMemorySize(); + *adjusted_value = env->globalObject()->vm().heap.extraMemorySize(); return napi_ok; } @@ -1567,7 +1567,7 @@ extern "C" napi_status napi_is_exception_pending(napi_env env, bool* result) return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); *result = globalObject->vm().exceptionForInspection() != nullptr; return napi_ok; } @@ -1580,7 +1580,7 @@ extern "C" napi_status napi_get_and_clear_last_exception(napi_env env, return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto scope = DECLARE_CATCH_SCOPE(globalObject->vm()); if (scope.exception()) { *result = toNapi(JSC::JSValue(scope.exception()->value()), globalObject); @@ -1595,7 +1595,7 @@ extern "C" napi_status napi_fatal_exception(napi_env env, napi_value err) { NAPI_PREMABLE - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); JSC::JSValue value = toJS(err); JSC::JSObject* obj = value.getObject(); if (UNLIKELY(obj == nullptr || !obj->isErrorInstance())) { @@ -1610,7 +1610,7 @@ extern "C" napi_status napi_fatal_exception(napi_env env, extern "C" napi_status napi_throw(napi_env env, napi_value error) { NAPI_PREMABLE - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -1629,7 +1629,7 @@ extern "C" napi_status node_api_symbol_for(napi_env env, size_t length, napi_value* result) { NAPI_PREMABLE - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); if (UNLIKELY(!result || !utf8description)) { return napi_invalid_arg; @@ -1694,7 +1694,7 @@ extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value) return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -1713,7 +1713,7 @@ extern "C" napi_status napi_object_freeze(napi_env env, napi_value object_value) extern "C" napi_status napi_object_seal(napi_env env, napi_value object_value) { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -1739,7 +1739,7 @@ extern "C" napi_status napi_get_global(napi_env env, napi_value* result) return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); *result = toNapi(globalObject->globalThis(), globalObject); return napi_ok; } @@ -1767,12 +1767,12 @@ extern "C" napi_status napi_get_new_target(napi_env env, CallFrame* callFrame = reinterpret_cast(cbinfo); if (NAPICallFrame* frame = NAPICallFrame::get(callFrame).value_or(nullptr)) { - *result = toNapi(frame->newTarget, toJS(env)); + *result = toNapi(frame->newTarget, env->globalObject()); return napi_ok; } JSC::JSValue newTarget = callFrame->newTarget(); - *result = toNapi(newTarget, toJS(env)); + *result = toNapi(newTarget, env->globalObject()); return napi_ok; } @@ -1787,7 +1787,7 @@ extern "C" napi_status napi_create_dataview(napi_env env, size_t length, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -1866,7 +1866,7 @@ NapiClass* NapiClass::create(VM& vm, napi_env env, const char* utf8name, { WTF::String name = WTF::String::fromUTF8({ utf8name, length }).isolatedCopy(); NativeExecutable* executable = vm.getHostFunction(NapiClass_ConstructorFunction, ImplementationVisibility::Public, NapiClass_ConstructorFunction, name); - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); Structure* structure = globalObject->NapiClassStructure(); NapiClass* napiClass = new (NotNull, allocateCell(vm)) NapiClass(vm, executable, env, structure); napiClass->finishCreation(vm, executable, length, name, constructor, data, property_count, properties); @@ -1929,7 +1929,7 @@ extern "C" napi_status napi_get_all_property_names( jsc_property_mode = PropertyNameMode::Symbols; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); auto objectValue = toJS(objectNapi); auto* object = objectValue.getObject(); @@ -1954,7 +1954,7 @@ napi_get_last_error_info(napi_env env, const napi_extended_error_info** result) return napi_invalid_arg; } - auto globalObject = toJS(env); + auto globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto lastException = vm.lastException(); if (!lastException) { @@ -1998,7 +1998,7 @@ extern "C" napi_status napi_define_class(napi_env env, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); size_t len = length; if (len == NAPI_AUTO_LENGTH) { @@ -2023,7 +2023,7 @@ extern "C" napi_status napi_coerce_to_string(napi_env env, napi_value value, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -2052,7 +2052,7 @@ extern "C" napi_status napi_get_property_names(napi_env env, napi_value object, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSC::JSValue jsValue = toJS(object); @@ -2086,7 +2086,7 @@ extern "C" napi_status napi_create_external_buffer(napi_env env, size_t length, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(data), length }, createSharedTask([finalize_hint, finalize_cb, env](void* p) { #if NAPI_VERBOSE @@ -2113,7 +2113,7 @@ extern "C" napi_status napi_create_external_arraybuffer(napi_env env, void* exte return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto arrayBuffer = ArrayBuffer::createFromBytes({ reinterpret_cast(external_data), byte_length }, createSharedTask([finalize_hint, finalize_cb, env](void* p) { @@ -2140,7 +2140,7 @@ extern "C" napi_status napi_create_double(napi_env env, double value, return napi_invalid_arg; } - *result = toNapi(jsDoubleNumber(value), toJS(env)); + *result = toNapi(jsDoubleNumber(value), env->globalObject()); return napi_ok; } @@ -2149,7 +2149,7 @@ extern "C" napi_status napi_get_value_double(napi_env env, napi_value value, { NAPI_PREMABLE - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); JSC::JSValue jsValue = toJS(value); if (UNLIKELY(result == nullptr || !globalObject)) { @@ -2173,7 +2173,7 @@ extern "C" napi_status napi_get_value_int32(napi_env env, napi_value value, int3 { NAPI_PREMABLE - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); JSC::JSValue jsValue = toJS(value); if (UNLIKELY(result == nullptr || !globalObject)) { @@ -2197,7 +2197,7 @@ extern "C" napi_status napi_get_value_uint32(napi_env env, napi_value value, uin { NAPI_PREMABLE - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); JSC::JSValue jsValue = toJS(value); if (UNLIKELY(result == nullptr || !globalObject)) { @@ -2221,7 +2221,7 @@ extern "C" napi_status napi_get_value_int64(napi_env env, napi_value value, int6 { NAPI_PREMABLE - auto* globalObject = toJS(env); + auto* globalObject = env->globalObject(); JSC::JSValue jsValue = toJS(value); if (UNLIKELY(result == nullptr || !globalObject)) { @@ -2258,7 +2258,7 @@ extern "C" napi_status napi_get_value_string_utf8(napi_env env, { NAPI_PREMABLE - JSGlobalObject* globalObject = toJS(env); + JSGlobalObject* globalObject = env->globalObject(); JSValue jsValue = toJS(napiValue); if (!jsValue || !jsValue.isString()) { @@ -2332,10 +2332,10 @@ extern "C" napi_status napi_get_element(napi_env env, napi_value objectValue, JSObject* object = jsValue.getObject(); auto scope = DECLARE_THROW_SCOPE(object->vm()); - JSValue element = object->getIndex(toJS(env), index); + JSValue element = object->getIndex(env->globalObject(), index); RETURN_IF_EXCEPTION(scope, napi_generic_failure); - *result = toNapi(element, toJS(env)); + *result = toNapi(element, env->globalObject()); return napi_ok; } @@ -2354,7 +2354,7 @@ extern "C" napi_status napi_delete_element(napi_env env, napi_value objectValue, auto scope = DECLARE_THROW_SCOPE(object->vm()); if (LIKELY(result)) { - *result = JSObject::deletePropertyByIndex(object, toJS(env), index); + *result = JSObject::deletePropertyByIndex(object, env->globalObject(), index); } RETURN_IF_EXCEPTION(scope, napi_generic_failure); @@ -2369,7 +2369,7 @@ extern "C" napi_status napi_create_object(napi_env env, napi_value* result) return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSValue value = JSValue(NapiPrototype::create(vm, globalObject->NapiPrototypeStructure())); @@ -2389,7 +2389,7 @@ extern "C" napi_status napi_create_external(napi_env env, void* data, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto* structure = globalObject->NapiExternalStructure(); @@ -2569,7 +2569,7 @@ extern "C" napi_status napi_run_script(napi_env env, napi_value script, { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); if (UNLIKELY(result == nullptr)) { return napi_invalid_arg; } @@ -2631,7 +2631,7 @@ extern "C" napi_status napi_create_bigint_words(napi_env env, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto* bigint = JSC::JSBigInt::tryCreateWithLength(vm, word_count); if (UNLIKELY(!bigint)) { @@ -2659,7 +2659,7 @@ extern "C" napi_status napi_create_symbol(napi_env env, napi_value description, { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); if (UNLIKELY(result == nullptr || globalObject == nullptr)) { @@ -2698,7 +2698,7 @@ extern "C" napi_status napi_new_instance(napi_env env, napi_value constructor, return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -2731,7 +2731,7 @@ extern "C" napi_status napi_call_function(napi_env env, napi_value recv_napi, { NAPI_PREMABLE - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSC::VM& vm = globalObject->vm(); JSC::JSValue funcValue = toJS(func_napi); @@ -2777,7 +2777,7 @@ extern "C" napi_status napi_type_tag_object(napi_env env, napi_value value, cons if (!env || !value || !type_tag) { return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSObject* js_object = toJS(value).getObject(); if (!js_object) { return napi_object_expected; @@ -2801,7 +2801,7 @@ extern "C" napi_status napi_check_object_type_tag(napi_env env, napi_value value if (!env || !value || !type_tag) { return napi_invalid_arg; } - Zig::GlobalObject* globalObject = toJS(env); + Zig::GlobalObject* globalObject = env->globalObject(); JSObject* js_object = toJS(value).getObject(); if (!js_object) { return napi_object_expected; diff --git a/src/bun.js/bindings/napi.h b/src/bun.js/bindings/napi.h index 066b50e7621046..0bb11856deeaa9 100644 --- a/src/bun.js/bindings/napi.h +++ b/src/bun.js/bindings/napi.h @@ -70,11 +70,6 @@ static inline JSValue toJS(napi_value val) return JSC::JSValue::decode(reinterpret_cast(val)); } -static inline Zig::GlobalObject* toJS(napi_env val) -{ - return val->globalObject(); -} - static inline napi_value toNapi(JSC::JSValue val, Zig::GlobalObject* globalObject) { if (val.isCell()) { diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index f6a41674db2d50..9ee3a292a3e667 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -46,6 +46,7 @@ class NapiExternal : public JSC::JSDestructibleObject { JSC::TypeInfo(JSC::ObjectType, StructureFlags), info()); } + // env is needed only if a finalizer is passed, otherwise it may be null 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(vm)) NapiExternal(vm, structure); From f92e2d5d3778ffce6a4c6eb57c6f44c1db289196 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Mon, 9 Dec 2024 14:38:42 -0800 Subject: [PATCH 08/11] Add napi instance data test --- src/bun.js/bindings/napi.cpp | 1 - test/napi/napi-app/binding.gyp | 13 ++++++++- test/napi/napi-app/main.cpp | 46 ++++++++++++++++++++++++++++--- test/napi/napi-app/module.js | 16 +++++++++++ test/napi/napi-app/second_addon.c | 46 +++++++++++++++++++++++++++++++ test/napi/napi.test.ts | 6 ++++ 6 files changed, 122 insertions(+), 6 deletions(-) create mode 100644 test/napi/napi-app/second_addon.c diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index c62f6ece8d0248..016cb2c5622faa 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -2550,7 +2550,6 @@ extern "C" napi_status napi_get_value_external(napi_env env, napi_value value, return napi_ok; } -// TODO: make this per addon instead of globally shared for ALL addons extern "C" napi_status napi_get_instance_data(napi_env env, void** data) { diff --git a/test/napi/napi-app/binding.gyp b/test/napi/napi-app/binding.gyp index aebdebb7efaace..83baa6c2c496e4 100644 --- a/test/napi/napi-app/binding.gyp +++ b/test/napi/napi-app/binding.gyp @@ -18,6 +18,17 @@ "NAPI_DISABLE_CPP_EXCEPTIONS", "NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT=1", ], - } + }, + { + "target_name": "second_addon", + "sources": ["second_addon.c"], + "include_dirs": ["(&v8::Isolate::GetCurrent); + (void)static_cast( + &node::AddEnvironmentCleanupHook); + (void)static_cast( + &node::RemoveEnvironmentCleanupHook); Napi::Object exports = Init2(env, exports1); - node::AddEnvironmentCleanupHook(isolate, [](void *) {}, isolate); - node::RemoveEnvironmentCleanupHook(isolate, [](void *) {}, isolate); - exports.Set("test_issue_7685", Napi::Function::New(env, test_issue_7685)); exports.Set("test_issue_11949", Napi::Function::New(env, test_issue_11949)); exports.Set( @@ -1079,6 +1113,10 @@ Napi::Object InitAll(Napi::Env env, Napi::Object exports1) { exports.Set("add_tag", Napi::Function::New(env, add_tag)); exports.Set("try_add_tag", Napi::Function::New(env, try_add_tag)); exports.Set("check_tag", Napi::Function::New(env, check_tag)); + exports.Set("set_instance_data", Napi::Function::New(env, set_instance_data)); + exports.Set("get_instance_data", Napi::Function::New(env, get_instance_data)); + exports.Set("reset_instance_data", + Napi::Function::New(env, reset_instance_data)); return exports; } diff --git a/test/napi/napi-app/module.js b/test/napi/napi-app/module.js index 6cb1280cf2f494..33342d6f5fe53b 100644 --- a/test/napi/napi-app/module.js +++ b/test/napi/napi-app/module.js @@ -1,4 +1,5 @@ const nativeTests = require("./build/Release/napitests.node"); +const secondAddon = require("./build/Release/second_addon.node"); nativeTests.test_napi_class_constructor_handle_scope = () => { const NapiClass = nativeTests.get_class_with_constructor(); @@ -270,4 +271,19 @@ nativeTests.test_type_tag = () => { console.log("o2 matches o2:", nativeTests.check_tag(o2, 3, 4)); }; +nativeTests.test_instance_data = () => { + const first_data = Math.floor(Math.random() * 1e9) + 1337; + const results = []; + + results.push(nativeTests.get_instance_data() === null); + results.push(nativeTests.set_instance_data(first_data) === undefined); + results.push(nativeTests.get_instance_data() === first_data); + results.push(secondAddon.get_instance_data() === 42); + results.push(secondAddon.get_instance_data() !== nativeTests.get_instance_data()); + + console.log(results.join(" ")); + + nativeTests.reset_instance_data(); +}; + module.exports = nativeTests; diff --git a/test/napi/napi-app/second_addon.c b/test/napi/napi-app/second_addon.c new file mode 100644 index 00000000000000..ca300eb166f68b --- /dev/null +++ b/test/napi/napi-app/second_addon.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +#define NODE_API_CALL(env, call) \ + do { \ + napi_status status = (call); \ + if (status != napi_ok) { \ + const napi_extended_error_info *error_info = NULL; \ + napi_get_last_error_info((env), &error_info); \ + const char *err_message = error_info->error_message; \ + bool is_pending; \ + napi_is_exception_pending((env), &is_pending); \ + /* If an exception is already pending, don't rethrow it */ \ + if (!is_pending) { \ + const char *message = \ + (err_message == NULL) ? "empty error message" : err_message; \ + napi_throw_error((env), NULL, message); \ + } \ + return NULL; \ + } \ + } while (0) + +static napi_value get_instance_data(napi_env env, napi_callback_info info) { + void *data_ptr = NULL; + NODE_API_CALL(env, napi_get_instance_data(env, &data_ptr)); + + napi_value out; + NODE_API_CALL(env, napi_create_int32(env, *(int32_t *)data_ptr, &out)); + return out; +} + +/* napi_value */ NAPI_MODULE_INIT(/* napi_env env, napi_value exports */) { + napi_value get_instance_data_function; + NODE_API_CALL(env, napi_create_function(env, "get_instance_data", + NAPI_AUTO_LENGTH, get_instance_data, + NULL, &get_instance_data_function)); + NODE_API_CALL(env, napi_set_named_property(env, exports, "get_instance_data", + get_instance_data_function)); + + static int32_t instance_data = 42; + NODE_API_CALL(env, napi_set_instance_data(env, &instance_data, NULL, NULL)); + + return exports; +} diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 25144c80f7da01..ef1e33fea941dd 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -319,6 +319,12 @@ describe("napi", () => { checkSameOutput("test_type_tag", []); }); }); + + describe("instance data", () => { + it("works with multiple envs", () => { + checkSameOutput("test_instance_data", []); + }); + }); }); function checkSameOutput(test: string, args: any[] | string) { From 4b97c78dff69cf6c0ba3a7817190e2fd8a1b6297 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Mon, 9 Dec 2024 15:00:51 -0800 Subject: [PATCH 09/11] Undo changes to isolate usage --- test/napi/napi-app/main.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/napi/napi-app/main.cpp b/test/napi/napi-app/main.cpp index 0eaa48f7a4fe05..0b9d587a0c9146 100644 --- a/test/napi/napi-app/main.cpp +++ b/test/napi/napi-app/main.cpp @@ -1059,14 +1059,13 @@ Napi::Object Init2(Napi::Env env, Napi::Object exports) { Napi::Object InitAll(Napi::Env env, Napi::Object exports1) { // check that these symbols are defined - (void)static_cast(&v8::Isolate::GetCurrent); - (void)static_cast( - &node::AddEnvironmentCleanupHook); - (void)static_cast( - &node::RemoveEnvironmentCleanupHook); + auto *isolate = v8::Isolate::GetCurrent(); Napi::Object exports = Init2(env, exports1); + node::AddEnvironmentCleanupHook(isolate, [](void *) {}, isolate); + node::RemoveEnvironmentCleanupHook(isolate, [](void *) {}, isolate); + exports.Set("test_issue_7685", Napi::Function::New(env, test_issue_7685)); exports.Set("test_issue_11949", Napi::Function::New(env, test_issue_11949)); exports.Set( From 12de2d58c25485f85b0570ca4841a73b37873fd1 Mon Sep 17 00:00:00 2001 From: Kai Tamkun Date: Mon, 9 Dec 2024 15:09:12 -0800 Subject: [PATCH 10/11] Don't use a random number --- test/napi/napi-app/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/napi/napi-app/module.js b/test/napi/napi-app/module.js index 33342d6f5fe53b..f2862bb263179d 100644 --- a/test/napi/napi-app/module.js +++ b/test/napi/napi-app/module.js @@ -272,7 +272,7 @@ nativeTests.test_type_tag = () => { }; nativeTests.test_instance_data = () => { - const first_data = Math.floor(Math.random() * 1e9) + 1337; + const first_data = 1337; const results = []; results.push(nativeTests.get_instance_data() === null); From f49690a3847799af96d4325faaaeae78bd2bb159 Mon Sep 17 00:00:00 2001 From: Ben Grant Date: Mon, 9 Dec 2024 16:17:01 -0800 Subject: [PATCH 11/11] Bikeshed NapiExternal::create parameter order --- src/bun.js/bindings/BunProcess.cpp | 2 +- src/bun.js/bindings/napi.cpp | 4 ++-- src/bun.js/bindings/napi_external.cpp | 2 +- src/bun.js/bindings/napi_external.h | 20 ++++++++++---------- src/bun.js/bindings/v8/V8External.cpp | 3 ++- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/bun.js/bindings/BunProcess.cpp b/src/bun.js/bindings/BunProcess.cpp index 8f79d5f9c75e4d..1d6f44bfb1a195 100644 --- a/src/bun.js/bindings/BunProcess.cpp +++ b/src/bun.js/bindings/BunProcess.cpp @@ -450,7 +450,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, // 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, nullptr); + Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), env, meta, nullptr, nullptr); bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); ASSERT(success); diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index e944ba7c87460e..c10fbf93d843a2 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -1010,7 +1010,7 @@ extern "C" void napi_module_register(napi_module* mod) 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, nullptr); + Bun::NapiExternal* napi_external = Bun::NapiExternal::create(vm, globalObject->NapiExternalStructure(), env, meta, nullptr, nullptr); bool success = resultValue.getObject()->putDirect(vm, WebCore::builtinNames(vm).napiDlopenHandlePrivateName(), napi_external, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); ASSERT(success); @@ -2404,7 +2404,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, env, reinterpret_cast(finalize_cb)); + JSValue value = Bun::NapiExternal::create(vm, structure, env, data, finalize_hint, finalize_cb); JSC::EnsureStillAliveScope ensureStillAlive(value); *result = toNapi(value, globalObject); return napi_ok; diff --git a/src/bun.js/bindings/napi_external.cpp b/src/bun.js/bindings/napi_external.cpp index 874d088ab639b6..efa05b6e706164 100644 --- a/src/bun.js/bindings/napi_external.cpp +++ b/src/bun.js/bindings/napi_external.cpp @@ -8,7 +8,7 @@ NapiExternal::~NapiExternal() 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(m_finalizer)(m_env, m_value, m_finalizerHint); + m_finalizer(m_env, m_value, m_finalizerHint); } } diff --git a/src/bun.js/bindings/napi_external.h b/src/bun.js/bindings/napi_external.h index 8d4ad6add11963..d65a68420cc89a 100644 --- a/src/bun.js/bindings/napi_external.h +++ b/src/bun.js/bindings/napi_external.h @@ -52,35 +52,35 @@ class NapiExternal : public JSC::JSDestructibleObject { } // env is needed only if a finalizer is passed, otherwise it may be null - static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, void* value, void* finalizer_hint, napi_env env, void* finalizer) + static NapiExternal* create(JSC::VM& vm, JSC::Structure* structure, napi_env env, void* value, void* finalizer_hint, napi_finalize finalizer) { - NapiExternal* accessor = new (NotNull, JSC::allocateCell(vm)) NapiExternal(vm, structure); + NapiExternal* external = new (NotNull, JSC::allocateCell(vm)) NapiExternal(vm, structure); - accessor->finishCreation(vm, value, finalizer_hint, env, finalizer); + external->finishCreation(vm, env, value, finalizer_hint, finalizer); #if BUN_DEBUG if (auto* callFrame = vm.topCallFrame) { auto origin = callFrame->callerSourceOrigin(vm); - accessor->sourceOriginURL = origin.string(); + external->sourceOriginURL = origin.string(); std::unique_ptr> stackTrace = makeUnique>(); - vm.interpreter.getStackTrace(accessor, *stackTrace, 0, 20); + vm.interpreter.getStackTrace(external, *stackTrace, 0, 20); if (!stackTrace->isEmpty()) { for (auto& frame : *stackTrace) { if (frame.hasLineAndColumnInfo()) { LineColumn lineColumn = frame.computeLineAndColumn(); - accessor->sourceOriginLine = lineColumn.line; - accessor->sourceOriginColumn = lineColumn.column; + external->sourceOriginLine = lineColumn.line; + external->sourceOriginColumn = lineColumn.column; break; } } } } #endif - return accessor; + return external; } - void finishCreation(JSC::VM& vm, void* value, void* finalizer_hint, napi_env env, void* finalizer) + void finishCreation(JSC::VM& vm, napi_env env, void* value, void* finalizer_hint, napi_finalize finalizer) { Base::finishCreation(vm); m_value = value; @@ -95,7 +95,7 @@ class NapiExternal : public JSC::JSDestructibleObject { void* m_value = nullptr; void* m_finalizerHint = nullptr; - void* m_finalizer = nullptr; + napi_finalize m_finalizer = nullptr; napi_env m_env = nullptr; #if BUN_DEBUG diff --git a/src/bun.js/bindings/v8/V8External.cpp b/src/bun.js/bindings/v8/V8External.cpp index 1a003d8509a974..1063e8b56413a7 100644 --- a/src/bun.js/bindings/v8/V8External.cpp +++ b/src/bun.js/bindings/v8/V8External.cpp @@ -12,7 +12,8 @@ Local 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, nullptr); + // nullptr napi_env -- this is OK when there is no finalizer + Bun::NapiExternal* val = Bun::NapiExternal::create(vm, structure, nullptr, value, nullptr, nullptr); return isolate->currentHandleScope()->createLocal(vm, val); }