const std = @import("std"); const StaticExport = @import("./static_export.zig"); const Sizes = @import("./sizes.zig"); pub const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false; const headers = @import("./headers.zig"); fn isNullableType(comptime Type: type) bool { return @typeInfo(Type) == .Optional or (@typeInfo(Type) == .Pointer and @typeInfo(Type).Pointer.is_allowzero); } const log = @import("../../output.zig").scoped(.CPP, true); pub fn Shimmer(comptime _namespace: []const u8, comptime _name: []const u8, comptime Parent: type) type { return struct { pub const namespace = _namespace; pub const name = _name; pub fn ref() void { if (comptime @hasDecl(Parent, "Export")) { inline for (Parent.Export) |exp| { _ = exp; } } if (comptime @hasDecl(Parent, "Extern")) { inline for (Parent.Extern) |exp| { _ = @field(Parent, exp); } } } // fn toCppType(comptime FromType: type) type { // var NewReturnType = FromType; // if (NewReturnType == anyopaque) { // return FromType; // } // var ReturnTypeInfo: std.builtin.Type = @typeInfo(FromType); // if (ReturnTypeInfo == .Pointer and NewReturnType != *anyopaque) { // NewReturnType = ReturnTypeInfo.Pointer.child; // ReturnTypeInfo = @typeInfo(NewReturnType); // } // switch (ReturnTypeInfo) { // .Union, // .Struct, // .Enum, // => { // if (@hasDecl(ReturnTypeInfo., "Type")) { // return NewReturnType; // } // }, // else => {}, // } // return FromType; // } pub const align_of_symbol = std.fmt.comptimePrint("{s}__{s}_object_align_", .{ namespace, name }); pub const size_of_symbol = std.fmt.comptimePrint("{s}__{s}_object_size_", .{ namespace, name }); const align_symbol = std.fmt.comptimePrint("{s}__{s}_align", .{ namespace, name }); pub const byte_size = brk: { const identifier = std.fmt.comptimePrint("{s}__{s}", .{ namespace, name }); if (@hasDecl(Sizes, identifier)) { break :brk @field(Sizes, identifier); } else { break :brk 0; } }; pub const align_size = brk: { const identifier = std.fmt.comptimePrint("{s}__{s}_align", .{ namespace, name }); if (@hasDecl(Sizes, identifier)) { break :brk @field(Sizes, identifier); } else { break :brk 0; } }; pub const Bytes = if (byte_size > 16) [byte_size]u8 else std.meta.Int(.unsigned, byte_size * 8); pub const Return = struct { pub const Type = Parent; pub const is_return = true; }; fn pointerChild(comptime Type: type) type { if (@typeInfo(Type) == .Pointer) { return @typeInfo(Type).Pointer.child_type; } return Type; } pub fn symbolName(comptime typeName: []const u8) []const u8 { if (comptime namespace.len > 0) { return comptime std.fmt.comptimePrint("{s}__{s}__{s}", .{ namespace, name, typeName }); } else { return comptime std.fmt.comptimePrint("{s}__{s}", .{ name, typeName }); } } pub fn exportFunctions(comptime Functions: anytype) [std.meta.fieldNames(@TypeOf(Functions)).len]StaticExport { const FunctionsType = @TypeOf(Functions); return comptime brk: { var functions: [std.meta.fieldNames(FunctionsType).len]StaticExport = undefined; for (std.meta.fieldNames(FunctionsType), 0..) |fn_name, i| { const Function = @TypeOf(@field(Functions, fn_name)); if (@typeInfo(Function) != .Fn) { @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to be a function but received " ++ @tagName(@typeInfo(Function))); } var Fn: std.builtin.Type.Fn = @typeInfo(Function).Fn; if (Fn.calling_convention != .C) { @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to have a C Calling Convention."); } const export_name = symbolName(fn_name); functions[i] = StaticExport{ .Type = Function, .symbol_name = export_name, .local_name = fn_name, .Parent = Parent, }; } break :brk functions; }; } pub fn thenables(comptime Functions: anytype) [std.meta.fieldNames(@TypeOf(Functions)).len * 2]StaticExport { const FunctionsType = @TypeOf(Functions); return comptime brk: { var functions: [std.meta.fieldNames(FunctionsType).len * 2]StaticExport = undefined; var j: usize = 0; inline for (Functions) |thenable| { inline for ([_][]const u8{ "resolve", "reject" }) |fn_name| { const Function = @TypeOf(@field(thenable, fn_name)); if (@typeInfo(Function) != .Fn) { @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to be a function but received " ++ @tagName(@typeInfo(Function))); } var Fn: std.builtin.Type.Fn = @typeInfo(Function).Fn; if (Fn.calling_convention != .C) { @compileError("Expected " ++ @typeName(Parent) ++ "." ++ @typeName(Function) ++ " to have a C Calling Convention."); } const export_name = symbolName(fn_name); functions[j] = StaticExport{ .Type = Function, .symbol_name = export_name, .local_name = fn_name, .Parent = thenable, }; j += 1; } } break :brk functions; }; } pub inline fn matchNullable(comptime ExpectedReturnType: type, comptime ExternReturnType: type, value: ExternReturnType) ExpectedReturnType { if (comptime isNullableType(ExpectedReturnType) != isNullableType(ExternReturnType)) { return value.?; } else if (comptime (@typeInfo(ExpectedReturnType) == .Enum) and (@typeInfo(ExternReturnType) != .Enum)) { return @as(ExpectedReturnType, @enumFromInt(value)); } else { return value; } } pub inline fn cppFn(comptime typeName: []const u8, args: anytype) (ret: { @setEvalBranchQuota(99999); if (!@hasDecl(Parent, typeName)) { @compileError(@typeName(Parent) ++ " is missing cppFn: " ++ typeName); } break :ret @typeInfo(@TypeOf(@field(Parent, typeName))).Fn.return_type.?; }) { log(comptime name ++ "__" ++ typeName, .{}); @setEvalBranchQuota(99999); if (comptime is_bindgen) { unreachable; } else { const Fn = comptime @field(headers, symbolName(typeName)); if (@typeInfo(@TypeOf(Fn)).Fn.params.len > 0) return matchNullable( comptime @typeInfo(@TypeOf(@field(Parent, typeName))).Fn.return_type.?, comptime @typeInfo(@TypeOf(Fn)).Fn.return_type.?, @call(.auto, Fn, args), ); return matchNullable( comptime @typeInfo(@TypeOf(@field(Parent, typeName))).Fn.return_type.?, comptime @typeInfo(@TypeOf(Fn)).Fn.return_type.?, @call(.auto, Fn, .{}), ); } } }; }