]> git.proxmox.com Git - mirror_qemu.git/blame - qobject/json-parser.c
qobject: Drop superfluous includes of qemu-common.h
[mirror_qemu.git] / qobject / json-parser.c
CommitLineData
4a5fcab7 1/*
6e8e5cb9 2 * JSON Parser
4a5fcab7
AL
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
f2ad72b3 14#include "qemu/osdep.h"
2bc7cfea 15#include "qemu/cutils.h"
e59f39d4 16#include "qemu/unicode.h"
da34e65c 17#include "qapi/error.h"
4a5fcab7 18#include "qemu-common.h"
6b673957 19#include "qapi/qmp/qbool.h"
452fcdbc 20#include "qapi/qmp/qdict.h"
47e6b297 21#include "qapi/qmp/qlist.h"
15280c36
MA
22#include "qapi/qmp/qnull.h"
23#include "qapi/qmp/qnum.h"
6b673957 24#include "qapi/qmp/qstring.h"
7b1b5d19
PB
25#include "qapi/qmp/json-parser.h"
26#include "qapi/qmp/json-lexer.h"
9bada897 27#include "qapi/qmp/json-streamer.h"
4a5fcab7 28
abe7c206
MA
29struct JSONToken {
30 JSONTokenType type;
31 int x;
32 int y;
33 char str[];
34};
35
4a5fcab7
AL
36typedef struct JSONParserContext
37{
ef749d07 38 Error *err;
9bada897 39 JSONToken *current;
95385fe9 40 GQueue *buf;
4a5fcab7
AL
41} JSONParserContext;
42
43#define BUG_ON(cond) assert(!(cond))
44
45/**
46 * TODO
47 *
48 * 0) make errors meaningful again
49 * 1) add geometry information to tokens
50 * 3) should we return a parsed size?
51 * 4) deal with premature EOI
52 */
53
65c0f1e9 54static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
4a5fcab7 55
4a5fcab7
AL
56/**
57 * Error handler
58 */
8b7968f7 59static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
9bada897 60 JSONToken *token, const char *msg, ...)
4a5fcab7 61{
c96c84a9 62 va_list ap;
ef749d07 63 char message[1024];
574bf16f
MA
64
65 if (ctxt->err) {
66 return;
67 }
c96c84a9 68 va_start(ap, msg);
ef749d07 69 vsnprintf(message, sizeof(message), msg, ap);
c96c84a9 70 va_end(ap);
f231b88d 71 error_setg(&ctxt->err, "JSON parse error, %s", message);
4a5fcab7
AL
72}
73
dc45a07c 74static int cvt4hex(const char *s)
4a5fcab7 75{
dc45a07c
MA
76 int cp, i;
77
78 cp = 0;
79 for (i = 0; i < 4; i++) {
80 if (!qemu_isxdigit(s[i])) {
81 return -1;
82 }
83 cp <<= 4;
84 if (s[i] >= '0' && s[i] <= '9') {
85 cp |= s[i] - '0';
86 } else if (s[i] >= 'a' && s[i] <= 'f') {
87 cp |= 10 + s[i] - 'a';
88 } else if (s[i] >= 'A' && s[i] <= 'F') {
89 cp |= 10 + s[i] - 'A';
90 } else {
91 return -1;
92 }
4a5fcab7 93 }
dc45a07c 94 return cp;
4a5fcab7
AL
95}
96
97/**
b2da4a4d 98 * parse_string(): Parse a JSON string
4a5fcab7 99 *
b2da4a4d
MA
100 * From RFC 8259 "The JavaScript Object Notation (JSON) Data
101 * Interchange Format":
102 *
103 * char = unescaped /
104 * escape (
105 * %x22 / ; " quotation mark U+0022
106 * %x5C / ; \ reverse solidus U+005C
107 * %x2F / ; / solidus U+002F
108 * %x62 / ; b backspace U+0008
109 * %x66 / ; f form feed U+000C
110 * %x6E / ; n line feed U+000A
111 * %x72 / ; r carriage return U+000D
112 * %x74 / ; t tab U+0009
113 * %x75 4HEXDIG ) ; uXXXX U+XXXX
114 * escape = %x5C ; \
115 * quotation-mark = %x22 ; "
116 * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
117 *
118 * Extensions over RFC 8259:
119 * - Extra escape sequence in strings:
120 * 0x27 (apostrophe) is recognized after escape, too
121 * - Single-quoted strings:
122 * Like double-quoted strings, except they're delimited by %x27
123 * (apostrophe) instead of %x22 (quotation mark), and can't contain
124 * unescaped apostrophe, but can contain unescaped quotation mark.
125 *
126 * Note:
127 * - Encoding is modified UTF-8.
128 * - Invalid Unicode characters are rejected.
129 * - Control characters \x00..\x1F are rejected by the lexer.
4a5fcab7 130 */
b2da4a4d 131static QString *parse_string(JSONParserContext *ctxt, JSONToken *token)
4a5fcab7 132{
9bada897 133 const char *ptr = token->str;
4a5fcab7 134 QString *str;
00ea57fa 135 char quote;
dc45a07c
MA
136 const char *beg;
137 int cp, trailing;
e59f39d4
MA
138 char *end;
139 ssize_t len;
140 char utf8_buf[5];
4a5fcab7 141
00ea57fa
MA
142 assert(*ptr == '"' || *ptr == '\'');
143 quote = *ptr++;
4a5fcab7 144 str = qstring_new();
00ea57fa
MA
145
146 while (*ptr != quote) {
147 assert(*ptr);
4a5fcab7 148 if (*ptr == '\\') {
dc45a07c 149 beg = ptr++;
00ea57fa 150 switch (*ptr++) {
4a5fcab7 151 case '"':
de6decfe 152 qstring_append_chr(str, '"');
4a5fcab7
AL
153 break;
154 case '\'':
de6decfe 155 qstring_append_chr(str, '\'');
4a5fcab7
AL
156 break;
157 case '\\':
de6decfe 158 qstring_append_chr(str, '\\');
4a5fcab7
AL
159 break;
160 case '/':
de6decfe 161 qstring_append_chr(str, '/');
4a5fcab7
AL
162 break;
163 case 'b':
de6decfe 164 qstring_append_chr(str, '\b');
4a5fcab7 165 break;
bd032695 166 case 'f':
de6decfe 167 qstring_append_chr(str, '\f');
bd032695 168 break;
4a5fcab7 169 case 'n':
de6decfe 170 qstring_append_chr(str, '\n');
4a5fcab7
AL
171 break;
172 case 'r':
de6decfe 173 qstring_append_chr(str, '\r');
4a5fcab7
AL
174 break;
175 case 't':
de6decfe 176 qstring_append_chr(str, '\t');
4a5fcab7 177 break;
de6decfe 178 case 'u':
dc45a07c
MA
179 cp = cvt4hex(ptr);
180 ptr += 4;
181
182 /* handle surrogate pairs */
183 if (cp >= 0xD800 && cp <= 0xDBFF
184 && ptr[0] == '\\' && ptr[1] == 'u') {
185 /* leading surrogate followed by \u */
186 cp = 0x10000 + ((cp & 0x3FF) << 10);
187 trailing = cvt4hex(ptr + 2);
188 if (trailing >= 0xDC00 && trailing <= 0xDFFF) {
189 /* followed by trailing surrogate */
190 cp |= trailing & 0x3FF;
191 ptr += 6;
192 } else {
193 cp = -1; /* invalid */
4a5fcab7 194 }
4a5fcab7
AL
195 }
196
46a628b1
MA
197 if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) {
198 parse_error(ctxt, token,
dc45a07c
MA
199 "%.*s is not a valid Unicode character",
200 (int)(ptr - beg), beg);
46a628b1
MA
201 goto out;
202 }
de6decfe
MA
203 qstring_append(str, utf8_buf);
204 break;
4a5fcab7
AL
205 default:
206 parse_error(ctxt, token, "invalid escape sequence in string");
207 goto out;
208 }
209 } else {
e59f39d4 210 cp = mod_utf8_codepoint(ptr, 6, &end);
4b1c0cd7 211 if (cp < 0) {
e59f39d4
MA
212 parse_error(ctxt, token, "invalid UTF-8 sequence in string");
213 goto out;
214 }
215 ptr = end;
216 len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp);
217 assert(len >= 0);
218 qstring_append(str, utf8_buf);
4a5fcab7
AL
219 }
220 }
221
4a5fcab7
AL
222 return str;
223
224out:
cb3e7f08 225 qobject_unref(str);
4a5fcab7
AL
226 return NULL;
227}
228
9bada897
PB
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.
95385fe9 232 */
9bada897 233static JSONToken *parser_context_pop_token(JSONParserContext *ctxt)
65c0f1e9 234{
9bada897 235 g_free(ctxt->current);
95385fe9
PB
236 ctxt->current = g_queue_pop_head(ctxt->buf);
237 return ctxt->current;
65c0f1e9
MR
238}
239
9bada897 240static JSONToken *parser_context_peek_token(JSONParserContext *ctxt)
65c0f1e9 241{
95385fe9 242 return g_queue_peek_head(ctxt->buf);
65c0f1e9
MR
243}
244
4a5fcab7
AL
245/**
246 * Parsing rules
247 */
65c0f1e9 248static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
4a5fcab7 249{
532fb532
HR
250 QObject *value;
251 QString *key = NULL;
9bada897 252 JSONToken *peek, *token;
4a5fcab7 253
65c0f1e9 254 peek = parser_context_peek_token(ctxt);
11e8a46c
AL
255 if (peek == NULL) {
256 parse_error(ctxt, NULL, "premature EOI");
257 goto out;
258 }
259
532fb532
HR
260 key = qobject_to(QString, parse_value(ctxt, ap));
261 if (!key) {
4a5fcab7
AL
262 parse_error(ctxt, peek, "key is not a string in object");
263 goto out;
264 }
265
65c0f1e9 266 token = parser_context_pop_token(ctxt);
11e8a46c
AL
267 if (token == NULL) {
268 parse_error(ctxt, NULL, "premature EOI");
269 goto out;
270 }
271
9bada897 272 if (token->type != JSON_COLON) {
4a5fcab7
AL
273 parse_error(ctxt, token, "missing : in object pair");
274 goto out;
275 }
276
65c0f1e9 277 value = parse_value(ctxt, ap);
4a5fcab7
AL
278 if (value == NULL) {
279 parse_error(ctxt, token, "Missing value in dict");
280 goto out;
281 }
282
532fb532 283 qdict_put_obj(dict, qstring_get_str(key), value);
4a5fcab7 284
cb3e7f08 285 qobject_unref(key);
4a5fcab7
AL
286
287 return 0;
288
289out:
cb3e7f08 290 qobject_unref(key);
4a5fcab7
AL
291
292 return -1;
293}
294
65c0f1e9 295static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
4a5fcab7
AL
296{
297 QDict *dict = NULL;
9bada897 298 JSONToken *token, *peek;
4a5fcab7 299
65c0f1e9 300 token = parser_context_pop_token(ctxt);
9bada897 301 assert(token && token->type == JSON_LCURLY);
4a5fcab7
AL
302
303 dict = qdict_new();
304
65c0f1e9 305 peek = parser_context_peek_token(ctxt);
11e8a46c
AL
306 if (peek == NULL) {
307 parse_error(ctxt, NULL, "premature EOI");
308 goto out;
309 }
310
9bada897 311 if (peek->type != JSON_RCURLY) {
65c0f1e9 312 if (parse_pair(ctxt, dict, ap) == -1) {
4a5fcab7
AL
313 goto out;
314 }
315
65c0f1e9 316 token = parser_context_pop_token(ctxt);
11e8a46c
AL
317 if (token == NULL) {
318 parse_error(ctxt, NULL, "premature EOI");
319 goto out;
320 }
321
9bada897
PB
322 while (token->type != JSON_RCURLY) {
323 if (token->type != JSON_COMMA) {
4a5fcab7
AL
324 parse_error(ctxt, token, "expected separator in dict");
325 goto out;
326 }
4a5fcab7 327
65c0f1e9 328 if (parse_pair(ctxt, dict, ap) == -1) {
4a5fcab7
AL
329 goto out;
330 }
331
65c0f1e9 332 token = parser_context_pop_token(ctxt);
11e8a46c
AL
333 if (token == NULL) {
334 parse_error(ctxt, NULL, "premature EOI");
335 goto out;
336 }
4a5fcab7 337 }
4a5fcab7 338 } else {
a491af47 339 (void)parser_context_pop_token(ctxt);
4a5fcab7
AL
340 }
341
4a5fcab7
AL
342 return QOBJECT(dict);
343
344out:
cb3e7f08 345 qobject_unref(dict);
4a5fcab7
AL
346 return NULL;
347}
348
65c0f1e9 349static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
4a5fcab7
AL
350{
351 QList *list = NULL;
9bada897 352 JSONToken *token, *peek;
4a5fcab7 353
65c0f1e9 354 token = parser_context_pop_token(ctxt);
9bada897 355 assert(token && token->type == JSON_LSQUARE);
4a5fcab7
AL
356
357 list = qlist_new();
358
65c0f1e9 359 peek = parser_context_peek_token(ctxt);
11e8a46c
AL
360 if (peek == NULL) {
361 parse_error(ctxt, NULL, "premature EOI");
362 goto out;
363 }
364
9bada897 365 if (peek->type != JSON_RSQUARE) {
4a5fcab7
AL
366 QObject *obj;
367
65c0f1e9 368 obj = parse_value(ctxt, ap);
4a5fcab7
AL
369 if (obj == NULL) {
370 parse_error(ctxt, token, "expecting value");
371 goto out;
372 }
373
374 qlist_append_obj(list, obj);
375
65c0f1e9 376 token = parser_context_pop_token(ctxt);
11e8a46c
AL
377 if (token == NULL) {
378 parse_error(ctxt, NULL, "premature EOI");
379 goto out;
380 }
381
9bada897
PB
382 while (token->type != JSON_RSQUARE) {
383 if (token->type != JSON_COMMA) {
4a5fcab7
AL
384 parse_error(ctxt, token, "expected separator in list");
385 goto out;
386 }
387
65c0f1e9 388 obj = parse_value(ctxt, ap);
4a5fcab7
AL
389 if (obj == NULL) {
390 parse_error(ctxt, token, "expecting value");
391 goto out;
392 }
393
394 qlist_append_obj(list, obj);
395
65c0f1e9 396 token = parser_context_pop_token(ctxt);
11e8a46c
AL
397 if (token == NULL) {
398 parse_error(ctxt, NULL, "premature EOI");
399 goto out;
400 }
4a5fcab7 401 }
4a5fcab7 402 } else {
a491af47 403 (void)parser_context_pop_token(ctxt);
4a5fcab7
AL
404 }
405
4a5fcab7
AL
406 return QOBJECT(list);
407
408out:
cb3e7f08 409 qobject_unref(list);
4a5fcab7
AL
410 return NULL;
411}
412
65c0f1e9 413static QObject *parse_keyword(JSONParserContext *ctxt)
4a5fcab7 414{
9bada897 415 JSONToken *token;
4a5fcab7 416
65c0f1e9 417 token = parser_context_pop_token(ctxt);
9bada897 418 assert(token && token->type == JSON_KEYWORD);
50e2a467 419
9bada897 420 if (!strcmp(token->str, "true")) {
d538b255 421 return QOBJECT(qbool_from_bool(true));
9bada897 422 } else if (!strcmp(token->str, "false")) {
d538b255 423 return QOBJECT(qbool_from_bool(false));
9bada897 424 } else if (!strcmp(token->str, "null")) {
006ca09f 425 return QOBJECT(qnull());
4a5fcab7 426 }
9bada897 427 parse_error(ctxt, token, "invalid keyword '%s'", token->str);
4a5fcab7
AL
428 return NULL;
429}
430
61030280 431static QObject *parse_interpolation(JSONParserContext *ctxt, va_list *ap)
4a5fcab7 432{
9bada897 433 JSONToken *token;
4a5fcab7 434
65c0f1e9 435 token = parser_context_pop_token(ctxt);
61030280 436 assert(token && token->type == JSON_INTERP);
6b9606f6 437
9bada897 438 if (!strcmp(token->str, "%p")) {
d538b255 439 return va_arg(*ap, QObject *);
9bada897 440 } else if (!strcmp(token->str, "%i")) {
d538b255 441 return QOBJECT(qbool_from_bool(va_arg(*ap, int)));
9bada897 442 } else if (!strcmp(token->str, "%d")) {
01b2ffce 443 return QOBJECT(qnum_from_int(va_arg(*ap, int)));
9bada897 444 } else if (!strcmp(token->str, "%ld")) {
01b2ffce 445 return QOBJECT(qnum_from_int(va_arg(*ap, long)));
53a0d616 446 } else if (!strcmp(token->str, "%lld")) {
01b2ffce 447 return QOBJECT(qnum_from_int(va_arg(*ap, long long)));
53a0d616
MA
448 } else if (!strcmp(token->str, "%" PRId64)) {
449 return QOBJECT(qnum_from_int(va_arg(*ap, int64_t)));
2bc7cfea
MAL
450 } else if (!strcmp(token->str, "%u")) {
451 return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned int)));
452 } else if (!strcmp(token->str, "%lu")) {
453 return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long)));
53a0d616 454 } else if (!strcmp(token->str, "%llu")) {
2bc7cfea 455 return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long long)));
53a0d616
MA
456 } else if (!strcmp(token->str, "%" PRIu64)) {
457 return QOBJECT(qnum_from_uint(va_arg(*ap, uint64_t)));
9bada897 458 } else if (!strcmp(token->str, "%s")) {
d538b255 459 return QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
9bada897 460 } else if (!strcmp(token->str, "%f")) {
01b2ffce 461 return QOBJECT(qnum_from_double(va_arg(*ap, double)));
4a5fcab7 462 }
f7617d45 463 parse_error(ctxt, token, "invalid interpolation '%s'", token->str);
4a5fcab7
AL
464 return NULL;
465}
466
65c0f1e9 467static QObject *parse_literal(JSONParserContext *ctxt)
4a5fcab7 468{
9bada897 469 JSONToken *token;
4a5fcab7 470
65c0f1e9 471 token = parser_context_pop_token(ctxt);
d538b255 472 assert(token);
11e8a46c 473
9bada897 474 switch (token->type) {
4a5fcab7 475 case JSON_STRING:
b2da4a4d 476 return QOBJECT(parse_string(ctxt, token));
3d5b3ec6 477 case JSON_INTEGER: {
01b2ffce
MAL
478 /*
479 * Represent JSON_INTEGER as QNUM_I64 if possible, else as
2bc7cfea
MAL
480 * QNUM_U64, else as QNUM_DOUBLE. Note that qemu_strtoi64()
481 * and qemu_strtou64() fail with ERANGE when it's not
482 * possible.
3d5b3ec6 483 *
01b2ffce 484 * qnum_get_int() will then work for any signed 64-bit
2bc7cfea
MAL
485 * JSON_INTEGER, qnum_get_uint() for any unsigned 64-bit
486 * integer, and qnum_get_double() both for any JSON_INTEGER
487 * and any JSON_FLOAT (with precision loss for integers beyond
488 * 53 bits)
3d5b3ec6 489 */
2bc7cfea 490 int ret;
3d5b3ec6 491 int64_t value;
2bc7cfea 492 uint64_t uvalue;
3d5b3ec6 493
2bc7cfea
MAL
494 ret = qemu_strtoi64(token->str, NULL, 10, &value);
495 if (!ret) {
01b2ffce 496 return QOBJECT(qnum_from_int(value));
3d5b3ec6 497 }
2bc7cfea
MAL
498 assert(ret == -ERANGE);
499
500 if (token->str[0] != '-') {
501 ret = qemu_strtou64(token->str, NULL, 10, &uvalue);
502 if (!ret) {
503 return QOBJECT(qnum_from_uint(uvalue));
504 }
505 assert(ret == -ERANGE);
506 }
3d5b3ec6
MR
507 /* fall through to JSON_FLOAT */
508 }
4a5fcab7 509 case JSON_FLOAT:
6e8e5cb9
EB
510 /* FIXME dependent on locale; a pervasive issue in QEMU */
511 /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN,
512 * but those might be useful extensions beyond JSON */
01b2ffce 513 return QOBJECT(qnum_from_double(strtod(token->str, NULL)));
4a5fcab7 514 default:
d538b255 515 abort();
4a5fcab7 516 }
4a5fcab7
AL
517}
518
65c0f1e9 519static QObject *parse_value(JSONParserContext *ctxt, va_list *ap)
4a5fcab7 520{
9bada897 521 JSONToken *token;
4a5fcab7 522
d538b255
MA
523 token = parser_context_peek_token(ctxt);
524 if (token == NULL) {
525 parse_error(ctxt, NULL, "premature EOI");
526 return NULL;
4a5fcab7
AL
527 }
528
9bada897 529 switch (token->type) {
d538b255
MA
530 case JSON_LCURLY:
531 return parse_object(ctxt, ap);
532 case JSON_LSQUARE:
533 return parse_array(ctxt, ap);
61030280
MA
534 case JSON_INTERP:
535 return parse_interpolation(ctxt, ap);
d538b255
MA
536 case JSON_INTEGER:
537 case JSON_FLOAT:
538 case JSON_STRING:
539 return parse_literal(ctxt);
540 case JSON_KEYWORD:
541 return parse_keyword(ctxt);
542 default:
543 parse_error(ctxt, token, "expecting value");
544 return NULL;
545 }
4a5fcab7
AL
546}
547
abe7c206
MA
548JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr)
549{
550 JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1);
551
552 token->type = type;
553 memcpy(token->str, tokstr->str, tokstr->len);
554 token->str[tokstr->len] = 0;
555 token->x = x;
556 token->y = y;
557 return token;
558}
559
62815d85 560QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp)
4a5fcab7 561{
e8b19d7d 562 JSONParserContext ctxt = { .buf = tokens };
4a5fcab7
AL
563 QObject *result;
564
e8b19d7d 565 result = parse_value(&ctxt, ap);
5d50113c 566 assert(ctxt.err || g_queue_is_empty(ctxt.buf));
65c0f1e9 567
e8b19d7d 568 error_propagate(errp, ctxt.err);
4a5fcab7 569
e8b19d7d
MAL
570 while (!g_queue_is_empty(ctxt.buf)) {
571 parser_context_pop_token(&ctxt);
572 }
573 g_free(ctxt.current);
ef749d07 574
4a5fcab7
AL
575 return result;
576}