qapi.py 59.7 KB
Newer Older
1 2 3 4
#
# QAPI helper library
#
# Copyright IBM, Corp. 2011
5
# Copyright (c) 2013-2016 Red Hat Inc.
6 7 8
#
# Authors:
#  Anthony Liguori <aliguori@us.ibm.com>
9
#  Markus Armbruster <armbru@redhat.com>
10
#
11 12
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
13

14
import re
15
from ordereddict import OrderedDict
16
import errno
17
import getopt
18
import os
19
import sys
20
import string
21

22
builtin_types = {
K
Kevin Wolf 已提交
23 24 25 26 27 28 29 30 31 32 33 34
    'str':      'QTYPE_QSTRING',
    'int':      'QTYPE_QINT',
    'number':   'QTYPE_QFLOAT',
    'bool':     'QTYPE_QBOOL',
    'int8':     'QTYPE_QINT',
    'int16':    'QTYPE_QINT',
    'int32':    'QTYPE_QINT',
    'int64':    'QTYPE_QINT',
    'uint8':    'QTYPE_QINT',
    'uint16':   'QTYPE_QINT',
    'uint32':   'QTYPE_QINT',
    'uint64':   'QTYPE_QINT',
35
    'size':     'QTYPE_QINT',
E
Eric Blake 已提交
36
    'any':      None,           # any QType possible, actually
37
    'QType':    'QTYPE_QSTRING',
K
Kevin Wolf 已提交
38 39
}

40 41 42 43
# Whitelist of commands allowed to return a non-dictionary
returns_whitelist = [
    # From QMP:
    'human-monitor-command',
44
    'qom-get',
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
    'query-migrate-cache-size',
    'query-tpm-models',
    'query-tpm-types',
    'ringbuf-read',

    # From QGA:
    'guest-file-open',
    'guest-fsfreeze-freeze',
    'guest-fsfreeze-freeze-list',
    'guest-fsfreeze-status',
    'guest-fsfreeze-thaw',
    'guest-get-time',
    'guest-set-vcpus',
    'guest-sync',
    'guest-sync-delimited',
]

62 63 64 65 66 67 68 69 70 71 72 73 74 75
# Whitelist of entities allowed to violate case conventions
case_whitelist = [
    # From QMP:
    'ACPISlotType',         # DIMM, visible through query-acpi-ospm-status
    'CpuInfoBase',          # CPU, visible through query-cpu
    'CpuInfoMIPS',          # PC, visible through query-cpu
    'CpuInfoTricore',       # PC, visible through query-cpu
    'InputAxis',            # TODO: drop when x-input-send-event is fixed
    'InputButton',          # TODO: drop when x-input-send-event is fixed
    'QapiErrorClass',       # all members, visible through errors
    'UuidInfo',             # UUID, visible through query-uuid
    'X86CPURegister32',     # all members, visible indirectly through qom-get
]

76 77 78 79 80 81
enum_types = []
struct_types = []
union_types = []
events = []
all_names = {}

82 83 84 85
#
# Parsing the schema into expressions
#

E
Eric Blake 已提交
86

87 88 89 90 91 92 93 94
def error_path(parent):
    res = ""
    while parent:
        res = ("In file included from %s:%d:\n" % (parent['file'],
                                                   parent['line'])) + res
        parent = parent['parent']
    return res

E
Eric Blake 已提交
95

96 97
class QAPISchemaError(Exception):
    def __init__(self, schema, msg):
98
        Exception.__init__(self)
99
        self.fname = schema.fname
100
        self.msg = msg
101 102 103 104
        self.col = 1
        self.line = schema.line
        for ch in schema.src[schema.line_pos:schema.pos]:
            if ch == '\t':
105 106 107
                self.col = (self.col + 7) % 8 + 1
            else:
                self.col += 1
108
        self.info = schema.incl_info
109 110

    def __str__(self):
111
        return error_path(self.info) + \
112
            "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
113

E
Eric Blake 已提交
114

115 116
class QAPIExprError(Exception):
    def __init__(self, expr_info, msg):
117
        Exception.__init__(self)
118
        assert expr_info
119
        self.info = expr_info
120 121 122
        self.msg = msg

    def __str__(self):
123 124
        return error_path(self.info['parent']) + \
            "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
125

E
Eric Blake 已提交
126

127
class QAPISchemaParser(object):
128

E
Eric Blake 已提交
129
    def __init__(self, fp, previously_included=[], incl_info=None):
130
        abs_fname = os.path.abspath(fp.name)
131
        fname = fp.name
132 133 134
        self.fname = fname
        previously_included.append(abs_fname)
        self.incl_info = incl_info
135 136 137 138
        self.src = fp.read()
        if self.src == '' or self.src[-1] != '\n':
            self.src += '\n'
        self.cursor = 0
139 140
        self.line = 1
        self.line_pos = 0
141 142 143
        self.exprs = []
        self.accept()

E
Eric Blake 已提交
144
        while self.tok is not None:
145 146
            expr_info = {'file': fname, 'line': self.line,
                         'parent': self.incl_info}
147 148 149
            expr = self.get_expr(False)
            if isinstance(expr, dict) and "include" in expr:
                if len(expr) != 1:
E
Eric Blake 已提交
150 151
                    raise QAPIExprError(expr_info,
                                        "Invalid 'include' directive")
152 153 154
                include = expr["include"]
                if not isinstance(include, str):
                    raise QAPIExprError(expr_info,
E
Eric Blake 已提交
155
                                        "Value of 'include' must be a string")
156 157
                incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
                                              include)
158 159 160 161
                # catch inclusion cycle
                inf = expr_info
                while inf:
                    if incl_abs_fname == os.path.abspath(inf['file']):
162 163
                        raise QAPIExprError(expr_info, "Inclusion loop for %s"
                                            % include)
164
                    inf = inf['parent']
B
Benoît Canet 已提交
165
                # skip multiple include of the same file
166
                if incl_abs_fname in previously_included:
B
Benoît Canet 已提交
167
                    continue
168
                try:
169
                    fobj = open(incl_abs_fname, 'r')
170
                except IOError as e:
171 172
                    raise QAPIExprError(expr_info,
                                        '%s: %s' % (e.strerror, include))
173 174
                exprs_include = QAPISchemaParser(fobj, previously_included,
                                                 expr_info)
175 176 177 178 179
                self.exprs.extend(exprs_include.exprs)
            else:
                expr_elem = {'expr': expr,
                             'info': expr_info}
                self.exprs.append(expr_elem)
180 181 182 183

    def accept(self):
        while True:
            self.tok = self.src[self.cursor]
184
            self.pos = self.cursor
185 186 187
            self.cursor += 1
            self.val = None

188
            if self.tok == '#':
189
                self.cursor = self.src.find('\n', self.cursor)
190
            elif self.tok in "{}:,[]":
191 192 193 194 195 196 197 198
                return
            elif self.tok == "'":
                string = ''
                esc = False
                while True:
                    ch = self.src[self.cursor]
                    self.cursor += 1
                    if ch == '\n':
199 200
                        raise QAPISchemaError(self,
                                              'Missing terminating "\'"')
201
                    if esc:
202 203 204 205 206 207 208 209 210 211 212 213
                        if ch == 'b':
                            string += '\b'
                        elif ch == 'f':
                            string += '\f'
                        elif ch == 'n':
                            string += '\n'
                        elif ch == 'r':
                            string += '\r'
                        elif ch == 't':
                            string += '\t'
                        elif ch == 'u':
                            value = 0
E
Eric Blake 已提交
214
                            for _ in range(0, 4):
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
                                ch = self.src[self.cursor]
                                self.cursor += 1
                                if ch not in "0123456789abcdefABCDEF":
                                    raise QAPISchemaError(self,
                                                          '\\u escape needs 4 '
                                                          'hex digits')
                                value = (value << 4) + int(ch, 16)
                            # If Python 2 and 3 didn't disagree so much on
                            # how to handle Unicode, then we could allow
                            # Unicode string defaults.  But most of QAPI is
                            # ASCII-only, so we aren't losing much for now.
                            if not value or value > 0x7f:
                                raise QAPISchemaError(self,
                                                      'For now, \\u escape '
                                                      'only supports non-zero '
                                                      'values up to \\u007f')
                            string += chr(value)
                        elif ch in "\\/'\"":
                            string += ch
                        else:
                            raise QAPISchemaError(self,
E
Eric Blake 已提交
236
                                                  "Unknown escape \\%s" % ch)
237 238 239 240 241 242 243 244
                        esc = False
                    elif ch == "\\":
                        esc = True
                    elif ch == "'":
                        self.val = string
                        return
                    else:
                        string += ch
245 246 247 248 249 250 251 252 253 254 255 256
            elif self.src.startswith("true", self.pos):
                self.val = True
                self.cursor += 3
                return
            elif self.src.startswith("false", self.pos):
                self.val = False
                self.cursor += 4
                return
            elif self.src.startswith("null", self.pos):
                self.val = None
                self.cursor += 3
                return
257 258 259 260
            elif self.tok == '\n':
                if self.cursor == len(self.src):
                    self.tok = None
                    return
261 262
                self.line += 1
                self.line_pos = self.cursor
263 264
            elif not self.tok.isspace():
                raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
265 266 267

    def get_members(self):
        expr = OrderedDict()
268 269 270 271 272 273
        if self.tok == '}':
            self.accept()
            return expr
        if self.tok != "'":
            raise QAPISchemaError(self, 'Expected string or "}"')
        while True:
274 275
            key = self.val
            self.accept()
276 277 278
            if self.tok != ':':
                raise QAPISchemaError(self, 'Expected ":"')
            self.accept()
279 280
            if key in expr:
                raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
281
            expr[key] = self.get_expr(True)
282
            if self.tok == '}':
283
                self.accept()
284 285 286 287 288 289
                return expr
            if self.tok != ',':
                raise QAPISchemaError(self, 'Expected "," or "}"')
            self.accept()
            if self.tok != "'":
                raise QAPISchemaError(self, 'Expected string')
290 291 292

    def get_values(self):
        expr = []
293 294 295
        if self.tok == ']':
            self.accept()
            return expr
E
Eric Blake 已提交
296
        if self.tok not in "{['tfn":
297 298
            raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
                                  'boolean or "null"')
299
        while True:
300
            expr.append(self.get_expr(True))
301
            if self.tok == ']':
302
                self.accept()
303 304 305 306
                return expr
            if self.tok != ',':
                raise QAPISchemaError(self, 'Expected "," or "]"')
            self.accept()
307

308 309 310
    def get_expr(self, nested):
        if self.tok != '{' and not nested:
            raise QAPISchemaError(self, 'Expected "{"')
311 312 313 314 315 316
        if self.tok == '{':
            self.accept()
            expr = self.get_members()
        elif self.tok == '[':
            self.accept()
            expr = self.get_values()
317
        elif self.tok in "'tfn":
318 319
            expr = self.val
            self.accept()
320 321
        else:
            raise QAPISchemaError(self, 'Expected "{", "[" or string')
322
        return expr
K
Kevin Wolf 已提交
323

324 325
#
# Semantic analysis of schema expressions
326 327
# TODO fold into QAPISchema
# TODO catching name collisions in generated code would be nice
328 329
#

E
Eric Blake 已提交
330

331 332 333 334 335 336
def find_base_fields(base):
    base_struct_define = find_struct(base)
    if not base_struct_define:
        return None
    return base_struct_define['data']

E
Eric Blake 已提交
337

338 339
# Return the qtype of an alternate branch, or None on error.
def find_alternate_member_qtype(qapi_type):
E
Eric Blake 已提交
340
    if qapi_type in builtin_types:
E
Eric Blake 已提交
341 342 343 344 345
        return builtin_types[qapi_type]
    elif find_struct(qapi_type):
        return "QTYPE_QDICT"
    elif find_enum(qapi_type):
        return "QTYPE_QSTRING"
346 347
    elif find_union(qapi_type):
        return "QTYPE_QDICT"
E
Eric Blake 已提交
348 349
    return None

E
Eric Blake 已提交
350

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
# Return the discriminator enum define if discriminator is specified as an
# enum type, otherwise return None.
def discriminator_find_enum_define(expr):
    base = expr.get('base')
    discriminator = expr.get('discriminator')

    if not (discriminator and base):
        return None

    base_fields = find_base_fields(base)
    if not base_fields:
        return None

    discriminator_type = base_fields.get(discriminator)
    if not discriminator_type:
        return None

    return find_enum(discriminator_type)

E
Eric Blake 已提交
370

371 372 373 374 375
# Names must be letters, numbers, -, and _.  They must start with letter,
# except for downstream extensions which must start with __RFQDN_.
# Dots are only valid in the downstream extension prefix.
valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
                        '[a-zA-Z][a-zA-Z0-9_-]*$')
E
Eric Blake 已提交
376 377 378 379


def check_name(expr_info, source, name, allow_optional=False,
               enum_member=False):
E
Eric Blake 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393
    global valid_name
    membername = name

    if not isinstance(name, str):
        raise QAPIExprError(expr_info,
                            "%s requires a string name" % source)
    if name.startswith('*'):
        membername = name[1:]
        if not allow_optional:
            raise QAPIExprError(expr_info,
                                "%s does not allow optional name '%s'"
                                % (source, name))
    # Enum members can start with a digit, because the generated C
    # code always prefixes it with the enum name
394 395
    if enum_member and membername[0].isdigit():
        membername = 'D' + membername
396 397 398
    # Reserve the entire 'q_' namespace for c_name()
    if not valid_name.match(membername) or \
       c_name(membername, False).startswith('q_'):
E
Eric Blake 已提交
399 400 401
        raise QAPIExprError(expr_info,
                            "%s uses invalid name '%s'" % (source, name))

E
Eric Blake 已提交
402 403

def add_name(name, info, meta, implicit=False):
404 405
    global all_names
    check_name(info, "'%s'" % meta, name)
406 407
    # FIXME should reject names that differ only in '_' vs. '.'
    # vs. '-', because they're liable to clash in generated C.
408 409 410 411
    if name in all_names:
        raise QAPIExprError(info,
                            "%s '%s' is already defined"
                            % (all_names[name], name))
412
    if not implicit and (name.endswith('Kind') or name.endswith('List')):
413
        raise QAPIExprError(info,
414 415
                            "%s '%s' should not end in '%s'"
                            % (meta, name, name[-4:]))
416 417
    all_names[name] = meta

E
Eric Blake 已提交
418

419 420 421 422 423 424
def add_struct(definition, info):
    global struct_types
    name = definition['struct']
    add_name(name, info, 'struct')
    struct_types.append(definition)

E
Eric Blake 已提交
425

426 427 428 429 430 431 432
def find_struct(name):
    global struct_types
    for struct in struct_types:
        if struct['struct'] == name:
            return struct
    return None

E
Eric Blake 已提交
433

434 435 436 437 438 439
def add_union(definition, info):
    global union_types
    name = definition['union']
    add_name(name, info, 'union')
    union_types.append(definition)

E
Eric Blake 已提交
440

441 442 443 444 445 446 447
def find_union(name):
    global union_types
    for union in union_types:
        if union['union'] == name:
            return union
    return None

E
Eric Blake 已提交
448 449

def add_enum(name, info, enum_values=None, implicit=False):
450 451 452 453
    global enum_types
    add_name(name, info, 'enum', implicit)
    enum_types.append({"enum_name": name, "enum_values": enum_values})

E
Eric Blake 已提交
454

455 456 457 458 459 460 461
def find_enum(name):
    global enum_types
    for enum in enum_types:
        if enum['enum_name'] == name:
            return enum
    return None

E
Eric Blake 已提交
462

463
def is_enum(name):
E
Eric Blake 已提交
464 465
    return find_enum(name) is not None

466

