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>
437 lines
11 KiB
Python
437 lines
11 KiB
Python
#
|
|
# QAPI visitor 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
|
|
|
|
implicit_structs_seen = set()
|
|
struct_fields_seen = set()
|
|
|
|
|
|
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 *v, %(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(typ)
|
|
|
|
ret = ''
|
|
if typ.name not in struct_fields_seen:
|
|
# Need a forward declaration
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
|
|
''',
|
|
c_type=typ.c_name())
|
|
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
|
|
if (!err) {
|
|
visit_type_%(c_type)s_fields(v, obj, errp);
|
|
visit_end_implicit_struct(v, &err);
|
|
}
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
c_type=typ.c_name())
|
|
return ret
|
|
|
|
|
|
def gen_visit_struct_fields(name, base, members):
|
|
struct_fields_seen.add(name)
|
|
|
|
ret = ''
|
|
|
|
if base:
|
|
ret += gen_visit_implicit_struct(base)
|
|
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
if base:
|
|
ret += mcgen('''
|
|
visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
|
|
''',
|
|
c_type=base.c_name(), c_name=c_name('base'))
|
|
ret += gen_err_check()
|
|
|
|
ret += gen_visit_fields(members, prefix='(*obj)->')
|
|
|
|
# 'goto out' produced for base, and by gen_visit_fields() for each member
|
|
if base or members:
|
|
ret += mcgen('''
|
|
|
|
out:
|
|
''')
|
|
ret += mcgen('''
|
|
error_propagate(errp, err);
|
|
}
|
|
''')
|
|
return ret
|
|
|
|
|
|
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('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
|
|
if (!err) {
|
|
if (*obj) {
|
|
visit_type_%(c_name)s_fields(v, obj, errp);
|
|
}
|
|
visit_end_struct(v, &err);
|
|
}
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
name=name, c_name=c_name(name))
|
|
|
|
return ret
|
|
|
|
|
|
def gen_visit_list(name, element_type):
|
|
return mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
GenericList *i, **prev;
|
|
|
|
visit_start_list(v, name, &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
|
|
for (prev = (GenericList **)obj;
|
|
!err && (i = visit_next_list(v, prev, &err)) != NULL;
|
|
prev = &i) {
|
|
%(c_name)s *native_i = (%(c_name)s *)i;
|
|
visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
|
|
}
|
|
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
visit_end_list(v, &err);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
c_name=c_name(name), c_elt_type=element_type.c_name())
|
|
|
|
|
|
def gen_visit_enum(name):
|
|
return mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
|
|
{
|
|
visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
|
|
}
|
|
''',
|
|
c_name=c_name(name), name=name)
|
|
|
|
|
|
def gen_visit_alternate(name, variants):
|
|
ret = mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
|
|
if (err) {
|
|
goto out_obj;
|
|
}
|
|
switch ((*obj)->kind) {
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
for var in variants.variants:
|
|
ret += mcgen('''
|
|
case %(case)s:
|
|
visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
|
|
break;
|
|
''',
|
|
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:
|
|
abort();
|
|
}
|
|
out_obj:
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
visit_end_implicit_struct(v, &err);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''')
|
|
|
|
return ret
|
|
|
|
|
|
def gen_visit_union(name, base, variants):
|
|
ret = ''
|
|
|
|
if base:
|
|
members = [m for m in base.members if m != variants.tag_member]
|
|
ret += gen_visit_struct_fields(name, None, members)
|
|
|
|
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('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
if (!*obj) {
|
|
goto out_obj;
|
|
}
|
|
''',
|
|
c_name=c_name(name), name=name)
|
|
|
|
if base:
|
|
ret += mcgen('''
|
|
visit_type_%(c_name)s_fields(v, obj, &err);
|
|
''',
|
|
c_name=c_name(name))
|
|
ret += gen_err_check(label='out_obj')
|
|
|
|
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_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
|
|
if (err) {
|
|
goto out_obj;
|
|
}
|
|
if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
|
|
goto out_obj;
|
|
}
|
|
switch ((*obj)->%(c_name)s) {
|
|
''',
|
|
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(v, &(*obj)->%(c_name)s, "data", &err);
|
|
''',
|
|
c_type=simple_union_type.c_name(),
|
|
c_name=c_name(var.name))
|
|
else:
|
|
ret += mcgen('''
|
|
visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
|
|
''',
|
|
c_type=var.type.c_name(),
|
|
c_name=c_name(var.name))
|
|
ret += mcgen('''
|
|
break;
|
|
''')
|
|
|
|
ret += mcgen('''
|
|
default:
|
|
abort();
|
|
}
|
|
out_obj:
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
if (*obj) {
|
|
visit_end_union(v, !!(*obj)->data, &err);
|
|
}
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
visit_end_struct(v, &err);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''')
|
|
|
|
return ret
|
|
|
|
|
|
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_needed(self, entity):
|
|
# Visit everything except implicit objects
|
|
return not (entity.is_implicit() and
|
|
isinstance(entity, QAPISchemaObjectType))
|
|
|
|
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):
|
|
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) = \
|
|
parse_command_line("b", ["builtins"])
|
|
|
|
for o, a in opts:
|
|
if o in ("-b", "--builtins"):
|
|
do_builtins = True
|
|
|
|
c_comment = '''
|
|
/*
|
|
* schema-defined QAPI visitor functions
|
|
*
|
|
* 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 visitor functions
|
|
*
|
|
* 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,
|
|
'qapi-visit.c', 'qapi-visit.h',
|
|
c_comment, h_comment)
|
|
|
|
fdef.write(mcgen('''
|
|
#include "qemu-common.h"
|
|
#include "%(prefix)sqapi-visit.h"
|
|
''',
|
|
prefix=prefix))
|
|
|
|
fdecl.write(mcgen('''
|
|
#include "qapi/visitor.h"
|
|
#include "%(prefix)sqapi-types.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
schema = QAPISchema(input_file)
|
|
gen = QAPISchemaGenVisitVisitor()
|
|
schema.visit(gen)
|
|
fdef.write(gen.defn)
|
|
fdecl.write(gen.decl)
|
|
|
|
close_output(fdef, fdecl)
|