mirror of
https://github.com/qemu/qemu.git
synced 2024-12-09 11:43:38 +08:00
82ca8e4696
Consolidate the code between visit, command marshalling, and event generation that iterates over the members of a struct. It reduces code duplication in the generator, so that a future patch can reduce the size of generated code while touching only one instead of three locations. There are no changes to the generated marshal code. The visitor code becomes slightly more verbose, but remains semantically equivalent, and is actually easier to read as it follows a more common idiom: | visit_optional(v, &(*obj)->has_device, "device", &err); |- if (!err && (*obj)->has_device) { |- visit_type_str(v, &(*obj)->device, "device", &err); |- } | if (err) { | goto out; | } |+ if ((*obj)->has_device) { |+ visit_type_str(v, &(*obj)->device, "device", &err); |+ if (err) { |+ goto out; |+ } |+ } The event code becomes slightly more verbose, but this is arguably a bug fix: although the visitors are not well documented, use of an optional member should not be attempted unless guarded by a prior call to visit_optional(). Works only because the output qmp visitor has a no-op visit_optional(): |+ visit_optional(v, &has_offset, "offset", &err); |+ if (err) { |+ goto out; |+ } | if (has_offset) { | visit_type_int(v, &offset, "offset", &err); Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1443565276-4535-17-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
330 lines
7.5 KiB
Python
330 lines
7.5 KiB
Python
#
|
|
# QAPI command marshaller generator
|
|
#
|
|
# Copyright IBM, Corp. 2011
|
|
# Copyright (C) 2014-2015 Red Hat, Inc.
|
|
#
|
|
# Authors:
|
|
# Anthony Liguori <aliguori@us.ibm.com>
|
|
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
|
# Markus Armbruster <armbru@redhat.com>
|
|
#
|
|
# This work is licensed under the terms of the GNU GPL, version 2.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
from qapi import *
|
|
import re
|
|
|
|
|
|
def gen_command_decl(name, arg_type, 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'))
|
|
|
|
|
|
def gen_call(name, arg_type, ret_type):
|
|
ret = ''
|
|
|
|
argstr = ''
|
|
if arg_type:
|
|
for memb in arg_type.members:
|
|
if memb.optional:
|
|
argstr += 'has_%s, ' % c_name(memb.name)
|
|
argstr += '%s, ' % c_name(memb.name)
|
|
|
|
lhs = ''
|
|
if ret_type:
|
|
lhs = 'retval = '
|
|
|
|
ret = mcgen('''
|
|
|
|
%(lhs)sqmp_%(c_name)s(%(args)s&err);
|
|
''',
|
|
c_name=c_name(name), args=argstr, lhs=lhs)
|
|
if ret_type:
|
|
ret += gen_err_check()
|
|
ret += mcgen('''
|
|
|
|
qmp_marshal_output_%(c_name)s(retval, ret, &err);
|
|
''',
|
|
c_name=ret_type.c_name())
|
|
return ret
|
|
|
|
|
|
def gen_marshal_vars(arg_type, ret_type):
|
|
ret = mcgen('''
|
|
Error *err = NULL;
|
|
''')
|
|
|
|
if ret_type:
|
|
ret += mcgen('''
|
|
%(c_type)s retval;
|
|
''',
|
|
c_type=ret_type.c_type())
|
|
|
|
if arg_type:
|
|
ret += mcgen('''
|
|
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
|
|
QapiDeallocVisitor *qdv;
|
|
Visitor *v;
|
|
''')
|
|
|
|
for memb in arg_type.members:
|
|
if memb.optional:
|
|
ret += mcgen('''
|
|
bool has_%(c_name)s = false;
|
|
''',
|
|
c_name=c_name(memb.name))
|
|
ret += mcgen('''
|
|
%(c_type)s %(c_name)s = %(c_null)s;
|
|
''',
|
|
c_name=c_name(memb.name),
|
|
c_type=memb.type.c_type(),
|
|
c_null=memb.type.c_null())
|
|
ret += '\n'
|
|
else:
|
|
ret += mcgen('''
|
|
|
|
(void)args;
|
|
''')
|
|
|
|
return ret
|
|
|
|
|
|
def gen_marshal_input_visit(arg_type, dealloc=False):
|
|
ret = ''
|
|
|
|
if not arg_type:
|
|
return ret
|
|
|
|
if dealloc:
|
|
errarg = None
|
|
ret += mcgen('''
|
|
qmp_input_visitor_cleanup(qiv);
|
|
qdv = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(qdv);
|
|
''')
|
|
else:
|
|
errarg = 'err'
|
|
ret += mcgen('''
|
|
v = qmp_input_get_visitor(qiv);
|
|
''')
|
|
|
|
ret += gen_visit_fields(arg_type.members, errarg=errarg)
|
|
|
|
if dealloc:
|
|
ret += mcgen('''
|
|
qapi_dealloc_visitor_cleanup(qdv);
|
|
''')
|
|
return ret
|
|
|
|
|
|
def gen_marshal_output(ret_type):
|
|
return mcgen('''
|
|
|
|
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
QmpOutputVisitor *qov = qmp_output_visitor_new();
|
|
QapiDeallocVisitor *qdv;
|
|
Visitor *v;
|
|
|
|
v = qmp_output_get_visitor(qov);
|
|
visit_type_%(c_name)s(v, &ret_in, "unused", &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
*ret_out = qmp_output_get_qobject(qov);
|
|
|
|
out:
|
|
error_propagate(errp, err);
|
|
qmp_output_visitor_cleanup(qov);
|
|
qdv = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(qdv);
|
|
visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
|
|
qapi_dealloc_visitor_cleanup(qdv);
|
|
}
|
|
''',
|
|
c_type=ret_type.c_type(), c_name=ret_type.c_name())
|
|
|
|
|
|
def gen_marshal_proto(name):
|
|
ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
|
|
if not middle_mode:
|
|
ret = 'static ' + ret
|
|
return ret
|
|
|
|
|
|
def gen_marshal_decl(name):
|
|
return mcgen('''
|
|
%(proto)s;
|
|
''',
|
|
proto=gen_marshal_proto(name))
|
|
|
|
|
|
def gen_marshal(name, arg_type, ret_type):
|
|
ret = mcgen('''
|
|
|
|
%(proto)s
|
|
{
|
|
''',
|
|
proto=gen_marshal_proto(name))
|
|
|
|
ret += gen_marshal_vars(arg_type, ret_type)
|
|
ret += gen_marshal_input_visit(arg_type)
|
|
ret += gen_call(name, arg_type, ret_type)
|
|
|
|
if re.search('^ *goto out;', ret, re.MULTILINE):
|
|
ret += mcgen('''
|
|
|
|
out:
|
|
''')
|
|
ret += mcgen('''
|
|
error_propagate(errp, err);
|
|
''')
|
|
ret += gen_marshal_input_visit(arg_type, dealloc=True)
|
|
ret += mcgen('''
|
|
}
|
|
''')
|
|
return ret
|
|
|
|
|
|
def gen_register_command(name, success_response):
|
|
options = 'QCO_NO_OPTIONS'
|
|
if not success_response:
|
|
options = 'QCO_NO_SUCCESS_RESP'
|
|
|
|
ret = mcgen('''
|
|
qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
|
|
''',
|
|
name=name, c_name=c_name(name),
|
|
opts=options)
|
|
return ret
|
|
|
|
|
|
def gen_registry(registry):
|
|
ret = mcgen('''
|
|
|
|
static void qmp_init_marshal(void)
|
|
{
|
|
''')
|
|
ret += registry
|
|
ret += mcgen('''
|
|
}
|
|
|
|
qapi_init(qmp_init_marshal);
|
|
''')
|
|
return ret
|
|
|
|
|
|
class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
|
|
def __init__(self):
|
|
self.decl = None
|
|
self.defn = None
|
|
self._regy = None
|
|
self._visited_ret_types = None
|
|
|
|
def visit_begin(self, schema):
|
|
self.decl = ''
|
|
self.defn = ''
|
|
self._regy = ''
|
|
self._visited_ret_types = set()
|
|
|
|
def visit_end(self):
|
|
if not middle_mode:
|
|
self.defn += gen_registry(self._regy)
|
|
self._regy = None
|
|
self._visited_ret_types = None
|
|
|
|
def visit_command(self, name, info, arg_type, ret_type,
|
|
gen, success_response):
|
|
if not gen:
|
|
return
|
|
self.decl += gen_command_decl(name, arg_type, ret_type)
|
|
if ret_type 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)
|
|
if not middle_mode:
|
|
self._regy += gen_register_command(name, success_response)
|
|
|
|
|
|
middle_mode = False
|
|
|
|
(input_file, output_dir, do_c, do_h, prefix, opts) = \
|
|
parse_command_line("m", ["middle"])
|
|
|
|
for o, a in opts:
|
|
if o in ("-m", "--middle"):
|
|
middle_mode = True
|
|
|
|
c_comment = '''
|
|
/*
|
|
* schema-defined QMP->QAPI command dispatch
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
*
|
|
*/
|
|
'''
|
|
h_comment = '''
|
|
/*
|
|
* schema-defined QAPI function prototypes
|
|
*
|
|
* Copyright IBM, Corp. 2011
|
|
*
|
|
* Authors:
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
*
|
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
*
|
|
*/
|
|
'''
|
|
|
|
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
|
|
'qmp-marshal.c', 'qmp-commands.h',
|
|
c_comment, h_comment)
|
|
|
|
fdef.write(mcgen('''
|
|
#include "qemu-common.h"
|
|
#include "qemu/module.h"
|
|
#include "qapi/qmp/types.h"
|
|
#include "qapi/qmp/dispatch.h"
|
|
#include "qapi/visitor.h"
|
|
#include "qapi/qmp-output-visitor.h"
|
|
#include "qapi/qmp-input-visitor.h"
|
|
#include "qapi/dealloc-visitor.h"
|
|
#include "%(prefix)sqapi-types.h"
|
|
#include "%(prefix)sqapi-visit.h"
|
|
#include "%(prefix)sqmp-commands.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
fdecl.write(mcgen('''
|
|
#include "%(prefix)sqapi-types.h"
|
|
#include "qapi/qmp/qdict.h"
|
|
#include "qapi/error.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
schema = QAPISchema(input_file)
|
|
gen = QAPISchemaGenCommandVisitor()
|
|
schema.visit(gen)
|
|
fdef.write(gen.defn)
|
|
fdecl.write(gen.decl)
|
|
|
|
close_output(fdef, fdecl)
|