From dcd81b3a2fa015a7b49a2977b1ff9610444aea69 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Mon, 20 Jul 2026 15:35:21 -0600 Subject: [PATCH] reduce global variable usage --- src/genzig.zig | 460 +++++++++++++++++++++++++++---------------------- 1 file changed, 252 insertions(+), 208 deletions(-) diff --git a/src/genzig.zig b/src/genzig.zig index 00d872d..732d531 100644 --- a/src/genzig.zig +++ b/src/genzig.zig @@ -19,18 +19,52 @@ const failMsg = common.failMsg; const enforce = common.enforce; const enforceMsg = common.enforceMsg; -var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); -const allocator = arena.allocator(); +const global = struct { + var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); + const arena = arena_instance.allocator(); + var symbol_pool = StringPool.init(arena); + const symbol = struct { + var none: CachedSymbol("none") = .{}; + var None: CachedSymbol("None") = .{}; + var WIN32_ERROR: CachedSymbol("WIN32_ERROR") = .{}; + }; +}; -const autogen_header = "//! NOTE: this file is autogenerated, DO NOT MODIFY\n"; +const TypeIndex = std.HashMapUnmanaged( + TypeKey, + *const metadata.Type, + TypeKey.Context, + std.hash_map.default_max_load_percentage, +); +const Gen = struct { + const Mutable = struct { + generate_time_millis: i64 = 0, + found_win32_error: bool = false, + dll_modules: StringPool.HashMapUnmanaged(*DllModule) = .{}, + missing_com_overloads: std.ArrayListUnmanaged(MissingOverload) = .{}, + }; + mut: *Mutable, + root_module: *Module, + extra_api_map: *const extra.Root, + com_overload_map: *const StringPool.HashMapUnmanaged(ComTypeMap), + pass1: *const pass1data.Root, + type_index: *const TypeIndex, +}; -var global_symbol_pool = StringPool.init(allocator); +const autogen_header = "//! NOTE: this file is autogenerated, DO NOT MODIFY\n"; -var global_symbol_none: StringPool.Val = undefined; -var global_symbol_None: StringPool.Val = undefined; +fn CachedSymbol(comptime value: [:0]const u8) type { + return struct { + cached: ?StringPool.Val = null, + pub fn get(symbol: *@This()) StringPool.Val { + if (symbol.cached == null) { + symbol.cached = global.symbol_pool.add(value) catch |e| oom(e); + } + return symbol.cached.?; + } + }; +} -var global_pass1: pass1data.Root = undefined; -var global_extra: extra.Root = undefined; // Resolves a type by its (api, name) reference in O(1); needed to expand struct // initializer constants against their target type's fields (win32metadata #1337). const TypeKey = struct { @@ -48,16 +82,12 @@ const TypeKey = struct { } }; }; -var global_type_index: std.HashMapUnmanaged(TypeKey, *const metadata.Type, TypeKey.Context, std.hash_map.default_max_load_percentage) = .{}; -var global_com_overloads: StringPool.HashMapUnmanaged(ComTypeMap) = .{}; -var found_win32_error = false; const MissingOverload = struct { api: StringPool.Val, com_type: []const u8, method: []const u8, method_index: u16, }; -var global_missing_com_overloads: std.ArrayListUnmanaged(MissingOverload) = .{}; fn zigTypeFromIntegerBase(maybe_explicit_base: ?metadata.EnumIntegerBase) []const u8 { return if (maybe_explicit_base) |base| switch (base) { @@ -107,12 +137,12 @@ const Module = struct { children: StringPool.HashMap(*Module), file: ?SdkFile, pub fn alloc(optional_parent: ?*Module, name: StringPool.Val) !*Module { - const module = try allocator.create(Module); + const module = try global.arena.create(Module); module.* = Module{ .optional_parent = optional_parent, .name = name, - .zig_basename = try std.mem.concat(allocator, u8, &[_][]const u8{ name.slice, ".zig" }), - .children = StringPool.HashMap(*Module).init(allocator), + .zig_basename = try std.mem.concat(global.arena, u8, &[_][]const u8{ name.slice, ".zig" }), + .children = StringPool.HashMap(*Module).init(global.arena), .file = null, }; return module; @@ -175,7 +205,7 @@ const SdkFile = struct { if (self.api_name.eql(api) and !redirects.has(name)) return; - const top_level_symbol = try global_symbol_pool.add(if (parents.len == 0) name else parents[0]); + const top_level_symbol = try global.symbol_pool.add(if (parents.len == 0) name else parents[0]); if (self.top_level_api_imports.getPtr(top_level_symbol)) |import| { enforceMsg( api.eql(import.api), @@ -189,26 +219,19 @@ const SdkFile = struct { } }; -const Times = struct { - generate_time_millis: i64 = 0, -}; -var global_times = Times{}; - pub fn main() !u8 { const main_start_millis = std.time.milliTimestamp(); + var mut: Gen.Mutable = .{}; var print_time_summary = false; defer { if (print_time_summary) { var total_millis = std.time.milliTimestamp() - main_start_millis; if (total_millis == 0) total_millis = 1; // prevent divide by 0 - std.debug.print("Gen Time : {} millis ({}%)\n", .{ global_times.generate_time_millis, @divTrunc(100 * global_times.generate_time_millis, total_millis) }); + std.debug.print("Gen Time : {} millis ({}%)\n", .{ mut.generate_time_millis, @divTrunc(100 * mut.generate_time_millis, total_millis) }); std.debug.print("Total Time: {} millis\n", .{total_millis}); } } - global_symbol_none = try global_symbol_pool.add("none"); - global_symbol_None = try global_symbol_pool.add("None"); - - const all_args = try std.process.argsAlloc(allocator); + const all_args = try std.process.argsAlloc(global.arena); // don't care about freeing args const cmd_args = all_args[1..]; @@ -235,14 +258,14 @@ pub fn main() !u8 { defer file.close(); const size = try file.getEndPos(); var reader = file.reader(&.{}); - break :blk try reader.interface.readAlloc(allocator, @intCast(size)); + break :blk try reader.interface.readAlloc(global.arena, @intCast(size)); }; - const parsed = textparse.parseAll(allocator, text); + const parsed = textparse.parseAll(global.arena, text); const api_list: []StringPool.Val = blk: { - var api_list = std.array_list.Managed(StringPool.Val).init(allocator); + var api_list = std.array_list.Managed(StringPool.Val).init(global.arena); for (parsed) |named| { - try api_list.append(try global_symbol_pool.add(named.name)); + try api_list.append(try global.symbol_pool.add(named.name)); } break :blk try api_list.toOwnedSlice(); }; @@ -251,29 +274,30 @@ pub fn main() !u8 { // Index parsed apis by name, then build the `apis` array in sorted order. var api_by_name: std.StringHashMapUnmanaged(metadata.Api) = .{}; + var type_index: TypeIndex = .{}; for (parsed) |named| { - try api_by_name.put(allocator, named.name, named.api); + try api_by_name.put(global.arena, named.name, named.api); for (named.api.Types) |*t| { - try global_type_index.put(allocator, .{ .api = named.name, .name = t.Name }, t); + try type_index.put(global.arena, .{ .api = named.name, .name = t.Name }, t); } } - const apis = try allocator.alloc(metadata.Api, api_list.len); + const apis = try global.arena.alloc(metadata.Api, api_list.len); for (api_list, apis) |api_name, *api| { api.* = api_by_name.get(api_name.slice).?; } // Build the pass1 index (per-type category + com interface chain) in memory, // directly from the loaded models (replaces the old separate pass1 exe). - { - const names = try allocator.alloc([]const u8, api_list.len); + const pass1_root = blk: { + const names = try global.arena.alloc([]const u8, api_list.len); for (api_list, names) |a, *n| n.* = a.slice; - global_pass1 = pass1.buildIndex(allocator, names, apis); - } + break :blk pass1.buildIndex(global.arena, names, apis); + }; const api_set = blk: { var api_set: StringPool.HashMapUnmanaged(void) = .{}; for (api_list) |api| { - try api_set.putNoClobber(allocator, api, {}); + try api_set.putNoClobber(global.arena, api, {}); } break :blk api_set; }; @@ -281,12 +305,13 @@ pub fn main() !u8 { const extra_content = blk: { var file = try std.fs.cwd().openFile(extra_filename, .{}); defer file.close(); - break :blk try file.readToEndAlloc(allocator, std.math.maxInt(usize)); + break :blk try file.readToEndAlloc(global.arena, std.math.maxInt(usize)); }; // no need to free extra_content - global_extra = extra.read(api_set, &global_symbol_pool, allocator, extra_filename, extra_content); + const extra_api_map = extra.read(api_set, &global.symbol_pool, global.arena, extra_filename, extra_content); // no need to free - try readComOverloads(api_set, &global_com_overloads, com_overloads_filename); + var com_overload_map: StringPool.HashMapUnmanaged(ComTypeMap) = .{}; + try readComOverloads(api_set, &com_overload_map, com_overloads_filename); try cleanDir(std.fs.cwd(), zigwin32_out_path); var out_dir = try std.fs.cwd().openDir(zigwin32_out_path, .{}); @@ -313,8 +338,14 @@ pub fn main() !u8 { try installStaticFile(out_dir, name); } - const root_module = try Module.alloc(null, try global_symbol_pool.add("win32")); - g_root_module = root_module; + const gen: Gen = .{ + .root_module = try Module.alloc(null, try global.symbol_pool.add("win32")), + .extra_api_map = &extra_api_map, + .com_overload_map = &com_overload_map, + .pass1 = &pass1_root, + .type_index = &type_index, + .mut = &mut, + }; { std.debug.print("-----------------------------------------------------------------------\n", .{}); @@ -325,18 +356,18 @@ pub fn main() !u8 { for (api_list, apis, 0..) |api_name, api, api_index| { std.debug.print("{}/{}: generating '{f}'\n", .{ api_index + 1, api_list.len, api_name }); - try generateApiModule(root_module, out_win32_dir, api_name, api); + try generateApiModule(&gen, out_win32_dir, api_name, api); } - std.debug.assert(found_win32_error); + std.debug.assert(gen.mut.found_win32_error); - if (global_missing_com_overloads.items.len > 0) { + if (gen.mut.missing_com_overloads.items.len > 0) { std.log.err( "missing {} entries in ComOverloads.txt, copy/paste the following to it:", - .{global_missing_com_overloads.items.len}, + .{gen.mut.missing_com_overloads.items.len}, ); var stderr_buf: [4096]u8 = undefined; var stderr = std.fs.File.stderr().writer(&stderr_buf); - for (global_missing_com_overloads.items) |overload| { + for (gen.mut.missing_com_overloads.items) |overload| { try stderr.interface.print( "{f} {s} {s} {d} TODO_FILL_IN_SUFFIX\n", .{ @@ -354,14 +385,14 @@ pub fn main() !u8 { for (static_zig_files ++ &[_][]const u8{ "everything", }) |submodule_str| { - const submodule = try global_symbol_pool.add(submodule_str); - try root_module.children.put(submodule, try Module.alloc(root_module, submodule)); + const submodule = try global.symbol_pool.add(submodule_str); + try gen.root_module.children.put(submodule, try Module.alloc(gen.root_module, submodule)); } - try writeDllModules(out_dir); + try writeDllModules(&gen.mut.dll_modules, out_dir); - try generateContainerModules(out_dir, root_module); - try generateEverythingModule(out_win32_dir, root_module); + try generateContainerModules(&gen.mut.dll_modules, out_dir, gen.root_module); + try generateEverythingModule(&gen, out_win32_dir); } { @@ -419,14 +450,14 @@ fn readComOverloads( ) !void { var file = try std.fs.cwd().openFile(filename, .{}); defer file.close(); - const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize)); + const content = try file.readToEndAlloc(global.arena, std.math.maxInt(usize)); // don't free, we'll keep the strings around var lines = std.mem.splitScalar(u8, content, '\n'); var line_number: u32 = 1; while (lines.next()) |line| : (line_number += 1) { if (line.len == 0) continue; var field_it = std.mem.tokenizeScalar(u8, line, ' '); - const api = try global_symbol_pool.add(field_it.next() orelse continue); + const api = try global.symbol_pool.add(field_it.next() orelse continue); if (api_name_set.get(api)) |_| {} else fatal("{s} line {}: unknown api '{f}'", .{ filename, line_number, api }); const com_type = field_it.next() orelse fatal("{s} line {}: missing type field", .{ filename, line_number }); const method = field_it.next() orelse fatal("{s} line {}: missing method field", .{ filename, line_number }); @@ -441,22 +472,22 @@ fn readComOverloads( .{ filename, line_number, f }, ); - const api_entry = try api_map.getOrPut(allocator, api); + const api_entry = try api_map.getOrPut(global.arena, api); if (!api_entry.found_existing) { api_entry.value_ptr.* = .{}; } const type_map = api_entry.value_ptr; - const type_entry = try type_map.getOrPut(allocator, com_type); + const type_entry = try type_map.getOrPut(global.arena, com_type); if (!type_entry.found_existing) { type_entry.value_ptr.* = .{}; } const method_map = type_entry.value_ptr; - const method_entry = try method_map.getOrPut(allocator, method); + const method_entry = try method_map.getOrPut(global.arena, method); if (!method_entry.found_existing) { method_entry.value_ptr.* = .{}; } const suffix_map = method_entry.value_ptr; - const suffix_entry = try suffix_map.getOrPut(allocator, method_index); + const suffix_entry = try suffix_map.getOrPut(global.arena, method_index); if (suffix_entry.found_existing) fatal( "api '{f}' type '{s}' method '{s}' has duplicate entries for index {}", .{ api, com_type, method, method_index }, @@ -469,8 +500,8 @@ fn gatherSdkFiles(sdk_files: *std.array_list.Managed(*SdkFile), module: *Module) if (module.file) |_| { try sdk_files.append(&module.file.?); } - const children = try common.allocMapValues(allocator, *Module, module.children); - defer allocator.free(children); + const children = try common.allocMapValues(global.arena, *Module, module.children); + defer global.arena.free(children); std.mem.sort(*Module, children, {}, moduleLessThan); // sort so the order is predictable for (children) |child| { try gatherSdkFiles(sdk_files, child); @@ -498,7 +529,7 @@ const Export = struct { } }; -fn generateEverythingModule(out_win32_dir: std.fs.Dir, root_module: *Module) !void { +fn generateEverythingModule(gen: *const Gen, out_win32_dir: std.fs.Dir) !void { var everything_file = try out_win32_dir.createFile("everything.zig", .{}); defer everything_file.close(); var buffer: [4096]u8 = undefined; @@ -512,14 +543,14 @@ fn generateEverythingModule(out_win32_dir: std.fs.Dir, root_module: *Module) !vo \\ )); { - var dll_names = std.array_list.Managed(StringPool.Val).init(allocator); + var dll_names = std.array_list.Managed(StringPool.Val).init(global.arena); defer dll_names.deinit(); - var it = dll_modules.keyIterator(); + var it = gen.mut.dll_modules.keyIterator(); while (it.next()) |name| try dll_names.append(name.*); std.mem.sort(StringPool.Val, dll_names.items, {}, StringPool.asciiLessThanIgnoreCase); try writer.print("\n// {} dll modules:\n", .{dll_names.items.len}); for (dll_names.items) |name| { - const dm = dll_modules.get(name).?; + const dm = gen.mut.dll_modules.get(name).?; try writer.print("pub const {f} = @import(\"../win32.zig\").{s};\n", .{ name, dm.module.file.?.zig_name }); } } @@ -530,17 +561,17 @@ fn generateEverythingModule(out_win32_dir: std.fs.Dir, root_module: *Module) !vo .import => |file| try writer.print("pub const {s} = @import(\"{s}.zig\").{0s};\n", .{ name, file }), }; - var sdk_files = std.array_list.Managed(*SdkFile).init(allocator); + var sdk_files = std.array_list.Managed(*SdkFile).init(global.arena); defer sdk_files.deinit(); - try gatherSdkFiles(&sdk_files, root_module); + try gatherSdkFiles(&sdk_files, gen.root_module); // TODO: workaround issue where constants/functions are defined more than once, not sure what the right solution // is for all these, maybe some modules are not compatible with each other. This could just be the permanent // solution as well, if there are conflicts, we could just say the user has to import the specific module they want. // TODO: I think the right way to reslve conflicts in everything.zig is to have a priority order for the apis. // If I just sort the API's in the right order, more common apis go first, then my current logic will work. - var exports = StringPool.HashMap(Export).init(allocator); + var exports = StringPool.HashMap(Export).init(global.arena); defer exports.deinit(); // populate the exports with type names first @@ -612,7 +643,7 @@ fn addZigExports(writer: *std.Io.Writer, exports: *StringPool.HashMap(Export)) ! inline for (zigexports.declarations) |decl| { // redirected types are emitted by the redirects loop above if (comptime redirects.has(decl.name)) continue; - const name = try global_symbol_pool.add(decl.name); + const name = try global.symbol_pool.add(decl.name); const result = try exports.getOrPut(name); if (result.found_existing) std.debug.panic( "zig.zig {t} export '{f}' conflicts with {t} from {s}", @@ -628,7 +659,11 @@ fn moduleLessThan(context: void, lhs: *Module, rhs: *Module) bool { return std.ascii.lessThanIgnoreCase(lhs.name.slice, rhs.name.slice); } -fn generateContainerModules(dir: std.fs.Dir, module: *Module) anyerror!void { +fn generateContainerModules( + dll_modules: *const StringPool.HashMapUnmanaged(*DllModule), + dir: std.fs.Dir, + module: *Module, +) anyerror!void { if (module.children.count() == 0) { return; } @@ -646,15 +681,15 @@ fn generateContainerModules(dir: std.fs.Dir, module: *Module) anyerror!void { var file_writer = file.writerStreaming(&buffer); const writer = &file_writer.interface; - const children = try common.allocMapValues(allocator, *Module, module.children); - defer allocator.free(children); + const children = try common.allocMapValues(global.arena, *Module, module.children); + defer global.arena.free(children); std.mem.sort(*Module, children, {}, moduleLessThan); // Only the root win32.zig has per-dll modules mixed among the namespace // modules; those are emitted in a separate section from the namespaces. var dll_count: usize = 0; for (children) |child| { - if (isDllModule(child)) dll_count += 1; + if (isDllModule(dll_modules, child)) dll_count += 1; } if (module.file) |_| { @@ -675,14 +710,14 @@ fn generateContainerModules(dir: std.fs.Dir, module: *Module) anyerror!void { try writer.print("// Section: Namespaces ({})\n", .{children.len - dll_count}); try writer.print("//--------------------------------------------------------------------------------\n", .{}); for (children) |child| { - if (!isDllModule(child)) + if (!isDllModule(dll_modules, child)) try writer.print("pub const {f} = @import(\"{s}/{0f}.zig\");\n", .{ child.name, module.name.slice }); } try writer.print("//--------------------------------------------------------------------------------\n", .{}); try writer.print("// Section: DLLs ({})\n", .{dll_count}); try writer.print("//--------------------------------------------------------------------------------\n", .{}); for (children) |child| { - if (isDllModule(child)) + if (isDllModule(dll_modules, child)) try writer.print("pub const {f} = @import(\"dll/{0f}.zig\");\n", .{child.name}); } } @@ -701,31 +736,31 @@ fn generateContainerModules(dir: std.fs.Dir, module: *Module) anyerror!void { defer next_dir.close(); for (children) |child| { - try generateContainerModules(next_dir, child); + try generateContainerModules(dll_modules, next_dir, child); } try writer.flush(); } fn generateApiModule( - root_module: *Module, + gen: *const Gen, out_dir: std.fs.Dir, api_name: StringPool.Val, api_root: metadata.Api, ) !void { - const zig_name = try cameltosnake.camelToSnakeAlloc(allocator, api_name.slice); - errdefer allocator.free(zig_name); + const zig_name = try cameltosnake.camelToSnakeAlloc(global.arena, api_name.slice); + errdefer global.arena.free(zig_name); var module_dir = out_dir; defer if (module_dir.fd != out_dir.fd) module_dir.close(); - var module: *Module = root_module; + var module: *Module = gen.root_module; var depth: u2 = 0; { var it = std.mem.tokenizeScalar(u8, zig_name, '.'); while (it.next()) |name_part| { - if (module != root_module) { + if (module != gen.root_module) { depth += 1; if (module.children.count() == 0) { try module_dir.makeDir(module.name.slice); @@ -736,7 +771,7 @@ fn generateApiModule( module_dir = next_dir; } - const name_pool = try global_symbol_pool.add(name_part); + const name_pool = try global.symbol_pool.add(name_part); if (module.children.get(name_pool)) |existing| { module = existing; } else { @@ -753,7 +788,7 @@ fn generateApiModule( var extra_funcs: extra.Functions = .{}; var extra_consts: extra.Constants = .{}; - if (global_extra.get(api_name)) |api_obj| { + if (gen.extra_api_map.get(api_name)) |api_obj| { extra_funcs = api_obj.functions; extra_consts = api_obj.constants; } @@ -762,24 +797,24 @@ fn generateApiModule( .api_name = api_name, .zig_name = zig_name, .depth = depth, - .const_exports = std.array_list.Managed(StringPool.Val).init(allocator), + .const_exports = std.array_list.Managed(StringPool.Val).init(global.arena), .uses_guid = false, - .top_level_api_imports = StringPool.HashMap(ApiImport).init(allocator), - .type_exports = StringPoolArrayHashMap(void).init(allocator), - .func_exports = StringPoolArrayHashMap(void).init(allocator), - .tmp_func_ptr_workaround_list = std.array_list.Managed(StringPool.Val).init(allocator), + .top_level_api_imports = StringPool.HashMap(ApiImport).init(global.arena), + .type_exports = StringPoolArrayHashMap(void).init(global.arena), + .func_exports = StringPoolArrayHashMap(void).init(global.arena), + .tmp_func_ptr_workaround_list = std.array_list.Managed(StringPool.Val).init(global.arena), .method_conflict_map = getMethodConflictMap(api_name.slice), .param_conflict_map = getParamConflictMap(api_name.slice), .extra_funcs = extra_funcs, - .extra_funcs_applied = StringPool.HashMap(void).init(allocator), + .extra_funcs_applied = StringPool.HashMap(void).init(global.arena), .extra_consts = extra_consts, - .extra_consts_applied = StringPool.HashMap(void).init(allocator), - .com_type_overloads = global_com_overloads.get(api_name), + .extra_consts_applied = StringPool.HashMap(void).init(global.arena), + .com_type_overloads = gen.com_overload_map.get(api_name), }; const generate_start_millis = std.time.milliTimestamp(); - try generateFile(module_dir, module, api_root); - global_times.generate_time_millis += std.time.milliTimestamp() - generate_start_millis; + try generateFile(gen, module_dir, module, api_root); + gen.mut.generate_time_millis += std.time.milliTimestamp() - generate_start_millis; } fn ArchSpecificMap(comptime T: type) type { @@ -799,8 +834,6 @@ const DllModule = struct { // whole arch `switch` group); emitted sorted by name in phase 2. funcs: std.ArrayListUnmanaged(DllFuncText) = .{}, }; -var dll_modules: StringPool.HashMapUnmanaged(*DllModule) = .{}; -var g_root_module: *Module = undefined; fn dllFuncLessThan(_: void, a: DllFuncText, b: DllFuncText) bool { return StringPool.asciiLessThanIgnoreCase({}, a.name, b.name); @@ -808,7 +841,7 @@ fn dllFuncLessThan(_: void, a: DllFuncText, b: DllFuncText) bool { // True only if `module` IS a per-dll module (not merely a namespace that shares // a name with a dll, e.g. the Graphics.DXCore namespace vs the dxcore dll). -fn isDllModule(module: *Module) bool { +fn isDllModule(dll_modules: *const StringPool.HashMapUnmanaged(*DllModule), module: *Module) bool { return if (dll_modules.get(module.name)) |dm| dm.module == module else false; } @@ -817,7 +850,7 @@ fn isDllModule(module: *Module) bool { // metadata (e.g. OLE32.dll / ole32.dll) into a single module. fn dllModuleName(dll_import: []const u8) []const u8 { const base = externFromDllImport(dll_import); - const out = allocator.alloc(u8, base.len) catch |e| oom(e); + const out = global.arena.alloc(u8, base.len) catch |e| oom(e); for (base, out) |c, *o| { o.* = switch (c) { 'A'...'Z' => c + ('a' - 'A'), @@ -828,43 +861,42 @@ fn dllModuleName(dll_import: []const u8) []const u8 { return out; } -fn getDllModule(dll_import: []const u8) *DllModule { +fn getDllModule(gen: *const Gen, dll_import: []const u8) *DllModule { const canon = dllModuleName(dll_import); - const canon_pool = global_symbol_pool.add(canon) catch |e| oom(e); - if (dll_modules.get(canon_pool)) |dm| return dm; - if (g_root_module.children.get(canon_pool)) |_| std.debug.panic( + const canon_pool = global.symbol_pool.add(canon) catch |e| oom(e); + if (gen.mut.dll_modules.get(canon_pool)) |dm| return dm; + if (gen.root_module.children.get(canon_pool)) |_| std.debug.panic( "dll module '{s}' collides with an existing top-level module", .{canon}, ); - const module = Module.alloc(g_root_module, canon_pool) catch |e| oom(e); + const module = Module.alloc(gen.root_module, canon_pool) catch |e| oom(e); module.file = SdkFile{ .api_name = canon_pool, .zig_name = canon, .depth = 0, - .const_exports = std.array_list.Managed(StringPool.Val).init(allocator), + .const_exports = std.array_list.Managed(StringPool.Val).init(global.arena), .uses_guid = false, - .top_level_api_imports = StringPool.HashMap(ApiImport).init(allocator), - .type_exports = StringPoolArrayHashMap(void).init(allocator), - .func_exports = StringPoolArrayHashMap(void).init(allocator), - .tmp_func_ptr_workaround_list = std.array_list.Managed(StringPool.Val).init(allocator), + .top_level_api_imports = StringPool.HashMap(ApiImport).init(global.arena), + .type_exports = StringPoolArrayHashMap(void).init(global.arena), + .func_exports = StringPoolArrayHashMap(void).init(global.arena), + .tmp_func_ptr_workaround_list = std.array_list.Managed(StringPool.Val).init(global.arena), .method_conflict_map = getMethodConflictMap(canon), .param_conflict_map = getParamConflictMap(canon), .extra_funcs = .{}, - .extra_funcs_applied = StringPool.HashMap(void).init(allocator), + .extra_funcs_applied = StringPool.HashMap(void).init(global.arena), .extra_consts = .{}, - .extra_consts_applied = StringPool.HashMap(void).init(allocator), + .extra_consts_applied = StringPool.HashMap(void).init(global.arena), .com_type_overloads = null, .win32_import_prefix = "../win32/", }; - g_root_module.children.put(canon_pool, module) catch |e| oom(e); - const dm = allocator.create(DllModule) catch |e| oom(e); + gen.root_module.children.put(canon_pool, module) catch |e| oom(e); + const dm = global.arena.create(DllModule) catch |e| oom(e); dm.* = .{ .module = module }; - dll_modules.put(allocator, canon_pool, dm) catch |e| oom(e); + gen.mut.dll_modules.put(global.arena, canon_pool, dm) catch |e| oom(e); return dm; } -fn writeDllModules(out_dir: std.fs.Dir) !void { - if (dll_modules.count() == 0) return; +fn writeDllModules(dll_modules: *const StringPool.HashMapUnmanaged(*DllModule), out_dir: std.fs.Dir) !void { out_dir.makeDir("dll") catch |e| switch (e) { error.PathAlreadyExists => {}, else => return e, @@ -904,7 +936,7 @@ fn writeImportsSection(sdk_file: *SdkFile, writer: *CodeWriter) !void { try writer.linef("const Guid = @import(\"{s}zig.zig\").Guid;", .{sdk_file.getWin32DirImportPrefix()}); } { - var arch_specific_imports = ArchSpecificMap(StringPool.Val).init(allocator); + var arch_specific_imports = ArchSpecificMap(StringPool.Val).init(global.arena); defer arch_specific_imports.deinit(); const NamedApiImport = struct { @@ -914,7 +946,7 @@ fn writeImportsSection(sdk_file: *SdkFile, writer: *CodeWriter) !void { return std.ascii.lessThanIgnoreCase(lhs.name.slice, rhs.name.slice); } }; - var sorted_imports = std.array_list.Managed(NamedApiImport).init(allocator); + var sorted_imports = std.array_list.Managed(NamedApiImport).init(global.arena); defer sorted_imports.deinit(); { var it = sdk_file.top_level_api_imports.iterator(); @@ -937,7 +969,7 @@ fn writeImportsSection(sdk_file: *SdkFile, writer: *CodeWriter) !void { } else { // TODO: should I cache this mapping from api ref to api import path? const api_path = try allocApiImportPathFromRef(api_upper.slice); - defer allocator.free(api_path); + defer global.arena.free(api_path); try writer.linef("const {f} = @import(\"{s}{s}.zig\").{0f};", .{ import.name, sdk_file.getWin32DirImportPrefix(), api_path }); } } @@ -955,7 +987,7 @@ fn writeImportsSection(sdk_file: *SdkFile, writer: *CodeWriter) !void { const api_upper = object.obj; // TODO: should I cache this mapping from api ref to api import path? const api_path = try allocApiImportPathFromRef(api_upper.slice); - defer allocator.free(api_path); + defer global.arena.free(api_path); try writer.linef(" {s}@import(\"{s}{s}.zig\").{f},", .{ def_prefix, sdk_file.getWin32DirImportPrefix(), api_path, name }); } if (combined_arches.filter != null) { @@ -996,7 +1028,7 @@ fn writeTestBlock(sdk_file: *SdkFile, writer: *CodeWriter) !void { )); } -fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !void { +fn generateFile(gen: *const Gen, module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !void { const sdk_file = &module.file.?; var out_file = try module_dir.createFile(module.zig_basename, .{}); @@ -1012,7 +1044,7 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi try writer.linef("// Section: Constants ({})", .{api.Constants.len}); try writer.line("//--------------------------------------------------------------------------------"); for (api.Constants) |constant| { - try generateConstant(sdk_file, writer, constant); + try generateConstant(gen, sdk_file, writer, constant); } std.debug.assert(api.Constants.len == sdk_file.const_exports.items.len); try writer.line(""); @@ -1020,12 +1052,12 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi try writer.linef("// Section: Types ({})", .{api.Types.len}); try writer.line("//--------------------------------------------------------------------------------"); { - var arch_specific_types = ArchSpecificMap(metadata.Type).init(allocator); + var arch_specific_types = ArchSpecificMap(metadata.Type).init(global.arena); defer arch_specific_types.deinit(); - var enum_alias_conflicts = StringPool.HashMap(StringPool.Val).init(allocator); + var enum_alias_conflicts = StringPool.HashMap(StringPool.Val).init(global.arena); defer enum_alias_conflicts.deinit(); for (api.Types) |t| { - try generateType(sdk_file, writer, &arch_specific_types, t, &enum_alias_conflicts); + try generateType(gen, sdk_file, writer, &arch_specific_types, t, &enum_alias_conflicts); try writer.line(""); } var it = arch_specific_types.iterator(); @@ -1046,7 +1078,7 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi // If it doesn't we might need to update generateTypeDefinition to take an extra // arches parameter. std.debug.assert(object.obj.Architectures.eql(.{ .filter = object.filter })); - try generateTypeDefinition(sdk_file, writer, object.obj, &enum_alias_conflicts, name_pool, def_prefix, ","); + try generateTypeDefinition(gen, sdk_file, writer, object.obj, &enum_alias_conflicts, name_pool, def_prefix, ","); } if (combined_arches.filter != null) { //try writer.line(" else => @compileError(\"unsupported on this arch\"),"); @@ -1068,10 +1100,10 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi var arch_func_map: StringPool.HashMapUnmanaged(ArchFunction) = .{}; for (api.Functions) |*function| { if (function.Architectures.filter != null) { - const name_pool = try global_symbol_pool.add(function.Name); - const entry = arch_func_map.getOrPut(allocator, name_pool) catch |e| oom(e); + const name_pool = try global.symbol_pool.add(function.Name); + const entry = arch_func_map.getOrPut(global.arena, name_pool) catch |e| oom(e); const new_func_node_index: FuncNodeIndex = @enumFromInt(arch_func_nodes.items.len); - arch_func_nodes.append(allocator, .{ + arch_func_nodes.append(global.arena, .{ .func = function, .next = if (entry.found_existing) entry.value_ptr.root_node_index else null, }) catch |e| oom(e); @@ -1086,9 +1118,9 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi // namespace module. Type imports / func_exports accumulate on the dll // module's SdkFile (dsf); extra.txt modifiers are still looked up on // this namespace's sdk_file so its reconciliation check stays valid. - const dm = getDllModule(function.DllImport); + const dm = getDllModule(gen, function.DllImport); const dsf = &dm.module.file.?; - const name_pool = try global_symbol_pool.add(function.Name); + const name_pool = try global.symbol_pool.add(function.Name); // arch variants of a name collapse into one switch group, generated // only on the first occurrence. @@ -1098,12 +1130,12 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi // Generate this function's text into its own buffer so phase 2 can emit // the dll module's functions sorted by name. - var aw = std.Io.Writer.Allocating.init(allocator); + var aw = std.Io.Writer.Allocating.init(global.arena); var cw = CodeWriter{ .writer = &aw.writer, .depth = 0, .midline = false }; const dw = &cw; if (function.Architectures.filter == null) { - try generateFunction(dsf, sdk_file, dw, .{ .dll = function }); + try generateFunction(gen, dsf, sdk_file, dw, .{ .dll = function }); } else { const entry = arch_func_map.getEntry(name_pool) orelse unreachable; @@ -1120,7 +1152,7 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi const case_prefix = buf[0..try formatArchesCase(node.func.Architectures.filter.?, &buf)]; try dw.linef("{s}(struct {{", .{case_prefix}); try dw.line(""); - try generateFunction(dsf, sdk_file, dw, .{ .dll = node.func.* }); + try generateFunction(gen, dsf, sdk_file, dw, .{ .dll = node.func.* }); try dw.line(""); try dw.linef("}}).{f},", .{name_pool}); @@ -1138,7 +1170,7 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi entry.value_ptr.generated = true; } try dw.line(""); - dm.funcs.append(allocator, .{ .name = name_pool, .text = allocator.dupe(u8, aw.written()) catch |e| oom(e) }) catch |e| oom(e); + dm.funcs.append(global.arena, .{ .name = name_pool, .text = global.arena.dupe(u8, aw.written()) catch |e| oom(e) }) catch |e| oom(e); } std.debug.assert(sdk_file.func_exports.count() == 0); try writer.line(""); @@ -1182,7 +1214,7 @@ fn generateFile(module_dir: std.fs.Dir, module: *Module, api: metadata.Api) !voi // TODO: should I cache this mapping from api ref to api import path? fn allocApiImportPathFromRef(api_ref: []const u8) ![]u8 { - const api_path = try cameltosnake.camelToSnakeAlloc(allocator, api_ref); + const api_path = try cameltosnake.camelToSnakeAlloc(global.arena, api_ref); for (api_path, 0..) |c, i| { if (c == '.') api_path[i] = '/'; @@ -1214,7 +1246,7 @@ fn addTypeRefsNoFormatter(sdk_file: *SdkFile, arches: metadata.Architectures, ty .ApiRef => |api_ref| { const name = getApiRefSubstitute(api_ref.Name, api_ref.Parents) orelse api_ref.Name; if (inline_types.has(name)) return; - const api = try global_symbol_pool.add(api_ref.Api); + const api = try global.symbol_pool.add(api_ref.Api); try sdk_file.addApiImport(arches, renameType(name), api, api_ref.Parents); }, .PointerTo => |to| try addTypeRefsNoFormatter(sdk_file, arches, to.Child.*), @@ -1381,13 +1413,15 @@ pub fn fmtTypeRef( } fn generateTypeRef( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, self: TypeRefFormatter, ) !void { - try generateTypeRefRec(sdk_file, writer, self, .top_level); + try generateTypeRefRec(gen, sdk_file, writer, self, .top_level); } fn generateTypeRefRec( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, self: TypeRefFormatter, @@ -1403,12 +1437,12 @@ fn generateTypeRefRec( if (isAnonymousTypeName(name)) { const anon_types = self.options.anon_types orelse failMsg("missing anonymous type '{s}' (this scope does not have any anonymous types)!", .{name}); - const name_pool = try global_symbol_pool.add(name); + const name_pool = try global.symbol_pool.add(name); const t = anon_types.types.get(name_pool) orelse failMsg("missing anonymous type '{f}'!", .{name_pool}); switch (t.Kind) { - .Struct => try generateStructOrUnionDef(sdk_file, writer, t, self.nested_context), - .Union => try generateStructOrUnionDef(sdk_file, writer, t, self.nested_context), + .Struct => try generateStructOrUnionDef(gen, sdk_file, writer, t, self.nested_context), + .Union => try generateStructOrUnionDef(gen, sdk_file, writer, t, self.nested_context), else => fail(), } try writer.write("}", .{ .nl = false }); @@ -1416,7 +1450,7 @@ fn generateTypeRefRec( } const type_kind_category: Pass1TypeCategory = blk: { - const pass1_api_map = global_pass1.get(api_ref.Api) orelse failMsg("type '{s}' is from API '{s}' that is missing from pass1 data", .{ name, api_ref.Api }); + const pass1_api_map = gen.pass1.get(api_ref.Api) orelse failMsg("type '{s}' is from API '{s}' that is missing from pass1 data", .{ name, api_ref.Api }); const pass1_type: pass1data.Type = pass1_api_map.get(name) orelse { if (api_ref.Parents.len == 0) { @@ -1493,7 +1527,7 @@ fn generateTypeRefRec( child_options.is_const = false; // TODO: this doesn't seem right try writer.write("const ", .{ .start = .any, .nl = false }); } - try generateTypeRefRec(sdk_file, writer, fmtTypeRef(to.Child.*, self.arches, child_options, self.nested_context), .child); + try generateTypeRefRec(gen, sdk_file, writer, fmtTypeRef(to.Child.*, self.arches, child_options, self.nested_context), .child); }, .Array => |array| { const shape_size: u32 = init: { @@ -1502,7 +1536,7 @@ fn generateTypeRefRec( break :init 1; }; try writer.writef("[{}]", .{shape_size}, .{ .start = .any, .nl = false }); - try generateTypeRefRec(sdk_file, writer, fmtTypeRef(array.Child.*, self.arches, self.options, self.nested_context), .child); + try generateTypeRefRec(gen, sdk_file, writer, fmtTypeRef(array.Child.*, self.arches, self.options, self.nested_context), .child); }, .LPArray => |array| { if (self.options.optional) { @@ -1526,7 +1560,7 @@ fn generateTypeRefRec( } if (array.CountConst <= 0 and self.options.is_const) try writer.write("const ", .{ .start = .any, .nl = false }); - try generateTypeRefRec(sdk_file, writer, fmtTypeRef(array.Child.*, self.arches, self.options.getChildOptions(), self.nested_context), .array); + try generateTypeRefRec(gen, sdk_file, writer, fmtTypeRef(array.Child.*, self.arches, self.options.getChildOptions(), self.nested_context), .array); } }, .MissingClrType => |t| try writer.writef( @@ -1663,8 +1697,8 @@ const InitNumbers = struct { } }; -fn resolveInitStruct(api: []const u8, name: []const u8) *const metadata.StructOrUnion { - const t = global_type_index.get(.{ .api = api, .name = name }) orelse +fn resolveInitStruct(gen: *const Gen, api: []const u8, name: []const u8) *const metadata.StructOrUnion { + const t = gen.type_index.get(.{ .api = api, .name = name }) orelse std.debug.panic("not implemented: initializer type '{s}:{s}' not found", .{ api, name }); return switch (t.Kind) { .Struct => |*s| s, @@ -1711,8 +1745,8 @@ fn emitInitStructBody(writer: *CodeWriter, su: *const metadata.StructOrUnion, nu try writer.write(" }", .{ .start = .mid, .nl = false }); } -fn generateConstant(sdk_file: *SdkFile, writer: *CodeWriter, constant: metadata.Constant) !void { - const name_pool = try global_symbol_pool.add(constant.Name); +fn generateConstant(gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, constant: metadata.Constant) !void { + const name_pool = try global.symbol_pool.add(constant.Name); try sdk_file.const_exports.append(name_pool); if (constants_to_skip.get(constant.Name)) |_| { @@ -1737,10 +1771,10 @@ fn generateConstant(sdk_file: *SdkFile, writer: *CodeWriter, constant: metadata. .ApiRef => |r| r, else => std.debug.panic("not implemented: initializer const '{s}' with non-ApiRef type", .{constant.Name}), }; - const su = resolveInitStruct(api_ref.Api, api_ref.Name); + const su = resolveInitStruct(gen, api_ref.Api, api_ref.Name); var nums = InitNumbers.init(init_str); try writer.writef("pub const {f} = ", .{name_pool}, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, zig_type_formatter); + try generateTypeRef(gen, sdk_file, writer, zig_type_formatter); try emitInitStructBody(writer, su, &nums); try writer.write(";", .{ .start = .mid }); if (!nums.atEnd()) std.debug.panic("initializer for '{s}' has leftover values", .{constant.Name}); @@ -1771,7 +1805,7 @@ fn generateConstant(sdk_file: *SdkFile, writer: *CodeWriter, constant: metadata. const fmtid = pk.Fmtid; const pid = pk.Pid; try writer.writef("pub const {f} = ", .{name_pool}, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, zig_type_formatter); + try generateTypeRef(gen, sdk_file, writer, zig_type_formatter); sdk_file.uses_guid = true; try writer.writef(" {{ .fmtid = Guid.initString(\"{s}\"), .pid = {} }};", .{ fmtid, pid }, .{ .start = .mid }); } else { @@ -1779,7 +1813,7 @@ fn generateConstant(sdk_file: *SdkFile, writer: *CodeWriter, constant: metadata. name_pool, sdk_file.getWin32DirImportPrefix(), }, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, zig_type_formatter); + try generateTypeRef(gen, sdk_file, writer, zig_type_formatter); try writer.writef(", {f});", .{ fmtConstValue(constant.ValueType, constant.Value, sdk_file), }, .{ .start = .mid }); @@ -1873,6 +1907,7 @@ pub fn addArchSpecific( } fn generateType( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, arch_specific: *ArchSpecificMap(metadata.Type), @@ -1896,7 +1931,7 @@ fn generateType( .ComClassID => |class_id| { if (t.Architectures.filter != null) failMsg("not impl", .{}); - const clsid_pool = try global_symbol_pool.addFormatted("CLSID_{s}", .{t.Name}); + const clsid_pool = try global.symbol_pool.addFormatted("CLSID_{s}", .{t.Name}); sdk_file.uses_guid = true; try writer.linef("const {f}_Value = Guid.initString(\"{s}\");", .{ clsid_pool, class_id.Guid }); try writer.linef("pub const {f} = &{0f}_Value;", .{clsid_pool}); @@ -1906,7 +1941,7 @@ fn generateType( else => {}, } - const pool_name = try global_symbol_pool.add(renameType(t.Name)); + const pool_name = try global.symbol_pool.add(renameType(t.Name)); // TODO: should I be adding this to type_exports if it's arch specific? // type_exports may need to have an ArchFlags for each symbol @@ -1927,13 +1962,14 @@ fn generateType( } else if (t.Architectures.filter) |filter| { try addArchSpecific(metadata.Type, arch_specific, pool_name, filter, t); } else { - const def_prefix = try std.fmt.allocPrint(allocator, "pub const {f} = ", .{fmtIdP(renameType(t.Name))}); - defer allocator.free(def_prefix); - try generateTypeDefinition(sdk_file, writer, t, enum_alias_conflicts, pool_name, def_prefix, ";"); + const def_prefix = try std.fmt.allocPrint(global.arena, "pub const {f} = ", .{fmtIdP(renameType(t.Name))}); + defer global.arena.free(def_prefix); + try generateTypeDefinition(gen, sdk_file, writer, t, enum_alias_conflicts, pool_name, def_prefix, ";"); } } fn generateTypeDefinition( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, t: metadata.Type, @@ -1953,7 +1989,7 @@ fn generateTypeDefinition( if (typedef.AlsoUsableFor) |also_usable_for| { if (also_usable_type_api_map.get(also_usable_for)) |api| { - const api_pool = try global_symbol_pool.add(api); + const api_pool = try global.symbol_pool.add(api); try sdk_file.addApiImport(t.Architectures, also_usable_for, api_pool, &.{}); try writer.linef("//TODO: type '{f}' is \"AlsoUsableFor\" '{s}' which means this type is implicitly", .{ pool_name, also_usable_for }); try writer.linef("// convertible to '{s}' but not the other way around. I don't know how to do this", .{also_usable_for}); @@ -1995,14 +2031,14 @@ fn generateTypeDefinition( null, ); try writer.writef("{s}", .{def_prefix}, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, zig_type_formatter); + try generateTypeRef(gen, sdk_file, writer, zig_type_formatter); try writer.writef("{s}", .{def_suffix}, .{ .start = .mid }); }, - .Enum => |type_enum| try generateEnum(sdk_file, writer, type_enum, pool_name, enum_alias_conflicts, def_prefix, def_suffix), - .Struct => try generateStructOrUnion(sdk_file, writer, t, def_prefix, def_suffix, null), - .Union => try generateStructOrUnion(sdk_file, writer, t, def_prefix, def_suffix, null), + .Enum => |type_enum| try generateEnum(gen, sdk_file, writer, type_enum, pool_name, enum_alias_conflicts, def_prefix, def_suffix), + .Struct => try generateStructOrUnion(gen, sdk_file, writer, t, def_prefix, def_suffix, null), + .Union => try generateStructOrUnion(gen, sdk_file, writer, t, def_prefix, def_suffix, null), .ComClassID => @panic("hasn't happened yet?"), - .Com => |com| try generateCom(sdk_file, writer, t, com, pool_name, def_prefix), + .Com => |com| try generateCom(gen, sdk_file, writer, t, com, pool_name, def_prefix), .FunctionPointer => |func| { if (funcPtrHasDependencyLoop(pool_name.slice)) { try writer.line("// TODO: this function pointer causes dependency loop problems, so it's stubbed out"); @@ -2012,7 +2048,7 @@ fn generateTypeDefinition( ); return; } - try generateFunction(sdk_file, sdk_file, writer, .{ .ptr = .{ + try generateFunction(gen, sdk_file, sdk_file, writer, .{ .ptr = .{ .t = t, .func = func, .def_prefix = def_prefix, @@ -2127,7 +2163,7 @@ const AnonTypes = struct { types: StringPool.HashMap(metadata.Type), pub fn init() AnonTypes { return .{ - .types = StringPool.HashMap(metadata.Type).init(allocator), + .types = StringPool.HashMap(metadata.Type).init(global.arena), }; } pub fn deinit(self: *AnonTypes) void { @@ -2136,6 +2172,7 @@ const AnonTypes = struct { }; fn generateStructOrUnion( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, t: metadata.Type, @@ -2145,11 +2182,12 @@ fn generateStructOrUnion( ) !void { std.debug.assert(!isAnonymousTypeName(t.Name)); try writer.writef("{s}", .{def_prefix}, .{ .nl = false }); - try generateStructOrUnionDef(sdk_file, writer, t, nested_context); + try generateStructOrUnionDef(gen, sdk_file, writer, t, nested_context); try writer.linef("}}{s}", .{def_suffix}); } fn generateStructOrUnionDef( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, t: metadata.Type, @@ -2183,19 +2221,19 @@ fn generateStructOrUnionDef( const this_nested_context = if (container.NestedTypes.len > 0) &this_nested_context_data else nested_context; for (container.NestedTypes) |*nested_type| { - const pool_name = try global_symbol_pool.add(nested_type.Name); + const pool_name = try global.symbol_pool.add(nested_type.Name); if (isAnonymousTypeName(nested_type.Name)) { if (nested_type.Architectures.filter) |_| // we don't handle architectures in this case failMsg("not impl", .{}); try anon_types.types.put(pool_name, nested_type.*); } else { // TODO: I don't know why this isn't working!!! - //const def_prefix = try std.fmt.allocPrint(allocator, "pub const {f} = ", .{fmtIdP(pool_name)}); - //defer allocator.free(def_prefix); + //const def_prefix = try std.fmt.allocPrint(global.arena, "pub const {f} = ", .{fmtIdP(pool_name)}); + //defer global.arena.free(def_prefix); try writer.writef("pub const {f} = ", .{pool_name}, .{ .nl = false }); switch (nested_type.Kind) { - .Union => try generateStructOrUnionDef(sdk_file, writer, nested_type.*, this_nested_context), - .Struct => try generateStructOrUnionDef(sdk_file, writer, nested_type.*, this_nested_context), + .Union => try generateStructOrUnionDef(gen, sdk_file, writer, nested_type.*, this_nested_context), + .Struct => try generateStructOrUnionDef(gen, sdk_file, writer, nested_type.*, this_nested_context), else => failMsg("not impl", .{}), } try writer.line("};"); @@ -2226,7 +2264,7 @@ fn generateStructOrUnionDef( } const field_type_formatter = try addTypeRefs(sdk_file, t.Architectures, field.Type, field_options, this_nested_context); try writer.writef("{f}: ", .{fmtIdP(field.Name)}, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, field_type_formatter); + try generateTypeRef(gen, sdk_file, writer, field_type_formatter); if (container.PackingSize >= 1) { try writer.writef(" align({})", .{container.PackingSize}, .{ .start = .mid, .nl = false }); } @@ -2419,8 +2457,8 @@ fn setShortNames(values: []EnumValue) void { var at_first = true; var longest_prefix_match: []const u8 = undefined; for (values) |*val_ref| { - if ((val_ref.pool_name.eql(global_symbol_none) or - val_ref.pool_name.eql(global_symbol_None)) and + if ((val_ref.pool_name.eql(global.symbol.none.get()) or + val_ref.pool_name.eql(global.symbol.None.get())) and val_ref.valueIsZero()) { val_ref.short_name = val_ref.pool_name.slice; @@ -2462,6 +2500,7 @@ fn flagsBitCount(base: ?metadata.EnumIntegerBase) u7 { } fn generateEnum( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, type_enum: metadata.Type.Enum, @@ -2472,12 +2511,12 @@ fn generateEnum( ) !void { const integer_base = zigTypeFromIntegerBase(type_enum.IntegerBase); - const values = try allocator.alloc(EnumValue, type_enum.Values.len); + const values = try global.arena.alloc(EnumValue, type_enum.Values.len); var values_initialized_len: usize = 0; - defer allocator.free(values); + defer global.arena.free(values); for (type_enum.Values) |*enum_field| { values[values_initialized_len] = .{ - .pool_name = try global_symbol_pool.add(enum_field.Name), + .pool_name = try global.symbol_pool.add(enum_field.Name), .short_name = "", .value = enum_field.Value, .no_alias = false, @@ -2533,10 +2572,10 @@ fn generateEnum( try writer.line(" else => null,"); try writer.line(" };"); try writer.line("}"); - const is_win32_error = std.mem.eql(u8, pool_name.slice, "WIN32_ERROR"); + const is_win32_error = pool_name.eql(global.symbol.WIN32_ERROR.get()); if (is_win32_error) { - std.debug.assert(!found_win32_error); - found_win32_error = true; + std.debug.assert(!gen.mut.found_win32_error); + gen.mut.found_win32_error = true; try writer.line("// We use a special format implementation for the WIN32_ERROR enum that avoids"); try writer.line("// getting the tag name. This is because the enum has over 3,000 values which"); try writer.line("// results in needing over 100Kb to store them as strings."); @@ -2672,8 +2711,8 @@ fn generateEnum( } } -fn getComInterface(api: []const u8, name: []const u8) ?metadata.TypeRef { - const pass1_api_map = global_pass1.get(api) orelse failMsg("com interface inside unknown api '{s}'", .{api}); +fn getComInterface(gen: *const Gen, api: []const u8, name: []const u8) ?metadata.TypeRef { + const pass1_api_map = gen.pass1.get(api) orelse failMsg("com interface inside unknown api '{s}'", .{api}); const pass1_type = pass1_api_map.get(name) orelse failMsg( "com interface '{s}' does not exist in api '{s}'", .{ name, api }, @@ -2685,6 +2724,7 @@ fn getComInterface(api: []const u8, name: []const u8) ?metadata.TypeRef { } fn generateCom( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, t: metadata.Type, @@ -2700,7 +2740,7 @@ fn generateCom( try writer.line("// WARNING: this COM type has been skipped because it causes some sort of error"); } - const iid_pool = try global_symbol_pool.addFormatted("IID_{s}", .{com_pool_name.slice}); + const iid_pool = try global.symbol_pool.addFormatted("IID_{s}", .{com_pool_name.slice}); if (type_com.Guid) |guid| { sdk_file.uses_guid = true; try writer.linef("const {f}_Value = Guid.initString(\"{s}\");", .{ iid_pool, guid }); @@ -2716,7 +2756,7 @@ fn generateCom( { const ComSymbolState = enum { unique, conflicts }; - var method_set = StringHashMap(ComSymbolState).init(allocator); + var method_set = StringHashMap(ComSymbolState).init(global.arena); defer method_set.deinit(); for (type_com.Methods) |*method| { @@ -2728,7 +2768,7 @@ fn generateCom( } } - const count_before = global_missing_com_overloads.items.len; + const count_before = gen.mut.missing_com_overloads.items.len; for (type_com.Methods, 0..) |*method, method_index| { const maybe_overload_suffixes: ?ComSuffixMap = if (maybe_overloads) |o| o.get(method.Name) @@ -2750,17 +2790,17 @@ fn generateCom( break :blk true; }; if (!have_overload) { - try global_missing_com_overloads.append(allocator, .{ + try gen.mut.missing_com_overloads.append(global.arena, .{ .api = sdk_file.api_name, .com_type = com_pool_name.slice, - .method = try allocator.dupe(u8, method.Name), + .method = try global.arena.dupe(u8, method.Name), .method_index = @intCast(method_index), }); } }, } } - if (count_before != global_missing_com_overloads.items.len) + if (count_before != gen.mut.missing_com_overloads.items.len) return; } @@ -2772,7 +2812,7 @@ fn generateCom( .reason = .direct_type_access, }), null); try writer.write(" base: ", .{ .nl = false }); - try generateTypeRef(sdk_file, writer, maybe_iface_formatter.?); + try generateTypeRef(gen, sdk_file, writer, maybe_iface_formatter.?); try writer.write(".VTable,", .{ .start = .mid }); } @@ -2795,7 +2835,7 @@ fn generateCom( .{ method.Name, suffix }, ); - try generateFunction(sdk_file, sdk_file, writer, .{ .com = .{ + try generateFunction(gen, sdk_file, sdk_file, writer, .{ .com = .{ .method = method, .type_name = com_pool_name.slice, .zig_name = zig_name, @@ -2816,17 +2856,18 @@ fn generateCom( }), null); try writer.write(" ", .{ .nl = false }); - try generateTypeRef(sdk_file, writer, iface_formatter); + try generateTypeRef(gen, sdk_file, writer, iface_formatter); try writer.write(": ", .{ .start = .mid, .nl = false }); - try generateTypeRef(sdk_file, writer, iface_formatter); + try generateTypeRef(gen, sdk_file, writer, iface_formatter); try writer.write(",", .{ .start = .mid }); - next_iface = getComInterface(next_iface_com.api, next_iface_com.name) orelse break; + next_iface = getComInterface(gen, next_iface_com.api, next_iface_com.name) orelse break; next_iface_com = common.getComInterface(next_iface); } } try generateComMethods( + gen, sdk_file, writer, t.Architectures, @@ -2838,6 +2879,7 @@ fn generateCom( } fn generateComMethods( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, arches: metadata.Architectures, @@ -2901,7 +2943,7 @@ fn generateComMethods( //try writer.linef("// TODO: what to do with BytesParamIndex {}?", .{bytes_param_index}); } try writer.writef(", {f}: ", .{fmtParamId(param.Name, sdk_file.param_conflict_map)}, .{ .start = .mid, .nl = false }); - try generateTypeRef(sdk_file, writer, param_type_formatter); + try generateTypeRef(gen, sdk_file, writer, param_type_formatter); } // NOTE: don't need to call addTypeRefs because it was already called in generateFunction above // TODO: set is_const, in and out properly @@ -2916,16 +2958,16 @@ fn generateComMethods( TypeRefFormatter.Options.fromParamAttrs(method.ReturnAttrs, .var_decl, modifier_set.ret), null, ); - const is_struct_return = comMethodReturnsStructByValue(method.ReturnType); + const is_struct_return = comMethodReturnsStructByValue(gen, method.ReturnType); try writer.write(") callconv(.@\"inline\") ", .{ .start = .mid, .nl = false }); - try generateTypeRef(sdk_file, writer, return_type_formatter); + try generateTypeRef(gen, sdk_file, writer, return_type_formatter); try writer.write(" {", .{ .start = .mid }); if (is_struct_return) { // For COM methods returning structs by value, call the vtable // function with a hidden return pointer and return the result. try writer.write(" var __result: ", .{ .nl = false }); - try generateTypeRef(sdk_file, writer, return_type_formatter); + try generateTypeRef(gen, sdk_file, writer, return_type_formatter); try writer.write(" = undefined;", .{ .start = .mid }); try writer.write(" _ = ", .{ .nl = false }); try writer.writef( @@ -3053,7 +3095,7 @@ fn getComMethodConfigName(type_name: []const u8, method: *const metadata.ComMeth const name = std.fmt.bufPrint(&name_buf, "{s}.{s}", .{ type_name, method.Name }) catch @panic( "name_buf not big enough", ); - return global_symbol_pool.add(name) catch |e| oom(e); + return global.symbol_pool.add(name) catch |e| oom(e); } const Function = union(enum) { @@ -3076,9 +3118,9 @@ const Function = union(enum) { // such as extra.txt pub fn ConfigName(self: Function) StringPool.Val { return switch (self) { - .dll => |dll| global_symbol_pool.add(dll.Name) catch |e| oom(e), + .dll => |dll| global.symbol_pool.add(dll.Name) catch |e| oom(e), .com => |com| return getComMethodConfigName(com.type_name, com.method), - .ptr => |ptr| global_symbol_pool.add(ptr.t.Name) catch |e| oom(e), + .ptr => |ptr| global.symbol_pool.add(ptr.t.Name) catch |e| oom(e), }; } @@ -3177,11 +3219,11 @@ fn externFromDllImport(import: []const u8) []const u8 { /// returning user-defined types (structs/unions) pass the return value via /// a hidden pointer parameter after 'this', rather than as a true return value. /// See: https://github.com/microsoft/win32metadata/issues/636 -fn comMethodReturnsStructByValue(return_type: metadata.TypeRef) bool { +fn comMethodReturnsStructByValue(gen: *const Gen, return_type: metadata.TypeRef) bool { switch (return_type) { .ApiRef => |api_ref| { if (api_ref.TargetKind != .Default) return false; - const type_map = global_pass1.get(api_ref.Api) orelse return false; + const type_map = gen.pass1.get(api_ref.Api) orelse return false; const pass1_type = type_map.get(api_ref.Name) orelse return false; return switch (pass1_type) { .Struct, .Union => true, @@ -3193,6 +3235,7 @@ fn comMethodReturnsStructByValue(return_type: metadata.TypeRef) bool { } fn generateFunction( + gen: *const Gen, sdk_file: *SdkFile, modifier_sdk_file: *SdkFile, writer: *CodeWriter, @@ -3205,7 +3248,7 @@ fn generateFunction( const params = func.Params(); switch (func) { - .dll => |dll| sdk_file.func_exports.put(try global_symbol_pool.add(dll.Name), {}) catch |e| oom(e), + .dll => |dll| sdk_file.func_exports.put(try global.symbol_pool.add(dll.Name), {}) catch |e| oom(e), .com => {}, .ptr => {}, } @@ -3216,7 +3259,7 @@ fn generateFunction( // hidden return pointer parameter after 'this'. We need to transform the // vtable signature to match. const is_com_struct_return = switch (func) { - .com => comMethodReturnsStructByValue(return_type), + .com => comMethodReturnsStructByValue(gen, return_type), else => false, }; @@ -3256,14 +3299,14 @@ fn generateFunction( const return_opts = TypeRefFormatter.Options.fromParamAttrs(return_attrs, .var_decl, modifier_set.ret); const return_type_formatter = try addTypeRefs(sdk_file, arches, return_type, return_opts, null); try writer.write(" __return_ptr: *", .{ .nl = false }); - try generateTypeRef(sdk_file, writer, return_type_formatter); + try generateTypeRef(gen, sdk_file, writer, return_type_formatter); try writer.write(",", .{ .start = .mid }); } }, .ptr => |ptr| try writer.linef("{s}*const fn(", .{ptr.def_prefix}), } - try generateParams(sdk_file, writer, arches, modifier_set, params); + try generateParams(gen, sdk_file, writer, arches, modifier_set, params); try writer.writef(") callconv(.winapi) ", .{}, .{ .nl = false }); if (attrs.DoesNotReturn) { @@ -3273,12 +3316,12 @@ fn generateFunction( const return_opts = TypeRefFormatter.Options.fromParamAttrs(return_attrs, .var_decl, modifier_set.ret); const return_type_formatter = try addTypeRefs(sdk_file, arches, return_type, return_opts, null); try writer.write("*", .{ .start = .mid, .nl = false }); - try generateTypeRef(sdk_file, writer, return_type_formatter); + try generateTypeRef(gen, sdk_file, writer, return_type_formatter); } else { // TODO: set is_const, in and out properly const return_opts = TypeRefFormatter.Options.fromParamAttrs(return_attrs, .var_decl, modifier_set.ret); const return_type_formatter = try addTypeRefs(sdk_file, arches, return_type, return_opts, null); - try generateTypeRef(sdk_file, writer, return_type_formatter); + try generateTypeRef(gen, sdk_file, writer, return_type_formatter); } const term = switch (func) { .dll => ";", @@ -3289,6 +3332,7 @@ fn generateFunction( } fn generateParams( + gen: *const Gen, sdk_file: *SdkFile, writer: *CodeWriter, arches: metadata.Architectures, @@ -3306,7 +3350,7 @@ fn generateParams( ); } try writer.writef(" {f}: ", .{fmtIdP(param.Name)}, .{ .nl = false }); - try generateTypeRef(sdk_file, writer, param_type_formatter); + try generateTypeRef(gen, sdk_file, writer, param_type_formatter); try writer.write(",", .{ .start = .mid }); } }