E
Eric Blake 已提交
467 468 469
def check_type(expr_info, source, value, allow_array=False,
               allow_dict=False, allow_optional=False,
               allow_metas=[]):
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
    global all_names

    if value is None:
        return

    # Check if array type for value is okay
    if isinstance(value, list):
        if not allow_array:
            raise QAPIExprError(expr_info,
                                "%s cannot be an array" % source)
        if len(value) != 1 or not isinstance(value[0], str):
            raise QAPIExprError(expr_info,
                                "%s: array type must contain single type name"
                                % source)
        value = value[0]

    # Check if type name for value is okay
    if isinstance(value, str):
E
Eric Blake 已提交
488
        if value not in all_names:
489 490
            raise QAPIExprError(expr_info,
                                "%s uses unknown type '%s'"
491
                                % (source, value))
492 493 494
        if not all_names[value] in allow_metas:
            raise QAPIExprError(expr_info,
                                "%s cannot use %s type '%s'"
495
                                % (source, all_names[value], value))
496 497 498 499 500
        return

    if not allow_dict:
        raise QAPIExprError(expr_info,
                            "%s should be a type name" % source)
501 502 503 504 505 506

    if not isinstance(value, OrderedDict):
        raise QAPIExprError(expr_info,
                            "%s should be a dictionary or type name" % source)

    # value is a dictionary, check that each member is okay
507
    for (key, arg) in value.items():
E
Eric Blake 已提交
508 509
        check_name(expr_info, "Member of %s" % source, key,
                   allow_optional=allow_optional)
E
Eric Blake 已提交
510
        if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
511 512 513
            raise QAPIExprError(expr_info,
                                "Member of %s uses reserved name '%s'"
                                % (source, key))
514 515
        # Todo: allow dictionaries to represent default values of
        # an optional argument.
516
        check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
517
                   allow_array=True,
518
                   allow_metas=['built-in', 'union', 'alternate', 'struct',
519
                                'enum'])
520

E
Eric Blake 已提交
521

522 523
def check_command(expr, expr_info):
    name = expr['command']
524

525
    check_type(expr_info, "'data' for command '%s'" % name,
E
Eric Blake 已提交
526
               expr.get('data'), allow_dict=True, allow_optional=True,
527
               allow_metas=['struct'])
528 529 530
    returns_meta = ['union', 'struct']
    if name in returns_whitelist:
        returns_meta += ['built-in', 'alternate', 'enum']
531
    check_type(expr_info, "'returns' for command '%s'" % name,
532
               expr.get('returns'), allow_array=True,
533
               allow_optional=True, allow_metas=returns_meta)
534

E
Eric Blake 已提交
535

W
Wenchao Xia 已提交
536
def check_event(expr, expr_info):
537 538 539 540
    global events
    name = expr['event']

    events.append(name)
541
    check_type(expr_info, "'data' for event '%s'" % name,
E
Eric Blake 已提交
542
               expr.get('data'), allow_dict=True, allow_optional=True,
543
               allow_metas=['struct'])
W
Wenchao Xia 已提交
544

E
Eric Blake 已提交
545

546 547 548 549 550 551
def check_union(expr, expr_info):
    name = expr['union']
    base = expr.get('base')
    discriminator = expr.get('discriminator')
    members = expr['data']

552 553 554 555
    # Two types of unions, determined by discriminator.

    # With no discriminator it is a simple union.
    if discriminator is None:
556
        enum_define = None
E
Eric Blake 已提交
557
        allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
E
Eric Blake 已提交
558 559
        if base is not None:
            raise QAPIExprError(expr_info,
560
                                "Simple union '%s' must not have a base"
E
Eric Blake 已提交
561
                                % name)
562 563 564

    # Else, it's a flat union.
    else:
E
Eric Blake 已提交
565
        # The object must have a string member 'base'.
566 567 568
        check_type(expr_info, "'base' for union '%s'" % name,
                   base, allow_metas=['struct'])
        if not base:
569
            raise QAPIExprError(expr_info,
570
                                "Flat union '%s' must have a base"
571
                                % name)
E
Eric Blake 已提交
572
        base_fields = find_base_fields(base)
573
        assert base_fields
E
Eric Blake 已提交
574

E
Eric Blake 已提交
575
        # The value of member 'discriminator' must name a non-optional
576
        # member of the base struct.
E
Eric Blake 已提交
577 578
        check_name(expr_info, "Discriminator of flat union '%s'" % name,
                   discriminator)
579 580 581 582
        discriminator_type = base_fields.get(discriminator)
        if not discriminator_type:
            raise QAPIExprError(expr_info,
                                "Discriminator '%s' is not a member of base "
583
                                "struct '%s'"
584 585
                                % (discriminator, base))
        enum_define = find_enum(discriminator_type)
E
Eric Blake 已提交
586
        allow_metas = ['struct']
587 588 589 590 591
        # Do not allow string discriminator
        if not enum_define:
            raise QAPIExprError(expr_info,
                                "Discriminator '%s' must be of enumeration "
                                "type" % discriminator)
592

593 594 595 596
    # Check every branch; don't allow an empty union
    if len(members) == 0:
        raise QAPIExprError(expr_info,
                            "Union '%s' cannot have empty 'data'" % name)
597
    for (key, value) in members.items():
E
Eric Blake 已提交
598 599
        check_name(expr_info, "Member of union '%s'" % name, key)

600
        # Each value must name a known type
601
        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
602
                   value, allow_array=not base, allow_metas=allow_metas)
603

E
Eric Blake 已提交
604
        # If the discriminator names an enum type, then all members
605
        # of 'data' must also be members of the enum type.
E
Eric Blake 已提交
606
        if enum_define:
E
Eric Blake 已提交
607
            if key not in enum_define['enum_values']:
E
Eric Blake 已提交
608 609 610 611 612
                raise QAPIExprError(expr_info,
                                    "Discriminator value '%s' is not found in "
                                    "enum '%s'" %
                                    (key, enum_define["enum_name"]))

E
Eric Blake 已提交
613

614
def check_alternate(expr, expr_info):
615
    name = expr['alternate']
616 617 618
    members = expr['data']
    types_seen = {}

619 620 621 622 623
    # Check every branch; require at least two branches
    if len(members) < 2:
        raise QAPIExprError(expr_info,
                            "Alternate '%s' should have at least two branches "
                            "in 'data'" % name)
624
    for (key, value) in members.items():
E
Eric Blake 已提交
625 626
        check_name(expr_info, "Member of alternate '%s'" % name, key)

627
        # Ensure alternates have no type conflicts.
628 629 630
        check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
                   value,
                   allow_metas=['built-in', 'union', 'struct', 'enum'])
631
        qtype = find_alternate_member_qtype(value)
632
        assert qtype
633 634
        if qtype in types_seen:
            raise QAPIExprError(expr_info,
635
                                "Alternate '%s' member '%s' can't "
636 637 638
                                "be distinguished from member '%s'"
                                % (name, key, types_seen[qtype]))
        types_seen[qtype] = key
639

E
Eric Blake 已提交
640

641 642 643
def check_enum(expr, expr_info):
    name = expr['enum']
    members = expr.get('data')
644
    prefix = expr.get('prefix')
645 646 647 648

    if not isinstance(members, list):
        raise QAPIExprError(expr_info,
                            "Enum '%s' requires an array for 'data'" % name)
649 650 651
    if prefix is not None and not isinstance(prefix, str):
        raise QAPIExprError(expr_info,
                            "Enum '%s' requires a string for 'prefix'" % name)
652
    for member in members:
E
Eric Blake 已提交
653
        check_name(expr_info, "Member of enum '%s'" % name, member,
E
Eric Blake 已提交
654
                   enum_member=True)
655

E
Eric Blake 已提交
656

657
def check_struct(expr, expr_info):
658
    name = expr['struct']
659 660
    members = expr['data']

661
    check_type(expr_info, "'data' for struct '%s'" % name, members,
E
Eric Blake 已提交
662
               allow_dict=True, allow_optional=True)
663
    check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
664 665
               allow_metas=['struct'])

E
Eric Blake 已提交
666

