]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi.py
test-qga: Avoid qobject_from_jsonv("%"PRId64)
[mirror_qemu.git] / scripts / qapi.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2016 Red Hat Inc.
6 #
7 # Authors:
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
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 import re
15 from ordereddict import OrderedDict
16 import errno
17 import getopt
18 import os
19 import sys
20 import string
21
22 builtin_types = {
23 'str': 'QTYPE_QSTRING',
24 'int': 'QTYPE_QINT',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
27 'int8': 'QTYPE_QINT',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
35 'size': 'QTYPE_QINT',
36 'any': None, # any QType possible, actually
37 'QType': 'QTYPE_QSTRING',
38 }
39
40 # Whitelist of commands allowed to return a non-dictionary
41 returns_whitelist = [
42 # From QMP:
43 'human-monitor-command',
44 'qom-get',
45 'query-migrate-cache-size',
46 'query-tpm-models',
47 'query-tpm-types',
48 'ringbuf-read',
49
50 # From QGA:
51 'guest-file-open',
52 'guest-fsfreeze-freeze',
53 'guest-fsfreeze-freeze-list',
54 'guest-fsfreeze-status',
55 'guest-fsfreeze-thaw',
56 'guest-get-time',
57 'guest-set-vcpus',
58 'guest-sync',
59 'guest-sync-delimited',
60 ]
61
62 # Whitelist of entities allowed to violate case conventions
63 case_whitelist = [
64 # From QMP:
65 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status
66 'CpuInfoMIPS', # PC, visible through query-cpu
67 'CpuInfoTricore', # PC, visible through query-cpu
68 'QapiErrorClass', # all members, visible through errors
69 'UuidInfo', # UUID, visible through query-uuid
70 'X86CPURegister32', # all members, visible indirectly through qom-get
71 'q_obj_CpuInfo-base', # CPU, visible through query-cpu
72 ]
73
74 enum_types = []
75 struct_types = []
76 union_types = []
77 events = []
78 all_names = {}
79
80 #
81 # Parsing the schema into expressions
82 #
83
84
85 def error_path(parent):
86 res = ""
87 while parent:
88 res = ("In file included from %s:%d:\n" % (parent['file'],
89 parent['line'])) + res
90 parent = parent['parent']
91 return res
92
93
94 class QAPISchemaError(Exception):
95 def __init__(self, schema, msg):
96 Exception.__init__(self)
97 self.fname = schema.fname
98 self.msg = msg
99 self.col = 1
100 self.line = schema.line
101 for ch in schema.src[schema.line_pos:schema.pos]:
102 if ch == '\t':
103 self.col = (self.col + 7) % 8 + 1
104 else:
105 self.col += 1
106 self.info = schema.incl_info
107
108 def __str__(self):
109 return error_path(self.info) + \
110 "%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
111
112
113 class QAPIExprError(Exception):
114 def __init__(self, expr_info, msg):
115 Exception.__init__(self)
116 assert expr_info
117 self.info = expr_info
118 self.msg = msg
119
120 def __str__(self):
121 return error_path(self.info['parent']) + \
122 "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
123
124
125 class QAPISchemaParser(object):
126
127 def __init__(self, fp, previously_included=[], incl_info=None):
128 abs_fname = os.path.abspath(fp.name)
129 fname = fp.name
130 self.fname = fname
131 previously_included.append(abs_fname)
132 self.incl_info = incl_info
133 self.src = fp.read()
134 if self.src == '' or self.src[-1] != '\n':
135 self.src += '\n'
136 self.cursor = 0
137 self.line = 1
138 self.line_pos = 0
139 self.exprs = []
140 self.accept()
141
142 while self.tok is not None:
143 expr_info = {'file': fname, 'line': self.line,
144 'parent': self.incl_info}
145 expr = self.get_expr(False)
146 if isinstance(expr, dict) and "include" in expr:
147 if len(expr) != 1:
148 raise QAPIExprError(expr_info,
149 "Invalid 'include' directive")
150 include = expr["include"]
151 if not isinstance(include, str):
152 raise QAPIExprError(expr_info,
153 "Value of 'include' must be a string")
154 incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
155 include)
156 # catch inclusion cycle
157 inf = expr_info
158 while inf:
159 if incl_abs_fname == os.path.abspath(inf['file']):
160 raise QAPIExprError(expr_info, "Inclusion loop for %s"
161 % include)
162 inf = inf['parent']
163 # skip multiple include of the same file
164 if incl_abs_fname in previously_included:
165 continue
166 try:
167 fobj = open(incl_abs_fname, 'r')
168 except IOError as e:
169 raise QAPIExprError(expr_info,
170 '%s: %s' % (e.strerror, include))
171 exprs_include = QAPISchemaParser(fobj, previously_included,
172 expr_info)
173 self.exprs.extend(exprs_include.exprs)
174 else:
175 expr_elem = {'expr': expr,
176 'info': expr_info}
177 self.exprs.append(expr_elem)
178
179 def accept(self):
180 while True:
181 self.tok = self.src[self.cursor]
182 self.pos = self.cursor
183 self.cursor += 1
184 self.val = None
185
186 if self.tok == '#':
187 self.cursor = self.src.find('\n', self.cursor)
188 elif self.tok in "{}:,[]":
189 return
190 elif self.tok == "'":
191 string = ''
192 esc = False
193 while True:
194 ch = self.src[self.cursor]
195 self.cursor += 1
196 if ch == '\n':
197 raise QAPISchemaError(self,
198 'Missing terminating "\'"')
199 if esc:
200 if ch == 'b':
201 string += '\b'
202 elif ch == 'f':
203 string += '\f'
204 elif ch == 'n':
205 string += '\n'
206 elif ch == 'r':
207 string += '\r'
208 elif ch == 't':
209 string += '\t'
210 elif ch == 'u':
211 value = 0
212 for _ in range(0, 4):
213 ch = self.src[self.cursor]
214 self.cursor += 1
215 if ch not in "0123456789abcdefABCDEF":
216 raise QAPISchemaError(self,
217 '\\u escape needs 4 '
218 'hex digits')
219 value = (value << 4) + int(ch, 16)
220 # If Python 2 and 3 didn't disagree so much on
221 # how to handle Unicode, then we could allow
222 # Unicode string defaults. But most of QAPI is
223 # ASCII-only, so we aren't losing much for now.
224 if not value or value > 0x7f:
225 raise QAPISchemaError(self,
226 'For now, \\u escape '
227 'only supports non-zero '
228 'values up to \\u007f')
229 string += chr(value)
230 elif ch in "\\/'\"":
231 string += ch
232 else:
233 raise QAPISchemaError(self,
234 "Unknown escape \\%s" % ch)
235 esc = False
236 elif ch == "\\":
237 esc = True
238 elif ch == "'":
239 self.val = string
240 return
241 else:
242 string += ch
243 elif self.src.startswith("true", self.pos):
244 self.val = True
245 self.cursor += 3
246 return
247 elif self.src.startswith("false", self.pos):
248 self.val = False
249 self.cursor += 4
250 return
251 elif self.src.startswith("null", self.pos):
252 self.val = None
253 self.cursor += 3
254 return
255 elif self.tok == '\n':
256 if self.cursor == len(self.src):
257 self.tok = None
258 return
259 self.line += 1
260 self.line_pos = self.cursor
261 elif not self.tok.isspace():
262 raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
263
264 def get_members(self):
265 expr = OrderedDict()
266 if self.tok == '}':
267 self.accept()
268 return expr
269 if self.tok != "'":
270 raise QAPISchemaError(self, 'Expected string or "}"')
271 while True:
272 key = self.val
273 self.accept()
274 if self.tok != ':':
275 raise QAPISchemaError(self, 'Expected ":"')
276 self.accept()
277 if key in expr:
278 raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
279 expr[key] = self.get_expr(True)
280 if self.tok == '}':
281 self.accept()
282 return expr
283 if self.tok != ',':
284 raise QAPISchemaError(self, 'Expected "," or "}"')
285 self.accept()
286 if self.tok != "'":
287 raise QAPISchemaError(self, 'Expected string')
288
289 def get_values(self):
290 expr = []
291 if self.tok == ']':
292 self.accept()
293 return expr
294 if self.tok not in "{['tfn":
295 raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
296 'boolean or "null"')
297 while True:
298 expr.append(self.get_expr(True))
299 if self.tok == ']':
300 self.accept()
301 return expr
302 if self.tok != ',':
303 raise QAPISchemaError(self, 'Expected "," or "]"')
304 self.accept()
305
306 def get_expr(self, nested):
307 if self.tok != '{' and not nested:
308 raise QAPISchemaError(self, 'Expected "{"')
309 if self.tok == '{':
310 self.accept()
311 expr = self.get_members()
312 elif self.tok == '[':
313 self.accept()
314 expr = self.get_values()
315 elif self.tok in "'tfn":
316 expr = self.val
317 self.accept()
318 else:
319 raise QAPISchemaError(self, 'Expected "{", "[" or string')
320 return expr
321
322 #
323 # Semantic analysis of schema expressions
324 # TODO fold into QAPISchema
325 # TODO catching name collisions in generated code would be nice
326 #
327
328
329 def find_base_members(base):
330 if isinstance(base, dict):
331 return base
332 base_struct_define = find_struct(base)
333 if not base_struct_define:
334 return None
335 return base_struct_define['data']
336
337
338 # Return the qtype of an alternate branch, or None on error.
339 def find_alternate_member_qtype(qapi_type):
340 if qapi_type in builtin_types:
341 return builtin_types[qapi_type]
342 elif find_struct(qapi_type):
343 return "QTYPE_QDICT"
344 elif find_enum(qapi_type):
345 return "QTYPE_QSTRING"
346 elif find_union(qapi_type):
347 return "QTYPE_QDICT"
348 return None
349
350
351 # Return the discriminator enum define if discriminator is specified as an
352 # enum type, otherwise return None.
353 def discriminator_find_enum_define(expr):
354 base = expr.get('base')
355 discriminator = expr.get('discriminator')
356
357 if not (discriminator and base):
358 return None
359
360 base_members = find_base_members(base)
361 if not base_members:
362 return None
363
364 discriminator_type = base_members.get(discriminator)
365 if not discriminator_type:
366 return None
367
368 return find_enum(discriminator_type)
369
370
371 # Names must be letters, numbers, -, and _. They must start with letter,
372 # except for downstream extensions which must start with __RFQDN_.
373 # Dots are only valid in the downstream extension prefix.
374 valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
375 '[a-zA-Z][a-zA-Z0-9_-]*$')
376
377
378 def check_name(expr_info, source, name, allow_optional=False,
379 enum_member=False):
380 global valid_name
381 membername = name
382
383 if not isinstance(name, str):
384 raise QAPIExprError(expr_info,
385 "%s requires a string name" % source)
386 if name.startswith('*'):
387 membername = name[1:]
388 if not allow_optional:
389 raise QAPIExprError(expr_info,
390 "%s does not allow optional name '%s'"
391 % (source, name))
392 # Enum members can start with a digit, because the generated C
393 # code always prefixes it with the enum name
394 if enum_member and membername[0].isdigit():
395 membername = 'D' + membername
396 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
397 # and 'q_obj_*' implicit type names.
398 if not valid_name.match(membername) or \
399 c_name(membername, False).startswith('q_'):
400 raise QAPIExprError(expr_info,
401 "%s uses invalid name '%s'" % (source, name))
402
403
404 def add_name(name, info, meta, implicit=False):
405 global all_names
406 check_name(info, "'%s'" % meta, name)
407 # FIXME should reject names that differ only in '_' vs. '.'
408 # vs. '-', because they're liable to clash in generated C.
409 if name in all_names:
410 raise QAPIExprError(info,
411 "%s '%s' is already defined"
412 % (all_names[name], name))
413 if not implicit and (name.endswith('Kind') or name.endswith('List')):
414 raise QAPIExprError(info,
415 "%s '%s' should not end in '%s'"
416 % (meta, name, name[-4:]))
417 all_names[name] = meta
418
419
420 def add_struct(definition, info):
421 global struct_types
422 name = definition['struct']
423 add_name(name, info, 'struct')
424 struct_types.append(definition)
425
426
427 def find_struct(name):
428 global struct_types
429 for struct in struct_types:
430 if struct['struct'] == name:
431 return struct
432 return None
433
434
435 def add_union(definition, info):
436 global union_types
437 name = definition['union']
438 add_name(name, info, 'union')
439 union_types.append(definition)
440
441
442 def find_union(name):
443 global union_types
444 for union in union_types:
445 if union['union'] == name:
446 return union
447 return None
448
449
450 def add_enum(name, info, enum_values=None, implicit=False):
451 global enum_types
452 add_name(name, info, 'enum', implicit)
453 enum_types.append({"enum_name": name, "enum_values": enum_values})
454
455
456 def find_enum(name):
457 global enum_types
458 for enum in enum_types:
459 if enum['enum_name'] == name:
460 return enum
461 return None
462
463
464 def is_enum(name):
465 return find_enum(name) is not None
466
467
468 def check_type(expr_info, source, value, allow_array=False,
469 allow_dict=False, allow_optional=False,
470 allow_metas=[]):
471 global all_names
472
473 if value is None:
474 return
475
476 # Check if array type for value is okay
477 if isinstance(value, list):
478 if not allow_array:
479 raise QAPIExprError(expr_info,
480 "%s cannot be an array" % source)
481 if len(value) != 1 or not isinstance(value[0], str):
482 raise QAPIExprError(expr_info,
483 "%s: array type must contain single type name"
484 % source)
485 value = value[0]
486
487 # Check if type name for value is okay
488 if isinstance(value, str):
489 if value not in all_names:
490 raise QAPIExprError(expr_info,
491 "%s uses unknown type '%s'"
492 % (source, value))
493 if not all_names[value] in allow_metas:
494 raise QAPIExprError(expr_info,
495 "%s cannot use %s type '%s'"
496 % (source, all_names[value], value))
497 return
498
499 if not allow_dict:
500 raise QAPIExprError(expr_info,
501 "%s should be a type name" % source)
502
503 if not isinstance(value, OrderedDict):
504 raise QAPIExprError(expr_info,
505 "%s should be a dictionary or type name" % source)
506
507 # value is a dictionary, check that each member is okay
508 for (key, arg) in value.items():
509 check_name(expr_info, "Member of %s" % source, key,
510 allow_optional=allow_optional)
511 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
512 raise QAPIExprError(expr_info,
513 "Member of %s uses reserved name '%s'"
514 % (source, key))
515 # Todo: allow dictionaries to represent default values of
516 # an optional argument.
517 check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
518 allow_array=True,
519 allow_metas=['built-in', 'union', 'alternate', 'struct',
520 'enum'])
521
522
523 def check_command(expr, expr_info):
524 name = expr['command']
525 boxed = expr.get('boxed', False)
526
527 args_meta = ['struct']
528 if boxed:
529 args_meta += ['union', 'alternate']
530 check_type(expr_info, "'data' for command '%s'" % name,
531 expr.get('data'), allow_dict=not boxed, allow_optional=True,
532 allow_metas=args_meta)
533 returns_meta = ['union', 'struct']
534 if name in returns_whitelist:
535 returns_meta += ['built-in', 'alternate', 'enum']
536 check_type(expr_info, "'returns' for command '%s'" % name,
537 expr.get('returns'), allow_array=True,
538 allow_optional=True, allow_metas=returns_meta)
539
540
541 def check_event(expr, expr_info):
542 global events
543 name = expr['event']
544 boxed = expr.get('boxed', False)
545
546 meta = ['struct']
547 if boxed:
548 meta += ['union', 'alternate']
549 events.append(name)
550 check_type(expr_info, "'data' for event '%s'" % name,
551 expr.get('data'), allow_dict=not boxed, allow_optional=True,
552 allow_metas=meta)
553
554
555 def check_union(expr, expr_info):
556 name = expr['union']
557 base = expr.get('base')
558 discriminator = expr.get('discriminator')
559 members = expr['data']
560
561 # Two types of unions, determined by discriminator.
562
563 # With no discriminator it is a simple union.
564 if discriminator is None:
565 enum_define = None
566 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
567 if base is not None:
568 raise QAPIExprError(expr_info,
569 "Simple union '%s' must not have a base"
570 % name)
571
572 # Else, it's a flat union.
573 else:
574 # The object must have a string or dictionary 'base'.
575 check_type(expr_info, "'base' for union '%s'" % name,
576 base, allow_dict=True, allow_optional=True,
577 allow_metas=['struct'])
578 if not base:
579 raise QAPIExprError(expr_info,
580 "Flat union '%s' must have a base"
581 % name)
582 base_members = find_base_members(base)
583 assert base_members
584
585 # The value of member 'discriminator' must name a non-optional
586 # member of the base struct.
587 check_name(expr_info, "Discriminator of flat union '%s'" % name,
588 discriminator)
589 discriminator_type = base_members.get(discriminator)
590 if not discriminator_type:
591 raise QAPIExprError(expr_info,
592 "Discriminator '%s' is not a member of base "
593 "struct '%s'"
594 % (discriminator, base))
595 enum_define = find_enum(discriminator_type)
596 allow_metas = ['struct']
597 # Do not allow string discriminator
598 if not enum_define:
599 raise QAPIExprError(expr_info,
600 "Discriminator '%s' must be of enumeration "
601 "type" % discriminator)
602
603 # Check every branch; don't allow an empty union
604 if len(members) == 0:
605 raise QAPIExprError(expr_info,
606 "Union '%s' cannot have empty 'data'" % name)
607 for (key, value) in members.items():
608 check_name(expr_info, "Member of union '%s'" % name, key)
609
610 # Each value must name a known type
611 check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
612 value, allow_array=not base, allow_metas=allow_metas)
613
614 # If the discriminator names an enum type, then all members
615 # of 'data' must also be members of the enum type.
616 if enum_define:
617 if key not in enum_define['enum_values']:
618 raise QAPIExprError(expr_info,
619 "Discriminator value '%s' is not found in "
620 "enum '%s'" %
621 (key, enum_define["enum_name"]))
622
623 # If discriminator is user-defined, ensure all values are covered
624 if enum_define:
625 for value in enum_define['enum_values']:
626 if value not in members.keys():
627 raise QAPIExprError(expr_info,
628 "Union '%s' data missing '%s' branch"
629 % (name, value))
630
631
632 def check_alternate(expr, expr_info):
633 name = expr['alternate']
634 members = expr['data']
635 types_seen = {}
636
637 # Check every branch; require at least two branches
638 if len(members) < 2:
639 raise QAPIExprError(expr_info,
640 "Alternate '%s' should have at least two branches "
641 "in 'data'" % name)
642 for (key, value) in members.items():
643 check_name(expr_info, "Member of alternate '%s'" % name, key)
644
645 # Ensure alternates have no type conflicts.
646 check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
647 value,
648 allow_metas=['built-in', 'union', 'struct', 'enum'])
649 qtype = find_alternate_member_qtype(value)
650 if not qtype:
651 raise QAPIExprError(expr_info,
652 "Alternate '%s' member '%s' cannot use "
653 "type '%s'" % (name, key, value))
654 if qtype in types_seen:
655 raise QAPIExprError(expr_info,
656 "Alternate '%s' member '%s' can't "
657 "be distinguished from member '%s'"
658 % (name, key, types_seen[qtype]))
659 types_seen[qtype] = key
660
661
662 def check_enum(expr, expr_info):
663 name = expr['enum']
664 members = expr.get('data')
665 prefix = expr.get('prefix')
666
667 if not isinstance(members, list):
668 raise QAPIExprError(expr_info,
669 "Enum '%s' requires an array for 'data'" % name)
670 if prefix is not None and not isinstance(prefix, str):
671 raise QAPIExprError(expr_info,
672 "Enum '%s' requires a string for 'prefix'" % name)
673 for member in members:
674 check_name(expr_info, "Member of enum '%s'" % name, member,
675 enum_member=True)
676
677
678 def check_struct(expr, expr_info):
679 name = expr['struct']
680 members = expr['data']
681
682 check_type(expr_info, "'data' for struct '%s'" % name, members,
683 allow_dict=True, allow_optional=True)
684 check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
685 allow_metas=['struct'])
686
687
688 def check_keys(expr_elem, meta, required, optional=[]):
689 expr = expr_elem['expr']
690 info = expr_elem['info']
691 name = expr[meta]
692 if not isinstance(name, str):
693 raise QAPIExprError(info,
694 "'%s' key must have a string value" % meta)
695 required = required + [meta]
696 for (key, value) in expr.items():
697 if key not in required and key not in optional:
698 raise QAPIExprError(info,
699 "Unknown key '%s' in %s '%s'"
700 % (key, meta, name))
701 if (key == 'gen' or key == 'success-response') and value is not False:
702 raise QAPIExprError(info,
703 "'%s' of %s '%s' should only use false value"
704 % (key, meta, name))
705 if key == 'boxed' and value is not True:
706 raise QAPIExprError(info,
707 "'%s' of %s '%s' should only use true value"
708 % (key, meta, name))
709 for key in required:
710 if key not in expr:
711 raise QAPIExprError(info,
712 "Key '%s' is missing from %s '%s'"
713 % (key, meta, name))
714
715
716 def check_exprs(exprs):
717 global all_names
718
719 # Learn the types and check for valid expression keys
720 for builtin in builtin_types.keys():
721 all_names[builtin] = 'built-in'
722 for expr_elem in exprs:
723 expr = expr_elem['expr']
724 info = expr_elem['info']
725 if 'enum' in expr:
726 check_keys(expr_elem, 'enum', ['data'], ['prefix'])
727 add_enum(expr['enum'], info, expr['data'])
728 elif 'union' in expr:
729 check_keys(expr_elem, 'union', ['data'],
730 ['base', 'discriminator'])
731 add_union(expr, info)
732 elif 'alternate' in expr:
733 check_keys(expr_elem, 'alternate', ['data'])
734 add_name(expr['alternate'], info, 'alternate')
735 elif 'struct' in expr:
736 check_keys(expr_elem, 'struct', ['data'], ['base'])
737 add_struct(expr, info)
738 elif 'command' in expr:
739 check_keys(expr_elem, 'command', [],
740 ['data', 'returns', 'gen', 'success-response', 'boxed'])
741 add_name(expr['command'], info, 'command')
742 elif 'event' in expr:
743 check_keys(expr_elem, 'event', [], ['data', 'boxed'])
744 add_name(expr['event'], info, 'event')
745 else:
746 raise QAPIExprError(expr_elem['info'],
747 "Expression is missing metatype")
748
749 # Try again for hidden UnionKind enum
750 for expr_elem in exprs:
751 expr = expr_elem['expr']
752 if 'union' in expr:
753 if not discriminator_find_enum_define(expr):
754 add_enum('%sKind' % expr['union'], expr_elem['info'],
755 implicit=True)
756 elif 'alternate' in expr:
757 add_enum('%sKind' % expr['alternate'], expr_elem['info'],
758 implicit=True)
759
760 # Validate that exprs make sense
761 for expr_elem in exprs:
762 expr = expr_elem['expr']
763 info = expr_elem['info']
764
765 if 'enum' in expr:
766 check_enum(expr, info)
767 elif 'union' in expr:
768 check_union(expr, info)
769 elif 'alternate' in expr:
770 check_alternate(expr, info)
771 elif 'struct' in expr:
772 check_struct(expr, info)
773 elif 'command' in expr:
774 check_command(expr, info)
775 elif 'event' in expr:
776 check_event(expr, info)
777 else:
778 assert False, 'unexpected meta type'
779
780 return exprs
781
782
783 #
784 # Schema compiler frontend
785 #
786
787 class QAPISchemaEntity(object):
788 def __init__(self, name, info):
789 assert isinstance(name, str)
790 self.name = name
791 # For explicitly defined entities, info points to the (explicit)
792 # definition. For builtins (and their arrays), info is None.
793 # For implicitly defined entities, info points to a place that
794 # triggered the implicit definition (there may be more than one
795 # such place).
796 self.info = info
797
798 def c_name(self):
799 return c_name(self.name)
800
801 def check(self, schema):
802 pass
803
804 def is_implicit(self):
805 return not self.info
806
807 def visit(self, visitor):
808 pass
809
810
811 class QAPISchemaVisitor(object):
812 def visit_begin(self, schema):
813 pass
814
815 def visit_end(self):
816 pass
817
818 def visit_needed(self, entity):
819 # Default to visiting everything
820 return True
821
822 def visit_builtin_type(self, name, info, json_type):
823 pass
824
825 def visit_enum_type(self, name, info, values, prefix):
826 pass
827
828 def visit_array_type(self, name, info, element_type):
829 pass
830
831 def visit_object_type(self, name, info, base, members, variants):
832 pass
833
834 def visit_object_type_flat(self, name, info, members, variants):
835 pass
836
837 def visit_alternate_type(self, name, info, variants):
838 pass
839
840 def visit_command(self, name, info, arg_type, ret_type,
841 gen, success_response, boxed):
842 pass
843
844 def visit_event(self, name, info, arg_type, boxed):
845 pass
846
847
848 class QAPISchemaType(QAPISchemaEntity):
849 # Return the C type for common use.
850 # For the types we commonly box, this is a pointer type.
851 def c_type(self):
852 pass
853
854 # Return the C type to be used in a parameter list.
855 def c_param_type(self):
856 return self.c_type()
857
858 # Return the C type to be used where we suppress boxing.
859 def c_unboxed_type(self):
860 return self.c_type()
861
862 def json_type(self):
863 pass
864
865 def alternate_qtype(self):
866 json2qtype = {
867 'string': 'QTYPE_QSTRING',
868 'number': 'QTYPE_QFLOAT',
869 'int': 'QTYPE_QINT',
870 'boolean': 'QTYPE_QBOOL',
871 'object': 'QTYPE_QDICT'
872 }
873 return json2qtype.get(self.json_type())
874
875
876 class QAPISchemaBuiltinType(QAPISchemaType):
877 def __init__(self, name, json_type, c_type):
878 QAPISchemaType.__init__(self, name, None)
879 assert not c_type or isinstance(c_type, str)
880 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
881 'value')
882 self._json_type_name = json_type
883 self._c_type_name = c_type
884
885 def c_name(self):
886 return self.name
887
888 def c_type(self):
889 return self._c_type_name
890
891 def c_param_type(self):
892 if self.name == 'str':
893 return 'const ' + self._c_type_name
894 return self._c_type_name
895
896 def json_type(self):
897 return self._json_type_name
898
899 def visit(self, visitor):
900 visitor.visit_builtin_type(self.name, self.info, self.json_type())
901
902
903 class QAPISchemaEnumType(QAPISchemaType):
904 def __init__(self, name, info, values, prefix):
905 QAPISchemaType.__init__(self, name, info)
906 for v in values:
907 assert isinstance(v, QAPISchemaMember)
908 v.set_owner(name)
909 assert prefix is None or isinstance(prefix, str)
910 self.values = values
911 self.prefix = prefix
912
913 def check(self, schema):
914 seen = {}
915 for v in self.values:
916 v.check_clash(self.info, seen)
917
918 def is_implicit(self):
919 # See QAPISchema._make_implicit_enum_type()
920 return self.name.endswith('Kind')
921
922 def c_type(self):
923 return c_name(self.name)
924
925 def member_names(self):
926 return [v.name for v in self.values]
927
928 def json_type(self):
929 return 'string'
930
931 def visit(self, visitor):
932 visitor.visit_enum_type(self.name, self.info,
933 self.member_names(), self.prefix)
934
935
936 class QAPISchemaArrayType(QAPISchemaType):
937 def __init__(self, name, info, element_type):
938 QAPISchemaType.__init__(self, name, info)
939 assert isinstance(element_type, str)
940 self._element_type_name = element_type
941 self.element_type = None
942
943 def check(self, schema):
944 self.element_type = schema.lookup_type(self._element_type_name)
945 assert self.element_type
946
947 def is_implicit(self):
948 return True
949
950 def c_type(self):
951 return c_name(self.name) + pointer_suffix
952
953 def json_type(self):
954 return 'array'
955
956 def visit(self, visitor):
957 visitor.visit_array_type(self.name, self.info, self.element_type)
958
959
960 class QAPISchemaObjectType(QAPISchemaType):
961 def __init__(self, name, info, base, local_members, variants):
962 # struct has local_members, optional base, and no variants
963 # flat union has base, variants, and no local_members
964 # simple union has local_members, variants, and no base
965 QAPISchemaType.__init__(self, name, info)
966 assert base is None or isinstance(base, str)
967 for m in local_members:
968 assert isinstance(m, QAPISchemaObjectTypeMember)
969 m.set_owner(name)
970 if variants is not None:
971 assert isinstance(variants, QAPISchemaObjectTypeVariants)
972 variants.set_owner(name)
973 self._base_name = base
974 self.base = None
975 self.local_members = local_members
976 self.variants = variants
977 self.members = None
978
979 def check(self, schema):
980 if self.members is False: # check for cycles
981 raise QAPIExprError(self.info,
982 "Object %s contains itself" % self.name)
983 if self.members:
984 return
985 self.members = False # mark as being checked
986 seen = OrderedDict()
987 if self._base_name:
988 self.base = schema.lookup_type(self._base_name)
989 assert isinstance(self.base, QAPISchemaObjectType)
990 self.base.check(schema)
991 self.base.check_clash(schema, self.info, seen)
992 for m in self.local_members:
993 m.check(schema)
994 m.check_clash(self.info, seen)
995 self.members = seen.values()
996 if self.variants:
997 self.variants.check(schema, seen)
998 assert self.variants.tag_member in self.members
999 self.variants.check_clash(schema, self.info, seen)
1000
1001 # Check that the members of this type do not cause duplicate JSON members,
1002 # and update seen to track the members seen so far. Report any errors
1003 # on behalf of info, which is not necessarily self.info
1004 def check_clash(self, schema, info, seen):
1005 assert not self.variants # not implemented
1006 for m in self.members:
1007 m.check_clash(info, seen)
1008
1009 def is_implicit(self):
1010 # See QAPISchema._make_implicit_object_type(), as well as
1011 # _def_predefineds()
1012 return self.name.startswith('q_')
1013
1014 def is_empty(self):
1015 assert self.members is not None
1016 return not self.members and not self.variants
1017
1018 def c_name(self):
1019 assert self.name != 'q_empty'
1020 return QAPISchemaType.c_name(self)
1021
1022 def c_type(self):
1023 assert not self.is_implicit()
1024 return c_name(self.name) + pointer_suffix
1025
1026 def c_unboxed_type(self):
1027 return c_name(self.name)
1028
1029 def json_type(self):
1030 return 'object'
1031
1032 def visit(self, visitor):
1033 visitor.visit_object_type(self.name, self.info,
1034 self.base, self.local_members, self.variants)
1035 visitor.visit_object_type_flat(self.name, self.info,
1036 self.members, self.variants)
1037
1038
1039 class QAPISchemaMember(object):
1040 role = 'member'
1041
1042 def __init__(self, name):
1043 assert isinstance(name, str)
1044 self.name = name
1045 self.owner = None
1046
1047 def set_owner(self, name):
1048 assert not self.owner
1049 self.owner = name
1050
1051 def check_clash(self, info, seen):
1052 cname = c_name(self.name)
1053 if cname.lower() != cname and self.owner not in case_whitelist:
1054 raise QAPIExprError(info,
1055 "%s should not use uppercase" % self.describe())
1056 if cname in seen:
1057 raise QAPIExprError(info,
1058 "%s collides with %s"
1059 % (self.describe(), seen[cname].describe()))
1060 seen[cname] = self
1061
1062 def _pretty_owner(self):
1063 owner = self.owner
1064 if owner.startswith('q_obj_'):
1065 # See QAPISchema._make_implicit_object_type() - reverse the
1066 # mapping there to create a nice human-readable description
1067 owner = owner[6:]
1068 if owner.endswith('-arg'):
1069 return '(parameter of %s)' % owner[:-4]
1070 elif owner.endswith('-base'):
1071 return '(base of %s)' % owner[:-5]
1072 else:
1073 assert owner.endswith('-wrapper')
1074 # Unreachable and not implemented
1075 assert False
1076 if owner.endswith('Kind'):
1077 # See QAPISchema._make_implicit_enum_type()
1078 return '(branch of %s)' % owner[:-4]
1079 return '(%s of %s)' % (self.role, owner)
1080
1081 def describe(self):
1082 return "'%s' %s" % (self.name, self._pretty_owner())
1083
1084
1085 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1086 def __init__(self, name, typ, optional):
1087 QAPISchemaMember.__init__(self, name)
1088 assert isinstance(typ, str)
1089 assert isinstance(optional, bool)
1090 self._type_name = typ
1091 self.type = None
1092 self.optional = optional
1093
1094 def check(self, schema):
1095 assert self.owner
1096 self.type = schema.lookup_type(self._type_name)
1097 assert self.type
1098
1099
1100 class QAPISchemaObjectTypeVariants(object):
1101 def __init__(self, tag_name, tag_member, variants):
1102 # Flat unions pass tag_name but not tag_member.
1103 # Simple unions and alternates pass tag_member but not tag_name.
1104 # After check(), tag_member is always set, and tag_name remains
1105 # a reliable witness of being used by a flat union.
1106 assert bool(tag_member) != bool(tag_name)
1107 assert (isinstance(tag_name, str) or
1108 isinstance(tag_member, QAPISchemaObjectTypeMember))
1109 assert len(variants) > 0
1110 for v in variants:
1111 assert isinstance(v, QAPISchemaObjectTypeVariant)
1112 self._tag_name = tag_name
1113 self.tag_member = tag_member
1114 self.variants = variants
1115
1116 def set_owner(self, name):
1117 for v in self.variants:
1118 v.set_owner(name)
1119
1120 def check(self, schema, seen):
1121 if not self.tag_member: # flat union
1122 self.tag_member = seen[c_name(self._tag_name)]
1123 assert self._tag_name == self.tag_member.name
1124 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1125 for v in self.variants:
1126 v.check(schema)
1127 # Union names must match enum values; alternate names are
1128 # checked separately. Use 'seen' to tell the two apart.
1129 if seen:
1130 assert v.name in self.tag_member.type.member_names()
1131 assert isinstance(v.type, QAPISchemaObjectType)
1132 v.type.check(schema)
1133
1134 def check_clash(self, schema, info, seen):
1135 for v in self.variants:
1136 # Reset seen map for each variant, since qapi names from one
1137 # branch do not affect another branch
1138 assert isinstance(v.type, QAPISchemaObjectType)
1139 v.type.check_clash(schema, info, dict(seen))
1140
1141
1142 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1143 role = 'branch'
1144
1145 def __init__(self, name, typ):
1146 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1147
1148
1149 class QAPISchemaAlternateType(QAPISchemaType):
1150 def __init__(self, name, info, variants):
1151 QAPISchemaType.__init__(self, name, info)
1152 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1153 assert variants.tag_member
1154 variants.set_owner(name)
1155 variants.tag_member.set_owner(self.name)
1156 self.variants = variants
1157
1158 def check(self, schema):
1159 self.variants.tag_member.check(schema)
1160 # Not calling self.variants.check_clash(), because there's nothing
1161 # to clash with
1162 self.variants.check(schema, {})
1163 # Alternate branch names have no relation to the tag enum values;
1164 # so we have to check for potential name collisions ourselves.
1165 seen = {}
1166 for v in self.variants.variants:
1167 v.check_clash(self.info, seen)
1168
1169 def c_type(self):
1170 return c_name(self.name) + pointer_suffix
1171
1172 def json_type(self):
1173 return 'value'
1174
1175 def visit(self, visitor):
1176 visitor.visit_alternate_type(self.name, self.info, self.variants)
1177
1178 def is_empty(self):
1179 return False
1180
1181
1182 class QAPISchemaCommand(QAPISchemaEntity):
1183 def __init__(self, name, info, arg_type, ret_type, gen, success_response,
1184 boxed):
1185 QAPISchemaEntity.__init__(self, name, info)
1186 assert not arg_type or isinstance(arg_type, str)
1187 assert not ret_type or isinstance(ret_type, str)
1188 self._arg_type_name = arg_type
1189 self.arg_type = None
1190 self._ret_type_name = ret_type
1191 self.ret_type = None
1192 self.gen = gen
1193 self.success_response = success_response
1194 self.boxed = boxed
1195
1196 def check(self, schema):
1197 if self._arg_type_name:
1198 self.arg_type = schema.lookup_type(self._arg_type_name)
1199 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1200 isinstance(self.arg_type, QAPISchemaAlternateType))
1201 self.arg_type.check(schema)
1202 if self.boxed:
1203 if self.arg_type.is_empty():
1204 raise QAPIExprError(self.info,
1205 "Cannot use 'boxed' with empty type")
1206 else:
1207 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1208 assert not self.arg_type.variants
1209 elif self.boxed:
1210 raise QAPIExprError(self.info,
1211 "Use of 'boxed' requires 'data'")
1212 if self._ret_type_name:
1213 self.ret_type = schema.lookup_type(self._ret_type_name)
1214 assert isinstance(self.ret_type, QAPISchemaType)
1215
1216 def visit(self, visitor):
1217 visitor.visit_command(self.name, self.info,
1218 self.arg_type, self.ret_type,
1219 self.gen, self.success_response, self.boxed)
1220
1221
1222 class QAPISchemaEvent(QAPISchemaEntity):
1223 def __init__(self, name, info, arg_type, boxed):
1224 QAPISchemaEntity.__init__(self, name, info)
1225 assert not arg_type or isinstance(arg_type, str)
1226 self._arg_type_name = arg_type
1227 self.arg_type = None
1228 self.boxed = boxed
1229
1230 def check(self, schema):
1231 if self._arg_type_name:
1232 self.arg_type = schema.lookup_type(self._arg_type_name)
1233 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1234 isinstance(self.arg_type, QAPISchemaAlternateType))
1235 self.arg_type.check(schema)
1236 if self.boxed:
1237 if self.arg_type.is_empty():
1238 raise QAPIExprError(self.info,
1239 "Cannot use 'boxed' with empty type")
1240 else:
1241 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1242 assert not self.arg_type.variants
1243 elif self.boxed:
1244 raise QAPIExprError(self.info,
1245 "Use of 'boxed' requires 'data'")
1246
1247 def visit(self, visitor):
1248 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1249
1250
1251 class QAPISchema(object):
1252 def __init__(self, fname):
1253 try:
1254 self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
1255 self._entity_dict = {}
1256 self._predefining = True
1257 self._def_predefineds()
1258 self._predefining = False
1259 self._def_exprs()
1260 self.check()
1261 except (QAPISchemaError, QAPIExprError) as err:
1262 print >>sys.stderr, err
1263 exit(1)
1264
1265 def _def_entity(self, ent):
1266 # Only the predefined types are allowed to not have info
1267 assert ent.info or self._predefining
1268 assert ent.name not in self._entity_dict
1269 self._entity_dict[ent.name] = ent
1270
1271 def lookup_entity(self, name, typ=None):
1272 ent = self._entity_dict.get(name)
1273 if typ and not isinstance(ent, typ):
1274 return None
1275 return ent
1276
1277 def lookup_type(self, name):
1278 return self.lookup_entity(name, QAPISchemaType)
1279
1280 def _def_builtin_type(self, name, json_type, c_type):
1281 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1282 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1283 # qapi-types.h from a single .c, all arrays of builtins must be
1284 # declared in the first file whether or not they are used. Nicer
1285 # would be to use lazy instantiation, while figuring out how to
1286 # avoid compilation issues with multiple qapi-types.h.
1287 self._make_array_type(name, None)
1288
1289 def _def_predefineds(self):
1290 for t in [('str', 'string', 'char' + pointer_suffix),
1291 ('number', 'number', 'double'),
1292 ('int', 'int', 'int64_t'),
1293 ('int8', 'int', 'int8_t'),
1294 ('int16', 'int', 'int16_t'),
1295 ('int32', 'int', 'int32_t'),
1296 ('int64', 'int', 'int64_t'),
1297 ('uint8', 'int', 'uint8_t'),
1298 ('uint16', 'int', 'uint16_t'),
1299 ('uint32', 'int', 'uint32_t'),
1300 ('uint64', 'int', 'uint64_t'),
1301 ('size', 'int', 'uint64_t'),
1302 ('bool', 'boolean', 'bool'),
1303 ('any', 'value', 'QObject' + pointer_suffix)]:
1304 self._def_builtin_type(*t)
1305 self.the_empty_object_type = QAPISchemaObjectType('q_empty', None,
1306 None, [], None)
1307 self._def_entity(self.the_empty_object_type)
1308 qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
1309 'qstring', 'qdict', 'qlist',
1310 'qfloat', 'qbool'])
1311 self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
1312 'QTYPE'))
1313
1314 def _make_enum_members(self, values):
1315 return [QAPISchemaMember(v) for v in values]
1316
1317 def _make_implicit_enum_type(self, name, info, values):
1318 # See also QAPISchemaObjectTypeMember._pretty_owner()
1319 name = name + 'Kind' # Use namespace reserved by add_name()
1320 self._def_entity(QAPISchemaEnumType(
1321 name, info, self._make_enum_members(values), None))
1322 return name
1323
1324 def _make_array_type(self, element_type, info):
1325 name = element_type + 'List' # Use namespace reserved by add_name()
1326 if not self.lookup_type(name):
1327 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1328 return name
1329
1330 def _make_implicit_object_type(self, name, info, role, members):
1331 if not members:
1332 return None
1333 # See also QAPISchemaObjectTypeMember._pretty_owner()
1334 name = 'q_obj_%s-%s' % (name, role)
1335 if not self.lookup_entity(name, QAPISchemaObjectType):
1336 self._def_entity(QAPISchemaObjectType(name, info, None,
1337 members, None))
1338 return name
1339
1340 def _def_enum_type(self, expr, info):
1341 name = expr['enum']
1342 data = expr['data']
1343 prefix = expr.get('prefix')
1344 self._def_entity(QAPISchemaEnumType(
1345 name, info, self._make_enum_members(data), prefix))
1346
1347 def _make_member(self, name, typ, info):
1348 optional = False
1349 if name.startswith('*'):
1350 name = name[1:]
1351 optional = True
1352 if isinstance(typ, list):
1353 assert len(typ) == 1
1354 typ = self._make_array_type(typ[0], info)
1355 return QAPISchemaObjectTypeMember(name, typ, optional)
1356
1357 def _make_members(self, data, info):
1358 return [self._make_member(key, value, info)
1359 for (key, value) in data.iteritems()]
1360
1361 def _def_struct_type(self, expr, info):
1362 name = expr['struct']
1363 base = expr.get('base')
1364 data = expr['data']
1365 self._def_entity(QAPISchemaObjectType(name, info, base,
1366 self._make_members(data, info),
1367 None))
1368
1369 def _make_variant(self, case, typ):
1370 return QAPISchemaObjectTypeVariant(case, typ)
1371
1372 def _make_simple_variant(self, case, typ, info):
1373 if isinstance(typ, list):
1374 assert len(typ) == 1
1375 typ = self._make_array_type(typ[0], info)
1376 typ = self._make_implicit_object_type(
1377 typ, info, 'wrapper', [self._make_member('data', typ, info)])
1378 return QAPISchemaObjectTypeVariant(case, typ)
1379
1380 def _def_union_type(self, expr, info):
1381 name = expr['union']
1382 data = expr['data']
1383 base = expr.get('base')
1384 tag_name = expr.get('discriminator')
1385 tag_member = None
1386 if isinstance(base, dict):
1387 base = (self._make_implicit_object_type(
1388 name, info, 'base', self._make_members(base, info)))
1389 if tag_name:
1390 variants = [self._make_variant(key, value)
1391 for (key, value) in data.iteritems()]
1392 members = []
1393 else:
1394 variants = [self._make_simple_variant(key, value, info)
1395 for (key, value) in data.iteritems()]
1396 typ = self._make_implicit_enum_type(name, info,
1397 [v.name for v in variants])
1398 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1399 members = [tag_member]
1400 self._def_entity(
1401 QAPISchemaObjectType(name, info, base, members,
1402 QAPISchemaObjectTypeVariants(tag_name,
1403 tag_member,
1404 variants)))
1405
1406 def _def_alternate_type(self, expr, info):
1407 name = expr['alternate']
1408 data = expr['data']
1409 variants = [self._make_variant(key, value)
1410 for (key, value) in data.iteritems()]
1411 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1412 self._def_entity(
1413 QAPISchemaAlternateType(name, info,
1414 QAPISchemaObjectTypeVariants(None,
1415 tag_member,
1416 variants)))
1417
1418 def _def_command(self, expr, info):
1419 name = expr['command']
1420 data = expr.get('data')
1421 rets = expr.get('returns')
1422 gen = expr.get('gen', True)
1423 success_response = expr.get('success-response', True)
1424 boxed = expr.get('boxed', False)
1425 if isinstance(data, OrderedDict):
1426 data = self._make_implicit_object_type(
1427 name, info, 'arg', self._make_members(data, info))
1428 if isinstance(rets, list):
1429 assert len(rets) == 1
1430 rets = self._make_array_type(rets[0], info)
1431 self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
1432 success_response, boxed))
1433
1434 def _def_event(self, expr, info):
1435 name = expr['event']
1436 data = expr.get('data')
1437 boxed = expr.get('boxed', False)
1438 if isinstance(data, OrderedDict):
1439 data = self._make_implicit_object_type(
1440 name, info, 'arg', self._make_members(data, info))
1441 self._def_entity(QAPISchemaEvent(name, info, data, boxed))
1442
1443 def _def_exprs(self):
1444 for expr_elem in self.exprs:
1445 expr = expr_elem['expr']
1446 info = expr_elem['info']
1447 if 'enum' in expr:
1448 self._def_enum_type(expr, info)
1449 elif 'struct' in expr:
1450 self._def_struct_type(expr, info)
1451 elif 'union' in expr:
1452 self._def_union_type(expr, info)
1453 elif 'alternate' in expr:
1454 self._def_alternate_type(expr, info)
1455 elif 'command' in expr:
1456 self._def_command(expr, info)
1457 elif 'event' in expr:
1458 self._def_event(expr, info)
1459 else:
1460 assert False
1461
1462 def check(self):
1463 for ent in self._entity_dict.values():
1464 ent.check(self)
1465
1466 def visit(self, visitor):
1467 visitor.visit_begin(self)
1468 for (name, entity) in sorted(self._entity_dict.items()):
1469 if visitor.visit_needed(entity):
1470 entity.visit(visitor)
1471 visitor.visit_end()
1472
1473
1474 #
1475 # Code generation helpers
1476 #
1477
1478 def camel_case(name):
1479 new_name = ''
1480 first = True
1481 for ch in name:
1482 if ch in ['_', '-']:
1483 first = True
1484 elif first:
1485 new_name += ch.upper()
1486 first = False
1487 else:
1488 new_name += ch.lower()
1489 return new_name
1490
1491
1492 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1493 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1494 # ENUM24_Name -> ENUM24_NAME
1495 def camel_to_upper(value):
1496 c_fun_str = c_name(value, False)
1497 if value.isupper():
1498 return c_fun_str
1499
1500 new_name = ''
1501 l = len(c_fun_str)
1502 for i in range(l):
1503 c = c_fun_str[i]
1504 # When c is upper and no "_" appears before, do more checks
1505 if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
1506 if i < l - 1 and c_fun_str[i + 1].islower():
1507 new_name += '_'
1508 elif c_fun_str[i - 1].isdigit():
1509 new_name += '_'
1510 new_name += c
1511 return new_name.lstrip('_').upper()
1512
1513
1514 def c_enum_const(type_name, const_name, prefix=None):
1515 if prefix is not None:
1516 type_name = prefix
1517 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1518
1519 c_name_trans = string.maketrans('.-', '__')
1520
1521
1522 # Map @name to a valid C identifier.
1523 # If @protect, avoid returning certain ticklish identifiers (like
1524 # C keywords) by prepending "q_".
1525 #
1526 # Used for converting 'name' from a 'name':'type' qapi definition
1527 # into a generated struct member, as well as converting type names
1528 # into substrings of a generated C function name.
1529 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1530 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1531 def c_name(name, protect=True):
1532 # ANSI X3J11/88-090, 3.1.1
1533 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1534 'default', 'do', 'double', 'else', 'enum', 'extern',
1535 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1536 'return', 'short', 'signed', 'sizeof', 'static',
1537 'struct', 'switch', 'typedef', 'union', 'unsigned',
1538 'void', 'volatile', 'while'])
1539 # ISO/IEC 9899:1999, 6.4.1
1540 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1541 # ISO/IEC 9899:2011, 6.4.1
1542 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1543 '_Noreturn', '_Static_assert', '_Thread_local'])
1544 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1545 # excluding _.*
1546 gcc_words = set(['asm', 'typeof'])
1547 # C++ ISO/IEC 14882:2003 2.11
1548 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1549 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1550 'namespace', 'new', 'operator', 'private', 'protected',
1551 'public', 'reinterpret_cast', 'static_cast', 'template',
1552 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1553 'using', 'virtual', 'wchar_t',
1554 # alternative representations
1555 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1556 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1557 # namespace pollution:
1558 polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1559 name = name.translate(c_name_trans)
1560 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1561 | cpp_words | polluted_words):
1562 return "q_" + name
1563 return name
1564
1565 eatspace = '\033EATSPACE.'
1566 pointer_suffix = ' *' + eatspace
1567
1568
1569 def genindent(count):
1570 ret = ""
1571 for _ in range(count):
1572 ret += " "
1573 return ret
1574
1575 indent_level = 0
1576
1577
1578 def push_indent(indent_amount=4):
1579 global indent_level
1580 indent_level += indent_amount
1581
1582
1583 def pop_indent(indent_amount=4):
1584 global indent_level
1585 indent_level -= indent_amount
1586
1587
1588 # Generate @code with @kwds interpolated.
1589 # Obey indent_level, and strip eatspace.
1590 def cgen(code, **kwds):
1591 raw = code % kwds
1592 if indent_level:
1593 indent = genindent(indent_level)
1594 # re.subn() lacks flags support before Python 2.7, use re.compile()
1595 raw = re.subn(re.compile("^.", re.MULTILINE),
1596 indent + r'\g<0>', raw)
1597 raw = raw[0]
1598 return re.sub(re.escape(eatspace) + ' *', '', raw)
1599
1600
1601 def mcgen(code, **kwds):
1602 if code[0] == '\n':
1603 code = code[1:]
1604 return cgen(code, **kwds)
1605
1606
1607 def guardname(filename):
1608 return c_name(filename, protect=False).upper()
1609
1610
1611 def guardstart(name):
1612 return mcgen('''
1613
1614 #ifndef %(name)s
1615 #define %(name)s
1616
1617 ''',
1618 name=guardname(name))
1619
1620
1621 def guardend(name):
1622 return mcgen('''
1623
1624 #endif /* %(name)s */
1625
1626 ''',
1627 name=guardname(name))
1628
1629
1630 def gen_enum_lookup(name, values, prefix=None):
1631 ret = mcgen('''
1632
1633 const char *const %(c_name)s_lookup[] = {
1634 ''',
1635 c_name=c_name(name))
1636 for value in values:
1637 index = c_enum_const(name, value, prefix)
1638 ret += mcgen('''
1639 [%(index)s] = "%(value)s",
1640 ''',
1641 index=index, value=value)
1642
1643 max_index = c_enum_const(name, '_MAX', prefix)
1644 ret += mcgen('''
1645 [%(max_index)s] = NULL,
1646 };
1647 ''',
1648 max_index=max_index)
1649 return ret
1650
1651
1652 def gen_enum(name, values, prefix=None):
1653 # append automatically generated _MAX value
1654 enum_values = values + ['_MAX']
1655
1656 ret = mcgen('''
1657
1658 typedef enum %(c_name)s {
1659 ''',
1660 c_name=c_name(name))
1661
1662 i = 0
1663 for value in enum_values:
1664 ret += mcgen('''
1665 %(c_enum)s = %(i)d,
1666 ''',
1667 c_enum=c_enum_const(name, value, prefix),
1668 i=i)
1669 i += 1
1670
1671 ret += mcgen('''
1672 } %(c_name)s;
1673 ''',
1674 c_name=c_name(name))
1675
1676 ret += mcgen('''
1677
1678 extern const char *const %(c_name)s_lookup[];
1679 ''',
1680 c_name=c_name(name))
1681 return ret
1682
1683
1684 def gen_params(arg_type, boxed, extra):
1685 if not arg_type:
1686 assert not boxed
1687 return extra
1688 ret = ''
1689 sep = ''
1690 if boxed:
1691 ret += '%s arg' % arg_type.c_param_type()
1692 sep = ', '
1693 else:
1694 assert not arg_type.variants
1695 for memb in arg_type.members:
1696 ret += sep
1697 sep = ', '
1698 if memb.optional:
1699 ret += 'bool has_%s, ' % c_name(memb.name)
1700 ret += '%s %s' % (memb.type.c_param_type(),
1701 c_name(memb.name))
1702 if extra:
1703 ret += sep + extra
1704 return ret
1705
1706
1707 #
1708 # Common command line parsing
1709 #
1710
1711
1712 def parse_command_line(extra_options="", extra_long_options=[]):
1713
1714 try:
1715 opts, args = getopt.gnu_getopt(sys.argv[1:],
1716 "chp:o:" + extra_options,
1717 ["source", "header", "prefix=",
1718 "output-dir="] + extra_long_options)
1719 except getopt.GetoptError as err:
1720 print >>sys.stderr, "%s: %s" % (sys.argv[0], str(err))
1721 sys.exit(1)
1722
1723 output_dir = ""
1724 prefix = ""
1725 do_c = False
1726 do_h = False
1727 extra_opts = []
1728
1729 for oa in opts:
1730 o, a = oa
1731 if o in ("-p", "--prefix"):
1732 match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a)
1733 if match.end() != len(a):
1734 print >>sys.stderr, \
1735 "%s: 'funny character '%s' in argument of --prefix" \
1736 % (sys.argv[0], a[match.end()])
1737 sys.exit(1)
1738 prefix = a
1739 elif o in ("-o", "--output-dir"):
1740 output_dir = a + "/"
1741 elif o in ("-c", "--source"):
1742 do_c = True
1743 elif o in ("-h", "--header"):
1744 do_h = True
1745 else:
1746 extra_opts.append(oa)
1747
1748 if not do_c and not do_h:
1749 do_c = True
1750 do_h = True
1751
1752 if len(args) != 1:
1753 print >>sys.stderr, "%s: need exactly one argument" % sys.argv[0]
1754 sys.exit(1)
1755 fname = args[0]
1756
1757 return (fname, output_dir, do_c, do_h, prefix, extra_opts)
1758
1759 #
1760 # Generate output files with boilerplate
1761 #
1762
1763
1764 def open_output(output_dir, do_c, do_h, prefix, c_file, h_file,
1765 c_comment, h_comment):
1766 guard = guardname(prefix + h_file)
1767 c_file = output_dir + prefix + c_file
1768 h_file = output_dir + prefix + h_file
1769
1770 if output_dir:
1771 try:
1772 os.makedirs(output_dir)
1773 except os.error as e:
1774 if e.errno != errno.EEXIST:
1775 raise
1776
1777 def maybe_open(really, name, opt):
1778 if really:
1779 return open(name, opt)
1780 else:
1781 import StringIO
1782 return StringIO.StringIO()
1783
1784 fdef = maybe_open(do_c, c_file, 'w')
1785 fdecl = maybe_open(do_h, h_file, 'w')
1786
1787 fdef.write(mcgen('''
1788 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1789 %(comment)s
1790 ''',
1791 comment=c_comment))
1792
1793 fdecl.write(mcgen('''
1794 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1795 %(comment)s
1796 #ifndef %(guard)s
1797 #define %(guard)s
1798
1799 ''',
1800 comment=h_comment, guard=guard))
1801
1802 return (fdef, fdecl)
1803
1804
1805 def close_output(fdef, fdecl):
1806 fdecl.write('''
1807 #endif
1808 ''')
1809 fdecl.close()
1810 fdef.close()