123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581 |
- import os, sys, io
- from . import ffiplatform, model
- from .error import VerificationError
- from .cffi_opcode import *
- VERSION_BASE = 0x2601
- VERSION_EMBEDDED = 0x2701
- VERSION_CHAR16CHAR32 = 0x2801
- USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
- sys.version_info >= (3, 5))
- class GlobalExpr:
- def __init__(self, name, address, type_op, size=0, check_value=0):
- self.name = name
- self.address = address
- self.type_op = type_op
- self.size = size
- self.check_value = check_value
- def as_c_expr(self):
- return ' { "%s", (void *)%s, %s, (void *)%s },' % (
- self.name, self.address, self.type_op.as_c_expr(), self.size)
- def as_python_expr(self):
- return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
- self.check_value)
- class FieldExpr:
- def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
- self.name = name
- self.field_offset = field_offset
- self.field_size = field_size
- self.fbitsize = fbitsize
- self.field_type_op = field_type_op
- def as_c_expr(self):
- spaces = " " * len(self.name)
- return (' { "%s", %s,\n' % (self.name, self.field_offset) +
- ' %s %s,\n' % (spaces, self.field_size) +
- ' %s %s },' % (spaces, self.field_type_op.as_c_expr()))
- def as_python_expr(self):
- raise NotImplementedError
- def as_field_python_expr(self):
- if self.field_type_op.op == OP_NOOP:
- size_expr = ''
- elif self.field_type_op.op == OP_BITFIELD:
- size_expr = format_four_bytes(self.fbitsize)
- else:
- raise NotImplementedError
- return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
- size_expr,
- self.name)
- class StructUnionExpr:
- def __init__(self, name, type_index, flags, size, alignment, comment,
- first_field_index, c_fields):
- self.name = name
- self.type_index = type_index
- self.flags = flags
- self.size = size
- self.alignment = alignment
- self.comment = comment
- self.first_field_index = first_field_index
- self.c_fields = c_fields
- def as_c_expr(self):
- return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
- + '\n %s, %s, ' % (self.size, self.alignment)
- + '%d, %d ' % (self.first_field_index, len(self.c_fields))
- + ('/* %s */ ' % self.comment if self.comment else '')
- + '},')
- def as_python_expr(self):
- flags = eval(self.flags, G_FLAGS)
- fields_expr = [c_field.as_field_python_expr()
- for c_field in self.c_fields]
- return "(b'%s%s%s',%s)" % (
- format_four_bytes(self.type_index),
- format_four_bytes(flags),
- self.name,
- ','.join(fields_expr))
- class EnumExpr:
- def __init__(self, name, type_index, size, signed, allenums):
- self.name = name
- self.type_index = type_index
- self.size = size
- self.signed = signed
- self.allenums = allenums
- def as_c_expr(self):
- return (' { "%s", %d, _cffi_prim_int(%s, %s),\n'
- ' "%s" },' % (self.name, self.type_index,
- self.size, self.signed, self.allenums))
- def as_python_expr(self):
- prim_index = {
- (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8,
- (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16,
- (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32,
- (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64,
- }[self.size, self.signed]
- return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
- format_four_bytes(prim_index),
- self.name, self.allenums)
- class TypenameExpr:
- def __init__(self, name, type_index):
- self.name = name
- self.type_index = type_index
- def as_c_expr(self):
- return ' { "%s", %d },' % (self.name, self.type_index)
- def as_python_expr(self):
- return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
- # ____________________________________________________________
- class Recompiler:
- _num_externpy = 0
- def __init__(self, ffi, module_name, target_is_python=False):
- self.ffi = ffi
- self.module_name = module_name
- self.target_is_python = target_is_python
- self._version = VERSION_BASE
- def needs_version(self, ver):
- self._version = max(self._version, ver)
- def collect_type_table(self):
- self._typesdict = {}
- self._generate("collecttype")
- #
- all_decls = sorted(self._typesdict, key=str)
- #
- # prepare all FUNCTION bytecode sequences first
- self.cffi_types = []
- for tp in all_decls:
- if tp.is_raw_function:
- assert self._typesdict[tp] is None
- self._typesdict[tp] = len(self.cffi_types)
- self.cffi_types.append(tp) # placeholder
- for tp1 in tp.args:
- assert isinstance(tp1, (model.VoidType,
- model.BasePrimitiveType,
- model.PointerType,
- model.StructOrUnionOrEnum,
- model.FunctionPtrType))
- if self._typesdict[tp1] is None:
- self._typesdict[tp1] = len(self.cffi_types)
- self.cffi_types.append(tp1) # placeholder
- self.cffi_types.append('END') # placeholder
- #
- # prepare all OTHER bytecode sequences
- for tp in all_decls:
- if not tp.is_raw_function and self._typesdict[tp] is None:
- self._typesdict[tp] = len(self.cffi_types)
- self.cffi_types.append(tp) # placeholder
- if tp.is_array_type and tp.length is not None:
- self.cffi_types.append('LEN') # placeholder
- assert None not in self._typesdict.values()
- #
- # collect all structs and unions and enums
- self._struct_unions = {}
- self._enums = {}
- for tp in all_decls:
- if isinstance(tp, model.StructOrUnion):
- self._struct_unions[tp] = None
- elif isinstance(tp, model.EnumType):
- self._enums[tp] = None
- for i, tp in enumerate(sorted(self._struct_unions,
- key=lambda tp: tp.name)):
- self._struct_unions[tp] = i
- for i, tp in enumerate(sorted(self._enums,
- key=lambda tp: tp.name)):
- self._enums[tp] = i
- #
- # emit all bytecode sequences now
- for tp in all_decls:
- method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
- method(tp, self._typesdict[tp])
- #
- # consistency check
- for op in self.cffi_types:
- assert isinstance(op, CffiOp)
- self.cffi_types = tuple(self.cffi_types) # don't change any more
- def _enum_fields(self, tp):
- # When producing C, expand all anonymous struct/union fields.
- # That's necessary to have C code checking the offsets of the
- # individual fields contained in them. When producing Python,
- # don't do it and instead write it like it is, with the
- # corresponding fields having an empty name. Empty names are
- # recognized at runtime when we import the generated Python
- # file.
- expand_anonymous_struct_union = not self.target_is_python
- return tp.enumfields(expand_anonymous_struct_union)
- def _do_collect_type(self, tp):
- if not isinstance(tp, model.BaseTypeByIdentity):
- if isinstance(tp, tuple):
- for x in tp:
- self._do_collect_type(x)
- return
- if tp not in self._typesdict:
- self._typesdict[tp] = None
- if isinstance(tp, model.FunctionPtrType):
- self._do_collect_type(tp.as_raw_function())
- elif isinstance(tp, model.StructOrUnion):
- if tp.fldtypes is not None and (
- tp not in self.ffi._parser._included_declarations):
- for name1, tp1, _, _ in self._enum_fields(tp):
- self._do_collect_type(self._field_type(tp, name1, tp1))
- else:
- for _, x in tp._get_items():
- self._do_collect_type(x)
- def _generate(self, step_name):
- lst = self.ffi._parser._declarations.items()
- for name, (tp, quals) in sorted(lst):
- kind, realname = name.split(' ', 1)
- try:
- method = getattr(self, '_generate_cpy_%s_%s' % (kind,
- step_name))
- except AttributeError:
- raise VerificationError(
- "not implemented in recompile(): %r" % name)
- try:
- self._current_quals = quals
- method(tp, realname)
- except Exception as e:
- model.attach_exception_info(e, name)
- raise
- # ----------
- ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
- def collect_step_tables(self):
- # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
- self._lsts = {}
- for step_name in self.ALL_STEPS:
- self._lsts[step_name] = []
- self._seen_struct_unions = set()
- self._generate("ctx")
- self._add_missing_struct_unions()
- #
- for step_name in self.ALL_STEPS:
- lst = self._lsts[step_name]
- if step_name != "field":
- lst.sort(key=lambda entry: entry.name)
- self._lsts[step_name] = tuple(lst) # don't change any more
- #
- # check for a possible internal inconsistency: _cffi_struct_unions
- # should have been generated with exactly self._struct_unions
- lst = self._lsts["struct_union"]
- for tp, i in self._struct_unions.items():
- assert i < len(lst)
- assert lst[i].name == tp.name
- assert len(lst) == len(self._struct_unions)
- # same with enums
- lst = self._lsts["enum"]
- for tp, i in self._enums.items():
- assert i < len(lst)
- assert lst[i].name == tp.name
- assert len(lst) == len(self._enums)
- # ----------
- def _prnt(self, what=''):
- self._f.write(what + '\n')
- def write_source_to_f(self, f, preamble):
- if self.target_is_python:
- assert preamble is None
- self.write_py_source_to_f(f)
- else:
- assert preamble is not None
- self.write_c_source_to_f(f, preamble)
- def _rel_readlines(self, filename):
- g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
- lines = g.readlines()
- g.close()
- return lines
- def write_c_source_to_f(self, f, preamble):
- self._f = f
- prnt = self._prnt
- if self.ffi._embedding is not None:
- prnt('#define _CFFI_USE_EMBEDDING')
- if not USE_LIMITED_API:
- prnt('#define _CFFI_NO_LIMITED_API')
- #
- # first the '#include' (actually done by inlining the file's content)
- lines = self._rel_readlines('_cffi_include.h')
- i = lines.index('#include "parse_c_type.h"\n')
- lines[i:i+1] = self._rel_readlines('parse_c_type.h')
- prnt(''.join(lines))
- #
- # if we have ffi._embedding != None, we give it here as a macro
- # and include an extra file
- base_module_name = self.module_name.split('.')[-1]
- if self.ffi._embedding is not None:
- prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,))
- prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
- self._print_string_literal_in_array(self.ffi._embedding)
- prnt('0 };')
- prnt('#ifdef PYPY_VERSION')
- prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % (
- base_module_name,))
- prnt('#elif PY_MAJOR_VERSION >= 3')
- prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % (
- base_module_name,))
- prnt('#else')
- prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % (
- base_module_name,))
- prnt('#endif')
- lines = self._rel_readlines('_embedding.h')
- i = lines.index('#include "_cffi_errors.h"\n')
- lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
- prnt(''.join(lines))
- self.needs_version(VERSION_EMBEDDED)
- #
- # then paste the C source given by the user, verbatim.
- prnt('/************************************************************/')
- prnt()
- prnt(preamble)
- prnt()
- prnt('/************************************************************/')
- prnt()
- #
- # the declaration of '_cffi_types'
- prnt('static void *_cffi_types[] = {')
- typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
- for i, op in enumerate(self.cffi_types):
- comment = ''
- if i in typeindex2type:
- comment = ' // ' + typeindex2type[i]._get_c_name()
- prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
- if not self.cffi_types:
- prnt(' 0')
- prnt('};')
- prnt()
- #
- # call generate_cpy_xxx_decl(), for every xxx found from
- # ffi._parser._declarations. This generates all the functions.
- self._seen_constants = set()
- self._generate("decl")
- #
- # the declaration of '_cffi_globals' and '_cffi_typenames'
- nums = {}
- for step_name in self.ALL_STEPS:
- lst = self._lsts[step_name]
- nums[step_name] = len(lst)
- if nums[step_name] > 0:
- prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
- step_name, step_name))
- for entry in lst:
- prnt(entry.as_c_expr())
- prnt('};')
- prnt()
- #
- # the declaration of '_cffi_includes'
- if self.ffi._included_ffis:
- prnt('static const char * const _cffi_includes[] = {')
- for ffi_to_include in self.ffi._included_ffis:
- try:
- included_module_name, included_source = (
- ffi_to_include._assigned_source[:2])
- except AttributeError:
- raise VerificationError(
- "ffi object %r includes %r, but the latter has not "
- "been prepared with set_source()" % (
- self.ffi, ffi_to_include,))
- if included_source is None:
- raise VerificationError(
- "not implemented yet: ffi.include() of a Python-based "
- "ffi inside a C-based ffi")
- prnt(' "%s",' % (included_module_name,))
- prnt(' NULL')
- prnt('};')
- prnt()
- #
- # the declaration of '_cffi_type_context'
- prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
- prnt(' _cffi_types,')
- for step_name in self.ALL_STEPS:
- if nums[step_name] > 0:
- prnt(' _cffi_%ss,' % step_name)
- else:
- prnt(' NULL, /* no %ss */' % step_name)
- for step_name in self.ALL_STEPS:
- if step_name != "field":
- prnt(' %d, /* num_%ss */' % (nums[step_name], step_name))
- if self.ffi._included_ffis:
- prnt(' _cffi_includes,')
- else:
- prnt(' NULL, /* no includes */')
- prnt(' %d, /* num_types */' % (len(self.cffi_types),))
- flags = 0
- if self._num_externpy:
- flags |= 1 # set to mean that we use extern "Python"
- prnt(' %d, /* flags */' % flags)
- prnt('};')
- prnt()
- #
- # the init function
- prnt('#ifdef __GNUC__')
- prnt('# pragma GCC visibility push(default) /* for -fvisibility= */')
- prnt('#endif')
- prnt()
- prnt('#ifdef PYPY_VERSION')
- prnt('PyMODINIT_FUNC')
- prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
- prnt('{')
- if self._num_externpy:
- prnt(' if (((intptr_t)p[0]) >= 0x0A03) {')
- prnt(' _cffi_call_python_org = '
- '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
- prnt(' }')
- prnt(' p[0] = (const void *)0x%x;' % self._version)
- prnt(' p[1] = &_cffi_type_context;')
- prnt('#if PY_MAJOR_VERSION >= 3')
- prnt(' return NULL;')
- prnt('#endif')
- prnt('}')
- # on Windows, distutils insists on putting init_cffi_xyz in
- # 'export_symbols', so instead of fighting it, just give up and
- # give it one
- prnt('# ifdef _MSC_VER')
- prnt(' PyMODINIT_FUNC')
- prnt('# if PY_MAJOR_VERSION >= 3')
- prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,))
- prnt('# else')
- prnt(' init%s(void) { }' % (base_module_name,))
- prnt('# endif')
- prnt('# endif')
- prnt('#elif PY_MAJOR_VERSION >= 3')
- prnt('PyMODINIT_FUNC')
- prnt('PyInit_%s(void)' % (base_module_name,))
- prnt('{')
- prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
- self.module_name, self._version))
- prnt('}')
- prnt('#else')
- prnt('PyMODINIT_FUNC')
- prnt('init%s(void)' % (base_module_name,))
- prnt('{')
- prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
- self.module_name, self._version))
- prnt('}')
- prnt('#endif')
- prnt()
- prnt('#ifdef __GNUC__')
- prnt('# pragma GCC visibility pop')
- prnt('#endif')
- self._version = None
- def _to_py(self, x):
- if isinstance(x, str):
- return "b'%s'" % (x,)
- if isinstance(x, (list, tuple)):
- rep = [self._to_py(item) for item in x]
- if len(rep) == 1:
- rep.append('')
- return "(%s)" % (','.join(rep),)
- return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp.
- def write_py_source_to_f(self, f):
- self._f = f
- prnt = self._prnt
- #
- # header
- prnt("# auto-generated file")
- prnt("import _cffi_backend")
- #
- # the 'import' of the included ffis
- num_includes = len(self.ffi._included_ffis or ())
- for i in range(num_includes):
- ffi_to_include = self.ffi._included_ffis[i]
- try:
- included_module_name, included_source = (
- ffi_to_include._assigned_source[:2])
- except AttributeError:
- raise VerificationError(
- "ffi object %r includes %r, but the latter has not "
- "been prepared with set_source()" % (
- self.ffi, ffi_to_include,))
- if included_source is not None:
- raise VerificationError(
- "not implemented yet: ffi.include() of a C-based "
- "ffi inside a Python-based ffi")
- prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
- prnt()
- prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
- prnt(" _version = 0x%x," % (self._version,))
- self._version = None
- #
- # the '_types' keyword argument
- self.cffi_types = tuple(self.cffi_types) # don't change any more
- types_lst = [op.as_python_bytes() for op in self.cffi_types]
- prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),))
- typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
- #
- # the keyword arguments from ALL_STEPS
- for step_name in self.ALL_STEPS:
- lst = self._lsts[step_name]
- if len(lst) > 0 and step_name != "field":
- prnt(' _%ss = %s,' % (step_name, self._to_py(lst)))
- #
- # the '_includes' keyword argument
- if num_includes > 0:
- prnt(' _includes = (%s,),' % (
- ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
- #
- # the footer
- prnt(')')
- # ----------
- def _gettypenum(self, type):
- # a KeyError here is a bug. please report it! :-)
- return self._typesdict[type]
- def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
- extraarg = ''
- if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
- if tp.is_integer_type() and tp.name != '_Bool':
- converter = '_cffi_to_c_int'
- extraarg = ', %s' % tp.name
- elif isinstance(tp, model.UnknownFloatType):
- # don't check with is_float_type(): it may be a 'long
- # double' here, and _cffi_to_c_double would loose precision
- converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
- else:
- cname = tp.get_c_name('')
- converter = '(%s)_cffi_to_c_%s' % (cname,
- tp.name.replace(' ', '_'))
- if cname in ('char16_t', 'char32_t'):
- self.needs_version(VERSION_CHAR16CHAR32)
- errvalue = '-1'
- #
- elif isinstance(tp, model.PointerType):
- self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
- tovar, errcode)
- return
- #
- elif (isinstance(tp, model.StructOrUnionOrEnum) or
- isinstance(tp, model.BasePrimitiveType)):
- # a struct (not a struct pointer) as a function argument;
- # or, a complex (the same code works)
- self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
- % (tovar, self._gettypenum(tp), fromvar))
- self._prnt(' %s;' % errcode)
- return
- #
- elif isinstance(tp, model.FunctionPtrType):
- converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
- extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
- errvalue = 'NULL'
- #
- else:
- raise NotImplementedError(tp)
- #
- self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
- self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % (
- tovar, tp.get_c_name(''), errvalue))
- self._prnt(' %s;' % errcode)
- def _extra_local_variables(self, tp, localvars, freelines):
- if isinstance(tp, model.PointerType):
- localvars.add('Py_ssize_t datasize')
- localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
- freelines.add('if (large_args_free != NULL)'
- ' _cffi_free_array_arguments(large_args_free);')
- def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
- self._prnt(' datasize = _cffi_prepare_pointer_call_argument(')
- self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % (
- self._gettypenum(tp), fromvar, tovar))
- self._prnt(' if (datasize != 0) {')
- self._prnt(' %s = ((size_t)datasize) <= 640 ? '
- '(%s)alloca((size_t)datasize) : NULL;' % (
- tovar, tp.get_c_name('')))
- self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
- '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
- self._prnt(' datasize, &large_args_free) < 0)')
- self._prnt(' %s;' % errcode)
- self._prnt(' }')
- def _convert_expr_from_c(self, tp, var, context):
- if isinstance(tp, model.BasePrimitiveType):
- if tp.is_integer_type() and tp.name != '_Bool':
- return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
- elif isinstance(tp, model.UnknownFloatType):
- return '_cffi_from_c_double(%s)' % (var,)
- elif tp.name != 'long double' and not tp.is_complex_type():
- cname = tp.name.replace(' ', '_')
- if cname in ('char16_t', 'char32_t'):
- self.needs_version(VERSION_CHAR16CHAR32)
- return '_cffi_from_c_%s(%s)' % (cname, var)
- else:
- return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
- return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, model.ArrayType):
- return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
- var, self._gettypenum(model.PointerType(tp.item)))
- elif isinstance(tp, model.StructOrUnion):
- if tp.fldnames is None:
- raise TypeError("'%s' is used as %s, but is opaque" % (
- tp._get_c_name(), context))
- return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- elif isinstance(tp, model.EnumType):
- return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
- var, self._gettypenum(tp))
- else:
- raise NotImplementedError(tp)
- # ----------
- # typedefs
- def _typedef_type(self, tp, name):
- return self._global_type(tp, "(*(%s *)0)" % (name,))
- def _generate_cpy_typedef_collecttype(self, tp, name):
- self._do_collect_type(self._typedef_type(tp, name))
- def _generate_cpy_typedef_decl(self, tp, name):
- pass
- def _typedef_ctx(self, tp, name):
- type_index = self._typesdict[tp]
- self._lsts["typename"].append(TypenameExpr(name, type_index))
- def _generate_cpy_typedef_ctx(self, tp, name):
- tp = self._typedef_type(tp, name)
- self._typedef_ctx(tp, name)
- if getattr(tp, "origin", None) == "unknown_type":
- self._struct_ctx(tp, tp.name, approxname=None)
- elif isinstance(tp, model.NamedPointerType):
- self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
- named_ptr=tp)
- # ----------
- # function declarations
- def _generate_cpy_function_collecttype(self, tp, name):
- self._do_collect_type(tp.as_raw_function())
- if tp.ellipsis and not self.target_is_python:
- self._do_collect_type(tp)
- def _generate_cpy_function_decl(self, tp, name):
- assert not self.target_is_python
- assert isinstance(tp, model.FunctionPtrType)
- if tp.ellipsis:
- # cannot support vararg functions better than this: check for its
- # exact type (including the fixed arguments), and build it as a
- # constant function pointer (no CPython wrapper)
- self._generate_cpy_constant_decl(tp, name)
- return
- prnt = self._prnt
- numargs = len(tp.args)
- if numargs == 0:
- argname = 'noarg'
- elif numargs == 1:
- argname = 'arg0'
- else:
- argname = 'args'
- #
- # ------------------------------
- # the 'd' version of the function, only for addressof(lib, 'func')
- arguments = []
- call_arguments = []
- context = 'argument of %s' % name
- for i, type in enumerate(tp.args):
- arguments.append(type.get_c_name(' x%d' % i, context))
- call_arguments.append('x%d' % i)
- repr_arguments = ', '.join(arguments)
- repr_arguments = repr_arguments or 'void'
- if tp.abi:
- abi = tp.abi + ' '
- else:
- abi = ''
- name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
- prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
- prnt('{')
- call_arguments = ', '.join(call_arguments)
- result_code = 'return '
- if isinstance(tp.result, model.VoidType):
- result_code = ''
- prnt(' %s%s(%s);' % (result_code, name, call_arguments))
- prnt('}')
- #
- prnt('#ifndef PYPY_VERSION') # ------------------------------
- #
- prnt('static PyObject *')
- prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
- prnt('{')
- #
- context = 'argument of %s' % name
- for i, type in enumerate(tp.args):
- arg = type.get_c_name(' x%d' % i, context)
- prnt(' %s;' % arg)
- #
- localvars = set()
- freelines = set()
- for type in tp.args:
- self._extra_local_variables(type, localvars, freelines)
- for decl in sorted(localvars):
- prnt(' %s;' % (decl,))
- #
- if not isinstance(tp.result, model.VoidType):
- result_code = 'result = '
- context = 'result of %s' % name
- result_decl = ' %s;' % tp.result.get_c_name(' result', context)
- prnt(result_decl)
- prnt(' PyObject *pyresult;')
- else:
- result_decl = None
- result_code = ''
- #
- if len(tp.args) > 1:
- rng = range(len(tp.args))
- for i in rng:
- prnt(' PyObject *arg%d;' % i)
- prnt()
- prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
- name, len(rng), len(rng),
- ', '.join(['&arg%d' % i for i in rng])))
- prnt(' return NULL;')
- prnt()
- #
- for i, type in enumerate(tp.args):
- self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
- 'return NULL')
- prnt()
- #
- prnt(' Py_BEGIN_ALLOW_THREADS')
- prnt(' _cffi_restore_errno();')
- call_arguments = ['x%d' % i for i in range(len(tp.args))]
- call_arguments = ', '.join(call_arguments)
- prnt(' { %s%s(%s); }' % (result_code, name, call_arguments))
- prnt(' _cffi_save_errno();')
- prnt(' Py_END_ALLOW_THREADS')
- prnt()
- #
- prnt(' (void)self; /* unused */')
- if numargs == 0:
- prnt(' (void)noarg; /* unused */')
- if result_code:
- prnt(' pyresult = %s;' %
- self._convert_expr_from_c(tp.result, 'result', 'result type'))
- for freeline in freelines:
- prnt(' ' + freeline)
- prnt(' return pyresult;')
- else:
- for freeline in freelines:
- prnt(' ' + freeline)
- prnt(' Py_INCREF(Py_None);')
- prnt(' return Py_None;')
- prnt('}')
- #
- prnt('#else') # ------------------------------
- #
- # the PyPy version: need to replace struct/union arguments with
- # pointers, and if the result is a struct/union, insert a first
- # arg that is a pointer to the result. We also do that for
- # complex args and return type.
- def need_indirection(type):
- return (isinstance(type, model.StructOrUnion) or
- (isinstance(type, model.PrimitiveType) and
- type.is_complex_type()))
- difference = False
- arguments = []
- call_arguments = []
- context = 'argument of %s' % name
- for i, type in enumerate(tp.args):
- indirection = ''
- if need_indirection(type):
- indirection = '*'
- difference = True
- arg = type.get_c_name(' %sx%d' % (indirection, i), context)
- arguments.append(arg)
- call_arguments.append('%sx%d' % (indirection, i))
- tp_result = tp.result
- if need_indirection(tp_result):
- context = 'result of %s' % name
- arg = tp_result.get_c_name(' *result', context)
- arguments.insert(0, arg)
- tp_result = model.void_type
- result_decl = None
- result_code = '*result = '
- difference = True
- if difference:
- repr_arguments = ', '.join(arguments)
- repr_arguments = repr_arguments or 'void'
- name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
- repr_arguments)
- prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
- prnt('{')
- if result_decl:
- prnt(result_decl)
- call_arguments = ', '.join(call_arguments)
- prnt(' { %s%s(%s); }' % (result_code, name, call_arguments))
- if result_decl:
- prnt(' return result;')
- prnt('}')
- else:
- prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name))
- #
- prnt('#endif') # ------------------------------
- prnt()
- def _generate_cpy_function_ctx(self, tp, name):
- if tp.ellipsis and not self.target_is_python:
- self._generate_cpy_constant_ctx(tp, name)
- return
- type_index = self._typesdict[tp.as_raw_function()]
- numargs = len(tp.args)
- if self.target_is_python:
- meth_kind = OP_DLOPEN_FUNC
- elif numargs == 0:
- meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS'
- elif numargs == 1:
- meth_kind = OP_CPYTHON_BLTN_O # 'METH_O'
- else:
- meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS'
- self._lsts["global"].append(
- GlobalExpr(name, '_cffi_f_%s' % name,
- CffiOp(meth_kind, type_index),
- size='_cffi_d_%s' % name))
- # ----------
- # named structs or unions
- def _field_type(self, tp_struct, field_name, tp_field):
- if isinstance(tp_field, model.ArrayType):
- actual_length = tp_field.length
- if actual_length == '...':
- ptr_struct_name = tp_struct.get_c_name('*')
- actual_length = '_cffi_array_len(((%s)0)->%s)' % (
- ptr_struct_name, field_name)
- tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
- tp_field.item)
- tp_field = model.ArrayType(tp_item, actual_length)
- return tp_field
- def _struct_collecttype(self, tp):
- self._do_collect_type(tp)
- if self.target_is_python:
- # also requires nested anon struct/unions in ABI mode, recursively
- for fldtype in tp.anonymous_struct_fields():
- self._struct_collecttype(fldtype)
- def _struct_decl(self, tp, cname, approxname):
- if tp.fldtypes is None:
- return
- prnt = self._prnt
- checkfuncname = '_cffi_checkfld_%s' % (approxname,)
- prnt('_CFFI_UNUSED_FN')
- prnt('static void %s(%s *p)' % (checkfuncname, cname))
- prnt('{')
- prnt(' /* only to generate compile-time warnings or errors */')
- prnt(' (void)p;')
- for fname, ftype, fbitsize, fqual in self._enum_fields(tp):
- try:
- if ftype.is_integer_type() or fbitsize >= 0:
- # accept all integers, but complain on float or double
- if fname != '':
- prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is "
- "an integer */" % (fname, cname, fname))
- continue
- # only accept exactly the type declared, except that '[]'
- # is interpreted as a '*' and so will match any array length.
- # (It would also match '*', but that's harder to detect...)
- while (isinstance(ftype, model.ArrayType)
- and (ftype.length is None or ftype.length == '...')):
- ftype = ftype.item
- fname = fname + '[0]'
- prnt(' { %s = &p->%s; (void)tmp; }' % (
- ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
- fname))
- except VerificationError as e:
- prnt(' /* %s */' % str(e)) # cannot verify it, ignore
- prnt('}')
- prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
- prnt()
- def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
- type_index = self._typesdict[tp]
- reason_for_not_expanding = None
- flags = []
- if isinstance(tp, model.UnionType):
- flags.append("_CFFI_F_UNION")
- if tp.fldtypes is None:
- flags.append("_CFFI_F_OPAQUE")
- reason_for_not_expanding = "opaque"
- if (tp not in self.ffi._parser._included_declarations and
- (named_ptr is None or
- named_ptr not in self.ffi._parser._included_declarations)):
- if tp.fldtypes is None:
- pass # opaque
- elif tp.partial or any(tp.anonymous_struct_fields()):
- pass # field layout obtained silently from the C compiler
- else:
- flags.append("_CFFI_F_CHECK_FIELDS")
- if tp.packed:
- if tp.packed > 1:
- raise NotImplementedError(
- "%r is declared with 'pack=%r'; only 0 or 1 are "
- "supported in API mode (try to use \"...;\", which "
- "does not require a 'pack' declaration)" %
- (tp, tp.packed))
- flags.append("_CFFI_F_PACKED")
- else:
- flags.append("_CFFI_F_EXTERNAL")
- reason_for_not_expanding = "external"
- flags = '|'.join(flags) or '0'
- c_fields = []
- if reason_for_not_expanding is None:
- enumfields = list(self._enum_fields(tp))
- for fldname, fldtype, fbitsize, fqual in enumfields:
- fldtype = self._field_type(tp, fldname, fldtype)
- self._check_not_opaque(fldtype,
- "field '%s.%s'" % (tp.name, fldname))
- # cname is None for _add_missing_struct_unions() only
- op = OP_NOOP
- if fbitsize >= 0:
- op = OP_BITFIELD
- size = '%d /* bits */' % fbitsize
- elif cname is None or (
- isinstance(fldtype, model.ArrayType) and
- fldtype.length is None):
- size = '(size_t)-1'
- else:
- size = 'sizeof(((%s)0)->%s)' % (
- tp.get_c_name('*') if named_ptr is None
- else named_ptr.name,
- fldname)
- if cname is None or fbitsize >= 0:
- offset = '(size_t)-1'
- elif named_ptr is not None:
- offset = '((char *)&((%s)0)->%s) - (char *)0' % (
- named_ptr.name, fldname)
- else:
- offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
- c_fields.append(
- FieldExpr(fldname, offset, size, fbitsize,
- CffiOp(op, self._typesdict[fldtype])))
- first_field_index = len(self._lsts["field"])
- self._lsts["field"].extend(c_fields)
- #
- if cname is None: # unknown name, for _add_missing_struct_unions
- size = '(size_t)-2'
- align = -2
- comment = "unnamed"
- else:
- if named_ptr is not None:
- size = 'sizeof(*(%s)0)' % (named_ptr.name,)
- align = '-1 /* unknown alignment */'
- else:
- size = 'sizeof(%s)' % (cname,)
- align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
- comment = None
- else:
- size = '(size_t)-1'
- align = -1
- first_field_index = -1
- comment = reason_for_not_expanding
- self._lsts["struct_union"].append(
- StructUnionExpr(tp.name, type_index, flags, size, align, comment,
- first_field_index, c_fields))
- self._seen_struct_unions.add(tp)
- def _check_not_opaque(self, tp, location):
- while isinstance(tp, model.ArrayType):
- tp = tp.item
- if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
- raise TypeError(
- "%s is of an opaque type (not declared in cdef())" % location)
- def _add_missing_struct_unions(self):
- # not very nice, but some struct declarations might be missing
- # because they don't have any known C name. Check that they are
- # not partial (we can't complete or verify them!) and emit them
- # anonymously.
- lst = list(self._struct_unions.items())
- lst.sort(key=lambda tp_order: tp_order[1])
- for tp, order in lst:
- if tp not in self._seen_struct_unions:
- if tp.partial:
- raise NotImplementedError("internal inconsistency: %r is "
- "partial but was not seen at "
- "this point" % (tp,))
- if tp.name.startswith('$') and tp.name[1:].isdigit():
- approxname = tp.name[1:]
- elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
- approxname = 'FILE'
- self._typedef_ctx(tp, 'FILE')
- else:
- raise NotImplementedError("internal inconsistency: %r" %
- (tp,))
- self._struct_ctx(tp, None, approxname)
- def _generate_cpy_struct_collecttype(self, tp, name):
- self._struct_collecttype(tp)
- _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
- def _struct_names(self, tp):
- cname = tp.get_c_name('')
- if ' ' in cname:
- return cname, cname.replace(' ', '_')
- else:
- return cname, '_' + cname
- def _generate_cpy_struct_decl(self, tp, name):
- self._struct_decl(tp, *self._struct_names(tp))
- _generate_cpy_union_decl = _generate_cpy_struct_decl
- def _generate_cpy_struct_ctx(self, tp, name):
- self._struct_ctx(tp, *self._struct_names(tp))
- _generate_cpy_union_ctx = _generate_cpy_struct_ctx
- # ----------
- # 'anonymous' declarations. These are produced for anonymous structs
- # or unions; the 'name' is obtained by a typedef.
- def _generate_cpy_anonymous_collecttype(self, tp, name):
- if isinstance(tp, model.EnumType):
- self._generate_cpy_enum_collecttype(tp, name)
- else:
- self._struct_collecttype(tp)
- def _generate_cpy_anonymous_decl(self, tp, name):
- if isinstance(tp, model.EnumType):
- self._generate_cpy_enum_decl(tp)
- else:
- self._struct_decl(tp, name, 'typedef_' + name)
- def _generate_cpy_anonymous_ctx(self, tp, name):
- if isinstance(tp, model.EnumType):
- self._enum_ctx(tp, name)
- else:
- self._struct_ctx(tp, name, 'typedef_' + name)
- # ----------
- # constants, declared with "static const ..."
- def _generate_cpy_const(self, is_int, name, tp=None, category='const',
- check_value=None):
- if (category, name) in self._seen_constants:
- raise VerificationError(
- "duplicate declaration of %s '%s'" % (category, name))
- self._seen_constants.add((category, name))
- #
- prnt = self._prnt
- funcname = '_cffi_%s_%s' % (category, name)
- if is_int:
- prnt('static int %s(unsigned long long *o)' % funcname)
- prnt('{')
- prnt(' int n = (%s) <= 0;' % (name,))
- prnt(' *o = (unsigned long long)((%s) | 0);'
- ' /* check that %s is an integer */' % (name, name))
- if check_value is not None:
- if check_value > 0:
- check_value = '%dU' % (check_value,)
- prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,))
- prnt(' n |= 2;')
- prnt(' return n;')
- prnt('}')
- else:
- assert check_value is None
- prnt('static void %s(char *o)' % funcname)
- prnt('{')
- prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name))
- prnt('}')
- prnt()
- def _generate_cpy_constant_collecttype(self, tp, name):
- is_int = tp.is_integer_type()
- if not is_int or self.target_is_python:
- self._do_collect_type(tp)
- def _generate_cpy_constant_decl(self, tp, name):
- is_int = tp.is_integer_type()
- self._generate_cpy_const(is_int, name, tp)
- def _generate_cpy_constant_ctx(self, tp, name):
- if not self.target_is_python and tp.is_integer_type():
- type_op = CffiOp(OP_CONSTANT_INT, -1)
- else:
- if self.target_is_python:
- const_kind = OP_DLOPEN_CONST
- else:
- const_kind = OP_CONSTANT
- type_index = self._typesdict[tp]
- type_op = CffiOp(const_kind, type_index)
- self._lsts["global"].append(
- GlobalExpr(name, '_cffi_const_%s' % name, type_op))
- # ----------
- # enums
- def _generate_cpy_enum_collecttype(self, tp, name):
- self._do_collect_type(tp)
- def _generate_cpy_enum_decl(self, tp, name=None):
- for enumerator in tp.enumerators:
- self._generate_cpy_const(True, enumerator)
- def _enum_ctx(self, tp, cname):
- type_index = self._typesdict[tp]
- type_op = CffiOp(OP_ENUM, -1)
- if self.target_is_python:
- tp.check_not_partial()
- for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- self._lsts["global"].append(
- GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
- check_value=enumvalue))
- #
- if cname is not None and '$' not in cname and not self.target_is_python:
- size = "sizeof(%s)" % cname
- signed = "((%s)-1) <= 0" % cname
- else:
- basetp = tp.build_baseinttype(self.ffi, [])
- size = self.ffi.sizeof(basetp)
- signed = int(int(self.ffi.cast(basetp, -1)) < 0)
- allenums = ",".join(tp.enumerators)
- self._lsts["enum"].append(
- EnumExpr(tp.name, type_index, size, signed, allenums))
- def _generate_cpy_enum_ctx(self, tp, name):
- self._enum_ctx(tp, tp._get_c_name())
- # ----------
- # macros: for now only for integers
- def _generate_cpy_macro_collecttype(self, tp, name):
- pass
- def _generate_cpy_macro_decl(self, tp, name):
- if tp == '...':
- check_value = None
- else:
- check_value = tp # an integer
- self._generate_cpy_const(True, name, check_value=check_value)
- def _generate_cpy_macro_ctx(self, tp, name):
- if tp == '...':
- if self.target_is_python:
- raise VerificationError(
- "cannot use the syntax '...' in '#define %s ...' when "
- "using the ABI mode" % (name,))
- check_value = None
- else:
- check_value = tp # an integer
- type_op = CffiOp(OP_CONSTANT_INT, -1)
- self._lsts["global"].append(
- GlobalExpr(name, '_cffi_const_%s' % name, type_op,
- check_value=check_value))
- # ----------
- # global variables
- def _global_type(self, tp, global_name):
- if isinstance(tp, model.ArrayType):
- actual_length = tp.length
- if actual_length == '...':
- actual_length = '_cffi_array_len(%s)' % (global_name,)
- tp_item = self._global_type(tp.item, '%s[0]' % global_name)
- tp = model.ArrayType(tp_item, actual_length)
- return tp
- def _generate_cpy_variable_collecttype(self, tp, name):
- self._do_collect_type(self._global_type(tp, name))
- def _generate_cpy_variable_decl(self, tp, name):
- prnt = self._prnt
- tp = self._global_type(tp, name)
- if isinstance(tp, model.ArrayType) and tp.length is None:
- tp = tp.item
- ampersand = ''
- else:
- ampersand = '&'
- # This code assumes that casts from "tp *" to "void *" is a
- # no-op, i.e. a function that returns a "tp *" can be called
- # as if it returned a "void *". This should be generally true
- # on any modern machine. The only exception to that rule (on
- # uncommon architectures, and as far as I can tell) might be
- # if 'tp' were a function type, but that is not possible here.
- # (If 'tp' is a function _pointer_ type, then casts from "fn_t
- # **" to "void *" are again no-ops, as far as I can tell.)
- decl = '*_cffi_var_%s(void)' % (name,)
- prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
- prnt('{')
- prnt(' return %s(%s);' % (ampersand, name))
- prnt('}')
- prnt()
- def _generate_cpy_variable_ctx(self, tp, name):
- tp = self._global_type(tp, name)
- type_index = self._typesdict[tp]
- if self.target_is_python:
- op = OP_GLOBAL_VAR
- else:
- op = OP_GLOBAL_VAR_F
- self._lsts["global"].append(
- GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))
- # ----------
- # extern "Python"
- def _generate_cpy_extern_python_collecttype(self, tp, name):
- assert isinstance(tp, model.FunctionPtrType)
- self._do_collect_type(tp)
- _generate_cpy_dllexport_python_collecttype = \
- _generate_cpy_extern_python_plus_c_collecttype = \
- _generate_cpy_extern_python_collecttype
- def _extern_python_decl(self, tp, name, tag_and_space):
- prnt = self._prnt
- if isinstance(tp.result, model.VoidType):
- size_of_result = '0'
- else:
- context = 'result of %s' % name
- size_of_result = '(int)sizeof(%s)' % (
- tp.result.get_c_name('', context),)
- prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
- prnt(' { "%s.%s", %s, 0, 0 };' % (
- self.module_name, name, size_of_result))
- prnt()
- #
- arguments = []
- context = 'argument of %s' % name
- for i, type in enumerate(tp.args):
- arg = type.get_c_name(' a%d' % i, context)
- arguments.append(arg)
- #
- repr_arguments = ', '.join(arguments)
- repr_arguments = repr_arguments or 'void'
- name_and_arguments = '%s(%s)' % (name, repr_arguments)
- if tp.abi == "__stdcall":
- name_and_arguments = '_cffi_stdcall ' + name_and_arguments
- #
- def may_need_128_bits(tp):
- return (isinstance(tp, model.PrimitiveType) and
- tp.name == 'long double')
- #
- size_of_a = max(len(tp.args)*8, 8)
- if may_need_128_bits(tp.result):
- size_of_a = max(size_of_a, 16)
- if isinstance(tp.result, model.StructOrUnion):
- size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
- tp.result.get_c_name(''), size_of_a,
- tp.result.get_c_name(''), size_of_a)
- prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
- prnt('{')
- prnt(' char a[%s];' % size_of_a)
- prnt(' char *p = a;')
- for i, type in enumerate(tp.args):
- arg = 'a%d' % i
- if (isinstance(type, model.StructOrUnion) or
- may_need_128_bits(type)):
- arg = '&' + arg
- type = model.PointerType(type)
- prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
- prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name)
- if not isinstance(tp.result, model.VoidType):
- prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),))
- prnt('}')
- prnt()
- self._num_externpy += 1
- def _generate_cpy_extern_python_decl(self, tp, name):
- self._extern_python_decl(tp, name, 'static ')
- def _generate_cpy_dllexport_python_decl(self, tp, name):
- self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
- def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
- self._extern_python_decl(tp, name, '')
- def _generate_cpy_extern_python_ctx(self, tp, name):
- if self.target_is_python:
- raise VerificationError(
- "cannot use 'extern \"Python\"' in the ABI mode")
- if tp.ellipsis:
- raise NotImplementedError("a vararg function is extern \"Python\"")
- type_index = self._typesdict[tp]
- type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
- self._lsts["global"].append(
- GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
- _generate_cpy_dllexport_python_ctx = \
- _generate_cpy_extern_python_plus_c_ctx = \
- _generate_cpy_extern_python_ctx
- def _print_string_literal_in_array(self, s):
- prnt = self._prnt
- prnt('// # NB. this is not a string because of a size limit in MSVC')
- if not isinstance(s, bytes): # unicode
- s = s.encode('utf-8') # -> bytes
- else:
- s.decode('utf-8') # got bytes, check for valid utf-8
- try:
- s.decode('ascii')
- except UnicodeDecodeError:
- s = b'# -*- encoding: utf8 -*-\n' + s
- for line in s.splitlines(True):
- comment = line
- if type('//') is bytes: # python2
- line = map(ord, line) # make a list of integers
- else: # python3
- # type(line) is bytes, which enumerates like a list of integers
- comment = ascii(comment)[1:-1]
- prnt(('// ' + comment).rstrip())
- printed_line = ''
- for c in line:
- if len(printed_line) >= 76:
- prnt(printed_line)
- printed_line = ''
- printed_line += '%d,' % (c,)
- prnt(printed_line)
- # ----------
- # emitting the opcodes for individual types
- def _emit_bytecode_VoidType(self, tp, index):
- self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)
- def _emit_bytecode_PrimitiveType(self, tp, index):
- prim_index = PRIMITIVE_TO_INDEX[tp.name]
- self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
- def _emit_bytecode_UnknownIntegerType(self, tp, index):
- s = ('_cffi_prim_int(sizeof(%s), (\n'
- ' ((%s)-1) | 0 /* check that %s is an integer type */\n'
- ' ) <= 0)' % (tp.name, tp.name, tp.name))
- self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
- def _emit_bytecode_UnknownFloatType(self, tp, index):
- s = ('_cffi_prim_float(sizeof(%s) *\n'
- ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
- ' )' % (tp.name, tp.name))
- self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
- def _emit_bytecode_RawFunctionType(self, tp, index):
- self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
- index += 1
- for tp1 in tp.args:
- realindex = self._typesdict[tp1]
- if index != realindex:
- if isinstance(tp1, model.PrimitiveType):
- self._emit_bytecode_PrimitiveType(tp1, index)
- else:
- self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
- index += 1
- flags = int(tp.ellipsis)
- if tp.abi is not None:
- if tp.abi == '__stdcall':
- flags |= 2
- else:
- raise NotImplementedError("abi=%r" % (tp.abi,))
- self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
- def _emit_bytecode_PointerType(self, tp, index):
- self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
- _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
- _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType
- def _emit_bytecode_FunctionPtrType(self, tp, index):
- raw = tp.as_raw_function()
- self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])
- def _emit_bytecode_ArrayType(self, tp, index):
- item_index = self._typesdict[tp.item]
- if tp.length is None:
- self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
- elif tp.length == '...':
- raise VerificationError(
- "type %s badly placed: the '...' array length can only be "
- "used on global arrays or on fields of structures" % (
- str(tp).replace('/*...*/', '...'),))
- else:
- assert self.cffi_types[index + 1] == 'LEN'
- self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
- self.cffi_types[index + 1] = CffiOp(None, str(tp.length))
- def _emit_bytecode_StructType(self, tp, index):
- struct_index = self._struct_unions[tp]
- self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
- _emit_bytecode_UnionType = _emit_bytecode_StructType
- def _emit_bytecode_EnumType(self, tp, index):
- enum_index = self._enums[tp]
- self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)
- if sys.version_info >= (3,):
- NativeIO = io.StringIO
- else:
- class NativeIO(io.BytesIO):
- def write(self, s):
- if isinstance(s, unicode):
- s = s.encode('ascii')
- super(NativeIO, self).write(s)
- def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
- if verbose:
- print("generating %s" % (target_file,))
- recompiler = Recompiler(ffi, module_name,
- target_is_python=(preamble is None))
- recompiler.collect_type_table()
- recompiler.collect_step_tables()
- f = NativeIO()
- recompiler.write_source_to_f(f, preamble)
- output = f.getvalue()
- try:
- with open(target_file, 'r') as f1:
- if f1.read(len(output) + 1) != output:
- raise IOError
- if verbose:
- print("(already up-to-date)")
- return False # already up-to-date
- except IOError:
- tmp_file = '%s.~%d' % (target_file, os.getpid())
- with open(tmp_file, 'w') as f1:
- f1.write(output)
- try:
- os.rename(tmp_file, target_file)
- except OSError:
- os.unlink(target_file)
- os.rename(tmp_file, target_file)
- return True
- def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
- assert preamble is not None
- return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
- verbose)
- def make_py_source(ffi, module_name, target_py_file, verbose=False):
- return _make_c_or_py_source(ffi, module_name, None, target_py_file,
- verbose)
- def _modname_to_file(outputdir, modname, extension):
- parts = modname.split('.')
- try:
- os.makedirs(os.path.join(outputdir, *parts[:-1]))
- except OSError:
- pass
- parts[-1] += extension
- return os.path.join(outputdir, *parts), parts
- # Aaargh. Distutils is not tested at all for the purpose of compiling
- # DLLs that are not extension modules. Here are some hacks to work
- # around that, in the _patch_for_*() functions...
- def _patch_meth(patchlist, cls, name, new_meth):
- old = getattr(cls, name)
- patchlist.append((cls, name, old))
- setattr(cls, name, new_meth)
- return old
- def _unpatch_meths(patchlist):
- for cls, name, old_meth in reversed(patchlist):
- setattr(cls, name, old_meth)
- def _patch_for_embedding(patchlist):
- if sys.platform == 'win32':
- # we must not remove the manifest when building for embedding!
- from distutils.msvc9compiler import MSVCCompiler
- _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
- lambda self, manifest_file: manifest_file)
- if sys.platform == 'darwin':
- # we must not make a '-bundle', but a '-dynamiclib' instead
- from distutils.ccompiler import CCompiler
- def my_link_shared_object(self, *args, **kwds):
- if '-bundle' in self.linker_so:
- self.linker_so = list(self.linker_so)
- i = self.linker_so.index('-bundle')
- self.linker_so[i] = '-dynamiclib'
- return old_link_shared_object(self, *args, **kwds)
- old_link_shared_object = _patch_meth(patchlist, CCompiler,
- 'link_shared_object',
- my_link_shared_object)
- def _patch_for_target(patchlist, target):
- from distutils.command.build_ext import build_ext
- # if 'target' is different from '*', we need to patch some internal
- # method to just return this 'target' value, instead of having it
- # built from module_name
- if target.endswith('.*'):
- target = target[:-2]
- if sys.platform == 'win32':
- target += '.dll'
- elif sys.platform == 'darwin':
- target += '.dylib'
- else:
- target += '.so'
- _patch_meth(patchlist, build_ext, 'get_ext_filename',
- lambda self, ext_name: target)
- def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
- c_file=None, source_extension='.c', extradir=None,
- compiler_verbose=1, target=None, debug=None, **kwds):
- if not isinstance(module_name, str):
- module_name = module_name.encode('ascii')
- if ffi._windows_unicode:
- ffi._apply_windows_unicode(kwds)
- if preamble is not None:
- embedding = (ffi._embedding is not None)
- if embedding:
- ffi._apply_embedding_fix(kwds)
- if c_file is None:
- c_file, parts = _modname_to_file(tmpdir, module_name,
- source_extension)
- if extradir:
- parts = [extradir] + parts
- ext_c_file = os.path.join(*parts)
- else:
- ext_c_file = c_file
- #
- if target is None:
- if embedding:
- target = '%s.*' % module_name
- else:
- target = '*'
- #
- ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
- updated = make_c_source(ffi, module_name, preamble, c_file,
- verbose=compiler_verbose)
- if call_c_compiler:
- patchlist = []
- cwd = os.getcwd()
- try:
- if embedding:
- _patch_for_embedding(patchlist)
- if target != '*':
- _patch_for_target(patchlist, target)
- if compiler_verbose:
- if tmpdir == '.':
- msg = 'the current directory is'
- else:
- msg = 'setting the current directory to'
- print('%s %r' % (msg, os.path.abspath(tmpdir)))
- os.chdir(tmpdir)
- outputfilename = ffiplatform.compile('.', ext,
- compiler_verbose, debug)
- finally:
- os.chdir(cwd)
- _unpatch_meths(patchlist)
- return outputfilename
- else:
- return ext, updated
- else:
- if c_file is None:
- c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
- updated = make_py_source(ffi, module_name, c_file,
- verbose=compiler_verbose)
- if call_c_compiler:
- return c_file
- else:
- return None, updated
|