qapi: pass 'if' condition into QAPISchemaEntity objects

Built-in objects remain unconditional.  Explicitly defined objects use
the condition specified in the schema.  Implicitly defined objects
inherit their condition from their users.  For most of them, there is
exactly one user, so the condition to use is obvious.  The exception
is wrapped types generated for simple union variants, which can be
shared by any number of simple unions.  The tight condition would be
the disjunction of the conditions of these simple unions.  For now,
use the wrapped type's condition instead.  Much simpler and good
enough for now.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180703155648.11933-3-marcandre.lureau@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Marc-André Lureau 2018-07-03 17:56:36 +02:00 committed by Markus Armbruster
parent 967c885108
commit 2cbc94376e
1 changed files with 65 additions and 32 deletions

View File

@ -1001,8 +1001,16 @@ def check_exprs(exprs):
# Schema compiler frontend # Schema compiler frontend
# #
def listify_cond(ifcond):
if not ifcond:
return []
if not isinstance(ifcond, list):
return [ifcond]
return ifcond
class QAPISchemaEntity(object): class QAPISchemaEntity(object):
def __init__(self, name, info, doc): def __init__(self, name, info, doc, ifcond=None):
assert name is None or isinstance(name, str) assert name is None or isinstance(name, str)
self.name = name self.name = name
self.module = None self.module = None
@ -1013,6 +1021,7 @@ class QAPISchemaEntity(object):
# such place). # such place).
self.info = info self.info = info
self.doc = doc self.doc = doc
self.ifcond = listify_cond(ifcond)
def c_name(self): def c_name(self):
return c_name(self.name) return c_name(self.name)
@ -1145,8 +1154,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
class QAPISchemaEnumType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType):
def __init__(self, name, info, doc, values, prefix): def __init__(self, name, info, doc, ifcond, values, prefix):
QAPISchemaType.__init__(self, name, info, doc) QAPISchemaType.__init__(self, name, info, doc, ifcond)
for v in values: for v in values:
assert isinstance(v, QAPISchemaMember) assert isinstance(v, QAPISchemaMember)
v.set_owner(name) v.set_owner(name)
@ -1181,7 +1190,7 @@ class QAPISchemaEnumType(QAPISchemaType):
class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaArrayType(QAPISchemaType):
def __init__(self, name, info, element_type): def __init__(self, name, info, element_type):
QAPISchemaType.__init__(self, name, info, None) QAPISchemaType.__init__(self, name, info, None, None)
assert isinstance(element_type, str) assert isinstance(element_type, str)
self._element_type_name = element_type self._element_type_name = element_type
self.element_type = None self.element_type = None
@ -1189,6 +1198,7 @@ class QAPISchemaArrayType(QAPISchemaType):
def check(self, schema): def check(self, schema):
self.element_type = schema.lookup_type(self._element_type_name) self.element_type = schema.lookup_type(self._element_type_name)
assert self.element_type assert self.element_type
self.ifcond = self.element_type.ifcond
def is_implicit(self): def is_implicit(self):
return True return True
@ -1210,11 +1220,12 @@ class QAPISchemaArrayType(QAPISchemaType):
class QAPISchemaObjectType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType):
def __init__(self, name, info, doc, base, local_members, variants): def __init__(self, name, info, doc, ifcond,
base, local_members, variants):
# struct has local_members, optional base, and no variants # struct has local_members, optional base, and no variants
# flat union has base, variants, and no local_members # flat union has base, variants, and no local_members
# simple union has local_members, variants, and no base # simple union has local_members, variants, and no base
QAPISchemaType.__init__(self, name, info, doc) QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert base is None or isinstance(base, str) assert base is None or isinstance(base, str)
for m in local_members: for m in local_members:
assert isinstance(m, QAPISchemaObjectTypeMember) assert isinstance(m, QAPISchemaObjectTypeMember)
@ -1410,8 +1421,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaAlternateType(QAPISchemaType):
def __init__(self, name, info, doc, variants): def __init__(self, name, info, doc, ifcond, variants):
QAPISchemaType.__init__(self, name, info, doc) QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert isinstance(variants, QAPISchemaObjectTypeVariants) assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert variants.tag_member assert variants.tag_member
variants.set_owner(name) variants.set_owner(name)
@ -1447,9 +1458,9 @@ class QAPISchemaAlternateType(QAPISchemaType):
class QAPISchemaCommand(QAPISchemaEntity): class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type, def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
gen, success_response, boxed, allow_oob, allow_preconfig): gen, success_response, boxed, allow_oob, allow_preconfig):
QAPISchemaEntity.__init__(self, name, info, doc) QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str) assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str) assert not ret_type or isinstance(ret_type, str)
self._arg_type_name = arg_type self._arg_type_name = arg_type
@ -1490,8 +1501,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
class QAPISchemaEvent(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, boxed): def __init__(self, name, info, doc, ifcond, arg_type, boxed):
QAPISchemaEntity.__init__(self, name, info, doc) QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str) assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type self._arg_type_name = arg_type
self.arg_type = None self.arg_type = None
@ -1590,22 +1601,22 @@ class QAPISchema(object):
('null', 'null', 'QNull' + pointer_suffix)]: ('null', 'null', 'QNull' + pointer_suffix)]:
self._def_builtin_type(*t) self._def_builtin_type(*t)
self.the_empty_object_type = QAPISchemaObjectType( self.the_empty_object_type = QAPISchemaObjectType(
'q_empty', None, None, None, [], None) 'q_empty', None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type) self._def_entity(self.the_empty_object_type)
qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
'qstring', 'qdict', 'qlist', 'qstring', 'qdict', 'qlist',
'qbool']) 'qbool'])
self._def_entity(QAPISchemaEnumType('QType', None, None, self._def_entity(QAPISchemaEnumType('QType', None, None, None,
qtype_values, 'QTYPE')) qtype_values, 'QTYPE'))
def _make_enum_members(self, values): def _make_enum_members(self, values):
return [QAPISchemaMember(v) for v in values] return [QAPISchemaMember(v) for v in values]
def _make_implicit_enum_type(self, name, info, values): def _make_implicit_enum_type(self, name, info, ifcond, values):
# See also QAPISchemaObjectTypeMember._pretty_owner() # See also QAPISchemaObjectTypeMember._pretty_owner()
name = name + 'Kind' # Use namespace reserved by add_name() name = name + 'Kind' # Use namespace reserved by add_name()
self._def_entity(QAPISchemaEnumType( self._def_entity(QAPISchemaEnumType(
name, info, None, self._make_enum_members(values), None)) name, info, None, ifcond, self._make_enum_members(values), None))
return name return name
def _make_array_type(self, element_type, info): def _make_array_type(self, element_type, info):
@ -1614,22 +1625,37 @@ class QAPISchema(object):
self._def_entity(QAPISchemaArrayType(name, info, element_type)) self._def_entity(QAPISchemaArrayType(name, info, element_type))
return name return name
def _make_implicit_object_type(self, name, info, doc, role, members): def _make_implicit_object_type(self, name, info, doc, ifcond,
role, members):
if not members: if not members:
return None return None
# See also QAPISchemaObjectTypeMember._pretty_owner() # See also QAPISchemaObjectTypeMember._pretty_owner()
name = 'q_obj_%s-%s' % (name, role) name = 'q_obj_%s-%s' % (name, role)
if not self.lookup_entity(name, QAPISchemaObjectType): typ = self.lookup_entity(name, QAPISchemaObjectType)
self._def_entity(QAPISchemaObjectType(name, info, doc, None, if typ:
members, None)) # The implicit object type has multiple users. This can
# happen only for simple unions' implicit wrapper types.
# Its ifcond should be the disjunction of its user's
# ifconds. Not implemented. Instead, we always pass the
# wrapped type's ifcond, which is trivially the same for all
# users. It's also necessary for the wrapper to compile.
# But it's not tight: the disjunction need not imply it. We
# may end up compiling useless wrapper types.
# TODO kill simple unions or implement the disjunction
assert ifcond == typ.ifcond
else:
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
None, members, None))
return name return name
def _def_enum_type(self, expr, info, doc): def _def_enum_type(self, expr, info, doc):
name = expr['enum'] name = expr['enum']
data = expr['data'] data = expr['data']
prefix = expr.get('prefix') prefix = expr.get('prefix')
ifcond = expr.get('if')
self._def_entity(QAPISchemaEnumType( self._def_entity(QAPISchemaEnumType(
name, info, doc, self._make_enum_members(data), prefix)) name, info, doc, ifcond,
self._make_enum_members(data), prefix))
def _make_member(self, name, typ, info): def _make_member(self, name, typ, info):
optional = False optional = False
@ -1649,7 +1675,8 @@ class QAPISchema(object):
name = expr['struct'] name = expr['struct']
base = expr.get('base') base = expr.get('base')
data = expr['data'] data = expr['data']
self._def_entity(QAPISchemaObjectType(name, info, doc, base, ifcond = expr.get('if')
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
self._make_members(data, info), self._make_members(data, info),
None)) None))
@ -1661,18 +1688,21 @@ class QAPISchema(object):
assert len(typ) == 1 assert len(typ) == 1
typ = self._make_array_type(typ[0], info) typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type( typ = self._make_implicit_object_type(
typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) typ, info, None, self.lookup_type(typ).ifcond,
'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ) return QAPISchemaObjectTypeVariant(case, typ)
def _def_union_type(self, expr, info, doc): def _def_union_type(self, expr, info, doc):
name = expr['union'] name = expr['union']
data = expr['data'] data = expr['data']
base = expr.get('base') base = expr.get('base')
ifcond = expr.get('if')
tag_name = expr.get('discriminator') tag_name = expr.get('discriminator')
tag_member = None tag_member = None
if isinstance(base, dict): if isinstance(base, dict):
base = (self._make_implicit_object_type( base = self._make_implicit_object_type(
name, info, doc, 'base', self._make_members(base, info))) name, info, doc, ifcond,
'base', self._make_members(base, info))
if tag_name: if tag_name:
variants = [self._make_variant(key, value) variants = [self._make_variant(key, value)
for (key, value) in data.items()] for (key, value) in data.items()]
@ -1680,12 +1710,12 @@ class QAPISchema(object):
else: else:
variants = [self._make_simple_variant(key, value, info) variants = [self._make_simple_variant(key, value, info)
for (key, value) in data.items()] for (key, value) in data.items()]
typ = self._make_implicit_enum_type(name, info, typ = self._make_implicit_enum_type(name, info, ifcond,
[v.name for v in variants]) [v.name for v in variants])
tag_member = QAPISchemaObjectTypeMember('type', typ, False) tag_member = QAPISchemaObjectTypeMember('type', typ, False)
members = [tag_member] members = [tag_member]
self._def_entity( self._def_entity(
QAPISchemaObjectType(name, info, doc, base, members, QAPISchemaObjectType(name, info, doc, ifcond, base, members,
QAPISchemaObjectTypeVariants(tag_name, QAPISchemaObjectTypeVariants(tag_name,
tag_member, tag_member,
variants))) variants)))
@ -1693,11 +1723,12 @@ class QAPISchema(object):
def _def_alternate_type(self, expr, info, doc): def _def_alternate_type(self, expr, info, doc):
name = expr['alternate'] name = expr['alternate']
data = expr['data'] data = expr['data']
ifcond = expr.get('if')
variants = [self._make_variant(key, value) variants = [self._make_variant(key, value)
for (key, value) in data.items()] for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
self._def_entity( self._def_entity(
QAPISchemaAlternateType(name, info, doc, QAPISchemaAlternateType(name, info, doc, ifcond,
QAPISchemaObjectTypeVariants(None, QAPISchemaObjectTypeVariants(None,
tag_member, tag_member,
variants))) variants)))
@ -1711,13 +1742,14 @@ class QAPISchema(object):
boxed = expr.get('boxed', False) boxed = expr.get('boxed', False)
allow_oob = expr.get('allow-oob', False) allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False) allow_preconfig = expr.get('allow-preconfig', False)
ifcond = expr.get('if')
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info)) name, info, doc, ifcond, 'arg', self._make_members(data, info))
if isinstance(rets, list): if isinstance(rets, list):
assert len(rets) == 1 assert len(rets) == 1
rets = self._make_array_type(rets[0], info) rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
gen, success_response, gen, success_response,
boxed, allow_oob, allow_preconfig)) boxed, allow_oob, allow_preconfig))
@ -1725,10 +1757,11 @@ class QAPISchema(object):
name = expr['event'] name = expr['event']
data = expr.get('data') data = expr.get('data')
boxed = expr.get('boxed', False) boxed = expr.get('boxed', False)
ifcond = expr.get('if')
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info)) name, info, doc, ifcond, 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
def _def_exprs(self, exprs): def _def_exprs(self, exprs):
for expr_elem in exprs: for expr_elem in exprs: