]> Git Repo - qemu.git/blob - scripts/qapi/common.py
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[qemu.git] / scripts / qapi / common.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
6 #
7 # Authors:
8 #  Anthony Liguori <[email protected]>
9 #  Markus Armbruster <[email protected]>
10 #
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
13
14 from __future__ import print_function
15 from contextlib import contextmanager
16 import errno
17 import os
18 import re
19 import string
20 import sys
21 from collections import OrderedDict
22
23 builtin_types = {
24     'null':     'QTYPE_QNULL',
25     'str':      'QTYPE_QSTRING',
26     'int':      'QTYPE_QNUM',
27     'number':   'QTYPE_QNUM',
28     'bool':     'QTYPE_QBOOL',
29     'int8':     'QTYPE_QNUM',
30     'int16':    'QTYPE_QNUM',
31     'int32':    'QTYPE_QNUM',
32     'int64':    'QTYPE_QNUM',
33     'uint8':    'QTYPE_QNUM',
34     'uint16':   'QTYPE_QNUM',
35     'uint32':   'QTYPE_QNUM',
36     'uint64':   'QTYPE_QNUM',
37     'size':     'QTYPE_QNUM',
38     'any':      None,           # any QType possible, actually
39     'QType':    'QTYPE_QSTRING',
40 }
41
42 # Are documentation comments required?
43 doc_required = False
44
45 # Whitelist of commands allowed to return a non-dictionary
46 returns_whitelist = []
47
48 # Whitelist of entities allowed to violate case conventions
49 name_case_whitelist = []
50
51 enum_types = {}
52 struct_types = {}
53 union_types = {}
54 all_names = {}
55
56 #
57 # Parsing the schema into expressions
58 #
59
60
61 def error_path(parent):
62     res = ''
63     while parent:
64         res = ('In file included from %s:%d:\n' % (parent['file'],
65                                                    parent['line'])) + res
66         parent = parent['parent']
67     return res
68
69
70 class QAPIError(Exception):
71     def __init__(self, fname, line, col, incl_info, msg):
72         Exception.__init__(self)
73         self.fname = fname
74         self.line = line
75         self.col = col
76         self.info = incl_info
77         self.msg = msg
78
79     def __str__(self):
80         loc = '%s:%d' % (self.fname, self.line)
81         if self.col is not None:
82             loc += ':%s' % self.col
83         return error_path(self.info) + '%s: %s' % (loc, self.msg)
84
85
86 class QAPIParseError(QAPIError):
87     def __init__(self, parser, msg):
88         col = 1
89         for ch in parser.src[parser.line_pos:parser.pos]:
90             if ch == '\t':
91                 col = (col + 7) % 8 + 1
92             else:
93                 col += 1
94         QAPIError.__init__(self, parser.fname, parser.line, col,
95                            parser.incl_info, msg)
96
97
98 class QAPISemError(QAPIError):
99     def __init__(self, info, msg):
100         QAPIError.__init__(self, info['file'], info['line'], None,
101                            info['parent'], msg)
102
103
104 class QAPIDoc(object):
105     class Section(object):
106         def __init__(self, name=None):
107             # optional section name (argument/member or section name)
108             self.name = name
109             # the list of lines for this section
110             self.text = ''
111
112         def append(self, line):
113             self.text += line.rstrip() + '\n'
114
115     class ArgSection(Section):
116         def __init__(self, name):
117             QAPIDoc.Section.__init__(self, name)
118             self.member = None
119
120         def connect(self, member):
121             self.member = member
122
123     def __init__(self, parser, info):
124         # self._parser is used to report errors with QAPIParseError.  The
125         # resulting error position depends on the state of the parser.
126         # It happens to be the beginning of the comment.  More or less
127         # servicable, but action at a distance.
128         self._parser = parser
129         self.info = info
130         self.symbol = None
131         self.body = QAPIDoc.Section()
132         # dict mapping parameter name to ArgSection
133         self.args = OrderedDict()
134         # a list of Section
135         self.sections = []
136         # the current section
137         self._section = self.body
138
139     def has_section(self, name):
140         """Return True if we have a section with this name."""
141         for i in self.sections:
142             if i.name == name:
143                 return True
144         return False
145
146     def append(self, line):
147         """Parse a comment line and add it to the documentation."""
148         line = line[1:]
149         if not line:
150             self._append_freeform(line)
151             return
152
153         if line[0] != ' ':
154             raise QAPIParseError(self._parser, "Missing space after #")
155         line = line[1:]
156
157         # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
158         # recognized, and get silently treated as ordinary text
159         if self.symbol:
160             self._append_symbol_line(line)
161         elif not self.body.text and line.startswith('@'):
162             if not line.endswith(':'):
163                 raise QAPIParseError(self._parser, "Line should end with :")
164             self.symbol = line[1:-1]
165             # FIXME invalid names other than the empty string aren't flagged
166             if not self.symbol:
167                 raise QAPIParseError(self._parser, "Invalid name")
168         else:
169             self._append_freeform(line)
170
171     def end_comment(self):
172         self._end_section()
173
174     def _append_symbol_line(self, line):
175         name = line.split(' ', 1)[0]
176
177         if name.startswith('@') and name.endswith(':'):
178             line = line[len(name)+1:]
179             self._start_args_section(name[1:-1])
180         elif name in ('Returns:', 'Since:',
181                       # those are often singular or plural
182                       'Note:', 'Notes:',
183                       'Example:', 'Examples:',
184                       'TODO:'):
185             line = line[len(name)+1:]
186             self._start_section(name[:-1])
187
188         self._append_freeform(line)
189
190     def _start_args_section(self, name):
191         # FIXME invalid names other than the empty string aren't flagged
192         if not name:
193             raise QAPIParseError(self._parser, "Invalid parameter name")
194         if name in self.args:
195             raise QAPIParseError(self._parser,
196                                  "'%s' parameter name duplicated" % name)
197         if self.sections:
198             raise QAPIParseError(self._parser,
199                                  "'@%s:' can't follow '%s' section"
200                                  % (name, self.sections[0].name))
201         self._end_section()
202         self._section = QAPIDoc.ArgSection(name)
203         self.args[name] = self._section
204
205     def _start_section(self, name=None):
206         if name in ('Returns', 'Since') and self.has_section(name):
207             raise QAPIParseError(self._parser,
208                                  "Duplicated '%s' section" % name)
209         self._end_section()
210         self._section = QAPIDoc.Section(name)
211         self.sections.append(self._section)
212
213     def _end_section(self):
214         if self._section:
215             text = self._section.text = self._section.text.strip()
216             if self._section.name and (not text or text.isspace()):
217                 raise QAPIParseError(self._parser, "Empty doc section '%s'"
218                                      % self._section.name)
219             self._section = None
220
221     def _append_freeform(self, line):
222         in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223         if (in_arg and self._section.text.endswith('\n\n')
224                 and line and not line[0].isspace()):
225             self._start_section()
226         if (in_arg or not self._section.name
227                 or not self._section.name.startswith('Example')):
228             line = line.strip()
229         match = re.match(r'(@\S+:)', line)
230         if match:
231             raise QAPIParseError(self._parser,
232                                  "'%s' not allowed in free-form documentation"
233                                  % match.group(1))
234         self._section.append(line)
235
236     def connect_member(self, member):
237         if member.name not in self.args:
238             # Undocumented TODO outlaw
239             self.args[member.name] = QAPIDoc.ArgSection(member.name)
240         self.args[member.name].connect(member)
241
242     def check_expr(self, expr):
243         if self.has_section('Returns') and 'command' not in expr:
244             raise QAPISemError(self.info,
245                                "'Returns:' is only valid for commands")
246
247     def check(self):
248         bogus = [name for name, section in self.args.items()
249                  if not section.member]
250         if bogus:
251             raise QAPISemError(
252                 self.info,
253                 "The following documented members are not in "
254                 "the declaration: %s" % ", ".join(bogus))
255
256
257 class QAPISchemaParser(object):
258
259     def __init__(self, fp, previously_included=[], incl_info=None):
260         self.fname = fp.name
261         previously_included.append(os.path.abspath(fp.name))
262         self.incl_info = incl_info
263         self.src = fp.read()
264         if self.src == '' or self.src[-1] != '\n':
265             self.src += '\n'
266         self.cursor = 0
267         self.line = 1
268         self.line_pos = 0
269         self.exprs = []
270         self.docs = []
271         self.accept()
272         cur_doc = None
273
274         while self.tok is not None:
275             info = {'file': self.fname, 'line': self.line,
276                     'parent': self.incl_info}
277             if self.tok == '#':
278                 self.reject_expr_doc(cur_doc)
279                 cur_doc = self.get_doc(info)
280                 self.docs.append(cur_doc)
281                 continue
282
283             expr = self.get_expr(False)
284             if 'include' in expr:
285                 self.reject_expr_doc(cur_doc)
286                 if len(expr) != 1:
287                     raise QAPISemError(info, "Invalid 'include' directive")
288                 include = expr['include']
289                 if not isinstance(include, str):
290                     raise QAPISemError(info,
291                                        "Value of 'include' must be a string")
292                 incl_fname = os.path.join(os.path.dirname(self.fname),
293                                           include)
294                 self.exprs.append({'expr': {'include': incl_fname},
295                                    'info': info})
296                 exprs_include = self._include(include, info, incl_fname,
297                                               previously_included)
298                 if exprs_include:
299                     self.exprs.extend(exprs_include.exprs)
300                     self.docs.extend(exprs_include.docs)
301             elif "pragma" in expr:
302                 self.reject_expr_doc(cur_doc)
303                 if len(expr) != 1:
304                     raise QAPISemError(info, "Invalid 'pragma' directive")
305                 pragma = expr['pragma']
306                 if not isinstance(pragma, dict):
307                     raise QAPISemError(
308                         info, "Value of 'pragma' must be a dictionary")
309                 for name, value in pragma.items():
310                     self._pragma(name, value, info)
311             else:
312                 expr_elem = {'expr': expr,
313                              'info': info}
314                 if cur_doc:
315                     if not cur_doc.symbol:
316                         raise QAPISemError(
317                             cur_doc.info, "Expression documentation required")
318                     expr_elem['doc'] = cur_doc
319                 self.exprs.append(expr_elem)
320             cur_doc = None
321         self.reject_expr_doc(cur_doc)
322
323     @staticmethod
324     def reject_expr_doc(doc):
325         if doc and doc.symbol:
326             raise QAPISemError(
327                 doc.info,
328                 "Documentation for '%s' is not followed by the definition"
329                 % doc.symbol)
330
331     def _include(self, include, info, incl_fname, previously_included):
332         incl_abs_fname = os.path.abspath(incl_fname)
333         # catch inclusion cycle
334         inf = info
335         while inf:
336             if incl_abs_fname == os.path.abspath(inf['file']):
337                 raise QAPISemError(info, "Inclusion loop for %s" % include)
338             inf = inf['parent']
339
340         # skip multiple include of the same file
341         if incl_abs_fname in previously_included:
342             return None
343
344         try:
345             if sys.version_info[0] >= 3:
346                 fobj = open(incl_fname, 'r', encoding='utf-8')
347             else:
348                 fobj = open(incl_fname, 'r')
349         except IOError as e:
350             raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
351         return QAPISchemaParser(fobj, previously_included, info)
352
353     def _pragma(self, name, value, info):
354         global doc_required, returns_whitelist, name_case_whitelist
355         if name == 'doc-required':
356             if not isinstance(value, bool):
357                 raise QAPISemError(info,
358                                    "Pragma 'doc-required' must be boolean")
359             doc_required = value
360         elif name == 'returns-whitelist':
361             if (not isinstance(value, list)
362                     or any([not isinstance(elt, str) for elt in value])):
363                 raise QAPISemError(info,
364                                    "Pragma returns-whitelist must be"
365                                    " a list of strings")
366             returns_whitelist = value
367         elif name == 'name-case-whitelist':
368             if (not isinstance(value, list)
369                     or any([not isinstance(elt, str) for elt in value])):
370                 raise QAPISemError(info,
371                                    "Pragma name-case-whitelist must be"
372                                    " a list of strings")
373             name_case_whitelist = value
374         else:
375             raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
377     def accept(self, skip_comment=True):
378         while True:
379             self.tok = self.src[self.cursor]
380             self.pos = self.cursor
381             self.cursor += 1
382             self.val = None
383
384             if self.tok == '#':
385                 if self.src[self.cursor] == '#':
386                     # Start of doc comment
387                     skip_comment = False
388                 self.cursor = self.src.find('\n', self.cursor)
389                 if not skip_comment:
390                     self.val = self.src[self.pos:self.cursor]
391                     return
392             elif self.tok in '{}:,[]':
393                 return
394             elif self.tok == "'":
395                 string = ''
396                 esc = False
397                 while True:
398                     ch = self.src[self.cursor]
399                     self.cursor += 1
400                     if ch == '\n':
401                         raise QAPIParseError(self, 'Missing terminating "\'"')
402                     if esc:
403                         if ch == 'b':
404                             string += '\b'
405                         elif ch == 'f':
406                             string += '\f'
407                         elif ch == 'n':
408                             string += '\n'
409                         elif ch == 'r':
410                             string += '\r'
411                         elif ch == 't':
412                             string += '\t'
413                         elif ch == 'u':
414                             value = 0
415                             for _ in range(0, 4):
416                                 ch = self.src[self.cursor]
417                                 self.cursor += 1
418                                 if ch not in '0123456789abcdefABCDEF':
419                                     raise QAPIParseError(self,
420                                                          '\\u escape needs 4 '
421                                                          'hex digits')
422                                 value = (value << 4) + int(ch, 16)
423                             # If Python 2 and 3 didn't disagree so much on
424                             # how to handle Unicode, then we could allow
425                             # Unicode string defaults.  But most of QAPI is
426                             # ASCII-only, so we aren't losing much for now.
427                             if not value or value > 0x7f:
428                                 raise QAPIParseError(self,
429                                                      'For now, \\u escape '
430                                                      'only supports non-zero '
431                                                      'values up to \\u007f')
432                             string += chr(value)
433                         elif ch in '\\/\'"':
434                             string += ch
435                         else:
436                             raise QAPIParseError(self,
437                                                  "Unknown escape \\%s" % ch)
438                         esc = False
439                     elif ch == '\\':
440                         esc = True
441                     elif ch == "'":
442                         self.val = string
443                         return
444                     else:
445                         string += ch
446             elif self.src.startswith('true', self.pos):
447                 self.val = True
448                 self.cursor += 3
449                 return
450             elif self.src.startswith('false', self.pos):
451                 self.val = False
452                 self.cursor += 4
453                 return
454             elif self.src.startswith('null', self.pos):
455                 self.val = None
456                 self.cursor += 3
457                 return
458             elif self.tok == '\n':
459                 if self.cursor == len(self.src):
460                     self.tok = None
461                     return
462                 self.line += 1
463                 self.line_pos = self.cursor
464             elif not self.tok.isspace():
465                 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
466
467     def get_members(self):
468         expr = OrderedDict()
469         if self.tok == '}':
470             self.accept()
471             return expr
472         if self.tok != "'":
473             raise QAPIParseError(self, 'Expected string or "}"')
474         while True:
475             key = self.val
476             self.accept()
477             if self.tok != ':':
478                 raise QAPIParseError(self, 'Expected ":"')
479             self.accept()
480             if key in expr:
481                 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482             expr[key] = self.get_expr(True)
483             if self.tok == '}':
484                 self.accept()
485                 return expr
486             if self.tok != ',':
487                 raise QAPIParseError(self, 'Expected "," or "}"')
488             self.accept()
489             if self.tok != "'":
490                 raise QAPIParseError(self, 'Expected string')
491
492     def get_values(self):
493         expr = []
494         if self.tok == ']':
495             self.accept()
496             return expr
497         if self.tok not in "{['tfn":
498             raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499                                  'boolean or "null"')
500         while True:
501             expr.append(self.get_expr(True))
502             if self.tok == ']':
503                 self.accept()
504                 return expr
505             if self.tok != ',':
506                 raise QAPIParseError(self, 'Expected "," or "]"')
507             self.accept()
508
509     def get_expr(self, nested):
510         if self.tok != '{' and not nested:
511             raise QAPIParseError(self, 'Expected "{"')
512         if self.tok == '{':
513             self.accept()
514             expr = self.get_members()
515         elif self.tok == '[':
516             self.accept()
517             expr = self.get_values()
518         elif self.tok in "'tfn":
519             expr = self.val
520             self.accept()
521         else:
522             raise QAPIParseError(self, 'Expected "{", "[", string, '
523                                  'boolean or "null"')
524         return expr
525
526     def get_doc(self, info):
527         if self.val != '##':
528             raise QAPIParseError(self, "Junk after '##' at start of "
529                                  "documentation comment")
530
531         doc = QAPIDoc(self, info)
532         self.accept(False)
533         while self.tok == '#':
534             if self.val.startswith('##'):
535                 # End of doc comment
536                 if self.val != '##':
537                     raise QAPIParseError(self, "Junk after '##' at end of "
538                                          "documentation comment")
539                 doc.end_comment()
540                 self.accept()
541                 return doc
542             else:
543                 doc.append(self.val)
544             self.accept(False)
545
546         raise QAPIParseError(self, "Documentation comment must end with '##'")
547
548
549 #
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
553 #
554
555
556 def find_base_members(base):
557     if isinstance(base, dict):
558         return base
559     base_struct_define = struct_types.get(base)
560     if not base_struct_define:
561         return None
562     return base_struct_define['data']
563
564
565 # Return the qtype of an alternate branch, or None on error.
566 def find_alternate_member_qtype(qapi_type):
567     if qapi_type in builtin_types:
568         return builtin_types[qapi_type]
569     elif qapi_type in struct_types:
570         return 'QTYPE_QDICT'
571     elif qapi_type in enum_types:
572         return 'QTYPE_QSTRING'
573     elif qapi_type in union_types:
574         return 'QTYPE_QDICT'
575     return None
576
577
578 # Return the discriminator enum define if discriminator is specified as an
579 # enum type, otherwise return None.
580 def discriminator_find_enum_define(expr):
581     base = expr.get('base')
582     discriminator = expr.get('discriminator')
583
584     if not (discriminator and base):
585         return None
586
587     base_members = find_base_members(base)
588     if not base_members:
589         return None
590
591     discriminator_type = base_members.get(discriminator)
592     if not discriminator_type:
593         return None
594
595     return enum_types.get(discriminator_type)
596
597
598 # Names must be letters, numbers, -, and _.  They must start with letter,
599 # except for downstream extensions which must start with __RFQDN_.
600 # Dots are only valid in the downstream extension prefix.
601 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
602                         '[a-zA-Z][a-zA-Z0-9_-]*$')
603
604
605 def check_name(info, source, name, allow_optional=False,
606                enum_member=False):
607     global valid_name
608     membername = name
609
610     if not isinstance(name, str):
611         raise QAPISemError(info, "%s requires a string name" % source)
612     if name.startswith('*'):
613         membername = name[1:]
614         if not allow_optional:
615             raise QAPISemError(info, "%s does not allow optional name '%s'"
616                                % (source, name))
617     # Enum members can start with a digit, because the generated C
618     # code always prefixes it with the enum name
619     if enum_member and membername[0].isdigit():
620         membername = 'D' + membername
621     # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622     # and 'q_obj_*' implicit type names.
623     if not valid_name.match(membername) or \
624        c_name(membername, False).startswith('q_'):
625         raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
626
627
628 def add_name(name, info, meta, implicit=False):
629     global all_names
630     check_name(info, "'%s'" % meta, name)
631     # FIXME should reject names that differ only in '_' vs. '.'
632     # vs. '-', because they're liable to clash in generated C.
633     if name in all_names:
634         raise QAPISemError(info, "%s '%s' is already defined"
635                            % (all_names[name], name))
636     if not implicit and (name.endswith('Kind') or name.endswith('List')):
637         raise QAPISemError(info, "%s '%s' should not end in '%s'"
638                            % (meta, name, name[-4:]))
639     all_names[name] = meta
640
641
642 def check_if(expr, info):
643
644     def check_if_str(ifcond, info):
645         if not isinstance(ifcond, str):
646             raise QAPISemError(
647                 info, "'if' condition must be a string or a list of strings")
648         if ifcond == '':
649             raise QAPISemError(info, "'if' condition '' makes no sense")
650
651     ifcond = expr.get('if')
652     if ifcond is None:
653         return
654     if isinstance(ifcond, list):
655         if ifcond == []:
656             raise QAPISemError(info, "'if' condition [] is useless")
657         for elt in ifcond:
658             check_if_str(elt, info)
659     else:
660         check_if_str(ifcond, info)
661
662
663 def check_type(info, source, value, allow_array=False,
664                allow_dict=False, allow_optional=False,
665                allow_metas=[]):
666     global all_names
667
668     if value is None:
669         return
670
671     # Check if array type for value is okay
672     if isinstance(value, list):
673         if not allow_array:
674             raise QAPISemError(info, "%s cannot be an array" % source)
675         if len(value) != 1 or not isinstance(value[0], str):
676             raise QAPISemError(info,
677                                "%s: array type must contain single type name" %
678                                source)
679         value = value[0]
680
681     # Check if type name for value is okay
682     if isinstance(value, str):
683         if value not in all_names:
684             raise QAPISemError(info, "%s uses unknown type '%s'"
685                                % (source, value))
686         if not all_names[value] in allow_metas:
687             raise QAPISemError(info, "%s cannot use %s type '%s'" %
688                                (source, all_names[value], value))
689         return
690
691     if not allow_dict:
692         raise QAPISemError(info, "%s should be a type name" % source)
693
694     if not isinstance(value, OrderedDict):
695         raise QAPISemError(info,
696                            "%s should be a dictionary or type name" % source)
697
698     # value is a dictionary, check that each member is okay
699     for (key, arg) in value.items():
700         check_name(info, "Member of %s" % source, key,
701                    allow_optional=allow_optional)
702         if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
703             raise QAPISemError(info, "Member of %s uses reserved name '%s'"
704                                % (source, key))
705         # Todo: allow dictionaries to represent default values of
706         # an optional argument.
707         check_type(info, "Member '%s' of %s" % (key, source), arg,
708                    allow_array=True,
709                    allow_metas=['built-in', 'union', 'alternate', 'struct',
710                                 'enum'])
711
712
713 def check_command(expr, info):
714     name = expr['command']
715     boxed = expr.get('boxed', False)
716
717     args_meta = ['struct']
718     if boxed:
719         args_meta += ['union', 'alternate']
720     check_type(info, "'data' for command '%s'" % name,
721                expr.get('data'), allow_dict=not boxed, allow_optional=True,
722                allow_metas=args_meta)
723     returns_meta = ['union', 'struct']
724     if name in returns_whitelist:
725         returns_meta += ['built-in', 'alternate', 'enum']
726     check_type(info, "'returns' for command '%s'" % name,
727                expr.get('returns'), allow_array=True,
728                allow_optional=True, allow_metas=returns_meta)
729
730
731 def check_event(expr, info):
732     name = expr['event']
733     boxed = expr.get('boxed', False)
734
735     meta = ['struct']
736     if boxed:
737         meta += ['union', 'alternate']
738     check_type(info, "'data' for event '%s'" % name,
739                expr.get('data'), allow_dict=not boxed, allow_optional=True,
740                allow_metas=meta)
741
742
743 def check_union(expr, info):
744     name = expr['union']
745     base = expr.get('base')
746     discriminator = expr.get('discriminator')
747     members = expr['data']
748
749     # Two types of unions, determined by discriminator.
750
751     # With no discriminator it is a simple union.
752     if discriminator is None:
753         enum_define = None
754         allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
755         if base is not None:
756             raise QAPISemError(info, "Simple union '%s' must not have a base" %
757                                name)
758
759     # Else, it's a flat union.
760     else:
761         # The object must have a string or dictionary 'base'.
762         check_type(info, "'base' for union '%s'" % name,
763                    base, allow_dict=True, allow_optional=True,
764                    allow_metas=['struct'])
765         if not base:
766             raise QAPISemError(info, "Flat union '%s' must have a base"
767                                % name)
768         base_members = find_base_members(base)
769         assert base_members is not None
770
771         # The value of member 'discriminator' must name a non-optional
772         # member of the base struct.
773         check_name(info, "Discriminator of flat union '%s'" % name,
774                    discriminator)
775         discriminator_type = base_members.get(discriminator)
776         if not discriminator_type:
777             raise QAPISemError(info,
778                                "Discriminator '%s' is not a member of base "
779                                "struct '%s'"
780                                % (discriminator, base))
781         enum_define = enum_types.get(discriminator_type)
782         allow_metas = ['struct']
783         # Do not allow string discriminator
784         if not enum_define:
785             raise QAPISemError(info,
786                                "Discriminator '%s' must be of enumeration "
787                                "type" % discriminator)
788
789     # Check every branch; don't allow an empty union
790     if len(members) == 0:
791         raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
792     for (key, value) in members.items():
793         check_name(info, "Member of union '%s'" % name, key)
794
795         # Each value must name a known type
796         check_type(info, "Member '%s' of union '%s'" % (key, name),
797                    value, allow_array=not base, allow_metas=allow_metas)
798
799         # If the discriminator names an enum type, then all members
800         # of 'data' must also be members of the enum type.
801         if enum_define:
802             if key not in enum_define['data']:
803                 raise QAPISemError(info,
804                                    "Discriminator value '%s' is not found in "
805                                    "enum '%s'"
806                                    % (key, enum_define['enum']))
807
808
809 def check_alternate(expr, info):
810     name = expr['alternate']
811     members = expr['data']
812     types_seen = {}
813
814     # Check every branch; require at least two branches
815     if len(members) < 2:
816         raise QAPISemError(info,
817                            "Alternate '%s' should have at least two branches "
818                            "in 'data'" % name)
819     for (key, value) in members.items():
820         check_name(info, "Member of alternate '%s'" % name, key)
821
822         # Ensure alternates have no type conflicts.
823         check_type(info, "Member '%s' of alternate '%s'" % (key, name),
824                    value,
825                    allow_metas=['built-in', 'union', 'struct', 'enum'])
826         qtype = find_alternate_member_qtype(value)
827         if not qtype:
828             raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
829                                "type '%s'" % (name, key, value))
830         conflicting = set([qtype])
831         if qtype == 'QTYPE_QSTRING':
832             enum_expr = enum_types.get(value)
833             if enum_expr:
834                 for v in enum_expr['data']:
835                     if v in ['on', 'off']:
836                         conflicting.add('QTYPE_QBOOL')
837                     if re.match(r'[-+0-9.]', v): # lazy, could be tightened
838                         conflicting.add('QTYPE_QNUM')
839             else:
840                 conflicting.add('QTYPE_QNUM')
841                 conflicting.add('QTYPE_QBOOL')
842         for qt in conflicting:
843             if qt in types_seen:
844                 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
845                                    "be distinguished from member '%s'"
846                                    % (name, key, types_seen[qt]))
847             types_seen[qt] = key
848
849
850 def check_enum(expr, info):
851     name = expr['enum']
852     members = expr.get('data')
853     prefix = expr.get('prefix')
854
855     if not isinstance(members, list):
856         raise QAPISemError(info,
857                            "Enum '%s' requires an array for 'data'" % name)
858     if prefix is not None and not isinstance(prefix, str):
859         raise QAPISemError(info,
860                            "Enum '%s' requires a string for 'prefix'" % name)
861     for member in members:
862         check_name(info, "Member of enum '%s'" % name, member,
863                    enum_member=True)
864
865
866 def check_struct(expr, info):
867     name = expr['struct']
868     members = expr['data']
869
870     check_type(info, "'data' for struct '%s'" % name, members,
871                allow_dict=True, allow_optional=True)
872     check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
873                allow_metas=['struct'])
874
875
876 def check_keys(expr_elem, meta, required, optional=[]):
877     expr = expr_elem['expr']
878     info = expr_elem['info']
879     name = expr[meta]
880     if not isinstance(name, str):
881         raise QAPISemError(info, "'%s' key must have a string value" % meta)
882     required = required + [meta]
883     for (key, value) in expr.items():
884         if key not in required and key not in optional:
885             raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
886                                % (key, meta, name))
887         if (key == 'gen' or key == 'success-response') and value is not False:
888             raise QAPISemError(info,
889                                "'%s' of %s '%s' should only use false value"
890                                % (key, meta, name))
891         if (key == 'boxed' or key == 'allow-oob' or
892             key == 'allow-preconfig') and value is not True:
893             raise QAPISemError(info,
894                                "'%s' of %s '%s' should only use true value"
895                                % (key, meta, name))
896         if key == 'if':
897             check_if(expr, info)
898     for key in required:
899         if key not in expr:
900             raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
901                                % (key, meta, name))
902
903
904 def check_exprs(exprs):
905     global all_names
906
907     # Populate name table with names of built-in types
908     for builtin in builtin_types.keys():
909         all_names[builtin] = 'built-in'
910
911     # Learn the types and check for valid expression keys
912     for expr_elem in exprs:
913         expr = expr_elem['expr']
914         info = expr_elem['info']
915         doc = expr_elem.get('doc')
916
917         if 'include' in expr:
918             continue
919
920         if not doc and doc_required:
921             raise QAPISemError(info,
922                                "Expression missing documentation comment")
923
924         if 'enum' in expr:
925             meta = 'enum'
926             check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
927             enum_types[expr[meta]] = expr
928         elif 'union' in expr:
929             meta = 'union'
930             check_keys(expr_elem, 'union', ['data'],
931                        ['base', 'discriminator', 'if'])
932             union_types[expr[meta]] = expr
933         elif 'alternate' in expr:
934             meta = 'alternate'
935             check_keys(expr_elem, 'alternate', ['data'], ['if'])
936         elif 'struct' in expr:
937             meta = 'struct'
938             check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
939             struct_types[expr[meta]] = expr
940         elif 'command' in expr:
941             meta = 'command'
942             check_keys(expr_elem, 'command', [],
943                        ['data', 'returns', 'gen', 'success-response',
944                         'boxed', 'allow-oob', 'allow-preconfig', 'if'])
945         elif 'event' in expr:
946             meta = 'event'
947             check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
948         else:
949             raise QAPISemError(expr_elem['info'],
950                                "Expression is missing metatype")
951         name = expr[meta]
952         add_name(name, info, meta)
953         if doc and doc.symbol != name:
954             raise QAPISemError(info, "Definition of '%s' follows documentation"
955                                " for '%s'" % (name, doc.symbol))
956
957     # Try again for hidden UnionKind enum
958     for expr_elem in exprs:
959         expr = expr_elem['expr']
960
961         if 'include' in expr:
962             continue
963         if 'union' in expr and not discriminator_find_enum_define(expr):
964             name = '%sKind' % expr['union']
965         elif 'alternate' in expr:
966             name = '%sKind' % expr['alternate']
967         else:
968             continue
969         enum_types[name] = {'enum': name}
970         add_name(name, info, 'enum', implicit=True)
971
972     # Validate that exprs make sense
973     for expr_elem in exprs:
974         expr = expr_elem['expr']
975         info = expr_elem['info']
976         doc = expr_elem.get('doc')
977
978         if 'include' in expr:
979             continue
980         if 'enum' in expr:
981             check_enum(expr, info)
982         elif 'union' in expr:
983             check_union(expr, info)
984         elif 'alternate' in expr:
985             check_alternate(expr, info)
986         elif 'struct' in expr:
987             check_struct(expr, info)
988         elif 'command' in expr:
989             check_command(expr, info)
990         elif 'event' in expr:
991             check_event(expr, info)
992         else:
993             assert False, 'unexpected meta type'
994
995         if doc:
996             doc.check_expr(expr)
997
998     return exprs
999
1000
1001 #
1002 # Schema compiler frontend
1003 #
1004
1005 def listify_cond(ifcond):
1006     if not ifcond:
1007         return []
1008     if not isinstance(ifcond, list):
1009         return [ifcond]
1010     return ifcond
1011
1012
1013 class QAPISchemaEntity(object):
1014     def __init__(self, name, info, doc, ifcond=None):
1015         assert name is None or isinstance(name, str)
1016         self.name = name
1017         self.module = None
1018         # For explicitly defined entities, info points to the (explicit)
1019         # definition.  For builtins (and their arrays), info is None.
1020         # For implicitly defined entities, info points to a place that
1021         # triggered the implicit definition (there may be more than one
1022         # such place).
1023         self.info = info
1024         self.doc = doc
1025         self._ifcond = ifcond  # self.ifcond is set only after .check()
1026
1027     def c_name(self):
1028         return c_name(self.name)
1029
1030     def check(self, schema):
1031         if isinstance(self._ifcond, QAPISchemaType):
1032             # inherit the condition from a type
1033             typ = self._ifcond
1034             typ.check(schema)
1035             self.ifcond = typ.ifcond
1036         else:
1037             self.ifcond = listify_cond(self._ifcond)
1038
1039     def is_implicit(self):
1040         return not self.info
1041
1042     def visit(self, visitor):
1043         pass
1044
1045
1046 class QAPISchemaVisitor(object):
1047     def visit_begin(self, schema):
1048         pass
1049
1050     def visit_end(self):
1051         pass
1052
1053     def visit_module(self, fname):
1054         pass
1055
1056     def visit_needed(self, entity):
1057         # Default to visiting everything
1058         return True
1059
1060     def visit_include(self, fname, info):
1061         pass
1062
1063     def visit_builtin_type(self, name, info, json_type):
1064         pass
1065
1066     def visit_enum_type(self, name, info, ifcond, values, prefix):
1067         pass
1068
1069     def visit_array_type(self, name, info, ifcond, element_type):
1070         pass
1071
1072     def visit_object_type(self, name, info, ifcond, base, members, variants):
1073         pass
1074
1075     def visit_object_type_flat(self, name, info, ifcond, members, variants):
1076         pass
1077
1078     def visit_alternate_type(self, name, info, ifcond, variants):
1079         pass
1080
1081     def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1082                       success_response, boxed, allow_oob, allow_preconfig):
1083         pass
1084
1085     def visit_event(self, name, info, ifcond, arg_type, boxed):
1086         pass
1087
1088
1089 class QAPISchemaInclude(QAPISchemaEntity):
1090
1091     def __init__(self, fname, info):
1092         QAPISchemaEntity.__init__(self, None, info, None)
1093         self.fname = fname
1094
1095     def visit(self, visitor):
1096         visitor.visit_include(self.fname, self.info)
1097
1098
1099 class QAPISchemaType(QAPISchemaEntity):
1100     # Return the C type for common use.
1101     # For the types we commonly box, this is a pointer type.
1102     def c_type(self):
1103         pass
1104
1105     # Return the C type to be used in a parameter list.
1106     def c_param_type(self):
1107         return self.c_type()
1108
1109     # Return the C type to be used where we suppress boxing.
1110     def c_unboxed_type(self):
1111         return self.c_type()
1112
1113     def json_type(self):
1114         pass
1115
1116     def alternate_qtype(self):
1117         json2qtype = {
1118             'null':    'QTYPE_QNULL',
1119             'string':  'QTYPE_QSTRING',
1120             'number':  'QTYPE_QNUM',
1121             'int':     'QTYPE_QNUM',
1122             'boolean': 'QTYPE_QBOOL',
1123             'object':  'QTYPE_QDICT'
1124         }
1125         return json2qtype.get(self.json_type())
1126
1127     def doc_type(self):
1128         if self.is_implicit():
1129             return None
1130         return self.name
1131
1132
1133 class QAPISchemaBuiltinType(QAPISchemaType):
1134     def __init__(self, name, json_type, c_type):
1135         QAPISchemaType.__init__(self, name, None, None)
1136         assert not c_type or isinstance(c_type, str)
1137         assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1138                              'value')
1139         self._json_type_name = json_type
1140         self._c_type_name = c_type
1141
1142     def c_name(self):
1143         return self.name
1144
1145     def c_type(self):
1146         return self._c_type_name
1147
1148     def c_param_type(self):
1149         if self.name == 'str':
1150             return 'const ' + self._c_type_name
1151         return self._c_type_name
1152
1153     def json_type(self):
1154         return self._json_type_name
1155
1156     def doc_type(self):
1157         return self.json_type()
1158
1159     def visit(self, visitor):
1160         visitor.visit_builtin_type(self.name, self.info, self.json_type())
1161
1162
1163 class QAPISchemaEnumType(QAPISchemaType):
1164     def __init__(self, name, info, doc, ifcond, values, prefix):
1165         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1166         for v in values:
1167             assert isinstance(v, QAPISchemaMember)
1168             v.set_owner(name)
1169         assert prefix is None or isinstance(prefix, str)
1170         self.values = values
1171         self.prefix = prefix
1172
1173     def check(self, schema):
1174         QAPISchemaType.check(self, schema)
1175         seen = {}
1176         for v in self.values:
1177             v.check_clash(self.info, seen)
1178             if self.doc:
1179                 self.doc.connect_member(v)
1180
1181     def is_implicit(self):
1182         # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1183         return self.name.endswith('Kind') or self.name == 'QType'
1184
1185     def c_type(self):
1186         return c_name(self.name)
1187
1188     def member_names(self):
1189         return [v.name for v in self.values]
1190
1191     def json_type(self):
1192         return 'string'
1193
1194     def visit(self, visitor):
1195         visitor.visit_enum_type(self.name, self.info, self.ifcond,
1196                                 self.member_names(), self.prefix)
1197
1198
1199 class QAPISchemaArrayType(QAPISchemaType):
1200     def __init__(self, name, info, element_type):
1201         QAPISchemaType.__init__(self, name, info, None, None)
1202         assert isinstance(element_type, str)
1203         self._element_type_name = element_type
1204         self.element_type = None
1205
1206     def check(self, schema):
1207         QAPISchemaType.check(self, schema)
1208         self.element_type = schema.lookup_type(self._element_type_name)
1209         assert self.element_type
1210         self.element_type.check(schema)
1211         self.ifcond = self.element_type.ifcond
1212
1213     def is_implicit(self):
1214         return True
1215
1216     def c_type(self):
1217         return c_name(self.name) + pointer_suffix
1218
1219     def json_type(self):
1220         return 'array'
1221
1222     def doc_type(self):
1223         elt_doc_type = self.element_type.doc_type()
1224         if not elt_doc_type:
1225             return None
1226         return 'array of ' + elt_doc_type
1227
1228     def visit(self, visitor):
1229         visitor.visit_array_type(self.name, self.info, self.ifcond,
1230                                  self.element_type)
1231
1232
1233 class QAPISchemaObjectType(QAPISchemaType):
1234     def __init__(self, name, info, doc, ifcond,
1235                  base, local_members, variants):
1236         # struct has local_members, optional base, and no variants
1237         # flat union has base, variants, and no local_members
1238         # simple union has local_members, variants, and no base
1239         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1240         assert base is None or isinstance(base, str)
1241         for m in local_members:
1242             assert isinstance(m, QAPISchemaObjectTypeMember)
1243             m.set_owner(name)
1244         if variants is not None:
1245             assert isinstance(variants, QAPISchemaObjectTypeVariants)
1246             variants.set_owner(name)
1247         self._base_name = base
1248         self.base = None
1249         self.local_members = local_members
1250         self.variants = variants
1251         self.members = None
1252
1253     def check(self, schema):
1254         QAPISchemaType.check(self, schema)
1255         if self.members is False:               # check for cycles
1256             raise QAPISemError(self.info,
1257                                "Object %s contains itself" % self.name)
1258         if self.members:
1259             return
1260         self.members = False                    # mark as being checked
1261         seen = OrderedDict()
1262         if self._base_name:
1263             self.base = schema.lookup_type(self._base_name)
1264             assert isinstance(self.base, QAPISchemaObjectType)
1265             self.base.check(schema)
1266             self.base.check_clash(self.info, seen)
1267         for m in self.local_members:
1268             m.check(schema)
1269             m.check_clash(self.info, seen)
1270             if self.doc:
1271                 self.doc.connect_member(m)
1272         self.members = seen.values()
1273         if self.variants:
1274             self.variants.check(schema, seen)
1275             assert self.variants.tag_member in self.members
1276             self.variants.check_clash(self.info, seen)
1277         if self.doc:
1278             self.doc.check()
1279
1280     # Check that the members of this type do not cause duplicate JSON members,
1281     # and update seen to track the members seen so far. Report any errors
1282     # on behalf of info, which is not necessarily self.info
1283     def check_clash(self, info, seen):
1284         assert not self.variants       # not implemented
1285         for m in self.members:
1286             m.check_clash(info, seen)
1287
1288     def is_implicit(self):
1289         # See QAPISchema._make_implicit_object_type(), as well as
1290         # _def_predefineds()
1291         return self.name.startswith('q_')
1292
1293     def is_empty(self):
1294         assert self.members is not None
1295         return not self.members and not self.variants
1296
1297     def c_name(self):
1298         assert self.name != 'q_empty'
1299         return QAPISchemaType.c_name(self)
1300
1301     def c_type(self):
1302         assert not self.is_implicit()
1303         return c_name(self.name) + pointer_suffix
1304
1305     def c_unboxed_type(self):
1306         return c_name(self.name)
1307
1308     def json_type(self):
1309         return 'object'
1310
1311     def visit(self, visitor):
1312         visitor.visit_object_type(self.name, self.info, self.ifcond,
1313                                   self.base, self.local_members, self.variants)
1314         visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1315                                        self.members, self.variants)
1316
1317
1318 class QAPISchemaMember(object):
1319     role = 'member'
1320
1321     def __init__(self, name):
1322         assert isinstance(name, str)
1323         self.name = name
1324         self.owner = None
1325
1326     def set_owner(self, name):
1327         assert not self.owner
1328         self.owner = name
1329
1330     def check_clash(self, info, seen):
1331         cname = c_name(self.name)
1332         if cname.lower() != cname and self.owner not in name_case_whitelist:
1333             raise QAPISemError(info,
1334                                "%s should not use uppercase" % self.describe())
1335         if cname in seen:
1336             raise QAPISemError(info, "%s collides with %s" %
1337                                (self.describe(), seen[cname].describe()))
1338         seen[cname] = self
1339
1340     def _pretty_owner(self):
1341         owner = self.owner
1342         if owner.startswith('q_obj_'):
1343             # See QAPISchema._make_implicit_object_type() - reverse the
1344             # mapping there to create a nice human-readable description
1345             owner = owner[6:]
1346             if owner.endswith('-arg'):
1347                 return '(parameter of %s)' % owner[:-4]
1348             elif owner.endswith('-base'):
1349                 return '(base of %s)' % owner[:-5]
1350             else:
1351                 assert owner.endswith('-wrapper')
1352                 # Unreachable and not implemented
1353                 assert False
1354         if owner.endswith('Kind'):
1355             # See QAPISchema._make_implicit_enum_type()
1356             return '(branch of %s)' % owner[:-4]
1357         return '(%s of %s)' % (self.role, owner)
1358
1359     def describe(self):
1360         return "'%s' %s" % (self.name, self._pretty_owner())
1361
1362
1363 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1364     def __init__(self, name, typ, optional):
1365         QAPISchemaMember.__init__(self, name)
1366         assert isinstance(typ, str)
1367         assert isinstance(optional, bool)
1368         self._type_name = typ
1369         self.type = None
1370         self.optional = optional
1371
1372     def check(self, schema):
1373         assert self.owner
1374         self.type = schema.lookup_type(self._type_name)
1375         assert self.type
1376
1377
1378 class QAPISchemaObjectTypeVariants(object):
1379     def __init__(self, tag_name, tag_member, variants):
1380         # Flat unions pass tag_name but not tag_member.
1381         # Simple unions and alternates pass tag_member but not tag_name.
1382         # After check(), tag_member is always set, and tag_name remains
1383         # a reliable witness of being used by a flat union.
1384         assert bool(tag_member) != bool(tag_name)
1385         assert (isinstance(tag_name, str) or
1386                 isinstance(tag_member, QAPISchemaObjectTypeMember))
1387         assert len(variants) > 0
1388         for v in variants:
1389             assert isinstance(v, QAPISchemaObjectTypeVariant)
1390         self._tag_name = tag_name
1391         self.tag_member = tag_member
1392         self.variants = variants
1393
1394     def set_owner(self, name):
1395         for v in self.variants:
1396             v.set_owner(name)
1397
1398     def check(self, schema, seen):
1399         if not self.tag_member:    # flat union
1400             self.tag_member = seen[c_name(self._tag_name)]
1401             assert self._tag_name == self.tag_member.name
1402         assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1403         if self._tag_name:    # flat union
1404             # branches that are not explicitly covered get an empty type
1405             cases = set([v.name for v in self.variants])
1406             for val in self.tag_member.type.values:
1407                 if val.name not in cases:
1408                     v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
1409                     v.set_owner(self.tag_member.owner)
1410                     self.variants.append(v)
1411         for v in self.variants:
1412             v.check(schema)
1413             # Union names must match enum values; alternate names are
1414             # checked separately. Use 'seen' to tell the two apart.
1415             if seen:
1416                 assert v.name in self.tag_member.type.member_names()
1417                 assert isinstance(v.type, QAPISchemaObjectType)
1418                 v.type.check(schema)
1419
1420     def check_clash(self, info, seen):
1421         for v in self.variants:
1422             # Reset seen map for each variant, since qapi names from one
1423             # branch do not affect another branch
1424             assert isinstance(v.type, QAPISchemaObjectType)
1425             v.type.check_clash(info, dict(seen))
1426
1427
1428 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1429     role = 'branch'
1430
1431     def __init__(self, name, typ):
1432         QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1433
1434
1435 class QAPISchemaAlternateType(QAPISchemaType):
1436     def __init__(self, name, info, doc, ifcond, variants):
1437         QAPISchemaType.__init__(self, name, info, doc, ifcond)
1438         assert isinstance(variants, QAPISchemaObjectTypeVariants)
1439         assert variants.tag_member
1440         variants.set_owner(name)
1441         variants.tag_member.set_owner(self.name)
1442         self.variants = variants
1443
1444     def check(self, schema):
1445         QAPISchemaType.check(self, schema)
1446         self.variants.tag_member.check(schema)
1447         # Not calling self.variants.check_clash(), because there's nothing
1448         # to clash with
1449         self.variants.check(schema, {})
1450         # Alternate branch names have no relation to the tag enum values;
1451         # so we have to check for potential name collisions ourselves.
1452         seen = {}
1453         for v in self.variants.variants:
1454             v.check_clash(self.info, seen)
1455             if self.doc:
1456                 self.doc.connect_member(v)
1457         if self.doc:
1458             self.doc.check()
1459
1460     def c_type(self):
1461         return c_name(self.name) + pointer_suffix
1462
1463     def json_type(self):
1464         return 'value'
1465
1466     def visit(self, visitor):
1467         visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1468                                      self.variants)
1469
1470     def is_empty(self):
1471         return False
1472
1473
1474 class QAPISchemaCommand(QAPISchemaEntity):
1475     def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1476                  gen, success_response, boxed, allow_oob, allow_preconfig):
1477         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1478         assert not arg_type or isinstance(arg_type, str)
1479         assert not ret_type or isinstance(ret_type, str)
1480         self._arg_type_name = arg_type
1481         self.arg_type = None
1482         self._ret_type_name = ret_type
1483         self.ret_type = None
1484         self.gen = gen
1485         self.success_response = success_response
1486         self.boxed = boxed
1487         self.allow_oob = allow_oob
1488         self.allow_preconfig = allow_preconfig
1489
1490     def check(self, schema):
1491         QAPISchemaEntity.check(self, schema)
1492         if self._arg_type_name:
1493             self.arg_type = schema.lookup_type(self._arg_type_name)
1494             assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1495                     isinstance(self.arg_type, QAPISchemaAlternateType))
1496             self.arg_type.check(schema)
1497             if self.boxed:
1498                 if self.arg_type.is_empty():
1499                     raise QAPISemError(self.info,
1500                                        "Cannot use 'boxed' with empty type")
1501             else:
1502                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1503                 assert not self.arg_type.variants
1504         elif self.boxed:
1505             raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1506         if self._ret_type_name:
1507             self.ret_type = schema.lookup_type(self._ret_type_name)
1508             assert isinstance(self.ret_type, QAPISchemaType)
1509
1510     def visit(self, visitor):
1511         visitor.visit_command(self.name, self.info, self.ifcond,
1512                               self.arg_type, self.ret_type,
1513                               self.gen, self.success_response,
1514                               self.boxed, self.allow_oob,
1515                               self.allow_preconfig)
1516
1517
1518 class QAPISchemaEvent(QAPISchemaEntity):
1519     def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1520         QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1521         assert not arg_type or isinstance(arg_type, str)
1522         self._arg_type_name = arg_type
1523         self.arg_type = None
1524         self.boxed = boxed
1525
1526     def check(self, schema):
1527         QAPISchemaEntity.check(self, schema)
1528         if self._arg_type_name:
1529             self.arg_type = schema.lookup_type(self._arg_type_name)
1530             assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1531                     isinstance(self.arg_type, QAPISchemaAlternateType))
1532             self.arg_type.check(schema)
1533             if self.boxed:
1534                 if self.arg_type.is_empty():
1535                     raise QAPISemError(self.info,
1536                                        "Cannot use 'boxed' with empty type")
1537             else:
1538                 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1539                 assert not self.arg_type.variants
1540         elif self.boxed:
1541             raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1542
1543     def visit(self, visitor):
1544         visitor.visit_event(self.name, self.info, self.ifcond,
1545                             self.arg_type, self.boxed)
1546
1547
1548 class QAPISchema(object):
1549     def __init__(self, fname):
1550         self._fname = fname
1551         if sys.version_info[0] >= 3:
1552             f = open(fname, 'r', encoding='utf-8')
1553         else:
1554             f = open(fname, 'r')
1555         parser = QAPISchemaParser(f)
1556         exprs = check_exprs(parser.exprs)
1557         self.docs = parser.docs
1558         self._entity_list = []
1559         self._entity_dict = {}
1560         self._predefining = True
1561         self._def_predefineds()
1562         self._predefining = False
1563         self._def_exprs(exprs)
1564         self.check()
1565
1566     def _def_entity(self, ent):
1567         # Only the predefined types are allowed to not have info
1568         assert ent.info or self._predefining
1569         assert ent.name is None or ent.name not in self._entity_dict
1570         self._entity_list.append(ent)
1571         if ent.name is not None:
1572             self._entity_dict[ent.name] = ent
1573         if ent.info:
1574             ent.module = os.path.relpath(ent.info['file'],
1575                                          os.path.dirname(self._fname))
1576
1577     def lookup_entity(self, name, typ=None):
1578         ent = self._entity_dict.get(name)
1579         if typ and not isinstance(ent, typ):
1580             return None
1581         return ent
1582
1583     def lookup_type(self, name):
1584         return self.lookup_entity(name, QAPISchemaType)
1585
1586     def _def_include(self, expr, info, doc):
1587         include = expr['include']
1588         assert doc is None
1589         main_info = info
1590         while main_info['parent']:
1591             main_info = main_info['parent']
1592         fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1593         self._def_entity(QAPISchemaInclude(fname, info))
1594
1595     def _def_builtin_type(self, name, json_type, c_type):
1596         self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1597         # Instantiating only the arrays that are actually used would
1598         # be nice, but we can't as long as their generated code
1599         # (qapi-builtin-types.[ch]) may be shared by some other
1600         # schema.
1601         self._make_array_type(name, None)
1602
1603     def _def_predefineds(self):
1604         for t in [('str',    'string',  'char' + pointer_suffix),
1605                   ('number', 'number',  'double'),
1606                   ('int',    'int',     'int64_t'),
1607                   ('int8',   'int',     'int8_t'),
1608                   ('int16',  'int',     'int16_t'),
1609                   ('int32',  'int',     'int32_t'),
1610                   ('int64',  'int',     'int64_t'),
1611                   ('uint8',  'int',     'uint8_t'),
1612                   ('uint16', 'int',     'uint16_t'),
1613                   ('uint32', 'int',     'uint32_t'),
1614                   ('uint64', 'int',     'uint64_t'),
1615                   ('size',   'int',     'uint64_t'),
1616                   ('bool',   'boolean', 'bool'),
1617                   ('any',    'value',   'QObject' + pointer_suffix),
1618                   ('null',   'null',    'QNull' + pointer_suffix)]:
1619             self._def_builtin_type(*t)
1620         self.the_empty_object_type = QAPISchemaObjectType(
1621             'q_empty', None, None, None, None, [], None)
1622         self._def_entity(self.the_empty_object_type)
1623         qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1624                                                 'qstring', 'qdict', 'qlist',
1625                                                 'qbool'])
1626         self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1627                                             qtype_values, 'QTYPE'))
1628
1629     def _make_enum_members(self, values):
1630         return [QAPISchemaMember(v) for v in values]
1631
1632     def _make_implicit_enum_type(self, name, info, ifcond, values):
1633         # See also QAPISchemaObjectTypeMember._pretty_owner()
1634         name = name + 'Kind'   # Use namespace reserved by add_name()
1635         self._def_entity(QAPISchemaEnumType(
1636             name, info, None, ifcond, self._make_enum_members(values), None))
1637         return name
1638
1639     def _make_array_type(self, element_type, info):
1640         name = element_type + 'List'   # Use namespace reserved by add_name()
1641         if not self.lookup_type(name):
1642             self._def_entity(QAPISchemaArrayType(name, info, element_type))
1643         return name
1644
1645     def _make_implicit_object_type(self, name, info, doc, ifcond,
1646                                    role, members):
1647         if not members:
1648             return None
1649         # See also QAPISchemaObjectTypeMember._pretty_owner()
1650         name = 'q_obj_%s-%s' % (name, role)
1651         typ = self.lookup_entity(name, QAPISchemaObjectType)
1652         if typ:
1653             # The implicit object type has multiple users.  This can
1654             # happen only for simple unions' implicit wrapper types.
1655             # Its ifcond should be the disjunction of its user's
1656             # ifconds.  Not implemented.  Instead, we always pass the
1657             # wrapped type's ifcond, which is trivially the same for all
1658             # users.  It's also necessary for the wrapper to compile.
1659             # But it's not tight: the disjunction need not imply it.  We
1660             # may end up compiling useless wrapper types.
1661             # TODO kill simple unions or implement the disjunction
1662             assert ifcond == typ._ifcond # pylint: disable=protected-access
1663         else:
1664             self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1665                                                   None, members, None))
1666         return name
1667
1668     def _def_enum_type(self, expr, info, doc):
1669         name = expr['enum']
1670         data = expr['data']
1671         prefix = expr.get('prefix')
1672         ifcond = expr.get('if')
1673         self._def_entity(QAPISchemaEnumType(
1674             name, info, doc, ifcond,
1675             self._make_enum_members(data), prefix))
1676
1677     def _make_member(self, name, typ, info):
1678         optional = False
1679         if name.startswith('*'):
1680             name = name[1:]
1681             optional = True
1682         if isinstance(typ, list):
1683             assert len(typ) == 1
1684             typ = self._make_array_type(typ[0], info)
1685         return QAPISchemaObjectTypeMember(name, typ, optional)
1686
1687     def _make_members(self, data, info):
1688         return [self._make_member(key, value, info)
1689                 for (key, value) in data.items()]
1690
1691     def _def_struct_type(self, expr, info, doc):
1692         name = expr['struct']
1693         base = expr.get('base')
1694         data = expr['data']
1695         ifcond = expr.get('if')
1696         self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1697                                               self._make_members(data, info),
1698                                               None))
1699
1700     def _make_variant(self, case, typ):
1701         return QAPISchemaObjectTypeVariant(case, typ)
1702
1703     def _make_simple_variant(self, case, typ, info):
1704         if isinstance(typ, list):
1705             assert len(typ) == 1
1706             typ = self._make_array_type(typ[0], info)
1707         typ = self._make_implicit_object_type(
1708             typ, info, None, self.lookup_type(typ),
1709             'wrapper', [self._make_member('data', typ, info)])
1710         return QAPISchemaObjectTypeVariant(case, typ)
1711
1712     def _def_union_type(self, expr, info, doc):
1713         name = expr['union']
1714         data = expr['data']
1715         base = expr.get('base')
1716         ifcond = expr.get('if')
1717         tag_name = expr.get('discriminator')
1718         tag_member = None
1719         if isinstance(base, dict):
1720             base = self._make_implicit_object_type(
1721                 name, info, doc, ifcond,
1722                 'base', self._make_members(base, info))
1723         if tag_name:
1724             variants = [self._make_variant(key, value)
1725                         for (key, value) in data.items()]
1726             members = []
1727         else:
1728             variants = [self._make_simple_variant(key, value, info)
1729                         for (key, value) in data.items()]
1730             typ = self._make_implicit_enum_type(name, info, ifcond,
1731                                                 [v.name for v in variants])
1732             tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1733             members = [tag_member]
1734         self._def_entity(
1735             QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1736                                  QAPISchemaObjectTypeVariants(tag_name,
1737                                                               tag_member,
1738                                                               variants)))
1739
1740     def _def_alternate_type(self, expr, info, doc):
1741         name = expr['alternate']
1742         data = expr['data']
1743         ifcond = expr.get('if')
1744         variants = [self._make_variant(key, value)
1745                     for (key, value) in data.items()]
1746         tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1747         self._def_entity(
1748             QAPISchemaAlternateType(name, info, doc, ifcond,
1749                                     QAPISchemaObjectTypeVariants(None,
1750                                                                  tag_member,
1751                                                                  variants)))
1752
1753     def _def_command(self, expr, info, doc):
1754         name = expr['command']
1755         data = expr.get('data')
1756         rets = expr.get('returns')
1757         gen = expr.get('gen', True)
1758         success_response = expr.get('success-response', True)
1759         boxed = expr.get('boxed', False)
1760         allow_oob = expr.get('allow-oob', False)
1761         allow_preconfig = expr.get('allow-preconfig', False)
1762         ifcond = expr.get('if')
1763         if isinstance(data, OrderedDict):
1764             data = self._make_implicit_object_type(
1765                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1766         if isinstance(rets, list):
1767             assert len(rets) == 1
1768             rets = self._make_array_type(rets[0], info)
1769         self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1770                                            gen, success_response,
1771                                            boxed, allow_oob, allow_preconfig))
1772
1773     def _def_event(self, expr, info, doc):
1774         name = expr['event']
1775         data = expr.get('data')
1776         boxed = expr.get('boxed', False)
1777         ifcond = expr.get('if')
1778         if isinstance(data, OrderedDict):
1779             data = self._make_implicit_object_type(
1780                 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1781         self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1782
1783     def _def_exprs(self, exprs):
1784         for expr_elem in exprs:
1785             expr = expr_elem['expr']
1786             info = expr_elem['info']
1787             doc = expr_elem.get('doc')
1788             if 'enum' in expr:
1789                 self._def_enum_type(expr, info, doc)
1790             elif 'struct' in expr:
1791                 self._def_struct_type(expr, info, doc)
1792             elif 'union' in expr:
1793                 self._def_union_type(expr, info, doc)
1794             elif 'alternate' in expr:
1795                 self._def_alternate_type(expr, info, doc)
1796             elif 'command' in expr:
1797                 self._def_command(expr, info, doc)
1798             elif 'event' in expr:
1799                 self._def_event(expr, info, doc)
1800             elif 'include' in expr:
1801                 self._def_include(expr, info, doc)
1802             else:
1803                 assert False
1804
1805     def check(self):
1806         for ent in self._entity_list:
1807             ent.check(self)
1808
1809     def visit(self, visitor):
1810         visitor.visit_begin(self)
1811         module = None
1812         for entity in self._entity_list:
1813             if visitor.visit_needed(entity):
1814                 if entity.module != module:
1815                     module = entity.module
1816                     visitor.visit_module(module)
1817                 entity.visit(visitor)
1818         visitor.visit_end()
1819
1820
1821 #
1822 # Code generation helpers
1823 #
1824
1825 def camel_case(name):
1826     new_name = ''
1827     first = True
1828     for ch in name:
1829         if ch in ['_', '-']:
1830             first = True
1831         elif first:
1832             new_name += ch.upper()
1833             first = False
1834         else:
1835             new_name += ch.lower()
1836     return new_name
1837
1838
1839 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1840 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1841 # ENUM24_Name -> ENUM24_NAME
1842 def camel_to_upper(value):
1843     c_fun_str = c_name(value, False)
1844     if value.isupper():
1845         return c_fun_str
1846
1847     new_name = ''
1848     l = len(c_fun_str)
1849     for i in range(l):
1850         c = c_fun_str[i]
1851         # When c is upper and no '_' appears before, do more checks
1852         if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1853             if i < l - 1 and c_fun_str[i + 1].islower():
1854                 new_name += '_'
1855             elif c_fun_str[i - 1].isdigit():
1856                 new_name += '_'
1857         new_name += c
1858     return new_name.lstrip('_').upper()
1859
1860
1861 def c_enum_const(type_name, const_name, prefix=None):
1862     if prefix is not None:
1863         type_name = prefix
1864     return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1865
1866 if hasattr(str, 'maketrans'):
1867     c_name_trans = str.maketrans('.-', '__')
1868 else:
1869     c_name_trans = string.maketrans('.-', '__')
1870
1871
1872 # Map @name to a valid C identifier.
1873 # If @protect, avoid returning certain ticklish identifiers (like
1874 # C keywords) by prepending 'q_'.
1875 #
1876 # Used for converting 'name' from a 'name':'type' qapi definition
1877 # into a generated struct member, as well as converting type names
1878 # into substrings of a generated C function name.
1879 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1880 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1881 def c_name(name, protect=True):
1882     # ANSI X3J11/88-090, 3.1.1
1883     c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1884                      'default', 'do', 'double', 'else', 'enum', 'extern',
1885                      'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1886                      'return', 'short', 'signed', 'sizeof', 'static',
1887                      'struct', 'switch', 'typedef', 'union', 'unsigned',
1888                      'void', 'volatile', 'while'])
1889     # ISO/IEC 9899:1999, 6.4.1
1890     c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1891     # ISO/IEC 9899:2011, 6.4.1
1892     c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1893                      '_Noreturn', '_Static_assert', '_Thread_local'])
1894     # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1895     # excluding _.*
1896     gcc_words = set(['asm', 'typeof'])
1897     # C++ ISO/IEC 14882:2003 2.11
1898     cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1899                      'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1900                      'namespace', 'new', 'operator', 'private', 'protected',
1901                      'public', 'reinterpret_cast', 'static_cast', 'template',
1902                      'this', 'throw', 'true', 'try', 'typeid', 'typename',
1903                      'using', 'virtual', 'wchar_t',
1904                      # alternative representations
1905                      'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1906                      'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1907     # namespace pollution:
1908     polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1909     name = name.translate(c_name_trans)
1910     if protect and (name in c89_words | c99_words | c11_words | gcc_words
1911                     | cpp_words | polluted_words):
1912         return 'q_' + name
1913     return name
1914
1915 eatspace = '\033EATSPACE.'
1916 pointer_suffix = ' *' + eatspace
1917
1918
1919 def genindent(count):
1920     ret = ''
1921     for _ in range(count):
1922         ret += ' '
1923     return ret
1924
1925 indent_level = 0
1926
1927
1928 def push_indent(indent_amount=4):
1929     global indent_level
1930     indent_level += indent_amount
1931
1932
1933 def pop_indent(indent_amount=4):
1934     global indent_level
1935     indent_level -= indent_amount
1936
1937
1938 # Generate @code with @kwds interpolated.
1939 # Obey indent_level, and strip eatspace.
1940 def cgen(code, **kwds):
1941     raw = code % kwds
1942     if indent_level:
1943         indent = genindent(indent_level)
1944         # re.subn() lacks flags support before Python 2.7, use re.compile()
1945         raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
1946                       indent, raw)
1947         raw = raw[0]
1948     return re.sub(re.escape(eatspace) + r' *', '', raw)
1949
1950
1951 def mcgen(code, **kwds):
1952     if code[0] == '\n':
1953         code = code[1:]
1954     return cgen(code, **kwds)
1955
1956
1957 def guardname(filename):
1958     return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
1959
1960
1961 def guardstart(name):
1962     return mcgen('''
1963 #ifndef %(name)s
1964 #define %(name)s
1965
1966 ''',
1967                  name=guardname(name))
1968
1969
1970 def guardend(name):
1971     return mcgen('''
1972
1973 #endif /* %(name)s */
1974 ''',
1975                  name=guardname(name))
1976
1977
1978 def gen_if(ifcond):
1979     ret = ''
1980     for ifc in ifcond:
1981         ret += mcgen('''
1982 #if %(cond)s
1983 ''', cond=ifc)
1984     return ret
1985
1986
1987 def gen_endif(ifcond):
1988     ret = ''
1989     for ifc in reversed(ifcond):
1990         ret += mcgen('''
1991 #endif /* %(cond)s */
1992 ''', cond=ifc)
1993     return ret
1994
1995
1996 def _wrap_ifcond(ifcond, before, after):
1997     if before == after:
1998         return after   # suppress empty #if ... #endif
1999
2000     assert after.startswith(before)
2001     out = before
2002     added = after[len(before):]
2003     if added[0] == '\n':
2004         out += '\n'
2005         added = added[1:]
2006     out += gen_if(ifcond)
2007     out += added
2008     out += gen_endif(ifcond)
2009     return out
2010
2011
2012 def gen_enum_lookup(name, values, prefix=None):
2013     ret = mcgen('''
2014
2015 const QEnumLookup %(c_name)s_lookup = {
2016     .array = (const char *const[]) {
2017 ''',
2018                 c_name=c_name(name))
2019     for value in values:
2020         index = c_enum_const(name, value, prefix)
2021         ret += mcgen('''
2022         [%(index)s] = "%(value)s",
2023 ''',
2024                      index=index, value=value)
2025
2026     ret += mcgen('''
2027     },
2028     .size = %(max_index)s
2029 };
2030 ''',
2031                  max_index=c_enum_const(name, '_MAX', prefix))
2032     return ret
2033
2034
2035 def gen_enum(name, values, prefix=None):
2036     # append automatically generated _MAX value
2037     enum_values = values + ['_MAX']
2038
2039     ret = mcgen('''
2040
2041 typedef enum %(c_name)s {
2042 ''',
2043                 c_name=c_name(name))
2044
2045     i = 0
2046     for value in enum_values:
2047         ret += mcgen('''
2048     %(c_enum)s = %(i)d,
2049 ''',
2050                      c_enum=c_enum_const(name, value, prefix),
2051                      i=i)
2052         i += 1
2053
2054     ret += mcgen('''
2055 } %(c_name)s;
2056 ''',
2057                  c_name=c_name(name))
2058
2059     ret += mcgen('''
2060
2061 #define %(c_name)s_str(val) \\
2062     qapi_enum_lookup(&%(c_name)s_lookup, (val))
2063
2064 extern const QEnumLookup %(c_name)s_lookup;
2065 ''',
2066                  c_name=c_name(name))
2067     return ret
2068
2069
2070 def build_params(arg_type, boxed, extra):
2071     if not arg_type:
2072         assert not boxed
2073         return extra
2074     ret = ''
2075     sep = ''
2076     if boxed:
2077         ret += '%s arg' % arg_type.c_param_type()
2078         sep = ', '
2079     else:
2080         assert not arg_type.variants
2081         for memb in arg_type.members:
2082             ret += sep
2083             sep = ', '
2084             if memb.optional:
2085                 ret += 'bool has_%s, ' % c_name(memb.name)
2086             ret += '%s %s' % (memb.type.c_param_type(),
2087                               c_name(memb.name))
2088     if extra:
2089         ret += sep + extra
2090     return ret
2091
2092
2093 #
2094 # Accumulate and write output
2095 #
2096
2097 class QAPIGen(object):
2098
2099     def __init__(self):
2100         self._preamble = ''
2101         self._body = ''
2102
2103     def preamble_add(self, text):
2104         self._preamble += text
2105
2106     def add(self, text):
2107         self._body += text
2108
2109     def get_content(self, fname=None):
2110         return (self._top(fname) + self._preamble + self._body
2111                 + self._bottom(fname))
2112
2113     def _top(self, fname):
2114         return ''
2115
2116     def _bottom(self, fname):
2117         return ''
2118
2119     def write(self, output_dir, fname):
2120         pathname = os.path.join(output_dir, fname)
2121         dir = os.path.dirname(pathname)
2122         if dir:
2123             try:
2124                 os.makedirs(dir)
2125             except os.error as e:
2126                 if e.errno != errno.EEXIST:
2127                     raise
2128         fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2129         if sys.version_info[0] >= 3:
2130             f = open(fd, 'r+', encoding='utf-8')
2131         else:
2132             f = os.fdopen(fd, 'r+')
2133         text = self.get_content(fname)
2134         oldtext = f.read(len(text) + 1)
2135         if text != oldtext:
2136             f.seek(0)
2137             f.truncate(0)
2138             f.write(text)
2139         f.close()
2140
2141
2142 @contextmanager
2143 def ifcontext(ifcond, *args):
2144     """A 'with' statement context manager to wrap with start_if()/end_if()
2145
2146     *args: any number of QAPIGenCCode
2147
2148     Example::
2149
2150         with ifcontext(ifcond, self._genh, self._genc):
2151             modify self._genh and self._genc ...
2152
2153     Is equivalent to calling::
2154
2155         self._genh.start_if(ifcond)
2156         self._genc.start_if(ifcond)
2157         modify self._genh and self._genc ...
2158         self._genh.end_if()
2159         self._genc.end_if()
2160     """
2161     for arg in args:
2162         arg.start_if(ifcond)
2163     yield
2164     for arg in args:
2165         arg.end_if()
2166
2167
2168 class QAPIGenCCode(QAPIGen):
2169
2170     def __init__(self):
2171         QAPIGen.__init__(self)
2172         self._start_if = None
2173
2174     def start_if(self, ifcond):
2175         assert self._start_if is None
2176         self._start_if = (ifcond, self._body, self._preamble)
2177
2178     def end_if(self):
2179         assert self._start_if
2180         self._wrap_ifcond()
2181         self._start_if = None
2182
2183     def _wrap_ifcond(self):
2184         self._body = _wrap_ifcond(self._start_if[0],
2185                                   self._start_if[1], self._body)
2186         self._preamble = _wrap_ifcond(self._start_if[0],
2187                                       self._start_if[2], self._preamble)
2188
2189     def get_content(self, fname=None):
2190         assert self._start_if is None
2191         return QAPIGen.get_content(self, fname)
2192
2193
2194 class QAPIGenC(QAPIGenCCode):
2195
2196     def __init__(self, blurb, pydoc):
2197         QAPIGenCCode.__init__(self)
2198         self._blurb = blurb
2199         self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2200                                                   re.MULTILINE))
2201
2202     def _top(self, fname):
2203         return mcgen('''
2204 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2205
2206 /*
2207 %(blurb)s
2208  *
2209  * %(copyright)s
2210  *
2211  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2212  * See the COPYING.LIB file in the top-level directory.
2213  */
2214
2215 ''',
2216                      blurb=self._blurb, copyright=self._copyright)
2217
2218     def _bottom(self, fname):
2219         return mcgen('''
2220 /* Dummy declaration to prevent empty .o file */
2221 char dummy_%(name)s;
2222 ''',
2223                      name=c_name(fname))
2224
2225
2226 class QAPIGenH(QAPIGenC):
2227
2228     def _top(self, fname):
2229         return QAPIGenC._top(self, fname) + guardstart(fname)
2230
2231     def _bottom(self, fname):
2232         return guardend(fname)
2233
2234
2235 class QAPIGenDoc(QAPIGen):
2236
2237     def _top(self, fname):
2238         return (QAPIGen._top(self, fname)
2239                 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2240
2241
2242 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2243
2244     def __init__(self, prefix, what, blurb, pydoc):
2245         self._prefix = prefix
2246         self._what = what
2247         self._genc = QAPIGenC(blurb, pydoc)
2248         self._genh = QAPIGenH(blurb, pydoc)
2249
2250     def write(self, output_dir):
2251         self._genc.write(output_dir, self._prefix + self._what + '.c')
2252         self._genh.write(output_dir, self._prefix + self._what + '.h')
2253
2254
2255 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2256
2257     def __init__(self, prefix, what, blurb, pydoc):
2258         self._prefix = prefix
2259         self._what = what
2260         self._blurb = blurb
2261         self._pydoc = pydoc
2262         self._module = {}
2263         self._main_module = None
2264
2265     def _module_basename(self, what, name):
2266         if name is None:
2267             return re.sub(r'-', '-builtin-', what)
2268         basename = os.path.join(os.path.dirname(name),
2269                                 self._prefix + what)
2270         if name == self._main_module:
2271             return basename
2272         return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2273
2274     def _add_module(self, name, blurb):
2275         if self._main_module is None and name is not None:
2276             self._main_module = name
2277         genc = QAPIGenC(blurb, self._pydoc)
2278         genh = QAPIGenH(blurb, self._pydoc)
2279         self._module[name] = (genc, genh)
2280         self._set_module(name)
2281
2282     def _set_module(self, name):
2283         self._genc, self._genh = self._module[name]
2284
2285     def write(self, output_dir, opt_builtins=False):
2286         for name in self._module:
2287             if name is None and not opt_builtins:
2288                 continue
2289             basename = self._module_basename(self._what, name)
2290             (genc, genh) = self._module[name]
2291             genc.write(output_dir, basename + '.c')
2292             genh.write(output_dir, basename + '.h')
2293
2294     def _begin_module(self, name):
2295         pass
2296
2297     def visit_module(self, name):
2298         if name in self._module:
2299             self._set_module(name)
2300             return
2301         self._add_module(name, self._blurb)
2302         self._begin_module(name)
2303
2304     def visit_include(self, name, info):
2305         basename = self._module_basename(self._what, name)
2306         self._genh.preamble_add(mcgen('''
2307 #include "%(basename)s.h"
2308 ''',
2309                                       basename=basename))
This page took 0.154513 seconds and 4 git commands to generate.