667 668 669 670 671 672 673
def check_keys(expr_elem, meta, required, optional=[]):
    expr = expr_elem['expr']
    info = expr_elem['info']
    name = expr[meta]
    if not isinstance(name, str):
        raise QAPIExprError(info,
                            "'%s' key must have a string value" % meta)
E
Eric Blake 已提交
674
    required = required + [meta]
675
    for (key, value) in expr.items():
E
Eric Blake 已提交
676
        if key not in required and key not in optional:
677 678 679
            raise QAPIExprError(info,
                                "Unknown key '%s' in %s '%s'"
                                % (key, meta, name))
E
Eric Blake 已提交
680
        if (key == 'gen' or key == 'success-response') and value is not False:
681 682 683
            raise QAPIExprError(info,
                                "'%s' of %s '%s' should only use false value"
                                % (key, meta, name))
684
    for key in required:
E
Eric Blake 已提交
685
        if key not in expr:
686 687 688 689
            raise QAPIExprError(info,
                                "Key '%s' is missing from %s '%s'"
                                % (key, meta, name))

E
Eric Blake 已提交
690

691
def check_exprs(exprs):
692 693
    global all_names

694 695 696 697 698 699
    # Learn the types and check for valid expression keys
    for builtin in builtin_types.keys():
        all_names[builtin] = 'built-in'
    for expr_elem in exprs:
        expr = expr_elem['expr']
        info = expr_elem['info']
E
Eric Blake 已提交
700
        if 'enum' in expr:
701
            check_keys(expr_elem, 'enum', ['data'], ['prefix'])
702
            add_enum(expr['enum'], info, expr['data'])
E
Eric Blake 已提交
703
        elif 'union' in expr:
704 705 706
            check_keys(expr_elem, 'union', ['data'],
                       ['base', 'discriminator'])
            add_union(expr, info)
E
Eric Blake 已提交
707
        elif 'alternate' in expr:
708 709
            check_keys(expr_elem, 'alternate', ['data'])
            add_name(expr['alternate'], info, 'alternate')
E
Eric Blake 已提交
710
        elif 'struct' in expr:
711 712
            check_keys(expr_elem, 'struct', ['data'], ['base'])
            add_struct(expr, info)
E
Eric Blake 已提交
713
        elif 'command' in expr:
714 715 716
            check_keys(expr_elem, 'command', [],
                       ['data', 'returns', 'gen', 'success-response'])
            add_name(expr['command'], info, 'command')
E
Eric Blake 已提交
717
        elif 'event' in expr:
718 719 720 721 722
            check_keys(expr_elem, 'event', [], ['data'])
            add_name(expr['event'], info, 'event')
        else:
            raise QAPIExprError(expr_elem['info'],
                                "Expression is missing metatype")
723

724 725 726
    # Try again for hidden UnionKind enum
    for expr_elem in exprs:
        expr = expr_elem['expr']
E
Eric Blake 已提交
727
        if 'union' in expr:
728 729
            if not discriminator_find_enum_define(expr):
                add_enum('%sKind' % expr['union'], expr_elem['info'],
730
                         implicit=True)
E
Eric Blake 已提交
731
        elif 'alternate' in expr:
732 733 734 735 736 737 738
            add_enum('%sKind' % expr['alternate'], expr_elem['info'],
                     implicit=True)

    # Validate that exprs make sense
    for expr_elem in exprs:
        expr = expr_elem['expr']
        info = expr_elem['info']
739

E
Eric Blake 已提交
740
        if 'enum' in expr:
741
            check_enum(expr, info)
E
Eric Blake 已提交
742
        elif 'union' in expr:
743
            check_union(expr, info)
E
Eric Blake 已提交
744
        elif 'alternate' in expr:
745
            check_alternate(expr, info)
E
Eric Blake 已提交
746
        elif 'struct' in expr:
747
            check_struct(expr, info)
E
Eric Blake 已提交
748
        elif 'command' in expr:
749
            check_command(expr, info)
E
Eric Blake 已提交
750
        elif 'event' in expr:
751 752 753 754
            check_event(expr, info)
        else:
            assert False, 'unexpected meta type'

755 756 757 758 759 760 761 762 763 764 765
    return exprs


#
# Schema compiler frontend
#

class QAPISchemaEntity(object):
    def __init__(self, name, info):
        assert isinstance(name, str)
        self.name = name
766 767 768 769 770
        # For explicitly defined entities, info points to the (explicit)
        # definition.  For builtins (and their arrays), info is None.
        # For implicitly defined entities, info points to a place that
        # triggered the implicit definition (there may be more than one
        # such place).
771 772
        self.info = info

773 774 775
    def c_name(self):
        return c_name(self.name)

776 777 778
    def check(self, schema):
        pass

779 780 781
    def is_implicit(self):
        return not self.info

M
Markus Armbruster 已提交
782 783 784 785 786 787 788 789 790 791 792
    def visit(self, visitor):
        pass


class QAPISchemaVisitor(object):
    def visit_begin(self, schema):
        pass

    def visit_end(self):
        pass

793 794 795 796
    def visit_needed(self, entity):
        # Default to visiting everything
        return True

M
Markus Armbruster 已提交
797 798 799 800 801 802 803 804 805 806 807 808
    def visit_builtin_type(self, name, info, json_type):
        pass

    def visit_enum_type(self, name, info, values, prefix):
        pass

    def visit_array_type(self, name, info, element_type):
        pass

    def visit_object_type(self, name, info, base, members, variants):
        pass

809 810 811
    def visit_object_type_flat(self, name, info, members, variants):
        pass

M
Markus Armbruster 已提交
812 813 814 815 816 817 818 819 820 821
    def visit_alternate_type(self, name, info, variants):
        pass

    def visit_command(self, name, info, arg_type, ret_type,
                      gen, success_response):
        pass

    def visit_event(self, name, info, arg_type):
        pass

822 823

class QAPISchemaType(QAPISchemaEntity):
824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
    def c_type(self, is_param=False):
        return c_name(self.name) + pointer_suffix

    def c_null(self):
        return 'NULL'

    def json_type(self):
        pass

    def alternate_qtype(self):
        json2qtype = {
            'string':  'QTYPE_QSTRING',
            'number':  'QTYPE_QFLOAT',
            'int':     'QTYPE_QINT',
            'boolean': 'QTYPE_QBOOL',
            'object':  'QTYPE_QDICT'
        }
        return json2qtype.get(self.json_type())
842 843 844


class QAPISchemaBuiltinType(QAPISchemaType):
845
    def __init__(self, name, json_type, c_type, c_null):
846
        QAPISchemaType.__init__(self, name, None)
847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        assert not c_type or isinstance(c_type, str)
        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
                             'value')
        self._json_type_name = json_type
        self._c_type_name = c_type
        self._c_null_val = c_null

    def c_name(self):
        return self.name

    def c_type(self, is_param=False):
        if is_param and self.name == 'str':
            return 'const ' + self._c_type_name
        return self._c_type_name

    def c_null(self):
        return self._c_null_val

    def json_type(self):
        return self._json_type_name
867

M
Markus Armbruster 已提交
868 869 870
    def visit(self, visitor):
        visitor.visit_builtin_type(self.name, self.info, self.json_type())

871 872 873 874 875

class QAPISchemaEnumType(QAPISchemaType):
    def __init__(self, name, info, values, prefix):
        QAPISchemaType.__init__(self, name, info)
        for v in values:
876 877
            assert isinstance(v, QAPISchemaMember)
            v.set_owner(name)
878 879 880 881 882
        assert prefix is None or isinstance(prefix, str)
        self.values = values
        self.prefix = prefix

    def check(self, schema):
883 884 885
        seen = {}
        for v in self.values:
            v.check_clash(self.info, seen)
886

887 888
    def is_implicit(self):
        # See QAPISchema._make_implicit_enum_type()
889
        return self.name.endswith('Kind')
890

891 892 893
    def c_type(self, is_param=False):
        return c_name(self.name)

894 895 896
    def member_names(self):
        return [v.name for v in self.values]

897
    def c_null(self):
