#How to catch assert()'s unreachable?

1 messages · Page 1 of 1 (latest)

wintry wadi
#

The std.json code has a number of asserts, see examples below, that can be triggered by invalid JSON input. I want to be able to catch these errors and handle them instead of crashing the program.

https://github.com/ziglang/zig/blob/master/lib/std/json/static.zig#L151
https://github.com/ziglang/zig/blob/master/lib/std/json/static.zig#L527

GitHub

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software. - ziglang/zig

random harness
wintry wadi
#

As simple as having duplicate fields would trigger the assert at line 151.

#

Shouldn't it report any errors on user input instead of bailing out with assert?

random harness
wintry wadi
#

ok, i'll get a simple test together.

random harness
scarlet nymph
#

imo if you can trip an assert in the json parser you should write a test case that triggers it and open an issue

#

it's either assert being used somewhere it shouldn't be or a bug in the parser

wintry wadi
#

actually just reading at the code at line 151. it's asserting because there's more input than expected. i mean it's a really easy condition to trip.

random harness
#

it's asserting because it expects to be at the end of the file if innerParse returned successfully. that's a different thing

#

if you can create a test case that actually does trip that, i'll be surprised

wintry wadi
#

it's a Reader, not just for file. it could be a Reader on a string.

random harness
#

same difference :)

wintry wadi
#

having { "foo": 1 } { "bar": 2} would trigger it.

#

because it has parsed { "foo": 1} and expects that's the end of the document.

random harness
#

does it? did you check?

wintry wadi
#

i'll isolate the tests.

scarlet nymph
#

i'm pretty sure i did some fuzzing of std.json, but it's been a while

random harness
# wintry wadi i'll isolate the tests.

I'll save you some time: ```rs
test {
const parsed = try std.json.parseFromSlice(S, std.testing.allocator,
\{"foo": 1} {"bar": 2}
, .{});
parsed.deinit();
}
const S = struct {
foo: u32,
};
const std = @import("std");

1/1 test.test_0...FAIL (SyntaxError)

#

no assert :)

#

because innerParse returns an error before the assert is hit

#

or, actually it seems to come from inside the scanner

#

either way, it's correctly caught and handled. the assert is just there to verify that for internal debugging purposes

wintry wadi
#

Ok. It's a bit involved, but I've isolated the test case for triggering the assert.

#
const U1 = union(enum) {
    s2:     S2,
    arr:    []S2,

    pub fn jsonParse(alloc: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) !U1 {
        return switch (try source.peekNextTokenType()) {
            .object_begin => .{ .s2  = try std.json.innerParse(S2, alloc, source, options) },
            .array_begin =>  .{ .arr = try std.json.innerParse([]S2, alloc, source, options) },
            else => error.UnexpectedToken,
        };
    }    
};

// This is the struct returned with the error if any.
const S2 = struct {
    id:  u32 = 0,
    err: []const u8 = "",

    pub fn jsonParse(alloc: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) !S2 {
        const s2a = std.json.innerParse(S2a, alloc, source, options) catch |err| {
            return .{ .err = @errorName(err) };
        };
        return .{ .id = s2a.id };
    }    
};

// This is the struct for parsing into.
const S2a = struct {
    id:  u32,
};


test "test1" {
    const str1 = \\{ "id": 123 }
    ;
    var res1 = std.json.parseFromSlice(U1, std.testing.allocator, str1, .{}) catch |err| {
        std.debug.print("Parse error: {}\n", .{err});
        return;
    };
    res1.deinit();

    // This triggers an assert in std.json.parseFromTokenSourceLeaky() at line 151.
    const str2 = \\{ "idx": "123" }
    ;
    var res2 = std.json.parseFromSlice(U1, std.testing.allocator, str2, .{}) catch |err| {
        std.debug.print("parseFromSlice error: {}\n", .{err});
        return;
    };
    res2.deinit();

}
#

The intent of the code is to parse a tagged union on either one of the JSON. Then internally carrying the parse error if any up the chain (since Zig doesn't support carrying error detail via the standard error).

random harness
#

you can't catch an error and then just keep on parsing, the scanner will be in an invalid state

wintry wadi
#

Then how do I handle error in the middle?

random harness
#

you just return it up the stack