qemu/scripts/qapi-visit.py
<<
>>
Prefs
   1#
   2# QAPI visitor generator
   3#
   4# Copyright IBM, Corp. 2011
   5# Copyright (C) 2014-2016 Red Hat, Inc.
   6#
   7# Authors:
   8#  Anthony Liguori <aliguori@us.ibm.com>
   9#  Michael Roth    <mdroth@linux.vnet.ibm.com>
  10#  Markus Armbruster <armbru@redhat.com>
  11#
  12# This work is licensed under the terms of the GNU GPL, version 2.
  13# See the COPYING file in the top-level directory.
  14
  15from qapi import *
  16import re
  17
  18
  19def gen_visit_decl(name, scalar=False):
  20    c_type = c_name(name) + ' *'
  21    if not scalar:
  22        c_type += '*'
  23    return mcgen('''
  24void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
  25''',
  26                 c_name=c_name(name), c_type=c_type)
  27
  28
  29def gen_visit_members_decl(name):
  30    return mcgen('''
  31
  32void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
  33''',
  34                 c_name=c_name(name))
  35
  36
  37def gen_visit_object_members(name, base, members, variants):
  38    ret = mcgen('''
  39
  40void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
  41{
  42    Error *err = NULL;
  43
  44''',
  45                c_name=c_name(name))
  46
  47    if base:
  48        ret += mcgen('''
  49    visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
  50''',
  51                     c_type=base.c_name())
  52        ret += gen_err_check()
  53
  54    for memb in members:
  55        if memb.optional:
  56            ret += mcgen('''
  57    if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
  58''',
  59                         name=memb.name, c_name=c_name(memb.name))
  60            push_indent()
  61        ret += mcgen('''
  62    visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err);
  63''',
  64                     c_type=memb.type.c_name(), name=memb.name,
  65                     c_name=c_name(memb.name))
  66        ret += gen_err_check()
  67        if memb.optional:
  68            pop_indent()
  69            ret += mcgen('''
  70    }
  71''')
  72
  73    if variants:
  74        ret += mcgen('''
  75    switch (obj->%(c_name)s) {
  76''',
  77                     c_name=c_name(variants.tag_member.name))
  78
  79        for var in variants.variants:
  80            ret += mcgen('''
  81    case %(case)s:
  82        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
  83        break;
  84''',
  85                         case=c_enum_const(variants.tag_member.type.name,
  86                                           var.name,
  87                                           variants.tag_member.type.prefix),
  88                         c_type=var.type.c_name(), c_name=c_name(var.name))
  89
  90        ret += mcgen('''
  91    default:
  92        abort();
  93    }
  94''')
  95
  96    # 'goto out' produced for base, for each member, and if variants were
  97    # present
  98    if base or members or variants:
  99        ret += mcgen('''
 100
 101out:
 102''')
 103    ret += mcgen('''
 104    error_propagate(errp, err);
 105}
 106''')
 107    return ret
 108
 109
 110def gen_visit_list(name, element_type):
 111    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
 112    # assigns to *obj, while a later one fails, we should clean up *obj
 113    # rather than leaving it non-NULL. As currently written, the caller must
 114    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
 115    return mcgen('''
 116
 117void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 118{
 119    Error *err = NULL;
 120    GenericList *i, **prev;
 121
 122    visit_start_list(v, name, &err);
 123    if (err) {
 124        goto out;
 125    }
 126
 127    for (prev = (GenericList **)obj;
 128         !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
 129         prev = &i) {
 130        %(c_name)s *native_i = (%(c_name)s *)i;
 131        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
 132    }
 133
 134    visit_end_list(v);
 135out:
 136    error_propagate(errp, err);
 137}
 138''',
 139                 c_name=c_name(name), c_elt_type=element_type.c_name())
 140
 141
 142def gen_visit_enum(name):
 143    return mcgen('''
 144
 145void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
 146{
 147    int value = *obj;
 148    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
 149    *obj = value;
 150}
 151''',
 152                 c_name=c_name(name))
 153
 154
 155def gen_visit_alternate(name, variants):
 156    promote_int = 'true'
 157    ret = ''
 158    for var in variants.variants:
 159        if var.type.alternate_qtype() == 'QTYPE_QINT':
 160            promote_int = 'false'
 161
 162    ret += mcgen('''
 163
 164void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 165{
 166    Error *err = NULL;
 167
 168    visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj),
 169                          %(promote_int)s, &err);
 170    if (err) {
 171        goto out;
 172    }
 173    switch ((*obj)->type) {
 174''',
 175                 c_name=c_name(name), promote_int=promote_int)
 176
 177    for var in variants.variants:
 178        ret += mcgen('''
 179    case %(case)s:
 180''',
 181                     case=var.type.alternate_qtype())
 182        if isinstance(var.type, QAPISchemaObjectType):
 183            ret += mcgen('''
 184        visit_start_struct(v, name, NULL, 0, &err);
 185        if (err) {
 186            break;
 187        }
 188        visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
 189        error_propagate(errp, err);
 190        err = NULL;
 191        visit_end_struct(v, &err);
 192''',
 193                         c_type=var.type.c_name(),
 194                         c_name=c_name(var.name))
 195        else:
 196            ret += mcgen('''
 197        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
 198''',
 199                         c_type=var.type.c_name(),
 200                         c_name=c_name(var.name))
 201        ret += mcgen('''
 202        break;
 203''')
 204
 205    ret += mcgen('''
 206    default:
 207        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 208                   "%(name)s");
 209    }
 210    visit_end_alternate(v);
 211out:
 212    error_propagate(errp, err);
 213}
 214''',
 215                 name=name)
 216
 217    return ret
 218
 219
 220def gen_visit_object(name, base, members, variants):
 221    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
 222    # *obj, but then visit_type_FOO_members() fails, we should clean up *obj
 223    # rather than leaving it non-NULL. As currently written, the caller must
 224    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
 225    return mcgen('''
 226
 227void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
 228{
 229    Error *err = NULL;
 230
 231    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
 232    if (err) {
 233        goto out;
 234    }
 235    if (!*obj) {
 236        goto out_obj;
 237    }
 238    visit_type_%(c_name)s_members(v, *obj, &err);
 239    error_propagate(errp, err);
 240    err = NULL;
 241out_obj:
 242    visit_end_struct(v, &err);
 243out:
 244    error_propagate(errp, err);
 245}
 246''',
 247                 c_name=c_name(name))
 248
 249
 250class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
 251    def __init__(self):
 252        self.decl = None
 253        self.defn = None
 254        self._btin = None
 255
 256    def visit_begin(self, schema):
 257        self.decl = ''
 258        self.defn = ''
 259        self._btin = guardstart('QAPI_VISIT_BUILTIN')
 260
 261    def visit_end(self):
 262        # To avoid header dependency hell, we always generate
 263        # declarations for built-in types in our header files and
 264        # simply guard them.  See also do_builtins (command line
 265        # option -b).
 266        self._btin += guardend('QAPI_VISIT_BUILTIN')
 267        self.decl = self._btin + self.decl
 268        self._btin = None
 269
 270    def visit_enum_type(self, name, info, values, prefix):
 271        # Special case for our lone builtin enum type
 272        # TODO use something cleaner than existence of info
 273        if not info:
 274            self._btin += gen_visit_decl(name, scalar=True)
 275            if do_builtins:
 276                self.defn += gen_visit_enum(name)
 277        else:
 278            self.decl += gen_visit_decl(name, scalar=True)
 279            self.defn += gen_visit_enum(name)
 280
 281    def visit_array_type(self, name, info, element_type):
 282        decl = gen_visit_decl(name)
 283        defn = gen_visit_list(name, element_type)
 284        if isinstance(element_type, QAPISchemaBuiltinType):
 285            self._btin += decl
 286            if do_builtins:
 287                self.defn += defn
 288        else:
 289            self.decl += decl
 290            self.defn += defn
 291
 292    def visit_object_type(self, name, info, base, members, variants):
 293        # Nothing to do for the special empty builtin
 294        if name == 'q_empty':
 295            return
 296        self.decl += gen_visit_members_decl(name)
 297        self.defn += gen_visit_object_members(name, base, members, variants)
 298        # TODO Worth changing the visitor signature, so we could
 299        # directly use rather than repeat type.is_implicit()?
 300        if not name.startswith('q_'):
 301            # only explicit types need an allocating visit
 302            self.decl += gen_visit_decl(name)
 303            self.defn += gen_visit_object(name, base, members, variants)
 304
 305    def visit_alternate_type(self, name, info, variants):
 306        self.decl += gen_visit_decl(name)
 307        self.defn += gen_visit_alternate(name, variants)
 308
 309# If you link code generated from multiple schemata, you want only one
 310# instance of the code for built-in types.  Generate it only when
 311# do_builtins, enabled by command line option -b.  See also
 312# QAPISchemaGenVisitVisitor.visit_end().
 313do_builtins = False
 314
 315(input_file, output_dir, do_c, do_h, prefix, opts) = \
 316    parse_command_line("b", ["builtins"])
 317
 318for o, a in opts:
 319    if o in ("-b", "--builtins"):
 320        do_builtins = True
 321
 322c_comment = '''
 323/*
 324 * schema-defined QAPI visitor functions
 325 *
 326 * Copyright IBM, Corp. 2011
 327 *
 328 * Authors:
 329 *  Anthony Liguori   <aliguori@us.ibm.com>
 330 *
 331 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 332 * See the COPYING.LIB file in the top-level directory.
 333 *
 334 */
 335'''
 336h_comment = '''
 337/*
 338 * schema-defined QAPI visitor functions
 339 *
 340 * Copyright IBM, Corp. 2011
 341 *
 342 * Authors:
 343 *  Anthony Liguori   <aliguori@us.ibm.com>
 344 *
 345 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 346 * See the COPYING.LIB file in the top-level directory.
 347 *
 348 */
 349'''
 350
 351(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
 352                            'qapi-visit.c', 'qapi-visit.h',
 353                            c_comment, h_comment)
 354
 355fdef.write(mcgen('''
 356#include "qemu/osdep.h"
 357#include "qemu-common.h"
 358#include "qapi/error.h"
 359#include "%(prefix)sqapi-visit.h"
 360''',
 361                 prefix=prefix))
 362
 363fdecl.write(mcgen('''
 364#include "qapi/visitor.h"
 365#include "qapi/qmp/qerror.h"
 366#include "%(prefix)sqapi-types.h"
 367
 368''',
 369                  prefix=prefix))
 370
 371schema = QAPISchema(input_file)
 372gen = QAPISchemaGenVisitVisitor()
 373schema.visit(gen)
 374fdef.write(gen.defn)
 375fdecl.write(gen.decl)
 376
 377close_output(fdef, fdecl)
 378