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

     1  import os, sys, io
     2  from . import ffiplatform, model
     3  from .cffi_opcode import *
     4  
     5  VERSION = "0x2601"
     6  VERSION_EMBEDDED = "0x2701"
     7  
     8  
     9  class GlobalExpr:
    10      def __init__(self, name, address, type_op, size=0, check_value=0):
    11          self.name = name
    12          self.address = address
    13          self.type_op = type_op
    14          self.size = size
    15          self.check_value = check_value
    16  
    17      def as_c_expr(self):
    18          return '  { "%s", (void *)%s, %s, (void *)%s },' % (
    19              self.name, self.address, self.type_op.as_c_expr(), self.size)
    20  
    21      def as_python_expr(self):
    22          return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
    23                                 self.check_value)
    24  
    25  class FieldExpr:
    26      def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
    27          self.name = name
    28          self.field_offset = field_offset
    29          self.field_size = field_size
    30          self.fbitsize = fbitsize
    31          self.field_type_op = field_type_op
    32  
    33      def as_c_expr(self):
    34          spaces = " " * len(self.name)
    35          return ('  { "%s", %s,\n' % (self.name, self.field_offset) +
    36                  '     %s   %s,\n' % (spaces, self.field_size) +
    37                  '     %s   %s },' % (spaces, self.field_type_op.as_c_expr()))
    38  
    39      def as_python_expr(self):
    40          raise NotImplementedError
    41  
    42      def as_field_python_expr(self):
    43          if self.field_type_op.op == OP_NOOP:
    44              size_expr = ''
    45          elif self.field_type_op.op == OP_BITFIELD:
    46              size_expr = format_four_bytes(self.fbitsize)
    47          else:
    48              raise NotImplementedError
    49          return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
    50                                size_expr,
    51                                self.name)
    52  
    53  class StructUnionExpr:
    54      def __init__(self, name, type_index, flags, size, alignment, comment,
    55                   first_field_index, c_fields):
    56          self.name = name
    57          self.type_index = type_index
    58          self.flags = flags
    59          self.size = size
    60          self.alignment = alignment
    61          self.comment = comment
    62          self.first_field_index = first_field_index
    63          self.c_fields = c_fields
    64  
    65      def as_c_expr(self):
    66          return ('  { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
    67                  + '\n    %s, %s, ' % (self.size, self.alignment)
    68                  + '%d, %d ' % (self.first_field_index, len(self.c_fields))
    69                  + ('/* %s */ ' % self.comment if self.comment else '')
    70                  + '},')
    71  
    72      def as_python_expr(self):
    73          flags = eval(self.flags, G_FLAGS)
    74          fields_expr = [c_field.as_field_python_expr()
    75                         for c_field in self.c_fields]
    76          return "(b'%s%s%s',%s)" % (
    77              format_four_bytes(self.type_index),
    78              format_four_bytes(flags),
    79              self.name,
    80              ','.join(fields_expr))
    81  
    82  class EnumExpr:
    83      def __init__(self, name, type_index, size, signed, allenums):
    84          self.name = name
    85          self.type_index = type_index
    86          self.size = size
    87          self.signed = signed
    88          self.allenums = allenums
    89  
    90      def as_c_expr(self):
    91          return ('  { "%s", %d, _cffi_prim_int(%s, %s),\n'
    92                  '    "%s" },' % (self.name, self.type_index,
    93                                   self.size, self.signed, self.allenums))
    94  
    95      def as_python_expr(self):
    96          prim_index = {
    97              (1, 0): PRIM_UINT8,  (1, 1):  PRIM_INT8,
    98              (2, 0): PRIM_UINT16, (2, 1):  PRIM_INT16,
    99              (4, 0): PRIM_UINT32, (4, 1):  PRIM_INT32,
   100              (8, 0): PRIM_UINT64, (8, 1):  PRIM_INT64,
   101              }[self.size, self.signed]
   102          return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
   103                                       format_four_bytes(prim_index),
   104                                       self.name, self.allenums)
   105  
   106  class TypenameExpr:
   107      def __init__(self, name, type_index):
   108          self.name = name
   109          self.type_index = type_index
   110  
   111      def as_c_expr(self):
   112          return '  { "%s", %d },' % (self.name, self.type_index)
   113  
   114      def as_python_expr(self):
   115          return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
   116  
   117  
   118  # ____________________________________________________________
   119  
   120  
   121  class Recompiler:
   122      _num_externpy = 0
   123  
   124      def __init__(self, ffi, module_name, target_is_python=False):
   125          self.ffi = ffi
   126          self.module_name = module_name
   127          self.target_is_python = target_is_python
   128  
   129      def collect_type_table(self):
   130          self._typesdict = {}
   131          self._generate("collecttype")
   132          #
   133          all_decls = sorted(self._typesdict, key=str)
   134          #
   135          # prepare all FUNCTION bytecode sequences first
   136          self.cffi_types = []
   137          for tp in all_decls:
   138              if tp.is_raw_function:
   139                  assert self._typesdict[tp] is None
   140                  self._typesdict[tp] = len(self.cffi_types)
   141                  self.cffi_types.append(tp)     # placeholder
   142                  for tp1 in tp.args:
   143                      assert isinstance(tp1, (model.VoidType,
   144                                              model.BasePrimitiveType,
   145                                              model.PointerType,
   146                                              model.StructOrUnionOrEnum,
   147                                              model.FunctionPtrType))
   148                      if self._typesdict[tp1] is None:
   149                          self._typesdict[tp1] = len(self.cffi_types)
   150                      self.cffi_types.append(tp1)   # placeholder
   151                  self.cffi_types.append('END')     # placeholder
   152          #
   153          # prepare all OTHER bytecode sequences
   154          for tp in all_decls:
   155              if not tp.is_raw_function and self._typesdict[tp] is None:
   156                  self._typesdict[tp] = len(self.cffi_types)
   157                  self.cffi_types.append(tp)        # placeholder
   158                  if tp.is_array_type and tp.length is not None:
   159                      self.cffi_types.append('LEN') # placeholder
   160          assert None not in self._typesdict.values()
   161          #
   162          # collect all structs and unions and enums
   163          self._struct_unions = {}
   164          self._enums = {}
   165          for tp in all_decls:
   166              if isinstance(tp, model.StructOrUnion):
   167                  self._struct_unions[tp] = None
   168              elif isinstance(tp, model.EnumType):
   169                  self._enums[tp] = None
   170          for i, tp in enumerate(sorted(self._struct_unions,
   171                                        key=lambda tp: tp.name)):
   172              self._struct_unions[tp] = i
   173          for i, tp in enumerate(sorted(self._enums,
   174                                        key=lambda tp: tp.name)):
   175              self._enums[tp] = i
   176          #
   177          # emit all bytecode sequences now
   178          for tp in all_decls:
   179              method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
   180              method(tp, self._typesdict[tp])
   181          #
   182          # consistency check
   183          for op in self.cffi_types:
   184              assert isinstance(op, CffiOp)
   185          self.cffi_types = tuple(self.cffi_types)    # don't change any more
   186  
   187      def _do_collect_type(self, tp):
   188          if not isinstance(tp, model.BaseTypeByIdentity):
   189              if isinstance(tp, tuple):
   190                  for x in tp:
   191                      self._do_collect_type(x)
   192              return
   193          if tp not in self._typesdict:
   194              self._typesdict[tp] = None
   195              if isinstance(tp, model.FunctionPtrType):
   196                  self._do_collect_type(tp.as_raw_function())
   197              elif isinstance(tp, model.StructOrUnion):
   198                  if tp.fldtypes is not None and (
   199                          tp not in self.ffi._parser._included_declarations):
   200                      for name1, tp1, _, _ in tp.enumfields():
   201                          self._do_collect_type(self._field_type(tp, name1, tp1))
   202              else:
   203                  for _, x in tp._get_items():
   204                      self._do_collect_type(x)
   205  
   206      def _generate(self, step_name):
   207          lst = self.ffi._parser._declarations.items()
   208          for name, (tp, quals) in sorted(lst):
   209              kind, realname = name.split(' ', 1)
   210              try:
   211                  method = getattr(self, '_generate_cpy_%s_%s' % (kind,
   212                                                                  step_name))
   213              except AttributeError:
   214                  raise ffiplatform.VerificationError(
   215                      "not implemented in recompile(): %r" % name)
   216              try:
   217                  self._current_quals = quals
   218                  method(tp, realname)
   219              except Exception as e:
   220                  model.attach_exception_info(e, name)
   221                  raise
   222  
   223      # ----------
   224  
   225      ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
   226  
   227      def collect_step_tables(self):
   228          # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
   229          self._lsts = {}
   230          for step_name in self.ALL_STEPS:
   231              self._lsts[step_name] = []
   232          self._seen_struct_unions = set()
   233          self._generate("ctx")
   234          self._add_missing_struct_unions()
   235          #
   236          for step_name in self.ALL_STEPS:
   237              lst = self._lsts[step_name]
   238              if step_name != "field":
   239                  lst.sort(key=lambda entry: entry.name)
   240              self._lsts[step_name] = tuple(lst)    # don't change any more
   241          #
   242          # check for a possible internal inconsistency: _cffi_struct_unions
   243          # should have been generated with exactly self._struct_unions
   244          lst = self._lsts["struct_union"]
   245          for tp, i in self._struct_unions.items():
   246              assert i < len(lst)
   247              assert lst[i].name == tp.name
   248          assert len(lst) == len(self._struct_unions)
   249          # same with enums
   250          lst = self._lsts["enum"]
   251          for tp, i in self._enums.items():
   252              assert i < len(lst)
   253              assert lst[i].name == tp.name
   254          assert len(lst) == len(self._enums)
   255  
   256      # ----------
   257  
   258      def _prnt(self, what=''):
   259          self._f.write(what + '\n')
   260  
   261      def write_source_to_f(self, f, preamble):
   262          if self.target_is_python:
   263              assert preamble is None
   264              self.write_py_source_to_f(f)
   265          else:
   266              assert preamble is not None
   267              self.write_c_source_to_f(f, preamble)
   268  
   269      def _rel_readlines(self, filename):
   270          g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
   271          lines = g.readlines()
   272          g.close()
   273          return lines
   274  
   275      def write_c_source_to_f(self, f, preamble):
   276          self._f = f
   277          prnt = self._prnt
   278          #
   279          # first the '#include' (actually done by inlining the file's content)
   280          lines = self._rel_readlines('_cffi_include.h')
   281          i = lines.index('#include "parse_c_type.h"\n')
   282          lines[i:i+1] = self._rel_readlines('parse_c_type.h')
   283          prnt(''.join(lines))
   284          #
   285          # if we have ffi._embedding != None, we give it here as a macro
   286          # and include an extra file
   287          base_module_name = self.module_name.split('.')[-1]
   288          if self.ffi._embedding is not None:
   289              prnt('#define _CFFI_MODULE_NAME  "%s"' % (self.module_name,))
   290              prnt('#define _CFFI_PYTHON_STARTUP_CODE  %s' %
   291                   (self._string_literal(self.ffi._embedding),))
   292              prnt('#ifdef PYPY_VERSION')
   293              prnt('# define _CFFI_PYTHON_STARTUP_FUNC  _cffi_pypyinit_%s' % (
   294                  base_module_name,))
   295              prnt('#elif PY_MAJOR_VERSION >= 3')
   296              prnt('# define _CFFI_PYTHON_STARTUP_FUNC  PyInit_%s' % (
   297                  base_module_name,))
   298              prnt('#else')
   299              prnt('# define _CFFI_PYTHON_STARTUP_FUNC  init%s' % (
   300                  base_module_name,))
   301              prnt('#endif')
   302              lines = self._rel_readlines('_embedding.h')
   303              prnt(''.join(lines))
   304              version = VERSION_EMBEDDED
   305          else:
   306              version = VERSION
   307          #
   308          # then paste the C source given by the user, verbatim.
   309          prnt('/************************************************************/')
   310          prnt()
   311          prnt(preamble)
   312          prnt()
   313          prnt('/************************************************************/')
   314          prnt()
   315          #
   316          # the declaration of '_cffi_types'
   317          prnt('static void *_cffi_types[] = {')
   318          typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
   319          for i, op in enumerate(self.cffi_types):
   320              comment = ''
   321              if i in typeindex2type:
   322                  comment = ' // ' + typeindex2type[i]._get_c_name()
   323              prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
   324          if not self.cffi_types:
   325              prnt('  0')
   326          prnt('};')
   327          prnt()
   328          #
   329          # call generate_cpy_xxx_decl(), for every xxx found from
   330          # ffi._parser._declarations.  This generates all the functions.
   331          self._seen_constants = set()
   332          self._generate("decl")
   333          #
   334          # the declaration of '_cffi_globals' and '_cffi_typenames'
   335          nums = {}
   336          for step_name in self.ALL_STEPS:
   337              lst = self._lsts[step_name]
   338              nums[step_name] = len(lst)
   339              if nums[step_name] > 0:
   340                  prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
   341                      step_name, step_name))
   342                  for entry in lst:
   343                      prnt(entry.as_c_expr())
   344                  prnt('};')
   345                  prnt()
   346          #
   347          # the declaration of '_cffi_includes'
   348          if self.ffi._included_ffis:
   349              prnt('static const char * const _cffi_includes[] = {')
   350              for ffi_to_include in self.ffi._included_ffis:
   351                  try:
   352                      included_module_name, included_source = (
   353                          ffi_to_include._assigned_source[:2])
   354                  except AttributeError:
   355                      raise ffiplatform.VerificationError(
   356                          "ffi object %r includes %r, but the latter has not "
   357                          "been prepared with set_source()" % (
   358                              self.ffi, ffi_to_include,))
   359                  if included_source is None:
   360                      raise ffiplatform.VerificationError(
   361                          "not implemented yet: ffi.include() of a Python-based "
   362                          "ffi inside a C-based ffi")
   363                  prnt('  "%s",' % (included_module_name,))
   364              prnt('  NULL')
   365              prnt('};')
   366              prnt()
   367          #
   368          # the declaration of '_cffi_type_context'
   369          prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
   370          prnt('  _cffi_types,')
   371          for step_name in self.ALL_STEPS:
   372              if nums[step_name] > 0:
   373                  prnt('  _cffi_%ss,' % step_name)
   374              else:
   375                  prnt('  NULL,  /* no %ss */' % step_name)
   376          for step_name in self.ALL_STEPS:
   377              if step_name != "field":
   378                  prnt('  %d,  /* num_%ss */' % (nums[step_name], step_name))
   379          if self.ffi._included_ffis:
   380              prnt('  _cffi_includes,')
   381          else:
   382              prnt('  NULL,  /* no includes */')
   383          prnt('  %d,  /* num_types */' % (len(self.cffi_types),))
   384          flags = 0
   385          if self._num_externpy:
   386              flags |= 1     # set to mean that we use extern "Python"
   387          prnt('  %d,  /* flags */' % flags)
   388          prnt('};')
   389          prnt()
   390          #
   391          # the init function
   392          prnt('#ifdef PYPY_VERSION')
   393          prnt('PyMODINIT_FUNC')
   394          prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
   395          prnt('{')
   396          if self._num_externpy:
   397              prnt('    if (((intptr_t)p[0]) >= 0x0A03) {')
   398              prnt('        _cffi_call_python_org = '
   399                   '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
   400              prnt('    }')
   401          prnt('    p[0] = (const void *)%s;' % version)
   402          prnt('    p[1] = &_cffi_type_context;')
   403          prnt('}')
   404          # on Windows, distutils insists on putting init_cffi_xyz in
   405          # 'export_symbols', so instead of fighting it, just give up and
   406          # give it one
   407          prnt('#  ifdef _MSC_VER')
   408          prnt('     PyMODINIT_FUNC')
   409          prnt('#  if PY_MAJOR_VERSION >= 3')
   410          prnt('     PyInit_%s(void) { return NULL; }' % (base_module_name,))
   411          prnt('#  else')
   412          prnt('     init%s(void) { }' % (base_module_name,))
   413          prnt('#  endif')
   414          prnt('#  endif')
   415          prnt('#elif PY_MAJOR_VERSION >= 3')
   416          prnt('PyMODINIT_FUNC')
   417          prnt('PyInit_%s(void)' % (base_module_name,))
   418          prnt('{')
   419          prnt('  return _cffi_init("%s", %s, &_cffi_type_context);' % (
   420              self.module_name, version))
   421          prnt('}')
   422          prnt('#else')
   423          prnt('PyMODINIT_FUNC')
   424          prnt('init%s(void)' % (base_module_name,))
   425          prnt('{')
   426          prnt('  _cffi_init("%s", %s, &_cffi_type_context);' % (
   427              self.module_name, version))
   428          prnt('}')
   429          prnt('#endif')
   430  
   431      def _to_py(self, x):
   432          if isinstance(x, str):
   433              return "b'%s'" % (x,)
   434          if isinstance(x, (list, tuple)):
   435              rep = [self._to_py(item) for item in x]
   436              if len(rep) == 1:
   437                  rep.append('')
   438              return "(%s)" % (','.join(rep),)
   439          return x.as_python_expr()  # Py2: unicode unexpected; Py3: bytes unexp.
   440  
   441      def write_py_source_to_f(self, f):
   442          self._f = f
   443          prnt = self._prnt
   444          #
   445          # header
   446          prnt("# auto-generated file")
   447          prnt("import _cffi_backend")
   448          #
   449          # the 'import' of the included ffis
   450          num_includes = len(self.ffi._included_ffis or ())
   451          for i in range(num_includes):
   452              ffi_to_include = self.ffi._included_ffis[i]
   453              try:
   454                  included_module_name, included_source = (
   455                      ffi_to_include._assigned_source[:2])
   456              except AttributeError:
   457                  raise ffiplatform.VerificationError(
   458                      "ffi object %r includes %r, but the latter has not "
   459                      "been prepared with set_source()" % (
   460                          self.ffi, ffi_to_include,))
   461              if included_source is not None:
   462                  raise ffiplatform.VerificationError(
   463                      "not implemented yet: ffi.include() of a C-based "
   464                      "ffi inside a Python-based ffi")
   465              prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
   466          prnt()
   467          prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
   468          prnt("    _version = %s," % (VERSION,))
   469          #
   470          # the '_types' keyword argument
   471          self.cffi_types = tuple(self.cffi_types)    # don't change any more
   472          types_lst = [op.as_python_bytes() for op in self.cffi_types]
   473          prnt('    _types = %s,' % (self._to_py(''.join(types_lst)),))
   474          typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
   475          #
   476          # the keyword arguments from ALL_STEPS
   477          for step_name in self.ALL_STEPS:
   478              lst = self._lsts[step_name]
   479              if len(lst) > 0 and step_name != "field":
   480                  prnt('    _%ss = %s,' % (step_name, self._to_py(lst)))
   481          #
   482          # the '_includes' keyword argument
   483          if num_includes > 0:
   484              prnt('    _includes = (%s,),' % (
   485                  ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
   486          #
   487          # the footer
   488          prnt(')')
   489  
   490      # ----------
   491  
   492      def _gettypenum(self, type):
   493          # a KeyError here is a bug.  please report it! :-)
   494          return self._typesdict[type]
   495  
   496      def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
   497          extraarg = ''
   498          if isinstance(tp, model.BasePrimitiveType):
   499              if tp.is_integer_type() and tp.name != '_Bool':
   500                  converter = '_cffi_to_c_int'
   501                  extraarg = ', %s' % tp.name
   502              elif isinstance(tp, model.UnknownFloatType):
   503                  # don't check with is_float_type(): it may be a 'long
   504                  # double' here, and _cffi_to_c_double would loose precision
   505                  converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
   506              else:
   507                  converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
   508                                                     tp.name.replace(' ', '_'))
   509              errvalue = '-1'
   510          #
   511          elif isinstance(tp, model.PointerType):
   512              self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
   513                                                      tovar, errcode)
   514              return
   515          #
   516          elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
   517              # a struct (not a struct pointer) as a function argument
   518              self._prnt('  if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
   519                        % (tovar, self._gettypenum(tp), fromvar))
   520              self._prnt('    %s;' % errcode)
   521              return
   522          #
   523          elif isinstance(tp, model.FunctionPtrType):
   524              converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
   525              extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
   526              errvalue = 'NULL'
   527          #
   528          else:
   529              raise NotImplementedError(tp)
   530          #
   531          self._prnt('  %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
   532          self._prnt('  if (%s == (%s)%s && PyErr_Occurred())' % (
   533              tovar, tp.get_c_name(''), errvalue))
   534          self._prnt('    %s;' % errcode)
   535  
   536      def _extra_local_variables(self, tp, localvars):
   537          if isinstance(tp, model.PointerType):
   538              localvars.add('Py_ssize_t datasize')
   539  
   540      def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
   541          self._prnt('  datasize = _cffi_prepare_pointer_call_argument(')
   542          self._prnt('      _cffi_type(%d), %s, (char **)&%s);' % (
   543              self._gettypenum(tp), fromvar, tovar))
   544          self._prnt('  if (datasize != 0) {')
   545          self._prnt('    if (datasize < 0)')
   546          self._prnt('      %s;' % errcode)
   547          self._prnt('    %s = (%s)alloca((size_t)datasize);' % (
   548              tovar, tp.get_c_name('')))
   549          self._prnt('    memset((void *)%s, 0, (size_t)datasize);' % (tovar,))
   550          self._prnt('    if (_cffi_convert_array_from_object('
   551                     '(char *)%s, _cffi_type(%d), %s) < 0)' % (
   552              tovar, self._gettypenum(tp), fromvar))
   553          self._prnt('      %s;' % errcode)
   554          self._prnt('  }')
   555  
   556      def _convert_expr_from_c(self, tp, var, context):
   557          if isinstance(tp, model.BasePrimitiveType):
   558              if tp.is_integer_type():
   559                  return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
   560              elif isinstance(tp, model.UnknownFloatType):
   561                  return '_cffi_from_c_double(%s)' % (var,)
   562              elif tp.name != 'long double':
   563                  return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
   564              else:
   565                  return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
   566                      var, self._gettypenum(tp))
   567          elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
   568              return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
   569                  var, self._gettypenum(tp))
   570          elif isinstance(tp, model.ArrayType):
   571              return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
   572                  var, self._gettypenum(model.PointerType(tp.item)))
   573          elif isinstance(tp, model.StructType):
   574              if tp.fldnames is None:
   575                  raise TypeError("'%s' is used as %s, but is opaque" % (
   576                      tp._get_c_name(), context))
   577              return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
   578                  var, self._gettypenum(tp))
   579          elif isinstance(tp, model.EnumType):
   580              return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
   581                  var, self._gettypenum(tp))
   582          else:
   583              raise NotImplementedError(tp)
   584  
   585      # ----------
   586      # typedefs
   587  
   588      def _generate_cpy_typedef_collecttype(self, tp, name):
   589          self._do_collect_type(tp)
   590  
   591      def _generate_cpy_typedef_decl(self, tp, name):
   592          pass
   593  
   594      def _typedef_ctx(self, tp, name):
   595          type_index = self._typesdict[tp]
   596          self._lsts["typename"].append(TypenameExpr(name, type_index))
   597  
   598      def _generate_cpy_typedef_ctx(self, tp, name):
   599          self._typedef_ctx(tp, name)
   600          if getattr(tp, "origin", None) == "unknown_type":
   601              self._struct_ctx(tp, tp.name, approxname=None)
   602          elif isinstance(tp, model.NamedPointerType):
   603              self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
   604                               named_ptr=tp)
   605  
   606      # ----------
   607      # function declarations
   608  
   609      def _generate_cpy_function_collecttype(self, tp, name):
   610          self._do_collect_type(tp.as_raw_function())
   611          if tp.ellipsis and not self.target_is_python:
   612              self._do_collect_type(tp)
   613  
   614      def _generate_cpy_function_decl(self, tp, name):
   615          assert not self.target_is_python
   616          assert isinstance(tp, model.FunctionPtrType)
   617          if tp.ellipsis:
   618              # cannot support vararg functions better than this: check for its
   619              # exact type (including the fixed arguments), and build it as a
   620              # constant function pointer (no CPython wrapper)
   621              self._generate_cpy_constant_decl(tp, name)
   622              return
   623          prnt = self._prnt
   624          numargs = len(tp.args)
   625          if numargs == 0:
   626              argname = 'noarg'
   627          elif numargs == 1:
   628              argname = 'arg0'
   629          else:
   630              argname = 'args'
   631          #
   632          # ------------------------------
   633          # the 'd' version of the function, only for addressof(lib, 'func')
   634          arguments = []
   635          call_arguments = []
   636          context = 'argument of %s' % name
   637          for i, type in enumerate(tp.args):
   638              arguments.append(type.get_c_name(' x%d' % i, context))
   639              call_arguments.append('x%d' % i)
   640          repr_arguments = ', '.join(arguments)
   641          repr_arguments = repr_arguments or 'void'
   642          if tp.abi:
   643              abi = tp.abi + ' '
   644          else:
   645              abi = ''
   646          name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
   647          prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
   648          prnt('{')
   649          call_arguments = ', '.join(call_arguments)
   650          result_code = 'return '
   651          if isinstance(tp.result, model.VoidType):
   652              result_code = ''
   653          prnt('  %s%s(%s);' % (result_code, name, call_arguments))
   654          prnt('}')
   655          #
   656          prnt('#ifndef PYPY_VERSION')        # ------------------------------
   657          #
   658          prnt('static PyObject *')
   659          prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
   660          prnt('{')
   661          #
   662          context = 'argument of %s' % name
   663          for i, type in enumerate(tp.args):
   664              arg = type.get_c_name(' x%d' % i, context)
   665              prnt('  %s;' % arg)
   666          #
   667          localvars = set()
   668          for type in tp.args:
   669              self._extra_local_variables(type, localvars)
   670          for decl in localvars:
   671              prnt('  %s;' % (decl,))
   672          #
   673          if not isinstance(tp.result, model.VoidType):
   674              result_code = 'result = '
   675              context = 'result of %s' % name
   676              result_decl = '  %s;' % tp.result.get_c_name(' result', context)
   677              prnt(result_decl)
   678          else:
   679              result_decl = None
   680              result_code = ''
   681          #
   682          if len(tp.args) > 1:
   683              rng = range(len(tp.args))
   684              for i in rng:
   685                  prnt('  PyObject *arg%d;' % i)
   686              prnt('  PyObject **aa;')
   687              prnt()
   688              prnt('  aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name))
   689              prnt('  if (aa == NULL)')
   690              prnt('    return NULL;')
   691              for i in rng:
   692                  prnt('  arg%d = aa[%d];' % (i, i))
   693          prnt()
   694          #
   695          for i, type in enumerate(tp.args):
   696              self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
   697                                         'return NULL')
   698              prnt()
   699          #
   700          prnt('  Py_BEGIN_ALLOW_THREADS')
   701          prnt('  _cffi_restore_errno();')
   702          call_arguments = ['x%d' % i for i in range(len(tp.args))]
   703          call_arguments = ', '.join(call_arguments)
   704          prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
   705          prnt('  _cffi_save_errno();')
   706          prnt('  Py_END_ALLOW_THREADS')
   707          prnt()
   708          #
   709          prnt('  (void)self; /* unused */')
   710          if numargs == 0:
   711              prnt('  (void)noarg; /* unused */')
   712          if result_code:
   713              prnt('  return %s;' %
   714                   self._convert_expr_from_c(tp.result, 'result', 'result type'))
   715          else:
   716              prnt('  Py_INCREF(Py_None);')
   717              prnt('  return Py_None;')
   718          prnt('}')
   719          #
   720          prnt('#else')        # ------------------------------
   721          #
   722          # the PyPy version: need to replace struct/union arguments with
   723          # pointers, and if the result is a struct/union, insert a first
   724          # arg that is a pointer to the result.
   725          difference = False
   726          arguments = []
   727          call_arguments = []
   728          context = 'argument of %s' % name
   729          for i, type in enumerate(tp.args):
   730              indirection = ''
   731              if isinstance(type, model.StructOrUnion):
   732                  indirection = '*'
   733                  difference = True
   734              arg = type.get_c_name(' %sx%d' % (indirection, i), context)
   735              arguments.append(arg)
   736              call_arguments.append('%sx%d' % (indirection, i))
   737          tp_result = tp.result
   738          if isinstance(tp_result, model.StructOrUnion):
   739              context = 'result of %s' % name
   740              arg = tp_result.get_c_name(' *result', context)
   741              arguments.insert(0, arg)
   742              tp_result = model.void_type
   743              result_decl = None
   744              result_code = '*result = '
   745              difference = True
   746          if difference:
   747              repr_arguments = ', '.join(arguments)
   748              repr_arguments = repr_arguments or 'void'
   749              name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
   750                                                         repr_arguments)
   751              prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
   752              prnt('{')
   753              if result_decl:
   754                  prnt(result_decl)
   755              call_arguments = ', '.join(call_arguments)
   756              prnt('  { %s%s(%s); }' % (result_code, name, call_arguments))
   757              if result_decl:
   758                  prnt('  return result;')
   759              prnt('}')
   760          else:
   761              prnt('#  define _cffi_f_%s _cffi_d_%s' % (name, name))
   762          #
   763          prnt('#endif')        # ------------------------------
   764          prnt()
   765  
   766      def _generate_cpy_function_ctx(self, tp, name):
   767          if tp.ellipsis and not self.target_is_python:
   768              self._generate_cpy_constant_ctx(tp, name)
   769              return
   770          type_index = self._typesdict[tp.as_raw_function()]
   771          numargs = len(tp.args)
   772          if self.target_is_python:
   773              meth_kind = OP_DLOPEN_FUNC
   774          elif numargs == 0:
   775              meth_kind = OP_CPYTHON_BLTN_N   # 'METH_NOARGS'
   776          elif numargs == 1:
   777              meth_kind = OP_CPYTHON_BLTN_O   # 'METH_O'
   778          else:
   779              meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
   780          self._lsts["global"].append(
   781              GlobalExpr(name, '_cffi_f_%s' % name,
   782                         CffiOp(meth_kind, type_index),
   783                         size='_cffi_d_%s' % name))
   784  
   785      # ----------
   786      # named structs or unions
   787  
   788      def _field_type(self, tp_struct, field_name, tp_field):
   789          if isinstance(tp_field, model.ArrayType):
   790              actual_length = tp_field.length
   791              if actual_length == '...':
   792                  ptr_struct_name = tp_struct.get_c_name('*')
   793                  actual_length = '_cffi_array_len(((%s)0)->%s)' % (
   794                      ptr_struct_name, field_name)
   795              tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
   796                                         tp_field.item)
   797              tp_field = model.ArrayType(tp_item, actual_length)
   798          return tp_field
   799  
   800      def _struct_collecttype(self, tp):
   801          self._do_collect_type(tp)
   802  
   803      def _struct_decl(self, tp, cname, approxname):
   804          if tp.fldtypes is None:
   805              return
   806          prnt = self._prnt
   807          checkfuncname = '_cffi_checkfld_%s' % (approxname,)
   808          prnt('_CFFI_UNUSED_FN')
   809          prnt('static void %s(%s *p)' % (checkfuncname, cname))
   810          prnt('{')
   811          prnt('  /* only to generate compile-time warnings or errors */')
   812          prnt('  (void)p;')
   813          for fname, ftype, fbitsize, fqual in tp.enumfields():
   814              try:
   815                  if ftype.is_integer_type() or fbitsize >= 0:
   816                      # accept all integers, but complain on float or double
   817                      prnt("  (void)((p->%s) | 0);  /* check that '%s.%s' is "
   818                           "an integer */" % (fname, cname, fname))
   819                      continue
   820                  # only accept exactly the type declared, except that '[]'
   821                  # is interpreted as a '*' and so will match any array length.
   822                  # (It would also match '*', but that's harder to detect...)
   823                  while (isinstance(ftype, model.ArrayType)
   824                         and (ftype.length is None or ftype.length == '...')):
   825                      ftype = ftype.item
   826                      fname = fname + '[0]'
   827                  prnt('  { %s = &p->%s; (void)tmp; }' % (
   828                      ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
   829                      fname))
   830              except ffiplatform.VerificationError as e:
   831                  prnt('  /* %s */' % str(e))   # cannot verify it, ignore
   832          prnt('}')
   833          prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
   834          prnt()
   835  
   836      def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
   837          type_index = self._typesdict[tp]
   838          reason_for_not_expanding = None
   839          flags = []
   840          if isinstance(tp, model.UnionType):
   841              flags.append("_CFFI_F_UNION")
   842          if tp.fldtypes is None:
   843              flags.append("_CFFI_F_OPAQUE")
   844              reason_for_not_expanding = "opaque"
   845          if (tp not in self.ffi._parser._included_declarations and
   846                  (named_ptr is None or
   847                   named_ptr not in self.ffi._parser._included_declarations)):
   848              if tp.fldtypes is None:
   849                  pass    # opaque
   850              elif tp.partial or tp.has_anonymous_struct_fields():
   851                  pass    # field layout obtained silently from the C compiler
   852              else:
   853                  flags.append("_CFFI_F_CHECK_FIELDS")
   854              if tp.packed:
   855                  flags.append("_CFFI_F_PACKED")
   856          else:
   857              flags.append("_CFFI_F_EXTERNAL")
   858              reason_for_not_expanding = "external"
   859          flags = '|'.join(flags) or '0'
   860          c_fields = []
   861          if reason_for_not_expanding is None:
   862              enumfields = list(tp.enumfields())
   863              for fldname, fldtype, fbitsize, fqual in enumfields:
   864                  fldtype = self._field_type(tp, fldname, fldtype)
   865                  # cname is None for _add_missing_struct_unions() only
   866                  op = OP_NOOP
   867                  if fbitsize >= 0:
   868                      op = OP_BITFIELD
   869                      size = '%d /* bits */' % fbitsize
   870                  elif cname is None or (
   871                          isinstance(fldtype, model.ArrayType) and
   872                          fldtype.length is None):
   873                      size = '(size_t)-1'
   874                  else:
   875                      size = 'sizeof(((%s)0)->%s)' % (
   876                          tp.get_c_name('*') if named_ptr is None
   877                                             else named_ptr.name,
   878                          fldname)
   879                  if cname is None or fbitsize >= 0:
   880                      offset = '(size_t)-1'
   881                  elif named_ptr is not None:
   882                      offset = '((char *)&((%s)0)->%s) - (char *)0' % (
   883                          named_ptr.name, fldname)
   884                  else:
   885                      offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
   886                  c_fields.append(
   887                      FieldExpr(fldname, offset, size, fbitsize,
   888                                CffiOp(op, self._typesdict[fldtype])))
   889              first_field_index = len(self._lsts["field"])
   890              self._lsts["field"].extend(c_fields)
   891              #
   892              if cname is None:  # unknown name, for _add_missing_struct_unions
   893                  size = '(size_t)-2'
   894                  align = -2
   895                  comment = "unnamed"
   896              else:
   897                  if named_ptr is not None:
   898                      size = 'sizeof(*(%s)0)' % (named_ptr.name,)
   899                      align = '-1 /* unknown alignment */'
   900                  else:
   901                      size = 'sizeof(%s)' % (cname,)
   902                      align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
   903                  comment = None
   904          else:
   905              size = '(size_t)-1'
   906              align = -1
   907              first_field_index = -1
   908              comment = reason_for_not_expanding
   909          self._lsts["struct_union"].append(
   910              StructUnionExpr(tp.name, type_index, flags, size, align, comment,
   911                              first_field_index, c_fields))
   912          self._seen_struct_unions.add(tp)
   913  
   914      def _add_missing_struct_unions(self):
   915          # not very nice, but some struct declarations might be missing
   916          # because they don't have any known C name.  Check that they are
   917          # not partial (we can't complete or verify them!) and emit them
   918          # anonymously.
   919          lst = list(self._struct_unions.items())
   920          lst.sort(key=lambda tp_order: tp_order[1])
   921          for tp, order in lst:
   922              if tp not in self._seen_struct_unions:
   923                  if tp.partial:
   924                      raise NotImplementedError("internal inconsistency: %r is "
   925                                                "partial but was not seen at "
   926                                                "this point" % (tp,))
   927                  if tp.name.startswith('$') and tp.name[1:].isdigit():
   928                      approxname = tp.name[1:]
   929                  elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
   930                      approxname = 'FILE'
   931                      self._typedef_ctx(tp, 'FILE')
   932                  else:
   933                      raise NotImplementedError("internal inconsistency: %r" %
   934                                                (tp,))
   935                  self._struct_ctx(tp, None, approxname)
   936  
   937      def _generate_cpy_struct_collecttype(self, tp, name):
   938          self._struct_collecttype(tp)
   939      _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
   940  
   941      def _struct_names(self, tp):
   942          cname = tp.get_c_name('')
   943          if ' ' in cname:
   944              return cname, cname.replace(' ', '_')
   945          else:
   946              return cname, '_' + cname
   947  
   948      def _generate_cpy_struct_decl(self, tp, name):
   949          self._struct_decl(tp, *self._struct_names(tp))
   950      _generate_cpy_union_decl = _generate_cpy_struct_decl
   951  
   952      def _generate_cpy_struct_ctx(self, tp, name):
   953          self._struct_ctx(tp, *self._struct_names(tp))
   954      _generate_cpy_union_ctx = _generate_cpy_struct_ctx
   955  
   956      # ----------
   957      # 'anonymous' declarations.  These are produced for anonymous structs
   958      # or unions; the 'name' is obtained by a typedef.
   959  
   960      def _generate_cpy_anonymous_collecttype(self, tp, name):
   961          if isinstance(tp, model.EnumType):
   962              self._generate_cpy_enum_collecttype(tp, name)
   963          else:
   964              self._struct_collecttype(tp)
   965  
   966      def _generate_cpy_anonymous_decl(self, tp, name):
   967          if isinstance(tp, model.EnumType):
   968              self._generate_cpy_enum_decl(tp)
   969          else:
   970              self._struct_decl(tp, name, 'typedef_' + name)
   971  
   972      def _generate_cpy_anonymous_ctx(self, tp, name):
   973          if isinstance(tp, model.EnumType):
   974              self._enum_ctx(tp, name)
   975          else:
   976              self._struct_ctx(tp, name, 'typedef_' + name)
   977  
   978      # ----------
   979      # constants, declared with "static const ..."
   980  
   981      def _generate_cpy_const(self, is_int, name, tp=None, category='const',
   982                              check_value=None):
   983          if (category, name) in self._seen_constants:
   984              raise ffiplatform.VerificationError(
   985                  "duplicate declaration of %s '%s'" % (category, name))
   986          self._seen_constants.add((category, name))
   987          #
   988          prnt = self._prnt
   989          funcname = '_cffi_%s_%s' % (category, name)
   990          if is_int:
   991              prnt('static int %s(unsigned long long *o)' % funcname)
   992              prnt('{')
   993              prnt('  int n = (%s) <= 0;' % (name,))
   994              prnt('  *o = (unsigned long long)((%s) | 0);'
   995                   '  /* check that %s is an integer */' % (name, name))
   996              if check_value is not None:
   997                  if check_value > 0:
   998                      check_value = '%dU' % (check_value,)
   999                  prnt('  if (!_cffi_check_int(*o, n, %s))' % (check_value,))
  1000                  prnt('    n |= 2;')
  1001              prnt('  return n;')
  1002              prnt('}')
  1003          else:
  1004              assert check_value is None
  1005              prnt('static void %s(char *o)' % funcname)
  1006              prnt('{')
  1007              prnt('  *(%s)o = %s;' % (tp.get_c_name('*'), name))
  1008              prnt('}')
  1009          prnt()
  1010  
  1011      def _generate_cpy_constant_collecttype(self, tp, name):
  1012          is_int = tp.is_integer_type()
  1013          if not is_int or self.target_is_python:
  1014              self._do_collect_type(tp)
  1015  
  1016      def _generate_cpy_constant_decl(self, tp, name):
  1017          is_int = tp.is_integer_type()
  1018          self._generate_cpy_const(is_int, name, tp)
  1019  
  1020      def _generate_cpy_constant_ctx(self, tp, name):
  1021          if not self.target_is_python and tp.is_integer_type():
  1022              type_op = CffiOp(OP_CONSTANT_INT, -1)
  1023          else:
  1024              if self.target_is_python:
  1025                  const_kind = OP_DLOPEN_CONST
  1026              else:
  1027                  const_kind = OP_CONSTANT
  1028              type_index = self._typesdict[tp]
  1029              type_op = CffiOp(const_kind, type_index)
  1030          self._lsts["global"].append(
  1031              GlobalExpr(name, '_cffi_const_%s' % name, type_op))
  1032  
  1033      # ----------
  1034      # enums
  1035  
  1036      def _generate_cpy_enum_collecttype(self, tp, name):
  1037          self._do_collect_type(tp)
  1038  
  1039      def _generate_cpy_enum_decl(self, tp, name=None):
  1040          for enumerator in tp.enumerators:
  1041              self._generate_cpy_const(True, enumerator)
  1042  
  1043      def _enum_ctx(self, tp, cname):
  1044          type_index = self._typesdict[tp]
  1045          type_op = CffiOp(OP_ENUM, -1)
  1046          if self.target_is_python:
  1047              tp.check_not_partial()
  1048          for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
  1049              self._lsts["global"].append(
  1050                  GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
  1051                             check_value=enumvalue))
  1052          #
  1053          if cname is not None and '$' not in cname and not self.target_is_python:
  1054              size = "sizeof(%s)" % cname
  1055              signed = "((%s)-1) <= 0" % cname
  1056          else:
  1057              basetp = tp.build_baseinttype(self.ffi, [])
  1058              size = self.ffi.sizeof(basetp)
  1059              signed = int(int(self.ffi.cast(basetp, -1)) < 0)
  1060          allenums = ",".join(tp.enumerators)
  1061          self._lsts["enum"].append(
  1062              EnumExpr(tp.name, type_index, size, signed, allenums))
  1063  
  1064      def _generate_cpy_enum_ctx(self, tp, name):
  1065          self._enum_ctx(tp, tp._get_c_name())
  1066  
  1067      # ----------
  1068      # macros: for now only for integers
  1069  
  1070      def _generate_cpy_macro_collecttype(self, tp, name):
  1071          pass
  1072  
  1073      def _generate_cpy_macro_decl(self, tp, name):
  1074          if tp == '...':
  1075              check_value = None
  1076          else:
  1077              check_value = tp     # an integer
  1078          self._generate_cpy_const(True, name, check_value=check_value)
  1079  
  1080      def _generate_cpy_macro_ctx(self, tp, name):
  1081          if tp == '...':
  1082              if self.target_is_python:
  1083                  raise ffiplatform.VerificationError(
  1084                      "cannot use the syntax '...' in '#define %s ...' when "
  1085                      "using the ABI mode" % (name,))
  1086              check_value = None
  1087          else:
  1088              check_value = tp     # an integer
  1089          type_op = CffiOp(OP_CONSTANT_INT, -1)
  1090          self._lsts["global"].append(
  1091              GlobalExpr(name, '_cffi_const_%s' % name, type_op,
  1092                         check_value=check_value))
  1093  
  1094      # ----------
  1095      # global variables
  1096  
  1097      def _global_type(self, tp, global_name):
  1098          if isinstance(tp, model.ArrayType):
  1099              actual_length = tp.length
  1100              if actual_length == '...':
  1101                  actual_length = '_cffi_array_len(%s)' % (global_name,)
  1102              tp_item = self._global_type(tp.item, '%s[0]' % global_name)
  1103              tp = model.ArrayType(tp_item, actual_length)
  1104          return tp
  1105  
  1106      def _generate_cpy_variable_collecttype(self, tp, name):
  1107          self._do_collect_type(self._global_type(tp, name))
  1108  
  1109      def _generate_cpy_variable_decl(self, tp, name):
  1110          prnt = self._prnt
  1111          tp = self._global_type(tp, name)
  1112          if isinstance(tp, model.ArrayType) and tp.length is None:
  1113              tp = tp.item
  1114              ampersand = ''
  1115          else:
  1116              ampersand = '&'
  1117          # This code assumes that casts from "tp *" to "void *" is a
  1118          # no-op, i.e. a function that returns a "tp *" can be called
  1119          # as if it returned a "void *".  This should be generally true
  1120          # on any modern machine.  The only exception to that rule (on
  1121          # uncommon architectures, and as far as I can tell) might be
  1122          # if 'tp' were a function type, but that is not possible here.
  1123          # (If 'tp' is a function _pointer_ type, then casts from "fn_t
  1124          # **" to "void *" are again no-ops, as far as I can tell.)
  1125          decl = '*_cffi_var_%s(void)' % (name,)
  1126          prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
  1127          prnt('{')
  1128          prnt('  return %s(%s);' % (ampersand, name))
  1129          prnt('}')
  1130          prnt()
  1131  
  1132      def _generate_cpy_variable_ctx(self, tp, name):
  1133          tp = self._global_type(tp, name)
  1134          type_index = self._typesdict[tp]
  1135          if self.target_is_python:
  1136              op = OP_GLOBAL_VAR
  1137          else:
  1138              op = OP_GLOBAL_VAR_F
  1139          self._lsts["global"].append(
  1140              GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))
  1141  
  1142      # ----------
  1143      # extern "Python"
  1144  
  1145      def _generate_cpy_extern_python_collecttype(self, tp, name):
  1146          assert isinstance(tp, model.FunctionPtrType)
  1147          self._do_collect_type(tp)
  1148      _generate_cpy_dllexport_python_collecttype = \
  1149        _generate_cpy_extern_python_plus_c_collecttype = \
  1150        _generate_cpy_extern_python_collecttype
  1151  
  1152      def _extern_python_decl(self, tp, name, tag_and_space):
  1153          prnt = self._prnt
  1154          if isinstance(tp.result, model.VoidType):
  1155              size_of_result = '0'
  1156          else:
  1157              context = 'result of %s' % name
  1158              size_of_result = '(int)sizeof(%s)' % (
  1159                  tp.result.get_c_name('', context),)
  1160          prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
  1161          prnt('  { "%s", %s };' % (name, size_of_result))
  1162          prnt()
  1163          #
  1164          arguments = []
  1165          context = 'argument of %s' % name
  1166          for i, type in enumerate(tp.args):
  1167              arg = type.get_c_name(' a%d' % i, context)
  1168              arguments.append(arg)
  1169          #
  1170          repr_arguments = ', '.join(arguments)
  1171          repr_arguments = repr_arguments or 'void'
  1172          name_and_arguments = '%s(%s)' % (name, repr_arguments)
  1173          if tp.abi == "__stdcall":
  1174              name_and_arguments = '_cffi_stdcall ' + name_and_arguments
  1175          #
  1176          def may_need_128_bits(tp):
  1177              return (isinstance(tp, model.PrimitiveType) and
  1178                      tp.name == 'long double')
  1179          #
  1180          size_of_a = max(len(tp.args)*8, 8)
  1181          if may_need_128_bits(tp.result):
  1182              size_of_a = max(size_of_a, 16)
  1183          if isinstance(tp.result, model.StructOrUnion):
  1184              size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
  1185                  tp.result.get_c_name(''), size_of_a,
  1186                  tp.result.get_c_name(''), size_of_a)
  1187          prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
  1188          prnt('{')
  1189          prnt('  char a[%s];' % size_of_a)
  1190          prnt('  char *p = a;')
  1191          for i, type in enumerate(tp.args):
  1192              arg = 'a%d' % i
  1193              if (isinstance(type, model.StructOrUnion) or
  1194                      may_need_128_bits(type)):
  1195                  arg = '&' + arg
  1196                  type = model.PointerType(type)
  1197              prnt('  *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
  1198          prnt('  _cffi_call_python(&_cffi_externpy__%s, p);' % name)
  1199          if not isinstance(tp.result, model.VoidType):
  1200              prnt('  return *(%s)p;' % (tp.result.get_c_name('*'),))
  1201          prnt('}')
  1202          prnt()
  1203          self._num_externpy += 1
  1204  
  1205      def _generate_cpy_extern_python_decl(self, tp, name):
  1206          self._extern_python_decl(tp, name, 'static ')
  1207  
  1208      def _generate_cpy_dllexport_python_decl(self, tp, name):
  1209          self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
  1210  
  1211      def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
  1212          self._extern_python_decl(tp, name, '')
  1213  
  1214      def _generate_cpy_extern_python_ctx(self, tp, name):
  1215          if self.target_is_python:
  1216              raise ffiplatform.VerificationError(
  1217                  "cannot use 'extern \"Python\"' in the ABI mode")
  1218          if tp.ellipsis:
  1219              raise NotImplementedError("a vararg function is extern \"Python\"")
  1220          type_index = self._typesdict[tp]
  1221          type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
  1222          self._lsts["global"].append(
  1223              GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
  1224  
  1225      _generate_cpy_dllexport_python_ctx = \
  1226        _generate_cpy_extern_python_plus_c_ctx = \
  1227        _generate_cpy_extern_python_ctx
  1228  
  1229      def _string_literal(self, s):
  1230          def _char_repr(c):
  1231              # escape with a '\' the characters '\', '"' or (for trigraphs) '?'
  1232              if c in '\\"?': return '\\' + c
  1233              if ' ' <= c < '\x7F': return c
  1234              if c == '\n': return '\\n'
  1235              return '\\%03o' % ord(c)
  1236          lines = []
  1237          for line in s.splitlines(True) or ['']:
  1238              lines.append('"%s"' % ''.join([_char_repr(c) for c in line]))
  1239          return ' \\\n'.join(lines)
  1240  
  1241      # ----------
  1242      # emitting the opcodes for individual types
  1243  
  1244      def _emit_bytecode_VoidType(self, tp, index):
  1245          self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)
  1246  
  1247      def _emit_bytecode_PrimitiveType(self, tp, index):
  1248          prim_index = PRIMITIVE_TO_INDEX[tp.name]
  1249          self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
  1250  
  1251      def _emit_bytecode_UnknownIntegerType(self, tp, index):
  1252          s = ('_cffi_prim_int(sizeof(%s), (\n'
  1253               '           ((%s)-1) | 0 /* check that %s is an integer type */\n'
  1254               '         ) <= 0)' % (tp.name, tp.name, tp.name))
  1255          self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
  1256  
  1257      def _emit_bytecode_UnknownFloatType(self, tp, index):
  1258          s = ('_cffi_prim_float(sizeof(%s) *\n'
  1259               '           (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
  1260               '         )' % (tp.name, tp.name))
  1261          self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
  1262  
  1263      def _emit_bytecode_RawFunctionType(self, tp, index):
  1264          self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
  1265          index += 1
  1266          for tp1 in tp.args:
  1267              realindex = self._typesdict[tp1]
  1268              if index != realindex:
  1269                  if isinstance(tp1, model.PrimitiveType):
  1270                      self._emit_bytecode_PrimitiveType(tp1, index)
  1271                  else:
  1272                      self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
  1273              index += 1
  1274          flags = int(tp.ellipsis)
  1275          if tp.abi is not None:
  1276              if tp.abi == '__stdcall':
  1277                  flags |= 2
  1278              else:
  1279                  raise NotImplementedError("abi=%r" % (tp.abi,))
  1280          self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
  1281  
  1282      def _emit_bytecode_PointerType(self, tp, index):
  1283          self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
  1284  
  1285      _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
  1286      _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType
  1287  
  1288      def _emit_bytecode_FunctionPtrType(self, tp, index):
  1289          raw = tp.as_raw_function()
  1290          self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])
  1291  
  1292      def _emit_bytecode_ArrayType(self, tp, index):
  1293          item_index = self._typesdict[tp.item]
  1294          if tp.length is None:
  1295              self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
  1296          elif tp.length == '...':
  1297              raise ffiplatform.VerificationError(
  1298                  "type %s badly placed: the '...' array length can only be "
  1299                  "used on global arrays or on fields of structures" % (
  1300                      str(tp).replace('/*...*/', '...'),))
  1301          else:
  1302              assert self.cffi_types[index + 1] == 'LEN'
  1303              self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
  1304              self.cffi_types[index + 1] = CffiOp(None, str(tp.length))
  1305  
  1306      def _emit_bytecode_StructType(self, tp, index):
  1307          struct_index = self._struct_unions[tp]
  1308          self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
  1309      _emit_bytecode_UnionType = _emit_bytecode_StructType
  1310  
  1311      def _emit_bytecode_EnumType(self, tp, index):
  1312          enum_index = self._enums[tp]
  1313          self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)
  1314  
  1315  
  1316  if sys.version_info >= (3,):
  1317      NativeIO = io.StringIO
  1318  else:
  1319      class NativeIO(io.BytesIO):
  1320          def write(self, s):
  1321              if isinstance(s, unicode):
  1322                  s = s.encode('ascii')
  1323              super(NativeIO, self).write(s)
  1324  
  1325  def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
  1326      if verbose:
  1327          print("generating %s" % (target_file,))
  1328      recompiler = Recompiler(ffi, module_name,
  1329                              target_is_python=(preamble is None))
  1330      recompiler.collect_type_table()
  1331      recompiler.collect_step_tables()
  1332      f = NativeIO()
  1333      recompiler.write_source_to_f(f, preamble)
  1334      output = f.getvalue()
  1335      try:
  1336          with open(target_file, 'r') as f1:
  1337              if f1.read(len(output) + 1) != output:
  1338                  raise IOError
  1339          if verbose:
  1340              print("(already up-to-date)")
  1341          return False     # already up-to-date
  1342      except IOError:
  1343          tmp_file = '%s.~%d' % (target_file, os.getpid())
  1344          with open(tmp_file, 'w') as f1:
  1345              f1.write(output)
  1346          try:
  1347              os.rename(tmp_file, target_file)
  1348          except OSError:
  1349              os.unlink(target_file)
  1350              os.rename(tmp_file, target_file)
  1351          return True
  1352  
  1353  def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
  1354      assert preamble is not None
  1355      return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
  1356                                  verbose)
  1357  
  1358  def make_py_source(ffi, module_name, target_py_file, verbose=False):
  1359      return _make_c_or_py_source(ffi, module_name, None, target_py_file,
  1360                                  verbose)
  1361  
  1362  def _modname_to_file(outputdir, modname, extension):
  1363      parts = modname.split('.')
  1364      try:
  1365          os.makedirs(os.path.join(outputdir, *parts[:-1]))
  1366      except OSError:
  1367          pass
  1368      parts[-1] += extension
  1369      return os.path.join(outputdir, *parts), parts
  1370  
  1371  
  1372  # Aaargh.  Distutils is not tested at all for the purpose of compiling
  1373  # DLLs that are not extension modules.  Here are some hacks to work
  1374  # around that, in the _patch_for_*() functions...
  1375  
  1376  def _patch_meth(patchlist, cls, name, new_meth):
  1377      old = getattr(cls, name)
  1378      patchlist.append((cls, name, old))
  1379      setattr(cls, name, new_meth)
  1380      return old
  1381  
  1382  def _unpatch_meths(patchlist):
  1383      for cls, name, old_meth in reversed(patchlist):
  1384          setattr(cls, name, old_meth)
  1385  
  1386  def _patch_for_embedding(patchlist):
  1387      if sys.platform == 'win32':
  1388          # we must not remove the manifest when building for embedding!
  1389          from distutils.msvc9compiler import MSVCCompiler
  1390          _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
  1391                      lambda self, manifest_file: manifest_file)
  1392  
  1393      if sys.platform == 'darwin':
  1394          # we must not make a '-bundle', but a '-dynamiclib' instead
  1395          from distutils.ccompiler import CCompiler
  1396          def my_link_shared_object(self, *args, **kwds):
  1397              if '-bundle' in self.linker_so:
  1398                  self.linker_so = list(self.linker_so)
  1399                  i = self.linker_so.index('-bundle')
  1400                  self.linker_so[i] = '-dynamiclib'
  1401              return old_link_shared_object(self, *args, **kwds)
  1402          old_link_shared_object = _patch_meth(patchlist, CCompiler,
  1403                                               'link_shared_object',
  1404                                               my_link_shared_object)
  1405  
  1406  def _patch_for_target(patchlist, target):
  1407      from distutils.command.build_ext import build_ext
  1408      # if 'target' is different from '*', we need to patch some internal
  1409      # method to just return this 'target' value, instead of having it
  1410      # built from module_name
  1411      if target.endswith('.*'):
  1412          target = target[:-2]
  1413          if sys.platform == 'win32':
  1414              target += '.dll'
  1415          elif sys.platform == 'darwin':
  1416              target += '.dylib'
  1417          else:
  1418              target += '.so'
  1419      _patch_meth(patchlist, build_ext, 'get_ext_filename',
  1420                  lambda self, ext_name: target)
  1421  
  1422  
  1423  def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
  1424                c_file=None, source_extension='.c', extradir=None,
  1425                compiler_verbose=1, target=None, **kwds):
  1426      if not isinstance(module_name, str):
  1427          module_name = module_name.encode('ascii')
  1428      if ffi._windows_unicode:
  1429          ffi._apply_windows_unicode(kwds)
  1430      if preamble is not None:
  1431          embedding = (ffi._embedding is not None)
  1432          if embedding:
  1433              ffi._apply_embedding_fix(kwds)
  1434          if c_file is None:
  1435              c_file, parts = _modname_to_file(tmpdir, module_name,
  1436                                               source_extension)
  1437              if extradir:
  1438                  parts = [extradir] + parts
  1439              ext_c_file = os.path.join(*parts)
  1440          else:
  1441              ext_c_file = c_file
  1442          #
  1443          if target is None:
  1444              if embedding:
  1445                  target = '%s.*' % module_name
  1446              else:
  1447                  target = '*'
  1448          #
  1449          ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
  1450          updated = make_c_source(ffi, module_name, preamble, c_file,
  1451                                  verbose=compiler_verbose)
  1452          if call_c_compiler:
  1453              patchlist = []
  1454              cwd = os.getcwd()
  1455              try:
  1456                  if embedding:
  1457                      _patch_for_embedding(patchlist)
  1458                  if target != '*':
  1459                      _patch_for_target(patchlist, target)
  1460                  os.chdir(tmpdir)
  1461                  outputfilename = ffiplatform.compile('.', ext, compiler_verbose)
  1462              finally:
  1463                  os.chdir(cwd)
  1464                  _unpatch_meths(patchlist)
  1465              return outputfilename
  1466          else:
  1467              return ext, updated
  1468      else:
  1469          if c_file is None:
  1470              c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
  1471          updated = make_py_source(ffi, module_name, c_file,
  1472                                   verbose=compiler_verbose)
  1473          if call_c_compiler:
  1474              return c_file
  1475          else:
  1476              return None, updated
  1477  
  1478  def _verify(ffi, module_name, preamble, *args, **kwds):
  1479      # FOR TESTS ONLY
  1480      from testing.udir import udir
  1481      import imp
  1482      assert module_name not in sys.modules, "module name conflict: %r" % (
  1483          module_name,)
  1484      kwds.setdefault('tmpdir', str(udir))
  1485      outputfilename = recompile(ffi, module_name, preamble, *args, **kwds)
  1486      module = imp.load_dynamic(module_name, outputfilename)
  1487      #
  1488      # hack hack hack: copy all *bound methods* from module.ffi back to the
  1489      # ffi instance.  Then calls like ffi.new() will invoke module.ffi.new().
  1490      for name in dir(module.ffi):
  1491          if not name.startswith('_'):
  1492              attr = getattr(module.ffi, name)
  1493              if attr is not getattr(ffi, name, object()):
  1494                  setattr(ffi, name, attr)
  1495      def typeof_disabled(*args, **kwds):
  1496          raise NotImplementedError
  1497      ffi._typeof = typeof_disabled
  1498      for name in dir(ffi):
  1499          if not name.startswith('_') and not hasattr(module.ffi, name):
  1500              setattr(ffi, name, NotImplemented)
  1501      return module.lib