aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/qapi/qmp/json-lexer.h16
-rw-r--r--include/qapi/qmp/json-parser.h4
-rw-r--r--include/qapi/qmp/json-streamer.h16
-rw-r--r--monitor.c3
-rw-r--r--qga/main.c2
-rw-r--r--qobject/json-lexer.c48
-rw-r--r--qobject/json-parser.c330
-rw-r--r--qobject/json-streamer.c91
-rw-r--r--qobject/qjson.c2
-rw-r--r--tests/check-qjson.c25
-rw-r--r--tests/libqtest.c2
11 files changed, 225 insertions, 314 deletions
diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h
index cdff0460a8..cb456d53e5 100644
--- a/include/qapi/qmp/json-lexer.h
+++ b/include/qapi/qmp/json-lexer.h
@@ -14,11 +14,16 @@
#ifndef QEMU_JSON_LEXER_H
#define QEMU_JSON_LEXER_H
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qlist.h"
+#include "glib-compat.h"
typedef enum json_token_type {
- JSON_OPERATOR = 100,
+ JSON_MIN = 100,
+ JSON_LCURLY = JSON_MIN,
+ JSON_RCURLY,
+ JSON_LSQUARE,
+ JSON_RSQUARE,
+ JSON_COLON,
+ JSON_COMMA,
JSON_INTEGER,
JSON_FLOAT,
JSON_KEYWORD,
@@ -30,13 +35,14 @@ typedef enum json_token_type {
typedef struct JSONLexer JSONLexer;
-typedef void (JSONLexerEmitter)(JSONLexer *, QString *, JSONTokenType, int x, int y);
+typedef void (JSONLexerEmitter)(JSONLexer *, GString *,
+ JSONTokenType, int x, int y);
struct JSONLexer
{
JSONLexerEmitter *emit;
int state;
- QString *token;
+ GString *token;
int x, y;
};
diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h
index 44d88f3468..fea89f873a 100644
--- a/include/qapi/qmp/json-parser.h
+++ b/include/qapi/qmp/json-parser.h
@@ -18,7 +18,7 @@
#include "qapi/qmp/qlist.h"
#include "qapi/error.h"
-QObject *json_parser_parse(QList *tokens, va_list *ap);
-QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp);
+QObject *json_parser_parse(GQueue *tokens, va_list *ap);
+QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp);
#endif
diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h
index 823f7d7fa4..09b3d3ec15 100644
--- a/include/qapi/qmp/json-streamer.h
+++ b/include/qapi/qmp/json-streamer.h
@@ -14,21 +14,29 @@
#ifndef QEMU_JSON_STREAMER_H
#define QEMU_JSON_STREAMER_H
-#include "qapi/qmp/qlist.h"
+#include <stdint.h>
+#include "glib-compat.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, QList *tokens);
+ void (*emit)(struct JSONMessageParser *parser, GQueue *tokens);
JSONLexer lexer;
int brace_count;
int bracket_count;
- QList *tokens;
+ GQueue *tokens;
uint64_t token_size;
} JSONMessageParser;
void json_message_parser_init(JSONMessageParser *parser,
- void (*func)(JSONMessageParser *, QList *));
+ void (*func)(JSONMessageParser *, GQueue *));
int json_message_parser_feed(JSONMessageParser *parser,
const char *buffer, size_t size);
diff --git a/monitor.c b/monitor.c
index e4cf34e3cb..9a35d7265f 100644
--- a/monitor.c
+++ b/monitor.c
@@ -3849,7 +3849,7 @@ static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
return input_dict;
}
-static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
+static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
{
Error *local_err = NULL;
QObject *obj, *data;
@@ -3907,6 +3907,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
err_out:
monitor_protocol_emitter(mon, data, local_err);
qobject_decref(data);
+ error_free(local_err);
QDECREF(input);
QDECREF(args);
}
diff --git a/qga/main.c b/qga/main.c
index d2a0ffc807..f83a97d245 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -570,7 +570,7 @@ static void process_command(GAState *s, QDict *req)
}
/* handle requests/control events coming in over the channel */
-static void process_event(JSONMessageParser *parser, QList *tokens)
+static void process_event(JSONMessageParser *parser, GQueue *tokens)
{
GAState *s = container_of(parser, GAState, parser);
QDict *qdict;
diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c
index b19623e229..92798ae3a4 100644
--- a/qobject/json-lexer.c
+++ b/qobject/json-lexer.c
@@ -11,12 +11,9 @@
*
*/
-#include "qapi/qmp/qstring.h"
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qdict.h"
-#include "qapi/qmp/qint.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
+#include <stdint.h>
#define MAX_TOKEN_SIZE (64ULL << 20)
@@ -30,7 +27,7 @@
*/
enum json_lexer_state {
- IN_ERROR = 0,
+ IN_ERROR = 0, /* must really be 0, see json_lexer[] */
IN_DQ_UCODE3,
IN_DQ_UCODE2,
IN_DQ_UCODE1,
@@ -62,6 +59,8 @@ enum json_lexer_state {
IN_START,
};
+QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START);
+
#define TERMINAL(state) [0 ... 0x7F] = (state)
/* Return whether TERMINAL is a terminal state and the transition to it
@@ -71,6 +70,8 @@ enum json_lexer_state {
(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,
@@ -253,12 +254,12 @@ static const uint8_t json_lexer[][256] = {
['0'] = IN_ZERO,
['1' ... '9'] = IN_NONZERO_NUMBER,
['-'] = IN_NEG_NONZERO_NUMBER,
- ['{'] = JSON_OPERATOR,
- ['}'] = JSON_OPERATOR,
- ['['] = JSON_OPERATOR,
- [']'] = JSON_OPERATOR,
- [','] = JSON_OPERATOR,
- [':'] = JSON_OPERATOR,
+ ['{'] = JSON_LCURLY,
+ ['}'] = JSON_RCURLY,
+ ['['] = JSON_LSQUARE,
+ [']'] = JSON_RSQUARE,
+ [','] = JSON_COMMA,
+ [':'] = JSON_COLON,
['a' ... 'z'] = IN_KEYWORD,
['%'] = IN_ESCAPE,
[' '] = IN_WHITESPACE,
@@ -272,7 +273,7 @@ void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func)
{
lexer->emit = func;
lexer->state = IN_START;
- lexer->token = qstring_new();
+ lexer->token = g_string_sized_new(3);
lexer->x = lexer->y = 0;
}
@@ -287,14 +288,20 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
}
do {
+ 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) {
- qstring_append_chr(lexer->token, ch);
+ g_string_append_c(lexer->token, ch);
}
switch (new_state) {
- case JSON_OPERATOR:
+ case JSON_LCURLY:
+ case JSON_RCURLY:
+ case JSON_LSQUARE:
+ case JSON_RSQUARE:
+ case JSON_COLON:
+ case JSON_COMMA:
case JSON_ESCAPE:
case JSON_INTEGER:
case JSON_FLOAT:
@@ -303,8 +310,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y);
/* fall through */
case JSON_SKIP:
- QDECREF(lexer->token);
- lexer->token = qstring_new();
+ g_string_truncate(lexer->token, 0);
new_state = IN_START;
break;
case IN_ERROR:
@@ -322,8 +328,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
* induce an error/flush state.
*/
lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y);
- QDECREF(lexer->token);
- lexer->token = qstring_new();
+ g_string_truncate(lexer->token, 0);
new_state = IN_START;
lexer->state = new_state;
return 0;
@@ -336,10 +341,9 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush)
/* Do not let a single token grow to an arbitrarily large size,
* this is a security consideration.
*/
- if (lexer->token->length > MAX_TOKEN_SIZE) {
+ if (lexer->token->len > MAX_TOKEN_SIZE) {
lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y);
- QDECREF(lexer->token);
- lexer->token = qstring_new();
+ g_string_truncate(lexer->token, 0);
lexer->state = IN_START;
}
@@ -369,5 +373,5 @@ int json_lexer_flush(JSONLexer *lexer)
void json_lexer_destroy(JSONLexer *lexer)
{
- QDECREF(lexer->token);
+ g_string_free(lexer->token, true);
}
diff --git a/qobject/json-parser.c b/qobject/json-parser.c
index ac991ba3cf..3c5d35d7b0 100644
--- a/qobject/json-parser.c
+++ b/qobject/json-parser.c
@@ -22,15 +22,13 @@
#include "qapi/qmp/qbool.h"
#include "qapi/qmp/json-parser.h"
#include "qapi/qmp/json-lexer.h"
+#include "qapi/qmp/json-streamer.h"
typedef struct JSONParserContext
{
Error *err;
- struct {
- QObject **buf;
- size_t pos;
- size_t count;
- } tokens;
+ JSONToken *current;
+ GQueue *buf;
} JSONParserContext;
#define BUG_ON(cond) assert(!(cond))
@@ -47,58 +45,10 @@ typedef struct JSONParserContext
static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
/**
- * Token manipulators
- *
- * tokens are dictionaries that contain a type, a string value, and geometry information
- * about a token identified by the lexer. These are routines that make working with
- * these objects a bit easier.
- */
-static const char *token_get_value(QObject *obj)
-{
- return qdict_get_str(qobject_to_qdict(obj), "token");
-}
-
-static JSONTokenType token_get_type(QObject *obj)
-{
- return qdict_get_int(qobject_to_qdict(obj), "type");
-}
-
-static int token_is_operator(QObject *obj, char op)
-{
- const char *val;
-
- if (token_get_type(obj) != JSON_OPERATOR) {
- return 0;
- }
-
- val = token_get_value(obj);
-
- return (val[0] == op) && (val[1] == 0);
-}
-
-static int token_is_keyword(QObject *obj, const char *value)
-{
- if (token_get_type(obj) != JSON_KEYWORD) {
- return 0;
- }
-
- return strcmp(token_get_value(obj), value) == 0;
-}
-
-static int token_is_escape(QObject *obj, const char *value)
-{
- if (token_get_type(obj) != JSON_ESCAPE) {
- return 0;
- }
-
- return (strcmp(token_get_value(obj), value) == 0);
-}
-
-/**
* Error handler
*/
static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
- QObject *token, const char *msg, ...)
+ JSONToken *token, const char *msg, ...)
{
va_list ap;
char message[1024];
@@ -176,9 +126,10 @@ static int hex2decimal(char ch)
* \t
* \u four-hex-digits
*/
-static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token)
+static QString *qstring_from_escaped_str(JSONParserContext *ctxt,
+ JSONToken *token)
{
- const char *ptr = token_get_value(token);
+ const char *ptr = token->str;
QString *str;
int double_quote = 1;
@@ -274,73 +225,34 @@ out:
return NULL;
}
-static QObject *parser_context_pop_token(JSONParserContext *ctxt)
-{
- QObject *token;
- g_assert(ctxt->tokens.pos < ctxt->tokens.count);
- token = ctxt->tokens.buf[ctxt->tokens.pos];
- ctxt->tokens.pos++;
- return token;
-}
-
-/* Note: parser_context_{peek|pop}_token do not increment the
- * token object's refcount. In both cases the references will continue
- * to be tracked and cleaned up in parser_context_free(), so do not
- * attempt to free the token object.
+/* Note: the token object returned by parser_context_peek_token or
+ * parser_context_pop_token is deleted as soon as parser_context_pop_token
+ * is called again.
*/
-static QObject *parser_context_peek_token(JSONParserContext *ctxt)
-{
- QObject *token;
- g_assert(ctxt->tokens.pos < ctxt->tokens.count);
- token = ctxt->tokens.buf[ctxt->tokens.pos];
- return token;
-}
-
-static JSONParserContext parser_context_save(JSONParserContext *ctxt)
-{
- JSONParserContext saved_ctxt = {0};
- saved_ctxt.tokens.pos = ctxt->tokens.pos;
- saved_ctxt.tokens.count = ctxt->tokens.count;
- saved_ctxt.tokens.buf = ctxt->tokens.buf;
- return saved_ctxt;
-}
-
-static void parser_context_restore(JSONParserContext *ctxt,
- JSONParserContext saved_ctxt)
+static JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
{
- ctxt->tokens.pos = saved_ctxt.tokens.pos;
- ctxt->tokens.count = saved_ctxt.tokens.count;
- ctxt->tokens.buf = saved_ctxt.tokens.buf;
+ g_free(ctxt->current);
+ assert(!g_queue_is_empty(ctxt->buf));
+ ctxt->current = g_queue_pop_head(ctxt->buf);
+ return ctxt->current;
}
-static void tokens_append_from_iter(QObject *obj, void *opaque)
+static JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
{
- JSONParserContext *ctxt = opaque;
- g_assert(ctxt->tokens.pos < ctxt->tokens.count);
- ctxt->tokens.buf[ctxt->tokens.pos++] = obj;
- qobject_incref(obj);
+ assert(!g_queue_is_empty(ctxt->buf));
+ return g_queue_peek_head(ctxt->buf);
}
-static JSONParserContext *parser_context_new(QList *tokens)
+static JSONParserContext *parser_context_new(GQueue *tokens)
{
JSONParserContext *ctxt;
- size_t count;
if (!tokens) {
return NULL;
}
- count = qlist_size(tokens);
- if (count == 0) {
- return NULL;
- }
-
ctxt = g_malloc0(sizeof(JSONParserContext));
- ctxt->tokens.pos = 0;
- ctxt->tokens.count = count;
- ctxt->tokens.buf = g_malloc(count * sizeof(QObject *));
- qlist_iter(tokens, tokens_append_from_iter, ctxt);
- ctxt->tokens.pos = 0;
+ ctxt->buf = tokens;
return ctxt;
}
@@ -348,12 +260,12 @@ static JSONParserContext *parser_context_new(QList *tokens)
/* to support error propagation, ctxt->err must be freed separately */
static void parser_context_free(JSONParserContext *ctxt)
{
- int i;
if (ctxt) {
- for (i = 0; i < ctxt->tokens.count; i++) {
- qobject_decref(ctxt->tokens.buf[i]);
+ while (!g_queue_is_empty(ctxt->buf)) {
+ parser_context_pop_token(ctxt);
}
- g_free(ctxt->tokens.buf);
+ g_free(ctxt->current);
+ g_queue_free(ctxt->buf);
g_free(ctxt);
}
}
@@ -363,8 +275,8 @@ static void parser_context_free(JSONParserContext *ctxt)
*/
static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
{
- QObject *key = NULL, *token = NULL, *value, *peek;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ QObject *key = NULL, *value;
+ JSONToken *peek, *token;
peek = parser_context_peek_token(ctxt);
if (peek == NULL) {
@@ -384,7 +296,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
goto out;
}
- if (!token_is_operator(token, ':')) {
+ if (token->type != JSON_COLON) {
parse_error(ctxt, token, "missing : in object pair");
goto out;
}
@@ -402,7 +314,6 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
return 0;
out:
- parser_context_restore(ctxt, saved_ctxt);
qobject_decref(key);
return -1;
@@ -411,17 +322,10 @@ out:
static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
{
QDict *dict = NULL;
- QObject *token, *peek;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ JSONToken *token, *peek;
token = parser_context_pop_token(ctxt);
- if (token == NULL) {
- goto out;
- }
-
- if (!token_is_operator(token, '{')) {
- goto out;
- }
+ assert(token && token->type == JSON_LCURLY);
dict = qdict_new();
@@ -431,7 +335,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
goto out;
}
- if (!token_is_operator(peek, '}')) {
+ if (peek->type != JSON_RCURLY) {
if (parse_pair(ctxt, dict, ap) == -1) {
goto out;
}
@@ -442,8 +346,8 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
goto out;
}
- while (!token_is_operator(token, '}')) {
- if (!token_is_operator(token, ',')) {
+ while (token->type != JSON_RCURLY) {
+ if (token->type != JSON_COMMA) {
parse_error(ctxt, token, "expected separator in dict");
goto out;
}
@@ -465,7 +369,6 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
return QOBJECT(dict);
out:
- parser_context_restore(ctxt, saved_ctxt);
QDECREF(dict);
return NULL;
}
@@ -473,17 +376,10 @@ out:
static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
{
QList *list = NULL;
- QObject *token, *peek;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ JSONToken *token, *peek;
token = parser_context_pop_token(ctxt);
- if (token == NULL) {
- goto out;
- }
-
- if (!token_is_operator(token, '[')) {
- goto out;
- }
+ assert(token && token->type == JSON_LSQUARE);
list = qlist_new();
@@ -493,7 +389,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
goto out;
}
- if (!token_is_operator(peek, ']')) {
+ if (peek->type != JSON_RSQUARE) {
QObject *obj;
obj = parse_value(ctxt, ap);
@@ -510,8 +406,8 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
goto out;
}
- while (!token_is_operator(token, ']')) {
- if (!token_is_operator(token, ',')) {
+ while (token->type != JSON_RSQUARE) {
+ if (token->type != JSON_COMMA) {
parse_error(ctxt, token, "expected separator in list");
goto out;
}
@@ -537,99 +433,68 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
return QOBJECT(list);
out:
- parser_context_restore(ctxt, saved_ctxt);
QDECREF(list);
return NULL;
}
static QObject *parse_keyword(JSONParserContext *ctxt)
{
- QObject *token, *ret;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ JSONToken *token;
token = parser_context_pop_token(ctxt);
- if (token == NULL) {
- goto out;
- }
-
- if (token_get_type(token) != JSON_KEYWORD) {
- goto out;
- }
+ assert(token && token->type == JSON_KEYWORD);
- if (token_is_keyword(token, "true")) {
- ret = QOBJECT(qbool_from_bool(true));
- } else if (token_is_keyword(token, "false")) {
- ret = QOBJECT(qbool_from_bool(false));
- } else if (token_is_keyword(token, "null")) {
- ret = qnull();
- } else {
- parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token));
- goto out;
+ if (!strcmp(token->str, "true")) {
+ return QOBJECT(qbool_from_bool(true));
+ } else if (!strcmp(token->str, "false")) {
+ return QOBJECT(qbool_from_bool(false));
+ } else if (!strcmp(token->str, "null")) {
+ return qnull();
}
-
- return ret;
-
-out:
- parser_context_restore(ctxt, saved_ctxt);
-
+ parse_error(ctxt, token, "invalid keyword '%s'", token->str);
return NULL;
}
static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap)
{
- QObject *token = NULL, *obj;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ JSONToken *token;
if (ap == NULL) {
- goto out;
+ return NULL;
}
token = parser_context_pop_token(ctxt);
- if (token == NULL) {
- goto out;
- }
-
- if (token_is_escape(token, "%p")) {
- obj = va_arg(*ap, QObject *);
- } else if (token_is_escape(token, "%i")) {
- obj = QOBJECT(qbool_from_bool(va_arg(*ap, int)));
- } else if (token_is_escape(token, "%d")) {
- obj = QOBJECT(qint_from_int(va_arg(*ap, int)));
- } else if (token_is_escape(token, "%ld")) {
- obj = QOBJECT(qint_from_int(va_arg(*ap, long)));
- } else if (token_is_escape(token, "%lld") ||
- token_is_escape(token, "%I64d")) {
- obj = QOBJECT(qint_from_int(va_arg(*ap, long long)));
- } else if (token_is_escape(token, "%s")) {
- obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
- } else if (token_is_escape(token, "%f")) {
- obj = QOBJECT(qfloat_from_double(va_arg(*ap, double)));
- } else {
- goto out;
+ assert(token && token->type == JSON_ESCAPE);
+
+ if (!strcmp(token->str, "%p")) {
+ return va_arg(*ap, QObject *);
+ } else if (!strcmp(token->str, "%i")) {
+ return QOBJECT(qbool_from_bool(va_arg(*ap, int)));
+ } else if (!strcmp(token->str, "%d")) {
+ return QOBJECT(qint_from_int(va_arg(*ap, int)));
+ } else if (!strcmp(token->str, "%ld")) {
+ return QOBJECT(qint_from_int(va_arg(*ap, long)));
+ } else if (!strcmp(token->str, "%lld") ||
+ !strcmp(token->str, "%I64d")) {
+ return QOBJECT(qint_from_int(va_arg(*ap, long long)));
+ } else if (!strcmp(token->str, "%s")) {
+ return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
+ } else if (!strcmp(token->str, "%f")) {
+ return QOBJECT(qfloat_from_double(va_arg(*ap, double)));
}
-
- return obj;
-
-out:
- parser_context_restore(ctxt, saved_ctxt);
-
return NULL;
}
static QObject *parse_literal(JSONParserContext *ctxt)
{
- QObject *token, *obj;
- JSONParserContext saved_ctxt = parser_context_save(ctxt);
+ JSONToken *token;
token = parser_context_pop_token(ctxt);
- if (token == NULL) {
- goto out;
- }
+ assert(token);
- switch (token_get_type(token)) {
+ switch (token->type) {
case JSON_STRING:
- obj = QOBJECT(qstring_from_escaped_str(ctxt, token));
- break;
+ return QOBJECT(qstring_from_escaped_str(ctxt, token));
case JSON_INTEGER: {
/* A possibility exists that this is a whole-valued float where the
* fractional part was left out due to being 0 (.0). It's not a big
@@ -646,56 +511,55 @@ static QObject *parse_literal(JSONParserContext *ctxt)
int64_t value;
errno = 0; /* strtoll doesn't set errno on success */
- value = strtoll(token_get_value(token), NULL, 10);
+ value = strtoll(token->str, NULL, 10);
if (errno != ERANGE) {
- obj = QOBJECT(qint_from_int(value));
- break;
+ return QOBJECT(qint_from_int(value));
}
/* fall through to JSON_FLOAT */
}
case JSON_FLOAT:
/* FIXME dependent on locale */
- obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL)));
- break;
+ return QOBJECT(qfloat_from_double(strtod(token->str, NULL)));
default:
- goto out;
+ abort();
}
-
- return obj;
-
-out:
- parser_context_restore(ctxt, saved_ctxt);
-
- return NULL;
}
static QObject *parse_value(JSONParserContext *ctxt, va_list *ap)
{
- QObject *obj;
+ JSONToken *token;
- obj = parse_object(ctxt, ap);
- if (obj == NULL) {
- obj = parse_array(ctxt, ap);
- }
- if (obj == NULL) {
- obj = parse_escape(ctxt, ap);
- }
- if (obj == NULL) {
- obj = parse_keyword(ctxt);
- }
- if (obj == NULL) {
- obj = parse_literal(ctxt);
+ token = parser_context_peek_token(ctxt);
+ if (token == NULL) {
+ parse_error(ctxt, NULL, "premature EOI");
+ return NULL;
}
- return obj;
+ switch (token->type) {
+ case JSON_LCURLY:
+ return parse_object(ctxt, ap);
+ case JSON_LSQUARE:
+ return parse_array(ctxt, ap);
+ case JSON_ESCAPE:
+ return parse_escape(ctxt, ap);
+ case JSON_INTEGER:
+ case JSON_FLOAT:
+ case JSON_STRING:
+ return parse_literal(ctxt);
+ case JSON_KEYWORD:
+ return parse_keyword(ctxt);
+ default:
+ parse_error(ctxt, token, "expecting value");
+ return NULL;
+ }
}
-QObject *json_parser_parse(QList *tokens, va_list *ap)
+QObject *json_parser_parse(GQueue *tokens, va_list *ap)
{
return json_parser_parse_err(tokens, ap, NULL);
}
-QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp)
+QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp)
{
JSONParserContext *ctxt = parser_context_new(tokens);
QObject *result;
diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c
index 1b2f9b1d10..a4db4b832e 100644
--- a/qobject/json-streamer.c
+++ b/qobject/json-streamer.c
@@ -11,50 +11,55 @@
*
*/
-#include "qapi/qmp/qlist.h"
-#include "qapi/qmp/qint.h"
-#include "qapi/qmp/qdict.h"
#include "qemu-common.h"
#include "qapi/qmp/json-lexer.h"
#include "qapi/qmp/json-streamer.h"
#define MAX_TOKEN_SIZE (64ULL << 20)
+#define MAX_TOKEN_COUNT (2ULL << 20)
#define MAX_NESTING (1ULL << 10)
-static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTokenType type, int x, int y)
+static void json_message_free_tokens(JSONMessageParser *parser)
+{
+ if (parser->tokens) {
+ g_queue_free(parser->tokens);
+ parser->tokens = NULL;
+ }
+}
+
+static void json_message_process_token(JSONLexer *lexer, GString *input,
+ JSONTokenType type, int x, int y)
{
JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
- QDict *dict;
-
- if (type == JSON_OPERATOR) {
- switch (qstring_get_str(token)[0]) {
- case '{':
- parser->brace_count++;
- break;
- case '}':
- parser->brace_count--;
- break;
- case '[':
- parser->bracket_count++;
- break;
- case ']':
- parser->bracket_count--;
- break;
- default:
- break;
- }
+ JSONToken *token;
+
+ switch (type) {
+ case JSON_LCURLY:
+ parser->brace_count++;
+ break;
+ case JSON_RCURLY:
+ parser->brace_count--;
+ break;
+ case JSON_LSQUARE:
+ parser->bracket_count++;
+ break;
+ case JSON_RSQUARE:
+ parser->bracket_count--;
+ break;
+ default:
+ break;
}
- dict = qdict_new();
- qdict_put(dict, "type", qint_from_int(type));
- QINCREF(token);
- qdict_put(dict, "token", token);
- qdict_put(dict, "x", qint_from_int(x));
- qdict_put(dict, "y", qint_from_int(y));
+ 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 += token->length;
+ parser->token_size += input->len;
- qlist_append(parser->tokens, dict);
+ g_queue_push_tail(parser->tokens, token);
if (type == JSON_ERROR) {
goto out_emit_bad;
@@ -64,41 +69,39 @@ static void json_message_process_token(JSONLexer *lexer, QString *token, JSONTok
parser->bracket_count == 0)) {
goto out_emit;
} else if (parser->token_size > MAX_TOKEN_SIZE ||
- parser->bracket_count > MAX_NESTING ||
- parser->brace_count > MAX_NESTING) {
+ 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;
+ goto out_emit_bad;
}
return;
out_emit_bad:
- /* clear out token list and tell the parser to emit and error
+ /*
+ * Clear out token list and tell the parser to emit an error
* indication by passing it a NULL list
*/
- QDECREF(parser->tokens);
- parser->tokens = NULL;
+ 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. */
parser->emit(parser, parser->tokens);
- if (parser->tokens) {
- QDECREF(parser->tokens);
- }
- parser->tokens = qlist_new();
+ parser->tokens = g_queue_new();
parser->token_size = 0;
}
void json_message_parser_init(JSONMessageParser *parser,
- void (*func)(JSONMessageParser *, QList *))
+ void (*func)(JSONMessageParser *, GQueue *))
{
parser->emit = func;
parser->brace_count = 0;
parser->bracket_count = 0;
- parser->tokens = qlist_new();
+ parser->tokens = g_queue_new();
parser->token_size = 0;
json_lexer_init(&parser->lexer, json_message_process_token);
@@ -118,5 +121,5 @@ int json_message_parser_flush(JSONMessageParser *parser)
void json_message_parser_destroy(JSONMessageParser *parser)
{
json_lexer_destroy(&parser->lexer);
- QDECREF(parser->tokens);
+ json_message_free_tokens(parser);
}
diff --git a/qobject/qjson.c b/qobject/qjson.c
index 33f8ef530c..a3e6a7ca42 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -28,7 +28,7 @@ typedef struct JSONParsingState
QObject *result;
} JSONParsingState;
-static void parse_json(JSONMessageParser *parser, QList *tokens)
+static void parse_json(JSONMessageParser *parser, GQueue *tokens)
{
JSONParsingState *s = container_of(parser, JSONParsingState, parser);
s->result = json_parser_parse(tokens, s->ap);
diff --git a/tests/check-qjson.c b/tests/check-qjson.c
index 1cfffa5934..61e9bfbabf 100644
--- a/tests/check-qjson.c
+++ b/tests/check-qjson.c
@@ -1484,6 +1484,30 @@ static void unterminated_literal(void)
g_assert(obj == NULL);
}
+static char *make_nest(char *buf, size_t cnt)
+{
+ memset(buf, '[', cnt - 1);
+ buf[cnt - 1] = '{';
+ buf[cnt] = '}';
+ memset(buf + cnt + 1, ']', cnt - 1);
+ buf[2 * cnt] = 0;
+ return buf;
+}
+
+static void limits_nesting(void)
+{
+ enum { max_nesting = 1024 }; /* see qobject/json-streamer.c */
+ char buf[2 * (max_nesting + 1) + 1];
+ QObject *obj;
+
+ obj = qobject_from_json(make_nest(buf, max_nesting));
+ g_assert(obj != NULL);
+ qobject_decref(obj);
+
+ obj = qobject_from_json(make_nest(buf, max_nesting + 1));
+ g_assert(obj == NULL);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -1519,6 +1543,7 @@ int main(int argc, char **argv)
g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
g_test_add_func("/errors/unterminated/literal", unterminated_literal);
+ g_test_add_func("/errors/limits/nesting", limits_nesting);
return g_test_run();
}
diff --git a/tests/libqtest.c b/tests/libqtest.c
index f6f3d7a950..9753161916 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -351,7 +351,7 @@ typedef struct {
QDict *response;
} QMPResponseParser;
-static void qmp_response(JSONMessageParser *parser, QList *tokens)
+static void qmp_response(JSONMessageParser *parser, GQueue *tokens)
{
QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser);
QObject *obj;