+ def visit_event(self, name, info, arg_type):
+ pass
+
+
+class QAPISchemaType(QAPISchemaEntity):
+ # Return the C type for common use.
+ # For the types we commonly box, this is a pointer type.
+ def c_type(self):
+ pass
+
+ # Return the C type to be used in a parameter list.
+ def c_param_type(self):
+ return self.c_type()
+
+ # Return the C type to be used where we suppress boxing.
+ def c_unboxed_type(self):
+ return self.c_type()
+
+ def json_type(self):
+ pass
+
+ def alternate_qtype(self):
+ json2qtype = {
+ 'string': 'QTYPE_QSTRING',
+ 'number': 'QTYPE_QFLOAT',
+ 'int': 'QTYPE_QINT',
+ 'boolean': 'QTYPE_QBOOL',
+ 'object': 'QTYPE_QDICT'
+ }
+ return json2qtype.get(self.json_type())
+
+
+class QAPISchemaBuiltinType(QAPISchemaType):
+ def __init__(self, name, json_type, c_type):
+ QAPISchemaType.__init__(self, name, None)
+ assert not c_type or isinstance(c_type, str)
+ assert json_type in ('string', 'number', 'int', 'boolean', 'null',
+ 'value')
+ self._json_type_name = json_type
+ self._c_type_name = c_type
+
+ def c_name(self):
+ return self.name
+
+ def c_type(self):
+ return self._c_type_name
+
+ def c_param_type(self):
+ if self.name == 'str':
+ return 'const ' + self._c_type_name
+ return self._c_type_name
+
+ def json_type(self):
+ return self._json_type_name
+
+ def visit(self, visitor):
+ visitor.visit_builtin_type(self.name, self.info, self.json_type())
+
+
+class QAPISchemaEnumType(QAPISchemaType):
+ def __init__(self, name, info, values, prefix):
+ QAPISchemaType.__init__(self, name, info)
+ for v in values:
+ assert isinstance(v, QAPISchemaMember)
+ v.set_owner(name)
+ assert prefix is None or isinstance(prefix, str)
+ self.values = values
+ self.prefix = prefix
+
+ def check(self, schema):
+ seen = {}
+ for v in self.values:
+ v.check_clash(self.info, seen)
+
+ def is_implicit(self):
+ # See QAPISchema._make_implicit_enum_type()
+ return self.name.endswith('Kind')
+
+ def c_type(self):
+ return c_name(self.name)
+
+ def member_names(self):
+ return [v.name for v in self.values]
+
+ def json_type(self):
+ return 'string'
+
+ def visit(self, visitor):
+ visitor.visit_enum_type(self.name, self.info,
+ self.member_names(), self.prefix)
+
+
+class QAPISchemaArrayType(QAPISchemaType):
+ def __init__(self, name, info, element_type):
+ QAPISchemaType.__init__(self, name, info)
+ assert isinstance(element_type, str)
+ self._element_type_name = element_type
+ self.element_type = None
+
+ def check(self, schema):
+ self.element_type = schema.lookup_type(self._element_type_name)
+ assert self.element_type
+
+ def is_implicit(self):
+ return True
+
+ def c_type(self):
+ return c_name(self.name) + pointer_suffix
+
+ def json_type(self):
+ return 'array'
+
+ def visit(self, visitor):
+ visitor.visit_array_type(self.name, self.info, self.element_type)
+
+
+class QAPISchemaObjectType(QAPISchemaType):
+ def __init__(self, name, info, base, local_members, variants):
+ # struct has local_members, optional base, and no variants
+ # flat union has base, variants, and no local_members
+ # simple union has local_members, variants, and no base
+ QAPISchemaType.__init__(self, name, info)
+ assert base is None or isinstance(base, str)
+ for m in local_members:
+ assert isinstance(m, QAPISchemaObjectTypeMember)
+ m.set_owner(name)
+ if variants is not None:
+ assert isinstance(variants, QAPISchemaObjectTypeVariants)
+ variants.set_owner(name)
+ self._base_name = base
+ self.base = None
+ self.local_members = local_members
+ self.variants = variants
+ self.members = None
+
+ def check(self, schema):
+ if self.members is False: # check for cycles
+ raise QAPIExprError(self.info,
+ "Object %s contains itself" % self.name)
+ if self.members:
+ return
+ self.members = False # mark as being checked
+ seen = OrderedDict()
+ if self._base_name:
+ self.base = schema.lookup_type(self._base_name)
+ assert isinstance(self.base, QAPISchemaObjectType)
+ self.base.check(schema)
+ self.base.check_clash(schema, self.info, seen)
+ for m in self.local_members:
+ m.check(schema)
+ m.check_clash(self.info, seen)
+ self.members = seen.values()
+ if self.variants:
+ self.variants.check(schema, seen)
+ assert self.variants.tag_member in self.members
+ self.variants.check_clash(schema, self.info, seen)
+
+ # Check that the members of this type do not cause duplicate JSON members,
+ # and update seen to track the members seen so far. Report any errors
+ # on behalf of info, which is not necessarily self.info
+ def check_clash(self, schema, info, seen):
+ assert not self.variants # not implemented
+ for m in self.members:
+ m.check_clash(info, seen)
+
+ def is_implicit(self):
+ # See QAPISchema._make_implicit_object_type(), as well as
+ # _def_predefineds()
+ return self.name.startswith('q_')
+
+ def c_name(self):
+ return QAPISchemaType.c_name(self)
+
+ def c_type(self):
+ assert not self.is_implicit()
+ return c_name(self.name) + pointer_suffix
+
+ def c_unboxed_type(self):
+ return c_name(self.name)
+
+ def json_type(self):
+ return 'object'
+
+ def visit(self, visitor):
+ visitor.visit_object_type(self.name, self.info,
+ self.base, self.local_members, self.variants)
+ visitor.visit_object_type_flat(self.name, self.info,
+ self.members, self.variants)
+
+
+class QAPISchemaMember(object):
+ role = 'member'
+
+ def __init__(self, name):
+ assert isinstance(name, str)
+ self.name = name
+ self.owner = None
+
+ def set_owner(self, name):
+ assert not self.owner
+ self.owner = name
+
+ def check_clash(self, info, seen):
+ cname = c_name(self.name)
+ if cname.lower() != cname and self.owner not in case_whitelist:
+ raise QAPIExprError(info,
+ "%s should not use uppercase" % self.describe())
+ if cname in seen:
+ raise QAPIExprError(info,
+ "%s collides with %s"
+ % (self.describe(), seen[cname].describe()))
+ seen[cname] = self
+
+ def _pretty_owner(self):
+ owner = self.owner
+ if owner.startswith('q_obj_'):
+ # See QAPISchema._make_implicit_object_type() - reverse the
+ # mapping there to create a nice human-readable description
+ owner = owner[6:]
+ if owner.endswith('-arg'):
+ return '(parameter of %s)' % owner[:-4]
+ elif owner.endswith('-base'):
+ return '(base of %s)' % owner[:-5]
+ else:
+ assert owner.endswith('-wrapper')
+ # Unreachable and not implemented
+ assert False
+ if owner.endswith('Kind'):
+ # See QAPISchema._make_implicit_enum_type()
+ return '(branch of %s)' % owner[:-4]
+ return '(%s of %s)' % (self.role, owner)
+
+ def describe(self):
+ return "'%s' %s" % (self.name, self._pretty_owner())
+
+
+class QAPISchemaObjectTypeMember(QAPISchemaMember):
+ def __init__(self, name, typ, optional):
+ QAPISchemaMember.__init__(self, name)
+ assert isinstance(typ, str)
+ assert isinstance(optional, bool)
+ self._type_name = typ
+ self.type = None
+ self.optional = optional
+
+ def check(self, schema):
+ assert self.owner
+ self.type = schema.lookup_type(self._type_name)
+ assert self.type
+
+
+class QAPISchemaObjectTypeVariants(object):
+ def __init__(self, tag_name, tag_member, variants):
+ # Flat unions pass tag_name but not tag_member.
+ # Simple unions and alternates pass tag_member but not tag_name.
+ # After check(), tag_member is always set, and tag_name remains
+ # a reliable witness of being used by a flat union.
+ assert bool(tag_member) != bool(tag_name)
+ assert (isinstance(tag_name, str) or
+ isinstance(tag_member, QAPISchemaObjectTypeMember))
+ assert len(variants) > 0
+ for v in variants:
+ assert isinstance(v, QAPISchemaObjectTypeVariant)
+ self.tag_name = tag_name
+ self.tag_member = tag_member
+ self.variants = variants
+
+ def set_owner(self, name):
+ for v in self.variants:
+ v.set_owner(name)
+
+ def check(self, schema, seen):
+ if not self.tag_member: # flat union
+ self.tag_member = seen[c_name(self.tag_name)]
+ assert self.tag_name == self.tag_member.name
+ assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+ for v in self.variants:
+ v.check(schema)
+ # Union names must match enum values; alternate names are
+ # checked separately. Use 'seen' to tell the two apart.
+ if seen:
+ assert v.name in self.tag_member.type.member_names()
+ assert isinstance(v.type, QAPISchemaObjectType)
+ v.type.check(schema)
+
+ def check_clash(self, schema, info, seen):
+ for v in self.variants:
+ # Reset seen map for each variant, since qapi names from one
+ # branch do not affect another branch
+ assert isinstance(v.type, QAPISchemaObjectType)
+ v.type.check_clash(schema, info, dict(seen))
+
+
+class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+ role = 'branch'
+
+ def __init__(self, name, typ):
+ QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
+
+
+class QAPISchemaAlternateType(QAPISchemaType):
+ def __init__(self, name, info, variants):
+ QAPISchemaType.__init__(self, name, info)
+ assert isinstance(variants, QAPISchemaObjectTypeVariants)
+ assert not variants.tag_name
+ variants.set_owner(name)
+ variants.tag_member.set_owner(self.name)
+ self.variants = variants
+
+ def check(self, schema):
+ self.variants.tag_member.check(schema)
+ # Not calling self.variants.check_clash(), because there's nothing
+ # to clash with
+ self.variants.check(schema, {})
+ # Alternate branch names have no relation to the tag enum values;
+ # so we have to check for potential name collisions ourselves.
+ seen = {}
+ for v in self.variants.variants:
+ v.check_clash(self.info, seen)
+
+ def c_type(self):
+ return c_name(self.name) + pointer_suffix
+
+ def json_type(self):
+ return 'value'
+
+ def visit(self, visitor):
+ visitor.visit_alternate_type(self.name, self.info, self.variants)
+
+
+class QAPISchemaCommand(QAPISchemaEntity):
+ def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+ QAPISchemaEntity.__init__(self, name, info)
+ assert not arg_type or isinstance(arg_type, str)
+ assert not ret_type or isinstance(ret_type, str)
+ self._arg_type_name = arg_type
+ self.arg_type = None
+ self._ret_type_name = ret_type
+ self.ret_type = None
+ self.gen = gen
+ self.success_response = success_response
+
+ def check(self, schema):
+ if self._arg_type_name:
+ self.arg_type = schema.lookup_type(self._arg_type_name)
+ assert isinstance(self.arg_type, QAPISchemaObjectType)
+ assert not self.arg_type.variants # not implemented
+ if self._ret_type_name:
+ self.ret_type = schema.lookup_type(self._ret_type_name)
+ assert isinstance(self.ret_type, QAPISchemaType)
+
+ def visit(self, visitor):
+ visitor.visit_command(self.name, self.info,
+ self.arg_type, self.ret_type,
+ self.gen, self.success_response)
+
+
+class QAPISchemaEvent(QAPISchemaEntity):
+ def __init__(self, name, info, arg_type):
+ QAPISchemaEntity.__init__(self, name, info)
+ assert not arg_type or isinstance(arg_type, str)
+ self._arg_type_name = arg_type
+ self.arg_type = None
+
+ def check(self, schema):
+ if self._arg_type_name:
+ self.arg_type = schema.lookup_type(self._arg_type_name)
+ assert isinstance(self.arg_type, QAPISchemaObjectType)
+ assert not self.arg_type.variants # not implemented
+
+ def visit(self, visitor):
+ visitor.visit_event(self.name, self.info, self.arg_type)
+
+
+class QAPISchema(object):
+ def __init__(self, fname):
+ try:
+ self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
+ self._entity_dict = {}
+ self._predefining = True
+ self._def_predefineds()
+ self._predefining = False
+ self._def_exprs()
+ self.check()
+ except (QAPISchemaError, QAPIExprError) as err:
+ print >>sys.stderr, err
+ exit(1)
+
+ def _def_entity(self, ent):
+ # Only the predefined types are allowed to not have info
+ assert ent.info or self._predefining
+ assert ent.name not in self._entity_dict
+ self._entity_dict[ent.name] = ent
+
+ def lookup_entity(self, name, typ=None):
+ ent = self._entity_dict.get(name)
+ if typ and not isinstance(ent, typ):
+ return None
+ return ent
+
+ def lookup_type(self, name):
+ return self.lookup_entity(name, QAPISchemaType)
+
+ def _def_builtin_type(self, name, json_type, c_type):
+ self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
+ # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
+ # qapi-types.h from a single .c, all arrays of builtins must be
+ # declared in the first file whether or not they are used. Nicer
+ # would be to use lazy instantiation, while figuring out how to
+ # avoid compilation issues with multiple qapi-types.h.
+ self._make_array_type(name, None)
+
+ def _def_predefineds(self):
+ for t in [('str', 'string', 'char' + pointer_suffix),
+ ('number', 'number', 'double'),
+ ('int', 'int', 'int64_t'),
+ ('int8', 'int', 'int8_t'),
+ ('int16', 'int', 'int16_t'),
+ ('int32', 'int', 'int32_t'),
+ ('int64', 'int', 'int64_t'),
+ ('uint8', 'int', 'uint8_t'),
+ ('uint16', 'int', 'uint16_t'),
+ ('uint32', 'int', 'uint32_t'),
+ ('uint64', 'int', 'uint64_t'),
+ ('size', 'int', 'uint64_t'),
+ ('bool', 'boolean', 'bool'),
+ ('any', 'value', 'QObject' + pointer_suffix)]:
+ self._def_builtin_type(*t)
+ self.the_empty_object_type = QAPISchemaObjectType('q_empty', None,
+ None, [], None)
+ self._def_entity(self.the_empty_object_type)
+ qtype_values = self._make_enum_members(['none', 'qnull', 'qint',
+ 'qstring', 'qdict', 'qlist',
+ 'qfloat', 'qbool'])
+ self._def_entity(QAPISchemaEnumType('QType', None, qtype_values,
+ 'QTYPE'))
+
+ def _make_enum_members(self, values):
+ return [QAPISchemaMember(v) for v in values]
+
+ def _make_implicit_enum_type(self, name, info, values):
+ # See also QAPISchemaObjectTypeMember._pretty_owner()
+ name = name + 'Kind' # Use namespace reserved by add_name()
+ self._def_entity(QAPISchemaEnumType(
+ name, info, self._make_enum_members(values), None))
+ return name
+
+ def _make_array_type(self, element_type, info):
+ name = element_type + 'List' # Use namespace reserved by add_name()
+ if not self.lookup_type(name):
+ self._def_entity(QAPISchemaArrayType(name, info, element_type))
+ return name
+
+ def _make_implicit_object_type(self, name, info, role, members):
+ if not members:
+ return None
+ # See also QAPISchemaObjectTypeMember._pretty_owner()
+ name = 'q_obj_%s-%s' % (name, role)
+ if not self.lookup_entity(name, QAPISchemaObjectType):
+ self._def_entity(QAPISchemaObjectType(name, info, None,
+ members, None))
+ return name
+
+ def _def_enum_type(self, expr, info):
+ name = expr['enum']
+ data = expr['data']
+ prefix = expr.get('prefix')
+ self._def_entity(QAPISchemaEnumType(
+ name, info, self._make_enum_members(data), prefix))
+
+ def _make_member(self, name, typ, info):