898
        return c_enum_const(self.name, (self.member_names() + ['_MAX'])[0],
899 900 901 902 903
                            self.prefix)

    def json_type(self):
        return 'string'

M
Markus Armbruster 已提交
904 905
    def visit(self, visitor):
        visitor.visit_enum_type(self.name, self.info,
906
                                self.member_names(), self.prefix)
M
Markus Armbruster 已提交
907

908 909 910 911 912 913 914 915 916 917 918 919

class QAPISchemaArrayType(QAPISchemaType):
    def __init__(self, name, info, element_type):
        QAPISchemaType.__init__(self, name, info)
        assert isinstance(element_type, str)
        self._element_type_name = element_type
        self.element_type = None

    def check(self, schema):
        self.element_type = schema.lookup_type(self._element_type_name)
        assert self.element_type

920 921 922
    def is_implicit(self):
        return True

923 924 925
    def json_type(self):
        return 'array'

M
Markus Armbruster 已提交
926 927 928
    def visit(self, visitor):
        visitor.visit_array_type(self.name, self.info, self.element_type)

929 930 931

class QAPISchemaObjectType(QAPISchemaType):
    def __init__(self, name, info, base, local_members, variants):
932 933 934
        # struct has local_members, optional base, and no variants
        # flat union has base, variants, and no local_members
        # simple union has local_members, variants, and no base
935 936 937 938
        QAPISchemaType.__init__(self, name, info)
        assert base is None or isinstance(base, str)
        for m in local_members:
            assert isinstance(m, QAPISchemaObjectTypeMember)
939 940 941 942
            m.set_owner(name)
        if variants is not None:
            assert isinstance(variants, QAPISchemaObjectTypeVariants)
            variants.set_owner(name)
943 944 945 946 947 948 949
        self._base_name = base
        self.base = None
        self.local_members = local_members
        self.variants = variants
        self.members = None

    def check(self, schema):
E
Eric Blake 已提交
950 951 952
        if self.members is False:               # check for cycles
            raise QAPIExprError(self.info,
                                "Object %s contains itself" % self.name)
953 954 955
        if self.members:
            return
        self.members = False                    # mark as being checked
956
        seen = OrderedDict()
957 958 959 960
        if self._base_name:
            self.base = schema.lookup_type(self._base_name)
            assert isinstance(self.base, QAPISchemaObjectType)
            self.base.check(schema)
961
            self.base.check_clash(schema, self.info, seen)
962
        for m in self.local_members:
963
            m.check(schema)
964
            m.check_clash(self.info, seen)
965
        self.members = seen.values()
966
        if self.variants:
967
            self.variants.check(schema, seen)
968
            assert self.variants.tag_member in self.members
969
            self.variants.check_clash(schema, self.info, seen)
970

971 972 973 974
    # Check that the members of this type do not cause duplicate JSON fields,
    # and update seen to track the members seen so far. Report any errors
    # on behalf of info, which is not necessarily self.info
    def check_clash(self, schema, info, seen):
975 976
        assert not self.variants       # not implemented
        for m in self.members:
977
            m.check_clash(info, seen)
978

979 980 981 982
    def is_implicit(self):
        # See QAPISchema._make_implicit_object_type()
        return self.name[0] == ':'

983
    def c_name(self):
984
        assert not self.is_implicit()
985 986 987
        return QAPISchemaType.c_name(self)

    def c_type(self, is_param=False):
988
        assert not self.is_implicit()
989 990 991 992 993
        return QAPISchemaType.c_type(self)

    def json_type(self):
        return 'object'

M
Markus Armbruster 已提交
994 995 996
    def visit(self, visitor):
        visitor.visit_object_type(self.name, self.info,
                                  self.base, self.local_members, self.variants)
997 998
        visitor.visit_object_type_flat(self.name, self.info,
                                       self.members, self.variants)
M
Markus Armbruster 已提交
999

1000

1001
class QAPISchemaMember(object):
1002 1003
    role = 'member'

1004
    def __init__(self, name):
1005 1006
        assert isinstance(name, str)
        self.name = name
1007 1008 1009 1010 1011
        self.owner = None

    def set_owner(self, name):
        assert not self.owner
        self.owner = name
1012

1013 1014
    def check_clash(self, info, seen):
        cname = c_name(self.name)
1015 1016 1017
        if cname.lower() != cname and self.owner not in case_whitelist:
            raise QAPIExprError(info,
                                "%s should not use uppercase" % self.describe())
1018 1019 1020 1021 1022
        if cname in seen:
            raise QAPIExprError(info,
                                "%s collides with %s"
                                % (self.describe(), seen[cname].describe()))
        seen[cname] = self
1023

1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035
    def _pretty_owner(self):
        owner = self.owner
        if owner.startswith(':obj-'):
            # See QAPISchema._make_implicit_object_type() - reverse the
            # mapping there to create a nice human-readable description
            owner = owner[5:]
            if owner.endswith('-arg'):
                return '(parameter of %s)' % owner[:-4]
            else:
                assert owner.endswith('-wrapper')
                # Unreachable and not implemented
                assert False
1036 1037 1038
        if owner.endswith('Kind'):
            # See QAPISchema._make_implicit_enum_type()
            return '(branch of %s)' % owner[:-4]
1039 1040 1041 1042 1043
        return '(%s of %s)' % (self.role, owner)

    def describe(self):
        return "'%s' %s" % (self.name, self._pretty_owner())

1044

1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
class QAPISchemaObjectTypeMember(QAPISchemaMember):
    def __init__(self, name, typ, optional):
        QAPISchemaMember.__init__(self, name)
        assert isinstance(typ, str)
        assert isinstance(optional, bool)
        self._type_name = typ
        self.type = None
        self.optional = optional

    def check(self, schema):
        assert self.owner
        self.type = schema.lookup_type(self._type_name)
        assert self.type


1060
class QAPISchemaObjectTypeVariants(object):
1061 1062 1063 1064 1065 1066 1067 1068
    def __init__(self, tag_name, tag_member, variants):
        # Flat unions pass tag_name but not tag_member.
        # Simple unions and alternates pass tag_member but not tag_name.
        # After check(), tag_member is always set, and tag_name remains
        # a reliable witness of being used by a flat union.
        assert bool(tag_member) != bool(tag_name)
        assert (isinstance(tag_name, str) or
                isinstance(tag_member, QAPISchemaObjectTypeMember))
1069
        assert len(variants) > 0
1070 1071 1072
        for v in variants:
            assert isinstance(v, QAPISchemaObjectTypeVariant)
        self.tag_name = tag_name
1073
        self.tag_member = tag_member
1074 1075
        self.variants = variants

1076 1077 1078 1079
    def set_owner(self, name):
        for v in self.variants:
            v.set_owner(name)

1080
    def check(self, schema, seen):
1081
        if not self.tag_member:    # flat union
1082 1083
            self.tag_member = seen[c_name(self.tag_name)]
            assert self.tag_name == self.tag_member.name
1084 1085
        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
        for v in self.variants:
1086
            v.check(schema)
1087 1088 1089
            # Union names must match enum values; alternate names are
            # checked separately. Use 'seen' to tell the two apart.
            if seen:
1090
                assert v.name in self.tag_member.type.member_names()
1091
                assert isinstance(v.type, QAPISchemaObjectType)
1092 1093
                v.type.check(schema)

1094
    def check_clash(self, schema, info, seen):
1095 1096 1097 1098
        for v in self.variants:
            # Reset seen map for each variant, since qapi names from one
            # branch do not affect another branch
            assert isinstance(v.type, QAPISchemaObjectType)
1099
            v.type.check_clash(schema, info, dict(seen))
1100

E
Eric Blake 已提交
1101

1102
class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1103 1104
    role = 'branch'

1105 1106 1107
    def __init__(self, name, typ):
        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)

1108 1109 1110
    # This function exists to support ugly simple union special cases
    # TODO get rid of them, and drop the function
    def simple_union_type(self):
1111 1112
        if (self.type.is_implicit() and
                isinstance(self.type, QAPISchemaObjectType)):
