7b1cd1c6 | 15-Apr-2020 |
Markus Armbruster <armbru@redhat.com> |
qobject: Eliminate qdict_iter(), use qdict_first(), qdict_next()
qdict_iter() has just three uses and no test coverage. Replace by qdict_first(), qdict_next() for more concise code and less type pu
qobject: Eliminate qdict_iter(), use qdict_first(), qdict_next()
qdict_iter() has just three uses and no test coverage. Replace by qdict_first(), qdict_next() for more concise code and less type punning.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20200415083048.14339-5-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
show more ...
|
2f2ec111 | 15-Apr-2020 |
Markus Armbruster <armbru@redhat.com> |
qobject: Eliminate qlist_iter(), use QLIST_FOREACH_ENTRY() instead
qlist_iter() has just three uses outside tests/. Replace by QLIST_FOREACH_ENTRY() for more concise code and less type punning.
Si
qobject: Eliminate qlist_iter(), use QLIST_FOREACH_ENTRY() instead
qlist_iter() has just three uses outside tests/. Replace by QLIST_FOREACH_ENTRY() for more concise code and less type punning.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20200415083048.14339-4-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
show more ...
|
1e960b46 | 31-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Eliminate lexer state IN_WHITESPACE, pseudo-token JSON_SKIP
The lexer ignores whitespace like this:
on whitespace on non-ws spontaneously IN_START --> IN_WHITESPACE --> JS
json: Eliminate lexer state IN_WHITESPACE, pseudo-token JSON_SKIP
The lexer ignores whitespace like this:
on whitespace on non-ws spontaneously IN_START --> IN_WHITESPACE --> JSON_SKIP --> IN_START ^ | \__/ on whitespace
This accumulates a whitespace token in state IN_WHITESPACE, only to throw it away on the transition via JSON_SKIP to the start state. Wasteful. Go from IN_START to IN_START on whitespace directly, dropping the whitespace character.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180831075841.13363-7-armbru@redhat.com>
show more ...
|
0f07a5d5 | 31-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Nicer recovery from lexical errors
When the lexer chokes on an input character, it consumes the character, emits a JSON error token, and enters its start state. This can lead to suboptimal er
json: Nicer recovery from lexical errors
When the lexer chokes on an input character, it consumes the character, emits a JSON error token, and enters its start state. This can lead to suboptimal error recovery. For instance, input
0123 ,
produces the tokens
JSON_ERROR 01 JSON_INTEGER 23 JSON_COMMA ,
Make the lexer skip characters after a lexical error until a structural character ('[', ']', '{', '}', ':', ','), an ASCII control character, or '\xFE', or '\xFF'.
Note that we must not skip ASCII control characters, '\xFE', '\xFF', because those are documented to force the JSON parser into known-good state, by docs/interop/qmp-spec.txt.
The lexer now produces
JSON_ERROR 01 JSON_COMMA ,
Update qmp-test for the nicer error recovery: QMP now reports just one error for input %p instead of two. Also drop the newline after %p; it was needed to tease out the second error.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180831075841.13363-5-armbru@redhat.com> [Conflict with commit ebb4d82d888 resolved]
show more ...
|
c0ee3afa | 31-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Make lexer's "character consumed" logic less confusing
The lexer uses macro TERMINAL_NEEDED_LOOKAHEAD() to decide whether a state transition consumes the input character. It returns true when
json: Make lexer's "character consumed" logic less confusing
The lexer uses macro TERMINAL_NEEDED_LOOKAHEAD() to decide whether a state transition consumes the input character. It returns true when the state transition is defined with the TERMINAL() macro. To detect that, it checks whether input '\0' would have resulted in the same state transition, and the new state is not IN_ERROR.
Why does that even work? For all states, the new state on input '\0' is either IN_ERROR or defined with TERMINAL(). If the state transition equals the one we'd get for input '\0', it goes to IN_ERROR or to the argument of TERMINAL(). We never use TERMINAL(IN_ERROR), because it makes no sense. Thus, if it doesn't go to IN_ERROR, it must be defined with TERMINAL().
Since this isn't quite confusing enough, we negate the result to get @char_consumed, and ignore it when @flush is true.
Instead of deriving the lookahead bit from the state transition, make it explicit. This is easier to understand, and a bit more flexible, too.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180831075841.13363-4-armbru@redhat.com>
show more ...
|
852dfa76 | 31-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Clean up how lexer consumes "end of input"
When the lexer isn't in its start state at the end of input, it's working on a token. To flush it out, it needs to transit to its start state on "en
json: Clean up how lexer consumes "end of input"
When the lexer isn't in its start state at the end of input, it's working on a token. To flush it out, it needs to transit to its start state on "end of input" lookahead.
There are two ways to the start state, depending on the current state:
* If the lexer is in a TERMINAL(JSON_FOO) state, it can emit a JSON_FOO token.
* Else, it can go to IN_ERROR state, and emit a JSON_ERROR token.
There are complications, however:
* The transition to IN_ERROR state consumes the input character and adds it to the JSON_ERROR token. The latter is inappropriate for the "end of input" character, so we suppress that. See also recent commit a2ec6be72b8 "json: Fix lexer to include the bad character in JSON_ERROR token".
* The transition to a TERMINAL(JSON_FOO) state doesn't consume the input character. In that case, the lexer normally loops until it is consumed. We have to suppress that for the "end of input" input character. If we didn't, the lexer would consume it by entering IN_ERROR state, emitting a bogus JSON_ERROR token. We fixed that in commit bd3924a33a6.
However, simply breaking the loop this way assumes that the lexer needs exactly one state transition to reach its start state. That assumption is correct now, but it's unclean, and I'll soon break it. Clean up: instead of breaking the loop after one iteration, break it after it reached the start state.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180831075841.13363-3-armbru@redhat.com>
show more ...
|
8bca4613 | 23-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Support %% in JSON strings when interpolating
The previous commit makes JSON strings containing '%' awkward to express in templates: you'd have to mask the '%' with an Unicode escape \u0025.
json: Support %% in JSON strings when interpolating
The previous commit makes JSON strings containing '%' awkward to express in templates: you'd have to mask the '%' with an Unicode escape \u0025. No template currently contains such JSON strings. Support the printf conversion specification %% in JSON strings as a convenience anyway, because it's trivially easy to do.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180823164025.12553-58-armbru@redhat.com>
show more ...
|
16a48599 | 23-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Improve safety of qobject_from_jsonf_nofail() & friends
The JSON parser optionally supports interpolation. This is used to build QObjects by parsing string templates. The templates are C lit
json: Improve safety of qobject_from_jsonf_nofail() & friends
The JSON parser optionally supports interpolation. This is used to build QObjects by parsing string templates. The templates are C literals, so parse errors (such as invalid interpolation specifications) are actually programming errors. Consequently, the functions providing parsing with interpolation (qobject_from_jsonf_nofail(), qobject_from_vjsonf_nofail(), qdict_from_jsonf_nofail(), qdict_from_vjsonf_nofail()) pass &error_abort to the parser.
However, there's another, more dangerous kind of programming error: since we use va_arg() to get the value to interpolate, behavior is undefined when the variable argument isn't consistent with the interpolation specification.
The same problem exists with printf()-like functions, and the solution is to have the compiler check consistency. This is what GCC_FMT_ATTR() is about.
To enable this type checking for interpolation as well, we carefully chose our interpolation specifications to match printf conversion specifications, and decorate functions parsing templates with GCC_FMT_ATTR().
Note that this only protects against undefined behavior due to type errors. It can't protect against use of invalid interpolation specifications that happen to be valid printf conversion specifications.
However, there's still a gaping hole in the type checking: GCC recognizes '%' as start of printf conversion specification anywhere in the template, but the parser recognizes it only outside JSON strings. For instance, if someone were to pass a "{ '%s': %d }" template, GCC would require a char * and an int argument, but the parser would va_arg() only an int argument, resulting in undefined behavior.
Avoid undefined behavior by catching the programming error at run time: have the parser recognize and reject '%' in JSON strings.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180823164025.12553-57-armbru@redhat.com>
show more ...
|
ada74c3b | 23-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Keep interpolation state in JSONParserContext
The recursive descent parser passes along a pointer to JSONParserContext. It additionally passes a pointer to interpolation state (a va_alist *)
json: Keep interpolation state in JSONParserContext
The recursive descent parser passes along a pointer to JSONParserContext. It additionally passes a pointer to interpolation state (a va_alist *) as needed to reach its consumer parse_interpolation().
Stuffing the latter pointer into JSONParserContext saves us the trouble of passing it along, so do that.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180823164025.12553-56-armbru@redhat.com>
show more ...
|
86cdf9ec | 23-Aug-2018 |
Markus Armbruster <armbru@redhat.com> |
json: Clean up headers
The JSON parser has three public headers, json-lexer.h, json-parser.h, json-streamer.h. They all contain stuff that is of no interest outside qobject/json-*.c.
Collect the p
json: Clean up headers
The JSON parser has three public headers, json-lexer.h, json-parser.h, json-streamer.h. They all contain stuff that is of no interest outside qobject/json-*.c.
Collect the public interface in include/qapi/qmp/json-parser.h, and everything else in qobject/json-parser-int.h.
Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20180823164025.12553-54-armbru@redhat.com>
show more ...
|