qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

visit.py (12101B)


      1 """
      2 QAPI visitor generator
      3 
      4 Copyright IBM, Corp. 2011
      5 Copyright (C) 2014-2018 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 """
     15 
     16 from typing import List, Optional
     17 
     18 from .common import (
     19     c_enum_const,
     20     c_name,
     21     indent,
     22     mcgen,
     23 )
     24 from .gen import (
     25     QAPISchemaModularCVisitor,
     26     gen_special_features,
     27     ifcontext,
     28 )
     29 from .schema import (
     30     QAPISchema,
     31     QAPISchemaEnumMember,
     32     QAPISchemaEnumType,
     33     QAPISchemaFeature,
     34     QAPISchemaIfCond,
     35     QAPISchemaObjectType,
     36     QAPISchemaObjectTypeMember,
     37     QAPISchemaType,
     38     QAPISchemaVariants,
     39 )
     40 from .source import QAPISourceInfo
     41 
     42 
     43 def gen_visit_decl(name: str, scalar: bool = False) -> str:
     44     c_type = c_name(name) + ' *'
     45     if not scalar:
     46         c_type += '*'
     47     return mcgen('''
     48 
     49 bool visit_type_%(c_name)s(Visitor *v, const char *name,
     50                  %(c_type)sobj, Error **errp);
     51 ''',
     52                  c_name=c_name(name), c_type=c_type)
     53 
     54 
     55 def gen_visit_members_decl(name: str) -> str:
     56     return mcgen('''
     57 
     58 bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
     59 ''',
     60                  c_name=c_name(name))
     61 
     62 
     63 def gen_visit_object_members(name: str,
     64                              base: Optional[QAPISchemaObjectType],
     65                              members: List[QAPISchemaObjectTypeMember],
     66                              variants: Optional[QAPISchemaVariants]) -> str:
     67     ret = mcgen('''
     68 
     69 bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
     70 {
     71 ''',
     72                 c_name=c_name(name))
     73 
     74     if base:
     75         ret += mcgen('''
     76     if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) {
     77         return false;
     78     }
     79 ''',
     80                      c_type=base.c_name())
     81 
     82     for memb in members:
     83         ret += memb.ifcond.gen_if()
     84         if memb.optional:
     85             ret += mcgen('''
     86     if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
     87 ''',
     88                          name=memb.name, c_name=c_name(memb.name))
     89             indent.increase()
     90         special_features = gen_special_features(memb.features)
     91         if special_features != '0':
     92             ret += mcgen('''
     93     if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) {
     94         return false;
     95     }
     96     if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) {
     97 ''',
     98                          name=memb.name, special_features=special_features)
     99             indent.increase()
    100         ret += mcgen('''
    101     if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
    102         return false;
    103     }
    104 ''',
    105                      c_type=memb.type.c_name(), name=memb.name,
    106                      c_name=c_name(memb.name))
    107         if special_features != '0':
    108             indent.decrease()
    109             ret += mcgen('''
    110     }
    111 ''')
    112         if memb.optional:
    113             indent.decrease()
    114             ret += mcgen('''
    115     }
    116 ''')
    117         ret += memb.ifcond.gen_endif()
    118 
    119     if variants:
    120         tag_member = variants.tag_member
    121         assert isinstance(tag_member.type, QAPISchemaEnumType)
    122 
    123         ret += mcgen('''
    124     switch (obj->%(c_name)s) {
    125 ''',
    126                      c_name=c_name(tag_member.name))
    127 
    128         for var in variants.variants:
    129             case_str = c_enum_const(tag_member.type.name, var.name,
    130                                     tag_member.type.prefix)
    131             ret += var.ifcond.gen_if()
    132             if var.type.name == 'q_empty':
    133                 # valid variant and nothing to do
    134                 ret += mcgen('''
    135     case %(case)s:
    136         break;
    137 ''',
    138                              case=case_str)
    139             else:
    140                 ret += mcgen('''
    141     case %(case)s:
    142         return visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp);
    143 ''',
    144                              case=case_str,
    145                              c_type=var.type.c_name(), c_name=c_name(var.name))
    146 
    147             ret += var.ifcond.gen_endif()
    148         ret += mcgen('''
    149     default:
    150         abort();
    151     }
    152 ''')
    153 
    154     ret += mcgen('''
    155     return true;
    156 }
    157 ''')
    158     return ret
    159 
    160 
    161 def gen_visit_list(name: str, element_type: QAPISchemaType) -> str:
    162     return mcgen('''
    163 
    164 bool visit_type_%(c_name)s(Visitor *v, const char *name,
    165                  %(c_name)s **obj, Error **errp)
    166 {
    167     bool ok = false;
    168     %(c_name)s *tail;
    169     size_t size = sizeof(**obj);
    170 
    171     if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) {
    172         return false;
    173     }
    174 
    175     for (tail = *obj; tail;
    176          tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
    177         if (!visit_type_%(c_elt_type)s(v, NULL, &tail->value, errp)) {
    178             goto out_obj;
    179         }
    180     }
    181 
    182     ok = visit_check_list(v, errp);
    183 out_obj:
    184     visit_end_list(v, (void **)obj);
    185     if (!ok && visit_is_input(v)) {
    186         qapi_free_%(c_name)s(*obj);
    187         *obj = NULL;
    188     }
    189     return ok;
    190 }
    191 ''',
    192                  c_name=c_name(name), c_elt_type=element_type.c_name())
    193 
    194 
    195 def gen_visit_enum(name: str) -> str:
    196     return mcgen('''
    197 
    198 bool visit_type_%(c_name)s(Visitor *v, const char *name,
    199                  %(c_name)s *obj, Error **errp)
    200 {
    201     int value = *obj;
    202     bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
    203     *obj = value;
    204     return ok;
    205 }
    206 ''',
    207                  c_name=c_name(name))
    208 
    209 
    210 def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
    211     ret = mcgen('''
    212 
    213 bool visit_type_%(c_name)s(Visitor *v, const char *name,
    214                  %(c_name)s **obj, Error **errp)
    215 {
    216     bool ok = false;
    217 
    218     if (!visit_start_alternate(v, name, (GenericAlternate **)obj,
    219                                sizeof(**obj), errp)) {
    220         return false;
    221     }
    222     if (!*obj) {
    223         /* incomplete */
    224         assert(visit_is_dealloc(v));
    225         ok = true;
    226         goto out_obj;
    227     }
    228     switch ((*obj)->type) {
    229 ''',
    230                 c_name=c_name(name))
    231 
    232     for var in variants.variants:
    233         ret += var.ifcond.gen_if()
    234         ret += mcgen('''
    235     case %(case)s:
    236 ''',
    237                      case=var.type.alternate_qtype())
    238         if isinstance(var.type, QAPISchemaObjectType):
    239             ret += mcgen('''
    240         if (!visit_start_struct(v, name, NULL, 0, errp)) {
    241             break;
    242         }
    243         if (visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, errp)) {
    244             ok = visit_check_struct(v, errp);
    245         }
    246         visit_end_struct(v, NULL);
    247 ''',
    248                          c_type=var.type.c_name(),
    249                          c_name=c_name(var.name))
    250         else:
    251             ret += mcgen('''
    252         ok = visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, errp);
    253 ''',
    254                          c_type=var.type.c_name(),
    255                          c_name=c_name(var.name))
    256         ret += mcgen('''
    257         break;
    258 ''')
    259         ret += var.ifcond.gen_endif()
    260 
    261     ret += mcgen('''
    262     case QTYPE_NONE:
    263         abort();
    264     default:
    265         assert(visit_is_input(v));
    266         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
    267                    "%(name)s");
    268         /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */
    269         g_free(*obj);
    270         *obj = NULL;
    271     }
    272 out_obj:
    273     visit_end_alternate(v, (void **)obj);
    274     if (!ok && visit_is_input(v)) {
    275         qapi_free_%(c_name)s(*obj);
    276         *obj = NULL;
    277     }
    278     return ok;
    279 }
    280 ''',
    281                  name=name, c_name=c_name(name))
    282 
    283     return ret
    284 
    285 
    286 def gen_visit_object(name: str) -> str:
    287     return mcgen('''
    288 
    289 bool visit_type_%(c_name)s(Visitor *v, const char *name,
    290                  %(c_name)s **obj, Error **errp)
    291 {
    292     bool ok = false;
    293 
    294     if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) {
    295         return false;
    296     }
    297     if (!*obj) {
    298         /* incomplete */
    299         assert(visit_is_dealloc(v));
    300         ok = true;
    301         goto out_obj;
    302     }
    303     if (!visit_type_%(c_name)s_members(v, *obj, errp)) {
    304         goto out_obj;
    305     }
    306     ok = visit_check_struct(v, errp);
    307 out_obj:
    308     visit_end_struct(v, (void **)obj);
    309     if (!ok && visit_is_input(v)) {
    310         qapi_free_%(c_name)s(*obj);
    311         *obj = NULL;
    312     }
    313     return ok;
    314 }
    315 ''',
    316                  c_name=c_name(name))
    317 
    318 
    319 class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
    320 
    321     def __init__(self, prefix: str):
    322         super().__init__(
    323             prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
    324             ' * Built-in QAPI visitors', __doc__)
    325 
    326     def _begin_builtin_module(self) -> None:
    327         self._genc.preamble_add(mcgen('''
    328 #include "qemu/osdep.h"
    329 #include "qapi/error.h"
    330 #include "qapi/qapi-builtin-visit.h"
    331 '''))
    332         self._genh.preamble_add(mcgen('''
    333 #include "qapi/visitor.h"
    334 #include "qapi/qapi-builtin-types.h"
    335 
    336 '''))
    337 
    338     def _begin_user_module(self, name: str) -> None:
    339         types = self._module_basename('qapi-types', name)
    340         visit = self._module_basename('qapi-visit', name)
    341         self._genc.preamble_add(mcgen('''
    342 #include "qemu/osdep.h"
    343 #include "qapi/error.h"
    344 #include "qapi/qmp/qerror.h"
    345 #include "%(visit)s.h"
    346 ''',
    347                                       visit=visit))
    348         self._genh.preamble_add(mcgen('''
    349 #include "qapi/qapi-builtin-visit.h"
    350 #include "%(types)s.h"
    351 
    352 ''',
    353                                       types=types))
    354 
    355     def visit_enum_type(self,
    356                         name: str,
    357                         info: Optional[QAPISourceInfo],
    358                         ifcond: QAPISchemaIfCond,
    359                         features: List[QAPISchemaFeature],
    360                         members: List[QAPISchemaEnumMember],
    361                         prefix: Optional[str]) -> None:
    362         with ifcontext(ifcond, self._genh, self._genc):
    363             self._genh.add(gen_visit_decl(name, scalar=True))
    364             self._genc.add(gen_visit_enum(name))
    365 
    366     def visit_array_type(self,
    367                          name: str,
    368                          info: Optional[QAPISourceInfo],
    369                          ifcond: QAPISchemaIfCond,
    370                          element_type: QAPISchemaType) -> None:
    371         with ifcontext(ifcond, self._genh, self._genc):
    372             self._genh.add(gen_visit_decl(name))
    373             self._genc.add(gen_visit_list(name, element_type))
    374 
    375     def visit_object_type(self,
    376                           name: str,
    377                           info: Optional[QAPISourceInfo],
    378                           ifcond: QAPISchemaIfCond,
    379                           features: List[QAPISchemaFeature],
    380                           base: Optional[QAPISchemaObjectType],
    381                           members: List[QAPISchemaObjectTypeMember],
    382                           variants: Optional[QAPISchemaVariants]) -> None:
    383         # Nothing to do for the special empty builtin
    384         if name == 'q_empty':
    385             return
    386         with ifcontext(ifcond, self._genh, self._genc):
    387             self._genh.add(gen_visit_members_decl(name))
    388             self._genc.add(gen_visit_object_members(name, base,
    389                                                     members, variants))
    390             # TODO Worth changing the visitor signature, so we could
    391             # directly use rather than repeat type.is_implicit()?
    392             if not name.startswith('q_'):
    393                 # only explicit types need an allocating visit
    394                 self._genh.add(gen_visit_decl(name))
    395                 self._genc.add(gen_visit_object(name))
    396 
    397     def visit_alternate_type(self,
    398                              name: str,
    399                              info: Optional[QAPISourceInfo],
    400                              ifcond: QAPISchemaIfCond,
    401                              features: List[QAPISchemaFeature],
    402                              variants: QAPISchemaVariants) -> None:
    403         with ifcontext(ifcond, self._genh, self._genc):
    404             self._genh.add(gen_visit_decl(name))
    405             self._genc.add(gen_visit_alternate(name, variants))
    406 
    407 
    408 def gen_visit(schema: QAPISchema,
    409               output_dir: str,
    410               prefix: str,
    411               opt_builtins: bool) -> None:
    412     vis = QAPISchemaGenVisitVisitor(prefix)
    413     schema.visit(vis)
    414     vis.write(output_dir, opt_builtins)