diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/qapi-commands.py | 31 | ||||
-rw-r--r-- | scripts/qapi-event.py | 48 | ||||
-rw-r--r-- | scripts/qapi-introspect.py | 4 | ||||
-rw-r--r-- | scripts/qapi-types.py | 2 | ||||
-rw-r--r-- | scripts/qapi-visit.py | 8 | ||||
-rw-r--r-- | scripts/qapi.py | 127 |
6 files changed, 150 insertions, 70 deletions
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 34b6a3a07f..a06a2c4f9b 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -16,20 +16,23 @@ from qapi import * import re -def gen_command_decl(name, arg_type, ret_type): +def gen_command_decl(name, arg_type, boxed, ret_type): return mcgen(''' %(c_type)s qmp_%(c_name)s(%(params)s); ''', c_type=(ret_type and ret_type.c_type()) or 'void', c_name=c_name(name), - params=gen_params(arg_type, 'Error **errp')) + params=gen_params(arg_type, boxed, 'Error **errp')) -def gen_call(name, arg_type, ret_type): +def gen_call(name, arg_type, boxed, ret_type): ret = '' argstr = '' - if arg_type: + if boxed: + assert arg_type and not arg_type.is_empty() + argstr = '&arg, ' + elif arg_type: assert not arg_type.variants for memb in arg_type.members: if memb.optional: @@ -46,8 +49,10 @@ def gen_call(name, arg_type, ret_type): ''', c_name=c_name(name), args=argstr, lhs=lhs) if ret_type: - ret += gen_err_check() ret += mcgen(''' + if (err) { + goto out; + } qmp_marshal_output_%(c_name)s(retval, ret, &err); ''', @@ -92,7 +97,7 @@ def gen_marshal_decl(name): proto=gen_marshal_proto(name)) -def gen_marshal(name, arg_type, ret_type): +def gen_marshal(name, arg_type, boxed, ret_type): ret = mcgen(''' %(proto)s @@ -107,7 +112,7 @@ def gen_marshal(name, arg_type, ret_type): ''', c_type=ret_type.c_type()) - if arg_type and arg_type.members: + if arg_type and not arg_type.is_empty(): ret += mcgen(''' Visitor *v; %(c_name)s arg = {0}; @@ -134,10 +139,10 @@ def gen_marshal(name, arg_type, ret_type): (void)args; ''') - ret += gen_call(name, arg_type, ret_type) + ret += gen_call(name, arg_type, boxed, ret_type) # 'goto out' produced above for arg_type, and by gen_call() for ret_type - if (arg_type and arg_type.members) or ret_type: + if (arg_type and not arg_type.is_empty()) or ret_type: ret += mcgen(''' out: @@ -145,7 +150,7 @@ out: ret += mcgen(''' error_propagate(errp, err); ''') - if arg_type and arg_type.members: + if arg_type and not arg_type.is_empty(): ret += mcgen(''' visit_free(v); v = qapi_dealloc_visitor_new(); @@ -210,16 +215,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): self._visited_ret_types = None def visit_command(self, name, info, arg_type, ret_type, - gen, success_response): + gen, success_response, boxed): if not gen: return - self.decl += gen_command_decl(name, arg_type, ret_type) + self.decl += gen_command_decl(name, arg_type, boxed, 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) + self.defn += gen_marshal(name, arg_type, boxed, ret_type) if not middle_mode: self._regy += gen_register_command(name, success_response) diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 9c88627c9f..38d82111ad 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -14,18 +14,18 @@ from qapi import * -def gen_event_send_proto(name, arg_type): +def gen_event_send_proto(name, arg_type, boxed): return 'void qapi_event_send_%(c_name)s(%(param)s)' % { 'c_name': c_name(name.lower()), - 'param': gen_params(arg_type, 'Error **errp')} + 'param': gen_params(arg_type, boxed, 'Error **errp')} -def gen_event_send_decl(name, arg_type): +def gen_event_send_decl(name, arg_type, boxed): return mcgen(''' %(proto)s; ''', - proto=gen_event_send_proto(name, arg_type)) + proto=gen_event_send_proto(name, arg_type, boxed)) # Declare and initialize an object 'qapi' using parameters from gen_params() @@ -49,10 +49,15 @@ def gen_param_var(typ): }; ''') + if not typ.is_implicit(): + ret += mcgen(''' + %(c_name)s *arg = ¶m; +''', + c_name=typ.c_name()) return ret -def gen_event_send(name, arg_type): +def gen_event_send(name, arg_type, boxed): # FIXME: Our declaration of local variables (and of 'errp' in the # parameter list) can collide with exploded members of the event's # data type passed in as parameters. If this collision ever hits in @@ -67,14 +72,17 @@ def gen_event_send(name, arg_type): Error *err = NULL; QMPEventFuncEmit emit; ''', - proto=gen_event_send_proto(name, arg_type)) + proto=gen_event_send_proto(name, arg_type, boxed)) - if arg_type and arg_type.members: + if arg_type and not arg_type.is_empty(): ret += mcgen(''' QObject *obj; Visitor *v; ''') - ret += gen_param_var(arg_type) + if not boxed: + ret += gen_param_var(arg_type) + else: + assert not boxed ret += mcgen(''' @@ -88,9 +96,17 @@ def gen_event_send(name, arg_type): ''', name=name) - if arg_type and arg_type.members: + if arg_type and not arg_type.is_empty(): ret += mcgen(''' v = qmp_output_visitor_new(&obj); +''') + if not arg_type.is_implicit(): + ret += mcgen(''' + visit_type_%(c_name)s(v, "%(name)s", &arg, &err); +''', + name=name, c_name=arg_type.c_name()) + else: + ret += mcgen(''' visit_start_struct(v, "%(name)s", NULL, 0, &err); if (err) { @@ -101,14 +117,16 @@ def gen_event_send(name, arg_type): visit_check_struct(v, &err); } visit_end_struct(v, NULL); +''', + name=name, c_name=arg_type.c_name()) + ret += mcgen(''' if (err) { goto out; } visit_complete(v, &obj); qdict_put_obj(qmp, "data", obj); -''', - name=name, c_name=arg_type.c_name()) +''') ret += mcgen(''' emit(%(c_enum)s, qmp, &err); @@ -116,7 +134,7 @@ def gen_event_send(name, arg_type): ''', c_enum=c_enum_const(event_enum_name, name)) - if arg_type and arg_type.members: + if arg_type and not arg_type.is_empty(): ret += mcgen(''' out: visit_free(v); @@ -145,9 +163,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor): self.defn += gen_enum_lookup(event_enum_name, self._event_names) self._event_names = None - def visit_event(self, name, info, arg_type): - self.decl += gen_event_send_decl(name, arg_type) - self.defn += gen_event_send(name, arg_type) + def visit_event(self, name, info, arg_type, boxed): + self.decl += gen_event_send_decl(name, arg_type, boxed) + self.defn += gen_event_send(name, arg_type, boxed) self._event_names.append(name) diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py index e0f926be04..541644e350 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi-introspect.py @@ -154,14 +154,14 @@ const char %(c_name)s[] = %(c_string)s; for m in variants.variants]}) def visit_command(self, name, info, arg_type, ret_type, - gen, success_response): + gen, success_response, boxed): 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): + def visit_event(self, name, info, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 5ace2cf065..dabc42e047 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -91,7 +91,7 @@ struct %(c_name)s { # 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 base.members) and not members and not variants: + if (not base or base.is_empty()) and not members and not variants: ret += mcgen(''' char qapi_dummy_for_empty_struct; ''') diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 0b9e298bc4..96f2491c16 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -47,9 +47,11 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) if base: ret += mcgen(''' visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err); + if (err) { + goto out; + } ''', c_type=base.c_name()) - ret += gen_err_check() for memb in members: if memb.optional: @@ -60,10 +62,12 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) push_indent() ret += mcgen(''' visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err); + if (err) { + goto out; + } ''', c_type=memb.type.c_name(), name=memb.name, c_name=c_name(memb.name)) - ret += gen_err_check() if memb.optional: pop_indent() ret += mcgen(''' diff --git a/scripts/qapi.py b/scripts/qapi.py index b13ae47899..21bc32fda3 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -522,10 +522,14 @@ def check_type(expr_info, source, value, allow_array=False, def check_command(expr, expr_info): name = expr['command'] + boxed = expr.get('boxed', False) + args_meta = ['struct'] + if boxed: + args_meta += ['union', 'alternate'] check_type(expr_info, "'data' for command '%s'" % name, - expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['struct']) + expr.get('data'), allow_dict=not boxed, allow_optional=True, + allow_metas=args_meta) returns_meta = ['union', 'struct'] if name in returns_whitelist: returns_meta += ['built-in', 'alternate', 'enum'] @@ -537,11 +541,15 @@ def check_command(expr, expr_info): def check_event(expr, expr_info): global events name = expr['event'] + boxed = expr.get('boxed', False) + meta = ['struct'] + if boxed: + meta += ['union', 'alternate'] events.append(name) check_type(expr_info, "'data' for event '%s'" % name, - expr.get('data'), allow_dict=True, allow_optional=True, - allow_metas=['struct']) + expr.get('data'), allow_dict=not boxed, allow_optional=True, + allow_metas=meta) def check_union(expr, expr_info): @@ -612,6 +620,14 @@ def check_union(expr, expr_info): "enum '%s'" % (key, enum_define["enum_name"])) + # If discriminator is user-defined, ensure all values are covered + if enum_define: + for value in enum_define['enum_values']: + if value not in members.keys(): + raise QAPIExprError(expr_info, + "Union '%s' data missing '%s' branch" + % (name, value)) + def check_alternate(expr, expr_info): name = expr['alternate'] @@ -686,6 +702,10 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPIExprError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) + if key == 'boxed' and value is not True: + raise QAPIExprError(info, + "'%s' of %s '%s' should only use true value" + % (key, meta, name)) for key in required: if key not in expr: raise QAPIExprError(info, @@ -717,10 +737,10 @@ def check_exprs(exprs): add_struct(expr, info) elif 'command' in expr: check_keys(expr_elem, 'command', [], - ['data', 'returns', 'gen', 'success-response']) + ['data', 'returns', 'gen', 'success-response', 'boxed']) add_name(expr['command'], info, 'command') elif 'event' in expr: - check_keys(expr_elem, 'event', [], ['data']) + check_keys(expr_elem, 'event', [], ['data', 'boxed']) add_name(expr['event'], info, 'event') else: raise QAPIExprError(expr_elem['info'], @@ -818,10 +838,10 @@ class QAPISchemaVisitor(object): pass def visit_command(self, name, info, arg_type, ret_type, - gen, success_response): + gen, success_response, boxed): pass - def visit_event(self, name, info, arg_type): + def visit_event(self, name, info, arg_type, boxed): pass @@ -991,7 +1011,12 @@ class QAPISchemaObjectType(QAPISchemaType): # _def_predefineds() return self.name.startswith('q_') + def is_empty(self): + assert self.members is not None + return not self.members and not self.variants + def c_name(self): + assert self.name != 'q_empty' return QAPISchemaType.c_name(self) def c_type(self): @@ -1084,7 +1109,7 @@ class QAPISchemaObjectTypeVariants(object): assert len(variants) > 0 for v in variants: assert isinstance(v, QAPISchemaObjectTypeVariant) - self.tag_name = tag_name + self._tag_name = tag_name self.tag_member = tag_member self.variants = variants @@ -1094,8 +1119,8 @@ class QAPISchemaObjectTypeVariants(object): def check(self, schema, seen): if not self.tag_member: # flat union - self.tag_member = seen[c_name(self.tag_name)] - assert self.tag_name == self.tag_member.name + self.tag_member = seen[c_name(self._tag_name)] + assert self._tag_name == self.tag_member.name assert isinstance(self.tag_member.type, QAPISchemaEnumType) for v in self.variants: v.check(schema) @@ -1125,7 +1150,7 @@ class QAPISchemaAlternateType(QAPISchemaType): def __init__(self, name, info, variants): QAPISchemaType.__init__(self, name, info) assert isinstance(variants, QAPISchemaObjectTypeVariants) - assert not variants.tag_name + assert variants.tag_member variants.set_owner(name) variants.tag_member.set_owner(self.name) self.variants = variants @@ -1150,9 +1175,13 @@ class QAPISchemaAlternateType(QAPISchemaType): def visit(self, visitor): visitor.visit_alternate_type(self.name, self.info, self.variants) + def is_empty(self): + return False + class QAPISchemaCommand(QAPISchemaEntity): - def __init__(self, name, info, arg_type, ret_type, gen, success_response): + def __init__(self, name, info, arg_type, ret_type, gen, success_response, + boxed): QAPISchemaEntity.__init__(self, name, info) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -1162,12 +1191,24 @@ class QAPISchemaCommand(QAPISchemaEntity): self.ret_type = None self.gen = gen self.success_response = success_response + self.boxed = boxed def check(self, schema): if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) - assert isinstance(self.arg_type, QAPISchemaObjectType) - assert not self.arg_type.variants # not implemented + assert (isinstance(self.arg_type, QAPISchemaObjectType) or + isinstance(self.arg_type, QAPISchemaAlternateType)) + self.arg_type.check(schema) + if self.boxed: + if self.arg_type.is_empty(): + raise QAPIExprError(self.info, + "Cannot use 'boxed' with empty type") + else: + assert not isinstance(self.arg_type, QAPISchemaAlternateType) + assert not self.arg_type.variants + elif self.boxed: + raise QAPIExprError(self.info, + "Use of 'boxed' requires 'data'") if self._ret_type_name: self.ret_type = schema.lookup_type(self._ret_type_name) assert isinstance(self.ret_type, QAPISchemaType) @@ -1175,24 +1216,36 @@ class QAPISchemaCommand(QAPISchemaEntity): def visit(self, visitor): visitor.visit_command(self.name, self.info, self.arg_type, self.ret_type, - self.gen, self.success_response) + self.gen, self.success_response, self.boxed) class QAPISchemaEvent(QAPISchemaEntity): - def __init__(self, name, info, arg_type): + def __init__(self, name, info, arg_type, boxed): QAPISchemaEntity.__init__(self, name, info) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None + self.boxed = boxed def check(self, schema): if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) - assert isinstance(self.arg_type, QAPISchemaObjectType) - assert not self.arg_type.variants # not implemented + assert (isinstance(self.arg_type, QAPISchemaObjectType) or + isinstance(self.arg_type, QAPISchemaAlternateType)) + self.arg_type.check(schema) + if self.boxed: + if self.arg_type.is_empty(): + raise QAPIExprError(self.info, + "Cannot use 'boxed' with empty type") + else: + assert not isinstance(self.arg_type, QAPISchemaAlternateType) + assert not self.arg_type.variants + elif self.boxed: + raise QAPIExprError(self.info, + "Use of 'boxed' requires 'data'") def visit(self, visitor): - visitor.visit_event(self.name, self.info, self.arg_type) + visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) class QAPISchema(object): @@ -1368,6 +1421,7 @@ class QAPISchema(object): rets = expr.get('returns') gen = expr.get('gen', True) success_response = expr.get('success-response', True) + boxed = expr.get('boxed', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( name, info, 'arg', self._make_members(data, info)) @@ -1375,15 +1429,16 @@ class QAPISchema(object): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, - success_response)) + success_response, boxed)) def _def_event(self, expr, info): name = expr['event'] data = expr.get('data') + boxed = expr.get('boxed', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( name, info, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, data)) + self._def_entity(QAPISchemaEvent(name, info, data, boxed)) def _def_exprs(self): for expr_elem in self.exprs: @@ -1626,31 +1681,29 @@ extern const char *const %(c_name)s_lookup[]; return ret -def gen_params(arg_type, extra): +def gen_params(arg_type, boxed, extra): if not arg_type: + assert not boxed return extra - assert not arg_type.variants ret = '' sep = '' - for memb in arg_type.members: - ret += sep + if boxed: + ret += '%s arg' % arg_type.c_param_type() sep = ', ' - if memb.optional: - ret += 'bool has_%s, ' % c_name(memb.name) - ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name)) + else: + assert not arg_type.variants + for memb in arg_type.members: + ret += sep + sep = ', ' + if memb.optional: + ret += 'bool has_%s, ' % c_name(memb.name) + ret += '%s %s' % (memb.type.c_param_type(), + c_name(memb.name)) if extra: ret += sep + extra return ret -def gen_err_check(): - return mcgen(''' - if (err) { - goto out; - } -''') - - # # Common command line parsing # |