lib/choir/src/backends/artifact/model/artifact.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const artifact = @import("root.zig");
3
4 const Allocator = std.mem.Allocator;
5
6 pub const Artifact = struct {
7 allocator: Allocator,
8 metadata: artifact.Metadata,
9 options: artifact.Options,
10 linkage: artifact.Linkage,
11 payload: artifact.Payload,
12 verification: artifact.VerificationStatus = .{},
13
14 pub fn init(allocator: Allocator, metadata: artifact.Metadata) Allocator.Error!Artifact {
15 return .{
16 .allocator = allocator,
17 .metadata = try metadata.dupe(allocator),
18 .options = .init(allocator),
19 .linkage = .init(allocator),
20 .payload = .init(allocator),
21 };
22 }
23
24 pub fn clone(self: Artifact, allocator: Allocator) Allocator.Error!Artifact {
25 var copy = try Artifact.init(allocator, self.metadata);
26 errdefer copy.deinit();
27 try self.options.cloneInto(©.options);
28 try self.linkage.cloneInto(©.linkage);
29 try self.payload.cloneInto(©.payload);
30 try copy.verification.replace(copy.allocator, self.verification);
31 return copy;
32 }
33
34 pub fn eql(self: Artifact, other: Artifact) bool {
35 return self.metadata.eql(other.metadata) and
36 self.options.eql(other.options) and
37 self.linkage.eql(other.linkage) and
38 self.payload.eql(other.payload) and
39 self.verification.eql(other.verification);
40 }
41
42 pub fn deinit(self: *Artifact) void {
43 self.options.deinit();
44 self.linkage.deinit();
45 self.payload.deinit();
46 self.verification.deinit(self.allocator);
47 self.metadata.deinit(self.allocator);
48 self.* = undefined;
49 }
50 };
51
52 test "artifact owns provided symbol names" {
53 var value = try Artifact.init(std.testing.allocator, .{
54 .kind = .container,
55 .producer = "test",
56 });
57 defer value.deinit();
58
59 var name = [_]u8{ 'e', 'n', 't', 'r', 'y' };
60 try value.linkage.addProvided(.{ .name = &name, .kind = .function });
61 name[0] = 'x';
62
63 try std.testing.expectEqualStrings("entry", value.linkage.provided_symbols.items[0].name);
64 try std.testing.expect(value.linkage.hasProvided("entry"));
65 }
66
67 test "artifact clone survives every allocation failure" {
68 const Harness = struct {
69 fn run(allocator: Allocator) !void {
70 var value = try Artifact.init(allocator, .{
71 .kind = .object_file,
72 .producer = "allocation",
73 .target = .{ .architecture = .x86_64, .triple = "x86_64-linux", .features = &.{"sse2"} },
74 .abi = .{ .name = "sysv", .calling_convention = "c", .object_format = "elf" },
75 });
76 defer value.deinit();
77 try value.options.add(.{ .key = "opt", .value = .{ .text = "speed" } });
78 try value.linkage.addRequired(.{ .name = "runtime", .kind = .runtime });
79 try value.linkage.addProvided(.{ .name = "entry", .kind = .function });
80 try value.linkage.addRelocation(.{ .offset = 0, .symbol = "runtime" });
81 try value.payload.addDebugRecord(.{ .name = "lines", .kind = .line_table, .bytes = &.{1} });
82 try value.payload.addBuffer(.{ .name = "entry.o", .format = .object_file, .bytes = &.{2} });
83 try value.payload.addTextDump(.{ .name = "ir", .contents = "entry" });
84 try value.verification.replace(value.allocator, .{ .state = .passed, .stage = "verify", .message = "ok" });
85
86 var copy = try value.clone(allocator);
87 defer copy.deinit();
88 try std.testing.expect(copy.linkage.hasProvided("entry"));
89 try std.testing.expectEqualStrings("verify", copy.verification.stage.?);
90 try std.testing.expect(value.eql(copy));
91 copy.metadata.kind = .container;
92 try std.testing.expect(!value.eql(copy));
93 }
94 };
95
96 try std.testing.checkAllAllocationFailures(std.testing.allocator, Harness.run, .{});
97 }