mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 18:05:37 +00:00
![Markus Armbruster](/assets/img/avatar_default.png)
The QAPI code generators work with a syntax tree (nested dictionaries) plus a few symbol tables (also dictionaries) on the side. They have clearly outgrown these simple data structures. There's lots of rummaging around in dictionaries, and information is recomputed on the fly. For the work I'm going to do, I want more clearly defined and more convenient interfaces. Going forward, I also want less coupling between the back-ends and the syntax tree, to make messing with the syntax easier. Create a bunch of classes to represent QAPI schemata. Have the QAPISchema initializer call the parser, then walk the syntax tree to create the new internal representation, and finally perform semantic analysis. Shortcut: the semantic analysis still relies on existing check_exprs() to do the actual semantic checking. All this code needs to move into the classes. Mark as TODO. Simple unions are lowered to flat unions. Flat unions and structs are represented as a more general object type. Catching name collisions in generated code would be nice. Mark as TODO. We generate array types eagerly, even though most of them aren't used. Mark as TODO. Nothing uses the new intermediate representation just yet, thus no change to generated files. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Daniel P. Berrange <berrange@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
360 lines
8.7 KiB
Python
360 lines
8.7 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 ordereddict import OrderedDict
|
|
from qapi import *
|
|
import re
|
|
|
|
def generate_command_decl(name, args, ret_type):
|
|
arglist=""
|
|
for argname, argtype, optional in parse_args(args):
|
|
argtype = c_type(argtype, is_param=True)
|
|
if optional:
|
|
arglist += "bool has_%s, " % c_name(argname)
|
|
arglist += "%s %s, " % (argtype, c_name(argname))
|
|
return mcgen('''
|
|
%(ret_type)s qmp_%(name)s(%(args)sError **errp);
|
|
''',
|
|
ret_type=c_type(ret_type), name=c_name(name),
|
|
args=arglist)
|
|
|
|
def gen_err_check(err):
|
|
if not err:
|
|
return ''
|
|
return mcgen('''
|
|
if (%(err)s) {
|
|
goto out;
|
|
}
|
|
''',
|
|
err=err)
|
|
|
|
def gen_sync_call(name, args, ret_type):
|
|
ret = ""
|
|
arglist=""
|
|
retval=""
|
|
if ret_type:
|
|
retval = "retval = "
|
|
for argname, argtype, optional in parse_args(args):
|
|
if optional:
|
|
arglist += "has_%s, " % c_name(argname)
|
|
arglist += "%s, " % (c_name(argname))
|
|
push_indent()
|
|
ret = mcgen('''
|
|
%(retval)sqmp_%(name)s(%(args)s&local_err);
|
|
''',
|
|
name=c_name(name), args=arglist, retval=retval)
|
|
if ret_type:
|
|
ret += gen_err_check('local_err')
|
|
ret += mcgen('''
|
|
|
|
qmp_marshal_output_%(c_name)s(retval, ret, &local_err);
|
|
''',
|
|
c_name=c_name(name))
|
|
pop_indent()
|
|
return ret
|
|
|
|
def gen_visitor_input_containers_decl(args):
|
|
ret = ""
|
|
|
|
push_indent()
|
|
if len(args) > 0:
|
|
ret += mcgen('''
|
|
QmpInputVisitor *mi = qmp_input_visitor_new_strict(QOBJECT(args));
|
|
QapiDeallocVisitor *md;
|
|
Visitor *v;
|
|
''')
|
|
pop_indent()
|
|
|
|
return ret
|
|
|
|
def gen_visitor_input_vars_decl(args):
|
|
ret = ""
|
|
push_indent()
|
|
for argname, argtype, optional in parse_args(args):
|
|
if optional:
|
|
ret += mcgen('''
|
|
bool has_%(argname)s = false;
|
|
''',
|
|
argname=c_name(argname))
|
|
if is_c_ptr(argtype):
|
|
ret += mcgen('''
|
|
%(argtype)s %(argname)s = NULL;
|
|
''',
|
|
argname=c_name(argname), argtype=c_type(argtype))
|
|
else:
|
|
ret += mcgen('''
|
|
%(argtype)s %(argname)s = {0};
|
|
''',
|
|
argname=c_name(argname), argtype=c_type(argtype))
|
|
|
|
pop_indent()
|
|
return ret
|
|
|
|
def gen_visitor_input_block(args, dealloc=False):
|
|
ret = ""
|
|
errparg = '&local_err'
|
|
errarg = 'local_err'
|
|
|
|
if len(args) == 0:
|
|
return ret
|
|
|
|
push_indent()
|
|
|
|
if dealloc:
|
|
errparg = 'NULL'
|
|
errarg = None;
|
|
ret += mcgen('''
|
|
qmp_input_visitor_cleanup(mi);
|
|
md = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(md);
|
|
''')
|
|
else:
|
|
ret += mcgen('''
|
|
v = qmp_input_get_visitor(mi);
|
|
''')
|
|
|
|
for argname, argtype, optional in parse_args(args):
|
|
if optional:
|
|
ret += mcgen('''
|
|
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
|
''',
|
|
c_name=c_name(argname), name=argname, errp=errparg)
|
|
ret += gen_err_check(errarg)
|
|
ret += mcgen('''
|
|
if (has_%(c_name)s) {
|
|
''',
|
|
c_name=c_name(argname))
|
|
push_indent()
|
|
ret += mcgen('''
|
|
visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
|
''',
|
|
c_name=c_name(argname), name=argname, argtype=argtype,
|
|
visitor=type_name(argtype), errp=errparg)
|
|
ret += gen_err_check(errarg)
|
|
if optional:
|
|
pop_indent()
|
|
ret += mcgen('''
|
|
}
|
|
''')
|
|
|
|
if dealloc:
|
|
ret += mcgen('''
|
|
qapi_dealloc_visitor_cleanup(md);
|
|
''')
|
|
pop_indent()
|
|
return ret
|
|
|
|
def gen_marshal_output(name, ret_type):
|
|
if not ret_type:
|
|
return ""
|
|
|
|
ret = mcgen('''
|
|
static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp)
|
|
{
|
|
Error *local_err = NULL;
|
|
QmpOutputVisitor *mo = qmp_output_visitor_new();
|
|
QapiDeallocVisitor *md;
|
|
Visitor *v;
|
|
|
|
v = qmp_output_get_visitor(mo);
|
|
visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
|
|
if (local_err) {
|
|
goto out;
|
|
}
|
|
*ret_out = qmp_output_get_qobject(mo);
|
|
|
|
out:
|
|
error_propagate(errp, local_err);
|
|
qmp_output_visitor_cleanup(mo);
|
|
md = qapi_dealloc_visitor_new();
|
|
v = qapi_dealloc_get_visitor(md);
|
|
visit_type_%(visitor)s(v, &ret_in, "unused", NULL);
|
|
qapi_dealloc_visitor_cleanup(md);
|
|
}
|
|
''',
|
|
c_ret_type=c_type(ret_type), c_name=c_name(name),
|
|
visitor=type_name(ret_type))
|
|
|
|
return ret
|
|
|
|
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, middle_mode)
|
|
|
|
ret = mcgen('''
|
|
%(header)s
|
|
{
|
|
Error *local_err = NULL;
|
|
''',
|
|
header=hdr)
|
|
|
|
if ret_type:
|
|
ret += mcgen('''
|
|
%(c_type)s retval;
|
|
''',
|
|
c_type=c_type(ret_type))
|
|
|
|
if len(args) > 0:
|
|
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 += gen_sync_call(name, args, ret_type)
|
|
|
|
if re.search('^ *goto out\\;', ret, re.MULTILINE):
|
|
ret += mcgen('''
|
|
|
|
out:
|
|
''')
|
|
ret += mcgen('''
|
|
error_propagate(errp, local_err);
|
|
''')
|
|
ret += gen_visitor_input_block(args, dealloc=True)
|
|
ret += mcgen('''
|
|
}
|
|
''')
|
|
return ret
|
|
|
|
def gen_registry(commands):
|
|
registry=""
|
|
push_indent()
|
|
for cmd in commands:
|
|
options = 'QCO_NO_OPTIONS'
|
|
if not cmd.get('success-response', True):
|
|
options = 'QCO_NO_SUCCESS_RESP'
|
|
|
|
registry += mcgen('''
|
|
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
|
|
''',
|
|
name=cmd['command'], c_name=c_name(cmd['command']),
|
|
opts=options)
|
|
pop_indent()
|
|
ret = mcgen('''
|
|
static void qmp_init_marshal(void)
|
|
{
|
|
''')
|
|
ret += registry
|
|
ret += mcgen('''
|
|
}
|
|
|
|
qapi_init(qmp_init_marshal);
|
|
''')
|
|
return ret
|
|
|
|
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
|
|
|
|
exprs = QAPISchema(input_file).get_exprs()
|
|
commands = filter(lambda expr: expr.has_key('command'), exprs)
|
|
commands = filter(lambda expr: not expr.has_key('gen'), commands)
|
|
|
|
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))
|
|
|
|
for cmd in commands:
|
|
arglist = []
|
|
ret_type = None
|
|
if cmd.has_key('data'):
|
|
arglist = cmd['data']
|
|
if cmd.has_key('returns'):
|
|
ret_type = cmd['returns']
|
|
ret = generate_command_decl(cmd['command'], arglist, ret_type)
|
|
fdecl.write(ret)
|
|
if ret_type:
|
|
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'], middle_mode))
|
|
|
|
ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n"
|
|
fdef.write(ret)
|
|
|
|
if not middle_mode:
|
|
ret = gen_registry(commands)
|
|
fdef.write(ret)
|
|
|
|
close_output(fdef, fdecl)
|