|
@@ -19,12 +19,31 @@
|
|
|
import re
|
|
|
from typing import Optional
|
|
|
|
|
|
-from .common import POINTER_SUFFIX, c_name
|
|
|
+from .common import (
|
|
|
+ POINTER_SUFFIX,
|
|
|
+ c_name,
|
|
|
+ cgen_ifcond,
|
|
|
+ docgen_ifcond,
|
|
|
+)
|
|
|
from .error import QAPIError, QAPISemError, QAPISourceError
|
|
|
from .expr import check_exprs
|
|
|
from .parser import QAPISchemaParser
|
|
|
|
|
|
|
|
|
+class QAPISchemaIfCond:
|
|
|
+ def __init__(self, ifcond=None):
|
|
|
+ self.ifcond = ifcond or {}
|
|
|
+
|
|
|
+ def cgen(self):
|
|
|
+ return cgen_ifcond(self.ifcond)
|
|
|
+
|
|
|
+ def docgen(self):
|
|
|
+ return docgen_ifcond(self.ifcond)
|
|
|
+
|
|
|
+ def is_present(self):
|
|
|
+ return bool(self.ifcond)
|
|
|
+
|
|
|
+
|
|
|
class QAPISchemaEntity:
|
|
|
meta: Optional[str] = None
|
|
|
|
|
@@ -42,7 +61,7 @@ def __init__(self, name: str, info, doc, ifcond=None, features=None):
|
|
|
# such place).
|
|
|
self.info = info
|
|
|
self.doc = doc
|
|
|
- self._ifcond = ifcond or []
|
|
|
+ self._ifcond = ifcond or QAPISchemaIfCond()
|
|
|
self.features = features or []
|
|
|
self._checked = False
|
|
|
|
|
@@ -593,7 +612,7 @@ def check(self, schema, seen):
|
|
|
self.info,
|
|
|
"discriminator member '%s' of %s must not be optional"
|
|
|
% (self._tag_name, base))
|
|
|
- if self.tag_member.ifcond:
|
|
|
+ if self.tag_member.ifcond.is_present():
|
|
|
raise QAPISemError(
|
|
|
self.info,
|
|
|
"discriminator member '%s' of %s must not be conditional"
|
|
@@ -601,7 +620,7 @@ def check(self, schema, seen):
|
|
|
else: # simple union
|
|
|
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
|
|
|
assert not self.tag_member.optional
|
|
|
- assert self.tag_member.ifcond == []
|
|
|
+ assert not self.tag_member.ifcond.is_present()
|
|
|
if self._tag_name: # flat union
|
|
|
# branches that are not explicitly covered get an empty type
|
|
|
cases = {v.name for v in self.variants}
|
|
@@ -646,7 +665,7 @@ def __init__(self, name, info, ifcond=None):
|
|
|
assert isinstance(name, str)
|
|
|
self.name = name
|
|
|
self.info = info
|
|
|
- self.ifcond = ifcond or []
|
|
|
+ self.ifcond = ifcond or QAPISchemaIfCond()
|
|
|
self.defined_in = None
|
|
|
|
|
|
def set_defined_in(self, name):
|
|
@@ -968,11 +987,13 @@ def _def_predefineds(self):
|
|
|
def _make_features(self, features, info):
|
|
|
if features is None:
|
|
|
return []
|
|
|
- return [QAPISchemaFeature(f['name'], info, f.get('if'))
|
|
|
+ return [QAPISchemaFeature(f['name'], info,
|
|
|
+ QAPISchemaIfCond(f.get('if')))
|
|
|
for f in features]
|
|
|
|
|
|
def _make_enum_members(self, values, info):
|
|
|
- return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
|
|
|
+ return [QAPISchemaEnumMember(v['name'], info,
|
|
|
+ QAPISchemaIfCond(v.get('if')))
|
|
|
for v in values]
|
|
|
|
|
|
def _make_implicit_enum_type(self, name, info, ifcond, values):
|
|
@@ -997,18 +1018,18 @@ def _make_implicit_object_type(self, name, info, ifcond, role, members):
|
|
|
name = 'q_obj_%s-%s' % (name, role)
|
|
|
typ = self.lookup_entity(name, QAPISchemaObjectType)
|
|
|
if typ:
|
|
|
- # 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.
|
|
|
+ # The implicit object type has multiple users. This is
|
|
|
+ # either a duplicate definition (which will be flagged
|
|
|
+ # later), or an implicit wrapper type used for multiple
|
|
|
+ # simple unions. In the latter case, 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
|
|
|
-
|
|
|
- # pylint: disable=protected-access
|
|
|
- assert (ifcond or []) == typ._ifcond
|
|
|
+ pass
|
|
|
else:
|
|
|
self._def_entity(QAPISchemaObjectType(
|
|
|
name, info, None, ifcond, None, None, members, None))
|
|
@@ -1018,7 +1039,7 @@ def _def_enum_type(self, expr, info, doc):
|
|
|
name = expr['enum']
|
|
|
data = expr['data']
|
|
|
prefix = expr.get('prefix')
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
self._def_entity(QAPISchemaEnumType(
|
|
|
name, info, doc, ifcond, features,
|
|
@@ -1036,7 +1057,8 @@ def _make_member(self, name, typ, ifcond, features, info):
|
|
|
self._make_features(features, info))
|
|
|
|
|
|
def _make_members(self, data, info):
|
|
|
- return [self._make_member(key, value['type'], value.get('if'),
|
|
|
+ return [self._make_member(key, value['type'],
|
|
|
+ QAPISchemaIfCond(value.get('if')),
|
|
|
value.get('features'), info)
|
|
|
for (key, value) in data.items()]
|
|
|
|
|
@@ -1044,7 +1066,7 @@ def _def_struct_type(self, expr, info, doc):
|
|
|
name = expr['struct']
|
|
|
base = expr.get('base')
|
|
|
data = expr['data']
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
self._def_entity(QAPISchemaObjectType(
|
|
|
name, info, doc, ifcond, features, base,
|
|
@@ -1067,7 +1089,7 @@ def _def_union_type(self, expr, info, doc):
|
|
|
name = expr['union']
|
|
|
data = expr['data']
|
|
|
base = expr.get('base')
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
tag_name = expr.get('discriminator')
|
|
|
tag_member = None
|
|
@@ -1076,15 +1098,19 @@ def _def_union_type(self, expr, info, doc):
|
|
|
name, info, ifcond,
|
|
|
'base', self._make_members(base, info))
|
|
|
if tag_name:
|
|
|
- variants = [self._make_variant(key, value['type'],
|
|
|
- value.get('if'), info)
|
|
|
- for (key, value) in data.items()]
|
|
|
+ variants = [
|
|
|
+ self._make_variant(key, value['type'],
|
|
|
+ QAPISchemaIfCond(value.get('if')),
|
|
|
+ info)
|
|
|
+ for (key, value) in data.items()]
|
|
|
members = []
|
|
|
else:
|
|
|
- variants = [self._make_simple_variant(key, value['type'],
|
|
|
- value.get('if'), info)
|
|
|
- for (key, value) in data.items()]
|
|
|
- enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
|
|
|
+ variants = [
|
|
|
+ self._make_simple_variant(key, value['type'],
|
|
|
+ QAPISchemaIfCond(value.get('if')),
|
|
|
+ info)
|
|
|
+ for (key, value) in data.items()]
|
|
|
+ enum = [{'name': v.name, 'if': v.ifcond.ifcond} for v in variants]
|
|
|
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
|
|
|
tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
|
|
|
members = [tag_member]
|
|
@@ -1097,11 +1123,13 @@ def _def_union_type(self, expr, info, doc):
|
|
|
def _def_alternate_type(self, expr, info, doc):
|
|
|
name = expr['alternate']
|
|
|
data = expr['data']
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
- variants = [self._make_variant(key, value['type'], value.get('if'),
|
|
|
- info)
|
|
|
- for (key, value) in data.items()]
|
|
|
+ variants = [
|
|
|
+ self._make_variant(key, value['type'],
|
|
|
+ QAPISchemaIfCond(value.get('if')),
|
|
|
+ info)
|
|
|
+ for (key, value) in data.items()]
|
|
|
tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
|
|
|
self._def_entity(
|
|
|
QAPISchemaAlternateType(name, info, doc, ifcond, features,
|
|
@@ -1118,7 +1146,7 @@ def _def_command(self, expr, info, doc):
|
|
|
allow_oob = expr.get('allow-oob', False)
|
|
|
allow_preconfig = expr.get('allow-preconfig', False)
|
|
|
coroutine = expr.get('coroutine', False)
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
if isinstance(data, OrderedDict):
|
|
|
data = self._make_implicit_object_type(
|
|
@@ -1137,7 +1165,7 @@ def _def_event(self, expr, info, doc):
|
|
|
name = expr['event']
|
|
|
data = expr.get('data')
|
|
|
boxed = expr.get('boxed', False)
|
|
|
- ifcond = expr.get('if')
|
|
|
+ ifcond = QAPISchemaIfCond(expr.get('if'))
|
|
|
features = self._make_features(expr.get('features'), info)
|
|
|
if isinstance(data, OrderedDict):
|
|
|
data = self._make_implicit_object_type(
|