Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

agriconnect / cffi   python

Repository URL to install this package:

/ vengine_gen.py

#
# DEPRECATED: implementation for ffi.verify()
#
import sys, os
import types

from . import model
from .error import VerificationError


class VGenericEngine(object):
    _class_key = 'g'
    _gen_python_module = False

    def __init__(self, verifier):
        self.verifier = verifier
        self.ffi = verifier.ffi
        self.export_symbols = []
        self._struct_pending_verification = {}

    def patch_extension_kwds(self, kwds):
        # add 'export_symbols' to the dictionary.  Note that we add the
        # list before filling it.  When we fill it, it will thus also show
        # up in kwds['export_symbols'].
        kwds.setdefault('export_symbols', self.export_symbols)

    def find_module(self, module_name, path, so_suffixes):
        for so_suffix in so_suffixes:
            basename = module_name + so_suffix
            if path is None:
                path = sys.path
            for dirname in path:
                filename = os.path.join(dirname, basename)
                if os.path.isfile(filename):
                    return filename

    def collect_types(self):
        pass      # not needed in the generic engine

    def _prnt(self, what=''):
        self._f.write(what + '\n')

    def write_source_to_f(self):
        prnt = self._prnt
        # first paste some standard set of lines that are mostly '#include'
        prnt(cffimod_header)
        # then paste the C source given by the user, verbatim.
        prnt(self.verifier.preamble)
        #
        # call generate_gen_xxx_decl(), for every xxx found from
        # ffi._parser._declarations.  This generates all the functions.
        self._generate('decl')
        #
        # on Windows, distutils insists on putting init_cffi_xyz in
        # 'export_symbols', so instead of fighting it, just give up and
        # give it one
        if sys.platform == 'win32':
            if sys.version_info >= (3,):
                prefix = 'PyInit_'
            else:
                prefix = 'init'
            modname = self.verifier.get_module_name()
            prnt("void %s%s(void) { }\n" % (prefix, modname))

    def load_library(self, flags=0):
        # import it with the CFFI backend
        backend = self.ffi._backend
        # needs to make a path that contains '/', on Posix
        filename = os.path.join(os.curdir, self.verifier.modulefilename)
        module = backend.load_library(filename, flags)
        #
        # call loading_gen_struct() to get the struct layout inferred by
        # the C compiler
        self._load(module, 'loading')

        # build the FFILibrary class and instance, this is a module subclass
        # because modules are expected to have usually-constant-attributes and
        # in PyPy this means the JIT is able to treat attributes as constant,
        # which we want.
        class FFILibrary(types.ModuleType):
            _cffi_generic_module = module
            _cffi_ffi = self.ffi
            _cffi_dir = []
            def __dir__(self):
                return FFILibrary._cffi_dir
        library = FFILibrary("")
        #
        # finally, call the loaded_gen_xxx() functions.  This will set
        # up the 'library' object.
        self._load(module, 'loaded', library=library)
        return library

    def _get_declarations(self):
        lst = [(key, tp) for (key, (tp, qual)) in
                                self.ffi._parser._declarations.items()]
        lst.sort()
        return lst

    def _generate(self, step_name):
        for name, tp in self._get_declarations():
            kind, realname = name.split(' ', 1)
            try:
                method = getattr(self, '_generate_gen_%s_%s' % (kind,
                                                                step_name))
            except AttributeError:
                raise VerificationError(
                    "not implemented in verify(): %r" % name)
            try:
                method(tp, realname)
            except Exception as e:
                model.attach_exception_info(e, name)
                raise

    def _load(self, module, step_name, **kwds):
        for name, tp in self._get_declarations():
            kind, realname = name.split(' ', 1)
            method = getattr(self, '_%s_gen_%s' % (step_name, kind))
            try:
                method(tp, realname, module, **kwds)
            except Exception as e:
                model.attach_exception_info(e, name)
                raise

    def _generate_nothing(self, tp, name):
        pass

    def _loaded_noop(self, tp, name, module, **kwds):
        pass

    # ----------
    # typedefs: generates no code so far

    _generate_gen_typedef_decl   = _generate_nothing
    _loading_gen_typedef         = _loaded_noop
    _loaded_gen_typedef          = _loaded_noop

    # ----------
    # function declarations

    def _generate_gen_function_decl(self, tp, name):
        assert isinstance(tp, model.FunctionPtrType)
        if tp.ellipsis:
            # cannot support vararg functions better than this: check for its
            # exact type (including the fixed arguments), and build it as a
            # constant function pointer (no _cffi_f_%s wrapper)
            self._generate_gen_const(False, name, tp)
            return
        prnt = self._prnt
        numargs = len(tp.args)
        argnames = []
        for i, type in enumerate(tp.args):
            indirection = ''
            if isinstance(type, model.StructOrUnion):
                indirection = '*'
            argnames.append('%sx%d' % (indirection, i))
        context = 'argument of %s' % name
        arglist = [type.get_c_name(' %s' % arg, context)
                   for type, arg in zip(tp.args, argnames)]
        tpresult = tp.result
        if isinstance(tpresult, model.StructOrUnion):
            arglist.insert(0, tpresult.get_c_name(' *r', context))
            tpresult = model.void_type
        arglist = ', '.join(arglist) or 'void'
        wrappername = '_cffi_f_%s' % name
        self.export_symbols.append(wrappername)
        if tp.abi:
            abi = tp.abi + ' '
        else:
            abi = ''
        funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
        context = 'result of %s' % name
        prnt(tpresult.get_c_name(funcdecl, context))
        prnt('{')
        #
        if isinstance(tp.result, model.StructOrUnion):
            result_code = '*r = '
        elif not isinstance(tp.result, model.VoidType):
            result_code = 'return '
        else:
            result_code = ''
        prnt('  %s%s(%s);' % (result_code, name, ', '.join(argnames)))
        prnt('}')
        prnt()

    _loading_gen_function = _loaded_noop

    def _loaded_gen_function(self, tp, name, module, library):
        assert isinstance(tp, model.FunctionPtrType)
        if tp.ellipsis:
            newfunction = self._load_constant(False, tp, name, module)
        else:
            indirections = []
            base_tp = tp
            if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args)
                    or isinstance(tp.result, model.StructOrUnion)):
                indirect_args = []
                for i, typ in enumerate(tp.args):
                    if isinstance(typ, model.StructOrUnion):
                        typ = model.PointerType(typ)
                        indirections.append((i, typ))
                    indirect_args.append(typ)
                indirect_result = tp.result
                if isinstance(indirect_result, model.StructOrUnion):
                    if indirect_result.fldtypes is None:
                        raise TypeError("'%s' is used as result type, "
                                        "but is opaque" % (
                                            indirect_result._get_c_name(),))
                    indirect_result = model.PointerType(indirect_result)
                    indirect_args.insert(0, indirect_result)
                    indirections.insert(0, ("result", indirect_result))
                    indirect_result = model.void_type
                tp = model.FunctionPtrType(tuple(indirect_args),
                                           indirect_result, tp.ellipsis)
            BFunc = self.ffi._get_cached_btype(tp)
            wrappername = '_cffi_f_%s' % name
            newfunction = module.load_function(BFunc, wrappername)
            for i, typ in indirections:
                newfunction = self._make_struct_wrapper(newfunction, i, typ,
                                                        base_tp)
        setattr(library, name, newfunction)
        type(library)._cffi_dir.append(name)

    def _make_struct_wrapper(self, oldfunc, i, tp, base_tp):
        backend = self.ffi._backend
        BType = self.ffi._get_cached_btype(tp)
        if i == "result":
            ffi = self.ffi
            def newfunc(*args):
                res = ffi.new(BType)
                oldfunc(res, *args)
                return res[0]
        else:
            def newfunc(*args):
                args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
                return oldfunc(*args)
        newfunc._cffi_base_type = base_tp
        return newfunc

    # ----------
    # named structs

    def _generate_gen_struct_decl(self, tp, name):
        assert name == tp.name
        self._generate_struct_or_union_decl(tp, 'struct', name)

    def _loading_gen_struct(self, tp, name, module):
        self._loading_struct_or_union(tp, 'struct', name, module)

    def _loaded_gen_struct(self, tp, name, module, **kwds):
        self._loaded_struct_or_union(tp)

    def _generate_gen_union_decl(self, tp, name):
        assert name == tp.name
        self._generate_struct_or_union_decl(tp, 'union', name)

    def _loading_gen_union(self, tp, name, module):
        self._loading_struct_or_union(tp, 'union', name, module)

    def _loaded_gen_union(self, tp, name, module, **kwds):
        self._loaded_struct_or_union(tp)

    def _generate_struct_or_union_decl(self, tp, prefix, name):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
        cname = ('%s %s' % (prefix, name)).strip()
        #
        prnt = self._prnt
        prnt('static void %s(%s *p)' % (checkfuncname, cname))
        prnt('{')
        prnt('  /* only to generate compile-time warnings or errors */')
        prnt('  (void)p;')
        for fname, ftype, fbitsize, fqual in tp.enumfields():
            if (isinstance(ftype, model.PrimitiveType)
                and ftype.is_integer_type()) or fbitsize >= 0:
                # accept all integers, but complain on float or double
                prnt('  (void)((p->%s) << 1);' % fname)
            else:
                # only accept exactly the type declared.
                try:
                    prnt('  { %s = &p->%s; (void)tmp; }' % (
                        ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
                        fname))
                except VerificationError as e:
                    prnt('  /* %s */' % str(e))   # cannot verify it, ignore
        prnt('}')
        self.export_symbols.append(layoutfuncname)
        prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,))
        prnt('{')
        prnt('  struct _cffi_aligncheck { char x; %s y; };' % cname)
        prnt('  static intptr_t nums[] = {')
        prnt('    sizeof(%s),' % cname)
        prnt('    offsetof(struct _cffi_aligncheck, y),')
        for fname, ftype, fbitsize, fqual in tp.enumfields():
            if fbitsize >= 0:
                continue      # xxx ignore fbitsize for now
            prnt('    offsetof(%s, %s),' % (cname, fname))
            if isinstance(ftype, model.ArrayType) and ftype.length is None:
                prnt('    0,  /* %s */' % ftype._get_c_name())
            else:
                prnt('    sizeof(((%s *)0)->%s),' % (cname, fname))
        prnt('    -1')
        prnt('  };')
        prnt('  return nums[i];')
        prnt('  /* the next line is not executed, but compiled */')
        prnt('  %s(0);' % (checkfuncname,))
        prnt('}')
        prnt()

    def _loading_struct_or_union(self, tp, prefix, name, module):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
        #
        BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0]
        function = module.load_function(BFunc, layoutfuncname)
        layout = []
        num = 0
        while True:
            x = function(num)
            if x < 0: break
            layout.append(x)
            num += 1
        if isinstance(tp, model.StructOrUnion) and tp.partial:
            # use the function()'s sizes and offsets to guide the
            # layout of the struct
            totalsize = layout[0]
            totalalignment = layout[1]
            fieldofs = layout[2::2]
            fieldsize = layout[3::2]
            tp.force_flatten()
            assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
            tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
        else:
            cname = ('%s %s' % (prefix, name)).strip()
            self._struct_pending_verification[tp] = layout, cname

    def _loaded_struct_or_union(self, tp):
        if tp.fldnames is None:
            return     # nothing to do with opaque structs
        self.ffi._get_cached_btype(tp)   # force 'fixedlayout' to be considered

        if tp in self._struct_pending_verification:
            # check that the layout sizes and offsets match the real ones
Loading ...