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