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