qapi: Emit implicit structs in generated C
We already have several places that want to visit all the members of an implicit object within a larger context (simple union variant, event with anonymous data, command with anonymous arguments struct); and will be adding another one soon (the ability to declare an anonymous base for a flat union). Having a C struct declared for these implicit types, along with a visit_type_FOO_members() helper function, will make for fewer special cases in our generator. We do not, however, need qapi_free_FOO() or visit_type_FOO() functions for implicit types, because they should not be used directly outside of the generated code. This is done by adding a conditional in visit_object_type() for both qapi-types.py and qapi-visit.py based on the object name. The comparison of "name.startswith('q_')" is a bit hacky (it's basically duplicating what .is_implicit() already uses), but beats changing the signature of the visit_object_type() callback to pass a new 'implicit' flag. The hack should be temporary: we are considering adding a future patch that consolidates the narrow visit_object_type(..., base, local_members, variants) and visit_object_type_flat(..., all_members, variants) [where different sets of information are already broken out, and the QAPISchemaObjectType is no longer available] into a broader visit_object_type(obj_type) [where the visitor can query the needed fields from obj_type directly]. Also, now that we WANT to output C code for implicits, we no longer need the visit_needed() filter, leaving 'q_empty' as the only object still needing a special case. Remember, 'q_empty' is the only built-in generated object, which means that without a special case it would be emitted in multiple files (the main qapi-types.h and in qga-qapi-types.h) causing compilation failure due to redefinition. But since it has no members, it's easier to just avoid an attempt to visit that particular type; since gen_object() is called recursively, we also prime the objects_seen set to cover any recursion into the empty type. The patch relies on the changed naming of implicit types in the previous patch. It is a bit unfortunate that the generated struct names and visit_type_FOO_members() don't match normal naming conventions, but it's not too bad, since they will only be used in generated code. The generated code grows substantially in size: the implicit '-wrapper' types must be emitted in qapi-types.h before any union can include an unboxed member of that type. Arguably, the '-args' types could be emitted in a private header for just qapi-visit.c and qmp-marshal.c, rather than polluting qapi-types.h; but adding complexity to the generator to split the output location according to role doesn't seem worth the maintenance costs. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1458254921-17042-6-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
7599697c66
commit
7ce106a96f
@ -61,8 +61,7 @@ def gen_object(name, base, members, variants):
|
|||||||
ret = ''
|
ret = ''
|
||||||
if variants:
|
if variants:
|
||||||
for v in variants.variants:
|
for v in variants.variants:
|
||||||
if (isinstance(v.type, QAPISchemaObjectType) and
|
if isinstance(v.type, QAPISchemaObjectType):
|
||||||
not v.type.is_implicit()):
|
|
||||||
ret += gen_object(v.type.name, v.type.base,
|
ret += gen_object(v.type.name, v.type.base,
|
||||||
v.type.local_members, v.type.variants)
|
v.type.local_members, v.type.variants)
|
||||||
|
|
||||||
@ -180,6 +179,8 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
|
|||||||
self._btin = None
|
self._btin = None
|
||||||
|
|
||||||
def visit_begin(self, schema):
|
def visit_begin(self, schema):
|
||||||
|
# gen_object() is recursive, ensure it doesn't visit the empty type
|
||||||
|
objects_seen.add(schema.the_empty_object_type.name)
|
||||||
self.decl = ''
|
self.decl = ''
|
||||||
self.defn = ''
|
self.defn = ''
|
||||||
self._fwdecl = ''
|
self._fwdecl = ''
|
||||||
@ -196,11 +197,6 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
|
|||||||
self.decl = self._btin + self.decl
|
self.decl = self._btin + self.decl
|
||||||
self._btin = None
|
self._btin = None
|
||||||
|
|
||||||
def visit_needed(self, entity):
|
|
||||||
# Visit everything except implicit objects
|
|
||||||
return not (entity.is_implicit() and
|
|
||||||
isinstance(entity, QAPISchemaObjectType))
|
|
||||||
|
|
||||||
def _gen_type_cleanup(self, name):
|
def _gen_type_cleanup(self, name):
|
||||||
self.decl += gen_type_cleanup_decl(name)
|
self.decl += gen_type_cleanup_decl(name)
|
||||||
self.defn += gen_type_cleanup(name)
|
self.defn += gen_type_cleanup(name)
|
||||||
@ -229,10 +225,17 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
|
|||||||
self._gen_type_cleanup(name)
|
self._gen_type_cleanup(name)
|
||||||
|
|
||||||
def visit_object_type(self, name, info, base, members, variants):
|
def visit_object_type(self, name, info, base, members, variants):
|
||||||
|
# Nothing to do for the special empty builtin
|
||||||
|
if name == 'q_empty':
|
||||||
|
return
|
||||||
self._fwdecl += gen_fwd_object_or_array(name)
|
self._fwdecl += gen_fwd_object_or_array(name)
|
||||||
self.decl += gen_object(name, base, members, variants)
|
self.decl += gen_object(name, base, members, variants)
|
||||||
if base:
|
if base:
|
||||||
self.decl += gen_upcast(name, base)
|
self.decl += gen_upcast(name, base)
|
||||||
|
# TODO Worth changing the visitor signature, so we could
|
||||||
|
# directly use rather than repeat type.is_implicit()?
|
||||||
|
if not name.startswith('q_'):
|
||||||
|
# implicit types won't be directly allocated/freed
|
||||||
self._gen_type_cleanup(name)
|
self._gen_type_cleanup(name)
|
||||||
|
|
||||||
def visit_alternate_type(self, name, info, variants):
|
def visit_alternate_type(self, name, info, variants):
|
||||||
|
@ -215,13 +215,11 @@ out:
|
|||||||
|
|
||||||
|
|
||||||
def gen_visit_object(name, base, members, variants):
|
def gen_visit_object(name, base, members, variants):
|
||||||
ret = gen_visit_object_members(name, base, members, variants)
|
|
||||||
|
|
||||||
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
|
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
|
||||||
# *obj, but then visit_type_FOO_members() fails, we should clean up *obj
|
# *obj, but then visit_type_FOO_members() fails, we should clean up *obj
|
||||||
# rather than leaving it non-NULL. As currently written, the caller must
|
# rather than leaving it non-NULL. As currently written, the caller must
|
||||||
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
|
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
|
||||||
ret += mcgen('''
|
return mcgen('''
|
||||||
|
|
||||||
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
||||||
{
|
{
|
||||||
@ -245,8 +243,6 @@ out:
|
|||||||
''',
|
''',
|
||||||
c_name=c_name(name))
|
c_name=c_name(name))
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
|
class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -268,11 +264,6 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
|
|||||||
self.decl = self._btin + self.decl
|
self.decl = self._btin + self.decl
|
||||||
self._btin = None
|
self._btin = None
|
||||||
|
|
||||||
def visit_needed(self, entity):
|
|
||||||
# Visit everything except implicit objects
|
|
||||||
return not (entity.is_implicit() and
|
|
||||||
isinstance(entity, QAPISchemaObjectType))
|
|
||||||
|
|
||||||
def visit_enum_type(self, name, info, values, prefix):
|
def visit_enum_type(self, name, info, values, prefix):
|
||||||
# Special case for our lone builtin enum type
|
# Special case for our lone builtin enum type
|
||||||
# TODO use something cleaner than existence of info
|
# TODO use something cleaner than existence of info
|
||||||
@ -296,7 +287,15 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
|
|||||||
self.defn += defn
|
self.defn += defn
|
||||||
|
|
||||||
def visit_object_type(self, name, info, base, members, variants):
|
def visit_object_type(self, name, info, base, members, variants):
|
||||||
|
# Nothing to do for the special empty builtin
|
||||||
|
if name == 'q_empty':
|
||||||
|
return
|
||||||
self.decl += gen_visit_members_decl(name)
|
self.decl += gen_visit_members_decl(name)
|
||||||
|
self.defn += gen_visit_object_members(name, base, members, variants)
|
||||||
|
# TODO Worth changing the visitor signature, so we could
|
||||||
|
# directly use rather than repeat type.is_implicit()?
|
||||||
|
if not name.startswith('q_'):
|
||||||
|
# only explicit types need an allocating visit
|
||||||
self.decl += gen_visit_decl(name)
|
self.decl += gen_visit_decl(name)
|
||||||
self.defn += gen_visit_object(name, base, members, variants)
|
self.defn += gen_visit_object(name, base, members, variants)
|
||||||
|
|
||||||
|
@ -1000,7 +1000,6 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||||||
return self.name.startswith('q_')
|
return self.name.startswith('q_')
|
||||||
|
|
||||||
def c_name(self):
|
def c_name(self):
|
||||||
assert not self.is_implicit()
|
|
||||||
return QAPISchemaType.c_name(self)
|
return QAPISchemaType.c_name(self)
|
||||||
|
|
||||||
def c_type(self):
|
def c_type(self):
|
||||||
@ -1008,7 +1007,6 @@ class QAPISchemaObjectType(QAPISchemaType):
|
|||||||
return c_name(self.name) + pointer_suffix
|
return c_name(self.name) + pointer_suffix
|
||||||
|
|
||||||
def c_unboxed_type(self):
|
def c_unboxed_type(self):
|
||||||
assert not self.is_implicit()
|
|
||||||
return c_name(self.name)
|
return c_name(self.name)
|
||||||
|
|
||||||
def json_type(self):
|
def json_type(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user