Files
bun.sh/src/bun.js/api/FFIObject.zig
Meghan Denny 45760cd53c ci: instrument being able to run leaksanitizer (#21142)
tests not in `test/no-validate-leaksan.txt` will run with leaksanitizer
in CI
leaks documented in `test/leaksan.supp` will not cause a test failure

-- notes about leaksanitizer

- will not catch garbage collected objects accumulated during
long-running processes
- will not catch js objects (eg a strong held to a promise)
- will catch native calls to `malloc` not `free`d
- will catch allocations made in C, Zig, C++, libc, dependencies,
dlopen'd

---------

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-09-19 02:06:02 -07:00

640 lines
24 KiB
Zig

const FFIObject = @This();
pub fn newCString(globalThis: *JSGlobalObject, value: JSValue, byteOffset: ?JSValue, lengthValue: ?JSValue) bun.JSError!jsc.JSValue {
switch (FFIObject.getPtrSlice(globalThis, value, byteOffset, lengthValue)) {
.err => |err| {
return err;
},
.slice => |slice| {
return bun.String.createUTF8ForJS(globalThis, slice);
},
}
}
pub const dom_call = DOMCall("FFI", @This(), "ptr", DOMEffect.forRead(.TypedArrayProperties));
pub fn toJS(globalObject: *jsc.JSGlobalObject) jsc.JSValue {
const object = jsc.JSValue.createEmptyObject(globalObject, comptime std.meta.fieldNames(@TypeOf(fields)).len + 2);
inline for (comptime std.meta.fieldNames(@TypeOf(fields))) |field| {
object.put(
globalObject,
comptime ZigString.static(field),
jsc.createCallback(globalObject, comptime ZigString.static(field), 1, comptime @field(fields, field)),
);
}
dom_call.put(globalObject, object);
object.put(globalObject, ZigString.static("read"), Reader.toJS(globalObject));
return object;
}
pub const Reader = struct {
pub const dom_calls = .{
.u8 = DOMCall("Reader", @This(), "u8", DOMEffect.forRead(.World)),
.u16 = DOMCall("Reader", @This(), "u16", DOMEffect.forRead(.World)),
.u32 = DOMCall("Reader", @This(), "u32", DOMEffect.forRead(.World)),
.ptr = DOMCall("Reader", @This(), "ptr", DOMEffect.forRead(.World)),
.i8 = DOMCall("Reader", @This(), "i8", DOMEffect.forRead(.World)),
.i16 = DOMCall("Reader", @This(), "i16", DOMEffect.forRead(.World)),
.i32 = DOMCall("Reader", @This(), "i32", DOMEffect.forRead(.World)),
.i64 = DOMCall("Reader", @This(), "i64", DOMEffect.forRead(.World)),
.u64 = DOMCall("Reader", @This(), "u64", DOMEffect.forRead(.World)),
.intptr = DOMCall("Reader", @This(), "intptr", DOMEffect.forRead(.World)),
.f32 = DOMCall("Reader", @This(), "f32", DOMEffect.forRead(.World)),
.f64 = DOMCall("Reader", @This(), "f64", DOMEffect.forRead(.World)),
};
pub fn toJS(globalThis: *jsc.JSGlobalObject) jsc.JSValue {
const obj = jsc.JSValue.createEmptyObject(globalThis, std.meta.fieldNames(@TypeOf(Reader.dom_calls)).len);
inline for (comptime std.meta.fieldNames(@TypeOf(Reader.dom_calls))) |field| {
@field(Reader.dom_calls, field).put(globalThis, obj);
}
return obj;
}
pub fn @"u8"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) u8, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"u16"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) u16, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"u32"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) u32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn ptr(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) u64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"i8"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) i8, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"i16"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) i16, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"i32"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) i32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn intptr(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) i64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"f32"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) f32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"f64"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) f64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn @"i64"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) i64, @ptrFromInt(addr)).*;
return JSValue.fromInt64NoTruncate(globalObject, value);
}
pub fn @"u64"(
globalObject: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) bun.JSError!JSValue {
if (arguments.len == 0 or !arguments[0].isNumber()) {
return globalObject.throwInvalidArguments("Expected a pointer", .{});
}
const addr = arguments[0].asPtrAddress() + if (arguments.len > 1) @as(usize, @intCast(arguments[1].to(i32))) else @as(usize, 0);
const value = @as(*align(1) u64, @ptrFromInt(addr)).*;
return JSValue.fromUInt64NoTruncate(globalObject, value);
}
pub fn u8WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) u8, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn u16WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) u16, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn u32WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) u32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn ptrWithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) u64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn i8WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) i8, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn i16WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) i16, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn i32WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) i32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn intptrWithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) i64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn f32WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) f32, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn f64WithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) f64, @ptrFromInt(addr)).*;
return JSValue.jsNumber(value);
}
pub fn u64WithoutTypeChecks(
global: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) u64, @ptrFromInt(addr)).*;
return JSValue.fromUInt64NoTruncate(global, value);
}
pub fn i64WithoutTypeChecks(
global: *JSGlobalObject,
_: *anyopaque,
raw_addr: i64,
offset: i32,
) callconv(jsc.conv) JSValue {
const addr = @as(usize, @intCast(raw_addr)) + @as(usize, @intCast(offset));
const value = @as(*align(1) i64, @ptrFromInt(addr)).*;
return JSValue.fromInt64NoTruncate(global, value);
}
};
pub fn ptr(
globalThis: *JSGlobalObject,
_: JSValue,
arguments: []const JSValue,
) JSValue {
return switch (arguments.len) {
0 => ptr_(globalThis, JSValue.zero, null),
1 => ptr_(globalThis, arguments[0], null),
else => ptr_(globalThis, arguments[0], arguments[1]),
};
}
pub fn ptrWithoutTypeChecks(
_: *JSGlobalObject,
_: *anyopaque,
array: *jsc.JSUint8Array,
) callconv(jsc.conv) JSValue {
return JSValue.fromPtrAddress(@intFromPtr(array.ptr()));
}
fn ptr_(
globalThis: *JSGlobalObject,
value: JSValue,
byteOffset: ?JSValue,
) JSValue {
if (value == .zero) {
return jsc.JSValue.jsNull();
}
const array_buffer = value.asArrayBuffer(globalThis) orelse {
return globalThis.toInvalidArguments("Expected ArrayBufferView but received {s}", .{@tagName(value.jsType())});
};
if (array_buffer.len == 0) {
return globalThis.toInvalidArguments("ArrayBufferView must have a length > 0. A pointer to empty memory doesn't work", .{});
}
var addr: usize = @intFromPtr(array_buffer.ptr);
// const Sizes = @import("../bindings/sizes.zig");
// assert(addr == @intFromPtr(value.asEncoded().ptr) + Sizes.Bun_FFI_PointerOffsetToTypedArrayVector);
if (byteOffset) |off| {
if (!off.isEmptyOrUndefinedOrNull()) {
if (!off.isNumber()) {
return globalThis.toInvalidArguments("Expected number for byteOffset", .{});
}
}
const bytei64 = off.toInt64();
if (bytei64 < 0) {
addr -|= @as(usize, @intCast(bytei64 * -1));
} else {
addr += @as(usize, @intCast(bytei64));
}
if (addr > @intFromPtr(array_buffer.ptr) + @as(usize, array_buffer.byte_len)) {
return globalThis.toInvalidArguments("byteOffset out of bounds", .{});
}
}
if (addr > max_addressable_memory) {
return globalThis.toInvalidArguments("Pointer is outside max addressible memory, which usually means a bug in your program.", .{});
}
if (addr == 0) {
return globalThis.toInvalidArguments("Pointer must not be 0", .{});
}
if (addr == 0xDEADBEEF or addr == 0xaaaaaaaa or addr == 0xAAAAAAAA) {
return globalThis.toInvalidArguments("ptr to invalid memory, that would segfault Bun :(", .{});
}
if (comptime Environment.allow_assert) {
assert(jsc.JSValue.fromPtrAddress(addr).asPtrAddress() == addr);
}
return jsc.JSValue.fromPtrAddress(addr);
}
const ValueOrError = union(enum) {
err: JSValue,
slice: []u8,
};
pub fn getPtrSlice(globalThis: *JSGlobalObject, value: JSValue, byteOffset: ?JSValue, byteLength: ?JSValue) ValueOrError {
if (!value.isNumber() or value.asNumber() < 0 or value.asNumber() > std.math.maxInt(usize)) {
return .{ .err = globalThis.toInvalidArguments("ptr must be a number.", .{}) };
}
const num = value.asPtrAddress();
if (num == 0) {
return .{ .err = globalThis.toInvalidArguments("ptr cannot be zero, that would segfault Bun :(", .{}) };
}
// if (!std.math.isFinite(num)) {
// return .{ .err = globalThis.toInvalidArguments("ptr must be a finite number.", .{}) };
// }
var addr = @as(usize, @bitCast(num));
if (byteOffset) |byte_off| {
if (byte_off.isNumber()) {
const off = byte_off.toInt64();
if (off < 0) {
addr -|= @as(usize, @intCast(off * -1));
} else {
addr +|= @as(usize, @intCast(off));
}
if (addr == 0) {
return .{ .err = globalThis.toInvalidArguments("ptr cannot be zero, that would segfault Bun :(", .{}) };
}
if (!std.math.isFinite(byte_off.asNumber())) {
return .{ .err = globalThis.toInvalidArguments("ptr must be a finite number.", .{}) };
}
} else if (!byte_off.isEmptyOrUndefinedOrNull()) {
// do nothing
} else {
return .{ .err = globalThis.toInvalidArguments("Expected number for byteOffset", .{}) };
}
}
if (addr == 0xDEADBEEF or addr == 0xaaaaaaaa or addr == 0xAAAAAAAA) {
return .{ .err = globalThis.toInvalidArguments("ptr to invalid memory, that would segfault Bun :(", .{}) };
}
if (byteLength) |valueLength| {
if (!valueLength.isEmptyOrUndefinedOrNull()) {
if (!valueLength.isNumber()) {
return .{ .err = globalThis.toInvalidArguments("length must be a number.", .{}) };
}
if (valueLength.asNumber() == 0.0) {
return .{ .err = globalThis.toInvalidArguments("length must be > 0. This usually means a bug in your code.", .{}) };
}
const length_i = valueLength.toInt64();
if (length_i < 0) {
return .{ .err = globalThis.toInvalidArguments("length must be > 0. This usually means a bug in your code.", .{}) };
}
if (length_i > max_addressable_memory) {
return .{ .err = globalThis.toInvalidArguments("length exceeds max addressable memory. This usually means a bug in your code.", .{}) };
}
const length = @as(usize, @intCast(length_i));
return .{ .slice = @as([*]u8, @ptrFromInt(addr))[0..length] };
}
}
return .{ .slice = bun.span(@as([*:0]u8, @ptrFromInt(addr))) };
}
fn getCPtr(value: JSValue) ?usize {
// pointer to C function
if (value.isNumber()) {
const addr = value.asPtrAddress();
if (addr > 0) return addr;
} else if (value.isBigInt()) {
const addr = @as(u64, @bitCast(value.toUInt64NoTruncate()));
if (addr > 0) {
return addr;
}
}
return null;
}
pub fn toArrayBuffer(
globalThis: *JSGlobalObject,
value: JSValue,
byteOffset: ?JSValue,
valueLength: ?JSValue,
finalizationCtxOrPtr: ?JSValue,
finalizationCallback: ?JSValue,
) bun.JSError!jsc.JSValue {
switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) {
.err => |erro| {
return erro;
},
.slice => |slice| {
var callback: jsc.C.JSTypedArrayBytesDeallocator = null;
var ctx: ?*anyopaque = null;
if (finalizationCallback) |callback_value| {
if (getCPtr(callback_value)) |callback_ptr| {
callback = @as(jsc.C.JSTypedArrayBytesDeallocator, @ptrFromInt(callback_ptr));
if (finalizationCtxOrPtr) |ctx_value| {
if (getCPtr(ctx_value)) |ctx_ptr| {
ctx = @as(*anyopaque, @ptrFromInt(ctx_ptr));
} else if (!ctx_value.isUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected user data to be a C pointer (number or BigInt)", .{});
}
}
} else if (!callback_value.isEmptyOrUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected callback to be a C pointer (number or BigInt)", .{});
}
} else if (finalizationCtxOrPtr) |callback_value| {
if (getCPtr(callback_value)) |callback_ptr| {
callback = @as(jsc.C.JSTypedArrayBytesDeallocator, @ptrFromInt(callback_ptr));
} else if (!callback_value.isEmptyOrUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected callback to be a C pointer (number or BigInt)", .{});
}
}
return jsc.ArrayBuffer.fromBytes(slice, jsc.JSValue.JSType.ArrayBuffer).toJSWithContext(globalThis, ctx, callback);
},
}
}
pub fn toBuffer(
globalThis: *JSGlobalObject,
value: JSValue,
byteOffset: ?JSValue,
valueLength: ?JSValue,
finalizationCtxOrPtr: ?JSValue,
finalizationCallback: ?JSValue,
) bun.JSError!jsc.JSValue {
switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) {
.err => |err| {
return err;
},
.slice => |slice| {
var callback: jsc.C.JSTypedArrayBytesDeallocator = null;
var ctx: ?*anyopaque = null;
if (finalizationCallback) |callback_value| {
if (getCPtr(callback_value)) |callback_ptr| {
callback = @as(jsc.C.JSTypedArrayBytesDeallocator, @ptrFromInt(callback_ptr));
if (finalizationCtxOrPtr) |ctx_value| {
if (getCPtr(ctx_value)) |ctx_ptr| {
ctx = @as(*anyopaque, @ptrFromInt(ctx_ptr));
} else if (!ctx_value.isEmptyOrUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected user data to be a C pointer (number or BigInt)", .{});
}
}
} else if (!callback_value.isEmptyOrUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected callback to be a C pointer (number or BigInt)", .{});
}
} else if (finalizationCtxOrPtr) |callback_value| {
if (getCPtr(callback_value)) |callback_ptr| {
callback = @as(jsc.C.JSTypedArrayBytesDeallocator, @ptrFromInt(callback_ptr));
} else if (!callback_value.isEmptyOrUndefinedOrNull()) {
return globalThis.toInvalidArguments("Expected callback to be a C pointer (number or BigInt)", .{});
}
}
if (callback != null or ctx != null) {
return jsc.JSValue.createBufferWithCtx(globalThis, slice, ctx, callback);
}
return jsc.JSValue.createBuffer(globalThis, slice);
},
}
}
pub fn toCStringBuffer(
globalThis: *JSGlobalObject,
value: JSValue,
byteOffset: ?JSValue,
valueLength: ?JSValue,
) jsc.JSValue {
switch (getPtrSlice(globalThis, value, byteOffset, valueLength)) {
.err => |err| {
return err;
},
.slice => |slice| {
return jsc.JSValue.createBuffer(globalThis, slice, null);
},
}
}
pub fn getter(
globalObject: *jsc.JSGlobalObject,
_: *jsc.JSObject,
) jsc.JSValue {
return FFIObject.toJS(globalObject);
}
const fields = .{
.viewSource = jsc.host_fn.wrapStaticMethod(bun.api.FFI, "print", false),
.dlopen = jsc.host_fn.wrapStaticMethod(bun.api.FFI, "open", false),
.callback = jsc.host_fn.wrapStaticMethod(bun.api.FFI, "callback", false),
.linkSymbols = jsc.host_fn.wrapStaticMethod(bun.api.FFI, "linkSymbols", false),
.toBuffer = jsc.host_fn.wrapStaticMethod(@This(), "toBuffer", false),
.toArrayBuffer = jsc.host_fn.wrapStaticMethod(@This(), "toArrayBuffer", false),
.closeCallback = jsc.host_fn.wrapStaticMethod(bun.api.FFI, "closeCallback", false),
.CString = jsc.host_fn.wrapStaticMethod(bun.api.FFIObject, "newCString", false),
};
const max_addressable_memory = std.math.maxInt(u56);
const std = @import("std");
const bun = @import("bun");
const Environment = bun.Environment;
const assert = bun.assert;
const jsc = bun.jsc;
const JSGlobalObject = jsc.JSGlobalObject;
const JSObject = jsc.JSObject;
const JSValue = jsc.JSValue;
const ZigString = jsc.ZigString;
const Bun = jsc.API.Bun;
const DOMCall = jsc.host_fn.DOMCall;
const DOMEffect = jsc.host_fn.DOMEffect;