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 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) (limited to 'docs/qapi-code-gen.txt') 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 -- cgit v1.2.3-55-g7522 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 'docs/qapi-code-gen.txt') 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 v1.2.3-55-g7522 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 'docs/qapi-code-gen.txt') 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 v1.2.3-55-g7522 From 7fad30f06eb6aa57aaa8f3d264288f24ae7646f0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:19 +0200 Subject: qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() These functions marshal both input and output. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange Message-Id: <1442401589-24189-17-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 4 +- docs/writing-qmp-commands.txt | 8 +- monitor.c | 2 +- qmp-commands.hx | 242 +++++++++++++++++++++--------------------- qmp.c | 6 +- scripts/qapi-commands.py | 4 +- 6 files changed, 133 insertions(+), 133 deletions(-) (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 3eaff367a4..50da1d08b8 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -744,7 +744,7 @@ Example: qapi_dealloc_visitor_cleanup(md); } - static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) + static void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp) { Error *local_err = NULL; UserDefOne *retval; @@ -777,7 +777,7 @@ Example: static void qmp_init_marshal(void) { - qmp_register_command("my-command", qmp_marshal_input_my_command, QCO_NO_OPTIONS); + qmp_register_command("my-command", qmp_marshal_my_command, QCO_NO_OPTIONS); } qapi_init(qmp_init_marshal); diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt index f7693ca267..8647cac4e7 100644 --- a/docs/writing-qmp-commands.txt +++ b/docs/writing-qmp-commands.txt @@ -127,7 +127,7 @@ following at the bottom: { .name = "hello-world", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_hello_world, + .mhandler.cmd_new = qmp_marshal_hello_world, }, You're done. Now build qemu, run it as suggested in the "Testing" section, @@ -179,7 +179,7 @@ The last step is to update the qmp-commands.hx file: { .name = "hello-world", .args_type = "message:s?", - .mhandler.cmd_new = qmp_marshal_input_hello_world, + .mhandler.cmd_new = qmp_marshal_hello_world, }, Notice that the "args_type" member got our "message" argument. The character @@ -461,7 +461,7 @@ The last step is to add the correspoding entry in the qmp-commands.hx file: { .name = "query-alarm-clock", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_alarm_clock, + .mhandler.cmd_new = qmp_marshal_query_alarm_clock, }, Time to test the new command. Build qemu, run it as described in the "Testing" @@ -607,7 +607,7 @@ To test this you have to add the corresponding qmp-commands.hx entry: { .name = "query-alarm-methods", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_alarm_methods, + .mhandler.cmd_new = qmp_marshal_query_alarm_methods, }, Now Build qemu, run it as explained in the "Testing" section and try our new diff --git a/monitor.c b/monitor.c index 1f432635ad..dde1f220a1 100644 --- a/monitor.c +++ b/monitor.c @@ -3912,7 +3912,7 @@ static QObject *get_qmp_greeting(void) { QObject *ver = NULL; - qmp_marshal_input_query_version(NULL, &ver, NULL); + qmp_marshal_query_version(NULL, &ver, NULL); return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver); } diff --git a/qmp-commands.hx b/qmp-commands.hx index 9848fd88f0..4640a3d964 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -63,7 +63,7 @@ EQMP { .name = "quit", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_quit, + .mhandler.cmd_new = qmp_marshal_quit, }, SQMP @@ -84,7 +84,7 @@ EQMP { .name = "eject", .args_type = "force:-f,device:B", - .mhandler.cmd_new = qmp_marshal_input_eject, + .mhandler.cmd_new = qmp_marshal_eject, }, SQMP @@ -110,7 +110,7 @@ EQMP { .name = "change", .args_type = "device:B,target:F,arg:s?", - .mhandler.cmd_new = qmp_marshal_input_change, + .mhandler.cmd_new = qmp_marshal_change, }, SQMP @@ -146,7 +146,7 @@ EQMP { .name = "screendump", .args_type = "filename:F", - .mhandler.cmd_new = qmp_marshal_input_screendump, + .mhandler.cmd_new = qmp_marshal_screendump, }, SQMP @@ -169,7 +169,7 @@ EQMP { .name = "stop", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_stop, + .mhandler.cmd_new = qmp_marshal_stop, }, SQMP @@ -190,7 +190,7 @@ EQMP { .name = "cont", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_cont, + .mhandler.cmd_new = qmp_marshal_cont, }, SQMP @@ -211,7 +211,7 @@ EQMP { .name = "system_wakeup", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_wakeup, + .mhandler.cmd_new = qmp_marshal_system_wakeup, }, SQMP @@ -232,7 +232,7 @@ EQMP { .name = "system_reset", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_reset, + .mhandler.cmd_new = qmp_marshal_system_reset, }, SQMP @@ -253,7 +253,7 @@ EQMP { .name = "system_powerdown", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_system_powerdown, + .mhandler.cmd_new = qmp_marshal_system_powerdown, }, SQMP @@ -310,7 +310,7 @@ EQMP { .name = "device_del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_device_del, + .mhandler.cmd_new = qmp_marshal_device_del, }, SQMP @@ -333,7 +333,7 @@ EQMP { .name = "send-key", .args_type = "keys:q,hold-time:i?", - .mhandler.cmd_new = qmp_marshal_input_send_key, + .mhandler.cmd_new = qmp_marshal_send_key, }, SQMP @@ -364,7 +364,7 @@ EQMP { .name = "cpu", .args_type = "index:i", - .mhandler.cmd_new = qmp_marshal_input_cpu, + .mhandler.cmd_new = qmp_marshal_cpu, }, SQMP @@ -389,7 +389,7 @@ EQMP { .name = "cpu-add", .args_type = "id:i", - .mhandler.cmd_new = qmp_marshal_input_cpu_add, + .mhandler.cmd_new = qmp_marshal_cpu_add, }, SQMP @@ -412,7 +412,7 @@ EQMP { .name = "memsave", .args_type = "val:l,size:i,filename:s,cpu:i?", - .mhandler.cmd_new = qmp_marshal_input_memsave, + .mhandler.cmd_new = qmp_marshal_memsave, }, SQMP @@ -441,7 +441,7 @@ EQMP { .name = "pmemsave", .args_type = "val:l,size:i,filename:s", - .mhandler.cmd_new = qmp_marshal_input_pmemsave, + .mhandler.cmd_new = qmp_marshal_pmemsave, }, SQMP @@ -469,7 +469,7 @@ EQMP { .name = "inject-nmi", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_inject_nmi, + .mhandler.cmd_new = qmp_marshal_inject_nmi, }, SQMP @@ -492,7 +492,7 @@ EQMP { .name = "ringbuf-write", .args_type = "device:s,data:s,format:s?", - .mhandler.cmd_new = qmp_marshal_input_ringbuf_write, + .mhandler.cmd_new = qmp_marshal_ringbuf_write, }, SQMP @@ -523,7 +523,7 @@ EQMP { .name = "ringbuf-read", .args_type = "device:s,size:i,format:s?", - .mhandler.cmd_new = qmp_marshal_input_ringbuf_read, + .mhandler.cmd_new = qmp_marshal_ringbuf_read, }, SQMP @@ -559,7 +559,7 @@ EQMP { .name = "xen-save-devices-state", .args_type = "filename:F", - .mhandler.cmd_new = qmp_marshal_input_xen_save_devices_state, + .mhandler.cmd_new = qmp_marshal_xen_save_devices_state, }, SQMP @@ -586,7 +586,7 @@ EQMP { .name = "xen-set-global-dirty-log", .args_type = "enable:b", - .mhandler.cmd_new = qmp_marshal_input_xen_set_global_dirty_log, + .mhandler.cmd_new = qmp_marshal_xen_set_global_dirty_log, }, SQMP @@ -610,7 +610,7 @@ EQMP { .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .mhandler.cmd_new = qmp_marshal_input_migrate, + .mhandler.cmd_new = qmp_marshal_migrate, }, SQMP @@ -643,7 +643,7 @@ EQMP { .name = "migrate_cancel", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_migrate_cancel, + .mhandler.cmd_new = qmp_marshal_migrate_cancel, }, SQMP @@ -664,7 +664,7 @@ EQMP { .name = "migrate-incoming", .args_type = "uri:s", - .mhandler.cmd_new = qmp_marshal_input_migrate_incoming, + .mhandler.cmd_new = qmp_marshal_migrate_incoming, }, SQMP @@ -692,7 +692,7 @@ EQMP { .name = "migrate-set-cache-size", .args_type = "value:o", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_cache_size, + .mhandler.cmd_new = qmp_marshal_migrate_set_cache_size, }, SQMP @@ -715,7 +715,7 @@ EQMP { .name = "query-migrate-cache-size", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_cache_size, + .mhandler.cmd_new = qmp_marshal_query_migrate_cache_size, }, SQMP @@ -737,7 +737,7 @@ EQMP { .name = "migrate_set_speed", .args_type = "value:o", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_speed, + .mhandler.cmd_new = qmp_marshal_migrate_set_speed, }, SQMP @@ -760,7 +760,7 @@ EQMP { .name = "migrate_set_downtime", .args_type = "value:T", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_downtime, + .mhandler.cmd_new = qmp_marshal_migrate_set_downtime, }, SQMP @@ -785,7 +785,7 @@ EQMP .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", .params = "protocol hostname port tls-port cert-subject", .help = "set migration information for remote display", - .mhandler.cmd_new = qmp_marshal_input_client_migrate_info, + .mhandler.cmd_new = qmp_marshal_client_migrate_info, }, SQMP @@ -819,7 +819,7 @@ EQMP .args_type = "paging:b,protocol:s,begin:i?,end:i?,format:s?", .params = "-p protocol [begin] [length] [format]", .help = "dump guest memory to file", - .mhandler.cmd_new = qmp_marshal_input_dump_guest_memory, + .mhandler.cmd_new = qmp_marshal_dump_guest_memory, }, SQMP @@ -855,7 +855,7 @@ EQMP { .name = "query-dump-guest-memory-capability", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_dump_guest_memory_capability, + .mhandler.cmd_new = qmp_marshal_query_dump_guest_memory_capability, }, SQMP @@ -876,7 +876,7 @@ EQMP { .name = "dump-skeys", .args_type = "filename:F", - .mhandler.cmd_new = qmp_marshal_input_dump_skeys, + .mhandler.cmd_new = qmp_marshal_dump_skeys, }, #endif @@ -929,7 +929,7 @@ EQMP { .name = "netdev_del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_netdev_del, + .mhandler.cmd_new = qmp_marshal_netdev_del, }, SQMP @@ -979,7 +979,7 @@ EQMP { .name = "object-del", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_object_del, + .mhandler.cmd_new = qmp_marshal_object_del, }, SQMP @@ -1004,7 +1004,7 @@ EQMP { .name = "block_resize", .args_type = "device:s?,node-name:s?,size:o", - .mhandler.cmd_new = qmp_marshal_input_block_resize, + .mhandler.cmd_new = qmp_marshal_block_resize, }, SQMP @@ -1029,7 +1029,7 @@ EQMP { .name = "block-stream", .args_type = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?", - .mhandler.cmd_new = qmp_marshal_input_block_stream, + .mhandler.cmd_new = qmp_marshal_block_stream, }, SQMP @@ -1072,7 +1072,7 @@ EQMP { .name = "block-commit", .args_type = "device:B,base:s?,top:s?,backing-file:s?,speed:o?", - .mhandler.cmd_new = qmp_marshal_input_block_commit, + .mhandler.cmd_new = qmp_marshal_block_commit, }, SQMP @@ -1136,7 +1136,7 @@ EQMP .name = "drive-backup", .args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?," "bitmap:s?,on-source-error:s?,on-target-error:s?", - .mhandler.cmd_new = qmp_marshal_input_drive_backup, + .mhandler.cmd_new = qmp_marshal_drive_backup, }, SQMP @@ -1190,7 +1190,7 @@ EQMP .name = "blockdev-backup", .args_type = "sync:s,device:B,target:B,speed:i?," "on-source-error:s?,on-target-error:s?", - .mhandler.cmd_new = qmp_marshal_input_blockdev_backup, + .mhandler.cmd_new = qmp_marshal_blockdev_backup, }, SQMP @@ -1230,33 +1230,33 @@ EQMP { .name = "block-job-set-speed", .args_type = "device:B,speed:o", - .mhandler.cmd_new = qmp_marshal_input_block_job_set_speed, + .mhandler.cmd_new = qmp_marshal_block_job_set_speed, }, { .name = "block-job-cancel", .args_type = "device:B,force:b?", - .mhandler.cmd_new = qmp_marshal_input_block_job_cancel, + .mhandler.cmd_new = qmp_marshal_block_job_cancel, }, { .name = "block-job-pause", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_pause, + .mhandler.cmd_new = qmp_marshal_block_job_pause, }, { .name = "block-job-resume", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_resume, + .mhandler.cmd_new = qmp_marshal_block_job_resume, }, { .name = "block-job-complete", .args_type = "device:B", - .mhandler.cmd_new = qmp_marshal_input_block_job_complete, + .mhandler.cmd_new = qmp_marshal_block_job_complete, }, { .name = "transaction", .args_type = "actions:q", - .mhandler.cmd_new = qmp_marshal_input_transaction, + .mhandler.cmd_new = qmp_marshal_transaction, }, SQMP @@ -1335,7 +1335,7 @@ EQMP { .name = "block-dirty-bitmap-add", .args_type = "node:B,name:s,granularity:i?", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_add, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_add, }, SQMP @@ -1363,7 +1363,7 @@ EQMP { .name = "block-dirty-bitmap-remove", .args_type = "node:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_remove, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_remove, }, SQMP @@ -1391,7 +1391,7 @@ EQMP { .name = "block-dirty-bitmap-clear", .args_type = "node:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_block_dirty_bitmap_clear, + .mhandler.cmd_new = qmp_marshal_block_dirty_bitmap_clear, }, SQMP @@ -1420,7 +1420,7 @@ EQMP { .name = "blockdev-snapshot-sync", .args_type = "device:s?,node-name:s?,snapshot-file:s,snapshot-node-name:s?,format:s?,mode:s?", - .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_sync, + .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_sync, }, SQMP @@ -1456,7 +1456,7 @@ EQMP { .name = "blockdev-snapshot-internal-sync", .args_type = "device:B,name:s", - .mhandler.cmd_new = qmp_marshal_input_blockdev_snapshot_internal_sync, + .mhandler.cmd_new = qmp_marshal_blockdev_snapshot_internal_sync, }, SQMP @@ -1486,7 +1486,7 @@ EQMP .name = "blockdev-snapshot-delete-internal-sync", .args_type = "device:B,id:s?,name:s?", .mhandler.cmd_new = - qmp_marshal_input_blockdev_snapshot_delete_internal_sync, + qmp_marshal_blockdev_snapshot_delete_internal_sync, }, SQMP @@ -1530,7 +1530,7 @@ EQMP "on-source-error:s?,on-target-error:s?," "unmap:b?," "granularity:i?,buf-size:i?", - .mhandler.cmd_new = qmp_marshal_input_drive_mirror, + .mhandler.cmd_new = qmp_marshal_drive_mirror, }, SQMP @@ -1590,7 +1590,7 @@ EQMP { .name = "change-backing-file", .args_type = "device:s,image-node-name:s,backing-file:s", - .mhandler.cmd_new = qmp_marshal_input_change_backing_file, + .mhandler.cmd_new = qmp_marshal_change_backing_file, }, SQMP @@ -1629,7 +1629,7 @@ EQMP { .name = "balloon", .args_type = "value:M", - .mhandler.cmd_new = qmp_marshal_input_balloon, + .mhandler.cmd_new = qmp_marshal_balloon, }, SQMP @@ -1652,7 +1652,7 @@ EQMP { .name = "set_link", .args_type = "name:s,up:b", - .mhandler.cmd_new = qmp_marshal_input_set_link, + .mhandler.cmd_new = qmp_marshal_set_link, }, SQMP @@ -1678,7 +1678,7 @@ EQMP .args_type = "fdname:s", .params = "getfd name", .help = "receive a file descriptor via SCM rights and assign it a name", - .mhandler.cmd_new = qmp_marshal_input_getfd, + .mhandler.cmd_new = qmp_marshal_getfd, }, SQMP @@ -1711,7 +1711,7 @@ EQMP .args_type = "fdname:s", .params = "closefd name", .help = "close a file descriptor previously passed via SCM rights", - .mhandler.cmd_new = qmp_marshal_input_closefd, + .mhandler.cmd_new = qmp_marshal_closefd, }, SQMP @@ -1736,7 +1736,7 @@ EQMP .args_type = "fdset-id:i?,opaque:s?", .params = "add-fd fdset-id opaque", .help = "Add a file descriptor, that was passed via SCM rights, to an fd set", - .mhandler.cmd_new = qmp_marshal_input_add_fd, + .mhandler.cmd_new = qmp_marshal_add_fd, }, SQMP @@ -1775,7 +1775,7 @@ EQMP .args_type = "fdset-id:i,fd:i?", .params = "remove-fd fdset-id fd", .help = "Remove a file descriptor from an fd set", - .mhandler.cmd_new = qmp_marshal_input_remove_fd, + .mhandler.cmd_new = qmp_marshal_remove_fd, }, SQMP @@ -1807,7 +1807,7 @@ EQMP .name = "query-fdsets", .args_type = "", .help = "Return information describing all fd sets", - .mhandler.cmd_new = qmp_marshal_input_query_fdsets, + .mhandler.cmd_new = qmp_marshal_query_fdsets, }, SQMP @@ -1856,7 +1856,7 @@ EQMP { .name = "block_passwd", .args_type = "device:s?,node-name:s?,password:s", - .mhandler.cmd_new = qmp_marshal_input_block_passwd, + .mhandler.cmd_new = qmp_marshal_block_passwd, }, SQMP @@ -1882,7 +1882,7 @@ EQMP { .name = "block_set_io_throttle", .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?", - .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle, + .mhandler.cmd_new = qmp_marshal_block_set_io_throttle, }, SQMP @@ -1932,7 +1932,7 @@ EQMP { .name = "set_password", .args_type = "protocol:s,password:s,connected:s?", - .mhandler.cmd_new = qmp_marshal_input_set_password, + .mhandler.cmd_new = qmp_marshal_set_password, }, SQMP @@ -1958,7 +1958,7 @@ EQMP { .name = "expire_password", .args_type = "protocol:s,time:s", - .mhandler.cmd_new = qmp_marshal_input_expire_password, + .mhandler.cmd_new = qmp_marshal_expire_password, }, SQMP @@ -1983,7 +1983,7 @@ EQMP { .name = "add_client", .args_type = "protocol:s,fdname:s,skipauth:b?,tls:b?", - .mhandler.cmd_new = qmp_marshal_input_add_client, + .mhandler.cmd_new = qmp_marshal_add_client, }, SQMP @@ -2034,7 +2034,7 @@ EQMP { .name = "human-monitor-command", .args_type = "command-line:s,cpu-index:i?", - .mhandler.cmd_new = qmp_marshal_input_human_monitor_command, + .mhandler.cmd_new = qmp_marshal_human_monitor_command, }, SQMP @@ -2113,7 +2113,7 @@ EQMP { .name = "query-version", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_version, + .mhandler.cmd_new = qmp_marshal_query_version, }, SQMP @@ -2150,7 +2150,7 @@ EQMP { .name = "query-commands", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_commands, + .mhandler.cmd_new = qmp_marshal_query_commands, }, SQMP @@ -2187,7 +2187,7 @@ EQMP { .name = "query-events", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_events, + .mhandler.cmd_new = qmp_marshal_query_events, }, SQMP @@ -2232,7 +2232,7 @@ EQMP { .name = "query-chardev", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_chardev, + .mhandler.cmd_new = qmp_marshal_query_chardev, }, SQMP @@ -2273,7 +2273,7 @@ EQMP { .name = "query-chardev-backends", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_chardev_backends, + .mhandler.cmd_new = qmp_marshal_query_chardev_backends, }, SQMP @@ -2457,7 +2457,7 @@ EQMP { .name = "query-block", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_block, + .mhandler.cmd_new = qmp_marshal_query_block, }, SQMP @@ -2584,7 +2584,7 @@ EQMP { .name = "query-blockstats", .args_type = "query-nodes:b?", - .mhandler.cmd_new = qmp_marshal_input_query_blockstats, + .mhandler.cmd_new = qmp_marshal_query_blockstats, }, SQMP @@ -2635,7 +2635,7 @@ EQMP { .name = "query-cpus", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_cpus, + .mhandler.cmd_new = qmp_marshal_query_cpus, }, SQMP @@ -2674,7 +2674,7 @@ EQMP { .name = "query-iothreads", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_iothreads, + .mhandler.cmd_new = qmp_marshal_query_iothreads, }, SQMP @@ -2891,7 +2891,7 @@ EQMP { .name = "query-pci", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_pci, + .mhandler.cmd_new = qmp_marshal_query_pci, }, SQMP @@ -2915,7 +2915,7 @@ EQMP { .name = "query-kvm", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_kvm, + .mhandler.cmd_new = qmp_marshal_query_kvm, }, SQMP @@ -2955,7 +2955,7 @@ EQMP { .name = "query-status", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_status, + .mhandler.cmd_new = qmp_marshal_query_status, }, SQMP @@ -2999,7 +2999,7 @@ EQMP { .name = "query-mice", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_mice, + .mhandler.cmd_new = qmp_marshal_query_mice, }, SQMP @@ -3062,12 +3062,12 @@ EQMP { .name = "query-vnc", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_vnc, + .mhandler.cmd_new = qmp_marshal_query_vnc, }, { .name = "query-vnc-servers", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_vnc_servers, + .mhandler.cmd_new = qmp_marshal_query_vnc_servers, }, SQMP @@ -3144,7 +3144,7 @@ EQMP { .name = "query-spice", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_spice, + .mhandler.cmd_new = qmp_marshal_query_spice, }, #endif @@ -3168,7 +3168,7 @@ EQMP { .name = "query-name", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_name, + .mhandler.cmd_new = qmp_marshal_query_name, }, SQMP @@ -3191,7 +3191,7 @@ EQMP { .name = "query-uuid", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_uuid, + .mhandler.cmd_new = qmp_marshal_query_uuid, }, SQMP @@ -3240,7 +3240,7 @@ EQMP { .name = "query-command-line-options", .args_type = "option:s?", - .mhandler.cmd_new = qmp_marshal_input_query_command_line_options, + .mhandler.cmd_new = qmp_marshal_query_command_line_options, }, SQMP @@ -3418,7 +3418,7 @@ EQMP { .name = "query-migrate", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate, + .mhandler.cmd_new = qmp_marshal_query_migrate, }, SQMP @@ -3446,7 +3446,7 @@ EQMP .name = "migrate-set-capabilities", .args_type = "capabilities:q", .params = "capability:s,state:b", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_capabilities, + .mhandler.cmd_new = qmp_marshal_migrate_set_capabilities, }, SQMP query-migrate-capabilities @@ -3472,7 +3472,7 @@ EQMP { .name = "query-migrate-capabilities", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_capabilities, + .mhandler.cmd_new = qmp_marshal_query_migrate_capabilities, }, SQMP @@ -3498,7 +3498,7 @@ EQMP .name = "migrate-set-parameters", .args_type = "compress-level:i?,compress-threads:i?,decompress-threads:i?", - .mhandler.cmd_new = qmp_marshal_input_migrate_set_parameters, + .mhandler.cmd_new = qmp_marshal_migrate_set_parameters, }, SQMP query-migrate-parameters @@ -3529,7 +3529,7 @@ EQMP { .name = "query-migrate-parameters", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_migrate_parameters, + .mhandler.cmd_new = qmp_marshal_query_migrate_parameters, }, SQMP @@ -3557,19 +3557,19 @@ EQMP { .name = "query-balloon", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_balloon, + .mhandler.cmd_new = qmp_marshal_query_balloon, }, { .name = "query-block-jobs", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_block_jobs, + .mhandler.cmd_new = qmp_marshal_query_block_jobs, }, { .name = "qom-list", .args_type = "path:s", - .mhandler.cmd_new = qmp_marshal_input_qom_list, + .mhandler.cmd_new = qmp_marshal_qom_list, }, { @@ -3587,58 +3587,58 @@ EQMP { .name = "nbd-server-start", .args_type = "addr:q", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_start, + .mhandler.cmd_new = qmp_marshal_nbd_server_start, }, { .name = "nbd-server-add", .args_type = "device:B,writable:b?", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_add, + .mhandler.cmd_new = qmp_marshal_nbd_server_add, }, { .name = "nbd-server-stop", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_nbd_server_stop, + .mhandler.cmd_new = qmp_marshal_nbd_server_stop, }, { .name = "change-vnc-password", .args_type = "password:s", - .mhandler.cmd_new = qmp_marshal_input_change_vnc_password, + .mhandler.cmd_new = qmp_marshal_change_vnc_password, }, { .name = "qom-list-types", .args_type = "implements:s?,abstract:b?", - .mhandler.cmd_new = qmp_marshal_input_qom_list_types, + .mhandler.cmd_new = qmp_marshal_qom_list_types, }, { .name = "device-list-properties", .args_type = "typename:s", - .mhandler.cmd_new = qmp_marshal_input_device_list_properties, + .mhandler.cmd_new = qmp_marshal_device_list_properties, }, { .name = "query-machines", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_machines, + .mhandler.cmd_new = qmp_marshal_query_machines, }, { .name = "query-cpu-definitions", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_cpu_definitions, + .mhandler.cmd_new = qmp_marshal_query_cpu_definitions, }, { .name = "query-target", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_target, + .mhandler.cmd_new = qmp_marshal_query_target, }, { .name = "query-tpm", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm, + .mhandler.cmd_new = qmp_marshal_query_tpm, }, SQMP @@ -3672,7 +3672,7 @@ EQMP { .name = "query-tpm-models", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm_models, + .mhandler.cmd_new = qmp_marshal_query_tpm_models, }, SQMP @@ -3693,7 +3693,7 @@ EQMP { .name = "query-tpm-types", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_tpm_types, + .mhandler.cmd_new = qmp_marshal_query_tpm_types, }, SQMP @@ -3714,7 +3714,7 @@ EQMP { .name = "chardev-add", .args_type = "id:s,backend:q", - .mhandler.cmd_new = qmp_marshal_input_chardev_add, + .mhandler.cmd_new = qmp_marshal_chardev_add, }, SQMP @@ -3751,7 +3751,7 @@ EQMP { .name = "chardev-remove", .args_type = "id:s", - .mhandler.cmd_new = qmp_marshal_input_chardev_remove, + .mhandler.cmd_new = qmp_marshal_chardev_remove, }, @@ -3774,7 +3774,7 @@ EQMP { .name = "query-rx-filter", .args_type = "name:s?", - .mhandler.cmd_new = qmp_marshal_input_query_rx_filter, + .mhandler.cmd_new = qmp_marshal_query_rx_filter, }, SQMP @@ -3840,7 +3840,7 @@ EQMP { .name = "blockdev-add", .args_type = "options:q", - .mhandler.cmd_new = qmp_marshal_input_blockdev_add, + .mhandler.cmd_new = qmp_marshal_blockdev_add, }, SQMP @@ -3899,7 +3899,7 @@ EQMP { .name = "query-named-block-nodes", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_named_block_nodes, + .mhandler.cmd_new = qmp_marshal_query_named_block_nodes, }, SQMP @@ -3961,7 +3961,7 @@ EQMP { .name = "query-memdev", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_memdev, + .mhandler.cmd_new = qmp_marshal_query_memdev, }, SQMP @@ -3999,7 +3999,7 @@ EQMP { .name = "query-memory-devices", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_memory_devices, + .mhandler.cmd_new = qmp_marshal_query_memory_devices, }, SQMP @@ -4026,7 +4026,7 @@ EQMP { .name = "query-acpi-ospm-status", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_query_acpi_ospm_status, + .mhandler.cmd_new = qmp_marshal_query_acpi_ospm_status, }, SQMP @@ -4049,7 +4049,7 @@ EQMP { .name = "rtc-reset-reinjection", .args_type = "", - .mhandler.cmd_new = qmp_marshal_input_rtc_reset_reinjection, + .mhandler.cmd_new = qmp_marshal_rtc_reset_reinjection, }, #endif @@ -4070,7 +4070,7 @@ EQMP { .name = "trace-event-get-state", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_trace_event_get_state, + .mhandler.cmd_new = qmp_marshal_trace_event_get_state, }, SQMP @@ -4088,7 +4088,7 @@ EQMP { .name = "trace-event-set-state", .args_type = "name:s,enable:b,ignore-unavailable:b?", - .mhandler.cmd_new = qmp_marshal_input_trace_event_set_state, + .mhandler.cmd_new = qmp_marshal_trace_event_set_state, }, SQMP @@ -4106,7 +4106,7 @@ EQMP { .name = "x-input-send-event", .args_type = "console:i?,events:q", - .mhandler.cmd_new = qmp_marshal_input_x_input_send_event, + .mhandler.cmd_new = qmp_marshal_x_input_send_event, }, SQMP @@ -4171,7 +4171,7 @@ EQMP { .name = "block-set-write-threshold", .args_type = "node-name:s,write-threshold:l", - .mhandler.cmd_new = qmp_marshal_input_block_set_write_threshold, + .mhandler.cmd_new = qmp_marshal_block_set_write_threshold, }, SQMP @@ -4199,7 +4199,7 @@ EQMP { .name = "query-rocker", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_query_rocker, + .mhandler.cmd_new = qmp_marshal_query_rocker, }, SQMP @@ -4220,7 +4220,7 @@ EQMP { .name = "query-rocker-ports", .args_type = "name:s", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports, + .mhandler.cmd_new = qmp_marshal_query_rocker_ports, }, SQMP @@ -4245,7 +4245,7 @@ EQMP { .name = "query-rocker-of-dpa-flows", .args_type = "name:s,tbl-id:i?", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows, + .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_flows, }, SQMP @@ -4274,7 +4274,7 @@ EQMP { .name = "query-rocker-of-dpa-groups", .args_type = "name:s,type:i?", - .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups, + .mhandler.cmd_new = qmp_marshal_query_rocker_of_dpa_groups, }, SQMP diff --git a/qmp.c b/qmp.c index 9623c802ab..3feae9f88b 100644 --- a/qmp.c +++ b/qmp.c @@ -157,9 +157,9 @@ VncInfo2List *qmp_query_vnc_servers(Error **errp) * #ifdef CONFIG_SPICE. Necessary for an accurate query-commands * result. However, the QAPI schema is blissfully unaware of that, * and the QAPI code generator happily generates a dead - * qmp_marshal_input_query_spice() that calls qmp_query_spice(). - * Provide it one, or else linking fails. - * FIXME Educate the QAPI schema on CONFIG_SPICE. + * qmp_marshal_query_spice() that calls qmp_query_spice(). Provide it + * one, or else linking fails. FIXME Educate the QAPI schema on + * CONFIG_SPICE. */ SpiceInfo *qmp_query_spice(Error **errp) { diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 6a52c88080..c68659a7b1 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -204,7 +204,7 @@ out: def gen_marshal_proto(name): - ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) + ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: ret = 'static ' + ret return ret @@ -251,7 +251,7 @@ def gen_register_command(name, success_response): options = 'QCO_NO_SUCCESS_RESP' ret = mcgen(''' -qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); +qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s); ''', name=name, c_name=c_name(name), opts=options) -- cgit v1.2.3-55-g7522 From 56d92b003a223585980df5403ee9e3a55de90adf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:21 +0200 Subject: qapi-commands: De-duplicate output marshaling functions gen_marshal_output() uses its parameter name only for name of the generated function. Name it after the type being marshaled instead of its caller, and drop duplicates. Saves 7 copies of qmp_marshal_output_int() in qemu-ga, and one copy of qmp_marshal_output_str() in qemu-system-*. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange Message-Id: <1442401589-24189-19-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 4 ++-- scripts/qapi-commands.py | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 50da1d08b8..0884b5813c 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -721,7 +721,7 @@ Example: $ cat qapi-generated/example-qmp-marshal.c [Uninteresting stuff omitted...] - static void qmp_marshal_output_my_command(UserDefOne *ret_in, QObject **ret_out, Error **errp) + static void qmp_marshal_output_UserDefOne(UserDefOne *ret_in, QObject **ret_out, Error **errp) { Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); @@ -764,7 +764,7 @@ Example: goto out; } - qmp_marshal_output_my_command(retval, ret, &local_err); + qmp_marshal_output_UserDefOne(retval, ret, &local_err); out: error_propagate(errp, local_err); diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 833768ef0e..810a897625 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -62,7 +62,7 @@ def gen_call(name, arg_type, ret_type): qmp_marshal_output_%(c_name)s(retval, ret, &local_err); ''', - c_name=c_name(name)) + c_name=ret_type.c_name()) pop_indent() return ret @@ -166,10 +166,10 @@ qapi_dealloc_visitor_cleanup(md); return ret -def gen_marshal_output(name, ret_type): +def gen_marshal_output(ret_type): return mcgen(''' -static void qmp_marshal_output_%(c_cmd_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) +static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) { Error *local_err = NULL; QmpOutputVisitor *mo = qmp_output_visitor_new(); @@ -192,8 +192,7 @@ out: qapi_dealloc_visitor_cleanup(md); } ''', - c_type=ret_type.c_type(), c_cmd_name=c_name(name), - c_name=ret_type.c_name()) + c_type=ret_type.c_type(), c_name=ret_type.c_name()) def gen_marshal_proto(name): @@ -272,24 +271,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): self.decl = None self.defn = None self._regy = None + self._visited_ret_types = None def visit_begin(self, schema): self.decl = '' self.defn = '' self._regy = '' + self._visited_ret_types = set() def visit_end(self): if not middle_mode: self.defn += gen_registry(self._regy) self._regy = None + self._visited_ret_types = None def visit_command(self, name, info, arg_type, ret_type, gen, success_response): if not gen: return self.decl += gen_command_decl(name, arg_type, ret_type) - if ret_type: - self.defn += gen_marshal_output(name, ret_type) + if ret_type and ret_type not in self._visited_ret_types: + self._visited_ret_types.add(ret_type) + self.defn += gen_marshal_output(ret_type) if middle_mode: self.decl += gen_marshal_decl(name) self.defn += gen_marshal(name, arg_type, ret_type) -- cgit v1.2.3-55-g7522 From f133f2db1eedd409d3c1b0892f65b99f83c74754 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:22 +0200 Subject: qapi: Improve built-in type documentation Clarify how they map to JSON. Add how they map to C. Fix the reference to StringInputVisitor. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange Message-Id: <1442401589-24189-20-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 0884b5813c..5c2c278a60 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -140,17 +140,24 @@ must have a value that forms a struct name. === Built-in Types === -The following types are built-in to the parser: - 'str' - arbitrary UTF-8 string - 'int' - 64-bit signed integer (although the C code may place further - restrictions on acceptable range) - 'number' - floating point number - 'bool' - JSON value of true or false - 'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum - bit size - 'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts - 'size' - like 'uint64', but allows scaled suffix from command line - visitor +The following types are predefined, and map to C as follows: + + Schema C JSON + str char * any JSON string, UTF-8 + number double any JSON number + int int64_t a JSON number without fractional part + that fits into the C integer type + int8 int8_t likewise + int16 int16_t likewise + int32 int32_t likewise + int64 int64_t likewise + uint8 uint8_t likewise + uint16 uint16_t likewise + uint32 uint32_t likewise + uint64 uint64_t likewise + size uint64_t like uint64_t, except StringInputVisitor + accepts size suffixes + bool bool JSON true or false === Includes === -- cgit v1.2.3-55-g7522 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 'docs/qapi-code-gen.txt') 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 v1.2.3-55-g7522 From b8a98326d565516bfcaa6582781605d167471b48 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:26 +0200 Subject: qapi-schema: Fix up misleading specification of netdev_add It doesn't take a 'props' argument, let alone one in the format "NAME=VALUE,..." The bogus arguments specification doesn't matter due to 'gen': false. Clean it up to be incomplete rather than wrong, and document the incompleteness. While there, improve netdev_add usage example in the manual: add a device option to show how it's done. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange Message-Id: <1442401589-24189-24-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 2 +- qapi-schema.json | 13 +++++++------ qmp-commands.hx | 4 +++- 3 files changed, 11 insertions(+), 8 deletions(-) (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 70fdae77d1..4e331a0f1d 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -471,7 +471,7 @@ try to avoid adding new commands that rely on this, and instead use type-safe unions. For an example of bypass usage: { 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str', '*props': '**'}, + 'data': {'type': 'str', 'id': 'str'}, 'gen': false } Normally, the QAPI schema is used to describe synchronous exchanges, diff --git a/qapi-schema.json b/qapi-schema.json index 756ed9e96c..3ff9fec61d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2079,11 +2079,12 @@ # # @id: the name of the new network backend # -# @props: #optional a list of properties to be passed to the backend in -# the format 'name=value', like 'ifname=tap0,script=no' +# Additional arguments depend on the type. # -# Notes: The semantics of @props is not well defined. Future commands will be -# introduced that provide stronger typing for backend creation. +# TODO This command effectively bypasses QAPI completely due to its +# "additional arguments" business. It shouldn't have been added to +# the schema in this form. It should be qapified properly, or +# replaced by a properly qapified command. # # Since: 0.14.0 # @@ -2091,8 +2092,8 @@ # If @type is not a valid network backend, DeviceNotFound ## { 'command': 'netdev_add', - 'data': {'type': 'str', 'id': 'str', '*props': '**'}, - 'gen': false } + 'data': {'type': 'str', 'id': 'str'}, + 'gen': false } # so we can get the additional arguments ## # @netdev_del: diff --git a/qmp-commands.hx b/qmp-commands.hx index 7c74e20087..5a5440636d 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -917,7 +917,9 @@ Arguments: Example: --> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } } +-> { "execute": "netdev_add", + "arguments": { "type": "user", "id": "netdev1", + "dnssearch": "example.org" } } <- { "return": {} } Note: The supported device options are the same ones supported by the '-netdev' -- cgit v1.2.3-55-g7522 From 2d21291ae645955fcc4652ebfec81ad338169ac6 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:27 +0200 Subject: qapi: Pseudo-type '**' is now unused, drop it 'gen': false needs to stay for now, because netdev_add is still using it. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Daniel P. Berrange Message-Id: <1442401589-24189-25-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 18 ++++++------------ scripts/qapi.py | 20 ++++---------------- tests/Makefile | 2 +- tests/qapi-schema/type-bypass-no-gen.err | 1 - tests/qapi-schema/type-bypass-no-gen.exit | 1 - tests/qapi-schema/type-bypass-no-gen.json | 2 -- tests/qapi-schema/type-bypass-no-gen.out | 0 tests/qapi-schema/type-bypass.err | 0 tests/qapi-schema/type-bypass.exit | 1 - tests/qapi-schema/type-bypass.json | 2 -- tests/qapi-schema/type-bypass.out | 4 ---- 11 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 tests/qapi-schema/type-bypass-no-gen.err delete mode 100644 tests/qapi-schema/type-bypass-no-gen.exit delete mode 100644 tests/qapi-schema/type-bypass-no-gen.json delete mode 100644 tests/qapi-schema/type-bypass-no-gen.out delete mode 100644 tests/qapi-schema/type-bypass.err delete mode 100644 tests/qapi-schema/type-bypass.exit delete mode 100644 tests/qapi-schema/type-bypass.json delete mode 100644 tests/qapi-schema/type-bypass.out (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 4e331a0f1d..b917962315 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -111,10 +111,7 @@ and field names within a type, should be all lower case with words separated by a hyphen. However, some existing older commands and complex types use underscore; when extending such expressions, consistency is preferred over blindly avoiding underscore. Event -names should be ALL_CAPS with words separated by underscore. The -special string '**' appears for some commands that manually perform -their own type checking rather than relying on the type-safe code -produced by the qapi code generators. +names should be ALL_CAPS with words separated by underscore. Any name (command, event, type, field, or enum value) beginning with "x-" is marked experimental, and may be withdrawn or changed @@ -461,14 +458,11 @@ which would validate this Client JSON Protocol transaction: <= { "return": [ { "value": "one" }, { } ] } In rare cases, QAPI cannot express a type-safe representation of a -corresponding Client JSON Protocol command. In these cases, if the -command expression includes the key 'gen' with boolean value false, -then the 'data' or 'returns' member that intends to bypass generated -type-safety and do its own manual validation should use an inline -dictionary definition, with a value of '**' rather than a valid type -name for the keys that the generated code will not validate. Please -try to avoid adding new commands that rely on this, and instead use -type-safe unions. For an example of bypass usage: +corresponding Client JSON Protocol command. You then have to suppress +generation of a marshalling function by including a key 'gen' with +boolean value false, and instead write your own function. Please try +to avoid adding new commands that rely on this, and instead use +type-safe unions. For an example of this usage: { 'command': 'netdev_add', 'data': {'type': 'str', 'id': 'str'}, diff --git a/scripts/qapi.py b/scripts/qapi.py index 8808ae6784..f4072978c1 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -428,15 +428,12 @@ def is_enum(name): def check_type(expr_info, source, value, allow_array = False, allow_dict = False, allow_optional = False, - allow_star = False, allow_metas = []): + allow_metas = []): global all_names if value is None: return - if allow_star and value == '**': - return - # Check if array type for value is okay if isinstance(value, list): if not allow_array: @@ -450,10 +447,6 @@ def check_type(expr_info, source, value, allow_array = False, # Check if type name for value is okay if isinstance(value, str): - if value == '**': - raise QAPIExprError(expr_info, - "%s uses '**' but did not request 'gen':false" - % source) if not value in all_names: raise QAPIExprError(expr_info, "%s uses unknown type '%s'" @@ -479,7 +472,7 @@ def check_type(expr_info, source, value, allow_array = False, # Todo: allow dictionaries to represent default values of # an optional argument. check_type(expr_info, "Member '%s' of %s" % (key, source), arg, - allow_array=True, allow_star=allow_star, + allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) @@ -499,18 +492,16 @@ def check_member_clash(expr_info, base_name, data, source = ""): def check_command(expr, expr_info): name = expr['command'] - allow_star = expr.has_key('gen') check_type(expr_info, "'data' for command '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['struct'], allow_star=allow_star) + allow_metas=['struct']) returns_meta = ['union', 'struct'] if name in returns_whitelist: returns_meta += ['built-in', 'alternate', 'enum'] check_type(expr_info, "'returns' for command '%s'" % name, expr.get('returns'), allow_array=True, - allow_optional=True, allow_metas=returns_meta, - allow_star=allow_star) + allow_optional=True, allow_metas=returns_meta) def check_event(expr, expr_info): global events @@ -1122,7 +1113,6 @@ class QAPISchema(object): ('bool', 'boolean', 'bool', 'false'), ('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' @@ -1272,8 +1262,6 @@ 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 10e55b36ee..aadfb38fdc 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -234,7 +234,7 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ bad-type-dict.json double-data.json unknown-expr-key.json \ redefined-type.json redefined-command.json redefined-builtin.json \ redefined-event.json command-int.json bad-data.json event-max.json \ - type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \ + type-bypass-bad-gen.json \ args-invalid.json \ args-array-empty.json args-array-unknown.json args-int.json \ args-unknown.json args-member-unknown.json args-member-array.json \ diff --git a/tests/qapi-schema/type-bypass-no-gen.err b/tests/qapi-schema/type-bypass-no-gen.err deleted file mode 100644 index 20cef0a8a7..0000000000 --- a/tests/qapi-schema/type-bypass-no-gen.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/type-bypass-no-gen.json:2: Member 'arg' of 'data' for command 'unsafe' uses '**' but did not request 'gen':false diff --git a/tests/qapi-schema/type-bypass-no-gen.exit b/tests/qapi-schema/type-bypass-no-gen.exit deleted file mode 100644 index d00491fd7e..0000000000 --- a/tests/qapi-schema/type-bypass-no-gen.exit +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/tests/qapi-schema/type-bypass-no-gen.json b/tests/qapi-schema/type-bypass-no-gen.json deleted file mode 100644 index 4feae3719c..0000000000 --- a/tests/qapi-schema/type-bypass-no-gen.json +++ /dev/null @@ -1,2 +0,0 @@ -# type bypass only works with 'gen':false -{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**' } diff --git a/tests/qapi-schema/type-bypass-no-gen.out b/tests/qapi-schema/type-bypass-no-gen.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/type-bypass.err b/tests/qapi-schema/type-bypass.err deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/type-bypass.exit b/tests/qapi-schema/type-bypass.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/type-bypass.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/type-bypass.json b/tests/qapi-schema/type-bypass.json deleted file mode 100644 index 48b2137833..0000000000 --- a/tests/qapi-schema/type-bypass.json +++ /dev/null @@ -1,2 +0,0 @@ -# Use of 'gen':false allows bypassing type system -{ 'command': 'unsafe', 'data': { 'arg': '**' }, 'returns': '**', 'gen': false } diff --git a/tests/qapi-schema/type-bypass.out b/tests/qapi-schema/type-bypass.out deleted file mode 100644 index db2a4e6d84..0000000000 --- a/tests/qapi-schema/type-bypass.out +++ /dev/null @@ -1,4 +0,0 @@ -object :obj-unsafe-arg - member arg: any optional=False -command unsafe :obj-unsafe-arg -> any - gen=False success_response=True -- cgit v1.2.3-55-g7522 From 39a181581650f4d50f4445bc6276d9716cece050 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:28 +0200 Subject: qapi: New QMP command query-qmp-schema for QMP introspection qapi/introspect.json defines the introspection schema. It's designed for QMP introspection, but should do for similar uses, such as QGA. The introspection schema does not reflect all the rules and restrictions that apply to QAPI schemata. A valid QAPI schema has an introspection value conforming to the introspection schema, but the converse is not true. Introspection lowers away a number of schema details, and makes implicit things explicit: * The built-in types are declared with their JSON type. All integer types are mapped to 'int', because how many bits we use internally is an implementation detail. It could be pressed into external interface service as very approximate range information, but that's a bad idea. If we need range information, we better do it properly. * Implicit type definitions are made explicit, and given auto-generated names: - Array types, named by appending "List" to the name of their element type, like in generated C. - The enumeration types implicitly defined by simple union types, named by appending "Kind" to the name of their simple union type, like in generated C. - Types that don't occur in generated C. Their names start with ':' so they don't clash with the user's names. * All type references are by name. * The struct and union types are generalized into an object type. * Base types are flattened. * Commands take a single argument and return a single result. Dictionary argument or list result is an implicit type definition. The empty object type is used when a command takes no arguments or produces no results. The argument is always of object type, but the introspection schema doesn't reflect that. The 'gen': false directive is omitted as implementation detail. The 'success-response' directive is omitted as well for now, even though it's not an implementation detail, because it's not used by QMP. * Events carry a single data value. Implicit type definition and empty object type use, just like for commands. The value is of object type, but the introspection schema doesn't reflect that. * Types not used by commands or events are omitted. Indirect use counts as use. * Optional members have a default, which can only be null right now Instead of a mandatory "optional" flag, we have an optional default. No default means mandatory, default null means optional without default value. Non-null is available for optional with default (possible future extension). * Clients should *not* look up types by name, because type names are not ABI. Look up the command or event you're interested in, then follow the references. TODO Should we hide the type names to eliminate the temptation? New generator scripts/qapi-introspect.py computes an introspection value for its input, and generates a C variable holding it. It can generate awfully long lines. Marked TODO. A new test-qmp-input-visitor test case feeds its result for both tests/qapi-schema/qapi-schema-test.json and qapi-schema.json to a QmpInputVisitor to verify it actually conforms to the schema. New QMP command query-qmp-schema takes its return value from that variable. Its reply is some 85KiBytes for me right now. If this turns out to be too much, we have a couple of options: * We can use shorter names in the JSON. Not the QMP style. * Optionally return the sub-schema for commands and events given as arguments. Right now qmp_query_schema() sends the string literal computed by qmp-introspect.py. To compute sub-schema at run time, we'd have to duplicate parts of qapi-introspect.py in C. Unattractive. * Let clients cache the output of query-qmp-schema. It changes only on QEMU upgrades, i.e. rarely. Provide a command query-qmp-schema-hash. Clients can have a cache indexed by hash, and re-query the schema only when they don't have it cached. Even simpler: put the hash in the QMP greeting. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake --- .gitignore | 1 + Makefile | 9 +- Makefile.objs | 4 +- docs/qapi-code-gen.txt | 237 +++++++++++++++++++- monitor.c | 16 ++ qapi-schema.json | 3 + qapi/introspect.json | 277 ++++++++++++++++++++++++ qmp-commands.hx | 17 ++ scripts/qapi-introspect.py | 184 ++++++++++++++++ scripts/qapi.py | 13 +- tests/.gitignore | 1 + tests/Makefile | 10 +- tests/qapi-schema/alternate-good.out | 1 + tests/qapi-schema/args-member-array.out | 1 + tests/qapi-schema/comments.out | 1 + tests/qapi-schema/empty.out | 1 + tests/qapi-schema/enum-empty.out | 1 + tests/qapi-schema/event-case.out | 1 + tests/qapi-schema/flat-union-reverse-define.out | 1 + tests/qapi-schema/ident-with-escape.out | 1 + tests/qapi-schema/include-relpath.out | 1 + tests/qapi-schema/include-repetition.out | 1 + tests/qapi-schema/include-simple.out | 1 + tests/qapi-schema/indented-expr.out | 1 + tests/qapi-schema/qapi-schema-test.out | 1 + tests/qapi-schema/returns-int.out | 1 + tests/test-qmp-input-strict.c | 55 +++++ 27 files changed, 830 insertions(+), 11 deletions(-) create mode 100644 qapi/introspect.json create mode 100644 scripts/qapi-introspect.py (limited to 'docs/qapi-code-gen.txt') diff --git a/.gitignore b/.gitignore index 367bc706d8..fdd721dba1 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ /qapi-visit.[ch] /qapi-event.[ch] /qmp-commands.h +/qmp-introspect.[ch] /qmp-marshal.c /qemu-doc.html /qemu-tech.html diff --git a/Makefile b/Makefile index 291fb54d35..8ec9b69b2f 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,8 @@ endif GENERATED_HEADERS = config-host.h qemu-options.def GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c +GENERATED_HEADERS += qmp-introspect.h +GENERATED_SOURCES += qmp-introspect.c GENERATED_HEADERS += trace/generated-events.h GENERATED_SOURCES += trace/generated-events.c @@ -269,7 +271,7 @@ $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ - $(SRC_PATH)/qapi/event.json + $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json qapi-types.c qapi-types.h :\ $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) @@ -291,6 +293,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \ $(gen-out-type) -o "." -m $<, \ " GEN $@") +qmp-introspect.h qmp-introspect.c :\ +$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \ + $(gen-out-type) -o "." $<, \ + " GEN $@") QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h) $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) diff --git a/Makefile.objs b/Makefile.objs index 3df2efca1c..ce87778828 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,7 +1,8 @@ ####################################################################### # Common libraries for tools and emulators stub-obj-y = stubs/ -util-obj-y = util/ qobject/ qapi/ qapi-types.o qapi-visit.o qapi-event.o +util-obj-y = util/ qobject/ qapi/ +util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img @@ -92,6 +93,7 @@ common-obj-$(CONFIG_FDT) += device_tree.o # qapi common-obj-y += qmp-marshal.o +common-obj-y += qmp-introspect.o common-obj-y += qmp.o hmp.o endif diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index b917962315..f321d4b949 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -502,13 +502,206 @@ Resulting in this JSON object: "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +== Client JSON Protocol introspection == + +Clients of a Client JSON Protocol commonly need to figure out what +exactly the server (QEMU) supports. + +For this purpose, QMP provides introspection via command +query-qmp-schema. QGA currently doesn't support introspection. + +query-qmp-schema returns a JSON array of SchemaInfo objects. These +objects together describe the wire ABI, as defined in the QAPI schema. + +However, the SchemaInfo can't reflect all the rules and restrictions +that apply to QMP. It's interface introspection (figuring out what's +there), not interface specification. The specification is in the QAPI +schema. To understand how QMP is to be used, you need to study the +QAPI schema. + +Like any other command, query-qmp-schema is itself defined in the QAPI +schema, along with the SchemaInfo type. This text attempts to give an +overview how things work. For details you need to consult the QAPI +schema. + +SchemaInfo objects have common members "name" and "meta-type", and +additional variant members depending on the value of meta-type. + +Each SchemaInfo object describes a wire ABI entity of a certain +meta-type: a command, event or one of several kinds of type. + +SchemaInfo for entities defined in the QAPI schema have the same name +as in the schema. This is the case for all commands and events, and +most types. + +Command and event names are part of the wire ABI, but type names are +not. Therefore, looking up a type by its name in the QAPI schema is +wrong. Look up the command or event, then follow references by name. + +QAPI schema definitions not reachable that way are omitted. + +The SchemaInfo for a command has meta-type "command", and variant +members "arg-type" and "ret-type". On the wire, the "arguments" +member of a client's "execute" command must conform to the object type +named by "arg-type". The "return" member that the server passes in a +success response conforms to the type named by "ret-type". + +If the command takes no arguments, "arg-type" names an object type +without members. Likewise, if the command returns nothing, "ret-type" +names an object type without members. + +Example: the SchemaInfo for command query-qmp-schema + + { "name": "query-qmp-schema", "meta-type": "command", + "arg-type": ":empty", "ret-type": "SchemaInfoList" } + + Type ":empty" is an object type without members, and type + "SchemaInfoList" is the array of SchemaInfo type. + +The SchemaInfo for an event has meta-type "event", and variant member +"arg-type". On the wire, a "data" member that the server passes in an +event conforms to the object type named by "arg-type". + +If the event carries no additional information, "arg-type" names an +object type without members. The event may not have a data member on +the wire then. + +Each command or event defined with dictionary-valued 'data' in the +QAPI schema implicitly defines an object type called ":obj-NAME-arg", +where NAME is the command or event's name. + +Example: the SchemaInfo for EVENT_C from section Events + + { "name": "EVENT_C", "meta-type": "event", + "arg-type": ":obj-EVENT_C-arg" } + + Type ":obj-EVENT_C-arg" is an implicitly defined object type with + the two members from the event's definition. + +The SchemaInfo for struct and union types has meta-type "object". + +The SchemaInfo for a struct type has variant member "members". + +The SchemaInfo for a union type additionally has variant members "tag" +and "variants". + +"members" is a JSON array describing the object's common members, if +any. Each element is a JSON object with members "name" (the member's +name), "type" (the name of its type), and optionally "default". The +member is optional if "default" is present. Currently, "default" can +only have value null. Other values are reserved for future +extensions. + +Example: the SchemaInfo for MyType from section Struct types + + { "name": "MyType", "meta-type": "object", + "members": [ + { "name": "member1", "type": "str" }, + { "name": "member2", "type": "int" }, + { "name": "member3", "type": "str", "default": null } ] } + +"tag" is the name of the common member serving as type tag. +"variants" is a JSON array describing the object's variant members. +Each element is a JSON object with members "case" (the value of type +tag this element applies to) and "type" (the name of an object type +that provides the variant members for this type tag value). + +Example: the SchemaInfo for flat union BlockdevOptions from section +Union types + + { "name": "BlockdevOptions", "meta-type": "object", + "members": [ + { "name": "driver", "type": "BlockdevDriver" }, + { "name": "readonly", "type": "bool"} ], + "tag": "driver", + "variants": [ + { "case": "file", "type": "FileOptions" }, + { "case": "qcow2", "type": "Qcow2Options" } ] } + +Note that base types are "flattened": its members are included in the +"members" array. + +A simple union implicitly defines an enumeration type for its implicit +discriminator (called "type" on the wire, see section Union types). +Such a type's name is made by appending "Kind" to the simple union's +name. + +A simple union implicitly defines an object type for each of its +variants. The type's name is ":obj-NAME-wrapper", where NAME is the +name of the name of the variant's type. + +Example: the SchemaInfo for simple union BlockdevOptions from section +Union types + + { "name": "BlockdevOptions", "meta-type": "object", + "members": [ + { "name": "kind", "type": "BlockdevOptionsKind" } ], + "tag": "type", + "variants": [ + { "case": "file", "type": ":obj-FileOptions-wrapper" }, + { "case": "qcow2", "type": ":obj-Qcow2Options-wrapper" } ] } + + Enumeration type "BlockdevOptionsKind" and the object types + ":obj-FileOptions-wrapper", ":obj-Qcow2Options-wrapper" are + implicitly defined. + +The SchemaInfo for an alternate type has meta-type "alternate", and +variant member "members". "members" is a JSON array. Each element is +a JSON object with member "type", which names a type. Values of the +alternate type conform to exactly one of its member types. + +Example: the SchemaInfo for BlockRef from section Alternate types + + { "name": "BlockRef", "meta-type": "alternate", + "members": [ + { "type": "BlockdevOptions" }, + { "type": "str" } ] } + +The SchemaInfo for an array type has meta-type "array", and variant +member "element-type", which names the array's element type. Array +types are implicitly defined. An array type's name is made by +appending "List" to its element type's name. + +Example: the SchemaInfo for ['str'] + + { "name": "strList", "meta-type": "array", + "element-type": "str" } + +The SchemaInfo for an enumeration type has meta-type "enum" and +variant member "values". + +Example: the SchemaInfo for MyEnum from section Enumeration types + + { "name": "MyEnum", "meta-type": "enum", + "values": [ "value1", "value2", "value3" ] } + +The SchemaInfo for a built-in type has the same name as the type in +the QAPI schema (see section Built-in Types), with one exception +detailed below. It has variant member "json-type" that shows how +values of this type are encoded on the wire. + +Example: the SchemaInfo for str + + { "name": "str", "meta-type": "builtin", "json-type": "string" } + +The QAPI schema supports a number of integer types that only differ in +how they map to C. They are identical as far as SchemaInfo is +concerned. Therefore, they get all mapped to a single type "int" in +SchemaInfo. + +As explained above, type names are not part of the wire ABI. Not even +the names of built-in types. Clients should examine member +"json-type" instead of hard-coding names of built-in types. + + == Code generation == -Schemas are fed into 3 scripts to generate all the code/files that, paired -with the core QAPI libraries, comprise everything required to take JSON -commands read in by a Client JSON Protocol server, unmarshal the arguments into -the underlying C types, call into the corresponding C function, and map the -response back to a Client JSON Protocol response to be returned to the user. +Schemas are fed into four scripts to generate all the code/files that, +paired with the core QAPI libraries, comprise everything required to +take JSON commands read in by a Client JSON Protocol server, unmarshal +the arguments into the underlying C types, call into the corresponding +C function, and map the response back to a Client JSON Protocol +response to be returned to the user. As an example, we'll use the following schema, which describes a single complex user-defined type (which will produce a C struct, along with a list @@ -856,3 +1049,37 @@ Example: extern const char *const example_QAPIEvent_lookup[]; #endif + +=== scripts/qapi-introspect.py === + +Used to generate the introspection C code for a schema. The following +files are created: + +$(prefix)qmp-introspect.c - Defines a string holding a JSON + description of the schema. +$(prefix)qmp-introspect.h - Declares the above string. + +Example: + + $ python scripts/qapi-introspect.py --output-dir="qapi-generated" + --prefix="example-" example-schema.json + $ cat qapi-generated/example-qmp-introspect.c +[Uninteresting stuff omitted...] + + const char example_qmp_schema_json[] = "[" + "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, " + "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, " + "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}, " + "{\"members\": [], \"meta-type\": \"object\", \"name\": \":empty\"}, " + "{\"members\": [{\"name\": \"arg1\", \"type\": \"UserDefOne\"}], \"meta-type\": \"object\", \"name\": \":obj-my-command-arg\"}, " + "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"UserDefOne\"}, " + "{\"arg-type\": \":obj-my-command-arg\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"UserDefOne\"}]"; + $ cat qapi-generated/example-qmp-introspect.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QMP_INTROSPECT_H + #define EXAMPLE_QMP_INTROSPECT_H + + extern const char example_qmp_schema_json[]; + + #endif diff --git a/monitor.c b/monitor.c index dde1f220a1..d0edb3bb15 100644 --- a/monitor.c +++ b/monitor.c @@ -74,6 +74,7 @@ #include "block/qapi.h" #include "qapi/qmp-event.h" #include "qapi-event.h" +#include "qmp-introspect.h" #include "sysemu/block-backend.h" /* for hmp_info_irq/pic */ @@ -928,6 +929,21 @@ EventInfoList *qmp_query_events(Error **errp) return ev_list; } +/* + * Minor hack: generated marshalling suppressed for this command + * ('gen': false in the schema) so we can parse the JSON string + * directly into QObject instead of first parsing it with + * visit_type_SchemaInfoList() into a SchemaInfoList, then marshal it + * to QObject with generated output marshallers, every time. Instead, + * we do it in test-qmp-input-visitor.c, just to make sure + * qapi-introspect.py's output actually conforms to the schema. + */ +static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, + Error **errp) +{ + *ret_data = qobject_from_json(qmp_schema_json); +} + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { diff --git a/qapi-schema.json b/qapi-schema.json index 3ff9fec61d..821362d637 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -17,6 +17,9 @@ # Tracing commands { 'include': 'qapi/trace.json' } +# QAPI introspection +{ 'include': 'qapi/introspect.json' } + ## # @LostTickPolicy: # diff --git a/qapi/introspect.json b/qapi/introspect.json new file mode 100644 index 0000000000..9c8ad53d7a --- /dev/null +++ b/qapi/introspect.json @@ -0,0 +1,277 @@ +# -*- Mode: Python -*- +# +# QAPI/QMP introspection +# +# Copyright (C) 2015 Red Hat, Inc. +# +# Authors: +# Markus Armbruster +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# @query-qmp-schema +# +# Command query-qmp-schema exposes the QMP wire ABI as an array of +# SchemaInfo. This lets QMP clients figure out what commands and +# events are available in this QEMU, and their parameters and results. +# +# However, the SchemaInfo can't reflect all the rules and restrictions +# that apply to QMP. It's interface introspection (figuring out +# what's there), not interface specification. The specification is in +# the QAPI schema. +# +# Returns: array of @SchemaInfo, where each element describes an +# entity in the ABI: command, event, type, ... +# +# Note: the QAPI schema is also used to help define *internal* +# interfaces, by defining QAPI types. These are not part of the QMP +# wire ABI, and therefore not returned by this command. +# +# Since: 2.5 +## +{ 'command': 'query-qmp-schema', + 'returns': [ 'SchemaInfo' ], + 'gen': false } # just to simplify qmp_query_json() + +## +# @SchemaMetaType +# +# This is a @SchemaInfo's meta type, i.e. the kind of entity it +# describes. +# +# @builtin: a predefined type such as 'int' or 'bool'. +# +# @enum: an enumeration type +# +# @array: an array type +# +# @object: an object type (struct or union) +# +# @alternate: an alternate type +# +# @command: a QMP command +# +# @event: a QMP event +# +# Since: 2.5 +## +{ 'enum': 'SchemaMetaType', + 'data': [ 'builtin', 'enum', 'array', 'object', 'alternate', + 'command', 'event' ] } + +## +# @SchemaInfoBase +# +# Members common to any @SchemaInfo. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoBase', + 'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } } + +## +# @SchemaInfo +# +# @name: the entity's name, inherited from @base. +# Entities defined in the QAPI schema have the name defined in +# the schema. Implicitly defined entities have generated +# names. See docs/qapi-code-gen.txt section "Client JSON +# Protocol introspection" for details. +# +# All references to other SchemaInfo are by name. +# +# Command and event names are part of the wire ABI, but type names are +# not. Therefore, looking up a type by "well-known" name is wrong. +# Look up the command or event, then follow the references. +# +# @meta-type: the entity's meta type, inherited from @base. +# +# Additional members depend on the value of @meta-type. +# +# Since: 2.5 +## +{ 'union': 'SchemaInfo', + 'base': 'SchemaInfoBase', + 'discriminator': 'meta-type', + 'data': { + 'builtin': 'SchemaInfoBuiltin', + 'enum': 'SchemaInfoEnum', + 'array': 'SchemaInfoArray', + 'object': 'SchemaInfoObject', + 'alternate': 'SchemaInfoAlternate', + 'command': 'SchemaInfoCommand', + 'event': 'SchemaInfoEvent' } } + +## +# @SchemaInfoBuiltin +# +# Additional SchemaInfo members for meta-type 'builtin'. +# +# @json-type: the JSON type used for this type on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoBuiltin', + 'data': { 'json-type': 'JSONType' } } + +## +# @JSONType +# +# The four primitive and two structured types according to RFC 7159 +# section 1, plus 'int' (split off 'number'), plus the obvious top +# type 'value'. +# +# Since: 2.5 +## +{ 'enum': 'JSONType', + 'data': [ 'string', 'number', 'int', 'boolean', 'null', + 'object', 'array', 'value' ] } + +## +# @SchemaInfoEnum +# +# Additional SchemaInfo members for meta-type 'enum'. +# +# @values: the enumeration type's values. +# +# Values of this type are JSON string on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoEnum', + 'data': { 'values': ['str'] } } + +## +# @SchemaInfoArray +# +# Additional SchemaInfo members for meta-type 'array'. +# +# @element-type: the array type's element type. +# +# Values of this type are JSON array on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoArray', + 'data': { 'element-type': 'str' } } + +## +# @SchemaInfoObject +# +# Additional SchemaInfo members for meta-type 'object'. +# +# @members: the object type's (non-variant) members. +# +# @tag: #optional the name of the member serving as type tag. +# An element of @members with this name must exist. +# +# @variants: #optional variant members, i.e. additional members that +# depend on the type tag's value. Present exactly when +# @tag is present. +# +# Values of this type are JSON object on the wire. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObject', + 'data': { 'members': [ 'SchemaInfoObjectMember' ], + '*tag': 'str', + '*variants': [ 'SchemaInfoObjectVariant' ] } } + +## +# @SchemaInfoObjectMember +# +# An object member. +# +# @name: the member's name, as defined in the QAPI schema. +# +# @type: the name of the member's type. +# +# @default: #optional default when used as command parameter. +# If absent, the parameter is mandatory. +# If present, the value must be null. The parameter is +# optional, and behavior when it's missing is not specified +# here. +# Future extension: if present and non-null, the parameter +# is optional, and defaults to this value. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObjectMember', + 'data': { 'name': 'str', 'type': 'str', '*default': 'any' } } +# @default's type must be null or match @type + +## +# @SchemaInfoObjectVariant +# +# The variant members for a value of the type tag. +# +# @case: a value of the type tag. +# +# @type: the name of the object type that provides the variant members +# when the type tag has value @case. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoObjectVariant', + 'data': { 'case': 'str', 'type': 'str' } } + +## +# @SchemaInfoAlternate +# +# Additional SchemaInfo members for meta-type 'alternate'. +# +# @members: the alternate type's members. +# The members' wire encoding is distinct, see +# docs/qapi-code-gen.txt section Alternate types. +# +# On the wire, this can be any of the members. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoAlternate', + 'data': { 'members': [ 'SchemaInfoAlternateMember' ] } } + +## +# @SchemaInfoAlternateMember +# +# An alternate member. +# +# @type: the name of the member's type. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoAlternateMember', + 'data': { 'type': 'str' } } + +## +# @SchemaInfoCommand +# +# Additional SchemaInfo members for meta-type 'command'. +# +# @arg-type: the name of the object type that provides the command's +# parameters. +# +# @ret-type: the name of the command's result type. +# +# TODO @success-response (currently irrelevant, because it's QGA, not QMP) +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoCommand', + 'data': { 'arg-type': 'str', 'ret-type': 'str' } } + +## +# @SchemaInfoEvent +# +# Additional SchemaInfo members for meta-type 'event'. +# +# @arg-type: the name of the object type that provides the event's +# parameters. +# +# Since: 2.5 +## +{ 'struct': 'SchemaInfoEvent', + 'data': { 'arg-type': 'str' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 5a5440636d..66f03007ec 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2192,6 +2192,23 @@ EQMP .mhandler.cmd_new = qmp_marshal_query_events, }, +SQMP +query-qmp-schema +---------------- + +Return the QMP wire schema. The returned value is a json-array of +named schema entities. Entities are commands, events and various +types. See docs/qapi-code-gen.txt for information on their structure +and intended use. + +EQMP + + { + .name = "query-qmp-schema", + .args_type = "", + .mhandler.cmd_new = qmp_query_qmp_schema, + }, + SQMP query-chardev ------------- diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py new file mode 100644 index 0000000000..831d6d5944 --- /dev/null +++ b/scripts/qapi-introspect.py @@ -0,0 +1,184 @@ +# +# QAPI introspection generator +# +# Copyright (C) 2015 Red Hat, Inc. +# +# Authors: +# 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 qapi import * + + +# Caveman's json.dumps() replacement (we're stuck at Python 2.4) +# TODO try to use json.dumps() once we get unstuck +def to_json(obj, level=0): + if obj is None: + ret = 'null' + elif isinstance(obj, str): + ret = '"' + obj.replace('"', r'\"') + '"' + elif isinstance(obj, list): + elts = [to_json(elt, level + 1) + for elt in obj] + ret = '[' + ', '.join(elts) + ']' + elif isinstance(obj, dict): + elts = ['"%s": %s' % (key.replace('"', r'\"'), + to_json(obj[key], level + 1)) + for key in sorted(obj.keys())] + ret = '{' + ', '.join(elts) + '}' + else: + assert False # not implemented + if level == 1: + ret = '\n' + ret + return ret + + +def to_c_string(string): + return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' + + +class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): + def __init__(self): + self.defn = None + self.decl = None + self._schema = None + self._jsons = None + self._used_types = None + + def visit_begin(self, schema): + self._schema = schema + self._jsons = [] + self._used_types = [] + return QAPISchemaType # don't visit types for now + + def visit_end(self): + # visit the types that are actually used + for typ in self._used_types: + typ.visit(self) + self._jsons.sort() + # generate C + # TODO can generate awfully long lines + name = prefix + 'qmp_schema_json' + self.decl = mcgen(''' +extern const char %(c_name)s[]; +''', + c_name=c_name(name)) + lines = to_json(self._jsons).split('\n') + c_string = '\n '.join([to_c_string(line) for line in lines]) + self.defn = mcgen(''' +const char %(c_name)s[] = %(c_string)s; +''', + c_name=c_name(name), + c_string=c_string) + self._schema = None + self._jsons = None + self._used_types = None + + def _use_type(self, typ): + # Map the various integer types to plain int + if typ.json_type() == 'int': + typ = self._schema.lookup_type('int') + elif (isinstance(typ, QAPISchemaArrayType) and + typ.element_type.json_type() == 'int'): + typ = self._schema.lookup_type('intList') + # Add type to work queue if new + if typ not in self._used_types: + self._used_types.append(typ) + return typ.name + + def _gen_json(self, name, mtype, obj): + obj['name'] = name + obj['meta-type'] = mtype + self._jsons.append(obj) + + def _gen_member(self, member): + ret = {'name': member.name, 'type': self._use_type(member.type)} + if member.optional: + ret['default'] = None + return ret + + def _gen_variants(self, tag_name, variants): + return {'tag': tag_name, + 'variants': [self._gen_variant(v) for v in variants]} + + def _gen_variant(self, variant): + return {'case': variant.name, 'type': self._use_type(variant.type)} + + def visit_builtin_type(self, name, info, json_type): + self._gen_json(name, 'builtin', {'json-type': json_type}) + + def visit_enum_type(self, name, info, values, prefix): + self._gen_json(name, 'enum', {'values': values}) + + def visit_array_type(self, name, info, element_type): + self._gen_json(name, 'array', + {'element-type': self._use_type(element_type)}) + + def visit_object_type_flat(self, name, info, members, variants): + obj = {'members': [self._gen_member(m) for m in members]} + if variants: + obj.update(self._gen_variants(variants.tag_member.name, + variants.variants)) + self._gen_json(name, 'object', obj) + + def visit_alternate_type(self, name, info, variants): + self._gen_json(name, 'alternate', + {'members': [{'type': self._use_type(m.type)} + for m in variants.variants]}) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response): + arg_type = arg_type or self._schema.the_empty_object_type + ret_type = ret_type or self._schema.the_empty_object_type + self._gen_json(name, 'command', + {'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type)}) + + def visit_event(self, name, info, arg_type): + arg_type = arg_type or self._schema.the_empty_object_type + self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) + +(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() + +c_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' +h_comment = ''' +/* + * QAPI/QMP schema introspection + * + * Copyright (C) 2015 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ +''' + +(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, + 'qmp-introspect.c', 'qmp-introspect.h', + c_comment, h_comment) + +fdef.write(mcgen(''' +#include "%(prefix)sqmp-introspect.h" + +''', + prefix=prefix)) + +schema = QAPISchema(input_file) +gen = QAPISchemaGenIntrospectVisitor() +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 f4072978c1..06478bb269 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -788,6 +788,9 @@ class QAPISchemaVisitor(object): def visit_object_type(self, name, info, base, members, variants): pass + def visit_object_type_flat(self, name, info, members, variants): + pass + def visit_alternate_type(self, name, info, variants): pass @@ -943,6 +946,8 @@ class QAPISchemaObjectType(QAPISchemaType): def visit(self, visitor): visitor.visit_object_type(self.name, self.info, self.base, self.local_members, self.variants) + visitor.visit_object_type_flat(self.name, self.info, + self.members, self.variants) class QAPISchemaObjectTypeMember(object): @@ -1113,6 +1118,9 @@ class QAPISchema(object): ('bool', 'boolean', 'bool', 'false'), ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]: self._def_builtin_type(*t) + self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None, + [], None) + self._def_entity(self.the_empty_object_type) def _make_implicit_enum_type(self, name, values): name = name + 'Kind' @@ -1260,9 +1268,10 @@ class QAPISchema(object): ent.check(self) def visit(self, visitor): - visitor.visit_begin(self) + ignore = visitor.visit_begin(self) for name in sorted(self._entity_dict.keys()): - self._entity_dict[name].visit(visitor) + if not ignore or not isinstance(self._entity_dict[name], ignore): + self._entity_dict[name].visit(visitor) visitor.visit_end() diff --git a/tests/.gitignore b/tests/.gitignore index 2c5e2c31e4..a607bddce4 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -35,6 +35,7 @@ test-qmp-commands.h test-qmp-event test-qmp-input-strict test-qmp-input-visitor +test-qmp-introspect.[ch] test-qmp-marshal.c test-qmp-output-visitor test-rcu-list diff --git a/tests/Makefile b/tests/Makefile index aadfb38fdc..4063639a59 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -269,7 +269,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ struct-base-clash.json struct-base-clash-deep.json ) GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ - tests/test-qmp-commands.h tests/test-qapi-event.h + tests/test-qmp-commands.h tests/test-qapi-event.h \ + tests/test-qmp-introspect.h test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \ @@ -289,7 +290,7 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests test-util-obj-y = libqemuutil.a libqemustub.a test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y) test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ - tests/test-qapi-event.o \ + tests/test-qapi-event.o tests/test-qmp-introspect.o \ $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) @@ -346,6 +347,11 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-eve $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \ $(gen-out-type) -o tests -p "test-" $<, \ " GEN $@") +tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\ +$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \ + $(gen-out-type) -o tests -p "test-" $<, \ + " GEN $@") tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) diff --git a/tests/qapi-schema/alternate-good.out b/tests/qapi-schema/alternate-good.out index 3d765ffd7b..65af7278f4 100644 --- a/tests/qapi-schema/alternate-good.out +++ b/tests/qapi-schema/alternate-good.out @@ -1,3 +1,4 @@ +object :empty alternate Alt case value: int case string: Enum diff --git a/tests/qapi-schema/args-member-array.out b/tests/qapi-schema/args-member-array.out index b67384cc2e..b3b92dfc8e 100644 --- a/tests/qapi-schema/args-member-array.out +++ b/tests/qapi-schema/args-member-array.out @@ -1,3 +1,4 @@ +object :empty object :obj-okay-arg member member1: intList optional=False member member2: defList optional=False diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index 6161b90e91..9e2c656fa0 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1 +1,2 @@ +object :empty enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index e69de29bb2..272b1616f4 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -0,0 +1 @@ +object :empty diff --git a/tests/qapi-schema/enum-empty.out b/tests/qapi-schema/enum-empty.out index e09b00ffd6..a449d455fb 100644 --- a/tests/qapi-schema/enum-empty.out +++ b/tests/qapi-schema/enum-empty.out @@ -1 +1,2 @@ +object :empty enum MyEnum [] diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index b5ae4c20b3..cdfd264f9c 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -1 +1,2 @@ +object :empty event oops None diff --git a/tests/qapi-schema/flat-union-reverse-define.out b/tests/qapi-schema/flat-union-reverse-define.out index 477fb31198..a5a9134e7e 100644 --- a/tests/qapi-schema/flat-union-reverse-define.out +++ b/tests/qapi-schema/flat-union-reverse-define.out @@ -1,3 +1,4 @@ +object :empty object TestBase member enum1: TestEnum optional=False enum TestEnum ['value1', 'value2'] diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 9577d1bde7..f4542b12d8 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -1,3 +1,4 @@ +object :empty object :obj-fooA-arg member bar1: str optional=False command fooA :obj-fooA-arg -> None diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out index 6161b90e91..9e2c656fa0 100644 --- a/tests/qapi-schema/include-relpath.out +++ b/tests/qapi-schema/include-relpath.out @@ -1 +1,2 @@ +object :empty enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 6161b90e91..9e2c656fa0 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1 +1,2 @@ +object :empty enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 6161b90e91..9e2c656fa0 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1 +1,2 @@ +object :empty enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index c5af55a05c..226d300798 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,3 +1,4 @@ +object :empty command eins None -> None gen=True success_response=True command zwei None -> None diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index a52ac31ea5..1f6e858def 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,3 +1,4 @@ +object :empty object :obj-EVENT_C-arg member a: int optional=True member b: UserDefOne optional=True diff --git a/tests/qapi-schema/returns-int.out b/tests/qapi-schema/returns-int.out index 1ac3e1e267..a2da259be4 100644 --- a/tests/qapi-schema/returns-int.out +++ b/tests/qapi-schema/returns-int.out @@ -1,2 +1,3 @@ +object :empty command guest-get-time None -> int gen=True success_response=True diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index a2ae78661b..53a769388c 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -19,6 +19,9 @@ #include "test-qapi-types.h" #include "test-qapi-visit.h" #include "qapi/qmp/types.h" +#include "test-qmp-introspect.h" +#include "qmp-introspect.h" +#include "qapi-visit.h" typedef struct TestInputVisitorData { QObject *obj; @@ -62,6 +65,30 @@ Visitor *validate_test_init(TestInputVisitorData *data, return v; } +/* similar to validate_test_init(), but does not expect a string + * literal/format json_string argument and so can be used for + * programatically generated strings (and we can't pass in programatically + * generated strings via %s format parameters since qobject_from_jsonv() + * will wrap those in double-quotes and treat the entire object as a + * string) + */ +static Visitor *validate_test_init_raw(TestInputVisitorData *data, + const char *json_string) +{ + Visitor *v; + + data->obj = qobject_from_json(json_string); + g_assert(data->obj != NULL); + + data->qiv = qmp_input_visitor_new_strict(data->obj); + g_assert(data->qiv != NULL); + + v = qmp_input_get_visitor(data->qiv); + g_assert(v != NULL); + + return v; +} + typedef struct TestStruct { int64_t integer; @@ -293,6 +320,32 @@ static void test_validate_fail_alternate(TestInputVisitorData *data, qapi_free_UserDefAlternate(tmp); } +static void do_test_validate_qmp_introspect(TestInputVisitorData *data, + const char *schema_json) +{ + SchemaInfoList *schema = NULL; + Error *err = NULL; + Visitor *v; + + v = validate_test_init_raw(data, schema_json); + + visit_type_SchemaInfoList(v, &schema, NULL, &err); + if (err) { + fprintf(stderr, "%s", error_get_pretty(err)); + } + g_assert(!err); + g_assert(schema); + + qapi_free_SchemaInfoList(schema); +} + +static void test_validate_qmp_introspect(TestInputVisitorData *data, + const void *unused) +{ + do_test_validate_qmp_introspect(data, test_qmp_schema_json); + do_test_validate_qmp_introspect(data, qmp_schema_json); +} + static void validate_test_add(const char *testpath, TestInputVisitorData *data, void (*test_func)(TestInputVisitorData *data, const void *user_data)) @@ -333,6 +386,8 @@ int main(int argc, char **argv) &testdata, test_validate_fail_alternate); validate_test_add("/visitor/input-strict/fail/union-native-list", &testdata, test_validate_fail_union_native_list); + validate_test_add("/visitor/input-strict/pass/qmp-introspect", + &testdata, test_validate_qmp_introspect); g_test_run(); -- cgit v1.2.3-55-g7522 From 1a9a507b2e3e90aa719c96b4c092e7fad7215f21 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 16 Sep 2015 13:06:29 +0200 Subject: qapi-introspect: Hide type names To eliminate the temptation for clients to look up types by name (which are not ABI), replace all type names by meaningless strings. Reduces output of query-schema by 13 out of 85KiB. As a debugging aid, provide option -u to suppress the hiding. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <1442401589-24189-27-git-send-email-armbru@redhat.com> --- docs/qapi-code-gen.txt | 36 +++++++++++++++++------------------- qapi/introspect.json | 12 ++++-------- scripts/qapi-introspect.py | 41 +++++++++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 33 deletions(-) (limited to 'docs/qapi-code-gen.txt') diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index f321d4b949..b1c8361d22 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -530,13 +530,16 @@ additional variant members depending on the value of meta-type. Each SchemaInfo object describes a wire ABI entity of a certain meta-type: a command, event or one of several kinds of type. -SchemaInfo for entities defined in the QAPI schema have the same name -as in the schema. This is the case for all commands and events, and -most types. +SchemaInfo for commands and events have the same name as in the QAPI +schema. Command and event names are part of the wire ABI, but type names are -not. Therefore, looking up a type by its name in the QAPI schema is -wrong. Look up the command or event, then follow references by name. +not. Therefore, the SchemaInfo for types have auto-generated +meaningless names. For readability, the examples in this section use +meaningful type names instead. + +To examine a type, start with a command or event using it, then follow +references by name. QAPI schema definitions not reachable that way are omitted. @@ -567,8 +570,7 @@ object type without members. The event may not have a data member on the wire then. Each command or event defined with dictionary-valued 'data' in the -QAPI schema implicitly defines an object type called ":obj-NAME-arg", -where NAME is the command or event's name. +QAPI schema implicitly defines an object type. Example: the SchemaInfo for EVENT_C from section Events @@ -623,12 +625,9 @@ Note that base types are "flattened": its members are included in the A simple union implicitly defines an enumeration type for its implicit discriminator (called "type" on the wire, see section Union types). -Such a type's name is made by appending "Kind" to the simple union's -name. A simple union implicitly defines an object type for each of its -variants. The type's name is ":obj-NAME-wrapper", where NAME is the -name of the name of the variant's type. +variants. Example: the SchemaInfo for simple union BlockdevOptions from section Union types @@ -659,8 +658,7 @@ Example: the SchemaInfo for BlockRef from section Alternate types The SchemaInfo for an array type has meta-type "array", and variant member "element-type", which names the array's element type. Array -types are implicitly defined. An array type's name is made by -appending "List" to its element type's name. +types are implicitly defined. Example: the SchemaInfo for ['str'] @@ -1067,13 +1065,13 @@ Example: [Uninteresting stuff omitted...] const char example_qmp_schema_json[] = "[" - "{\"arg-type\": \":empty\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, " + "{\"arg-type\": \"0\", \"meta-type\": \"event\", \"name\": \"MY_EVENT\"}, " + "{\"arg-type\": \"1\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"2\"}, " + "{\"members\": [], \"meta-type\": \"object\", \"name\": \"0\"}, " + "{\"members\": [{\"name\": \"arg1\", \"type\": \"2\"}], \"meta-type\": \"object\", \"name\": \"1\"}, " + "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"2\"}, " "{\"json-type\": \"int\", \"meta-type\": \"builtin\", \"name\": \"int\"}, " - "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}, " - "{\"members\": [], \"meta-type\": \"object\", \"name\": \":empty\"}, " - "{\"members\": [{\"name\": \"arg1\", \"type\": \"UserDefOne\"}], \"meta-type\": \"object\", \"name\": \":obj-my-command-arg\"}, " - "{\"members\": [{\"name\": \"integer\", \"type\": \"int\"}, {\"name\": \"string\", \"type\": \"str\"}], \"meta-type\": \"object\", \"name\": \"UserDefOne\"}, " - "{\"arg-type\": \":obj-my-command-arg\", \"meta-type\": \"command\", \"name\": \"my-command\", \"ret-type\": \"UserDefOne\"}]"; + "{\"json-type\": \"string\", \"meta-type\": \"builtin\", \"name\": \"str\"}]"; $ cat qapi-generated/example-qmp-introspect.h [Uninteresting stuff omitted...] diff --git a/qapi/introspect.json b/qapi/introspect.json index 9c8ad53d7a..cc50dc6bcb 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -75,17 +75,13 @@ # @SchemaInfo # # @name: the entity's name, inherited from @base. -# Entities defined in the QAPI schema have the name defined in -# the schema. Implicitly defined entities have generated -# names. See docs/qapi-code-gen.txt section "Client JSON -# Protocol introspection" for details. +# Commands and events have the name defined in the QAPI schema. +# Unlike command and event names, type names are not part of +# the wire ABI. Consequently, type names are meaningless +# strings here. # # All references to other SchemaInfo are by name. # -# Command and event names are part of the wire ABI, but type names are -# not. Therefore, looking up a type by "well-known" name is wrong. -# Look up the command or event, then follow the references. -# # @meta-type: the entity's meta type, inherited from @base. # # Additional members depend on the value of @meta-type. diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py index 831d6d5944..7d39320174 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi-introspect.py @@ -40,32 +40,37 @@ def to_c_string(string): class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): - def __init__(self): + def __init__(self, unmask): + self._unmask = unmask self.defn = None self.decl = None self._schema = None self._jsons = None self._used_types = None + self._name_map = None def visit_begin(self, schema): self._schema = schema self._jsons = [] self._used_types = [] + self._name_map = {} return QAPISchemaType # don't visit types for now def visit_end(self): # visit the types that are actually used + jsons = self._jsons + self._jsons = [] for typ in self._used_types: typ.visit(self) - self._jsons.sort() # generate C # TODO can generate awfully long lines + jsons.extend(self._jsons) name = prefix + 'qmp_schema_json' self.decl = mcgen(''' extern const char %(c_name)s[]; ''', c_name=c_name(name)) - lines = to_json(self._jsons).split('\n') + lines = to_json(jsons).split('\n') c_string = '\n '.join([to_c_string(line) for line in lines]) self.defn = mcgen(''' const char %(c_name)s[] = %(c_string)s; @@ -75,6 +80,14 @@ const char %(c_name)s[] = %(c_string)s; self._schema = None self._jsons = None self._used_types = None + self._name_map = None + + def _name(self, name): + if self._unmask: + return name + if name not in self._name_map: + self._name_map[name] = '%d' % len(self._name_map) + return self._name_map[name] def _use_type(self, typ): # Map the various integer types to plain int @@ -86,9 +99,16 @@ const char %(c_name)s[] = %(c_string)s; # Add type to work queue if new if typ not in self._used_types: self._used_types.append(typ) - return typ.name + # Clients should examine commands and events, not types. Hide + # type names to reduce the temptation. Also saves a few + # characters. + if isinstance(typ, QAPISchemaBuiltinType): + return typ.name + return self._name(typ.name) def _gen_json(self, name, mtype, obj): + if mtype != 'command' and mtype != 'event' and mtype != 'builtin': + name = self._name(name) obj['name'] = name obj['meta-type'] = mtype self._jsons.append(obj) @@ -140,7 +160,16 @@ const char %(c_name)s[] = %(c_string)s; arg_type = arg_type or self._schema.the_empty_object_type self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) -(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() +# Debugging aid: unmask QAPI schema's type names +# We normally mask them, because they're not QMP wire ABI +opt_unmask = False + +(input_file, output_dir, do_c, do_h, prefix, opts) = \ + parse_command_line("u", ["unmask-non-abi-names"]) + +for o, a in opts: + if o in ("-u", "--unmask-non-abi-names"): + opt_unmask = True c_comment = ''' /* @@ -176,7 +205,7 @@ fdef.write(mcgen(''' prefix=prefix)) schema = QAPISchema(input_file) -gen = QAPISchemaGenIntrospectVisitor() +gen = QAPISchemaGenIntrospectVisitor(opt_unmask) schema.visit(gen) fdef.write(gen.defn) fdecl.write(gen.decl) -- cgit v1.2.3-55-g7522