diff options
Diffstat (limited to '')
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/ffi-test.c | 9 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/ffi.test.js | 302 | ||||
-rw-r--r-- | src/javascript/jsc/api/FFI.h | 66 | ||||
-rw-r--r-- | src/javascript/jsc/api/ffi.zig | 40 |
5 files changed, 260 insertions, 159 deletions
@@ -388,7 +388,7 @@ boringssl: boringssl-build boringssl-copy boringssl-debug: boringssl-build-debug boringssl-copy compile-ffi-test: - clang -O3 -shared -undefined dynamic_lookup -o /tmp/libffi-test$(SHARED_LIB_EXTENSION) ./integration/bunjs-only-snippets/ffi-test.c + clang -O3 -shared -undefined dynamic_lookup -o /tmp/bun-ffi-test$(SHARED_LIB_EXTENSION) ./integration/bunjs-only-snippets/ffi-test.c libbacktrace: cd $(BUN_DEPS_DIR)/libbacktrace && \ diff --git a/integration/bunjs-only-snippets/ffi-test.c b/integration/bunjs-only-snippets/ffi-test.c index eb9e33fc3..4b2784a84 100644 --- a/integration/bunjs-only-snippets/ffi-test.c +++ b/integration/bunjs-only-snippets/ffi-test.c @@ -42,18 +42,21 @@ uint16_t add_uint16_t(uint16_t a, uint16_t b); uint32_t add_uint32_t(uint32_t a, uint32_t b); uint64_t add_uint64_t(uint64_t a, uint64_t b); -int8_t returns_neg_42_int8_t() { return 42; } uint16_t returns_42_uint16_t() { return 42; } uint32_t returns_42_uint32_t() { return 42; } uint64_t returns_42_uint64_t() { return 42; } + +int8_t returns_neg_42_int8_t() { return -42; } int16_t returns_neg_42_int16_t() { return -42; } int32_t returns_neg_42_int32_t() { return -42; } int64_t returns_neg_42_int64_t() { return -42; } + bool returns_true() { return true; } bool returns_false() { return false; } + char returns_42_char() { return '*'; } -float returns_42_float() { return 42.0f; } -double returns_42_double() { return (double)42.0; } +float returns_42_float() { return 42.42f; } +double returns_42_double() { return (double)42.42; } uint8_t returns_42_uint8_t() { return (uint8_t)42; } char identity_char(char a) { return a; } diff --git a/integration/bunjs-only-snippets/ffi.test.js b/integration/bunjs-only-snippets/ffi.test.js index 83a6c8950..1a18ae2cd 100644 --- a/integration/bunjs-only-snippets/ffi.test.js +++ b/integration/bunjs-only-snippets/ffi.test.js @@ -10,197 +10,225 @@ it("ffi print", () => { }); it("ffi run", () => { - const { - symbols: { - returns_true, - returns_false, - returns_42_char, - returns_42_float, - returns_42_double, - returns_42_uint8_t, - returns_neg_42_int8_t, - returns_42_uint16_t, - returns_42_uint32_t, - returns_42_uint64_t, - returns_neg_42_int16_t, - returns_neg_42_int32_t, - returns_neg_42_int64_t, - identity_char, - identity_float, - identity_bool, - identity_double, - identity_int8_t, - identity_int16_t, - identity_int32_t, - identity_int64_t, - identity_uint8_t, - identity_uint16_t, - identity_uint32_t, - identity_uint64_t, - add_char, - add_float, - add_double, - add_int8_t, - add_int16_t, - add_int32_t, - add_int64_t, - add_uint8_t, - add_uint16_t, - add_uint32_t, - add_uint64_t, - }, - close, - } = Bun.dlopen("/tmp/bun-ffi-test.dylib", { + const types = { returns_true: { - returns: "bool", - expects: [], + return_type: "bool", + params: [], }, returns_false: { - returns: "bool", - expects: [], + return_type: "bool", + params: [], }, returns_42_char: { - returns: "char", - expects: [], - }, - returns_42_float: { - returns: "float", - expects: [], - }, - returns_42_double: { - returns: "double", - expects: [], - }, + return_type: "char", + params: [], + }, + // returns_42_float: { + // return_type: "float", + // params: [], + // }, + // returns_42_double: { + // return_type: "double", + // params: [], + // }, returns_42_uint8_t: { - returns: "uint8_t", - expects: [], + return_type: "uint8_t", + params: [], }, returns_neg_42_int8_t: { - returns: "int8_t", - expects: [], + return_type: "int8_t", + params: [], }, returns_42_uint16_t: { - returns: "uint16_t", - expects: [], + return_type: "uint16_t", + params: [], }, returns_42_uint32_t: { - returns: "uint32_t", - expects: [], - }, - returns_42_uint64_t: { - returns: "uint64_t", - expects: [], + return_type: "uint32_t", + params: [], }, + // // returns_42_uint64_t: { + // // return_type: "uint64_t", + // // params: [], + // // }, returns_neg_42_int16_t: { - returns: "int16_t", - expects: [], + return_type: "int16_t", + params: [], }, returns_neg_42_int32_t: { - returns: "int32_t", - expects: [], - }, - returns_neg_42_int64_t: { - returns: "int64_t", - expects: [], + return_type: "int32_t", + params: [], }, + // returns_neg_42_int64_t: { + // return_type: "int64_t", + // params: [], + // }, identity_char: { - returns: "char", - expects: ["char"], - }, - identity_float: { - returns: "float", - expects: ["float"], + return_type: "char", + params: ["char"], }, + // identity_float: { + // return_type: "float", + // params: ["float"], + // }, identity_bool: { - returns: "bool", - expects: ["bool"], - }, - identity_double: { - returns: "double", - expects: ["double"], + return_type: "bool", + params: ["bool"], }, + // identity_double: { + // return_type: "double", + // params: ["double"], + // }, identity_int8_t: { - returns: "int8_t", - expects: ["int8_t"], + return_type: "int8_t", + params: ["int8_t"], }, identity_int16_t: { - returns: "int16_t", - expects: ["int16_t"], + return_type: "int16_t", + params: ["int16_t"], }, identity_int32_t: { - returns: "int32_t", - expects: ["int32_t"], - }, - identity_int64_t: { - returns: "int64_t", - expects: ["int64_t"], + return_type: "int32_t", + params: ["int32_t"], }, + // identity_int64_t: { + // return_type: "int64_t", + // params: ["int64_t"], + // }, identity_uint8_t: { - returns: "uint8_t", - expects: ["uint8_t"], + return_type: "uint8_t", + params: ["uint8_t"], }, identity_uint16_t: { - returns: "uint16_t", - expects: ["uint16_t"], + return_type: "uint16_t", + params: ["uint16_t"], }, identity_uint32_t: { - returns: "uint32_t", - expects: ["uint32_t"], - }, - identity_uint64_t: { - returns: "uint64_t", - expects: ["uint64_t"], + return_type: "uint32_t", + params: ["uint32_t"], }, + // identity_uint64_t: { + // return_type: "uint64_t", + // params: ["uint64_t"], + // }, add_char: { - returns: "char", - expects: ["char", "char"], + return_type: "char", + params: ["char", "char"], }, add_float: { - returns: "float", - expects: ["float", "float"], + return_type: "float", + params: ["float", "float"], }, add_double: { - returns: "double", - expects: ["double", "double"], + return_type: "double", + params: ["double", "double"], }, add_int8_t: { - returns: "int8_t", - expects: ["int8_t", "int8_t"], + return_type: "int8_t", + params: ["int8_t", "int8_t"], }, add_int16_t: { - returns: "int16_t", - expects: ["int16_t", "int16_t"], + return_type: "int16_t", + params: ["int16_t", "int16_t"], }, add_int32_t: { - returns: "int32_t", - expects: ["int32_t", "int32_t"], - }, - add_int64_t: { - returns: "int64_t", - expects: ["int64_t", "int64_t"], + return_type: "int32_t", + params: ["int32_t", "int32_t"], }, + // add_int64_t: { + // return_type: "int64_t", + // params: ["int64_t", "int64_t"], + // }, add_uint8_t: { - returns: "uint8_t", - expects: ["uint8_t", "uint8_t"], + return_type: "uint8_t", + params: ["uint8_t", "uint8_t"], }, add_uint16_t: { - returns: "uint16_t", - expects: ["uint16_t", "uint16_t"], + return_type: "uint16_t", + params: ["uint16_t", "uint16_t"], }, add_uint32_t: { - returns: "uint32_t", - expects: ["uint32_t", "uint32_t"], - }, - add_uint64_t: { - returns: "uint64_t", - expects: ["uint64_t", "uint64_t"], + return_type: "uint32_t", + params: ["uint32_t", "uint32_t"], + }, + // add_uint64_t: { + // return_type: "uint64_t", + // params: ["uint64_t", "uint64_t"], + // }, + }; + console.log(Bun.dlprint(types)[0]); + const { + symbols: { + returns_true, + returns_false, + returns_42_char, + returns_42_float, + returns_42_double, + returns_42_uint8_t, + returns_neg_42_int8_t, + returns_42_uint16_t, + returns_42_uint32_t, + returns_42_uint64_t, + returns_neg_42_int16_t, + returns_neg_42_int32_t, + returns_neg_42_int64_t, + identity_char, + identity_float, + identity_bool, + identity_double, + identity_int8_t, + identity_int16_t, + identity_int32_t, + identity_int64_t, + identity_uint8_t, + identity_uint16_t, + identity_uint32_t, + identity_uint64_t, + add_char, + add_float, + add_double, + add_int8_t, + add_int16_t, + add_int32_t, + add_int64_t, + add_uint8_t, + add_uint16_t, + add_uint32_t, + add_uint64_t, }, - }); + close, + } = Bun.dlopen("/tmp/bun-ffi-test.dylib", types); + + expect(returns_true()).toBe(true); + expect(returns_false()).toBe(false); + expect(returns_42_char()).toBe(42); + // expect(returns_42_float()).toBe(42); + // expect(returns_42_double()).toBe(42); + expect(returns_42_uint8_t()).toBe(42); + expect(returns_neg_42_int8_t()).toBe(-42); + expect(returns_42_uint16_t()).toBe(42); + expect(returns_42_uint32_t()).toBe(42); + // expect(returns_42_uint64_t()).toBe(42); + expect(returns_neg_42_int16_t()).toBe(-42); + expect(returns_neg_42_int32_t()).toBe(-42); + // expect(returns_neg_42_int64_t()).toBe(-42); + expect(identity_char(10)).toBe(10); + // expect(identity_float(10.1)).toBe(10.1); + expect(identity_bool(true)).toBe(true); + expect(identity_bool(false)).toBe(false); + // expect(identity_double(10.1)).toBe(10.1); + expect(identity_int8_t(10)).toBe(10); + expect(identity_int16_t(10)).toBe(10); + expect(identity_int32_t(10)).toBe(10); + // expect(identity_int64_t(10)).toBe(10); + expect(identity_uint8_t(10)).toBe(10); + expect(identity_uint16_t(10)).toBe(10); + expect(identity_uint32_t(10)).toBe(10); expect(add_char(1, 1)).toBe(2); - expect(add_float(1.1, 1.1)).toBe(2.2); - expect(add_double(1.1, 1.1)).toBe(2.2); + // expect(add_float(1.1, 1.1)).toBe(2.2); + // expect(add_double(1.1, 1.1)).toBe(2.2); expect(add_int8_t(1, 1)).toBe(2); expect(add_int16_t(1, 1)).toBe(2); expect(add_int32_t(1, 1)).toBe(2); diff --git a/src/javascript/jsc/api/FFI.h b/src/javascript/jsc/api/FFI.h index ffed86138..a6ad1cccb 100644 --- a/src/javascript/jsc/api/FFI.h +++ b/src/javascript/jsc/api/FFI.h @@ -1,10 +1,15 @@ -// This is an auto-generated file +// This file is part of Bun! +// You can find the original source: +// https://github.com/Jarred-Sumner/bun/blob/main/src/javascript/jsc/api/FFI.h#L2 // // clang-format off // This file is only compatible with 64 bit CPUs // It must be kept in sync with JSCJSValue.h // https://github.com/Jarred-Sumner/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458 +#ifdef USES_FLOAT +#include <math.h> +#endif #define IS_BIG_ENDIAN 0 #define USE_JSVALUE64 1 @@ -25,12 +30,12 @@ typedef uintptr_t size_t; #define true 1 #define false 0 -#define bool _bool +#define bool _Bool // This value is 2^49, used to encode doubles such that the encoded value will // begin with a 15-bit pattern within the range 0x0002..0xFFFC. -#define DoubleEncodeOffsetBit (size_t)(49) -#define DoubleEncodeOffset (int64_t)(1ll << DoubleEncodeOffsetBit) +#define DoubleEncodeOffsetBit 49 +#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit) #define OtherTag 0x2 #define BoolTag 0x4 #define UndefinedTag 0x8 @@ -71,25 +76,60 @@ typedef union EncodedJSValue { EncodedJSValue ValueUndefined = { TagValueUndefined }; EncodedJSValue ValueTrue = { TagValueTrue }; +static EncodedJSValue INT32_TO_JSVALUE(int32_t val) __attribute__((__always_inline__)); +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 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 int32_t JSVALUE_TO_INT32(EncodedJSValue val) { + return val.asInt64; +} -static EncodedJSValue INT32_TO_JSVALUE(int32_t val); static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { EncodedJSValue res; res.asInt64 = NumberTag | (uint32_t)val; return res; } -#define JSVALUE_IS_TRUE(i) (!!(i.asInt64 == ValueTrue)) -#define JSVALUE_TO_INT32(i) (int32_t)i.asInt64 -#define JSVALUE_TO_DOUBLE(i) ((double)(i.asInt64 - DoubleEncodeOffset)) -#define JSVALUE_TO_FLOAT(i) ((float)(i.asInt64 - DoubleEncodeOffset)) +static EncodedJSValue DOUBLE_TO_JSVALUE(double val) { + EncodedJSValue res; +#ifdef USES_FLOAT + res.asInt64 = trunc(val) == val ? val : val - DoubleEncodeOffset; +#else +// should never get here + res.asInt64 = 0xa; +#endif + return res; +} + +static EncodedJSValue FLOAT_TO_JSVALUE(float val) { + return DOUBLE_TO_JSVALUE(val); +} + +static EncodedJSValue BOOLEAN_TO_JSVALUE(bool val) { + EncodedJSValue res; + res.asInt64 = val ? TagValueTrue : TagValueFalse; + return res; +} -#define BOOLEAN_TO_JSVALUE(i) (i ? ValueTrue : ValueFalse) -#define DOUBLE_TO_JSVALUE(i) ((double)(i.asInt64 - DoubleEncodeOffset)) -#define FLOAT_TO_JSVALUE(i) ((float)(i.asInt64 - DoubleEncodeOffset)) +static double JSVALUE_TO_DOUBLE(EncodedJSValue val) { + return val.asInt64 + DoubleEncodeOffset; +} +static float JSVALUE_TO_FLOAT(EncodedJSValue val) { + return (float)JSVALUE_TO_DOUBLE(val); +} +static bool JSVALUE_TO_BOOL(EncodedJSValue val) { + return val.asInt64 == TagValueTrue; +} typedef void* JSContext; @@ -105,3 +145,5 @@ void* Bun__CallbackFunctionPlaceholder(JSContext ctx, EncodedJSValue function, E void* Bun__CallbackFunctionPlaceholder(JSContext ctx, EncodedJSValue function, EncodedJSValue thisObject, size_t argumentCount, const EncodedJSValue arguments[], JSException exception) { return (void*)123; } + +// --- Generated Code --- diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig index 4062baadb..02dc0364c 100644 --- a/src/javascript/jsc/api/ffi.zig +++ b/src/javascript/jsc/api/ffi.zig @@ -404,6 +404,7 @@ pub const FFI = struct { try source_code.append(0); defer source_code.deinit(); var state = TCC.tcc_new() orelse return error.TCCMissing; + TCC.tcc_set_options(state, "-std=c11"); TCC.tcc_set_error_func(state, this, handleTCCError); // defer TCC.tcc_delete(state); _ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY); @@ -469,6 +470,21 @@ pub const FFI = struct { this: *Function, writer: anytype, ) !void { + brk: { + if (this.return_type == .primitive and this.return_type.primitive.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()) { + try writer.writeAll("#define USES_FLOAT 1\n"); + break; + } + } + } + if (comptime Environment.isRelease) { try writer.writeAll(std.mem.span(FFI_HEADER)); } else { @@ -524,7 +540,8 @@ pub const FFI = struct { first = false; try writer.print("arg{d}", .{i}); } - try writer.writeAll(");\n\n"); + try writer.writeAll(");\n"); + if (!first) try writer.writeAll("\n"); try writer.writeAll(" "); @@ -548,6 +565,10 @@ pub const FFI = struct { 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 } }, @@ -702,6 +723,13 @@ pub const FFI = struct { bool = 13, + pub fn isFloatingPoint(this: Tag) bool { + return switch (this) { + .double, .float => true, + else => false, + }; + } + const ToCFormatter = struct { symbol: string, tag: Tag, @@ -710,7 +738,7 @@ pub const FFI = struct { switch (self.tag) { .void => {}, .bool => { - try writer.print("JSVALUE_IS_TRUE({s})", .{self.symbol}); + 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}); @@ -744,10 +772,10 @@ pub const FFI = struct { .int64_t => {}, .uint64_t => {}, .double => { - try writer.print("DOUBLE_to_JSVALUE({s})", .{self.symbol}); + try writer.print("DOUBLE_TO_JSVALUE({s})", .{self.symbol}); }, .float => { - try writer.print("FLOAT_to_JSVALUE({s})", .{self.symbol}); + try writer.print("FLOAT_TO_JSVALUE({s})", .{self.symbol}); }, else => unreachable, } @@ -781,8 +809,8 @@ pub const FFI = struct { .int64_t => "int64_t", .uint64_t => "uint64_t", .double => "float", - .float => "double", - .char => "int8_t", + .float => "float", + .char => "char", else => unreachable, }; } |