cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

gen.py (10094B)


      1# -*- coding: utf-8 -*-
      2#
      3# QAPI code generation
      4#
      5# Copyright (c) 2015-2019 Red Hat Inc.
      6#
      7# Authors:
      8#  Markus Armbruster <armbru@redhat.com>
      9#  Marc-André Lureau <marcandre.lureau@redhat.com>
     10#
     11# This work is licensed under the terms of the GNU GPL, version 2.
     12# See the COPYING file in the top-level directory.
     13
     14from contextlib import contextmanager
     15import os
     16import re
     17from typing import (
     18    Dict,
     19    Iterator,
     20    Optional,
     21    Tuple,
     22)
     23
     24from .common import (
     25    c_fname,
     26    c_name,
     27    guardend,
     28    guardstart,
     29    mcgen,
     30)
     31from .schema import (
     32    QAPISchemaIfCond,
     33    QAPISchemaModule,
     34    QAPISchemaObjectType,
     35    QAPISchemaVisitor,
     36)
     37from .source import QAPISourceInfo
     38
     39
     40class QAPIGen:
     41    def __init__(self, fname: str):
     42        self.fname = fname
     43        self._preamble = ''
     44        self._body = ''
     45
     46    def preamble_add(self, text: str) -> None:
     47        self._preamble += text
     48
     49    def add(self, text: str) -> None:
     50        self._body += text
     51
     52    def get_content(self) -> str:
     53        return self._top() + self._preamble + self._body + self._bottom()
     54
     55    def _top(self) -> str:
     56        # pylint: disable=no-self-use
     57        return ''
     58
     59    def _bottom(self) -> str:
     60        # pylint: disable=no-self-use
     61        return ''
     62
     63    def write(self, output_dir: str) -> None:
     64        # Include paths starting with ../ are used to reuse modules of the main
     65        # schema in specialised schemas. Don't overwrite the files that are
     66        # already generated for the main schema.
     67        if self.fname.startswith('../'):
     68            return
     69        pathname = os.path.join(output_dir, self.fname)
     70        odir = os.path.dirname(pathname)
     71
     72        if odir:
     73            os.makedirs(odir, exist_ok=True)
     74
     75        # use os.open for O_CREAT to create and read a non-existant file
     76        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
     77        with os.fdopen(fd, 'r+', encoding='utf-8') as fp:
     78            text = self.get_content()
     79            oldtext = fp.read(len(text) + 1)
     80            if text != oldtext:
     81                fp.seek(0)
     82                fp.truncate(0)
     83                fp.write(text)
     84
     85
     86def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str:
     87    if before == after:
     88        return after   # suppress empty #if ... #endif
     89
     90    assert after.startswith(before)
     91    out = before
     92    added = after[len(before):]
     93    if added[0] == '\n':
     94        out += '\n'
     95        added = added[1:]
     96    out += ifcond.gen_if()
     97    out += added
     98    out += ifcond.gen_endif()
     99    return out
    100
    101
    102def build_params(arg_type: Optional[QAPISchemaObjectType],
    103                 boxed: bool,
    104                 extra: Optional[str] = None) -> str:
    105    ret = ''
    106    sep = ''
    107    if boxed:
    108        assert arg_type
    109        ret += '%s arg' % arg_type.c_param_type()
    110        sep = ', '
    111    elif arg_type:
    112        assert not arg_type.variants
    113        for memb in arg_type.members:
    114            ret += sep
    115            sep = ', '
    116            if memb.optional:
    117                ret += 'bool has_%s, ' % c_name(memb.name)
    118            ret += '%s %s' % (memb.type.c_param_type(),
    119                              c_name(memb.name))
    120    if extra:
    121        ret += sep + extra
    122    return ret if ret else 'void'
    123
    124
    125class QAPIGenCCode(QAPIGen):
    126    def __init__(self, fname: str):
    127        super().__init__(fname)
    128        self._start_if: Optional[Tuple[QAPISchemaIfCond, str, str]] = None
    129
    130    def start_if(self, ifcond: QAPISchemaIfCond) -> None:
    131        assert self._start_if is None
    132        self._start_if = (ifcond, self._body, self._preamble)
    133
    134    def end_if(self) -> None:
    135        assert self._start_if is not None
    136        self._body = _wrap_ifcond(self._start_if[0],
    137                                  self._start_if[1], self._body)
    138        self._preamble = _wrap_ifcond(self._start_if[0],
    139                                      self._start_if[2], self._preamble)
    140        self._start_if = None
    141
    142    def get_content(self) -> str:
    143        assert self._start_if is None
    144        return super().get_content()
    145
    146
    147class QAPIGenC(QAPIGenCCode):
    148    def __init__(self, fname: str, blurb: str, pydoc: str):
    149        super().__init__(fname)
    150        self._blurb = blurb
    151        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
    152                                                  re.MULTILINE))
    153
    154    def _top(self) -> str:
    155        return mcgen('''
    156/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
    157
    158/*
    159%(blurb)s
    160 *
    161 * %(copyright)s
    162 *
    163 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
    164 * See the COPYING.LIB file in the top-level directory.
    165 */
    166
    167''',
    168                     blurb=self._blurb, copyright=self._copyright)
    169
    170    def _bottom(self) -> str:
    171        return mcgen('''
    172
    173/* Dummy declaration to prevent empty .o file */
    174char qapi_dummy_%(name)s;
    175''',
    176                     name=c_fname(self.fname))
    177
    178
    179class QAPIGenH(QAPIGenC):
    180    def _top(self) -> str:
    181        return super()._top() + guardstart(self.fname)
    182
    183    def _bottom(self) -> str:
    184        return guardend(self.fname)
    185
    186
    187@contextmanager
    188def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]:
    189    """
    190    A with-statement context manager that wraps with `start_if()` / `end_if()`.
    191
    192    :param ifcond: A sequence of conditionals, passed to `start_if()`.
    193    :param args: any number of `QAPIGenCCode`.
    194
    195    Example::
    196
    197        with ifcontext(ifcond, self._genh, self._genc):
    198            modify self._genh and self._genc ...
    199
    200    Is equivalent to calling::
    201
    202        self._genh.start_if(ifcond)
    203        self._genc.start_if(ifcond)
    204        modify self._genh and self._genc ...
    205        self._genh.end_if()
    206        self._genc.end_if()
    207    """
    208    for arg in args:
    209        arg.start_if(ifcond)
    210    yield
    211    for arg in args:
    212        arg.end_if()
    213
    214
    215class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
    216    def __init__(self,
    217                 prefix: str,
    218                 what: str,
    219                 blurb: str,
    220                 pydoc: str):
    221        self._prefix = prefix
    222        self._what = what
    223        self._genc = QAPIGenC(self._prefix + self._what + '.c',
    224                              blurb, pydoc)
    225        self._genh = QAPIGenH(self._prefix + self._what + '.h',
    226                              blurb, pydoc)
    227
    228    def write(self, output_dir: str) -> None:
    229        self._genc.write(output_dir)
    230        self._genh.write(output_dir)
    231
    232
    233class QAPISchemaModularCVisitor(QAPISchemaVisitor):
    234    def __init__(self,
    235                 prefix: str,
    236                 what: str,
    237                 user_blurb: str,
    238                 builtin_blurb: Optional[str],
    239                 pydoc: str):
    240        self._prefix = prefix
    241        self._what = what
    242        self._user_blurb = user_blurb
    243        self._builtin_blurb = builtin_blurb
    244        self._pydoc = pydoc
    245        self._current_module: Optional[str] = None
    246        self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH]] = {}
    247        self._main_module: Optional[str] = None
    248
    249    @property
    250    def _genc(self) -> QAPIGenC:
    251        assert self._current_module is not None
    252        return self._module[self._current_module][0]
    253
    254    @property
    255    def _genh(self) -> QAPIGenH:
    256        assert self._current_module is not None
    257        return self._module[self._current_module][1]
    258
    259    @staticmethod
    260    def _module_dirname(name: str) -> str:
    261        if QAPISchemaModule.is_user_module(name):
    262            return os.path.dirname(name)
    263        return ''
    264
    265    def _module_basename(self, what: str, name: str) -> str:
    266        ret = '' if QAPISchemaModule.is_builtin_module(name) else self._prefix
    267        if QAPISchemaModule.is_user_module(name):
    268            basename = os.path.basename(name)
    269            ret += what
    270            if name != self._main_module:
    271                ret += '-' + os.path.splitext(basename)[0]
    272        else:
    273            assert QAPISchemaModule.is_system_module(name)
    274            ret += re.sub(r'-', '-' + name[2:] + '-', what)
    275        return ret
    276
    277    def _module_filename(self, what: str, name: str) -> str:
    278        return os.path.join(self._module_dirname(name),
    279                            self._module_basename(what, name))
    280
    281    def _add_module(self, name: str, blurb: str) -> None:
    282        if QAPISchemaModule.is_user_module(name):
    283            if self._main_module is None:
    284                self._main_module = name
    285        basename = self._module_filename(self._what, name)
    286        genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
    287        genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
    288        self._module[name] = (genc, genh)
    289        self._current_module = name
    290
    291    @contextmanager
    292    def _temp_module(self, name: str) -> Iterator[None]:
    293        old_module = self._current_module
    294        self._current_module = name
    295        yield
    296        self._current_module = old_module
    297
    298    def write(self, output_dir: str, opt_builtins: bool = False) -> None:
    299        for name, (genc, genh) in self._module.items():
    300            if QAPISchemaModule.is_builtin_module(name) and not opt_builtins:
    301                continue
    302            genc.write(output_dir)
    303            genh.write(output_dir)
    304
    305    def _begin_builtin_module(self) -> None:
    306        pass
    307
    308    def _begin_user_module(self, name: str) -> None:
    309        pass
    310
    311    def visit_module(self, name: str) -> None:
    312        if QAPISchemaModule.is_builtin_module(name):
    313            if self._builtin_blurb:
    314                self._add_module(name, self._builtin_blurb)
    315                self._begin_builtin_module()
    316            else:
    317                # The built-in module has not been created.  No code may
    318                # be generated.
    319                self._current_module = None
    320        else:
    321            assert QAPISchemaModule.is_user_module(name)
    322            self._add_module(name, self._user_blurb)
    323            self._begin_user_module(name)
    324
    325    def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
    326        relname = os.path.relpath(self._module_filename(self._what, name),
    327                                  os.path.dirname(self._genh.fname))
    328        self._genh.preamble_add(mcgen('''
    329#include "%(relname)s.h"
    330''',
    331                                      relname=relname))