@samitouri / QOSamiQemu / commits / 3b2f714e4a

json-parser: replace with a push parser

In order to avoid stashing all the tokens corresponding to a JSON value, embed the parsing stack and state machine in JSONParser. This is more efficient and allows for more prompt error recovery; it also does not make the code substantially larger than the current recursive descent parser, though the state machine is probably a bit harder to follow. The stack consists of QLists and QDicts corresponding to open brackets and braces, plus optionally a QString with the current key on top of each QDict. After each value is parsed, it is added to the top array or dictionary or, if the stack is empty, json_parser_feed returns the complete QObject. For now, json-streamer.c keeps tracking the tokens up until braces and brackets are balanced, and then shoves the whole queue of tokens into the push parser. The only logic change is that JSON_END_OF_INPUT always triggers the emptying of the queue; the parser takes notice and checks that there is nothing on the stack. Not using brace_count and bracket_count for this is the first step towards improved separation of concerns between json-parser.c and json-streamer.c. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-ID: <20260626101727.1727389-2-pbonzini@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Minor comment improvements] Signed-off-by: Markus Armbruster <armbru@redhat.com>

Paolo Bonzini committed Jun 26, 2026 at 12:17 UTC 3b2f714e4a17d42b75d92422b191ab095cbf7c6b
4 files changed +358 -235
include/qobject/json-parser.h
+6
@@ -20,6 +20,12 @@ typedef struct JSONLexer {
20 int x, y;
21 } JSONLexer;
22
23 +typedef struct JSONParserContext {
24 + Error *err;
25 + GQueue *stack;
26 + va_list *ap;
27 +} JSONParserContext;
28 +
29 typedef struct JSONMessageParser {
30 void (*emit)(void *opaque, QObject *json, Error *err);
31 void *opaque;
qobject/json-parser-int.h
+4 -1
@@ -49,6 +49,9 @@ void json_message_process_token(JSONLexer *lexer, GString *input,
49
50 /* json-parser.c */
51 JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr);
52 -QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp);
52 +void json_parser_init(JSONParserContext *ctxt, va_list *ap);
53 +void json_parser_reset(JSONParserContext *ctxt);
54 +QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token, Error **errp);
55 +void json_parser_destroy(JSONParserContext *ctxt);
56
57 #endif
qobject/json-parser.c
+331 -230
@@ -31,12 +31,112 @@ struct JSONToken {
31 char str[];
32 };
33
34 -typedef struct JSONParserContext {
35 - Error *err;
36 - JSONToken *current;
37 - GQueue *buf;
38 - va_list *ap;
39 -} JSONParserContext;
34 +/*
35 + * The JSON parser is a push parser, returning a completed top-level
36 + * object, an error, or NULL (if the object is incomplete and no error
37 + * happened) after every token. Therefore it has an explicit
38 + * representation of its parser stack; each stack entry consists of a
39 + * parser state and a QObject: - a QList, for an array that is being
40 + * added to - a QDict, for a dictionary that is being added to - a
41 + * QString, for the key of the next pair that will be added to a QDict
42 + *
43 + * The stack represents an arbitrary nesting of arrays and dictionaries
44 + * (whose next key has been parsed); it can also have a dictionary whose
45 + * next key has not been parsed, but that can only happen at the top level.
46 + * Because of this, the stack contents are always of the form
47 + * "(QList | QDict QString)* QDict?".
48 + *
49 + * An empty stack represents the beginning of the parsing process, with
50 + * start state BEFORE_VALUE.
51 + */
52 +
53 +typedef enum JSONParserState {
54 + AFTER_LCURLY,
55 + AFTER_LSQUARE,
56 + BEFORE_KEY,
57 + BEFORE_VALUE,
58 + END_OF_KEY,
59 + END_OF_VALUE,
60 +} JSONParserState;
61 +
62 +typedef struct JSONParserStackEntry {
63 + /*
64 + * State when the container is completed or, for the top of the stack,
65 + * entry state for the next token.
66 + */
67 + JSONParserState state;
68 +
69 + /*
70 + * A QString with the last parsed key, or a QList/QDict for the current
71 + * container.
72 + */
73 + QObject *partial;
74 +} JSONParserStackEntry;
75 +
76 +/*
77 + * This is the JSON grammar that's parsed, with the state transition and
78 + * action at each point of the grammar. While this is not a formal
79 + * description, "-> action" represents the pseudocode of the action
80 + * and "-> STATE" sets the top stack entry's state to STATE.
81 + *
82 + * The state alone is enough to tell you what to parse; the state plus
83 + * the type of the top of stack tells you which action to take.
84 + *
85 + * // The initial state is BEFORE_VALUE.
86 + * input := value -> END_OF_VALUE -> return parsed value
87 + * (input | END_OF_INPUT)
88 + *
89 + * // entered on BEFORE_VALUE; after any of these rules are processed, the
90 + * // parser has completed a QObject and is in the END_OF_VALUE state.
91 + * //
92 + * // When the parser reaches the END_OF_VALUE state, it examines the
93 + * // top of the stack to see if it's coming from "input" (stack empty),
94 + * // "array_items" (TOS is a QList) or "dict_pairs" (TOS is a QString; the
95 + * // item below will be a QDict). It then proceeds with the corresponding
96 + * // actions, which will be one of:
97 + * // - return parsed value
98 + * // - add value to QList
99 + * // - pop QString with the key, add key/value to the QDict
100 + * value := literal -> END_OF_VALUE
101 + * | '[' -> push empty QList -> AFTER_LSQUARE
102 + * after_lsquare -> END_OF_VALUE
103 + * | '{' -> push empty QDict -> AFTER_LCURLY
104 + * after_lcurly -> END_OF_VALUE
105 + *
106 + * // non-recursive values, entered on BEFORE_VALUE
107 + * literal := INTEGER -> END_OF_VALUE
108 + * | FLOAT -> END_OF_VALUE
109 + * | KEYWORD -> END_OF_VALUE
110 + * | STRING -> END_OF_VALUE
111 + * | INTERP -> END_OF_VALUE
112 + *
113 + * // entered on AFTER_LSQUARE
114 + * after_lsquare := ']' -> pop completed QList -> END_OF_VALUE
115 + * | ϵ -> BEFORE_VALUE
116 + * array_items -> END_OF_VALUE
117 + *
118 + * // entered on BEFORE_VALUE, with TOS being a QList
119 + * array_items := value -> add value to QList -> END_OF_VALUE
120 + * (']' -> pop completed QList -> END_OF_VALUE
121 + * | ',' -> BEFORE_VALUE
122 + * array_items) -> END_OF_VALUE
123 + *
124 + * // entered on AFTER_LCURLY
125 + * after_lcurly := '}' -> pop completed QDict -> END_OF_VALUE
126 + * | ϵ -> BEFORE_KEY
127 + * dict_pairs -> END_OF_VALUE
128 + *
129 + * // entered on BEFORE_KEY, with TOS being a QDict
130 + * dict_pairs := (STRING | INTERP) -> push QString -> END_OF_KEY
131 + * ':' -> BEFORE_VALUE
132 + * value -> pop QString + add pair to QDict -> END_OF_VALUE
133 + * ('}' -> pop completed QDict -> END_OF_VALUE
134 + * | ',' -> BEFORE_KEY
135 + * dict_pairs) -> END_OF_VALUE
136 + *
137 + * Parse errors ignore the token. json_parser_reset() can be
138 + * called to restart parsing from scratch, with an empty stack.
139 + */
140
141 #define BUG_ON(cond) assert(!(cond))
142
@@ -49,7 +149,27 @@ typedef struct JSONParserContext {
149 * 4) deal with premature EOI
150 */
151
52 -static QObject *parse_value(JSONParserContext *ctxt);
152 +static inline JSONParserStackEntry *current_entry(JSONParserContext *ctxt)
153 +{
154 + return g_queue_peek_tail(ctxt->stack);
155 +}
156 +
157 +static void push_entry(JSONParserContext *ctxt, QObject *partial,
158 + JSONParserState state)
159 +{
160 + JSONParserStackEntry *entry = g_new(JSONParserStackEntry, 1);
161 + entry->partial = partial;
162 + entry->state = state;
163 + g_queue_push_tail(ctxt->stack, entry);
164 +}
165 +
166 +/* Drop the top entry and return the new top entry. */
167 +static JSONParserStackEntry *pop_entry(JSONParserContext *ctxt)
168 +{
169 + JSONParserStackEntry *entry = g_queue_pop_tail(ctxt->stack);
170 + g_free(entry);
171 + return current_entry(ctxt);
172 +}
173
174 /**
175 * Error handler
@@ -236,200 +356,10 @@ out:
356 return NULL;
357 }
358
239 -/* Note: the token object returned by parser_context_peek_token or
240 - * parser_context_pop_token is deleted as soon as parser_context_pop_token
241 - * is called again.
242 - */
243 -static const JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
244 -{
245 - g_free(ctxt->current);
246 - ctxt->current = g_queue_pop_head(ctxt->buf);
247 - return ctxt->current;
248 -}
249 -
250 -static const JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
251 -{
252 - return g_queue_peek_head(ctxt->buf);
253 -}
254 -
255 -/**
256 - * Parsing rules
257 - */
258 -static int parse_pair(JSONParserContext *ctxt, QDict *dict)
259 -{
260 - QObject *key_obj = NULL;
261 - QString *key;
262 - QObject *value;
263 - const JSONToken *peek, *token;
264 -
265 - peek = parser_context_peek_token(ctxt);
266 - if (peek == NULL) {
267 - parse_error(ctxt, NULL, "premature EOI");
268 - goto out;
269 - }
270 -
271 - key_obj = parse_value(ctxt);
272 - key = qobject_to(QString, key_obj);
273 - if (!key) {
274 - parse_error(ctxt, peek, "key is not a string in object");
275 - goto out;
276 - }
277 -
278 - token = parser_context_pop_token(ctxt);
279 - if (token == NULL) {
280 - parse_error(ctxt, NULL, "premature EOI");
281 - goto out;
282 - }
283 -
284 - if (token->type != JSON_COLON) {
285 - parse_error(ctxt, token, "missing : in object pair");
286 - goto out;
287 - }
288 -
289 - value = parse_value(ctxt);
290 - if (value == NULL) {
291 - parse_error(ctxt, token, "Missing value in dict");
292 - goto out;
293 - }
294 -
295 - if (qdict_haskey(dict, qstring_get_str(key))) {
296 - parse_error(ctxt, token, "duplicate key");
297 - goto out;
298 - }
299 -
300 - qdict_put_obj(dict, qstring_get_str(key), value);
301 -
302 - qobject_unref(key_obj);
303 - return 0;
304 -
305 -out:
306 - qobject_unref(key_obj);
307 - return -1;
308 -}
309 -
310 -static QObject *parse_object(JSONParserContext *ctxt)
311 -{
312 - QDict *dict = NULL;
313 - const JSONToken *token, *peek;
314 -
315 - token = parser_context_pop_token(ctxt);
316 - assert(token && token->type == JSON_LCURLY);
317 -
318 - dict = qdict_new();
319 -
320 - peek = parser_context_peek_token(ctxt);
321 - if (peek == NULL) {
322 - parse_error(ctxt, NULL, "premature EOI");
323 - goto out;
324 - }
325 -
326 - if (peek->type != JSON_RCURLY) {
327 - if (parse_pair(ctxt, dict) == -1) {
328 - goto out;
329 - }
330 -
331 - token = parser_context_pop_token(ctxt);
332 - if (token == NULL) {
333 - parse_error(ctxt, NULL, "premature EOI");
334 - goto out;
335 - }
336 -
337 - while (token->type != JSON_RCURLY) {
338 - if (token->type != JSON_COMMA) {
339 - parse_error(ctxt, token, "expected separator in dict");
340 - goto out;
341 - }
342 -
343 - if (parse_pair(ctxt, dict) == -1) {
344 - goto out;
345 - }
346 -
347 - token = parser_context_pop_token(ctxt);
348 - if (token == NULL) {
349 - parse_error(ctxt, NULL, "premature EOI");
350 - goto out;
351 - }
352 - }
353 - } else {
354 - (void)parser_context_pop_token(ctxt);
355 - }
356 -
357 - return QOBJECT(dict);
358 -
359 -out:
360 - qobject_unref(dict);
361 - return NULL;
362 -}
363 -
364 -static QObject *parse_array(JSONParserContext *ctxt)
365 -{
366 - QList *list = NULL;
367 - const JSONToken *token, *peek;
368 -
369 - token = parser_context_pop_token(ctxt);
370 - assert(token && token->type == JSON_LSQUARE);
371 -
372 - list = qlist_new();
373 -
374 - peek = parser_context_peek_token(ctxt);
375 - if (peek == NULL) {
376 - parse_error(ctxt, NULL, "premature EOI");
377 - goto out;
378 - }
379 -
380 - if (peek->type != JSON_RSQUARE) {
381 - QObject *obj;
382 -
383 - obj = parse_value(ctxt);
384 - if (obj == NULL) {
385 - parse_error(ctxt, token, "expecting value");
386 - goto out;
387 - }
388 -
389 - qlist_append_obj(list, obj);
390 -
391 - token = parser_context_pop_token(ctxt);
392 - if (token == NULL) {
393 - parse_error(ctxt, NULL, "premature EOI");
394 - goto out;
395 - }
396 -
397 - while (token->type != JSON_RSQUARE) {
398 - if (token->type != JSON_COMMA) {
399 - parse_error(ctxt, token, "expected separator in list");
400 - goto out;
401 - }
402 -
403 - obj = parse_value(ctxt);
404 - if (obj == NULL) {
405 - parse_error(ctxt, token, "expecting value");
406 - goto out;
407 - }
408 -
409 - qlist_append_obj(list, obj);
359 +/* Terminals */
360
411 - token = parser_context_pop_token(ctxt);
412 - if (token == NULL) {
413 - parse_error(ctxt, NULL, "premature EOI");
414 - goto out;
415 - }
416 - }
417 - } else {
418 - (void)parser_context_pop_token(ctxt);
419 - }
420 -
421 - return QOBJECT(list);
422 -
423 -out:
424 - qobject_unref(list);
425 - return NULL;
426 -}
427 -
428 -static QObject *parse_keyword(JSONParserContext *ctxt)
361 +static QObject *parse_keyword(JSONParserContext *ctxt, const JSONToken *token)
362 {
430 - const JSONToken *token;
431 -
432 - token = parser_context_pop_token(ctxt);
363 assert(token && token->type == JSON_KEYWORD);
364
365 if (!strcmp(token->str, "true")) {
@@ -443,11 +373,9 @@ static QObject *parse_keyword(JSONParserContext *ctxt)
373 return NULL;
374 }
375
446 -static QObject *parse_interpolation(JSONParserContext *ctxt)
376 +static QObject *parse_interpolation(JSONParserContext *ctxt,
377 + const JSONToken *token)
378 {
448 - const JSONToken *token;
449 -
450 - token = parser_context_pop_token(ctxt);
379 assert(token && token->type == JSON_INTERP);
380
381 if (!strcmp(token->str, "%p")) {
@@ -479,11 +407,8 @@ static QObject *parse_interpolation(JSONParserContext *ctxt)
407 return NULL;
408 }
409
482 -static QObject *parse_literal(JSONParserContext *ctxt)
410 +static QObject *parse_literal(JSONParserContext *ctxt, const JSONToken *token)
411 {
484 - const JSONToken *token;
485 -
486 - token = parser_context_pop_token(ctxt);
412 assert(token);
413
414 switch (token->type) {
@@ -531,35 +456,174 @@ static QObject *parse_literal(JSONParserContext *ctxt)
456 }
457 }
458
534 -static QObject *parse_value(JSONParserContext *ctxt)
535 -{
536 - const JSONToken *token;
537 -
538 - token = parser_context_peek_token(ctxt);
539 - if (token == NULL) {
540 - parse_error(ctxt, NULL, "premature EOI");
541 - return NULL;
542 - }
459 +/* Parsing state machine */
460
461 +static QObject *parse_begin_value(JSONParserContext *ctxt,
462 + const JSONToken *token)
463 +{
464 switch (token->type) {
465 case JSON_LCURLY:
546 - return parse_object(ctxt);
466 + push_entry(ctxt, QOBJECT(qdict_new()), AFTER_LCURLY);
467 + return NULL;
468 case JSON_LSQUARE:
548 - return parse_array(ctxt);
469 + push_entry(ctxt, QOBJECT(qlist_new()), AFTER_LSQUARE);
470 + return NULL;
471 case JSON_INTERP:
550 - return parse_interpolation(ctxt);
472 + return parse_interpolation(ctxt, token);
473 case JSON_INTEGER:
474 case JSON_FLOAT:
475 case JSON_STRING:
554 - return parse_literal(ctxt);
476 + return parse_literal(ctxt, token);
477 case JSON_KEYWORD:
556 - return parse_keyword(ctxt);
478 + return parse_keyword(ctxt, token);
479 default:
480 parse_error(ctxt, token, "expecting value");
481 return NULL;
482 }
483 }
484
485 +static QObject *parse_token(JSONParserContext *ctxt, const JSONToken *token)
486 +{
487 + JSONParserStackEntry *entry;
488 + JSONParserState state;
489 + QString *key;
490 + QObject *key_obj = NULL, *value = NULL;
491 +
492 + entry = current_entry(ctxt);
493 + state = entry ? entry->state : BEFORE_VALUE;
494 + switch (state) {
495 + case AFTER_LCURLY:
496 + /* Grab '}' for empty object or fall through to BEFORE_KEY */
497 + assert(qobject_type(entry->partial) == QTYPE_QDICT);
498 + if (token->type == JSON_RCURLY) {
499 + value = entry->partial;
500 + entry = pop_entry(ctxt);
501 + break;
502 + }
503 + entry->state = BEFORE_KEY;
504 + /* fall through */
505 +
506 + case BEFORE_KEY:
507 + /* Expecting object key */
508 + assert(qobject_type(entry->partial) == QTYPE_QDICT);
509 + if (token->type != JSON_STRING && token->type != JSON_INTERP) {
510 + parse_error(ctxt, token, "expecting key");
511 + return NULL;
512 + }
513 +
514 + key_obj = parse_begin_value(ctxt, token);
515 + if (!key_obj) {
516 + /* Parse error already reported */
517 + } else if (qobject_type(key_obj) != QTYPE_QSTRING) {
518 + /* An interpolation was valid syntactically but not %s */
519 + parse_error(ctxt, token, "key is not a string in object");
520 + } else {
521 + /* Store key in a special entry on the stack */
522 + push_entry(ctxt, key_obj, END_OF_KEY);
523 + }
524 + return NULL;
525 +
526 + case END_OF_KEY:
527 + /* Expecting ':' after key */
528 + assert(qobject_type(entry->partial) == QTYPE_QSTRING);
529 + if (token->type == JSON_COLON) {
530 + entry->state = BEFORE_VALUE;
531 + } else {
532 + parse_error(ctxt, token, "expecting ':'");
533 + }
534 + return NULL;
535 +
536 + case AFTER_LSQUARE:
537 + /* Grab ']' for empty array or fall through to BEFORE_VALUE */
538 + assert(qobject_type(entry->partial) == QTYPE_QLIST);
539 + if (token->type == JSON_RSQUARE) {
540 + value = entry->partial;
541 + entry = pop_entry(ctxt);
542 + break;
543 + }
544 + entry->state = BEFORE_VALUE;
545 + /* fall through */
546 +
547 + case BEFORE_VALUE:
548 + /* Expecting value */
549 + assert(!entry || qobject_type(entry->partial) != QTYPE_QDICT);
550 + value = parse_begin_value(ctxt, token);
551 + if (!value) {
552 + /* Error or '['/'{' */
553 + return NULL;
554 + }
555 + /* Return value or insert it into a container */
556 + break;
557 +
558 + case END_OF_VALUE:
559 + /* Grab ',' or ']' for array; ',' or '}' for object */
560 + if (qobject_to(QList, entry->partial)) {
561 + /* Array */
562 + if (token->type != JSON_RSQUARE) {
563 + if (token->type == JSON_COMMA) {
564 + entry->state = BEFORE_VALUE;
565 + } else {
566 + parse_error(ctxt, token, "expected ',' or ']'");
567 + }
568 + return NULL;
569 + }
570 + } else if (qobject_to(QDict, entry->partial)) {
571 + /* Object */
572 + if (token->type != JSON_RCURLY) {
573 + if (token->type == JSON_COMMA) {
574 + entry->state = BEFORE_KEY;
575 + } else {
576 + parse_error(ctxt, token, "expected ',' or '}'");
577 + }
578 + return NULL;
579 + }
580 + } else {
581 + g_assert_not_reached();
582 + }
583 +
584 + /* Got ']' or '}'; return full value or insert into parent container */
585 + value = entry->partial;
586 + entry = pop_entry(ctxt);
587 + break;
588 + }
589 +
590 + assert(value);
591 + if (entry == NULL) {
592 + /* Parse stack now empty, the top-level value is complete. */
593 + return value;
594 + }
595 +
596 + /*
597 + * Parse stack is not empty and entry->partial is the top of stack.
598 + * It's a QString with the key (and a QDict is below it) if we're
599 + * parsing an object, or a QList if we're parsing an array.
600 + */
601 + key = qobject_to(QString, entry->partial);
602 + if (key) {
603 + const char *key_str;
604 + QDict *dict;
605 +
606 + /* Pop off key, and store (key, value) in QDict. */
607 + entry = pop_entry(ctxt);
608 + dict = qobject_to(QDict, entry->partial);
609 + assert(dict);
610 + key_str = qstring_get_str(key);
611 + if (qdict_haskey(dict, key_str)) {
612 + parse_error(ctxt, token, "duplicate key");
613 + qobject_unref(value);
614 + return NULL;
615 + }
616 + qdict_put_obj(dict, key_str, value);
617 + qobject_unref(key);
618 + } else {
619 + /* Array, just store value in the QList. */
620 + qlist_append_obj(qobject_to(QList, entry->partial), value);
621 + }
622 +
623 + entry->state = END_OF_VALUE;
624 + return NULL;
625 +}
626 +
627 JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr)
628 {
629 JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1);
@@ -572,20 +636,57 @@ JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr)
636 return token;
637 }
638
575 -QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp)
639 +void json_parser_reset(JSONParserContext *ctxt)
640 {
577 - JSONParserContext ctxt = { .buf = tokens, .ap = ap };
578 - QObject *result;
641 + JSONParserStackEntry *entry;
642
580 - result = parse_value(&ctxt);
581 - assert(ctxt.err || g_queue_is_empty(ctxt.buf));
643 + ctxt->err = NULL;
644 + while ((entry = g_queue_pop_tail(ctxt->stack)) != NULL) {
645 + qobject_unref(entry->partial);
646 + g_free(entry);
647 + }
648 +}
649
583 - error_propagate(errp, ctxt.err);
650 +void json_parser_init(JSONParserContext *ctxt, va_list *ap)
651 +{
652 + ctxt->stack = g_queue_new();
653 + ctxt->ap = ap;
654 + json_parser_reset(ctxt);
655 +}
656
585 - while (!g_queue_is_empty(ctxt.buf)) {
586 - parser_context_pop_token(&ctxt);
657 +void json_parser_destroy(JSONParserContext *ctxt)
658 +{
659 + json_parser_reset(ctxt);
660 + g_queue_free(ctxt->stack);
661 + ctxt->stack = NULL;
662 +}
663 +
664 +/*
665 + * Advance the parser based on the token that is passed.
666 + * Return the finished top-level value if the token completes it, else
667 + * NULL.
668 + * Once an error is returned, the function must not be called again
669 + * without first resetting the parser.
670 + */
671 +QObject *json_parser_feed(JSONParserContext *ctxt, const JSONToken *token,
672 + Error **errp)
673 +{
674 + QObject *result = NULL;
675 +
676 + assert(!ctxt->err);
677 + switch (token->type) {
678 + case JSON_END_OF_INPUT:
679 + /* Check for premature end of input */
680 + if (!g_queue_is_empty(ctxt->stack)) {
681 + parse_error(ctxt, token, "premature end of input");
682 + }
683 + break;
684 +
685 + default:
686 + result = parse_token(ctxt, token);
687 + break;
688 }
588 - g_free(ctxt.current);
689
690 + error_propagate(errp, ctxt->err);
691 return result;
692 }
qobject/json-streamer.c
+17 -4
@@ -32,6 +32,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input,
32 JSONTokenType type, int x, int y)
33 {
34 JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer);
35 + JSONParserContext ctxt;
36 QObject *json = NULL;
37 Error *err = NULL;
38 JSONToken *token;
@@ -56,8 +57,7 @@ void json_message_process_token(JSONLexer *lexer, GString *input,
57 if (g_queue_is_empty(&parser->tokens)) {
58 return;
59 }
59 - json = json_parser_parse(&parser->tokens, parser->ap, &err);
60 - goto out_emit;
60 + break;
61 default:
62 break;
63 }
@@ -85,11 +85,24 @@ void json_message_process_token(JSONLexer *lexer, GString *input,
85 g_queue_push_tail(&parser->tokens, token);
86
87 if ((parser->brace_count > 0 || parser->bracket_count > 0)
88 - && parser->brace_count >= 0 && parser->bracket_count >= 0) {
88 + && parser->brace_count >= 0 && parser->bracket_count >= 0
89 + && type != JSON_END_OF_INPUT) {
90 return;
91 }
92
92 - json = json_parser_parse(&parser->tokens, parser->ap, &err);
93 + json_parser_init(&ctxt, parser->ap);
94 +
95 + /* Process all tokens in the queue */
96 + while (!g_queue_is_empty(&parser->tokens)) {
97 + token = g_queue_pop_head(&parser->tokens);
98 + json = json_parser_feed(&ctxt, token, &err);
99 + g_free(token);
100 + if (json || err) {
101 + break;
102 + }
103 + }
104 +
105 + json_parser_destroy(&ctxt);
106
107 out_emit:
108 parser->brace_count = 0;