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:...@@ -1573,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
1573 // .UseLast => {},1573 // .UseLast => {},
1574 // }1574 // }
1575 if (options.duplicate_field_behavior == .UseFirst) {1575 if (options.duplicate_field_behavior == .UseFirst) {
1576 // do nothing, check of fields_seen[i] below will parse value without overwriting field1576 // 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;
1577 } else if (options.duplicate_field_behavior == .Error) {1580 } else if (options.duplicate_field_behavior == .Error) {
1578 return error.DuplicateJSONField;1581 return error.DuplicateJSONField;
1579 } else if (options.duplicate_field_behavior == .UseLast) {1582 } 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 }
1581 fields_seen[i] = false;1586 fields_seen[i] = false;
1582 }1587 }
1583 }1588 }
...@@ -1586,11 +1591,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:...@@ -1586,11 +1591,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
1586 return error.UnexpectedValue;1591 return error.UnexpectedValue;
1587 }1592 }
1588 } else {1593 } else {
1589 if (fields_seen[i]) {1594 @field(r, field.name) = try parse(field.field_type, tokens, options);
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 }1595 }
1595 fields_seen[i] = true;1596 fields_seen[i] = true;
1596 found = true;1597 found = true;
...@@ -1735,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {...@@ -1735,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
1735 },1736 },
1736 .Struct => |structInfo| {1737 .Struct => |structInfo| {
1737 inline for (structInfo.fields) |field| {1738 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 }
1739 }1742 }
1740 },1743 },
1741 .Array => |arrayInfo| {1744 .Array => |arrayInfo| {
...@@ -1912,14 +1915,19 @@ test "parse with comptime field" {...@@ -1912,14 +1915,19 @@ test "parse with comptime field" {
1912 },1915 },
1913 };1916 };
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(
1916 \\{1923 \\{
1917 \\ "kind": "float",1924 \\ "kind": "float",
1918 \\ "b": 1.01925 \\ "b": 1.0
1919 \\}1926 \\}
1920 ), .{1927 ), options);
1921 .allocator = std.testing.allocator,1928
1922 });1929 // check that parseFree doesn't try to free comptime fields
1930 parseFree(T, r, options);
1923 }1931 }
1924}1932}
19251933
...@@ -2006,20 +2014,33 @@ test "parse into struct with duplicate field" {...@@ -2006,20 +2014,33 @@ test "parse into struct with duplicate field" {
2006 const ballast = try testing.allocator.alloc(u64, 1);2014 const ballast = try testing.allocator.alloc(u64, 1);
2007 defer testing.allocator.free(ballast);2015 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{
2010 .allocator = testing.allocator,2023 .allocator = testing.allocator,
2011 .duplicate_field_behavior = .UseLast,2024 .duplicate_field_behavior = .UseLast,
2012 };2025 };
2026
2013 const str = "{ \"a\": 1, \"a\": 0.25 }";2027 const str = "{ \"a\": 1, \"a\": 0.25 }";
20142028
2015 const T1 = struct { a: *u64 };2029 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
2018 const T2 = struct { a: f64 };2034 const T2 = struct { a: f64 };
2019 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options));2035 try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first));
2020 try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str),2036 try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last));
2021 .{ .duplicate_field_behavior = .UseFirst }2037
2022 ));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));
2023}2044}
20242045
2025/// A non-stream JSON parser which constructs a tree of Value's.2046/// A non-stream JSON parser which constructs a tree of Value's.