summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2012-08-04 17:46:09 +0200
committerArmin Rigo <arigo@tunes.org>2012-08-04 17:46:09 +0200
commite2024b64eb37c607c0742caa3bb1af682e7855f5 (patch)
tree87499ea81aab472fb763bd2f5c146223217ee0ce
parent599300811cc1015773147b625d20f3e033865e2f (diff)
downloadcffi-e2024b64eb37c607c0742caa3bb1af682e7855f5.tar.gz
Merge the two verifiers into two VEngine classes. There is still a little bit
of code duplication but not too much.
-rw-r--r--cffi/vengine_cpy.py729
-rw-r--r--cffi/vengine_gen.py420
-rw-r--r--cffi/verifier.py466
-rw-r--r--testing/test_verify.py22
-rw-r--r--testing/test_vgen.py12
-rw-r--r--testing/test_vgen2.py13
-rw-r--r--testing/test_zdistutils.py277
7 files changed, 1380 insertions, 559 deletions
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
new file mode 100644
index 0000000..3ee3140
--- /dev/null
+++ b/cffi/vengine_cpy.py
@@ -0,0 +1,729 @@
+import imp
+from . import model, ffiplatform
+
+
+class VCPythonEngine(object):
+ _class_key = 'x'
+ _gen_python_module = True
+
+ def __init__(self, verifier):
+ self.verifier = verifier
+ self.ffi = verifier.ffi
+
+ def collect_types(self):
+ self._typesdict = {}
+ self._generate("collecttype")
+
+ def _prnt(self, what=''):
+ print >> self._f, what
+
+ def _gettypenum(self, type):
+ # a KeyError here is a bug. please report it! :-)
+ return self._typesdict[type]
+
+ def _do_collect_type(self, tp):
+ if (not isinstance(tp, model.PrimitiveType) and
+ tp not in self._typesdict):
+ num = len(self._typesdict)
+ self._typesdict[tp] = num
+
+ def write_source_to_f(self):
+ self.collect_types()
+ #
+ # The new module will have a _cffi_setup() function that receives
+ # objects from the ffi world, and that calls some setup code in
+ # the module. This setup code is split in several independent
+ # functions, e.g. one per constant. The functions are "chained"
+ # by ending in a tail call to each other.
+ #
+ # This is further split in two chained lists, depending on if we
+ # can do it at import-time or if we must wait for _cffi_setup() to
+ # provide us with the <ctype> objects. This is needed because we
+ # need the values of the enum constants in order to build the
+ # <ctype 'enum'> that we may have to pass to _cffi_setup().
+ #
+ # The following two 'chained_list_constants' items contains
+ # the head of these two chained lists, as a string that gives the
+ # call to do, if any.
+ self._chained_list_constants = ['0', '0']
+ #
+ prnt = self._prnt
+ # first paste some standard set of lines that are mostly '#define'
+ prnt(cffimod_header)
+ prnt()
+ # then paste the C source given by the user, verbatim.
+ prnt(self.verifier.preamble)
+ prnt()
+ #
+ # call generate_cpy_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
+ self._generate("decl")
+ #
+ # implement the function _cffi_setup_custom() as calling the
+ # head of the chained list.
+ self._generate_setup_custom()
+ prnt()
+ #
+ # produce the method table, including the entries for the
+ # generated Python->C function wrappers, which are done
+ # by generate_cpy_function_method().
+ prnt('static PyMethodDef _cffi_methods[] = {')
+ self._generate("method")
+ prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
+ prnt(' {NULL, NULL} /* Sentinel */')
+ prnt('};')
+ prnt()
+ #
+ # standard init.
+ modname = self.verifier.get_module_name()
+ prnt('PyMODINIT_FUNC')
+ prnt('init%s(void)' % modname)
+ prnt('{')
+ prnt(' PyObject *lib;')
+ prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname)
+ prnt(' if (lib == NULL || %s < 0)' % (
+ self._chained_list_constants[False],))
+ prnt(' return;')
+ prnt(' _cffi_init();')
+ prnt('}')
+
+ def load_library(self):
+ # XXX review all usages of 'self' here!
+ # import it as a new extension module
+ try:
+ module = imp.load_dynamic(self.verifier.get_module_name(),
+ self.verifier.modulefilename)
+ except ImportError, e:
+ error = "importing %r: %s" % (self.modulefilename, e)
+ raise ffiplatform.VerificationError(error)
+ #
+ # call loading_cpy_struct() to get the struct layout inferred by
+ # the C compiler
+ self._load(module, 'loading')
+ #
+ # the C code will need the <ctype> objects. Collect them in
+ # order in a list.
+ revmapping = dict([(value, key)
+ for (key, value) in self._typesdict.items()])
+ lst = [revmapping[i] for i in range(len(revmapping))]
+ lst = map(self.ffi._get_cached_btype, lst)
+ #
+ # build the FFILibrary class and instance and call _cffi_setup().
+ # this will set up some fields like '_cffi_types', and only then
+ # it will invoke the chained list of functions that will really
+ # build (notably) the constant objects, as <cdata> if they are
+ # pointers, and store them as attributes on the 'library' object.
+ class FFILibrary(object):
+ _cffi_python_module = module
+ library = FFILibrary()
+ module._cffi_setup(lst, ffiplatform.VerificationError, library)
+ #
+ # finally, call the loaded_cpy_xxx() functions. This will perform
+ # the final adjustments, like copying the Python->C wrapper
+ # functions from the module to the 'library' object, and setting
+ # up the FFILibrary class with properties for the global C variables.
+ self._load(module, 'loaded', library=library)
+ return library
+
+ def _generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ try:
+ method = getattr(self, '_generate_cpy_%s_%s' % (kind,
+ step_name))
+ except AttributeError:
+ raise ffiplatform.VerificationError(
+ "not implemented in verify(): %r" % name)
+ method(tp, realname)
+
+ def _load(self, module, step_name, **kwds):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
+ method(tp, realname, module, **kwds)
+
+ def _generate_nothing(self, tp, name):
+ pass
+
+ def _loaded_noop(self, tp, name, module, **kwds):
+ pass
+
+ # ----------
+
+ def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
+ extraarg = ''
+ if isinstance(tp, model.PrimitiveType):
+ converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),)
+ errvalue = '-1'
+ #
+ elif isinstance(tp, model.PointerType):
+ if (isinstance(tp.totype, model.PrimitiveType) and
+ tp.totype.name == 'char'):
+ converter = '_cffi_to_c_char_p'
+ else:
+ converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
+ extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
+ errvalue = 'NULL'
+ #
+ elif isinstance(tp, (model.StructOrUnion, model.EnumType)):
+ # a struct (not a struct pointer) as a function argument
+ 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 _convert_expr_from_c(self, tp, var):
+ if isinstance(tp, model.PrimitiveType):
+ return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
+ 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_deref((char *)%s, _cffi_type(%d))' % (
+ var, self._gettypenum(tp))
+ elif isinstance(tp, model.StructType):
+ 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: generates no code so far
+
+ _generate_cpy_typedef_collecttype = _generate_nothing
+ _generate_cpy_typedef_decl = _generate_nothing
+ _generate_cpy_typedef_method = _generate_nothing
+ _loading_cpy_typedef = _loaded_noop
+ _loaded_cpy_typedef = _loaded_noop
+
+ # ----------
+ # function declarations
+
+ def _generate_cpy_function_collecttype(self, tp, name):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ self._do_collect_type(tp)
+ else:
+ for type in tp.args:
+ self._do_collect_type(type)
+ self._do_collect_type(tp.result)
+
+ def _generate_cpy_function_decl(self, tp, name):
+ 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_const(False, name, tp)
+ return
+ prnt = self._prnt
+ numargs = len(tp.args)
+ if numargs == 0:
+ argname = 'no_arg'
+ elif numargs == 1:
+ argname = 'arg0'
+ else:
+ argname = 'args'
+ prnt('static PyObject *')
+ prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
+ prnt('{')
+ #
+ for i, type in enumerate(tp.args):
+ prnt(' %s;' % type.get_c_name(' x%d' % i))
+ if not isinstance(tp.result, model.VoidType):
+ result_code = 'result = '
+ prnt(' %s;' % tp.result.get_c_name(' result'))
+ else:
+ 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_ParseTuple(args, "%s:%s", %s))' % (
+ 'O' * numargs, name, ', '.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();')
+ prnt(' { %s%s(%s); }' % (
+ result_code, name,
+ ', '.join(['x%d' % i for i in range(len(tp.args))])))
+ prnt(' _cffi_save_errno();')
+ prnt(' Py_END_ALLOW_THREADS')
+ prnt()
+ #
+ if result_code:
+ prnt(' return %s;' %
+ self._convert_expr_from_c(tp.result, 'result'))
+ else:
+ prnt(' Py_INCREF(Py_None);')
+ prnt(' return Py_None;')
+ prnt('}')
+ prnt()
+
+ def _generate_cpy_function_method(self, tp, name):
+ if tp.ellipsis:
+ return
+ numargs = len(tp.args)
+ if numargs == 0:
+ meth = 'METH_NOARGS'
+ elif numargs == 1:
+ meth = 'METH_O'
+ else:
+ meth = 'METH_VARARGS'
+ self._prnt(' {"%s", _cffi_f_%s, %s},' % (name, name, meth))
+
+ _loading_cpy_function = _loaded_noop
+
+ def _loaded_cpy_function(self, tp, name, module, library):
+ if tp.ellipsis:
+ return
+ setattr(library, name, getattr(module, name))
+
+ # ----------
+ # named structs
+
+ _generate_cpy_struct_collecttype = _generate_nothing
+
+ def _generate_cpy_struct_decl(self, tp, name):
+ assert name == tp.name
+ self._generate_struct_or_union_decl(tp, 'struct', name)
+
+ def _generate_cpy_struct_method(self, tp, name):
+ self._generate_struct_or_union_method(tp, 'struct', name)
+
+ def _loading_cpy_struct(self, tp, name, module):
+ self._loading_struct_or_union(tp, 'struct', name, module)
+
+ def _loaded_cpy_struct(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ def _generate_struct_or_union_decl(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ prnt = self._prnt
+ prnt('static void %s(%s *p)' % (checkfuncname, cname))
+ prnt('{')
+ prnt(' /* only to generate compile-time warnings or errors */')
+ for i in range(len(tp.fldnames)):
+ fname = tp.fldnames[i]
+ ftype = tp.fldtypes[i]
+ if (isinstance(ftype, model.PrimitiveType)
+ and ftype.is_integer_type()):
+ # accept all integers, but complain on float or double
+ prnt(' (void)((p->%s) << 1);' % fname)
+ else:
+ # only accept exactly the type declared. Note the parentheses
+ # around the '*tmp' below. In most cases they are not needed
+ # but don't hurt --- except test_struct_array_field.
+ prnt(' { %s = &p->%s; (void)tmp; }' % (
+ ftype.get_c_name('(*tmp)'), fname))
+ prnt('}')
+ prnt('static PyObject *')
+ prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,))
+ prnt('{')
+ prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
+ if tp.partial:
+ prnt(' static Py_ssize_t nums[] = {')
+ prnt(' sizeof(%s),' % cname)
+ prnt(' offsetof(struct _cffi_aligncheck, y),')
+ for fname in tp.fldnames:
+ prnt(' offsetof(%s, %s),' % (cname, fname))
+ prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
+ prnt(' -1')
+ prnt(' };')
+ prnt(' return _cffi_get_struct_layout(nums);')
+ else:
+ ffi = self.ffi
+ BStruct = ffi._get_cached_btype(tp)
+ conditions = [
+ 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
+ 'offsetof(struct _cffi_aligncheck, y) != %d' % (
+ ffi.alignof(BStruct),)]
+ for fname, ftype in zip(tp.fldnames, tp.fldtypes):
+ BField = ffi._get_cached_btype(ftype)
+ conditions += [
+ 'offsetof(%s, %s) != %d' % (
+ cname, fname, ffi.offsetof(BStruct, fname)),
+ 'sizeof(((%s *)0)->%s) != %d' % (
+ cname, fname, ffi.sizeof(BField))]
+ prnt(' if (%s ||' % conditions[0])
+ for i in range(1, len(conditions)-1):
+ prnt(' %s ||' % conditions[i])
+ prnt(' %s) {' % conditions[-1])
+ prnt(' Py_INCREF(Py_False);')
+ prnt(' return Py_False;')
+ prnt(' }')
+ prnt(' else {')
+ prnt(' Py_INCREF(Py_True);')
+ prnt(' return Py_True;')
+ prnt(' }')
+ prnt(' /* the next line is not executed, but compiled */')
+ prnt(' %s(0);' % (checkfuncname,))
+ prnt('}')
+ prnt()
+
+ def _generate_struct_or_union_method(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ self._prnt(' {"%s", %s, METH_NOARGS},' % (layoutfuncname,
+ layoutfuncname))
+
+ def _loading_struct_or_union(self, tp, prefix, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ function = getattr(module, layoutfuncname)
+ layout = function()
+ if layout is False:
+ raise ffiplatform.VerificationError(
+ "incompatible layout for %s" % cname)
+ elif layout is True:
+ assert not tp.partial
+ else:
+ totalsize = layout[0]
+ totalalignment = layout[1]
+ fieldofs = layout[2::2]
+ fieldsize = layout[3::2]
+ assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+ tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+
+ def _loaded_struct_or_union(self, tp):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
+
+ # ----------
+ # 'anonymous' declarations. These are produced for anonymous structs
+ # or unions; the 'name' is obtained by a typedef.
+
+ _generate_cpy_anonymous_collecttype = _generate_nothing
+
+ def _generate_cpy_anonymous_decl(self, tp, name):
+ self._generate_struct_or_union_decl(tp, '', name)
+
+ def _generate_cpy_anonymous_method(self, tp, name):
+ self._generate_struct_or_union_method(tp, '', name)
+
+ def _loading_cpy_anonymous(self, tp, name, module):
+ self._loading_struct_or_union(tp, '', name, module)
+
+ def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ # ----------
+ # constants, likely declared with '#define'
+
+ def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+ vartp=None, delayed=True):
+ prnt = self._prnt
+ funcname = '_cffi_%s_%s' % (category, name)
+ prnt('static int %s(PyObject *lib)' % funcname)
+ prnt('{')
+ prnt(' PyObject *o;')
+ prnt(' int res;')
+ if not is_int:
+ prnt(' %s;' % (vartp or tp).get_c_name(' i'))
+ else:
+ assert category == 'const'
+ #
+ if not is_int:
+ if category == 'var':
+ realexpr = '&' + name
+ else:
+ realexpr = name
+ prnt(' i = (%s);' % (realexpr,))
+ prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i'),))
+ assert delayed
+ else:
+ prnt(' if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name))
+ prnt(' o = PyInt_FromLong((long)(%s));' % (name,))
+ prnt(' else if ((%s) <= 0)' % (name,))
+ prnt(' o = PyLong_FromLongLong((long long)(%s));' % (name,))
+ prnt(' else')
+ prnt(' o = PyLong_FromUnsignedLongLong('
+ '(unsigned long long)(%s));' % (name,))
+ prnt(' if (o == NULL)')
+ prnt(' return -1;')
+ prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name)
+ prnt(' Py_DECREF(o);')
+ prnt(' if (res < 0)')
+ prnt(' return -1;')
+ prnt(' return %s;' % self._chained_list_constants[delayed])
+ self._chained_list_constants[delayed] = funcname + '(lib)'
+ prnt('}')
+ prnt()
+
+ def _generate_cpy_constant_collecttype(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ if not is_int:
+ self._do_collect_type(tp)
+
+ def _generate_cpy_constant_decl(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ self._generate_cpy_const(is_int, name, tp)
+
+ _generate_cpy_constant_method = _generate_nothing
+ _loading_cpy_constant = _loaded_noop
+ _loaded_cpy_constant = _loaded_noop
+
+ # ----------
+ # enums
+
+ def _generate_cpy_enum_decl(self, tp, name):
+ if tp.partial:
+ for enumerator in tp.enumerators:
+ self._generate_cpy_const(True, enumerator, delayed=False)
+ return
+ #
+ funcname = '_cffi_enum_%s' % name
+ prnt = self._prnt
+ prnt('static int %s(PyObject *lib)' % funcname)
+ prnt('{')
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ prnt(' if (%s != %d) {' % (enumerator, enumvalue))
+ prnt(' PyErr_Format(_cffi_VerificationError,')
+ prnt(' "in enum %s: %s has the real value %d, '
+ 'not %d",')
+ prnt(' "%s", "%s", (int)%s, %d);' % (
+ name, enumerator, enumerator, enumvalue))
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' return %s;' % self._chained_list_constants[True])
+ self._chained_list_constants[True] = funcname + '(lib)'
+ prnt('}')
+ prnt()
+
+ _generate_cpy_enum_collecttype = _generate_nothing
+ _generate_cpy_enum_method = _generate_nothing
+ _loading_cpy_enum = _loaded_noop
+
+ def _loading_cpy_enum(self, tp, name, module):
+ if tp.partial:
+ enumvalues = [getattr(module, enumerator)
+ for enumerator in tp.enumerators]
+ tp.enumvalues = tuple(enumvalues)
+ tp.partial = False
+
+ def _loaded_cpy_enum(self, tp, name, module, library):
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ setattr(library, enumerator, enumvalue)
+
+ # ----------
+ # macros: for now only for integers
+
+ def _generate_cpy_macro_decl(self, tp, name):
+ assert tp == '...'
+ self._generate_cpy_const(True, name)
+
+ _generate_cpy_macro_collecttype = _generate_nothing
+ _generate_cpy_macro_method = _generate_nothing
+ _loading_cpy_macro = _loaded_noop
+ _loaded_cpy_macro = _loaded_noop
+
+ # ----------
+ # global variables
+
+ def _generate_cpy_variable_collecttype(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ self._do_collect_type(tp)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._do_collect_type(tp_ptr)
+
+ def _generate_cpy_variable_decl(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ tp_ptr = model.PointerType(tp.item)
+ self._generate_cpy_const(False, name, tp, vartp=tp_ptr)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._generate_cpy_const(False, name, tp_ptr, category='var')
+
+ _generate_cpy_variable_method = _generate_nothing
+ _loading_cpy_variable = _loaded_noop
+
+ def _loaded_cpy_variable(self, tp, name, module, library):
+ if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
+ return # sense that "a=..." is forbidden
+ # remove ptr=<cdata 'int *'> from the library instance, and replace
+ # it by a property on the class, which reads/writes into ptr[0].
+ ptr = getattr(library, name)
+ delattr(library, name)
+ def getter(library):
+ return ptr[0]
+ def setter(library, value):
+ ptr[0] = value
+ setattr(library.__class__, name, property(getter, setter))
+
+ # ----------
+
+ def _generate_setup_custom(self):
+ prnt = self._prnt
+ prnt('static PyObject *_cffi_setup_custom(PyObject *lib)')
+ prnt('{')
+ prnt(' if (%s < 0)' % self._chained_list_constants[True])
+ prnt(' return NULL;')
+ prnt(' Py_INCREF(Py_None);')
+ prnt(' return Py_None;')
+ prnt('}')
+
+cffimod_header = r'''
+#include <Python.h>
+#include <stddef.h>
+
+#define _cffi_from_c_double PyFloat_FromDouble
+#define _cffi_from_c_float PyFloat_FromDouble
+#define _cffi_from_c_signed_char PyInt_FromLong
+#define _cffi_from_c_short PyInt_FromLong
+#define _cffi_from_c_int PyInt_FromLong
+#define _cffi_from_c_long PyInt_FromLong
+#define _cffi_from_c_unsigned_char PyInt_FromLong
+#define _cffi_from_c_unsigned_short PyInt_FromLong
+#define _cffi_from_c_unsigned_long PyLong_FromUnsignedLong
+#define _cffi_from_c_unsigned_long_long PyLong_FromUnsignedLongLong
+
+#if SIZEOF_INT < SIZEOF_LONG
+# define _cffi_from_c_unsigned_int PyInt_FromLong
+#else
+# define _cffi_from_c_unsigned_int PyLong_FromUnsignedLong
+#endif
+
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+# define _cffi_from_c_long_long PyLong_FromLongLong
+#else
+# define _cffi_from_c_long_long PyInt_FromLong
+#endif
+
+#define _cffi_to_c_long PyInt_AsLong
+#define _cffi_to_c_double PyFloat_AsDouble
+#define _cffi_to_c_float PyFloat_AsDouble
+
+#define _cffi_to_c_char_p \
+ ((char *(*)(PyObject *))_cffi_exports[0])
+#define _cffi_to_c_signed_char \
+ ((signed char(*)(PyObject *))_cffi_exports[1])
+#define _cffi_to_c_unsigned_char \
+ ((unsigned char(*)(PyObject *))_cffi_exports[2])
+#define _cffi_to_c_short \
+ ((short(*)(PyObject *))_cffi_exports[3])
+#define _cffi_to_c_unsigned_short \
+ ((unsigned short(*)(PyObject *))_cffi_exports[4])
+
+#if SIZEOF_INT < SIZEOF_LONG
+# define _cffi_to_c_int \
+ ((int(*)(PyObject *))_cffi_exports[5])
+# define _cffi_to_c_unsigned_int \
+ ((unsigned int(*)(PyObject *))_cffi_exports[6])
+#else
+# define _cffi_to_c_int _cffi_to_c_long
+# define _cffi_to_c_unsigned_int _cffi_to_c_unsigned_long
+#endif
+
+#define _cffi_to_c_unsigned_long \
+ ((unsigned long(*)(PyObject *))_cffi_exports[7])
+#define _cffi_to_c_unsigned_long_long \
+ ((unsigned long long(*)(PyObject *))_cffi_exports[8])
+#define _cffi_to_c_char \
+ ((char(*)(PyObject *))_cffi_exports[9])
+#define _cffi_from_c_pointer \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10])
+#define _cffi_to_c_pointer \
+ ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11])
+#define _cffi_get_struct_layout \
+ ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12])
+#define _cffi_restore_errno \
+ ((void(*)(void))_cffi_exports[13])
+#define _cffi_save_errno \
+ ((void(*)(void))_cffi_exports[14])
+#define _cffi_from_c_char \
+ ((PyObject *(*)(char))_cffi_exports[15])
+#define _cffi_from_c_deref \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16])
+#define _cffi_to_c \
+ ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17])
+#define _cffi_from_c_struct \
+ ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18])
+#define _cffi_to_c_wchar_t \
+ ((wchar_t(*)(PyObject *))_cffi_exports[19])
+#define _cffi_from_c_wchar_t \
+ ((PyObject *(*)(wchar_t))_cffi_exports[20])
+#define _CFFI_NUM_EXPORTS 21
+
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+# define _cffi_to_c_long_long PyLong_AsLongLong
+#else
+# define _cffi_to_c_long_long _cffi_to_c_long
+#endif
+
+typedef struct _ctypedescr CTypeDescrObject;
+
+static void *_cffi_exports[_CFFI_NUM_EXPORTS];
+static PyObject *_cffi_types, *_cffi_VerificationError;
+
+static PyObject *_cffi_setup_custom(PyObject *lib); /* forward */
+
+static PyObject *_cffi_setup(PyObject *self, PyObject *args)
+{
+ PyObject *library;
+ if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError,
+ &library))
+ return NULL;
+ Py_INCREF(_cffi_types);
+ Py_INCREF(_cffi_VerificationError);
+ return _cffi_setup_custom(library);
+}
+
+static void _cffi_init(void)
+{
+ PyObject *module = PyImport_ImportModule("_cffi_backend");
+ PyObject *c_api_object;
+
+ if (module == NULL)
+ return;
+
+ c_api_object = PyObject_GetAttrString(module, "_C_API");
+ if (c_api_object == NULL)
+ return;
+ if (!PyCObject_Check(c_api_object)) {
+ PyErr_SetNone(PyExc_ImportError);
+ return;
+ }
+ memcpy(_cffi_exports, PyCObject_AsVoidPtr(c_api_object),
+ _CFFI_NUM_EXPORTS * sizeof(void *));
+}
+
+#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
+
+/**********/
+'''
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
new file mode 100644
index 0000000..68b9224
--- /dev/null
+++ b/cffi/vengine_gen.py
@@ -0,0 +1,420 @@
+import sys, os, binascii, imp, shutil
+from . import model, ffiplatform
+
+
+class VGenericEngine(object):
+ _class_key = 'g'
+ _gen_python_module = False
+
+ def __init__(self, verifier):
+ self.verifier = verifier
+ self.ffi = verifier.ffi
+
+ def collect_types(self):
+ pass # not needed in the generic engine
+
+ def _prnt(self, what=''):
+ print >> self._f, what
+
+ def write_source_to_f(self):
+ prnt = self._prnt
+ # first paste some standard set of lines that are mostly '#include'
+ prnt(cffimod_header)
+ # then paste the C source given by the user, verbatim.
+ prnt(self.verifier.preamble)
+ #
+ # call generate_gen_xxx_decl(), for every xxx found from
+ # ffi._parser._declarations. This generates all the functions.
+ self._generate('decl')
+
+ def load_library(self):
+ # import it with the CFFI backend
+ backend = self.ffi._backend
+ module = backend.load_library(self.verifier.modulefilename)
+ #
+ # call loading_gen_struct() to get the struct layout inferred by
+ # the C compiler
+ self._load(module, 'loading')
+ #
+ # build the FFILibrary class and instance
+ class FFILibrary(object):
+ _cffi_generic_module = module
+ library = FFILibrary()
+ #
+ # finally, call the loaded_gen_xxx() functions. This will set
+ # up the 'library' object.
+ self._load(module, 'loaded', library=library)
+ return library
+
+ def _generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ try:
+ method = getattr(self, '_generate_gen_%s_%s' % (kind,
+ step_name))
+ except AttributeError:
+ raise ffiplatform.VerificationError(
+ "not implemented in verify(): %r" % name)
+ method(tp, realname)
+
+ def _load(self, module, step_name, **kwds):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ method = getattr(self, '_%s_gen_%s' % (step_name, kind))
+ method(tp, realname, module, **kwds)
+
+ def _generate_nothing(self, tp, name):
+ pass
+
+ def _loaded_noop(self, tp, name, module, **kwds):
+ pass
+
+ # ----------
+ # typedefs: generates no code so far
+
+ _generate_gen_typedef_decl = _generate_nothing
+ _loading_gen_typedef = _loaded_noop
+ _loaded_gen_typedef = _loaded_noop
+
+ # ----------
+ # function declarations
+
+ def _generate_gen_function_decl(self, tp, name):
+ 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 _cffi_f_%s wrapper)
+ self._generate_gen_const(False, name, tp)
+ return
+ prnt = self._prnt
+ numargs = len(tp.args)
+ argnames = []
+ for i, type in enumerate(tp.args):
+ indirection = ''
+ if isinstance(type, model.StructOrUnion):
+ indirection = '*'
+ argnames.append('%sx%d' % (indirection, i))
+ arglist = [type.get_c_name(' %s' % arg)
+ for type, arg in zip(tp.args, argnames)]
+ arglist = ', '.join(arglist) or 'void'
+ funcdecl = ' _cffi_f_%s(%s)' % (name, arglist)
+ prnt(tp.result.get_c_name(funcdecl))
+ prnt('{')
+ #
+ if not isinstance(tp.result, model.VoidType):
+ result_code = 'return '
+ else:
+ result_code = ''
+ prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames)))
+ prnt('}')
+ prnt()
+
+ _loading_gen_function = _loaded_noop
+
+ def _loaded_gen_function(self, tp, name, module, library):
+ assert isinstance(tp, model.FunctionPtrType)
+ if tp.ellipsis:
+ newfunction = self._load_constant(False, tp, name, module)
+ else:
+ indirections = []
+ if any(isinstance(type, model.StructOrUnion) for type in tp.args):
+ indirect_args = []
+ for i, type in enumerate(tp.args):
+ if isinstance(type, model.StructOrUnion):
+ type = model.PointerType(type)
+ indirections.append((i, type))
+ indirect_args.append(type)
+ tp = model.FunctionPtrType(tuple(indirect_args),
+ tp.result, tp.ellipsis)
+ BFunc = self.ffi._get_cached_btype(tp)
+ wrappername = '_cffi_f_%s' % name
+ newfunction = module.load_function(BFunc, wrappername)
+ for i, type in indirections:
+ newfunction = self._make_struct_wrapper(newfunction, i, type)
+ setattr(library, name, newfunction)
+
+ def _make_struct_wrapper(self, oldfunc, i, tp):
+ backend = self.ffi._backend
+ BType = self.ffi._get_cached_btype(tp)
+ def newfunc(*args):
+ args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+ return oldfunc(*args)
+ return newfunc
+
+ # ----------
+ # named structs
+
+ def _generate_gen_struct_decl(self, tp, name):
+ assert name == tp.name
+ self._generate_struct_or_union_decl(tp, 'struct', name)
+
+ def _loading_gen_struct(self, tp, name, module):
+ self._loading_struct_or_union(tp, 'struct', name, module)
+
+ def _loaded_gen_struct(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ def _generate_struct_or_union_decl(self, tp, prefix, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ prnt = self._prnt
+ prnt('static void %s(%s *p)' % (checkfuncname, cname))
+ prnt('{')
+ prnt(' /* only to generate compile-time warnings or errors */')
+ for i in range(len(tp.fldnames)):
+ fname = tp.fldnames[i]
+ ftype = tp.fldtypes[i]
+ if (isinstance(ftype, model.PrimitiveType)
+ and ftype.is_integer_type()):
+ # accept all integers, but complain on float or double
+ prnt(' (void)((p->%s) << 1);' % fname)
+ else:
+ # only accept exactly the type declared. Note the parentheses
+ # around the '*tmp' below. In most cases they are not needed
+ # but don't hurt --- except test_struct_array_field.
+ prnt(' { %s = &p->%s; (void)tmp; }' % (
+ ftype.get_c_name('(*tmp)'), fname))
+ prnt('}')
+ prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,))
+ prnt('{')
+ prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
+ if tp.partial:
+ prnt(' static ssize_t nums[] = {')
+ prnt(' 1, sizeof(%s),' % cname)
+ prnt(' offsetof(struct _cffi_aligncheck, y),')
+ for fname in tp.fldnames:
+ prnt(' offsetof(%s, %s),' % (cname, fname))
+ prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
+ prnt(' -1')
+ prnt(' };')
+ prnt(' return nums[i];')
+ else:
+ ffi = self.ffi
+ BStruct = ffi._get_cached_btype(tp)
+ conditions = [
+ 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
+ 'offsetof(struct _cffi_aligncheck, y) != %d' % (
+ ffi.alignof(BStruct),)]
+ for fname, ftype in zip(tp.fldnames, tp.fldtypes):
+ BField = ffi._get_cached_btype(ftype)
+ conditions += [
+ 'offsetof(%s, %s) != %d' % (
+ cname, fname, ffi.offsetof(BStruct, fname)),
+ 'sizeof(((%s *)0)->%s) != %d' % (
+ cname, fname, ffi.sizeof(BField))]
+ prnt(' if (%s ||' % conditions[0])
+ for i in range(1, len(conditions)-1):
+ prnt(' %s ||' % conditions[i])
+ prnt(' %s) {' % conditions[-1])
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' else {')
+ prnt(' return 0;')
+ prnt(' }')
+ prnt(' /* the next line is not executed, but compiled */')
+ prnt(' %s(0);' % (checkfuncname,))
+ prnt('}')
+ prnt()
+
+ def _loading_struct_or_union(self, tp, prefix, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
+ cname = ('%s %s' % (prefix, name)).strip()
+ #
+ BFunc = self.ffi.typeof("ssize_t(*)(ssize_t)")
+ function = module.load_function(BFunc, layoutfuncname)
+ layout = function(0)
+ if layout < 0:
+ raise ffiplatform.VerificationError(
+ "incompatible layout for %s" % cname)
+ elif layout == 0:
+ assert not tp.partial
+ else:
+ totalsize = function(1)
+ totalalignment = function(2)
+ fieldofs = []
+ fieldsize = []
+ num = 3
+ while True:
+ x = function(num)
+ if x < 0: break
+ fieldofs.append(x)
+ fieldsize.append(function(num+1))
+ num += 2
+ assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
+ tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
+
+ def _loaded_struct_or_union(self, tp):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
+
+ # ----------
+ # 'anonymous' declarations. These are produced for anonymous structs
+ # or unions; the 'name' is obtained by a typedef.
+
+ def _generate_gen_anonymous_decl(self, tp, name):
+ self._generate_struct_or_union_decl(tp, '', name)
+
+ def _loading_gen_anonymous(self, tp, name, module):
+ self._loading_struct_or_union(tp, '', name, module)
+
+ def _loaded_gen_anonymous(self, tp, name, module, **kwds):
+ self._loaded_struct_or_union(tp)
+
+ # ----------
+ # constants, likely declared with '#define'
+
+ def _generate_gen_const(self, is_int, name, tp=None, category='const'):
+ prnt = self._prnt
+ funcname = '_cffi_%s_%s' % (category, name)
+ if is_int:
+ assert category == 'const'
+ prnt('int %s(long long *out_value)' % funcname)
+ prnt('{')
+ prnt(' *out_value = (long long)(%s);' % (name,))
+ prnt(' return (%s) <= 0;' % (name,))
+ prnt('}')
+ else:
+ assert tp is not None
+ prnt(tp.get_c_name(' %s(void)' % funcname),)
+ prnt('{')
+ if category == 'var':
+ ampersand = '&'
+ else:
+ ampersand = ''
+ prnt(' return (%s%s);' % (ampersand, name))
+ prnt('}')
+ prnt()
+
+ def _generate_gen_constant_decl(self, tp, name):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ self._generate_gen_const(is_int, name, tp)
+
+ _loading_gen_constant = _loaded_noop
+
+ def _load_constant(self, is_int, tp, name, module):
+ funcname = '_cffi_const_%s' % name
+ if is_int:
+ BFunc = self.ffi.typeof("int(*)(long long*)")
+ function = module.load_function(BFunc, funcname)
+ p = self.ffi.new("long long*")
+ negative = function(p)
+ value = int(p[0])
+ if value < 0 and not negative:
+ value += (1 << (8*self.ffi.sizeof("long long")))
+ else:
+ BFunc = self.ffi.typeof(tp.get_c_name('(*)(void)'))
+ function = module.load_function(BFunc, funcname)
+ value = function()
+ return value
+
+ def _loaded_gen_constant(self, tp, name, module, library):
+ is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+ value = self._load_constant(is_int, tp, name, module)
+ setattr(library, name, value)
+
+ # ----------
+ # enums
+
+ def _generate_gen_enum_decl(self, tp, name):
+ if tp.partial:
+ for enumerator in tp.enumerators:
+ self._generate_gen_const(True, enumerator)
+ return
+ #
+ funcname = '_cffi_enum_%s' % name
+ prnt = self._prnt
+ prnt('int %s(char *out_error)' % funcname)
+ prnt('{')
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ prnt(' if (%s != %d) {' % (enumerator, enumvalue))
+ prnt(' snprintf(out_error, 255, "in enum %s: '
+ '%s has the real value %d, not %d",')
+ prnt(' "%s", "%s", (int)%s, %d);' % (
+ name, enumerator, enumerator, enumvalue))
+ prnt(' return -1;')
+ prnt(' }')
+ prnt(' return 0;')
+ prnt('}')
+ prnt()
+
+ _loading_gen_enum = _loaded_noop
+
+ def _loading_gen_enum(self, tp, name, module):
+ if tp.partial:
+ enumvalues = [self._load_constant(True, tp, enumerator, module)
+ for enumerator in tp.enumerators]
+ tp.enumvalues = tuple(enumvalues)
+ tp.partial = False
+ else:
+ BFunc = self.ffi.typeof("int(*)(char*)")
+ funcname = '_cffi_enum_%s' % name
+ function = module.load_function(BFunc, funcname)
+ p = self.ffi.new("char[]", 256)
+ if function(p) < 0:
+ raise ffiplatform.VerificationError(self.ffi.string(p))
+
+ def _loaded_gen_enum(self, tp, name, module, library):
+ for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
+ setattr(library, enumerator, enumvalue)
+
+ # ----------
+ # macros: for now only for integers
+
+ def _generate_gen_macro_decl(self, tp, name):
+ assert tp == '...'
+ self._generate_gen_const(True, name)
+
+ _loading_gen_macro = _loaded_noop
+
+ def _loaded_gen_macro(self, tp, name, module, library):
+ value = self._load_constant(True, tp, name, module)
+ setattr(library, name, value)
+
+ # ----------
+ # global variables
+
+ def _generate_gen_variable_decl(self, tp, name):
+ if isinstance(tp, model.ArrayType):
+ tp_ptr = model.PointerType(tp.item)
+ self._generate_gen_const(False, name, tp_ptr)
+ else:
+ tp_ptr = model.PointerType(tp)
+ self._generate_gen_const(False, name, tp_ptr, category='var')
+
+ _loading_gen_variable = _loaded_noop
+
+ def _loaded_gen_variable(self, tp, name, module, library):
+ if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
+ # sense that "a=..." is forbidden
+ tp_ptr = model.PointerType(tp.item)
+ value = self._load_constant(False, tp_ptr, name, module)
+ setattr(library, name, value)
+ return
+ # remove ptr=<cdata 'int *'> from the library instance, and replace
+ # it by a property on the class, which reads/writes into ptr[0].
+ funcname = '_cffi_var_%s' % name
+ BFunc = self.ffi.typeof(tp.get_c_name('*(*)(void)'))
+ function = module.load_function(BFunc, funcname)
+ ptr = function()
+ def getter(library):
+ return ptr[0]
+ def setter(library, value):
+ ptr[0] = value
+ setattr(library.__class__, name, property(getter, setter))
+
+cffimod_header = r'''
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h> /* XXX for ssize_t */
+'''
diff --git a/cffi/verifier.py b/cffi/verifier.py
index 322169b..00aa5f5 100644
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -1,26 +1,25 @@
import sys, os, binascii, imp, shutil
-from . import model, ffiplatform
from . import __version__
+from . import ffiplatform
class Verifier(object):
_status = '?'
- def __init__(self, ffi, preamble, **kwds):
- import _cffi_backend
- if ffi._backend is not _cffi_backend:
- raise NotImplementedError(
- "verify() is only available for the _cffi_backend")
- #
+ def __init__(self, ffi, preamble, force_generic_engine=False, **kwds):
self.ffi = ffi
self.preamble = preamble
self.kwds = kwds
+ vengine_class = _locate_engine_class(ffi, force_generic_engine)
+ self._vengine = vengine_class(self)
#
- key = '\x00'.join(['2', sys.version[:3], __version__, preamble] +
+ key = '\x00'.join(['1', sys.version[:3], __version__, preamble] +
ffi._cdefsources)
- k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff).lstrip('0').rstrip('L')
- k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff).lstrip('0').rstrip('L')
- modulename = '_cffi_%s%s' % (k1, k2)
+ k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)
+ k1 = k1.lstrip('0x').rstrip('L')
+ k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)
+ k2 = k2.lstrip('0').rstrip('L')
+ modulename = '_cffi_%s%s%s' % (self._vengine._class_key, k1, k2)
suffix = _get_so_suffix()
self.sourcefilename = os.path.join(_TMPDIR, modulename + '.c')
self.modulefilename = os.path.join(_TMPDIR, modulename + suffix)
@@ -71,7 +70,7 @@ class Verifier(object):
return ffiplatform.get_extension(sourcename, modname, **self.kwds)
def generates_python_module(self):
- return False
+ return self._vengine._gen_python_module
# ----------
@@ -84,36 +83,23 @@ class Verifier(object):
if f is not None:
f.close()
self.modulefilename = filename
+ self._vengine.collect_types()
self._status = 'module'
- def _prnt(self, what=''):
- print >> self._f, what
-
def _write_source(self, file=None):
must_close = (file is None)
if must_close:
_ensure_dir(self.sourcefilename)
file = open(self.sourcefilename, 'w')
- self._f = file
+ self._vengine._f = file
try:
- self._write_source_to_f()
+ self._vengine.write_source_to_f()
finally:
- del self._f
+ del self._vengine._f
if must_close:
file.close()
self._status = 'source'
- def _write_source_to_f(self):
- prnt = self._prnt
- # first paste some standard set of lines that are mostly '#include'
- prnt(cffimod_header)
- # then paste the C source given by the user, verbatim.
- prnt(self.preamble)
- #
- # call generate_cpy_xxx_decl(), for every xxx found from
- # ffi._parser._declarations. This generates all the functions.
- self._generate("decl")
-
def _compile_module(self):
# compile this C source
tmpdir = os.path.dirname(self.sourcefilename)
@@ -128,411 +114,31 @@ class Verifier(object):
self._status = 'module'
def _load_library(self):
- # XXX review all usages of 'self' here!
- # import it with the CFFI backend
- backend = self.ffi._backend
- module = backend.load_library(self.modulefilename)
- #
- # call loading_cpy_struct() to get the struct layout inferred by
- # the C compiler
- self._load(module, 'loading')
- #
- # the C code will need the <ctype> objects. Collect them in
- # order in a list.
- #revmapping = dict([(value, key)
- # for (key, value) in self._typesdict.items()])
- #lst = [revmapping[i] for i in range(len(revmapping))]
- #lst = map(self.ffi._get_cached_btype, lst)
- #
- # build the FFILibrary class and instance and call _cffi_setup().
- # this will set up some fields like '_cffi_types', and only then
- # it will invoke the chained list of functions that will really
- # build (notably) the constant objects, as <cdata> if they are
- # pointers, and store them as attributes on the 'library' object.
- class FFILibrary(object):
- _cffi_module = module
- library = FFILibrary()
- #module._cffi_setup(lst, ffiplatform.VerificationError, library)
- #
- # finally, call the loaded_cpy_xxx() functions. This will perform
- # the final adjustments, like copying the Python->C wrapper
- # functions from the module to the 'library' object, and setting
- # up the FFILibrary class with properties for the global C variables.
- self._load(module, 'loaded', library=library)
- return library
-
- def _generate(self, step_name):
- for name, tp in self.ffi._parser._declarations.iteritems():
- kind, realname = name.split(' ', 1)
- try:
- method = getattr(self, '_generate_cpy_%s_%s' % (kind,
- step_name))
- except AttributeError:
- raise ffiplatform.VerificationError(
- "not implemented in verify(): %r" % name)
- method(tp, realname)
+ return self._vengine.load_library()
- def _load(self, module, step_name, **kwds):
- for name, tp in self.ffi._parser._declarations.iteritems():
- kind, realname = name.split(' ', 1)
- method = getattr(self, '_%s_cpy_%s' % (step_name, kind))
- method(tp, realname, module, **kwds)
-
- def _generate_nothing(self, tp, name):
- pass
-
- def _loaded_noop(self, tp, name, module, **kwds):
- pass
-
- # ----------
- # typedefs: generates no code so far
-
- _generate_cpy_typedef_decl = _generate_nothing
- _loading_cpy_typedef = _loaded_noop
- _loaded_cpy_typedef = _loaded_noop
-
- # ----------
- # function declarations
-
- def _generate_cpy_function_decl(self, tp, name):
- 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 _cffi_f_%s wrapper)
- self._generate_cpy_const(False, name, tp)
- return
- prnt = self._prnt
- numargs = len(tp.args)
- argnames = []
- for i, type in enumerate(tp.args):
- indirection = ''
- if isinstance(type, model.StructOrUnion):
- indirection = '*'
- argnames.append('%sx%d' % (indirection, i))
- arglist = [type.get_c_name(' %s' % arg)
- for type, arg in zip(tp.args, argnames)]
- arglist = ', '.join(arglist) or 'void'
- funcdecl = ' _cffi_f_%s(%s)' % (name, arglist)
- prnt(tp.result.get_c_name(funcdecl))
- prnt('{')
- #
- if not isinstance(tp.result, model.VoidType):
- result_code = 'return '
- else:
- result_code = ''
- prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames)))
- prnt('}')
- prnt()
-
- _loading_cpy_function = _loaded_noop
-
- def _loaded_cpy_function(self, tp, name, module, library):
- assert isinstance(tp, model.FunctionPtrType)
- if tp.ellipsis:
- newfunction = self._load_constant(False, tp, name, module)
- else:
- indirections = []
- if any(isinstance(type, model.StructOrUnion) for type in tp.args):
- indirect_args = []
- for i, type in enumerate(tp.args):
- if isinstance(type, model.StructOrUnion):
- type = model.PointerType(type)
- indirections.append((i, type))
- indirect_args.append(type)
- tp = model.FunctionPtrType(tuple(indirect_args),
- tp.result, tp.ellipsis)
- BFunc = self.ffi._get_cached_btype(tp)
- wrappername = '_cffi_f_%s' % name
- newfunction = module.load_function(BFunc, wrappername)
- for i, type in indirections:
- newfunction = self._make_struct_wrapper(newfunction, i, type)
- setattr(library, name, newfunction)
-
- def _make_struct_wrapper(self, oldfunc, i, tp):
- backend = self.ffi._backend
- BType = self.ffi._get_cached_btype(tp)
- def newfunc(*args):
- args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
- return oldfunc(*args)
- return newfunc
-
- # ----------
- # named structs
-
- def _generate_cpy_struct_decl(self, tp, name):
- assert name == tp.name
- self._generate_struct_or_union_decl(tp, 'struct', name)
-
- def _loading_cpy_struct(self, tp, name, module):
- self._loading_struct_or_union(tp, 'struct', name, module)
-
- def _loaded_cpy_struct(self, tp, name, module, **kwds):
- self._loaded_struct_or_union(tp)
-
- def _generate_struct_or_union_decl(self, tp, prefix, name):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- checkfuncname = '_cffi_check_%s_%s' % (prefix, name)
- layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- cname = ('%s %s' % (prefix, name)).strip()
- #
- prnt = self._prnt
- prnt('static void %s(%s *p)' % (checkfuncname, cname))
- prnt('{')
- prnt(' /* only to generate compile-time warnings or errors */')
- for i in range(len(tp.fldnames)):
- fname = tp.fldnames[i]
- ftype = tp.fldtypes[i]
- if (isinstance(ftype, model.PrimitiveType)
- and ftype.is_integer_type()):
- # accept all integers, but complain on float or double
- prnt(' (void)((p->%s) << 1);' % fname)
- else:
- # only accept exactly the type declared. Note the parentheses
- # around the '*tmp' below. In most cases they are not needed
- # but don't hurt --- except test_struct_array_field.
- prnt(' { %s = &p->%s; (void)tmp; }' % (
- ftype.get_c_name('(*tmp)'), fname))
- prnt('}')
- prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,))
- prnt('{')
- prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
- if tp.partial:
- prnt(' static ssize_t nums[] = {')
- prnt(' 1, sizeof(%s),' % cname)
- prnt(' offsetof(struct _cffi_aligncheck, y),')
- for fname in tp.fldnames:
- prnt(' offsetof(%s, %s),' % (cname, fname))
- prnt(' sizeof(((%s *)0)->%s),' % (cname, fname))
- prnt(' -1')
- prnt(' };')
- prnt(' return nums[i];')
- else:
- ffi = self.ffi
- BStruct = ffi._get_cached_btype(tp)
- conditions = [
- 'sizeof(%s) != %d' % (cname, ffi.sizeof(BStruct)),
- 'offsetof(struct _cffi_aligncheck, y) != %d' % (
- ffi.alignof(BStruct),)]
- for fname, ftype in zip(tp.fldnames, tp.fldtypes):
- BField = ffi._get_cached_btype(ftype)
- conditions += [
- 'offsetof(%s, %s) != %d' % (
- cname, fname, ffi.offsetof(BStruct, fname)),
- 'sizeof(((%s *)0)->%s) != %d' % (
- cname, fname, ffi.sizeof(BField))]
- prnt(' if (%s ||' % conditions[0])
- for i in range(1, len(conditions)-1):
- prnt(' %s ||' % conditions[i])
- prnt(' %s) {' % conditions[-1])
- prnt(' return -1;')
- prnt(' }')
- prnt(' else {')
- prnt(' return 0;')
- prnt(' }')
- prnt(' /* the next line is not executed, but compiled */')
- prnt(' %s(0);' % (checkfuncname,))
- prnt('}')
- prnt()
-
- def _loading_struct_or_union(self, tp, prefix, name, module):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- cname = ('%s %s' % (prefix, name)).strip()
- #
- BFunc = self.ffi.typeof("ssize_t(*)(ssize_t)")
- function = module.load_function(BFunc, layoutfuncname)
- layout = function(0)
- if layout < 0:
- raise ffiplatform.VerificationError(
- "incompatible layout for %s" % cname)
- elif layout == 0:
- assert not tp.partial
- else:
- totalsize = function(1)
- totalalignment = function(2)
- fieldofs = []
- fieldsize = []
- num = 3
- while True:
- x = function(num)
- if x < 0: break
- fieldofs.append(x)
- fieldsize.append(function(num+1))
- num += 2
- assert len(fieldofs) == len(fieldsize) == len(tp.fldnames)
- tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment
-
- def _loaded_struct_or_union(self, tp):
- if tp.fldnames is None:
- return # nothing to do with opaque structs
- self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
-
- # ----------
- # 'anonymous' declarations. These are produced for anonymous structs
- # or unions; the 'name' is obtained by a typedef.
-
- def _generate_cpy_anonymous_decl(self, tp, name):
- self._generate_struct_or_union_decl(tp, '', name)
-
- def _loading_cpy_anonymous(self, tp, name, module):
- self._loading_struct_or_union(tp, '', name, module)
-
- def _loaded_cpy_anonymous(self, tp, name, module, **kwds):
- self._loaded_struct_or_union(tp)
-
- # ----------
- # constants, likely declared with '#define'
-
- def _generate_cpy_const(self, is_int, name, tp=None, category='const'):
- prnt = self._prnt
- funcname = '_cffi_%s_%s' % (category, name)
- if is_int:
- assert category == 'const'
- prnt('int %s(long long *out_value)' % funcname)
- prnt('{')
- prnt(' *out_value = (long long)(%s);' % (name,))
- prnt(' return (%s) <= 0;' % (name,))
- prnt('}')
- else:
- assert tp is not None
- prnt(tp.get_c_name(' %s(void)' % funcname),)
- prnt('{')
- if category == 'var':
- ampersand = '&'
- else:
- ampersand = ''
- prnt(' return (%s%s);' % (ampersand, name))
- prnt('}')
- prnt()
-
- def _generate_cpy_constant_decl(self, tp, name):
- is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
- self._generate_cpy_const(is_int, name, tp)
-
- _loading_cpy_constant = _loaded_noop
-
- def _load_constant(self, is_int, tp, name, module):
- funcname = '_cffi_const_%s' % name
- if is_int:
- BFunc = self.ffi.typeof("int(*)(long long*)")
- function = module.load_function(BFunc, funcname)
- p = self.ffi.new("long long*")
- negative = function(p)
- value = int(p[0])
- if value < 0 and not negative:
- value += (1 << (8*self.ffi.sizeof("long long")))
- else:
- BFunc = self.ffi.typeof(tp.get_c_name('(*)(void)'))
- function = module.load_function(BFunc, funcname)
- value = function()
- return value
-
- def _loaded_cpy_constant(self, tp, name, module, library):
- is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
- value = self._load_constant(is_int, tp, name, module)
- setattr(library, name, value)
-
- # ----------
- # enums
-
- def _generate_cpy_enum_decl(self, tp, name):
- if tp.partial:
- for enumerator in tp.enumerators:
- self._generate_cpy_const(True, enumerator)
- return
- #
- funcname = '_cffi_enum_%s' % name
- prnt = self._prnt
- prnt('int %s(char *out_error)' % funcname)
- prnt('{')
- for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- prnt(' if (%s != %d) {' % (enumerator, enumvalue))
- prnt(' snprintf(out_error, 255, "in enum %s: '
- '%s has the real value %d, not %d",')
- prnt(' "%s", "%s", (int)%s, %d);' % (
- name, enumerator, enumerator, enumvalue))
- prnt(' return -1;')
- prnt(' }')
- prnt(' return 0;')
- prnt('}')
- prnt()
-
- _loading_cpy_enum = _loaded_noop
-
- def _loading_cpy_enum(self, tp, name, module):
- if tp.partial:
- enumvalues = [self._load_constant(True, tp, enumerator, module)
- for enumerator in tp.enumerators]
- tp.enumvalues = tuple(enumvalues)
- tp.partial = False
- else:
- BFunc = self.ffi.typeof("int(*)(char*)")
- funcname = '_cffi_enum_%s' % name
- function = module.load_function(BFunc, funcname)
- p = self.ffi.new("char[]", 256)
- if function(p) < 0:
- raise ffiplatform.VerificationError(self.ffi.string(p))
-
- def _loaded_cpy_enum(self, tp, name, module, library):
- for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
- setattr(library, enumerator, enumvalue)
-
- # ----------
- # macros: for now only for integers
-
- def _generate_cpy_macro_decl(self, tp, name):
- assert tp == '...'
- self._generate_cpy_const(True, name)
-
- _loading_cpy_macro = _loaded_noop
-
- def _loaded_cpy_macro(self, tp, name, module, library):
- value = self._load_constant(True, tp, name, module)
- setattr(library, name, value)
+# ____________________________________________________________
- # ----------
- # global variables
+_FORCE_GENERIC_ENGINE = False # for tests
- def _generate_cpy_variable_decl(self, tp, name):
- if isinstance(tp, model.ArrayType):
- tp_ptr = model.PointerType(tp.item)
- self._generate_cpy_const(False, name, tp_ptr)
+def _locate_engine_class(ffi, force_generic_engine):
+ if _FORCE_GENERIC_ENGINE:
+ force_generic_engine = True
+ if not force_generic_engine:
+ if '__pypy__' in sys.builtin_module_names:
+ force_generic_engine = True
else:
- tp_ptr = model.PointerType(tp)
- self._generate_cpy_const(False, name, tp_ptr, category='var')
-
- _loading_cpy_variable = _loaded_noop
-
- def _loaded_cpy_variable(self, tp, name, module, library):
- if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the
- # sense that "a=..." is forbidden
- tp_ptr = model.PointerType(tp.item)
- value = self._load_constant(False, tp_ptr, name, module)
- setattr(library, name, value)
- return
- # remove ptr=<cdata 'int *'> from the library instance, and replace
- # it by a property on the class, which reads/writes into ptr[0].
- funcname = '_cffi_var_%s' % name
- BFunc = self.ffi.typeof(tp.get_c_name('*(*)(void)'))
- function = module.load_function(BFunc, funcname)
- ptr = function()
- def getter(library):
- return ptr[0]
- def setter(library, value):
- ptr[0] = value
- setattr(library.__class__, name, property(getter, setter))
-
-cffimod_header = r'''
-#include <stdio.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <sys/types.h> /* XXX for ssize_t */
-'''
+ try:
+ import _cffi_backend
+ except ImportError:
+ _cffi_backend = '?'
+ if ffi._backend is not _cffi_backend:
+ force_generic_engine = True
+ if force_generic_engine:
+ from . import vengine_gen
+ return vengine_gen.VGenericEngine
+ else:
+ from . import vengine_cpy
+ return vengine_cpy.VCPythonEngine
# ____________________________________________________________
diff --git a/testing/test_verify.py b/testing/test_verify.py
index e9f5d74..13e7192 100644
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -15,6 +15,19 @@ def setup_module():
cffi.verifier.cleanup_tmpdir()
+def test_module_type():
+ import cffi.verifier
+ ffi = FFI()
+ lib = ffi.verify()
+ if hasattr(lib, '_cffi_python_module'):
+ print 'verify got a PYTHON module'
+ if hasattr(lib, '_cffi_generic_module'):
+ print 'verify got a GENERIC module'
+ expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or
+ '__pypy__' in sys.builtin_module_names)
+ assert hasattr(lib, '_cffi_python_module') == (not expected_generic)
+ assert hasattr(lib, '_cffi_generic_module') == expected_generic
+
def test_missing_function():
ffi = FFI()
ffi.cdef("void some_completely_unknown_function();")
@@ -474,11 +487,14 @@ def test_access_callback():
lib.cb = my_callback
assert lib.foo(4) == 887
-def test_cannot_verify_with_ctypes():
+def test_ctypes_backend_forces_generic_engine():
from cffi.backend_ctypes import CTypesBackend
ffi = FFI(backend=CTypesBackend())
- ffi.cdef("int a;")
- py.test.raises(NotImplementedError, ffi.verify, "int a;")
+ ffi.cdef("int func(int a);")
+ lib = ffi.verify("int func(int a) { return a * 42; }")
+ assert not hasattr(lib, '_cffi_python_module')
+ assert hasattr(lib, '_cffi_generic_module')
+ assert lib.func(100) == 4200
def test_call_with_struct_ptr():
ffi = FFI()
diff --git a/testing/test_vgen.py b/testing/test_vgen.py
new file mode 100644
index 0000000..1a7e05d
--- /dev/null
+++ b/testing/test_vgen.py
@@ -0,0 +1,12 @@
+import cffi.verifier
+from .test_verify import *
+
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir()
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+ # Runs all tests with _FORCE_GENERIC_ENGINE = True, to make sure we
+ # also test vengine_gen.py.
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/test_vgen2.py b/testing/test_vgen2.py
new file mode 100644
index 0000000..34147c8
--- /dev/null
+++ b/testing/test_vgen2.py
@@ -0,0 +1,13 @@
+import cffi.verifier
+from .test_vgen import *
+
+# This test file runs normally after test_vgen. We only clean up the .c
+# sources, to check that it also works when we have only the .so. The
+# tests should run much faster than test_vgen.
+
+def setup_module():
+ cffi.verifier.cleanup_tmpdir(keep_so=True)
+ cffi.verifier._FORCE_GENERIC_ENGINE = True
+
+def teardown_module():
+ cffi.verifier._FORCE_GENERIC_ENGINE = False
diff --git a/testing/test_zdistutils.py b/testing/test_zdistutils.py
index 7272ab5..92b5dd8 100644
--- a/testing/test_zdistutils.py
+++ b/testing/test_zdistutils.py
@@ -1,134 +1,159 @@
-import os, imp, math, StringIO, random
+import sys, os, imp, math, StringIO, random
import py
from cffi import FFI, FFIError
-from cffi.verifier import Verifier
+from cffi.verifier import Verifier, _locate_engine_class
from testing.udir import udir
-def test_write_source():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.write_source()
- with file(v.sourcefilename, 'r') as f:
- data = f.read()
- assert csrc in data
-
-def test_write_source_explicit_filename():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.sourcefilename = filename = str(udir.join('write_source.c'))
- v.write_source()
- assert filename == v.sourcefilename
- with file(filename, 'r') as f:
- data = f.read()
- assert csrc in data
-
-def test_write_source_to_file_obj():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- f = StringIO.StringIO()
- v.write_source(file=f)
- assert csrc in f.getvalue()
-
-def test_compile_module():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.compile_module()
- assert v.get_module_name().startswith('_cffi_')
- if v.generates_python_module():
- mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
- assert hasattr(mod, '_cffi_setup')
-
-def test_compile_module_explicit_filename():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!2*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- v.modulefilename = filename = str(udir.join('test_compile_module.so'))
- v.compile_module()
- assert filename == v.modulefilename
- assert v.get_module_name() == 'test_compile_module'
- if v.generates_python_module():
- mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
- assert hasattr(mod, '_cffi_setup')
-
-def test_name_from_checksum_of_cdef():
- names = []
- for csrc in ['double', 'double', 'float']:
+class DistUtilsTest(object):
+
+ def test_locate_engine_class(self):
+ cls = _locate_engine_class(FFI(), self.generic)
+ if self.generic:
+ # asked for the generic engine, which must not generate a
+ # CPython extension module
+ assert not cls._gen_python_module
+ else:
+ # asked for the CPython engine: check that we got it, unless
+ # we are running on top of PyPy, where the generic engine is
+ # always better
+ if '__pypy__' not in sys.builtin_module_names:
+ assert cls._gen_python_module
+
+ def test_write_source(self):
ffi = FFI()
- ffi.cdef("%s sin(double x);" % csrc)
- v = Verifier(ffi, "#include <math.h>")
- names.append(v.get_module_name())
- assert names[0] == names[1] != names[2]
-
-def test_name_from_checksum_of_csrc():
- names = []
- for csrc in ['123', '123', '1234']:
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.write_source()
+ with file(v.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_write_source_explicit_filename(self):
ffi = FFI()
ffi.cdef("double sin(double x);")
- v = Verifier(ffi, csrc)
- names.append(v.get_module_name())
- assert names[0] == names[1] != names[2]
-
-def test_load_library():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!3*/\n#include <math.h>\n'
- v = Verifier(ffi, csrc)
- library = v.load_library()
- assert library.sin(12.3) == math.sin(12.3)
-
-def test_verifier_args():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!4*/#include "test_verifier_args.h"\n'
- udir.join('test_verifier_args.h').write('#include <math.h>\n')
- v = Verifier(ffi, csrc, include_dirs=[str(udir)])
- library = v.load_library()
- assert library.sin(12.3) == math.sin(12.3)
-
-def test_verifier_object_from_ffi():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = "/*6*/\n#include <math.h>"
- lib = ffi.verify(csrc)
- assert lib.sin(12.3) == math.sin(12.3)
- assert isinstance(ffi.verifier, Verifier)
- with file(ffi.verifier.sourcefilename, 'r') as f:
- data = f.read()
- assert csrc in data
-
-def test_extension_object():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '''/*7*/
-#include <math.h>
-#ifndef TEST_EXTENSION_OBJECT
-# error "define_macros missing"
-#endif
-'''
- lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')])
- assert lib.sin(12.3) == math.sin(12.3)
- v = ffi.verifier
- ext = v.get_extension()
- assert str(ext.__class__) == 'distutils.extension.Extension'
- assert ext.sources == [v.sourcefilename]
- assert ext.name == v.get_module_name()
- assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
-
-def test_extension_forces_write_source():
- ffi = FFI()
- ffi.cdef("double sin(double x);")
- csrc = '/*hi there!%r*/\n#include <math.h>\n' % random.random()
- v = Verifier(ffi, csrc)
- assert not os.path.exists(v.sourcefilename)
- v.get_extension()
- assert os.path.exists(v.sourcefilename)
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.sourcefilename = filename = str(udir.join('write_source.c'))
+ v.write_source()
+ assert filename == v.sourcefilename
+ with file(filename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_write_source_to_file_obj(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ f = StringIO.StringIO()
+ v.write_source(file=f)
+ assert csrc in f.getvalue()
+
+ def test_compile_module(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.compile_module()
+ assert v.get_module_name().startswith('_cffi_')
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
+
+ def test_compile_module_explicit_filename(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!2*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ v.modulefilename = filename = str(udir.join('test_compile_module.so'))
+ v.compile_module()
+ assert filename == v.modulefilename
+ assert v.get_module_name() == 'test_compile_module'
+ if v.generates_python_module():
+ mod = imp.load_dynamic(v.get_module_name(), v.modulefilename)
+ assert hasattr(mod, '_cffi_setup')
+
+ def test_name_from_checksum_of_cdef(self):
+ names = []
+ for csrc in ['double', 'double', 'float']:
+ ffi = FFI()
+ ffi.cdef("%s sin(double x);" % csrc)
+ v = Verifier(ffi, "#include <math.h>",
+ force_generic_engine=self.generic)
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_name_from_checksum_of_csrc(self):
+ names = []
+ for csrc in ['123', '123', '1234']:
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ names.append(v.get_module_name())
+ assert names[0] == names[1] != names[2]
+
+ def test_load_library(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!3*/\n#include <math.h>\n'
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_args(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!4*/#include "test_verifier_args.h"\n'
+ udir.join('test_verifier_args.h').write('#include <math.h>\n')
+ v = Verifier(ffi, csrc, include_dirs=[str(udir)],
+ force_generic_engine=self.generic)
+ library = v.load_library()
+ assert library.sin(12.3) == math.sin(12.3)
+
+ def test_verifier_object_from_ffi(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = "/*6*/\n#include <math.h>"
+ lib = ffi.verify(csrc, force_generic_engine=self.generic)
+ assert lib.sin(12.3) == math.sin(12.3)
+ assert isinstance(ffi.verifier, Verifier)
+ with file(ffi.verifier.sourcefilename, 'r') as f:
+ data = f.read()
+ assert csrc in data
+
+ def test_extension_object(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '''/*7*/
+ #include <math.h>
+ #ifndef TEST_EXTENSION_OBJECT
+ # error "define_macros missing"
+ #endif
+ '''
+ lib = ffi.verify(csrc, define_macros=[('TEST_EXTENSION_OBJECT', '1')],
+ force_generic_engine=self.generic)
+ assert lib.sin(12.3) == math.sin(12.3)
+ v = ffi.verifier
+ ext = v.get_extension()
+ assert str(ext.__class__) == 'distutils.extension.Extension'
+ assert ext.sources == [v.sourcefilename]
+ assert ext.name == v.get_module_name()
+ assert ext.define_macros == [('TEST_EXTENSION_OBJECT', '1')]
+
+ def test_extension_forces_write_source(self):
+ ffi = FFI()
+ ffi.cdef("double sin(double x);")
+ csrc = '/*hi there!%r*/\n#include <math.h>\n' % random.random()
+ v = Verifier(ffi, csrc, force_generic_engine=self.generic)
+ assert not os.path.exists(v.sourcefilename)
+ v.get_extension()
+ assert os.path.exists(v.sourcefilename)
+
+
+class TestDistUtilsCPython(DistUtilsTest):
+ generic = False
+
+class TestDistUtilsGeneric(DistUtilsTest):
+ generic = True