unicorn/qemu/scripts/qapi-visit.py

440 lines
12 KiB
Python
Raw Normal View History

2015-08-21 07:04:50 +00:00
#
# QAPI visitor generator
#
# Copyright IBM, Corp. 2011
# Copyright (C) 2014-2015 Red Hat, Inc.
2015-08-21 07:04:50 +00:00
#
# 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
# visit_type_FOO_implicit() is emitted as needed; track if it has already
# been output.
implicit_structs_seen = set()
# visit_type_FOO_fields() is always emitted; track if a forward declaration
# or implementation has already been output.
struct_fields_seen = set()
2015-08-21 07:04:50 +00:00
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_fields_decl(typ):
ret = ''
if typ.name not in struct_fields_seen:
ret += mcgen('''
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
''',
c_type=typ.c_name())
struct_fields_seen.add(typ.name)
return ret
def gen_visit_implicit_struct(typ):
if typ in implicit_structs_seen:
return ''
implicit_structs_seen.add(typ)
ret = gen_visit_fields_decl(typ)
ret += mcgen('''
2015-08-21 07:04:50 +00:00
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
2015-08-21 07:04:50 +00:00
{
Error *err = NULL;
visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
2015-08-21 07:04:50 +00:00
if (!err) {
visit_type_%(c_type)s_fields(v, obj, errp);
visit_end_implicit_struct(v);
2015-08-21 07:04:50 +00:00
}
error_propagate(errp, err);
}
''',
c_type=typ.c_name())
return ret
2015-08-21 07:04:50 +00:00
def gen_visit_struct_fields(name, base, members):
2015-08-21 07:04:50 +00:00
ret = ''
if base:
qapi: Unbox base members Rather than storing a base class as a pointer to a box, just store the fields of that base class in the same order, so that a child struct can be directly cast to its parent. This gives less malloc overhead, less pointer dereferencing, and even less generated code. Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer struct for flat unions" (although that patch had fewer places to change, as less of qemu was directly using qapi structs for flat unions). It also allows us to turn on automatic type-safe wrappers for upcasting to the base class of a struct. Changes to the generated code look like this in qapi-types.h: | struct SpiceChannel { |- SpiceBasicInfo *base; |+ /* Members inherited from SpiceBasicInfo: */ |+ char *host; |+ char *port; |+ NetworkAddressFamily family; |+ /* Own members: */ | int64_t connection_id; as well as additional upcast functions like qapi_SpiceChannel_base(). Meanwhile, changes to qapi-visit.c look like: | static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp) | { | Error *err = NULL; | |- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err); |+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err); | if (err) { (the cast is necessary, since our upcast wrappers only deal with a single pointer, not pointer-to-pointer); plus the wholesale elimination of some now-unused visit_type_implicit_FOO() functions. Without boxing, the corner case of one empty struct having another empty struct as its base type now requires inserting a dummy member (previously, the 'Base *base' member sufficed). And now that we no longer consume a 'base' member in the generated C struct, we can delete the former negative struct-base-clash-base test. Backports commit ddf21908961073199f3d186204da4810f2ea150b from qemu
2018-02-20 00:15:35 +00:00
ret += gen_visit_fields_decl(base)
2015-08-21 07:04:50 +00:00
qapi: Unbox base members Rather than storing a base class as a pointer to a box, just store the fields of that base class in the same order, so that a child struct can be directly cast to its parent. This gives less malloc overhead, less pointer dereferencing, and even less generated code. Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer struct for flat unions" (although that patch had fewer places to change, as less of qemu was directly using qapi structs for flat unions). It also allows us to turn on automatic type-safe wrappers for upcasting to the base class of a struct. Changes to the generated code look like this in qapi-types.h: | struct SpiceChannel { |- SpiceBasicInfo *base; |+ /* Members inherited from SpiceBasicInfo: */ |+ char *host; |+ char *port; |+ NetworkAddressFamily family; |+ /* Own members: */ | int64_t connection_id; as well as additional upcast functions like qapi_SpiceChannel_base(). Meanwhile, changes to qapi-visit.c look like: | static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp) | { | Error *err = NULL; | |- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err); |+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err); | if (err) { (the cast is necessary, since our upcast wrappers only deal with a single pointer, not pointer-to-pointer); plus the wholesale elimination of some now-unused visit_type_implicit_FOO() functions. Without boxing, the corner case of one empty struct having another empty struct as its base type now requires inserting a dummy member (previously, the 'Base *base' member sufficed). And now that we no longer consume a 'base' member in the generated C struct, we can delete the former negative struct-base-clash-base test. Backports commit ddf21908961073199f3d186204da4810f2ea150b from qemu
2018-02-20 00:15:35 +00:00
struct_fields_seen.add(name)
2015-08-21 07:04:50 +00:00
ret += mcgen('''
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
2015-08-21 07:04:50 +00:00
{
Error *err = NULL;
2015-08-21 07:04:50 +00:00
''',
c_name=c_name(name))
2015-08-21 07:04:50 +00:00
if base:
ret += mcgen('''
qapi: Unbox base members Rather than storing a base class as a pointer to a box, just store the fields of that base class in the same order, so that a child struct can be directly cast to its parent. This gives less malloc overhead, less pointer dereferencing, and even less generated code. Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer struct for flat unions" (although that patch had fewer places to change, as less of qemu was directly using qapi structs for flat unions). It also allows us to turn on automatic type-safe wrappers for upcasting to the base class of a struct. Changes to the generated code look like this in qapi-types.h: | struct SpiceChannel { |- SpiceBasicInfo *base; |+ /* Members inherited from SpiceBasicInfo: */ |+ char *host; |+ char *port; |+ NetworkAddressFamily family; |+ /* Own members: */ | int64_t connection_id; as well as additional upcast functions like qapi_SpiceChannel_base(). Meanwhile, changes to qapi-visit.c look like: | static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp) | { | Error *err = NULL; | |- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err); |+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err); | if (err) { (the cast is necessary, since our upcast wrappers only deal with a single pointer, not pointer-to-pointer); plus the wholesale elimination of some now-unused visit_type_implicit_FOO() functions. Without boxing, the corner case of one empty struct having another empty struct as its base type now requires inserting a dummy member (previously, the 'Base *base' member sufficed). And now that we no longer consume a 'base' member in the generated C struct, we can delete the former negative struct-base-clash-base test. Backports commit ddf21908961073199f3d186204da4810f2ea150b from qemu
2018-02-20 00:15:35 +00:00
visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
2015-08-21 07:04:50 +00:00
''',
qapi: Unbox base members Rather than storing a base class as a pointer to a box, just store the fields of that base class in the same order, so that a child struct can be directly cast to its parent. This gives less malloc overhead, less pointer dereferencing, and even less generated code. Compare to the earlier commit 1e6c1616a "qapi: Generate a nicer struct for flat unions" (although that patch had fewer places to change, as less of qemu was directly using qapi structs for flat unions). It also allows us to turn on automatic type-safe wrappers for upcasting to the base class of a struct. Changes to the generated code look like this in qapi-types.h: | struct SpiceChannel { |- SpiceBasicInfo *base; |+ /* Members inherited from SpiceBasicInfo: */ |+ char *host; |+ char *port; |+ NetworkAddressFamily family; |+ /* Own members: */ | int64_t connection_id; as well as additional upcast functions like qapi_SpiceChannel_base(). Meanwhile, changes to qapi-visit.c look like: | static void visit_type_SpiceChannel_fields(Visitor *v, SpiceChannel **obj, Error **errp) | { | Error *err = NULL; | |- visit_type_implicit_SpiceBasicInfo(v, &(*obj)->base, &err); |+ visit_type_SpiceBasicInfo_fields(v, (SpiceBasicInfo **)obj, &err); | if (err) { (the cast is necessary, since our upcast wrappers only deal with a single pointer, not pointer-to-pointer); plus the wholesale elimination of some now-unused visit_type_implicit_FOO() functions. Without boxing, the corner case of one empty struct having another empty struct as its base type now requires inserting a dummy member (previously, the 'Base *base' member sufficed). And now that we no longer consume a 'base' member in the generated C struct, we can delete the former negative struct-base-clash-base test. Backports commit ddf21908961073199f3d186204da4810f2ea150b from qemu
2018-02-20 00:15:35 +00:00
c_type=base.c_name())
ret += gen_err_check()
2015-08-21 07:04:50 +00:00
ret += gen_visit_fields(members, prefix='(*obj)->')
2015-08-21 07:04:50 +00:00
# 'goto out' produced for base, and by gen_visit_fields() for each member
if base or members:
2015-08-21 07:04:50 +00:00
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)
{
2015-08-21 07:04:50 +00:00
Error *err = NULL;
visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
2015-08-21 07:04:50 +00:00
if (!err) {
if (*obj) {
visit_type_%(c_name)s_fields(v, obj, errp);
2015-08-21 07:04:50 +00:00
}
visit_end_struct(v, &err);
2015-08-21 07:04:50 +00:00
}
error_propagate(errp, err);
}
''',
name=name, c_name=c_name(name))
2015-08-21 07:04:50 +00:00
return ret
def gen_visit_list(name, element_type):
2015-08-21 07:04:50 +00:00
return mcgen('''
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
2015-08-21 07:04:50 +00:00
{
Error *err = NULL;
GenericList *i, **prev;
visit_start_list(v, name, &err);
2015-08-21 07:04:50 +00:00
if (err) {
goto out;
}
for (prev = (GenericList **)obj;
!err && (i = visit_next_list(v, prev)) != NULL;
2015-08-21 07:04:50 +00:00
prev = &i) {
%(c_name)s *native_i = (%(c_name)s *)i;
visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
2015-08-21 07:04:50 +00:00
}
error_propagate(errp, err);
err = NULL;
visit_end_list(v);
2015-08-21 07:04:50 +00:00
out:
error_propagate(errp, err);
}
''',
c_name=c_name(name), c_elt_type=element_type.c_name())
2015-08-21 07:04:50 +00:00
def gen_visit_enum(name):
2015-08-21 07:04:50 +00:00
return mcgen('''
void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
2015-08-21 07:04:50 +00:00
{
visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
2015-08-21 07:04:50 +00:00
}
''',
c_name=c_name(name), name=name)
2015-08-21 07:04:50 +00:00
def gen_visit_alternate(name, variants):
2015-08-21 07:04:50 +00:00
ret = mcgen('''
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
2015-08-21 07:04:50 +00:00
{
Error *err = NULL;
visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
2015-08-21 07:04:50 +00:00
if (err) {
goto out;
}
visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
2015-08-21 07:04:50 +00:00
if (err) {
goto out_obj;
2015-08-21 07:04:50 +00:00
}
switch ((*obj)->kind) {
''',
c_name=c_name(name))
2015-08-21 07:04:50 +00:00
for var in variants.variants:
2015-08-21 07:04:50 +00:00
ret += mcgen('''
case %(case)s:
visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
2015-08-21 07:04:50 +00:00
break;
''',
case=c_enum_const(variants.tag_member.type.name,
var.name),
c_type=var.type.c_name(),
c_name=c_name(var.name))
2015-08-21 07:04:50 +00:00
ret += mcgen('''
default:
abort();
}
out_obj:
2015-08-21 07:04:50 +00:00
error_propagate(errp, err);
err = NULL;
visit_end_implicit_struct(v);
2015-08-21 07:04:50 +00:00
out:
error_propagate(errp, err);
}
''')
return ret
def gen_visit_union(name, base, variants):
ret = ''
2015-08-21 07:04:50 +00:00
if base:
ret += gen_visit_fields_decl(base)
2015-08-21 07:04:50 +00:00
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)
2015-08-21 07:04:50 +00:00
ret += mcgen('''
void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
2015-08-21 07:04:50 +00:00
{
Error *err = NULL;
visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
2015-08-21 07:04:50 +00:00
if (err) {
goto out;
}
if (!*obj) {
goto out_obj;
}
2015-08-21 07:04:50 +00:00
''',
c_name=c_name(name), name=name)
2015-08-21 07:04:50 +00:00
if base:
ret += mcgen('''
visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
2015-08-21 07:04:50 +00:00
''',
c_name=base.c_name())
else:
ret += mcgen('''
visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &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='kind',
name=variants.tag_member.name)
ret += gen_err_check(label='out_obj')
ret += mcgen('''
if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
goto out_obj;
}
switch ((*obj)->%(c_name)s) {
2015-08-21 07:04:50 +00:00
''',
# 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'))
2015-08-21 07:04:50 +00:00
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))
2015-08-21 07:04:50 +00:00
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))
2015-08-21 07:04:50 +00:00
ret += mcgen('''
break;
''')
2015-08-21 07:04:50 +00:00
ret += mcgen('''
default:
abort();
2015-08-21 07:04:50 +00:00
}
out_obj:
error_propagate(errp, err);
err = NULL;
visit_end_union(v, !!(*obj)->data, &err);
error_propagate(errp, err);
err = NULL;
visit_end_struct(v, &err);
2015-08-21 07:04:50 +00:00
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().
2015-08-21 07:04:50 +00:00
do_builtins = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
parse_command_line("b", ["builtins"])
2015-08-21 07:04:50 +00:00
for o, a in opts:
if o in ("-b", "--builtins"):
2015-08-21 07:04:50 +00:00
do_builtins = True
c_comment = '''
2015-08-21 07:04:50 +00:00
/*
* 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 = '''
2015-08-21 07:04:50 +00:00
/*
* 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.
*
*/
'''
2015-08-21 07:04:50 +00:00
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
'qapi-visit.c', 'qapi-visit.h',
c_comment, h_comment)
2015-08-21 07:04:50 +00:00
fdef.write(mcgen('''
#include "qemu-common.h"
#include "%(prefix)sqapi-visit.h"
''',
prefix=prefix))
fdecl.write(mcgen('''
2015-08-21 07:04:50 +00:00
#include "qapi/visitor.h"
#include "%(prefix)sqapi-types.h"
''',
prefix=prefix))
2015-08-21 07:04:50 +00:00
schema = QAPISchema(input_file)
gen = QAPISchemaGenVisitVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
2015-08-21 07:04:50 +00:00
close_output(fdef, fdecl)