authorgravatar for jesse.rudolph@gmail.comJesse Rudolph <jesse.rudolph@gmail.com> 2020-07-07 14:19:34-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-10-17 22:35:36-04:00
loge821a57683e5cc69655e8aa2c2fa38b834b31c26
tree597490d87885918ed8e44034a7777075ddc2cd19
parentb0684bf084dbb5fe9ad7fb75dacf24cbd0a17231

add Builder.dupePkg()

mimics the duplication of strings in `Builder` for `Pkg`. This ensures the lifetime of the memory backing strings in a `Pkg` struct and the `Pkg.dependencies` slice is not shorter than the `Builder` that the data is associated with.

1 files changed, 59 insertions(+), 1 deletions(-)

lib/std/build.zig+59-1
......@@ -311,6 +311,23 @@ pub const Builder = struct {
311311 return the_copy;
312312 }
313313
314 pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
315 var the_copy = Pkg{
316 .name = self.dupe(package.name),
317 .path = self.dupePath(package.path),
318 };
319
320 if (package.dependencies) |dependencies| {
321 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
322 the_copy.dependencies = new_dependencies;
323
324 for (dependencies) |dep_package, i| {
325 new_dependencies[i] = self.dupePkg(dep_package);
326 }
327 }
328 return the_copy;
329 }
330
314331 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
315332 const write_file_step = self.addWriteFiles();
316333 write_file_step.add(file_path, data);
......@@ -1826,7 +1843,7 @@ pub const LibExeObjStep = struct {
18261843 }
18271844
18281845 pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
1829 self.packages.append(package) catch unreachable;
1846 self.packages.append(self.dupePkg(package)) catch unreachable;
18301847 }
18311848
18321849 pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
......@@ -2691,6 +2708,47 @@ pub const InstalledFile = struct {
26912708 path: []const u8,
26922709};
26932710
2711test "Builder.dupePkg()" {
2712 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
2713 defer arena.deinit();
2714 var builder = try Builder.create(
2715 &arena.allocator,
2716 "test",
2717 "test",
2718 "test",
2719 );
2720 defer builder.destroy();
2721
2722 var pkg_dep = Pkg{
2723 .name = "pkg_dep",
2724 .path = "/not/a/pkg_dep.zig",
2725 };
2726 var pkg_top = Pkg{
2727 .name = "pkg_top",
2728 .path = "/not/a/pkg_top.zig",
2729 .dependencies = &[_]Pkg{pkg_dep},
2730 };
2731 const dupe = builder.dupePkg(pkg_top);
2732
2733 const original_deps = pkg_top.dependencies.?;
2734 const dupe_deps = dupe.dependencies.?;
2735
2736 // probably the same top level package details
2737 std.testing.expectEqualStrings(pkg_top.name, dupe.name);
2738
2739 // probably the same dependencies
2740 std.testing.expectEqual(original_deps.len, dupe_deps.len);
2741 std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
2742
2743 // could segfault otherwise if pointers in duplicated package's fields are
2744 // the same as those in stack allocated package's fields
2745 std.testing.expect(dupe_deps.ptr != original_deps.ptr);
2746 std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
2747 std.testing.expect(dupe.path.ptr != pkg_top.path.ptr);
2748 std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
2749 std.testing.expect(dupe_deps[0].path.ptr != pkg_dep.path.ptr);
2750}
2751
26942752test "" {
26952753 // The only purpose of this test is to get all these untested functions
26962754 // to be referenced to avoid regression so it is okay to skip some targets.