authorgravatar for matthew.h.borkowski@gmail.comMatthew Borkowski <matthew.h.borkowski@gmail.com> 2021-05-14 01:51:39-04:00
committergravatar for matthew.h.borkowski@gmail.comMatthew Borkowski <matthew.h.borkowski@gmail.com> 2021-05-14 01:51:39-04:00
log11fd2aa76770a0bef87a6ce304e1893b490bba83
treea57b3566d71b728d786640cd58e6fd8bcab8cfd3
parentcadb84b3ac891a99f7740be056d6bf6063b266f1

fix logic for duplicate comptime fields and avoid freeing comptime fields in parseFree and parseInternal


1 files changed, 39 insertions(+), 18 deletions(-)

lib/std/json.zig+39-18
......@@ -1573,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15731573 // .UseLast => {},
15741574 // }
15751575 if (options.duplicate_field_behavior == .UseFirst) {
1576 // do nothing, check of fields_seen[i] below will parse value without overwriting field
1576 // unconditonally ignore value. for comptime fields, this skips check against default_value
1577 parseFree(field.field_type, try parse(field.field_type, tokens, options), options);
1578 found = true;
1579 break;
15771580 } else if (options.duplicate_field_behavior == .Error) {
15781581 return error.DuplicateJSONField;
15791582 } else if (options.duplicate_field_behavior == .UseLast) {
1580 parseFree(field.field_type, @field(r, field.name), options);
1583 if (!field.is_comptime) {
1584 parseFree(field.field_type, @field(r, field.name), options);
1585 }
15811586 fields_seen[i] = false;
15821587 }
15831588 }
......@@ -1586,11 +1591,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
15861591 return error.UnexpectedValue;
15871592 }
15881593 } else {
1589 if (fields_seen[i]) {
1590 parseFree(field.field_type, try parse(field.field_type, tokens, options), options);
1591 } else {
1592 @field(r, field.name) = try parse(field.field_type, tokens, options);
1593 }
1594 @field(r, field.name) = try parse(field.field_type, tokens, options);
15941595 }
15951596 fields_seen[i] = true;
15961597 found = true;
......@@ -1735,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
17351736 },
17361737 .Struct => |structInfo| {
17371738 inline for (structInfo.fields) |field| {
1738 parseFree(field.field_type, @field(value, field.name), options);
1739 if (!field.is_comptime) {
1740 parseFree(field.field_type, @field(value, field.name), options);
1741 }
17391742 }
17401743 },
17411744 .Array => |arrayInfo| {
......@@ -1912,14 +1915,19 @@ test "parse with comptime field" {
19121915 },
19131916 };
19141917
1915 const r = try std.json.parse(T, &std.json.TokenStream.init(
1918 const options = ParseOptions{
1919 .allocator = std.testing.allocator,
1920 };
1921
1922 const r = try parse(T, &TokenStream.init(
19161923 \\{
19171924 \\ "kind": "float",
19181925 \\ "b": 1.0
19191926 \\}
1920 ), .{
1921 .allocator = std.testing.allocator,
1922 });
1927 ), options);
1928
1929 // check that parseFree doesn't try to free comptime fields
1930 parseFree(T, r, options);
19231931 }
19241932}
19251933
......@@ -2006,20 +2014,33 @@ test "parse into struct with duplicate field" {
20062014 const ballast = try testing.allocator.alloc(u64, 1);
20072015 defer testing.allocator.free(ballast);
20082016
2009 const options = ParseOptions{
2017 const options_first = ParseOptions{
2018 .allocator = testing.allocator,
2019 .duplicate_field_behavior = .UseFirst
2020 };
2021
2022 const options_last = ParseOptions{
20102023 .allocator = testing.allocator,
20112024 .duplicate_field_behavior = .UseLast,
20122025 };
2026
20132027 const str = "{ \"a\": 1, \"a\": 0.25 }";
20142028
20152029 const T1 = struct { a: *u64 };
2016 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options));
2030 // both .UseFirst and .UseLast should fail because second "a" value isn't a u64
2031 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_first));
2032 try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_last));
20172033
20182034 const T2 = struct { a: f64 };
2019 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options));
2020 try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str),
2021 .{ .duplicate_field_behavior = .UseFirst }
2022 ));
2035 try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first));
2036 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last));
2037
2038 const T3 = struct { comptime a: f64 = 1.0 };
2039 // .UseFirst should succeed because second "a" value is unconditionally ignored (even though != 1.0)
2040 const t3 = T3{ .a = 1.0 };
2041 try testing.expectEqual(t3, try parse(T3, &TokenStream.init(str), options_first));
2042 // .UseLast should fail because second "a" value is 0.25 which is not equal to default value of 1.0
2043 try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last));
20232044}
20242045
20252046/// A non-stream JSON parser which constructs a tree of Value's.