diff options
Diffstat (limited to 'integration/bunjs-only-snippets')
3 files changed, 428 insertions, 5 deletions
diff --git a/integration/bunjs-only-snippets/ffi.test.fixture.callback.c b/integration/bunjs-only-snippets/ffi.test.fixture.callback.c new file mode 100644 index 000000000..a428926ef --- /dev/null +++ b/integration/bunjs-only-snippets/ffi.test.fixture.callback.c @@ -0,0 +1,211 @@ +#define IS_CALLBACK 1 +// 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 IS_CALLBACK +// #define INJECT_BEFORE printf("bun_call %p cachedJSContext %p cachedCallbackFunction %p\n", &bun_call, cachedJSContext, cachedCallbackFunction); +#endif + +#define IS_BIG_ENDIAN 0 +#define USE_JSVALUE64 1 +#define USE_JSVALUE32_64 0 + +// #include <stdint.h> +#ifdef INJECT_BEFORE +// #include <stdio.h> +#endif +// #include <tcclib.h> + +// // /* 7.18.1.1 Exact-width integer types */ +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef uint64_t size_t; +typedef long intptr_t; +typedef uint64_t uintptr_t; +typedef _Bool bool; + +#define true 1 +#define false 0 + +#ifdef USES_FLOAT +// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c +double trunc(double x); +double trunc(double x) +{ + union {double f; uint64_t i;} u = {x}; + int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12; + uint64_t m; + + if (e >= 52 + 12) + return x; + if (e < 12) + e = 1; + m = -1ULL >> e; + if ((u.i & m) == 0) + return x; + x + 0x1p120f; + u.i &= ~m; + return u.f; +} +#endif + + +// 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 49 +#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit) +#define OtherTag 0x2 +#define BoolTag 0x4 +#define UndefinedTag 0x8 +#define TagValueFalse (OtherTag | BoolTag | false) +#define TagValueTrue (OtherTag | BoolTag | true) +#define TagValueUndefined (OtherTag | UndefinedTag) +#define TagValueNull (OtherTag) + +// If all bits in the mask are set, this indicates an integer number, +// if any but not all are set this value is a double precision number. +#define NumberTag 0xfffe000000000000ll + +typedef void* JSCell; + +typedef union EncodedJSValue { + int64_t asInt64; +#if USE_JSVALUE32_64 + double asDouble; +#elif USE_JSVALUE64 + JSCell *ptr; +#endif + +#if IS_BIG_ENDIAN + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif + + void* asPtr; +} EncodedJSValue; + +EncodedJSValue ValueUndefined = { TagValueUndefined }; +EncodedJSValue ValueTrue = { TagValueTrue }; + +typedef void* JSContext; + +#define LOAD_ARGUMENTS_FROM_CALL_FRAME EncodedJSValue* args = (EncodedJSValue*)((size_t*)callFrame + Bun_FFI_PointerOffsetToArgumentsList); + + +#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; +#endif + +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 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; +} + +static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { + EncodedJSValue res; + res.asInt64 = NumberTag | (uint32_t)val; + return res; +} + +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; +} + + +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; +} + +#define arg(i) ((EncodedJSValue*)args)[i] +void* JSFunctionCall(void* globalObject, void* callFrame); +// int64_t JSFunctionCall(void* globalObject, void* callFrame) { +// EncodedJSValue* args = (EncodedJSValue*)((unsigned char*)callFrame + Bun_FFI_PointerOffsetToArgumentsList); +// } + + + +// --- Generated Code --- + + +/* --- The Callback Function */ +/* --- The Callback Function */ +bool my_callback_function(void* arg0); + +bool my_callback_function(, void* arg0) { +#ifdef INJECT_BEFORE +INJECT_BEFORE; +#endif + EncodedJSValue arguments[1] = { +void* PTR_TO_JSVALUE(arg0) + }; + EncodedJSValue return_value = {bun_call(cachedJSContext, cachedCallbackFunction, (void*)0, 1, arguments, 0)}; + return JSVALUE_TO_BOOL(return_value); +} + diff --git a/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c b/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c new file mode 100644 index 000000000..2107b684e --- /dev/null +++ b/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c @@ -0,0 +1,211 @@ +#define HAS_ARGUMENTS +// 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 IS_CALLBACK +// #define INJECT_BEFORE printf("bun_call %p cachedJSContext %p cachedCallbackFunction %p\n", &bun_call, cachedJSContext, cachedCallbackFunction); +#endif + +#define IS_BIG_ENDIAN 0 +#define USE_JSVALUE64 1 +#define USE_JSVALUE32_64 0 + +// #include <stdint.h> +#ifdef INJECT_BEFORE +// #include <stdio.h> +#endif +// #include <tcclib.h> + +// // /* 7.18.1.1 Exact-width integer types */ +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef uint64_t size_t; +typedef long intptr_t; +typedef uint64_t uintptr_t; +typedef _Bool bool; + +#define true 1 +#define false 0 + +#ifdef USES_FLOAT +// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c +double trunc(double x); +double trunc(double x) +{ + union {double f; uint64_t i;} u = {x}; + int e = (int)(u.i >> 52 & 0x7ff) - 0x3ff + 12; + uint64_t m; + + if (e >= 52 + 12) + return x; + if (e < 12) + e = 1; + m = -1ULL >> e; + if ((u.i & m) == 0) + return x; + x + 0x1p120f; + u.i &= ~m; + return u.f; +} +#endif + + +// 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 49 +#define DoubleEncodeOffset (1ll << DoubleEncodeOffsetBit) +#define OtherTag 0x2 +#define BoolTag 0x4 +#define UndefinedTag 0x8 +#define TagValueFalse (OtherTag | BoolTag | false) +#define TagValueTrue (OtherTag | BoolTag | true) +#define TagValueUndefined (OtherTag | UndefinedTag) +#define TagValueNull (OtherTag) + +// If all bits in the mask are set, this indicates an integer number, +// if any but not all are set this value is a double precision number. +#define NumberTag 0xfffe000000000000ll + +typedef void* JSCell; + +typedef union EncodedJSValue { + int64_t asInt64; +#if USE_JSVALUE32_64 + double asDouble; +#elif USE_JSVALUE64 + JSCell *ptr; +#endif + +#if IS_BIG_ENDIAN + struct { + int32_t tag; + int32_t payload; + } asBits; +#else + struct { + int32_t payload; + int32_t tag; + } asBits; +#endif + + void* asPtr; +} EncodedJSValue; + +EncodedJSValue ValueUndefined = { TagValueUndefined }; +EncodedJSValue ValueTrue = { TagValueTrue }; + +typedef void* JSContext; + +#define LOAD_ARGUMENTS_FROM_CALL_FRAME EncodedJSValue* args = (EncodedJSValue*)((size_t*)callFrame + Bun_FFI_PointerOffsetToArgumentsList); + + +#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; +#endif + +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 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; +} + +static EncodedJSValue INT32_TO_JSVALUE(int32_t val) { + EncodedJSValue res; + res.asInt64 = NumberTag | (uint32_t)val; + return res; +} + +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; +} + + +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; +} + +#define arg(i) ((EncodedJSValue*)args)[i] +void* JSFunctionCall(void* globalObject, void* callFrame); +// int64_t JSFunctionCall(void* globalObject, void* callFrame) { +// EncodedJSValue* args = (EncodedJSValue*)((unsigned char*)callFrame + Bun_FFI_PointerOffsetToArgumentsList); +// } + + + +// --- Generated Code --- +/* --- The Function To Call */ +void callback(void* arg0); + +/* ---- Your Wrapper Function ---- */ +void* JSFunctionCall(void* globalObject, void* callFrame) { +#ifdef HAS_ARGUMENTS + LOAD_ARGUMENTS_FROM_CALL_FRAME; +#endif +#ifdef INJECT_BEFORE +//Bun_FFI_PointerOffsetToArgumentsList: 6 +//Bun_FFI_PointerOffsetToArgumentsCount: 0 +#endif + callback( JSVALUE_TO_PTR(arg(0))); + + return ValueUndefined.asPtr; +} + diff --git a/integration/bunjs-only-snippets/ffi.test.js b/integration/bunjs-only-snippets/ffi.test.js index fb297dae2..14a38d6b2 100644 --- a/integration/bunjs-only-snippets/ffi.test.js +++ b/integration/bunjs-only-snippets/ffi.test.js @@ -14,17 +14,17 @@ import { it("ffi print", async () => { await Bun.write( - "ffi.test.fixture.callback.c", + import.meta.dir + "/ffi.test.fixture.callback.c", viewSource( { return_type: "bool", - args: [], + args: ["ptr"], }, true ) ); await Bun.write( - "ffi.test.fixture.receiver.c", + import.meta.dir + "/ffi.test.fixture.receiver.c", viewSource( { callback: { @@ -349,6 +349,7 @@ it("ffi run", () => { // expect(returns_42_uint64_t()).toBe(42); expect(returns_neg_42_int16_t()).toBe(-42); expect(returns_neg_42_int32_t()).toBe(-42); + expect(identity_int32_t(10)).toBe(10); // expect(returns_neg_42_int64_t()).toBe(-42); expect(identity_char(10)).toBe(10); // expect(identity_float(10.1)).toBe(10.1); @@ -357,7 +358,7 @@ it("ffi run", () => { // 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); @@ -383,7 +384,7 @@ it("ffi run", () => { 42 ); expect(ptr(buffer)).toBe(cptr); - expect(new CString(cptr, 0, 1)).toBe("*"); + expect(new CString(cptr, 0, 1).toString()).toBe("*"); expect(identity_ptr(cptr)).toBe(cptr); const second_ptr = ptr(new Buffer(8)); expect(identity_ptr(second_ptr)).toBe(second_ptr); |