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