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