| author | |
| committer | |
| log | b1dd4b17d89633c4d26c39952f69d7ff2efb98ce |
| tree | c7aa18ada844a59848fba0b730ee69f04de65ffe |
| parent | b42caff2a20eb34073f6a766f55d27288028165a |
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 | } |
| 2248 | 2248 | ||
| 2249 | const test_default_usize: usize = 123; | ||
| 2250 | const test_default_usize_ptr: *align(1) const usize = &test_default_usize; | ||
| 2251 | const test_default_str: []const u8 = "test str"; | ||
| 2252 | const test_default_str_slice: [2][]const u8 = [_][]const u8{ | ||
| 2253 | "test1", | ||
| 2254 | "test2", | ||
| 2255 | }; | ||
| 2256 | |||
| 2257 | test "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 | |||
| 2249 | test "parse into struct where destination and source lengths mismatch" { | 2274 | test "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\"}"); |