]> Git Repo - qemu.git/blame - scripts/qapi/common.py
qapi: Drop qapi_event_send_FOO()'s Error ** argument
[qemu.git] / scripts / qapi / common.py
CommitLineData
0f923be2
MR
1#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
47a6ea9a 5# Copyright (c) 2013-2018 Red Hat Inc.
0f923be2
MR
6#
7# Authors:
8# Anthony Liguori <[email protected]>
c7a3f252 9# Markus Armbruster <[email protected]>
0f923be2 10#
678e48a2
MA
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
0f923be2 13
ef9d9108 14from __future__ import print_function
ded9fc28 15from contextlib import contextmanager
12f8e1b9 16import errno
33aaad52 17import os
c2613949 18import re
47299262 19import string
de685ae5 20import sys
c7883412 21from collections import OrderedDict
0f923be2 22
b52c4b9c 23builtin_types = {
4d2d5c41 24 'null': 'QTYPE_QNULL',
69dd62df 25 'str': 'QTYPE_QSTRING',
01b2ffce
MAL
26 'int': 'QTYPE_QNUM',
27 'number': 'QTYPE_QNUM',
69dd62df 28 'bool': 'QTYPE_QBOOL',
01b2ffce
MAL
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',
1310a3d3 38 'any': None, # any QType possible, actually
7264f5c5 39 'QType': 'QTYPE_QSTRING',
69dd62df
KW
40}
41
bc52d03f
MA
42# Are documentation comments required?
43doc_required = False
44
10d4d997 45# Whitelist of commands allowed to return a non-dictionary
1554a8fa 46returns_whitelist = []
10d4d997 47
893e1f2c 48# Whitelist of entities allowed to violate case conventions
2cfbae3c 49name_case_whitelist = []
893e1f2c 50
5f018446 51enum_types = {}
ed285bf8 52struct_types = {}
768562de 53union_types = {}
4dc2e690
EB
54all_names = {}
55
00e4b285
MA
56#
57# Parsing the schema into expressions
58#
59
437db254 60
a719a27c 61def error_path(parent):
ef801a9b 62 res = ''
a719a27c 63 while parent:
ef801a9b 64 res = ('In file included from %s:%d:\n' % (parent['file'],
a719a27c
LV
65 parent['line'])) + res
66 parent = parent['parent']
67 return res
68
437db254 69
4148c298
MAL
70class QAPIError(Exception):
71 def __init__(self, fname, line, col, incl_info, msg):
59b00542 72 Exception.__init__(self)
4148c298
MAL
73 self.fname = fname
74 self.line = line
75 self.col = col
76 self.info = incl_info
2caba36c 77 self.msg = msg
2caba36c
MA
78
79 def __str__(self):
ef801a9b 80 loc = '%s:%d' % (self.fname, self.line)
4148c298 81 if self.col is not None:
ef801a9b
MA
82 loc += ':%s' % self.col
83 return error_path(self.info) + '%s: %s' % (loc, self.msg)
2caba36c 84
437db254 85
4148c298
MAL
86class 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)
b86b05ed 96
4148c298
MAL
97
98class QAPISemError(QAPIError):
99 def __init__(self, info, msg):
100 QAPIError.__init__(self, info['file'], info['line'], None,
101 info['parent'], msg)
b86b05ed 102
437db254 103
3313b612
MAL
104class 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
09331fce 110 self.text = ''
3313b612
MAL
111
112 def append(self, line):
09331fce 113 self.text += line.rstrip() + '\n'
3313b612
MAL
114
115 class ArgSection(Section):
069fb5b2
MA
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
3313b612
MAL
122
123 def __init__(self, parser, info):
8cbf1a53 124 # self._parser is used to report errors with QAPIParseError. The
3313b612
MAL
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.
8cbf1a53 128 self._parser = parser
3313b612
MAL
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
8cbf1a53 137 self._section = self.body
3313b612
MAL
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] != ' ':
8cbf1a53 154 raise QAPIParseError(self._parser, "Missing space after #")
3313b612
MAL
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)
09331fce 161 elif not self.body.text and line.startswith('@'):
ef801a9b 162 if not line.endswith(':'):
8cbf1a53 163 raise QAPIParseError(self._parser, "Line should end with :")
3313b612
MAL
164 self.symbol = line[1:-1]
165 # FIXME invalid names other than the empty string aren't flagged
166 if not self.symbol:
8cbf1a53 167 raise QAPIParseError(self._parser, "Invalid name")
3313b612
MAL
168 else:
169 self._append_freeform(line)
170
4ea7148e
MA
171 def end_comment(self):
172 self._end_section()
173
3313b612
MAL
174 def _append_symbol_line(self, line):
175 name = line.split(' ', 1)[0]
176
ef801a9b 177 if name.startswith('@') and name.endswith(':'):
3313b612
MAL
178 line = line[len(name)+1:]
179 self._start_args_section(name[1:-1])
ef801a9b 180 elif name in ('Returns:', 'Since:',
3313b612 181 # those are often singular or plural
ef801a9b
MA
182 'Note:', 'Notes:',
183 'Example:', 'Examples:',
184 'TODO:'):
3313b612
MAL
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:
8cbf1a53 193 raise QAPIParseError(self._parser, "Invalid parameter name")
3313b612 194 if name in self.args:
8cbf1a53 195 raise QAPIParseError(self._parser,
3313b612
MAL
196 "'%s' parameter name duplicated" % name)
197 if self.sections:
8cbf1a53 198 raise QAPIParseError(self._parser,
3313b612
MAL
199 "'@%s:' can't follow '%s' section"
200 % (name, self.sections[0].name))
4ea7148e 201 self._end_section()
8cbf1a53
MA
202 self._section = QAPIDoc.ArgSection(name)
203 self.args[name] = self._section
3313b612 204
fc3f0df1 205 def _start_section(self, name=None):
ef801a9b 206 if name in ('Returns', 'Since') and self.has_section(name):
8cbf1a53 207 raise QAPIParseError(self._parser,
3313b612 208 "Duplicated '%s' section" % name)
4ea7148e 209 self._end_section()
8cbf1a53
MA
210 self._section = QAPIDoc.Section(name)
211 self.sections.append(self._section)
3313b612 212
4ea7148e 213 def _end_section(self):
8cbf1a53
MA
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
4ea7148e 220
3313b612 221 def _append_freeform(self, line):
8cbf1a53
MA
222 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223 if (in_arg and self._section.text.endswith('\n\n')
3313b612
MAL
224 and line and not line[0].isspace()):
225 self._start_section()
8cbf1a53
MA
226 if (in_arg or not self._section.name
227 or not self._section.name.startswith('Example')):
3313b612 228 line = line.strip()
2d433236
MA
229 match = re.match(r'(@\S+:)', line)
230 if match:
8cbf1a53 231 raise QAPIParseError(self._parser,
2d433236
MA
232 "'%s' not allowed in free-form documentation"
233 % match.group(1))
8cbf1a53 234 self._section.append(line)
3313b612 235
069fb5b2
MA
236 def connect_member(self, member):
237 if member.name not in self.args:
238 # Undocumented TODO outlaw
860e8778
MA
239 self.args[member.name] = QAPIDoc.ArgSection(member.name)
240 self.args[member.name].connect(member)
069fb5b2 241
a9f396b0
MA
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
816a57cd 247 def check(self):
2f848044 248 bogus = [name for name, section in self.args.items()
816a57cd
MA
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
3313b612 256
a4bcb208 257class QAPISchemaParser(object):
c7a3f252 258
437db254 259 def __init__(self, fp, previously_included=[], incl_info=None):
2281d00c 260 self.fname = fp.name
af97502c 261 previously_included.append(os.path.abspath(fp.name))
54414047 262 self.incl_info = incl_info
c7a3f252
MA
263 self.src = fp.read()
264 if self.src == '' or self.src[-1] != '\n':
265 self.src += '\n'
266 self.cursor = 0
515b943a
WX
267 self.line = 1
268 self.line_pos = 0
c7a3f252 269 self.exprs = []
3313b612 270 self.docs = []
c7a3f252 271 self.accept()
64d6033b 272 cur_doc = None
c7a3f252 273
437db254 274 while self.tok is not None:
2281d00c 275 info = {'file': self.fname, 'line': self.line,
4148c298 276 'parent': self.incl_info}
3313b612 277 if self.tok == '#':
64d6033b
MA
278 self.reject_expr_doc(cur_doc)
279 cur_doc = self.get_doc(info)
280 self.docs.append(cur_doc)
3313b612
MAL
281 continue
282
a719a27c 283 expr = self.get_expr(False)
e04dea88 284 if 'include' in expr:
64d6033b 285 self.reject_expr_doc(cur_doc)
a719a27c 286 if len(expr) != 1:
4148c298 287 raise QAPISemError(info, "Invalid 'include' directive")
ef801a9b 288 include = expr['include']
a719a27c 289 if not isinstance(include, str):
4148c298
MAL
290 raise QAPISemError(info,
291 "Value of 'include' must be a string")
97f02494
MA
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,
42570530
MA
297 previously_included)
298 if exprs_include:
299 self.exprs.extend(exprs_include.exprs)
300 self.docs.extend(exprs_include.docs)
bc52d03f 301 elif "pragma" in expr:
64d6033b 302 self.reject_expr_doc(cur_doc)
bc52d03f
MA
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")
2f848044 309 for name, value in pragma.items():
bc52d03f 310 self._pragma(name, value, info)
a719a27c
LV
311 else:
312 expr_elem = {'expr': expr,
4148c298 313 'info': info}
64d6033b
MA
314 if cur_doc:
315 if not cur_doc.symbol:
e7823a2a 316 raise QAPISemError(
64d6033b
MA
317 cur_doc.info, "Expression documentation required")
318 expr_elem['doc'] = cur_doc
a719a27c 319 self.exprs.append(expr_elem)
64d6033b
MA
320 cur_doc = None
321 self.reject_expr_doc(cur_doc)
e7823a2a 322
64d6033b
MA
323 @staticmethod
324 def reject_expr_doc(doc):
325 if doc and doc.symbol:
e7823a2a 326 raise QAPISemError(
64d6033b 327 doc.info,
e7823a2a 328 "Documentation for '%s' is not followed by the definition"
64d6033b 329 % doc.symbol)
c7a3f252 330
97f02494 331 def _include(self, include, info, incl_fname, previously_included):
af97502c 332 incl_abs_fname = os.path.abspath(incl_fname)
e04dea88
MA
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:
42570530
MA
342 return None
343
e04dea88 344 try:
de685ae5
MA
345 if sys.version_info[0] >= 3:
346 fobj = open(incl_fname, 'r', encoding='utf-8')
347 else:
348 fobj = open(incl_fname, 'r')
e04dea88 349 except IOError as e:
af97502c 350 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
42570530 351 return QAPISchemaParser(fobj, previously_included, info)
e04dea88 352
bc52d03f 353 def _pragma(self, name, value, info):
2cfbae3c 354 global doc_required, returns_whitelist, name_case_whitelist
bc52d03f
MA
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
1554a8fa
MA
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
2cfbae3c
MA
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
bc52d03f
MA
374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
3313b612 377 def accept(self, skip_comment=True):
c7a3f252 378 while True:
c7a3f252 379 self.tok = self.src[self.cursor]
2caba36c 380 self.pos = self.cursor
c7a3f252
MA
381 self.cursor += 1
382 self.val = None
383
f1a145e1 384 if self.tok == '#':
3313b612
MAL
385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
c7a3f252 388 self.cursor = self.src.find('\n', self.cursor)
3313b612
MAL
389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
ef801a9b 392 elif self.tok in '{}:,[]':
c7a3f252
MA
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':
4148c298 401 raise QAPIParseError(self, 'Missing terminating "\'"')
c7a3f252 402 if esc:
a7f5966b
EB
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
437db254 415 for _ in range(0, 4):
a7f5966b
EB
416 ch = self.src[self.cursor]
417 self.cursor += 1
ef801a9b 418 if ch not in '0123456789abcdefABCDEF':
4148c298
MAL
419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
a7f5966b
EB
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:
4148c298
MAL
428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
a7f5966b 432 string += chr(value)
ef801a9b 433 elif ch in '\\/\'"':
a7f5966b
EB
434 string += ch
435 else:
4148c298
MAL
436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
c7a3f252 438 esc = False
ef801a9b 439 elif ch == '\\':
c7a3f252
MA
440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
ef801a9b 446 elif self.src.startswith('true', self.pos):
e565d934
MA
447 self.val = True
448 self.cursor += 3
449 return
ef801a9b 450 elif self.src.startswith('false', self.pos):
e565d934
MA
451 self.val = False
452 self.cursor += 4
453 return
ef801a9b 454 elif self.src.startswith('null', self.pos):
e565d934
MA
455 self.val = None
456 self.cursor += 3
457 return
c7a3f252
MA
458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
515b943a
WX
462 self.line += 1
463 self.line_pos = self.cursor
9213aa53 464 elif not self.tok.isspace():
4148c298 465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
c7a3f252
MA
466
467 def get_members(self):
468 expr = OrderedDict()
6974ccd5
MA
469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
4148c298 473 raise QAPIParseError(self, 'Expected string or "}"')
6974ccd5 474 while True:
c7a3f252
MA
475 key = self.val
476 self.accept()
6974ccd5 477 if self.tok != ':':
4148c298 478 raise QAPIParseError(self, 'Expected ":"')
6974ccd5 479 self.accept()
4b35991a 480 if key in expr:
4148c298 481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
5f3cd2b7 482 expr[key] = self.get_expr(True)
6974ccd5 483 if self.tok == '}':
c7a3f252 484 self.accept()
6974ccd5
MA
485 return expr
486 if self.tok != ',':
4148c298 487 raise QAPIParseError(self, 'Expected "," or "}"')
6974ccd5
MA
488 self.accept()
489 if self.tok != "'":
4148c298 490 raise QAPIParseError(self, 'Expected string')
c7a3f252
MA
491
492 def get_values(self):
493 expr = []
6974ccd5
MA
494 if self.tok == ']':
495 self.accept()
496 return expr
437db254 497 if self.tok not in "{['tfn":
4148c298
MAL
498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
6974ccd5 500 while True:
5f3cd2b7 501 expr.append(self.get_expr(True))
6974ccd5 502 if self.tok == ']':
c7a3f252 503 self.accept()
6974ccd5
MA
504 return expr
505 if self.tok != ',':
4148c298 506 raise QAPIParseError(self, 'Expected "," or "]"')
6974ccd5 507 self.accept()
c7a3f252 508
5f3cd2b7
MA
509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
4148c298 511 raise QAPIParseError(self, 'Expected "{"')
c7a3f252
MA
512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
e53188ad 518 elif self.tok in "'tfn":
c7a3f252
MA
519 expr = self.val
520 self.accept()
6974ccd5 521 else:
012b126d
MA
522 raise QAPIParseError(self, 'Expected "{", "[", string, '
523 'boolean or "null"')
c7a3f252 524 return expr
bd9927fe 525
3313b612
MAL
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")
4ea7148e 539 doc.end_comment()
3313b612
MAL
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
00e4b285
MA
549#
550# Semantic analysis of schema expressions
ac88219a
MA
551# TODO fold into QAPISchema
552# TODO catching name collisions in generated code would be nice
00e4b285
MA
553#
554
437db254 555
14f00c6c 556def find_base_members(base):
ac4338f8
EB
557 if isinstance(base, dict):
558 return base
ed285bf8 559 base_struct_define = struct_types.get(base)
b86b05ed
WX
560 if not base_struct_define:
561 return None
562 return base_struct_define['data']
563
437db254 564
811d04fd
EB
565# Return the qtype of an alternate branch, or None on error.
566def find_alternate_member_qtype(qapi_type):
437db254 567 if qapi_type in builtin_types:
44bd1276 568 return builtin_types[qapi_type]
ed285bf8 569 elif qapi_type in struct_types:
ef801a9b 570 return 'QTYPE_QDICT'
5f018446 571 elif qapi_type in enum_types:
ef801a9b 572 return 'QTYPE_QSTRING'
768562de 573 elif qapi_type in union_types:
ef801a9b 574 return 'QTYPE_QDICT'
44bd1276
EB
575 return None
576
437db254 577
bceae769
WX
578# Return the discriminator enum define if discriminator is specified as an
579# enum type, otherwise return None.
580def 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
14f00c6c
EB
587 base_members = find_base_members(base)
588 if not base_members:
bceae769
WX
589 return None
590
14f00c6c 591 discriminator_type = base_members.get(discriminator)
bceae769
WX
592 if not discriminator_type:
593 return None
594
5f018446 595 return enum_types.get(discriminator_type)
bceae769 596
437db254 597
59a92fee
EB
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.
0fe675af 601valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
59a92fee 602 '[a-zA-Z][a-zA-Z0-9_-]*$')
437db254
EB
603
604
4148c298 605def check_name(info, source, name, allow_optional=False,
437db254 606 enum_member=False):
c9e0a798
EB
607 global valid_name
608 membername = name
609
610 if not isinstance(name, str):
4148c298 611 raise QAPISemError(info, "%s requires a string name" % source)
c9e0a798
EB
612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
4148c298
MAL
615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
c9e0a798
EB
617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
59a92fee
EB
619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
7599697c
EB
621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
9fb081e0
EB
623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
4148c298 625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
c9e0a798 626
437db254
EB
627
628def add_name(name, info, meta, implicit=False):
00e4b285
MA
629 global all_names
630 check_name(info, "'%s'" % meta, name)
d90675fa
MA
631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
00e4b285 633 if name in all_names:
4148c298
MAL
634 raise QAPISemError(info, "%s '%s' is already defined"
635 % (all_names[name], name))
255960dd 636 if not implicit and (name.endswith('Kind') or name.endswith('List')):
4148c298
MAL
637 raise QAPISemError(info, "%s '%s' should not end in '%s'"
638 % (meta, name, name[-4:]))
00e4b285
MA
639 all_names[name] = meta
640
437db254 641
967c8851
MAL
642def 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
4148c298 663def check_type(info, source, value, allow_array=False,
437db254
EB
664 allow_dict=False, allow_optional=False,
665 allow_metas=[]):
dd883c6f 666 global all_names
dd883c6f
EB
667
668 if value is None:
669 return
670
dd883c6f
EB
671 # Check if array type for value is okay
672 if isinstance(value, list):
673 if not allow_array:
4148c298 674 raise QAPISemError(info, "%s cannot be an array" % source)
dd883c6f 675 if len(value) != 1 or not isinstance(value[0], str):
4148c298
MAL
676 raise QAPISemError(info,
677 "%s: array type must contain single type name" %
678 source)
dd883c6f 679 value = value[0]
dd883c6f
EB
680
681 # Check if type name for value is okay
682 if isinstance(value, str):
437db254 683 if value not in all_names:
4148c298
MAL
684 raise QAPISemError(info, "%s uses unknown type '%s'"
685 % (source, value))
dd883c6f 686 if not all_names[value] in allow_metas:
4148c298
MAL
687 raise QAPISemError(info, "%s cannot use %s type '%s'" %
688 (source, all_names[value], value))
dd883c6f
EB
689 return
690
dd883c6f 691 if not allow_dict:
4148c298 692 raise QAPISemError(info, "%s should be a type name" % source)
c6b71e5a
MA
693
694 if not isinstance(value, OrderedDict):
4148c298
MAL
695 raise QAPISemError(info,
696 "%s should be a dictionary or type name" % source)
c6b71e5a
MA
697
698 # value is a dictionary, check that each member is okay
dd883c6f 699 for (key, arg) in value.items():
4148c298 700 check_name(info, "Member of %s" % source, key,
c9e0a798 701 allow_optional=allow_optional)
5e59baf9 702 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
4148c298
MAL
703 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
704 % (source, key))
6b5abc7d
EB
705 # Todo: allow dictionaries to represent default values of
706 # an optional argument.
4148c298 707 check_type(info, "Member '%s' of %s" % (key, source), arg,
2d21291a 708 allow_array=True,
dd883c6f 709 allow_metas=['built-in', 'union', 'alternate', 'struct',
6b5abc7d 710 'enum'])
dd883c6f 711
437db254 712
4148c298 713def check_command(expr, info):
dd883c6f 714 name = expr['command']
c818408e 715 boxed = expr.get('boxed', False)
2cbf0992 716
c818408e
EB
717 args_meta = ['struct']
718 if boxed:
719 args_meta += ['union', 'alternate']
4148c298 720 check_type(info, "'data' for command '%s'" % name,
c818408e
EB
721 expr.get('data'), allow_dict=not boxed, allow_optional=True,
722 allow_metas=args_meta)
10d4d997
EB
723 returns_meta = ['union', 'struct']
724 if name in returns_whitelist:
725 returns_meta += ['built-in', 'alternate', 'enum']
4148c298 726 check_type(info, "'returns' for command '%s'" % name,
9b090d42 727 expr.get('returns'), allow_array=True,
2d21291a 728 allow_optional=True, allow_metas=returns_meta)
dd883c6f 729
437db254 730
4148c298 731def check_event(expr, info):
4dc2e690 732 name = expr['event']
c818408e 733 boxed = expr.get('boxed', False)
4dc2e690 734
c818408e
EB
735 meta = ['struct']
736 if boxed:
737 meta += ['union', 'alternate']
4148c298 738 check_type(info, "'data' for event '%s'" % name,
c818408e
EB
739 expr.get('data'), allow_dict=not boxed, allow_optional=True,
740 allow_metas=meta)
21cd70df 741
437db254 742
4148c298 743def check_union(expr, info):
b86b05ed
WX
744 name = expr['union']
745 base = expr.get('base')
746 discriminator = expr.get('discriminator')
747 members = expr['data']
748
811d04fd 749 # Two types of unions, determined by discriminator.
811d04fd
EB
750
751 # With no discriminator it is a simple union.
752 if discriminator is None:
b86b05ed 753 enum_define = None
437db254 754 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
44bd1276 755 if base is not None:
4148c298
MAL
756 raise QAPISemError(info, "Simple union '%s' must not have a base" %
757 name)
b86b05ed
WX
758
759 # Else, it's a flat union.
760 else:
ac4338f8 761 # The object must have a string or dictionary 'base'.
4148c298 762 check_type(info, "'base' for union '%s'" % name,
ac4338f8
EB
763 base, allow_dict=True, allow_optional=True,
764 allow_metas=['struct'])
376863ef 765 if not base:
4148c298
MAL
766 raise QAPISemError(info, "Flat union '%s' must have a base"
767 % name)
14f00c6c 768 base_members = find_base_members(base)
48153745 769 assert base_members is not None
44bd1276 770
c9e0a798 771 # The value of member 'discriminator' must name a non-optional
fd41dd4e 772 # member of the base struct.
4148c298 773 check_name(info, "Discriminator of flat union '%s'" % name,
c9e0a798 774 discriminator)
14f00c6c 775 discriminator_type = base_members.get(discriminator)
b86b05ed 776 if not discriminator_type:
4148c298
MAL
777 raise QAPISemError(info,
778 "Discriminator '%s' is not a member of base "
779 "struct '%s'"
780 % (discriminator, base))
5f018446 781 enum_define = enum_types.get(discriminator_type)
437db254 782 allow_metas = ['struct']
5223070c
WX
783 # Do not allow string discriminator
784 if not enum_define:
4148c298
MAL
785 raise QAPISemError(info,
786 "Discriminator '%s' must be of enumeration "
787 "type" % discriminator)
b86b05ed 788
02a57ae3
EB
789 # Check every branch; don't allow an empty union
790 if len(members) == 0:
4148c298 791 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
b86b05ed 792 for (key, value) in members.items():
4148c298 793 check_name(info, "Member of union '%s'" % name, key)
c9e0a798 794
01cfbaa4 795 # Each value must name a known type
4148c298 796 check_type(info, "Member '%s' of union '%s'" % (key, name),
f9a14273 797 value, allow_array=not base, allow_metas=allow_metas)
dd883c6f 798
44bd1276 799 # If the discriminator names an enum type, then all members
61a94661 800 # of 'data' must also be members of the enum type.
44bd1276 801 if enum_define:
eda43c68 802 if key not in enum_define['data']:
4148c298
MAL
803 raise QAPISemError(info,
804 "Discriminator value '%s' is not found in "
805 "enum '%s'"
eda43c68 806 % (key, enum_define['enum']))
44bd1276 807
437db254 808
4148c298 809def check_alternate(expr, info):
ab916fad 810 name = expr['alternate']
811d04fd 811 members = expr['data']
811d04fd
EB
812 types_seen = {}
813
02a57ae3
EB
814 # Check every branch; require at least two branches
815 if len(members) < 2:
4148c298
MAL
816 raise QAPISemError(info,
817 "Alternate '%s' should have at least two branches "
818 "in 'data'" % name)
811d04fd 819 for (key, value) in members.items():
4148c298 820 check_name(info, "Member of alternate '%s'" % name, key)
c9e0a798 821
811d04fd 822 # Ensure alternates have no type conflicts.
4148c298 823 check_type(info, "Member '%s' of alternate '%s'" % (key, name),
dd883c6f
EB
824 value,
825 allow_metas=['built-in', 'union', 'struct', 'enum'])
811d04fd 826 qtype = find_alternate_member_qtype(value)
46534309 827 if not qtype:
4148c298
MAL
828 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
829 "type '%s'" % (name, key, value))
c0644771
MA
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
01b2ffce 838 conflicting.add('QTYPE_QNUM')
c0644771 839 else:
01b2ffce 840 conflicting.add('QTYPE_QNUM')
c0644771 841 conflicting.add('QTYPE_QBOOL')
c0644771 842 for qt in conflicting:
fda72ab4
EH
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]))
c0644771 847 types_seen[qt] = key
b86b05ed 848
437db254 849
4148c298 850def check_enum(expr, info):
cf393590
EB
851 name = expr['enum']
852 members = expr.get('data')
351d36e4 853 prefix = expr.get('prefix')
cf393590
EB
854
855 if not isinstance(members, list):
4148c298
MAL
856 raise QAPISemError(info,
857 "Enum '%s' requires an array for 'data'" % name)
351d36e4 858 if prefix is not None and not isinstance(prefix, str):
4148c298
MAL
859 raise QAPISemError(info,
860 "Enum '%s' requires a string for 'prefix'" % name)
cf393590 861 for member in members:
4148c298 862 check_name(info, "Member of enum '%s'" % name, member,
c9e0a798 863 enum_member=True)
cf393590 864
437db254 865
4148c298 866def check_struct(expr, info):
fd41dd4e 867 name = expr['struct']
dd883c6f
EB
868 members = expr['data']
869
4148c298 870 check_type(info, "'data' for struct '%s'" % name, members,
c9e0a798 871 allow_dict=True, allow_optional=True)
4148c298 872 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
dd883c6f
EB
873 allow_metas=['struct'])
874
437db254 875
0545f6b8
EB
876def 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):
4148c298 881 raise QAPISemError(info, "'%s' key must have a string value" % meta)
437db254 882 required = required + [meta]
0545f6b8 883 for (key, value) in expr.items():
437db254 884 if key not in required and key not in optional:
4148c298
MAL
885 raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
886 % (key, meta, name))
b736e25a 887 if key in ['gen', 'success-response'] and value is not False:
4148c298
MAL
888 raise QAPISemError(info,
889 "'%s' of %s '%s' should only use false value"
890 % (key, meta, name))
b736e25a
MA
891 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
892 and value is not True):
4148c298
MAL
893 raise QAPISemError(info,
894 "'%s' of %s '%s' should only use true value"
895 % (key, meta, name))
967c8851
MAL
896 if key == 'if':
897 check_if(expr, info)
0545f6b8 898 for key in required:
437db254 899 if key not in expr:
4148c298
MAL
900 raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
901 % (key, meta, name))
0545f6b8 902
437db254 903
4d076d67 904def check_exprs(exprs):
4dc2e690 905 global all_names
4dc2e690 906
7947016d 907 # Populate name table with names of built-in types
4d076d67
MA
908 for builtin in builtin_types.keys():
909 all_names[builtin] = 'built-in'
7947016d
MA
910
911 # Learn the types and check for valid expression keys
4d076d67
MA
912 for expr_elem in exprs:
913 expr = expr_elem['expr']
914 info = expr_elem['info']
7947016d 915 doc = expr_elem.get('doc')
3313b612 916
97f02494
MA
917 if 'include' in expr:
918 continue
919
7947016d 920 if not doc and doc_required:
3313b612
MAL
921 raise QAPISemError(info,
922 "Expression missing documentation comment")
923
437db254 924 if 'enum' in expr:
6f05345f 925 meta = 'enum'
967c8851 926 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
5f018446 927 enum_types[expr[meta]] = expr
437db254 928 elif 'union' in expr:
6f05345f 929 meta = 'union'
4d076d67 930 check_keys(expr_elem, 'union', ['data'],
967c8851 931 ['base', 'discriminator', 'if'])
768562de 932 union_types[expr[meta]] = expr
437db254 933 elif 'alternate' in expr:
6f05345f 934 meta = 'alternate'
967c8851 935 check_keys(expr_elem, 'alternate', ['data'], ['if'])
437db254 936 elif 'struct' in expr:
6f05345f 937 meta = 'struct'
967c8851 938 check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
ed285bf8 939 struct_types[expr[meta]] = expr
437db254 940 elif 'command' in expr:
6f05345f 941 meta = 'command'
4d076d67 942 check_keys(expr_elem, 'command', [],
876c6751 943 ['data', 'returns', 'gen', 'success-response',
967c8851 944 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
437db254 945 elif 'event' in expr:
6f05345f 946 meta = 'event'
967c8851 947 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
4d076d67 948 else:
4148c298
MAL
949 raise QAPISemError(expr_elem['info'],
950 "Expression is missing metatype")
6f05345f
MA
951 name = expr[meta]
952 add_name(name, info, meta)
7947016d
MA
953 if doc and doc.symbol != name:
954 raise QAPISemError(info, "Definition of '%s' follows documentation"
955 " for '%s'" % (name, doc.symbol))
2caba36c 956
4d076d67
MA
957 # Try again for hidden UnionKind enum
958 for expr_elem in exprs:
959 expr = expr_elem['expr']
97f02494
MA
960
961 if 'include' in expr:
962 continue
eda43c68
MA
963 if 'union' in expr and not discriminator_find_enum_define(expr):
964 name = '%sKind' % expr['union']
437db254 965 elif 'alternate' in expr:
eda43c68
MA
966 name = '%sKind' % expr['alternate']
967 else:
968 continue
5f018446 969 enum_types[name] = {'enum': name}
6f05345f 970 add_name(name, info, 'enum', implicit=True)
4d076d67
MA
971
972 # Validate that exprs make sense
973 for expr_elem in exprs:
974 expr = expr_elem['expr']
975 info = expr_elem['info']
a9f396b0 976 doc = expr_elem.get('doc')
268a1c5e 977
97f02494
MA
978 if 'include' in expr:
979 continue
437db254 980 if 'enum' in expr:
4d076d67 981 check_enum(expr, info)
437db254 982 elif 'union' in expr:
4d076d67 983 check_union(expr, info)
437db254 984 elif 'alternate' in expr:
4d076d67 985 check_alternate(expr, info)
437db254 986 elif 'struct' in expr:
4d076d67 987 check_struct(expr, info)
437db254 988 elif 'command' in expr:
4d076d67 989 check_command(expr, info)
437db254 990 elif 'event' in expr:
4d076d67
MA
991 check_event(expr, info)
992 else:
993 assert False, 'unexpected meta type'
994
a9f396b0
MA
995 if doc:
996 doc.check_expr(expr)
ac88219a 997
a9f396b0 998 return exprs
3313b612
MAL
999
1000
ac88219a
MA
1001#
1002# Schema compiler frontend
1003#
1004
2cbc9437
MAL
1005def listify_cond(ifcond):
1006 if not ifcond:
1007 return []
1008 if not isinstance(ifcond, list):
1009 return [ifcond]
1010 return ifcond
1011
1012
ac88219a 1013class QAPISchemaEntity(object):
2cbc9437 1014 def __init__(self, name, info, doc, ifcond=None):
cf40a0a5 1015 assert name is None or isinstance(name, str)
ac88219a 1016 self.name = name
cf40a0a5 1017 self.module = None
99df5289
EB
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).
ac88219a 1023 self.info = info
069fb5b2 1024 self.doc = doc
4fca21c1 1025 self._ifcond = ifcond # self.ifcond is set only after .check()
ac88219a 1026
f51d8c3d
MA
1027 def c_name(self):
1028 return c_name(self.name)
1029
ac88219a 1030 def check(self, schema):
4fca21c1
MAL
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)
ac88219a 1038
49823c4b
EB
1039 def is_implicit(self):
1040 return not self.info
1041
3f7dc21b
MA
1042 def visit(self, visitor):
1043 pass
1044
1045
1046class QAPISchemaVisitor(object):
1047 def visit_begin(self, schema):
1048 pass
1049
1050 def visit_end(self):
1051 pass
1052
cf40a0a5
MA
1053 def visit_module(self, fname):
1054 pass
1055
25a0d9c9
EB
1056 def visit_needed(self, entity):
1057 # Default to visiting everything
1058 return True
1059
cf40a0a5
MA
1060 def visit_include(self, fname, info):
1061 pass
1062
3f7dc21b
MA
1063 def visit_builtin_type(self, name, info, json_type):
1064 pass
1065
fbf09a2f 1066 def visit_enum_type(self, name, info, ifcond, values, prefix):
3f7dc21b
MA
1067 pass
1068
fbf09a2f 1069 def visit_array_type(self, name, info, ifcond, element_type):
3f7dc21b
MA
1070 pass
1071
fbf09a2f 1072 def visit_object_type(self, name, info, ifcond, base, members, variants):
3f7dc21b
MA
1073 pass
1074
fbf09a2f 1075 def visit_object_type_flat(self, name, info, ifcond, members, variants):
39a18158
MA
1076 pass
1077
fbf09a2f 1078 def visit_alternate_type(self, name, info, ifcond, variants):
3f7dc21b
MA
1079 pass
1080
fbf09a2f 1081 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
d6fe3d02 1082 success_response, boxed, allow_oob, allow_preconfig):
3f7dc21b
MA
1083 pass
1084
fbf09a2f 1085 def visit_event(self, name, info, ifcond, arg_type, boxed):
3f7dc21b
MA
1086 pass
1087
ac88219a 1088
cf40a0a5
MA
1089class 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
ac88219a 1099class QAPISchemaType(QAPISchemaEntity):
4040d995
EB
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()
f51d8c3d 1112
f51d8c3d
MA
1113 def json_type(self):
1114 pass
1115
1116 def alternate_qtype(self):
1117 json2qtype = {
4d2d5c41 1118 'null': 'QTYPE_QNULL',
f51d8c3d 1119 'string': 'QTYPE_QSTRING',
01b2ffce
MAL
1120 'number': 'QTYPE_QNUM',
1121 'int': 'QTYPE_QNUM',
f51d8c3d
MA
1122 'boolean': 'QTYPE_QBOOL',
1123 'object': 'QTYPE_QDICT'
1124 }
1125 return json2qtype.get(self.json_type())
ac88219a 1126
691e0313
MA
1127 def doc_type(self):
1128 if self.is_implicit():
1129 return None
1130 return self.name
1131
ac88219a
MA
1132
1133class QAPISchemaBuiltinType(QAPISchemaType):
861877a0 1134 def __init__(self, name, json_type, c_type):
069fb5b2 1135 QAPISchemaType.__init__(self, name, None, None)
f51d8c3d
MA
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
f51d8c3d
MA
1141
1142 def c_name(self):
1143 return self.name
1144
4040d995
EB
1145 def c_type(self):
1146 return self._c_type_name
1147
1148 def c_param_type(self):
1149 if self.name == 'str':
f51d8c3d
MA
1150 return 'const ' + self._c_type_name
1151 return self._c_type_name
1152
f51d8c3d
MA
1153 def json_type(self):
1154 return self._json_type_name
ac88219a 1155
691e0313
MA
1156 def doc_type(self):
1157 return self.json_type()
1158
3f7dc21b
MA
1159 def visit(self, visitor):
1160 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1161
ac88219a
MA
1162
1163class QAPISchemaEnumType(QAPISchemaType):
2cbc9437
MAL
1164 def __init__(self, name, info, doc, ifcond, values, prefix):
1165 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a 1166 for v in values:
93bda4dd
EB
1167 assert isinstance(v, QAPISchemaMember)
1168 v.set_owner(name)
ac88219a
MA
1169 assert prefix is None or isinstance(prefix, str)
1170 self.values = values
1171 self.prefix = prefix
1172
1173 def check(self, schema):
4fca21c1 1174 QAPISchemaType.check(self, schema)
93bda4dd
EB
1175 seen = {}
1176 for v in self.values:
1177 v.check_clash(self.info, seen)
069fb5b2
MA
1178 if self.doc:
1179 self.doc.connect_member(v)
ac88219a 1180
99df5289 1181 def is_implicit(self):
4636211e
MA
1182 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1183 return self.name.endswith('Kind') or self.name == 'QType'
99df5289 1184
4040d995 1185 def c_type(self):
f51d8c3d
MA
1186 return c_name(self.name)
1187
93bda4dd
EB
1188 def member_names(self):
1189 return [v.name for v in self.values]
1190
f51d8c3d
MA
1191 def json_type(self):
1192 return 'string'
1193
3f7dc21b 1194 def visit(self, visitor):
fbf09a2f 1195 visitor.visit_enum_type(self.name, self.info, self.ifcond,
93bda4dd 1196 self.member_names(), self.prefix)
3f7dc21b 1197
ac88219a
MA
1198
1199class QAPISchemaArrayType(QAPISchemaType):
1200 def __init__(self, name, info, element_type):
2cbc9437 1201 QAPISchemaType.__init__(self, name, info, None, None)
ac88219a
MA
1202 assert isinstance(element_type, str)
1203 self._element_type_name = element_type
1204 self.element_type = None
1205
1206 def check(self, schema):
4fca21c1 1207 QAPISchemaType.check(self, schema)
ac88219a
MA
1208 self.element_type = schema.lookup_type(self._element_type_name)
1209 assert self.element_type
4fca21c1 1210 self.element_type.check(schema)
2cbc9437 1211 self.ifcond = self.element_type.ifcond
ac88219a 1212
99df5289
EB
1213 def is_implicit(self):
1214 return True
1215
4040d995
EB
1216 def c_type(self):
1217 return c_name(self.name) + pointer_suffix
1218
f51d8c3d
MA
1219 def json_type(self):
1220 return 'array'
1221
691e0313
MA
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
3f7dc21b 1228 def visit(self, visitor):
fbf09a2f
MAL
1229 visitor.visit_array_type(self.name, self.info, self.ifcond,
1230 self.element_type)
3f7dc21b 1231
ac88219a
MA
1232
1233class QAPISchemaObjectType(QAPISchemaType):
2cbc9437
MAL
1234 def __init__(self, name, info, doc, ifcond,
1235 base, local_members, variants):
da34a9bd
EB
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
2cbc9437 1239 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1240 assert base is None or isinstance(base, str)
1241 for m in local_members:
1242 assert isinstance(m, QAPISchemaObjectTypeMember)
88d4ef8b
EB
1243 m.set_owner(name)
1244 if variants is not None:
1245 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1246 variants.set_owner(name)
ac88219a
MA
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):
4fca21c1 1254 QAPISchemaType.check(self, schema)
bac5429c 1255 if self.members is False: # check for cycles
4148c298
MAL
1256 raise QAPISemError(self.info,
1257 "Object %s contains itself" % self.name)
ac88219a
MA
1258 if self.members:
1259 return
1260 self.members = False # mark as being checked
23a4b2c6 1261 seen = OrderedDict()
ac88219a
MA
1262 if self._base_name:
1263 self.base = schema.lookup_type(self._base_name)
1264 assert isinstance(self.base, QAPISchemaObjectType)
ac88219a 1265 self.base.check(schema)
6bbfb12d 1266 self.base.check_clash(self.info, seen)
ac88219a 1267 for m in self.local_members:
e564e2dd 1268 m.check(schema)
27b60ab9 1269 m.check_clash(self.info, seen)
069fb5b2
MA
1270 if self.doc:
1271 self.doc.connect_member(m)
14ff8461 1272 self.members = seen.values()
ac88219a 1273 if self.variants:
cdc5fa37 1274 self.variants.check(schema, seen)
14ff8461 1275 assert self.variants.tag_member in self.members
6bbfb12d 1276 self.variants.check_clash(self.info, seen)
816a57cd
MA
1277 if self.doc:
1278 self.doc.check()
ac88219a 1279
14f00c6c 1280 # Check that the members of this type do not cause duplicate JSON members,
27b60ab9
EB
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
6bbfb12d 1283 def check_clash(self, info, seen):
c2183d2e
EB
1284 assert not self.variants # not implemented
1285 for m in self.members:
27b60ab9 1286 m.check_clash(info, seen)
c2183d2e 1287
99df5289 1288 def is_implicit(self):
7599697c
EB
1289 # See QAPISchema._make_implicit_object_type(), as well as
1290 # _def_predefineds()
1291 return self.name.startswith('q_')
99df5289 1292
b6167706
EB
1293 def is_empty(self):
1294 assert self.members is not None
1295 return not self.members and not self.variants
1296
f51d8c3d 1297 def c_name(self):
cd50a256 1298 assert self.name != 'q_empty'
f51d8c3d
MA
1299 return QAPISchemaType.c_name(self)
1300
4040d995 1301 def c_type(self):
49823c4b 1302 assert not self.is_implicit()
becceedc 1303 return c_name(self.name) + pointer_suffix
f51d8c3d 1304
4040d995 1305 def c_unboxed_type(self):
4040d995
EB
1306 return c_name(self.name)
1307
f51d8c3d
MA
1308 def json_type(self):
1309 return 'object'
1310
3f7dc21b 1311 def visit(self, visitor):
fbf09a2f 1312 visitor.visit_object_type(self.name, self.info, self.ifcond,
3f7dc21b 1313 self.base, self.local_members, self.variants)
fbf09a2f 1314 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
39a18158 1315 self.members, self.variants)
3f7dc21b 1316
ac88219a 1317
d44f9ac8 1318class QAPISchemaMember(object):
88d4ef8b
EB
1319 role = 'member'
1320
d44f9ac8 1321 def __init__(self, name):
ac88219a 1322 assert isinstance(name, str)
ac88219a 1323 self.name = name
88d4ef8b
EB
1324 self.owner = None
1325
1326 def set_owner(self, name):
1327 assert not self.owner
1328 self.owner = name
ac88219a 1329
27b60ab9
EB
1330 def check_clash(self, info, seen):
1331 cname = c_name(self.name)
2cfbae3c 1332 if cname.lower() != cname and self.owner not in name_case_whitelist:
4148c298
MAL
1333 raise QAPISemError(info,
1334 "%s should not use uppercase" % self.describe())
27b60ab9 1335 if cname in seen:
4148c298
MAL
1336 raise QAPISemError(info, "%s collides with %s" %
1337 (self.describe(), seen[cname].describe()))
27b60ab9 1338 seen[cname] = self
577de12d 1339
88d4ef8b
EB
1340 def _pretty_owner(self):
1341 owner = self.owner
7599697c 1342 if owner.startswith('q_obj_'):
88d4ef8b
EB
1343 # See QAPISchema._make_implicit_object_type() - reverse the
1344 # mapping there to create a nice human-readable description
7599697c 1345 owner = owner[6:]
88d4ef8b
EB
1346 if owner.endswith('-arg'):
1347 return '(parameter of %s)' % owner[:-4]
ac4338f8
EB
1348 elif owner.endswith('-base'):
1349 return '(base of %s)' % owner[:-5]
88d4ef8b
EB
1350 else:
1351 assert owner.endswith('-wrapper')
1352 # Unreachable and not implemented
1353 assert False
93bda4dd
EB
1354 if owner.endswith('Kind'):
1355 # See QAPISchema._make_implicit_enum_type()
1356 return '(branch of %s)' % owner[:-4]
88d4ef8b
EB
1357 return '(%s of %s)' % (self.role, owner)
1358
1359 def describe(self):
1360 return "'%s' %s" % (self.name, self._pretty_owner())
1361
ac88219a 1362
d44f9ac8
EB
1363class 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
ac88219a 1378class QAPISchemaObjectTypeVariants(object):
46292ba7
EB
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))
02a57ae3 1387 assert len(variants) > 0
ac88219a
MA
1388 for v in variants:
1389 assert isinstance(v, QAPISchemaObjectTypeVariant)
da9cb193 1390 self._tag_name = tag_name
46292ba7 1391 self.tag_member = tag_member
ac88219a
MA
1392 self.variants = variants
1393
88d4ef8b
EB
1394 def set_owner(self, name):
1395 for v in self.variants:
1396 v.set_owner(name)
1397
cdc5fa37 1398 def check(self, schema, seen):
14ff8461 1399 if not self.tag_member: # flat union
da9cb193
EB
1400 self.tag_member = seen[c_name(self._tag_name)]
1401 assert self._tag_name == self.tag_member.name
ac88219a 1402 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
800877bb
AN
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)
ac88219a 1411 for v in self.variants:
10565ca9 1412 v.check(schema)
0426d53c
EB
1413 # Union names must match enum values; alternate names are
1414 # checked separately. Use 'seen' to tell the two apart.
1415 if seen:
93bda4dd 1416 assert v.name in self.tag_member.type.member_names()
0426d53c 1417 assert isinstance(v.type, QAPISchemaObjectType)
b807a1e1
EB
1418 v.type.check(schema)
1419
6bbfb12d 1420 def check_clash(self, info, seen):
b807a1e1
EB
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
b807a1e1 1424 assert isinstance(v.type, QAPISchemaObjectType)
6bbfb12d 1425 v.type.check_clash(info, dict(seen))
ac88219a 1426
437db254 1427
ac88219a 1428class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
88d4ef8b
EB
1429 role = 'branch'
1430
ac88219a
MA
1431 def __init__(self, name, typ):
1432 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1433
ac88219a
MA
1434
1435class QAPISchemaAlternateType(QAPISchemaType):
2cbc9437
MAL
1436 def __init__(self, name, info, doc, ifcond, variants):
1437 QAPISchemaType.__init__(self, name, info, doc, ifcond)
ac88219a 1438 assert isinstance(variants, QAPISchemaObjectTypeVariants)
da9cb193 1439 assert variants.tag_member
88d4ef8b
EB
1440 variants.set_owner(name)
1441 variants.tag_member.set_owner(self.name)
ac88219a
MA
1442 self.variants = variants
1443
1444 def check(self, schema):
4fca21c1 1445 QAPISchemaType.check(self, schema)
e564e2dd 1446 self.variants.tag_member.check(schema)
b807a1e1
EB
1447 # Not calling self.variants.check_clash(), because there's nothing
1448 # to clash with
cdc5fa37 1449 self.variants.check(schema, {})
0426d53c
EB
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)
069fb5b2
MA
1455 if self.doc:
1456 self.doc.connect_member(v)
816a57cd
MA
1457 if self.doc:
1458 self.doc.check()
ac88219a 1459
4040d995
EB
1460 def c_type(self):
1461 return c_name(self.name) + pointer_suffix
1462
f51d8c3d
MA
1463 def json_type(self):
1464 return 'value'
1465
3f7dc21b 1466 def visit(self, visitor):
fbf09a2f
MAL
1467 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1468 self.variants)
3f7dc21b 1469
c818408e
EB
1470 def is_empty(self):
1471 return False
1472
ac88219a
MA
1473
1474class QAPISchemaCommand(QAPISchemaEntity):
2cbc9437 1475 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
d6fe3d02 1476 gen, success_response, boxed, allow_oob, allow_preconfig):
2cbc9437 1477 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
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
48825ca4 1486 self.boxed = boxed
876c6751 1487 self.allow_oob = allow_oob
d6fe3d02 1488 self.allow_preconfig = allow_preconfig
ac88219a
MA
1489
1490 def check(self, schema):
4fca21c1 1491 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1492 if self._arg_type_name:
1493 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
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():
4148c298
MAL
1499 raise QAPISemError(self.info,
1500 "Cannot use 'boxed' with empty type")
c818408e
EB
1501 else:
1502 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1503 assert not self.arg_type.variants
1504 elif self.boxed:
4148c298 1505 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a
MA
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
3f7dc21b 1510 def visit(self, visitor):
fbf09a2f 1511 visitor.visit_command(self.name, self.info, self.ifcond,
3f7dc21b 1512 self.arg_type, self.ret_type,
876c6751 1513 self.gen, self.success_response,
d6fe3d02
IM
1514 self.boxed, self.allow_oob,
1515 self.allow_preconfig)
3f7dc21b 1516
ac88219a
MA
1517
1518class QAPISchemaEvent(QAPISchemaEntity):
2cbc9437
MAL
1519 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1520 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
ac88219a
MA
1521 assert not arg_type or isinstance(arg_type, str)
1522 self._arg_type_name = arg_type
1523 self.arg_type = None
48825ca4 1524 self.boxed = boxed
ac88219a
MA
1525
1526 def check(self, schema):
4fca21c1 1527 QAPISchemaEntity.check(self, schema)
ac88219a
MA
1528 if self._arg_type_name:
1529 self.arg_type = schema.lookup_type(self._arg_type_name)
c818408e
EB
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():
4148c298
MAL
1535 raise QAPISemError(self.info,
1536 "Cannot use 'boxed' with empty type")
c818408e
EB
1537 else:
1538 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1539 assert not self.arg_type.variants
1540 elif self.boxed:
4148c298 1541 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
ac88219a 1542
3f7dc21b 1543 def visit(self, visitor):
fbf09a2f
MAL
1544 visitor.visit_event(self.name, self.info, self.ifcond,
1545 self.arg_type, self.boxed)
3f7dc21b 1546
ac88219a
MA
1547
1548class QAPISchema(object):
1549 def __init__(self, fname):
cf40a0a5 1550 self._fname = fname
de685ae5
MA
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)
181feaf3
MA
1556 exprs = check_exprs(parser.exprs)
1557 self.docs = parser.docs
8a84767c 1558 self._entity_list = []
181feaf3
MA
1559 self._entity_dict = {}
1560 self._predefining = True
1561 self._def_predefineds()
1562 self._predefining = False
1563 self._def_exprs(exprs)
1564 self.check()
ac88219a 1565
ac88219a 1566 def _def_entity(self, ent):
99df5289
EB
1567 # Only the predefined types are allowed to not have info
1568 assert ent.info or self._predefining
cf40a0a5 1569 assert ent.name is None or ent.name not in self._entity_dict
8a84767c 1570 self._entity_list.append(ent)
cf40a0a5
MA
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))
ac88219a
MA
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
cf40a0a5
MA
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
861877a0
EB
1595 def _def_builtin_type(self, name, json_type, c_type):
1596 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
cdb6610a
MA
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.
99df5289 1601 self._make_array_type(name, None)
ac88219a
MA
1602
1603 def _def_predefineds(self):
861877a0
EB
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'),
4d2d5c41
MA
1617 ('any', 'value', 'QObject' + pointer_suffix),
1618 ('null', 'null', 'QNull' + pointer_suffix)]:
f51d8c3d 1619 self._def_builtin_type(*t)
069fb5b2 1620 self.the_empty_object_type = QAPISchemaObjectType(
2cbc9437 1621 'q_empty', None, None, None, None, [], None)
39a18158 1622 self._def_entity(self.the_empty_object_type)
01b2ffce 1623 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
93bda4dd 1624 'qstring', 'qdict', 'qlist',
01b2ffce 1625 'qbool'])
2cbc9437 1626 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
069fb5b2 1627 qtype_values, 'QTYPE'))
ac88219a 1628
93bda4dd
EB
1629 def _make_enum_members(self, values):
1630 return [QAPISchemaMember(v) for v in values]
1631
2cbc9437 1632 def _make_implicit_enum_type(self, name, info, ifcond, values):
93bda4dd 1633 # See also QAPISchemaObjectTypeMember._pretty_owner()
49823c4b 1634 name = name + 'Kind' # Use namespace reserved by add_name()
93bda4dd 1635 self._def_entity(QAPISchemaEnumType(
2cbc9437 1636 name, info, None, ifcond, self._make_enum_members(values), None))
ac88219a
MA
1637 return name
1638
99df5289 1639 def _make_array_type(self, element_type, info):
255960dd 1640 name = element_type + 'List' # Use namespace reserved by add_name()
ac88219a 1641 if not self.lookup_type(name):
99df5289 1642 self._def_entity(QAPISchemaArrayType(name, info, element_type))
ac88219a
MA
1643 return name
1644
2cbc9437
MAL
1645 def _make_implicit_object_type(self, name, info, doc, ifcond,
1646 role, members):
ac88219a
MA
1647 if not members:
1648 return None
88d4ef8b 1649 # See also QAPISchemaObjectTypeMember._pretty_owner()
7599697c 1650 name = 'q_obj_%s-%s' % (name, role)
2cbc9437
MAL
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
4fca21c1 1662 assert ifcond == typ._ifcond # pylint: disable=protected-access
2cbc9437
MAL
1663 else:
1664 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1665 None, members, None))
ac88219a
MA
1666 return name
1667
069fb5b2 1668 def _def_enum_type(self, expr, info, doc):
ac88219a
MA
1669 name = expr['enum']
1670 data = expr['data']
1671 prefix = expr.get('prefix')
2cbc9437 1672 ifcond = expr.get('if')
93bda4dd 1673 self._def_entity(QAPISchemaEnumType(
2cbc9437
MAL
1674 name, info, doc, ifcond,
1675 self._make_enum_members(data), prefix))
ac88219a 1676
99df5289 1677 def _make_member(self, name, typ, info):
ac88219a
MA
1678 optional = False
1679 if name.startswith('*'):
1680 name = name[1:]
1681 optional = True
1682 if isinstance(typ, list):
1683 assert len(typ) == 1
99df5289 1684 typ = self._make_array_type(typ[0], info)
ac88219a
MA
1685 return QAPISchemaObjectTypeMember(name, typ, optional)
1686
99df5289
EB
1687 def _make_members(self, data, info):
1688 return [self._make_member(key, value, info)
2f848044 1689 for (key, value) in data.items()]
ac88219a 1690
069fb5b2 1691 def _def_struct_type(self, expr, info, doc):
ac88219a
MA
1692 name = expr['struct']
1693 base = expr.get('base')
1694 data = expr['data']
2cbc9437
MAL
1695 ifcond = expr.get('if')
1696 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
99df5289 1697 self._make_members(data, info),
ac88219a 1698 None))
ac88219a
MA
1699
1700 def _make_variant(self, case, typ):
1701 return QAPISchemaObjectTypeVariant(case, typ)
1702
99df5289 1703 def _make_simple_variant(self, case, typ, info):
ac88219a
MA
1704 if isinstance(typ, list):
1705 assert len(typ) == 1
99df5289
EB
1706 typ = self._make_array_type(typ[0], info)
1707 typ = self._make_implicit_object_type(
4fca21c1 1708 typ, info, None, self.lookup_type(typ),
2cbc9437 1709 'wrapper', [self._make_member('data', typ, info)])
ac88219a
MA
1710 return QAPISchemaObjectTypeVariant(case, typ)
1711
069fb5b2 1712 def _def_union_type(self, expr, info, doc):
ac88219a
MA
1713 name = expr['union']
1714 data = expr['data']
1715 base = expr.get('base')
2cbc9437 1716 ifcond = expr.get('if')
ac88219a 1717 tag_name = expr.get('discriminator')
46292ba7 1718 tag_member = None
ac4338f8 1719 if isinstance(base, dict):
2cbc9437
MAL
1720 base = self._make_implicit_object_type(
1721 name, info, doc, ifcond,
1722 'base', self._make_members(base, info))
ac88219a
MA
1723 if tag_name:
1724 variants = [self._make_variant(key, value)
2f848044 1725 for (key, value) in data.items()]
da34a9bd 1726 members = []
ac88219a 1727 else:
99df5289 1728 variants = [self._make_simple_variant(key, value, info)
2f848044 1729 for (key, value) in data.items()]
2cbc9437 1730 typ = self._make_implicit_enum_type(name, info, ifcond,
9d3f3494
EB
1731 [v.name for v in variants])
1732 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
da34a9bd 1733 members = [tag_member]
ac88219a 1734 self._def_entity(
2cbc9437 1735 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
ac88219a 1736 QAPISchemaObjectTypeVariants(tag_name,
46292ba7 1737 tag_member,
ac88219a 1738 variants)))
ac88219a 1739
069fb5b2 1740 def _def_alternate_type(self, expr, info, doc):
ac88219a
MA
1741 name = expr['alternate']
1742 data = expr['data']
2cbc9437 1743 ifcond = expr.get('if')
ac88219a 1744 variants = [self._make_variant(key, value)
2f848044 1745 for (key, value) in data.items()]
0426d53c 1746 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
ac88219a 1747 self._def_entity(
2cbc9437 1748 QAPISchemaAlternateType(name, info, doc, ifcond,
ac88219a 1749 QAPISchemaObjectTypeVariants(None,
46292ba7 1750 tag_member,
ac88219a 1751 variants)))
ac88219a 1752
069fb5b2 1753 def _def_command(self, expr, info, doc):
ac88219a
MA
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)
48825ca4 1759 boxed = expr.get('boxed', False)
876c6751 1760 allow_oob = expr.get('allow-oob', False)
d6fe3d02 1761 allow_preconfig = expr.get('allow-preconfig', False)
2cbc9437 1762 ifcond = expr.get('if')
ac88219a 1763 if isinstance(data, OrderedDict):
99df5289 1764 data = self._make_implicit_object_type(
2cbc9437 1765 name, info, doc, ifcond, 'arg', self._make_members(data, info))
ac88219a
MA
1766 if isinstance(rets, list):
1767 assert len(rets) == 1
99df5289 1768 rets = self._make_array_type(rets[0], info)
2cbc9437 1769 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
876c6751 1770 gen, success_response,
d6fe3d02 1771 boxed, allow_oob, allow_preconfig))
ac88219a 1772
069fb5b2 1773 def _def_event(self, expr, info, doc):
ac88219a
MA
1774 name = expr['event']
1775 data = expr.get('data')
48825ca4 1776 boxed = expr.get('boxed', False)
2cbc9437 1777 ifcond = expr.get('if')
ac88219a 1778 if isinstance(data, OrderedDict):
99df5289 1779 data = self._make_implicit_object_type(
2cbc9437
MAL
1780 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1781 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
ac88219a 1782
71a7510b
MA
1783 def _def_exprs(self, exprs):
1784 for expr_elem in exprs:
ac88219a
MA
1785 expr = expr_elem['expr']
1786 info = expr_elem['info']
069fb5b2 1787 doc = expr_elem.get('doc')
ac88219a 1788 if 'enum' in expr:
069fb5b2 1789 self._def_enum_type(expr, info, doc)
ac88219a 1790 elif 'struct' in expr:
069fb5b2 1791 self._def_struct_type(expr, info, doc)
ac88219a 1792 elif 'union' in expr:
069fb5b2 1793 self._def_union_type(expr, info, doc)
ac88219a 1794 elif 'alternate' in expr:
069fb5b2 1795 self._def_alternate_type(expr, info, doc)
ac88219a 1796 elif 'command' in expr:
069fb5b2 1797 self._def_command(expr, info, doc)
ac88219a 1798 elif 'event' in expr:
069fb5b2 1799 self._def_event(expr, info, doc)
97f02494 1800 elif 'include' in expr:
cf40a0a5 1801 self._def_include(expr, info, doc)
ac88219a
MA
1802 else:
1803 assert False
1804
1805 def check(self):
8a84767c 1806 for ent in self._entity_list:
ac88219a 1807 ent.check(self)
4d076d67 1808
3f7dc21b 1809 def visit(self, visitor):
25a0d9c9 1810 visitor.visit_begin(self)
cf40a0a5 1811 module = None
8a84767c 1812 for entity in self._entity_list:
25a0d9c9 1813 if visitor.visit_needed(entity):
cf40a0a5
MA
1814 if entity.module != module:
1815 module = entity.module
1816 visitor.visit_module(module)
25a0d9c9 1817 entity.visit(visitor)
3f7dc21b
MA
1818 visitor.visit_end()
1819
b86b05ed 1820
00e4b285
MA
1821#
1822# Code generation helpers
1823#
1824
0f923be2
MR
1825def 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
437db254 1838
849bc538
MA
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
1842def 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 = ''
b736e25a
MA
1848 length = len(c_fun_str)
1849 for i in range(length):
849bc538 1850 c = c_fun_str[i]
ef801a9b
MA
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] != '_':
b736e25a 1853 if i < length - 1 and c_fun_str[i + 1].islower():
437db254
EB
1854 new_name += '_'
1855 elif c_fun_str[i - 1].isdigit():
849bc538
MA
1856 new_name += '_'
1857 new_name += c
1858 return new_name.lstrip('_').upper()
1859
437db254 1860
351d36e4
DB
1861def c_enum_const(type_name, const_name, prefix=None):
1862 if prefix is not None:
1863 type_name = prefix
d20a580b 1864 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
849bc538 1865
b736e25a 1866
52c4272c 1867if hasattr(str, 'maketrans'):
eb815e24 1868 c_name_trans = str.maketrans('.-', '__')
52c4272c 1869else:
eb815e24 1870 c_name_trans = string.maketrans('.-', '__')
47299262 1871
437db254 1872
c6405b54
EB
1873# Map @name to a valid C identifier.
1874# If @protect, avoid returning certain ticklish identifiers (like
ef801a9b 1875# C keywords) by prepending 'q_'.
c6405b54
EB
1876#
1877# Used for converting 'name' from a 'name':'type' qapi definition
1878# into a generated struct member, as well as converting type names
1879# into substrings of a generated C function name.
1880# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1881# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
18df515e 1882def c_name(name, protect=True):
427a1a2c
BS
1883 # ANSI X3J11/88-090, 3.1.1
1884 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
437db254
EB
1885 'default', 'do', 'double', 'else', 'enum', 'extern',
1886 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1887 'return', 'short', 'signed', 'sizeof', 'static',
1888 'struct', 'switch', 'typedef', 'union', 'unsigned',
1889 'void', 'volatile', 'while'])
427a1a2c
BS
1890 # ISO/IEC 9899:1999, 6.4.1
1891 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1892 # ISO/IEC 9899:2011, 6.4.1
437db254
EB
1893 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1894 '_Noreturn', '_Static_assert', '_Thread_local'])
427a1a2c
BS
1895 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1896 # excluding _.*
1897 gcc_words = set(['asm', 'typeof'])
6f88009e
TS
1898 # C++ ISO/IEC 14882:2003 2.11
1899 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1900 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1901 'namespace', 'new', 'operator', 'private', 'protected',
1902 'public', 'reinterpret_cast', 'static_cast', 'template',
1903 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1904 'using', 'virtual', 'wchar_t',
1905 # alternative representations
1906 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1907 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1057725f 1908 # namespace pollution:
9a801c7d 1909 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
c43567c1 1910 name = name.translate(c_name_trans)
437db254
EB
1911 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1912 | cpp_words | polluted_words):
ef801a9b 1913 return 'q_' + name
c43567c1 1914 return name
0f923be2 1915
b736e25a 1916
05dfb26c 1917eatspace = '\033EATSPACE.'
d5573446 1918pointer_suffix = ' *' + eatspace
05dfb26c 1919
437db254 1920
0f923be2 1921def genindent(count):
ef801a9b 1922 ret = ''
437db254 1923 for _ in range(count):
ef801a9b 1924 ret += ' '
0f923be2
MR
1925 return ret
1926
b736e25a 1927
0f923be2
MR
1928indent_level = 0
1929
437db254 1930
0f923be2
MR
1931def push_indent(indent_amount=4):
1932 global indent_level
1933 indent_level += indent_amount
1934
437db254 1935
0f923be2
MR
1936def pop_indent(indent_amount=4):
1937 global indent_level
1938 indent_level -= indent_amount
1939
437db254 1940
77e703b8
MA
1941# Generate @code with @kwds interpolated.
1942# Obey indent_level, and strip eatspace.
0f923be2 1943def cgen(code, **kwds):
77e703b8
MA
1944 raw = code % kwds
1945 if indent_level:
1946 indent = genindent(indent_level)
2752e5be 1947 # re.subn() lacks flags support before Python 2.7, use re.compile()
485d948c
MAL
1948 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
1949 indent, raw)
77e703b8 1950 raw = raw[0]
0fe675af 1951 return re.sub(re.escape(eatspace) + r' *', '', raw)
0f923be2 1952
437db254 1953
0f923be2 1954def mcgen(code, **kwds):
77e703b8
MA
1955 if code[0] == '\n':
1956 code = code[1:]
1957 return cgen(code, **kwds)
0f923be2 1958
0f923be2
MR
1959
1960def guardname(filename):
f9c14639 1961 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
c0afa9c5 1962
437db254 1963
c0afa9c5
MR
1964def guardstart(name):
1965 return mcgen('''
c0afa9c5
MR
1966#ifndef %(name)s
1967#define %(name)s
1968
1969''',
1970 name=guardname(name))
1971
437db254 1972
c0afa9c5
MR
1973def guardend(name):
1974 return mcgen('''
1975
1976#endif /* %(name)s */
c0afa9c5
MR
1977''',
1978 name=guardname(name))
2114f5a9 1979
437db254 1980
ded9fc28
MAL
1981def gen_if(ifcond):
1982 ret = ''
1983 for ifc in ifcond:
1984 ret += mcgen('''
1985#if %(cond)s
1986''', cond=ifc)
1987 return ret
1988
1989
1990def gen_endif(ifcond):
1991 ret = ''
1992 for ifc in reversed(ifcond):
1993 ret += mcgen('''
1994#endif /* %(cond)s */
1995''', cond=ifc)
1996 return ret
1997
1998
1999def _wrap_ifcond(ifcond, before, after):
2000 if before == after:
2001 return after # suppress empty #if ... #endif
2002
2003 assert after.startswith(before)
2004 out = before
2005 added = after[len(before):]
2006 if added[0] == '\n':
2007 out += '\n'
2008 added = added[1:]
2009 out += gen_if(ifcond)
2010 out += added
2011 out += gen_endif(ifcond)
2012 return out
2013
2014
e98859a9 2015def gen_enum_lookup(name, values, prefix=None):
efd2eaa6
MA
2016 ret = mcgen('''
2017
f7abe0ec
MAL
2018const QEnumLookup %(c_name)s_lookup = {
2019 .array = (const char *const[]) {
efd2eaa6 2020''',
e98859a9 2021 c_name=c_name(name))
efd2eaa6
MA
2022 for value in values:
2023 index = c_enum_const(name, value, prefix)
2024 ret += mcgen('''
f7abe0ec 2025 [%(index)s] = "%(value)s",
efd2eaa6 2026''',
e98859a9 2027 index=index, value=value)
efd2eaa6 2028
efd2eaa6 2029 ret += mcgen('''
f7abe0ec
MAL
2030 },
2031 .size = %(max_index)s
efd2eaa6
MA
2032};
2033''',
ebf677c8 2034 max_index=c_enum_const(name, '_MAX', prefix))
efd2eaa6
MA
2035 return ret
2036
437db254 2037
e98859a9
MA
2038def gen_enum(name, values, prefix=None):
2039 # append automatically generated _MAX value
7fb1cf16 2040 enum_values = values + ['_MAX']
efd2eaa6 2041
e98859a9 2042 ret = mcgen('''
efd2eaa6 2043
e98859a9 2044typedef enum %(c_name)s {
efd2eaa6 2045''',
e98859a9 2046 c_name=c_name(name))
efd2eaa6
MA
2047
2048 i = 0
2049 for value in enum_values:
e98859a9
MA
2050 ret += mcgen('''
2051 %(c_enum)s = %(i)d,
efd2eaa6 2052''',
e98859a9 2053 c_enum=c_enum_const(name, value, prefix),
efd2eaa6
MA
2054 i=i)
2055 i += 1
2056
e98859a9
MA
2057 ret += mcgen('''
2058} %(c_name)s;
efd2eaa6 2059''',
e98859a9
MA
2060 c_name=c_name(name))
2061
2062 ret += mcgen('''
efd2eaa6 2063
5b5f825d 2064#define %(c_name)s_str(val) \\
f7abe0ec 2065 qapi_enum_lookup(&%(c_name)s_lookup, (val))
5b5f825d 2066
f7abe0ec 2067extern const QEnumLookup %(c_name)s_lookup;
e98859a9
MA
2068''',
2069 c_name=c_name(name))
2070 return ret
efd2eaa6 2071
437db254 2072
bdd2d42b 2073def build_params(arg_type, boxed, extra=None):
03b4367a
MA
2074 ret = ''
2075 sep = ''
48825ca4 2076 if boxed:
bdd2d42b 2077 assert arg_type
c818408e
EB
2078 ret += '%s arg' % arg_type.c_param_type()
2079 sep = ', '
bdd2d42b 2080 elif arg_type:
48825ca4
EB
2081 assert not arg_type.variants
2082 for memb in arg_type.members:
2083 ret += sep
2084 sep = ', '
2085 if memb.optional:
2086 ret += 'bool has_%s, ' % c_name(memb.name)
2087 ret += '%s %s' % (memb.type.c_param_type(),
2088 c_name(memb.name))
03b4367a
MA
2089 if extra:
2090 ret += sep + extra
bdd2d42b 2091 return ret if ret else 'void'
03b4367a 2092
1f353344 2093
00e4b285 2094#
47a6ea9a 2095# Accumulate and write output
00e4b285
MA
2096#
2097
47a6ea9a
MA
2098class QAPIGen(object):
2099
2100 def __init__(self):
2101 self._preamble = ''
2102 self._body = ''
2103
2104 def preamble_add(self, text):
2105 self._preamble += text
2106
2107 def add(self, text):
2108 self._body += text
2109
ded9fc28
MAL
2110 def get_content(self, fname=None):
2111 return (self._top(fname) + self._preamble + self._body
2112 + self._bottom(fname))
2113
47a6ea9a
MA
2114 def _top(self, fname):
2115 return ''
2116
2117 def _bottom(self, fname):
2118 return ''
437db254 2119
47a6ea9a 2120 def write(self, output_dir, fname):
cdb6610a
MA
2121 pathname = os.path.join(output_dir, fname)
2122 dir = os.path.dirname(pathname)
2123 if dir:
47a6ea9a 2124 try:
cdb6610a 2125 os.makedirs(dir)
47a6ea9a
MA
2126 except os.error as e:
2127 if e.errno != errno.EEXIST:
2128 raise
cdb6610a 2129 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
de685ae5
MA
2130 if sys.version_info[0] >= 3:
2131 f = open(fd, 'r+', encoding='utf-8')
2132 else:
2133 f = os.fdopen(fd, 'r+')
ded9fc28 2134 text = self.get_content(fname)
907b8466
MA
2135 oldtext = f.read(len(text) + 1)
2136 if text != oldtext:
2137 f.seek(0)
2138 f.truncate(0)
2139 f.write(text)
47a6ea9a
MA
2140 f.close()
2141
2142
ded9fc28
MAL
2143@contextmanager
2144def ifcontext(ifcond, *args):
2145 """A 'with' statement context manager to wrap with start_if()/end_if()
47a6ea9a 2146
ded9fc28
MAL
2147 *args: any number of QAPIGenCCode
2148
2149 Example::
2150
2151 with ifcontext(ifcond, self._genh, self._genc):
2152 modify self._genh and self._genc ...
2153
2154 Is equivalent to calling::
2155
2156 self._genh.start_if(ifcond)
2157 self._genc.start_if(ifcond)
2158 modify self._genh and self._genc ...
2159 self._genh.end_if()
2160 self._genc.end_if()
2161 """
2162 for arg in args:
2163 arg.start_if(ifcond)
2164 yield
2165 for arg in args:
2166 arg.end_if()
2167
2168
2169class QAPIGenCCode(QAPIGen):
2170
2171 def __init__(self):
47a6ea9a 2172 QAPIGen.__init__(self)
ded9fc28
MAL
2173 self._start_if = None
2174
2175 def start_if(self, ifcond):
2176 assert self._start_if is None
2177 self._start_if = (ifcond, self._body, self._preamble)
2178
2179 def end_if(self):
2180 assert self._start_if
2181 self._wrap_ifcond()
2182 self._start_if = None
2183
2184 def _wrap_ifcond(self):
2185 self._body = _wrap_ifcond(self._start_if[0],
2186 self._start_if[1], self._body)
2187 self._preamble = _wrap_ifcond(self._start_if[0],
2188 self._start_if[2], self._preamble)
2189
2190 def get_content(self, fname=None):
2191 assert self._start_if is None
2192 return QAPIGen.get_content(self, fname)
2193
2194
2195class QAPIGenC(QAPIGenCCode):
2196
2197 def __init__(self, blurb, pydoc):
2198 QAPIGenCCode.__init__(self)
47a6ea9a
MA
2199 self._blurb = blurb
2200 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2201 re.MULTILINE))
2202
2203 def _top(self, fname):
2204 return mcgen('''
2205/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
c263de3f
MA
2206
2207/*
2208%(blurb)s
5ddeec83
MA
2209 *
2210 * %(copyright)s
c263de3f
MA
2211 *
2212 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2213 * See the COPYING.LIB file in the top-level directory.
2214 */
2215
2216''',
47a6ea9a 2217 blurb=self._blurb, copyright=self._copyright)
12f8e1b9 2218
252dc310
MA
2219 def _bottom(self, fname):
2220 return mcgen('''
2221/* Dummy declaration to prevent empty .o file */
2222char dummy_%(name)s;
2223''',
2224 name=c_name(fname))
2225
12f8e1b9 2226
47a6ea9a 2227class QAPIGenH(QAPIGenC):
12f8e1b9 2228
47a6ea9a
MA
2229 def _top(self, fname):
2230 return QAPIGenC._top(self, fname) + guardstart(fname)
12f8e1b9 2231
47a6ea9a
MA
2232 def _bottom(self, fname):
2233 return guardend(fname)
12f8e1b9 2234
437db254 2235
47a6ea9a 2236class QAPIGenDoc(QAPIGen):
c263de3f 2237
47a6ea9a
MA
2238 def _top(self, fname):
2239 return (QAPIGen._top(self, fname)
2240 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
71b3f045
MA
2241
2242
2243class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2244
2245 def __init__(self, prefix, what, blurb, pydoc):
2246 self._prefix = prefix
2247 self._what = what
2248 self._genc = QAPIGenC(blurb, pydoc)
2249 self._genh = QAPIGenH(blurb, pydoc)
2250
2251 def write(self, output_dir):
2252 self._genc.write(output_dir, self._prefix + self._what + '.c')
2253 self._genh.write(output_dir, self._prefix + self._what + '.h')
cdb6610a
MA
2254
2255
2256class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2257
2258 def __init__(self, prefix, what, blurb, pydoc):
2259 self._prefix = prefix
2260 self._what = what
2261 self._blurb = blurb
2262 self._pydoc = pydoc
2263 self._module = {}
252dc310 2264 self._main_module = None
cdb6610a
MA
2265
2266 def _module_basename(self, what, name):
2267 if name is None:
2268 return re.sub(r'-', '-builtin-', what)
252dc310
MA
2269 basename = os.path.join(os.path.dirname(name),
2270 self._prefix + what)
2271 if name == self._main_module:
2272 return basename
2273 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
cdb6610a
MA
2274
2275 def _add_module(self, name, blurb):
252dc310
MA
2276 if self._main_module is None and name is not None:
2277 self._main_module = name
cdb6610a
MA
2278 genc = QAPIGenC(blurb, self._pydoc)
2279 genh = QAPIGenH(blurb, self._pydoc)
2280 self._module[name] = (genc, genh)
2281 self._set_module(name)
2282
2283 def _set_module(self, name):
2284 self._genc, self._genh = self._module[name]
2285
252dc310 2286 def write(self, output_dir, opt_builtins=False):
cdb6610a
MA
2287 for name in self._module:
2288 if name is None and not opt_builtins:
2289 continue
2290 basename = self._module_basename(self._what, name)
2291 (genc, genh) = self._module[name]
2292 genc.write(output_dir, basename + '.c')
2293 genh.write(output_dir, basename + '.h')
2294
2295 def _begin_module(self, name):
2296 pass
2297
2298 def visit_module(self, name):
252dc310
MA
2299 if name in self._module:
2300 self._set_module(name)
cdb6610a
MA
2301 return
2302 self._add_module(name, self._blurb)
2303 self._begin_module(name)
252dc310
MA
2304
2305 def visit_include(self, name, info):
2306 basename = self._module_basename(self._what, name)
2307 self._genh.preamble_add(mcgen('''
2308#include "%(basename)s.h"
2309''',
2310 basename=basename))
This page took 0.863192 seconds and 4 git commands to generate.