mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/armbru/tags/pull-qobject-2018-08-24' into staging
QObject patches for 2018-08-24 # gpg: Signature made Fri 24 Aug 2018 20:28:53 BST # gpg: using RSA key 3870B400EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qobject-2018-08-24: (58 commits) json: Update references to RFC 7159 to RFC 8259 json: Support %% in JSON strings when interpolating json: Improve safety of qobject_from_jsonf_nofail() & friends json: Keep interpolation state in JSONParserContext tests/drive_del-test: Fix harmless JSON interpolation bug json: Clean up headers qobject: Drop superfluous includes of qemu-common.h json: Make JSONToken opaque outside json-parser.c json: Unbox tokens queue in JSONMessageParser json: Streamline json_message_process_token() json: Enforce token count and size limits more tightly qjson: Have qobject_from_json() & friends reject empty and blank json: Assert json_parser_parse() consumes all tokens on success json: Fix streamer not to ignore trailing unterminated structures json: Fix latent parser aborts at end of input qjson: Fix qobject_from_json() & friends for multiple values json: Improve names of lexer states related to numbers json: Replace %I64d, %I64u by %PRId64, %PRIu64 json: Leave rejecting invalid interpolation to parser json: Pass lexical errors and limit violations to callback ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -1715,6 +1715,7 @@ F: monitor.c
|
||||
F: docs/devel/*qmp-*
|
||||
F: scripts/qmp/
|
||||
F: tests/qmp-test.c
|
||||
F: tests/qmp-cmd-test.c
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
qtest
|
||||
|
||||
@@ -1478,11 +1478,6 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
|
||||
options_obj = qobject_from_json(filename, errp);
|
||||
if (!options_obj) {
|
||||
/* Work around qobject_from_json() lossage TODO fix that */
|
||||
if (errp && !*errp) {
|
||||
error_setg(errp, "Could not parse the JSON options");
|
||||
return NULL;
|
||||
}
|
||||
error_prepend(errp, "Could not parse the JSON options: ");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+28
-14
@@ -20,9 +20,9 @@ operating system.
|
||||
2. Protocol Specification
|
||||
=========================
|
||||
|
||||
This section details the protocol format. For the purpose of this document
|
||||
"Client" is any application which is using QMP to communicate with QEMU and
|
||||
"Server" is QEMU itself.
|
||||
This section details the protocol format. For the purpose of this
|
||||
document, "Server" is either QEMU or the QEMU Guest Agent, and
|
||||
"Client" is any application communicating with it via QMP.
|
||||
|
||||
JSON data structures, when mentioned in this document, are always in the
|
||||
following format:
|
||||
@@ -34,9 +34,8 @@ by the JSON standard:
|
||||
|
||||
http://www.ietf.org/rfc/rfc7159.txt
|
||||
|
||||
The protocol is always encoded in UTF-8 except for synchronization
|
||||
bytes (documented below); although thanks to json-string escape
|
||||
sequences, the server will reply using only the strict ASCII subset.
|
||||
The server expects its input to be encoded in UTF-8, and sends its
|
||||
output encoded in ASCII.
|
||||
|
||||
For convenience, json-object members mentioned in this document will
|
||||
be in a certain order. However, in real protocol usage they can be in
|
||||
@@ -215,16 +214,31 @@ Some events are rate-limited to at most one per second. If additional
|
||||
dropped, and the last one is delayed. "Similar" normally means same
|
||||
event type. See qmp-events.txt for details.
|
||||
|
||||
2.6 QGA Synchronization
|
||||
2.6 Forcing the JSON parser into known-good state
|
||||
-------------------------------------------------
|
||||
|
||||
Incomplete or invalid input can leave the server's JSON parser in a
|
||||
state where it can't parse additional commands. To get it back into
|
||||
known-good state, the client should provoke a lexical error.
|
||||
|
||||
The cleanest way to do that is sending an ASCII control character
|
||||
other than '\t' (horizontal tab), '\r' (carriage return), or '\n' (new
|
||||
line).
|
||||
|
||||
Sadly, older versions of QEMU can fail to flag this as an error. If a
|
||||
client needs to deal with them, it should send a 0xFF byte.
|
||||
|
||||
2.7 QGA Synchronization
|
||||
-----------------------
|
||||
|
||||
When using QGA, an additional synchronization feature is built into
|
||||
the protocol. If the Client sends a raw 0xFF sentinel byte (not valid
|
||||
JSON), then the Server will reset its state and discard all pending
|
||||
data prior to the sentinel. Conversely, if the Client makes use of
|
||||
the 'guest-sync-delimited' command, the Server will send a raw 0xFF
|
||||
sentinel byte prior to its response, to aid the Client in discarding
|
||||
any data prior to the sentinel.
|
||||
When a client connects to QGA over a transport lacking proper
|
||||
connection semantics such as virtio-serial, QGA may have read partial
|
||||
input from a previous client. The client needs to force QGA's parser
|
||||
into known-good state using the previous section's technique.
|
||||
Moreover, the client may receive output a previous client didn't read.
|
||||
To help with skipping that output, QGA provides the
|
||||
'guest-sync-delimited' command. Refer to its documentation for
|
||||
details.
|
||||
|
||||
|
||||
3. QMP Examples
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* JSON lexer
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_JSON_LEXER_H
|
||||
#define QEMU_JSON_LEXER_H
|
||||
|
||||
|
||||
typedef enum json_token_type {
|
||||
JSON_MIN = 100,
|
||||
JSON_LCURLY = JSON_MIN,
|
||||
JSON_RCURLY,
|
||||
JSON_LSQUARE,
|
||||
JSON_RSQUARE,
|
||||
JSON_COLON,
|
||||
JSON_COMMA,
|
||||
JSON_INTEGER,
|
||||
JSON_FLOAT,
|
||||
JSON_KEYWORD,
|
||||
JSON_STRING,
|
||||
JSON_ESCAPE,
|
||||
JSON_SKIP,
|
||||
JSON_ERROR,
|
||||
} JSONTokenType;
|
||||
|
||||
typedef struct JSONLexer JSONLexer;
|
||||
|
||||
typedef void (JSONLexerEmitter)(JSONLexer *, GString *,
|
||||
JSONTokenType, int x, int y);
|
||||
|
||||
struct JSONLexer
|
||||
{
|
||||
JSONLexerEmitter *emit;
|
||||
int state;
|
||||
GString *token;
|
||||
int x, y;
|
||||
};
|
||||
|
||||
void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func);
|
||||
|
||||
int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
|
||||
|
||||
int json_lexer_flush(JSONLexer *lexer);
|
||||
|
||||
void json_lexer_destroy(JSONLexer *lexer);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* JSON Parser
|
||||
* JSON Parser
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
@@ -11,12 +11,36 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_JSON_PARSER_H
|
||||
#define QEMU_JSON_PARSER_H
|
||||
#ifndef QAPI_QMP_JSON_PARSER_H
|
||||
#define QAPI_QMP_JSON_PARSER_H
|
||||
|
||||
#include "qemu-common.h"
|
||||
typedef struct JSONLexer {
|
||||
int start_state, state;
|
||||
GString *token;
|
||||
int x, y;
|
||||
} JSONLexer;
|
||||
|
||||
QObject *json_parser_parse(GQueue *tokens, va_list *ap);
|
||||
QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp);
|
||||
typedef struct JSONMessageParser {
|
||||
void (*emit)(void *opaque, QObject *json, Error *err);
|
||||
void *opaque;
|
||||
va_list *ap;
|
||||
JSONLexer lexer;
|
||||
int brace_count;
|
||||
int bracket_count;
|
||||
GQueue tokens;
|
||||
uint64_t token_size;
|
||||
} JSONMessageParser;
|
||||
|
||||
void json_message_parser_init(JSONMessageParser *parser,
|
||||
void (*emit)(void *opaque, QObject *json,
|
||||
Error *err),
|
||||
void *opaque, va_list *ap);
|
||||
|
||||
void json_message_parser_feed(JSONMessageParser *parser,
|
||||
const char *buffer, size_t size);
|
||||
|
||||
void json_message_parser_flush(JSONMessageParser *parser);
|
||||
|
||||
void json_message_parser_destroy(JSONMessageParser *parser);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* JSON streaming support
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_JSON_STREAMER_H
|
||||
#define QEMU_JSON_STREAMER_H
|
||||
|
||||
#include "qapi/qmp/json-lexer.h"
|
||||
|
||||
typedef struct JSONToken {
|
||||
int type;
|
||||
int x;
|
||||
int y;
|
||||
char str[];
|
||||
} JSONToken;
|
||||
|
||||
typedef struct JSONMessageParser
|
||||
{
|
||||
void (*emit)(struct JSONMessageParser *parser, GQueue *tokens);
|
||||
JSONLexer lexer;
|
||||
int brace_count;
|
||||
int bracket_count;
|
||||
GQueue *tokens;
|
||||
uint64_t token_size;
|
||||
} JSONMessageParser;
|
||||
|
||||
void json_message_parser_init(JSONMessageParser *parser,
|
||||
void (*func)(JSONMessageParser *, GQueue *));
|
||||
|
||||
int json_message_parser_feed(JSONMessageParser *parser,
|
||||
const char *buffer, size_t size);
|
||||
|
||||
int json_message_parser_flush(JSONMessageParser *parser);
|
||||
|
||||
void json_message_parser_destroy(JSONMessageParser *parser);
|
||||
|
||||
#endif
|
||||
@@ -61,9 +61,6 @@
|
||||
#define QERR_IO_ERROR \
|
||||
"An IO error has occurred"
|
||||
|
||||
#define QERR_JSON_PARSING \
|
||||
"Invalid JSON syntax"
|
||||
|
||||
#define QERR_MIGRATION_ACTIVE \
|
||||
"There's a migration process in progress"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef enum {
|
||||
|
||||
/*
|
||||
* QNum encapsulates how our dialect of JSON fills in the blanks left
|
||||
* by the JSON specification (RFC 7159) regarding numbers.
|
||||
* by the JSON specification (RFC 8259) regarding numbers.
|
||||
*
|
||||
* Conceptually, we treat number as an abstract type with three
|
||||
* concrete subtypes: floating-point, signed integer, unsigned
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
#define QEMU_UNICODE_H
|
||||
|
||||
int mod_utf8_codepoint(const char *s, size_t n, char **end);
|
||||
ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
#include "qapi/qmp/qnum.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/json-streamer.h"
|
||||
#include "qapi/qmp/json-parser.h"
|
||||
#include "qapi/qmp/qlist.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
@@ -4256,20 +4255,14 @@ static void monitor_qmp_bh_dispatcher(void *data)
|
||||
|
||||
#define QMP_REQ_QUEUE_LEN_MAX (8)
|
||||
|
||||
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
|
||||
static void handle_qmp_command(void *opaque, QObject *req, Error *err)
|
||||
{
|
||||
QObject *req, *id = NULL;
|
||||
Monitor *mon = opaque;
|
||||
QObject *id = NULL;
|
||||
QDict *qdict;
|
||||
MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser);
|
||||
Monitor *mon = container_of(mon_qmp, Monitor, qmp);
|
||||
Error *err = NULL;
|
||||
QMPRequest *req_obj;
|
||||
|
||||
req = json_parser_parse_err(tokens, NULL, &err);
|
||||
if (!req && !err) {
|
||||
/* json_parser_parse_err() sucks: can fail without setting @err */
|
||||
error_setg(&err, QERR_JSON_PARSING);
|
||||
}
|
||||
assert(!req != !err);
|
||||
|
||||
qdict = qobject_to(QDict, req);
|
||||
if (qdict) {
|
||||
@@ -4465,7 +4458,8 @@ static void monitor_qmp_event(void *opaque, int event)
|
||||
monitor_qmp_response_flush(mon);
|
||||
monitor_qmp_cleanup_queues(mon);
|
||||
json_message_parser_destroy(&mon->qmp.parser);
|
||||
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
|
||||
json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
|
||||
mon, NULL);
|
||||
mon_refcount--;
|
||||
monitor_fdsets_cleanup();
|
||||
break;
|
||||
@@ -4683,7 +4677,8 @@ void monitor_init(Chardev *chr, int flags)
|
||||
|
||||
if (monitor_is_qmp(mon)) {
|
||||
qemu_chr_fe_set_echo(&mon->chr, true);
|
||||
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
|
||||
json_message_parser_init(&mon->qmp.parser, handle_qmp_command,
|
||||
mon, NULL);
|
||||
if (mon->use_io_thread) {
|
||||
/*
|
||||
* Make sure the old iowatch is gone. It's possible when
|
||||
|
||||
@@ -120,7 +120,7 @@
|
||||
##
|
||||
# @JSONType:
|
||||
#
|
||||
# The four primitive and two structured types according to RFC 7159
|
||||
# The four primitive and two structured types according to RFC 8259
|
||||
# section 1, plus 'int' (split off 'number'), plus the obvious top
|
||||
# type 'value'.
|
||||
#
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/dispatch.h"
|
||||
#include "qapi/qmp/json-parser.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
|
||||
@@ -725,11 +725,6 @@ Visitor *qobject_input_visitor_new_str(const char *str,
|
||||
if (is_json) {
|
||||
obj = qobject_from_json(str, errp);
|
||||
if (!obj) {
|
||||
/* Work around qobject_from_json() lossage TODO fix that */
|
||||
if (errp && !*errp) {
|
||||
error_setg(errp, "JSON parse error");
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
args = qobject_to(QDict, obj);
|
||||
|
||||
+5
-10
@@ -18,7 +18,6 @@
|
||||
#include <syslog.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
#include "qapi/qmp/json-streamer.h"
|
||||
#include "qapi/qmp/json-parser.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
@@ -597,24 +596,20 @@ static void process_command(GAState *s, QDict *req)
|
||||
}
|
||||
|
||||
/* handle requests/control events coming in over the channel */
|
||||
static void process_event(JSONMessageParser *parser, GQueue *tokens)
|
||||
static void process_event(void *opaque, QObject *obj, Error *err)
|
||||
{
|
||||
GAState *s = container_of(parser, GAState, parser);
|
||||
QObject *obj;
|
||||
GAState *s = opaque;
|
||||
QDict *req, *rsp;
|
||||
Error *err = NULL;
|
||||
int ret;
|
||||
|
||||
g_assert(s && parser);
|
||||
|
||||
g_debug("process_event: called");
|
||||
obj = json_parser_parse_err(tokens, NULL, &err);
|
||||
assert(!obj != !err);
|
||||
if (err) {
|
||||
goto err;
|
||||
}
|
||||
req = qobject_to(QDict, obj);
|
||||
if (!req) {
|
||||
error_setg(&err, QERR_JSON_PARSING);
|
||||
error_setg(&err, "Input must be a JSON object");
|
||||
goto err;
|
||||
}
|
||||
if (!qdict_haskey(req, "execute")) {
|
||||
@@ -1320,7 +1315,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation)
|
||||
s->command_state = ga_command_state_new();
|
||||
ga_command_state_init(s, s->command_state);
|
||||
ga_command_state_init_all(s->command_state);
|
||||
json_message_parser_init(&s->parser, process_event);
|
||||
json_message_parser_init(&s->parser, process_event, s, NULL);
|
||||
|
||||
#ifndef _WIN32
|
||||
if (!register_signal_handlers()) {
|
||||
|
||||
+139
-174
@@ -12,63 +12,116 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/json-lexer.h"
|
||||
#include "json-parser-int.h"
|
||||
|
||||
#define MAX_TOKEN_SIZE (64ULL << 20)
|
||||
|
||||
/*
|
||||
* Required by JSON (RFC 7159):
|
||||
* From RFC 8259 "The JavaScript Object Notation (JSON) Data
|
||||
* Interchange Format", with [comments in brackets]:
|
||||
*
|
||||
* \"([^\\\"]|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*\"
|
||||
* -?(0|[1-9][0-9]*)(.[0-9]+)?([eE][-+]?[0-9]+)?
|
||||
* [{}\[\],:]
|
||||
* [a-z]+ # covers null, true, false
|
||||
* The set of tokens includes six structural characters, strings,
|
||||
* numbers, and three literal names.
|
||||
*
|
||||
* Extension of '' strings:
|
||||
* These are the six structural characters:
|
||||
*
|
||||
* '([^\\']|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*'
|
||||
* begin-array = ws %x5B ws ; [ left square bracket
|
||||
* begin-object = ws %x7B ws ; { left curly bracket
|
||||
* end-array = ws %x5D ws ; ] right square bracket
|
||||
* end-object = ws %x7D ws ; } right curly bracket
|
||||
* name-separator = ws %x3A ws ; : colon
|
||||
* value-separator = ws %x2C ws ; , comma
|
||||
*
|
||||
* Extension for vararg handling in JSON construction:
|
||||
* Insignificant whitespace is allowed before or after any of the six
|
||||
* structural characters.
|
||||
* [This lexer accepts it before or after any token, which is actually
|
||||
* the same, as the grammar always has structural characters between
|
||||
* other tokens.]
|
||||
*
|
||||
* %((l|ll|I64)?d|[ipsf])
|
||||
* ws = *(
|
||||
* %x20 / ; Space
|
||||
* %x09 / ; Horizontal tab
|
||||
* %x0A / ; Line feed or New line
|
||||
* %x0D ) ; Carriage return
|
||||
*
|
||||
* [...] three literal names:
|
||||
* false null true
|
||||
* [This lexer accepts [a-z]+, and leaves rejecting unknown literal
|
||||
* names to the parser.]
|
||||
*
|
||||
* [Numbers:]
|
||||
*
|
||||
* number = [ minus ] int [ frac ] [ exp ]
|
||||
* decimal-point = %x2E ; .
|
||||
* digit1-9 = %x31-39 ; 1-9
|
||||
* e = %x65 / %x45 ; e E
|
||||
* exp = e [ minus / plus ] 1*DIGIT
|
||||
* frac = decimal-point 1*DIGIT
|
||||
* int = zero / ( digit1-9 *DIGIT )
|
||||
* minus = %x2D ; -
|
||||
* plus = %x2B ; +
|
||||
* zero = %x30 ; 0
|
||||
*
|
||||
* [Strings:]
|
||||
* string = quotation-mark *char quotation-mark
|
||||
*
|
||||
* char = unescaped /
|
||||
* escape (
|
||||
* %x22 / ; " quotation mark U+0022
|
||||
* %x5C / ; \ reverse solidus U+005C
|
||||
* %x2F / ; / solidus U+002F
|
||||
* %x62 / ; b backspace U+0008
|
||||
* %x66 / ; f form feed U+000C
|
||||
* %x6E / ; n line feed U+000A
|
||||
* %x72 / ; r carriage return U+000D
|
||||
* %x74 / ; t tab U+0009
|
||||
* %x75 4HEXDIG ) ; uXXXX U+XXXX
|
||||
* escape = %x5C ; \
|
||||
* quotation-mark = %x22 ; "
|
||||
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
|
||||
* [This lexer accepts any non-control character after escape, and
|
||||
* leaves rejecting invalid ones to the parser.]
|
||||
*
|
||||
*
|
||||
* Extensions over RFC 8259:
|
||||
* - Extra escape sequence in strings:
|
||||
* 0x27 (apostrophe) is recognized after escape, too
|
||||
* - Single-quoted strings:
|
||||
* Like double-quoted strings, except they're delimited by %x27
|
||||
* (apostrophe) instead of %x22 (quotation mark), and can't contain
|
||||
* unescaped apostrophe, but can contain unescaped quotation mark.
|
||||
* - Interpolation, if enabled:
|
||||
* The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid
|
||||
* ones to the parser.
|
||||
*
|
||||
* Note:
|
||||
* - Input must be encoded in modified UTF-8.
|
||||
* - Decoding and validating is left to the parser.
|
||||
*/
|
||||
|
||||
enum json_lexer_state {
|
||||
IN_ERROR = 0, /* must really be 0, see json_lexer[] */
|
||||
IN_DQ_UCODE3,
|
||||
IN_DQ_UCODE2,
|
||||
IN_DQ_UCODE1,
|
||||
IN_DQ_UCODE0,
|
||||
IN_DQ_STRING_ESCAPE,
|
||||
IN_DQ_STRING,
|
||||
IN_SQ_UCODE3,
|
||||
IN_SQ_UCODE2,
|
||||
IN_SQ_UCODE1,
|
||||
IN_SQ_UCODE0,
|
||||
IN_SQ_STRING_ESCAPE,
|
||||
IN_SQ_STRING,
|
||||
IN_ZERO,
|
||||
IN_DIGITS,
|
||||
IN_DIGIT,
|
||||
IN_EXP_DIGITS,
|
||||
IN_EXP_SIGN,
|
||||
IN_EXP_E,
|
||||
IN_MANTISSA,
|
||||
IN_MANTISSA_DIGITS,
|
||||
IN_NONZERO_NUMBER,
|
||||
IN_NEG_NONZERO_NUMBER,
|
||||
IN_DIGITS,
|
||||
IN_SIGN,
|
||||
IN_KEYWORD,
|
||||
IN_ESCAPE,
|
||||
IN_ESCAPE_L,
|
||||
IN_ESCAPE_LL,
|
||||
IN_ESCAPE_I,
|
||||
IN_ESCAPE_I6,
|
||||
IN_ESCAPE_I64,
|
||||
IN_INTERP,
|
||||
IN_WHITESPACE,
|
||||
IN_START,
|
||||
IN_START_INTERP, /* must be IN_START + 1 */
|
||||
};
|
||||
|
||||
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
|
||||
QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP);
|
||||
QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1);
|
||||
|
||||
#define TERMINAL(state) [0 ... 0x7F] = (state)
|
||||
|
||||
@@ -76,87 +129,27 @@ QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
|
||||
from OLD_STATE required lookahead. This happens whenever the table
|
||||
below uses the TERMINAL macro. */
|
||||
#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \
|
||||
(json_lexer[(old_state)][0] == (terminal))
|
||||
(terminal != IN_ERROR && json_lexer[(old_state)][0] == (terminal))
|
||||
|
||||
static const uint8_t json_lexer[][256] = {
|
||||
/* Relies on default initialization to IN_ERROR! */
|
||||
|
||||
/* double quote string */
|
||||
[IN_DQ_UCODE3] = {
|
||||
['0' ... '9'] = IN_DQ_STRING,
|
||||
['a' ... 'f'] = IN_DQ_STRING,
|
||||
['A' ... 'F'] = IN_DQ_STRING,
|
||||
},
|
||||
[IN_DQ_UCODE2] = {
|
||||
['0' ... '9'] = IN_DQ_UCODE3,
|
||||
['a' ... 'f'] = IN_DQ_UCODE3,
|
||||
['A' ... 'F'] = IN_DQ_UCODE3,
|
||||
},
|
||||
[IN_DQ_UCODE1] = {
|
||||
['0' ... '9'] = IN_DQ_UCODE2,
|
||||
['a' ... 'f'] = IN_DQ_UCODE2,
|
||||
['A' ... 'F'] = IN_DQ_UCODE2,
|
||||
},
|
||||
[IN_DQ_UCODE0] = {
|
||||
['0' ... '9'] = IN_DQ_UCODE1,
|
||||
['a' ... 'f'] = IN_DQ_UCODE1,
|
||||
['A' ... 'F'] = IN_DQ_UCODE1,
|
||||
},
|
||||
[IN_DQ_STRING_ESCAPE] = {
|
||||
['b'] = IN_DQ_STRING,
|
||||
['f'] = IN_DQ_STRING,
|
||||
['n'] = IN_DQ_STRING,
|
||||
['r'] = IN_DQ_STRING,
|
||||
['t'] = IN_DQ_STRING,
|
||||
['/'] = IN_DQ_STRING,
|
||||
['\\'] = IN_DQ_STRING,
|
||||
['\''] = IN_DQ_STRING,
|
||||
['\"'] = IN_DQ_STRING,
|
||||
['u'] = IN_DQ_UCODE0,
|
||||
[0x20 ... 0xFD] = IN_DQ_STRING,
|
||||
},
|
||||
[IN_DQ_STRING] = {
|
||||
[1 ... 0xBF] = IN_DQ_STRING,
|
||||
[0xC2 ... 0xF4] = IN_DQ_STRING,
|
||||
[0x20 ... 0xFD] = IN_DQ_STRING,
|
||||
['\\'] = IN_DQ_STRING_ESCAPE,
|
||||
['"'] = JSON_STRING,
|
||||
},
|
||||
|
||||
/* single quote string */
|
||||
[IN_SQ_UCODE3] = {
|
||||
['0' ... '9'] = IN_SQ_STRING,
|
||||
['a' ... 'f'] = IN_SQ_STRING,
|
||||
['A' ... 'F'] = IN_SQ_STRING,
|
||||
},
|
||||
[IN_SQ_UCODE2] = {
|
||||
['0' ... '9'] = IN_SQ_UCODE3,
|
||||
['a' ... 'f'] = IN_SQ_UCODE3,
|
||||
['A' ... 'F'] = IN_SQ_UCODE3,
|
||||
},
|
||||
[IN_SQ_UCODE1] = {
|
||||
['0' ... '9'] = IN_SQ_UCODE2,
|
||||
['a' ... 'f'] = IN_SQ_UCODE2,
|
||||
['A' ... 'F'] = IN_SQ_UCODE2,
|
||||
},
|
||||
[IN_SQ_UCODE0] = {
|
||||
['0' ... '9'] = IN_SQ_UCODE1,
|
||||
['a' ... 'f'] = IN_SQ_UCODE1,
|
||||
['A' ... 'F'] = IN_SQ_UCODE1,
|
||||
},
|
||||
[IN_SQ_STRING_ESCAPE] = {
|
||||
['b'] = IN_SQ_STRING,
|
||||
['f'] = IN_SQ_STRING,
|
||||
['n'] = IN_SQ_STRING,
|
||||
['r'] = IN_SQ_STRING,
|
||||
['t'] = IN_SQ_STRING,
|
||||
['/'] = IN_SQ_STRING,
|
||||
['\\'] = IN_SQ_STRING,
|
||||
['\''] = IN_SQ_STRING,
|
||||
['\"'] = IN_SQ_STRING,
|
||||
['u'] = IN_SQ_UCODE0,
|
||||
[0x20 ... 0xFD] = IN_SQ_STRING,
|
||||
},
|
||||
[IN_SQ_STRING] = {
|
||||
[1 ... 0xBF] = IN_SQ_STRING,
|
||||
[0xC2 ... 0xF4] = IN_SQ_STRING,
|
||||
[0x20 ... 0xFD] = IN_SQ_STRING,
|
||||
['\\'] = IN_SQ_STRING_ESCAPE,
|
||||
['\''] = JSON_STRING,
|
||||
},
|
||||
@@ -169,19 +162,19 @@ static const uint8_t json_lexer[][256] = {
|
||||
},
|
||||
|
||||
/* Float */
|
||||
[IN_DIGITS] = {
|
||||
[IN_EXP_DIGITS] = {
|
||||
TERMINAL(JSON_FLOAT),
|
||||
['0' ... '9'] = IN_DIGITS,
|
||||
['0' ... '9'] = IN_EXP_DIGITS,
|
||||
},
|
||||
|
||||
[IN_DIGIT] = {
|
||||
['0' ... '9'] = IN_DIGITS,
|
||||
[IN_EXP_SIGN] = {
|
||||
['0' ... '9'] = IN_EXP_DIGITS,
|
||||
},
|
||||
|
||||
[IN_EXP_E] = {
|
||||
['-'] = IN_DIGIT,
|
||||
['+'] = IN_DIGIT,
|
||||
['0' ... '9'] = IN_DIGITS,
|
||||
['-'] = IN_EXP_SIGN,
|
||||
['+'] = IN_EXP_SIGN,
|
||||
['0' ... '9'] = IN_EXP_DIGITS,
|
||||
},
|
||||
|
||||
[IN_MANTISSA_DIGITS] = {
|
||||
@@ -196,17 +189,17 @@ static const uint8_t json_lexer[][256] = {
|
||||
},
|
||||
|
||||
/* Number */
|
||||
[IN_NONZERO_NUMBER] = {
|
||||
[IN_DIGITS] = {
|
||||
TERMINAL(JSON_INTEGER),
|
||||
['0' ... '9'] = IN_NONZERO_NUMBER,
|
||||
['0' ... '9'] = IN_DIGITS,
|
||||
['e'] = IN_EXP_E,
|
||||
['E'] = IN_EXP_E,
|
||||
['.'] = IN_MANTISSA,
|
||||
},
|
||||
|
||||
[IN_NEG_NONZERO_NUMBER] = {
|
||||
[IN_SIGN] = {
|
||||
['0'] = IN_ZERO,
|
||||
['1' ... '9'] = IN_NONZERO_NUMBER,
|
||||
['1' ... '9'] = IN_DIGITS,
|
||||
},
|
||||
|
||||
/* keywords */
|
||||
@@ -224,49 +217,25 @@ static const uint8_t json_lexer[][256] = {
|
||||
['\n'] = IN_WHITESPACE,
|
||||
},
|
||||
|
||||
/* escape */
|
||||
[IN_ESCAPE_LL] = {
|
||||
['d'] = JSON_ESCAPE,
|
||||
['u'] = JSON_ESCAPE,
|
||||
/* interpolation */
|
||||
[IN_INTERP] = {
|
||||
TERMINAL(JSON_INTERP),
|
||||
['A' ... 'Z'] = IN_INTERP,
|
||||
['a' ... 'z'] = IN_INTERP,
|
||||
['0' ... '9'] = IN_INTERP,
|
||||
},
|
||||
|
||||
[IN_ESCAPE_L] = {
|
||||
['d'] = JSON_ESCAPE,
|
||||
['l'] = IN_ESCAPE_LL,
|
||||
['u'] = JSON_ESCAPE,
|
||||
},
|
||||
|
||||
[IN_ESCAPE_I64] = {
|
||||
['d'] = JSON_ESCAPE,
|
||||
['u'] = JSON_ESCAPE,
|
||||
},
|
||||
|
||||
[IN_ESCAPE_I6] = {
|
||||
['4'] = IN_ESCAPE_I64,
|
||||
},
|
||||
|
||||
[IN_ESCAPE_I] = {
|
||||
['6'] = IN_ESCAPE_I6,
|
||||
},
|
||||
|
||||
[IN_ESCAPE] = {
|
||||
['d'] = JSON_ESCAPE,
|
||||
['i'] = JSON_ESCAPE,
|
||||
['p'] = JSON_ESCAPE,
|
||||
['s'] = JSON_ESCAPE,
|
||||
['u'] = JSON_ESCAPE,
|
||||
['f'] = JSON_ESCAPE,
|
||||
['l'] = IN_ESCAPE_L,
|
||||
['I'] = IN_ESCAPE_I,
|
||||
},
|
||||
|
||||
/* top level rule */
|
||||
[IN_START] = {
|
||||
/*
|
||||
* Two start states:
|
||||
* - IN_START recognizes JSON tokens with our string extensions
|
||||
* - IN_START_INTERP additionally recognizes interpolation.
|
||||
*/
|
||||
[IN_START ... IN_START_INTERP] = {
|
||||
['"'] = IN_DQ_STRING,
|
||||
['\''] = IN_SQ_STRING,
|
||||
['0'] = IN_ZERO,
|
||||
['1' ... '9'] = IN_NONZERO_NUMBER,
|
||||
['-'] = IN_NEG_NONZERO_NUMBER,
|
||||
['1' ... '9'] = IN_DIGITS,
|
||||
['-'] = IN_SIGN,
|
||||
['{'] = JSON_LCURLY,
|
||||
['}'] = JSON_RCURLY,
|
||||
['['] = JSON_LSQUARE,
|
||||
@@ -274,23 +243,23 @@ static const uint8_t json_lexer[][256] = {
|
||||
[','] = JSON_COMMA,
|
||||
[':'] = JSON_COLON,
|
||||
['a' ... 'z'] = IN_KEYWORD,
|
||||
['%'] = IN_ESCAPE,
|
||||
[' '] = IN_WHITESPACE,
|
||||
['\t'] = IN_WHITESPACE,
|
||||
['\r'] = IN_WHITESPACE,
|
||||
['\n'] = IN_WHITESPACE,
|
||||
},
|
||||
[IN_START_INTERP]['%'] = IN_INTERP,
|
||||
};
|
||||
|
||||
void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func)
|
||||
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation)
|
||||
{
|
||||
lexer->emit = func;
|
||||
lexer->state = IN_START;
|
||||
lexer->start_state = lexer->state = enable_interpolation
|
||||
? IN_START_INTERP : IN_START;
|
||||
lexer->token = g_string_sized_new(3);
|
||||
lexer->x = lexer->y = 0;
|
||||
}
|
||||
|
||||
static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
{
|
||||
int char_consumed, new_state;
|
||||
|
||||
@@ -304,7 +273,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
assert(lexer->state <= ARRAY_SIZE(json_lexer));
|
||||
new_state = json_lexer[lexer->state][(uint8_t)ch];
|
||||
char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state);
|
||||
if (char_consumed) {
|
||||
if (char_consumed && !flush) {
|
||||
g_string_append_c(lexer->token, ch);
|
||||
}
|
||||
|
||||
@@ -315,23 +284,23 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
case JSON_RSQUARE:
|
||||
case JSON_COLON:
|
||||
case JSON_COMMA:
|
||||
case JSON_ESCAPE:
|
||||
case JSON_INTERP:
|
||||
case JSON_INTEGER:
|
||||
case JSON_FLOAT:
|
||||
case JSON_KEYWORD:
|
||||
case JSON_STRING:
|
||||
lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y);
|
||||
json_message_process_token(lexer, lexer->token, new_state,
|
||||
lexer->x, lexer->y);
|
||||
/* fall through */
|
||||
case JSON_SKIP:
|
||||
g_string_truncate(lexer->token, 0);
|
||||
new_state = IN_START;
|
||||
new_state = lexer->start_state;
|
||||
break;
|
||||
case IN_ERROR:
|
||||
/* XXX: To avoid having previous bad input leaving the parser in an
|
||||
* unresponsive state where we consume unpredictable amounts of
|
||||
* subsequent "good" input, percolate this error state up to the
|
||||
* tokenizer/parser by forcing a NULL object to be emitted, then
|
||||
* reset state.
|
||||
* parser by emitting a JSON_ERROR token, then reset lexer state.
|
||||
*
|
||||
* Also note that this handling is required for reliable channel
|
||||
* negotiation between QMP and the guest agent, since chr(0xFF)
|
||||
@@ -340,11 +309,11 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
* never a valid ASCII/UTF-8 sequence, so this should reliably
|
||||
* induce an error/flush state.
|
||||
*/
|
||||
lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y);
|
||||
json_message_process_token(lexer, lexer->token, JSON_ERROR,
|
||||
lexer->x, lexer->y);
|
||||
g_string_truncate(lexer->token, 0);
|
||||
new_state = IN_START;
|
||||
lexer->state = new_state;
|
||||
return 0;
|
||||
lexer->state = lexer->start_state;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -355,33 +324,29 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
|
||||
* this is a security consideration.
|
||||
*/
|
||||
if (lexer->token->len > MAX_TOKEN_SIZE) {
|
||||
lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y);
|
||||
json_message_process_token(lexer, lexer->token, lexer->state,
|
||||
lexer->x, lexer->y);
|
||||
g_string_truncate(lexer->token, 0);
|
||||
lexer->state = IN_START;
|
||||
lexer->state = lexer->start_state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
|
||||
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
int err;
|
||||
|
||||
err = json_lexer_feed_char(lexer, buffer[i], false);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
json_lexer_feed_char(lexer, buffer[i], false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_lexer_flush(JSONLexer *lexer)
|
||||
void json_lexer_flush(JSONLexer *lexer)
|
||||
{
|
||||
return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true);
|
||||
if (lexer->state != lexer->start_state) {
|
||||
json_lexer_feed_char(lexer, 0, true);
|
||||
}
|
||||
json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT,
|
||||
lexer->x, lexer->y);
|
||||
}
|
||||
|
||||
void json_lexer_destroy(JSONLexer *lexer)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* JSON Parser
|
||||
*
|
||||
* Copyright IBM, Corp. 2009
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
||||
* See the COPYING.LIB file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JSON_PARSER_INT_H
|
||||
#define JSON_PARSER_INT_H
|
||||
|
||||
#include "qapi/qmp/json-parser.h"
|
||||
|
||||
|
||||
typedef enum json_token_type {
|
||||
JSON_MIN = 100,
|
||||
JSON_LCURLY = JSON_MIN,
|
||||
JSON_RCURLY,
|
||||
JSON_LSQUARE,
|
||||
JSON_RSQUARE,
|
||||
JSON_COLON,
|
||||
JSON_COMMA,
|
||||
JSON_INTEGER,
|
||||
JSON_FLOAT,
|
||||
JSON_KEYWORD,
|
||||
JSON_STRING,
|
||||
JSON_INTERP,
|
||||
JSON_SKIP,
|
||||
JSON_ERROR,
|
||||
JSON_END_OF_INPUT,
|
||||
} JSONTokenType;
|
||||
|
||||
typedef struct JSONToken JSONToken;
|
||||
|
||||
/* json-lexer.c */
|
||||
void json_lexer_init(JSONLexer *lexer, bool enable_interpolation);
|
||||
void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size);
|
||||
void json_lexer_flush(JSONLexer *lexer);
|
||||
void json_lexer_destroy(JSONLexer *lexer);
|
||||
|
||||
/* json-streamer.c */
|
||||
void json_message_process_token(JSONLexer *lexer, GString *input,
|
||||
JSONTokenType type, int x, int y);
|
||||
|
||||
/* json-parser.c */
|
||||
JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr);
|
||||
QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp);
|
||||
|
||||
#endif
|
||||
+182
-197
File diff suppressed because it is too large
Load Diff
+60
-62
@@ -12,34 +12,29 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/json-lexer.h"
|
||||
#include "qapi/qmp/json-streamer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "json-parser-int.h"
|
||||
|
||||
#define MAX_TOKEN_SIZE (64ULL << 20)
|
||||
#define MAX_TOKEN_COUNT (2ULL << 20)
|
||||
#define MAX_NESTING (1ULL << 10)
|
||||
|
||||
static void json_message_free_token(void *token, void *opaque)
|
||||
{
|
||||
g_free(token);
|
||||
}
|
||||
#define MAX_NESTING (1 << 10)
|
||||
|
||||
static void json_message_free_tokens(JSONMessageParser *parser)
|
||||
{
|
||||
if (parser->tokens) {
|
||||
g_queue_foreach(parser->tokens, json_message_free_token, NULL);
|
||||
g_queue_free(parser->tokens);
|
||||
parser->tokens = NULL;
|
||||
JSONToken *token;
|
||||
|
||||
while ((token = g_queue_pop_head(&parser->tokens))) {
|
||||
g_free(token);
|
||||
}
|
||||
}
|
||||
|
||||
static void json_message_process_token(JSONLexer *lexer, GString *input,
|
||||
JSONTokenType type, int x, int y)
|
||||
void json_message_process_token(JSONLexer *lexer, GString *input,
|
||||
JSONTokenType type, int x, int y)
|
||||
{
|
||||
JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
|
||||
QObject *json = NULL;
|
||||
Error *err = NULL;
|
||||
JSONToken *token;
|
||||
GQueue *tokens;
|
||||
|
||||
switch (type) {
|
||||
case JSON_LCURLY:
|
||||
@@ -54,79 +49,82 @@ static void json_message_process_token(JSONLexer *lexer, GString *input,
|
||||
case JSON_RSQUARE:
|
||||
parser->bracket_count--;
|
||||
break;
|
||||
case JSON_ERROR:
|
||||
error_setg(&err, "JSON parse error, stray '%s'", input->str);
|
||||
goto out_emit;
|
||||
case JSON_END_OF_INPUT:
|
||||
if (g_queue_is_empty(&parser->tokens)) {
|
||||
return;
|
||||
}
|
||||
json = json_parser_parse(&parser->tokens, parser->ap, &err);
|
||||
goto out_emit;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
token = g_malloc(sizeof(JSONToken) + input->len + 1);
|
||||
token->type = type;
|
||||
memcpy(token->str, input->str, input->len);
|
||||
token->str[input->len] = 0;
|
||||
token->x = x;
|
||||
token->y = y;
|
||||
|
||||
parser->token_size += input->len;
|
||||
|
||||
g_queue_push_tail(parser->tokens, token);
|
||||
|
||||
if (type == JSON_ERROR) {
|
||||
goto out_emit_bad;
|
||||
} else if (parser->brace_count < 0 ||
|
||||
parser->bracket_count < 0 ||
|
||||
(parser->brace_count == 0 &&
|
||||
parser->bracket_count == 0)) {
|
||||
/*
|
||||
* Security consideration, we limit total memory allocated per object
|
||||
* and the maximum recursion depth that a message can force.
|
||||
*/
|
||||
if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) {
|
||||
error_setg(&err, "JSON token size limit exceeded");
|
||||
goto out_emit;
|
||||
}
|
||||
if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) {
|
||||
error_setg(&err, "JSON token count limit exceeded");
|
||||
goto out_emit;
|
||||
}
|
||||
if (parser->bracket_count + parser->brace_count > MAX_NESTING) {
|
||||
error_setg(&err, "JSON nesting depth limit exceeded");
|
||||
goto out_emit;
|
||||
} else if (parser->token_size > MAX_TOKEN_SIZE ||
|
||||
g_queue_get_length(parser->tokens) > MAX_TOKEN_COUNT ||
|
||||
parser->bracket_count + parser->brace_count > MAX_NESTING) {
|
||||
/* Security consideration, we limit total memory allocated per object
|
||||
* and the maximum recursion depth that a message can force.
|
||||
*/
|
||||
goto out_emit_bad;
|
||||
}
|
||||
|
||||
return;
|
||||
token = json_token(type, x, y, input);
|
||||
parser->token_size += input->len;
|
||||
|
||||
g_queue_push_tail(&parser->tokens, token);
|
||||
|
||||
if ((parser->brace_count > 0 || parser->bracket_count > 0)
|
||||
&& parser->bracket_count >= 0 && parser->bracket_count >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
json = json_parser_parse(&parser->tokens, parser->ap, &err);
|
||||
|
||||
out_emit_bad:
|
||||
/*
|
||||
* Clear out token list and tell the parser to emit an error
|
||||
* indication by passing it a NULL list
|
||||
*/
|
||||
json_message_free_tokens(parser);
|
||||
out_emit:
|
||||
/* send current list of tokens to parser and reset tokenizer */
|
||||
parser->brace_count = 0;
|
||||
parser->bracket_count = 0;
|
||||
/* parser->emit takes ownership of parser->tokens. Remove our own
|
||||
* reference to parser->tokens before handing it out to parser->emit.
|
||||
*/
|
||||
tokens = parser->tokens;
|
||||
parser->tokens = g_queue_new();
|
||||
parser->emit(parser, tokens);
|
||||
json_message_free_tokens(parser);
|
||||
parser->token_size = 0;
|
||||
parser->emit(parser->opaque, json, err);
|
||||
}
|
||||
|
||||
void json_message_parser_init(JSONMessageParser *parser,
|
||||
void (*func)(JSONMessageParser *, GQueue *))
|
||||
void (*emit)(void *opaque, QObject *json,
|
||||
Error *err),
|
||||
void *opaque, va_list *ap)
|
||||
{
|
||||
parser->emit = func;
|
||||
parser->emit = emit;
|
||||
parser->opaque = opaque;
|
||||
parser->ap = ap;
|
||||
parser->brace_count = 0;
|
||||
parser->bracket_count = 0;
|
||||
parser->tokens = g_queue_new();
|
||||
g_queue_init(&parser->tokens);
|
||||
parser->token_size = 0;
|
||||
|
||||
json_lexer_init(&parser->lexer, json_message_process_token);
|
||||
json_lexer_init(&parser->lexer, !!ap);
|
||||
}
|
||||
|
||||
int json_message_parser_feed(JSONMessageParser *parser,
|
||||
void json_message_parser_feed(JSONMessageParser *parser,
|
||||
const char *buffer, size_t size)
|
||||
{
|
||||
return json_lexer_feed(&parser->lexer, buffer, size);
|
||||
json_lexer_feed(&parser->lexer, buffer, size);
|
||||
}
|
||||
|
||||
int json_message_parser_flush(JSONMessageParser *parser)
|
||||
void json_message_parser_flush(JSONMessageParser *parser)
|
||||
{
|
||||
return json_lexer_flush(&parser->lexer);
|
||||
json_lexer_flush(&parser->lexer);
|
||||
assert(g_queue_is_empty(&parser->tokens));
|
||||
}
|
||||
|
||||
void json_message_parser_destroy(JSONMessageParser *parser)
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
/**
|
||||
* qbool_from_bool(): Create a new QBool from a bool
|
||||
|
||||
+22
-9
@@ -13,9 +13,7 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/json-lexer.h"
|
||||
#include "qapi/qmp/json-parser.h"
|
||||
#include "qapi/qmp/json-streamer.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
@@ -27,16 +25,29 @@
|
||||
typedef struct JSONParsingState
|
||||
{
|
||||
JSONMessageParser parser;
|
||||
va_list *ap;
|
||||
QObject *result;
|
||||
Error *err;
|
||||
} JSONParsingState;
|
||||
|
||||
static void parse_json(JSONMessageParser *parser, GQueue *tokens)
|
||||
static void consume_json(void *opaque, QObject *json, Error *err)
|
||||
{
|
||||
JSONParsingState *s = container_of(parser, JSONParsingState, parser);
|
||||
JSONParsingState *s = opaque;
|
||||
|
||||
s->result = json_parser_parse_err(tokens, s->ap, &s->err);
|
||||
assert(!json != !err);
|
||||
assert(!s->result || !s->err);
|
||||
|
||||
if (s->result) {
|
||||
qobject_unref(s->result);
|
||||
s->result = NULL;
|
||||
error_setg(&s->err, "Expecting at most one JSON value");
|
||||
}
|
||||
if (s->err) {
|
||||
qobject_unref(json);
|
||||
error_free(err);
|
||||
return;
|
||||
}
|
||||
s->result = json;
|
||||
s->err = err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -54,13 +65,15 @@ static QObject *qobject_from_jsonv(const char *string, va_list *ap,
|
||||
{
|
||||
JSONParsingState state = {};
|
||||
|
||||
state.ap = ap;
|
||||
|
||||
json_message_parser_init(&state.parser, parse_json);
|
||||
json_message_parser_init(&state.parser, consume_json, &state, ap);
|
||||
json_message_parser_feed(&state.parser, string, strlen(string));
|
||||
json_message_parser_flush(&state.parser);
|
||||
json_message_parser_destroy(&state.parser);
|
||||
|
||||
if (!state.result && !state.err) {
|
||||
error_setg(&state.err, "Expecting a JSON value");
|
||||
}
|
||||
|
||||
error_propagate(errp, state.err);
|
||||
return state.result;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user