aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/javascript/jsc/api/FFI.h107
-rw-r--r--src/javascript/jsc/api/bun.zig6
-rw-r--r--src/javascript/jsc/api/ffi.zig797
-rw-r--r--src/javascript/jsc/api/libtcc1.a.macos-aarch64bin0 -> 29988 bytes
-rw-r--r--src/javascript/jsc/base.zig2
-rw-r--r--src/javascript/jsc/bindings/bindings.zig12
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
new file mode 100644
index 000000000..60696b611
--- /dev/null
+++ b/src/javascript/jsc/api/libtcc1.a.macos-aarch64
Binary files differ
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);
+ }
+}