qemu/qobject/json-parser.c
<<
>>
Prefs
   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
  22typedef 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
  40static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
  41
  42/**
  43 * Error handler
  44 */
  45static 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 */
  65static 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
  88static 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 */
 124static 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
 218out:
 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 */
 227static 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
 235static 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
 241static 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 */
 256static 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 */
 271static 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
 311out:
 312    qobject_decref(key);
 313
 314    return -1;
 315}
 316
 317static 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
 366out:
 367    QDECREF(dict);
 368    return NULL;
 369}
 370
 371static 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
 430out:
 431    QDECREF(list);
 432    return NULL;
 433}
 434
 435static 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
 453static 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
 483static 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
 525static 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
 554QObject *json_parser_parse(GQueue *tokens, va_list *ap)
 555{
 556    return json_parser_parse_err(tokens, ap, NULL);
 557}
 558
 559QObject *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