Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
recompiler.py1599 linesDownload Raw Back to cffi
1import io, os, sys, sysconfig2from . import ffiplatform, model3from .error import VerificationError4from .cffi_opcode import *5 6VERSION_BASE = 0x26017VERSION_EMBEDDED = 0x27018VERSION_CHAR16CHAR32 = 0x28019 10USE_LIMITED_API = ((sys.platform != 'win32' or sys.version_info < (3, 0) or11                   sys.version_info >= (3, 5)) and12                   not sysconfig.get_config_var("Py_GIL_DISABLED"))  # free-threaded doesn't yet support limited API13 14class GlobalExpr:15    def __init__(self, name, address, type_op, size=0, check_value=0):16        self.name = name17        self.address = address18        self.type_op = type_op19        self.size = size20        self.check_value = check_value21 22    def as_c_expr(self):23        return '  { "%s", (void *)%s, %s, (void *)%s },' % (24            self.name, self.address, self.type_op.as_c_expr(), self.size)25 26    def as_python_expr(self):27        return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,28                               self.check_value)29 30class FieldExpr:31    def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):32        self.name = name33        self.field_offset = field_offset34        self.field_size = field_size35        self.fbitsize = fbitsize36        self.field_type_op = field_type_op37 38    def as_c_expr(self):39        spaces = " " * len(self.name)40        return ('  { "%s", %s,\n' % (self.name, self.field_offset) +41                '     %s   %s,\n' % (spaces, self.field_size) +42                '     %s   %s },' % (spaces, self.field_type_op.as_c_expr()))43 44    def as_python_expr(self):45        raise NotImplementedError46 47    def as_field_python_expr(self):48        if self.field_type_op.op == OP_NOOP:49            size_expr = ''50        elif self.field_type_op.op == OP_BITFIELD:51            size_expr = format_four_bytes(self.fbitsize)52        else:53            raise NotImplementedError54        return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),55                              size_expr,56                              self.name)57 58class StructUnionExpr:59    def __init__(self, name, type_index, flags, size, alignment, comment,60                 first_field_index, c_fields):61        self.name = name62        self.type_index = type_index63        self.flags = flags64        self.size = size65        self.alignment = alignment66        self.comment = comment67        self.first_field_index = first_field_index68        self.c_fields = c_fields69 70    def as_c_expr(self):71        return ('  { "%s", %d, %s,' % (self.name, self.type_index, self.flags)72                + '\n    %s, %s, ' % (self.size, self.alignment)73                + '%d, %d ' % (self.first_field_index, len(self.c_fields))74                + ('/* %s */ ' % self.comment if self.comment else '')75                + '},')76 77    def as_python_expr(self):78        flags = eval(self.flags, G_FLAGS)79        fields_expr = [c_field.as_field_python_expr()80                       for c_field in self.c_fields]81        return "(b'%s%s%s',%s)" % (82            format_four_bytes(self.type_index),83            format_four_bytes(flags),84            self.name,85            ','.join(fields_expr))86 87class EnumExpr:88    def __init__(self, name, type_index, size, signed, allenums):89        self.name = name90        self.type_index = type_index91        self.size = size92        self.signed = signed93        self.allenums = allenums94 95    def as_c_expr(self):96        return ('  { "%s", %d, _cffi_prim_int(%s, %s),\n'97                '    "%s" },' % (self.name, self.type_index,98                                 self.size, self.signed, self.allenums))99 100    def as_python_expr(self):101        prim_index = {102            (1, 0): PRIM_UINT8,  (1, 1):  PRIM_INT8,103            (2, 0): PRIM_UINT16, (2, 1):  PRIM_INT16,104            (4, 0): PRIM_UINT32, (4, 1):  PRIM_INT32,105            (8, 0): PRIM_UINT64, (8, 1):  PRIM_INT64,106            }[self.size, self.signed]107        return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),108                                     format_four_bytes(prim_index),109                                     self.name, self.allenums)110 111class TypenameExpr:112    def __init__(self, name, type_index):113        self.name = name114        self.type_index = type_index115 116    def as_c_expr(self):117        return '  { "%s", %d },' % (self.name, self.type_index)118 119    def as_python_expr(self):120        return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)121 122 123# ____________________________________________________________124 125 126class Recompiler:127    _num_externpy = 0128 129    def __init__(self, ffi, module_name, target_is_python=False):130        self.ffi = ffi131        self.module_name = module_name132        self.target_is_python = target_is_python133        self._version = VERSION_BASE134 135    def needs_version(self, ver):136        self._version = max(self._version, ver)137 138    def collect_type_table(self):139        self._typesdict = {}140        self._generate("collecttype")141        #142        all_decls = sorted(self._typesdict, key=str)143        #144        # prepare all FUNCTION bytecode sequences first145        self.cffi_types = []146        for tp in all_decls:147            if tp.is_raw_function:148                assert self._typesdict[tp] is None149                self._typesdict[tp] = len(self.cffi_types)150                self.cffi_types.append(tp)     # placeholder151                for tp1 in tp.args:152                    assert isinstance(tp1, (model.VoidType,153                                            model.BasePrimitiveType,154                                            model.PointerType,155                                            model.StructOrUnionOrEnum,156                                            model.FunctionPtrType))157                    if self._typesdict[tp1] is None:158                        self._typesdict[tp1] = len(self.cffi_types)159                    self.cffi_types.append(tp1)   # placeholder160                self.cffi_types.append('END')     # placeholder161        #162        # prepare all OTHER bytecode sequences163        for tp in all_decls:164            if not tp.is_raw_function and self._typesdict[tp] is None:165                self._typesdict[tp] = len(self.cffi_types)166                self.cffi_types.append(tp)        # placeholder167                if tp.is_array_type and tp.length is not None:168                    self.cffi_types.append('LEN') # placeholder169        assert None not in self._typesdict.values()170        #171        # collect all structs and unions and enums172        self._struct_unions = {}173        self._enums = {}174        for tp in all_decls:175            if isinstance(tp, model.StructOrUnion):176                self._struct_unions[tp] = None177            elif isinstance(tp, model.EnumType):178                self._enums[tp] = None179        for i, tp in enumerate(sorted(self._struct_unions,180                                      key=lambda tp: tp.name)):181            self._struct_unions[tp] = i182        for i, tp in enumerate(sorted(self._enums,183                                      key=lambda tp: tp.name)):184            self._enums[tp] = i185        #186        # emit all bytecode sequences now187        for tp in all_decls:188            method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)189            method(tp, self._typesdict[tp])190        #191        # consistency check192        for op in self.cffi_types:193            assert isinstance(op, CffiOp)194        self.cffi_types = tuple(self.cffi_types)    # don't change any more195 196    def _enum_fields(self, tp):197        # When producing C, expand all anonymous struct/union fields.198        # That's necessary to have C code checking the offsets of the199        # individual fields contained in them.  When producing Python,200        # don't do it and instead write it like it is, with the201        # corresponding fields having an empty name.  Empty names are202        # recognized at runtime when we import the generated Python203        # file.204        expand_anonymous_struct_union = not self.target_is_python205        return tp.enumfields(expand_anonymous_struct_union)206 207    def _do_collect_type(self, tp):208        if not isinstance(tp, model.BaseTypeByIdentity):209            if isinstance(tp, tuple):210                for x in tp:211                    self._do_collect_type(x)212            return213        if tp not in self._typesdict:214            self._typesdict[tp] = None215            if isinstance(tp, model.FunctionPtrType):216                self._do_collect_type(tp.as_raw_function())217            elif isinstance(tp, model.StructOrUnion):218                if tp.fldtypes is not None and (219                        tp not in self.ffi._parser._included_declarations):220                    for name1, tp1, _, _ in self._enum_fields(tp):221                        self._do_collect_type(self._field_type(tp, name1, tp1))222            else:223                for _, x in tp._get_items():224                    self._do_collect_type(x)225 226    def _generate(self, step_name):227        lst = self.ffi._parser._declarations.items()228        for name, (tp, quals) in sorted(lst):229            kind, realname = name.split(' ', 1)230            try:231                method = getattr(self, '_generate_cpy_%s_%s' % (kind,232                                                                step_name))233            except AttributeError:234                raise VerificationError(235                    "not implemented in recompile(): %r" % name)236            try:237                self._current_quals = quals238                method(tp, realname)239            except Exception as e:240                model.attach_exception_info(e, name)241                raise242 243    # ----------244 245    ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]246 247    def collect_step_tables(self):248        # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.249        self._lsts = {}250        for step_name in self.ALL_STEPS:251            self._lsts[step_name] = []252        self._seen_struct_unions = set()253        self._generate("ctx")254        self._add_missing_struct_unions()255        #256        for step_name in self.ALL_STEPS:257            lst = self._lsts[step_name]258            if step_name != "field":259                lst.sort(key=lambda entry: entry.name)260            self._lsts[step_name] = tuple(lst)    # don't change any more261        #262        # check for a possible internal inconsistency: _cffi_struct_unions263        # should have been generated with exactly self._struct_unions264        lst = self._lsts["struct_union"]265        for tp, i in self._struct_unions.items():266            assert i < len(lst)267            assert lst[i].name == tp.name268        assert len(lst) == len(self._struct_unions)269        # same with enums270        lst = self._lsts["enum"]271        for tp, i in self._enums.items():272            assert i < len(lst)273            assert lst[i].name == tp.name274        assert len(lst) == len(self._enums)275 276    # ----------277 278    def _prnt(self, what=''):279        self._f.write(what + '\n')280 281    def write_source_to_f(self, f, preamble):282        if self.target_is_python:283            assert preamble is None284            self.write_py_source_to_f(f)285        else:286            assert preamble is not None287            self.write_c_source_to_f(f, preamble)288 289    def _rel_readlines(self, filename):290        g = open(os.path.join(os.path.dirname(__file__), filename), 'r')291        lines = g.readlines()292        g.close()293        return lines294 295    def write_c_source_to_f(self, f, preamble):296        self._f = f297        prnt = self._prnt298        if self.ffi._embedding is not None:299            prnt('#define _CFFI_USE_EMBEDDING')300        if not USE_LIMITED_API:301            prnt('#define _CFFI_NO_LIMITED_API')302        #303        # first the '#include' (actually done by inlining the file's content)304        lines = self._rel_readlines('_cffi_include.h')305        i = lines.index('#include "parse_c_type.h"\n')306        lines[i:i+1] = self._rel_readlines('parse_c_type.h')307        prnt(''.join(lines))308        #309        # if we have ffi._embedding != None, we give it here as a macro310        # and include an extra file311        base_module_name = self.module_name.split('.')[-1]312        if self.ffi._embedding is not None:313            prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))314            prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')315            self._print_string_literal_in_array(self.ffi._embedding)316            prnt('0 };')317            prnt('#ifdef PYPY_VERSION')318            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (319                base_module_name,))320            prnt('#elif PY_MAJOR_VERSION >= 3')321            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (322                base_module_name,))323            prnt('#else')324            prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (325                base_module_name,))326            prnt('#endif')327            lines = self._rel_readlines('_embedding.h')328            i = lines.index('#include "_cffi_errors.h"\n')329            lines[i:i+1] = self._rel_readlines('_cffi_errors.h')330            prnt(''.join(lines))331            self.needs_version(VERSION_EMBEDDED)332        #333        # then paste the C source given by the user, verbatim.334        prnt('/************************************************************/')335        prnt()336        prnt(preamble)337        prnt()338        prnt('/************************************************************/')339        prnt()340        #341        # the declaration of '_cffi_types'342        prnt('static void *_cffi_types[] = {')343        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])344        for i, op in enumerate(self.cffi_types):345            comment = ''346            if i in typeindex2type:347                comment = ' // ' + typeindex2type[i]._get_c_name()348            prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))349        if not self.cffi_types:350            prnt('  0')351        prnt('};')352        prnt()353        #354        # call generate_cpy_xxx_decl(), for every xxx found from355        # ffi._parser._declarations.  This generates all the functions.356        self._seen_constants = set()357        self._generate("decl")358        #359        # the declaration of '_cffi_globals' and '_cffi_typenames'360        nums = {}361        for step_name in self.ALL_STEPS:362            lst = self._lsts[step_name]363            nums[step_name] = len(lst)364            if nums[step_name] > 0:365                prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (366                    step_name, step_name))367                for entry in lst:368                    prnt(entry.as_c_expr())369                prnt('};')370                prnt()371        #372        # the declaration of '_cffi_includes'373        if self.ffi._included_ffis:374            prnt('static const char * const _cffi_includes[] = {')375            for ffi_to_include in self.ffi._included_ffis:376                try:377                    included_module_name, included_source = (378                        ffi_to_include._assigned_source[:2])379                except AttributeError:380                    raise VerificationError(381                        "ffi object %r includes %r, but the latter has not "382                        "been prepared with set_source()" % (383                            self.ffi, ffi_to_include,))384                if included_source is None:385                    raise VerificationError(386                        "not implemented yet: ffi.include() of a Python-based "387                        "ffi inside a C-based ffi")388                prnt('  "%s",' % (included_module_name,))389            prnt('  NULL')390            prnt('};')391            prnt()392        #393        # the declaration of '_cffi_type_context'394        prnt('static const struct _cffi_type_context_s _cffi_type_context = {')395        prnt('  _cffi_types,')396        for step_name in self.ALL_STEPS:397            if nums[step_name] > 0:398                prnt('  _cffi_%ss,' % step_name)399            else:400                prnt('  NULL,  /* no %ss */' % step_name)401        for step_name in self.ALL_STEPS:402            if step_name != "field":403                prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))404        if self.ffi._included_ffis:405            prnt('  _cffi_includes,')406        else:407            prnt('  NULL,  /* no includes */')408        prnt('  %d,  /* num_types */' % (len(self.cffi_types),))409        flags = 0410        if self._num_externpy > 0 or self.ffi._embedding is not None:411            flags |= 1     # set to mean that we use extern "Python"412        prnt('  %d,  /* flags */' % flags)413        prnt('};')414        prnt()415        #416        # the init function417        prnt('#ifdef __GNUC__')418        prnt('#  pragma GCC visibility push(default)  /* for -fvisibility= */')419        prnt('#endif')420        prnt()421        prnt('#ifdef PYPY_VERSION')422        prnt('PyMODINIT_FUNC')423        prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))424        prnt('{')425        if flags & 1:426            prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')427            prnt('        _cffi_call_python_org = '428                 '(void(*)(struct _cffi_externpy_s *, char *))p[1];')429            prnt('    }')430        prnt('    p[0] = (const void *)0x%x;' % self._version)431        prnt('    p[1] = &_cffi_type_context;')432        prnt('#if PY_MAJOR_VERSION >= 3')433        prnt('    return NULL;')434        prnt('#endif')435        prnt('}')436        # on Windows, distutils insists on putting init_cffi_xyz in437        # 'export_symbols', so instead of fighting it, just give up and438        # give it one439        prnt('#  ifdef _MSC_VER')440        prnt('     PyMODINIT_FUNC')441        prnt('#  if PY_MAJOR_VERSION >= 3')442        prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))443        prnt('#  else')444        prnt('     init%s(void) { }' % (base_module_name,))445        prnt('#  endif')446        prnt('#  endif')447        prnt('#elif PY_MAJOR_VERSION >= 3')448        prnt('PyMODINIT_FUNC')449        prnt('PyInit_%s(void)' % (base_module_name,))450        prnt('{')451        prnt('  return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (452            self.module_name, self._version))453        prnt('}')454        prnt('#else')455        prnt('PyMODINIT_FUNC')456        prnt('init%s(void)' % (base_module_name,))457        prnt('{')458        prnt('  _cffi_init("%s", 0x%x, &_cffi_type_context);' % (459            self.module_name, self._version))460        prnt('}')461        prnt('#endif')462        prnt()463        prnt('#ifdef __GNUC__')464        prnt('#  pragma GCC visibility pop')465        prnt('#endif')466        self._version = None467 468    def _to_py(self, x):469        if isinstance(x, str):470            return "b'%s'" % (x,)471        if isinstance(x, (list, tuple)):472            rep = [self._to_py(item) for item in x]473            if len(rep) == 1:474                rep.append('')475            return "(%s)" % (','.join(rep),)476        return x.as_python_expr()  # Py2: unicode unexpected; Py3: bytes unexp.477 478    def write_py_source_to_f(self, f):479        self._f = f480        prnt = self._prnt481        #482        # header483        prnt("# auto-generated file")484        prnt("import _cffi_backend")485        #486        # the 'import' of the included ffis487        num_includes = len(self.ffi._included_ffis or ())488        for i in range(num_includes):489            ffi_to_include = self.ffi._included_ffis[i]490            try:491                included_module_name, included_source = (492                    ffi_to_include._assigned_source[:2])493            except AttributeError:494                raise VerificationError(495                    "ffi object %r includes %r, but the latter has not "496                    "been prepared with set_source()" % (497                        self.ffi, ffi_to_include,))498            if included_source is not None:499                raise VerificationError(500                    "not implemented yet: ffi.include() of a C-based "501                    "ffi inside a Python-based ffi")502            prnt('from %s import ffi as _ffi%d' % (included_module_name, i))503        prnt()504        prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))505        prnt("    _version = 0x%x," % (self._version,))506        self._version = None507        #508        # the '_types' keyword argument509        self.cffi_types = tuple(self.cffi_types)    # don't change any more510        types_lst = [op.as_python_bytes() for op in self.cffi_types]511        prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))512        typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])513        #514        # the keyword arguments from ALL_STEPS515        for step_name in self.ALL_STEPS:516            lst = self._lsts[step_name]517            if len(lst) > 0 and step_name != "field":518                prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))519        #520        # the '_includes' keyword argument521        if num_includes > 0:522            prnt('    _includes = (%s,),' % (523                ', '.join(['_ffi%d' % i for i in range(num_includes)]),))524        #525        # the footer526        prnt(')')527 528    # ----------529 530    def _gettypenum(self, type):531        # a KeyError here is a bug.  please report it! :-)532        return self._typesdict[type]533 534    def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):535        extraarg = ''536        if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():537            if tp.is_integer_type() and tp.name != '_Bool':538                converter = '_cffi_to_c_int'539                extraarg = ', %s' % tp.name540            elif isinstance(tp, model.UnknownFloatType):541                # don't check with is_float_type(): it may be a 'long542                # double' here, and _cffi_to_c_double would loose precision543                converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)544            else:545                cname = tp.get_c_name('')546                converter = '(%s)_cffi_to_c_%s' % (cname,547                                                   tp.name.replace(' ', '_'))548                if cname in ('char16_t', 'char32_t'):549                    self.needs_version(VERSION_CHAR16CHAR32)550            errvalue = '-1'551        #552        elif isinstance(tp, model.PointerType):553            self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,554                                                    tovar, errcode)555            return556        #557        elif (isinstance(tp, model.StructOrUnionOrEnum) or558              isinstance(tp, model.BasePrimitiveType)):559            # a struct (not a struct pointer) as a function argument;560            # or, a complex (the same code works)561            self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'562                      % (tovar, self._gettypenum(tp), fromvar))563            self._prnt('    %s;' % errcode)564            return565        #566        elif isinstance(tp, model.FunctionPtrType):567            converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')568            extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)569            errvalue = 'NULL'570        #571        else:572            raise NotImplementedError(tp)573        #574        self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))575        self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (576            tovar, tp.get_c_name(''), errvalue))577        self._prnt('    %s;' % errcode)578 579    def _extra_local_variables(self, tp, localvars, freelines):580        if isinstance(tp, model.PointerType):581            localvars.add('Py_ssize_t datasize')582            localvars.add('struct _cffi_freeme_s *large_args_free = NULL')583            freelines.add('if (large_args_free != NULL)'584                          ' _cffi_free_array_arguments(large_args_free);')585 586    def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):587        self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')588        self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (589            self._gettypenum(tp), fromvar, tovar))590        self._prnt('  if (datasize != 0) {')591        self._prnt('    %s = ((size_t)datasize) <= 640 ? '592                   '(%s)alloca((size_t)datasize) : NULL;' % (593            tovar, tp.get_c_name('')))594        self._prnt('    if (_cffi_convert_array_argument(_cffi_type(%d), %s, '595                   '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))596        self._prnt('            datasize, &large_args_free) < 0)')597        self._prnt('      %s;' % errcode)598        self._prnt('  }')599 600    def _convert_expr_from_c(self, tp, var, context):601        if isinstance(tp, model.BasePrimitiveType):602            if tp.is_integer_type() and tp.name != '_Bool':603                return '_cffi_from_c_int(%s, %s)' % (var, tp.name)604            elif isinstance(tp, model.UnknownFloatType):605                return '_cffi_from_c_double(%s)' % (var,)606            elif tp.name != 'long double' and not tp.is_complex_type():607                cname = tp.name.replace(' ', '_')608                if cname in ('char16_t', 'char32_t'):609                    self.needs_version(VERSION_CHAR16CHAR32)610                return '_cffi_from_c_%s(%s)' % (cname, var)611            else:612                return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (613                    var, self._gettypenum(tp))614        elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):615            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (616                var, self._gettypenum(tp))617        elif isinstance(tp, model.ArrayType):618            return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (619                var, self._gettypenum(model.PointerType(tp.item)))620        elif isinstance(tp, model.StructOrUnion):621            if tp.fldnames is None:622                raise TypeError("'%s' is used as %s, but is opaque" % (623                    tp._get_c_name(), context))624            return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (625                var, self._gettypenum(tp))626        elif isinstance(tp, model.EnumType):627            return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (628                var, self._gettypenum(tp))629        else:630            raise NotImplementedError(tp)631 632    # ----------633    # typedefs634 635    def _typedef_type(self, tp, name):636        return self._global_type(tp, "(*(%s *)0)" % (name,))637 638    def _generate_cpy_typedef_collecttype(self, tp, name):639        self._do_collect_type(self._typedef_type(tp, name))640 641    def _generate_cpy_typedef_decl(self, tp, name):642        pass643 644    def _typedef_ctx(self, tp, name):645        type_index = self._typesdict[tp]646        self._lsts["typename"].append(TypenameExpr(name, type_index))647 648    def _generate_cpy_typedef_ctx(self, tp, name):649        tp = self._typedef_type(tp, name)650        self._typedef_ctx(tp, name)651        if getattr(tp, "origin", None) == "unknown_type":652            self._struct_ctx(tp, tp.name, approxname=None)653        elif isinstance(tp, model.NamedPointerType):654            self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,655                             named_ptr=tp)656 657    # ----------658    # function declarations659 660    def _generate_cpy_function_collecttype(self, tp, name):661        self._do_collect_type(tp.as_raw_function())662        if tp.ellipsis and not self.target_is_python:663            self._do_collect_type(tp)664 665    def _generate_cpy_function_decl(self, tp, name):666        assert not self.target_is_python667        assert isinstance(tp, model.FunctionPtrType)668        if tp.ellipsis:669            # cannot support vararg functions better than this: check for its670            # exact type (including the fixed arguments), and build it as a671            # constant function pointer (no CPython wrapper)672            self._generate_cpy_constant_decl(tp, name)673            return674        prnt = self._prnt675        numargs = len(tp.args)676        if numargs == 0:677            argname = 'noarg'678        elif numargs == 1:679            argname = 'arg0'680        else:681            argname = 'args'682        #683        # ------------------------------684        # the 'd' version of the function, only for addressof(lib, 'func')685        arguments = []686        call_arguments = []687        context = 'argument of %s' % name688        for i, type in enumerate(tp.args):689            arguments.append(type.get_c_name(' x%d' % i, context))690            call_arguments.append('x%d' % i)691        repr_arguments = ', '.join(arguments)692        repr_arguments = repr_arguments or 'void'693        if tp.abi:694            abi = tp.abi + ' '695        else:696            abi = ''697        name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)698        prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))699        prnt('{')700        call_arguments = ', '.join(call_arguments)701        result_code = 'return '702        if isinstance(tp.result, model.VoidType):703            result_code = ''704        prnt('  %s%s(%s);' % (result_code, name, call_arguments))705        prnt('}')706        #707        prnt('#ifndef PYPY_VERSION')        # ------------------------------708        #709        prnt('static PyObject *')710        prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))711        prnt('{')712        #713        context = 'argument of %s' % name714        for i, type in enumerate(tp.args):715            arg = type.get_c_name(' x%d' % i, context)716            prnt('  %s;' % arg)717        #718        localvars = set()719        freelines = set()720        for type in tp.args:721            self._extra_local_variables(type, localvars, freelines)722        for decl in sorted(localvars):723            prnt('  %s;' % (decl,))724        #725        if not isinstance(tp.result, model.VoidType):726            result_code = 'result = '727            context = 'result of %s' % name728            result_decl = '  %s;' % tp.result.get_c_name(' result', context)729            prnt(result_decl)730            prnt('  PyObject *pyresult;')731        else:732            result_decl = None733            result_code = ''734        #735        if len(tp.args) > 1:736            rng = range(len(tp.args))737            for i in rng:738                prnt('  PyObject *arg%d;' % i)739            prnt()740            prnt('  if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (741                name, len(rng), len(rng),742                ', '.join(['&arg%d' % i for i in rng])))743            prnt('    return NULL;')744        prnt()745        #746        for i, type in enumerate(tp.args):747            self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,748                                       'return NULL')749            prnt()750        #751        prnt('  Py_BEGIN_ALLOW_THREADS')752        prnt('  _cffi_restore_errno();')753        call_arguments = ['x%d' % i for i in range(len(tp.args))]754        call_arguments = ', '.join(call_arguments)755        prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))756        prnt('  _cffi_save_errno();')757        prnt('  Py_END_ALLOW_THREADS')758        prnt()759        #760        prnt('  (void)self; /* unused */')761        if numargs == 0:762            prnt('  (void)noarg; /* unused */')763        if result_code:764            prnt('  pyresult = %s;' %765                 self._convert_expr_from_c(tp.result, 'result', 'result type'))766            for freeline in freelines:767                prnt('  ' + freeline)768            prnt('  return pyresult;')769        else:770            for freeline in freelines:771                prnt('  ' + freeline)772            prnt('  Py_INCREF(Py_None);')773            prnt('  return Py_None;')774        prnt('}')775        #776        prnt('#else')        # ------------------------------777        #778        # the PyPy version: need to replace struct/union arguments with779        # pointers, and if the result is a struct/union, insert a first780        # arg that is a pointer to the result.  We also do that for781        # complex args and return type.782        def need_indirection(type):783            return (isinstance(type, model.StructOrUnion) or784                    (isinstance(type, model.PrimitiveType) and785                     type.is_complex_type()))786        difference = False787        arguments = []788        call_arguments = []789        context = 'argument of %s' % name790        for i, type in enumerate(tp.args):791            indirection = ''792            if need_indirection(type):793                indirection = '*'794                difference = True795            arg = type.get_c_name(' %sx%d' % (indirection, i), context)796            arguments.append(arg)797            call_arguments.append('%sx%d' % (indirection, i))798        tp_result = tp.result799        if need_indirection(tp_result):800            context = 'result of %s' % name801            arg = tp_result.get_c_name(' *result', context)802            arguments.insert(0, arg)803            tp_result = model.void_type804            result_decl = None805            result_code = '*result = '806            difference = True807        if difference:808            repr_arguments = ', '.join(arguments)809            repr_arguments = repr_arguments or 'void'810            name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,811                                                       repr_arguments)812            prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))813            prnt('{')814            if result_decl:815                prnt(result_decl)816            call_arguments = ', '.join(call_arguments)817            prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))818            if result_decl:819                prnt('  return result;')820            prnt('}')821        else:822            prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))823        #824        prnt('#endif')        # ------------------------------825        prnt()826 827    def _generate_cpy_function_ctx(self, tp, name):828        if tp.ellipsis and not self.target_is_python:829            self._generate_cpy_constant_ctx(tp, name)830            return831        type_index = self._typesdict[tp.as_raw_function()]832        numargs = len(tp.args)833        if self.target_is_python:834            meth_kind = OP_DLOPEN_FUNC835        elif numargs == 0:836            meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'837        elif numargs == 1:838            meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'839        else:840            meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'841        self._lsts["global"].append(842            GlobalExpr(name, '_cffi_f_%s' % name,843                       CffiOp(meth_kind, type_index),844                       size='_cffi_d_%s' % name))845 846    # ----------847    # named structs or unions848 849    def _field_type(self, tp_struct, field_name, tp_field):850        if isinstance(tp_field, model.ArrayType):851            actual_length = tp_field.length852            if actual_length == '...':853                ptr_struct_name = tp_struct.get_c_name('*')854                actual_length = '_cffi_array_len(((%s)0)->%s)' % (855                    ptr_struct_name, field_name)856            tp_item = self._field_type(tp_struct, '%s[0]' % field_name,857                                       tp_field.item)858            tp_field = model.ArrayType(tp_item, actual_length)859        return tp_field860 861    def _struct_collecttype(self, tp):862        self._do_collect_type(tp)863        if self.target_is_python:864            # also requires nested anon struct/unions in ABI mode, recursively865            for fldtype in tp.anonymous_struct_fields():866                self._struct_collecttype(fldtype)867 868    def _struct_decl(self, tp, cname, approxname):869        if tp.fldtypes is None:870            return871        prnt = self._prnt872        checkfuncname = '_cffi_checkfld_%s' % (approxname,)873        prnt('_CFFI_UNUSED_FN')874        prnt('static void %s(%s *p)' % (checkfuncname, cname))875        prnt('{')876        prnt('  /* only to generate compile-time warnings or errors */')877        prnt('  (void)p;')878        for fname, ftype, fbitsize, fqual in self._enum_fields(tp):879            try:880                if ftype.is_integer_type() or fbitsize >= 0:881                    # accept all integers, but complain on float or double882                    if fname != '':883                        prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "884                             "an integer */" % (fname, cname, fname))885                    continue886                # only accept exactly the type declared, except that '[]'887                # is interpreted as a '*' and so will match any array length.888                # (It would also match '*', but that's harder to detect...)889                while (isinstance(ftype, model.ArrayType)890                       and (ftype.length is None or ftype.length == '...')):891                    ftype = ftype.item892                    fname = fname + '[0]'893                prnt('  { %s = &p->%s; (void)tmp; }' % (894                    ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),895                    fname))896            except VerificationError as e:897                prnt('  /* %s */' % str(e))   # cannot verify it, ignore898        prnt('}')899        prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))900        prnt()901 902    def _struct_ctx(self, tp, cname, approxname, named_ptr=None):903        type_index = self._typesdict[tp]904        reason_for_not_expanding = None905        flags = []906        if isinstance(tp, model.UnionType):907            flags.append("_CFFI_F_UNION")908        if tp.fldtypes is None:909            flags.append("_CFFI_F_OPAQUE")910            reason_for_not_expanding = "opaque"911        if (tp not in self.ffi._parser._included_declarations and912                (named_ptr is None or913                 named_ptr not in self.ffi._parser._included_declarations)):914            if tp.fldtypes is None:915                pass    # opaque916            elif tp.partial or any(tp.anonymous_struct_fields()):917                pass    # field layout obtained silently from the C compiler918            else:919                flags.append("_CFFI_F_CHECK_FIELDS")920            if tp.packed:921                if tp.packed > 1:922                    raise NotImplementedError(923                        "%r is declared with 'pack=%r'; only 0 or 1 are "924                        "supported in API mode (try to use \"...;\", which "925                        "does not require a 'pack' declaration)" %926                        (tp, tp.packed))927                flags.append("_CFFI_F_PACKED")928        else:929            flags.append("_CFFI_F_EXTERNAL")930            reason_for_not_expanding = "external"931        flags = '|'.join(flags) or '0'932        c_fields = []933        if reason_for_not_expanding is None:934            enumfields = list(self._enum_fields(tp))935            for fldname, fldtype, fbitsize, fqual in enumfields:936                fldtype = self._field_type(tp, fldname, fldtype)937                self._check_not_opaque(fldtype,938                                       "field '%s.%s'" % (tp.name, fldname))939                # cname is None for _add_missing_struct_unions() only940                op = OP_NOOP941                if fbitsize >= 0:942                    op = OP_BITFIELD943                    size = '%d /* bits */' % fbitsize944                elif cname is None or (945                        isinstance(fldtype, model.ArrayType) and946                        fldtype.length is None):947                    size = '(size_t)-1'948                else:949                    size = 'sizeof(((%s)0)->%s)' % (950                        tp.get_c_name('*') if named_ptr is None951                                           else named_ptr.name,952                        fldname)953                if cname is None or fbitsize >= 0:954                    offset = '(size_t)-1'955                elif named_ptr is not None:956                    offset = '(size_t)(((char *)&((%s)4096)->%s) - (char *)4096)' % (957                        named_ptr.name, fldname)958                else:959                    offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)960                c_fields.append(961                    FieldExpr(fldname, offset, size, fbitsize,962                              CffiOp(op, self._typesdict[fldtype])))963            first_field_index = len(self._lsts["field"])964            self._lsts["field"].extend(c_fields)965            #966            if cname is None:  # unknown name, for _add_missing_struct_unions967                size = '(size_t)-2'968                align = -2969                comment = "unnamed"970            else:971                if named_ptr is not None:972                    size = 'sizeof(*(%s)0)' % (named_ptr.name,)973                    align = '-1 /* unknown alignment */'974                else:975                    size = 'sizeof(%s)' % (cname,)976                    align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)977                comment = None978        else:979            size = '(size_t)-1'980            align = -1981            first_field_index = -1982            comment = reason_for_not_expanding983        self._lsts["struct_union"].append(984            StructUnionExpr(tp.name, type_index, flags, size, align, comment,985                            first_field_index, c_fields))986        self._seen_struct_unions.add(tp)987 988    def _check_not_opaque(self, tp, location):989        while isinstance(tp, model.ArrayType):990            tp = tp.item991        if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:992            raise TypeError(993                "%s is of an opaque type (not declared in cdef())" % location)994 995    def _add_missing_struct_unions(self):996        # not very nice, but some struct declarations might be missing997        # because they don't have any known C name.  Check that they are998        # not partial (we can't complete or verify them!) and emit them999        # anonymously.1000        lst = list(self._struct_unions.items())1001        lst.sort(key=lambda tp_order: tp_order[1])1002        for tp, order in lst:1003            if tp not in self._seen_struct_unions:1004                if tp.partial:1005                    raise NotImplementedError("internal inconsistency: %r is "1006                                              "partial but was not seen at "1007                                              "this point" % (tp,))1008                if tp.name.startswith('$') and tp.name[1:].isdigit():1009                    approxname = tp.name[1:]1010                elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':1011                    approxname = 'FILE'1012                    self._typedef_ctx(tp, 'FILE')1013                else:1014                    raise NotImplementedError("internal inconsistency: %r" %1015                                              (tp,))1016                self._struct_ctx(tp, None, approxname)1017 1018    def _generate_cpy_struct_collecttype(self, tp, name):1019        self._struct_collecttype(tp)1020    _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype1021 1022    def _struct_names(self, tp):1023        cname = tp.get_c_name('')1024        if ' ' in cname:1025            return cname, cname.replace(' ', '_')1026        else:1027            return cname, '_' + cname1028 1029    def _generate_cpy_struct_decl(self, tp, name):1030        self._struct_decl(tp, *self._struct_names(tp))1031    _generate_cpy_union_decl = _generate_cpy_struct_decl1032 1033    def _generate_cpy_struct_ctx(self, tp, name):1034        self._struct_ctx(tp, *self._struct_names(tp))1035    _generate_cpy_union_ctx = _generate_cpy_struct_ctx1036 1037    # ----------1038    # 'anonymous' declarations.  These are produced for anonymous structs1039    # or unions; the 'name' is obtained by a typedef.1040 1041    def _generate_cpy_anonymous_collecttype(self, tp, name):1042        if isinstance(tp, model.EnumType):1043            self._generate_cpy_enum_collecttype(tp, name)1044        else:1045            self._struct_collecttype(tp)1046 1047    def _generate_cpy_anonymous_decl(self, tp, name):1048        if isinstance(tp, model.EnumType):1049            self._generate_cpy_enum_decl(tp)1050        else:1051            self._struct_decl(tp, name, 'typedef_' + name)1052 1053    def _generate_cpy_anonymous_ctx(self, tp, name):1054        if isinstance(tp, model.EnumType):1055            self._enum_ctx(tp, name)1056        else:1057            self._struct_ctx(tp, name, 'typedef_' + name)1058 1059    # ----------1060    # constants, declared with "static const ..."1061 1062    def _generate_cpy_const(self, is_int, name, tp=None, category='const',1063                            check_value=None):1064        if (category, name) in self._seen_constants:1065            raise VerificationError(1066                "duplicate declaration of %s '%s'" % (category, name))1067        self._seen_constants.add((category, name))1068        #1069        prnt = self._prnt1070        funcname = '_cffi_%s_%s' % (category, name)1071        if is_int:1072            prnt('static int %s(unsigned long long *o)' % funcname)1073            prnt('{')1074            prnt('  int n = (%s) <= 0;' % (name,))1075            prnt('  *o = (unsigned long long)((%s) | 0);'1076                 '  /* check that %s is an integer */' % (name, name))1077            if check_value is not None:1078                if check_value > 0:1079                    check_value = '%dU' % (check_value,)1080                prnt('  if (!_cffi_check_int(*o, n, %s))' % (check_value,))1081                prnt('    n |= 2;')1082            prnt('  return n;')1083            prnt('}')1084        else:1085            assert check_value is None1086            prnt('static void %s(char *o)' % funcname)1087            prnt('{')1088            prnt('  *(%s)o = %s;' % (tp.get_c_name('*'), name))1089            prnt('}')1090        prnt()1091 1092    def _generate_cpy_constant_collecttype(self, tp, name):1093        is_int = tp.is_integer_type()1094        if not is_int or self.target_is_python:1095            self._do_collect_type(tp)1096 1097    def _generate_cpy_constant_decl(self, tp, name):1098        is_int = tp.is_integer_type()1099        self._generate_cpy_const(is_int, name, tp)1100 1101    def _generate_cpy_constant_ctx(self, tp, name):1102        if not self.target_is_python and tp.is_integer_type():1103            type_op = CffiOp(OP_CONSTANT_INT, -1)1104        else:1105            if self.target_is_python:1106                const_kind = OP_DLOPEN_CONST1107            else:1108                const_kind = OP_CONSTANT1109            type_index = self._typesdict[tp]1110            type_op = CffiOp(const_kind, type_index)1111        self._lsts["global"].append(1112            GlobalExpr(name, '_cffi_const_%s' % name, type_op))1113 1114    # ----------1115    # enums1116 1117    def _generate_cpy_enum_collecttype(self, tp, name):1118        self._do_collect_type(tp)1119 1120    def _generate_cpy_enum_decl(self, tp, name=None):1121        for enumerator in tp.enumerators:1122            self._generate_cpy_const(True, enumerator)1123 1124    def _enum_ctx(self, tp, cname):1125        type_index = self._typesdict[tp]1126        type_op = CffiOp(OP_ENUM, -1)1127        if self.target_is_python:1128            tp.check_not_partial()1129        for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):1130            self._lsts["global"].append(1131                GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,1132                           check_value=enumvalue))1133        #1134        if cname is not None and '$' not in cname and not self.target_is_python:1135            size = "sizeof(%s)" % cname1136            signed = "((%s)-1) <= 0" % cname1137        else:1138            basetp = tp.build_baseinttype(self.ffi, [])1139            size = self.ffi.sizeof(basetp)1140            signed = int(int(self.ffi.cast(basetp, -1)) < 0)1141        allenums = ",".join(tp.enumerators)1142        self._lsts["enum"].append(1143            EnumExpr(tp.name, type_index, size, signed, allenums))1144 1145    def _generate_cpy_enum_ctx(self, tp, name):1146        self._enum_ctx(tp, tp._get_c_name())1147 1148    # ----------1149    # macros: for now only for integers1150 1151    def _generate_cpy_macro_collecttype(self, tp, name):1152        pass1153 1154    def _generate_cpy_macro_decl(self, tp, name):1155        if tp == '...':1156            check_value = None1157        else:1158            check_value = tp     # an integer1159        self._generate_cpy_const(True, name, check_value=check_value)1160 1161    def _generate_cpy_macro_ctx(self, tp, name):1162        if tp == '...':1163            if self.target_is_python:1164                raise VerificationError(1165                    "cannot use the syntax '...' in '#define %s ...' when "1166                    "using the ABI mode" % (name,))1167            check_value = None1168        else:1169            check_value = tp     # an integer1170        type_op = CffiOp(OP_CONSTANT_INT, -1)1171        self._lsts["global"].append(1172            GlobalExpr(name, '_cffi_const_%s' % name, type_op,1173                       check_value=check_value))1174 1175    # ----------1176    # global variables1177 1178    def _global_type(self, tp, global_name):1179        if isinstance(tp, model.ArrayType):1180            actual_length = tp.length1181            if actual_length == '...':1182                actual_length = '_cffi_array_len(%s)' % (global_name,)1183            tp_item = self._global_type(tp.item, '%s[0]' % global_name)1184            tp = model.ArrayType(tp_item, actual_length)1185        return tp1186 1187    def _generate_cpy_variable_collecttype(self, tp, name):1188        self._do_collect_type(self._global_type(tp, name))1189 1190    def _generate_cpy_variable_decl(self, tp, name):1191        prnt = self._prnt1192        tp = self._global_type(tp, name)1193        if isinstance(tp, model.ArrayType) and tp.length is None:1194            tp = tp.item1195            ampersand = ''1196        else:1197            ampersand = '&'1198        # This code assumes that casts from "tp *" to "void *" is a1199        # no-op, i.e. a function that returns a "tp *" can be called1200        # as if it returned a "void *".  This should be generally true

Showing the first 1,200 of 1599 lines. Download the file for the rest.

codekingpro/portable-devtools · Team Ai