f9e6102b48
We were using regular expressions to see if ret included any earlier text that emitted a 'goto out;' line, to decide whether we needed to output an 'out:' label. But this is fragile, if the ret text can possibly combine more than one generated function body, where the first function used a goto but the second does not. Change the code to just check for the known conditions which cause an error check to be needed. Besides, it's slightly more efficient to use plain checks than regular expression searching. No change to generated code. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1445898903-12082-4-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
330 lines
7.6 KiB
Python
330 lines
7.6 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:
|
|
ret += mcgen('''
|
|
qmp_input_visitor_cleanup(qiv);
|
|
qdv = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(qdv);
|
|
''')
|
|
else:
|
|
ret += mcgen('''
|
|
v = qmp_input_get_visitor(qiv);
|
|
''')
|
|
|
|
ret += gen_visit_fields(arg_type.members, skiperr=dealloc)
|
|
|
|
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)
|
|
|
|
# 'goto out' produced by gen_marshal_input_visit->gen_visit_fields()
|
|
# for each arg_type member, and by gen_call() for ret_type
|
|
if (arg_type and arg_type.members) or ret_type:
|
|
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)
|