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/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
  28typedef 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
  46static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
  47
  48/**
  49 * Error handler
  50 */
  51static 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 */
  71static 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
  94static 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 */
 130static 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
 224out:
 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 */
 233static 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
 241static 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
 247static 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 */
 262static 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 */
 277static 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
 318out:
 319    qobject_unref(key);
 320
 321    return -1;
 322}
 323
 324static 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
 373out:
 374    qobject_unref(dict);
 375    return NULL;
 376}
 377
 378static 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
 437out:
 438    qobject_unref(list);
 439    return NULL;
 440}
 441
 442static 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
 460static 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
 497static 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
 549static 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
 578QObject *json_parser_parse(GQueue *tokens, va_list *ap)
 579{
 580    return json_parser_parse_err(tokens, ap, NULL);
 581}
 582
 583QObject *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