1113 1114 1115 1116 1117
            assert len(self.type.members) == 1
            assert not self.type.variants
            return self.type.members[0].type
        return None

1118 1119 1120 1121 1122 1123

class QAPISchemaAlternateType(QAPISchemaType):
    def __init__(self, name, info, variants):
        QAPISchemaType.__init__(self, name, info)
        assert isinstance(variants, QAPISchemaObjectTypeVariants)
        assert not variants.tag_name
1124 1125
        variants.set_owner(name)
        variants.tag_member.set_owner(self.name)
1126 1127 1128
        self.variants = variants

    def check(self, schema):
1129
        self.variants.tag_member.check(schema)
1130 1131
        # Not calling self.variants.check_clash(), because there's nothing
        # to clash with
1132
        self.variants.check(schema, {})
1133 1134 1135 1136 1137
        # Alternate branch names have no relation to the tag enum values;
        # so we have to check for potential name collisions ourselves.
        seen = {}
        for v in self.variants.variants:
            v.check_clash(self.info, seen)
1138

1139 1140 1141
    def json_type(self):
        return 'value'

M
Markus Armbruster 已提交
1142 1143 1144
    def visit(self, visitor):
        visitor.visit_alternate_type(self.name, self.info, self.variants)

1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166

class QAPISchemaCommand(QAPISchemaEntity):
    def __init__(self, name, info, arg_type, ret_type, gen, success_response):
        QAPISchemaEntity.__init__(self, name, info)
        assert not arg_type or isinstance(arg_type, str)
        assert not ret_type or isinstance(ret_type, str)
        self._arg_type_name = arg_type
        self.arg_type = None
        self._ret_type_name = ret_type
        self.ret_type = None
        self.gen = gen
        self.success_response = success_response

    def check(self, schema):
        if self._arg_type_name:
            self.arg_type = schema.lookup_type(self._arg_type_name)
            assert isinstance(self.arg_type, QAPISchemaObjectType)
            assert not self.arg_type.variants   # not implemented
        if self._ret_type_name:
            self.ret_type = schema.lookup_type(self._ret_type_name)
            assert isinstance(self.ret_type, QAPISchemaType)

M
Markus Armbruster 已提交
1167 1168 1169 1170 1171
    def visit(self, visitor):
        visitor.visit_command(self.name, self.info,
                              self.arg_type, self.ret_type,
                              self.gen, self.success_response)

1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185

class QAPISchemaEvent(QAPISchemaEntity):
    def __init__(self, name, info, arg_type):
        QAPISchemaEntity.__init__(self, name, info)
        assert not arg_type or isinstance(arg_type, str)
        self._arg_type_name = arg_type
        self.arg_type = None

    def check(self, schema):
        if self._arg_type_name:
            self.arg_type = schema.lookup_type(self._arg_type_name)
            assert isinstance(self.arg_type, QAPISchemaObjectType)
            assert not self.arg_type.variants   # not implemented

M
Markus Armbruster 已提交
1186 1187 1188
    def visit(self, visitor):
        visitor.visit_event(self.name, self.info, self.arg_type)

1189 1190 1191 1192 1193

class QAPISchema(object):
    def __init__(self, fname):
        try:
            self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1194
            self._entity_dict = {}
1195
            self._predefining = True
1196
            self._def_predefineds()
1197
            self._predefining = False
1198 1199
            self._def_exprs()
            self.check()
1200
        except (QAPISchemaError, QAPIExprError) as err:
1201 1202 1203 1204
            print >>sys.stderr, err
            exit(1)

    def _def_entity(self, ent):
1205 1206
        # Only the predefined types are allowed to not have info
        assert ent.info or self._predefining
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
        assert ent.name not in self._entity_dict
        self._entity_dict[ent.name] = ent

    def lookup_entity(self, name, typ=None):
        ent = self._entity_dict.get(name)
        if typ and not isinstance(ent, typ):
            return None
        return ent

    def lookup_type(self, name):
        return self.lookup_entity(name, QAPISchemaType)

1219 1220 1221
    def _def_builtin_type(self, name, json_type, c_type, c_null):
        self._def_entity(QAPISchemaBuiltinType(name, json_type,
                                               c_type, c_null))
E
Eric Blake 已提交
1222 1223 1224 1225 1226
        # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
        # qapi-types.h from a single .c, all arrays of builtins must be
        # declared in the first file whether or not they are used.  Nicer
        # would be to use lazy instantiation, while figuring out how to
        # avoid compilation issues with multiple qapi-types.h.
1227
        self._make_array_type(name, None)
1228 1229

    def _def_predefineds(self):
1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
        for t in [('str',    'string',  'char' + pointer_suffix, 'NULL'),
                  ('number', 'number',  'double',   '0'),
                  ('int',    'int',     'int64_t',  '0'),
                  ('int8',   'int',     'int8_t',   '0'),
                  ('int16',  'int',     'int16_t',  '0'),
                  ('int32',  'int',     'int32_t',  '0'),
                  ('int64',  'int',     'int64_t',  '0'),
                  ('uint8',  'int',     'uint8_t',  '0'),
                  ('uint16', 'int',     'uint16_t', '0'),
                  ('uint32', 'int',     'uint32_t', '0'),
                  ('uint64', 'int',     'uint64_t', '0'),
                  ('size',   'int',     'uint64_t', '0'),
                  ('bool',   'boolean', 'bool',     'false'),
1243
                  ('any',    'value',   'QObject' + pointer_suffix, 'NULL')]:
1244
            self._def_builtin_type(*t)
1245 1246 1247
        self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
                                                          [], None)
        self._def_entity(self.the_empty_object_type)
1248 1249 1250 1251
        qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
                                                'qstring', 'qdict', 'qlist',
                                                'qfloat', 'qbool'])
        self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
1252
                                            'QTYPE'))
1253

1254 1255 1256
    def _make_enum_members(self, values):
        return [QAPISchemaMember(v) for v in values]

1257
    def _make_implicit_enum_type(self, name, info, values):
1258
        # See also QAPISchemaObjectTypeMember._pretty_owner()
1259
        name = name + 'Kind'   # Use namespace reserved by add_name()
1260 1261
        self._def_entity(QAPISchemaEnumType(
            name, info, self._make_enum_members(values), None))
1262 1263
        return name

1264
    def _make_array_type(self, element_type, info):
1265
        name = element_type + 'List'   # Use namespace reserved by add_name()
1266
        if not self.lookup_type(name):
1267
            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1268 1269
        return name

1270
    def _make_implicit_object_type(self, name, info, role, members):
1271 1272
        if not members:
            return None
1273
        # See also QAPISchemaObjectTypeMember._pretty_owner()
1274 1275
        name = ':obj-%s-%s' % (name, role)
        if not self.lookup_entity(name, QAPISchemaObjectType):
1276
            self._def_entity(QAPISchemaObjectType(name, info, None,
1277 1278 1279 1280 1281 1282 1283
                                                  members, None))
        return name

    def _def_enum_type(self, expr, info):
        name = expr['enum']
        data = expr['data']
        prefix = expr.get('prefix')
1284 1285
        self._def_entity(QAPISchemaEnumType(
            name, info, self._make_enum_members(data), prefix))
1286

1287
    def _make_member(self, name, typ, info):
1288 1289 1290 1291 1292 1293
        optional = False
        if name.startswith('*'):
            name = name[1:]
            optional = True
        if isinstance(typ, list):
            assert len(typ) == 1
1294
            typ = self._make_array_type(typ[0], info)
1295 1296
        return QAPISchemaObjectTypeMember(name, typ, optional)

1297 1298
    def _make_members(self, data, info):
        return [self._make_member(key, value, info)
1299 1300 1301 1302 1303 1304 1305
                for (key, value) in data.iteritems()]

    def _def_struct_type(self, expr, info):
        name = expr['struct']
        base = expr.get('base')
        data = expr['data']
        self._def_entity(QAPISchemaObjectType(name, info, base,
1306
                                              self._make_members(data, info),
1307 1308 1309 1310 1311
                                              None))

    def _make_variant(self, case, typ):
        return QAPISchemaObjectTypeVariant(case, typ)

