[pypy-commit] cffi default: hg merge cpy-extension
arigo
noreply at buildbot.pypy.org
Thu Jun 14 13:39:25 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r334:57830003252d
Date: 2012-06-14 13:39 +0200
http://bitbucket.org/cffi/cffi/changeset/57830003252d/
Log: hg merge cpy-extension
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -22,28 +22,11 @@
Next steps
----------
-the verify() step, which should handle:
+the verify() step, which is missing:
-* completing "...;" structs
+* global variables
-* checking the other structs, and the arguments to functions, using the real C compiler
+* typedef ... some_integer_type;
-* simple "#define FOO value" macros
-
-* macros of the kind "#define funcname otherfuncname"
-
-* more complicated macros "#define foo(a, b, c) ..."
-
-* checking and correcting the value of the enum {} declarations
-
-* probably also fixing the array lengths, e.g. declared as a field "int foo[...];"
-
-generating C extensions:
-
-* this is needed anyway to call macros
-
-* faster, libffi-free way to call C code
-
-* partial blockers: callbacks (probably still use libffi)
_ffi backend for PyPy
diff --git a/c/_ffi_backend.c b/c/_ffi_backend.c
--- a/c/_ffi_backend.c
+++ b/c/_ffi_backend.c
@@ -579,10 +579,55 @@
return 0;
}
+static int _convert_overflow(PyObject *init, const char *ct_name)
+{
+ PyObject *s;
+ if (PyErr_Occurred()) /* already an exception pending */
+ return -1;
+ s = PyObject_Str(init);
+ if (s == NULL)
+ return -1;
+ PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'",
+ PyString_AS_STRING(s), ct_name);
+ Py_DECREF(s);
+ return -1;
+}
+
+static int _convert_to_char(PyObject *init)
+{
+ if (PyString_Check(init) && PyString_GET_SIZE(init) == 1) {
+ return (unsigned char)(PyString_AS_STRING(init)[0]);
+ }
+ if (CData_Check(init) &&
+ (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR)) {
+ return (unsigned char)(((CDataObject *)init)->c_data[0]);
+ }
+ PyErr_Format(PyExc_TypeError,
+ "initializer for ctype 'char' must be a string of length 1, "
+ "not %.200s", Py_TYPE(init)->tp_name);
+ return -1;
+}
+
+static int _convert_error(PyObject *init, const char *ct_name,
+ const char *expected)
+{
+ if (CData_Check(init))
+ PyErr_Format(PyExc_TypeError,
+ "initializer for ctype '%s' must be a %s, "
+ "not cdata '%s'",
+ ct_name, expected,
+ ((CDataObject *)init)->c_type->ct_name);
+ else
+ PyErr_Format(PyExc_TypeError,
+ "initializer for ctype '%s' must be a %s, "
+ "not %.200s",
+ ct_name, expected, Py_TYPE(init)->tp_name);
+ return -1;
+}
+
static int
convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
{
- PyObject *s;
const char *expected;
char buf[sizeof(PY_LONG_LONG)];
@@ -695,17 +740,11 @@
return 0;
}
if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
- if (PyString_Check(init) && PyString_GET_SIZE(init) == 1) {
- data[0] = PyString_AS_STRING(init)[0];
- return 0;
- }
- if (CData_Check(init) &&
- (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR)) {
- data[0] = ((CDataObject *)init)->c_data[0];
- return 0;
- }
- expected = "string of length 1";
- goto cannot_convert;
+ int res = _convert_to_char(init);
+ if (res < 0)
+ return -1;
+ data[0] = res;
+ return 0;
}
if (ct->ct_flags & CT_STRUCT) {
@@ -773,27 +812,10 @@
return -1;
overflow:
- s = PyObject_Str(init);
- if (s == NULL)
- return -1;
- PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'",
- PyString_AS_STRING(s), ct->ct_name);
- Py_DECREF(s);
- return -1;
+ return _convert_overflow(init, ct->ct_name);
cannot_convert:
- if (CData_Check(init))
- PyErr_Format(PyExc_TypeError,
- "initializer for ctype '%s' must be a %s, "
- "not cdata '%s'",
- ct->ct_name, expected,
- ((CDataObject *)init)->c_type->ct_name);
- else
- PyErr_Format(PyExc_TypeError,
- "initializer for ctype '%s' must be a %s, "
- "not %.200s",
- ct->ct_name, expected, Py_TYPE(init)->tp_name);
- return -1;
+ return _convert_error(init, ct->ct_name, expected);
}
static int
@@ -1210,8 +1232,8 @@
ctptr);
not_implemented:
- Py_INCREF(Py_NotImplemented); \
- return Py_NotImplemented; \
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
}
static PyObject *
@@ -1988,7 +2010,7 @@
{ "ptrdiff_t", sizeof(ptrdiff_t) },
{ "size_t", sizeof(size_t) | UNSIGNED },
{ "ssize_t", sizeof(ssize_t) },
- { "wchar_t", sizeof(wchar_t) | UNSIGNED },
+ /*{ "wchar_t", sizeof(wchar_t) | UNSIGNED },*/
{ NULL }
};
#undef UNSIGNED
@@ -2127,12 +2149,17 @@
static PyObject *b_new_pointer_type(PyObject *self, PyObject *args)
{
CTypeDescrObject *td, *ctitem;
+ const char *extra;
if (!PyArg_ParseTuple(args, "O!:new_pointer_type",
&CTypeDescr_Type, &ctitem))
return NULL;
- td = ctypedescr_new_on_top(ctitem, " *", 2);
+ if (ctitem->ct_flags & CT_ARRAY)
+ extra = "(*)"; /* obscure case: see test_array_add */
+ else
+ extra = " *";
+ td = ctypedescr_new_on_top(ctitem, extra, 2);
if (td == NULL)
return NULL;
@@ -2249,12 +2276,14 @@
PyObject *fields, *interned_fields, *ignored;
int is_union, alignment;
Py_ssize_t offset, i, nb_fields, maxsize, prev_bit_position;
+ Py_ssize_t totalsize = -1;
+ int totalalignment = -1;
CFieldObject **previous, *prev_field;
- if (!PyArg_ParseTuple(args, "O!O!|O:complete_struct_or_union",
+ if (!PyArg_ParseTuple(args, "O!O!|Oni:complete_struct_or_union",
&CTypeDescr_Type, &ct,
&PyList_Type, &fields,
- &ignored))
+ &ignored, &totalsize, &totalalignment))
return NULL;
if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) ==
@@ -2286,19 +2315,19 @@
for (i=0; i<nb_fields; i++) {
PyObject *fname;
CTypeDescrObject *ftype;
- int fbitsize, falign, err, bitshift;
+ int fbitsize, falign, err, bitshift, foffset = -1;
CFieldObject *cf;
- if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!i:list item",
+ if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!i|i:list item",
&PyString_Type, &fname,
&CTypeDescr_Type, &ftype,
- &fbitsize))
+ &fbitsize, &foffset))
goto error;
if (ftype->ct_size < 0) {
PyErr_Format(PyExc_TypeError,
- "field '%s' has ctype '%s' of unknown size",
- PyString_AS_STRING(fname),
+ "field '%s.%s' has ctype '%s' of unknown size",
+ ct->ct_name, PyString_AS_STRING(fname),
ftype->ct_name);
goto error;
}
@@ -2309,8 +2338,12 @@
if (alignment < falign)
alignment = falign;
- /* align this field to its own 'falign' by inserting padding */
- offset = (offset + falign - 1) & ~(falign-1);
+ if (foffset < 0) {
+ /* align this field to its own 'falign' by inserting padding */
+ offset = (offset + falign - 1) & ~(falign-1);
+ }
+ else
+ offset = foffset;
if (fbitsize < 0 || (fbitsize == 8 * ftype->ct_size &&
!(ftype->ct_flags & CT_PRIMITIVE_CHAR))) {
@@ -2385,15 +2418,23 @@
if (is_union) {
assert(offset == 0);
- ct->ct_size = maxsize;
+ offset = maxsize;
}
else {
if (offset == 0)
offset = 1;
offset = (offset + alignment - 1) & ~(alignment-1);
- ct->ct_size = offset;
}
- ct->ct_length = alignment;
+ if (totalsize < 0)
+ totalsize = offset;
+ else if (totalsize < offset) {
+ PyErr_Format(PyExc_TypeError,
+ "%s cannot be of size %zd: there are fields at least "
+ "up to %zd", ct->ct_name, totalsize, offset);
+ goto error;
+ }
+ ct->ct_size = totalsize;
+ ct->ct_length = totalalignment < 0 ? alignment : totalalignment;
ct->ct_stuff = interned_fields;
ct->ct_flags &= ~CT_IS_OPAQUE;
@@ -3005,40 +3046,35 @@
return PyInt_FromLong(align);
}
-static PyObject *b_sizeof_type(PyObject *self, PyObject *arg)
+static PyObject *b_sizeof(PyObject *self, PyObject *arg)
{
- if (!CTypeDescr_Check(arg)) {
- PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object");
+ Py_ssize_t size;
+
+ if (CData_Check(arg)) {
+ CDataObject *cd = (CDataObject *)arg;
+
+ if (cd->c_type->ct_flags & CT_ARRAY)
+ size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
+ else
+ size = cd->c_type->ct_size;
+ }
+ else if (CTypeDescr_Check(arg)) {
+ if (((CTypeDescrObject *)arg)->ct_size < 0) {
+ PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size",
+ ((CTypeDescrObject *)arg)->ct_name);
+ return NULL;
+ }
+ size = ((CTypeDescrObject *)arg)->ct_size;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "expected a 'cdata' or 'ctype' object");
return NULL;
}
- if (((CTypeDescrObject *)arg)->ct_size < 0) {
- PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size",
- ((CTypeDescrObject *)arg)->ct_name);
- return NULL;
- }
- return PyInt_FromSsize_t(((CTypeDescrObject *)arg)->ct_size);
-}
-
-static PyObject *b_sizeof_instance(PyObject *self, PyObject *arg)
-{
- CDataObject *cd;
- Py_ssize_t size;
-
- if (!CData_Check(arg)) {
- PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
- return NULL;
- }
- cd = (CDataObject *)arg;
-
- if (cd->c_type->ct_flags & CT_ARRAY)
- size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size;
- else
- size = cd->c_type->ct_size;
-
return PyInt_FromSsize_t(size);
}
-static PyObject *b_typeof_instance(PyObject *self, PyObject *arg)
+static PyObject *b_typeof(PyObject *self, PyObject *arg)
{
PyObject *res;
@@ -3230,9 +3266,8 @@
{"cast", b_cast, METH_VARARGS},
{"callback", b_callback, METH_VARARGS},
{"alignof", b_alignof, METH_O},
- {"sizeof_type", b_sizeof_type, METH_O},
- {"sizeof_instance", b_sizeof_instance, METH_O},
- {"typeof_instance", b_typeof_instance, METH_O},
+ {"sizeof", b_sizeof, METH_O},
+ {"typeof", b_typeof, METH_O},
{"offsetof", b_offsetof, METH_VARARGS},
{"string", b_string, METH_VARARGS},
{"get_errno", b_get_errno, METH_NOARGS},
@@ -3242,6 +3277,121 @@
{NULL, NULL} /* Sentinel */
};
+/************************************************************/
+/* Functions used by '_cffi_N.so', the generated modules */
+
+static char *_cffi_to_c_char_p(PyObject *obj)
+{
+ if (PyString_Check(obj)) {
+ return PyString_AS_STRING(obj);
+ }
+ if (obj == Py_None) {
+ return NULL;
+ }
+ if (CData_Check(obj)) {
+ return ((CDataObject *)obj)->c_data;
+ }
+ _convert_error(obj, "char *", "compatible pointer");
+ return NULL;
+}
+
+#define _cffi_to_c_PRIMITIVE(TARGETNAME, TARGET) \
+static TARGET _cffi_to_c_##TARGETNAME(PyObject *obj) { \
+ long tmp = PyInt_AsLong(obj); \
+ if (tmp != (TARGET)tmp) \
+ return (TARGET)_convert_overflow(obj, #TARGET); \
+ return (TARGET)tmp; \
+}
+
+_cffi_to_c_PRIMITIVE(signed_char, signed char)
+_cffi_to_c_PRIMITIVE(unsigned_char, unsigned char)
+_cffi_to_c_PRIMITIVE(short, short)
+_cffi_to_c_PRIMITIVE(unsigned_short, unsigned short)
+#if SIZEOF_INT < SIZEOF_LONG
+_cffi_to_c_PRIMITIVE(int, int)
+_cffi_to_c_PRIMITIVE(unsigned_int, unsigned int)
+#endif
+
+#if SIZEOF_LONG < SIZEOF_LONG_LONG
+static unsigned long _cffi_to_c_unsigned_long(PyObject *obj)
+{
+ unsigned PY_LONG_LONG value = _my_PyLong_AsUnsignedLongLong(obj, 1);
+ if (value != (unsigned long)value)
+ return (unsigned long)_convert_overflow(obj, "unsigned long");
+ return (unsigned long)value;
+}
+#else
+# define _cffi_to_c_unsigned_long _cffi_to_c_unsigned_long_long
+#endif
+
+static unsigned PY_LONG_LONG _cffi_to_c_unsigned_long_long(PyObject *obj)
+{
+ return _my_PyLong_AsUnsignedLongLong(obj, 1);
+}
+
+static char _cffi_to_c_char(PyObject *obj)
+{
+ return (char)_convert_to_char(obj);
+}
+
+static PyObject *_cffi_from_c_pointer(char *ptr, CTypeDescrObject *ct)
+{
+ return convert_to_object((char *)&ptr, ct);
+}
+
+static char *_cffi_to_c_pointer(PyObject *obj, CTypeDescrObject *ct)
+{
+ char *result;
+ if (convert_from_object((char *)&result, ct, obj) < 0)
+ return NULL;
+ return result;
+}
+
+static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[])
+{
+ PyObject *result;
+ int count = 0;
+ while (nums[count] >= 0)
+ count++;
+
+ result = PyList_New(count);
+ if (result == NULL)
+ return NULL;
+
+ while (--count >= 0) {
+ PyObject *o = PyInt_FromSsize_t(nums[count]);
+ if (o == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ PyList_SET_ITEM(result, count, o);
+ }
+ return result;
+}
+
+static void *cffi_exports[] = {
+ _cffi_to_c_char_p,
+ _cffi_to_c_signed_char,
+ _cffi_to_c_unsigned_char,
+ _cffi_to_c_short,
+ _cffi_to_c_unsigned_short,
+#if SIZEOF_INT < SIZEOF_LONG
+ _cffi_to_c_int,
+ _cffi_to_c_unsigned_int,
+#else
+ 0,
+ 0,
+#endif
+ _cffi_to_c_unsigned_long,
+ _cffi_to_c_unsigned_long_long,
+ _cffi_to_c_char,
+ _cffi_from_c_pointer,
+ _cffi_to_c_pointer,
+ _cffi_get_struct_layout,
+};
+
+/************************************************************/
+
void init_ffi_backend(void)
{
PyObject *m, *v;
@@ -3270,4 +3420,8 @@
return;
if (PyType_Ready(&CDataWithDestructor_Type) < 0)
return;
+
+ v = PyCObject_FromVoidPtr((void *)cffi_exports, NULL);
+ if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
+ return;
}
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -11,16 +11,16 @@
def size_of_int():
BInt = new_primitive_type("int")
- return sizeof_type(BInt)
+ return sizeof(BInt)
def size_of_long():
BLong = new_primitive_type("long")
- return sizeof_type(BLong)
+ return sizeof(BLong)
def size_of_ptr():
BInt = new_primitive_type("int")
BPtr = new_pointer_type(BInt)
- return sizeof_type(BPtr)
+ return sizeof(BPtr)
def test_load_library():
@@ -55,14 +55,14 @@
assert (x != cast(q, -66)) is True
def test_sizeof_type():
- py.test.raises(TypeError, sizeof_type, 42.5)
+ py.test.raises(TypeError, sizeof, 42.5)
p = new_primitive_type("short")
- assert sizeof_type(p) == 2
+ assert sizeof(p) == 2
def test_integer_types():
for name in ['signed char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type(name)
- size = sizeof_type(p)
+ size = sizeof(p)
min = -(1 << (8*size-1))
max = (1 << (8*size-1)) - 1
assert int(cast(p, min)) == min
@@ -72,7 +72,7 @@
assert long(cast(p, min - 1)) == max
for name in ['char', 'short', 'int', 'long', 'long long']:
p = new_primitive_type('unsigned ' + name)
- size = sizeof_type(p)
+ size = sizeof(p)
max = (1 << (8*size)) - 1
assert int(cast(p, 0)) == 0
assert int(cast(p, max)) == max
@@ -268,6 +268,20 @@
assert a[i] == 100 + i
assert a[42] == 0 # extra uninitialized item
+def test_array_add():
+ p = new_primitive_type("int")
+ p1 = new_array_type(new_pointer_type(p), 5) # int[5]
+ p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5]
+ a = newp(p2, [range(n, n+5) for n in [100, 200, 300]])
+ assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % (
+ 3*5*size_of_int(),)
+ assert repr(a + 0) == "<cdata 'int(*)[5]'>"
+ assert repr(a[0]) == "<cdata 'int[5]'>"
+ assert repr((a + 0)[0]) == "<cdata 'int[5]'>"
+ assert repr(a[0] + 0) == "<cdata 'int *'>"
+ assert type(a[0][0]) is int
+ assert type((a[0] + 0)[0]) is int
+
def test_cast_primitive_from_cdata():
p = new_primitive_type("int")
n = cast(p, cast(p, -42))
@@ -338,9 +352,9 @@
def test_alignof():
BInt = new_primitive_type("int")
- assert alignof(BInt) == sizeof_type(BInt)
+ assert alignof(BInt) == sizeof(BInt)
BPtr = new_pointer_type(BInt)
- assert alignof(BPtr) == sizeof_type(BPtr)
+ assert alignof(BPtr) == sizeof(BPtr)
BArray = new_array_type(BPtr, None)
assert alignof(BArray) == alignof(BInt)
@@ -374,15 +388,15 @@
assert d[0][1].bitsize == -1
assert d[1][0] == 'a2'
assert d[1][1].type is BChar
- assert d[1][1].offset == sizeof_type(BLong)
+ assert d[1][1].offset == sizeof(BLong)
assert d[1][1].bitshift == -1
assert d[1][1].bitsize == -1
assert d[2][0] == 'a3'
assert d[2][1].type is BShort
- assert d[2][1].offset == sizeof_type(BLong) + sizeof_type(BShort)
+ assert d[2][1].offset == sizeof(BLong) + sizeof(BShort)
assert d[2][1].bitshift == -1
assert d[2][1].bitsize == -1
- assert sizeof_type(BStruct) == 2 * sizeof_type(BLong)
+ assert sizeof(BStruct) == 2 * sizeof(BLong)
assert alignof(BStruct) == alignof(BLong)
def test_complete_union():
@@ -400,7 +414,7 @@
assert d[1][0] == 'a2'
assert d[1][1].type is BChar
assert d[1][1].offset == 0
- assert sizeof_type(BUnion) == sizeof_type(BLong)
+ assert sizeof(BUnion) == sizeof(BLong)
assert alignof(BUnion) == alignof(BLong)
def test_struct_instance():
@@ -446,6 +460,15 @@
assert s.a2 == 456
assert s.a3 == 0
+def test_array_in_struct():
+ BInt = new_primitive_type("int")
+ BStruct = new_struct_type("foo")
+ BArrayInt5 = new_array_type(new_pointer_type(BInt), 5)
+ complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)])
+ s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]])
+ assert s.a1[2] == 27
+ assert repr(s.a1) == "<cdata 'int[5]'>"
+
def test_offsetof():
BInt = new_primitive_type("int")
BStruct = new_struct_type("foo")
@@ -505,7 +528,7 @@
BLongLong = new_primitive_type("long long")
BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False)
f = cast(BFunc2, _testfunc(2))
- longlong_max = (1 << (8*sizeof_type(BLongLong)-1)) - 1
+ longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1
assert f(longlong_max - 42, 42) == longlong_max
assert f(43, longlong_max - 42) == - longlong_max - 1
@@ -539,7 +562,7 @@
f = cast(BFunc6, _testfunc(6))
x = newp(BIntPtr, 42)
res = f(x)
- assert typeof_instance(res) is BIntPtr
+ assert typeof(res) is BIntPtr
assert res[0] == 42 - 1000
#
BIntArray = new_array_type(BIntPtr, None)
@@ -550,7 +573,7 @@
#
x = newp(BIntArray, [242])
res = f(x)
- assert typeof_instance(res) is BIntPtr
+ assert typeof(res) is BIntPtr
assert res[0] == 242 - 1000
def test_call_function_7():
@@ -660,14 +683,14 @@
def test_struct_with_bitfields():
BLong = new_primitive_type("long")
BStruct = new_struct_type("foo")
- LONGBITS = 8 * sizeof_type(BLong)
+ LONGBITS = 8 * sizeof(BLong)
complete_struct_or_union(BStruct, [('a1', BLong, 1),
('a2', BLong, 2),
('a3', BLong, 3),
('a4', BLong, LONGBITS - 5)])
d = _getfields(BStruct)
assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0
- assert d[3][1].offset == sizeof_type(BLong)
+ assert d[3][1].offset == sizeof(BLong)
assert d[0][1].bitshift == 0
assert d[0][1].bitsize == 1
assert d[1][1].bitshift == 1
@@ -676,7 +699,7 @@
assert d[2][1].bitsize == 3
assert d[3][1].bitshift == 0
assert d[3][1].bitsize == LONGBITS - 5
- assert sizeof_type(BStruct) == 2 * sizeof_type(BLong)
+ assert sizeof(BStruct) == 2 * sizeof(BLong)
assert alignof(BStruct) == alignof(BLong)
def test_bitfield_instance():
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -22,7 +22,12 @@
ffi.cdef("""
int printf(const char *, ...);
""")
- ffi.C.printf("hello, %s!\n", ffi.new("char[]", "world"))
+
+ C = ffi.rawload(name=None) # standard library
+ -or-
+ C = ffi.verify() # use a C compiler: verify the decl above is right
+
+ C.printf("hello, %s!\n", ffi.new("char[]", "world"))
'''
def __init__(self, backend=None):
@@ -46,7 +51,6 @@
self._new_types = new.module('new_types').__dict__
if hasattr(backend, 'set_ffi'):
backend.set_ffi(self)
- self.C = _make_ffi_library(self, None)
#
lines = []
by_size = {}
@@ -64,19 +68,19 @@
def cdef(self, csource):
"""Parse the given C source. This registers all declared functions,
types, and global variables. The functions and global variables can
- then be accessed via 'ffi.C' or 'ffi.load()'. The types can be used
+ then be accessed via 'ffi.rawload()'. The types can be used
in 'ffi.new()' and other functions.
"""
self._parser.parse(csource)
- def load(self, name):
+ def rawload(self, name):
"""Load and return a dynamic library identified by 'name'.
- The standard C library is preloaded into 'ffi.C'.
+ The standard C library can be loaded by passing None.
Note that functions and types declared by 'ffi.cdef()' are not
linked to a particular library, just like C headers; in the
library we only look for the actual (untyped) symbols.
"""
- assert isinstance(name, str)
+ assert isinstance(name, str) or name is None
return _make_ffi_library(self, name)
def typeof(self, cdecl):
@@ -89,11 +93,11 @@
return self._parsed_types[cdecl]
except KeyError:
type = self._parser.parse_type(cdecl)
- btype = type.get_backend_type(self)
+ btype = self._get_cached_btype(type)
self._parsed_types[cdecl] = btype
return btype
else:
- return self._backend.typeof_instance(cdecl)
+ return self._backend.typeof(cdecl)
def sizeof(self, cdecl):
"""Return the size in bytes of the argument. It can be a
@@ -101,23 +105,25 @@
"""
if isinstance(cdecl, basestring):
BType = self.typeof(cdecl)
- return self._backend.sizeof_type(BType)
+ return self._backend.sizeof(BType)
else:
- return self._backend.sizeof_instance(cdecl)
+ return self._backend.sizeof(cdecl)
def alignof(self, cdecl):
"""Return the natural alignment size in bytes of the C type
given as a string.
"""
- BType = self.typeof(cdecl)
- return self._backend.alignof(BType)
+ if isinstance(cdecl, basestring):
+ cdecl = self.typeof(cdecl)
+ return self._backend.alignof(cdecl)
def offsetof(self, cdecl, fieldname):
"""Return the offset of the named field inside the given
structure, which must be given as a C type name.
"""
- BType = self.typeof(cdecl)
- return self._backend.offsetof(BType, fieldname)
+ if isinstance(cdecl, basestring):
+ cdecl = self.typeof(cdecl)
+ return self._backend.offsetof(cdecl, fieldname)
def new(self, cdecl, init=None):
"""Allocate an instance 'x' of the named C type, and return a
@@ -141,7 +147,7 @@
BType = self._new_types[cdecl]
except KeyError:
type = self._parser.parse_type(cdecl, force_pointer=True)
- BType = type.get_backend_type(self)
+ BType = self._get_cached_btype(type)
self._new_types[cdecl] = BType
#
return self._backend.newp(BType, init)
@@ -179,10 +185,16 @@
return BType
def verify(self, preamble='', **kwargs):
- """ Verify that the current ffi signatures compile on this machine
+ """Verify that the current ffi signatures compile on this
+ machine, and return a dynamic library object. The dynamic
+ library can be used to call functions and access global
+ variables declared in this 'ffi'. The library is compiled
+ by the C compiler: it gives you C-level API compatibility
+ (including calling macros). This is unlike 'ffi.rawload()',
+ which requires binary compatibility in the signatures.
"""
from .verifier import Verifier
- return Verifier().verify(self, preamble, **kwargs)
+ return Verifier(self).verify(preamble, **kwargs)
def _make_ffi_library(ffi, libname):
name = libname
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -77,6 +77,10 @@
__slots__ = []
+class CTypesGenericArray(CTypesData):
+ __slots__ = []
+
+
class CTypesGenericPtr(CTypesData):
__slots__ = ['_address', '_as_ctype_ptr']
_automatic_casts = False
@@ -208,8 +212,8 @@
if size == ctypes.sizeof(ctypes.c_size_t):
result['size_t'] = size | UNSIGNED
result['ssize_t'] = size
- if size == ctypes.sizeof(ctypes.c_wchar):
- result['wchar_t'] = size | UNSIGNED
+ #if size == ctypes.sizeof(ctypes.c_wchar):
+ # result['wchar_t'] = size | UNSIGNED
return result
def load_library(self, path):
@@ -366,7 +370,10 @@
_bitem_size = ctypes.sizeof(BItem._ctype)
else:
_ctype = ctypes.c_void_p
- _reftypename = BItem._get_c_name(' * &')
+ if issubclass(BItem, CTypesGenericArray):
+ _reftypename = BItem._get_c_name('(* &)')
+ else:
+ _reftypename = BItem._get_c_name(' * &')
def __init__(self, init):
ctypeobj = BItem._create_ctype_obj(init)
@@ -436,7 +443,7 @@
else:
kind = 'generic'
#
- class CTypesArray(CTypesData):
+ class CTypesArray(CTypesGenericArray):
__slots__ = ['_blob', '_own']
if length is not None:
_ctype = BItem._ctype * length
@@ -760,13 +767,12 @@
p = ctypes.cast(bptr._as_ctype_ptr, ctypes.POINTER(ctypes.c_char))
return ''.join([p[i] for i in range(length)])
- def sizeof_type(self, BType):
- assert issubclass(BType, CTypesData)
- return BType._get_size()
-
- def sizeof_instance(self, cdata):
- assert isinstance(cdata, CTypesData)
- return cdata._get_size_of_instance()
+ def sizeof(self, cdata_or_BType):
+ if isinstance(cdata_or_BType, CTypesData):
+ return cdata_or_BType._get_size_of_instance()
+ else:
+ assert issubclass(cdata_or_BType, CTypesData)
+ return cdata_or_BType._get_size()
def alignof(self, BType):
assert issubclass(BType, CTypesData)
@@ -785,7 +791,7 @@
def callback(self, BType, source):
return BType(source)
- typeof_instance = type
+ typeof = type
class CTypesLibrary(object):
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -1,7 +1,10 @@
from . import api, model
-import pycparser, weakref
+import pycparser, weakref, re
+_r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE)
+_r_partial_enum = re.compile(r"\.\.\.\s*\}")
+_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
_parser_cache = None
def _get_parser():
@@ -10,26 +13,66 @@
_parser_cache = pycparser.CParser()
return _parser_cache
+def _preprocess(csource):
+ # Remove comments. NOTE: this only work because the cdef() section
+ # should not contain any string literal!
+ csource = _r_comment.sub(' ', csource)
+ # Replace "...}" with "__dotdotdotNUM__}". This construction should
+ # occur only at the end of enums; at the end of structs we have "...;}"
+ # and at the end of vararg functions "...);"
+ matches = list(_r_partial_enum.finditer(csource))
+ for number, match in enumerate(reversed(matches)):
+ p = match.start()
+ assert csource[p:p+3] == '...'
+ csource = '%s __dotdotdot%d__ %s' % (csource[:p], number,
+ csource[p+3:])
+ # Replace all remaining "..." with the same name, "__dotdotdot__",
+ # which is declared with a typedef for the purpose of C parsing.
+ return csource.replace('...', ' __dotdotdot__ ')
+
class Parser(object):
def __init__(self):
self._declarations = {}
self._anonymous_counter = 0
self._structnode2type = weakref.WeakKeyDictionary()
+ def _parse(self, csource):
+ # XXX: for more efficiency we would need to poke into the
+ # internals of CParser... the following registers the
+ # typedefs, because their presence or absence influences the
+ # parsing itself (but what they are typedef'ed to plays no role)
+ csourcelines = []
+ for name in sorted(self._declarations):
+ if name.startswith('typedef '):
+ csourcelines.append('typedef int %s;' % (name[8:],))
+ csourcelines.append('typedef int __dotdotdot__;')
+ csourcelines.append(_preprocess(csource))
+ csource = '\n'.join(csourcelines)
+ ast = _get_parser().parse(csource)
+ return ast
+
def parse(self, csource):
- csource = ("typedef int __dotdotdot__;\n" +
- csource.replace('...', '__dotdotdot__'))
- ast = _get_parser().parse(csource)
- for decl in ast.ext:
+ ast = self._parse(csource)
+ # find the first "__dotdotdot__" and use that as a separator
+ # between the repeated typedefs and the real csource
+ iterator = iter(ast.ext)
+ for decl in iterator:
+ if decl.name == '__dotdotdot__':
+ break
+ #
+ for decl in iterator:
if isinstance(decl, pycparser.c_ast.Decl):
self._parse_decl(decl)
elif isinstance(decl, pycparser.c_ast.Typedef):
if not decl.name:
raise api.CDefError("typedef does not declare any name",
decl)
- if decl.name != '__dotdotdot__':
- self._declare('typedef ' + decl.name,
- self._get_type(decl.type))
+ if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType)
+ and decl.type.type.names == ['__dotdotdot__']):
+ realtype = model.OpaqueType(decl.name)
+ else:
+ realtype = self._get_type(decl.type)
+ self._declare('typedef ' + decl.name, realtype)
else:
raise api.CDefError("unrecognized construct", decl)
@@ -54,33 +97,28 @@
decl)
#
if decl.name:
- self._declare('variable ' + decl.name, self._get_type(node))
+ tp = self._get_type(node)
+ if 'const' in decl.quals:
+ self._declare('constant ' + decl.name, tp)
+ else:
+ self._declare('variable ' + decl.name, tp)
- def parse_type(self, cdecl, force_pointer=False,
- convert_array_to_pointer=False):
- # XXX: for more efficiency we would need to poke into the
- # internals of CParser... the following registers the
- # typedefs, because their presence or absence influences the
- # parsing itself (but what they are typedef'ed to plays no role)
- csourcelines = []
- for name in sorted(self._declarations):
- if name.startswith('typedef '):
- csourcelines.append('typedef int %s;' % (name[8:],))
- #
- csourcelines.append('void __dummy(%s);' % cdecl)
- ast = _get_parser().parse('\n'.join(csourcelines))
+ def parse_type(self, cdecl, force_pointer=False):
+ ast = self._parse('void __dummy(%s);' % cdecl)
typenode = ast.ext[-1].type.args.params[0].type
- return self._get_type(typenode, force_pointer=force_pointer,
- convert_array_to_pointer=convert_array_to_pointer)
+ return self._get_type(typenode, force_pointer=force_pointer)
def _declare(self, name, obj):
if name in self._declarations:
raise api.FFIError("multiple declarations of %s" % (name,))
+ assert name != '__dotdotdot__'
self._declarations[name] = obj
- def _get_type_pointer(self, type):
+ def _get_type_pointer(self, type, const=False):
if isinstance(type, model.FunctionType):
return type # "pointer-to-function" ~== "function"
+ if const:
+ return model.ConstPointerType(type)
return model.PointerType(type)
def _get_type(self, typenode, convert_array_to_pointer=False,
@@ -114,7 +152,10 @@
#
if isinstance(typenode, pycparser.c_ast.PtrDecl):
# pointer type
- return self._get_type_pointer(self._get_type(typenode.type))
+ const = (isinstance(typenode.type, pycparser.c_ast.TypeDecl)
+ and 'const' in typenode.type.quals)
+ return self._get_type_pointer(self._get_type(typenode.type), const)
+
#
if isinstance(typenode, pycparser.c_ast.TypeDecl):
type = typenode.type
@@ -132,6 +173,8 @@
ident = ' '.join(names)
if ident == 'void':
return model.void_type
+ if ident == '__dotdotdot__':
+ raise api.FFIError('bad usage of "..."')
return model.PrimitiveType(ident)
#
if isinstance(type, pycparser.c_ast.Struct):
@@ -152,7 +195,7 @@
#
raise api.FFIError("bad or unsupported type declaration")
- def _parse_function_type(self, typenode, name=None):
+ def _parse_function_type(self, typenode, funcname=None):
params = list(getattr(typenode.args, 'params', []))
ellipsis = (
len(params) > 0 and
@@ -171,7 +214,7 @@
convert_array_to_pointer=True)
for argdeclnode in params]
result = self._get_type(typenode.type)
- return model.FunctionType(name, tuple(args), result, ellipsis)
+ return model.FunctionType(tuple(args), result, ellipsis)
def _get_struct_or_union_type(self, kind, type, typenode=None):
# First, a level of caching on the exact 'type' node of the AST.
@@ -238,10 +281,11 @@
for decl in type.decls:
if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and
''.join(decl.type.names) == '__dotdotdot__'):
- xxxx
# XXX pycparser is inconsistent: 'names' should be a list
# of strings, but is sometimes just one string. Use
# str.join() as a way to cope with both.
+ tp.partial = True
+ continue
if decl.bitsize is None:
bitsize = -1
else:
@@ -274,17 +318,23 @@
if key in self._declarations:
return self._declarations[key]
if decls is not None:
- enumerators = tuple([enum.name for enum in decls.enumerators])
+ enumerators = [enum.name for enum in decls.enumerators]
+ partial = False
+ if enumerators and _r_enum_dotdotdot.match(enumerators[-1]):
+ enumerators.pop()
+ partial = True
+ enumerators = tuple(enumerators)
enumvalues = []
nextenumvalue = 0
- for enum in decls.enumerators:
+ for enum in decls.enumerators[:len(enumerators)]:
if enum.value is not None:
nextenumvalue = self._parse_constant(enum.value)
enumvalues.append(nextenumvalue)
nextenumvalue += 1
enumvalues = tuple(enumvalues)
tp = model.EnumType(name, enumerators, enumvalues)
- self._declarations[key] = tp
+ tp.partial = partial
+ self._declare(key, tp)
else: # opaque enum
enumerators = ()
enumvalues = ()
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -1,5 +1,5 @@
+import os
-from platformer import udir
class VerificationError(Exception):
""" An error raised when verification fails
@@ -10,11 +10,22 @@
cdef, but no verification has been done
"""
-def _get_test_file():
- tst_file = udir.join('test.c')
- i = 0
- # XXX we want to put typedefs here
- while tst_file.check():
- tst_file = udir.join('test%d.c' % i)
- i += 1
- return tst_file
+_file_counter = 0
+_tmpdir = None
+
+def undercffi_module_name():
+ global _file_counter
+ modname = '_cffi_%d' % _file_counter
+ _file_counter += 1
+ return modname
+
+def tmpdir():
+ # for now, living in the __pycache__ subdirectory
+ global _tmpdir
+ if _tmpdir is None:
+ try:
+ os.mkdir('__pycache__')
+ except OSError:
+ pass
+ _tmpdir = os.path.abspath('__pycache__')
+ return _tmpdir
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -1,5 +1,9 @@
class BaseType(object):
+
+ def __repr__(self):
+ return '<%s>' % (self.get_c_name(),)
+
def __eq__(self, other):
return (self.__class__ == other.__class__ and
self._get_items() == other._get_items())
@@ -22,24 +26,18 @@
except KeyError:
return self.new_backend_type(ffi, *args)
- def get_backend_type(self, ffi):
- return ffi._get_cached_btype(self)
-
- def verifier_declare(self, verifier, f):
- # nothing to see here
- pass
class VoidType(BaseType):
_attrs_ = ()
- name = 'void'
-
+
+ def get_c_name(self, replace_with=''):
+ return 'void' + replace_with
+
def new_backend_type(self, ffi):
return ffi._backend.new_void_type()
- def __repr__(self):
- return '<void>'
+void_type = VoidType()
-void_type = VoidType()
class PrimitiveType(BaseType):
_attrs_ = ('name',)
@@ -47,26 +45,39 @@
def __init__(self, name):
self.name = name
+ def get_c_name(self, replace_with=''):
+ return self.name + replace_with
+
+ def is_char_type(self):
+ return self.name == 'char'
+ def is_signed_type(self):
+ return self.is_integer_type() and not self.is_unsigned_type()
+ def is_unsigned_type(self):
+ return self.name.startswith('unsigned ')
+ def is_integer_type(self):
+ return not self.is_float_type() and not self.is_char_type()
+ def is_float_type(self):
+ return self.name in ('double', 'float')
+
def new_backend_type(self, ffi):
return ffi._backend.new_primitive_type(self.name)
- def __repr__(self):
- return '<%s>' % (self.name,)
class FunctionType(BaseType):
_attrs_ = ('args', 'result', 'ellipsis')
- def __init__(self, name, args, result, ellipsis):
- self.name = name # can be None in case it's an empty type
+ def __init__(self, args, result, ellipsis):
self.args = args
self.result = result
self.ellipsis = ellipsis
- def __repr__(self):
- args = ', '.join([repr(x) for x in self.args])
+ def get_c_name(self, replace_with=''):
+ reprargs = [arg.get_c_name() for arg in self.args]
if self.ellipsis:
- return '<(%s, ...) -> %r>' % (args, self.result)
- return '<(%s) -> %r>' % (args, self.result)
+ reprargs.append('...')
+ reprargs = reprargs or ['void']
+ replace_with = '(*%s)(%s)' % (replace_with, ', '.join(reprargs))
+ return self.result.get_c_name(replace_with)
def prepare_backend_type(self, ffi):
args = [ffi._get_cached_btype(self.result)]
@@ -77,15 +88,6 @@
def new_backend_type(self, ffi, result, *args):
return ffi._backend.new_function_type(args, result, self.ellipsis)
- def verifier_declare(self, verifier, f):
- restype = self.result.name
- args = []
- for arg in self.args:
- args.append(arg.name)
- args = ', '.join(args)
- f.write(' %s(* res%d)(%s) = %s;\n' % (restype, verifier.rescount,
- args, self.name))
- verifier.rescount += 1
class PointerType(BaseType):
_attrs_ = ('totype',)
@@ -93,86 +95,172 @@
def __init__(self, totype):
self.totype = totype
+ def get_c_name(self, replace_with=''):
+ return self.totype.get_c_name('* ' + replace_with)
+
def prepare_backend_type(self, ffi):
return (ffi._get_cached_btype(self.totype),)
def new_backend_type(self, ffi, BItem):
return ffi._backend.new_pointer_type(BItem)
- def __repr__(self):
- return '<*%r>' % (self.totype,)
+
+class ConstPointerType(PointerType):
+
+ def get_c_name(self, replace_with=''):
+ return self.totype.get_c_name(' const * ' + replace_with)
+
+ def prepare_backend_type(self, ffi):
+ return (ffi._get_cached_btype(PointerType(self.totype)),)
+
+ def new_backend_type(self, ffi, BPtr):
+ return BPtr
+
class ArrayType(BaseType):
_attrs_ = ('item', 'length')
def __init__(self, item, length):
- self.item = PointerType(item) # XXX why is this pointer?
+ self.item = item
self.length = length
- def __repr__(self):
+ def resolve_length(self, newlength):
+ return ArrayType(self.item, newlength)
+
+ def get_c_name(self, replace_with=''):
if self.length is None:
- return '<%r[]>' % (self.item,)
- return '<%r[%s]>' % (self.item, self.length)
+ brackets = '[]'
+ else:
+ brackets = '[%d]' % self.length
+ return self.item.get_c_name(replace_with + brackets)
def prepare_backend_type(self, ffi):
- return (ffi._get_cached_btype(self.item),)
+ return (ffi._get_cached_btype(PointerType(self.item)),)
- def new_backend_type(self, ffi, BItem):
- return ffi._backend.new_array_type(BItem, self.length)
+ def new_backend_type(self, ffi, BPtrItem):
+ return ffi._backend.new_array_type(BPtrItem, self.length)
+
class StructOrUnion(BaseType):
_attrs_ = ('name',)
-
+ fixedlayout = None
+
def __init__(self, name, fldnames, fldtypes, fldbitsize):
self.name = name
self.fldnames = fldnames
self.fldtypes = fldtypes
self.fldbitsize = fldbitsize
- def __repr__(self):
- if self.fldnames is None:
- return '<struct %s>' % (self.name,)
- fldrepr = ', '.join(['%s: %r' % (name, tp) for name, tp in
- zip(self.fldnames, self.fldtypes)])
- return '<struct %s {%s}>' % (self.name, fldrepr)
+ def get_c_name(self, replace_with=''):
+ return '%s %s%s' % (self.kind, self.name, replace_with)
def prepare_backend_type(self, ffi):
BType = self.get_btype(ffi)
ffi._cached_btypes[self] = BType
args = [BType]
- for tp in self.fldtypes:
- args.append(ffi._get_cached_btype(tp))
+ if self.fldtypes is not None:
+ for tp in self.fldtypes:
+ args.append(ffi._get_cached_btype(tp))
return args
def finish_backend_type(self, ffi, BType, *fldtypes):
- lst = zip(self.fldnames, fldtypes, self.fldbitsize)
- ffi._backend.complete_struct_or_union(BType, lst, self)
+ if self.fldnames is None:
+ return BType # not completing it: it's an opaque struct
+ #
+ if self.fixedlayout is None:
+ lst = zip(self.fldnames, fldtypes, self.fldbitsize)
+ ffi._backend.complete_struct_or_union(BType, lst, self)
+ #
+ else:
+ fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
+ for i in range(len(self.fldnames)):
+ fsize = fieldsize[i]
+ ftype = self.fldtypes[i]
+ #
+ if isinstance(ftype, ArrayType) and ftype.length is None:
+ # fix the length to match the total size
+ BItemType = ffi._get_cached_btype(ftype.item)
+ nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
+ if nrest != 0:
+ self._verification_error(
+ "field '%s.%s' has a bogus size?" % (
+ self.name, self.fldnames[i]))
+ ftype = ftype.resolve_length(nlen)
+ self.fldtypes = (self.fldtypes[:i] + (ftype,) +
+ self.fldtypes[i+1:])
+ BArrayType = ffi._get_cached_btype(ftype)
+ fldtypes = (fldtypes[:i] + (BArrayType,) +
+ fldtypes[i+1:])
+ continue
+ #
+ bitemsize = ffi.sizeof(fldtypes[i])
+ if bitemsize != fsize:
+ self._verification_error(
+ "field '%s.%s' is declared as %d bytes, but is "
+ "really %d bytes" % (self.name, self.fldnames[i],
+ bitemsize, fsize))
+ lst = zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)
+ ffi._backend.complete_struct_or_union(BType, lst, self,
+ totalsize, totalalignment)
return BType
+ def _verification_error(self, msg):
+ from .ffiplatform import VerificationError
+ raise VerificationError(msg)
+
+
class StructType(StructOrUnion):
+ kind = 'struct'
+ partial = False
+
+ def check_not_partial(self):
+ if self.partial and self.fixedlayout is None:
+ from . import ffiplatform
+ raise ffiplatform.VerificationMissing(self.get_c_name())
+
def get_btype(self, ffi):
+ self.check_not_partial()
return ffi._backend.new_struct_type(self.name)
- def verifier_declare(self, verifier, f):
- verifier._write_printf(f, 'BEGIN struct %s size(%%ld)' % self.name,
- 'sizeof(struct %s)' % self.name)
- for decl in decl.decls:
- pass
- #_write_printf(f, 'FIELD ofs(%s) size(%s)')
- verifier._write_printf(f, 'END struct %s' % self.name)
class UnionType(StructOrUnion):
+ kind = 'union'
+
def get_btype(self, ffi):
return ffi._backend.new_union_type(self.name)
-
+
+
class EnumType(BaseType):
_attrs_ = ('name',)
+ partial = False
def __init__(self, name, enumerators, enumvalues):
self.name = name
self.enumerators = enumerators
self.enumvalues = enumvalues
+ def get_c_name(self, replace_with=''):
+ return 'enum %s%s' % (self.name, replace_with)
+
+ def check_not_partial(self):
+ if self.partial:
+ from . import ffiplatform
+ raise ffiplatform.VerificationMissing(self.get_c_name())
+
def new_backend_type(self, ffi):
+ self.check_not_partial()
return ffi._backend.new_enum_type(self.name, self.enumerators,
self.enumvalues)
+
+
+class OpaqueType(BaseType):
+ _attrs_ = ('name',)
+
+ def __init__(self, name):
+ self.name = name
+
+ def get_c_name(self, replace_with=''):
+ return self.name + replace_with
+
+ def new_backend_type(self, ffi):
+ return ffi._backend.new_struct_type('$' + self.name)
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -1,30 +1,520 @@
-
-from platformer import platform, ExternalCompilationInfo
-from . import ffiplatform
+import os
+from . import model, ffiplatform
class Verifier(object):
- def __init__(self):
- self.rescount = 0
- def _write_printf(f, what, *args):
- if not args:
- f.write(' printf("%s\\n");\n' % (what,))
+ def __init__(self, ffi):
+ self.ffi = ffi
+ self.typesdict = {}
+
+ def prnt(self, what=''):
+ print >> self.f, what
+
+ def gettypenum(self, type):
+ try:
+ return self.typesdict[type]
+ except KeyError:
+ num = len(self.typesdict)
+ self.typesdict[type] = num
+ return num
+
+ def verify(self, preamble, stop_on_warnings=True):
+ modname = ffiplatform.undercffi_module_name()
+ filebase = os.path.join(ffiplatform.tmpdir(), modname)
+ self.chained_list_constants = None
+
+ with open(filebase + '.c', 'w') as f:
+ self.f = f
+ self.prnt(cffimod_header)
+ self.prnt()
+ self.prnt(preamble)
+ self.prnt()
+ #
+ self.generate("decl")
+ #
+ self.prnt('static PyObject *_cffi_setup_custom(void)')
+ self.prnt('{')
+ self.prnt(' PyObject *dct = PyDict_New();')
+ if self.chained_list_constants is not None:
+ self.prnt(' if (dct == NULL)')
+ self.prnt(' return NULL;')
+ self.prnt(' if (%s(dct) < 0) {' % self.chained_list_constants)
+ self.prnt(' Py_DECREF(dct);')
+ self.prnt(' return NULL;')
+ self.prnt(' }')
+ self.prnt(' return dct;')
+ self.prnt('}')
+ self.prnt()
+ #
+ self.prnt('static PyMethodDef _cffi_methods[] = {')
+ self.generate("method")
+ self.prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
+ self.prnt(' {NULL, NULL} /* Sentinel */')
+ self.prnt('};')
+ self.prnt()
+ #
+ self.prnt('void init%s()' % modname)
+ self.prnt('{')
+ self.prnt(' Py_InitModule("%s", _cffi_methods);' % modname)
+ self.prnt(' _cffi_init();')
+ self.prnt('}')
+ #
+ del self.f
+
+ # XXX use more distutils?
+ import distutils.sysconfig
+ python_h = distutils.sysconfig.get_python_inc()
+ cmdline = "gcc -I'%s' -O2 -shared -fPIC %s.c -o %s.so" % (
+ python_h, filebase, filebase)
+ if stop_on_warnings:
+ cmdline += " -Werror"
+ err = os.system(cmdline)
+ if err:
+ raise ffiplatform.VerificationError(
+ '%s.c: see compilation errors above' % (filebase,))
+ #
+ import imp
+ try:
+ module = imp.load_dynamic(modname, '%s.so' % filebase)
+ except ImportError, e:
+ raise ffiplatform.VerificationError(str(e))
+ #
+ self.load(module, 'loading')
+ #
+ 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)
+ dct = module._cffi_setup(lst, ffiplatform.VerificationError)
+ del module._cffi_setup
+ module.__dict__.update(dct)
+ #
+ self.load(module, 'loaded')
+ #
+ return module
+
+ def generate(self, step_name):
+ for name, tp in self.ffi._parser._declarations.iteritems():
+ kind, realname = name.split(' ', 1)
+ method = getattr(self, 'generate_cpy_%s_%s' % (kind, step_name))
+ method(tp, realname)
+
+ def load(self, module, step_name):
+ 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)
+
+ def generate_nothing(self, tp, name):
+ pass
+
+ def loaded_noop(self, tp, name, module):
+ pass
+
+ # ----------
+
+ def convert_to_c(self, tp, fromvar, tovar, errcode, is_funcarg=False):
+ extraarg = ''
+ if isinstance(tp, model.PrimitiveType):
+ converter = '_cffi_to_c_%s' % (tp.name.replace(' ', '_'),)
+ errvalue = '-1'
+ #
+ elif isinstance(tp, model.PointerType):
+ if (is_funcarg and
+ 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'
+ #
else:
- f.write(' printf("%s\\n", %s);\n' % (what, ', '.join(args)))
+ 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 verify(self, ffi, preamble, **kwargs):
- tst_file = ffiplatform._get_test_file()
- with tst_file.open('w') as f:
- f.write('#include <stdio.h>\n')
- f.write(preamble + "\n\n")
- f.write('int main() {\n')
- for name, tp in ffi._parser._declarations.iteritems():
- tp.verifier_declare(self, f)
- f.write(' return 0;\n')
- f.write('}\n')
- f.close()
- exe_name = platform.compile([str(tst_file)],
- ExternalCompilationInfo(**kwargs))
- out = platform.execute(exe_name)
- assert out.returncode == 0
- outlines = out.out.splitlines()
+ 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):
+ return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
+ var, self.gettypenum(tp))
+ else:
+ raise NotImplementedError(tp)
+
+ # ----------
+ # typedefs: generates no code so far
+
+ 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_decl(self, tp, name):
+ assert isinstance(tp, model.FunctionType)
+ 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('{')
+ assert not tp.ellipsis # XXX later
+ #
+ 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_to_c(type, 'arg%d' % i, 'x%d' % i, 'return NULL',
+ is_funcarg=True)
+ prnt()
+ #
+ prnt(' { %s%s(%s); }' % (
+ result_code, name,
+ ', '.join(['x%d' % i for i in range(len(tp.args))])))
+ 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):
+ 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
+ loaded_cpy_function = loaded_noop
+
+ # ----------
+ # struct declarations
+
+ def generate_cpy_struct_decl(self, tp, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ assert name == tp.name
+ prnt = self.prnt
+ prnt('static PyObject *')
+ prnt('_cffi_struct_%s(PyObject *self, PyObject *noarg)' % name)
+ prnt('{')
+ prnt(' struct _cffi_aligncheck { char x; struct %s y; };' % name)
+ if tp.partial:
+ prnt(' static Py_ssize_t nums[] = {')
+ prnt(' sizeof(struct %s),' % name)
+ prnt(' offsetof(struct _cffi_aligncheck, y),')
+ for fname in tp.fldnames:
+ prnt(' offsetof(struct %s, %s),' % (name, fname))
+ prnt(' sizeof(((struct %s *)0)->%s),' % (name, fname))
+ prnt(' -1')
+ prnt(' };')
+ prnt(' return _cffi_get_struct_layout(nums);')
+ else:
+ ffi = self.ffi
+ BStruct = ffi._get_cached_btype(tp)
+ conditions = [
+ 'sizeof(struct %s) != %d' % (name, 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(struct %s, %s) != %d' % (
+ name, fname, ffi.offsetof(BStruct, fname)),
+ 'sizeof(((struct %s *)0)->%s) != %d' % (
+ name, 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('}')
+ prnt('static void _cffi_check_%s(struct %s *p)' % (name, name))
+ 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(' (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; }' % (
+ ftype.get_c_name('(*tmp)'), fname))
+ prnt('}')
+ prnt()
+
+ def generate_cpy_struct_method(self, tp, name):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.prnt(' {"_cffi_struct_%s", _cffi_struct_%s, METH_NOARGS},' % (
+ name, name))
+
+ def loading_cpy_struct(self, tp, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ assert name == tp.name
+ function = getattr(module, '_cffi_struct_%s' % name)
+ layout = function()
+ if layout is False:
+ raise ffiplatform.VerificationError(
+ "incompatible layout for struct %s" % name)
+ 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_cpy_struct(self, tp, name, module):
+ if tp.fldnames is None:
+ return # nothing to do with opaque structs
+ self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered
+
+ # ----------
+ # constants, likely declared with '#define'
+
+ def _generate_chain_header(self, funcname, *vardecls):
+ prnt = self.prnt
+ prnt('static int %s(PyObject *dct)' % funcname)
+ prnt('{')
+ for decl in vardecls:
+ prnt(' ' + decl)
+ if self.chained_list_constants is not None:
+ prnt(' if (%s(dct) < 0)' % self.chained_list_constants)
+ prnt(' return -1;')
+ self.chained_list_constants = funcname
+
+ def _generate_cpy_const(self, is_int, name, tp=None):
+ vardecls = ['PyObject *o;',
+ 'int res;']
+ if not is_int:
+ vardecls.append('%s;' % tp.get_c_name(' i'))
+ self._generate_chain_header('_cffi_const_%s' % name, *vardecls)
+ #
+ prnt = self.prnt
+ if not is_int:
+ prnt(' i = (%s);' % (name,))
+ prnt(' o = %s;' % (self.convert_expr_from_c(tp, 'i'),))
+ 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 = PyDict_SetItemString(dct, "%s", o);' % name)
+ prnt(' Py_DECREF(o);')
+ prnt(' return res;')
+ 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)
+
+ 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)
+ return
+ #
+ self._generate_chain_header('_cffi_enum_%s' % name)
+ prnt = self.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 0;')
+ prnt('}')
+ prnt()
+
+ generate_cpy_enum_method = generate_nothing
+ loading_cpy_enum = loaded_noop
+
+ def loaded_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
+
+ # ----------
+
+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
+
+static PyObject *_cffi_from_c_char(char x) {
+ return PyString_FromStringAndSize(&x, 1);
+}
+
+#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])
+
+#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;
+static PyObject *_cffi_types, *_cffi_VerificationError;
+
+static PyObject *_cffi_setup_custom(void); /* forward */
+
+static PyObject *_cffi_setup(PyObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, "OO", &_cffi_types, &_cffi_VerificationError))
+ return NULL;
+ Py_INCREF(_cffi_types);
+ Py_INCREF(_cffi_VerificationError);
+
+ return _cffi_setup_custom();
+}
+
+static void _cffi_init(void)
+{
+ PyObject *module = PyImport_ImportModule("_ffi_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;
+ }
+ _cffi_exports = (void **)PyCObject_AsVoidPtr(c_api_object);
+}
+
+#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
+
+/**********/
+'''
diff --git a/demo/readdir.py b/demo/readdir.py
--- a/demo/readdir.py
+++ b/demo/readdir.py
@@ -11,11 +11,12 @@
typedef long off_t;
struct dirent {
- ino_t d_ino;
- off_t d_off;
- unsigned short d_reclen;
- unsigned char d_type;
- char d_name[256];
+ ino_t d_ino; /* inode number */
+ off_t d_off; /* offset to the next dirent */
+ unsigned short d_reclen; /* length of this record */
+ unsigned char d_type; /* type of file; not supported
+ by all file system types */
+ char d_name[256]; /* filename */
};
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
@@ -24,6 +25,7 @@
int closedir(DIR *dirp);
""")
+ffi.C = ffi.rawload(None)
diff --git a/demo/readdir2.py b/demo/readdir2.py
new file mode 100644
--- /dev/null
+++ b/demo/readdir2.py
@@ -0,0 +1,62 @@
+# A Linux-only demo, using verify() instead of hard-coding the exact layouts
+#
+from cffi import FFI
+
+
+ffi = FFI()
+ffi.cdef("""
+
+ typedef ... DIR;
+
+ struct dirent {
+ unsigned char d_type; /* type of file; not supported
+ by all file system types */
+ char d_name[256]; /* filename */
+ ...;
+ };
+
+ int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+ int openat(int dirfd, const char *pathname, int flags);
+ DIR *fdopendir(int fd);
+ int closedir(DIR *dirp);
+
+ static const int DT_DIR;
+
+""")
+ffi.C = ffi.verify("""
+#ifndef _ATFILE_SOURCE
+# define _ATFILE_SOURCE
+#endif
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE
+#endif
+#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+""")
+
+
+def walk(basefd, path):
+ print '{', path
+ dirfd = ffi.C.openat(basefd, path, 0)
+ if dirfd < 0:
+ # error in openat()
+ return
+ dir = ffi.C.fdopendir(dirfd)
+ dirent = ffi.new("struct dirent")
+ result = ffi.new("struct dirent *")
+ while True:
+ if ffi.C.readdir_r(dir, dirent, result):
+ # error in readdir_r()
+ break
+ if result[0] is None:
+ break
+ name = str(dirent.d_name)
+ print '%3d %s' % (dirent.d_type, name)
+ if dirent.d_type == ffi.C.DT_DIR and name != '.' and name != '..':
+ walk(dirfd, name)
+ ffi.C.closedir(dir)
+ print '}'
+
+
+walk(-1, "/tmp")
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -6,7 +6,7 @@
SIZE_OF_LONG = ctypes.sizeof(ctypes.c_long)
SIZE_OF_SHORT = ctypes.sizeof(ctypes.c_short)
SIZE_OF_PTR = ctypes.sizeof(ctypes.c_void_p)
-SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
+#SIZE_OF_WCHAR = ctypes.sizeof(ctypes.c_wchar)
class BackendTests:
@@ -41,7 +41,7 @@
self._test_int_type(ffi, 'ptrdiff_t', SIZE_OF_PTR, False)
self._test_int_type(ffi, 'size_t', SIZE_OF_PTR, True)
self._test_int_type(ffi, 'ssize_t', SIZE_OF_PTR, False)
- self._test_int_type(ffi, 'wchar_t', SIZE_OF_WCHAR, True)
+ #self._test_int_type(ffi, 'wchar_t', SIZE_OF_WCHAR, True)
def _test_int_type(self, ffi, c_decl, size, unsigned):
if unsigned:
@@ -50,6 +50,8 @@
else:
min = -(1 << (8*size-1))
max = (1 << (8*size-1)) - 1
+ min = int(min)
+ max = int(max)
p = ffi.cast(c_decl, min)
assert p != min # no __eq__(int)
assert bool(p) is True
@@ -68,6 +70,10 @@
py.test.raises(OverflowError, ffi.new, c_decl, max + 1)
py.test.raises(OverflowError, ffi.new, c_decl, long(min - 1))
py.test.raises(OverflowError, ffi.new, c_decl, long(max + 1))
+ assert ffi.new(c_decl, min)[0] == min
+ assert ffi.new(c_decl, max)[0] == max
+ assert ffi.new(c_decl, long(min))[0] == min
+ assert ffi.new(c_decl, long(max))[0] == max
def test_new_single_integer(self):
ffi = FFI(backend=self.Backend())
@@ -680,6 +686,11 @@
assert s[0].b == 412
py.test.raises(IndexError, 's[1]')
+ def test_pointer_to_array(self):
+ ffi = FFI(backend=self.Backend())
+ p = ffi.new("int(*)[5]")
+ assert repr(p) == "<cdata 'int(* *)[5]' owning %d bytes>" % SIZE_OF_INT
+
def test_offsetof(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("struct foo { int a, b, c; };")
@@ -755,3 +766,12 @@
assert s == '\x64\x00\x00\x00\x65\x00\x00\x00'
else:
assert s == '\x00\x00\x00\x64\x00\x00\x00\x65'
+
+ def test_new_struct_containing_array_varsize(self):
+ py.test.skip("later?")
+ ffi = FFI(backend=_ffi_backend)
+ ffi.cdef("struct foo_s { int len; short data[]; };")
+ p = ffi.new("struct foo_s", 10) # a single integer is the length
+ assert p.len == 0
+ assert p.data[9] == 0
+ py.test.raises(IndexError, "p.data[10]")
diff --git a/testing/test_cdata.py b/testing/test_cdata.py
--- a/testing/test_cdata.py
+++ b/testing/test_cdata.py
@@ -6,7 +6,7 @@
def nonstandard_integer_types(self):
return {}
- def sizeof_type(self, name):
+ def sizeof(self, name):
return 1
def load_library(self, path):
diff --git a/testing/test_function.py b/testing/test_function.py
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -36,7 +36,7 @@
ffi.cdef("""
double sin(double x);
""")
- m = ffi.load("m")
+ m = ffi.rawload("m")
x = m.sin(1.23)
assert x == math.sin(1.23)
@@ -45,7 +45,7 @@
ffi.cdef("""
float sinf(float x);
""")
- m = ffi.load("m")
+ m = ffi.rawload("m")
x = m.sinf(1.23)
assert type(x) is float
assert x != math.sin(1.23) # rounding effects
@@ -57,6 +57,7 @@
int puts(const char *);
int fflush(void *);
""")
+ ffi.C = ffi.rawload(None)
ffi.C.puts # fetch before capturing, for easier debugging
with FdWriteCapture() as fd:
ffi.C.puts("hello")
@@ -71,6 +72,7 @@
int puts(char *);
int fflush(void *);
""")
+ ffi.C = ffi.rawload(None)
ffi.C.puts # fetch before capturing, for easier debugging
with FdWriteCapture() as fd:
ffi.C.puts("hello")
@@ -85,6 +87,7 @@
int fputs(const char *, void *);
void *stdout, *stderr;
""")
+ ffi.C = ffi.rawload(None)
with FdWriteCapture(2) as fd:
ffi.C.fputs("hello from stderr\n", ffi.C.stderr)
res = fd.getvalue()
@@ -96,6 +99,7 @@
int printf(const char *format, ...);
int fflush(void *);
""")
+ ffi.C = ffi.rawload(None)
with FdWriteCapture() as fd:
ffi.C.printf("hello with no arguments\n")
ffi.C.printf("hello, %s!\n", ffi.new("char[]", "world"))
@@ -119,6 +123,7 @@
ffi.cdef("""
int printf(const char *format, ...);
""")
+ ffi.C = ffi.rawload(None)
e = py.test.raises(TypeError, ffi.C.printf, "hello %d\n", 42)
assert str(e.value) == ("argument 2 passed in the variadic part "
"needs to be a cdata object (got int)")
@@ -128,6 +133,7 @@
ffi.cdef("""
int puts(const char *);
""")
+ ffi.C = ffi.rawload(None)
fptr = ffi.C.puts
assert ffi.typeof(fptr) == ffi.typeof("int(*)(const char*)")
if self.Backend is CTypesBackend:
@@ -148,6 +154,7 @@
int puts(const char *);
int fflush(void *);
""")
+ ffi.C = ffi.rawload(None)
fptr = ffi.cast("int(*)(const char *txt)", ffi.C.puts)
assert fptr == ffi.C.puts
assert repr(fptr) == "<cdata 'int(*)(char *)'>"
@@ -162,6 +169,7 @@
ffi.cdef("""
int strlen(char[]);
""")
+ ffi.C = ffi.rawload(None)
p = ffi.new("char[]", "hello")
res = ffi.C.strlen(p)
assert res == 5
@@ -172,6 +180,7 @@
int puts(const char *);
void *stdout, *stderr;
""")
+ ffi.C = ffi.rawload(None)
pout = ffi.C.stdout
perr = ffi.C.stderr
assert repr(pout) == "<cdata 'void *'>"
@@ -190,6 +199,7 @@
ffi.cdef("""
char *strchr(const char *s, int c);
""")
+ ffi.C = ffi.rawload(None)
p = ffi.new("char[]", "hello world!")
q = ffi.C.strchr(p, ord('w'))
assert str(q) == "world!"
@@ -200,6 +210,7 @@
struct in_addr { unsigned int s_addr; };
char *inet_ntoa(struct in_addr in);
""")
+ ffi.C = ffi.rawload(None)
ina = ffi.new("struct in_addr", [0x04040404])
a = ffi.C.inet_ntoa(ina[0])
assert str(a) == '4.4.4.4'
diff --git a/testing/test_ownlib.py b/testing/test_ownlib.py
--- a/testing/test_ownlib.py
+++ b/testing/test_ownlib.py
@@ -33,10 +33,11 @@
ffi.cdef("""
int test_getting_errno(void);
""")
- ownlib = ffi.load(self.module)
+ ownlib = ffi.rawload(self.module)
+ C = ffi.rawload(None)
res = ownlib.test_getting_errno()
assert res == -1
- assert ffi.C.errno == 123
+ assert C.errno == 123
def test_setting_errno(self):
if self.Backend is CTypesBackend and '__pypy__' in sys.modules:
@@ -45,8 +46,9 @@
ffi.cdef("""
int test_setting_errno(void);
""")
- ownlib = ffi.load(self.module)
- ffi.C.errno = 42
+ ownlib = ffi.rawload(self.module)
+ C = ffi.rawload(None)
+ C.errno = 42
res = ownlib.test_setting_errno()
assert res == 42
- assert ffi.C.errno == 42
+ assert C.errno == 42
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -5,7 +5,7 @@
def nonstandard_integer_types(self):
return {}
- def sizeof_type(self, name):
+ def sizeof(self, name):
return 1
def load_library(self, name):
@@ -53,7 +53,7 @@
def test_simple():
ffi = FFI(backend=FakeBackend())
ffi.cdef("double sin(double x);")
- m = ffi.load("m")
+ m = ffi.rawload("m")
func = m.sin # should be a callable on real backends
assert func.name == 'sin'
assert func.BType == '<func (<double>), <double>, False>'
@@ -61,14 +61,16 @@
def test_pipe():
ffi = FFI(backend=FakeBackend())
ffi.cdef("int pipe(int pipefd[2]);")
- func = ffi.C.pipe
+ C = ffi.rawload(None)
+ func = C.pipe
assert func.name == 'pipe'
assert func.BType == '<func (<pointer to <int>>), <int>, False>'
def test_vararg():
ffi = FFI(backend=FakeBackend())
ffi.cdef("short foo(int, ...);")
- func = ffi.C.foo
+ C = ffi.rawload(None)
+ func = C.foo
assert func.name == 'foo'
assert func.BType == '<func (<int>), <short>, True>'
@@ -77,7 +79,8 @@
ffi.cdef("""
int foo(void);
""")
- assert ffi.C.foo.BType == '<func (), <int>, False>'
+ C = ffi.rawload(None)
+ assert C.foo.BType == '<func (), <int>, False>'
def test_typedef():
ffi = FFI(backend=FakeBackend())
@@ -86,8 +89,9 @@
typedef UInt UIntReally;
UInt foo(void);
""")
+ C = ffi.rawload(None)
assert ffi.typeof("UIntReally") == '<unsigned int>'
- assert ffi.C.foo.BType == '<func (), <unsigned int>, False>'
+ assert C.foo.BType == '<func (), <unsigned int>, False>'
def test_typedef_more_complex():
ffi = FFI(backend=FakeBackend())
@@ -95,10 +99,11 @@
typedef struct { int a, b; } foo_t, *foo_p;
int foo(foo_p[]);
""")
+ C = ffi.rawload(None)
assert str(ffi.typeof("foo_t")) == '<int>a, <int>b'
assert ffi.typeof("foo_p") == '<pointer to <int>a, <int>b>'
- assert ffi.C.foo.BType == ('<func (<pointer to <pointer to '
- '<int>a, <int>b>>), <int>, False>')
+ assert C.foo.BType == ('<func (<pointer to <pointer to '
+ '<int>a, <int>b>>), <int>, False>')
def test_typedef_array_force_pointer():
ffi = FFI(backend=FakeBackend())
@@ -106,14 +111,31 @@
typedef int array_t[5];
""")
type = ffi._parser.parse_type("array_t", force_pointer=True)
- BType = type.get_backend_type(ffi)
+ BType = ffi._get_cached_btype(type)
assert BType == '<array <pointer to <int>> x 5>'
def test_typedef_array_convert_array_to_pointer():
ffi = FFI(backend=FakeBackend())
ffi.cdef("""
- typedef int array_t[5];
+ typedef int (*fn_t)(int[5]);
""")
- type = ffi._parser.parse_type("array_t", convert_array_to_pointer=True)
- BType = type.get_backend_type(ffi)
- assert BType == '<pointer to <int>>'
+ type = ffi._parser.parse_type("fn_t")
+ BType = ffi._get_cached_btype(type)
+ assert BType == '<func (<pointer to <int>>), <int>, False>'
+
+def test_remove_comments():
+ ffi = FFI(backend=FakeBackend())
+ ffi.cdef("""
+ double /*comment here*/ sin // blah blah
+ /* multi-
+ line-
+ //comment */ (
+ // foo
+ double // bar /* <- ignored, because it's in a comment itself
+ x, double/*several*//*comment*/y) /*on the same line*/
+ ;
+ """)
+ m = ffi.rawload("m")
+ func = m.sin
+ assert func.name == 'sin'
+ assert func.BType == '<func (<double>, <double>), <double>, False>'
diff --git a/testing/test_platform.py b/testing/test_platform.py
deleted file mode 100644
--- a/testing/test_platform.py
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import py
-from cffi import FFI, VerificationMissing
-
-def test_ffi_nonfull_struct():
- py.test.skip("XXX")
- ffi = FFI()
- ffi.cdef("""
- struct sockaddr {
- int sa_family;
- ...;
- };
- """)
- py.test.raises(VerificationMissing, ffi.sizeof, 'struct sockaddr')
- ffi.verify('''
- #include <sys/types.h>
- #include <sys/socket.h>
- ''')
- assert ffi.sizeof('struct sockaddr') == 14 + ffi.sizeof(int)
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1,21 +1,321 @@
+import py
+import math
+from cffi import FFI, VerificationError, VerificationMissing, model
-import py
-from cffi import FFI
-from platformer import CompilationError
-def test_simple_verify():
+def test_missing_function():
ffi = FFI()
ffi.cdef("void some_completely_unknown_function();")
- py.test.raises(CompilationError, ffi.verify)
+ py.test.raises(VerificationError, ffi.verify)
+
+def test_simple_case():
ffi = FFI()
ffi.cdef("double sin(double x);")
- # omission of math.h
- py.test.raises(CompilationError, ffi.verify)
- assert ffi.verify('#include <math.h>') is None
- #
+ lib = ffi.verify('#include <math.h>')
+ assert lib.sin(1.23) == math.sin(1.23)
+
+def test_rounding_1():
ffi = FFI()
ffi.cdef("float sin(double x);")
- py.test.raises(CompilationError, ffi.verify, '#include <math.h>')
+ lib = ffi.verify('#include <math.h>')
+ res = lib.sin(1.23)
+ assert res != math.sin(1.23) # not exact, because of double->float
+ assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_rounding_2():
ffi = FFI()
ffi.cdef("double sin(float x);")
- py.test.raises(CompilationError, ffi.verify, '#include <math.h>')
+ lib = ffi.verify('#include <math.h>')
+ res = lib.sin(1.23)
+ assert res != math.sin(1.23) # not exact, because of double->float
+ assert abs(res - math.sin(1.23)) < 1E-5
+
+def test_strlen_exact():
+ ffi = FFI()
+ ffi.cdef("size_t strlen(const char *s);")
+ lib = ffi.verify("#include <string.h>")
+ assert lib.strlen("hi there!") == 9
+
+def test_strlen_approximate():
+ ffi = FFI()
+ ffi.cdef("int strlen(char *s);")
+ lib = ffi.verify("#include <string.h>")
+ assert lib.strlen("hi there!") == 9
+
+
+all_integer_types = ['short', 'int', 'long', 'long long',
+ 'signed char', 'unsigned char',
+ 'unsigned short', 'unsigned int',
+ 'unsigned long', 'unsigned long long']
+all_signed_integer_types = [_typename for _typename in all_integer_types
+ if not _typename.startswith('unsigned ')]
+all_unsigned_integer_types = [_typename for _typename in all_integer_types
+ if _typename.startswith('unsigned ')]
+all_float_types = ['float', 'double']
+
+def test_primitive_category():
+ for typename in all_integer_types + all_float_types + ['char']:
+ tp = model.PrimitiveType(typename)
+ assert tp.is_char_type() == (typename == 'char')
+ assert tp.is_signed_type() == (typename in all_signed_integer_types)
+ assert tp.is_unsigned_type()== (typename in all_unsigned_integer_types)
+ assert tp.is_integer_type() == (typename in all_integer_types)
+ assert tp.is_float_type() == (typename in all_float_types)
+
+def test_all_integer_and_float_types():
+ for typename in all_integer_types + all_float_types:
+ ffi = FFI()
+ ffi.cdef("%s foo(%s);" % (typename, typename))
+ lib = ffi.verify("%s foo(%s x) { return x+1; }" % (typename, typename))
+ assert lib.foo(42) == 43
+ assert lib.foo(44L) == 45
+ assert lib.foo(ffi.cast(typename, 46)) == 47
+ py.test.raises(TypeError, lib.foo, None)
+ #
+ # check for overflow cases
+ if typename in all_float_types:
+ continue
+ for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1,
+ 2**5, 2**10, 2**20, 2**40, 2**80]:
+ overflows = int(ffi.cast(typename, value)) != value
+ if overflows:
+ py.test.raises(OverflowError, lib.foo, value)
+ else:
+ assert lib.foo(value) == value + 1
+
+def test_char_type():
+ ffi = FFI()
+ ffi.cdef("char foo(char);")
+ lib = ffi.verify("char foo(char x) { return x+1; }")
+ assert lib.foo("A") == "B"
+ py.test.raises(TypeError, lib.foo, "bar")
+
+def test_no_argument():
+ ffi = FFI()
+ ffi.cdef("int foo(void);")
+ lib = ffi.verify("int foo() { return 42; }")
+ assert lib.foo() == 42
+
+def test_two_arguments():
+ ffi = FFI()
+ ffi.cdef("int foo(int, int);")
+ lib = ffi.verify("int foo(int a, int b) { return a - b; }")
+ assert lib.foo(40, -2) == 42
+
+def test_macro():
+ ffi = FFI()
+ ffi.cdef("int foo(int, int);")
+ lib = ffi.verify("#define foo(a, b) ((a) * (b))")
+ assert lib.foo(-6, -7) == 42
+
+def test_ptr():
+ ffi = FFI()
+ ffi.cdef("int *foo(int *);")
+ lib = ffi.verify("int *foo(int *a) { return a; }")
+ assert lib.foo(None) is None
+ p = ffi.new("int", 42)
+ q = ffi.new("int", 42)
+ assert lib.foo(p) == p
+ assert lib.foo(q) != p
+
+def test_bogus_ptr():
+ ffi = FFI()
+ ffi.cdef("int *foo(int *);")
+ lib = ffi.verify("int *foo(int *a) { return a; }")
+ py.test.raises(TypeError, lib.foo, ffi.new("short", 42))
+
+
+def test_verify_typedefs():
+ py.test.skip("ignored so far")
+ types = ['signed char', 'unsigned char', 'int', 'long']
+ for cdefed in types:
+ for real in types:
+ ffi = FFI()
+ ffi.cdef("typedef %s foo_t;" % cdefed)
+ if cdefed == real:
+ ffi.verify("typedef %s foo_t;" % real)
+ else:
+ py.test.raises(VerificationError, ffi.verify,
+ "typedef %s foo_t;" % real)
+
+def test_nondecl_struct():
+ ffi = FFI()
+ ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);")
+ lib = ffi.verify("typedef struct foo_s foo_t;\n"
+ "int bar(foo_t *f) { return 42; }\n")
+ assert lib.bar(None) == 42
+
+def test_missing_typedef():
+ ffi = FFI()
+ ffi.cdef("typedef...foo_t; int bar(foo_t *);")
+ py.test.raises(TypeError, ffi.new, "foo_t")
+ lib = ffi.verify("typedef struct foo_s { int x; } foo_t;\n"
+ "int bar(foo_t *f) { return 42; }\n")
+ py.test.raises(TypeError, ffi.new, "foo_t")
+ f = ffi.cast("foo_t*", 0)
+ assert lib.bar(f) == 42
+
+
+def test_ffi_full_struct():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { char x; int y; long *z; };")
+ ffi.verify("struct foo_s { char x; int y; long *z; };")
+ #
+ for real in [
+ "struct foo_s { char x; int y; int *z; };",
+ "struct foo_s { char x; long *z; int y; };",
+ "struct foo_s { int y; long *z; };",
+ "struct foo_s { char x; int y; long *z; char extra; };",
+ ]:
+ py.test.raises(VerificationError, ffi.verify, real)
+ #
+ # a corner case that we cannot really detect, but where it has no
+ # bad consequences: the size is the same, but there is an extra field
+ # that replaces what is just padding in our declaration above
+ ffi.verify("struct foo_s { char x, extra; int y; long *z; };")
+
+
+def test_ffi_nonfull_struct():
+ ffi = FFI()
+ ffi.cdef("""
+ struct foo_s {
+ int x;
+ ...;
+ };
+ """)
+ py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s')
+ py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x')
+ py.test.raises(VerificationMissing, ffi.new, 'struct foo_s')
+ ffi.verify("""
+ struct foo_s {
+ int a, b, x, c, d, e;
+ };
+ """)
+ assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int')
+ assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int')
+
+def test_ffi_nonfull_alignment():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { char x; ...; };")
+ ffi.verify("struct foo_s { int a, b; char x; };")
+ assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int')
+ assert ffi.alignof('struct foo_s') == ffi.sizeof('int')
+
+def _check_field_match(typename, real, expect_mismatch):
+ ffi = FFI()
+ if expect_mismatch == 'by_size':
+ expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real)
+ ffi.cdef("struct foo_s { %s x; ...; };" % typename)
+ try:
+ ffi.verify("struct foo_s { %s x; };" % real)
+ except VerificationError:
+ if not expect_mismatch:
+ raise AssertionError("unexpected mismatch: %s should be accepted "
+ "as equal to %s" % (typename, real))
+ else:
+ if expect_mismatch:
+ raise AssertionError("mismatch not detected: "
+ "%s != %s" % (typename, real))
+
+def test_struct_bad_sized_integer():
+ for typename in all_signed_integer_types:
+ for real in all_signed_integer_types:
+ _check_field_match(typename, real, "by_size")
+
+def test_struct_bad_sized_float():
+ for typename in all_float_types:
+ for real in all_float_types:
+ _check_field_match(typename, real, "by_size")
+
+def test_struct_signedness_ignored():
+ _check_field_match("int", "unsigned int", expect_mismatch=False)
+ _check_field_match("unsigned short", "signed short", expect_mismatch=False)
+
+def test_struct_float_vs_int():
+ for typename in all_signed_integer_types:
+ for real in all_float_types:
+ _check_field_match(typename, real, expect_mismatch=True)
+ for typename in all_float_types:
+ for real in all_signed_integer_types:
+ _check_field_match(typename, real, expect_mismatch=True)
+
+def test_struct_array_field():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { int a[17]; ...; };")
+ ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+ assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+ s = ffi.new("struct foo_s")
+ assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+
+def test_struct_array_guess_length():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { int a[]; ...; };") # <= no declared length
+ ffi.verify("struct foo_s { int x; int a[17]; int y; };")
+ assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+ s = ffi.new("struct foo_s")
+ assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int')
+
+def test_struct_array_guess_length_2():
+ ffi = FFI()
+ ffi.cdef("struct foo_s { int a[]; ...; };\n" # <= no declared length
+ "int bar(struct foo_s *);\n")
+ lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n"
+ "int bar(struct foo_s *f) { return f->a[14]; }\n")
+ assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int')
+ s = ffi.new("struct foo_s")
+ s.a[14] = 4242
+ assert lib.bar(s) == 4242
+
+def test_global_constants():
+ ffi = FFI()
+ # use 'static const int', as generally documented, although in this
+ # case the 'static' is completely ignored.
+ ffi.cdef("static const int AA, BB, CC, DD;")
+ lib = ffi.verify("#define AA 42\n"
+ "#define BB (-43)\n"
+ "#define CC (22*2)\n"
+ "#define DD ((unsigned int)142)\n")
+ assert lib.AA == 42
+ assert lib.BB == -43
+ assert lib.CC == 44
+ assert lib.DD == 142
+
+def test_global_const_int_size():
+ # integer constants: ignore the declared type, always just use the value
+ for value in [-2**63, -2**31, -2**15,
+ 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32,
+ 2**63-1, 2**63, 2**64-1]:
+ ffi = FFI()
+ if value == int(ffi.cast("long long", value)):
+ if value < 0:
+ vstr = '(-%dLL-1)' % (~value,)
+ else:
+ vstr = '%dLL' % value
+ elif value == int(ffi.cast("unsigned long long", value)):
+ vstr = '%dULL' % value
+ else:
+ raise AssertionError(value)
+ ffi.cdef("static const unsigned short AA;")
+ lib = ffi.verify("#define AA %s\n" % vstr)
+ assert lib.AA == value
+ assert type(lib.AA) is type(int(lib.AA))
+
+def test_nonfull_enum():
+ ffi = FFI()
+ ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
+ py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2')
+ ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };")
+ assert int(ffi.cast('enum ee', 'EE2')) == 11
+ assert int(ffi.cast('enum ee', 'EE3')) == -10
+ py.test.raises(ValueError, ffi.cast, 'enum ee', '__dotdotdot0__')
+
+def test_full_enum():
+ ffi = FFI()
+ ffi.cdef("enum ee { EE1, EE2, EE3 };")
+ ffi.verify("enum ee { EE1, EE2, EE3 };")
+ py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };")
+ e = py.test.raises(VerificationError, ffi.verify,
+ "enum ee { EE1, EE3, EE2 };")
+ assert str(e.value) == 'in enum ee: EE2 has the real value 2, not 1'
+ # extra items cannot be seen and have no bad consequence anyway
+ ffi.verify("enum ee { EE1, EE2, EE3, EE4 };")
More information about the pypy-commit
mailing list