aboutsummaryrefslogtreecommitdiff
path: root/lib/std/json.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-05-15 21:44:38 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-05-15 21:44:38 -0700
commit597082adf45cebbf2c6a81c3f732b6d2ce4a1435 (patch)
tree9309977e204cceaac2c18efbea2153a69f86d1f4 /lib/std/json.zig
parent07606d12daabe8c201dba3d5b27e702ce58d0ffb (diff)
parentd98e39fa6864f287bc50f265f98b7195849afa68 (diff)
downloadzig-597082adf45cebbf2c6a81c3f732b6d2ce4a1435.tar.gz
zig-597082adf45cebbf2c6a81c3f732b6d2ce4a1435.zip
Merge remote-tracking branch 'origin/master' into stage2-whole-file-astgen
Conflicts: * build.zig * src/Compilation.zig * src/codegen/spirv/spec.zig * src/link/SpirV.zig * test/stage2/darwin.zig - this one might be problematic; start.zig looks for `main` in the root source file, not `_main`. Not sure why there is an underscore there in master branch.
Diffstat (limited to 'lib/std/json.zig')
-rw-r--r--lib/std/json.zig55
1 files changed, 45 insertions, 10 deletions
diff --git a/lib/std/json.zig b/lib/std/json.zig
index 109167bbc1..56d379f9f8 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -623,7 +623,7 @@ pub const StreamingParser = struct {
.ObjectSeparator => switch (c) {
':' => {
- p.state = .ValueBegin;
+ p.state = .ValueBeginNoClosing;
p.after_string_state = .ValueEnd;
},
0x09, 0x0A, 0x0D, 0x20 => {
@@ -1205,6 +1205,13 @@ test "json.token mismatched close" {
try testing.expectError(error.UnexpectedClosingBrace, p.next());
}
+test "json.token premature object close" {
+ var p = TokenStream.init("{ \"key\": }");
+ try checkNext(&p, .ObjectBegin);
+ try checkNext(&p, .String);
+ try testing.expectError(error.InvalidValueBegin, p.next());
+}
+
/// Validate a JSON string. This does not limit number precision so a decoder may not necessarily
/// be able to decode the string even if this returns true.
pub fn validate(s: []const u8) bool {
@@ -1566,11 +1573,16 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
// .UseLast => {},
// }
if (options.duplicate_field_behavior == .UseFirst) {
+ // unconditonally ignore value. for comptime fields, this skips check against default_value
+ parseFree(field.field_type, try parse(field.field_type, tokens, options), options);
+ found = true;
break;
} else if (options.duplicate_field_behavior == .Error) {
return error.DuplicateJSONField;
} else if (options.duplicate_field_behavior == .UseLast) {
- parseFree(field.field_type, @field(r, field.name), options);
+ if (!field.is_comptime) {
+ parseFree(field.field_type, @field(r, field.name), options);
+ }
fields_seen[i] = false;
}
}
@@ -1724,7 +1736,9 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
},
.Struct => |structInfo| {
inline for (structInfo.fields) |field| {
- parseFree(field.field_type, @field(value, field.name), options);
+ if (!field.is_comptime) {
+ parseFree(field.field_type, @field(value, field.name), options);
+ }
}
},
.Array => |arrayInfo| {
@@ -1901,14 +1915,19 @@ test "parse with comptime field" {
},
};
- const r = try std.json.parse(T, &std.json.TokenStream.init(
+ const options = ParseOptions{
+ .allocator = std.testing.allocator,
+ };
+
+ const r = try parse(T, &TokenStream.init(
\\{
\\ "kind": "float",
\\ "b": 1.0
\\}
- ), .{
- .allocator = std.testing.allocator,
- });
+ ), options);
+
+ // check that parseFree doesn't try to free comptime fields
+ parseFree(T, r, options);
}
}
@@ -1995,17 +2014,33 @@ test "parse into struct with duplicate field" {
const ballast = try testing.allocator.alloc(u64, 1);
defer testing.allocator.free(ballast);
- const options = ParseOptions{
+ const options_first = ParseOptions{
+ .allocator = testing.allocator,
+ .duplicate_field_behavior = .UseFirst
+ };
+
+ const options_last = ParseOptions{
.allocator = testing.allocator,
.duplicate_field_behavior = .UseLast,
};
+
const str = "{ \"a\": 1, \"a\": 0.25 }";
const T1 = struct { a: *u64 };
- try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options));
+ // both .UseFirst and .UseLast should fail because second "a" value isn't a u64
+ try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_first));
+ try testing.expectError(error.UnexpectedToken, parse(T1, &TokenStream.init(str), options_last));
const T2 = struct { a: f64 };
- try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options));
+ try testing.expectEqual(T2{ .a = 1.0 }, try parse(T2, &TokenStream.init(str), options_first));
+ try testing.expectEqual(T2{ .a = 0.25 }, try parse(T2, &TokenStream.init(str), options_last));
+
+ const T3 = struct { comptime a: f64 = 1.0 };
+ // .UseFirst should succeed because second "a" value is unconditionally ignored (even though != 1.0)
+ const t3 = T3{ .a = 1.0 };
+ try testing.expectEqual(t3, try parse(T3, &TokenStream.init(str), options_first));
+ // .UseLast should fail because second "a" value is 0.25 which is not equal to default value of 1.0
+ try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last));
}
/// A non-stream JSON parser which constructs a tree of Value's.