aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2015-09-21 22:33:51 +0100
committerPeter Maydell <peter.maydell@linaro.org>2015-09-21 22:33:51 +0100
commit9e72681d16792d0ffc42bab634b1753ff299bdfd (patch)
tree4b73c0b5685250599051e7f0c20ddfad784972da /scripts
parent75ebcd7f080fa30893272f6fe07354e4ffa11b46 (diff)
parent1a9a507b2e3e90aa719c96b4c092e7fad7215f21 (diff)
downloadqemu-9e72681d16792d0ffc42bab634b1753ff299bdfd.zip
qemu-9e72681d16792d0ffc42bab634b1753ff299bdfd.tar.gz
qemu-9e72681d16792d0ffc42bab634b1753ff299bdfd.tar.bz2
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2015-09-21' into staging
qapi: QMP introspection # gpg: Signature made Mon 21 Sep 2015 08:59:17 BST using RSA key ID EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" * remotes/armbru/tags/pull-qapi-2015-09-21: (26 commits) qapi-introspect: Hide type names qapi: New QMP command query-qmp-schema for QMP introspection qapi: Pseudo-type '**' is now unused, drop it qapi-schema: Fix up misleading specification of netdev_add qom: Don't use 'gen': false for qom-get, qom-set, object-add qapi: Introduce a first class 'any' type qapi: Make output visitor return qnull() instead of NULL qapi: Improve built-in type documentation qapi-commands: De-duplicate output marshaling functions qapi: De-duplicate parameter list generation qapi: Rename qmp_marshal_input_FOO() to qmp_marshal_FOO() qapi-commands: Rearrange code qapi-visit: Rearrange code a bit qapi: Clean up after recent conversions to QAPISchemaVisitor qapi: Replace dirty is_c_ptr() by method c_null() qapi-event: Convert to QAPISchemaVisitor, fixing data with base qapi-event: Eliminate global variable event_enum_value qapi: De-duplicate enum code generation qapi-commands: Convert to QAPISchemaVisitor qapi-visit: Convert to QAPISchemaVisitor, fixing bugs ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'scripts')
-rw-r--r--scripts/qapi-commands.py284
-rw-r--r--scripts/qapi-event.py235
-rw-r--r--scripts/qapi-introspect.py213
-rw-r--r--scripts/qapi-types.py377
-rw-r--r--scripts/qapi-visit.py369
-rw-r--r--scripts/qapi.py702
6 files changed, 1361 insertions, 819 deletions
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index 890ce5d..810a897 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -12,22 +12,18 @@
# 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))
+
+def gen_command_decl(name, arg_type, ret_type):
return mcgen('''
-%(ret_type)s qmp_%(name)s(%(args)sError **errp);
+%(c_type)s qmp_%(c_name)s(%(params)s);
''',
- ret_type=c_type(ret_type), name=c_name(name),
- args=arglist)
+ 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_err_check(err):
if not err:
@@ -39,110 +35,124 @@ if (%(err)s) {
''',
err=err)
-def gen_sync_call(name, args, ret_type):
- ret = ""
- arglist=""
- retval=""
+
+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:
- retval = "retval = "
- for argname, argtype, optional in parse_args(args):
- if optional:
- arglist += "has_%s, " % c_name(argname)
- arglist += "%s, " % (c_name(argname))
+ lhs = 'retval = '
+
push_indent()
ret = mcgen('''
-%(retval)sqmp_%(name)s(%(args)s&local_err);
+
+%(lhs)sqmp_%(c_name)s(%(args)s&local_err);
''',
- name=c_name(name), args=arglist, retval=retval)
+ c_name=c_name(name), args=argstr, lhs=lhs)
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))
+ c_name=ret_type.c_name())
pop_indent()
return ret
-def gen_visitor_input_containers_decl(args):
- ret = ""
+
+def gen_marshal_vars(arg_type, ret_type):
+ ret = mcgen('''
+ Error *local_err = NULL;
+''')
push_indent()
- if len(args) > 0:
+
+ if ret_type:
+ ret += mcgen('''
+%(c_type)s retval;
+''',
+ c_type=ret_type.c_type())
+
+ if arg_type:
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;
+ for memb in arg_type.members:
+ if memb.optional:
+ ret += mcgen('''
+bool has_%(c_name)s = false;
''',
- argname=c_name(argname), argtype=c_type(argtype))
- else:
+ c_name=c_name(memb.name))
ret += mcgen('''
-%(argtype)s %(argname)s = {0};
+%(c_type)s %(c_name)s = %(c_null)s;
''',
- argname=c_name(argname), argtype=c_type(argtype))
+ 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;
+''')
pop_indent()
return ret
-def gen_visitor_input_block(args, dealloc=False):
- ret = ""
- errparg = '&local_err'
- errarg = 'local_err'
- if len(args) == 0:
+def gen_marshal_input_visit(arg_type, dealloc=False):
+ ret = ''
+
+ if not arg_type:
return ret
push_indent()
if dealloc:
errparg = 'NULL'
- errarg = None;
+ errarg = None
ret += mcgen('''
qmp_input_visitor_cleanup(mi);
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
''')
else:
+ errparg = '&local_err'
+ errarg = 'local_err'
ret += mcgen('''
v = qmp_input_get_visitor(mi);
''')
- for argname, argtype, optional in parse_args(args):
- if optional:
+ for memb in arg_type.members:
+ if memb.optional:
ret += mcgen('''
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
''',
- c_name=c_name(argname), name=argname, errp=errparg)
+ c_name=c_name(memb.name), name=memb.name,
+ errp=errparg)
ret += gen_err_check(errarg)
ret += mcgen('''
if (has_%(c_name)s) {
''',
- c_name=c_name(argname))
+ c_name=c_name(memb.name))
push_indent()
ret += mcgen('''
-visit_type_%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
+visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
''',
- c_name=c_name(argname), name=argname, argtype=argtype,
- visitor=type_name(argtype), errp=errparg)
+ c_name=c_name(memb.name), name=memb.name,
+ c_type=memb.type.c_name(), errp=errparg)
ret += gen_err_check(errarg)
- if optional:
+ if memb.optional:
pop_indent()
ret += mcgen('''
}
@@ -155,12 +165,11 @@ 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)
+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 *local_err = NULL;
QmpOutputVisitor *mo = qmp_output_visitor_new();
@@ -168,7 +177,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_o
Visitor *v;
v = qmp_output_get_visitor(mo);
- visit_type_%(visitor)s(v, &ret_in, "unused", &local_err);
+ visit_type_%(c_name)s(v, &ret_in, "unused", &local_err);
if (local_err) {
goto out;
}
@@ -179,51 +188,40 @@ out:
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);
+ visit_type_%(c_name)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))
+ c_type=ret_type.c_type(), c_name=ret_type.c_name())
- 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)
+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
+ 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;
+def gen_marshal_decl(name):
+ return mcgen('''
+%(proto)s;
''',
- c_type=c_type(ret_type))
+ proto=gen_marshal_proto(name))
- 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;
+def gen_marshal(name, arg_type, ret_type):
+ ret = mcgen('''
-''')
+%(proto)s
+{
+''',
+ proto=gen_marshal_proto(name))
- ret += gen_sync_call(name, args, ret_type)
+ 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):
+ if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
out:
@@ -231,27 +229,31 @@ out:
ret += mcgen('''
error_propagate(errp, local_err);
''')
- ret += gen_visitor_input_block(args, dealloc=True)
+ ret += gen_marshal_input_visit(arg_type, dealloc=True)
ret += mcgen('''
}
''')
return ret
-def gen_registry(commands):
- registry=""
+
+def gen_register_command(name, success_response):
push_indent()
- for cmd in commands:
- options = 'QCO_NO_OPTIONS'
- if not cmd.get('success-response', True):
- options = 'QCO_NO_SUCCESS_RESP'
+ options = 'QCO_NO_OPTIONS'
+ if not success_response:
+ options = 'QCO_NO_SUCCESS_RESP'
- registry += mcgen('''
-qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
+ ret = mcgen('''
+qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
''',
- name=cmd['command'], c_name=c_name(cmd['command']),
- opts=options)
+ name=name, c_name=c_name(name),
+ opts=options)
pop_indent()
+ return ret
+
+
+def gen_registry(registry):
ret = mcgen('''
+
static void qmp_init_marshal(void)
{
''')
@@ -263,6 +265,41 @@ 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) = \
@@ -272,10 +309,6 @@ for o, a in opts:
if o in ("-m", "--middle"):
middle_mode = True
-exprs = parse_schema(input_file)
-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
@@ -323,7 +356,7 @@ fdef.write(mcgen('''
#include "%(prefix)sqmp-commands.h"
''',
- prefix=prefix))
+ prefix=prefix))
fdecl.write(mcgen('''
#include "%(prefix)sqapi-types.h"
@@ -331,29 +364,12 @@ fdecl.write(mcgen('''
#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)
+ prefix=prefix))
-if not middle_mode:
- ret = gen_registry(commands)
- fdef.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenCommandVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
close_output(fdef, fdecl)
diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py
index 7f238df..d15fad9 100644
--- a/scripts/qapi-event.py
+++ b/scripts/qapi-event.py
@@ -2,78 +2,64 @@
# QAPI event generator
#
# Copyright (c) 2014 Wenchao Xia
+# Copyright (c) 2015 Red Hat Inc.
#
# Authors:
# Wenchao Xia <wenchaoqemu@gmail.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 *
-def _generate_event_api_name(event_name, params):
- api_name = "void qapi_event_send_%s(" % c_name(event_name).lower();
- l = len(api_name)
- if params:
- for argname, argentry, optional in parse_args(params):
- if optional:
- api_name += "bool has_%s,\n" % c_name(argname)
- api_name += "".ljust(l)
+def gen_event_send_proto(name, arg_type):
+ return 'void qapi_event_send_%(c_name)s(%(param)s)' % {
+ 'c_name': c_name(name.lower()),
+ 'param': gen_params(arg_type, 'Error **errp')}
- api_name += "%s %s,\n" % (c_type(argentry, is_param=True),
- c_name(argname))
- api_name += "".ljust(l)
- api_name += "Error **errp)"
- return api_name;
-
-
-# Following are the core functions that generate C APIs to emit event.
-
-def generate_event_declaration(api_name):
+def gen_event_send_decl(name, arg_type):
return mcgen('''
-%(api_name)s;
+%(proto)s;
''',
- api_name = api_name)
+ proto=gen_event_send_proto(name, arg_type))
-def generate_event_implement(api_name, event_name, params):
- # step 1: declare any variables
- ret = mcgen("""
-%(api_name)s
+def gen_event_send(name, arg_type):
+ ret = mcgen('''
+
+%(proto)s
{
QDict *qmp;
Error *local_err = NULL;
QMPEventFuncEmit emit;
-""",
- api_name = api_name)
+''',
+ proto=gen_event_send_proto(name, arg_type))
- if params:
- ret += mcgen("""
+ if arg_type and arg_type.members:
+ ret += mcgen('''
QmpOutputVisitor *qov;
Visitor *v;
QObject *obj;
-""")
+''')
- # step 2: check emit function, create a dict
- ret += mcgen("""
+ ret += mcgen('''
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
- qmp = qmp_event_build_dict("%(event_name)s");
+ qmp = qmp_event_build_dict("%(name)s");
-""",
- event_name = event_name)
+''',
+ name=name)
- # step 3: visit the params if params != None
- if params:
- ret += mcgen("""
+ if arg_type and arg_type.members:
+ ret += mcgen('''
qov = qmp_output_visitor_new();
g_assert(qov);
@@ -81,45 +67,46 @@ def generate_event_implement(api_name, event_name, params):
g_assert(v);
/* Fake visit, as if all members are under a structure */
- visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
+ visit_start_struct(v, NULL, "", "%(name)s", 0, &local_err);
if (local_err) {
goto clean;
}
-""",
- event_name = event_name)
+''',
+ name=name)
- for argname, argentry, optional in parse_args(params):
- if optional:
- ret += mcgen("""
- if (has_%(var)s) {
-""",
- var = c_name(argname))
+ for memb in arg_type.members:
+ if memb.optional:
+ ret += mcgen('''
+ if (has_%(c_name)s) {
+''',
+ c_name=c_name(memb.name))
push_indent()
- if argentry == "str":
- var_type = "(char **)"
+ # Ugly: need to cast away the const
+ if memb.type.name == "str":
+ cast = '(char **)'
else:
- var_type = ""
+ cast = ''
- ret += mcgen("""
- visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
+ ret += mcgen('''
+ visit_type_%(c_type)s(v, %(cast)s&%(c_name)s, "%(name)s", &local_err);
if (local_err) {
goto clean;
}
-""",
- var_type = var_type,
- var = c_name(argname),
- type = type_name(argentry),
- name = argname)
+''',
+ cast=cast,
+ c_name=c_name(memb.name),
+ c_type=memb.type.c_name(),
+ name=memb.name)
- if optional:
+ if memb.optional:
pop_indent()
- ret += mcgen("""
+ ret += mcgen('''
}
-""")
+''')
- ret += mcgen("""
+ ret += mcgen('''
visit_end_struct(v, &local_err);
if (local_err) {
@@ -130,85 +117,48 @@ def generate_event_implement(api_name, event_name, params):
g_assert(obj != NULL);
qdict_put_obj(qmp, "data", obj);
-""")
+''')
- # step 4: call qmp event api
- ret += mcgen("""
- emit(%(event_enum_value)s, qmp, &local_err);
+ ret += mcgen('''
+ emit(%(c_enum)s, qmp, &local_err);
-""",
- event_enum_value = event_enum_value)
+''',
+ c_enum=c_enum_const(event_enum_name, name))
- # step 5: clean up
- if params:
- ret += mcgen("""
+ if arg_type and arg_type.members:
+ ret += mcgen('''
clean:
qmp_output_visitor_cleanup(qov);
-""")
- ret += mcgen("""
+''')
+ ret += mcgen('''
error_propagate(errp, local_err);
QDECREF(qmp);
}
-""")
-
+''')
return ret
-# Following are the functions that generate an enum type for all defined
-# events, similar to qapi-types.py. Here we already have enum name and
-# values which were generated before and recorded in event_enum_*. It also
-# works around the issue that "import qapi-types" can't work.
-
-def generate_event_enum_decl(event_enum_name, event_enum_values):
- lookup_decl = mcgen('''
-
-extern const char *%(event_enum_name)s_lookup[];
-''',
- event_enum_name = event_enum_name)
-
- enum_decl = mcgen('''
-typedef enum %(event_enum_name)s {
-''',
- event_enum_name = event_enum_name)
-
- # append automatically generated _MAX value
- enum_max_value = c_enum_const(event_enum_name, "MAX")
- enum_values = event_enum_values + [ enum_max_value ]
-
- i = 0
- for value in enum_values:
- enum_decl += mcgen('''
- %(value)s = %(i)d,
-''',
- value = value,
- i = i)
- i += 1
-
- enum_decl += mcgen('''
-} %(event_enum_name)s;
-''',
- event_enum_name = event_enum_name)
-
- return lookup_decl + enum_decl
+class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
+ def __init__(self):
+ self.decl = None
+ self.defn = None
+ self._event_names = None
-def generate_event_enum_lookup(event_enum_name, event_enum_strings):
- ret = mcgen('''
+ def visit_begin(self, schema):
+ self.decl = ''
+ self.defn = ''
+ self._event_names = []
-const char *%(event_enum_name)s_lookup[] = {
-''',
- event_enum_name = event_enum_name)
+ def visit_end(self):
+ self.decl += gen_enum(event_enum_name, self._event_names)
+ self.defn += gen_enum_lookup(event_enum_name, self._event_names)
+ self._event_names = None
- for string in event_enum_strings:
- ret += mcgen('''
- "%(string)s",
-''',
- string = string)
+ 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)
+ self._event_names.append(name)
- ret += mcgen('''
- NULL,
-};
-''')
- return ret
(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
@@ -263,35 +213,12 @@ fdecl.write(mcgen('''
''',
prefix=prefix))
-exprs = parse_schema(input_file)
-
event_enum_name = c_name(prefix + "QAPIEvent", protect=False)
-event_enum_values = []
-event_enum_strings = []
-
-for expr in exprs:
- if expr.has_key('event'):
- event_name = expr['event']
- params = expr.get('data')
- if params and len(params) == 0:
- params = None
-
- api_name = _generate_event_api_name(event_name, params)
- ret = generate_event_declaration(api_name)
- fdecl.write(ret)
-
- # We need an enum value per event
- event_enum_value = c_enum_const(event_enum_name, event_name)
- ret = generate_event_implement(api_name, event_name, params)
- fdef.write(ret)
-
- # Record it, and generate enum later
- event_enum_values.append(event_enum_value)
- event_enum_strings.append(event_name)
-
-ret = generate_event_enum_decl(event_enum_name, event_enum_values)
-fdecl.write(ret)
-ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
-fdef.write(ret)
+
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenEventVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
close_output(fdef, fdecl)
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
new file mode 100644
index 0000000..7d39320
--- /dev/null
+++ b/scripts/qapi-introspect.py
@@ -0,0 +1,213 @@
+#
+# QAPI introspection generator
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Authors:
+# 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 *
+
+
+# Caveman's json.dumps() replacement (we're stuck at Python 2.4)
+# TODO try to use json.dumps() once we get unstuck
+def to_json(obj, level=0):
+ if obj is None:
+ ret = 'null'
+ elif isinstance(obj, str):
+ ret = '"' + obj.replace('"', r'\"') + '"'
+ elif isinstance(obj, list):
+ elts = [to_json(elt, level + 1)
+ for elt in obj]
+ ret = '[' + ', '.join(elts) + ']'
+ elif isinstance(obj, dict):
+ elts = ['"%s": %s' % (key.replace('"', r'\"'),
+ to_json(obj[key], level + 1))
+ for key in sorted(obj.keys())]
+ ret = '{' + ', '.join(elts) + '}'
+ else:
+ assert False # not implemented
+ if level == 1:
+ ret = '\n' + ret
+ return ret
+
+
+def to_c_string(string):
+ return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
+
+
+class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
+ def __init__(self, unmask):
+ self._unmask = unmask
+ self.defn = None
+ self.decl = None
+ self._schema = None
+ self._jsons = None
+ self._used_types = None
+ self._name_map = None
+
+ def visit_begin(self, schema):
+ self._schema = schema
+ self._jsons = []
+ self._used_types = []
+ self._name_map = {}
+ return QAPISchemaType # don't visit types for now
+
+ def visit_end(self):
+ # visit the types that are actually used
+ jsons = self._jsons
+ self._jsons = []
+ for typ in self._used_types:
+ typ.visit(self)
+ # generate C
+ # TODO can generate awfully long lines
+ jsons.extend(self._jsons)
+ name = prefix + 'qmp_schema_json'
+ self.decl = mcgen('''
+extern const char %(c_name)s[];
+''',
+ c_name=c_name(name))
+ lines = to_json(jsons).split('\n')
+ c_string = '\n '.join([to_c_string(line) for line in lines])
+ self.defn = mcgen('''
+const char %(c_name)s[] = %(c_string)s;
+''',
+ c_name=c_name(name),
+ c_string=c_string)
+ self._schema = None
+ self._jsons = None
+ self._used_types = None
+ self._name_map = None
+
+ def _name(self, name):
+ if self._unmask:
+ return name
+ if name not in self._name_map:
+ self._name_map[name] = '%d' % len(self._name_map)
+ return self._name_map[name]
+
+ def _use_type(self, typ):
+ # Map the various integer types to plain int
+ if typ.json_type() == 'int':
+ typ = self._schema.lookup_type('int')
+ elif (isinstance(typ, QAPISchemaArrayType) and
+ typ.element_type.json_type() == 'int'):
+ typ = self._schema.lookup_type('intList')
+ # Add type to work queue if new
+ if typ not in self._used_types:
+ self._used_types.append(typ)
+ # Clients should examine commands and events, not types. Hide
+ # type names to reduce the temptation. Also saves a few
+ # characters.
+ if isinstance(typ, QAPISchemaBuiltinType):
+ return typ.name
+ return self._name(typ.name)
+
+ def _gen_json(self, name, mtype, obj):
+ if mtype != 'command' and mtype != 'event' and mtype != 'builtin':
+ name = self._name(name)
+ obj['name'] = name
+ obj['meta-type'] = mtype
+ self._jsons.append(obj)
+
+ def _gen_member(self, member):
+ ret = {'name': member.name, 'type': self._use_type(member.type)}
+ if member.optional:
+ ret['default'] = None
+ return ret
+
+ def _gen_variants(self, tag_name, variants):
+ return {'tag': tag_name,
+ 'variants': [self._gen_variant(v) for v in variants]}
+
+ def _gen_variant(self, variant):
+ return {'case': variant.name, 'type': self._use_type(variant.type)}
+
+ def visit_builtin_type(self, name, info, json_type):
+ self._gen_json(name, 'builtin', {'json-type': json_type})
+
+ def visit_enum_type(self, name, info, values, prefix):
+ self._gen_json(name, 'enum', {'values': values})
+
+ def visit_array_type(self, name, info, element_type):
+ self._gen_json(name, 'array',
+ {'element-type': self._use_type(element_type)})
+
+ def visit_object_type_flat(self, name, info, members, variants):
+ obj = {'members': [self._gen_member(m) for m in members]}
+ if variants:
+ obj.update(self._gen_variants(variants.tag_member.name,
+ variants.variants))
+ self._gen_json(name, 'object', obj)
+
+ def visit_alternate_type(self, name, info, variants):
+ self._gen_json(name, 'alternate',
+ {'members': [{'type': self._use_type(m.type)}
+ for m in variants.variants]})
+
+ def visit_command(self, name, info, arg_type, ret_type,
+ gen, success_response):
+ 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):
+ arg_type = arg_type or self._schema.the_empty_object_type
+ self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)})
+
+# Debugging aid: unmask QAPI schema's type names
+# We normally mask them, because they're not QMP wire ABI
+opt_unmask = False
+
+(input_file, output_dir, do_c, do_h, prefix, opts) = \
+ parse_command_line("u", ["unmask-non-abi-names"])
+
+for o, a in opts:
+ if o in ("-u", "--unmask-non-abi-names"):
+ opt_unmask = True
+
+c_comment = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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 = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * 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-introspect.c', 'qmp-introspect.h',
+ c_comment, h_comment)
+
+fdef.write(mcgen('''
+#include "%(prefix)sqmp-introspect.h"
+
+''',
+ prefix=prefix))
+
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenIntrospectVisitor(opt_unmask)
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
+
+close_output(fdef, fdecl)
diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
index a8453d1..b292682 100644
--- a/scripts/qapi-types.py
+++ b/scripts/qapi-types.py
@@ -2,96 +2,81 @@
# QAPI types generator
#
# Copyright IBM, Corp. 2011
+# Copyright (c) 2013-2015 Red Hat Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.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 *
-def generate_fwd_builtin(name):
- return mcgen('''
-
-typedef struct %(name)sList {
- union {
- %(type)s value;
- uint64_t padding;
- };
- struct %(name)sList *next;
-} %(name)sList;
-''',
- type=c_type(name),
- name=name)
-def generate_fwd_struct(name):
+def gen_fwd_object_or_array(name):
return mcgen('''
-typedef struct %(name)s %(name)s;
-
-typedef struct %(name)sList {
- union {
- %(name)s *value;
- uint64_t padding;
- };
- struct %(name)sList *next;
-} %(name)sList;
+typedef struct %(c_name)s %(c_name)s;
''',
- name=c_name(name))
+ c_name=c_name(name))
-def generate_fwd_enum_struct(name):
+
+def gen_array(name, element_type):
return mcgen('''
-typedef struct %(name)sList {
+struct %(c_name)s {
union {
- %(name)s value;
+ %(c_type)s value;
uint64_t padding;
};
- struct %(name)sList *next;
-} %(name)sList;
+ %(c_name)s *next;
+};
''',
- name=c_name(name))
+ c_name=c_name(name), c_type=element_type.c_type())
-def generate_struct_fields(members):
+
+def gen_struct_field(name, typ, optional):
ret = ''
- for argname, argentry, optional in parse_args(members):
- if optional:
- ret += mcgen('''
+ if optional:
+ ret += mcgen('''
bool has_%(c_name)s;
''',
- c_name=c_name(argname))
- ret += mcgen('''
+ c_name=c_name(name))
+ ret += mcgen('''
%(c_type)s %(c_name)s;
''',
- c_type=c_type(argentry), c_name=c_name(argname))
-
+ c_type=typ.c_type(), c_name=c_name(name))
return ret
-def generate_struct(expr):
- structname = expr.get('struct', "")
- members = expr['data']
- base = expr.get('base')
+def gen_struct_fields(members):
+ ret = ''
+ for memb in members:
+ ret += gen_struct_field(memb.name, memb.type, memb.optional)
+ return ret
+
+
+def gen_struct(name, base, members):
ret = mcgen('''
-struct %(name)s {
+struct %(c_name)s {
''',
- name=c_name(structname))
+ c_name=c_name(name))
if base:
- ret += generate_struct_fields({'base': base})
+ ret += gen_struct_field('base', base, False)
- ret += generate_struct_fields(members)
+ ret += gen_struct_fields(members)
# Make sure that all structs have at least one field; this avoids
- # 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).
+ # 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 not members:
- ret += mcgen('''
+ ret += mcgen('''
char qapi_dummy_field_for_empty_struct;
''')
@@ -101,81 +86,32 @@ struct %(name)s {
return ret
-def generate_enum_lookup(name, values, prefix=None):
- ret = mcgen('''
-
-const char *const %(name)s_lookup[] = {
-''',
- name=c_name(name))
- for value in values:
- index = c_enum_const(name, value, prefix)
- ret += mcgen('''
- [%(index)s] = "%(value)s",
-''',
- index = index, value = value)
-
- max_index = c_enum_const(name, 'MAX', prefix)
- ret += mcgen('''
- [%(max_index)s] = NULL,
-};
-''',
- max_index=max_index)
- return ret
-
-def generate_enum(name, values, prefix=None):
- name = c_name(name)
- lookup_decl = mcgen('''
-extern const char *const %(name)s_lookup[];
-''',
- name=name)
-
- enum_decl = mcgen('''
-
-typedef enum %(name)s {
-''',
- name=name)
-
- # append automatically generated _MAX value
- enum_values = values + [ 'MAX' ]
-
- i = 0
- for value in enum_values:
- enum_full_value = c_enum_const(name, value, prefix)
- enum_decl += mcgen('''
- %(enum_full_value)s = %(i)d,
-''',
- enum_full_value = enum_full_value,
- i=i)
- i += 1
+def gen_alternate_qtypes_decl(name):
+ return mcgen('''
- enum_decl += mcgen('''
-} %(name)s;
+extern const int %(c_name)s_qtypes[];
''',
- name=name)
+ c_name=c_name(name))
- return enum_decl + lookup_decl
-
-def generate_alternate_qtypes(expr):
-
- name = expr['alternate']
- members = expr['data']
+def gen_alternate_qtypes(name, variants):
ret = mcgen('''
-const int %(name)s_qtypes[QTYPE_MAX] = {
+const int %(c_name)s_qtypes[QTYPE_MAX] = {
''',
- name=c_name(name))
+ c_name=c_name(name))
- for key in members:
- qtype = find_alternate_member_qtype(members[key])
- assert qtype, "Invalid alternate member"
+ for var in variants.variants:
+ qtype = var.type.alternate_qtype()
+ assert qtype
ret += mcgen('''
[%(qtype)s] = %(enum_const)s,
''',
- qtype = qtype,
- enum_const = c_enum_const(name + 'Kind', key))
+ qtype=qtype,
+ enum_const=c_enum_const(variants.tag_member.type.name,
+ var.name))
ret += mcgen('''
};
@@ -183,41 +119,26 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
return ret
-def generate_union(expr, meta):
-
- name = c_name(expr[meta])
- typeinfo = expr['data']
-
- base = expr.get('base')
- discriminator = expr.get('discriminator')
-
- enum_define = discriminator_find_enum_define(expr)
- if enum_define:
- discriminator_type_name = enum_define['enum_name']
- else:
- discriminator_type_name = '%sKind' % (name)
-
+def gen_union(name, base, variants):
ret = mcgen('''
-struct %(name)s {
+struct %(c_name)s {
''',
- name=name)
+ c_name=c_name(name))
if base:
ret += mcgen('''
/* Members inherited from %(c_name)s: */
''',
- c_name=c_name(base))
- base_fields = find_struct(base)['data']
- ret += generate_struct_fields(base_fields)
+ c_name=c_name(base.name))
+ ret += gen_struct_fields(base.members)
ret += mcgen('''
/* Own members: */
''')
else:
- assert not discriminator
ret += mcgen('''
- %(discriminator_type_name)s kind;
+ %(c_type)s kind;
''',
- discriminator_type_name=c_name(discriminator_type_name))
+ c_type=c_name(variants.tag_member.type.name))
# FIXME: What purpose does data serve, besides preventing a union that
# has a branch named 'data'? We use it in qapi-visit.py to decide
@@ -231,39 +152,41 @@ struct %(name)s {
union { /* union tag is @%(c_name)s */
void *data;
''',
- c_name=c_name(discriminator or 'kind'))
-
- for key in typeinfo:
+ # TODO ugly special case for simple union
+ # Use same tag name in C as on the wire to get rid of
+ # it, then: c_name=c_name(variants.tag_member.name)
+ c_name=c_name(variants.tag_name or 'kind'))
+
+ for var in variants.variants:
+ # Ugly special case for simple union TODO get rid of it
+ typ = var.simple_union_type() or var.type
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
- c_type=c_type(typeinfo[key]),
- c_name=c_name(key))
+ c_type=typ.c_type(),
+ c_name=c_name(var.name))
ret += mcgen('''
};
};
''')
- if meta == 'alternate':
- ret += mcgen('''
-extern const int %(name)s_qtypes[];
-''',
- name=name)
-
return ret
-def generate_type_cleanup_decl(name):
+
+def gen_type_cleanup_decl(name):
ret = mcgen('''
-void qapi_free_%(name)s(%(c_type)s obj);
+
+void qapi_free_%(c_name)s(%(c_name)s *obj);
''',
- c_type=c_type(name), name=c_name(name))
+ c_name=c_name(name))
return ret
-def generate_type_cleanup(name):
+
+def gen_type_cleanup(name):
ret = mcgen('''
-void qapi_free_%(name)s(%(c_type)s obj)
+void qapi_free_%(c_name)s(%(c_name)s *obj)
{
QapiDeallocVisitor *md;
Visitor *v;
@@ -274,13 +197,83 @@ void qapi_free_%(name)s(%(c_type)s obj)
md = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(md);
- visit_type_%(name)s(v, &obj, NULL, NULL);
+ visit_type_%(c_name)s(v, &obj, NULL, NULL);
qapi_dealloc_visitor_cleanup(md);
}
''',
- c_type=c_type(name), name=c_name(name))
+ c_name=c_name(name))
return ret
+
+class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
+ def __init__(self):
+ self.decl = None
+ self.defn = None
+ self._fwdecl = None
+ self._fwdefn = None
+ self._btin = None
+
+ def visit_begin(self, schema):
+ self.decl = ''
+ self.defn = ''
+ self._fwdecl = ''
+ self._fwdefn = ''
+ self._btin = guardstart('QAPI_TYPES_BUILTIN')
+
+ def visit_end(self):
+ self.decl = self._fwdecl + self.decl
+ self._fwdecl = None
+ self.defn = self._fwdefn + self.defn
+ self._fwdefn = None
+ # To avoid header dependency hell, we always generate
+ # declarations for built-in types in our header files and
+ # simply guard them. See also do_builtins (command line
+ # option -b).
+ self._btin += guardend('QAPI_TYPES_BUILTIN')
+ self.decl = self._btin + self.decl
+ self._btin = None
+
+ def _gen_type_cleanup(self, name):
+ self.decl += gen_type_cleanup_decl(name)
+ self.defn += gen_type_cleanup(name)
+
+ def visit_enum_type(self, name, info, values, prefix):
+ self._fwdecl += gen_enum(name, values, prefix)
+ self._fwdefn += gen_enum_lookup(name, values, prefix)
+
+ def visit_array_type(self, name, info, element_type):
+ if isinstance(element_type, QAPISchemaBuiltinType):
+ self._btin += gen_fwd_object_or_array(name)
+ self._btin += gen_array(name, element_type)
+ self._btin += gen_type_cleanup_decl(name)
+ if do_builtins:
+ self.defn += gen_type_cleanup(name)
+ else:
+ self._fwdecl += gen_fwd_object_or_array(name)
+ self.decl += gen_array(name, element_type)
+ self._gen_type_cleanup(name)
+
+ def visit_object_type(self, name, info, base, members, variants):
+ if info:
+ self._fwdecl += gen_fwd_object_or_array(name)
+ if variants:
+ assert not members # not implemented
+ self.decl += gen_union(name, base, variants)
+ else:
+ self.decl += gen_struct(name, base, members)
+ self._gen_type_cleanup(name)
+
+ def visit_alternate_type(self, name, info, variants):
+ self._fwdecl += gen_fwd_object_or_array(name)
+ self._fwdefn += gen_alternate_qtypes(name, variants)
+ self.decl += gen_union(name, None, variants)
+ self.decl += gen_alternate_qtypes_decl(name)
+ self._gen_type_cleanup(name)
+
+# If you link code generated from multiple schemata, you want only one
+# instance of the code for built-in types. Generate it only when
+# do_builtins, enabled by command line option -b. See also
+# QAPISchemaGenTypeVisitor.visit_end().
do_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -334,81 +327,13 @@ fdef.write(mcgen('''
fdecl.write(mcgen('''
#include <stdbool.h>
#include <stdint.h>
+#include "qapi/qmp/qobject.h"
'''))
-exprs = parse_schema(input_file)
-
-fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
-for typename in builtin_types.keys():
- fdecl.write(generate_fwd_builtin(typename))
-fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
-
-for expr in exprs:
- ret = ""
- if expr.has_key('struct'):
- ret += generate_fwd_struct(expr['struct'])
- elif expr.has_key('enum'):
- ret += generate_enum(expr['enum'], expr['data'],
- expr.get('prefix'))
- ret += generate_fwd_enum_struct(expr['enum'])
- fdef.write(generate_enum_lookup(expr['enum'], expr['data'],
- expr.get('prefix')))
- elif expr.has_key('union'):
- ret += generate_fwd_struct(expr['union'])
- enum_define = discriminator_find_enum_define(expr)
- if not enum_define:
- ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
- fdef.write(generate_enum_lookup('%sKind' % expr['union'],
- expr['data'].keys()))
- elif expr.has_key('alternate'):
- ret += generate_fwd_struct(expr['alternate'])
- ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
- fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
- expr['data'].keys()))
- fdef.write(generate_alternate_qtypes(expr))
- else:
- continue
- fdecl.write(ret)
-
-# to avoid header dependency hell, we always generate declarations
-# for built-in types in our header files and simply guard them
-fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
-for typename in builtin_types.keys():
- fdecl.write(generate_type_cleanup_decl(typename + "List"))
-fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
-
-# ...this doesn't work for cases where we link in multiple objects that
-# have the functions defined, so we use -b option to provide control
-# over these cases
-if do_builtins:
- for typename in builtin_types.keys():
- fdef.write(generate_type_cleanup(typename + "List"))
-
-for expr in exprs:
- ret = ""
- if expr.has_key('struct'):
- ret += generate_struct(expr) + "\n"
- ret += generate_type_cleanup_decl(expr['struct'] + "List")
- fdef.write(generate_type_cleanup(expr['struct'] + "List"))
- ret += generate_type_cleanup_decl(expr['struct'])
- fdef.write(generate_type_cleanup(expr['struct']))
- elif expr.has_key('union'):
- ret += generate_union(expr, 'union') + "\n"
- ret += generate_type_cleanup_decl(expr['union'] + "List")
- fdef.write(generate_type_cleanup(expr['union'] + "List"))
- ret += generate_type_cleanup_decl(expr['union'])
- fdef.write(generate_type_cleanup(expr['union']))
- elif expr.has_key('alternate'):
- ret += generate_union(expr, 'alternate') + "\n"
- ret += generate_type_cleanup_decl(expr['alternate'] + "List")
- fdef.write(generate_type_cleanup(expr['alternate'] + "List"))
- ret += generate_type_cleanup_decl(expr['alternate'])
- fdef.write(generate_type_cleanup(expr['alternate']))
- elif expr.has_key('enum'):
- ret += "\n" + generate_type_cleanup_decl(expr['enum'] + "List")
- fdef.write(generate_type_cleanup(expr['enum'] + "List"))
- else:
- continue
- fdecl.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenTypeVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
close_output(fdef, fdecl)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index 3cd662b..97343cf 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -12,25 +12,36 @@
# 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
implicit_structs_seen = set()
struct_fields_seen = set()
-def generate_visit_implicit_struct(type):
- if type in implicit_structs_seen:
+
+def gen_visit_decl(name, scalar=False):
+ c_type = c_name(name) + ' *'
+ if not scalar:
+ c_type += '*'
+ return mcgen('''
+void visit_type_%(c_name)s(Visitor *m, %(c_type)sobj, const char *name, Error **errp);
+''',
+ c_name=c_name(name), c_type=c_type)
+
+
+def gen_visit_implicit_struct(typ):
+ if typ in implicit_structs_seen:
return ''
- implicit_structs_seen.add(type)
+ implicit_structs_seen.add(typ)
+
ret = ''
- if type not in struct_fields_seen:
+ if typ.name not in struct_fields_seen:
# Need a forward declaration
ret += mcgen('''
static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
''',
- c_type=type_name(type))
+ c_type=typ.c_name())
ret += mcgen('''
@@ -46,52 +57,53 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
error_propagate(errp, err);
}
''',
- c_type=type_name(type))
+ c_type=typ.c_name())
return ret
-def generate_visit_struct_fields(name, members, base = None):
+
+def gen_visit_struct_fields(name, base, members):
struct_fields_seen.add(name)
ret = ''
if base:
- ret += generate_visit_implicit_struct(base)
+ ret += gen_visit_implicit_struct(base)
ret += mcgen('''
-static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
+static void visit_type_%(c_name)s_fields(Visitor *m, %(c_name)s **obj, Error **errp)
{
Error *err = NULL;
''',
- name=c_name(name))
+ c_name=c_name(name))
push_indent()
if base:
ret += mcgen('''
-visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
+visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
if (err) {
goto out;
}
''',
- type=type_name(base), c_name=c_name('base'))
+ c_type=base.c_name(), c_name=c_name('base'))
- for argname, argentry, optional in parse_args(members):
- if optional:
+ for memb in members:
+ if memb.optional:
ret += mcgen('''
visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
if (!err && (*obj)->has_%(c_name)s) {
''',
- c_name=c_name(argname), name=argname)
+ c_name=c_name(memb.name), name=memb.name)
push_indent()
ret += mcgen('''
-visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
+visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
''',
- type=type_name(argentry), c_name=c_name(argname),
- name=argname)
+ c_type=memb.type.c_name(), c_name=c_name(memb.name),
+ name=memb.name)
- if optional:
+ if memb.optional:
pop_indent()
ret += mcgen('''
}
@@ -103,7 +115,7 @@ if (err) {
''')
pop_indent()
- if re.search('^ *goto out\\;', ret, re.MULTILINE):
+ if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
out:
@@ -115,12 +127,17 @@ out:
return ret
-def generate_visit_struct_body(name):
+def gen_visit_struct(name, base, members):
+ ret = gen_visit_struct_fields(name, base, members)
+
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
# *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
# rather than leaving it non-NULL. As currently written, the caller must
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
- ret = mcgen('''
+ ret += mcgen('''
+
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
+{
Error *err = NULL;
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
@@ -131,37 +148,17 @@ def generate_visit_struct_body(name):
visit_end_struct(m, &err);
}
error_propagate(errp, err);
+}
''',
- name=name, c_name=c_name(name))
+ name=name, c_name=c_name(name))
return ret
-def generate_visit_struct(expr):
-
- name = expr['struct']
- members = expr['data']
- base = expr.get('base')
- ret = generate_visit_struct_fields(name, members, base)
-
- ret += mcgen('''
-
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
-{
-''',
- name=c_name(name))
-
- ret += generate_visit_struct_body(name)
-
- ret += mcgen('''
-}
-''')
- return ret
-
-def generate_visit_list(name):
+def gen_visit_list(name, element_type):
return mcgen('''
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
{
Error *err = NULL;
GenericList *i, **prev;
@@ -174,8 +171,8 @@ void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, E
for (prev = (GenericList **)obj;
!err && (i = visit_next_list(m, prev, &err)) != NULL;
prev = &i) {
- %(name)sList *native_i = (%(name)sList *)i;
- visit_type_%(name)s(m, &native_i->value, NULL, &err);
+ %(c_name)s *native_i = (%(c_name)s *)i;
+ visit_type_%(c_elt_type)s(m, &native_i->value, NULL, &err);
}
error_propagate(errp, err);
@@ -185,9 +182,10 @@ out:
error_propagate(errp, err);
}
''',
- name=type_name(name))
+ c_name=c_name(name), c_elt_type=element_type.c_name())
+
-def generate_visit_enum(name):
+def gen_visit_enum(name):
return mcgen('''
void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
@@ -197,44 +195,36 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error
''',
c_name=c_name(name), name=name)
-def generate_visit_alternate(name, members):
+
+def gen_visit_alternate(name, variants):
ret = mcgen('''
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
+void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
{
Error *err = NULL;
- visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
+ visit_start_implicit_struct(m, (void**) obj, sizeof(%(c_name)s), &err);
if (err) {
goto out;
}
- visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
+ visit_get_next_type(m, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
if (err) {
goto out_end;
}
switch ((*obj)->kind) {
''',
- name=c_name(name))
-
- # For alternate, always use the default enum type automatically generated
- # as name + 'Kind'
- disc_type = c_name(name) + 'Kind'
-
- for key in members:
- assert (members[key] in builtin_types.keys()
- or find_struct(members[key])
- or find_union(members[key])
- or find_enum(members[key])), "Invalid alternate member"
+ c_name=c_name(name))
- enum_full_value = c_enum_const(disc_type, key)
+ for var in variants.variants:
ret += mcgen('''
- case %(enum_full_value)s:
+ case %(case)s:
visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
break;
''',
- enum_full_value = enum_full_value,
- c_type = type_name(members[key]),
- c_name = c_name(key))
+ case=c_enum_const(variants.tag_member.type.name,
+ var.name),
+ c_type=var.type.c_name(),
+ c_name=c_name(var.name))
ret += mcgen('''
default:
@@ -252,34 +242,17 @@ out:
return ret
-def generate_visit_union(expr):
-
- name = expr['union']
- members = expr['data']
-
- base = expr.get('base')
- discriminator = expr.get('discriminator')
-
- enum_define = discriminator_find_enum_define(expr)
- if enum_define:
- # Use the enum type as discriminator
- ret = ""
- disc_type = c_name(enum_define['enum_name'])
- else:
- # There will always be a discriminator in the C switch code, by default
- # it is an enum type generated silently
- ret = generate_visit_enum(name + 'Kind')
- disc_type = c_name(name) + 'Kind'
+def gen_visit_union(name, base, variants):
+ ret = ''
if base:
- assert discriminator
- base_fields = find_struct(base)['data'].copy()
- del base_fields[discriminator]
- ret += generate_visit_struct_fields(name, base_fields)
+ members = [m for m in base.members if m != variants.tag_member]
+ ret += gen_visit_struct_fields(name, None, members)
- if discriminator:
- for key in members:
- ret += generate_visit_implicit_struct(members[key])
+ for var in variants.variants:
+ # Ugly special case for simple union TODO get rid of it
+ if not var.simple_union_type():
+ ret += gen_visit_implicit_struct(var.type)
ret += mcgen('''
@@ -297,48 +270,57 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
if base:
ret += mcgen('''
- visit_type_%(name)s_fields(m, obj, &err);
+ visit_type_%(c_name)s_fields(m, obj, &err);
if (err) {
goto out_obj;
}
''',
- name=c_name(name))
-
- if not discriminator:
- tag = 'kind'
- disc_key = "type"
- else:
- tag = discriminator
- disc_key = discriminator
+ c_name=c_name(name))
+
+ tag_key = variants.tag_member.name
+ if not variants.tag_name:
+ # we pointlessly use a different key for simple unions
+ tag_key = 'type'
ret += mcgen('''
- visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
+ visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
if (err) {
goto out_obj;
}
if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
goto out_obj;
}
- switch ((*obj)->%(c_tag)s) {
+ switch ((*obj)->%(c_name)s) {
''',
- disc_type = disc_type,
- c_tag=c_name(tag),
- disc_key = disc_key)
-
- for key in members:
- if not discriminator:
- fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
+ c_type=variants.tag_member.type.c_name(),
+ # TODO ugly special case for simple union
+ # Use same tag name in C as on the wire to get rid of
+ # it, then: c_name=c_name(variants.tag_member.name)
+ c_name=c_name(variants.tag_name or 'kind'),
+ name=tag_key)
+
+ for var in variants.variants:
+ # TODO ugly special case for simple union
+ simple_union_type = var.simple_union_type()
+ ret += mcgen('''
+ case %(case)s:
+''',
+ case=c_enum_const(variants.tag_member.type.name,
+ var.name))
+ if simple_union_type:
+ ret += mcgen('''
+ visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);
+''',
+ c_type=simple_union_type.c_name(),
+ c_name=c_name(var.name))
else:
- fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
-
- enum_full_value = c_enum_const(disc_type, key)
+ ret += mcgen('''
+ visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);
+''',
+ c_type=var.type.c_name(),
+ c_name=c_name(var.name))
ret += mcgen('''
- case %(enum_full_value)s:
- ''' + fmt + '''
break;
-''',
- enum_full_value = enum_full_value,
- c_type=type_name(members[key]),
- c_name=c_name(key))
+''')
ret += mcgen('''
default:
@@ -359,38 +341,59 @@ out:
return ret
-def generate_declaration(name, builtin_type=False):
- ret = ""
- if not builtin_type:
- name = c_name(name)
- ret += mcgen('''
-
-void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
-''',
- name=name)
-
- ret += mcgen('''
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
-''',
- name=name)
-
- return ret
-
-def generate_enum_declaration(name):
- ret = mcgen('''
-void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
-''',
- name=c_name(name))
-
- return ret
-
-def generate_decl_enum(name):
- return mcgen('''
-
-void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
-''',
- name=c_name(name))
+class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
+ def __init__(self):
+ self.decl = None
+ self.defn = None
+ self._btin = None
+
+ def visit_begin(self, schema):
+ self.decl = ''
+ self.defn = ''
+ self._btin = guardstart('QAPI_VISIT_BUILTIN')
+
+ def visit_end(self):
+ # To avoid header dependency hell, we always generate
+ # declarations for built-in types in our header files and
+ # simply guard them. See also do_builtins (command line
+ # option -b).
+ self._btin += guardend('QAPI_VISIT_BUILTIN')
+ self.decl = self._btin + self.decl
+ self._btin = None
+
+ def visit_enum_type(self, name, info, values, prefix):
+ self.decl += gen_visit_decl(name, scalar=True)
+ self.defn += gen_visit_enum(name)
+
+ def visit_array_type(self, name, info, element_type):
+ decl = gen_visit_decl(name)
+ defn = gen_visit_list(name, element_type)
+ if isinstance(element_type, QAPISchemaBuiltinType):
+ self._btin += decl
+ if do_builtins:
+ self.defn += defn
+ else:
+ self.decl += decl
+ self.defn += defn
+
+ def visit_object_type(self, name, info, base, members, variants):
+ if info:
+ self.decl += gen_visit_decl(name)
+ if variants:
+ assert not members # not implemented
+ self.defn += gen_visit_union(name, base, variants)
+ else:
+ self.defn += gen_visit_struct(name, base, members)
+
+ def visit_alternate_type(self, name, info, variants):
+ self.decl += gen_visit_decl(name)
+ self.defn += gen_visit_alternate(name, variants)
+
+# If you link code generated from multiple schemata, you want only one
+# instance of the code for built-in types. Generate it only when
+# do_builtins, enabled by command line option -b. See also
+# QAPISchemaGenVisitVisitor.visit_end().
do_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
@@ -437,7 +440,7 @@ fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
- prefix = prefix))
+ prefix=prefix))
fdecl.write(mcgen('''
#include "qapi/visitor.h"
@@ -446,56 +449,10 @@ fdecl.write(mcgen('''
''',
prefix=prefix))
-exprs = parse_schema(input_file)
-
-# to avoid header dependency hell, we always generate declarations
-# for built-in types in our header files and simply guard them
-fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
-for typename in builtin_types.keys():
- fdecl.write(generate_declaration(typename, builtin_type=True))
-fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
-
-# ...this doesn't work for cases where we link in multiple objects that
-# have the functions defined, so we use -b option to provide control
-# over these cases
-if do_builtins:
- for typename in builtin_types.keys():
- fdef.write(generate_visit_list(typename))
-
-for expr in exprs:
- if expr.has_key('struct'):
- ret = generate_visit_struct(expr)
- ret += generate_visit_list(expr['struct'])
- fdef.write(ret)
-
- ret = generate_declaration(expr['struct'])
- fdecl.write(ret)
- elif expr.has_key('union'):
- ret = generate_visit_union(expr)
- ret += generate_visit_list(expr['union'])
- fdef.write(ret)
-
- enum_define = discriminator_find_enum_define(expr)
- ret = ""
- if not enum_define:
- ret = generate_decl_enum('%sKind' % expr['union'])
- ret += generate_declaration(expr['union'])
- fdecl.write(ret)
- elif expr.has_key('alternate'):
- ret = generate_visit_alternate(expr['alternate'], expr['data'])
- ret += generate_visit_list(expr['alternate'])
- fdef.write(ret)
-
- ret = generate_decl_enum('%sKind' % expr['alternate'])
- ret += generate_declaration(expr['alternate'])
- fdecl.write(ret)
- elif expr.has_key('enum'):
- ret = generate_visit_list(expr['enum'])
- ret += generate_visit_enum(expr['enum'])
- fdef.write(ret)
-
- ret = generate_decl_enum(expr['enum'])
- ret += generate_enum_declaration(expr['enum'])
- fdecl.write(ret)
+schema = QAPISchema(input_file)
+gen = QAPISchemaGenVisitVisitor()
+schema.visit(gen)
+fdef.write(gen.defn)
+fdecl.write(gen.decl)
close_output(fdef, fdecl)
diff --git a/scripts/qapi.py b/scripts/qapi.py
index c4423b7..06478bb 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -33,12 +33,14 @@ builtin_types = {
'uint32': 'QTYPE_QINT',
'uint64': 'QTYPE_QINT',
'size': 'QTYPE_QINT',
+ 'any': None, # any qtype_code possible, actually
}
# Whitelist of commands allowed to return a non-dictionary
returns_whitelist = [
# From QMP:
'human-monitor-command',
+ 'qom-get',
'query-migrate-cache-size',
'query-tpm-models',
'query-tpm-types',
@@ -103,7 +105,7 @@ class QAPIExprError(Exception):
return error_path(self.info['parent']) + \
"%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
-class QAPISchema:
+class QAPISchemaParser(object):
def __init__(self, fp, previously_included = [], incl_info = None):
abs_fname = os.path.abspath(fp.name)
@@ -149,8 +151,8 @@ class QAPISchema:
except IOError, e:
raise QAPIExprError(expr_info,
'%s: %s' % (e.strerror, include))
- exprs_include = QAPISchema(fobj, previously_included,
- expr_info)
+ exprs_include = QAPISchemaParser(fobj, previously_included,
+ expr_info)
self.exprs.extend(exprs_include.exprs)
else:
expr_elem = {'expr': expr,
@@ -302,6 +304,8 @@ class QAPISchema:
#
# Semantic analysis of schema expressions
+# TODO fold into QAPISchema
+# TODO catching name collisions in generated code would be nice
#
def find_base_fields(base):
@@ -424,15 +428,12 @@ def is_enum(name):
def check_type(expr_info, source, value, allow_array = False,
allow_dict = False, allow_optional = False,
- allow_star = False, allow_metas = []):
+ allow_metas = []):
global all_names
if value is None:
return
- if allow_star and value == '**':
- return
-
# Check if array type for value is okay
if isinstance(value, list):
if not allow_array:
@@ -446,10 +447,6 @@ def check_type(expr_info, source, value, allow_array = False,
# Check if type name for value is okay
if isinstance(value, str):
- if value == '**':
- raise QAPIExprError(expr_info,
- "%s uses '**' but did not request 'gen':false"
- % source)
if not value in all_names:
raise QAPIExprError(expr_info,
"%s uses unknown type '%s'"
@@ -475,7 +472,7 @@ def check_type(expr_info, source, value, allow_array = False,
# Todo: allow dictionaries to represent default values of
# an optional argument.
check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
- allow_array=True, allow_star=allow_star,
+ allow_array=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
'enum'])
@@ -495,18 +492,16 @@ def check_member_clash(expr_info, base_name, data, source = ""):
def check_command(expr, expr_info):
name = expr['command']
- allow_star = expr.has_key('gen')
check_type(expr_info, "'data' for command '%s'" % name,
expr.get('data'), allow_dict=True, allow_optional=True,
- allow_metas=['struct'], allow_star=allow_star)
+ allow_metas=['struct'])
returns_meta = ['union', 'struct']
if name in returns_whitelist:
returns_meta += ['built-in', 'alternate', 'enum']
check_type(expr_info, "'returns' for command '%s'" % name,
expr.get('returns'), allow_array=True,
- allow_optional=True, allow_metas=returns_meta,
- allow_star=allow_star)
+ allow_optional=True, allow_metas=returns_meta)
def check_event(expr, expr_info):
global events
@@ -751,36 +746,538 @@ def check_exprs(exprs):
else:
assert False, 'unexpected meta type'
- return map(lambda expr_elem: expr_elem['expr'], exprs)
+ return exprs
-def parse_schema(fname):
- try:
- schema = QAPISchema(open(fname, "r"))
- return check_exprs(schema.exprs)
- except (QAPISchemaError, QAPIExprError), e:
- print >>sys.stderr, e
- exit(1)
#
-# Code generation helpers
+# Schema compiler frontend
#
-def parse_args(typeinfo):
- if isinstance(typeinfo, str):
- struct = find_struct(typeinfo)
- assert struct != None
- typeinfo = struct['data']
+class QAPISchemaEntity(object):
+ def __init__(self, name, info):
+ assert isinstance(name, str)
+ self.name = name
+ self.info = info
+
+ def c_name(self):
+ return c_name(self.name)
+
+ def check(self, schema):
+ pass
+
+ def visit(self, visitor):
+ pass
+
+
+class QAPISchemaVisitor(object):
+ def visit_begin(self, schema):
+ pass
+
+ def visit_end(self):
+ pass
+
+ def visit_builtin_type(self, name, info, json_type):
+ pass
+
+ def visit_enum_type(self, name, info, values, prefix):
+ pass
+
+ def visit_array_type(self, name, info, element_type):
+ pass
+
+ def visit_object_type(self, name, info, base, members, variants):
+ pass
+
+ def visit_object_type_flat(self, name, info, members, variants):
+ pass
+
+ def visit_alternate_type(self, name, info, variants):
+ pass
+
+ def visit_command(self, name, info, arg_type, ret_type,
+ gen, success_response):
+ pass
+
+ def visit_event(self, name, info, arg_type):
+ pass
+
+
+class QAPISchemaType(QAPISchemaEntity):
+ def c_type(self, is_param=False):
+ return c_name(self.name) + pointer_suffix
+
+ def c_null(self):
+ return 'NULL'
+
+ def json_type(self):
+ pass
+
+ def alternate_qtype(self):
+ json2qtype = {
+ 'string': 'QTYPE_QSTRING',
+ 'number': 'QTYPE_QFLOAT',
+ 'int': 'QTYPE_QINT',
+ 'boolean': 'QTYPE_QBOOL',
+ 'object': 'QTYPE_QDICT'
+ }
+ return json2qtype.get(self.json_type())
+
+
+class QAPISchemaBuiltinType(QAPISchemaType):
+ def __init__(self, name, json_type, c_type, c_null):
+ QAPISchemaType.__init__(self, name, None)
+ assert not c_type or isinstance(c_type, str)
+ assert json_type in ('string', 'number', 'int', 'boolean', 'null',
+ 'value')
+ self._json_type_name = json_type
+ self._c_type_name = c_type
+ self._c_null_val = c_null
+
+ def c_name(self):
+ return self.name
+
+ def c_type(self, is_param=False):
+ if is_param and self.name == 'str':
+ return 'const ' + self._c_type_name
+ return self._c_type_name
+
+ def c_null(self):
+ return self._c_null_val
+
+ def json_type(self):
+ return self._json_type_name
+
+ def visit(self, visitor):
+ visitor.visit_builtin_type(self.name, self.info, self.json_type())
+
+
+class QAPISchemaEnumType(QAPISchemaType):
+ def __init__(self, name, info, values, prefix):
+ QAPISchemaType.__init__(self, name, info)
+ for v in values:
+ assert isinstance(v, str)
+ assert prefix is None or isinstance(prefix, str)
+ self.values = values
+ self.prefix = prefix
+
+ def check(self, schema):
+ assert len(set(self.values)) == len(self.values)
+
+ def c_type(self, is_param=False):
+ return c_name(self.name)
+
+ def c_null(self):
+ return c_enum_const(self.name, (self.values + ['MAX'])[0],
+ self.prefix)
+
+ def json_type(self):
+ return 'string'
+
+ def visit(self, visitor):
+ visitor.visit_enum_type(self.name, self.info,
+ self.values, self.prefix)
+
+
+class QAPISchemaArrayType(QAPISchemaType):
+ def __init__(self, name, info, element_type):
+ QAPISchemaType.__init__(self, name, info)
+ assert isinstance(element_type, str)
+ self._element_type_name = element_type
+ self.element_type = None
+
+ def check(self, schema):
+ self.element_type = schema.lookup_type(self._element_type_name)
+ assert self.element_type
+
+ def json_type(self):
+ return 'array'
+
+ def visit(self, visitor):
+ visitor.visit_array_type(self.name, self.info, self.element_type)
+
+
+class QAPISchemaObjectType(QAPISchemaType):
+ def __init__(self, name, info, base, local_members, variants):
+ QAPISchemaType.__init__(self, name, info)
+ assert base is None or isinstance(base, str)
+ for m in local_members:
+ assert isinstance(m, QAPISchemaObjectTypeMember)
+ assert (variants is None or
+ isinstance(variants, QAPISchemaObjectTypeVariants))
+ self._base_name = base
+ self.base = None
+ self.local_members = local_members
+ self.variants = variants
+ self.members = None
+
+ def check(self, schema):
+ assert self.members is not False # not running in cycles
+ if self.members:
+ return
+ self.members = False # mark as being checked
+ if self._base_name:
+ self.base = schema.lookup_type(self._base_name)
+ assert isinstance(self.base, QAPISchemaObjectType)
+ assert not self.base.variants # not implemented
+ self.base.check(schema)
+ members = list(self.base.members)
+ else:
+ members = []
+ seen = {}
+ for m in members:
+ seen[m.name] = m
+ for m in self.local_members:
+ m.check(schema, members, seen)
+ if self.variants:
+ self.variants.check(schema, members, seen)
+ self.members = members
+
+ def c_name(self):
+ assert self.info
+ return QAPISchemaType.c_name(self)
+
+ def c_type(self, is_param=False):
+ assert self.info
+ return QAPISchemaType.c_type(self)
+
+ def json_type(self):
+ return 'object'
+
+ def visit(self, visitor):
+ visitor.visit_object_type(self.name, self.info,
+ self.base, self.local_members, self.variants)
+ visitor.visit_object_type_flat(self.name, self.info,
+ self.members, self.variants)
+
+
+class QAPISchemaObjectTypeMember(object):
+ def __init__(self, name, typ, optional):
+ assert isinstance(name, str)
+ assert isinstance(typ, str)
+ assert isinstance(optional, bool)
+ self.name = name
+ self._type_name = typ
+ self.type = None
+ self.optional = optional
+
+ def check(self, schema, all_members, seen):
+ assert self.name not in seen
+ self.type = schema.lookup_type(self._type_name)
+ assert self.type
+ all_members.append(self)
+ seen[self.name] = self
+
+
+class QAPISchemaObjectTypeVariants(object):
+ def __init__(self, tag_name, tag_enum, variants):
+ assert tag_name is None or isinstance(tag_name, str)
+ assert tag_enum is None or isinstance(tag_enum, str)
+ for v in variants:
+ assert isinstance(v, QAPISchemaObjectTypeVariant)
+ self.tag_name = tag_name
+ if tag_name:
+ assert not tag_enum
+ self.tag_member = None
+ else:
+ self.tag_member = QAPISchemaObjectTypeMember('type', tag_enum,
+ False)
+ self.variants = variants
+
+ def check(self, schema, members, seen):
+ if self.tag_name:
+ self.tag_member = seen[self.tag_name]
+ else:
+ self.tag_member.check(schema, members, seen)
+ assert isinstance(self.tag_member.type, QAPISchemaEnumType)
+ for v in self.variants:
+ vseen = dict(seen)
+ v.check(schema, self.tag_member.type, vseen)
+
+class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
+ def __init__(self, name, typ):
+ QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
+
+ def check(self, schema, tag_type, seen):
+ QAPISchemaObjectTypeMember.check(self, schema, [], seen)
+ assert self.name in tag_type.values
+
+ # This function exists to support ugly simple union special cases
+ # TODO get rid of them, and drop the function
+ def simple_union_type(self):
+ if isinstance(self.type, QAPISchemaObjectType) and not self.type.info:
+ assert len(self.type.members) == 1
+ assert not self.type.variants
+ return self.type.members[0].type
+ return None
+
- for member in typeinfo:
- argname = member
- argentry = typeinfo[member]
+class QAPISchemaAlternateType(QAPISchemaType):
+ def __init__(self, name, info, variants):
+ QAPISchemaType.__init__(self, name, info)
+ assert isinstance(variants, QAPISchemaObjectTypeVariants)
+ assert not variants.tag_name
+ self.variants = variants
+
+ def check(self, schema):
+ self.variants.check(schema, [], {})
+
+ def json_type(self):
+ return 'value'
+
+ def visit(self, visitor):
+ visitor.visit_alternate_type(self.name, self.info, self.variants)
+
+
+class QAPISchemaCommand(QAPISchemaEntity):
+ def __init__(self, name, info, arg_type, ret_type, gen, success_response):
+ QAPISchemaEntity.__init__(self, name, info)
+ assert not arg_type or isinstance(arg_type, str)
+ assert not ret_type or isinstance(ret_type, str)
+ self._arg_type_name = arg_type
+ self.arg_type = None
+ self._ret_type_name = ret_type
+ self.ret_type = None
+ self.gen = gen
+ self.success_response = success_response
+
+ 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
+ if self._ret_type_name:
+ self.ret_type = schema.lookup_type(self._ret_type_name)
+ assert isinstance(self.ret_type, QAPISchemaType)
+
+ def visit(self, visitor):
+ visitor.visit_command(self.name, self.info,
+ self.arg_type, self.ret_type,
+ self.gen, self.success_response)
+
+
+class QAPISchemaEvent(QAPISchemaEntity):
+ def __init__(self, name, info, arg_type):
+ QAPISchemaEntity.__init__(self, name, info)
+ assert not arg_type or isinstance(arg_type, str)
+ self._arg_type_name = arg_type
+ self.arg_type = None
+
+ 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
+
+ def visit(self, visitor):
+ visitor.visit_event(self.name, self.info, self.arg_type)
+
+
+class QAPISchema(object):
+ def __init__(self, fname):
+ try:
+ self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
+ except (QAPISchemaError, QAPIExprError), err:
+ print >>sys.stderr, err
+ exit(1)
+ self._entity_dict = {}
+ self._def_predefineds()
+ self._def_exprs()
+ self.check()
+
+ def _def_entity(self, ent):
+ assert ent.name not in self._entity_dict
+ self._entity_dict[ent.name] = ent
+
+ def lookup_entity(self, name, typ=None):
+ ent = self._entity_dict.get(name)
+ if typ and not isinstance(ent, typ):
+ return None
+ return ent
+
+ def lookup_type(self, name):
+ return self.lookup_entity(name, QAPISchemaType)
+
+ def _def_builtin_type(self, name, json_type, c_type, c_null):
+ self._def_entity(QAPISchemaBuiltinType(name, json_type,
+ c_type, c_null))
+ self._make_array_type(name) # TODO really needed?
+
+ def _def_predefineds(self):
+ for t in [('str', 'string', 'char' + pointer_suffix, 'NULL'),
+ ('number', 'number', 'double', '0'),
+ ('int', 'int', 'int64_t', '0'),
+ ('int8', 'int', 'int8_t', '0'),
+ ('int16', 'int', 'int16_t', '0'),
+ ('int32', 'int', 'int32_t', '0'),
+ ('int64', 'int', 'int64_t', '0'),
+ ('uint8', 'int', 'uint8_t', '0'),
+ ('uint16', 'int', 'uint16_t', '0'),
+ ('uint32', 'int', 'uint32_t', '0'),
+ ('uint64', 'int', 'uint64_t', '0'),
+ ('size', 'int', 'uint64_t', '0'),
+ ('bool', 'boolean', 'bool', 'false'),
+ ('any', 'value', 'QObject' + pointer_suffix, 'NULL')]:
+ self._def_builtin_type(*t)
+ self.the_empty_object_type = QAPISchemaObjectType(':empty', None, None,
+ [], None)
+ self._def_entity(self.the_empty_object_type)
+
+ def _make_implicit_enum_type(self, name, values):
+ name = name + 'Kind'
+ self._def_entity(QAPISchemaEnumType(name, None, values, None))
+ return name
+
+ def _make_array_type(self, element_type):
+ name = element_type + 'List'
+ if not self.lookup_type(name):
+ self._def_entity(QAPISchemaArrayType(name, None, element_type))
+ return name
+
+ def _make_implicit_object_type(self, name, role, members):
+ if not members:
+ return None
+ name = ':obj-%s-%s' % (name, role)
+ if not self.lookup_entity(name, QAPISchemaObjectType):
+ self._def_entity(QAPISchemaObjectType(name, None, None,
+ members, None))
+ return name
+
+ def _def_enum_type(self, expr, info):
+ name = expr['enum']
+ data = expr['data']
+ prefix = expr.get('prefix')
+ self._def_entity(QAPISchemaEnumType(name, info, data, prefix))
+ self._make_array_type(name) # TODO really needed?
+
+ def _make_member(self, name, typ):
optional = False
- if member.startswith('*'):
- argname = member[1:]
+ if name.startswith('*'):
+ name = name[1:]
optional = True
- # Todo: allow argentry to be OrderedDict, for providing the
- # value of an optional argument.
- yield (argname, argentry, optional)
+ if isinstance(typ, list):
+ assert len(typ) == 1
+ typ = self._make_array_type(typ[0])
+ return QAPISchemaObjectTypeMember(name, typ, optional)
+
+ def _make_members(self, data):
+ return [self._make_member(key, value)
+ for (key, value) in data.iteritems()]
+
+ def _def_struct_type(self, expr, info):
+ name = expr['struct']
+ base = expr.get('base')
+ data = expr['data']
+ self._def_entity(QAPISchemaObjectType(name, info, base,
+ self._make_members(data),
+ None))
+ self._make_array_type(name) # TODO really needed?
+
+ def _make_variant(self, case, typ):
+ return QAPISchemaObjectTypeVariant(case, typ)
+
+ def _make_simple_variant(self, case, typ):
+ if isinstance(typ, list):
+ assert len(typ) == 1
+ typ = self._make_array_type(typ[0])
+ typ = self._make_implicit_object_type(typ, 'wrapper',
+ [self._make_member('data', typ)])
+ return QAPISchemaObjectTypeVariant(case, typ)
+
+ def _make_tag_enum(self, type_name, variants):
+ return self._make_implicit_enum_type(type_name,
+ [v.name for v in variants])
+
+ def _def_union_type(self, expr, info):
+ name = expr['union']
+ data = expr['data']
+ base = expr.get('base')
+ tag_name = expr.get('discriminator')
+ tag_enum = None
+ if tag_name:
+ variants = [self._make_variant(key, value)
+ for (key, value) in data.iteritems()]
+ else:
+ variants = [self._make_simple_variant(key, value)
+ for (key, value) in data.iteritems()]
+ tag_enum = self._make_tag_enum(name, variants)
+ self._def_entity(
+ QAPISchemaObjectType(name, info, base,
+ self._make_members(OrderedDict()),
+ QAPISchemaObjectTypeVariants(tag_name,
+ tag_enum,
+ variants)))
+ self._make_array_type(name) # TODO really needed?
+
+ def _def_alternate_type(self, expr, info):
+ name = expr['alternate']
+ data = expr['data']
+ variants = [self._make_variant(key, value)
+ for (key, value) in data.iteritems()]
+ tag_enum = self._make_tag_enum(name, variants)
+ self._def_entity(
+ QAPISchemaAlternateType(name, info,
+ QAPISchemaObjectTypeVariants(None,
+ tag_enum,
+ variants)))
+ self._make_array_type(name) # TODO really needed?
+
+ def _def_command(self, expr, info):
+ name = expr['command']
+ data = expr.get('data')
+ rets = expr.get('returns')
+ gen = expr.get('gen', True)
+ success_response = expr.get('success-response', True)
+ if isinstance(data, OrderedDict):
+ data = self._make_implicit_object_type(name, 'arg',
+ self._make_members(data))
+ if isinstance(rets, list):
+ assert len(rets) == 1
+ rets = self._make_array_type(rets[0])
+ self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
+ success_response))
+
+ def _def_event(self, expr, info):
+ name = expr['event']
+ data = expr.get('data')
+ if isinstance(data, OrderedDict):
+ data = self._make_implicit_object_type(name, 'arg',
+ self._make_members(data))
+ self._def_entity(QAPISchemaEvent(name, info, data))
+
+ def _def_exprs(self):
+ for expr_elem in self.exprs:
+ expr = expr_elem['expr']
+ info = expr_elem['info']
+ if 'enum' in expr:
+ self._def_enum_type(expr, info)
+ elif 'struct' in expr:
+ self._def_struct_type(expr, info)
+ elif 'union' in expr:
+ self._def_union_type(expr, info)
+ elif 'alternate' in expr:
+ self._def_alternate_type(expr, info)
+ elif 'command' in expr:
+ self._def_command(expr, info)
+ elif 'event' in expr:
+ self._def_event(expr, info)
+ else:
+ assert False
+
+ def check(self):
+ for ent in self._entity_dict.values():
+ ent.check(self)
+
+ def visit(self, visitor):
+ ignore = visitor.visit_begin(self)
+ for name in sorted(self._entity_dict.keys()):
+ if not ignore or not isinstance(self._entity_dict[name], ignore):
+ self._entity_dict[name].visit(visitor)
+ visitor.visit_end()
+
+
+#
+# Code generation helpers
+#
def camel_case(name):
new_name = ''
@@ -864,70 +1361,9 @@ def c_name(name, protect=True):
return "q_" + name
return name.translate(c_name_trans)
-# Map type @name to the C typedef name for the list form.
-#
-# ['Name'] -> 'NameList', ['x-Foo'] -> 'x_FooList', ['int'] -> 'intList'
-def c_list_type(name):
- return type_name(name) + 'List'
-
-# Map type @value to the C typedef form.
-#
-# Used for converting 'type' from a 'member':'type' qapi definition
-# into the alphanumeric portion of the type for a generated C parameter,
-# as well as generated C function names. See c_type() for the rest of
-# the conversion such as adding '*' on pointer types.
-# 'int' -> 'int', '[x-Foo]' -> 'x_FooList', '__a.b_c' -> '__a_b_c'
-def type_name(value):
- if type(value) == list:
- return c_list_type(value[0])
- if value in builtin_types.keys():
- return value
- return c_name(value)
-
eatspace = '\033EATSPACE.'
pointer_suffix = ' *' + eatspace
-# Map type @name to its C type expression.
-# If @is_param, const-qualify the string type.
-#
-# This function is used for computing the full C type of 'member':'name'.
-# A special suffix is added in c_type() for pointer types, and it's
-# stripped in mcgen(). So please notice this when you check the return
-# value of c_type() outside mcgen().
-def c_type(value, is_param=False):
- if value == 'str':
- if is_param:
- return 'const char' + pointer_suffix
- return 'char' + pointer_suffix
-
- elif value == 'int':
- return 'int64_t'
- elif (value == 'int8' or value == 'int16' or value == 'int32' or
- value == 'int64' or value == 'uint8' or value == 'uint16' or
- value == 'uint32' or value == 'uint64'):
- return value + '_t'
- elif value == 'size':
- return 'uint64_t'
- elif value == 'bool':
- return 'bool'
- elif value == 'number':
- return 'double'
- elif type(value) == list:
- return c_list_type(value[0]) + pointer_suffix
- elif is_enum(value):
- return c_name(value)
- elif value == None:
- return 'void'
- elif value in events:
- return camel_case(value) + 'Event' + pointer_suffix
- else:
- # complex type name
- assert isinstance(value, str) and value != ""
- return c_name(value) + pointer_suffix
-
-def is_c_ptr(value):
- return c_type(value).endswith(pointer_suffix)
-
def genindent(count):
ret = ""
for i in range(count):
@@ -982,6 +1418,74 @@ def guardend(name):
''',
name=guardname(name))
+def gen_enum_lookup(name, values, prefix=None):
+ ret = mcgen('''
+
+const char *const %(c_name)s_lookup[] = {
+''',
+ c_name=c_name(name))
+ for value in values:
+ index = c_enum_const(name, value, prefix)
+ ret += mcgen('''
+ [%(index)s] = "%(value)s",
+''',
+ index=index, value=value)
+
+ max_index = c_enum_const(name, 'MAX', prefix)
+ ret += mcgen('''
+ [%(max_index)s] = NULL,
+};
+''',
+ max_index=max_index)
+ return ret
+
+def gen_enum(name, values, prefix=None):
+ # append automatically generated _MAX value
+ enum_values = values + ['MAX']
+
+ ret = mcgen('''
+
+typedef enum %(c_name)s {
+''',
+ c_name=c_name(name))
+
+ i = 0
+ for value in enum_values:
+ ret += mcgen('''
+ %(c_enum)s = %(i)d,
+''',
+ c_enum=c_enum_const(name, value, prefix),
+ i=i)
+ i += 1
+
+ ret += mcgen('''
+} %(c_name)s;
+''',
+ c_name=c_name(name))
+
+ ret += mcgen('''
+
+extern const char *const %(c_name)s_lookup[];
+''',
+ c_name=c_name(name))
+ return ret
+
+def gen_params(arg_type, extra):
+ if not arg_type:
+ return extra
+ assert not arg_type.variants
+ ret = ''
+ sep = ''
+ 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_type(is_param=True), c_name(memb.name))
+ if extra:
+ ret += sep + extra
+ return ret
+
#
# Common command line parsing
#