github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/model.py (about)

     1  import types, sys
     2  import weakref
     3  
     4  from .lock import allocate_lock
     5  
     6  
     7  # type qualifiers
     8  Q_CONST    = 0x01
     9  Q_RESTRICT = 0x02
    10  Q_VOLATILE = 0x04
    11  
    12  def 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 in
    20          # _cffi_include.h for it (and in its copies, documented there)
    21          replace_with = ' __restrict ' + replace_with.lstrip()
    22      return replace_with
    23  
    24  
    25  class BaseTypeByIdentity(object):
    26      is_array_type = False
    27      is_raw_function = False
    28  
    29      def get_c_name(self, replace_with='', context='a C file', quals=0):
    30          result = self.c_name_with_marker
    31          assert result.count('&') == 1
    32          # 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_with
    37              elif not replace_with[0] in '[(':
    38                  replace_with = ' ' + replace_with
    39          replace_with = qualify(quals, replace_with)
    40          result = result.replace('&', replace_with)
    41          if '$' in result:
    42              from .ffiplatform import VerificationError
    43              raise VerificationError(
    44                  "cannot generate '%s' in %s: unknown type name"
    45                  % (self._get_c_name(), context))
    46          return result
    47  
    48      def _get_c_name(self):
    49          return self.c_name_with_marker.replace('&', '')
    50  
    51      def has_c_name(self):
    52          return '$' not in self._get_c_name()
    53  
    54      def is_integer_type(self):
    55          return False
    56  
    57      def get_cached_btype(self, ffi, finishlist, can_delay=False):
    58          try:
    59              BType = ffi._cached_btypes[self]
    60          except KeyError:
    61              BType = self.build_backend_type(ffi, finishlist)
    62              BType2 = ffi._cached_btypes.setdefault(self, BType)
    63              assert BType2 is BType
    64          return BType
    65  
    66      def __repr__(self):
    67          return '<%s>' % (self._get_c_name(),)
    68  
    69      def _get_items(self):
    70          return [(name, getattr(self, name)) for name in self._attrs_]
    71  
    72  
    73  class BaseType(BaseTypeByIdentity):
    74  
    75      def __eq__(self, other):
    76          return (self.__class__ == other.__class__ and
    77                  self._get_items() == other._get_items())
    78  
    79      def __ne__(self, other):
    80          return not self == other
    81  
    82      def __hash__(self):
    83          return hash((self.__class__, tuple(self._get_items())))
    84  
    85  
    86  class VoidType(BaseType):
    87      _attrs_ = ()
    88  
    89      def __init__(self):
    90          self.c_name_with_marker = 'void&'
    91  
    92      def build_backend_type(self, ffi, finishlist):
    93          return global_cache(self, ffi, 'new_void_type')
    94  
    95  void_type = VoidType()
    96  
    97  
    98  class BasePrimitiveType(BaseType):
    99      pass
   100  
   101  
   102  class 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          '_Bool':              'i',
   121          # the following types are not primitive in the C sense
   122          'wchar_t':            'c',
   123          'int8_t':             'i',
   124          'uint8_t':            'i',
   125          'int16_t':            'i',
   126          'uint16_t':           'i',
   127          'int32_t':            'i',
   128          'uint32_t':           'i',
   129          'int64_t':            'i',
   130          'uint64_t':           'i',
   131          'int_least8_t':       'i',
   132          'uint_least8_t':      'i',
   133          'int_least16_t':      'i',
   134          'uint_least16_t':     'i',
   135          'int_least32_t':      'i',
   136          'uint_least32_t':     'i',
   137          'int_least64_t':      'i',
   138          'uint_least64_t':     'i',
   139          'int_fast8_t':        'i',
   140          'uint_fast8_t':       'i',
   141          'int_fast16_t':       'i',
   142          'uint_fast16_t':      'i',
   143          'int_fast32_t':       'i',
   144          'uint_fast32_t':      'i',
   145          'int_fast64_t':       'i',
   146          'uint_fast64_t':      'i',
   147          'intptr_t':           'i',
   148          'uintptr_t':          'i',
   149          'intmax_t':           'i',
   150          'uintmax_t':          'i',
   151          'ptrdiff_t':          'i',
   152          'size_t':             'i',
   153          'ssize_t':            'i',
   154          }
   155  
   156      def __init__(self, name):
   157          assert name in self.ALL_PRIMITIVE_TYPES
   158          self.name = name
   159          self.c_name_with_marker = name + '&'
   160  
   161      def is_char_type(self):
   162          return self.ALL_PRIMITIVE_TYPES[self.name] == 'c'
   163      def is_integer_type(self):
   164          return self.ALL_PRIMITIVE_TYPES[self.name] == 'i'
   165      def is_float_type(self):
   166          return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
   167  
   168      def build_backend_type(self, ffi, finishlist):
   169          return global_cache(self, ffi, 'new_primitive_type', self.name)
   170  
   171  
   172  class UnknownIntegerType(BasePrimitiveType):
   173      _attrs_ = ('name',)
   174  
   175      def __init__(self, name):
   176          self.name = name
   177          self.c_name_with_marker = name + '&'
   178  
   179      def is_integer_type(self):
   180          return True
   181  
   182      def build_backend_type(self, ffi, finishlist):
   183          raise NotImplementedError("integer type '%s' can only be used after "
   184                                    "compilation" % self.name)
   185  
   186  class UnknownFloatType(BasePrimitiveType):
   187      _attrs_ = ('name', )
   188  
   189      def __init__(self, name):
   190          self.name = name
   191          self.c_name_with_marker = name + '&'
   192  
   193      def build_backend_type(self, ffi, finishlist):
   194          raise NotImplementedError("float type '%s' can only be used after "
   195                                    "compilation" % self.name)
   196  
   197  
   198  class BaseFunctionType(BaseType):
   199      _attrs_ = ('args', 'result', 'ellipsis', 'abi')
   200  
   201      def __init__(self, args, result, ellipsis, abi=None):
   202          self.args = args
   203          self.result = result
   204          self.ellipsis = ellipsis
   205          self.abi = abi
   206          #
   207          reprargs = [arg._get_c_name() for arg in self.args]
   208          if self.ellipsis:
   209              reprargs.append('...')
   210          reprargs = reprargs or ['void']
   211          replace_with = self._base_pattern % (', '.join(reprargs),)
   212          if abi is not None:
   213              replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
   214          self.c_name_with_marker = (
   215              self.result.c_name_with_marker.replace('&', replace_with))
   216  
   217  
   218  class RawFunctionType(BaseFunctionType):
   219      # Corresponds to a C type like 'int(int)', which is the C type of
   220      # a function, but not a pointer-to-function.  The backend has no
   221      # notion of such a type; it's used temporarily by parsing.
   222      _base_pattern = '(&)(%s)'
   223      is_raw_function = True
   224  
   225      def build_backend_type(self, ffi, finishlist):
   226          from . import api
   227          raise api.CDefError("cannot render the type %r: it is a function "
   228                              "type, not a pointer-to-function type" % (self,))
   229  
   230      def as_function_pointer(self):
   231          return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
   232  
   233  
   234  class FunctionPtrType(BaseFunctionType):
   235      _base_pattern = '(*&)(%s)'
   236  
   237      def build_backend_type(self, ffi, finishlist):
   238          result = self.result.get_cached_btype(ffi, finishlist)
   239          args = []
   240          for tp in self.args:
   241              args.append(tp.get_cached_btype(ffi, finishlist))
   242          abi_args = ()
   243          if self.abi == "__stdcall":
   244              if not self.ellipsis:    # __stdcall ignored for variadic funcs
   245                  try:
   246                      abi_args = (ffi._backend.FFI_STDCALL,)
   247                  except AttributeError:
   248                      pass
   249          return global_cache(self, ffi, 'new_function_type',
   250                              tuple(args), result, self.ellipsis, *abi_args)
   251  
   252      def as_raw_function(self):
   253          return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)
   254  
   255  
   256  class PointerType(BaseType):
   257      _attrs_ = ('totype', 'quals')
   258  
   259      def __init__(self, totype, quals=0):
   260          self.totype = totype
   261          self.quals = quals
   262          extra = qualify(quals, " *&")
   263          if totype.is_array_type:
   264              extra = "(%s)" % (extra.lstrip(),)
   265          self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)
   266  
   267      def build_backend_type(self, ffi, finishlist):
   268          BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
   269          return global_cache(self, ffi, 'new_pointer_type', BItem)
   270  
   271  voidp_type = PointerType(void_type)
   272  
   273  def ConstPointerType(totype):
   274      return PointerType(totype, Q_CONST)
   275  
   276  const_voidp_type = ConstPointerType(void_type)
   277  
   278  
   279  class NamedPointerType(PointerType):
   280      _attrs_ = ('totype', 'name')
   281  
   282      def __init__(self, totype, name, quals=0):
   283          PointerType.__init__(self, totype, quals)
   284          self.name = name
   285          self.c_name_with_marker = name + '&'
   286  
   287  
   288  class ArrayType(BaseType):
   289      _attrs_ = ('item', 'length')
   290      is_array_type = True
   291  
   292      def __init__(self, item, length):
   293          self.item = item
   294          self.length = length
   295          #
   296          if length is None:
   297              brackets = '&[]'
   298          elif length == '...':
   299              brackets = '&[/*...*/]'
   300          else:
   301              brackets = '&[%s]' % length
   302          self.c_name_with_marker = (
   303              self.item.c_name_with_marker.replace('&', brackets))
   304  
   305      def resolve_length(self, newlength):
   306          return ArrayType(self.item, newlength)
   307  
   308      def build_backend_type(self, ffi, finishlist):
   309          if self.length == '...':
   310              from . import api
   311              raise api.CDefError("cannot render the type %r: unknown length" %
   312                                  (self,))
   313          self.item.get_cached_btype(ffi, finishlist)   # force the item BType
   314          BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
   315          return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
   316  
   317  char_array_type = ArrayType(PrimitiveType('char'), None)
   318  
   319  
   320  class StructOrUnionOrEnum(BaseTypeByIdentity):
   321      _attrs_ = ('name',)
   322      forcename = None
   323  
   324      def build_c_name_with_marker(self):
   325          name = self.forcename or '%s %s' % (self.kind, self.name)
   326          self.c_name_with_marker = name + '&'
   327  
   328      def force_the_name(self, forcename):
   329          self.forcename = forcename
   330          self.build_c_name_with_marker()
   331  
   332      def get_official_name(self):
   333          assert self.c_name_with_marker.endswith('&')
   334          return self.c_name_with_marker[:-1]
   335  
   336  
   337  class StructOrUnion(StructOrUnionOrEnum):
   338      fixedlayout = None
   339      completed = 0
   340      partial = False
   341      packed = False
   342  
   343      def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):
   344          self.name = name
   345          self.fldnames = fldnames
   346          self.fldtypes = fldtypes
   347          self.fldbitsize = fldbitsize
   348          self.fldquals = fldquals
   349          self.build_c_name_with_marker()
   350  
   351      def has_anonymous_struct_fields(self):
   352          if self.fldtypes is None:
   353              return False
   354          for name, type in zip(self.fldnames, self.fldtypes):
   355              if name == '' and isinstance(type, StructOrUnion):
   356                  return True
   357          return False
   358  
   359      def enumfields(self):
   360          fldquals = self.fldquals
   361          if fldquals is None:
   362              fldquals = (0,) * len(self.fldnames)
   363          for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
   364                                                self.fldbitsize, fldquals):
   365              if name == '' and isinstance(type, StructOrUnion):
   366                  # nested anonymous struct/union
   367                  for result in type.enumfields():
   368                      yield result
   369              else:
   370                  yield (name, type, bitsize, quals)
   371  
   372      def force_flatten(self):
   373          # force the struct or union to have a declaration that lists
   374          # directly all fields returned by enumfields(), flattening
   375          # nested anonymous structs/unions.
   376          names = []
   377          types = []
   378          bitsizes = []
   379          fldquals = []
   380          for name, type, bitsize, quals in self.enumfields():
   381              names.append(name)
   382              types.append(type)
   383              bitsizes.append(bitsize)
   384              fldquals.append(quals)
   385          self.fldnames = tuple(names)
   386          self.fldtypes = tuple(types)
   387          self.fldbitsize = tuple(bitsizes)
   388          self.fldquals = tuple(fldquals)
   389  
   390      def get_cached_btype(self, ffi, finishlist, can_delay=False):
   391          BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
   392                                                       can_delay)
   393          if not can_delay:
   394              self.finish_backend_type(ffi, finishlist)
   395          return BType
   396  
   397      def finish_backend_type(self, ffi, finishlist):
   398          if self.completed:
   399              if self.completed != 2:
   400                  raise NotImplementedError("recursive structure declaration "
   401                                            "for '%s'" % (self.name,))
   402              return
   403          BType = ffi._cached_btypes[self]
   404          #
   405          self.completed = 1
   406          #
   407          if self.fldtypes is None:
   408              pass    # not completing it: it's an opaque struct
   409              #
   410          elif self.fixedlayout is None:
   411              fldtypes = [tp.get_cached_btype(ffi, finishlist)
   412                          for tp in self.fldtypes]
   413              lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
   414              sflags = 0
   415              if self.packed:
   416                  sflags = 8    # SF_PACKED
   417              ffi._backend.complete_struct_or_union(BType, lst, self,
   418                                                    -1, -1, sflags)
   419              #
   420          else:
   421              fldtypes = []
   422              fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
   423              for i in range(len(self.fldnames)):
   424                  fsize = fieldsize[i]
   425                  ftype = self.fldtypes[i]
   426                  #
   427                  if isinstance(ftype, ArrayType) and ftype.length == '...':
   428                      # fix the length to match the total size
   429                      BItemType = ftype.item.get_cached_btype(ffi, finishlist)
   430                      nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
   431                      if nrest != 0:
   432                          self._verification_error(
   433                              "field '%s.%s' has a bogus size?" % (
   434                              self.name, self.fldnames[i] or '{}'))
   435                      ftype = ftype.resolve_length(nlen)
   436                      self.fldtypes = (self.fldtypes[:i] + (ftype,) +
   437                                       self.fldtypes[i+1:])
   438                  #
   439                  BFieldType = ftype.get_cached_btype(ffi, finishlist)
   440                  if isinstance(ftype, ArrayType) and ftype.length is None:
   441                      assert fsize == 0
   442                  else:
   443                      bitemsize = ffi.sizeof(BFieldType)
   444                      if bitemsize != fsize:
   445                          self._verification_error(
   446                              "field '%s.%s' is declared as %d bytes, but is "
   447                              "really %d bytes" % (self.name,
   448                                                   self.fldnames[i] or '{}',
   449                                                   bitemsize, fsize))
   450                  fldtypes.append(BFieldType)
   451              #
   452              lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
   453              ffi._backend.complete_struct_or_union(BType, lst, self,
   454                                                    totalsize, totalalignment)
   455          self.completed = 2
   456  
   457      def _verification_error(self, msg):
   458          from .ffiplatform import VerificationError
   459          raise VerificationError(msg)
   460  
   461      def check_not_partial(self):
   462          if self.partial and self.fixedlayout is None:
   463              from . import ffiplatform
   464              raise ffiplatform.VerificationMissing(self._get_c_name())
   465  
   466      def build_backend_type(self, ffi, finishlist):
   467          self.check_not_partial()
   468          finishlist.append(self)
   469          #
   470          return global_cache(self, ffi, 'new_%s_type' % self.kind,
   471                              self.get_official_name(), key=self)
   472  
   473  
   474  class StructType(StructOrUnion):
   475      kind = 'struct'
   476  
   477  
   478  class UnionType(StructOrUnion):
   479      kind = 'union'
   480  
   481  
   482  class EnumType(StructOrUnionOrEnum):
   483      kind = 'enum'
   484      partial = False
   485      partial_resolved = False
   486  
   487      def __init__(self, name, enumerators, enumvalues, baseinttype=None):
   488          self.name = name
   489          self.enumerators = enumerators
   490          self.enumvalues = enumvalues
   491          self.baseinttype = baseinttype
   492          self.build_c_name_with_marker()
   493  
   494      def force_the_name(self, forcename):
   495          StructOrUnionOrEnum.force_the_name(self, forcename)
   496          if self.forcename is None:
   497              name = self.get_official_name()
   498              self.forcename = '$' + name.replace(' ', '_')
   499  
   500      def check_not_partial(self):
   501          if self.partial and not self.partial_resolved:
   502              from . import ffiplatform
   503              raise ffiplatform.VerificationMissing(self._get_c_name())
   504  
   505      def build_backend_type(self, ffi, finishlist):
   506          self.check_not_partial()
   507          base_btype = self.build_baseinttype(ffi, finishlist)
   508          return global_cache(self, ffi, 'new_enum_type',
   509                              self.get_official_name(),
   510                              self.enumerators, self.enumvalues,
   511                              base_btype, key=self)
   512  
   513      def build_baseinttype(self, ffi, finishlist):
   514          if self.baseinttype is not None:
   515              return self.baseinttype.get_cached_btype(ffi, finishlist)
   516          #
   517          from . import api
   518          if self.enumvalues:
   519              smallest_value = min(self.enumvalues)
   520              largest_value = max(self.enumvalues)
   521          else:
   522              import warnings
   523              warnings.warn("%r has no values explicitly defined; next version "
   524                            "will refuse to guess which integer type it is "
   525                            "meant to be (unsigned/signed, int/long)"
   526                            % self._get_c_name())
   527              smallest_value = largest_value = 0
   528          if smallest_value < 0:   # needs a signed type
   529              sign = 1
   530              candidate1 = PrimitiveType("int")
   531              candidate2 = PrimitiveType("long")
   532          else:
   533              sign = 0
   534              candidate1 = PrimitiveType("unsigned int")
   535              candidate2 = PrimitiveType("unsigned long")
   536          btype1 = candidate1.get_cached_btype(ffi, finishlist)
   537          btype2 = candidate2.get_cached_btype(ffi, finishlist)
   538          size1 = ffi.sizeof(btype1)
   539          size2 = ffi.sizeof(btype2)
   540          if (smallest_value >= ((-1) << (8*size1-1)) and
   541              largest_value < (1 << (8*size1-sign))):
   542              return btype1
   543          if (smallest_value >= ((-1) << (8*size2-1)) and
   544              largest_value < (1 << (8*size2-sign))):
   545              return btype2
   546          raise api.CDefError("%s values don't all fit into either 'long' "
   547                              "or 'unsigned long'" % self._get_c_name())
   548  
   549  def unknown_type(name, structname=None):
   550      if structname is None:
   551          structname = '$%s' % name
   552      tp = StructType(structname, None, None, None)
   553      tp.force_the_name(name)
   554      tp.origin = "unknown_type"
   555      return tp
   556  
   557  def unknown_ptr_type(name, structname=None):
   558      if structname is None:
   559          structname = '$$%s' % name
   560      tp = StructType(structname, None, None, None)
   561      return NamedPointerType(tp, name)
   562  
   563  
   564  global_lock = allocate_lock()
   565  
   566  def global_cache(srctype, ffi, funcname, *args, **kwds):
   567      key = kwds.pop('key', (funcname, args))
   568      assert not kwds
   569      try:
   570          return ffi._backend.__typecache[key]
   571      except KeyError:
   572          pass
   573      except AttributeError:
   574          # initialize the __typecache attribute, either at the module level
   575          # if ffi._backend is a module, or at the class level if ffi._backend
   576          # is some instance.
   577          if isinstance(ffi._backend, types.ModuleType):
   578              ffi._backend.__typecache = weakref.WeakValueDictionary()
   579          else:
   580              type(ffi._backend).__typecache = weakref.WeakValueDictionary()
   581      try:
   582          res = getattr(ffi._backend, funcname)(*args)
   583      except NotImplementedError as e:
   584          raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))
   585      # note that setdefault() on WeakValueDictionary is not atomic
   586      # and contains a rare bug (http://bugs.python.org/issue19542);
   587      # we have to use a lock and do it ourselves
   588      cache = ffi._backend.__typecache
   589      with global_lock:
   590          res1 = cache.get(key)
   591          if res1 is None:
   592              cache[key] = res
   593              return res
   594          else:
   595              return res1
   596  
   597  def pointer_cache(ffi, BType):
   598      return global_cache('?', ffi, 'new_pointer_type', BType)
   599  
   600  def attach_exception_info(e, name):
   601      if e.args and type(e.args[0]) is str:
   602          e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]