diff options
-rw-r--r-- | src/javascript/jsc/api/FFI.h | 107 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 6 | ||||
-rw-r--r-- | src/javascript/jsc/api/ffi.zig | 797 | ||||
-rw-r--r-- | src/javascript/jsc/api/libtcc1.a.macos-aarch64 | bin | 0 -> 29988 bytes | |||
-rw-r--r-- | src/javascript/jsc/base.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 12 |
6 files changed, 924 insertions, 0 deletions
diff --git a/src/javascript/jsc/api/FFI.h b/src/javascript/jsc/api/FFI.h new file mode 100644 index 000000000..ffed86138 --- /dev/null +++ b/src/javascript/jsc/api/FFI.h @@ -0,0 +1,107 @@ +// This is an auto-generated file +// +// 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 + + +#define IS_BIG_ENDIAN 0 +#define USE_JSVALUE64 1 +#define USE_JSVALUE32_64 0 + +/* 7.18.1.1 Exact-width integer types */ +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned uint32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef int64_t intptr_t; +typedef uint64_t uintptr_t; +typedef uintptr_t size_t; + +#define true 1 +#define false 0 +#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 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 }; + + +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)) + +#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)) + + + + +typedef void* JSContext; +typedef EncodedJSValue* JSException; + + +// typedef void* (^ArrayBufferLikeGetPtrFunction)(JSContext, EncodedJSValue); +// static ArrayBufferLikeGetPtrFunction JSArrayBufferGetPtr = (ArrayBufferLikeGetPtrFunction)MEMORY_ADDRESS_FOR_GET_ARRAY_BUFFER_FUNCTION; +// (*JSObjectCallAsFunctionCallback) (JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception); + +// This is an example of a function which does the bare minimum +void* Bun__CallbackFunctionPlaceholder(JSContext ctx, EncodedJSValue function, EncodedJSValue thisObject, size_t argumentCount, const EncodedJSValue arguments[], JSException exception); +void* Bun__CallbackFunctionPlaceholder(JSContext ctx, EncodedJSValue function, EncodedJSValue thisObject, size_t argumentCount, const EncodedJSValue arguments[], JSException exception) { + return (void*)123; +} diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index de85d6c8f..ff6e63724 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -1121,6 +1121,12 @@ pub const Class = NewClass( .sha = .{ .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false), }, + .dlprint = .{ + .rfn = JSC.wrapWithHasContainer(JSC.FFI, "print", false, false), + }, + .dlopen = .{ + .rfn = JSC.wrapWithHasContainer(JSC.FFI, "open", false, false), + }, }, .{ .main = .{ diff --git a/src/javascript/jsc/api/ffi.zig b/src/javascript/jsc/api/ffi.zig new file mode 100644 index 000000000..a6191beb0 --- /dev/null +++ b/src/javascript/jsc/api/ffi.zig @@ -0,0 +1,797 @@ +const Bun = @This(); +const default_allocator = @import("../../../global.zig").default_allocator; +const bun = @import("../../../global.zig"); +const Environment = bun.Environment; +const NetworkThread = @import("http").NetworkThread; +const Global = bun.Global; +const strings = bun.strings; +const string = bun.string; +const Output = @import("../../../global.zig").Output; +const MutableString = @import("../../../global.zig").MutableString; +const std = @import("std"); +const Allocator = std.mem.Allocator; +const IdentityContext = @import("../../../identity_context.zig").IdentityContext; +const Fs = @import("../../../fs.zig"); +const Resolver = @import("../../../resolver/resolver.zig"); +const ast = @import("../../../import_record.zig"); +const NodeModuleBundle = @import("../../../node_module_bundle.zig").NodeModuleBundle; +const MacroEntryPoint = @import("../../../bundler.zig").MacroEntryPoint; +const logger = @import("../../../logger.zig"); +const Api = @import("../../../api/schema.zig").Api; +const options = @import("../../../options.zig"); +const Bundler = @import("../../../bundler.zig").Bundler; +const ServerEntryPoint = @import("../../../bundler.zig").ServerEntryPoint; +const js_printer = @import("../../../js_printer.zig"); +const js_parser = @import("../../../js_parser.zig"); +const js_ast = @import("../../../js_ast.zig"); +const hash_map = @import("../../../hash_map.zig"); +const http = @import("../../../http.zig"); +const NodeFallbackModules = @import("../../../node_fallbacks.zig"); +const ImportKind = ast.ImportKind; +const Analytics = @import("../../../analytics/analytics_thread.zig"); +const ZigString = @import("../../../jsc.zig").ZigString; +const Runtime = @import("../../../runtime.zig"); +const Router = @import("./router.zig"); +const ImportRecord = ast.ImportRecord; +const DotEnv = @import("../../../env_loader.zig"); +const ParseResult = @import("../../../bundler.zig").ParseResult; +const PackageJSON = @import("../../../resolver/package_json.zig").PackageJSON; +const MacroRemap = @import("../../../resolver/package_json.zig").MacroMap; +const WebCore = @import("../../../jsc.zig").WebCore; +const Request = WebCore.Request; +const Response = WebCore.Response; +const Headers = WebCore.Headers; +const Fetch = WebCore.Fetch; +const FetchEvent = WebCore.FetchEvent; +const js = @import("../../../jsc.zig").C; +const JSC = @import("../../../jsc.zig"); +const JSError = @import("../base.zig").JSError; +const d = @import("../base.zig").d; +const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; +const getAllocator = @import("../base.zig").getAllocator; +const JSValue = @import("../../../jsc.zig").JSValue; +const NewClass = @import("../base.zig").NewClass; +const Microtask = @import("../../../jsc.zig").Microtask; +const JSGlobalObject = @import("../../../jsc.zig").JSGlobalObject; +const ExceptionValueRef = @import("../../../jsc.zig").ExceptionValueRef; +const JSPrivateDataPtr = @import("../../../jsc.zig").JSPrivateDataPtr; +const ZigConsoleClient = @import("../../../jsc.zig").ZigConsoleClient; +const Node = @import("../../../jsc.zig").Node; +const ZigException = @import("../../../jsc.zig").ZigException; +const ZigStackTrace = @import("../../../jsc.zig").ZigStackTrace; +const ErrorableResolvedSource = @import("../../../jsc.zig").ErrorableResolvedSource; +const ResolvedSource = @import("../../../jsc.zig").ResolvedSource; +const JSPromise = @import("../../../jsc.zig").JSPromise; +const JSInternalPromise = @import("../../../jsc.zig").JSInternalPromise; +const JSModuleLoader = @import("../../../jsc.zig").JSModuleLoader; +const JSPromiseRejectionOperation = @import("../../../jsc.zig").JSPromiseRejectionOperation; +const Exception = @import("../../../jsc.zig").Exception; +const ErrorableZigString = @import("../../../jsc.zig").ErrorableZigString; +const ZigGlobalObject = @import("../../../jsc.zig").ZigGlobalObject; +const VM = @import("../../../jsc.zig").VM; +const JSFunction = @import("../../../jsc.zig").JSFunction; +const Config = @import("../config.zig"); +const URL = @import("../../../url.zig").URL; +const Transpiler = @import("./transpiler.zig"); +const VirtualMachine = @import("../javascript.zig").VirtualMachine; +const IOTask = JSC.IOTask; +const ComptimeStringMap = @import("../../../comptime_string_map.zig").ComptimeStringMap; + +const TCC = @import("../../../../tcc.zig"); + +pub const FFI = struct { + dylib: std.DynLib, + functions: std.StringArrayHashMapUnmanaged(Function) = .{}, + closed: bool = false, + + pub const Class = JSC.NewClass( + FFI, + .{ .name = "class" }, + .{ .call = JSC.wrapWithHasContainer(FFI, "close", false, true) }, + .{}, + ); + + pub fn close(this: *FFI) JSValue { + if (this.closed) { + return JSC.JSValue.jsUndefined(); + } + this.closed = true; + this.dylib.close(); + + for (this.functions.values()) |*val| { + VirtualMachine.vm.allocator.free(bun.constStrToU8(std.mem.span(val.base_name))); + + val.arg_types.deinit(VirtualMachine.vm.allocator); + } + this.functions.deinit(VirtualMachine.vm.allocator); + + return JSC.JSValue.jsUndefined(); + } + + pub fn print(global: *JSGlobalObject, object: JSC.JSValue) JSValue { + const allocator = VirtualMachine.vm.allocator; + + if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { + return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); + } + + var symbols = std.StringArrayHashMapUnmanaged(Function){}; + if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { + // an error while validating symbols + for (symbols.keys()) |key| { + allocator.free(bun.constStrToU8(key)); + } + symbols.clearAndFree(allocator); + return val; + } + + var zig_strings = allocator.alloc(ZigString, symbols.count()) catch unreachable; + for (symbols.values()) |*function, i| { + var arraylist = std.ArrayList(u8).init(allocator); + var writer = arraylist.writer(); + function.printSourceCode(&writer) catch { + // an error while generating source code + for (symbols.keys()) |key| { + allocator.free(bun.constStrToU8(key)); + } + for (zig_strings) |zig_string| { + allocator.free(bun.constStrToU8(zig_string.slice())); + } + for (symbols.values()) |*function_| { + function_.arg_types.deinit(allocator); + } + symbols.clearAndFree(allocator); + allocator.free(zig_strings); + return ZigString.init("Error while printing code").toErrorInstance(global); + }; + zig_strings[i] = ZigString.init(arraylist.toOwnedSlice()); + } + + const ret = JSC.JSValue.createStringArray(global, zig_strings.ptr, zig_strings.len, true); + + for (symbols.keys()) |key| { + allocator.free(bun.constStrToU8(key)); + } + for (zig_strings) |zig_string| { + allocator.free(bun.constStrToU8(zig_string.slice())); + } + for (symbols.values()) |*function_| { + function_.arg_types.deinit(allocator); + if (function_.step == .compiled) { + allocator.free(function_.step.compiled.buf); + } + } + symbols.clearAndFree(allocator); + + return ret; + } + + // pub fn dlcompile(global: *JSGlobalObject, object: JSC.JSValue) JSValue { + // const allocator = VirtualMachine.vm.allocator; + + // if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { + // return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); + // } + + // var symbols = std.StringArrayHashMapUnmanaged(Function){}; + // if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { + // // an error while validating symbols + // for (symbols.keys()) |key| { + // allocator.free(bun.constStrToU8(key)); + // } + // symbols.clearAndFree(allocator); + // return val; + // } + + // } + + pub fn open(global: *JSGlobalObject, name_str: ZigString, object: JSC.JSValue) JSC.JSValue { + const allocator = VirtualMachine.vm.allocator; + var name_slice = name_str.toSlice(allocator); + defer name_slice.deinit(); + + if (name_slice.len == 0) { + return JSC.toInvalidArguments("Invalid library name", .{}, global.ref()); + } + + if (object.isEmptyOrUndefinedOrNull() or !object.isObject()) { + return JSC.toInvalidArguments("Expected an options object with symbol names", .{}, global.ref()); + } + + const name = name_slice.sliceZ(); + var symbols = std.StringArrayHashMapUnmanaged(Function){}; + if (generateSymbols(global, &symbols, object) catch JSC.JSValue.zero) |val| { + // an error while validating symbols + for (symbols.keys()) |key| { + allocator.free(bun.constStrToU8(key)); + } + symbols.clearAndFree(allocator); + return val; + } + if (symbols.count() == 0) { + return JSC.toInvalidArguments("Expected at least one symbol", .{}, global.ref()); + } + + var dylib = std.DynLib.open(name) catch { + return JSC.toInvalidArguments("Failed to open library", .{}, global.ref()); + }; + + var obj = JSC.JSValue.c(JSC.C.JSObjectMake(global.ref(), null, null)); + JSC.C.JSValueProtect(global.ref(), obj.asObjectRef()); + defer JSC.C.JSValueUnprotect(global.ref(), obj.asObjectRef()); + for (symbols.values()) |*function| { + var resolved_symbol = dylib.lookup(*anyopaque, function.base_name) orelse { + const ret = JSC.toInvalidArguments("Symbol \"{s}\" not found in \"{s}\"", .{ std.mem.span(function.base_name), name_slice.slice() }, global.ref()); + for (symbols.values()) |*value| { + allocator.free(bun.constStrToU8(std.mem.span(value.base_name))); + value.arg_types.clearAndFree(allocator); + } + symbols.clearAndFree(allocator); + dylib.close(); + return ret; + }; + + function.symbol_from_dynamic_library = resolved_symbol; + function.compile(allocator) catch { + const ret = JSC.toInvalidArguments("Failed to compile symbol \"{s}\" in \"{s}\"", .{ std.mem.span(function.base_name), name_slice.slice() }, global.ref()); + for (symbols.values()) |*value| { + allocator.free(bun.constStrToU8(std.mem.span(value.base_name))); + value.arg_types.clearAndFree(allocator); + } + symbols.clearAndFree(allocator); + dylib.close(); + return ret; + }; + switch (function.step) { + .failed => |err| { + for (symbols.values()) |*value| { + allocator.free(bun.constStrToU8(std.mem.span(value.base_name))); + value.arg_types.clearAndFree(allocator); + } + symbols.clearAndFree(allocator); + dylib.close(); + return ZigString.init(err).toErrorInstance(global); + }, + .pending => { + for (symbols.values()) |*value| { + allocator.free(bun.constStrToU8(std.mem.span(value.base_name))); + value.arg_types.clearAndFree(allocator); + } + symbols.clearAndFree(allocator); + dylib.close(); + return ZigString.init("Failed to compile (nothing happend!)").toErrorInstance(global); + }, + .compiled => |compiled| { + var callback = JSC.C.JSObjectMakeFunctionWithCallback(global.ref(), null, @ptrCast(JSC.C.JSObjectCallAsFunctionCallback, compiled.ptr)); + + obj.put(global, &ZigString.init(std.mem.span(function.base_name)), JSC.JSValue.cast(callback)); + }, + } + } + + var lib = allocator.create(FFI) catch unreachable; + lib.* = .{ + .dylib = dylib, + .functions = symbols, + }; + + var close_object = JSC.JSValue.c(Class.make(global.ref(), lib)); + + return JSC.JSValue.createObject2(global, &ZigString.init("close"), &ZigString.init("symbols"), close_object, obj); + } + pub fn generateSymbols(global: *JSGlobalObject, symbols: *std.StringArrayHashMapUnmanaged(Function), object: JSC.JSValue) !?JSValue { + const allocator = VirtualMachine.vm.allocator; + + var keys = JSC.C.JSObjectCopyPropertyNames(global.ref(), object.asObjectRef()); + defer JSC.C.JSPropertyNameArrayRelease(keys); + const count = JSC.C.JSPropertyNameArrayGetCount(keys); + + try symbols.ensureTotalCapacity(allocator, count); + + var i: usize = 0; + while (i < count) : (i += 1) { + var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(keys, i); + defer JSC.C.JSStringRelease(property_name_ref); + const len = JSC.C.JSStringGetLength(property_name_ref); + if (len == 0) continue; + var prop = JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..len]; + + var value = JSC.JSValue.c(JSC.C.JSObjectGetProperty(global.ref(), object.asObjectRef(), property_name_ref, null)); + if (value.isEmptyOrUndefinedOrNull()) { + return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Expected an object for key \"{s}\"", .{prop}, global.ref()); + } + + var abi_types = std.ArrayListUnmanaged(ABIType){}; + + if (value.get(global, "params")) |params| { + if (params.isEmptyOrUndefinedOrNull() or !params.jsType().isArray()) { + return ZigString.init("Expected an object with \"params\" as an array").toErrorInstance(global); + } + + var array = params.arrayIterator(global); + + try abi_types.ensureTotalCapacityPrecise(allocator, array.len); + while (array.next()) |val| { + if (val.isEmptyOrUndefinedOrNull() or !val.jsType().isStringLike()) { + abi_types.clearAndFree(allocator); + return ZigString.init("param must be a string (type name)").toErrorInstance(global); + } + + var type_name = val.toSlice(global, allocator); + defer type_name.deinit(); + abi_types.appendAssumeCapacity(ABIType.label.get(type_name.slice()) orelse { + abi_types.clearAndFree(allocator); + return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown type {s}", .{type_name.slice()}, global.ref()); + }); + } + } + // var function + var return_type = ABIType{ .primitive = .@"void" }; + + if (value.get(global, "return_type")) |ret_value| { + var ret_slice = ret_value.toSlice(global, allocator); + defer ret_slice.deinit(); + return_type = ABIType.label.get(ret_slice.slice()) orelse { + abi_types.clearAndFree(allocator); + return JSC.toTypeError(JSC.Node.ErrorCode.ERR_INVALID_ARG_VALUE, "Unknown return type {s}", .{ret_slice.slice()}, global.ref()); + }; + } + + const function = Function{ + .base_name = try allocator.dupeZ(u8, prop), + .arg_types = abi_types, + .return_type = return_type, + }; + symbols.putAssumeCapacity(std.mem.span(function.base_name), function); + } + + return null; + } + + pub const Function = struct { + symbol_from_dynamic_library: ?*anyopaque = null, + base_name: [:0]const u8 = "", + + return_type: ABIType, + arg_types: std.ArrayListUnmanaged(ABIType) = .{}, + step: Step = Step{ .pending = {} }, + + pub const Step = union(enum) { + pending: void, + compiled: struct { + ptr: *anyopaque, + buf: []u8, + }, + failed: []const u8, + }; + + const FFI_HEADER: string = @embedFile("./FFI.h"); + pub inline fn ffiHeader() string { + if (comptime Environment.isDebug) { + var dirpath = std.fs.path.dirname(@src().file).?; + var env = std.process.getEnvMap(default_allocator) catch unreachable; + + const dir = std.mem.replaceOwned( + u8, + default_allocator, + dirpath, + "jarred", + env.get("USER").?, + ) catch unreachable; + var runtime_path = std.fs.path.join(default_allocator, &[_]string{ dir, "FFI.h" }) catch unreachable; + const file = std.fs.openFileAbsolute(runtime_path, .{}) catch @panic("Missing bun/src/javascript/jsc/api/FFI.h."); + defer file.close(); + return file.readToEndAlloc(default_allocator, (file.stat() catch unreachable).size) catch unreachable; + } else { + return FFI_HEADER; + } + } + + pub fn handleTCCError(ctx: ?*anyopaque, message: [*c]const u8) callconv(.C) void { + var this = bun.cast(*Function, ctx.?); + this.step = .{ .failed = std.mem.span(message) }; + } + + extern fn pthread_jit_write_protect_np(enable: bool) callconv(.C) void; + + pub fn compile( + this: *Function, + allocator: std.mem.Allocator, + ) !void { + var source_code = std.ArrayList(u8).init(allocator); + var source_code_writer = source_code.writer(); + try this.printSourceCode(&source_code_writer); + try source_code.append(0); + defer source_code.deinit(); + var state = TCC.tcc_new() orelse return error.TCCMissing; + TCC.tcc_set_error_func(state, this, handleTCCError); + // defer TCC.tcc_delete(state); + _ = TCC.tcc_set_output_type(state, TCC.TCC_OUTPUT_MEMORY); + + const compilation_result = TCC.tcc_compile_string( + state, + source_code.items.ptr, + ); + // did tcc report an error? + if (this.step == .failed) { + return; + } + + // did tcc report failure but never called the error callback? + if (compilation_result == -1) { + this.step = .{ .failed = "tcc returned -1, which means it failed" }; + return; + } + + _ = TCC.tcc_add_symbol(state, this.base_name, this.symbol_from_dynamic_library.?); + + // i don't fully understand this, why it needs two calls + // but that is the API + var relocation_size = TCC.tcc_relocate(state, null); + if (relocation_size > 0) { + var bytes: []u8 = try allocator.rawAlloc(@intCast(usize, relocation_size), 16, 16, 0); + if (comptime Environment.isAarch64 and Environment.isMac) { + pthread_jit_write_protect_np(false); + } + _ = TCC.tcc_relocate(state, bytes.ptr); + if (comptime Environment.isAarch64 and Environment.isMac) { + pthread_jit_write_protect_np(true); + } + if (this.step == .failed) { + allocator.free(bytes); + return; + } + + var formatted_symbol_name = try std.fmt.allocPrintZ(allocator, "bun_gen_{s}", .{std.mem.span(this.base_name)}); + defer allocator.free(formatted_symbol_name); + var symbol = TCC.tcc_get_symbol(state, formatted_symbol_name) orelse { + this.step = .{ .failed = "missing generated symbol in source code" }; + allocator.free(bytes); + + return; + }; + if (this.step == .failed) { + allocator.free(bytes); + return; + } + + this.step = .{ + .compiled = .{ + .ptr = symbol, + .buf = bytes, + }, + }; + return; + } + } + + pub fn printSourceCode( + this: *Function, + writer: anytype, + ) !void { + if (comptime Environment.isRelease) { + try writer.writeAll(std.mem.span(FFI_HEADER)); + } else { + try writer.writeAll(ffiHeader()); + } + + // -- Generate the FFI function symbol + try writer.writeAll("/* --- The Function To Call */\n"); + try this.return_type.typename(writer); + try writer.writeAll(" "); + try writer.writeAll(std.mem.span(this.base_name)); + try writer.writeAll("("); + var first = true; + for (this.arg_types.items) |arg, i| { + if (!first) { + try writer.writeAll(", "); + } + first = false; + try arg.typename(writer); + try writer.print(" arg{d}", .{i}); + } + try writer.writeAll(");\n\n"); + + // -- Generate JavaScriptCore's C wrapper function + try writer.writeAll("/* ---- Your Wrapper Function ---- */\nvoid* bun_gen_"); + try writer.writeAll(std.mem.span(this.base_name)); + try writer.writeAll("(JSContext ctx, EncodedJSValue function, EncodedJSValue thisObject, size_t argumentCount, const EncodedJSValue arguments[], void* exception);\n\n"); + + try writer.writeAll("void* bun_gen_"); + try writer.writeAll(std.mem.span(this.base_name)); + try writer.writeAll("(JSContext ctx, EncodedJSValue function, EncodedJSValue thisObject, size_t argumentCount, const EncodedJSValue arguments[], void* exception) {\n\n"); + var arg_buf: [512]u8 = undefined; + arg_buf[0.."arguments[".len].* = "arguments[".*; + for (this.arg_types.items) |arg, i| { + try writer.writeAll(" "); + try arg.typename(writer); + var printed = std.fmt.bufPrintIntToSlice(arg_buf["arguments[".len..], i, 10, .lower, .{}); + arg_buf["arguments[".len + printed.len] = ']'; + try writer.print(" arg{d} = {};\n", .{ i, arg.toC(arg_buf[0 .. printed.len + "arguments[]".len]) }); + } + + try writer.writeAll(" "); + if (!(this.return_type == .primitive and this.return_type.primitive == .void)) { + try this.return_type.typename(writer); + try writer.writeAll(" return_value = "); + } + try writer.print("{s}(", .{std.mem.span(this.base_name)}); + first = true; + for (this.arg_types.items) |_, i| { + if (!first) { + try writer.writeAll(", "); + } + first = false; + try writer.print("arg{d}", .{i}); + } + try writer.writeAll(");\n\n"); + + try writer.writeAll(" "); + + try writer.writeAll("return "); + + if (!(this.return_type == .primitive and this.return_type.primitive == .void)) { + try writer.print("{}.asPtr", .{this.return_type.toJS("return_value")}); + } else { + try writer.writeAll("ValueUndefined.asPtr"); + } + + try writer.writeAll(";\n}\n\n"); + } + }; + + pub const ABIType = union(enum) { + primitive: Primitive.Tag, + pointer: Pointer, + + pub const label = ComptimeStringMap( + ABIType, + .{ + .{ "char", ABIType{ .primitive = Primitive.Tag.char } }, + .{ "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" } } }, + .{ "const char*", ABIType{ .pointer = .{ .is_const = true, .primitive = Primitive.Tag.char } } }, + .{ "const void*", ABIType{ .pointer = .{ .is_const = true, .primitive = Primitive.Tag.@"void" } } }, + }, + ); + + const ToJSFormatter = struct { + symbol: []const u8, + abi: ABIType, + + pub fn format(self: ToJSFormatter, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void { + switch (self.abi) { + .pointer => |ptr| { + _ = ptr; + }, + .primitive => |prim| { + try prim.toJS(self.symbol).format(comptime fmt, opts, writer); + }, + } + } + }; + + const ToCFormatter = 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, + ); + } + }; + + pub fn toJS( + this: ABIType, + symbol: string, + ) ToJSFormatter { + return ToJSFormatter{ + .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); + }, + } + } + }; + + 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, + + 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_IS_TRUE({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, + } + } + }; + + 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}); + }, + .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 => "double", + .char => "int8_t", + else => unreachable, + }; + } + }; + }; +}; diff --git a/src/javascript/jsc/api/libtcc1.a.macos-aarch64 b/src/javascript/jsc/api/libtcc1.a.macos-aarch64 Binary files differnew file mode 100644 index 000000000..60696b611 --- /dev/null +++ b/src/javascript/jsc/api/libtcc1.a.macos-aarch64 diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 3db5c0110..7abbe1343 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -2568,6 +2568,7 @@ const SHA384 = JSC.API.Bun.Crypto.SHA384; const SHA256 = JSC.API.Bun.Crypto.SHA256; const SHA512_256 = JSC.API.Bun.Crypto.SHA512_256; const MD5_SHA1 = JSC.API.Bun.Crypto.MD5_SHA1; +const FFI = JSC.FFI; pub const JSPrivateDataPtr = TaggedPointerUnion(.{ AttributeIterator, @@ -2614,6 +2615,7 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{ TextEncoder, TimeoutTask, Transpiler, + FFI, }); pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type { diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 2643abfed..840f9025e 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -141,6 +141,10 @@ pub const ZigString = extern struct { return this.ptr[0..this.len]; } + pub fn sliceZ(this: Slice) [:0]const u8 { + return std.meta.assumeSentinel(this.ptr[0..this.len], 0); + } + pub fn mut(this: Slice) []u8 { return @intToPtr([*]u8, @ptrToInt(this.ptr))[0..this.len]; } @@ -3484,3 +3488,11 @@ pub const WTF = struct { pub const Callback = struct { // zig: Value, }; + +const NodeBuffer = @import("../node/buffer.zig"); + +comptime { + if (!JSC.is_bindgen) { + std.testing.refAllDecls(NodeBuffer.Write); + } +} |