//! ZigObject encapsulates the state of the incrementally compiled Zig module. //! It stores the associated input local and global symbols, allocated atoms, //! and any relocations that may have been emitted. //! Think about this as fake in-memory Object file for the Zig module. path: []const u8, /// Index within the list of relocatable objects of the linker driver. index: File.Index, /// Map of all `Decl` that are currently alive. /// Each index maps to the corresponding `DeclInfo`. decls_map: std.AutoHashMapUnmanaged(InternPool.DeclIndex, DeclInfo) = .{}, /// List of function type signatures for this Zig module. func_types: std.ArrayListUnmanaged(std.wasm.Type) = .{}, /// List of `std.wasm.Func`. Each entry contains the function signature, /// rather than the actual body. functions: std.ArrayListUnmanaged(std.wasm.Func) = .{}, /// List of indexes pointing to an entry within the `functions` list which has been removed. functions_free_list: std.ArrayListUnmanaged(u32) = .{}, /// Map of symbol locations, represented by its `types.Import`. imports: std.AutoHashMapUnmanaged(Symbol.Index, types.Import) = .{}, /// List of WebAssembly globals. globals: std.ArrayListUnmanaged(std.wasm.Global) = .{}, /// Mapping between an `Atom` and its type index representing the Wasm /// type of the function signature. atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{}, /// List of all symbols generated by Zig code. symbols: std.ArrayListUnmanaged(Symbol) = .{}, /// Map from symbol name offset to their index into the `symbols` list. global_syms: std.AutoHashMapUnmanaged(u32, Symbol.Index) = .{}, /// List of symbol indexes which are free to be used. symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{}, /// Extra metadata about the linking section, such as alignment of segments and their name. segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, /// List of indexes which contain a free slot in the `segment_info` list. segment_free_list: std.ArrayListUnmanaged(u32) = .{}, /// File encapsulated string table, used to deduplicate strings within the generated file. string_table: StringTable = .{}, /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{}, /// List of atom indexes of functions that are generated by the backend. synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{}, /// Represents the symbol index of the error name table /// When this is `null`, no code references an error using runtime `@errorName`. /// During initializion, a symbol with corresponding atom will be created that is /// used to perform relocations to the pointer of this table. /// The actual table is populated during `flush`. error_table_symbol: Symbol.Index = .null, /// Atom index of the table of symbol names. This is stored so we can clean up the atom. error_names_atom: Atom.Index = .null, /// Amount of functions in the `import` sections. imported_functions_count: u32 = 0, /// Amount of globals in the `import` section. imported_globals_count: u32 = 0, /// Symbol index representing the stack pointer. This will be set upon initializion /// of a new `ZigObject`. Codegen will make calls into this to create relocations for /// this symbol each time the stack pointer is moved. stack_pointer_sym: Symbol.Index, /// Debug information for the Zig module. dwarf: ?Dwarf = null, // Debug section atoms. These are only set when the current compilation // unit contains Zig code. The lifetime of these atoms are extended // until the end of the compiler's lifetime. Meaning they're not freed // during `flush()` in incremental-mode. debug_info_atom: ?Atom.Index = null, debug_line_atom: ?Atom.Index = null, debug_loc_atom: ?Atom.Index = null, debug_ranges_atom: ?Atom.Index = null, debug_abbrev_atom: ?Atom.Index = null, debug_str_atom: ?Atom.Index = null, debug_pubnames_atom: ?Atom.Index = null, debug_pubtypes_atom: ?Atom.Index = null, /// The index of the segment representing the custom '.debug_info' section. debug_info_index: ?u32 = null, /// The index of the segment representing the custom '.debug_line' section. debug_line_index: ?u32 = null, /// The index of the segment representing the custom '.debug_loc' section. debug_loc_index: ?u32 = null, /// The index of the segment representing the custom '.debug_ranges' section. debug_ranges_index: ?u32 = null, /// The index of the segment representing the custom '.debug_pubnames' section. debug_pubnames_index: ?u32 = null, /// The index of the segment representing the custom '.debug_pubtypes' section. debug_pubtypes_index: ?u32 = null, /// The index of the segment representing the custom '.debug_pubtypes' section. debug_str_index: ?u32 = null, /// The index of the segment representing the custom '.debug_pubtypes' section. debug_abbrev_index: ?u32 = null, const DeclInfo = struct { atom: Atom.Index = .null, exports: std.ArrayListUnmanaged(Symbol.Index) = .{}, fn @"export"(di: DeclInfo, zig_object: *const ZigObject, name: []const u8) ?Symbol.Index { for (di.exports.items) |sym_index| { const sym_name_index = zig_object.symbol(sym_index).name; const sym_name = zig_object.string_table.getAssumeExists(sym_name_index); if (std.mem.eql(u8, name, sym_name)) { return sym_index; } } return null; } fn appendExport(di: *DeclInfo, gpa: std.mem.Allocator, sym_index: Symbol.Index) !void { return di.exports.append(gpa, sym_index); } fn deleteExport(di: *DeclInfo, sym_index: Symbol.Index) void { for (di.exports.items, 0..) |idx, index| { if (idx == sym_index) { _ = di.exports.swapRemove(index); return; } } unreachable; // invalid sym_index } }; /// Initializes the `ZigObject` with initial symbols. pub fn init(zig_object: *ZigObject, wasm_file: *Wasm) !void { // Initialize an undefined global with the name __stack_pointer. Codegen will use // this to generate relocations when moving the stack pointer. This symbol will be // resolved automatically by the final linking stage. try zig_object.createStackPointer(wasm_file); // TODO: Initialize debug information when we reimplement Dwarf support. } fn createStackPointer(zig_object: *ZigObject, wasm_file: *Wasm) !void { const gpa = wasm_file.base.comp.gpa; const sym_index = try zig_object.getGlobalSymbol(gpa, "__stack_pointer"); const sym = zig_object.symbol(sym_index); sym.index = zig_object.imported_globals_count; sym.tag = .global; const is_wasm32 = wasm_file.base.comp.root_mod.resolved_target.result.cpu.arch == .wasm32; try zig_object.imports.putNoClobber(gpa, sym_index, .{ .name = sym.name, .module_name = try zig_object.string_table.insert(gpa, wasm_file.host_name), .kind = .{ .global = .{ .valtype = if (is_wasm32) .i32 else .i64, .mutable = true } }, }); zig_object.imported_globals_count += 1; zig_object.stack_pointer_sym = sym_index; } fn symbol(zig_object: *const ZigObject, index: Symbol.Index) *Symbol { return &zig_object.symbols.items[@intFromEnum(index)]; } /// Frees and invalidates all memory of the incrementally compiled Zig module. /// It is illegal behavior to access the `ZigObject` after calling `deinit`. pub fn deinit(zig_object: *ZigObject, wasm_file: *Wasm) void { const gpa = wasm_file.base.comp.gpa; for (zig_object.segment_info.items) |segment_info| { gpa.free(segment_info.name); } { var it = zig_object.decls_map.valueIterator(); while (it.next()) |decl_info| { const atom = wasm_file.getAtomPtr(decl_info.atom); for (atom.locals.items) |local_index| { const local_atom = wasm_file.getAtomPtr(local_index); local_atom.deinit(gpa); } atom.deinit(gpa); decl_info.exports.deinit(gpa); } } { for (zig_object.anon_decls.values()) |atom_index| { const atom = wasm_file.getAtomPtr(atom_index); for (atom.locals.items) |local_index| { const local_atom = wasm_file.getAtomPtr(local_index); local_atom.deinit(gpa); } atom.deinit(gpa); } } if (zig_object.findGlobalSymbol("__zig_errors_len")) |sym_index| { const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index }).?; wasm_file.getAtomPtr(atom_index).deinit(gpa); } if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol })) |atom_index| { const atom = wasm_file.getAtomPtr(atom_index); atom.deinit(gpa); } for (zig_object.synthetic_functions.items) |atom_index| { const atom = wasm_file.getAtomPtr(atom_index); atom.deinit(gpa); } zig_object.synthetic_functions.deinit(gpa); for (zig_object.func_types.items) |*ty| { ty.deinit(gpa); } if (zig_object.error_names_atom != .null) { const atom = wasm_file.getAtomPtr(zig_object.error_names_atom); atom.deinit(gpa); } zig_object.global_syms.deinit(gpa); zig_object.func_types.deinit(gpa); zig_object.atom_types.deinit(gpa); zig_object.functions.deinit(gpa); zig_object.imports.deinit(gpa); zig_object.decls_map.deinit(gpa); zig_object.anon_decls.deinit(gpa); zig_object.symbols.deinit(gpa); zig_object.symbols_free_list.deinit(gpa); zig_object.segment_info.deinit(gpa); zig_object.segment_free_list.deinit(gpa); zig_object.string_table.deinit(gpa); if (zig_object.dwarf) |*dwarf| { dwarf.deinit(); } gpa.free(zig_object.path); zig_object.* = undefined; } /// Allocates a new symbol and returns its index. /// Will re-use slots when a symbol was freed at an earlier stage. pub fn allocateSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator) !Symbol.Index { try zig_object.symbols.ensureUnusedCapacity(gpa, 1); const sym: Symbol = .{ .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .tag = .undefined, // will be set after updateDecl .index = std.math.maxInt(u32), // will be set during atom parsing .virtual_address = std.math.maxInt(u32), // will be set during atom allocation }; if (zig_object.symbols_free_list.popOrNull()) |index| { zig_object.symbols.items[@intFromEnum(index)] = sym; return index; } const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len); zig_object.symbols.appendAssumeCapacity(sym); return index; } // Generate code for the Decl, storing it in memory to be later written to // the file on flush(). pub fn updateDecl( zig_object: *ZigObject, wasm_file: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex, ) !void { const decl = mod.declPtr(decl_index); if (decl.val.getFunction(mod)) |_| { return; } else if (decl.val.getExternFunc(mod)) |_| { return; } const gpa = wasm_file.base.comp.gpa; const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const atom = wasm_file.getAtomPtr(atom_index); atom.clear(); if (decl.isExtern(mod)) { const variable = decl.getOwnedVariable(mod).?; const name = mod.intern_pool.stringToSlice(decl.name); const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); return zig_object.addOrUpdateImport(wasm_file, name, atom.sym_index, lib_name, null); } const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); const res = try codegen.generateSymbol( &wasm_file.base, decl.srcLoc(mod), .{ .ty = decl.ty, .val = val }, &code_writer, .none, .{ .parent_atom_index = @intFromEnum(atom.sym_index) }, ); const code = switch (res) { .ok => code_writer.items, .fail => |em| { decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); return; }, }; return zig_object.finishUpdateDecl(wasm_file, decl_index, code); } pub fn updateFunc( zig_object: *ZigObject, wasm_file: *Wasm, mod: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness, ) !void { const gpa = wasm_file.base.comp.gpa; const func = mod.funcInfo(func_index); const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const atom = wasm_file.getAtomPtr(atom_index); atom.clear(); var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); const result = try codegen.generateFunction( &wasm_file.base, decl.srcLoc(mod), func_index, air, liveness, &code_writer, .none, ); const code = switch (result) { .ok => code_writer.items, .fail => |em| { decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); return; }, }; return zig_object.finishUpdateDecl(wasm_file, decl_index, code); } fn finishUpdateDecl( zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex, code: []const u8, ) !void { const gpa = wasm_file.base.comp.gpa; const mod = wasm_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_info = zig_object.decls_map.get(decl_index).?; const atom_index = decl_info.atom; const atom = wasm_file.getAtomPtr(atom_index); const sym = zig_object.symbol(atom.sym_index); const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); sym.name = try zig_object.string_table.insert(gpa, full_name); try atom.code.appendSlice(gpa, code); atom.size = @intCast(code.len); switch (decl.ty.zigTypeTag(mod)) { .Fn => { sym.index = try zig_object.appendFunction(gpa, .{ .type_index = zig_object.atom_types.get(atom_index).? }); sym.tag = .function; }, else => { const segment_name: []const u8 = if (decl.getOwnedVariable(mod)) |variable| name: { if (variable.is_const) { break :name ".rodata."; } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) { const decl_namespace = mod.namespacePtr(decl.src_namespace); const optimize_mode = decl_namespace.file_scope.mod.optimize_mode; const is_initialized = switch (optimize_mode) { .Debug, .ReleaseSafe => true, .ReleaseFast, .ReleaseSmall => false, }; if (is_initialized) { break :name ".data."; } break :name ".bss."; } // when the decl is all zeroes, we store the atom in the bss segment, // in all other cases it will be in the data segment. for (atom.code.items) |byte| { if (byte != 0) break :name ".data."; } break :name ".bss."; } else ".rodata."; if ((wasm_file.base.isObject() or wasm_file.base.comp.config.import_memory) and std.mem.startsWith(u8, segment_name, ".bss")) { @memset(atom.code.items, 0); } // Will be freed upon freeing of decl or after cleanup of Wasm binary. const full_segment_name = try std.mem.concat(gpa, u8, &.{ segment_name, full_name, }); errdefer gpa.free(full_segment_name); sym.tag = .data; sym.index = try zig_object.createDataSegment(gpa, full_segment_name, decl.alignment); }, } if (code.len == 0) return; atom.alignment = decl.getAlignment(mod); } /// Creates and initializes a new segment in the 'Data' section. /// Reuses free slots in the list of segments and returns the index. fn createDataSegment( zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8, alignment: InternPool.Alignment, ) !u32 { const segment_index: u32 = if (zig_object.segment_free_list.popOrNull()) |index| index else index: { const idx: u32 = @intCast(zig_object.segment_info.items.len); _ = try zig_object.segment_info.addOne(gpa); break :index idx; }; zig_object.segment_info.items[segment_index] = .{ .alignment = alignment, .flags = 0, .name = name, }; return segment_index; } /// For a given `InternPool.DeclIndex` returns its corresponding `Atom.Index`. /// When the index was not found, a new `Atom` will be created, and its index will be returned. /// The newly created Atom is empty with default fields as specified by `Atom.empty`. pub fn getOrCreateAtomForDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index { const gpa = wasm_file.base.comp.gpa; const gop = try zig_object.decls_map.getOrPut(gpa, decl_index); if (!gop.found_existing) { const sym_index = try zig_object.allocateSymbol(gpa); gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) }; const mod = wasm_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); const sym = zig_object.symbol(sym_index); sym.name = try zig_object.string_table.insert(gpa, full_name); } return gop.value_ptr.atom; } pub fn lowerAnonDecl( zig_object: *ZigObject, wasm_file: *Wasm, decl_val: InternPool.Index, explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { const gpa = wasm_file.base.comp.gpa; const gop = try zig_object.anon_decls.getOrPut(gpa, decl_val); if (!gop.found_existing) { const mod = wasm_file.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) }; var name_buf: [32]u8 = undefined; const name = std.fmt.bufPrint(&name_buf, "__anon_{d}", .{ @intFromEnum(decl_val), }) catch unreachable; switch (try zig_object.lowerConst(wasm_file, name, tv, src_loc)) { .ok => |atom_index| zig_object.anon_decls.values()[gop.index] = atom_index, .fail => |em| return .{ .fail = em }, } } const atom = wasm_file.getAtomPtr(zig_object.anon_decls.values()[gop.index]); atom.alignment = switch (atom.alignment) { .none => explicit_alignment, else => switch (explicit_alignment) { .none => atom.alignment, else => atom.alignment.maxStrict(explicit_alignment), }, }; return .ok; } /// Lowers a constant typed value to a local symbol and atom. /// Returns the symbol index of the local /// The given `decl` is the parent decl whom owns the constant. pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { const gpa = wasm_file.base.comp.gpa; const mod = wasm_file.base.comp.module.?; std.debug.assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions const decl = mod.declPtr(decl_index); const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const parent_atom = wasm_file.getAtom(parent_atom_index); const local_index = parent_atom.locals.items.len; const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{ fqn, local_index, }); defer gpa.free(name); switch (try zig_object.lowerConst(wasm_file, name, tv, decl.srcLoc(mod))) { .ok => |atom_index| { try wasm_file.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index); return @intFromEnum(wasm_file.getAtom(atom_index).sym_index); }, .fail => |em| { decl.analysis = .codegen_failure; try mod.failed_decls.put(mod.gpa, decl_index, em); return error.CodegenFail; }, } } const LowerConstResult = union(enum) { ok: Atom.Index, fail: *Module.ErrorMsg, }; fn lowerConst(zig_object: *ZigObject, wasm_file: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult { const gpa = wasm_file.base.comp.gpa; const mod = wasm_file.base.comp.module.?; // Create and initialize a new local symbol and atom const sym_index = try zig_object.allocateSymbol(gpa); const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); var value_bytes = std.ArrayList(u8).init(gpa); defer value_bytes.deinit(); const code = code: { const atom = wasm_file.getAtomPtr(atom_index); atom.alignment = tv.ty.abiAlignment(mod); const segment_name = try std.mem.concat(gpa, u8, &.{ ".rodata.", name }); errdefer gpa.free(segment_name); zig_object.symbol(sym_index).* = .{ .name = try zig_object.string_table.insert(gpa, name), .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .tag = .data, .index = try zig_object.createDataSegment( gpa, segment_name, tv.ty.abiAlignment(mod), ), .virtual_address = undefined, }; const result = try codegen.generateSymbol( &wasm_file.base, src_loc, tv, &value_bytes, .none, .{ .parent_atom_index = @intFromEnum(atom.sym_index), .addend = null, }, ); break :code switch (result) { .ok => value_bytes.items, .fail => |em| { return .{ .fail = em }; }, }; }; const atom = wasm_file.getAtomPtr(atom_index); atom.size = @intCast(code.len); try atom.code.appendSlice(gpa, code); return .{ .ok = atom_index }; } /// Returns the symbol index of the error name table. /// /// When the symbol does not yet exist, it will create a new one instead. pub fn getErrorTableSymbol(zig_object: *ZigObject, wasm_file: *Wasm) !Symbol.Index { if (zig_object.error_table_symbol != .null) { return zig_object.error_table_symbol; } // no error was referenced yet, so create a new symbol and atom for it // and then return said symbol's index. The final table will be populated // during `flush` when we know all possible error names. const gpa = wasm_file.base.comp.gpa; const sym_index = try zig_object.allocateSymbol(gpa); const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); const atom = wasm_file.getAtomPtr(atom_index); const slice_ty = Type.slice_const_u8_sentinel_0; const mod = wasm_file.base.comp.module.?; atom.alignment = slice_ty.abiAlignment(mod); const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_name_table"); const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_name_table"); const sym = zig_object.symbol(sym_index); sym.* = .{ .name = sym_name, .tag = .data, .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .index = try zig_object.createDataSegment(gpa, segment_name, atom.alignment), .virtual_address = undefined, }; log.debug("Error name table was created with symbol index: ({d})", .{@intFromEnum(sym_index)}); zig_object.error_table_symbol = sym_index; return sym_index; } /// Populates the error name table, when `error_table_symbol` is not null. /// /// This creates a table that consists of pointers and length to each error name. /// The table is what is being pointed to within the runtime bodies that are generated. fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void { if (zig_object.error_table_symbol == .null) return; const gpa = wasm_file.base.comp.gpa; const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = zig_object.error_table_symbol }).?; // Rather than creating a symbol for each individual error name, // we create a symbol for the entire region of error names. We then calculate // the pointers into the list using addends which are appended to the relocation. const names_sym_index = try zig_object.allocateSymbol(gpa); const names_atom_index = try wasm_file.createAtom(names_sym_index, zig_object.index); const names_atom = wasm_file.getAtomPtr(names_atom_index); names_atom.alignment = .@"1"; const sym_name = try zig_object.string_table.insert(gpa, "__zig_err_names"); const segment_name = try gpa.dupe(u8, ".rodata.__zig_err_names"); const names_symbol = zig_object.symbol(names_sym_index); names_symbol.* = .{ .name = sym_name, .tag = .data, .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .index = try zig_object.createDataSegment(gpa, segment_name, names_atom.alignment), .virtual_address = undefined, }; log.debug("Populating error names", .{}); // Addend for each relocation to the table var addend: u32 = 0; const mod = wasm_file.base.comp.module.?; for (mod.global_error_set.keys()) |error_name_nts| { const atom = wasm_file.getAtomPtr(atom_index); const error_name = mod.intern_pool.stringToSlice(error_name_nts); const len: u32 = @intCast(error_name.len + 1); // names are 0-terminated const slice_ty = Type.slice_const_u8_sentinel_0; const offset = @as(u32, @intCast(atom.code.items.len)); // first we create the data for the slice of the name try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated try atom.code.writer(gpa).writeInt(u32, len - 1, .little); // create relocation to the error name try atom.relocs.append(gpa, .{ .index = @intFromEnum(names_atom.sym_index), .relocation_type = .R_WASM_MEMORY_ADDR_I32, .offset = offset, .addend = @intCast(addend), }); atom.size += @intCast(slice_ty.abiSize(mod)); addend += len; // as we updated the error name table, we now store the actual name within the names atom try names_atom.code.ensureUnusedCapacity(gpa, len); names_atom.code.appendSliceAssumeCapacity(error_name); names_atom.code.appendAssumeCapacity(0); log.debug("Populated error name: '{s}'", .{error_name}); } names_atom.size = addend; zig_object.error_names_atom = names_atom_index; } /// Either creates a new import, or updates one if existing. /// When `type_index` is non-null, we assume an external function. /// In all other cases, a data-symbol will be created instead. pub fn addOrUpdateImport( zig_object: *ZigObject, wasm_file: *Wasm, /// Name of the import name: []const u8, /// Symbol index that is external symbol_index: Symbol.Index, /// Optional library name (i.e. `extern "c" fn foo() void` lib_name: ?[:0]const u8, /// The index of the type that represents the function signature /// when the extern is a function. When this is null, a data-symbol /// is asserted instead. type_index: ?u32, ) !void { const gpa = wasm_file.base.comp.gpa; std.debug.assert(symbol_index != .null); // For the import name, we use the decl's name, rather than the fully qualified name // Also mangle the name when the lib name is set and not equal to "C" so imports with the same // name but different module can be resolved correctly. const mangle_name = lib_name != null and !std.mem.eql(u8, lib_name.?, "c"); const full_name = if (mangle_name) full_name: { break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? }); } else name; defer if (mangle_name) gpa.free(full_name); const decl_name_index = try zig_object.string_table.insert(gpa, full_name); const sym: *Symbol = &zig_object.symbols.items[@intFromEnum(symbol_index)]; sym.setUndefined(true); sym.setGlobal(true); sym.name = decl_name_index; if (mangle_name) { // we specified a specific name for the symbol that does not match the import name sym.setFlag(.WASM_SYM_EXPLICIT_NAME); } if (type_index) |ty_index| { const gop = try zig_object.imports.getOrPut(gpa, symbol_index); const module_name = if (lib_name) |l_name| l_name else wasm_file.host_name; if (!gop.found_existing) { zig_object.imported_functions_count += 1; } gop.value_ptr.* = .{ .module_name = try zig_object.string_table.insert(gpa, module_name), .name = try zig_object.string_table.insert(gpa, name), .kind = .{ .function = ty_index }, }; sym.tag = .function; } else { sym.tag = .data; } } /// Returns the symbol index from a symbol of which its flag is set global, /// such as an exported or imported symbol. /// If the symbol does not yet exist, creates a new one symbol instead /// and then returns the index to it. pub fn getGlobalSymbol(zig_object: *ZigObject, gpa: std.mem.Allocator, name: []const u8) !Symbol.Index { const name_index = try zig_object.string_table.insert(gpa, name); const gop = try zig_object.global_syms.getOrPut(gpa, name_index); if (gop.found_existing) { return gop.value_ptr.*; } var sym: Symbol = .{ .name = name_index, .flags = 0, .index = undefined, // index to type will be set after merging symbols .tag = .function, .virtual_address = std.math.maxInt(u32), }; sym.setGlobal(true); sym.setUndefined(true); const sym_index = if (zig_object.symbols_free_list.popOrNull()) |index| index else blk: { const index: Symbol.Index = @enumFromInt(zig_object.symbols.items.len); try zig_object.symbols.ensureUnusedCapacity(gpa, 1); zig_object.symbols.items.len += 1; break :blk index; }; zig_object.symbol(sym_index).* = sym; gop.value_ptr.* = sym_index; return sym_index; } /// For a given decl, find the given symbol index's atom, and create a relocation for the type. /// Returns the given pointer address pub fn getDeclVAddr( zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo, ) !u64 { const target = wasm_file.base.comp.root_mod.resolved_target.result; const gpa = wasm_file.base.comp.gpa; const mod = wasm_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const target_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const target_symbol_index = @intFromEnum(wasm_file.getAtom(target_atom_index).sym_index); std.debug.assert(reloc_info.parent_atom_index != 0); const atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?; const atom = wasm_file.getAtomPtr(atom_index); const is_wasm32 = target.cpu.arch == .wasm32; if (decl.ty.zigTypeTag(mod) == .Fn) { std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations try atom.relocs.append(gpa, .{ .index = target_symbol_index, .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, }); } else { try atom.relocs.append(gpa, .{ .index = target_symbol_index, .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, .addend = @intCast(reloc_info.addend), }); } // we do not know the final address at this point, // as atom allocation will determine the address and relocations // will calculate and rewrite this. Therefore, we simply return the symbol index // that was targeted. return target_symbol_index; } pub fn getAnonDeclVAddr( zig_object: *ZigObject, wasm_file: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo, ) !u64 { const gpa = wasm_file.base.comp.gpa; const target = wasm_file.base.comp.root_mod.resolved_target.result; const atom_index = zig_object.anon_decls.get(decl_val).?; const target_symbol_index = @intFromEnum(wasm_file.getAtom(atom_index).sym_index); const parent_atom_index = wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = @enumFromInt(reloc_info.parent_atom_index) }).?; const parent_atom = wasm_file.getAtomPtr(parent_atom_index); const is_wasm32 = target.cpu.arch == .wasm32; const mod = wasm_file.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); if (ty.zigTypeTag(mod) == .Fn) { std.debug.assert(reloc_info.addend == 0); // addend not allowed for function relocations try parent_atom.relocs.append(gpa, .{ .index = target_symbol_index, .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, }); } else { try parent_atom.relocs.append(gpa, .{ .index = target_symbol_index, .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, .addend = @intCast(reloc_info.addend), }); } // we do not know the final address at this point, // as atom allocation will determine the address and relocations // will calculate and rewrite this. Therefore, we simply return the symbol index // that was targeted. return target_symbol_index; } pub fn deleteDeclExport( zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex, name: InternPool.NullTerminatedString, ) void { const mod = wasm_file.base.comp.module.?; const decl_info = zig_object.decls_map.getPtr(decl_index) orelse return; const export_name = mod.intern_pool.stringToSlice(name); if (decl_info.@"export"(zig_object, export_name)) |sym_index| { const sym = zig_object.symbol(sym_index); decl_info.deleteExport(sym_index); std.debug.assert(zig_object.global_syms.remove(sym.name)); std.debug.assert(wasm_file.symbol_atom.remove(.{ .file = zig_object.index, .index = sym_index })); zig_object.symbols_free_list.append(wasm_file.base.comp.gpa, sym_index) catch {}; sym.tag = .dead; } } pub fn updateExports( zig_object: *ZigObject, wasm_file: *Wasm, mod: *Module, exported: Module.Exported, exports: []const *Module.Export, ) !void { const decl_index = switch (exported) { .decl_index => |i| i, .value => |val| { _ = val; @panic("TODO: implement Wasm linker code for exporting a constant value"); }, }; const decl = mod.declPtr(decl_index); const atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const decl_info = zig_object.decls_map.getPtr(decl_index).?; const atom = wasm_file.getAtom(atom_index); const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*; const gpa = mod.gpa; log.debug("Updating exports for decl '{s}'", .{mod.intern_pool.stringToSlice(decl.name)}); for (exports) |exp| { if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| { try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, decl.srcLoc(mod), "Unimplemented: ExportOptions.section '{s}'", .{section}, )); continue; } const export_string = mod.intern_pool.stringToSlice(exp.opts.name); const sym_index = if (decl_info.@"export"(zig_object, export_string)) |idx| idx else index: { const sym_index = try zig_object.allocateSymbol(gpa); try decl_info.appendExport(gpa, sym_index); break :index sym_index; }; const export_name = try zig_object.string_table.insert(gpa, export_string); const sym = zig_object.symbol(sym_index); sym.setGlobal(true); sym.setUndefined(false); sym.index = atom_sym.index; sym.tag = atom_sym.tag; sym.name = export_name; switch (exp.opts.linkage) { .internal => { sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); }, .weak => { sym.setFlag(.WASM_SYM_BINDING_WEAK); }, .strong => {}, // symbols are strong by default .link_once => { try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, decl.srcLoc(mod), "Unimplemented: LinkOnce", .{}, )); continue; }, } if (exp.opts.visibility == .hidden) { sym.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); } log.debug(" with name '{s}' - {}", .{ export_string, sym }); try zig_object.global_syms.put(gpa, export_name, sym_index); try wasm_file.symbol_atom.put(gpa, .{ .file = zig_object.index, .index = sym_index }, atom_index); } } pub fn freeDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_index: InternPool.DeclIndex) void { const gpa = wasm_file.base.comp.gpa; const mod = wasm_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_info = zig_object.decls_map.getPtr(decl_index).?; const atom_index = decl_info.atom; const atom = wasm_file.getAtomPtr(atom_index); zig_object.symbols_free_list.append(gpa, atom.sym_index) catch {}; for (decl_info.exports.items) |exp_sym_index| { const exp_sym = zig_object.symbol(exp_sym_index); exp_sym.tag = .dead; zig_object.symbols_free_list.append(exp_sym_index) catch {}; } decl_info.exports.deinit(gpa); std.debug.assert(zig_object.decls_map.remove(decl_index)); const sym = &zig_object.symbols.items[atom.sym_index]; for (atom.locals.items) |local_atom_index| { const local_atom = wasm_file.getAtom(local_atom_index); const local_symbol = &zig_object.symbols.items[local_atom.sym_index]; std.debug.assert(local_symbol.tag == .data); zig_object.symbols_free_list.append(gpa, local_atom.sym_index) catch {}; std.debug.assert(wasm_file.symbol_atom.remove(local_atom.symbolLoc())); local_symbol.tag = .dead; // also for any local symbol const segment = &zig_object.segment_info.items[local_atom.sym_index]; gpa.free(segment.name); segment.name = &.{}; // Ensure no accidental double free } if (decl.isExtern(mod)) { std.debug.assert(zig_object.imports.remove(atom.sym_index)); } std.debug.assert(wasm_file.symbol_atom.remove(atom.symbolLoc())); // if (wasm.dwarf) |*dwarf| { // dwarf.freeDecl(decl_index); // } atom.prev = null; sym.tag = .dead; if (sym.isGlobal()) { std.debug.assert(zig_object.global_syms.remove(atom.sym_index)); } switch (decl.ty.zigTypeTag(mod)) { .Fn => { zig_object.functions_free_list.append(gpa, sym.index) catch {}; std.debug.assert(zig_object.atom_types.remove(atom_index)); }, else => { zig_object.segment_free_list.append(gpa, sym.index) catch {}; const segment = &zig_object.segment_info.items[sym.index]; gpa.free(segment.name); segment.name = &.{}; // Prevent accidental double free }, } } fn getTypeIndex(zig_object: *const ZigObject, func_type: std.wasm.Type) ?u32 { var index: u32 = 0; while (index < zig_object.func_types.items.len) : (index += 1) { if (zig_object.func_types.items[index].eql(func_type)) return index; } return null; } /// Searches for a matching function signature. When no matching signature is found, /// a new entry will be made. The value returned is the index of the type within `wasm.func_types`. pub fn putOrGetFuncType(zig_object: *ZigObject, gpa: std.mem.Allocator, func_type: std.wasm.Type) !u32 { if (zig_object.getTypeIndex(func_type)) |index| { return index; } // functype does not exist. const index: u32 = @intCast(zig_object.func_types.items.len); const params = try gpa.dupe(std.wasm.Valtype, func_type.params); errdefer gpa.free(params); const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns); errdefer gpa.free(returns); try zig_object.func_types.append(gpa, .{ .params = params, .returns = returns, }); return index; } /// Generates an atom containing the global error set' size. /// This will only be generated if the symbol exists. fn setupErrorsLen(zig_object: *ZigObject, wasm_file: *Wasm) !void { const gpa = wasm_file.base.comp.gpa; const sym_index = zig_object.findGlobalSymbol("__zig_errors_len") orelse return; const errors_len = wasm_file.base.comp.module.?.global_error_set.count(); // overwrite existing atom if it already exists (maybe the error set has increased) // if not, allcoate a new atom. const atom_index = if (wasm_file.symbol_atom.get(.{ .file = zig_object.index, .index = sym_index })) |index| blk: { const atom = wasm_file.getAtomPtr(index); atom.prev = .null; atom.deinit(gpa); break :blk index; } else idx: { // We found a call to __zig_errors_len so make the symbol a local symbol // and define it, so the final binary or resulting object file will not attempt // to resolve it. const sym = zig_object.symbol(sym_index); sym.setGlobal(false); sym.setUndefined(false); sym.tag = .data; const segment_name = try gpa.dupe(u8, ".rodata.__zig_errors_len"); sym.index = try zig_object.createDataSegment(gpa, segment_name, .@"2"); break :idx try wasm_file.createAtom(sym_index, zig_object.index); }; const atom = wasm_file.getAtomPtr(atom_index); atom.code.clearRetainingCapacity(); atom.sym_index = sym_index; atom.size = 2; atom.alignment = .@"2"; try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little); } fn findGlobalSymbol(zig_object: *ZigObject, name: []const u8) ?Symbol.Index { const offset = zig_object.string_table.getOffset(name) orelse return null; return zig_object.global_syms.get(offset); } /// Initializes symbols and atoms for the debug sections /// Initialization is only done when compiling Zig code. /// When Zig is invoked as a linker instead, the atoms /// and symbols come from the object files instead. pub fn initDebugSections(zig_object: *ZigObject) !void { if (zig_object.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections std.debug.assert(zig_object.debug_info_index == null); // this will create an Atom and set the index for us. zig_object.debug_info_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_info_index, ".debug_info"); zig_object.debug_line_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_line_index, ".debug_line"); zig_object.debug_loc_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_loc_index, ".debug_loc"); zig_object.debug_abbrev_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_abbrev_index, ".debug_abbrev"); zig_object.debug_ranges_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_ranges_index, ".debug_ranges"); zig_object.debug_str_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_str_index, ".debug_str"); zig_object.debug_pubnames_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubnames_index, ".debug_pubnames"); zig_object.debug_pubtypes_atom = try zig_object.createDebugSectionForIndex(&zig_object.debug_pubtypes_index, ".debug_pubtypes"); } /// From a given index variable, creates a new debug section. /// This initializes the index, appends a new segment, /// and finally, creates a managed `Atom`. pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, index: *?u32, name: []const u8) !Atom.Index { const gpa = wasm_file.base.comp.gpa; const new_index: u32 = @intCast(zig_object.segments.items.len); index.* = new_index; try zig_object.appendDummySegment(); const sym_index = try zig_object.allocateSymbol(gpa); const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); const atom = wasm_file.getAtomPtr(atom_index); zig_object.symbols.items[sym_index] = .{ .tag = .section, .name = try zig_object.string_table.put(gpa, name), .index = 0, .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), }; atom.alignment = .@"1"; // debug sections are always 1-byte-aligned return atom_index; } pub fn updateDeclLineNumber(zig_object: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void { if (zig_object.dwarf) |*dw| { const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); try dw.updateDeclLineNumber(mod, decl_index); } } /// Allocates debug atoms into their respective debug sections /// to merge them with maybe-existing debug atoms from object files. fn allocateDebugAtoms(zig_object: *ZigObject) !void { if (zig_object.dwarf == null) return; const allocAtom = struct { fn f(ctx: *ZigObject, maybe_index: *?u32, atom_index: Atom.Index) !void { const index = maybe_index.* orelse idx: { const index = @as(u32, @intCast(ctx.segments.items.len)); try ctx.appendDummySegment(); maybe_index.* = index; break :idx index; }; const atom = ctx.getAtomPtr(atom_index); atom.size = @as(u32, @intCast(atom.code.items.len)); ctx.symbols.items[atom.sym_index].index = index; try ctx.appendAtomAtIndex(index, atom_index); } }.f; try allocAtom(zig_object, &zig_object.debug_info_index, zig_object.debug_info_atom.?); try allocAtom(zig_object, &zig_object.debug_line_index, zig_object.debug_line_atom.?); try allocAtom(zig_object, &zig_object.debug_loc_index, zig_object.debug_loc_atom.?); try allocAtom(zig_object, &zig_object.debug_str_index, zig_object.debug_str_atom.?); try allocAtom(zig_object, &zig_object.debug_ranges_index, zig_object.debug_ranges_atom.?); try allocAtom(zig_object, &zig_object.debug_abbrev_index, zig_object.debug_abbrev_atom.?); try allocAtom(zig_object, &zig_object.debug_pubnames_index, zig_object.debug_pubnames_atom.?); try allocAtom(zig_object, &zig_object.debug_pubtypes_index, zig_object.debug_pubtypes_atom.?); } /// For the given `decl_index`, stores the corresponding type representing the function signature. /// Asserts declaration has an associated `Atom`. /// Returns the index into the list of types. pub fn storeDeclType(zig_object: *ZigObject, gpa: std.mem.Allocator, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 { const decl_info = zig_object.decls_map.get(decl_index).?; const index = try zig_object.putOrGetFuncType(gpa, func_type); try zig_object.atom_types.put(gpa, decl_info.atom, index); return index; } /// The symbols in ZigObject are already represented by an atom as we need to store its data. /// So rather than creating a new Atom and returning its index, we use this oppertunity to scan /// its relocations and create any GOT symbols or function table indexes it may require. pub fn parseSymbolIntoAtom(zig_object: *ZigObject, wasm_file: *Wasm, index: Symbol.Index) !Atom.Index { const gpa = wasm_file.base.comp.gpa; const loc: Wasm.SymbolLoc = .{ .file = zig_object.index, .index = index }; const atom_index = wasm_file.symbol_atom.get(loc).?; const final_index = try wasm_file.getMatchingSegment(zig_object.index, index); try wasm_file.appendAtomAtIndex(final_index, atom_index); const atom = wasm_file.getAtom(atom_index); for (atom.relocs.items) |reloc| { const reloc_index: Symbol.Index = @enumFromInt(reloc.index); switch (reloc.relocation_type) { .R_WASM_TABLE_INDEX_I32, .R_WASM_TABLE_INDEX_I64, .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => { try wasm_file.function_table.put(gpa, .{ .file = zig_object.index, .index = reloc_index, }, 0); }, .R_WASM_GLOBAL_INDEX_I32, .R_WASM_GLOBAL_INDEX_LEB, => { const sym = zig_object.symbol(reloc_index); if (sym.tag != .global) { try wasm_file.got_symbols.append(gpa, .{ .file = zig_object.index, .index = reloc_index, }); } }, else => {}, } } return atom_index; } /// Creates a new Wasm function with a given symbol name and body. /// Returns the symbol index of the new function. pub fn createFunction( zig_object: *ZigObject, wasm_file: *Wasm, symbol_name: []const u8, func_ty: std.wasm.Type, function_body: *std.ArrayList(u8), relocations: *std.ArrayList(types.Relocation), ) !Symbol.Index { const gpa = wasm_file.base.comp.gpa; const sym_index = try zig_object.allocateSymbol(gpa); const sym = zig_object.symbol(sym_index); sym.tag = .function; sym.name = try zig_object.string_table.insert(gpa, symbol_name); const type_index = try zig_object.putOrGetFuncType(gpa, func_ty); sym.index = try zig_object.appendFunction(gpa, .{ .type_index = type_index }); const atom_index = try wasm_file.createAtom(sym_index, zig_object.index); const atom = wasm_file.getAtomPtr(atom_index); atom.size = @intCast(function_body.items.len); atom.code = function_body.moveToUnmanaged(); atom.relocs = relocations.moveToUnmanaged(); try zig_object.synthetic_functions.append(gpa, atom_index); return sym_index; } /// Appends a new `std.wasm.Func` to the list of functions and returns its index. fn appendFunction(zig_object: *ZigObject, gpa: std.mem.Allocator, func: std.wasm.Func) !u32 { const index: u32 = if (zig_object.functions_free_list.popOrNull()) |idx| idx else idx: { const len: u32 = @intCast(zig_object.functions.items.len); _ = try zig_object.functions.addOne(gpa); break :idx len; }; zig_object.functions.items[index] = func; return index; } pub fn flushModule(zig_object: *ZigObject, wasm_file: *Wasm) !void { try zig_object.populateErrorNameTable(wasm_file); try zig_object.setupErrorsLen(wasm_file); } const build_options = @import("build_options"); const builtin = @import("builtin"); const codegen = @import("../../codegen.zig"); const link = @import("../../link.zig"); const log = std.log.scoped(.zig_object); const std = @import("std"); const types = @import("types.zig"); const Air = @import("../../Air.zig"); const Atom = @import("Atom.zig"); const Dwarf = @import("../Dwarf.zig"); const File = @import("file.zig").File; const InternPool = @import("../../InternPool.zig"); const Liveness = @import("../../Liveness.zig"); const Module = @import("../../Module.zig"); const StringTable = @import("../StringTable.zig"); const Symbol = @import("Symbol.zig"); const Type = @import("../../type.zig").Type; const TypedValue = @import("../../TypedValue.zig"); const Value = @import("../../Value.zig"); const Wasm = @import("../Wasm.zig"); const ZigObject = @This();