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