diff options
Diffstat (limited to 'src/bun.js/bindings/header-gen.zig')
-rw-r--r-- | src/bun.js/bindings/header-gen.zig | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/src/bun.js/bindings/header-gen.zig b/src/bun.js/bindings/header-gen.zig new file mode 100644 index 000000000..c11241233 --- /dev/null +++ b/src/bun.js/bindings/header-gen.zig @@ -0,0 +1,1035 @@ +const std = @import("std"); +const Dir = std.fs.Dir; +const FnMeta = std.builtin.TypeInfo.Fn; +const FnDecl = std.builtin.TypeInfo.Declaration.Data.FnDecl; +const StructMeta = std.builtin.TypeInfo.Struct; +const EnumMeta = std.builtin.TypeInfo.Enum; +const UnionMeta = std.builtin.TypeInfo.Union; +const warn = std.debug.warn; +const StaticExport = @import("./static_export.zig"); + +const TypeNameMap = std.StringHashMap([]const u8); + +fn isCppObject(comptime Type: type) bool { + return switch (@typeInfo(Type)) { + .Struct, .Union, .Opaque => true, + .Enum => @hasDecl(Type, "Type"), + else => false, + }; +} + +const ENABLE_REWRITE_RETURN = false; + +pub fn cTypeLabel(comptime Type: type) ?[]const u8 { + return switch (comptime Type) { + *StaticExport.c_char => "char*", + [*c]u8, *const StaticExport.c_char => "const char*", + StaticExport.c_char => "char", + *void => "void", + bool => "bool", + usize => "size_t", + isize => "int", + u8 => "unsigned char", + u16 => "uint16_t", + u32 => "uint32_t", + u64 => "uint64_t", + i8 => "int8_t", + i16 => "int16_t", + i24 => "int24_t", + i32 => "int32_t", + i64 => "int64_t", + f64 => "double", + f32 => "float", + *anyopaque => "void*", + *const anyopaque => "const void*", + [*]bool => "bool*", + [*]usize => "size_t*", + [*]isize => "int*", + [*]u8 => "unsigned char*", + [*]u16 => "uint16_t*", + [*]u32 => "uint32_t*", + [*]u64 => "uint64_t*", + [*]i8 => "int8_t*", + [*]i16 => "int16_t*", + [*]i32 => "int32_t*", + [*]i64 => "int64_t*", + [*]const bool => "const bool*", + [*]const usize => "const size_t*", + [*]const isize => "const int*", + [*c]const u8, [*]const u8 => "const unsigned char*", + [*]const u16 => "const uint16_t*", + [*]const u32 => "const uint32_t*", + [*]const u64 => "const uint64_t*", + [*]const i8 => "const int8_t*", + [*]const i16 => "const int16_t*", + [*]const i32 => "const int32_t*", + [*]const i64 => "const int64_t*", + else => null, + }; +} + +var buffer = std.ArrayList(u8).init(std.heap.c_allocator); +var writer = buffer.writer(); +var impl_buffer = std.ArrayList(u8).init(std.heap.c_allocator); +var impl_writer = impl_buffer.writer(); +var bufset = std.BufSet.init(std.heap.c_allocator); +var type_names = TypeNameMap.init(std.heap.c_allocator); +var opaque_types = std.BufSet.init(std.heap.c_allocator); +var size_map = std.StringHashMap(u32).init(std.heap.c_allocator); +var align_map = std.StringHashMap(u29).init(std.heap.c_allocator); + +pub const C_Generator = struct { + filebase: []const u8, + + direction: Direction = .export_cpp, + const Self = @This(); + + pub const Direction = enum { + export_cpp, + export_zig, + }; + + pub fn init(comptime src_file: []const u8, comptime Writer: type, _: Writer) Self { + var res = Self{ .filebase = src_file }; + + return res; + } + + pub fn deinit(_: *const Self) void { + // self.file.writeAll("\n/**** </") catch unreachable; + // self.file.writeAll(self.filebase) catch unreachable; + // self.file.writeAll("> ****/\n\n") catch unreachable; + } + + pub fn gen_func( + self: *Self, + comptime name: []const u8, + comptime func: anytype, + comptime meta: FnMeta, + comptime _: []const []const u8, + comptime rewrite_return: bool, + ) void { + switch (comptime meta.calling_convention) { + .Naked => self.write("__attribute__((naked)) "), + .Stdcall => self.write("__attribute__((stdcall)) "), + .Fastcall => self.write("__attribute__((fastcall)) "), + .Thiscall => self.write("__attribute__((thiscall)) "), + else => {}, + } + + switch (self.direction) { + .export_cpp => self.write("CPP_DECL "), + .export_zig => self.write("ZIG_DECL "), + } + + const return_type: type = comptime if (@TypeOf(func.return_type) == ?type) + (func.return_type orelse void) + else + func.return_type; + + if (comptime rewrite_return) { + self.writeType(void); + } else { + self.writeType(comptime return_type); + } + + self.write(" " ++ name ++ "("); + + if (comptime rewrite_return) { + self.writeType(comptime return_type); + self.write("_buf ret_value"); + + if (comptime meta.args.len > 0) { + self.write(", "); + } + } + + inline for (meta.args) |arg, i| { + const ArgType = comptime arg.arg_type.?; + + switch (@typeInfo(ArgType)) { + .Fn => { + self.gen_closure(comptime arg.arg_type.?, comptime std.fmt.comptimePrint(" ArgFn{d}", .{i})); + }, + else => { + self.writeType(comptime arg.arg_type.?); + + switch (@typeInfo(ArgType)) { + .Enum => { + self.write(comptime std.fmt.comptimePrint(" {s}{d}", .{ @typeName(ArgType), i })); + }, + + else => { + self.write(comptime std.fmt.comptimePrint(" arg{d}", .{i})); + }, + } + }, + } + + // if (comptime func.arg_names.len > 0 and func.arg_names.len > i) { + // self.write(comptime arg_names[i]); + // } else { + + //TODO: Figure out how to get arg names; for now just do arg0..argN + if (i != meta.args.len - 1) + self.write(", "); + } + + self.write(")"); + defer self.write(";\n"); + // const ReturnTypeInfo: std.builtin.TypeInfo = comptime @typeInfo(func.return_type); + // switch (comptime ReturnTypeInfo) { + // .Pointer => |Pointer| { + // self.write(" __attribute__((returns_nonnull))"); + // }, + // .Optional => |Optional| {}, + // else => {}, + // } + } + + pub fn gen_closure( + self: *Self, + comptime Function: type, + comptime name: []const u8, + ) void { + const func: std.builtin.TypeInfo.Fn = @typeInfo(Function).Fn; + self.writeType(func.return_type orelse void); + self.write(" (*" ++ name ++ ")("); + inline for (func.args) |arg, i| { + self.writeType(arg.arg_type.?); + // if (comptime func.arg_names.len > 0 and func.arg_names.len > i) { + // self.write(comptime arg_names[i]); + // } else { + const ArgType = arg.arg_type.?; + if (@typeInfo(ArgType) == .Enum) { + self.write(comptime std.fmt.comptimePrint(" {s}{d}", .{ @typeName(ArgType), i })); + } else { + self.write(comptime std.fmt.comptimePrint(" arg{d}", .{i})); + } + // } + + //TODO: Figure out how to get arg names; for now just do arg0..argN + if (i != func.args.len - 1) + self.write(", "); + } + + self.write(")"); + // const ReturnTypeInfo: std.builtin.TypeInfo = comptime @typeInfo(func.return_type); + // switch (comptime ReturnTypeInfo) { + // .Pointer => |Pointer| { + // self.write(" __attribute__((returns_nonnull))"); + // }, + // .Optional => |Optional| {}, + // else => {}, + // } + } + + pub fn gen_struct( + self: *Self, + comptime name: []const u8, + comptime meta: StructMeta, + comptime static_types: anytype, + ) void { + self.write("typedef struct "); + + if (meta.layout == .Packed) + self.write("__attribute__((__packed__)) "); + + self.write(name ++ " {\n"); + + inline for (meta.fields) |field| { + self.write(" "); + + const info = @typeInfo(field.field_type); + + if (info == .Array) { + const PrintType = comptime brk: { + for (static_types) |static_type| { + if (static_type.Type == info.Array.child) { + break :brk static_type.Type; + } + } + + break :brk info.Array.child; + }; + self.writeType(PrintType); + } else { + const PrintType = comptime brk: { + for (static_types) |static_type| { + if (static_type.Type == field.field_type) { + break :brk static_type.Type; + } + } + + break :brk field.field_type; + }; + self.writeType(PrintType); + } + + self.write(" " ++ field.name); + + if (info == .Array) { + writer.print("[{}]", .{info.Array.len}) catch unreachable; + } + + self.write(";\n"); + } + self.write("} " ++ name ++ ";\n\n"); + } + + pub fn gen_enum( + self: *Self, + comptime name: []const u8, + comptime meta: EnumMeta, + ) void { + self.write("enum " ++ name ++ " {\n"); + + comptime var last = 0; + inline for (meta.fields) |field, i| { + self.write(" " ++ field.name); + + // if field value is unexpected/custom, manually define it + if ((i == 0 and field.value != 0) or (i > 0 and field.value > last + 1)) { + writer.print(" = {}", .{field.value}) catch unreachable; + } + + self.write(",\n"); + + last = field.value; + } + + self.write("};\n\n"); + } + + pub fn gen_union( + _: *Self, + comptime _: []const u8, + comptime _: UnionMeta, + comptime _: anytype, + ) void { + @compileError("Not implemented"); + // self.write("typedef union "); + + // self.write(name ++ " {\n"); + + // inline for (meta.fields) |field, i| { + // self.write(" "); + + // self.writeType(comptime FieldType); + + // self.write(" " ++ field.name ++ ";\n"); + // } + // self.write("} " ++ name ++ ";\n\n"); + } + + fn writeType( + self: *Self, + comptime T: type, + ) void { + const TT = comptime brk: { + var Type = T; + if (@typeInfo(Type) == .Optional) { + const OtherType = std.meta.Child(Type); + if (@typeInfo(OtherType) == .Fn) { + Type = OtherType; + } + } + if (@typeInfo(Type) == .Pointer and !std.meta.trait.isManyItemPtr(Type)) { + Type = @typeInfo(Type).Pointer.child; + } + + break :brk Type; + }; + + if (comptime (isCppObject(TT)) and @hasDecl(TT, "name")) { + if (@typeInfo(T) == .Pointer or (@hasDecl(TT, "Type") and (@TypeOf(TT.Type) == type and @typeInfo(TT.Type) == .Pointer))) { + if (@hasDecl(TT, "is_pointer") and !TT.is_pointer) {} else if (@typeInfo(T).Pointer.is_const) { + write(self, "const "); + } + } + + const _formatted_name = comptime brk: { + var original: [TT.name.len]u8 = undefined; + _ = std.mem.replace(u8, TT.name, ":", "_", &original); + break :brk original; + }; + const formatted_name = comptime std.mem.span(&_formatted_name); + + if (@hasDecl(TT, "is_pointer") and !TT.is_pointer) { + if (@TypeOf(TT.Type) == type) { + if (cTypeLabel(TT.Type)) |label| { + type_names.put(comptime label, formatted_name) catch unreachable; + if (@typeInfo(TT) == .Struct and @hasField(TT, "bytes")) { + size_map.put(comptime formatted_name, @as(u32, TT.shim.byte_size)) catch unreachable; + align_map.put(comptime formatted_name, @as(u29, TT.shim.align_size)) catch unreachable; + } else if (@typeInfo(TT) == .Opaque) { + opaque_types.insert(comptime label) catch unreachable; + } + } + } else { + type_names.put(comptime TT.name, formatted_name) catch unreachable; + if (@typeInfo(TT) == .Struct and @hasField(TT, "bytes")) { + size_map.put(comptime formatted_name, @as(u32, TT.shim.byte_size)) catch unreachable; + align_map.put(comptime formatted_name, @as(u29, TT.shim.align_size)) catch unreachable; + } else if (@typeInfo(TT) == .Opaque) { + opaque_types.insert(comptime TT.name) catch unreachable; + } + } + } else { + type_names.put(comptime TT.name, formatted_name) catch unreachable; + if (@typeInfo(TT) == .Struct and @hasField(TT, "bytes")) { + size_map.put(comptime formatted_name, @as(u32, TT.shim.byte_size)) catch unreachable; + align_map.put(comptime formatted_name, @as(u29, TT.shim.align_size)) catch unreachable; + } else if (@typeInfo(TT) == .Opaque) { + opaque_types.insert(comptime TT.name) catch unreachable; + } + } + + if (TT == T and @hasField(T, "bytes")) { + write(self, comptime "b" ++ formatted_name); + } else { + write(self, comptime formatted_name); + } + + if (@typeInfo(T) == .Pointer or (@hasDecl(TT, "Type") and (@TypeOf(TT.Type) == type and @typeInfo(TT.Type) == .Pointer))) { + if (@hasDecl(TT, "is_pointer") and !TT.is_pointer) {} else { + write(self, "*"); + } + } + return; + } + + if (comptime cTypeLabel(T)) |label| { + self.write(comptime label); + } else { + const meta = @typeInfo(T); + switch (meta) { + .Pointer => |Pointer| { + const child = Pointer.child; + // if (childmeta == .Struct and childmeta.Struct.layout != .Extern) { + // self.write("void"); + // } else { + self.writeType(child); + // } + self.write("*"); + }, + .Optional => self.writeType(meta.Optional.child), + .Array => @compileError("Handle goofy looking C Arrays in the calling function"), + .Enum => |Enum| { + self.writeType(Enum.tag_type); + }, + else => { + return self.write(@typeName(T)); + }, + } + } + } + + fn write(_: *Self, comptime str: []const u8) void { + _ = writer.write(str) catch {}; + } +}; + +const builtin = @import("builtin"); +const TypeInfo = builtin.TypeInfo; +const Declaration = TypeInfo.Declaration; + +const GeneratorInterface = struct { + fn init() void {} + fn deinit() void {} + fn gen_func() void {} + fn gen_struct() void {} + fn gen_enum() void {} + fn gen_union() void {} +}; + +fn validateGenerator(comptime Generator: type) void { + comptime { + const interface = @typeInfo(GeneratorInterface).Struct.decls; + + for (interface) |decl| { + if (@hasDecl(Generator, decl.name) == false) { + @compileError("Generator: '" ++ + @typeName(Generator) ++ + "' is missing function: " ++ + decl.name); + } + } + } +} + +const NamedStruct = struct { + name: []const u8, + Type: type, +}; + +pub fn getCStruct(comptime T: type) ?NamedStruct { + if (!std.meta.trait.isContainer(T) or (std.meta.trait.isSingleItemPtr(T) and !std.meta.trait.isContainer(std.meta.Child(T)))) { + return null; + } + + inline for (std.meta.declarations(T)) |decl| { + if (std.mem.eql(u8, decl.name, "Type")) { + switch (decl.data) { + .Type => { + return NamedStruct{ .Type = T, .name = @typeName(T) }; + }, + else => {}, + } + } + } + + return null; +} + +var thenables = std.ArrayListUnmanaged([]const u8){}; +pub fn HeaderGen(comptime first_import: type, comptime second_import: type, comptime fname: []const u8) type { + return struct { + source_file: []const u8 = fname, + gen: C_Generator = undefined, + + const Self = @This(); + + pub fn init() Self { + return Self{}; + } + + pub fn startFile( + comptime _: Self, + comptime Type: type, + comptime _: []const u8, + file: anytype, + other: std.fs.File, + ) void { + if (comptime std.meta.trait.hasDecls(Type, .{"include"})) { + comptime var new_name = std.mem.zeroes([Type.include.len]u8); + + comptime { + _ = std.mem.replace(u8, Type.include, "/", "_", std.mem.span(&new_name)); + _ = std.mem.replace(u8, &new_name, ".", "_", std.mem.span(&new_name)); + _ = std.mem.replace(u8, &new_name, "<", "_", std.mem.span(&new_name)); + _ = std.mem.replace(u8, &new_name, ">", "_", std.mem.span(&new_name)); + _ = std.mem.replace(u8, &new_name, "\"", "_", std.mem.span(&new_name)); + } + file.writeAll("\n#pragma mark - " ++ Type.name ++ "\n\n") catch unreachable; + + if (@hasDecl(Type, "include")) { + other.writer().print( + \\ + \\#ifndef INCLUDED_{s} + \\#define INCLUDED_{s} + \\#include "{s}" + \\#endif + \\ + \\extern "C" const size_t {s} = sizeof({s}); + \\extern "C" const size_t {s} = alignof({s}); + \\ + , + .{ new_name, new_name, Type.include, Type.shim.size_of_symbol, Type.name, Type.shim.align_of_symbol, Type.name }, + ) catch unreachable; + } + } + } + + pub fn processStaticExport(comptime _: Self, _: anytype, gen: *C_Generator, comptime static_export: StaticExport) void { + const fn_meta = comptime @typeInfo(static_export.Type).Fn; + const DeclData = @typeInfo(@TypeOf(@field(static_export.Parent, static_export.local_name))); + + gen.gen_func( + comptime static_export.symbol_name, + DeclData.Fn, + comptime fn_meta, + comptime std.mem.zeroes([]const []const u8), + false, + ); + } + + pub fn processThenable(comptime _: Self, _: anytype, comptime static_export: StaticExport) void { + thenables.append(std.heap.c_allocator, comptime static_export.symbol_name) catch unreachable; + } + + pub fn processDecl( + comptime _: Self, + _: anytype, + gen: *C_Generator, + comptime ParentType: type, + comptime _: std.builtin.TypeInfo.Declaration, + comptime name: []const u8, + comptime prefix: []const u8, + ) void { + const DeclData = @typeInfo(@TypeOf(@field(ParentType, name))); + + switch (comptime DeclData) { + .Type => |Type| { + switch (@typeInfo(Type)) { + .Enum => |Enum| { + gen.gen_enum( + prefix ++ "__" ++ name, + Enum, + ); + }, + + .Fn => |func| { + // if (func.) { + // blocked by https://github.com/ziglang/zig/issues/8259 + gen.gen_func( + comptime prefix ++ "__" ++ name, + comptime func, + comptime func, + comptime &.{}, + false, // comptime ENABLE_REWRITE_RETURN and @typeInfo(fn_meta.return_type) == .Struct, + ); + }, + else => {}, + } + }, + .Fn => |func| { + // if (func.) { + // blocked by https://github.com/ziglang/zig/issues/8259 + gen.gen_func( + comptime prefix ++ "__" ++ name, + comptime func, + comptime func, + comptime &.{}, + comptime ENABLE_REWRITE_RETURN and @typeInfo(func.return_type) == .Struct, + ); + }, + else => {}, + } + } + + pub fn exec(comptime self: Self, file: std.fs.File, impl: std.fs.File, lazy_functions_header: std.fs.File, lazy_functions_impl: std.fs.File) void { + const Generator = C_Generator; + validateGenerator(Generator); + var file_writer = file.writer(); + file_writer.print("// clang-format off\n//-- AUTOGENERATED FILE -- {d}\n", .{std.time.timestamp()}) catch unreachable; + file.writeAll( + \\#pragma once + \\ + \\#include <stddef.h> + \\#include <stdint.h> + \\#include <stdbool.h> + \\ + \\#ifdef __cplusplus + \\ #define AUTO_EXTERN_C extern "C" + \\ #define AUTO_EXTERN_C_ZIG extern "C" __attribute__((weak)) + \\#else + \\ #define AUTO_EXTERN_C + \\ #define AUTO_EXTERN_C_ZIG __attribute__((weak)) + \\#endif + \\#define ZIG_DECL AUTO_EXTERN_C_ZIG + \\#define CPP_DECL AUTO_EXTERN_C + \\#define CPP_SIZE AUTO_EXTERN_C + \\ + \\#ifndef __cplusplus + \\typedef void* JSClassRef; + \\#endif + \\ + \\#ifdef __cplusplus + \\#include "root.h" + \\#include "JavaScriptCore/JSClassRef.h" + \\#endif + \\#include "headers-handwritten.h" + \\ + ) catch {}; + + impl.writer().print("//-- AUTOGENERATED FILE -- {d}\n// clang-format off\n", .{std.time.timestamp()}) catch unreachable; + impl.writer().writeAll( + \\#pragma once + \\ + \\#include <stddef.h> + \\#include <stdint.h> + \\#include <stdbool.h> + \\ + \\#include "root.h" + \\ + ) catch {}; + + var impl_second_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var impl_second_writer = impl_second_buffer.writer(); + + var impl_third_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var impl_third_writer = impl_third_buffer.writer(); + + var impl_fourth_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var impl_fourth_writer = impl_fourth_buffer.writer(); + + var lazy_functions_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var lazy_functions_buffer_writer = lazy_functions_buffer.writer(); + + var lazy_function_definitions_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var lazy_function_definitions_writer = lazy_function_definitions_buffer.writer(); + + var lazy_function_visitor_buffer = std.ArrayList(u8).init(std.heap.c_allocator); + var lazy_function_visitor_writer = lazy_function_visitor_buffer.writer(); + + // inline for (import.all_static_externs) |static_extern, i| { + // const Type = static_extern.Type; + // var gen = C_Generator.init(static_extern.name, @TypeOf(writer), writer); + // defer gen.deinit(); + + // switch (@typeInfo(Type)) { + // .Enum => |Enum| { + // gen.gen_enum( + // static_extern.name, + // Enum, + // ); + // }, + // .Union => |Union| { + // gen.gen_union(static_extern.name, Union, import.all_static_externs); + // }, + // .Struct => |Struct| { + // gen.gen_struct(static_extern.name, Struct, import.all_static_externs); + // }, + // else => {}, + // } + // } + + var to_get_sizes: usize = 0; + + const exclude_from_cpp = comptime [_][]const u8{ "ZigString", "ZigException" }; + const TypesToCheck = [_]type{ first_import, second_import }; + inline for (TypesToCheck) |BaseType| { + const all_decls = comptime std.meta.declarations(BaseType); + inline for (all_decls) |_decls| { + if (comptime _decls.is_pub) { + @setEvalBranchQuota(99999); + const Type = @field(BaseType, _decls.name); + if (@TypeOf(Type) == type) { + const TypeTypeInfo: std.builtin.TypeInfo = @typeInfo(@field(BaseType, _decls.name)); + const is_container_type = switch (TypeTypeInfo) { + .Opaque, .Struct, .Enum => true, + else => false, + }; + + if (is_container_type and (@hasDecl(Type, "Extern") or @hasDecl(Type, "Export") or @hasDecl(Type, "lazy_static_functions"))) { + const identifier = comptime std.fmt.comptimePrint("{s}_{s}", .{ Type.shim.name, Type.shim.namespace }); + if (!bufset.contains(identifier)) { + self.startFile( + Type, + Type.shim.name, + writer, + impl, + ); + + bufset.insert(identifier) catch unreachable; + + var gen = C_Generator.init(Type.name, @TypeOf(writer), writer); + defer gen.deinit(); + + if (@hasDecl(Type, "Extern")) { + if (comptime !(std.mem.eql(u8, Type.name, exclude_from_cpp[0]) or std.mem.eql(u8, Type.name, exclude_from_cpp[1]))) { + if (to_get_sizes > 0) { + impl_second_writer.writeAll(", ") catch unreachable; + impl_third_writer.writeAll(", ") catch unreachable; + impl_fourth_writer.writeAll(", ") catch unreachable; + } + } + + const formatted_name = comptime brk: { + var original: [Type.name.len]u8 = undefined; + _ = std.mem.replace(u8, Type.name, ":", "_", &original); + break :brk original; + }; + + if (comptime !(std.mem.eql(u8, Type.name, exclude_from_cpp[0]) or std.mem.eql(u8, Type.name, exclude_from_cpp[1]))) { + impl_third_writer.print("sizeof({s})", .{comptime Type.name}) catch unreachable; + impl_fourth_writer.print("alignof({s})", .{comptime Type.name}) catch unreachable; + impl_second_writer.print("\"{s}\"", .{formatted_name}) catch unreachable; + to_get_sizes += 1; + } + const ExternList = comptime brk: { + const Sorder = struct { + pub fn lessThan(_: @This(), lhs: []const u8, rhs: []const u8) bool { + return std.ascii.orderIgnoreCase(lhs, rhs) == std.math.Order.lt; + } + }; + var extern_list = Type.Extern; + std.sort.sort([]const u8, &extern_list, Sorder{}, Sorder.lessThan); + break :brk extern_list; + }; + // impl_writer.print(" #include {s}\n", .{Type.include}) catch unreachable; + inline for (&ExternList) |extern_decl| { + if (@hasDecl(Type, extern_decl)) { + const normalized_name = comptime brk: { + var _normalized_name: [Type.name.len]u8 = undefined; + _ = std.mem.replace(u8, Type.name, ":", "_", std.mem.span(&_normalized_name)); + break :brk _normalized_name; + }; + + processDecl( + self, + writer, + &gen, + Type, + comptime std.meta.declarationInfo(Type, extern_decl), + comptime extern_decl, + comptime std.mem.span(&normalized_name), + ); + } + } + } + + if (@hasDecl(Type, "Export")) { + const ExportLIst = comptime brk: { + const Sorder = struct { + pub fn lessThan(_: @This(), comptime lhs: StaticExport, comptime rhs: StaticExport) bool { + return std.ascii.orderIgnoreCase(lhs.symbol_name, rhs.symbol_name) == std.math.Order.lt; + } + }; + var extern_list = Type.Export; + std.sort.sort(StaticExport, &extern_list, Sorder{}, Sorder.lessThan); + break :brk extern_list; + }; + + gen.direction = C_Generator.Direction.export_zig; + if (ExportLIst.len > 0) { + gen.write("\n#ifdef __cplusplus\n\n"); + inline for (ExportLIst) |static_export| { + processStaticExport( + self, + file, + &gen, + comptime static_export, + ); + } + gen.write("\n#endif\n"); + } + } + + if (@hasDecl(Type, "lazy_static_functions")) { + const ExportLIst = comptime brk: { + const Sorder = struct { + pub fn lessThan(_: @This(), comptime lhs: StaticExport, comptime rhs: StaticExport) bool { + return std.ascii.orderIgnoreCase(lhs.symbol_name, rhs.symbol_name) == std.math.Order.lt; + } + }; + var extern_list = Type.lazy_static_functions; + std.sort.sort(StaticExport, &extern_list, Sorder{}, Sorder.lessThan); + break :brk extern_list; + }; + + gen.direction = C_Generator.Direction.export_zig; + if (ExportLIst.len > 0) { + lazy_function_definitions_writer.writeAll("\n#pragma mark ") catch unreachable; + lazy_function_definitions_writer.writeAll(Type.shim.name) catch unreachable; + lazy_function_definitions_writer.writeAll("\n\n") catch unreachable; + + inline for (ExportLIst) |static_export| { + const exp: StaticExport = static_export; + lazy_function_definitions_writer.print(" JSC::LazyProperty<Zig::GlobalObject, Zig::JSFFIFunction> m_{s};", .{exp.symbol_name}) catch unreachable; + lazy_function_definitions_writer.writeAll("\n") catch unreachable; + + lazy_function_definitions_writer.print( + " Zig::JSFFIFunction* get__{s}(Zig::GlobalObject *globalObject) {{ return m_{s}.getInitializedOnMainThread(globalObject); }}", + .{ exp.symbol_name, exp.symbol_name }, + ) catch unreachable; + lazy_function_definitions_writer.writeAll("\n") catch unreachable; + + const impl_format = + \\ + \\ m_{s}.initLater( + \\ [](const JSC::LazyProperty<Zig::GlobalObject, Zig::JSFFIFunction>::Initializer& init) {{ + \\ WTF::String functionName = WTF::String("{s}"_s); + \\ Zig::JSFFIFunction* function = Zig::JSFFIFunction::create( + \\ init.vm, + \\ init.owner, + \\ 1, + \\ functionName, + \\ {s}, + \\ JSC::NoIntrinsic, + \\ {s} + \\ ); + \\ init.set(function); + \\ }}); + \\ + ; + + lazy_functions_buffer_writer.print( + impl_format, + .{ + exp.symbol_name, + exp.local_name, + exp.symbol_name, + exp.symbol_name, + }, + ) catch unreachable; + + lazy_function_visitor_writer.print( + \\ this->m_{s}.visit(visitor); + \\ + , + .{exp.symbol_name}, + ) catch unreachable; + } + gen.write("\n"); + } + } + } + } + } + } + } + } + + impl.writer().print("\nconst size_t sizes[{d}] = {{", .{to_get_sizes}) catch unreachable; + impl.writeAll(impl_third_buffer.items) catch unreachable; + impl.writeAll("};\n") catch unreachable; + impl.writer().print("\nconst char* names[{d}] = {{", .{to_get_sizes}) catch unreachable; + impl.writeAll(impl_second_buffer.items) catch unreachable; + impl.writeAll("};\n") catch unreachable; + impl.writer().print("\nconst size_t aligns[{d}] = {{", .{to_get_sizes}) catch unreachable; + impl.writeAll(impl_fourth_buffer.items) catch unreachable; + impl.writeAll("};\n") catch unreachable; + var iter = type_names.iterator(); + + lazy_functions_header.writer().print( + \\// GENERATED FILE + \\#pragma once + \\#include "root.h" + \\ + \\namespace Zig {{ + \\ class GlobalObject; + \\ class JSFFIFunction; + \\ + \\ class LazyStaticFunctions {{ + \\ public: + \\ + \\ void init(Zig::GlobalObject* globalObject); + \\ + \\ template<typename Visitor> + \\ void visit(Visitor& visitor); + \\ + \\ + \\ /* -- BEGIN FUNCTION DEFINITIONS -- */ + \\ {s} + \\ /* -- END FUNCTION DEFINITIONS-- */ + \\ }}; + \\ + \\}} // namespace Zig + \\ + , .{lazy_function_definitions_buffer.items}) catch unreachable; + + lazy_functions_impl.writer().print( + \\// GENERATED FILE + \\#pragma once + \\ + \\namespace Zig {{ + \\ + \\ template<typename Visitor> + \\ void LazyStaticFunctions::visit(Visitor& visitor) {{ + \\ {s} + \\ }} + \\ + \\ void LazyStaticFunctions::init(Zig::GlobalObject *globalObject) {{ + \\ {s} + \\ }} + \\ + \\}} // namespace Zig + \\ + , .{ + lazy_function_visitor_buffer.items, + lazy_functions_buffer.items, + }) catch unreachable; + + const NamespaceMap = std.StringArrayHashMap(std.BufMap); + var namespaces = NamespaceMap.init(std.heap.c_allocator); + + var size_iter = size_map.iterator(); + while (size_iter.next()) |size| { + file_writer.print(" typedef struct b{s} {{ unsigned char bytes[{d}]; }} b{s};\n", .{ + size.key_ptr.*, + // align_map.get(size.key_ptr.*).?, + size.value_ptr.*, + size.key_ptr.*, + }) catch unreachable; + + file_writer.print(" typedef char* b{s}_buf;\n", .{ + size.key_ptr.*, + }) catch unreachable; + } + + file_writer.writeAll("\n#ifndef __cplusplus\n") catch unreachable; + while (iter.next()) |entry| { + const key = entry.key_ptr.*; + const value = entry.value_ptr.*; + if (std.mem.indexOfScalar(u8, entry.key_ptr.*, ':')) |namespace_start| { + const namespace = entry.key_ptr.*[0..namespace_start]; + + if (opaque_types.contains(entry.key_ptr.*)) { + file_writer.print(" typedef struct {s} {s}; // {s}\n", .{ + value, + value, + key, + }) catch unreachable; + } else { + file_writer.print(" typedef b{s} {s}; // {s}\n", .{ + value, + value, + key, + }) catch unreachable; + } + + if (!namespaces.contains(namespace)) { + namespaces.put(namespace, std.BufMap.init(std.heap.c_allocator)) catch unreachable; + } + const class = key[namespace_start + 2 ..]; + namespaces.getPtr(namespace).?.put(class, value) catch unreachable; + } else { + file_writer.print(" typedef {s} {s};\n", .{ + key, + value, + }) catch unreachable; + + impl_writer.print(" typedef {s} {s};\n", .{ + key, + value, + }) catch unreachable; + } + } + + file_writer.writeAll("\n#endif\n") catch unreachable; + + file_writer.writeAll("\n#ifdef __cplusplus\n") catch unreachable; + + iter = type_names.iterator(); + var namespace_iter = namespaces.iterator(); + while (namespace_iter.next()) |map| { + file_writer.print(" namespace {s} {{\n", .{map.key_ptr.*}) catch unreachable; + var classes = map.value_ptr.iterator(); + while (classes.next()) |class| { + file_writer.print(" class {s};\n", .{class.key_ptr.*}) catch unreachable; + } + file_writer.writeAll(" }\n") catch unreachable; + } + + file_writer.writeAll("\n") catch unreachable; + + file_writer.writeAll(impl_buffer.items) catch unreachable; + + iter = type_names.iterator(); + namespace_iter = namespaces.iterator(); + while (namespace_iter.next()) |map| { + var classes = map.value_ptr.iterator(); + while (classes.next()) |class| { + file_writer.print(" using {s} = {s}::{s};\n", .{ + class.value_ptr.*, + map.key_ptr.*, + class.key_ptr.*, + }) catch unreachable; + } + } + + file_writer.writeAll("\n#endif\n\n") catch unreachable; + + file.writeAll(buffer.items) catch unreachable; + + // processDecls( + // self, + // file, + // import, + // "Bindings", + // ); + } + }; +} |