diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index 61b5be47fb..ff16df2456 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -163,7 +163,7 @@ The QAPI schema definitions can be modularized using the 'include' directive: The directive is evaluated recursively, and include paths are relative to the file using the directive. Multiple includes of the same file are -safe. No other keys should appear in the expression, and the include +idempotent. No other keys should appear in the expression, and the include value should be a string. As a matter of style, it is a good idea to have all files be @@ -300,7 +300,6 @@ an implicit C enum 'NameKind' is created, corresponding to the union the union can be named 'max', as this would collide with the implicit enum. The value for each branch can be of any type. - A flat union definition specifies a struct as its base, and avoids nesting on the wire. All branches of the union must be complex types, and the top-level fields of the union dictionary on @@ -314,7 +313,7 @@ adding a common field 'readonly', renaming the discriminator to something more applicable, and reducing the number of {} required on the wire: - { 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] } + { 'enum': 'BlockdevDriver', 'data': [ 'file', 'qcow2' ] } { 'struct': 'BlockdevCommonOptions', 'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } } { 'union': 'BlockdevOptions', @@ -350,7 +349,7 @@ is identical on the wire to: { 'struct': 'Base', 'data': { 'type': 'Enum' } } { 'struct': 'Branch1', 'data': { 'data': 'str' } } { 'struct': 'Branch2', 'data': { 'data': 'int' } } - { 'union': 'Flat': 'base': 'Base', 'discriminator': 'type', + { 'union': 'Flat', 'base': 'Base', 'discriminator': 'type', 'data': { 'one': 'Branch1', 'two': 'Branch2' } } @@ -394,7 +393,7 @@ following example objects: === Commands === Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, - '*returns': TYPE-NAME-OR-DICT, + '*returns': TYPE-NAME, '*gen': false, '*success-response': false } Commands are defined by using a dictionary containing several members, @@ -405,10 +404,9 @@ Client JSON Protocol command exchange. The 'data' argument maps to the "arguments" dictionary passed in as part of a Client JSON Protocol command. The 'data' member is optional and defaults to {} (an empty dictionary). If present, it must be the -string name of a complex type, a one-element array containing the name -of a complex type, or a dictionary that declares an anonymous type -with the same semantics as a 'struct' expression, with one exception -noted below when 'gen' is used. +string name of a complex type, or a dictionary that declares an +anonymous type with the same semantics as a 'struct' expression, with +one exception noted below when 'gen' is used. The 'returns' member describes what will appear in the "return" field of a Client JSON Protocol reply on successful completion of a command. @@ -416,14 +414,13 @@ The member is optional from the command declaration; if absent, the "return" field will be an empty dictionary. If 'returns' is present, it must be the string name of a complex or built-in type, a one-element array containing the name of a complex or built-in type, -or a dictionary that declares an anonymous type with the same -semantics as a 'struct' expression, with one exception noted below -when 'gen' is used. Although it is permitted to have the 'returns' -member name a built-in type or an array of built-in types, any command -that does this cannot be extended to return additional information in -the future; thus, new commands should strongly consider returning a -dictionary-based type or an array of dictionaries, even if the -dictionary only contains one field at the present. +with one exception noted below when 'gen' is used. Although it is +permitted to have the 'returns' member name a built-in type or an +array of built-in types, any command that does this cannot be extended +to return additional information in the future; thus, new commands +should strongly consider returning a dictionary-based type or an array +of dictionaries, even if the dictionary only contains one field at the +present. All commands in Client JSON Protocol use a dictionary to report failure, with no way to specify that in QAPI. Where the error return @@ -555,6 +552,7 @@ Example: qapi_dealloc_visitor_cleanup(md); } + void qapi_free_UserDefOne(UserDefOne *obj) { QapiDeallocVisitor *md; @@ -569,7 +567,6 @@ Example: visit_type_UserDefOne(v, &obj, NULL, NULL); qapi_dealloc_visitor_cleanup(md); } - $ cat qapi-generated/example-qapi-types.h [Uninteresting stuff omitted...] @@ -580,8 +577,7 @@ Example: typedef struct UserDefOne UserDefOne; - typedef struct UserDefOneList - { + typedef struct UserDefOneList { union { UserDefOne *value; uint64_t padding; @@ -589,10 +585,10 @@ Example: struct UserDefOneList *next; } UserDefOneList; + [Functions on built-in types omitted...] - struct UserDefOne - { + struct UserDefOne { int64_t integer; char *string; }; @@ -630,6 +626,7 @@ Example: static void visit_type_UserDefOne_fields(Visitor *m, UserDefOne **obj, Error **errp) { Error *err = NULL; + visit_type_int(m, &(*obj)->integer, "integer", &err); if (err) { goto out; @@ -743,7 +740,7 @@ Example: static void qmp_marshal_input_my_command(QDict *args, QObject **ret, Error **errp) { Error *local_err = NULL; - UserDefOne *retval = NULL; + UserDefOne *retval; QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; Visitor *v; @@ -769,7 +766,6 @@ Example: v = qapi_dealloc_get_visitor(md); visit_type_UserDefOne(v, &arg1, "arg1", NULL); qapi_dealloc_visitor_cleanup(md); - return; } static void qmp_init_marshal(void) @@ -826,7 +822,7 @@ Example: QDECREF(qmp); } - const char *EXAMPLE_QAPIEvent_lookup[] = { + const char *example_QAPIEvent_lookup[] = { "MY_EVENT", NULL, }; @@ -843,11 +839,10 @@ Example: void qapi_event_send_my_event(Error **errp); - extern const char *EXAMPLE_QAPIEvent_lookup[]; - typedef enum EXAMPLE_QAPIEvent - { + extern const char *example_QAPIEvent_lookup[]; + typedef enum example_QAPIEvent { EXAMPLE_QAPI_EVENT_MY_EVENT = 0, EXAMPLE_QAPI_EVENT_MAX = 1, - } EXAMPLE_QAPIEvent; + } example_QAPIEvent; #endif diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index ca22acc1d5..890ce5db92 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -27,18 +27,19 @@ def generate_command_decl(name, args, ret_type): %(ret_type)s qmp_%(name)s(%(args)sError **errp); ''', ret_type=c_type(ret_type), name=c_name(name), - args=arglist).strip() + args=arglist) -def gen_err_check(errvar): - if errvar: - return mcgen(''' -if (local_err) { +def gen_err_check(err): + if not err: + return '' + return mcgen(''' +if (%(err)s) { goto out; } -''') - return '' +''', + err=err) -def gen_sync_call(name, args, ret_type, indent=0): +def gen_sync_call(name, args, ret_type): ret = "" arglist="" retval="" @@ -48,41 +49,34 @@ def gen_sync_call(name, args, ret_type, indent=0): if optional: arglist += "has_%s, " % c_name(argname) arglist += "%s, " % (c_name(argname)) - push_indent(indent) + push_indent() ret = mcgen(''' %(retval)sqmp_%(name)s(%(args)s&local_err); - ''', - name=c_name(name), args=arglist, retval=retval).rstrip() + name=c_name(name), args=arglist, retval=retval) if ret_type: - ret += "\n" + gen_err_check('local_err') - ret += "\n" + mcgen('''' -%(marshal_output_call)s + ret += gen_err_check('local_err') + ret += mcgen(''' + +qmp_marshal_output_%(c_name)s(retval, ret, &local_err); ''', - marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() - pop_indent(indent) - return ret.rstrip() + c_name=c_name(name)) + pop_indent() + return ret - -def gen_marshal_output_call(name, ret_type): - if not ret_type: - return "" - return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_name(name) - -def gen_visitor_input_containers_decl(args, obj): +def gen_visitor_input_containers_decl(args): ret = "" push_indent() if len(args) > 0: ret += mcgen(''' -QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); +QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args)); QapiDeallocVisitor *md; Visitor *v; -''', - obj=obj) +''') pop_indent() - return ret.rstrip() + return ret def gen_visitor_input_vars_decl(args): ret = "" @@ -105,7 +99,7 @@ bool has_%(argname)s = false; argname=c_name(argname), argtype=c_type(argtype)) pop_indent() - return ret.rstrip() + return ret def gen_visitor_input_block(args, dealloc=False): ret = "" @@ -159,9 +153,9 @@ visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); qapi_dealloc_visitor_cleanup(md); ''') pop_indent() - return ret.rstrip() + return ret -def gen_marshal_output(name, args, ret_type, middle_mode): +def gen_marshal_output(name, ret_type): if not ret_type: return "" @@ -194,14 +188,14 @@ out: return ret -def gen_marshal_input_decl(name, args, ret_type, middle_mode): +def gen_marshal_input_decl(name, middle_mode): ret = 'void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) if not middle_mode: ret = "static " + ret return ret def gen_marshal_input(name, args, ret_type, middle_mode): - hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) + hdr = gen_marshal_input_decl(name, middle_mode) ret = mcgen(''' %(header)s @@ -211,36 +205,24 @@ def gen_marshal_input(name, args, ret_type, middle_mode): header=hdr) if ret_type: - if is_c_ptr(ret_type): - retval = " %s retval = NULL;" % c_type(ret_type) - else: - retval = " %s retval;" % c_type(ret_type) ret += mcgen(''' -%(retval)s + %(c_type)s retval; ''', - retval=retval) + c_type=c_type(ret_type)) if len(args) > 0: - ret += mcgen(''' -%(visitor_input_containers_decl)s -%(visitor_input_vars_decl)s - -%(visitor_input_block)s - -''', - visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), - visitor_input_vars_decl=gen_visitor_input_vars_decl(args), - visitor_input_block=gen_visitor_input_block(args)) + ret += gen_visitor_input_containers_decl(args) + ret += gen_visitor_input_vars_decl(args) + '\n' + ret += gen_visitor_input_block(args) + '\n' else: ret += mcgen(''' (void)args; + ''') - ret += mcgen(''' -%(sync_call)s -''', - sync_call=gen_sync_call(name, args, ret_type, indent=4)) + ret += gen_sync_call(name, args, ret_type) + if re.search('^ *goto out\\;', ret, re.MULTILINE): ret += mcgen(''' @@ -248,11 +230,11 @@ out: ''') ret += mcgen(''' error_propagate(errp, local_err); -%(visitor_input_block_cleanup)s +''') + ret += gen_visitor_input_block(args, dealloc=True) + ret += mcgen(''' } -''', - visitor_input_block_cleanup=gen_visitor_input_block(args, - dealloc=True)) +''') return ret def gen_registry(commands): @@ -272,12 +254,13 @@ qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); ret = mcgen(''' static void qmp_init_marshal(void) { -%(registry)s +''') + ret += registry + ret += mcgen(''' } qapi_init(qmp_init_marshal); -''', - registry=registry.rstrip()) +''') return ret middle_mode = False @@ -357,14 +340,14 @@ for cmd in commands: arglist = cmd['data'] if cmd.has_key('returns'): ret_type = cmd['returns'] - ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" + ret = generate_command_decl(cmd['command'], arglist, ret_type) fdecl.write(ret) if ret_type: - ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" + ret = gen_marshal_output(cmd['command'], ret_type) + "\n" fdef.write(ret) if middle_mode: - fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) + fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], middle_mode)) ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" fdef.write(ret) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 56bc602a6d..7f238df5b2 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -167,8 +167,7 @@ extern const char *%(event_enum_name)s_lookup[]; event_enum_name = event_enum_name) enum_decl = mcgen(''' -typedef enum %(event_enum_name)s -{ +typedef enum %(event_enum_name)s { ''', event_enum_name = event_enum_name) @@ -199,7 +198,6 @@ const char *%(event_enum_name)s_lookup[] = { ''', event_enum_name = event_enum_name) - i = 0 for string in event_enum_strings: ret += mcgen(''' "%(string)s", @@ -267,7 +265,7 @@ fdecl.write(mcgen(''' exprs = parse_schema(input_file) -event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" +event_enum_name = c_name(prefix + "QAPIEvent", protect=False) event_enum_values = [] event_enum_strings = [] diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index e6eb4b613a..f2428f3807 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -15,8 +15,7 @@ from qapi import * def generate_fwd_builtin(name): return mcgen(''' -typedef struct %(name)sList -{ +typedef struct %(name)sList { union { %(type)s value; uint64_t padding; @@ -32,8 +31,7 @@ def generate_fwd_struct(name): typedef struct %(name)s %(name)s; -typedef struct %(name)sList -{ +typedef struct %(name)sList { union { %(name)s *value; uint64_t padding; @@ -45,8 +43,8 @@ typedef struct %(name)sList def generate_fwd_enum_struct(name): return mcgen(''' -typedef struct %(name)sList -{ + +typedef struct %(name)sList { union { %(name)s value; uint64_t padding; @@ -79,8 +77,8 @@ def generate_struct(expr): base = expr.get('base') ret = mcgen(''' -struct %(name)s -{ + +struct %(name)s { ''', name=c_name(structname)) @@ -105,10 +103,10 @@ struct %(name)s def generate_enum_lookup(name, values): ret = mcgen(''' -const char * const %(name)s_lookup[] = { + +const char *const %(name)s_lookup[] = { ''', name=c_name(name)) - i = 0 for value in values: index = c_enum_const(name, value) ret += mcgen(''' @@ -120,7 +118,6 @@ const char * const %(name)s_lookup[] = { ret += mcgen(''' [%(max_index)s] = NULL, }; - ''', max_index=max_index) return ret @@ -128,13 +125,14 @@ const char * const %(name)s_lookup[] = { def generate_enum(name, values): name = c_name(name) lookup_decl = mcgen(''' -extern const char * const %(name)s_lookup[]; + +extern const char *const %(name)s_lookup[]; ''', name=name) enum_decl = mcgen(''' -typedef enum %(name)s -{ + +typedef enum %(name)s { ''', name=name) @@ -156,7 +154,7 @@ typedef enum %(name)s ''', name=name) - return lookup_decl + enum_decl + return enum_decl + lookup_decl def generate_alternate_qtypes(expr): @@ -164,6 +162,7 @@ def generate_alternate_qtypes(expr): members = expr['data'] ret = mcgen(''' + const int %(name)s_qtypes[QTYPE_MAX] = { ''', name=c_name(name)) @@ -199,14 +198,40 @@ def generate_union(expr, meta): discriminator_type_name = '%sKind' % (name) ret = mcgen(''' -struct %(name)s -{ + +struct %(name)s { +''', + name=name) + if base: + ret += mcgen(''' + /* Members inherited from %(c_name)s: */ +''', + c_name=c_name(base)) + base_fields = find_struct(base)['data'] + ret += generate_struct_fields(base_fields) + ret += mcgen(''' + /* Own members: */ +''') + else: + assert not discriminator + ret += mcgen(''' %(discriminator_type_name)s kind; - union { +''', + discriminator_type_name=c_name(discriminator_type_name)) + + # FIXME: What purpose does data serve, besides preventing a union that + # has a branch named 'data'? We use it in qapi-visit.py to decide + # whether to bypass the switch statement if visiting the discriminator + # failed; but since we 0-initialize structs, and cannot tell what + # branch of the union is in use if the discriminator is invalid, there + # should not be any data leaks even without a data pointer. Or, if + # 'data' is merely added to guarantee we don't have an empty union, + # shouldn't we enforce that at .json parse time? + ret += mcgen(''' + union { /* union tag is @%(c_name)s */ void *data; ''', - name=name, - discriminator_type_name=c_name(discriminator_type_name)) + c_name=c_name(discriminator or 'kind')) for key in typeinfo: ret += mcgen(''' @@ -217,17 +242,6 @@ struct %(name)s ret += mcgen(''' }; -''') - - if base: - assert discriminator - base_fields = find_struct(base)['data'].copy() - del base_fields[discriminator] - ret += generate_struct_fields(base_fields) - else: - assert not discriminator - - ret += mcgen(''' }; ''') if meta == 'alternate': @@ -314,14 +328,12 @@ fdef.write(mcgen(''' #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" - ''', prefix=prefix)) fdecl.write(mcgen(''' #include #include - ''')) exprs = parse_schema(input_file) @@ -332,22 +344,22 @@ for typename in builtin_types.keys(): fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL")) for expr in exprs: - ret = "\n" + ret = "" if expr.has_key('struct'): ret += generate_fwd_struct(expr['struct']) elif expr.has_key('enum'): - ret += generate_enum(expr['enum'], expr['data']) + "\n" + ret += generate_enum(expr['enum'], expr['data']) ret += generate_fwd_enum_struct(expr['enum']) fdef.write(generate_enum_lookup(expr['enum'], expr['data'])) elif expr.has_key('union'): - ret += generate_fwd_struct(expr['union']) + "\n" + ret += generate_fwd_struct(expr['union']) enum_define = discriminator_find_enum_define(expr) if not enum_define: ret += generate_enum('%sKind' % expr['union'], expr['data'].keys()) fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys())) elif expr.has_key('alternate'): - ret += generate_fwd_struct(expr['alternate']) + "\n" + ret += generate_fwd_struct(expr['alternate']) ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys()) fdef.write(generate_enum_lookup('%sKind' % expr['alternate'], expr['data'].keys())) @@ -367,34 +379,32 @@ fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL")) # have the functions defined, so we use -b option to provide control # over these cases if do_builtins: - fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) for typename in builtin_types.keys(): fdef.write(generate_type_cleanup(typename + "List")) - fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF")) for expr in exprs: - ret = "\n" + ret = "" if expr.has_key('struct'): ret += generate_struct(expr) + "\n" ret += generate_type_cleanup_decl(expr['struct'] + "List") - fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['struct'] + "List")) ret += generate_type_cleanup_decl(expr['struct']) - fdef.write(generate_type_cleanup(expr['struct']) + "\n") + fdef.write(generate_type_cleanup(expr['struct'])) elif expr.has_key('union'): - ret += generate_union(expr, 'union') + ret += generate_union(expr, 'union') + "\n" ret += generate_type_cleanup_decl(expr['union'] + "List") - fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['union'] + "List")) ret += generate_type_cleanup_decl(expr['union']) - fdef.write(generate_type_cleanup(expr['union']) + "\n") + fdef.write(generate_type_cleanup(expr['union'])) elif expr.has_key('alternate'): - ret += generate_union(expr, 'alternate') + ret += generate_union(expr, 'alternate') + "\n" ret += generate_type_cleanup_decl(expr['alternate'] + "List") - fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n") + fdef.write(generate_type_cleanup(expr['alternate'] + "List")) ret += generate_type_cleanup_decl(expr['alternate']) - fdef.write(generate_type_cleanup(expr['alternate']) + "\n") + fdef.write(generate_type_cleanup(expr['alternate'])) elif expr.has_key('enum'): - ret += generate_type_cleanup_decl(expr['enum'] + "List") - fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n") + ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List") + fdef.write(generate_type_cleanup(expr['enum'] + "List")) else: continue fdecl.write(ret) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 5b99336488..3cd662bd6b 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -16,14 +16,23 @@ from ordereddict import OrderedDict from qapi import * import re -implicit_structs = [] +implicit_structs_seen = set() +struct_fields_seen = set() def generate_visit_implicit_struct(type): - global implicit_structs - if type in implicit_structs: + if type in implicit_structs_seen: return '' - implicit_structs.append(type) - return mcgen(''' + implicit_structs_seen.add(type) + ret = '' + if type 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_name(type)) + + ret += mcgen(''' static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp) { @@ -38,9 +47,11 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error * } ''', c_type=type_name(type)) + return ret def generate_visit_struct_fields(name, members, base = None): - substructs = [] + struct_fields_seen.add(name) + ret = '' if base: @@ -51,6 +62,7 @@ def generate_visit_struct_fields(name, members, base = None): static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp) { Error *err = NULL; + ''', name=c_name(name)) push_indent() @@ -103,7 +115,11 @@ out: return ret -def generate_visit_struct_body(name, members): +def generate_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 + # call qapi_free_FOO() to avoid a memory leak of the partial FOO. ret = mcgen(''' Error *err = NULL; @@ -135,14 +151,14 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e ''', name=c_name(name)) - ret += generate_visit_struct_body(name, members) + ret += generate_visit_struct_body(name) ret += mcgen(''' } ''') return ret -def generate_visit_list(name, members): +def generate_visit_list(name): return mcgen(''' void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp) @@ -171,15 +187,15 @@ out: ''', name=type_name(name)) -def generate_visit_enum(name, members): +def generate_visit_enum(name): 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) { - visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp); + visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp); } ''', - name=c_name(name)) + c_name=c_name(name), name=name) def generate_visit_alternate(name, members): ret = mcgen(''' @@ -252,7 +268,7 @@ def generate_visit_union(expr): else: # There will always be a discriminator in the C switch code, by default # it is an enum type generated silently - ret = generate_visit_enum(name + 'Kind', members.keys()) + ret = generate_visit_enum(name + 'Kind') disc_type = c_name(name) + 'Kind' if base: @@ -267,17 +283,17 @@ def generate_visit_union(expr): 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_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); + visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err); if (err) { goto out; } if (*obj) { ''', - name=c_name(name)) + c_name=c_name(name), name=name) if base: ret += mcgen(''' @@ -289,20 +305,23 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e name=c_name(name)) if not discriminator: + tag = 'kind' disc_key = "type" else: + tag = discriminator disc_key = discriminator ret += mcgen(''' - visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); + visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err); if (err) { goto out_obj; } if (!visit_start_union(m, !!(*obj)->data, &err) || err) { goto out_obj; } - switch ((*obj)->kind) { + switch ((*obj)->%(c_tag)s) { ''', disc_type = disc_type, + c_tag=c_name(tag), disc_key = disc_key) for key in members: @@ -340,7 +359,7 @@ out: return ret -def generate_declaration(name, members, builtin_type=False): +def generate_declaration(name, builtin_type=False): ret = "" if not builtin_type: name = c_name(name) @@ -357,7 +376,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E return ret -def generate_enum_declaration(name, members): +def generate_enum_declaration(name): ret = mcgen(''' void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); ''', @@ -365,7 +384,7 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E return ret -def generate_decl_enum(name, members): +def generate_decl_enum(name): return mcgen(''' void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); @@ -433,7 +452,7 @@ exprs = parse_schema(input_file) # for built-in types in our header files and simply guard them fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) for typename in builtin_types.keys(): - fdecl.write(generate_declaration(typename, None, builtin_type=True)) + fdecl.write(generate_declaration(typename, builtin_type=True)) fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # ...this doesn't work for cases where we link in multiple objects that @@ -441,44 +460,42 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) # over these cases if do_builtins: for typename in builtin_types.keys(): - fdef.write(generate_visit_list(typename, None)) + fdef.write(generate_visit_list(typename)) for expr in exprs: if expr.has_key('struct'): ret = generate_visit_struct(expr) - ret += generate_visit_list(expr['struct'], expr['data']) + ret += generate_visit_list(expr['struct']) fdef.write(ret) - ret = generate_declaration(expr['struct'], expr['data']) + ret = generate_declaration(expr['struct']) fdecl.write(ret) elif expr.has_key('union'): ret = generate_visit_union(expr) - ret += generate_visit_list(expr['union'], expr['data']) + ret += generate_visit_list(expr['union']) fdef.write(ret) enum_define = discriminator_find_enum_define(expr) ret = "" if not enum_define: - ret = generate_decl_enum('%sKind' % expr['union'], - expr['data'].keys()) - ret += generate_declaration(expr['union'], expr['data']) + ret = generate_decl_enum('%sKind' % expr['union']) + ret += generate_declaration(expr['union']) fdecl.write(ret) elif expr.has_key('alternate'): ret = generate_visit_alternate(expr['alternate'], expr['data']) - ret += generate_visit_list(expr['alternate'], expr['data']) + ret += generate_visit_list(expr['alternate']) fdef.write(ret) - ret = generate_decl_enum('%sKind' % expr['alternate'], - expr['data'].keys()) - ret += generate_declaration(expr['alternate'], expr['data']) + ret = generate_decl_enum('%sKind' % expr['alternate']) + ret += generate_declaration(expr['alternate']) fdecl.write(ret) elif expr.has_key('enum'): - ret = generate_visit_list(expr['enum'], expr['data']) - ret += generate_visit_enum(expr['enum'], expr['data']) + ret = generate_visit_list(expr['enum']) + ret += generate_visit_enum(expr['enum']) fdef.write(ret) - ret = generate_decl_enum(expr['enum'], expr['data']) - ret += generate_enum_declaration(expr['enum'], expr['data']) + ret = generate_decl_enum(expr['enum']) + ret += generate_enum_declaration(expr['enum']) fdecl.write(ret) close_output(fdef, fdecl) diff --git a/scripts/qapi.py b/scripts/qapi.py index 06d7fc2848..817d824bea 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -341,6 +341,8 @@ def discriminator_find_enum_define(expr): return find_enum(discriminator_type) +# FIXME should enforce "other than downstream extensions [...], all +# names should begin with a letter". valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$') def check_name(expr_info, source, name, allow_optional = False, enum_member = False): @@ -367,6 +369,8 @@ def check_name(expr_info, source, name, allow_optional = False, def add_name(name, info, meta, implicit = False): global all_names check_name(info, "'%s'" % meta, name) + # FIXME should reject names that differ only in '_' vs. '.' + # vs. '-', because they're liable to clash in generated C. if name in all_names: raise QAPIExprError(info, "%s '%s' is already defined" @@ -422,7 +426,6 @@ def check_type(expr_info, source, value, allow_array = False, allow_dict = False, allow_optional = False, allow_star = False, allow_metas = []): global all_names - orig_value = value if value is None: return @@ -440,7 +443,6 @@ def check_type(expr_info, source, value, allow_array = False, "%s: array type must contain single type name" % source) value = value[0] - orig_value = "array of %s" %value # Check if type name for value is okay if isinstance(value, str): @@ -451,20 +453,22 @@ def check_type(expr_info, source, value, allow_array = False, if not value in all_names: raise QAPIExprError(expr_info, "%s uses unknown type '%s'" - % (source, orig_value)) + % (source, value)) if not all_names[value] in allow_metas: raise QAPIExprError(expr_info, "%s cannot use %s type '%s'" - % (source, all_names[value], orig_value)) + % (source, all_names[value], value)) return - # value is a dictionary, check that each member is okay - if not isinstance(value, OrderedDict): - raise QAPIExprError(expr_info, - "%s should be a dictionary" % source) if not allow_dict: raise QAPIExprError(expr_info, "%s should be a type name" % source) + + if not isinstance(value, OrderedDict): + raise QAPIExprError(expr_info, + "%s should be a dictionary or type name" % source) + + # value is a dictionary, check that each member is okay for (key, arg) in value.items(): check_name(expr_info, "Member of %s" % source, key, allow_optional=allow_optional) @@ -495,26 +499,25 @@ def check_command(expr, expr_info): check_type(expr_info, "'data' for command '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct'], allow_star=allow_star) + allow_metas=['struct'], allow_star=allow_star) 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_dict=True, + expr.get('returns'), allow_array=True, allow_optional=True, allow_metas=returns_meta, allow_star=allow_star) def check_event(expr, expr_info): global events name = expr['event'] - params = expr.get('data') if name.upper() == 'MAX': raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created") events.append(name) check_type(expr_info, "'data' for event '%s'" % name, expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['union', 'struct']) + allow_metas=['struct']) def check_union(expr, expr_info): name = expr['union'] @@ -523,14 +526,6 @@ def check_union(expr, expr_info): members = expr['data'] values = { 'MAX': '(automatic)' } - # If the object has a member 'base', its value must name a struct, - # and there must be a discriminator. - if base is not None: - if discriminator is None: - raise QAPIExprError(expr_info, - "Union '%s' requires a discriminator to go " - "along with base" %name) - # Two types of unions, determined by discriminator. # With no discriminator it is a simple union. @@ -943,24 +938,24 @@ def pop_indent(indent_amount=4): global indent_level indent_level -= indent_amount +# Generate @code with @kwds interpolated. +# Obey indent_level, and strip eatspace. def cgen(code, **kwds): - indent = genindent(indent_level) - lines = code.split('\n') - lines = map(lambda x: indent + x, lines) - return '\n'.join(lines) % kwds + '\n' - -def mcgen(code, **kwds): - raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds) + raw = code % kwds + if indent_level: + indent = genindent(indent_level) + raw = re.subn("^.", indent + r'\g<0>', raw, 0, re.MULTILINE) + raw = raw[0] return re.sub(re.escape(eatspace) + ' *', '', raw) -def basename(filename): - return filename.split("/")[-1] +def mcgen(code, **kwds): + if code[0] == '\n': + code = code[1:] + return cgen(code, **kwds) + def guardname(filename): - guard = basename(filename).rsplit(".", 1)[0] - for substr in [".", " ", "-"]: - guard = guard.replace(substr, "_") - return guard.upper() + '_H' + return c_name(filename, protect=False).upper() def guardstart(name): return mcgen(''' @@ -1003,6 +998,12 @@ def parse_command_line(extra_options = "", extra_long_options = []): for oa in opts: o, a = oa if o in ("-p", "--prefix"): + match = re.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) + if match.end() != len(a): + print >>sys.stderr, \ + "%s: 'funny character '%s' in argument of --prefix" \ + % (sys.argv[0], a[match.end()]) + sys.exit(1) prefix = a elif o in ("-o", "--output-dir"): output_dir = a + "/" @@ -1030,14 +1031,16 @@ def parse_command_line(extra_options = "", extra_long_options = []): def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, c_comment, h_comment): + guard = guardname(prefix + h_file) c_file = output_dir + prefix + c_file h_file = output_dir + prefix + h_file - try: - os.makedirs(output_dir) - except os.error, e: - if e.errno != errno.EEXIST: - raise + if output_dir: + try: + os.makedirs(output_dir) + except os.error, e: + if e.errno != errno.EEXIST: + raise def maybe_open(really, name, opt): if really: @@ -1062,7 +1065,7 @@ def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, #define %(guard)s ''', - comment = h_comment, guard = guardname(h_file))) + comment = h_comment, guard = guard)) return (fdef, fdecl) diff --git a/tests/Makefile b/tests/Makefile index 52711237ca..b128e288e4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -229,13 +229,17 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ 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 \ - data-array-empty.json data-array-unknown.json data-int.json \ - data-unknown.json data-member-unknown.json data-member-array.json \ - data-member-array-bad.json returns-array-bad.json returns-int.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 \ + args-member-array-bad.json args-alternate.json args-union.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 \ - nested-struct-data.json nested-struct-returns.json non-objects.json \ + struct-data-invalid.json struct-member-invalid.json \ + nested-struct-data.json non-objects.json \ qapi-schema-test.json quoted-structural-chars.json \ + leading-comma-list.json leading-comma-object.json \ trailing-comma-list.json trailing-comma-object.json \ unclosed-list.json unclosed-object.json unclosed-string.json \ duplicate-key.json union-invalid-base.json union-bad-branch.json \ diff --git a/tests/qapi-schema/args-alternate.err b/tests/qapi-schema/args-alternate.err new file mode 100644 index 0000000000..3086eae56b --- /dev/null +++ b/tests/qapi-schema/args-alternate.err @@ -0,0 +1 @@ +tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt' diff --git a/tests/qapi-schema/data-array-empty.exit b/tests/qapi-schema/args-alternate.exit similarity index 100% rename from tests/qapi-schema/data-array-empty.exit rename to tests/qapi-schema/args-alternate.exit diff --git a/tests/qapi-schema/args-alternate.json b/tests/qapi-schema/args-alternate.json new file mode 100644 index 0000000000..69e94d4819 --- /dev/null +++ b/tests/qapi-schema/args-alternate.json @@ -0,0 +1,3 @@ +# we do not allow alternate arguments +{ 'alternate': 'Alt', 'data': { 'case1': 'int', 'case2': 'str' } } +{ 'command': 'oops', 'data': 'Alt' } diff --git a/tests/qapi-schema/data-array-empty.out b/tests/qapi-schema/args-alternate.out similarity index 100% rename from tests/qapi-schema/data-array-empty.out rename to tests/qapi-schema/args-alternate.out diff --git a/tests/qapi-schema/data-array-empty.err b/tests/qapi-schema/args-array-empty.err similarity index 50% rename from tests/qapi-schema/data-array-empty.err rename to tests/qapi-schema/args-array-empty.err index f713f14893..cb7ed33b3f 100644 --- a/tests/qapi-schema/data-array-empty.err +++ b/tests/qapi-schema/args-array-empty.err @@ -1 +1 @@ -tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name +tests/qapi-schema/args-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name diff --git a/tests/qapi-schema/data-array-unknown.exit b/tests/qapi-schema/args-array-empty.exit similarity index 100% rename from tests/qapi-schema/data-array-unknown.exit rename to tests/qapi-schema/args-array-empty.exit diff --git a/tests/qapi-schema/data-array-empty.json b/tests/qapi-schema/args-array-empty.json similarity index 100% rename from tests/qapi-schema/data-array-empty.json rename to tests/qapi-schema/args-array-empty.json diff --git a/tests/qapi-schema/data-array-unknown.out b/tests/qapi-schema/args-array-empty.out similarity index 100% rename from tests/qapi-schema/data-array-unknown.out rename to tests/qapi-schema/args-array-empty.out diff --git a/tests/qapi-schema/args-array-unknown.err b/tests/qapi-schema/args-array-unknown.err new file mode 100644 index 0000000000..cd7a0f98d7 --- /dev/null +++ b/tests/qapi-schema/args-array-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/data-int.exit b/tests/qapi-schema/args-array-unknown.exit similarity index 100% rename from tests/qapi-schema/data-int.exit rename to tests/qapi-schema/args-array-unknown.exit diff --git a/tests/qapi-schema/data-array-unknown.json b/tests/qapi-schema/args-array-unknown.json similarity index 100% rename from tests/qapi-schema/data-array-unknown.json rename to tests/qapi-schema/args-array-unknown.json diff --git a/tests/qapi-schema/data-int.out b/tests/qapi-schema/args-array-unknown.out similarity index 100% rename from tests/qapi-schema/data-int.out rename to tests/qapi-schema/args-array-unknown.out diff --git a/tests/qapi-schema/args-int.err b/tests/qapi-schema/args-int.err new file mode 100644 index 0000000000..dc1d2504ff --- /dev/null +++ b/tests/qapi-schema/args-int.err @@ -0,0 +1 @@ +tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int' diff --git a/tests/qapi-schema/data-member-array-bad.exit b/tests/qapi-schema/args-int.exit similarity index 100% rename from tests/qapi-schema/data-member-array-bad.exit rename to tests/qapi-schema/args-int.exit diff --git a/tests/qapi-schema/data-int.json b/tests/qapi-schema/args-int.json similarity index 100% rename from tests/qapi-schema/data-int.json rename to tests/qapi-schema/args-int.json diff --git a/tests/qapi-schema/data-member-array-bad.out b/tests/qapi-schema/args-int.out similarity index 100% rename from tests/qapi-schema/data-member-array-bad.out rename to tests/qapi-schema/args-int.out diff --git a/tests/qapi-schema/args-invalid.err b/tests/qapi-schema/args-invalid.err new file mode 100644 index 0000000000..fe1e94975b --- /dev/null +++ b/tests/qapi-schema/args-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be a dictionary or type name diff --git a/tests/qapi-schema/data-member-unknown.exit b/tests/qapi-schema/args-invalid.exit similarity index 100% rename from tests/qapi-schema/data-member-unknown.exit rename to tests/qapi-schema/args-invalid.exit diff --git a/tests/qapi-schema/args-invalid.json b/tests/qapi-schema/args-invalid.json new file mode 100644 index 0000000000..db0981341b --- /dev/null +++ b/tests/qapi-schema/args-invalid.json @@ -0,0 +1,2 @@ +{ 'command': 'foo', + 'data': false } diff --git a/tests/qapi-schema/data-member-array.err b/tests/qapi-schema/args-invalid.out similarity index 100% rename from tests/qapi-schema/data-member-array.err rename to tests/qapi-schema/args-invalid.out diff --git a/tests/qapi-schema/data-member-array-bad.err b/tests/qapi-schema/args-member-array-bad.err similarity index 52% rename from tests/qapi-schema/data-member-array-bad.err rename to tests/qapi-schema/args-member-array-bad.err index 2c072d5986..881b4d954f 100644 --- a/tests/qapi-schema/data-member-array-bad.err +++ b/tests/qapi-schema/args-member-array-bad.err @@ -1 +1 @@ -tests/qapi-schema/data-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name +tests/qapi-schema/args-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name diff --git a/tests/qapi-schema/data-unknown.exit b/tests/qapi-schema/args-member-array-bad.exit similarity index 100% rename from tests/qapi-schema/data-unknown.exit rename to tests/qapi-schema/args-member-array-bad.exit diff --git a/tests/qapi-schema/data-member-array-bad.json b/tests/qapi-schema/args-member-array-bad.json similarity index 100% rename from tests/qapi-schema/data-member-array-bad.json rename to tests/qapi-schema/args-member-array-bad.json diff --git a/tests/qapi-schema/data-member-unknown.out b/tests/qapi-schema/args-member-array-bad.out similarity index 100% rename from tests/qapi-schema/data-member-unknown.out rename to tests/qapi-schema/args-member-array-bad.out diff --git a/tests/qapi-schema/data-unknown.out b/tests/qapi-schema/args-member-array.err similarity index 100% rename from tests/qapi-schema/data-unknown.out rename to tests/qapi-schema/args-member-array.err diff --git a/tests/qapi-schema/data-member-array.exit b/tests/qapi-schema/args-member-array.exit similarity index 100% rename from tests/qapi-schema/data-member-array.exit rename to tests/qapi-schema/args-member-array.exit diff --git a/tests/qapi-schema/data-member-array.json b/tests/qapi-schema/args-member-array.json similarity index 100% rename from tests/qapi-schema/data-member-array.json rename to tests/qapi-schema/args-member-array.json diff --git a/tests/qapi-schema/data-member-array.out b/tests/qapi-schema/args-member-array.out similarity index 100% rename from tests/qapi-schema/data-member-array.out rename to tests/qapi-schema/args-member-array.out diff --git a/tests/qapi-schema/args-member-unknown.err b/tests/qapi-schema/args-member-unknown.err new file mode 100644 index 0000000000..f6f82828ce --- /dev/null +++ b/tests/qapi-schema/args-member-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/nested-struct-returns.exit b/tests/qapi-schema/args-member-unknown.exit similarity index 100% rename from tests/qapi-schema/nested-struct-returns.exit rename to tests/qapi-schema/args-member-unknown.exit diff --git a/tests/qapi-schema/data-member-unknown.json b/tests/qapi-schema/args-member-unknown.json similarity index 100% rename from tests/qapi-schema/data-member-unknown.json rename to tests/qapi-schema/args-member-unknown.json diff --git a/tests/qapi-schema/nested-struct-returns.out b/tests/qapi-schema/args-member-unknown.out similarity index 100% rename from tests/qapi-schema/nested-struct-returns.out rename to tests/qapi-schema/args-member-unknown.out diff --git a/tests/qapi-schema/args-union.err b/tests/qapi-schema/args-union.err new file mode 100644 index 0000000000..1d693d74da --- /dev/null +++ b/tests/qapi-schema/args-union.err @@ -0,0 +1 @@ +tests/qapi-schema/args-union.json:4: 'data' for command 'oops' cannot use union type 'Uni' diff --git a/tests/qapi-schema/args-union.exit b/tests/qapi-schema/args-union.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/args-union.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/args-union.json b/tests/qapi-schema/args-union.json new file mode 100644 index 0000000000..7bdcbb7f08 --- /dev/null +++ b/tests/qapi-schema/args-union.json @@ -0,0 +1,4 @@ +# we do not allow union arguments +# TODO should we support this? +{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } } +{ 'command': 'oops', 'data': 'Uni' } diff --git a/tests/qapi-schema/args-union.out b/tests/qapi-schema/args-union.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/args-unknown.err b/tests/qapi-schema/args-unknown.err new file mode 100644 index 0000000000..4d91ec869f --- /dev/null +++ b/tests/qapi-schema/args-unknown.err @@ -0,0 +1 @@ +tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/args-unknown.exit b/tests/qapi-schema/args-unknown.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/args-unknown.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/data-unknown.json b/tests/qapi-schema/args-unknown.json similarity index 100% rename from tests/qapi-schema/data-unknown.json rename to tests/qapi-schema/args-unknown.json diff --git a/tests/qapi-schema/args-unknown.out b/tests/qapi-schema/args-unknown.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/command-int.json b/tests/qapi-schema/command-int.json index c90d408abe..9a62554fc6 100644 --- a/tests/qapi-schema/command-int.json +++ b/tests/qapi-schema/command-int.json @@ -1,3 +1,2 @@ # we reject collisions between commands and types -{ 'command': 'int', 'data': { 'character': 'str' }, - 'returns': { 'value': 'int' } } +{ 'command': 'int', 'data': { 'character': 'str' } } diff --git a/tests/qapi-schema/data-array-unknown.err b/tests/qapi-schema/data-array-unknown.err deleted file mode 100644 index 8b731bbcc8..0000000000 --- a/tests/qapi-schema/data-array-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'array of NoSuchType' diff --git a/tests/qapi-schema/data-int.err b/tests/qapi-schema/data-int.err deleted file mode 100644 index 1a9b077c06..0000000000 --- a/tests/qapi-schema/data-int.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int' diff --git a/tests/qapi-schema/data-member-unknown.err b/tests/qapi-schema/data-member-unknown.err deleted file mode 100644 index ab905db802..0000000000 --- a/tests/qapi-schema/data-member-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/data-unknown.err b/tests/qapi-schema/data-unknown.err deleted file mode 100644 index 5b07277a95..0000000000 --- a/tests/qapi-schema/data-unknown.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/data-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType' diff --git a/tests/qapi-schema/leading-comma-list.err b/tests/qapi-schema/leading-comma-list.err new file mode 100644 index 0000000000..f5c870bb9c --- /dev/null +++ b/tests/qapi-schema/leading-comma-list.err @@ -0,0 +1 @@ +tests/qapi-schema/leading-comma-list.json:2:13: Expected "{", "[", "]", string, boolean or "null" diff --git a/tests/qapi-schema/leading-comma-list.exit b/tests/qapi-schema/leading-comma-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/leading-comma-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/leading-comma-list.json b/tests/qapi-schema/leading-comma-list.json new file mode 100644 index 0000000000..c5ba501590 --- /dev/null +++ b/tests/qapi-schema/leading-comma-list.json @@ -0,0 +1,2 @@ +{ 'enum': 'Status', + 'data': [ , 'good', 'bad', 'ugly' ] } diff --git a/tests/qapi-schema/leading-comma-list.out b/tests/qapi-schema/leading-comma-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/leading-comma-object.err b/tests/qapi-schema/leading-comma-object.err new file mode 100644 index 0000000000..f767b95544 --- /dev/null +++ b/tests/qapi-schema/leading-comma-object.err @@ -0,0 +1 @@ +tests/qapi-schema/leading-comma-object.json:1:3: Expected string or "}" diff --git a/tests/qapi-schema/leading-comma-object.exit b/tests/qapi-schema/leading-comma-object.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/leading-comma-object.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/leading-comma-object.json b/tests/qapi-schema/leading-comma-object.json new file mode 100644 index 0000000000..c89023ff3b --- /dev/null +++ b/tests/qapi-schema/leading-comma-object.json @@ -0,0 +1,2 @@ +{ , 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } diff --git a/tests/qapi-schema/leading-comma-object.out b/tests/qapi-schema/leading-comma-object.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json index 3d52d2b398..efbe773ded 100644 --- a/tests/qapi-schema/nested-struct-data.json +++ b/tests/qapi-schema/nested-struct-data.json @@ -1,4 +1,3 @@ # inline subtypes collide with our desired future use of defaults { 'command': 'foo', - 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' }, - 'returns': {} } + 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/tests/qapi-schema/nested-struct-returns.err b/tests/qapi-schema/nested-struct-returns.err deleted file mode 100644 index 5238d075b7..0000000000 --- a/tests/qapi-schema/nested-struct-returns.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/nested-struct-returns.json:2: Member 'a' of 'returns' for command 'foo' should be a type name diff --git a/tests/qapi-schema/nested-struct-returns.json b/tests/qapi-schema/nested-struct-returns.json deleted file mode 100644 index d2cd047f0d..0000000000 --- a/tests/qapi-schema/nested-struct-returns.json +++ /dev/null @@ -1,3 +0,0 @@ -# inline subtypes collide with our desired future use of defaults -{ 'command': 'foo', - 'returns': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index c7eaa865da..a9e5aab927 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -7,13 +7,13 @@ 'data': { 'enum1': 'EnumOne', '*enum2': 'EnumOne', 'enum3': 'EnumOne', '*enum4': 'EnumOne' } } # for testing nested structs +{ 'struct': 'UserDefOne', + 'base': 'UserDefZero', # intentional forward reference + 'data': { 'string': 'str', '*enum1': 'EnumOne' } } + { 'struct': 'UserDefZero', 'data': { 'integer': 'int' } } -{ 'struct': 'UserDefOne', - 'base': 'UserDefZero', - 'data': { 'string': 'str', '*enum1': 'EnumOne' } } - { 'struct': 'UserDefTwoDictDict', 'data': { 'userdef': 'UserDefOne', 'string': 'str' } } @@ -31,30 +31,36 @@ 'data': { 'boolean': 'bool' } } { 'struct': 'UserDefB', - 'data': { 'integer': 'int' } } - -{ 'struct': 'UserDefC', - 'data': { 'string1': 'str', 'string2': 'str' } } - -{ 'struct': 'UserDefUnionBase', - 'data': { 'string': 'str', 'enum1': 'EnumOne' } } + 'data': { 'intb': 'int' } } { 'union': 'UserDefFlatUnion', - 'base': 'UserDefUnionBase', + 'base': 'UserDefUnionBase', # intentional forward reference 'discriminator': 'enum1', - 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', 'value3' : 'UserDefB' } } + 'data': { 'value1' : 'UserDefA', + 'value2' : 'UserDefB', + 'value3' : 'UserDefB' } } # FIXME generated struct UserDefFlatUnion has members for direct base -# UserDefOne, but lacks members for indirect base UserDefZero +# UserDefUnionBase, but lacks members for indirect base UserDefZero + +{ 'struct': 'UserDefUnionBase', + 'base': 'UserDefZero', + 'data': { 'string': 'str', 'enum1': 'EnumOne' } } # this variant of UserDefFlatUnion defaults to a union that uses fields with # allocated types to test corner cases in the cleanup/dealloc visitor { 'union': 'UserDefFlatUnion2', 'base': 'UserDefUnionBase', 'discriminator': 'enum1', - 'data': { 'value1' : 'UserDefC', 'value2' : 'UserDefB', 'value3' : 'UserDefA' } } + 'data': { 'value1' : 'UserDefC', # intentional forward reference + 'value2' : 'UserDefB', + 'value3' : 'UserDefA' } } { 'alternate': 'UserDefAlternate', 'data': { 'uda': 'UserDefA', 's': 'str', 'i': 'int' } } +# FIXME only a declaration of visit_type_UserDefAlternateKind() generated + +{ 'struct': 'UserDefC', + 'data': { 'string1': 'str', 'string2': 'str' } } # for testing native lists { 'union': 'UserDefNativeListUnion', @@ -123,6 +129,9 @@ { 'alternate': '__org.qemu_x-Alt', 'data': { '__org.qemu_x-branch': 'str', 'b': '__org.qemu_x-Base' } } { 'event': '__ORG.QEMU_X-EVENT', 'data': '__org.qemu_x-Struct' } +# FIXME generated qapi_event_send___org_qemu_x_event() has only a +# parameter for data's member __org_qemu_x_member2, none for its base +# __org.qemu_x-Base's member __org_qemu_x_member1 { 'command': '__org.qemu_x-command', 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'], 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' }, diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index cf0ccc4025..b0b7187226 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,17 +1,17 @@ [OrderedDict([('enum', 'EnumOne'), ('data', ['value1', 'value2', 'value3'])]), OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]), OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]), OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), - OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), - OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]), - OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), + OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('intb', 'int')]))]), OrderedDict([('union', 'UserDefFlatUnion'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefA'), ('value2', 'UserDefB'), ('value3', 'UserDefB')]))]), + OrderedDict([('struct', 'UserDefUnionBase'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), OrderedDict([('union', 'UserDefFlatUnion2'), ('base', 'UserDefUnionBase'), ('discriminator', 'enum1'), ('data', OrderedDict([('value1', 'UserDefC'), ('value2', 'UserDefB'), ('value3', 'UserDefA')]))]), OrderedDict([('alternate', 'UserDefAlternate'), ('data', OrderedDict([('uda', 'UserDefA'), ('s', 'str'), ('i', 'int')]))]), + OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]), OrderedDict([('union', 'UserDefNativeListUnion'), ('data', OrderedDict([('integer', ['int']), ('s8', ['int8']), ('s16', ['int16']), ('s32', ['int32']), ('s64', ['int64']), ('u8', ['uint8']), ('u16', ['uint16']), ('u32', ['uint32']), ('u64', ['uint64']), ('number', ['number']), ('boolean', ['bool']), ('string', ['str']), ('sizes', ['size'])]))]), OrderedDict([('command', 'user_def_cmd'), ('data', OrderedDict())]), OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), @@ -39,15 +39,15 @@ {'enum_name': '__org.qemu_x-Union1Kind', 'enum_values': None}, {'enum_name': '__org.qemu_x-AltKind', 'enum_values': None}] [OrderedDict([('struct', 'NestedEnumsOne'), ('data', OrderedDict([('enum1', 'EnumOne'), ('*enum2', 'EnumOne'), ('enum3', 'EnumOne'), ('*enum4', 'EnumOne')]))]), - OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('struct', 'UserDefOne'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('*enum1', 'EnumOne')]))]), + OrderedDict([('struct', 'UserDefZero'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('struct', 'UserDefTwoDictDict'), ('data', OrderedDict([('userdef', 'UserDefOne'), ('string', 'str')]))]), OrderedDict([('struct', 'UserDefTwoDict'), ('data', OrderedDict([('string1', 'str'), ('dict2', 'UserDefTwoDictDict'), ('*dict3', 'UserDefTwoDictDict')]))]), OrderedDict([('struct', 'UserDefTwo'), ('data', OrderedDict([('string0', 'str'), ('dict1', 'UserDefTwoDict')]))]), OrderedDict([('struct', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), - OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), + OrderedDict([('struct', 'UserDefB'), ('data', OrderedDict([('intb', 'int')]))]), + OrderedDict([('struct', 'UserDefUnionBase'), ('base', 'UserDefZero'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), OrderedDict([('struct', 'UserDefC'), ('data', OrderedDict([('string1', 'str'), ('string2', 'str')]))]), - OrderedDict([('struct', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), OrderedDict([('struct', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), OrderedDict([('struct', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]), OrderedDict([('struct', '__org.qemu_x-Base'), ('data', OrderedDict([('__org.qemu_x-member1', '__org.qemu_x-Enum')]))]), diff --git a/tests/qapi-schema/returns-dict.err b/tests/qapi-schema/returns-dict.err new file mode 100644 index 0000000000..eb2d0c4661 --- /dev/null +++ b/tests/qapi-schema/returns-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/returns-dict.json:2: 'returns' for command 'oops' should be a type name diff --git a/tests/qapi-schema/returns-dict.exit b/tests/qapi-schema/returns-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/returns-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/returns-dict.json b/tests/qapi-schema/returns-dict.json new file mode 100644 index 0000000000..1cfef3ede7 --- /dev/null +++ b/tests/qapi-schema/returns-dict.json @@ -0,0 +1,2 @@ +# we reject inline struct return type +{ 'command': 'oops', 'returns': { 'a': 'str' } } diff --git a/tests/qapi-schema/returns-dict.out b/tests/qapi-schema/returns-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/returns-whitelist.err b/tests/qapi-schema/returns-whitelist.err index a41f019a52..f47c1ee7ca 100644 --- a/tests/qapi-schema/returns-whitelist.err +++ b/tests/qapi-schema/returns-whitelist.err @@ -1 +1 @@ -tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'array of int' +tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int' diff --git a/tests/qapi-schema/struct-data-invalid.err b/tests/qapi-schema/struct-data-invalid.err new file mode 100644 index 0000000000..6644f4c2ad --- /dev/null +++ b/tests/qapi-schema/struct-data-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-data-invalid.json:1: 'data' for struct 'foo' should be a dictionary or type name diff --git a/tests/qapi-schema/struct-data-invalid.exit b/tests/qapi-schema/struct-data-invalid.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/struct-data-invalid.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/struct-data-invalid.json b/tests/qapi-schema/struct-data-invalid.json new file mode 100644 index 0000000000..9adbc3bb6b --- /dev/null +++ b/tests/qapi-schema/struct-data-invalid.json @@ -0,0 +1,2 @@ +{ 'struct': 'foo', + 'data': false } diff --git a/tests/qapi-schema/struct-data-invalid.out b/tests/qapi-schema/struct-data-invalid.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/struct-member-invalid.err b/tests/qapi-schema/struct-member-invalid.err new file mode 100644 index 0000000000..69a326d450 --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-member-invalid.json:1: Member 'a' of 'data' for struct 'foo' should be a type name diff --git a/tests/qapi-schema/struct-member-invalid.exit b/tests/qapi-schema/struct-member-invalid.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/struct-member-invalid.json b/tests/qapi-schema/struct-member-invalid.json new file mode 100644 index 0000000000..8f172f7a87 --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid.json @@ -0,0 +1,2 @@ +{ 'struct': 'foo', + 'data': { 'a': false } } diff --git a/tests/qapi-schema/struct-member-invalid.out b/tests/qapi-schema/struct-member-invalid.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/union-base-no-discriminator.err b/tests/qapi-schema/union-base-no-discriminator.err index fc8b79c459..8b7a24260f 100644 --- a/tests/qapi-schema/union-base-no-discriminator.err +++ b/tests/qapi-schema/union-base-no-discriminator.err @@ -1 +1 @@ -tests/qapi-schema/union-base-no-discriminator.json:11: Union 'TestUnion' requires a discriminator to go along with base +tests/qapi-schema/union-base-no-discriminator.json:11: Simple union 'TestUnion' must not have a base diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 1ee40e148a..28f146d4b7 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -94,7 +94,7 @@ static bool qdict_cmp_simple(QDict *a, QDict *b) /* This function is hooked as final emit function, which can verify the correctness. */ -static void event_test_emit(TEST_QAPIEvent event, QDict *d, Error **errp) +static void event_test_emit(test_QAPIEvent event, QDict *d, Error **errp) { QObject *obj; QDict *t; diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index b96195309b..a5cfefae8b 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -313,7 +313,7 @@ static void test_visitor_in_union_flat(TestInputVisitorData *data, visit_type_UserDefFlatUnion(v, &tmp, NULL, &err); g_assert(err == NULL); - g_assert_cmpint(tmp->kind, ==, ENUM_ONE_VALUE1); + g_assert_cmpint(tmp->enum1, ==, ENUM_ONE_VALUE1); g_assert_cmpstr(tmp->string, ==, "str"); /* TODO g_assert_cmpint(tmp->integer, ==, 41); */ g_assert_cmpint(tmp->value1->boolean, ==, true); @@ -636,6 +636,8 @@ static void test_visitor_in_errors(TestInputVisitorData *data, visit_type_TestStruct(v, &p, NULL, &err); g_assert(err); + /* FIXME - a failed parse should not leave a partially-allocated p + * for us to clean up; this could cause callers to leak memory. */ g_assert(p->string == NULL); error_free(err); diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index 87ba350b43..338ada0253 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -437,7 +437,7 @@ static void test_visitor_out_union_flat(TestOutputVisitorData *data, Error *err = NULL; UserDefFlatUnion *tmp = g_malloc0(sizeof(UserDefFlatUnion)); - tmp->kind = ENUM_ONE_VALUE1; + tmp->enum1 = ENUM_ONE_VALUE1; tmp->string = g_strdup("str"); tmp->value1 = g_malloc0(sizeof(UserDefA)); /* TODO when generator bug is fixed: tmp->integer = 41; */