xref: /openbmc/qemu/qobject/json-parser.c (revision 0bdb12c7)
1 /*
2  * JSON Parser
3  *
4  * Copyright IBM, Corp. 2009
5  *
6  * Authors:
7  *  Anthony Liguori   <aliguori@us.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10  * See the COPYING.LIB file in the top-level directory.
11  *
12  */
13 
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "qemu-common.h"
17 #include "qapi/qmp/types.h"
18 #include "qapi/qmp/json-parser.h"
19 #include "qapi/qmp/json-lexer.h"
20 #include "qapi/qmp/json-streamer.h"
21 
22 typedef struct JSONParserContext
23 {
24     Error *err;
25     JSONToken *current;
26     GQueue *buf;
27 } JSONParserContext;
28 
29 #define BUG_ON(cond) assert(!(cond))
30 
31 /**
32  * TODO
33  *
34  * 0) make errors meaningful again
35  * 1) add geometry information to tokens
36  * 3) should we return a parsed size?
37  * 4) deal with premature EOI
38  */
39 
40 static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
41 
42 /**
43  * Error handler
44  */
45 static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
46                                            JSONToken *token, const char *msg, ...)
47 {
48     va_list ap;
49     char message[1024];
50     va_start(ap, msg);
51     vsnprintf(message, sizeof(message), msg, ap);
52     va_end(ap);
53     if (ctxt->err) {
54         error_free(ctxt->err);
55         ctxt->err = NULL;
56     }
57     error_setg(&ctxt->err, "JSON parse error, %s", message);
58 }
59 
60 /**
61  * String helpers
62  *
63  * These helpers are used to unescape strings.
64  */
65 static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length)
66 {
67     if (wchar <= 0x007F) {
68         BUG_ON(buffer_length < 2);
69 
70         buffer[0] = wchar & 0x7F;
71         buffer[1] = 0;
72     } else if (wchar <= 0x07FF) {
73         BUG_ON(buffer_length < 3);
74 
75         buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F);
76         buffer[1] = 0x80 | (wchar & 0x3F);
77         buffer[2] = 0;
78     } else {
79         BUG_ON(buffer_length < 4);
80 
81         buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F);
82         buffer[1] = 0x80 | ((wchar >> 6) & 0x3F);
83         buffer[2] = 0x80 | (wchar & 0x3F);
84         buffer[3] = 0;
85     }
86 }
87 
88 static int hex2decimal(char ch)
89 {
90     if (ch >= '0' && ch <= '9') {
91         return (ch - '0');
92     } else if (ch >= 'a' && ch <= 'f') {
93         return 10 + (ch - 'a');
94     } else if (ch >= 'A' && ch <= 'F') {
95         return 10 + (ch - 'A');
96     }
97 
98     return -1;
99 }
100 
101 /**
102  * parse_string(): Parse a json string and return a QObject
103  *
104  *  string
105  *      ""
106  *      " chars "
107  *  chars
108  *      char
109  *      char chars
110  *  char
111  *      any-Unicode-character-
112  *          except-"-or-\-or-
113  *          control-character
114  *      \"
115  *      \\
116  *      \/
117  *      \b
118  *      \f
119  *      \n
120  *      \r
121  *      \t
122  *      \u four-hex-digits
123  */
124 static QString *qstring_from_escaped_str(JSONParserContext *ctxt,
125                                          JSONToken *token)
126 {
127     const char *ptr = token->str;
128     QString *str;
129     int double_quote = 1;
130 
131     if (*ptr == '"') {
132         double_quote = 1;
133     } else {
134         double_quote = 0;
135     }
136     ptr++;
137 
138     str = qstring_new();
139     while (*ptr &&
140            ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) {
141         if (*ptr == '\\') {
142             ptr++;
143 
144             switch (*ptr) {
145             case '"':
146                 qstring_append(str, "\"");
147                 ptr++;
148                 break;
149             case '\'':
150                 qstring_append(str, "'");
151                 ptr++;
152                 break;
153             case '\\':
154                 qstring_append(str, "\\");
155                 ptr++;
156                 break;
157             case '/':
158                 qstring_append(str, "/");
159                 ptr++;
160                 break;
161             case 'b':
162                 qstring_append(str, "\b");
163                 ptr++;
164                 break;
165             case 'f':
166                 qstring_append(str, "\f");
167                 ptr++;
168                 break;
169             case 'n':
170                 qstring_append(str, "\n");
171                 ptr++;
172                 break;
173             case 'r':
174                 qstring_append(str, "\r");
175                 ptr++;
176                 break;
177             case 't':
178                 qstring_append(str, "\t");
179                 ptr++;
180                 break;
181             case 'u': {
182                 uint16_t unicode_char = 0;
183                 char utf8_char[4];
184                 int i = 0;
185 
186                 ptr++;
187 
188                 for (i = 0; i < 4; i++) {
189                     if (qemu_isxdigit(*ptr)) {
190                         unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4);
191                     } else {
192                         parse_error(ctxt, token,
193                                     "invalid hex escape sequence in string");
194                         goto out;
195                     }
196                     ptr++;
197                 }
198 
199                 wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char));
200                 qstring_append(str, utf8_char);
201             }   break;
202             default:
203                 parse_error(ctxt, token, "invalid escape sequence in string");
204                 goto out;
205             }
206         } else {
207             char dummy[2];
208 
209             dummy[0] = *ptr++;
210             dummy[1] = 0;
211 
212             qstring_append(str, dummy);
213         }
214     }
215 
216     return str;
217 
218 out:
219     QDECREF(str);
220     return NULL;
221 }
222 
223 /* Note: the token object returned by parser_context_peek_token or
224  * parser_context_pop_token is deleted as soon as parser_context_pop_token
225  * is called again.
226  */
227 static JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
228 {
229     g_free(ctxt->current);
230     assert(!g_queue_is_empty(ctxt->buf));
231     ctxt->current = g_queue_pop_head(ctxt->buf);
232     return ctxt->current;
233 }
234 
235 static JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
236 {
237     assert(!g_queue_is_empty(ctxt->buf));
238     return g_queue_peek_head(ctxt->buf);
239 }
240 
241 static JSONParserContext *parser_context_new(GQueue *tokens)
242 {
243     JSONParserContext *ctxt;
244 
245     if (!tokens) {
246         return NULL;
247     }
248 
249     ctxt = g_malloc0(sizeof(JSONParserContext));
250     ctxt->buf = tokens;
251 
252     return ctxt;
253 }
254 
255 /* to support error propagation, ctxt->err must be freed separately */
256 static void parser_context_free(JSONParserContext *ctxt)
257 {
258     if (ctxt) {
259         while (!g_queue_is_empty(ctxt->buf)) {
260             parser_context_pop_token(ctxt);
261         }
262         g_free(ctxt->current);
263         g_queue_free(ctxt->buf);
264         g_free(ctxt);
265     }
266 }
267 
268 /**
269  * Parsing rules
270  */
271 static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
272 {
273     QObject *key = NULL, *value;
274     JSONToken *peek, *token;
275 
276     peek = parser_context_peek_token(ctxt);
277     if (peek == NULL) {
278         parse_error(ctxt, NULL, "premature EOI");
279         goto out;
280     }
281 
282     key = parse_value(ctxt, ap);
283     if (!key || qobject_type(key) != QTYPE_QSTRING) {
284         parse_error(ctxt, peek, "key is not a string in object");
285         goto out;
286     }
287 
288     token = parser_context_pop_token(ctxt);
289     if (token == NULL) {
290         parse_error(ctxt, NULL, "premature EOI");
291         goto out;
292     }
293 
294     if (token->type != JSON_COLON) {
295         parse_error(ctxt, token, "missing : in object pair");
296         goto out;
297     }
298 
299     value = parse_value(ctxt, ap);
300     if (value == NULL) {
301         parse_error(ctxt, token, "Missing value in dict");
302         goto out;
303     }
304 
305     qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value);
306 
307     qobject_decref(key);
308 
309     return 0;
310 
311 out:
312     qobject_decref(key);
313 
314     return -1;
315 }
316 
317 static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
318 {
319     QDict *dict = NULL;
320     JSONToken *token, *peek;
321 
322     token = parser_context_pop_token(ctxt);
323     assert(token && token->type == JSON_LCURLY);
324 
325     dict = qdict_new();
326 
327     peek = parser_context_peek_token(ctxt);
328     if (peek == NULL) {
329         parse_error(ctxt, NULL, "premature EOI");
330         goto out;
331     }
332 
333     if (peek->type != JSON_RCURLY) {
334         if (parse_pair(ctxt, dict, ap) == -1) {
335             goto out;
336         }
337 
338         token = parser_context_pop_token(ctxt);
339         if (token == NULL) {
340             parse_error(ctxt, NULL, "premature EOI");
341             goto out;
342         }
343 
344         while (token->type != JSON_RCURLY) {
345             if (token->type != JSON_COMMA) {
346                 parse_error(ctxt, token, "expected separator in dict");
347                 goto out;
348             }
349 
350             if (parse_pair(ctxt, dict, ap) == -1) {
351                 goto out;
352             }
353 
354             token = parser_context_pop_token(ctxt);
355             if (token == NULL) {
356                 parse_error(ctxt, NULL, "premature EOI");
357                 goto out;
358             }
359         }
360     } else {
361         (void)parser_context_pop_token(ctxt);
362     }
363 
364     return QOBJECT(dict);
365 
366 out:
367     QDECREF(dict);
368     return NULL;
369 }
370 
371 static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
372 {
373     QList *list = NULL;
374     JSONToken *token, *peek;
375 
376     token = parser_context_pop_token(ctxt);
377     assert(token && token->type == JSON_LSQUARE);
378 
379     list = qlist_new();
380 
381     peek = parser_context_peek_token(ctxt);
382     if (peek == NULL) {
383         parse_error(ctxt, NULL, "premature EOI");
384         goto out;
385     }
386 
387     if (peek->type != JSON_RSQUARE) {
388         QObject *obj;
389 
390         obj = parse_value(ctxt, ap);
391         if (obj == NULL) {
392             parse_error(ctxt, token, "expecting value");
393             goto out;
394         }
395 
396         qlist_append_obj(list, obj);
397 
398         token = parser_context_pop_token(ctxt);
399         if (token == NULL) {
400             parse_error(ctxt, NULL, "premature EOI");
401             goto out;
402         }
403 
404         while (token->type != JSON_RSQUARE) {
405             if (token->type != JSON_COMMA) {
406                 parse_error(ctxt, token, "expected separator in list");
407                 goto out;
408             }
409 
410             obj = parse_value(ctxt, ap);
411             if (obj == NULL) {
412                 parse_error(ctxt, token, "expecting value");
413                 goto out;
414             }
415 
416             qlist_append_obj(list, obj);
417 
418             token = parser_context_pop_token(ctxt);
419             if (token == NULL) {
420                 parse_error(ctxt, NULL, "premature EOI");
421                 goto out;
422             }
423         }
424     } else {
425         (void)parser_context_pop_token(ctxt);
426     }
427 
428     return QOBJECT(list);
429 
430 out:
431     QDECREF(list);
432     return NULL;
433 }
434 
435 static QObject *parse_keyword(JSONParserContext *ctxt)
436 {
437     JSONToken *token;
438 
439     token = parser_context_pop_token(ctxt);
440     assert(token && token->type == JSON_KEYWORD);
441 
442     if (!strcmp(token->str, "true")) {
443         return QOBJECT(qbool_from_bool(true));
444     } else if (!strcmp(token->str, "false")) {
445         return QOBJECT(qbool_from_bool(false));
446     } else if (!strcmp(token->str, "null")) {
447         return qnull();
448     }
449     parse_error(ctxt, token, "invalid keyword '%s'", token->str);
450     return NULL;
451 }
452 
453 static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap)
454 {
455     JSONToken *token;
456 
457     if (ap == NULL) {
458         return NULL;
459     }
460 
461     token = parser_context_pop_token(ctxt);
462     assert(token && token->type == JSON_ESCAPE);
463 
464     if (!strcmp(token->str, "%p")) {
465         return va_arg(*ap, QObject *);
466     } else if (!strcmp(token->str, "%i")) {
467         return QOBJECT(qbool_from_bool(va_arg(*ap, int)));
468     } else if (!strcmp(token->str, "%d")) {
469         return QOBJECT(qint_from_int(va_arg(*ap, int)));
470     } else if (!strcmp(token->str, "%ld")) {
471         return QOBJECT(qint_from_int(va_arg(*ap, long)));
472     } else if (!strcmp(token->str, "%lld") ||
473                !strcmp(token->str, "%I64d")) {
474         return QOBJECT(qint_from_int(va_arg(*ap, long long)));
475     } else if (!strcmp(token->str, "%s")) {
476         return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
477     } else if (!strcmp(token->str, "%f")) {
478         return QOBJECT(qfloat_from_double(va_arg(*ap, double)));
479     }
480     return NULL;
481 }
482 
483 static QObject *parse_literal(JSONParserContext *ctxt)
484 {
485     JSONToken *token;
486 
487     token = parser_context_pop_token(ctxt);
488     assert(token);
489 
490     switch (token->type) {
491     case JSON_STRING:
492         return QOBJECT(qstring_from_escaped_str(ctxt, token));
493     case JSON_INTEGER: {
494         /* A possibility exists that this is a whole-valued float where the
495          * fractional part was left out due to being 0 (.0). It's not a big
496          * deal to treat these as ints in the parser, so long as users of the
497          * resulting QObject know to expect a QInt in place of a QFloat in
498          * cases like these.
499          *
500          * However, in some cases these values will overflow/underflow a
501          * QInt/int64 container, thus we should assume these are to be handled
502          * as QFloats/doubles rather than silently changing their values.
503          *
504          * strtoll() indicates these instances by setting errno to ERANGE
505          */
506         int64_t value;
507 
508         errno = 0; /* strtoll doesn't set errno on success */
509         value = strtoll(token->str, NULL, 10);
510         if (errno != ERANGE) {
511             return QOBJECT(qint_from_int(value));
512         }
513         /* fall through to JSON_FLOAT */
514     }
515     case JSON_FLOAT:
516         /* FIXME dependent on locale; a pervasive issue in QEMU */
517         /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN,
518          * but those might be useful extensions beyond JSON */
519         return QOBJECT(qfloat_from_double(strtod(token->str, NULL)));
520     default:
521         abort();
522     }
523 }
524 
525 static QObject *parse_value(JSONParserContext *ctxt, va_list *ap)
526 {
527     JSONToken *token;
528 
529     token = parser_context_peek_token(ctxt);
530     if (token == NULL) {
531         parse_error(ctxt, NULL, "premature EOI");
532         return NULL;
533     }
534 
535     switch (token->type) {
536     case JSON_LCURLY:
537         return parse_object(ctxt, ap);
538     case JSON_LSQUARE:
539         return parse_array(ctxt, ap);
540     case JSON_ESCAPE:
541         return parse_escape(ctxt, ap);
542     case JSON_INTEGER:
543     case JSON_FLOAT:
544     case JSON_STRING:
545         return parse_literal(ctxt);
546     case JSON_KEYWORD:
547         return parse_keyword(ctxt);
548     default:
549         parse_error(ctxt, token, "expecting value");
550         return NULL;
551     }
552 }
553 
554 QObject *json_parser_parse(GQueue *tokens, va_list *ap)
555 {
556     return json_parser_parse_err(tokens, ap, NULL);
557 }
558 
559 QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp)
560 {
561     JSONParserContext *ctxt = parser_context_new(tokens);
562     QObject *result;
563 
564     if (!ctxt) {
565         return NULL;
566     }
567 
568     result = parse_value(ctxt, ap);
569 
570     error_propagate(errp, ctxt->err);
571 
572     parser_context_free(ctxt);
573 
574     return result;
575 }
576