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 "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
  23typedef 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
  41static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
  42
  43/**
  44 * Error handler
  45 */
  46static 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 */
  66static 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
  89static 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 */
 125static 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
 219out:
 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 */
 228static 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
 236static 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
 242static 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 */
 257static 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 */
 272static 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
 312out:
 313    qobject_decref(key);
 314
 315    return -1;
 316}
 317
 318static 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
 367out:
 368    QDECREF(dict);
 369    return NULL;
 370}
 371
 372static 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
 431out:
 432    QDECREF(list);
 433    return NULL;
 434}
 435
 436static 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 QOBJECT(qnull());
 449    }
 450    parse_error(ctxt, token, "invalid keyword '%s'", token->str);
 451    return NULL;
 452}
 453
 454static 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
 491static 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
 543static 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
 572QObject *json_parser_parse(GQueue *tokens, va_list *ap)
 573{
 574    return json_parser_parse_err(tokens, ap, NULL);
 575}
 576
 577QObject *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