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 {...@@ -1745,7 +1745,37 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1745 .Struct => |structInfo| {1745 .Struct => |structInfo| {
1746 inline for (structInfo.fields) |field| {1746 inline for (structInfo.fields) |field| {
1747 if (!field.is_comptime) {1747 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 }
1749 }1779 }
1750 }1780 }
1751 },1781 },
lib/std/json/test.zig+25
...@@ -2246,6 +2246,31 @@ test "parse into struct with default const pointer field" {...@@ -2246,6 +2246,31 @@ test "parse into struct with default const pointer field" {
2246 try testing.expectEqual(T{}, try parse(T, &ts, .{}));2246 try testing.expectEqual(T{}, try parse(T, &ts, .{}));
2247}2247}
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
2249test "parse into struct where destination and source lengths mismatch" {2274test "parse into struct where destination and source lengths mismatch" {
2250 const T = struct { a: [2]u8 };2275 const T = struct { a: [2]u8 };
2251 var ts = TokenStream.init("{\"a\": \"bbb\"}");2276 var ts = TokenStream.init("{\"a\": \"bbb\"}");