X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f87ab7f9bd956250c48b5c6e9b607b537fd21543..e6b41ec37f0a9742374dfdb90e662745969cd7ea:/scripts/qapi-types.py diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 40e9f79b63..7e3051dbb9 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -2,7 +2,7 @@ # QAPI types generator # # Copyright IBM, Corp. 2011 -# Copyright (c) 2013-2015 Red Hat Inc. +# Copyright (c) 2013-2016 Red Hat Inc. # # Authors: # Anthony Liguori @@ -14,6 +14,11 @@ from qapi import * +# variants must be emitted before their container; track what has already +# been output +objects_seen = set() + + def gen_fwd_object_or_array(name): return mcgen(''' @@ -26,69 +31,69 @@ def gen_array(name, element_type): return mcgen(''' struct %(c_name)s { - union { - %(c_type)s value; - uint64_t padding; - }; %(c_name)s *next; + %(c_type)s value; }; ''', c_name=c_name(name), c_type=element_type.c_type()) -def gen_struct_field(name, typ, optional): +def gen_struct_members(members): ret = '' - - if optional: - ret += mcgen(''' + for memb in members: + if memb.optional: + ret += mcgen(''' bool has_%(c_name)s; ''', - c_name=c_name(name)) - ret += mcgen(''' + c_name=c_name(memb.name)) + ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=typ.c_type(), c_name=c_name(name)) + c_type=memb.type.c_type(), c_name=c_name(memb.name)) return ret -def gen_struct_fields(local_members, base=None): - ret = '' - - if base: - ret += mcgen(''' - /* Members inherited from %(c_name)s: */ -''', - c_name=base.c_name()) - for memb in base.members: - ret += gen_struct_field(memb.name, memb.type, memb.optional) - ret += mcgen(''' - /* Own members: */ -''') - - for memb in local_members: - ret += gen_struct_field(memb.name, memb.type, memb.optional) - return ret +def gen_object(name, base, members, variants): + if name in objects_seen: + return '' + objects_seen.add(name) + ret = '' + if variants: + for v in variants.variants: + if isinstance(v.type, QAPISchemaObjectType): + ret += gen_object(v.type.name, v.type.base, + v.type.local_members, v.type.variants) -def gen_struct(name, base, members): - ret = mcgen(''' + ret += mcgen(''' struct %(c_name)s { ''', - c_name=c_name(name)) + c_name=c_name(name)) if base: - ret += gen_struct_field('base', base, False) + if not base.is_implicit(): + ret += mcgen(''' + /* Members inherited from %(c_name)s: */ +''', + c_name=base.c_name()) + ret += gen_struct_members(base.members) + if not base.is_implicit(): + ret += mcgen(''' + /* Own members: */ +''') + ret += gen_struct_members(members) - ret += gen_struct_fields(members) + if variants: + ret += gen_variants(variants) - # Make sure that all structs have at least one field; this avoids + # Make sure that all structs have at least one member; this avoids # potential issues with attempting to malloc space for zero-length # structs in C, and also incompatibility with C++ (where an empty # struct is size 1). - if not base and not members: + if (not base or base.is_empty()) and not members and not variants: ret += mcgen(''' - char qapi_dummy_field_for_empty_struct; + char qapi_dummy_for_empty_struct; ''') ret += mcgen(''' @@ -98,81 +103,34 @@ struct %(c_name)s { return ret -def gen_alternate_qtypes_decl(name): +def gen_upcast(name, base): + # C makes const-correctness ugly. We have to cast away const to let + # this function work for both const and non-const obj. return mcgen(''' -extern const int %(c_name)s_qtypes[]; -''', - c_name=c_name(name)) - - -def gen_alternate_qtypes(name, variants): - ret = mcgen(''' - -const int %(c_name)s_qtypes[QTYPE_MAX] = { -''', - c_name=c_name(name)) - - for var in variants.variants: - qtype = var.type.alternate_qtype() - assert qtype - - ret += mcgen(''' - [%(qtype)s] = %(enum_const)s, +static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj) +{ + return (%(base)s *)obj; +} ''', - qtype=qtype, - enum_const=c_enum_const(variants.tag_member.type.name, - var.name)) - - ret += mcgen(''' -}; -''') - return ret + c_name=c_name(name), base=base.c_name()) -def gen_union(name, base, variants): +def gen_variants(variants): ret = mcgen(''' - -struct %(c_name)s { -''', - c_name=c_name(name)) - if base: - ret += gen_struct_fields([], base) - else: - ret += mcgen(''' - %(c_type)s kind; -''', - c_type=c_name(variants.tag_member.type.name)) - - # FIXME: What purpose does data serve, besides preventing a union that - # has a branch named 'data'? We use it in qapi-visit.py to decide - # whether to bypass the switch statement if visiting the discriminator - # failed; but since we 0-initialize structs, and cannot tell what - # branch of the union is in use if the discriminator is invalid, there - # should not be any data leaks even without a data pointer. Or, if - # 'data' is merely added to guarantee we don't have an empty union, - # shouldn't we enforce that at .json parse time? - ret += mcgen(''' union { /* union tag is @%(c_name)s */ - void *data; ''', - # TODO ugly special case for simple union - # Use same tag name in C as on the wire to get rid of - # it, then: c_name=c_name(variants.tag_member.name) - c_name=c_name(variants.tag_name or 'kind')) + c_name=c_name(variants.tag_member.name)) for var in variants.variants: - # Ugly special case for simple union TODO get rid of it - typ = var.simple_union_type() or var.type ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=typ.c_type(), + c_type=var.type.c_unboxed_type(), c_name=c_name(var.name)) ret += mcgen(''' - }; -}; + } u; ''') return ret @@ -192,17 +150,15 @@ def gen_type_cleanup(name): void qapi_free_%(c_name)s(%(c_name)s *obj) { - QapiDeallocVisitor *qdv; Visitor *v; if (!obj) { return; } - qdv = qapi_dealloc_visitor_new(); - v = qapi_dealloc_get_visitor(qdv); - visit_type_%(c_name)s(v, &obj, NULL, NULL); - qapi_dealloc_visitor_cleanup(qdv); + v = qapi_dealloc_visitor_new(); + visit_type_%(c_name)s(v, NULL, &obj, NULL); + visit_free(v); } ''', c_name=c_name(name)) @@ -214,21 +170,19 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self.decl = None self.defn = None self._fwdecl = None - self._fwdefn = None self._btin = None 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.defn = '' self._fwdecl = '' - self._fwdefn = '' self._btin = guardstart('QAPI_TYPES_BUILTIN') def visit_end(self): self.decl = self._fwdecl + self.decl self._fwdecl = None - self.defn = self._fwdefn + self.defn - self._fwdefn = None # To avoid header dependency hell, we always generate # declarations for built-in types in our header files and # simply guard them. See also do_builtins (command line @@ -237,18 +191,20 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self.decl = self._btin + self.decl 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): self.decl += gen_type_cleanup_decl(name) self.defn += gen_type_cleanup(name) def visit_enum_type(self, name, info, values, prefix): - self._fwdecl += gen_enum(name, values, prefix) - self._fwdefn += gen_enum_lookup(name, values, prefix) + # Special case for our lone builtin enum type + # TODO use something cleaner than existence of info + if not info: + self._btin += gen_enum(name, values, prefix) + if do_builtins: + self.defn += gen_enum_lookup(name, values, prefix) + else: + self._fwdecl += gen_enum(name, values, prefix) + self.defn += gen_enum_lookup(name, values, prefix) def visit_array_type(self, name, info, element_type): if isinstance(element_type, QAPISchemaBuiltinType): @@ -263,19 +219,22 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self._gen_type_cleanup(name) 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) - if variants: - assert not members # not implemented - self.decl += gen_union(name, base, variants) - else: - self.decl += gen_struct(name, base, members) - self._gen_type_cleanup(name) + self.decl += gen_object(name, base, members, variants) + if base and not base.is_implicit(): + 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) def visit_alternate_type(self, name, info, variants): self._fwdecl += gen_fwd_object_or_array(name) - self._fwdefn += gen_alternate_qtypes(name, variants) - self.decl += gen_union(name, None, variants) - self.decl += gen_alternate_qtypes_decl(name) + self.decl += gen_object(name, None, [variants.tag_member], variants) self._gen_type_cleanup(name) # If you link code generated from multiple schemata, you want only one @@ -285,10 +244,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line("b", ["builtins"]) + parse_command_line('b', ['builtins']) for o, a in opts: - if o in ("-b", "--builtins"): + if o in ('-b', '--builtins'): do_builtins = True c_comment = ''' @@ -326,6 +285,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" @@ -333,9 +293,7 @@ fdef.write(mcgen(''' prefix=prefix)) fdecl.write(mcgen(''' -#include -#include -#include "qapi/qmp/qobject.h" +#include "qapi/util.h" ''')) schema = QAPISchema(input_file)