From ac88219a6c78302c693fb60fe6cf04358540fbce Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:05 +0200 Subject: qapi: New QAPISchema intermediate reperesentation The QAPI code generators work with a syntax tree (nested dictionaries) plus a few symbol tables (also dictionaries) on the side. They have clearly outgrown these simple data structures. There's lots of rummaging around in dictionaries, and information is recomputed on the fly. For the work I'm going to do, I want more clearly defined and more convenient interfaces. Going forward, I also want less coupling between the back-ends and the syntax tree, to make messing with the syntax easier. Create a bunch of classes to represent QAPI schemata. Have the QAPISchema initializer call the parser, then walk the syntax tree to create the new internal representation, and finally perform semantic analysis. Shortcut: the semantic analysis still relies on existing check_exprs() to do the actual semantic checking. All this code needs to move into the classes. Mark as TODO. Simple unions are lowered to flat unions. Flat unions and structs are represented as a more general object type. Catching name collisions in generated code would be nice. Mark as TODO. We generate array types eagerly, even though most of them aren't used. Mark as TODO. Nothing uses the new intermediate representation just yet, thus no change to generated files. Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrange Reviewed-by: Eric Blake --- scripts/qapi-types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/qapi-types.py') diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index a8453d1365..23e95059c9 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -336,7 +336,7 @@ fdecl.write(mcgen(''' #include ''')) -exprs = parse_schema(input_file) +exprs = QAPISchema(input_file).get_exprs() fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL")) for typename in builtin_types.keys(): -- cgit 1.4.1 From 2b162ccbe875e5323fc04c1009addbdea4d35220 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:09 +0200 Subject: qapi-types: Convert to QAPISchemaVisitor, fixing flat unions Fixes flat unions to get the base's base members. Test case is from commit 2fc0043, in qapi-schema-test.json: { 'union': 'UserDefFlatUnion', 'base': 'UserDefUnionBase', 'discriminator': 'enum1', 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', 'value3' : 'UserDefB' } } { 'struct': 'UserDefUnionBase', 'base': 'UserDefZero', 'data': { 'string': 'str', 'enum1': 'EnumOne' } } { 'struct': 'UserDefZero', 'data': { 'integer': 'int' } } Patch's effect on UserDefFlatUnion: struct UserDefFlatUnion { /* Members inherited from UserDefUnionBase: */ + int64_t integer; char *string; EnumOne enum1; /* Own members: */ union { /* union tag is @enum1 */ void *data; UserDefA *value1; UserDefB *value2; UserDefB *value3; }; }; Flat union visitors remain broken. They'll be fixed next. Code is generated in a different order now, but that doesn't matter. The two guards QAPI_TYPES_BUILTIN_STRUCT_DECL and QAPI_TYPES_BUILTIN_CLEANUP_DECL are replaced by just QAPI_TYPES_BUILTIN. Two ugly special cases for simple unions now stand out like sore thumbs: 1. The type tag is named 'type' everywhere, except in generated C, where it's 'kind'. 2. QAPISchema lowers simple unions to semantically equivalent flat unions. However, the C generated for a simple unions differs from the C generated for its equivalent flat union, and we therefore need special code to preserve that pointless difference for now. Mark both TODO. Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrange Reviewed-by: Eric Blake --- docs/qapi-code-gen.txt | 29 ++-- scripts/qapi-types.py | 288 ++++++++++++++------------------ scripts/qapi.py | 10 +- tests/qapi-schema/qapi-schema-test.json | 4 +- 4 files changed, 154 insertions(+), 177 deletions(-) (limited to 'scripts/qapi-types.py') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index d960dc0234..c9e21fc6b2 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -545,7 +545,7 @@ Example: $ cat qapi-generated/example-qapi-types.c [Uninteresting stuff omitted...] - void qapi_free_UserDefOneList(UserDefOneList *obj) + void qapi_free_UserDefOne(UserDefOne *obj) { QapiDeallocVisitor *md; Visitor *v; @@ -556,12 +556,11 @@ Example: md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOneList(v, &obj, NULL, NULL); + visit_type_UserDefOne(v, &obj, NULL, NULL); qapi_dealloc_visitor_cleanup(md); } - - void qapi_free_UserDefOne(UserDefOne *obj) + void qapi_free_UserDefOneList(UserDefOneList *obj) { QapiDeallocVisitor *md; Visitor *v; @@ -572,7 +571,7 @@ Example: md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_UserDefOne(v, &obj, NULL, NULL); + visit_type_UserDefOneList(v, &obj, NULL, NULL); qapi_dealloc_visitor_cleanup(md); } $ cat qapi-generated/example-qapi-types.h @@ -585,24 +584,24 @@ Example: typedef struct UserDefOne UserDefOne; - typedef struct UserDefOneList { + typedef struct UserDefOneList UserDefOneList; + + struct UserDefOne { + int64_t integer; + char *string; + }; + + void qapi_free_UserDefOne(UserDefOne *obj); + + struct UserDefOneList { union { UserDefOne *value; uint64_t padding; }; struct UserDefOneList *next; - } UserDefOneList; - - -[Functions on built-in types omitted...] - - struct UserDefOne { - int64_t integer; - char *string; }; void qapi_free_UserDefOneList(UserDefOneList *obj); - void qapi_free_UserDefOne(UserDefOne *obj); #endif diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 23e95059c9..d78f529647 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -2,88 +2,67 @@ # QAPI types generator # # Copyright IBM, Corp. 2011 +# Copyright (c) 2013-2015 Red Hat Inc. # # Authors: # Anthony Liguori +# Markus Armbruster # # This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. -from ordereddict import OrderedDict from qapi import * -def generate_fwd_builtin(name): - return mcgen(''' - -typedef struct %(name)sList { - union { - %(type)s value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; -''', - type=c_type(name), - name=name) - -def generate_fwd_struct(name): +def gen_fwd_object_or_array(name): return mcgen(''' typedef struct %(name)s %(name)s; - -typedef struct %(name)sList { - union { - %(name)s *value; - uint64_t padding; - }; - struct %(name)sList *next; -} %(name)sList; ''', name=c_name(name)) -def generate_fwd_enum_struct(name): +def gen_array(name, element_type): return mcgen(''' -typedef struct %(name)sList { +struct %(name)s { union { - %(name)s value; + %(c_type)s value; uint64_t padding; }; - struct %(name)sList *next; -} %(name)sList; + struct %(name)s *next; +}; ''', - name=c_name(name)) + name=c_name(name), c_type=element_type.c_type()) -def generate_struct_fields(members): +def gen_struct_field(name, typ, optional): ret = '' - for argname, argentry, optional in parse_args(members): - if optional: - ret += mcgen(''' + if optional: + ret += mcgen(''' bool has_%(c_name)s; ''', - c_name=c_name(argname)) - ret += mcgen(''' + c_name=c_name(name)) + ret += mcgen(''' %(c_type)s %(c_name)s; ''', - c_type=c_type(argentry), c_name=c_name(argname)) - + c_type=typ.c_type(), c_name=c_name(name)) return ret -def generate_struct(expr): +def generate_struct_fields(members): + ret = '' - structname = expr.get('struct', "") - members = expr['data'] - base = expr.get('base') + for memb in members: + ret += gen_struct_field(memb.name, memb.type, memb.optional) + return ret +def gen_struct(name, base, members): ret = mcgen(''' struct %(name)s { ''', - name=c_name(structname)) + name=c_name(name)) if base: - ret += generate_struct_fields({'base': base}) + ret += gen_struct_field('base', base, False) ret += generate_struct_fields(members) @@ -156,46 +135,38 @@ typedef enum %(name)s { return enum_decl + lookup_decl -def generate_alternate_qtypes(expr): +def gen_alternate_qtypes_decl(name): + return mcgen(''' - name = expr['alternate'] - members = expr['data'] +extern const int %(c_name)s_qtypes[]; +''', + c_name=c_name(name)) +def gen_alternate_qtypes(name, variants): ret = mcgen(''' const int %(name)s_qtypes[QTYPE_MAX] = { ''', name=c_name(name)) - for key in members: - qtype = find_alternate_member_qtype(members[key]) - assert qtype, "Invalid alternate member" + for var in variants.variants: + qtype = var.type.alternate_qtype() + assert qtype ret += mcgen(''' [%(qtype)s] = %(enum_const)s, ''', qtype = qtype, - enum_const = c_enum_const(name + 'Kind', key)) + enum_const=c_enum_const(variants.tag_member.type.name, + var.name)) ret += mcgen(''' }; ''') return ret - -def generate_union(expr, meta): - - name = c_name(expr[meta]) - typeinfo = expr['data'] - - base = expr.get('base') - discriminator = expr.get('discriminator') - - enum_define = discriminator_find_enum_define(expr) - if enum_define: - discriminator_type_name = enum_define['enum_name'] - else: - discriminator_type_name = '%sKind' % (name) +def gen_union(name, base, variants): + name = c_name(name) ret = mcgen(''' @@ -206,18 +177,16 @@ struct %(name)s { ret += mcgen(''' /* Members inherited from %(c_name)s: */ ''', - c_name=c_name(base)) - base_fields = find_struct(base)['data'] - ret += generate_struct_fields(base_fields) + c_name=c_name(base.name)) + ret += generate_struct_fields(base.members) ret += mcgen(''' /* Own members: */ ''') else: - assert not discriminator ret += mcgen(''' %(discriminator_type_name)s kind; ''', - discriminator_type_name=c_name(discriminator_type_name)) + discriminator_type_name=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 @@ -231,39 +200,39 @@ struct %(name)s { union { /* union tag is @%(c_name)s */ void *data; ''', - c_name=c_name(discriminator or 'kind')) - - for key in typeinfo: + # 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')) + + 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=c_type(typeinfo[key]), - c_name=c_name(key)) + c_type=typ.c_type(), + c_name=c_name(var.name)) ret += mcgen(''' }; }; ''') - if meta == 'alternate': - ret += mcgen(''' -extern const int %(name)s_qtypes[]; -''', - name=name) - return ret def generate_type_cleanup_decl(name): ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj); + +void qapi_free_%(name)s(%(name)s *obj); ''', - c_type=c_type(name), name=c_name(name)) + name=c_name(name)) return ret def generate_type_cleanup(name): ret = mcgen(''' -void qapi_free_%(name)s(%(c_type)s obj) +void qapi_free_%(name)s(%(name)s *obj) { QapiDeallocVisitor *md; Visitor *v; @@ -278,9 +247,79 @@ void qapi_free_%(name)s(%(c_type)s obj) qapi_dealloc_visitor_cleanup(md); } ''', - c_type=c_type(name), name=c_name(name)) + name=c_name(name)) return ret + +class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): + def __init__(self): + self.decl = None + self.defn = None + self._fwdecl = None + self._fwdefn = None + self._btin = None + + def visit_begin(self, schema): + 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 + # option -b). + self._btin += guardend('QAPI_TYPES_BUILTIN') + self.decl = self._btin + self.decl + self._btin = None + + def _gen_type_cleanup(self, name): + self.decl += generate_type_cleanup_decl(name) + self.defn += generate_type_cleanup(name) + + def visit_enum_type(self, name, info, values, prefix): + self._fwdecl += generate_enum(name, values, prefix) + self._fwdefn += generate_enum_lookup(name, values, prefix) + + def visit_array_type(self, name, info, element_type): + if isinstance(element_type, QAPISchemaBuiltinType): + self._btin += gen_fwd_object_or_array(name) + self._btin += gen_array(name, element_type) + self._btin += generate_type_cleanup_decl(name) + if do_builtins: + self.defn += generate_type_cleanup(name) + else: + self._fwdecl += gen_fwd_object_or_array(name) + self.decl += gen_array(name, element_type) + self._gen_type_cleanup(name) + + def visit_object_type(self, name, info, base, members, variants): + if info: + 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) + + 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._gen_type_cleanup(name) + +# If you link code generated from multiple schemata, you want only one +# instance of the code for built-in types. Generate it only when +# do_builtins, enabled by command line option -b. See also +# QAPISchemaGenTypeVisitor.visit_end(). do_builtins = False (input_file, output_dir, do_c, do_h, prefix, opts) = \ @@ -336,79 +375,10 @@ fdecl.write(mcgen(''' #include ''')) -exprs = QAPISchema(input_file).get_exprs() - -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_fwd_builtin(typename)) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL")) - -for expr in exprs: - ret = "" - if expr.has_key('struct'): - ret += generate_fwd_struct(expr['struct']) - elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data'], - expr.get('prefix')) - ret += generate_fwd_enum_struct(expr['enum']) - fdef.write(generate_enum_lookup(expr['enum'], expr['data'], - expr.get('prefix'))) - elif expr.has_key('union'): - ret += generate_fwd_struct(expr['union']) - enum_define = discriminator_find_enum_define(expr) - if not enum_define: - ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['union'], - expr['data'].keys())) - elif expr.has_key('alternate'): - ret += generate_fwd_struct(expr['alternate']) - ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys()) - fdef.write(generate_enum_lookup('%sKind' % expr['alternate'], - expr['data'].keys())) - fdef.write(generate_alternate_qtypes(expr)) - else: - continue - fdecl.write(ret) - -# to avoid header dependency hell, we always generate declarations -# for built-in types in our header files and simply guard them -fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) -for typename in builtin_types.keys(): - fdecl.write(generate_type_cleanup_decl(typename + "List")) -fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) - -# ...this doesn't work for cases where we link in multiple objects that -# have the functions defined, so we use -b option to provide control -# over these cases -if do_builtins: - for typename in builtin_types.keys(): - fdef.write(generate_type_cleanup(typename + "List")) - -for expr in exprs: - ret = "" - if expr.has_key('struct'): - ret += generate_struct(expr) + "\n" - ret += generate_type_cleanup_decl(expr['struct'] + "List") - fdef.write(generate_type_cleanup(expr['struct'] + "List")) - ret += generate_type_cleanup_decl(expr['struct']) - fdef.write(generate_type_cleanup(expr['struct'])) - elif expr.has_key('union'): - ret += generate_union(expr, 'union') + "\n" - ret += generate_type_cleanup_decl(expr['union'] + "List") - fdef.write(generate_type_cleanup(expr['union'] + "List")) - ret += generate_type_cleanup_decl(expr['union']) - fdef.write(generate_type_cleanup(expr['union'])) - elif expr.has_key('alternate'): - ret += generate_union(expr, 'alternate') + "\n" - ret += generate_type_cleanup_decl(expr['alternate'] + "List") - fdef.write(generate_type_cleanup(expr['alternate'] + "List")) - ret += generate_type_cleanup_decl(expr['alternate']) - fdef.write(generate_type_cleanup(expr['alternate'])) - elif expr.has_key('enum'): - ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List") - fdef.write(generate_type_cleanup(expr['enum'] + "List")) - else: - continue - fdecl.write(ret) +schema = QAPISchema(input_file) +gen = QAPISchemaGenTypeVisitor() +schema.visit(gen) +fdef.write(gen.defn) +fdecl.write(gen.decl) close_output(fdef, fdecl) diff --git a/scripts/qapi.py b/scripts/qapi.py index 36e07024f4..c18400d51f 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -995,7 +995,6 @@ class QAPISchemaObjectTypeVariants(object): vseen = dict(seen) v.check(schema, self.tag_member.type, vseen) - class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): def __init__(self, name, typ): QAPISchemaObjectTypeMember.__init__(self, name, typ, False) @@ -1004,6 +1003,15 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): QAPISchemaObjectTypeMember.check(self, schema, [], seen) assert self.name in tag_type.values + # This function exists to support ugly simple union special cases + # TODO get rid of them, and drop the function + def simple_union_type(self): + if isinstance(self.type, QAPISchemaObjectType) and not self.type.info: + assert len(self.type.members) == 1 + assert not self.type.variants + return self.type.members[0].type + return None + class QAPISchemaAlternateType(QAPISchemaType): def __init__(self, name, info, variants): diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 677190c1ef..ca81a82826 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -44,8 +44,8 @@ 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', 'value3' : 'UserDefB' } } -# FIXME generated struct UserDefFlatUnion has members for direct base -# UserDefUnionBase, but lacks members for indirect base UserDefZero +# FIXME generated visit_type_UserDefFlatUnion_fields() fails to visit +# members of indirect base UserDefZero { 'struct': 'UserDefUnionBase', 'base': 'UserDefZero', -- cgit 1.4.1 From efd2eaa6c2992c214a13f102b6ddd4dca4697fb3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:12 +0200 Subject: qapi: De-duplicate enum code generation Duplicated in commit 21cd70d. Yes, we can't import qapi-types, but that's no excuse. Move the helpers from qapi-types.py to qapi.py, and replace the duplicates in qapi-event.py. The generated event enumeration type's lookup table becomes const-correct (see commit 2e4450f), and uses explicit indexes instead of relying on order (see commit 912ae9c). Signed-off-by: Markus Armbruster Message-Id: <1442401589-24189-10-git-send-email-armbru@redhat.com> Reviewed-by: Daniel P. Berrange Reviewed-by: Eric Blake --- docs/qapi-code-gen.txt | 9 ++++--- scripts/qapi-event.py | 67 +++----------------------------------------------- scripts/qapi-types.py | 55 ----------------------------------------- scripts/qapi.py | 55 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 122 deletions(-) (limited to 'scripts/qapi-types.py') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index c9e21fc6b2..81e87d2ec1 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -829,9 +829,9 @@ Example: QDECREF(qmp); } - const char *example_QAPIEvent_lookup[] = { - "MY_EVENT", - NULL, + const char *const example_QAPIEvent_lookup[] = { + [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT", + [EXAMPLE_QAPI_EVENT_MAX] = NULL, }; $ cat qapi-generated/example-qapi-event.h [Uninteresting stuff omitted...] @@ -846,10 +846,11 @@ Example: void qapi_event_send_my_event(Error **errp); - extern const char *example_QAPIEvent_lookup[]; typedef enum example_QAPIEvent { EXAMPLE_QAPI_EVENT_MY_EVENT = 0, EXAMPLE_QAPI_EVENT_MAX = 1, } example_QAPIEvent; + extern const char *const example_QAPIEvent_lookup[]; + #endif diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index aec2d32259..aed45d620c 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -153,63 +153,6 @@ def generate_event_implement(api_name, event_name, params): return ret - -# Following are the functions that generate an enum type for all defined -# events, similar to qapi-types.py. Here we already have enum name and -# values which were generated before and recorded in event_enum_*. It also -# works around the issue that "import qapi-types" can't work. - -def generate_event_enum_decl(event_enum_name, event_enum_values): - lookup_decl = mcgen(''' - -extern const char *%(event_enum_name)s_lookup[]; -''', - event_enum_name = event_enum_name) - - enum_decl = mcgen(''' -typedef enum %(event_enum_name)s { -''', - event_enum_name = event_enum_name) - - # append automatically generated _MAX value - enum_max_value = c_enum_const(event_enum_name, "MAX") - enum_values = event_enum_values + [ enum_max_value ] - - i = 0 - for value in enum_values: - enum_decl += mcgen(''' - %(value)s = %(i)d, -''', - value = value, - i = i) - i += 1 - - enum_decl += mcgen(''' -} %(event_enum_name)s; -''', - event_enum_name = event_enum_name) - - return lookup_decl + enum_decl - -def generate_event_enum_lookup(event_enum_name, event_enum_strings): - ret = mcgen(''' - -const char *%(event_enum_name)s_lookup[] = { -''', - event_enum_name = event_enum_name) - - for string in event_enum_strings: - ret += mcgen(''' - "%(string)s", -''', - string = string) - - ret += mcgen(''' - NULL, -}; -''') - return ret - (input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() c_comment = ''' @@ -266,8 +209,7 @@ fdecl.write(mcgen(''' exprs = QAPISchema(input_file).get_exprs() event_enum_name = c_name(prefix + "QAPIEvent", protect=False) -event_enum_values = [] -event_enum_strings = [] +event_names = [] for expr in exprs: if expr.has_key('event'): @@ -286,12 +228,11 @@ for expr in exprs: fdef.write(ret) # Record it, and generate enum later - event_enum_values.append(event_enum_value) - event_enum_strings.append(event_name) + event_names.append(event_name) -ret = generate_event_enum_decl(event_enum_name, event_enum_values) +ret = generate_enum(event_enum_name, event_names) fdecl.write(ret) -ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) +ret = generate_enum_lookup(event_enum_name, event_names) fdef.write(ret) close_output(fdef, fdecl) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index d78f529647..76c82d96b9 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -80,61 +80,6 @@ struct %(name)s { return ret -def generate_enum_lookup(name, values, prefix=None): - ret = mcgen(''' - -const char *const %(name)s_lookup[] = { -''', - name=c_name(name)) - for value in values: - index = c_enum_const(name, value, prefix) - ret += mcgen(''' - [%(index)s] = "%(value)s", -''', - index = index, value = value) - - max_index = c_enum_const(name, 'MAX', prefix) - ret += mcgen(''' - [%(max_index)s] = NULL, -}; -''', - max_index=max_index) - return ret - -def generate_enum(name, values, prefix=None): - name = c_name(name) - lookup_decl = mcgen(''' - -extern const char *const %(name)s_lookup[]; -''', - name=name) - - enum_decl = mcgen(''' - -typedef enum %(name)s { -''', - name=name) - - # append automatically generated _MAX value - enum_values = values + [ 'MAX' ] - - i = 0 - for value in enum_values: - enum_full_value = c_enum_const(name, value, prefix) - enum_decl += mcgen(''' - %(enum_full_value)s = %(i)d, -''', - enum_full_value = enum_full_value, - i=i) - i += 1 - - enum_decl += mcgen(''' -} %(name)s; -''', - name=name) - - return enum_decl + lookup_decl - def gen_alternate_qtypes_decl(name): return mcgen(''' diff --git a/scripts/qapi.py b/scripts/qapi.py index db5bc8533f..ba32aace99 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -1498,6 +1498,61 @@ def guardend(name): ''', name=guardname(name)) +def generate_enum_lookup(name, values, prefix=None): + ret = mcgen(''' + +const char *const %(name)s_lookup[] = { +''', + name=c_name(name)) + for value in values: + index = c_enum_const(name, value, prefix) + ret += mcgen(''' + [%(index)s] = "%(value)s", +''', + index = index, value = value) + + max_index = c_enum_const(name, 'MAX', prefix) + ret += mcgen(''' + [%(max_index)s] = NULL, +}; +''', + max_index=max_index) + return ret + +def generate_enum(name, values, prefix=None): + name = c_name(name) + lookup_decl = mcgen(''' + +extern const char *const %(name)s_lookup[]; +''', + name=name) + + enum_decl = mcgen(''' + +typedef enum %(name)s { +''', + name=name) + + # append automatically generated _MAX value + enum_values = values + [ 'MAX' ] + + i = 0 + for value in enum_values: + enum_full_value = c_enum_const(name, value, prefix) + enum_decl += mcgen(''' + %(enum_full_value)s = %(i)d, +''', + enum_full_value = enum_full_value, + i=i) + i += 1 + + enum_decl += mcgen(''' +} %(name)s; +''', + name=name) + + return enum_decl + lookup_decl + # # Common command line parsing # -- cgit 1.4.1 From e98859a9b96d71dea8f9af43325edd43c7effe66 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:16 +0200 Subject: qapi: Clean up after recent conversions to QAPISchemaVisitor Generate just 'FOO' instead of 'struct FOO' when possible. Drop helper functions that are now unused. Make pep8 and pylint reasonably happy. Rename generate_FOO() functions to gen_FOO() for consistency. Use more consistent and sensible variable names. Consistently use c_ for mapping keys when their value is a C identifier or type. Simplify gen_enum() and gen_visit_union() Consistently use single quotes for C text string literals. Signed-off-by: Markus Armbruster Message-Id: <1442401589-24189-14-git-send-email-armbru@redhat.com> Reviewed-by: Daniel P. Berrange Reviewed-by: Eric Blake --- docs/qapi-code-gen.txt | 2 +- scripts/qapi-commands.py | 140 ++++++++++++++++++++++++++--------------------- scripts/qapi-event.py | 122 ++++++++++++++++++++--------------------- scripts/qapi-types.py | 79 ++++++++++++++------------ scripts/qapi-visit.py | 127 ++++++++++++++++++++++-------------------- scripts/qapi.py | 127 ++++++++---------------------------------- 6 files changed, 272 insertions(+), 325 deletions(-) (limited to 'scripts/qapi-types.py') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 81e87d2ec1..3eaff367a4 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -598,7 +598,7 @@ Example: UserDefOne *value; uint64_t padding; }; - struct UserDefOneList *next; + UserDefOneList *next; }; void qapi_free_UserDefOneList(UserDefOneList *obj); diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index cbff3560f4..0501582c3b 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -15,20 +15,22 @@ from qapi import * import re -def generate_command_decl(name, args, ret_type): - arglist="" - if args: - for memb in args.members: - argtype = memb.type.c_type(is_param=True) + +def gen_command_decl(name, arg_type, ret_type): + argstr = '' + if arg_type: + for memb in arg_type.members: if memb.optional: - arglist += "bool has_%s, " % c_name(memb.name) - arglist += "%s %s, " % (argtype, c_name(memb.name)) + argstr += 'bool has_%s, ' % c_name(memb.name) + argstr += '%s %s, ' % (memb.type.c_type(is_param=True), + c_name(memb.name)) return mcgen(''' -%(ret_type)s qmp_%(name)s(%(args)sError **errp); +%(c_type)s qmp_%(c_name)s(%(args)sError **errp); ''', - ret_type=(ret_type and ret_type.c_type()) or 'void', - name=c_name(name), - args=arglist) + c_type=(ret_type and ret_type.c_type()) or 'void', + c_name=c_name(name), + args=argstr) + def gen_err_check(err): if not err: @@ -40,37 +42,42 @@ if (%(err)s) { ''', err=err) -def gen_sync_call(name, args, ret_type): - ret = "" - arglist="" - retval="" - if ret_type: - retval = "retval = " - if args: - for memb in args.members: + +def gen_call(name, arg_type, ret_type): + ret = '' + + argstr = '' + if arg_type: + for memb in arg_type.members: if memb.optional: - arglist += "has_%s, " % c_name(memb.name) - arglist += "%s, " % c_name(memb.name) + argstr += 'has_%s, ' % c_name(memb.name) + argstr += '%s, ' % c_name(memb.name) + + lhs = '' + if ret_type: + lhs = 'retval = ' + push_indent() ret = mcgen(''' -%(retval)sqmp_%(name)s(%(args)s&local_err); +%(lhs)sqmp_%(c_name)s(%(args)s&local_err); ''', - name=c_name(name), args=arglist, retval=retval) + c_name=c_name(name), args=argstr, lhs=lhs) if ret_type: ret += gen_err_check('local_err') ret += mcgen(''' qmp_marshal_output_%(c_name)s(retval, ret, &local_err); ''', - c_name=c_name(name)) + c_name=c_name(name)) pop_indent() return ret -def gen_visitor_input_containers_decl(args): - ret = "" + +def gen_visitor_input_containers_decl(arg_type): + ret = '' push_indent() - if args: + if arg_type: ret += mcgen(''' QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; @@ -80,17 +87,18 @@ Visitor *v; return ret -def gen_visitor_input_vars_decl(args): - ret = "" + +def gen_visitor_input_vars_decl(arg_type): + ret = '' push_indent() - if args: - for memb in args.members: + if arg_type: + for memb in arg_type.members: if memb.optional: ret += mcgen(''' -bool has_%(argname)s = false; +bool has_%(c_name)s = false; ''', - argname=c_name(memb.name)) + c_name=c_name(memb.name)) ret += mcgen(''' %(c_type)s %(c_name)s = %(c_null)s; ''', @@ -101,19 +109,20 @@ bool has_%(argname)s = false; pop_indent() return ret -def gen_visitor_input_block(args, dealloc=False): - ret = "" + +def gen_visitor_input_block(arg_type, dealloc=False): + ret = '' errparg = '&local_err' errarg = 'local_err' - if not args: + if not arg_type: return ret push_indent() if dealloc: errparg = 'NULL' - errarg = None; + errarg = None ret += mcgen(''' qmp_input_visitor_cleanup(mi); md = qapi_dealloc_visitor_new(); @@ -124,7 +133,7 @@ v = qapi_dealloc_get_visitor(md); v = qmp_input_get_visitor(mi); ''') - for memb in args.members: + for memb in arg_type.members: if memb.optional: ret += mcgen(''' visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); @@ -138,10 +147,10 @@ if (has_%(c_name)s) { c_name=c_name(memb.name)) push_indent() ret += mcgen(''' -visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); +visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s); ''', c_name=c_name(memb.name), name=memb.name, - visitor=memb.type.c_name(), errp=errparg) + c_type=memb.type.c_name(), errp=errparg) ret += gen_err_check(errarg) if memb.optional: pop_indent() @@ -156,13 +165,14 @@ qapi_dealloc_visitor_cleanup(md); pop_indent() return ret + def gen_marshal_output(name, ret_type): if not ret_type: - return "" + return '' ret = mcgen(''' -static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +static void qmp_marshal_output_%(c_cmd_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) { Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); @@ -170,7 +180,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o Visitor *v; v = qmp_output_get_visitor(mo); - visit_type_%(visitor)s(v, &ret_in, "unused", &local_err); + visit_type_%(c_name)s(v, &ret_in, "unused", &local_err); if (local_err) { goto out; } @@ -181,23 +191,25 @@ out: qmp_output_visitor_cleanup(mo); md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_%(visitor)s(v, &ret_in, "unused", NULL); + visit_type_%(c_name)s(v, &ret_in, "unused", NULL); qapi_dealloc_visitor_cleanup(md); } ''', - c_ret_type=ret_type.c_type(), c_name=c_name(name), - visitor=ret_type.c_name()) + c_type=ret_type.c_type(), c_cmd_name=c_name(name), + c_name=ret_type.c_name()) return ret -def gen_marshal_input_decl(name, middle_mode): + +def gen_marshal_input_decl(name): ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: - ret = "static " + ret + ret = 'static ' + ret return ret -def gen_marshal_input(name, args, ret_type, middle_mode): - hdr = gen_marshal_input_decl(name, middle_mode) + +def gen_marshal_input(name, arg_type, ret_type): + hdr = gen_marshal_input_decl(name) ret = mcgen(''' @@ -213,10 +225,10 @@ def gen_marshal_input(name, args, ret_type, middle_mode): ''', c_type=ret_type.c_type()) - if args: - ret += gen_visitor_input_containers_decl(args) - ret += gen_visitor_input_vars_decl(args) + '\n' - ret += gen_visitor_input_block(args) + '\n' + if arg_type: + ret += gen_visitor_input_containers_decl(arg_type) + ret += gen_visitor_input_vars_decl(arg_type) + '\n' + ret += gen_visitor_input_block(arg_type) + '\n' else: ret += mcgen(''' @@ -224,9 +236,9 @@ def gen_marshal_input(name, args, ret_type, middle_mode): ''') - ret += gen_sync_call(name, args, ret_type) + ret += gen_call(name, arg_type, ret_type) - if re.search('^ *goto out\\;', ret, re.MULTILINE): + if re.search('^ *goto out;', ret, re.MULTILINE): ret += mcgen(''' out: @@ -234,12 +246,13 @@ out: ret += mcgen(''' error_propagate(errp, local_err); ''') - ret += gen_visitor_input_block(args, dealloc=True) + ret += gen_visitor_input_block(arg_type, dealloc=True) ret += mcgen(''' } ''') return ret + def gen_register_command(name, success_response): push_indent() options = 'QCO_NO_OPTIONS' @@ -249,11 +262,12 @@ def gen_register_command(name, success_response): ret = mcgen(''' qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); ''', - name=name, c_name=c_name(name), - opts=options) + name=name, c_name=c_name(name), + opts=options) pop_indent() return ret + def gen_registry(registry): ret = mcgen(''' @@ -289,12 +303,12 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): gen, success_response): if not gen: return - self.decl += generate_command_decl(name, arg_type, ret_type) + self.decl += gen_command_decl(name, arg_type, ret_type) if ret_type: self.defn += gen_marshal_output(name, ret_type) if middle_mode: - self.decl += gen_marshal_input_decl(name, middle_mode) + ';\n' - self.defn += gen_marshal_input(name, arg_type, ret_type, middle_mode) + self.decl += gen_marshal_input_decl(name) + ';\n' + self.defn += gen_marshal_input(name, arg_type, ret_type) if not middle_mode: self._regy += gen_register_command(name, success_response) @@ -355,7 +369,7 @@ fdef.write(mcgen(''' #include "%(prefix)sqmp-commands.h" ''', - prefix=prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "%(prefix)sqapi-types.h" diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index d19acda7d3..5873a05888 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -13,12 +13,13 @@ from qapi import * -def _generate_event_api_name(event_name, params): - api_name = "void qapi_event_send_%s(" % c_name(event_name).lower(); + +def gen_event_send_proto(name, arg_type): + api_name = "void qapi_event_send_%s(" % c_name(name).lower() l = len(api_name) - if params: - for m in params.members: + if arg_type: + for m in arg_type.members: if m.optional: api_name += "bool has_%s,\n" % c_name(m.name) api_name += "".ljust(l) @@ -28,53 +29,49 @@ def _generate_event_api_name(event_name, params): api_name += "".ljust(l) api_name += "Error **errp)" - return api_name; - + return api_name -# Following are the core functions that generate C APIs to emit event. -def generate_event_declaration(api_name): +def gen_event_send_decl(name, arg_type): return mcgen(''' -%(api_name)s; +%(proto)s; ''', - api_name = api_name) + proto=gen_event_send_proto(name, arg_type)) + -def generate_event_implement(api_name, event_name, params): - # step 1: declare any variables - ret = mcgen(""" +def gen_event_send(name, arg_type): + ret = mcgen(''' -%(api_name)s +%(proto)s { QDict *qmp; Error *local_err = NULL; QMPEventFuncEmit emit; -""", - api_name = api_name) +''', + proto=gen_event_send_proto(name, arg_type)) - if params and params.members: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' QmpOutputVisitor *qov; Visitor *v; QObject *obj; -""") +''') - # step 2: check emit function, create a dict - ret += mcgen(""" + ret += mcgen(''' emit = qmp_event_get_func_emit(); if (!emit) { return; } - qmp = qmp_event_build_dict("%(event_name)s"); + qmp = qmp_event_build_dict("%(name)s"); -""", - event_name = event_name) +''', + name=name) - # step 3: visit the params if params != None - if params and params.members: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' qov = qmp_output_visitor_new(); g_assert(qov); @@ -82,45 +79,46 @@ def generate_event_implement(api_name, event_name, params): g_assert(v); /* Fake visit, as if all members are under a structure */ - visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); + visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err); if (local_err) { goto clean; } -""", - event_name = event_name) +''', + name=name) - for memb in params.members: + for memb in arg_type.members: if memb.optional: - ret += mcgen(""" - if (has_%(var)s) { -""", - var=c_name(memb.name)) + ret += mcgen(''' + if (has_%(c_name)s) { +''', + c_name=c_name(memb.name)) push_indent() + # Ugly: need to cast away the const if memb.type.name == "str": - var_type = "(char **)" + cast = '(char **)' else: - var_type = "" + cast = '' - ret += mcgen(""" - visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); + ret += mcgen(''' + visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err); if (local_err) { goto clean; } -""", - var_type = var_type, - var=c_name(memb.name), - type=memb.type.c_name(), +''', + cast=cast, + c_name=c_name(memb.name), + c_type=memb.type.c_name(), name=memb.name) if memb.optional: pop_indent() - ret += mcgen(""" + ret += mcgen(''' } -""") +''') - ret += mcgen(""" + ret += mcgen(''' visit_end_struct(v, &local_err); if (local_err) { @@ -131,27 +129,24 @@ def generate_event_implement(api_name, event_name, params): g_assert(obj != NULL); qdict_put_obj(qmp, "data", obj); -""") +''') - # step 4: call qmp event api - ret += mcgen(""" - emit(%(event_enum_value)s, qmp, &local_err); + ret += mcgen(''' + emit(%(c_enum)s, qmp, &local_err); -""", - event_enum_value = c_enum_const(event_enum_name, event_name)) +''', + c_enum=c_enum_const(event_enum_name, name)) - # step 5: clean up - if params and params.members: - ret += mcgen(""" + if arg_type and arg_type.members: + ret += mcgen(''' clean: qmp_output_visitor_cleanup(qov); -""") - ret += mcgen(""" +''') + ret += mcgen(''' error_propagate(errp, local_err); QDECREF(qmp); } -""") - +''') return ret @@ -167,14 +162,13 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor): self._event_names = [] def visit_end(self): - self.decl += generate_enum(event_enum_name, self._event_names) - self.defn += generate_enum_lookup(event_enum_name, self._event_names) + self.decl += gen_enum(event_enum_name, self._event_names) + self.defn += gen_enum_lookup(event_enum_name, self._event_names) self._event_names = None def visit_event(self, name, info, arg_type): - api_name = _generate_event_api_name(name, arg_type) - self.decl += generate_event_declaration(api_name) - self.defn += generate_event_implement(api_name, name, arg_type) + self.decl += gen_event_send_decl(name, arg_type) + self.defn += gen_event_send(name, arg_type) self._event_names.append(name) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 76c82d96b9..d1fee207e5 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -13,25 +13,28 @@ from qapi import * + def gen_fwd_object_or_array(name): return mcgen(''' -typedef struct %(name)s %(name)s; +typedef struct %(c_name)s %(c_name)s; ''', - name=c_name(name)) + c_name=c_name(name)) + def gen_array(name, element_type): return mcgen(''' -struct %(name)s { +struct %(c_name)s { union { %(c_type)s value; uint64_t padding; }; - struct %(name)s *next; + %(c_name)s *next; }; ''', - name=c_name(name), c_type=element_type.c_type()) + c_name=c_name(name), c_type=element_type.c_type()) + def gen_struct_field(name, typ, optional): ret = '' @@ -47,30 +50,33 @@ def gen_struct_field(name, typ, optional): c_type=typ.c_type(), c_name=c_name(name)) return ret -def generate_struct_fields(members): + +def gen_struct_fields(members): ret = '' for memb in members: ret += gen_struct_field(memb.name, memb.type, memb.optional) return ret + def gen_struct(name, base, members): ret = mcgen(''' -struct %(name)s { +struct %(c_name)s { ''', - name=c_name(name)) + c_name=c_name(name)) if base: ret += gen_struct_field('base', base, False) - ret += generate_struct_fields(members) + ret += gen_struct_fields(members) # Make sure that all structs have at least one field; 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). + # 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: - ret += mcgen(''' + ret += mcgen(''' char qapi_dummy_field_for_empty_struct; ''') @@ -80,6 +86,7 @@ struct %(name)s { return ret + def gen_alternate_qtypes_decl(name): return mcgen(''' @@ -87,12 +94,13 @@ extern const int %(c_name)s_qtypes[]; ''', c_name=c_name(name)) + def gen_alternate_qtypes(name, variants): ret = mcgen(''' -const int %(name)s_qtypes[QTYPE_MAX] = { +const int %(c_name)s_qtypes[QTYPE_MAX] = { ''', - name=c_name(name)) + c_name=c_name(name)) for var in variants.variants: qtype = var.type.alternate_qtype() @@ -101,7 +109,7 @@ const int %(name)s_qtypes[QTYPE_MAX] = { ret += mcgen(''' [%(qtype)s] = %(enum_const)s, ''', - qtype = qtype, + qtype=qtype, enum_const=c_enum_const(variants.tag_member.type.name, var.name)) @@ -110,28 +118,27 @@ const int %(name)s_qtypes[QTYPE_MAX] = { ''') return ret -def gen_union(name, base, variants): - name = c_name(name) +def gen_union(name, base, variants): ret = mcgen(''' -struct %(name)s { +struct %(c_name)s { ''', - name=name) + c_name=c_name(name)) if base: ret += mcgen(''' /* Members inherited from %(c_name)s: */ ''', c_name=c_name(base.name)) - ret += generate_struct_fields(base.members) + ret += gen_struct_fields(base.members) ret += mcgen(''' /* Own members: */ ''') else: ret += mcgen(''' - %(discriminator_type_name)s kind; + %(c_type)s kind; ''', - discriminator_type_name=c_name(variants.tag_member.type.name)) + 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 @@ -166,18 +173,20 @@ struct %(name)s { return ret -def generate_type_cleanup_decl(name): + +def gen_type_cleanup_decl(name): ret = mcgen(''' -void qapi_free_%(name)s(%(name)s *obj); +void qapi_free_%(c_name)s(%(c_name)s *obj); ''', - name=c_name(name)) + c_name=c_name(name)) return ret -def generate_type_cleanup(name): + +def gen_type_cleanup(name): ret = mcgen(''' -void qapi_free_%(name)s(%(name)s *obj) +void qapi_free_%(c_name)s(%(c_name)s *obj) { QapiDeallocVisitor *md; Visitor *v; @@ -188,11 +197,11 @@ void qapi_free_%(name)s(%(name)s *obj) md = qapi_dealloc_visitor_new(); v = qapi_dealloc_get_visitor(md); - visit_type_%(name)s(v, &obj, NULL, NULL); + visit_type_%(c_name)s(v, &obj, NULL, NULL); qapi_dealloc_visitor_cleanup(md); } ''', - name=c_name(name)) + c_name=c_name(name)) return ret @@ -225,20 +234,20 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self._btin = None def _gen_type_cleanup(self, name): - self.decl += generate_type_cleanup_decl(name) - self.defn += generate_type_cleanup(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 += generate_enum(name, values, prefix) - self._fwdefn += generate_enum_lookup(name, values, prefix) + self._fwdecl += gen_enum(name, values, prefix) + self._fwdefn += gen_enum_lookup(name, values, prefix) def visit_array_type(self, name, info, element_type): if isinstance(element_type, QAPISchemaBuiltinType): self._btin += gen_fwd_object_or_array(name) self._btin += gen_array(name, element_type) - self._btin += generate_type_cleanup_decl(name) + self._btin += gen_type_cleanup_decl(name) if do_builtins: - self.defn += generate_type_cleanup(name) + self.defn += gen_type_cleanup(name) else: self._fwdecl += gen_fwd_object_or_array(name) self.decl += gen_array(name, element_type) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 8e6e8cacbe..fae70e2d24 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -18,18 +18,20 @@ import re implicit_structs_seen = set() struct_fields_seen = set() -def generate_visit_implicit_struct(type): - if type in implicit_structs_seen: + +def gen_visit_implicit_struct(typ): + if typ in implicit_structs_seen: return '' - implicit_structs_seen.add(type) + implicit_structs_seen.add(typ) + ret = '' - if type.name not in struct_fields_seen: + if typ.name not in struct_fields_seen: # Need a forward declaration ret += mcgen(''' static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp); ''', - c_type=type.c_name()) + c_type=typ.c_name()) ret += mcgen(''' @@ -45,35 +47,36 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error * error_propagate(errp, err); } ''', - c_type=type.c_name()) + c_type=typ.c_name()) return ret -def generate_visit_struct_fields(name, members, base = None): + +def gen_visit_struct_fields(name, base, members): struct_fields_seen.add(name) ret = '' if base: - ret += generate_visit_implicit_struct(base) + ret += gen_visit_implicit_struct(base) ret += mcgen(''' -static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) +static void visit_type_%(c_name)s_fields(Visitor *m, %(c_name)s **obj, Error **errp) { Error *err = NULL; ''', - name=c_name(name)) + c_name=c_name(name)) push_indent() if base: ret += mcgen(''' -visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err); +visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err); if (err) { goto out; } ''', - type=base.c_name(), c_name=c_name('base')) + c_type=base.c_name(), c_name=c_name('base')) for memb in members: if memb.optional: @@ -85,9 +88,9 @@ if (!err && (*obj)->has_%(c_name)s) { push_indent() ret += mcgen(''' -visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); +visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); ''', - type=memb.type.c_name(), c_name=c_name(memb.name), + c_type=memb.type.c_name(), c_name=c_name(memb.name), name=memb.name) if memb.optional: @@ -102,7 +105,7 @@ if (err) { ''') pop_indent() - if re.search('^ *goto out\\;', ret, re.MULTILINE): + if re.search('^ *goto out;', ret, re.MULTILINE): ret += mcgen(''' out: @@ -114,7 +117,7 @@ out: return ret -def generate_visit_struct_body(name): +def gen_visit_struct_body(name): # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj # rather than leaving it non-NULL. As currently written, the caller must @@ -132,30 +135,30 @@ def generate_visit_struct_body(name): error_propagate(errp, err); ''', name=name, c_name=c_name(name)) - return ret -def gen_visit_struct(name, base, members): - ret = generate_visit_struct_fields(name, members, base) +def gen_visit_struct(name, base, members): + ret = gen_visit_struct_fields(name, base, members) ret += mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { ''', - name=c_name(name)) + c_name=c_name(name)) - ret += generate_visit_struct_body(name) + ret += gen_visit_struct_body(name) ret += mcgen(''' } ''') return ret + def gen_visit_list(name, element_type): return mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; GenericList *i, **prev; @@ -168,7 +171,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e for (prev = (GenericList **)obj; !err && (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) { - %(name)s *native_i = (%(name)s *)i; + %(c_name)s *native_i = (%(c_name)s *)i; visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err); } @@ -179,10 +182,10 @@ out: error_propagate(errp, err); } ''', - name=c_name(name), - c_elt_type=element_type.c_name()) + c_name=c_name(name), c_elt_type=element_type.c_name()) -def generate_visit_enum(name): + +def gen_visit_enum(name): return mcgen(''' void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp) @@ -192,36 +195,36 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error ''', c_name=c_name(name), name=name) + def gen_visit_alternate(name, variants): ret = mcgen(''' -void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) +void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp) { Error *err = NULL; - visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err); + visit_start_implicit_struct(m, (void**) obj, sizeof(%(c_name)s), &err); if (err) { goto out; } - visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err); + visit_get_next_type(m, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err); if (err) { goto out_end; } switch ((*obj)->kind) { ''', - name=c_name(name)) + c_name=c_name(name)) for var in variants.variants: - enum_full_value = c_enum_const(variants.tag_member.type.name, - var.name) ret += mcgen(''' - case %(enum_full_value)s: + case %(case)s: visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); break; ''', - enum_full_value = enum_full_value, - c_type=var.type.c_name(), - c_name=c_name(var.name)) + case=c_enum_const(variants.tag_member.type.name, + var.name), + c_type=var.type.c_name(), + c_name=c_name(var.name)) ret += mcgen(''' default: @@ -238,17 +241,18 @@ out: return ret + def gen_visit_union(name, base, variants): ret = '' if base: members = [m for m in base.members if m != variants.tag_member] - ret += generate_visit_struct_fields(name, members) + ret += gen_visit_struct_fields(name, None, members) for var in variants.variants: # Ugly special case for simple union TODO get rid of it if not var.simple_union_type(): - ret += generate_visit_implicit_struct(var.type) + ret += gen_visit_implicit_struct(var.type) ret += mcgen(''' @@ -266,19 +270,19 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error if base: ret += mcgen(''' - visit_type_%(name)s_fields(m, obj, &err); + visit_type_%(c_name)s_fields(m, obj, &err); if (err) { goto out_obj; } ''', - name=c_name(name)) + c_name=c_name(name)) - disc_key = variants.tag_member.name + tag_key = variants.tag_member.name if not variants.tag_name: # we pointlessly use a different key for simple unions - disc_key = 'type' + tag_key = 'type' ret += mcgen(''' - visit_type_%(disc_type)s(m, &(*obj)->%(c_name)s, "%(disc_key)s", &err); + visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err); if (err) { goto out_obj; } @@ -287,30 +291,36 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error } switch ((*obj)->%(c_name)s) { ''', - disc_type=variants.tag_member.type.c_name(), + c_type=variants.tag_member.type.c_name(), # 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'), - disc_key = disc_key) + name=tag_key) for var in variants.variants: # TODO ugly special case for simple union simple_union_type = var.simple_union_type() + ret += mcgen(''' + case %(case)s: +''', + case=c_enum_const(variants.tag_member.type.name, + var.name)) if simple_union_type: - fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' + ret += mcgen(''' + visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err); +''', + c_type=simple_union_type.c_name(), + c_name=c_name(var.name)) else: - fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' - - enum_full_value = c_enum_const(variants.tag_member.type.name, var.name) + ret += mcgen(''' + visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err); +''', + c_type=var.type.c_name(), + c_name=c_name(var.name)) ret += mcgen(''' - case %(enum_full_value)s: - ''' + fmt + ''' break; -''', - enum_full_value = enum_full_value, - c_type=(simple_union_type or var.type).c_name(), - c_name=c_name(var.name)) +''') ret += mcgen(''' default: @@ -331,6 +341,7 @@ out: return ret + def gen_visit_decl(name, scalar=False): c_type = c_name(name) + ' *' if not scalar: @@ -363,7 +374,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): def visit_enum_type(self, name, info, values, prefix): self.decl += gen_visit_decl(name, scalar=True) - self.defn += generate_visit_enum(name) + self.defn += gen_visit_enum(name) def visit_array_type(self, name, info, element_type): decl = gen_visit_decl(name) @@ -439,7 +450,7 @@ fdef.write(mcgen(''' #include "qemu-common.h" #include "%(prefix)sqapi-visit.h" ''', - prefix = prefix)) + prefix=prefix)) fdecl.write(mcgen(''' #include "qapi/visitor.h" diff --git a/scripts/qapi.py b/scripts/qapi.py index 0ffd02d8c2..7ac72f6a78 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -1086,9 +1086,6 @@ class QAPISchema(object): self._def_exprs() self.check() - def get_exprs(self): - return [expr_elem['expr'] for expr_elem in self.exprs] - def _def_entity(self, ent): assert ent.name not in self._entity_dict self._entity_dict[ent.name] = ent @@ -1281,23 +1278,6 @@ class QAPISchema(object): # Code generation helpers # -def parse_args(typeinfo): - if isinstance(typeinfo, str): - struct = find_struct(typeinfo) - assert struct != None - typeinfo = struct['data'] - - for member in typeinfo: - argname = member - argentry = typeinfo[member] - optional = False - if member.startswith('*'): - argname = member[1:] - optional = True - # Todo: allow argentry to be OrderedDict, for providing the - # value of an optional argument. - yield (argname, argentry, optional) - def camel_case(name): new_name = '' first = True @@ -1380,67 +1360,9 @@ def c_name(name, protect=True): return "q_" + name return name.translate(c_name_trans) -# Map type @name to the C typedef name for the list form. -# -# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList' -def c_list_type(name): - return type_name(name) + 'List' - -# Map type @value to the C typedef form. -# -# Used for converting 'type' from a 'member':'type' qapi definition -# into the alphanumeric portion of the type for a generated C parameter, -# as well as generated C function names. See c_type() for the rest of -# the conversion such as adding '*' on pointer types. -# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c' -def type_name(value): - if type(value) == list: - return c_list_type(value[0]) - if value in builtin_types.keys(): - return value - return c_name(value) - eatspace = '\033EATSPACE.' pointer_suffix = ' *' + eatspace -# Map type @name to its C type expression. -# If @is_param, const-qualify the string type. -# -# This function is used for computing the full C type of 'member':'name'. -# A special suffix is added in c_type() for pointer types, and it's -# stripped in mcgen(). So please notice this when you check the return -# value of c_type() outside mcgen(). -def c_type(value, is_param=False): - if value == 'str': - if is_param: - return 'const char' + pointer_suffix - return 'char' + pointer_suffix - - elif value == 'int': - return 'int64_t' - elif (value == 'int8' or value == 'int16' or value == 'int32' or - value == 'int64' or value == 'uint8' or value == 'uint16' or - value == 'uint32' or value == 'uint64'): - return value + '_t' - elif value == 'size': - return 'uint64_t' - elif value == 'bool': - return 'bool' - elif value == 'number': - return 'double' - elif type(value) == list: - return c_list_type(value[0]) + pointer_suffix - elif is_enum(value): - return c_name(value) - elif value == None: - return 'void' - elif value in events: - return camel_case(value) + 'Event' + pointer_suffix - else: - # complex type name - assert isinstance(value, str) and value != "" - return c_name(value) + pointer_suffix - def genindent(count): ret = "" for i in range(count): @@ -1495,60 +1417,57 @@ def guardend(name): ''', name=guardname(name)) -def generate_enum_lookup(name, values, prefix=None): +def gen_enum_lookup(name, values, prefix=None): ret = mcgen(''' -const char *const %(name)s_lookup[] = { +const char *const %(c_name)s_lookup[] = { ''', - name=c_name(name)) + c_name=c_name(name)) for value in values: index = c_enum_const(name, value, prefix) ret += mcgen(''' [%(index)s] = "%(value)s", ''', - index = index, value = value) + index=index, value=value) max_index = c_enum_const(name, 'MAX', prefix) ret += mcgen(''' [%(max_index)s] = NULL, }; ''', - max_index=max_index) + max_index=max_index) return ret -def generate_enum(name, values, prefix=None): - name = c_name(name) - lookup_decl = mcgen(''' - -extern const char *const %(name)s_lookup[]; -''', - name=name) +def gen_enum(name, values, prefix=None): + # append automatically generated _MAX value + enum_values = values + ['MAX'] - enum_decl = mcgen(''' + ret = mcgen(''' -typedef enum %(name)s { +typedef enum %(c_name)s { ''', - name=name) - - # append automatically generated _MAX value - enum_values = values + [ 'MAX' ] + c_name=c_name(name)) i = 0 for value in enum_values: - enum_full_value = c_enum_const(name, value, prefix) - enum_decl += mcgen(''' - %(enum_full_value)s = %(i)d, + ret += mcgen(''' + %(c_enum)s = %(i)d, ''', - enum_full_value = enum_full_value, + c_enum=c_enum_const(name, value, prefix), i=i) i += 1 - enum_decl += mcgen(''' -} %(name)s; + ret += mcgen(''' +} %(c_name)s; ''', - name=name) + c_name=c_name(name)) + + ret += mcgen(''' - return enum_decl + lookup_decl +extern const char *const %(c_name)s_lookup[]; +''', + c_name=c_name(name)) + return ret # # Common command line parsing -- cgit 1.4.1 From 28770e057f265a4e70bcbdfc2447cce7b5f2dc19 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:24 +0200 Subject: qapi: Introduce a first class 'any' type It's first class, because unlike '**', it actually works, i.e. doesn't require 'gen': false. '**' will go away next. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange --- docs/qapi-code-gen.txt | 1 + include/qapi/visitor-impl.h | 2 ++ include/qapi/visitor.h | 1 + qapi/qapi-dealloc-visitor.c | 9 +++++ qapi/qapi-visit-core.c | 6 ++++ qapi/qmp-input-visitor.c | 11 ++++++ qapi/qmp-output-visitor.c | 9 +++++ scripts/qapi-types.py | 1 + scripts/qapi.py | 9 +++-- tests/Makefile | 3 +- tests/qapi-schema/args-any.err | 1 + tests/qapi-schema/args-any.exit | 1 + tests/qapi-schema/args-any.json | 2 ++ tests/qapi-schema/args-any.out | 0 tests/qapi-schema/flat-union-base-any.err | 1 + tests/qapi-schema/flat-union-base-any.exit | 1 + tests/qapi-schema/flat-union-base-any.json | 12 +++++++ tests/qapi-schema/flat-union-base-any.out | 0 tests/qapi-schema/flat-union-base-star.err | 1 - tests/qapi-schema/flat-union-base-star.exit | 1 - tests/qapi-schema/flat-union-base-star.json | 12 ------- tests/qapi-schema/flat-union-base-star.out | 0 tests/qapi-schema/qapi-schema-test.json | 5 ++- tests/qapi-schema/qapi-schema-test.out | 9 ++++- tests/qapi-schema/type-bypass.out | 4 +-- tests/test-qmp-commands.c | 5 +++ tests/test-qmp-input-visitor.c | 45 ++++++++++++++++++++++++ tests/test-qmp-output-visitor.c | 53 +++++++++++++++++++++++++++++ 28 files changed, 183 insertions(+), 22 deletions(-) create mode 100644 tests/qapi-schema/args-any.err create mode 100644 tests/qapi-schema/args-any.exit create mode 100644 tests/qapi-schema/args-any.json create mode 100644 tests/qapi-schema/args-any.out create mode 100644 tests/qapi-schema/flat-union-base-any.err create mode 100644 tests/qapi-schema/flat-union-base-any.exit create mode 100644 tests/qapi-schema/flat-union-base-any.json create mode 100644 tests/qapi-schema/flat-union-base-any.out delete mode 100644 tests/qapi-schema/flat-union-base-star.err delete mode 100644 tests/qapi-schema/flat-union-base-star.exit delete mode 100644 tests/qapi-schema/flat-union-base-star.json delete mode 100644 tests/qapi-schema/flat-union-base-star.out (limited to 'scripts/qapi-types.py') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 5c2c278a60..70fdae77d1 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -158,6 +158,7 @@ The following types are predefined, and map to C as follows: size uint64_t like uint64_t, except StringInputVisitor accepts size suffixes bool bool JSON true or false + any QObject * any JSON value === Includes === diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h index f4a2f746c8..8c0ba57292 100644 --- a/include/qapi/visitor-impl.h +++ b/include/qapi/visitor-impl.h @@ -40,6 +40,8 @@ struct Visitor void (*type_str)(Visitor *v, char **obj, const char *name, Error **errp); void (*type_number)(Visitor *v, double *obj, const char *name, Error **errp); + void (*type_any)(Visitor *v, QObject **obj, const char *name, + Error **errp); /* May be NULL */ void (*optional)(Visitor *v, bool *present, const char *name, diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h index 00ba104cd4..cfc19a616e 100644 --- a/include/qapi/visitor.h +++ b/include/qapi/visitor.h @@ -58,6 +58,7 @@ void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp); void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp); void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp); void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp); +void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp); bool visit_start_union(Visitor *v, bool data_present, Error **errp); void visit_end_union(Visitor *v, bool data_present, Error **errp); diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index d7f92c5d68..737deab9e5 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -151,6 +151,14 @@ static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name, { } +static void qapi_dealloc_type_anything(Visitor *v, QObject **obj, + const char *name, Error **errp) +{ + if (obj) { + qobject_decref(*obj); + } +} + static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) { @@ -216,6 +224,7 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void) v->visitor.type_bool = qapi_dealloc_type_bool; v->visitor.type_str = qapi_dealloc_type_str; v->visitor.type_number = qapi_dealloc_type_number; + v->visitor.type_any = qapi_dealloc_type_anything; v->visitor.type_size = qapi_dealloc_type_size; v->visitor.start_union = qapi_dealloc_start_union; diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c index 5a7c900504..59ed5067fa 100644 --- a/qapi/qapi-visit-core.c +++ b/qapi/qapi-visit-core.c @@ -260,6 +260,12 @@ void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) v->type_number(v, obj, name, errp); } +void visit_type_any(Visitor *v, QObject **obj, const char *name, + Error **errp) +{ + v->type_any(v, obj, name, errp); +} + void output_type_enum(Visitor *v, int *obj, const char * const strings[], const char *kind, const char *name, Error **errp) diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c index e97b8a4282..5dd9ed5ce5 100644 --- a/qapi/qmp-input-visitor.c +++ b/qapi/qmp-input-visitor.c @@ -286,6 +286,16 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name, } } +static void qmp_input_type_any(Visitor *v, QObject **obj, const char *name, + Error **errp) +{ + QmpInputVisitor *qiv = to_qiv(v); + QObject *qobj = qmp_input_get_object(qiv, name, true); + + qobject_incref(qobj); + *obj = qobj; +} + static void qmp_input_optional(Visitor *v, bool *present, const char *name, Error **errp) { @@ -329,6 +339,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj) v->visitor.type_bool = qmp_input_type_bool; v->visitor.type_str = qmp_input_type_str; v->visitor.type_number = qmp_input_type_number; + v->visitor.type_any = qmp_input_type_any; v->visitor.optional = qmp_input_optional; v->visitor.get_next_type = qmp_input_get_next_type; diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c index 8dce08749d..29899acd46 100644 --- a/qapi/qmp-output-visitor.c +++ b/qapi/qmp-output-visitor.c @@ -190,6 +190,14 @@ static void qmp_output_type_number(Visitor *v, double *obj, const char *name, qmp_output_add(qov, name, qfloat_from_double(*obj)); } +static void qmp_output_type_any(Visitor *v, QObject **obj, const char *name, + Error **errp) +{ + QmpOutputVisitor *qov = to_qov(v); + qobject_incref(*obj); + qmp_output_add_obj(qov, name, *obj); +} + QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) { QObject *obj = qmp_output_first(qov); @@ -237,6 +245,7 @@ QmpOutputVisitor *qmp_output_visitor_new(void) v->visitor.type_bool = qmp_output_type_bool; v->visitor.type_str = qmp_output_type_str; v->visitor.type_number = qmp_output_type_number; + v->visitor.type_any = qmp_output_type_any; QTAILQ_INIT(&v->stack); diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index d1fee207e5..b292682df6 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -327,6 +327,7 @@ fdef.write(mcgen(''' fdecl.write(mcgen(''' #include #include +#include "qapi/qmp/qobject.h" ''')) schema = QAPISchema(input_file) diff --git a/scripts/qapi.py b/scripts/qapi.py index 6b6f1aecdc..6a21bd6eba 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -33,6 +33,7 @@ builtin_types = { 'uint32': 'QTYPE_QINT', 'uint64': 'QTYPE_QINT', 'size': 'QTYPE_QINT', + 'any': None, # any qtype_code possible, actually } # Whitelist of commands allowed to return a non-dictionary @@ -1102,8 +1103,7 @@ class QAPISchema(object): def _def_builtin_type(self, name, json_type, c_type, c_null): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type, c_null)) - if name != '**': - self._make_array_type(name) # TODO really needed? + self._make_array_type(name) # TODO really needed? def _def_predefineds(self): for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'), @@ -1119,8 +1119,9 @@ class QAPISchema(object): ('uint64', 'int', 'uint64_t', '0'), ('size', 'int', 'uint64_t', '0'), ('bool', 'boolean', 'bool', 'false'), - ('**', 'value', None, None)]: + ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]: self._def_builtin_type(*t) + self._entity_dict['**'] = self.lookup_type('any') # TODO drop this alias def _make_implicit_enum_type(self, name, values): name = name + 'Kind' @@ -1270,6 +1271,8 @@ class QAPISchema(object): def visit(self, visitor): visitor.visit_begin(self) for name in sorted(self._entity_dict.keys()): + if self._entity_dict[name].name != name: + continue # ignore alias TODO drop alias and remove self._entity_dict[name].visit(visitor) visitor.visit_end() diff --git a/tests/Makefile b/tests/Makefile index 7c6025a2c5..10e55b36ee 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -239,6 +239,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ args-array-empty.json args-array-unknown.json args-int.json \ args-unknown.json args-member-unknown.json args-member-array.json \ args-member-array-bad.json args-alternate.json args-union.json \ + args-any.json \ returns-array-bad.json returns-int.json returns-dict.json \ returns-unknown.json returns-alternate.json returns-whitelist.json \ missing-colon.json missing-comma-list.json missing-comma-object.json \ @@ -255,7 +256,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ flat-union-invalid-branch-key.json flat-union-reverse-define.json \ flat-union-string-discriminator.json union-base-no-discriminator.json \ flat-union-bad-discriminator.json flat-union-bad-base.json \ - flat-union-base-star.json \ + flat-union-base-any.json \ flat-union-array-branch.json flat-union-int-branch.json \ flat-union-base-union.json flat-union-branch-clash.json \ alternate-nested.json alternate-unknown.json alternate-clash.json \ diff --git a/tests/qapi-schema/args-any.err b/tests/qapi-schema/args-any.err new file mode 100644 index 0000000000..bf9b5e0730 --- /dev/null +++ b/tests/qapi-schema/args-any.err @@ -0,0 +1 @@ +tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any' diff --git a/tests/qapi-schema/args-any.exit b/tests/qapi-schema/args-any.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/args-any.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/args-any.json b/tests/qapi-schema/args-any.json new file mode 100644 index 0000000000..58fe5e470e --- /dev/null +++ b/tests/qapi-schema/args-any.json @@ -0,0 +1,2 @@ +# we do not allow an 'any' argument +{ 'command': 'oops', 'data': 'any' } diff --git a/tests/qapi-schema/args-any.out b/tests/qapi-schema/args-any.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/flat-union-base-any.err b/tests/qapi-schema/flat-union-base-any.err new file mode 100644 index 0000000000..ad4d629e75 --- /dev/null +++ b/tests/qapi-schema/flat-union-base-any.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-base-any.json:8: Base 'any' is not a valid struct diff --git a/tests/qapi-schema/flat-union-base-any.exit b/tests/qapi-schema/flat-union-base-any.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/flat-union-base-any.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-base-any.json b/tests/qapi-schema/flat-union-base-any.json new file mode 100644 index 0000000000..fe66b713ef --- /dev/null +++ b/tests/qapi-schema/flat-union-base-any.json @@ -0,0 +1,12 @@ +# we require the base to be an existing struct +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } +{ 'struct': 'TestTypeA', + 'data': { 'string': 'str' } } +{ 'struct': 'TestTypeB', + 'data': { 'integer': 'int' } } +{ 'union': 'TestUnion', + 'base': 'any', + 'discriminator': 'enum1', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-base-any.out b/tests/qapi-schema/flat-union-base-any.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/flat-union-base-star.err b/tests/qapi-schema/flat-union-base-star.err deleted file mode 100644 index b7748f08bf..0000000000 --- a/tests/qapi-schema/flat-union-base-star.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-base-star.json:8: Base '**' is not a valid struct diff --git a/tests/qapi-schema/flat-union-base-star.exit b/tests/qapi-schema/flat-union-base-star.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/flat-union-base-star.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/flat-union-base-star.json b/tests/qapi-schema/flat-union-base-star.json deleted file mode 100644 index 5099439a9d..0000000000 --- a/tests/qapi-schema/flat-union-base-star.json +++ /dev/null @@ -1,12 +0,0 @@ -# we require the base to be an existing struct -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } -{ 'struct': 'TestTypeA', - 'data': { 'string': 'str' } } -{ 'struct': 'TestTypeB', - 'data': { 'integer': 'int' } } -{ 'union': 'TestUnion', - 'base': '**', - 'discriminator': 'enum1', - 'data': { 'value1': 'TestTypeA', - 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-base-star.out b/tests/qapi-schema/flat-union-base-star.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index d48815764d..6897a6ea39 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -78,7 +78,8 @@ 'number': ['number'], 'boolean': ['bool'], 'string': ['str'], - 'sizes': ['size'] } } + 'sizes': ['size'], + 'any': ['any'] } } # testing commands { 'command': 'user_def_cmd', 'data': {} } @@ -88,6 +89,8 @@ 'returns': 'UserDefTwo' } { 'command': 'user_def_cmd3', 'data': {'a': 'int', '*b': 'int' }, 'returns': 'int' } +# note: command name 'guest-sync' chosen to avoid "cannot use built-in" error +{ 'command': 'guest-sync', 'data': { 'arg': 'any' }, 'returns': 'any' } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index f90cf4636c..a52ac31ea5 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -12,8 +12,12 @@ object :obj-__org.qemu_x-command-arg member b: __org.qemu_x-StructList optional=False member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False +object :obj-anyList-wrapper + member data: anyList optional=False object :obj-boolList-wrapper member data: boolList optional=False +object :obj-guest-sync-arg + member arg: any optional=False object :obj-int16List-wrapper member data: int16List optional=False object :obj-int32List-wrapper @@ -102,7 +106,8 @@ object UserDefNativeListUnion case boolean: :obj-boolList-wrapper case string: :obj-strList-wrapper case sizes: :obj-sizeList-wrapper -enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes'] + case any: :obj-anyList-wrapper +enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any'] object UserDefOne base UserDefZero member string: str optional=False @@ -151,6 +156,8 @@ object __org.qemu_x-Union2 case __org.qemu_x-value: __org.qemu_x-Struct2 command __org.qemu_x-command :obj-__org.qemu_x-command-arg -> __org.qemu_x-Union1 gen=True success_response=True +command guest-sync :obj-guest-sync-arg -> any + gen=True success_response=True command user_def_cmd None -> None gen=True success_response=True command user_def_cmd1 :obj-user_def_cmd1-arg -> None diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out index 0070d4b845..db2a4e6d84 100644 --- a/tests/qapi-schema/type-bypass.out +++ b/tests/qapi-schema/type-bypass.out @@ -1,4 +1,4 @@ object :obj-unsafe-arg - member arg: ** optional=False -command unsafe :obj-unsafe-arg -> ** + member arg: any optional=False +command unsafe :obj-unsafe-arg -> any gen=False success_response=True diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 9918f23062..8d5249e7e4 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -51,6 +51,11 @@ int64_t qmp_user_def_cmd3(int64_t a, bool has_b, int64_t b, Error **errp) return a + (has_b ? b : 0); } +QObject *qmp_guest_sync(QObject *arg, Error **errp) +{ + return arg; +} + __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, __org_qemu_x_StructList *b, __org_qemu_x_Union2 *c, diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index 508c11a85b..61715b3725 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -298,6 +298,49 @@ static void test_visitor_in_list(TestInputVisitorData *data, qapi_free_UserDefOneList(head); } +static void test_visitor_in_any(TestInputVisitorData *data, + const void *unused) +{ + QObject *res = NULL; + Error *err = NULL; + Visitor *v; + QInt *qint; + QBool *qbool; + QString *qstring; + QDict *qdict; + QObject *qobj; + + v = visitor_input_test_init(data, "-42"); + visit_type_any(v, &res, NULL, &err); + g_assert(!err); + qint = qobject_to_qint(res); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobject_decref(res); + + v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); + visit_type_any(v, &res, NULL, &err); + g_assert(!err); + qdict = qobject_to_qdict(res); + g_assert(qdict && qdict_size(qdict) == 3); + qobj = qdict_get(qdict, "integer"); + g_assert(qobj); + qint = qobject_to_qint(qobj); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobj = qdict_get(qdict, "boolean"); + g_assert(qobj); + qbool = qobject_to_qbool(qobj); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qobj = qdict_get(qdict, "string"); + g_assert(qobj); + qstring = qobject_to_qstring(qobj); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); + qobject_decref(res); +} + static void test_visitor_in_union_flat(TestInputVisitorData *data, const void *unused) { @@ -669,6 +712,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_struct_nested); input_visitor_test_add("/visitor/input/list", &in_visitor_data, test_visitor_in_list); + input_visitor_test_add("/visitor/input/any", + &in_visitor_data, test_visitor_in_any); input_visitor_test_add("/visitor/input/union-flat", &in_visitor_data, test_visitor_in_union_flat); input_visitor_test_add("/visitor/input/alternate", diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index a48ae72452..c84002e2f2 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -428,6 +428,57 @@ static void test_visitor_out_list_qapi_free(TestOutputVisitorData *data, qapi_free_UserDefTwoList(head); } +static void test_visitor_out_any(TestOutputVisitorData *data, + const void *unused) +{ + QObject *qobj; + Error *err = NULL; + QInt *qint; + QBool *qbool; + QString *qstring; + QDict *qdict; + QObject *obj; + + qobj = QOBJECT(qint_from_int(-42)); + visit_type_any(data->ov, &qobj, NULL, &err); + g_assert(!err); + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + g_assert(qobject_type(obj) == QTYPE_QINT); + g_assert_cmpint(qint_get_int(qobject_to_qint(obj)), ==, -42); + qobject_decref(obj); + qobject_decref(qobj); + + qdict = qdict_new(); + qdict_put(qdict, "integer", qint_from_int(-42)); + qdict_put(qdict, "boolean", qbool_from_bool(true)); + qdict_put(qdict, "string", qstring_from_str("foo")); + qobj = QOBJECT(qdict); + visit_type_any(data->ov, &qobj, NULL, &err); + g_assert(!err); + obj = qmp_output_get_qobject(data->qov); + g_assert(obj != NULL); + qdict = qobject_to_qdict(obj); + g_assert(qdict); + qobj = qdict_get(qdict, "integer"); + g_assert(qobj); + qint = qobject_to_qint(qobj); + g_assert(qint); + g_assert_cmpint(qint_get_int(qint), ==, -42); + qobj = qdict_get(qdict, "boolean"); + g_assert(qobj); + qbool = qobject_to_qbool(qobj); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qobj = qdict_get(qdict, "string"); + g_assert(qobj); + qstring = qobject_to_qstring(qobj); + g_assert(qstring); + g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); + qobject_decref(obj); + qobject_decref(qobj); +} + static void test_visitor_out_union_flat(TestOutputVisitorData *data, const void *unused) { @@ -833,6 +884,8 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_struct_errors); output_visitor_test_add("/visitor/output/list", &out_visitor_data, test_visitor_out_list); + output_visitor_test_add("/visitor/output/any", + &out_visitor_data, test_visitor_out_any); output_visitor_test_add("/visitor/output/list-qapi-free", &out_visitor_data, test_visitor_out_list_qapi_free); output_visitor_test_add("/visitor/output/union-flat", -- cgit 1.4.1