qapi: Plumb in 'boxed' to qapi generator lower levels

The next patch will add support for passing a qapi union type
as the 'data' of a command.  But to do that, the user function
for implementing the command, as called by the generated
marshal command, must take the corresponding C struct as a
single boxed pointer, rather than a breakdown into one
parameter per member.  Even without a union, being able to use
a C struct rather than a list of parameters can make it much
easier to handle coding with QAPI.

This patch adds the internal plumbing of a 'boxed' flag
associated with each command and event.  In several cases,
this means adding indentation, with one new dead branch and
the remaining branch being the original code more deeply
nested; this was done so that the new implementation in the
next patch is easier to review without also being mixed with
indentation changes.

For this patch, no behavior or generated output changes, other
than the testsuite outputting the value of the new flag
(always False for now).

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1468468228-27827-9-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
[Identifier box renamed to boxed in two places]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
Eric Blake 2016-07-13 21:50:19 -06:00 committed by Markus Armbruster
parent 4d0b268fdb
commit 48825ca419
9 changed files with 70 additions and 49 deletions

View File

@ -16,20 +16,22 @@ from qapi import *
import re import re
def gen_command_decl(name, arg_type, ret_type): def gen_command_decl(name, arg_type, boxed, ret_type):
return mcgen(''' return mcgen('''
%(c_type)s qmp_%(c_name)s(%(params)s); %(c_type)s qmp_%(c_name)s(%(params)s);
''', ''',
c_type=(ret_type and ret_type.c_type()) or 'void', c_type=(ret_type and ret_type.c_type()) or 'void',
c_name=c_name(name), 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 = '' ret = ''
argstr = '' argstr = ''
if arg_type: if boxed:
assert False # not implemented
elif arg_type:
assert not arg_type.variants assert not arg_type.variants
for memb in arg_type.members: for memb in arg_type.members:
if memb.optional: if memb.optional:
@ -94,7 +96,7 @@ def gen_marshal_decl(name):
proto=gen_marshal_proto(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(''' ret = mcgen('''
%(proto)s %(proto)s
@ -136,7 +138,7 @@ def gen_marshal(name, arg_type, ret_type):
(void)args; (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 # 'goto out' produced above for arg_type, and by gen_call() for ret_type
if (arg_type and not arg_type.is_empty()) or ret_type: if (arg_type and not arg_type.is_empty()) or ret_type:
@ -212,16 +214,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
self._visited_ret_types = None self._visited_ret_types = None
def visit_command(self, name, info, arg_type, ret_type, def visit_command(self, name, info, arg_type, ret_type,
gen, success_response): gen, success_response, boxed):
if not gen: if not gen:
return 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: if ret_type and ret_type not in self._visited_ret_types:
self._visited_ret_types.add(ret_type) self._visited_ret_types.add(ret_type)
self.defn += gen_marshal_output(ret_type) self.defn += gen_marshal_output(ret_type)
if middle_mode: if middle_mode:
self.decl += gen_marshal_decl(name) 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: if not middle_mode:
self._regy += gen_register_command(name, success_response) self._regy += gen_register_command(name, success_response)

View File

@ -14,18 +14,18 @@
from qapi import * 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)' % { return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
'c_name': c_name(name.lower()), '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(''' return mcgen('''
%(proto)s; %(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() # Declare and initialize an object 'qapi' using parameters from gen_params()
@ -57,7 +57,7 @@ def gen_param_var(typ):
return ret 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 # FIXME: Our declaration of local variables (and of 'errp' in the
# parameter list) can collide with exploded members of the event's # parameter list) can collide with exploded members of the event's
# data type passed in as parameters. If this collision ever hits in # data type passed in as parameters. If this collision ever hits in
@ -72,7 +72,7 @@ def gen_event_send(name, arg_type):
Error *err = NULL; Error *err = NULL;
QMPEventFuncEmit emit; QMPEventFuncEmit emit;
''', ''',
proto=gen_event_send_proto(name, arg_type)) proto=gen_event_send_proto(name, arg_type, boxed))
if arg_type and not arg_type.is_empty(): if arg_type and not arg_type.is_empty():
ret += mcgen(''' ret += mcgen('''
@ -160,9 +160,9 @@ class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
self.defn += gen_enum_lookup(event_enum_name, self._event_names) self.defn += gen_enum_lookup(event_enum_name, self._event_names)
self._event_names = None self._event_names = None
def visit_event(self, name, info, arg_type): def visit_event(self, name, info, arg_type, boxed):
self.decl += gen_event_send_decl(name, arg_type) self.decl += gen_event_send_decl(name, arg_type, boxed)
self.defn += gen_event_send(name, arg_type) self.defn += gen_event_send(name, arg_type, boxed)
self._event_names.append(name) self._event_names.append(name)

View File

@ -154,14 +154,14 @@ const char %(c_name)s[] = %(c_string)s;
for m in variants.variants]}) for m in variants.variants]})
def visit_command(self, name, info, arg_type, ret_type, 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 arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type
self._gen_json(name, 'command', self._gen_json(name, 'command',
{'arg-type': self._use_type(arg_type), {'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_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 arg_type = arg_type or self._schema.the_empty_object_type
self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})

View File

@ -826,10 +826,10 @@ class QAPISchemaVisitor(object):
pass pass
def visit_command(self, name, info, arg_type, ret_type, def visit_command(self, name, info, arg_type, ret_type,
gen, success_response): gen, success_response, boxed):
pass pass
def visit_event(self, name, info, arg_type): def visit_event(self, name, info, arg_type, boxed):
pass pass
@ -1165,7 +1165,8 @@ class QAPISchemaAlternateType(QAPISchemaType):
class QAPISchemaCommand(QAPISchemaEntity): 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) QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str) assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str) assert not ret_type or isinstance(ret_type, str)
@ -1175,12 +1176,14 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.ret_type = None self.ret_type = None
self.gen = gen self.gen = gen
self.success_response = success_response self.success_response = success_response
self.boxed = boxed
def check(self, schema): def check(self, schema):
if self._arg_type_name: if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name) self.arg_type = schema.lookup_type(self._arg_type_name)
assert isinstance(self.arg_type, QAPISchemaObjectType) assert isinstance(self.arg_type, QAPISchemaObjectType)
assert not self.arg_type.variants # not implemented assert not self.arg_type.variants # not implemented
assert not self.boxed # not implemented
if self._ret_type_name: if self._ret_type_name:
self.ret_type = schema.lookup_type(self._ret_type_name) self.ret_type = schema.lookup_type(self._ret_type_name)
assert isinstance(self.ret_type, QAPISchemaType) assert isinstance(self.ret_type, QAPISchemaType)
@ -1188,24 +1191,26 @@ class QAPISchemaCommand(QAPISchemaEntity):
def visit(self, visitor): def visit(self, visitor):
visitor.visit_command(self.name, self.info, visitor.visit_command(self.name, self.info,
self.arg_type, self.ret_type, self.arg_type, self.ret_type,
self.gen, self.success_response) self.gen, self.success_response, self.boxed)
class QAPISchemaEvent(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity):
def __init__(self, name, info, arg_type): def __init__(self, name, info, arg_type, boxed):
QAPISchemaEntity.__init__(self, name, info) QAPISchemaEntity.__init__(self, name, info)
assert not arg_type or isinstance(arg_type, str) assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type self._arg_type_name = arg_type
self.arg_type = None self.arg_type = None
self.boxed = boxed
def check(self, schema): def check(self, schema):
if self._arg_type_name: if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name) self.arg_type = schema.lookup_type(self._arg_type_name)
assert isinstance(self.arg_type, QAPISchemaObjectType) assert isinstance(self.arg_type, QAPISchemaObjectType)
assert not self.arg_type.variants # not implemented assert not self.arg_type.variants # not implemented
assert not self.boxed # not implemented
def visit(self, visitor): 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): class QAPISchema(object):
@ -1381,6 +1386,7 @@ class QAPISchema(object):
rets = expr.get('returns') rets = expr.get('returns')
gen = expr.get('gen', True) gen = expr.get('gen', True)
success_response = expr.get('success-response', True) success_response = expr.get('success-response', True)
boxed = expr.get('boxed', False)
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
name, info, 'arg', self._make_members(data, info)) name, info, 'arg', self._make_members(data, info))
@ -1388,15 +1394,16 @@ class QAPISchema(object):
assert len(rets) == 1 assert len(rets) == 1
rets = self._make_array_type(rets[0], info) rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, data, rets, gen, self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
success_response)) success_response, boxed))
def _def_event(self, expr, info): def _def_event(self, expr, info):
name = expr['event'] name = expr['event']
data = expr.get('data') data = expr.get('data')
boxed = expr.get('boxed', False)
if isinstance(data, OrderedDict): if isinstance(data, OrderedDict):
data = self._make_implicit_object_type( data = self._make_implicit_object_type(
name, info, 'arg', self._make_members(data, info)) 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): def _def_exprs(self):
for expr_elem in self.exprs: for expr_elem in self.exprs:
@ -1639,18 +1646,22 @@ extern const char *const %(c_name)s_lookup[];
return ret return ret
def gen_params(arg_type, extra): def gen_params(arg_type, boxed, extra):
if not arg_type: if not arg_type:
return extra return extra
assert not arg_type.variants
ret = '' ret = ''
sep = '' sep = ''
if boxed:
assert False # not implemented
else:
assert not arg_type.variants
for memb in arg_type.members: for memb in arg_type.members:
ret += sep ret += sep
sep = ', ' sep = ', '
if memb.optional: if memb.optional:
ret += 'bool has_%s, ' % c_name(memb.name) ret += 'bool has_%s, ' % c_name(memb.name)
ret += '%s %s' % (memb.type.c_param_type(), c_name(memb.name)) ret += '%s %s' % (memb.type.c_param_type(),
c_name(memb.name))
if extra: if extra:
ret += sep + extra ret += sep + extra
return ret return ret

View File

@ -1,4 +1,5 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE prefix QTYPE
event oops None event oops None
boxed=False
object q_empty object q_empty

View File

@ -1,7 +1,7 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE prefix QTYPE
command fooA q_obj_fooA-arg -> None command fooA q_obj_fooA-arg -> None
gen=True success_response=True gen=True success_response=True boxed=False
object q_empty object q_empty
object q_obj_fooA-arg object q_obj_fooA-arg
member bar1: str optional=False member bar1: str optional=False

View File

@ -1,7 +1,7 @@
enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool'] enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbool']
prefix QTYPE prefix QTYPE
command eins None -> None command eins None -> None
gen=True success_response=True gen=True success_response=True boxed=False
object q_empty object q_empty
command zwei None -> None command zwei None -> None
gen=True success_response=True gen=True success_response=True boxed=False

View File

@ -23,9 +23,13 @@ alternate AltStrNum
case s: str case s: str
case n: number case n: number
event EVENT_A None event EVENT_A None
boxed=False
event EVENT_B None event EVENT_B None
boxed=False
event EVENT_C q_obj_EVENT_C-arg event EVENT_C q_obj_EVENT_C-arg
boxed=False
event EVENT_D q_obj_EVENT_D-arg event EVENT_D q_obj_EVENT_D-arg
boxed=False
object Empty1 object Empty1
object Empty2 object Empty2
base Empty1 base Empty1
@ -124,6 +128,7 @@ object UserDefZero
object WrapAlternate object WrapAlternate
member alt: UserDefAlternate optional=False member alt: UserDefAlternate optional=False
event __ORG.QEMU_X-EVENT __org.qemu_x-Struct event __ORG.QEMU_X-EVENT __org.qemu_x-Struct
boxed=False
alternate __org.qemu_x-Alt alternate __org.qemu_x-Alt
tag type tag type
case __org.qemu_x-branch: str case __org.qemu_x-branch: str
@ -147,11 +152,11 @@ object __org.qemu_x-Union2
tag __org.qemu_x-member1 tag __org.qemu_x-member1
case __org.qemu_x-value: __org.qemu_x-Struct2 case __org.qemu_x-value: __org.qemu_x-Struct2
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
gen=True success_response=True gen=True success_response=True boxed=False
command guest-get-time q_obj_guest-get-time-arg -> int command guest-get-time q_obj_guest-get-time-arg -> int
gen=True success_response=True gen=True success_response=True boxed=False
command guest-sync q_obj_guest-sync-arg -> any command guest-sync q_obj_guest-sync-arg -> any
gen=True success_response=True gen=True success_response=True boxed=False
object q_empty object q_empty
object q_obj_EVENT_C-arg object q_obj_EVENT_C-arg
member a: int optional=True member a: int optional=True
@ -212,10 +217,10 @@ object q_obj_user_def_cmd2-arg
member ud1a: UserDefOne optional=False member ud1a: UserDefOne optional=False
member ud1b: UserDefOne optional=True member ud1b: UserDefOne optional=True
command user_def_cmd None -> None command user_def_cmd None -> None
gen=True success_response=True gen=True success_response=True boxed=False
command user_def_cmd0 Empty2 -> Empty2 command user_def_cmd0 Empty2 -> Empty2
gen=True success_response=True gen=True success_response=True boxed=False
command user_def_cmd1 q_obj_user_def_cmd1-arg -> None command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
gen=True success_response=True gen=True success_response=True boxed=False
command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
gen=True success_response=True gen=True success_response=True boxed=False

View File

@ -36,13 +36,15 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_variants(variants) self._print_variants(variants)
def visit_command(self, name, info, arg_type, ret_type, def visit_command(self, name, info, arg_type, ret_type,
gen, success_response): gen, success_response, boxed):
print 'command %s %s -> %s' % \ print 'command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name) (name, arg_type and arg_type.name, ret_type and ret_type.name)
print ' gen=%s success_response=%s' % (gen, success_response) print ' gen=%s success_response=%s boxed=%s' % \
(gen, success_response, boxed)
def visit_event(self, name, info, arg_type): def visit_event(self, name, info, arg_type, boxed):
print 'event %s %s' % (name, arg_type and arg_type.name) print 'event %s %s' % (name, arg_type and arg_type.name)
print ' boxed=%s' % boxed
@staticmethod @staticmethod
def _print_variants(variants): def _print_variants(variants):