aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--integration/bunjs-only-snippets/ffi.test.fixture.callback.c3
-rw-r--r--integration/bunjs-only-snippets/ffi.test.fixture.receiver.c3
-rw-r--r--integration/bunjs-only-snippets/ffi.test.js41
-rw-r--r--packages/bun-types/types.d.ts452
-rw-r--r--src/javascript/jsc/api/bun.zig259
-rw-r--r--src/javascript/jsc/api/ffi.zig23
-rw-r--r--src/javascript/jsc/ffi.exports.js60
-rw-r--r--types/bun/ffi.d.ts24
8 files changed, 664 insertions, 201 deletions
diff --git a/integration/bunjs-only-snippets/ffi.test.fixture.callback.c b/integration/bunjs-only-snippets/ffi.test.fixture.callback.c
index 97932e255..3a557e7d5 100644
--- a/integration/bunjs-only-snippets/ffi.test.fixture.callback.c
+++ b/integration/bunjs-only-snippets/ffi.test.fixture.callback.c
@@ -10,7 +10,6 @@
#ifdef IS_CALLBACK
#define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call
#endif
-
#define IS_BIG_ENDIAN 0
#define USE_JSVALUE64 1
#define USE_JSVALUE32_64 0
@@ -39,8 +38,6 @@ typedef _Bool bool;
#endif
// #include <tcclib.h>
-
-
// 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
diff --git a/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c b/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c
index 07d44c3a6..8a75e12f9 100644
--- a/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c
+++ b/integration/bunjs-only-snippets/ffi.test.fixture.receiver.c
@@ -11,7 +11,6 @@
#ifdef IS_CALLBACK
#define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call
#endif
-
#define IS_BIG_ENDIAN 0
#define USE_JSVALUE64 1
#define USE_JSVALUE32_64 0
@@ -40,8 +39,6 @@ typedef _Bool bool;
#endif
// #include <tcclib.h>
-
-
// 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
diff --git a/integration/bunjs-only-snippets/ffi.test.js b/integration/bunjs-only-snippets/ffi.test.js
index 258ee93ec..9198df3f7 100644
--- a/integration/bunjs-only-snippets/ffi.test.js
+++ b/integration/bunjs-only-snippets/ffi.test.js
@@ -58,8 +58,10 @@ it("ffi print", async () => {
).toBe(true);
});
-it("ffi run", () => {
- const types = {
+function getTypes(fast) {
+ const int64_t = fast ? "i64_fast" : "int64_t";
+ const uint64_t = fast ? "u64_fast" : "uint64_t";
+ return {
returns_true: {
returns: "bool",
args: [],
@@ -97,7 +99,7 @@ it("ffi run", () => {
args: [],
},
returns_42_uint64_t: {
- returns: "uint64_t",
+ returns: uint64_t,
args: [],
},
returns_neg_42_int16_t: {
@@ -109,7 +111,7 @@ it("ffi run", () => {
args: [],
},
returns_neg_42_int64_t: {
- returns: "int64_t",
+ returns: int64_t,
args: [],
},
@@ -142,8 +144,8 @@ it("ffi run", () => {
args: ["int32_t"],
},
identity_int64_t: {
- returns: "int64_t",
- args: ["int64_t"],
+ returns: int64_t,
+ args: [int64_t],
},
identity_uint8_t: {
returns: "uint8_t",
@@ -158,8 +160,8 @@ it("ffi run", () => {
args: ["uint32_t"],
},
identity_uint64_t: {
- returns: "uint64_t",
- args: ["uint64_t"],
+ returns: uint64_t,
+ args: [uint64_t],
},
add_char: {
@@ -187,8 +189,8 @@ it("ffi run", () => {
args: ["int32_t", "int32_t"],
},
add_int64_t: {
- returns: "int64_t",
- args: ["int64_t", "int64_t"],
+ returns: int64_t,
+ args: [int64_t, int64_t],
},
add_uint8_t: {
returns: "uint8_t",
@@ -217,8 +219,8 @@ it("ffi run", () => {
args: ["ptr"],
},
add_uint64_t: {
- returns: "uint64_t",
- args: ["uint64_t", "uint64_t"],
+ returns: uint64_t,
+ args: [uint64_t, uint64_t],
},
cb_identity_true: {
@@ -258,7 +260,7 @@ it("ffi run", () => {
args: ["ptr"],
},
cb_identity_42_uint64_t: {
- returns: "uint64_t",
+ returns: uint64_t,
args: ["ptr"],
},
cb_identity_neg_42_int16_t: {
@@ -270,7 +272,7 @@ it("ffi run", () => {
args: ["ptr"],
},
cb_identity_neg_42_int64_t: {
- returns: "int64_t",
+ returns: int64_t,
args: ["ptr"],
},
@@ -279,6 +281,9 @@ it("ffi run", () => {
args: [],
},
};
+}
+
+function ffiRunner(types) {
const {
symbols: {
returns_true,
@@ -514,4 +519,12 @@ it("ffi run", () => {
// ).toBe(-42);
close();
+}
+
+it("run ffi fast", () => {
+ ffiRunner(getTypes(true));
+});
+
+it("run ffi", () => {
+ ffiRunner(getTypes(false));
});
diff --git a/packages/bun-types/types.d.ts b/packages/bun-types/types.d.ts
index b3dfbc605..87db8a016 100644
--- a/packages/bun-types/types.d.ts
+++ b/packages/bun-types/types.d.ts
@@ -818,6 +818,16 @@ declare module "bun" {
}
/**
+ * Nanoseconds since Bun.js was started as an integer.
+ *
+ * This uses a high-resolution monotonic system timer.
+ *
+ * After 14 weeks of consecutive uptime, this function
+ * returns a `bigint` to prevent overflow
+ */
+ export function nanoseconds(): number | bigint;
+
+ /**
* Generate a heap snapshot for seeing where the heap is being used
*/
export function generateHeapSnapshot(): HeapSnapshot;
@@ -1035,14 +1045,13 @@ declare var Bun: typeof import("bun");
/**
* `bun:ffi` lets you efficiently call C functions & FFI functions from JavaScript
- * without writing any C code yourself.
+ * without writing bindings yourself.
*
* ```js
* import {dlopen, CString, ptr} from 'bun:ffi';
*
- * const lib = dlopen('libsqlite3', {});
- *
- *
+ * const lib = dlopen('libsqlite3', {
+ * });
* ```
*
* This is powered by just-in-time compiling C wrappers
@@ -1054,17 +1063,158 @@ declare var Bun: typeof import("bun");
declare module "bun:ffi" {
export enum FFIType {
char = 0,
-
+ /**
+ * 8-bit signed integer
+ *
+ * Must be a value between -127 and 127
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * signed char
+ * char // on x64 & aarch64 macOS
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
int8_t = 1,
+ /**
+ * 8-bit signed integer
+ *
+ * Must be a value between -127 and 127
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * signed char
+ * char // on x64 & aarch64 macOS
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
i8 = 1,
+ /**
+ * 8-bit unsigned integer
+ *
+ * Must be a value between 0 and 255
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * unsigned char
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
uint8_t = 2,
+ /**
+ * 8-bit unsigned integer
+ *
+ * Must be a value between 0 and 255
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * unsigned char
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
u8 = 2,
+ /**
+ * 16-bit signed integer
+ *
+ * Must be a value between -32768 and 32767
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * in16_t
+ * short // on arm64 & x64
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
int16_t = 3,
+ /**
+ * 16-bit signed integer
+ *
+ * Must be a value between -32768 and 32767
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * in16_t
+ * short // on arm64 & x64
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
i16 = 3,
+ /**
+ * 16-bit unsigned integer
+ *
+ * Must be a value between 0 and 65535, inclusive.
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * uint16_t
+ * unsigned short // on arm64 & x64
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
uint16_t = 4,
+ /**
+ * 16-bit unsigned integer
+ *
+ * Must be a value between 0 and 65535, inclusive.
+ *
+ * When passing to a FFI function (C ABI), type coercsion is not performed.
+ *
+ * In C:
+ * ```c
+ * uint16_t
+ * unsigned short // on arm64 & x64
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * var num = 0;
+ * ```
+ */
u16 = 4,
/**
@@ -1083,69 +1233,246 @@ declare module "bun:ffi" {
* 32-bit signed integer
*
* The same as `int` in C
+ *
+ * ```c
+ * int
+ * ```
*/
int = 5,
+ /**
+ * 32-bit unsigned integer
+ *
+ * The same as `unsigned int` in C (on x64 & arm64)
+ *
+ * C:
+ * ```c
+ * unsigned int
+ * ```
+ * JavaScript:
+ * ```js
+ * ptr(new Uint32Array(1))
+ * ```
+ */
uint32_t = 6,
+ /**
+ * 32-bit unsigned integer
+ *
+ * Alias of {@link FFIType.uint32_t}
+ */
u32 = 6,
+ /**
+ * int64 is a 64-bit signed integer
+ *
+ * This is not implemented yet!
+ */
int64_t = 7,
+ /**
+ * i64 is a 64-bit signed integer
+ *
+ * This is not implemented yet!
+ */
i64 = 7,
+ /**
+ * 64-bit unsigned integer
+ *
+ * This is not implemented yet!
+ */
uint64_t = 8,
+ /**
+ * 64-bit unsigned integer
+ *
+ * This is not implemented yet!
+ */
u64 = 8,
+ /**
+ * Doubles are not supported yet!
+ */
double = 9,
+ /**
+ * Doubles are not supported yet!
+ */
f64 = 9,
-
+ /**
+ * Floats are not supported yet!
+ */
float = 10,
+ /**
+ * Floats are not supported yet!
+ */
f32 = 10,
+ /**
+ * Booelan value
+ *
+ * Must be `true` or `false`. `0` and `1` type coercion is not supported.
+ *
+ * In C, this corresponds to:
+ * ```c
+ * bool
+ * _Bool
+ * ```
+ *
+ *
+ */
bool = 11,
+ /**
+ * Pointer value
+ *
+ * See {@link Bun.FFI.ptr} for more information
+ *
+ * In C:
+ * ```c
+ * void*
+ * ```
+ *
+ * In JavaScript:
+ * ```js
+ * ptr(new Uint8Array(1))
+ * ```
+ */
ptr = 12,
+ /**
+ * Pointer value
+ *
+ * alias of {@link FFIType.ptr}
+ */
pointer = 12,
+ /**
+ * void value
+ *
+ * void arguments are not supported
+ *
+ * void return type is the default return type
+ *
+ * In C:
+ * ```c
+ * void
+ * ```
+ *
+ */
void = 13,
+
+ /**
+ * When used as a `returns`, this will automatically become a {@link CString}.
+ *
+ * When used in `args` it is equivalent to {@link FFIType.pointer}
+ *
+ */
+ cstring = 14,
+ }
+ export type FFITypeOrString =
+ | FFIType
+ | "char"
+ | "int8_t"
+ | "i8"
+ | "uint8_t"
+ | "u8"
+ | "int16_t"
+ | "i16"
+ | "uint16_t"
+ | "u16"
+ | "int32_t"
+ | "i32"
+ | "int"
+ | "uint32_t"
+ | "u32"
+ | "int64_t"
+ | "i64"
+ | "uint64_t"
+ | "u64"
+ | "double"
+ | "f64"
+ | "float"
+ | "f32"
+ | "bool"
+ | "ptr"
+ | "pointer"
+ | "void"
+ | "cstring";
+
+ interface FFIFunction {
+ /**
+ * Arguments to a FFI function (C ABI)
+ *
+ * Defaults to an empty array, which means no arguments.
+ *
+ * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}.
+ *
+ * @example
+ * From JavaScript:
+ * ```js
+ * const lib = dlopen('add', {
+ * // FFIType can be used or you can pass string labels.
+ * args: [FFIType.i32, "i32"],
+ * returns: "i32",
+ * });
+ * lib.symbols.add(1, 2)
+ * ```
+ * In C:
+ * ```c
+ * int add(int a, int b) {
+ * return a + b;
+ * }
+ * ```
+ */
+ args?: FFITypeOrString[];
+ /**
+ * Return type to a FFI function (C ABI)
+ *
+ * Defaults to {@link FFIType.void}
+ *
+ * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}.
+ *
+ * @example
+ * From JavaScript:
+ * ```js
+ * const lib = dlopen('z', {
+ * version: {
+ * returns: "ptr",
+ * }
+ * });
+ * console.log(new CString(lib.symbols.version()));
+ * ```
+ * In C:
+ * ```c
+ * char* version()
+ * {
+ * return "1.0.0";
+ * }
+ * ```
+ */
+ returns?: FFITypeOrString;
}
- type Symbols = Record<
- string,
- {
- /**
- * Arguments to a C function
- *
- * Defaults to an empty array, which means no arguments.
- *
- * To pass a pointer, use "ptr" or "pointer" as the type name. To get a pointer, see {@link ptr}.
- *
- * @example
- * From JavaScript:
- * ```js
- * const lib = dlopen('add', {
- * // FFIType can be used or you can pass string labels.
- * args: [FFIType.i32, "i32"],
- * return_type: "i32",
- * });
- * lib.symbols.add(1, 2)
- * ```
- * In C:
- * ```c
- * int add(int a, int b) {
- * return a + b;
- * }
- * ```
- */
- args?: FFIType[];
- return_type?: FFIType;
- }
- >;
+ type Symbols = Record<string, FFIFunction>;
+
+ // /**
+ // * Compile a callback function
+ // *
+ // * Returns a function pointer
+ // *
+ // */
+ // export function callback(ffi: FFIFunction, cb: Function): number;
export interface Library {
- symbols: Record<string, CallableFunction>;
+ symbols: Record<
+ string,
+ CallableFunction & {
+ /**
+ * The function without a wrapper
+ */
+ native: CallableFunction;
+ }
+ >;
/**
- * `dlclose` the library, unloading the symbols and freeing memory allocated.
+ * `dlclose` the library, unloading the symbols and freeing allocated memory.
*
* Once called, the library is no longer usable.
*
@@ -1214,11 +1541,11 @@ declare module "bun:ffi" {
* ```js
* const array = new Uint8Array(10);
* const rawPtr = ptr(array);
- * myCFunction(rawPtr);
+ * myFFIFunction(rawPtr);
* ```
* To C:
* ```c
- * void myCFunction(char* rawPtr) {
+ * void myFFIFunction(char* rawPtr) {
* // Do something with rawPtr
* }
* ```
@@ -1251,7 +1578,8 @@ declare module "bun:ffi" {
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
- export interface CString {
+
+ export class CString extends String {
/**
* Get a string from a UTF-8 encoded C string
* If `byteLength` is not provided, the string is assumed to be null-terminated.
@@ -1279,7 +1607,25 @@ declare module "bun:ffi" {
* reading beyond the bounds of the pointer will crash the program or cause
* undefined behavior. Use with care!
*/
- new (ptr: number, byteOffset?: number, byteLength?: number): string;
+ constructor(ptr: number, byteOffset?: number, byteLength?: number): string;
+
+ /**
+ * The ptr to the C string
+ *
+ * This `CString` instance is a clone of the string, so it
+ * is safe to continue using this instance after the `ptr` has been
+ * freed.
+ */
+ ptr: number;
+ byteOffset?: number;
+ byteLength?: number;
+
+ /**
+ * Get the {@link ptr} as an `ArrayBuffer`
+ *
+ * `null` or empty ptrs returns an `ArrayBuffer` with `byteLength` 0
+ */
+ get arrayBuffer(): ArrayBuffer;
}
/**
@@ -1288,7 +1634,25 @@ declare module "bun:ffi" {
* You probably won't need this unless there's a bug in the FFI bindings
* generator or you're just curious.
*/
- export function viewSource(symbols: Symbols): string[];
+ export function viewSource(symbols: Symbols, is_callback?: false): string[];
+ export function viewSource(callback: FFIFunction, is_callback: true): string;
+
+ /**
+ * Platform-specific file extension name for dynamic libraries
+ *
+ * "." is not included
+ *
+ * @example
+ * ```js
+ * "dylib" // macOS
+ * ```
+ *
+ * @example
+ * ```js
+ * "so" // linux
+ * ```
+ */
+ export const suffix: string;
}
diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig
index 632d85500..afb3c9a60 100644
--- a/src/javascript/jsc/api/bun.zig
+++ b/src/javascript/jsc/api/bun.zig
@@ -981,121 +981,150 @@ pub const Class = NewClass(
},
},
},
- .{ .match = .{
- .rfn = Router.match,
- .ts = Router.match_type_definition,
- }, .sleepSync = .{
- .rfn = sleepSync,
- }, .fetch = .{
- .rfn = Fetch.call,
- .ts = d.ts{},
- }, .getImportedStyles = .{
- .rfn = Bun.getImportedStyles,
- .ts = d.ts{
- .name = "getImportedStyles",
- .@"return" = "string[]",
- },
- }, .inspect = .{
- .rfn = Bun.inspect,
- .ts = d.ts{
- .name = "inspect",
- .@"return" = "string",
- },
- }, .getRouteFiles = .{
- .rfn = Bun.getRouteFiles,
- .ts = d.ts{
- .name = "getRouteFiles",
- .@"return" = "string[]",
- },
- }, ._Path = .{
- .rfn = Bun.newPath,
- .ts = d.ts{},
- }, .getRouteNames = .{
- .rfn = Bun.getRouteNames,
- .ts = d.ts{
- .name = "getRouteNames",
- .@"return" = "string[]",
- },
- }, .readFile = .{
- .rfn = Bun.readFileAsString,
- .ts = d.ts{
- .name = "readFile",
- .@"return" = "string",
- },
- }, .resolveSync = .{
- .rfn = Bun.resolveSync,
- .ts = d.ts{
- .name = "resolveSync",
- .@"return" = "string",
- },
- }, .resolve = .{
- .rfn = Bun.resolve,
- .ts = d.ts{
- .name = "resolve",
- .@"return" = "string",
- },
- }, .readFileBytes = .{
- .rfn = Bun.readFileAsBytes,
- .ts = d.ts{
- .name = "readFile",
- .@"return" = "Uint8Array",
- },
- }, .getPublicPath = .{
- .rfn = Bun.getPublicPathJS,
- .ts = d.ts{
- .name = "getPublicPath",
- .@"return" = "string",
- },
- }, .registerMacro = .{
- .rfn = Bun.registerMacro,
- .ts = d.ts{
- .name = "registerMacro",
- .@"return" = "undefined",
- },
- .enumerable = false,
- }, .fs = .{
- .rfn = Bun.createNodeFS,
- .ts = d.ts{},
- .enumerable = false,
- }, .jest = .{
- .rfn = @import("../test/jest.zig").Jest.call,
- .ts = d.ts{},
- .enumerable = false,
- }, .gc = .{
- .rfn = Bun.runGC,
- .ts = d.ts{},
- }, .allocUnsafe = .{
- .rfn = Bun.allocUnsafe,
- .ts = .{},
- }, .mmap = .{
- .rfn = Bun.mmapFile,
- .ts = .{},
- }, .generateHeapSnapshot = .{
- .rfn = Bun.generateHeapSnapshot,
- .ts = d.ts{},
- }, .shrink = .{
- .rfn = Bun.shrink,
- .ts = d.ts{},
- }, .openInEditor = .{
- .rfn = Bun.openInEditor,
- .ts = d.ts{},
- }, .readAllStdinSync = .{
- .rfn = Bun.readAllStdinSync,
- .ts = d.ts{},
- }, .serve = .{
- .rfn = Bun.serve,
- .ts = d.ts{},
- }, .file = .{
- .rfn = JSC.WebCore.Blob.constructFile,
- .ts = d.ts{},
- }, .write = .{
- .rfn = JSC.WebCore.Blob.writeFile,
- .ts = d.ts{},
- }, .sha = .{
- .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false, true),
- }, .nanoseconds = .{
- .rfn = nanoseconds,
- } },
+ .{
+ .match = .{
+ .rfn = Router.match,
+ .ts = Router.match_type_definition,
+ },
+ .sleepSync = .{
+ .rfn = sleepSync,
+ },
+ .fetch = .{
+ .rfn = Fetch.call,
+ .ts = d.ts{},
+ },
+ .getImportedStyles = .{
+ .rfn = Bun.getImportedStyles,
+ .ts = d.ts{
+ .name = "getImportedStyles",
+ .@"return" = "string[]",
+ },
+ },
+ .inspect = .{
+ .rfn = Bun.inspect,
+ .ts = d.ts{
+ .name = "inspect",
+ .@"return" = "string",
+ },
+ },
+ .getRouteFiles = .{
+ .rfn = Bun.getRouteFiles,
+ .ts = d.ts{
+ .name = "getRouteFiles",
+ .@"return" = "string[]",
+ },
+ },
+ ._Path = .{
+ .rfn = Bun.newPath,
+ .ts = d.ts{},
+ },
+ .getRouteNames = .{
+ .rfn = Bun.getRouteNames,
+ .ts = d.ts{
+ .name = "getRouteNames",
+ .@"return" = "string[]",
+ },
+ },
+ .readFile = .{
+ .rfn = Bun.readFileAsString,
+ .ts = d.ts{
+ .name = "readFile",
+ .@"return" = "string",
+ },
+ },
+ .resolveSync = .{
+ .rfn = Bun.resolveSync,
+ .ts = d.ts{
+ .name = "resolveSync",
+ .@"return" = "string",
+ },
+ },
+ .resolve = .{
+ .rfn = Bun.resolve,
+ .ts = d.ts{
+ .name = "resolve",
+ .@"return" = "string",
+ },
+ },
+ .readFileBytes = .{
+ .rfn = Bun.readFileAsBytes,
+ .ts = d.ts{
+ .name = "readFile",
+ .@"return" = "Uint8Array",
+ },
+ },
+ .getPublicPath = .{
+ .rfn = Bun.getPublicPathJS,
+ .ts = d.ts{
+ .name = "getPublicPath",
+ .@"return" = "string",
+ },
+ },
+ .registerMacro = .{
+ .rfn = Bun.registerMacro,
+ .ts = d.ts{
+ .name = "registerMacro",
+ .@"return" = "undefined",
+ },
+ .enumerable = false,
+ },
+ .fs = .{
+ .rfn = Bun.createNodeFS,
+ .ts = d.ts{},
+ .enumerable = false,
+ },
+ .jest = .{
+ .rfn = @import("../test/jest.zig").Jest.call,
+ .ts = d.ts{},
+ .enumerable = false,
+ },
+ .gc = .{
+ .rfn = Bun.runGC,
+ .ts = d.ts{},
+ },
+ .allocUnsafe = .{
+ .rfn = Bun.allocUnsafe,
+ .ts = .{},
+ },
+ .mmap = .{
+ .rfn = Bun.mmapFile,
+ .ts = .{},
+ },
+ .generateHeapSnapshot = .{
+ .rfn = Bun.generateHeapSnapshot,
+ .ts = d.ts{},
+ },
+ .shrink = .{
+ .rfn = Bun.shrink,
+ .ts = d.ts{},
+ },
+ .openInEditor = .{
+ .rfn = Bun.openInEditor,
+ .ts = d.ts{},
+ },
+ .readAllStdinSync = .{
+ .rfn = Bun.readAllStdinSync,
+ .ts = d.ts{},
+ },
+ .serve = .{
+ .rfn = Bun.serve,
+ .ts = d.ts{},
+ },
+ .file = .{
+ .rfn = JSC.WebCore.Blob.constructFile,
+ .ts = d.ts{},
+ },
+ .write = .{
+ .rfn = JSC.WebCore.Blob.writeFile,
+ .ts = d.ts{},
+ },
+ .sha = .{
+ .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false, true),
+ },
+ .nanoseconds = .{
+ .rfn = nanoseconds,
+ },
+ },
.{
.main = .{
.get = getMain,
diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig
index c5ad6bf59..2e9173ee4 100644
--- a/src/javascript/jsc/api/ffi.zig
+++ b/src/javascript/jsc/api/ffi.zig
@@ -1118,6 +1118,9 @@ pub const FFI = struct {
cstring = 14,
+ i64_fast = 15,
+ u64_fast = 16,
+
/// Types that we can directly pass through as an `int64_t`
pub fn needsACastInC(this: ABIType) bool {
return switch (this) {
@@ -1160,6 +1163,8 @@ pub const FFI = struct {
.{ "pointer", ABIType.ptr },
.{ "void", ABIType.@"void" },
.{ "cstring", ABIType.@"cstring" },
+ .{ "i64_fast", ABIType.i64_fast },
+ .{ "u64_fast", ABIType.u64_fast },
};
pub const label = ComptimeStringMap(ABIType, map);
const EnumMapFormatter = struct {
@@ -1221,10 +1226,10 @@ pub const FFI = struct {
.char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
try writer.print("JSVALUE_TO_INT32({s})", .{self.symbol});
},
- .int64_t => {
+ .i64_fast, .int64_t => {
try writer.print("JSVALUE_TO_INT64({s})", .{self.symbol});
},
- .uint64_t => {
+ .u64_fast, .uint64_t => {
try writer.print("JSVALUE_TO_UINT64(globalObject, {s})", .{self.symbol});
},
.cstring, .ptr => {
@@ -1253,12 +1258,18 @@ pub const FFI = struct {
.char, .int8_t, .uint8_t, .int16_t, .uint16_t, .int32_t, .uint32_t => {
try writer.print("INT32_TO_JSVALUE({s})", .{self.symbol});
},
- .int64_t => {
+ .i64_fast => {
try writer.print("INT64_TO_JSVALUE(globalObject, {s})", .{self.symbol});
},
- .uint64_t => {
+ .int64_t => {
+ try writer.print("INT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol});
+ },
+ .u64_fast => {
try writer.print("UINT64_TO_JSVALUE(globalObject, {s})", .{self.symbol});
},
+ .uint64_t => {
+ try writer.print("UINT64_TO_JSVALUE_SLOW(globalObject, {s})", .{self.symbol});
+ },
.cstring, .ptr => {
try writer.print("PTR_TO_JSVALUE({s})", .{self.symbol});
},
@@ -1300,8 +1311,8 @@ pub const FFI = struct {
.uint16_t => "uint16_t",
.int32_t => "int32_t",
.uint32_t => "uint32_t",
- .int64_t => "int64_t",
- .uint64_t => "uint64_t",
+ .i64_fast, .int64_t => "int64_t",
+ .u64_fast, .uint64_t => "uint64_t",
.double => "double",
.float => "float",
.char => "char",
diff --git a/src/javascript/jsc/ffi.exports.js b/src/javascript/jsc/ffi.exports.js
index fea92e0cd..ae11326e5 100644
--- a/src/javascript/jsc/ffi.exports.js
+++ b/src/javascript/jsc/ffi.exports.js
@@ -52,7 +52,7 @@ Object.defineProperty(globalThis, "__GlobalBunCString", {
configurable: false,
});
-const ffiWrappers = new Array(15);
+const ffiWrappers = new Array(16);
var char = (val) => val | 0;
ffiWrappers.fill(char);
ffiWrappers[FFIType.uint8_t] = function uint8(val) {
@@ -67,13 +67,14 @@ ffiWrappers[FFIType.uint16_t] = function uint16(val) {
ffiWrappers[FFIType.int32_t] = function int32(val) {
return val | 0;
};
+// we never want to return NaN
ffiWrappers[FFIType.uint32_t] = function uint32(val) {
- return val <= 0 ? 0 : val >= 0xffffffff ? 0xffffffff : val;
+ return val <= 0 ? 0 : val >= 0xffffffff ? 0xffffffff : +val || 0;
};
-ffiWrappers[FFIType.int64_t] = function int64(val) {
+ffiWrappers[FFIType.i64_fast] = function int64(val) {
if (typeof val === "bigint") {
if (val < BigInt(Number.MAX_VALUE)) {
- return Number(val).valueOf();
+ return Number(val).valueOf() || 0;
}
}
@@ -81,13 +82,13 @@ ffiWrappers[FFIType.int64_t] = function int64(val) {
return 0;
}
- return val;
+ return +val || 0;
};
-ffiWrappers[FFIType.uint64_t] = function int64(val) {
+ffiWrappers[FFIType.u64_fast] = function u64_fast(val) {
if (typeof val === "bigint") {
if (val < BigInt(Number.MAX_VALUE) && val > 0) {
- return Number(val).valueOf();
+ return Number(val).valueOf() || 0;
}
}
@@ -95,21 +96,48 @@ ffiWrappers[FFIType.uint64_t] = function int64(val) {
return 0;
}
- return val;
+ return +val || 0;
};
-ffiWrappers[FFIType.uint16_t] = function uint64(val) {
+ffiWrappers[FFIType.int64_t] = function int64(val) {
if (typeof val === "bigint") {
- if (val < BigInt(Number.MAX_VALUE)) {
- return Math.abs(Number(val).valueOf());
- }
+ return val;
}
- if (!val) {
- return 0;
+ if (typeof val === "number") {
+ return BigInt(val);
+ }
+
+ return BigInt(+val || 0);
+};
+
+ffiWrappers[FFIType.uint64_t] = function uint64(val) {
+ if (typeof val === "bigint") {
+ return val;
}
- return Math.abs(val);
+ if (typeof val === "number") {
+ return val <= 0 ? BigInt(0) : BigInt(val);
+ }
+
+ return BigInt(+val || 0);
+};
+
+ffiWrappers[FFIType.u64_fast] = function u64_fast(val) {
+ if (typeof val === "bigint") {
+ return val < BigInt(Number.MAX_VALUE)
+ ? val <= BigInt(0)
+ ? 0
+ : Number(val)
+ : val;
+ }
+
+ return typeof val === "number" ? (val <= 0 ? 0 : +val || 0) : +val || 0;
+};
+
+ffiWrappers[FFIType.uint16_t] = function uint16(val) {
+ const ret = (typeof val === "bigint" ? Number(val) : val) | 0;
+ return ret <= 0 ? 0 : ret > 0xffff ? 0xffff : ret;
};
ffiWrappers[FFIType.double] = function double(val) {
@@ -231,7 +259,7 @@ export function dlopen(path, options) {
);
} else {
// consistentcy
- result.native = result;
+ result.symbols[key].native = result.symbols[key];
}
}
diff --git a/types/bun/ffi.d.ts b/types/bun/ffi.d.ts
index 96f6fa562..f27cbe551 100644
--- a/types/bun/ffi.d.ts
+++ b/types/bun/ffi.d.ts
@@ -320,6 +320,30 @@ declare module "bun:ffi" {
*
*/
cstring = 14,
+
+ /**
+ * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance
+ * but means you might get a `BigInt` or you might get a `number`.
+ *
+ * In C, this always becomes `int64_t`
+ *
+ * In JavaScript, this could be number or it could be BigInt, depending on what
+ * value is passed in.
+ *
+ */
+ i64_fast = 15,
+
+ /**
+ * Attempt to coerce `BigInt` into a `Number` if it fits. This improves performance
+ * but means you might get a `BigInt` or you might get a `number`.
+ *
+ * In C, this always becomes `uint64_t`
+ *
+ * In JavaScript, this could be number or it could be BigInt, depending on what
+ * value is passed in.
+ *
+ */
+ u64_fast = 16,
}
export type FFITypeOrString =
| FFIType