1312
    def _make_simple_variant(self, case, typ, info):
1313 1314
        if isinstance(typ, list):
            assert len(typ) == 1
1315 1316 1317
            typ = self._make_array_type(typ[0], info)
        typ = self._make_implicit_object_type(
            typ, info, 'wrapper', [self._make_member('data', typ, info)])
1318 1319 1320 1321 1322 1323 1324
        return QAPISchemaObjectTypeVariant(case, typ)

    def _def_union_type(self, expr, info):
        name = expr['union']
        data = expr['data']
        base = expr.get('base')
        tag_name = expr.get('discriminator')
1325
        tag_member = None
1326 1327 1328
        if tag_name:
            variants = [self._make_variant(key, value)
                        for (key, value) in data.iteritems()]
1329
            members = []
1330
        else:
1331
            variants = [self._make_simple_variant(key, value, info)
1332
                        for (key, value) in data.iteritems()]
E
Eric Blake 已提交
1333 1334 1335
            typ = self._make_implicit_enum_type(name, info,
                                                [v.name for v in variants])
            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1336
            members = [tag_member]
1337
        self._def_entity(
1338
            QAPISchemaObjectType(name, info, base, members,
1339
                                 QAPISchemaObjectTypeVariants(tag_name,
1340
                                                              tag_member,
1341 1342 1343 1344 1345 1346 1347
                                                              variants)))

    def _def_alternate_type(self, expr, info):
        name = expr['alternate']
        data = expr['data']
        variants = [self._make_variant(key, value)
                    for (key, value) in data.iteritems()]
1348
        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1349 1350 1351
        self._def_entity(
            QAPISchemaAlternateType(name, info,
                                    QAPISchemaObjectTypeVariants(None,
1352
                                                                 tag_member,
1353 1354 1355 1356 1357 1358 1359 1360 1361
                                                                 variants)))

    def _def_command(self, expr, info):
        name = expr['command']
        data = expr.get('data')
        rets = expr.get('returns')
        gen = expr.get('gen', True)
        success_response = expr.get('success-response', True)
        if isinstance(data, OrderedDict):
1362 1363
            data = self._make_implicit_object_type(
                name, info, 'arg', self._make_members(data, info))
1364 1365
        if isinstance(rets, list):
            assert len(rets) == 1
1366
            rets = self._make_array_type(rets[0], info)
1367 1368 1369 1370 1371 1372 1373
        self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
                                           success_response))

    def _def_event(self, expr, info):
        name = expr['event']
        data = expr.get('data')
        if isinstance(data, OrderedDict):
1374 1375
            data = self._make_implicit_object_type(
                name, info, 'arg', self._make_members(data, info))
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399
        self._def_entity(QAPISchemaEvent(name, info, data))

    def _def_exprs(self):
        for expr_elem in self.exprs:
            expr = expr_elem['expr']
            info = expr_elem['info']
            if 'enum' in expr:
                self._def_enum_type(expr, info)
            elif 'struct' in expr:
                self._def_struct_type(expr, info)
            elif 'union' in expr:
                self._def_union_type(expr, info)
            elif 'alternate' in expr:
                self._def_alternate_type(expr, info)
            elif 'command' in expr:
                self._def_command(expr, info)
            elif 'event' in expr:
                self._def_event(expr, info)
            else:
                assert False

    def check(self):
        for ent in self._entity_dict.values():
            ent.check(self)
1400

M
Markus Armbruster 已提交
1401
    def visit(self, visitor):
1402 1403 1404 1405
        visitor.visit_begin(self)
        for (name, entity) in sorted(self._entity_dict.items()):
            if visitor.visit_needed(entity):
                entity.visit(visitor)
M
Markus Armbruster 已提交
1406 1407
        visitor.visit_end()

1408

1409 1410 1411 1412
#
# Code generation helpers
#

1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
def camel_case(name):
    new_name = ''
    first = True
    for ch in name:
        if ch in ['_', '-']:
            first = True
        elif first:
            new_name += ch.upper()
            first = False
        else:
            new_name += ch.lower()
    return new_name

E
Eric Blake 已提交
1426

1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440
# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
# ENUM24_Name -> ENUM24_NAME
def camel_to_upper(value):
    c_fun_str = c_name(value, False)
    if value.isupper():
        return c_fun_str

    new_name = ''
    l = len(c_fun_str)
    for i in range(l):
        c = c_fun_str[i]
        # When c is upper and no "_" appears before, do more checks
        if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
E
Eric Blake 已提交
1441 1442 1443
            if i < l - 1 and c_fun_str[i + 1].islower():
                new_name += '_'
            elif c_fun_str[i - 1].isdigit():
1444 1445 1446 1447
                new_name += '_'
        new_name += c
    return new_name.lstrip('_').upper()

E
Eric Blake 已提交
1448

1449 1450 1451
def c_enum_const(type_name, const_name, prefix=None):
    if prefix is not None:
        type_name = prefix
1452
    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1453

1454
c_name_trans = string.maketrans('.-', '__')
1455

E
Eric Blake 已提交
1456

1457 1458 1459 1460 1461 1462 1463 1464 1465
# Map @name to a valid C identifier.
# If @protect, avoid returning certain ticklish identifiers (like
# C keywords) by prepending "q_".
#
# Used for converting 'name' from a 'name':'type' qapi definition
# into a generated struct member, as well as converting type names
# into substrings of a generated C function name.
# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1466
def c_name(name, protect=True):
B
Blue Swirl 已提交
1467 1468
    # ANSI X3J11/88-090, 3.1.1
    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
E
Eric Blake 已提交
1469 1470 1471 1472 1473
                     'default', 'do', 'double', 'else', 'enum', 'extern',
                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
                     'return', 'short', 'signed', 'sizeof', 'static',
                     'struct', 'switch', 'typedef', 'union', 'unsigned',
                     'void', 'volatile', 'while'])
B
Blue Swirl 已提交
1474 1475 1476
    # ISO/IEC 9899:1999, 6.4.1
    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
    # ISO/IEC 9899:2011, 6.4.1
E
Eric Blake 已提交
1477 1478
    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
                     '_Noreturn', '_Static_assert', '_Thread_local'])
B
Blue Swirl 已提交
1479 1480 1481
    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
    # excluding _.*
    gcc_words = set(['asm', 'typeof'])
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
    # C++ ISO/IEC 14882:2003 2.11
    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
                     'namespace', 'new', 'operator', 'private', 'protected',
                     'public', 'reinterpret_cast', 'static_cast', 'template',
                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
                     'using', 'virtual', 'wchar_t',
                     # alternative representations
                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1492
    # namespace pollution:
1493
    polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
E
Eric Blake 已提交
1494
    name = name.translate(c_name_trans)
E
Eric Blake 已提交
1495 1496
    if protect and (name in c89_words | c99_words | c11_words | gcc_words
                    | cpp_words | polluted_words):
B
Blue Swirl 已提交
1497
        return "q_" + name
E
Eric Blake 已提交
1498
    return name
1499

1500
eatspace = '\033EATSPACE.'
E
Eric Blake 已提交
1501
pointer_suffix = ' *' + eatspace
1502

E
Eric Blake 已提交
1503

1504 1505
def genindent(count):
    ret = ""
E
Eric Blake 已提交
1506
    for _ in range(count):
1507 1508 1509 1510 1511
        ret += " "
    return ret

indent_level = 0

E
Eric Blake 已提交
1512

1513 1514 1515 1516
def push_indent(indent_amount=4):
    global indent_level
    indent_level += indent_amount

E
Eric Blake 已提交
1517

1518 1519 1520 1521
def pop_indent(indent_amount=4):
    global indent_level
    indent_level -= indent_amount

E
Eric Blake 已提交
1522

1523 1524
# Generate @code with @kwds interpolated.
# Obey indent_level, and strip eatspace.
1525
def cgen(code, **kwds):
1526 1527 1528
    raw = code % kwds
    if indent_level:
        indent = genindent(indent_level)
