From 57516863644817ca59fab023e0c68d139929f3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 8 Dec 2018 15:15:42 +0400 Subject: qapi: rename QAPISchemaEnumType.values to .members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename QAPISchemaEnumType.values and related variables to members. Makes sense ever since commit 93bda4dd4 changed .values from list of string to list of QAPISchemaMember. Obvious no-op. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181208111606.8505-4-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 7b62a4c..046b7e5 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1161,22 +1161,22 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, values, prefix): + def __init__(self, name, info, doc, ifcond, members, prefix): QAPISchemaType.__init__(self, name, info, doc, ifcond) - for v in values: - assert isinstance(v, QAPISchemaMember) - v.set_owner(name) + for m in members: + assert isinstance(m, QAPISchemaMember) + m.set_owner(name) assert prefix is None or isinstance(prefix, str) - self.values = values + self.members = members self.prefix = prefix def check(self, schema): QAPISchemaType.check(self, schema) seen = {} - for v in self.values: - v.check_clash(self.info, seen) + for m in self.members: + m.check_clash(self.info, seen) if self.doc: - self.doc.connect_member(v) + self.doc.connect_member(m) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -1186,7 +1186,7 @@ class QAPISchemaEnumType(QAPISchemaType): return c_name(self.name) def member_names(self): - return [v.name for v in self.values] + return [m.name for m in self.members] def json_type(self): return 'string' @@ -1403,9 +1403,9 @@ class QAPISchemaObjectTypeVariants(object): if self._tag_name: # flat union # branches that are not explicitly covered get an empty type cases = set([v.name for v in self.variants]) - for val in self.tag_member.type.values: - if val.name not in cases: - v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') + for m in self.tag_member.type.members: + if m.name not in cases: + v = QAPISchemaObjectTypeVariant(m.name, 'q_empty') v.set_owner(self.tag_member.owner) self.variants.append(v) for v in self.variants: -- cgit v1.1 From 9c2f56e9f9d5a1f9ddac77dda35f997738e85d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:03 +0400 Subject: qapi: Do not define enumeration value explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated C enumeration types explicitly set the enumeration constants to 0, 1, 2, ... That's exactly what you get when you don't supply values. Drop the explicit values. No change now, but it will avoid gaps in the values when we later add support for 'if' conditions. Avoiding such gaps will save us the trouble of changing the ENUM_lookup[] tables to work without a sentinel. We'll have to take care to ensure the headers required by the 'if' conditions get always included before the generated QAPI code. Fortunately, our convention to include "qemu/osdep.h" first in any .c ensures that's the case for our CONFIG_FOO macros. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-2-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 046b7e5..55c914e 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -2045,14 +2045,11 @@ typedef enum %(c_name)s { ''', c_name=c_name(name)) - i = 0 for value in enum_values: ret += mcgen(''' - %(c_enum)s = %(i)d, + %(c_enum)s, ''', - c_enum=c_enum_const(name, value, prefix), - i=i) - i += 1 + c_enum=c_enum_const(name, value, prefix)) ret += mcgen(''' } %(c_name)s; -- cgit v1.1 From 1962bd39d567e8b44646e558b07b2742a5a61339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:04 +0400 Subject: qapi: change enum visitor and gen_enum* to take QAPISchemaMember MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will allow to add and access more properties associated with enum values/members, like the associated 'if' condition. We may want to have a specialized type QAPISchemaEnumMember, for now this will do. Modify gen_enum() and gen_enum_lookup() for the same reason. Suggested-by: Markus Armbruster Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-3-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 55c914e..1fa2f79 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -1063,7 +1063,7 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, values, prefix): + def visit_enum_type(self, name, info, ifcond, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): @@ -1193,7 +1193,7 @@ class QAPISchemaEnumType(QAPISchemaType): def visit(self, visitor): visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.member_names(), self.prefix) + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): @@ -2012,19 +2012,19 @@ def _wrap_ifcond(ifcond, before, after): return out -def gen_enum_lookup(name, values, prefix=None): +def gen_enum_lookup(name, members, prefix=None): ret = mcgen(''' const QEnumLookup %(c_name)s_lookup = { .array = (const char *const[]) { ''', c_name=c_name(name)) - for value in values: - index = c_enum_const(name, value, prefix) + for m in members: + index = c_enum_const(name, m.name, prefix) ret += mcgen(''' - [%(index)s] = "%(value)s", + [%(index)s] = "%(name)s", ''', - index=index, value=value) + index=index, name=m.name) ret += mcgen(''' }, @@ -2035,9 +2035,9 @@ const QEnumLookup %(c_name)s_lookup = { return ret -def gen_enum(name, values, prefix=None): +def gen_enum(name, members, prefix=None): # append automatically generated _MAX value - enum_values = values + ['_MAX'] + enum_members = members + [QAPISchemaMember('_MAX')] ret = mcgen(''' @@ -2045,11 +2045,11 @@ typedef enum %(c_name)s { ''', c_name=c_name(name)) - for value in enum_values: + for m in enum_members: ret += mcgen(''' %(c_enum)s, ''', - c_enum=c_enum_const(name, value, prefix)) + c_enum=c_enum_const(name, m.name, prefix)) ret += mcgen(''' } %(c_name)s; -- cgit v1.1 From 563bd35d87f7bcab644381319d6247e8aee8a4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:06 +0400 Subject: qapi: factor out checking for keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new helper function to check if the given keys are known, and if mandatory keys are present. The function will be reused in other places in the following code changes. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-5-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1fa2f79..18f5872 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -873,6 +873,17 @@ def check_struct(expr, info): allow_metas=['struct']) +def check_known_keys(info, source, keys, required, optional): + for key in keys: + if key not in required and key not in optional: + raise QAPISemError(info, "Unknown key '%s' in %s" % (key, source)) + + for key in required: + if key not in keys: + raise QAPISemError(info, "Key '%s' is missing from %s" + % (key, source)) + + def check_keys(expr_elem, meta, required, optional=[]): expr = expr_elem['expr'] info = expr_elem['info'] @@ -880,10 +891,9 @@ def check_keys(expr_elem, meta, required, optional=[]): if not isinstance(name, str): raise QAPISemError(info, "'%s' key must have a string value" % meta) required = required + [meta] + source = "%s '%s'" % (meta, name) + check_known_keys(info, source, expr.keys(), required, optional) for (key, value) in expr.items(): - if key not in required and key not in optional: - raise QAPISemError(info, "Unknown key '%s' in %s '%s'" - % (key, meta, name)) if key in ['gen', 'success-response'] and value is not False: raise QAPISemError(info, "'%s' of %s '%s' should only use false value" @@ -895,10 +905,6 @@ def check_keys(expr_elem, meta, required, optional=[]): % (key, meta, name)) if key == 'if': check_if(expr, info) - for key in required: - if key not in expr: - raise QAPISemError(info, "Key '%s' is missing from %s '%s'" - % (key, meta, name)) def check_exprs(exprs): -- cgit v1.1 From 7e80d48001c8ebc11358596f10ba48daddee05f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:07 +0400 Subject: qapi: improve reporting of unknown or missing keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report the set of missing or unknown keys. And give a hint about the accepted keys. The error message for multiple meta type members (visible in tests/qapi-schema/double-type.err) is not improved. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-6-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 18f5872..f205805 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -874,14 +874,21 @@ def check_struct(expr, info): def check_known_keys(info, source, keys, required, optional): - for key in keys: - if key not in required and key not in optional: - raise QAPISemError(info, "Unknown key '%s' in %s" % (key, source)) - - for key in required: - if key not in keys: - raise QAPISemError(info, "Key '%s' is missing from %s" - % (key, source)) + + def pprint(elems): + return ', '.join("'" + e + "'" for e in sorted(elems)) + + missing = set(required) - set(keys) + if missing: + raise QAPISemError(info, "Key%s %s %s missing from %s" + % ('s' if len(missing) > 1 else '', pprint(missing), + 'are' if len(missing) > 1 else 'is', source)) + allowed = set(required + optional) + unknown = set(keys) - allowed + if unknown: + raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s." + % ('s' if len(unknown) > 1 else '', pprint(unknown), + source, pprint(allowed))) def check_keys(expr_elem, meta, required, optional=[]): -- cgit v1.1 From ea738b21685814dc54352858afabc2ff33ade53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:08 +0400 Subject: qapi: add a dictionary form with 'name' key for enum members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Desugar the enum NAME form to { 'name': NAME }. This will allow to add new enum members, such as 'if' in the following patch. Signed-off-by: Marc-André Lureau Message-Id: <20181213123724.4866-7-marcandre.lureau@redhat.com> Message-Id: <20181213123724.4866-8-marcandre.lureau@redhat.com> Message-Id: <20181213123724.4866-9-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster [Harmless accidental move backed out, long line wrapped, patches squashed] Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index f205805..b1cf33a 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -740,6 +740,10 @@ def check_event(expr, info): allow_metas=meta) +def enum_get_names(expr): + return [e['name'] for e in expr['data']] + + def check_union(expr, info): name = expr['union'] base = expr.get('base') @@ -799,7 +803,7 @@ def check_union(expr, info): # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. if enum_define: - if key not in enum_define['data']: + if key not in enum_get_names(enum_define): raise QAPISemError(info, "Discriminator value '%s' is not found in " "enum '%s'" @@ -831,7 +835,7 @@ def check_alternate(expr, info): if qtype == 'QTYPE_QSTRING': enum_expr = enum_types.get(value) if enum_expr: - for v in enum_expr['data']: + for v in enum_get_names(enum_expr): if v in ['on', 'off']: conflicting.add('QTYPE_QBOOL') if re.match(r'[-+0-9.]', v): # lazy, could be tightened @@ -847,9 +851,15 @@ def check_alternate(expr, info): types_seen[qt] = key +def normalize_enum(expr): + if isinstance(expr['data'], list): + expr['data'] = [m if isinstance(m, dict) else {'name': m} + for m in expr['data']] + + def check_enum(expr, info): name = expr['enum'] - members = expr.get('data') + members = expr['data'] prefix = expr.get('prefix') if not isinstance(members, list): @@ -858,8 +868,11 @@ def check_enum(expr, info): if prefix is not None and not isinstance(prefix, str): raise QAPISemError(info, "Enum '%s' requires a string for 'prefix'" % name) + for member in members: - check_name(info, "Member of enum '%s'" % name, member, + source = "dictionary member of enum '%s'" % name + check_known_keys(info, source, member, ['name'], []) + check_name(info, "Member of enum '%s'" % name, member['name'], enum_member=True) @@ -937,6 +950,7 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) + normalize_enum(expr) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' @@ -1633,14 +1647,16 @@ class QAPISchema(object): self.the_empty_object_type = QAPISchemaObjectType( 'q_empty', None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) - qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', - 'qstring', 'qdict', 'qlist', - 'qbool']) + + qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', + 'qbool'] + qtype_values = self._make_enum_members([{'name': n} for n in qtypes]) + self._def_entity(QAPISchemaEnumType('QType', None, None, None, qtype_values, 'QTYPE')) def _make_enum_members(self, values): - return [QAPISchemaMember(v) for v in values] + return [QAPISchemaMember(v['name']) for v in values] def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() @@ -1740,8 +1756,8 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.items()] - typ = self._make_implicit_enum_type(name, info, ifcond, - [v.name for v in variants]) + enum = [{'name': v.name} for v in variants] + typ = self._make_implicit_enum_type(name, info, ifcond, enum) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( -- cgit v1.1 From 6cc32b0e14b3f91e15a9511d90b222abc1df391d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:11 +0400 Subject: qapi: add 'if' to enum members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QAPISchemaMember gains .ifcond for enum members: inherited classes, such as QAPISchemaObjectTypeMember, will thus have an ifcond member after this (those different types will also use the .ifcond to store the condition and generate conditional code in the following patches). The generated code remains unconditional for now. Later patches generate the conditionals. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-10-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index b1cf33a..a609ffc 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -871,7 +871,8 @@ def check_enum(expr, info): for member in members: source = "dictionary member of enum '%s'" % name - check_known_keys(info, source, member, ['name'], []) + check_known_keys(info, source, member, ['name'], ['if']) + check_if(member, info) check_name(info, "Member of enum '%s'" % name, member['name'], enum_member=True) @@ -1345,9 +1346,10 @@ class QAPISchemaObjectType(QAPISchemaType): class QAPISchemaMember(object): role = 'member' - def __init__(self, name): + def __init__(self, name, ifcond=None): assert isinstance(name, str) self.name = name + self.ifcond = listify_cond(ifcond) self.owner = None def set_owner(self, name): @@ -1656,7 +1658,7 @@ class QAPISchema(object): qtype_values, 'QTYPE')) def _make_enum_members(self, values): - return [QAPISchemaMember(v['name']) for v in values] + return [QAPISchemaMember(v['name'], v.get('if')) for v in values] def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() -- cgit v1.1 From 87adbbffd4b03c68d039ce0be5dcfde38a6a7b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:14 +0400 Subject: qapi: add a dictionary form for TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wherever a struct/union/alternate/command/event member with NAME: TYPE form is accepted, desugar it to a NAME: { 'type': TYPE } form. This will allow to add new member details, such as 'if' in the following patch to introduce conditionals, or 'default' for default values etc. Signed-off-by: Marc-André Lureau Message-Id: <20181213123724.4866-13-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 70 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 24 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index a609ffc..cdfe2bf 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr): if not base_members: return None - discriminator_type = base_members.get(discriminator) - if not discriminator_type: + discriminator_value = base_members.get(discriminator) + if not discriminator_value: return None - return enum_types.get(discriminator_type) + return enum_types.get(discriminator_value['type']) # Names must be letters, numbers, -, and _. They must start with letter, @@ -704,8 +704,10 @@ def check_type(info, source, value, allow_array=False, % (source, key)) # Todo: allow dictionaries to represent default values of # an optional argument. - check_type(info, "Member '%s' of %s" % (key, source), arg, - allow_array=True, + check_known_keys(info, "member '%s' of %s" % (key, source), + arg, ['type'], []) + check_type(info, "Member '%s' of %s" % (key, source), + arg['type'], allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) @@ -776,13 +778,13 @@ def check_union(expr, info): # member of the base struct. check_name(info, "Discriminator of flat union '%s'" % name, discriminator) - discriminator_type = base_members.get(discriminator) - if not discriminator_type: + discriminator_value = base_members.get(discriminator) + if not discriminator_value: raise QAPISemError(info, "Discriminator '%s' is not a member of base " "struct '%s'" % (discriminator, base)) - enum_define = enum_types.get(discriminator_type) + enum_define = enum_types.get(discriminator_value['type']) allow_metas = ['struct'] # Do not allow string discriminator if not enum_define: @@ -796,9 +798,12 @@ def check_union(expr, info): for (key, value) in members.items(): check_name(info, "Member of union '%s'" % name, key) + check_known_keys(info, "member '%s' of union '%s'" % (key, name), + value, ['type'], []) # Each value must name a known type check_type(info, "Member '%s' of union '%s'" % (key, name), - value, allow_array=not base, allow_metas=allow_metas) + value['type'], + allow_array=not base, allow_metas=allow_metas) # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. @@ -822,18 +827,21 @@ def check_alternate(expr, info): "in 'data'" % name) for (key, value) in members.items(): check_name(info, "Member of alternate '%s'" % name, key) + check_known_keys(info, + "member '%s' of alternate '%s'" % (key, name), + value, ['type'], []) + typ = value['type'] # Ensure alternates have no type conflicts. - check_type(info, "Member '%s' of alternate '%s'" % (key, name), - value, + check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ, allow_metas=['built-in', 'union', 'struct', 'enum']) - qtype = find_alternate_member_qtype(value) + qtype = find_alternate_member_qtype(typ) if not qtype: raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " - "type '%s'" % (name, key, value)) + "type '%s'" % (name, key, typ)) conflicting = set([qtype]) if qtype == 'QTYPE_QSTRING': - enum_expr = enum_types.get(value) + enum_expr = enum_types.get(typ) if enum_expr: for v in enum_get_names(enum_expr): if v in ['on', 'off']: @@ -851,12 +859,6 @@ def check_alternate(expr, info): types_seen[qt] = key -def normalize_enum(expr): - if isinstance(expr['data'], list): - expr['data'] = [m if isinstance(m, dict) else {'name': m} - for m in expr['data']] - - def check_enum(expr, info): name = expr['enum'] members = expr['data'] @@ -928,6 +930,20 @@ def check_keys(expr_elem, meta, required, optional=[]): check_if(expr, info) +def normalize_enum(expr): + if isinstance(expr['data'], list): + expr['data'] = [m if isinstance(m, dict) else {'name': m} + for m in expr['data']] + + +def normalize_members(members): + if isinstance(members, OrderedDict): + for key, arg in members.items(): + if isinstance(arg, dict): + continue + members[key] = {'type': arg} + + def check_exprs(exprs): global all_names @@ -957,22 +973,28 @@ def check_exprs(exprs): meta = 'union' check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator', 'if']) + normalize_members(expr.get('base')) + normalize_members(expr['data']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' check_keys(expr_elem, 'alternate', ['data'], ['if']) + normalize_members(expr['data']) elif 'struct' in expr: meta = 'struct' check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) + normalize_members(expr['data']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', 'boxed', 'allow-oob', 'allow-preconfig', 'if']) + normalize_members(expr.get('data')) elif 'event' in expr: meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) + normalize_members(expr.get('data')) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") @@ -1716,7 +1738,7 @@ class QAPISchema(object): return QAPISchemaObjectTypeMember(name, typ, optional) def _make_members(self, data, info): - return [self._make_member(key, value, info) + return [self._make_member(key, value['type'], info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -1752,11 +1774,11 @@ class QAPISchema(object): name, info, doc, ifcond, 'base', self._make_members(base, info)) if tag_name: - variants = [self._make_variant(key, value) + variants = [self._make_variant(key, value['type']) for (key, value) in data.items()] members = [] else: - variants = [self._make_simple_variant(key, value, info) + variants = [self._make_simple_variant(key, value['type'], info) for (key, value) in data.items()] enum = [{'name': v.name} for v in variants] typ = self._make_implicit_enum_type(name, info, ifcond, enum) @@ -1772,7 +1794,7 @@ class QAPISchema(object): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') - variants = [self._make_variant(key, value) + variants = [self._make_variant(key, value['type']) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( -- cgit v1.1 From ccadd6bcba3bef6c1954f533b10224fb3db7148e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:15 +0400 Subject: qapi: Add 'if' to implicit struct members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated code is for now *unconditional*. Later patches generate the conditionals. Note that union discriminators may not have 'if' conditionals. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-14-marcandre.lureau@redhat.com> Message-Id: <20181213123724.4866-15-marcandre.lureau@redhat.com> [Patches squashed, commit message tweaked] Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index cdfe2bf..98e9d6f 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -705,7 +705,7 @@ def check_type(info, source, value, allow_array=False, # Todo: allow dictionaries to represent default values of # an optional argument. check_known_keys(info, "member '%s' of %s" % (key, source), - arg, ['type'], []) + arg, ['type'], ['if']) check_type(info, "Member '%s' of %s" % (key, source), arg['type'], allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', @@ -784,6 +784,10 @@ def check_union(expr, info): "Discriminator '%s' is not a member of base " "struct '%s'" % (discriminator, base)) + if discriminator_value.get('if'): + raise QAPISemError(info, 'The discriminator %s.%s for union %s ' + 'must not be conditional' % + (base, discriminator, name)) enum_define = enum_types.get(discriminator_value['type']) allow_metas = ['struct'] # Do not allow string discriminator @@ -1412,8 +1416,8 @@ class QAPISchemaMember(object): class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, typ, optional): - QAPISchemaMember.__init__(self, name) + def __init__(self, name, typ, optional, ifcond=None): + QAPISchemaMember.__init__(self, name, ifcond) assert isinstance(typ, str) assert isinstance(optional, bool) self._type_name = typ @@ -1727,7 +1731,7 @@ class QAPISchema(object): name, info, doc, ifcond, self._make_enum_members(data), prefix)) - def _make_member(self, name, typ, info): + def _make_member(self, name, typ, ifcond, info): optional = False if name.startswith('*'): name = name[1:] @@ -1735,10 +1739,10 @@ class QAPISchema(object): if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - return QAPISchemaObjectTypeMember(name, typ, optional) + return QAPISchemaObjectTypeMember(name, typ, optional, ifcond) def _make_members(self, data, info): - return [self._make_member(key, value['type'], info) + return [self._make_member(key, value['type'], value.get('if'), info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -1759,7 +1763,7 @@ class QAPISchema(object): typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( typ, info, None, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, info)]) + 'wrapper', [self._make_member('data', typ, None, info)]) return QAPISchemaObjectTypeVariant(case, typ) def _def_union_type(self, expr, info, doc): -- cgit v1.1 From a2724280fbed914e517403890b4b1568261f0cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:17 +0400 Subject: qapi: add 'if' to union members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'if' key to union members: { 'union': 'TestIfUnion', 'data': 'mem': { 'type': 'str', 'if': 'COND'} } The generated code remains unconditional for now. Later patches generate the conditionals. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-16-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 98e9d6f..44fe886 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -803,7 +803,7 @@ def check_union(expr, info): check_name(info, "Member of union '%s'" % name, key) check_known_keys(info, "member '%s' of union '%s'" % (key, name), - value, ['type'], []) + value, ['type'], ['if']) # Each value must name a known type check_type(info, "Member '%s' of union '%s'" % (key, name), value['type'], @@ -1483,8 +1483,8 @@ class QAPISchemaObjectTypeVariants(object): class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): role = 'branch' - def __init__(self, name, typ): - QAPISchemaObjectTypeMember.__init__(self, name, typ, False) + def __init__(self, name, typ, ifcond=None): + QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond) class QAPISchemaAlternateType(QAPISchemaType): @@ -1757,14 +1757,14 @@ class QAPISchema(object): def _make_variant(self, case, typ): return QAPISchemaObjectTypeVariant(case, typ) - def _make_simple_variant(self, case, typ, info): + def _make_simple_variant(self, case, typ, ifcond, info): if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( typ, info, None, self.lookup_type(typ), 'wrapper', [self._make_member('data', typ, None, info)]) - return QAPISchemaObjectTypeVariant(case, typ) + return QAPISchemaObjectTypeVariant(case, typ, ifcond) def _def_union_type(self, expr, info, doc): name = expr['union'] @@ -1782,9 +1782,10 @@ class QAPISchema(object): for (key, value) in data.items()] members = [] else: - variants = [self._make_simple_variant(key, value['type'], info) + variants = [self._make_simple_variant(key, value['type'], + value.get('if'), info) for (key, value) in data.items()] - enum = [{'name': v.name} for v in variants] + enum = [{'name': v.name, 'if': v.ifcond} for v in variants] typ = self._make_implicit_enum_type(name, info, ifcond, enum) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] -- cgit v1.1 From 3e270dcacc08cca45e694ca48159915f81303cfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:18 +0400 Subject: qapi: add 'if' to alternate members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'if' key to alternate members: { 'alternate': 'TestIfAlternate', 'data': { 'alt': { 'type': 'TestStruct', 'if': 'COND' } } } Generated code is not changed by this patch but with "qapi: add #if conditions to generated code". Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-17-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 44fe886..17707b6 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -833,7 +833,7 @@ def check_alternate(expr, info): check_name(info, "Member of alternate '%s'" % name, key) check_known_keys(info, "member '%s' of alternate '%s'" % (key, name), - value, ['type'], []) + value, ['type'], ['if']) typ = value['type'] # Ensure alternates have no type conflicts. @@ -1754,8 +1754,8 @@ class QAPISchema(object): self._make_members(data, info), None)) - def _make_variant(self, case, typ): - return QAPISchemaObjectTypeVariant(case, typ) + def _make_variant(self, case, typ, ifcond): + return QAPISchemaObjectTypeVariant(case, typ, ifcond) def _make_simple_variant(self, case, typ, ifcond, info): if isinstance(typ, list): @@ -1778,7 +1778,7 @@ class QAPISchema(object): name, info, doc, ifcond, 'base', self._make_members(base, info)) if tag_name: - variants = [self._make_variant(key, value['type']) + variants = [self._make_variant(key, value['type'], value.get('if')) for (key, value) in data.items()] members = [] else: @@ -1799,7 +1799,7 @@ class QAPISchema(object): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') - variants = [self._make_variant(key, value['type']) + variants = [self._make_variant(key, value['type'], value.get('if')) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( -- cgit v1.1 From 8ee06f61e13701a54a9f76ceadafc856d279cdb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Thu, 13 Dec 2018 16:37:19 +0400 Subject: qapi: Add #if conditions to generated code members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap generated enum and struct members and their supporting code with #if/#endif, using the .ifcond members added in the previous patches. We do enum and struct in a single patch because union tag enum and the associated variants tie them together, and dealing with that to split the patch doesn't seem worthwhile. Signed-off-by: Marc-André Lureau Reviewed-by: Markus Armbruster Message-Id: <20181213123724.4866-18-marcandre.lureau@redhat.com> Signed-off-by: Markus Armbruster --- scripts/qapi/common.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts/qapi/common.py') diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 17707b6..8c2d973 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -2078,11 +2078,13 @@ const QEnumLookup %(c_name)s_lookup = { ''', c_name=c_name(name)) for m in members: + ret += gen_if(m.ifcond) index = c_enum_const(name, m.name, prefix) ret += mcgen(''' [%(index)s] = "%(name)s", ''', index=index, name=m.name) + ret += gen_endif(m.ifcond) ret += mcgen(''' }, @@ -2104,10 +2106,12 @@ typedef enum %(c_name)s { c_name=c_name(name)) for m in enum_members: + ret += gen_if(m.ifcond) ret += mcgen(''' %(c_enum)s, ''', c_enum=c_enum_const(name, m.name, prefix)) + ret += gen_endif(m.ifcond) ret += mcgen(''' } %(c_name)s; -- cgit v1.1