authorgravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2023-02-04 14:42:42+01:00
committergravatar for john.schmidt.h@gmail.comJohn Schmidt <john.schmidt.h@gmail.com> 2023-02-04 20:51:15+01:00
logb1dd4b17d89633c4d26c39952f69d7ff2efb98ce
treec7aa18ada844a59848fba0b730ee69f04de65ffe
parentb42caff2a20eb34073f6a766f55d27288028165a

std.json: don't free struct default values

Closes https://github.com/ziglang/zig/issues/9509.

2 files changed, 56 insertions(+), 1 deletions(-)

lib/std/json.zig+31-1
......@@ -1745,7 +1745,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
17451745 .Struct => |structInfo| {
17461746 inline for (structInfo.fields) |field| {
17471747 if (!field.is_comptime) {
1748 parseFree(field.type, @field(value, field.name), options);
1748 var should_free = true;
1749 if (field.default_value) |default| {
1750 switch (@typeInfo(field.type)) {
1751 // We must not attempt to free pointers to struct default values
1752 .Pointer => |fieldPtrInfo| {
1753 const field_value = @field(value, field.name);
1754 const field_ptr = switch (fieldPtrInfo.size) {
1755 .One => field_value,
1756 .Slice => field_value.ptr,
1757 else => unreachable, // Other pointer types are not parseable
1758 };
1759 const field_addr = @ptrToInt(field_ptr);
1760
1761 const casted_default = @ptrCast(*const field.type, @alignCast(@alignOf(field.type), default)).*;
1762 const default_ptr = switch (fieldPtrInfo.size) {
1763 .One => casted_default,
1764 .Slice => casted_default.ptr,
1765 else => unreachable, // Other pointer types are not parseable
1766 };
1767 const default_addr = @ptrToInt(default_ptr);
1768
1769 if (field_addr == default_addr) {
1770 should_free = false;
1771 }
1772 },
1773 else => {},
1774 }
1775 }
1776 if (should_free) {
1777 parseFree(field.type, @field(value, field.name), options);
1778 }
17491779 }
17501780 }
17511781 },
lib/std/json/test.zig+25
......@@ -2246,6 +2246,31 @@ test "parse into struct with default const pointer field" {
22462246 try testing.expectEqual(T{}, try parse(T, &ts, .{}));
22472247}
22482248
2249const test_default_usize: usize = 123;
2250const test_default_usize_ptr: *align(1) const usize = &test_default_usize;
2251const test_default_str: []const u8 = "test str";
2252const test_default_str_slice: [2][]const u8 = [_][]const u8{
2253 "test1",
2254 "test2",
2255};
2256
2257test "freeing parsed structs with pointers to default values" {
2258 const T = struct {
2259 int: *const usize = &test_default_usize,
2260 int_ptr: *allowzero align(1) const usize = test_default_usize_ptr,
2261 str: []const u8 = test_default_str,
2262 str_slice: []const []const u8 = &test_default_str_slice,
2263 };
2264
2265 var ts = json.TokenStream.init("{}");
2266 const options = .{ .allocator = std.heap.page_allocator };
2267 const parsed = try json.parse(T, &ts, options);
2268
2269 try testing.expectEqual(T{}, parsed);
2270
2271 json.parseFree(T, parsed, options);
2272}
2273
22492274test "parse into struct where destination and source lengths mismatch" {
22502275 const T = struct { a: [2]u8 };
22512276 var ts = TokenStream.init("{\"a\": \"bbb\"}");