Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
model.py619 linesDownload Raw Back to cffi
1import types2import weakref3 4from .lock import allocate_lock5from .error import CDefError, VerificationError, VerificationMissing6 7# type qualifiers8Q_CONST    = 0x019Q_RESTRICT = 0x0210Q_VOLATILE = 0x0411 12def qualify(quals, replace_with):13    if quals & Q_CONST:14        replace_with = ' const ' + replace_with.lstrip()15    if quals & Q_VOLATILE:16        replace_with = ' volatile ' + replace_with.lstrip()17    if quals & Q_RESTRICT:18        # It seems that __restrict is supported by gcc and msvc.19        # If you hit some different compiler, add a #define in20        # _cffi_include.h for it (and in its copies, documented there)21        replace_with = ' __restrict ' + replace_with.lstrip()22    return replace_with23 24 25class BaseTypeByIdentity(object):26    is_array_type = False27    is_raw_function = False28 29    def get_c_name(self, replace_with='', context='a C file', quals=0):30        result = self.c_name_with_marker31        assert result.count('&') == 132        # some logic duplication with ffi.getctype()... :-(33        replace_with = replace_with.strip()34        if replace_with:35            if replace_with.startswith('*') and '&[' in result:36                replace_with = '(%s)' % replace_with37            elif not replace_with[0] in '[(':38                replace_with = ' ' + replace_with39        replace_with = qualify(quals, replace_with)40        result = result.replace('&', replace_with)41        if '$' in result:42            raise VerificationError(43                "cannot generate '%s' in %s: unknown type name"44                % (self._get_c_name(), context))45        return result46 47    def _get_c_name(self):48        return self.c_name_with_marker.replace('&', '')49 50    def has_c_name(self):51        return '$' not in self._get_c_name()52 53    def is_integer_type(self):54        return False55 56    def get_cached_btype(self, ffi, finishlist, can_delay=False):57        try:58            BType = ffi._cached_btypes[self]59        except KeyError:60            BType = self.build_backend_type(ffi, finishlist)61            BType2 = ffi._cached_btypes.setdefault(self, BType)62            assert BType2 is BType63        return BType64 65    def __repr__(self):66        return '<%s>' % (self._get_c_name(),)67 68    def _get_items(self):69        return [(name, getattr(self, name)) for name in self._attrs_]70 71 72class BaseType(BaseTypeByIdentity):73 74    def __eq__(self, other):75        return (self.__class__ == other.__class__ and76                self._get_items() == other._get_items())77 78    def __ne__(self, other):79        return not self == other80 81    def __hash__(self):82        return hash((self.__class__, tuple(self._get_items())))83 84 85class VoidType(BaseType):86    _attrs_ = ()87 88    def __init__(self):89        self.c_name_with_marker = 'void&'90 91    def build_backend_type(self, ffi, finishlist):92        return global_cache(self, ffi, 'new_void_type')93 94void_type = VoidType()95 96 97class BasePrimitiveType(BaseType):98    def is_complex_type(self):99        return False100 101 102class PrimitiveType(BasePrimitiveType):103    _attrs_ = ('name',)104 105    ALL_PRIMITIVE_TYPES = {106        'char':               'c',107        'short':              'i',108        'int':                'i',109        'long':               'i',110        'long long':          'i',111        'signed char':        'i',112        'unsigned char':      'i',113        'unsigned short':     'i',114        'unsigned int':       'i',115        'unsigned long':      'i',116        'unsigned long long': 'i',117        'float':              'f',118        'double':             'f',119        'long double':        'f',120        '_cffi_float_complex_t': 'j',121        '_cffi_double_complex_t': 'j',122        '_Bool':              'i',123        # the following types are not primitive in the C sense124        'wchar_t':            'c',125        'char16_t':           'c',126        'char32_t':           'c',127        'int8_t':             'i',128        'uint8_t':            'i',129        'int16_t':            'i',130        'uint16_t':           'i',131        'int32_t':            'i',132        'uint32_t':           'i',133        'int64_t':            'i',134        'uint64_t':           'i',135        'int_least8_t':       'i',136        'uint_least8_t':      'i',137        'int_least16_t':      'i',138        'uint_least16_t':     'i',139        'int_least32_t':      'i',140        'uint_least32_t':     'i',141        'int_least64_t':      'i',142        'uint_least64_t':     'i',143        'int_fast8_t':        'i',144        'uint_fast8_t':       'i',145        'int_fast16_t':       'i',146        'uint_fast16_t':      'i',147        'int_fast32_t':       'i',148        'uint_fast32_t':      'i',149        'int_fast64_t':       'i',150        'uint_fast64_t':      'i',151        'intptr_t':           'i',152        'uintptr_t':          'i',153        'intmax_t':           'i',154        'uintmax_t':          'i',155        'ptrdiff_t':          'i',156        'size_t':             'i',157        'ssize_t':            'i',158        }159 160    def __init__(self, name):161        assert name in self.ALL_PRIMITIVE_TYPES162        self.name = name163        self.c_name_with_marker = name + '&'164 165    def is_char_type(self):166        return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'167    def is_integer_type(self):168        return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'169    def is_float_type(self):170        return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'171    def is_complex_type(self):172        return self.ALL_PRIMITIVE_TYPES[self.name] == 'j'173 174    def build_backend_type(self, ffi, finishlist):175        return global_cache(self, ffi, 'new_primitive_type', self.name)176 177 178class UnknownIntegerType(BasePrimitiveType):179    _attrs_ = ('name',)180 181    def __init__(self, name):182        self.name = name183        self.c_name_with_marker = name + '&'184 185    def is_integer_type(self):186        return True187 188    def build_backend_type(self, ffi, finishlist):189        raise NotImplementedError("integer type '%s' can only be used after "190                                  "compilation" % self.name)191 192class UnknownFloatType(BasePrimitiveType):193    _attrs_ = ('name', )194 195    def __init__(self, name):196        self.name = name197        self.c_name_with_marker = name + '&'198 199    def build_backend_type(self, ffi, finishlist):200        raise NotImplementedError("float type '%s' can only be used after "201                                  "compilation" % self.name)202 203 204class BaseFunctionType(BaseType):205    _attrs_ = ('args', 'result', 'ellipsis', 'abi')206 207    def __init__(self, args, result, ellipsis, abi=None):208        self.args = args209        self.result = result210        self.ellipsis = ellipsis211        self.abi = abi212        #213        reprargs = [arg._get_c_name() for arg in self.args]214        if self.ellipsis:215            reprargs.append('...')216        reprargs = reprargs or ['void']217        replace_with = self._base_pattern % (', '.join(reprargs),)218        if abi is not None:219            replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]220        self.c_name_with_marker = (221            self.result.c_name_with_marker.replace('&', replace_with))222 223 224class RawFunctionType(BaseFunctionType):225    # Corresponds to a C type like 'int(int)', which is the C type of226    # a function, but not a pointer-to-function.  The backend has no227    # notion of such a type; it's used temporarily by parsing.228    _base_pattern = '(&)(%s)'229    is_raw_function = True230 231    def build_backend_type(self, ffi, finishlist):232        raise CDefError("cannot render the type %r: it is a function "233                        "type, not a pointer-to-function type" % (self,))234 235    def as_function_pointer(self):236        return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)237 238 239class FunctionPtrType(BaseFunctionType):240    _base_pattern = '(*&)(%s)'241 242    def build_backend_type(self, ffi, finishlist):243        result = self.result.get_cached_btype(ffi, finishlist)244        args = []245        for tp in self.args:246            args.append(tp.get_cached_btype(ffi, finishlist))247        abi_args = ()248        if self.abi == "__stdcall":249            if not self.ellipsis:    # __stdcall ignored for variadic funcs250                try:251                    abi_args = (ffi._backend.FFI_STDCALL,)252                except AttributeError:253                    pass254        return global_cache(self, ffi, 'new_function_type',255                            tuple(args), result, self.ellipsis, *abi_args)256 257    def as_raw_function(self):258        return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)259 260 261class PointerType(BaseType):262    _attrs_ = ('totype', 'quals')263 264    def __init__(self, totype, quals=0):265        self.totype = totype266        self.quals = quals267        extra = " *&"268        if totype.is_array_type:269            extra = "(%s)" % (extra.lstrip(),)270        extra = qualify(quals, extra)271        self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)272 273    def build_backend_type(self, ffi, finishlist):274        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)275        return global_cache(self, ffi, 'new_pointer_type', BItem)276 277voidp_type = PointerType(void_type)278 279def ConstPointerType(totype):280    return PointerType(totype, Q_CONST)281 282const_voidp_type = ConstPointerType(void_type)283 284 285class NamedPointerType(PointerType):286    _attrs_ = ('totype', 'name')287 288    def __init__(self, totype, name, quals=0):289        PointerType.__init__(self, totype, quals)290        self.name = name291        self.c_name_with_marker = name + '&'292 293 294class ArrayType(BaseType):295    _attrs_ = ('item', 'length')296    is_array_type = True297 298    def __init__(self, item, length):299        self.item = item300        self.length = length301        #302        if length is None:303            brackets = '&[]'304        elif length == '...':305            brackets = '&[/*...*/]'306        else:307            brackets = '&[%s]' % length308        self.c_name_with_marker = (309            self.item.c_name_with_marker.replace('&', brackets))310 311    def length_is_unknown(self):312        return isinstance(self.length, str)313 314    def resolve_length(self, newlength):315        return ArrayType(self.item, newlength)316 317    def build_backend_type(self, ffi, finishlist):318        if self.length_is_unknown():319            raise CDefError("cannot render the type %r: unknown length" %320                            (self,))321        self.item.get_cached_btype(ffi, finishlist)   # force the item BType322        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)323        return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)324 325char_array_type = ArrayType(PrimitiveType('char'), None)326 327 328class StructOrUnionOrEnum(BaseTypeByIdentity):329    _attrs_ = ('name',)330    forcename = None331 332    def build_c_name_with_marker(self):333        name = self.forcename or '%s %s' % (self.kind, self.name)334        self.c_name_with_marker = name + '&'335 336    def force_the_name(self, forcename):337        self.forcename = forcename338        self.build_c_name_with_marker()339 340    def get_official_name(self):341        assert self.c_name_with_marker.endswith('&')342        return self.c_name_with_marker[:-1]343 344 345class StructOrUnion(StructOrUnionOrEnum):346    fixedlayout = None347    completed = 0348    partial = False349    packed = 0350 351    def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):352        self.name = name353        self.fldnames = fldnames354        self.fldtypes = fldtypes355        self.fldbitsize = fldbitsize356        self.fldquals = fldquals357        self.build_c_name_with_marker()358 359    def anonymous_struct_fields(self):360        if self.fldtypes is not None:361            for name, type in zip(self.fldnames, self.fldtypes):362                if name == '' and isinstance(type, StructOrUnion):363                    yield type364 365    def enumfields(self, expand_anonymous_struct_union=True):366        fldquals = self.fldquals367        if fldquals is None:368            fldquals = (0,) * len(self.fldnames)369        for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,370                                              self.fldbitsize, fldquals):371            if (name == '' and isinstance(type, StructOrUnion)372                    and expand_anonymous_struct_union):373                # nested anonymous struct/union374                for result in type.enumfields():375                    yield result376            else:377                yield (name, type, bitsize, quals)378 379    def force_flatten(self):380        # force the struct or union to have a declaration that lists381        # directly all fields returned by enumfields(), flattening382        # nested anonymous structs/unions.383        names = []384        types = []385        bitsizes = []386        fldquals = []387        for name, type, bitsize, quals in self.enumfields():388            names.append(name)389            types.append(type)390            bitsizes.append(bitsize)391            fldquals.append(quals)392        self.fldnames = tuple(names)393        self.fldtypes = tuple(types)394        self.fldbitsize = tuple(bitsizes)395        self.fldquals = tuple(fldquals)396 397    def get_cached_btype(self, ffi, finishlist, can_delay=False):398        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,399                                                     can_delay)400        if not can_delay:401            self.finish_backend_type(ffi, finishlist)402        return BType403 404    def finish_backend_type(self, ffi, finishlist):405        if self.completed:406            if self.completed != 2:407                raise NotImplementedError("recursive structure declaration "408                                          "for '%s'" % (self.name,))409            return410        BType = ffi._cached_btypes[self]411        #412        self.completed = 1413        #414        if self.fldtypes is None:415            pass    # not completing it: it's an opaque struct416            #417        elif self.fixedlayout is None:418            fldtypes = [tp.get_cached_btype(ffi, finishlist)419                        for tp in self.fldtypes]420            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))421            extra_flags = ()422            if self.packed:423                if self.packed == 1:424                    extra_flags = (8,)    # SF_PACKED425                else:426                    extra_flags = (0, self.packed)427            ffi._backend.complete_struct_or_union(BType, lst, self,428                                                  -1, -1, *extra_flags)429            #430        else:431            fldtypes = []432            fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout433            for i in range(len(self.fldnames)):434                fsize = fieldsize[i]435                ftype = self.fldtypes[i]436                #437                if isinstance(ftype, ArrayType) and ftype.length_is_unknown():438                    # fix the length to match the total size439                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)440                    nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))441                    if nrest != 0:442                        self._verification_error(443                            "field '%s.%s' has a bogus size?" % (444                            self.name, self.fldnames[i] or '{}'))445                    ftype = ftype.resolve_length(nlen)446                    self.fldtypes = (self.fldtypes[:i] + (ftype,) +447                                     self.fldtypes[i+1:])448                #449                BFieldType = ftype.get_cached_btype(ffi, finishlist)450                if isinstance(ftype, ArrayType) and ftype.length is None:451                    assert fsize == 0452                else:453                    bitemsize = ffi.sizeof(BFieldType)454                    if bitemsize != fsize:455                        self._verification_error(456                            "field '%s.%s' is declared as %d bytes, but is "457                            "really %d bytes" % (self.name,458                                                 self.fldnames[i] or '{}',459                                                 bitemsize, fsize))460                fldtypes.append(BFieldType)461            #462            lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))463            ffi._backend.complete_struct_or_union(BType, lst, self,464                                                  totalsize, totalalignment)465        self.completed = 2466 467    def _verification_error(self, msg):468        raise VerificationError(msg)469 470    def check_not_partial(self):471        if self.partial and self.fixedlayout is None:472            raise VerificationMissing(self._get_c_name())473 474    def build_backend_type(self, ffi, finishlist):475        self.check_not_partial()476        finishlist.append(self)477        #478        return global_cache(self, ffi, 'new_%s_type' % self.kind,479                            self.get_official_name(), key=self)480 481 482class StructType(StructOrUnion):483    kind = 'struct'484 485 486class UnionType(StructOrUnion):487    kind = 'union'488 489 490class EnumType(StructOrUnionOrEnum):491    kind = 'enum'492    partial = False493    partial_resolved = False494 495    def __init__(self, name, enumerators, enumvalues, baseinttype=None):496        self.name = name497        self.enumerators = enumerators498        self.enumvalues = enumvalues499        self.baseinttype = baseinttype500        self.build_c_name_with_marker()501 502    def force_the_name(self, forcename):503        StructOrUnionOrEnum.force_the_name(self, forcename)504        if self.forcename is None:505            name = self.get_official_name()506            self.forcename = '$' + name.replace(' ', '_')507 508    def check_not_partial(self):509        if self.partial and not self.partial_resolved:510            raise VerificationMissing(self._get_c_name())511 512    def build_backend_type(self, ffi, finishlist):513        self.check_not_partial()514        base_btype = self.build_baseinttype(ffi, finishlist)515        return global_cache(self, ffi, 'new_enum_type',516                            self.get_official_name(),517                            self.enumerators, self.enumvalues,518                            base_btype, key=self)519 520    def build_baseinttype(self, ffi, finishlist):521        if self.baseinttype is not None:522            return self.baseinttype.get_cached_btype(ffi, finishlist)523        #524        if self.enumvalues:525            smallest_value = min(self.enumvalues)526            largest_value = max(self.enumvalues)527        else:528            import warnings529            try:530                # XXX!  The goal is to ensure that the warnings.warn()531                # will not suppress the warning.  We want to get it532                # several times if we reach this point several times.533                __warningregistry__.clear()534            except NameError:535                pass536            warnings.warn("%r has no values explicitly defined; "537                          "guessing that it is equivalent to 'unsigned int'"538                          % self._get_c_name())539            smallest_value = largest_value = 0540        if smallest_value < 0:   # needs a signed type541            sign = 1542            candidate1 = PrimitiveType("int")543            candidate2 = PrimitiveType("long")544        else:545            sign = 0546            candidate1 = PrimitiveType("unsigned int")547            candidate2 = PrimitiveType("unsigned long")548        btype1 = candidate1.get_cached_btype(ffi, finishlist)549        btype2 = candidate2.get_cached_btype(ffi, finishlist)550        size1 = ffi.sizeof(btype1)551        size2 = ffi.sizeof(btype2)552        if (smallest_value >= ((-1) << (8*size1-1)) and553            largest_value < (1 << (8*size1-sign))):554            return btype1555        if (smallest_value >= ((-1) << (8*size2-1)) and556            largest_value < (1 << (8*size2-sign))):557            return btype2558        raise CDefError("%s values don't all fit into either 'long' "559                        "or 'unsigned long'" % self._get_c_name())560 561def unknown_type(name, structname=None):562    if structname is None:563        structname = '$%s' % name564    tp = StructType(structname, None, None, None)565    tp.force_the_name(name)566    tp.origin = "unknown_type"567    return tp568 569def unknown_ptr_type(name, structname=None):570    if structname is None:571        structname = '$$%s' % name572    tp = StructType(structname, None, None, None)573    return NamedPointerType(tp, name)574 575 576global_lock = allocate_lock()577_typecache_cffi_backend = weakref.WeakValueDictionary()578 579def get_typecache(backend):580    # returns _typecache_cffi_backend if backend is the _cffi_backend581    # module, or type(backend).__typecache if backend is an instance of582    # CTypesBackend (or some FakeBackend class during tests)583    if isinstance(backend, types.ModuleType):584        return _typecache_cffi_backend585    with global_lock:586        if not hasattr(type(backend), '__typecache'):587            type(backend).__typecache = weakref.WeakValueDictionary()588        return type(backend).__typecache589 590def global_cache(srctype, ffi, funcname, *args, **kwds):591    key = kwds.pop('key', (funcname, args))592    assert not kwds593    try:594        return ffi._typecache[key]595    except KeyError:596        pass597    try:598        res = getattr(ffi._backend, funcname)(*args)599    except NotImplementedError as e:600        raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))601    # note that setdefault() on WeakValueDictionary is not atomic602    # and contains a rare bug (http://bugs.python.org/issue19542);603    # we have to use a lock and do it ourselves604    cache = ffi._typecache605    with global_lock:606        res1 = cache.get(key)607        if res1 is None:608            cache[key] = res609            return res610        else:611            return res1612 613def pointer_cache(ffi, BType):614    return global_cache('?', ffi, 'new_pointer_type', BType)615 616def attach_exception_info(e, name):617    if e.args and type(e.args[0]) is str:618        e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]619 
codekingpro/portable-devtools · Team Ai