1529 1530 1531
        # re.subn() lacks flags support before Python 2.7, use re.compile()
        raw = re.subn(re.compile("^.", re.MULTILINE),
                      indent + r'\g<0>', raw)
1532 1533
        raw = raw[0]
    return re.sub(re.escape(eatspace) + ' *', '', raw)
1534

E
Eric Blake 已提交
1535

1536
def mcgen(code, **kwds):
1537 1538 1539
    if code[0] == '\n':
        code = code[1:]
    return cgen(code, **kwds)
1540 1541 1542


def guardname(filename):
M
Markus Armbruster 已提交
1543
    return c_name(filename, protect=False).upper()
1544

E
Eric Blake 已提交
1545

1546 1547 1548 1549 1550 1551 1552 1553 1554
def guardstart(name):
    return mcgen('''

#ifndef %(name)s
#define %(name)s

''',
                 name=guardname(name))

E
Eric Blake 已提交
1555

1556 1557 1558 1559 1560 1561 1562
def guardend(name):
    return mcgen('''

#endif /* %(name)s */

''',
                 name=guardname(name))
1563

E
Eric Blake 已提交
1564

1565
def gen_enum_lookup(name, values, prefix=None):
1566 1567
    ret = mcgen('''

1568
const char *const %(c_name)s_lookup[] = {
1569
''',
1570
                c_name=c_name(name))
1571 1572 1573 1574 1575
    for value in values:
        index = c_enum_const(name, value, prefix)
        ret += mcgen('''
    [%(index)s] = "%(value)s",
''',
1576
                     index=index, value=value)
1577

1578
    max_index = c_enum_const(name, '_MAX', prefix)
1579 1580 1581 1582
    ret += mcgen('''
    [%(max_index)s] = NULL,
};
''',
1583
                 max_index=max_index)
1584 1585
    return ret

E
Eric Blake 已提交
1586

1587 1588
def gen_enum(name, values, prefix=None):
    # append automatically generated _MAX value
1589
    enum_values = values + ['_MAX']
1590

1591
    ret = mcgen('''
1592

1593
typedef enum %(c_name)s {
1594
''',
1595
                c_name=c_name(name))
1596 1597 1598

    i = 0
    for value in enum_values:
1599 1600
        ret += mcgen('''
    %(c_enum)s = %(i)d,
1601
''',
1602
                     c_enum=c_enum_const(name, value, prefix),
1603 1604 1605
                     i=i)
        i += 1

1606 1607
    ret += mcgen('''
} %(c_name)s;
1608
''',
1609 1610 1611
                 c_name=c_name(name))

    ret += mcgen('''
1612

1613 1614 1615 1616
extern const char *const %(c_name)s_lookup[];
''',
                 c_name=c_name(name))
    return ret
1617

E
Eric Blake 已提交
1618

1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
def gen_params(arg_type, extra):
    if not arg_type:
        return extra
    assert not arg_type.variants
    ret = ''
    sep = ''
    for memb in arg_type.members:
        ret += sep
        sep = ', '
        if memb.optional:
            ret += 'bool has_%s, ' % c_name(memb.name)
        ret += '%s %s' % (memb.type.c_type(is_param=True), c_name(memb.name))
    if extra:
        ret += sep + extra
    return ret

E
Eric Blake 已提交
1635

1636 1637
def gen_err_check(label='out', skiperr=False):
    if skiperr:
E
Eric Blake 已提交
1638 1639
        return ''
    return mcgen('''
1640
    if (err) {
E
Eric Blake 已提交
1641 1642 1643
        goto %(label)s;
    }
''',
1644
                 label=label)
E
Eric Blake 已提交
1645 1646


1647 1648
def gen_visit_fields(members, prefix='', need_cast=False, skiperr=False,
                     label='out'):
E
Eric Blake 已提交
1649
    ret = ''
1650
    if skiperr:
E
Eric Blake 已提交
1651
        errparg = 'NULL'
1652 1653
    else:
        errparg = '&err'
E
Eric Blake 已提交
1654 1655 1656 1657

    for memb in members:
        if memb.optional:
            ret += mcgen('''
1658
    if (visit_optional(v, "%(name)s", &%(prefix)shas_%(c_name)s)) {
E
Eric Blake 已提交
1659 1660
''',
                         prefix=prefix, c_name=c_name(memb.name),
1661
                         name=memb.name)
E
Eric Blake 已提交
1662 1663 1664 1665 1666 1667 1668 1669 1670
            push_indent()

        # Ugly: sometimes we need to cast away const
        if need_cast and memb.type.name == 'str':
            cast = '(char **)'
        else:
            cast = ''

        ret += mcgen('''
1671
    visit_type_%(c_type)s(v, "%(name)s", %(cast)s&%(prefix)s%(c_name)s, %(errp)s);
E
Eric Blake 已提交
1672 1673 1674 1675
''',
                     c_type=memb.type.c_name(), prefix=prefix, cast=cast,
                     c_name=c_name(memb.name), name=memb.name,
                     errp=errparg)
1676
        ret += gen_err_check(skiperr=skiperr, label=label)
E
Eric Blake 已提交
1677 1678 1679 1680 1681 1682 1683 1684 1685

        if memb.optional:
            pop_indent()
            ret += mcgen('''
    }
''')
    return ret


1686 1687 1688 1689
#
# Common command line parsing
#

E
Eric Blake 已提交
1690 1691

def parse_command_line(extra_options="", extra_long_options=[]):
1692 1693 1694

    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:],
1695
                                       "chp:o:" + extra_options,
1696
                                       ["source", "header", "prefix=",
1697
                                        "output-dir="] + extra_long_options)
1698
    except getopt.GetoptError as err:
1699
        print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710
        sys.exit(1)

    output_dir = ""
    prefix = ""
    do_c = False
    do_h = False
    extra_opts = []

    for oa in opts:
        o, a = oa
        if o in ("-p", "--prefix"):
1711 1712 1713 1714 1715 1716
            match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
            if match.end() != len(a):
                print >>sys.stderr, \
                    "%s: 'funny character '%s' in argument of --prefix" \
                    % (sys.argv[0], a[match.end()])
                sys.exit(1)
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730
            prefix = a
        elif o in ("-o", "--output-dir"):
            output_dir = a + "/"
        elif o in ("-c", "--source"):
            do_c = True
        elif o in ("-h", "--header"):
            do_h = True
        else:
            extra_opts.append(oa)

    if not do_c and not do_h:
        do_c = True
        do_h = True

1731 1732
    if len(args) != 1:
        print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1733
        sys.exit(1)
1734
    fname = args[0]
1735

1736
    return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1737

1738 1739 1740 1741
#
# Generate output files with boilerplate
#

E
Eric Blake 已提交
1742

1743 1744
def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
                c_comment, h_comment):
M
Markus Armbruster 已提交
1745
    guard = guardname(prefix + h_file)
1746 1747 1748
    c_file = output_dir + prefix + c_file
    h_file = output_dir + prefix + h_file

1749 1750 1751
    if output_dir:
        try:
            os.makedirs(output_dir)
1752
        except os.error as e:
1753 1754
            if e.errno != errno.EEXIST:
                raise
1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769

    def maybe_open(really, name, opt):
        if really:
            return open(name, opt)
        else:
            import StringIO
            return StringIO.StringIO()

    fdef = maybe_open(do_c, c_file, 'w')
    fdecl = maybe_open(do_h, h_file, 'w')

    fdef.write(mcgen('''
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
%(comment)s
''',
E
Eric Blake 已提交
1770
                     comment=c_comment))
1771 1772 1773 1774 1775 1776 1777 1778

    fdecl.write(mcgen('''
/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
%(comment)s
#ifndef %(guard)s
#define %(guard)s

''',
E
Eric Blake 已提交
1779
                      comment=h_comment, guard=guard))
1780 1781 1782

    return (fdef, fdecl)

E
Eric Blake 已提交
1783

1784 1785 1786 1787 1788 1789
def close_output(fdef, fdecl):
    fdecl.write('''
#endif
''')
    fdecl.close()
    fdef.close()