aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bun.js/api/FFI.h31
-rw-r--r--src/bun.js/api/bun.zig3
-rw-r--r--src/bun.js/api/ffi.zig211
-rw-r--r--src/bun.js/bindings/JSFFIFunction.cpp239
-rw-r--r--src/bun.js/ffi.exports.js62
5 files changed, 460 insertions, 86 deletions
diff --git a/src/bun.js/api/FFI.h b/src/bun.js/api/FFI.h
index 1233e3e2e..4763c90a3 100644
--- a/src/bun.js/api/FFI.h
+++ b/src/bun.js/api/FFI.h
@@ -101,9 +101,16 @@ typedef void* JSContext;
#ifdef IS_CALLBACK
-extern int64_t bun_call(JSContext, void* func, void* thisValue, size_t len, const EncodedJSValue args[], void* exception);
-JSContext cachedJSContext;
-void* cachedCallbackFunction;
+ZIG_REPR_TYPE FFI_Callback_call(void* ctx, size_t argCount, ZIG_REPR_TYPE* args);
+// We wrap
+static EncodedJSValue _FFI_Callback_call(void* ctx, size_t argCount, ZIG_REPR_TYPE* args) __attribute__((__always_inline__));
+static EncodedJSValue _FFI_Callback_call(void* ctx, size_t argCount, ZIG_REPR_TYPE* args) {
+ EncodedJSValue return_value;
+ return_value.asZigRepr = FFI_Callback_call(ctx, argCount, args);
+ return return_value;
+}
+static ZIG_REPR_TYPE arguments[100];
+
#endif
static bool JSVALUE_IS_CELL(EncodedJSValue val) __attribute__((__always_inline__));
@@ -115,10 +122,10 @@ static int64_t JSVALUE_TO_INT64(EncodedJSValue value) __attribute__((__always_i
uint64_t JSVALUE_TO_UINT64_SLOW(EncodedJSValue value);
int64_t JSVALUE_TO_INT64_SLOW(EncodedJSValue value);
-EncodedJSValue UINT64_TO_JSVALUE_SLOW(void* globalObject, uint64_t val);
-EncodedJSValue INT64_TO_JSVALUE_SLOW(void* globalObject, int64_t val);
-static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) __attribute__((__always_inline__));
-static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) __attribute__((__always_inline__));
+EncodedJSValue UINT64_TO_JSVALUE_SLOW(void* jsGlobalObject, uint64_t val);
+EncodedJSValue INT64_TO_JSVALUE_SLOW(void* jsGlobalObject, int64_t val);
+static EncodedJSValue UINT64_TO_JSVALUE(void* jsGlobalObject, uint64_t val) __attribute__((__always_inline__));
+static EncodedJSValue INT64_TO_JSVALUE(void* jsGlobalObject, int64_t val) __attribute__((__always_inline__));
static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__));
@@ -240,7 +247,7 @@ static int64_t JSVALUE_TO_INT64(EncodedJSValue value) {
return JSVALUE_TO_INT64_SLOW(value);
}
-static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) {
+static EncodedJSValue UINT64_TO_JSVALUE(void* jsGlobalObject, uint64_t val) {
if (val < MAX_INT32) {
return INT32_TO_JSVALUE((int32_t)val);
}
@@ -249,10 +256,10 @@ static EncodedJSValue UINT64_TO_JSVALUE(void* globalObject, uint64_t val) {
return DOUBLE_TO_JSVALUE((double)val);
}
- return UINT64_TO_JSVALUE_SLOW(globalObject, val);
+ return UINT64_TO_JSVALUE_SLOW(jsGlobalObject, val);
}
-static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) {
+static EncodedJSValue INT64_TO_JSVALUE(void* jsGlobalObject, int64_t val) {
if (val >= -MAX_INT32 && val <= MAX_INT32) {
return INT32_TO_JSVALUE((int32_t)val);
}
@@ -261,11 +268,11 @@ static EncodedJSValue INT64_TO_JSVALUE(void* globalObject, int64_t val) {
return DOUBLE_TO_JSVALUE((double)val);
}
- return INT64_TO_JSVALUE_SLOW(globalObject, val);
+ return INT64_TO_JSVALUE_SLOW(jsGlobalObject, val);
}
#ifndef IS_CALLBACK
-ZIG_REPR_TYPE JSFunctionCall(void* globalObject, void* callFrame);
+ZIG_REPR_TYPE JSFunctionCall(void* jsGlobalObject, void* callFrame);
#endif
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index 5189da1a7..324a6f9db 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -2603,6 +2603,9 @@ pub const FFI = struct {
.toArrayBuffer = .{
.rfn = JSC.wrapWithHasContainer(@This(), "toArrayBuffer", false, false, true),
},
+ .closeCallback = .{
+ .rfn = JSC.wrapWithHasContainer(JSC.FFI, "closeCallback", false, false, false),
+ },
},
.{
.read = .{
diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig
index b9053de47..e9ee4d1ab 100644
--- a/src/bun.js/api/ffi.zig
+++ b/src/bun.js/api/ffi.zig
@@ -91,6 +91,12 @@ pub const FFI = struct {
.{},
);
+ pub fn closeCallback(globalThis: *JSGlobalObject, ctx: JSValue) JSValue {
+ var function = ctx.asPtr(Function);
+ function.deinit(globalThis, bun.default_allocator);
+ return JSValue.jsUndefined();
+ }
+
pub fn callback(globalThis: *JSGlobalObject, interface: JSC.JSValue, js_callback: JSC.JSValue) JSValue {
JSC.markBinding(@src());
if (!interface.isObject()) {
@@ -110,13 +116,12 @@ pub const FFI = struct {
}
// TODO: WeakRefHandle that automatically frees it?
- JSC.C.JSValueProtect(globalThis, js_callback.asObjectRef());
func.base_name = "";
+ js_callback.ensureStillAlive();
- func.compileCallback(allocator, globalThis, js_callback.asObjectRef().?) catch return ZigString.init("Out of memory").toErrorInstance(globalThis);
+ func.compileCallback(allocator, globalThis, js_callback) catch return ZigString.init("Out of memory").toErrorInstance(globalThis);
switch (func.step) {
.failed => |err| {
- JSC.C.JSValueUnprotect(globalThis, js_callback.asObjectRef());
const message = ZigString.init(err.msg).toErrorInstance(globalThis);
func.deinit(globalThis, allocator);
@@ -124,14 +129,19 @@ pub const FFI = struct {
return message;
},
.pending => {
- JSC.C.JSValueUnprotect(globalThis, js_callback.asObjectRef());
func.deinit(globalThis, allocator);
return ZigString.init("Failed to compile, but not sure why. Please report this bug").toErrorInstance(globalThis);
},
.compiled => {
var function_ = bun.default_allocator.create(Function) catch unreachable;
function_.* = func.*;
- return JSC.JSValue.fromPtrAddress(@ptrToInt(function_.step.compiled.ptr));
+ return JSValue.createObject2(
+ globalThis,
+ ZigString.static("ptr"),
+ ZigString.static("ctx"),
+ JSC.JSValue.fromPtrAddress(@ptrToInt(function_.step.compiled.ptr)),
+ JSC.JSValue.fromPtrAddress(@ptrToInt(function_)),
+ );
},
}
}
@@ -179,7 +189,7 @@ pub const FFI = struct {
function.base_name = "my_callback_function";
- function.printCallbackSourceCode(&writer) catch {
+ function.printCallbackSourceCode(null, null, &writer) catch {
return ZigString.init("Error while printing code").toErrorInstance(global);
};
return ZigString.init(arraylist.items).toValueGC(global);
@@ -628,6 +638,8 @@ pub const FFI = struct {
pub var lib_dirZ: [*:0]const u8 = "";
+ extern "C" fn FFICallbackFunctionWrapper_destroy(*anyopaque) void;
+
pub fn deinit(val: *Function, globalThis: *JSC.JSGlobalObject, allocator: std.mem.Allocator) void {
if (val.base_name) |base_name| {
if (std.mem.span(base_name).len > 0) {
@@ -649,6 +661,11 @@ pub const FFI = struct {
// _ = JSC.untrackFunction(globalThis, val.step.compiled.js_function);
val.step.compiled.js_function = .zero;
}
+
+ if (val.step.compiled.ffi_callback_function_wrapper) |wrapper| {
+ FFICallbackFunctionWrapper_destroy(wrapper);
+ val.step.compiled.ffi_callback_function_wrapper = null;
+ }
}
if (val.step == .failed and val.step.failed.allocated) {
@@ -660,10 +677,10 @@ pub const FFI = struct {
pending: void,
compiled: struct {
ptr: *anyopaque,
- fast_path_ptr: ?*anyopaque = null,
buf: []u8,
js_function: JSValue = JSValue.zero,
js_context: ?*anyopaque = null,
+ ffi_callback_function_wrapper: ?*anyopaque = null,
},
failed: struct {
msg: []const u8,
@@ -796,7 +813,8 @@ pub const FFI = struct {
}
if (relocation_size < 0) {
- this.step = .{ .failed = .{ .msg = "tcc_relocate returned a negative value" } };
+ if (this.step != .failed)
+ this.step = .{ .failed = .{ .msg = "tcc_relocate returned a negative value" } };
return;
}
@@ -891,14 +909,13 @@ pub const FFI = struct {
pub fn compileCallback(
this: *Function,
allocator: std.mem.Allocator,
- js_context: *anyopaque,
- js_function: *anyopaque,
+ js_context: *JSC.JSGlobalObject,
+ js_function: JSValue,
) !void {
- Output.debug("welcome", .{});
var source_code = std.ArrayList(u8).init(allocator);
var source_code_writer = source_code.writer();
- try this.printCallbackSourceCode(&source_code_writer);
- Output.debug("helllooo", .{});
+ var ffi_wrapper = Bun__createFFICallbackFunction(js_context, js_function);
+ try this.printCallbackSourceCode(js_context, ffi_wrapper, &source_code_writer);
try source_code.append(0);
// defer source_code.deinit();
var state = TCC.tcc_new() orelse return error.TCCMissing;
@@ -920,7 +937,6 @@ pub const FFI = struct {
state,
source_code.items.ptr,
);
- Output.debug("compile", .{});
// did tcc report an error?
if (this.step == .failed) {
return;
@@ -934,13 +950,30 @@ pub const FFI = struct {
}
CompilerRT.inject(state);
- Output.debug("here", .{});
- _ = TCC.tcc_add_symbol(state, "bun_call", workaround.bun_call.*);
- _ = TCC.tcc_add_symbol(state, "cachedJSContext", js_context);
- _ = TCC.tcc_add_symbol(state, "cachedCallbackFunction", js_function);
-
+ _ = TCC.tcc_add_symbol(
+ state,
+ "FFI_Callback_call",
+ // TODO: stage2 - make these ptrs
+ switch (this.arg_types.items.len) {
+ 0 => FFI_Callback_call_0,
+ 1 => FFI_Callback_call_1,
+ 2 => FFI_Callback_call_2,
+ 3 => FFI_Callback_call_3,
+ 4 => FFI_Callback_call_4,
+ 5 => FFI_Callback_call_5,
+ 6 => FFI_Callback_call_6,
+ 7 => FFI_Callback_call_7,
+ else => FFI_Callback_call,
+ },
+ );
var relocation_size = TCC.tcc_relocate(state, null);
- if (relocation_size == 0) return;
+
+ if (relocation_size < 0) {
+ if (this.step != .failed)
+ this.step = .{ .failed = .{ .msg = "tcc_relocate returned a negative value" } };
+ return;
+ }
+
var bytes: []u8 = try allocator.rawAlloc(@intCast(usize, relocation_size), 16, 16, 0);
defer {
if (this.step == .failed) {
@@ -966,8 +999,9 @@ pub const FFI = struct {
.compiled = .{
.ptr = symbol,
.buf = bytes,
- .js_function = JSC.JSValue.fromPtr(js_function),
+ .js_function = js_function,
.js_context = js_context,
+ .ffi_callback_function_wrapper = ffi_wrapper,
},
};
}
@@ -1021,7 +1055,7 @@ pub const FFI = struct {
\\
\\
\\/* ---- Your Wrapper Function ---- */
- \\ZIG_REPR_TYPE JSFunctionCall(void* globalObject, void* callFrame) {
+ \\ZIG_REPR_TYPE JSFunctionCall(void* JS_GLOBAL_OBJECT, void* callFrame) {
\\
);
@@ -1125,10 +1159,24 @@ pub const FFI = struct {
try writer.writeAll(";\n}\n\n");
}
+ extern fn FFI_Callback_call(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_0(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_1(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_2(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_3(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_4(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_5(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_6(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn FFI_Callback_call_7(*anyopaque, usize, [*]JSValue) JSValue;
+ extern fn Bun__createFFICallbackFunction(*JSC.JSGlobalObject, JSValue) *anyopaque;
+
pub fn printCallbackSourceCode(
this: *Function,
+ globalObject: ?*JSC.JSGlobalObject,
+ context_ptr: ?*anyopaque,
writer: anytype,
) !void {
+ try writer.print("#define JS_GLOBAL_OBJECT (void*)0x{X}UL\n", .{@ptrToInt(globalObject)});
try writer.writeAll("#define IS_CALLBACK 1\n");
brk: {
@@ -1193,44 +1241,40 @@ pub const FFI = struct {
first = true;
if (this.arg_types.items.len > 0) {
- try writer.print(" EncodedJSValue arguments[{d}] = {{\n", .{this.arg_types.items.len});
-
var arg_buf: [512]u8 = undefined;
arg_buf[0.."arg".len].* = "arg".*;
for (this.arg_types.items) |arg, i| {
const printed = std.fmt.bufPrintIntToSlice(arg_buf["arg".len..], i, 10, .lower, .{});
const arg_name = arg_buf[0 .. "arg".len + printed.len];
- try writer.print(" {}", .{arg.toJS(arg_name)});
- if (i < this.arg_types.items.len - 1) {
- try writer.writeAll(",\n");
- }
+ try writer.print("arguments[{d}] = {}.asZigRepr;\n", .{ i, arg.toJS(arg_name) });
}
- try writer.writeAll("\n };\n");
- } else {
- try writer.writeAll(" EncodedJSValue arguments[1] = {{0}};\n");
}
try writer.writeAll(" ");
- if (!(this.return_type == .void)) {
- try writer.writeAll("EncodedJSValue return_value = {");
- }
- // JSC.C.JSObjectCallAsFunction(
- // ctx,
- // object,
- // thisObject,
- // argumentCount,
- // arguments,
- // exception,
- // );
- try writer.writeAll("bun_call(cachedJSContext, cachedCallbackFunction, (void*)0, ");
+ var inner_buf_: [372]u8 = undefined;
+ var inner_buf: []u8 = &.{};
if (this.arg_types.items.len > 0) {
- try writer.print("{d}, &arguments[0], (void*)0)", .{this.arg_types.items.len});
+ inner_buf = try std.fmt.bufPrint(
+ inner_buf_[1..],
+ "FFI_Callback_call((void*)0x{X}UL, {d}, arguments)",
+ .{ @ptrToInt(context_ptr), this.arg_types.items.len },
+ );
} else {
- try writer.writeAll("0, &arguments[0], (void*)0)");
+ inner_buf = try std.fmt.bufPrint(
+ inner_buf_[1..],
+ "FFI_Callback_call((void*)0x{X}UL, 0, (ZIG_REPR_TYPE*)0)",
+ .{
+ @ptrToInt(context_ptr),
+ },
+ );
}
-
- if (this.return_type != .void) {
- try writer.print("}};\n return {}", .{this.return_type.toC("return_value")});
+ if (this.return_type == .void) {
+ try writer.writeAll(inner_buf);
+ } else {
+ const len = inner_buf.len + 1;
+ inner_buf = inner_buf_[0..len];
+ inner_buf[0] = '_';
+ try writer.print("return {s}", .{this.return_type.toCExact(inner_buf)});
}
try writer.writeAll(";\n}\n\n");
@@ -1267,6 +1311,8 @@ pub const FFI = struct {
i64_fast = 15,
u64_fast = 16,
+ function = 17,
+
/// Types that we can directly pass through as an `int64_t`
pub fn needsACastInC(this: ABIType) bool {
return switch (this) {
@@ -1311,6 +1357,9 @@ pub const FFI = struct {
.{ "cstring", ABIType.@"cstring" },
.{ "i64_fast", ABIType.i64_fast },
.{ "u64_fast", ABIType.u64_fast },
+ .{ "function", ABIType.function },
+ .{ "callback", ABIType.callback },
+ .{ "fn", ABIType.function },
};
pub const label = ComptimeStringMap(ABIType, map);
const EnumMapFormatter = struct {
@@ -1362,32 +1411,58 @@ pub const FFI = struct {
const ToCFormatter = struct {
symbol: string,
tag: ABIType,
+ exact: bool = false,
pub fn format(self: ToCFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
switch (self.tag) {
- .void => {},
+ .void => {
+ return;
+ },
.bool => {
- try writer.print("JSVALUE_TO_BOOL({s})", .{self.symbol});
+ if (self.exact)
+ try writer.writeAll("(bool)");
+ try writer.writeAll("JSVALUE_TO_BOOL(");
},
.char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
- try writer.print("JSVALUE_TO_INT32({s})", .{self.symbol});
+ if (self.exact)
+ try writer.print("({s})", .{std.mem.span(@tagName(self.tag))});
+
+ try writer.writeAll("JSVALUE_TO_INT32(");
},
.i64_fast, .int64_t => {
- try writer.print("JSVALUE_TO_INT64({s})", .{self.symbol});
+ if (self.exact)
+ try writer.writeAll("(int64_t)");
+ try writer.writeAll("JSVALUE_TO_INT64(");
},
.u64_fast, .uint64_t => {
- try writer.print("JSVALUE_TO_UINT64({s})", .{self.symbol});
+ if (self.exact)
+ try writer.writeAll("(uint64_t)");
+ try writer.writeAll("JSVALUE_TO_UINT64(");
},
- .cstring, .ptr => {
- try writer.print("JSVALUE_TO_PTR({s})", .{self.symbol});
+ .function, .cstring, .ptr => {
+ if (self.exact)
+ try writer.writeAll("(void*)");
+ try writer.writeAll("JSVALUE_TO_PTR(");
},
.double => {
- try writer.print("JSVALUE_TO_DOUBLE({s})", .{self.symbol});
+ if (self.exact)
+ try writer.writeAll("(double)");
+ try writer.writeAll("JSVALUE_TO_DOUBLE(");
},
.float => {
- try writer.print("JSVALUE_TO_FLOAT({s})", .{self.symbol});
+ if (self.exact)
+ try writer.writeAll("(float)");
+ try writer.writeAll("JSVALUE_TO_FLOAT(");
},
}
+ // if (self.fromi64) {
+ // try writer.writeAll("EncodedJSValue{ ");
+ // }
+ try writer.writeAll(self.symbol);
+ // if (self.fromi64) {
+ // try writer.writeAll(", }");
+ // }
+ try writer.writeAll(")");
}
};
@@ -1401,22 +1476,22 @@ pub const FFI = struct {
.bool => {
try writer.print("BOOLEAN_TO_JSVALUE({s})", .{self.symbol});
},
- .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
- try writer.print("INT32_TO_JSVALUE({s})", .{self.symbol});
+ .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t => {
+ try writer.print("INT32_TO_JSVALUE((int32_t){s})", .{self.symbol});
},
- .i64_fast => {
- try writer.print("INT64_TO_JSVALUE(globalObject, {s})", .{self.symbol});
+ .uint32_t, .i64_fast => {
+ try writer.print("INT64_TO_JSVALUE(JS_GLOBAL_OBJECT, (int64_t){s})", .{self.symbol});
},
.int64_t => {
- try writer.print("INT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol});
+ try writer.print("INT64_TO_JSVALUE_SLOW(JS_GLOBAL_OBJECT, {s})", .{self.symbol});
},
.u64_fast => {
- try writer.print("UINT64_TO_JSVALUE(globalObject, {s})", .{self.symbol});
+ try writer.print("UINT64_TO_JSVALUE(JS_GLOBAL_OBJECT, {s})", .{self.symbol});
},
.uint64_t => {
- try writer.print("UINT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol});
+ try writer.print("UINT64_TO_JSVALUE_SLOW(JS_GLOBAL_OBJECT, {s})", .{self.symbol});
},
- .cstring, .ptr => {
+ .function, .cstring, .ptr => {
try writer.print("PTR_TO_JSVALUE({s})", .{self.symbol});
},
.double => {
@@ -1433,6 +1508,10 @@ pub const FFI = struct {
return ToCFormatter{ .tag = this, .symbol = symbol };
}
+ pub fn toCExact(this: ABIType, symbol: string) ToCFormatter {
+ return ToCFormatter{ .tag = this, .symbol = symbol, .exact = true };
+ }
+
pub fn toJS(
this: ABIType,
symbol: string,
@@ -1449,7 +1528,7 @@ pub const FFI = struct {
pub fn typenameLabel(this: ABIType) []const u8 {
return switch (this) {
- .cstring, .ptr => "void*",
+ .function, .cstring, .ptr => "void*",
.bool => "bool",
.int8_t => "int8_t",
.uint8_t => "uint8_t",
diff --git a/src/bun.js/bindings/JSFFIFunction.cpp b/src/bun.js/bindings/JSFFIFunction.cpp
index 07fefac1f..66fdc701d 100644
--- a/src/bun.js/bindings/JSFFIFunction.cpp
+++ b/src/bun.js/bindings/JSFFIFunction.cpp
@@ -36,6 +36,40 @@
#include "DOMJITIDLTypeFilter.h"
#include "DOMJITHelpers.h"
+class FFICallbackFunctionWrapper {
+
+ WTF_MAKE_FAST_ALLOCATED;
+
+public:
+ JSC::Strong<JSC::JSFunction> m_function;
+ JSC::Strong<Zig::GlobalObject> globalObject;
+ ~FFICallbackFunctionWrapper() = default;
+
+ FFICallbackFunctionWrapper(JSC::JSFunction* function, Zig::GlobalObject* globalObject)
+ : m_function(globalObject->vm(), function)
+ , globalObject(globalObject->vm(), globalObject)
+ {
+ }
+};
+extern "C" void FFICallbackFunctionWrapper_destroy(FFICallbackFunctionWrapper* wrapper)
+{
+ delete wrapper;
+}
+
+extern "C" FFICallbackFunctionWrapper* Bun__createFFICallbackFunction(
+ Zig::GlobalObject* globalObject,
+ JSC::EncodedJSValue callbackFn)
+{
+ auto* vm = &globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(*vm);
+
+ auto* callbackFunction = jsCast<JSC::JSFunction*>(JSC::JSValue::decode(callbackFn));
+
+ auto* wrapper = new FFICallbackFunctionWrapper(callbackFunction, globalObject);
+
+ return wrapper;
+}
+
extern "C" Zig::JSFFIFunction* Bun__CreateFFIFunction(Zig::GlobalObject* globalObject, const ZigString* symbolName, unsigned argCount, Zig::FFIFunction functionPointer, bool strong)
{
JSC::VM& vm = globalObject->vm();
@@ -96,3 +130,208 @@ JSFFIFunction* JSFFIFunction::create(VM& vm, Zig::GlobalObject* globalObject, un
}
} // namespace JSC
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+ JSC::MarkedArgumentBuffer arguments;
+ for (size_t i = 0; i < argCount; ++i)
+ arguments.appendWithCrashOnOverflow(JSC::JSValue::decode(args[i]));
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call_0(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call_1(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call_2(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue FFI_Callback_call_3(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+ arguments.append(JSC::JSValue::decode(args[2]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue FFI_Callback_call_4(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+ arguments.append(JSC::JSValue::decode(args[2]));
+ arguments.append(JSC::JSValue::decode(args[3]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue FFI_Callback_call_5(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+ arguments.append(JSC::JSValue::decode(args[2]));
+ arguments.append(JSC::JSValue::decode(args[3]));
+ arguments.append(JSC::JSValue::decode(args[4]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call_6(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+ arguments.append(JSC::JSValue::decode(args[2]));
+ arguments.append(JSC::JSValue::decode(args[3]));
+ arguments.append(JSC::JSValue::decode(args[4]));
+ arguments.append(JSC::JSValue::decode(args[5]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
+
+extern "C" JSC::EncodedJSValue
+FFI_Callback_call_7(FFICallbackFunctionWrapper& wrapper, size_t argCount, JSC::EncodedJSValue* args)
+{
+ auto* function = wrapper.m_function.get();
+ auto* globalObject = wrapper.globalObject.get();
+ auto& vm = globalObject->vm();
+
+ JSC::MarkedArgumentBuffer arguments;
+ arguments.append(JSC::JSValue::decode(args[0]));
+ arguments.append(JSC::JSValue::decode(args[1]));
+ arguments.append(JSC::JSValue::decode(args[2]));
+ arguments.append(JSC::JSValue::decode(args[3]));
+ arguments.append(JSC::JSValue::decode(args[4]));
+ arguments.append(JSC::JSValue::decode(args[5]));
+ arguments.append(JSC::JSValue::decode(args[6]));
+
+ WTF::NakedPtr<JSC::Exception> exception;
+ auto result = JSC::call(globalObject, function, JSC::getCallData(function), JSC::jsUndefined(), arguments, exception);
+ if (UNLIKELY(exception)) {
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ scope.throwException(globalObject, exception);
+ return JSC::JSValue::encode(JSC::jsNull());
+ }
+
+ return JSC::JSValue::encode(result);
+}
diff --git a/src/bun.js/ffi.exports.js b/src/bun.js/ffi.exports.js
index 737b7204b..faf74d1b8 100644
--- a/src/bun.js/ffi.exports.js
+++ b/src/bun.js/ffi.exports.js
@@ -9,6 +9,36 @@ export const viewSource = ffi.viewSource;
const BunCString = ffi.CString;
const nativeLinkSymbols = ffi.linkSymbols;
+const nativeDLOpen = ffi.dlopen;
+const nativeCallback = ffi.callback;
+const closeCallback = ffi.closeCallback;
+delete ffi.callback;
+delete ffi.closeCallback;
+
+export class JSCallback {
+ constructor(options, cb) {
+ const { ctx, ptr } = nativeCallback(options, cb);
+ this.#ctx = ctx;
+ this.ptr = ptr;
+ }
+
+ ptr;
+ #ctx;
+
+ [Symbol.toPrimitive]() {
+ return this.ptr;
+ }
+
+ close() {
+ const ctx = this.#ctx;
+ this.ptr = null;
+ this.#ctx = null;
+
+ if (ctx) {
+ closeCallback(ctx);
+ }
+ }
+}
export class CString extends String {
constructor(ptr, byteOffset, byteLength) {
@@ -55,7 +85,8 @@ Object.defineProperty(globalThis, "__GlobalBunCString", {
configurable: false,
});
-const ffiWrappers = new Array(16);
+const ffiWrappers = new Array(18);
+
var char = (val) => val | 0;
ffiWrappers.fill(char);
ffiWrappers[FFIType.uint8_t] = function uint8(val) {
@@ -195,6 +226,25 @@ function cstringReturnType(val) {
return new __GlobalBunCString(val);
}
+ffiWrappers[FFIType.function] = function functionType(val) {
+ if (typeof val === "number") {
+ return val;
+ }
+
+ if (typeof val === "bigint") {
+ return Number(val);
+ }
+
+ // we overwrote Symbol.toPrimitive
+ var ptr = +val;
+
+ if (!Number.isFinite(ptr) || ptr === 0) {
+ throw new Error("Expected function to be a JSCallback or a number");
+ }
+
+ return ptr;
+};
+
function FFIBuilder(params, returnType, functionToCall, name) {
const hasReturnType =
typeof FFIType[returnType] === "number" &&
@@ -293,11 +343,11 @@ function FFIBuilder(params, returnType, functionToCall, name) {
return wrap;
}
-const nativeDLOpen = ffi.dlopen;
-const nativeCallback = ffi.callback;
export const native = {
dlopen: nativeDLOpen,
- callback: nativeCallback,
+ callback: () => {
+ throw new Error("Deprecated. Use new JSCallback(options, fn) instead");
+ },
};
export function dlopen(path, options) {
@@ -383,8 +433,4 @@ export function CFunction(options) {
return result.symbols[identifier];
}
-export function callback(options, cb) {
- return nativeCallback(options, cb);
-}
-
export const read = ffi.read;