aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--integration/bunjs-only-snippets/ffi-test.c11
-rw-r--r--integration/bunjs-only-snippets/ffi.test.js17
-rw-r--r--src/javascript/jsc/api/FFI.h14
-rw-r--r--src/javascript/jsc/api/ffi.zig375
4 files changed, 179 insertions, 238 deletions
diff --git a/integration/bunjs-only-snippets/ffi-test.c b/integration/bunjs-only-snippets/ffi-test.c
index 4b2784a84..562d31784 100644
--- a/integration/bunjs-only-snippets/ffi-test.c
+++ b/integration/bunjs-only-snippets/ffi-test.c
@@ -1,5 +1,6 @@
#include <stdbool.h>
#include <stdint.h>
+#include <stdlib.h>
bool returns_true();
bool returns_false();
@@ -83,3 +84,13 @@ uint8_t add_uint8_t(uint8_t a, uint8_t b) { return a + b; }
uint16_t add_uint16_t(uint16_t a, uint16_t b) { return a + b; }
uint32_t add_uint32_t(uint32_t a, uint32_t b) { return a + b; }
uint64_t add_uint64_t(uint64_t a, uint64_t b) { return a + b; }
+
+void *ptr_should_point_to_42_as_int32_t();
+void *ptr_should_point_to_42_as_int32_t() {
+ int32_t *ptr = malloc(sizeof(int32_t));
+ *ptr = 42;
+ return ptr;
+}
+
+bool does_pointer_equal_42_as_int32_t(int32_t *ptr);
+bool does_pointer_equal_42_as_int32_t(int32_t *ptr) { return *ptr == 42; }
diff --git a/integration/bunjs-only-snippets/ffi.test.js b/integration/bunjs-only-snippets/ffi.test.js
index 1a18ae2cd..cd51a4594 100644
--- a/integration/bunjs-only-snippets/ffi.test.js
+++ b/integration/bunjs-only-snippets/ffi.test.js
@@ -153,6 +153,16 @@ it("ffi run", () => {
return_type: "uint32_t",
params: ["uint32_t", "uint32_t"],
},
+
+ does_pointer_equal_42_as_int32_t: {
+ return_type: "bool",
+ params: ["ptr"],
+ },
+
+ ptr_should_point_to_42_as_int32_t: {
+ return_type: "ptr",
+ params: [],
+ },
// add_uint64_t: {
// return_type: "uint64_t",
// params: ["uint64_t", "uint64_t"],
@@ -197,6 +207,8 @@ it("ffi run", () => {
add_uint16_t,
add_uint32_t,
add_uint64_t,
+ does_pointer_equal_42_as_int32_t,
+ ptr_should_point_to_42_as_int32_t,
},
close,
} = Bun.dlopen("/tmp/bun-ffi-test.dylib", types);
@@ -236,6 +248,11 @@ it("ffi run", () => {
expect(add_uint8_t(1, 1)).toBe(2);
expect(add_uint16_t(1, 1)).toBe(2);
expect(add_uint32_t(1, 1)).toBe(2);
+
+ const ptr = ptr_should_point_to_42_as_int32_t();
+ expect(ptr != 0).toBe(true);
+ expect(typeof ptr === "number").toBe(true);
+ expect(does_pointer_equal_42_as_int32_t(ptr)).toBe(true);
// expect(add_uint64_t(1, 1)).toBe(2);
close();
});
diff --git a/src/javascript/jsc/api/FFI.h b/src/javascript/jsc/api/FFI.h
index a6ad1cccb..d5ccf3152 100644
--- a/src/javascript/jsc/api/FFI.h
+++ b/src/javascript/jsc/api/FFI.h
@@ -80,12 +80,26 @@ static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inli
static EncodedJSValue DOUBLE_TO_JSVALUE(double val) __attribute__((__always_inline__));
static EncodedJSValue FLOAT_TO_JSVALUE(float val) __attribute__((__always_inline__));
static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) __attribute__((__always_inline__));
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) __attribute__((__always_inline__));
+static void* JSVALUE_TO_PTR(EncodedJSValue val) __attribute__((__always_inline__));
static int32_t JSVALUE_TO_INT32(EncodedJSValue val) __attribute__((__always_inline__));
static float JSVALUE_TO_FLOAT(EncodedJSValue val) __attribute__((__always_inline__));
static double JSVALUE_TO_DOUBLE(EncodedJSValue val) __attribute__((__always_inline__));
static bool JSVALUE_TO_BOOL(EncodedJSValue val) __attribute__((__always_inline__));
+static void* JSVALUE_TO_PTR(EncodedJSValue val) {
+ // must be a double
+ return (void*)(val.asInt64 - DoubleEncodeOffset);
+}
+
+static EncodedJSValue PTR_TO_JSVALUE(void* ptr) {
+ EncodedJSValue val;
+ val.asInt64 = (int64_t)ptr + DoubleEncodeOffset;
+ return val;
+}
+
+
static int32_t JSVALUE_TO_INT32(EncodedJSValue val) {
return val.asInt64;
diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig
index 02dc0364c..ab866232b 100644
--- a/src/javascript/jsc/api/ffi.zig
+++ b/src/javascript/jsc/api/ffi.zig
@@ -326,7 +326,7 @@ pub const FFI = struct {
}
}
// var function
- var return_type = ABIType{ .primitive = .@"void" };
+ var return_type = ABIType.@"void";
if (value.get(global, "return_type")) |ret_value| {
var ret_slice = ret_value.toSlice(global, allocator);
@@ -471,14 +471,14 @@ pub const FFI = struct {
writer: anytype,
) !void {
brk: {
- if (this.return_type == .primitive and this.return_type.primitive.isFloatingPoint()) {
+ if (this.return_type.isFloatingPoint()) {
try writer.writeAll("#define USES_FLOAT 1\n");
break :brk;
}
for (this.arg_types.items) |arg| {
// conditionally include math.h
- if (arg == .primitive and arg.primitive.isFloatingPoint()) {
+ if (arg.isFloatingPoint()) {
try writer.writeAll("#define USES_FLOAT 1\n");
break;
}
@@ -527,7 +527,7 @@ pub const FFI = struct {
}
try writer.writeAll(" ");
- if (!(this.return_type == .primitive and this.return_type.primitive == .void)) {
+ if (!(this.return_type == .void)) {
try this.return_type.typename(writer);
try writer.writeAll(" return_value = ");
}
@@ -547,7 +547,7 @@ pub const FFI = struct {
try writer.writeAll("return ");
- if (!(this.return_type == .primitive and this.return_type.primitive == .void)) {
+ if (!(this.return_type == .void)) {
try writer.print("{}.asPtr", .{this.return_type.toJS("return_value")});
} else {
try writer.writeAll("ValueUndefined.asPtr");
@@ -557,263 +557,162 @@ pub const FFI = struct {
}
};
- pub const ABIType = union(enum) {
- primitive: Primitive.Tag,
- pointer: Pointer,
-
- pub const label = ComptimeStringMap(
- ABIType,
- .{
- .{ "char", ABIType{ .primitive = Primitive.Tag.char } },
- .{ "float", ABIType{ .primitive = Primitive.Tag.float } },
- .{ "double", ABIType{ .primitive = Primitive.Tag.double } },
- .{ "f32", ABIType{ .primitive = Primitive.Tag.float } },
- .{ "f64", ABIType{ .primitive = Primitive.Tag.double } },
- .{ "bool", ABIType{ .primitive = Primitive.Tag.@"bool" } },
-
- .{ "i8", ABIType{ .primitive = Primitive.Tag.int8_t } },
- .{ "u8", ABIType{ .primitive = Primitive.Tag.uint8_t } },
- .{ "i16", ABIType{ .primitive = Primitive.Tag.int16_t } },
- .{ "int", ABIType{ .primitive = Primitive.Tag.int32_t } },
- .{ "c_int", ABIType{ .primitive = Primitive.Tag.int32_t } },
- .{ "c_uint", ABIType{ .primitive = Primitive.Tag.uint32_t } },
- .{ "i32", ABIType{ .primitive = Primitive.Tag.int32_t } },
- .{ "i64", ABIType{ .primitive = Primitive.Tag.int64_t } },
- .{ "u16", ABIType{ .primitive = Primitive.Tag.uint16_t } },
- .{ "u32", ABIType{ .primitive = Primitive.Tag.uint32_t } },
- .{ "u64", ABIType{ .primitive = Primitive.Tag.uint64_t } },
- .{ "int8_t", ABIType{ .primitive = Primitive.Tag.int8_t } },
- .{ "isize", ABIType{ .primitive = Primitive.Tag.int64_t } },
- .{ "usize", ABIType{ .primitive = Primitive.Tag.uint64_t } },
- .{ "int16_t", ABIType{ .primitive = Primitive.Tag.int16_t } },
- .{ "int32_t", ABIType{ .primitive = Primitive.Tag.int32_t } },
- .{ "int64_t", ABIType{ .primitive = Primitive.Tag.int64_t } },
- .{ "uint8_t", ABIType{ .primitive = Primitive.Tag.uint8_t } },
- .{ "uint16_t", ABIType{ .primitive = Primitive.Tag.uint16_t } },
- .{ "uint32_t", ABIType{ .primitive = Primitive.Tag.uint32_t } },
- .{ "uint64_t", ABIType{ .primitive = Primitive.Tag.uint64_t } },
-
- .{ "char*", ABIType{ .pointer = .{ .primitive = Primitive.Tag.char } } },
- .{ "void*", ABIType{ .pointer = .{ .primitive = Primitive.Tag.@"void" } } },
- },
- );
+ pub const ABIType = enum(i32) {
+ char = 0,
+
+ int8_t = 1,
+ uint8_t = 2,
+
+ int16_t = 3,
+ uint16_t = 4,
+
+ int32_t = 5,
+ uint32_t = 6,
+
+ int64_t = 7,
+ uint64_t = 8,
+
+ double = 9,
+ float = 10,
+
+ bool = 11,
+
+ ptr = 12,
+
+ @"void" = 13,
+
+ pub const label = ComptimeStringMap(ABIType, .{
+ .{ "bool", .bool },
+ .{ "c_int", .int32_t },
+ .{ "c_uint", .uint32_t },
+ .{ "char", .char },
+ .{ "char*", .ptr },
+ .{ "double", .double },
+ .{ "f32", .float },
+ .{ "f64", .double },
+ .{ "float", .float },
+ .{ "i16", .int16_t },
+ .{ "i32", .int32_t },
+ .{ "i64", .int64_t },
+ .{ "i8", .int8_t },
+ .{ "int", .int32_t },
+ .{ "int16_t", .int16_t },
+ .{ "int32_t", .int32_t },
+ .{ "int64_t", .int64_t },
+ .{ "int8_t", .int8_t },
+ .{ "isize", .int64_t },
+ .{ "u16", .uint16_t },
+ .{ "u32", .uint32_t },
+ .{ "u64", .uint64_t },
+ .{ "u8", .uint8_t },
+ .{ "uint16_t", .uint16_t },
+ .{ "uint32_t", .uint32_t },
+ .{ "uint64_t", .uint64_t },
+ .{ "uint8_t", .uint8_t },
+ .{ "usize", .uint64_t },
+ .{ "void*", .ptr },
+ .{ "ptr", .ptr },
+ .{ "pointer", .ptr },
+ });
+
+ pub fn isFloatingPoint(this: ABIType) bool {
+ return switch (this) {
+ .double, .float => true,
+ else => false,
+ };
+ }
- const ToJSFormatter = struct {
- symbol: []const u8,
- abi: ABIType,
+ const ToCFormatter = struct {
+ symbol: string,
+ tag: ABIType,
- pub fn format(self: ToJSFormatter, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
- switch (self.abi) {
- .pointer => |ptr| {
- _ = ptr;
+ pub fn format(self: ToCFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ switch (self.tag) {
+ .void => {},
+ .bool => {
+ try writer.print("JSVALUE_TO_BOOL({s})", .{self.symbol});
+ },
+ .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
+ try writer.print("JSVALUE_TO_INT32({s})", .{self.symbol});
+ },
+ .int64_t => {},
+ .uint64_t => {},
+ .ptr => {
+ try writer.print("JSVALUE_TO_PTR({s})", .{self.symbol});
},
- .primitive => |prim| {
- try prim.toJS(self.symbol).format(comptime fmt, opts, writer);
+ .double => {
+ try writer.print("JSVALUE_TO_DOUBLE({s})", .{self.symbol});
+ },
+ .float => {
+ try writer.print("JSVALUE_TO_FLOAT({s})", .{self.symbol});
},
}
}
};
- const ToCFormatter = struct {
+ const ToJSFormatter = struct {
symbol: []const u8,
- abi: ABIType,
-
- pub fn format(self: ToCFormatter, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void {
- try self.abi.primitive.toC(self.symbol).format(
- comptime fmt,
- opts,
- writer,
- );
+ tag: ABIType,
+
+ pub fn format(self: ToJSFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ switch (self.tag) {
+ .void => {},
+ .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});
+ },
+ .int64_t => {},
+ .uint64_t => {},
+ .ptr => {
+ try writer.print("PTR_TO_JSVALUE({s})", .{self.symbol});
+ },
+ .double => {
+ try writer.print("DOUBLE_TO_JSVALUE({s})", .{self.symbol});
+ },
+ .float => {
+ try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol});
+ },
+ }
}
};
+ pub fn toC(this: ABIType, symbol: string) ToCFormatter {
+ return ToCFormatter{ .tag = this, .symbol = symbol };
+ }
+
pub fn toJS(
this: ABIType,
symbol: string,
) ToJSFormatter {
return ToJSFormatter{
+ .tag = this,
.symbol = symbol,
- .abi = this,
- };
- }
-
- pub fn toC(this: ABIType, symbol: string) ToCFormatter {
- return ToCFormatter{
- .symbol = symbol,
- .abi = this,
};
}
pub fn typename(this: ABIType, writer: anytype) !void {
- switch (this) {
- .primitive => |prim| {
- try writer.writeAll(prim.typename());
- },
- .pointer => |ptr| {
- try ptr.typename(writer);
- },
- }
+ try writer.writeAll(this.typenameLabel());
}
- };
-
- pub const Pointer = struct {
- count: u8 = 1,
- primitive: Primitive.Tag,
- is_const: bool = false,
-
- pub fn typename(this: Pointer, writer: anytype) !void {
- if (this.is_const) {
- try writer.writeAll("const ");
- }
-
- var i: u8 = 0;
- while (i < this.count) {
- try writer.writeAll("*");
- i = i + 1;
- }
- try writer.writeAll(" ");
- try writer.writeAll(this.primitive.typename());
- }
- };
-
- pub const Primitive = union(Tag) {
- char: i8,
- int8_t: i8,
- uint8_t: u8,
- int16_t: i16,
- uint16_t: u16,
- int32_t: c_int,
- uint32_t: c_uint,
- int64_t: i64,
- uint64_t: u64,
- double: f64,
- float: f32,
-
- void: *anyopaque,
-
- bool: bool,
-
- dynamic: struct {
- size: u32,
- alignment: u21,
- name: []const u8,
- },
-
- pub const Tag = enum(i32) {
- char = 0,
-
- int8_t = 1,
- uint8_t = 2,
-
- int16_t = 3,
- uint16_t = 4,
-
- int32_t = 5,
- uint32_t = 6,
-
- int64_t = 7,
- uint64_t = 8,
-
- double = 9,
- float = 10,
-
- void = 11,
- dynamic = 12,
-
- bool = 13,
-
- pub fn isFloatingPoint(this: Tag) bool {
- return switch (this) {
- .double, .float => true,
- else => false,
- };
- }
-
- const ToCFormatter = struct {
- symbol: string,
- tag: Tag,
-
- pub fn format(self: ToCFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
- switch (self.tag) {
- .void => {},
- .bool => {
- try writer.print("JSVALUE_TO_BOOL({s})", .{self.symbol});
- },
- .char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
- try writer.print("JSVALUE_TO_INT32({s})", .{self.symbol});
- },
- .int64_t => {},
- .uint64_t => {},
- .double => {
- try writer.print("JSVALUE_TO_DOUBLE({s})", .{self.symbol});
- },
- .float => {
- try writer.print("JSVALUE_TO_FLOAT({s})", .{self.symbol});
- },
- else => unreachable,
- }
- }
+ pub fn typenameLabel(this: ABIType) []const u8 {
+ return switch (this) {
+ .ptr => "void*",
+ .bool => "bool",
+ .int8_t => "int8_t",
+ .uint8_t => "uint8_t",
+ .int16_t => "int16_t",
+ .uint16_t => "uint16_t",
+ .int32_t => "int32_t",
+ .uint32_t => "uint32_t",
+ .int64_t => "int64_t",
+ .uint64_t => "uint64_t",
+ .double => "float",
+ .float => "float",
+ .char => "char",
+ .void => "void",
};
-
- const ToJSFormatter = struct {
- symbol: []const u8,
- tag: Tag,
-
- pub fn format(self: ToJSFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
- switch (self.tag) {
- .void => {},
- .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});
- },
- .int64_t => {},
- .uint64_t => {},
- .double => {
- try writer.print("DOUBLE_TO_JSVALUE({s})", .{self.symbol});
- },
- .float => {
- try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol});
- },
- else => unreachable,
- }
- }
- };
-
- pub fn toC(this: Tag, symbol: string) ToCFormatter {
- return ToCFormatter{ .tag = this, .symbol = symbol };
- }
-
- pub fn toJS(
- this: Tag,
- symbol: string,
- ) ToJSFormatter {
- return ToJSFormatter{
- .tag = this,
- .symbol = symbol,
- };
- }
-
- pub fn typename(this: Tag) []const u8 {
- return switch (this) {
- .void => "void",
- .bool => "bool",
- .int8_t => "int8_t",
- .uint8_t => "uint8_t",
- .int16_t => "int16_t",
- .uint16_t => "uint16_t",
- .int32_t => "int32_t",
- .uint32_t => "uint32_t",
- .int64_t => "int64_t",
- .uint64_t => "uint64_t",
- .double => "float",
- .float => "float",
- .char => "char",
- else => unreachable,
- };
- }
- };
+ }
};
};