Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r1770:9d55286c87b6
Date: 2015-04-19 10:34 +0200
http://bitbucket.org/cffi/cffi/changeset/9d55286c87b6/
Log: Reimplement the distinction between "...;" and non-"...;" structures
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -272,6 +272,8 @@
# include "wchar_helper.h"
#endif
+static PyObject *FFIError;
+
/************************************************************/
static CTypeDescrObject *
@@ -3745,6 +3747,7 @@
#define SF_GCC_LITTLE_ENDIAN 0x40
#define SF_PACKED 0x08
+#define SF_STD_FIELD_POS 0x80
static int complete_sflags(int sflags)
{
@@ -3772,6 +3775,28 @@
return sflags;
}
+static int detect_custom_layout(CTypeDescrObject *ct, int sflags,
+ Py_ssize_t cdef_value,
+ Py_ssize_t compiler_value,
+ const char *msg1, const char *txt,
+ const char *msg2)
+{
+ if (compiler_value != cdef_value) {
+ if (sflags & SF_STD_FIELD_POS) {
+ PyErr_Format(FFIError,
+ "%s: %s%s%s (cdef says %zd, but C compiler says %zd)."
+ " fix it or use \"...;\" in the cdef for %s to "
+ "make it flexible",
+ ct->ct_name, msg1, txt, msg2,
+ cdef_value, compiler_value,
+ ct->ct_name);
+ return -1;
+ }
+ ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+ }
+ return 0;
+}
+
static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct;
@@ -3805,6 +3830,7 @@
"first arg must be a non-initialized struct or union ctype");
return NULL;
}
+ ct->ct_flags &= ~CT_CUSTOM_FIELD_POS;
alignment = 1;
boffset = 0; /* this number is in *bits*, not bytes! */
@@ -3882,8 +3908,10 @@
if (foffset >= 0) {
/* a forced field position: ignore the offset just computed,
except to know if we must set CT_CUSTOM_FIELD_POS */
- if (boffset != foffset * 8)
- ct->ct_flags |= CT_CUSTOM_FIELD_POS;
+ if (detect_custom_layout(ct, sflags, boffset / 8, foffset,
+ "wrong offset for field '",
+ PyText_AS_UTF8(fname), "'") < 0)
+ goto error;
boffset = foffset * 8;
}
@@ -4061,14 +4089,28 @@
if (totalsize == 0)
totalsize = 1;
}
- else if (totalsize < boffsetmax) {
- PyErr_Format(PyExc_TypeError,
- "%s cannot be of size %zd: there are fields at least "
- "up to %zd", ct->ct_name, totalsize, boffsetmax);
- goto error;
- }
+ else {
+ if (detect_custom_layout(ct, sflags, boffsetmax, totalsize,
+ "wrong total size", "", "") < 0)
+ goto error;
+ if (totalsize < boffsetmax) {
+ PyErr_Format(PyExc_TypeError,
+ "%s cannot be of size %zd: there are fields at least "
+ "up to %zd", ct->ct_name, totalsize, boffsetmax);
+ goto error;
+ }
+ }
+ if (totalalignment < 0) {
+ totalalignment = alignment;
+ }
+ else {
+ if (detect_custom_layout(ct, sflags, alignment, totalalignment,
+ "wrong total alignment", "", "") < 0)
+ goto error;
+ }
+
ct->ct_size = totalsize;
- ct->ct_length = totalalignment < 0 ? alignment : totalalignment;
+ ct->ct_length = totalalignment;
ct->ct_stuff = interned_fields;
ct->ct_flags &= ~CT_IS_OPAQUE;
@@ -4076,6 +4118,7 @@
return Py_None;
error:
+ ct->ct_extra = NULL;
Py_DECREF(interned_fields);
return NULL;
}
diff --git a/new/cffi1_module.c b/new/cffi1_module.c
--- a/new/cffi1_module.c
+++ b/new/cffi1_module.c
@@ -1,4 +1,3 @@
-static PyObject *FFIError;
#include "parse_c_type.c"
#include "realize_c_type.c"
diff --git a/new/parse_c_type.h b/new/parse_c_type.h
--- a/new/parse_c_type.h
+++ b/new/parse_c_type.h
@@ -75,8 +75,11 @@
int first_field_index; // -> _cffi_fields array
int num_fields;
};
-#define CT_UNION 128
-#define CT_IS_OPAQUE 4096
+#define CT_UNION 128
+#define CT_IS_OPAQUE 4096
+#define CT_CUSTOM_FIELD_POS 32768
+/* ^^^ if not CUSTOM_FIELD_POS, complain if fields are not in the
+ "standard layout" and/or if some are missing */
struct _cffi_field_s {
const char *name;
diff --git a/new/realize_c_type.c b/new/realize_c_type.c
--- a/new/realize_c_type.c
+++ b/new/realize_c_type.c
@@ -451,14 +451,11 @@
return -1;
}
- if (ctf->ct_size != fld->field_size) {
- PyErr_Format(FFIError,
- "%s field '%s' was declared in the cdef to be"
- " %zd bytes, but is actually %zd bytes",
- ct->ct_name, fld->name,
- ctf->ct_size, fld->field_size);
+ if (detect_custom_layout(ct, SF_STD_FIELD_POS,
+ ctf->ct_size, fld->field_size,
+ "wrong size for field '",
+ fld->name, "'") < 0)
return -1;
- }
f = Py_BuildValue("(sOin)", fld->name, ctf,
(int)-1, (Py_ssize_t)fld->field_offset);
@@ -469,19 +466,24 @@
PyList_SET_ITEM(fields, i, f);
}
- PyObject *args = Py_BuildValue("(OOOnn)", ct, fields,
+ int sflags = (s->flags & CT_CUSTOM_FIELD_POS) ? 0 : SF_STD_FIELD_POS;
+
+ PyObject *args = Py_BuildValue("(OOOnni)", ct, fields,
Py_None,
(Py_ssize_t)s->size,
- (Py_ssize_t)s->alignment);
+ (Py_ssize_t)s->alignment,
+ sflags);
Py_DECREF(fields);
if (args == NULL)
return -1;
ct->ct_extra = NULL;
ct->ct_flags |= CT_IS_OPAQUE;
+ ct->ct_flags &= ~CT_CUSTOM_FIELD_POS;
PyObject *res = b_complete_struct_or_union(NULL, args);
ct->ct_flags &= ~CT_IS_OPAQUE;
Py_DECREF(args);
+
if (res == NULL) {
ct->ct_extra = builder;
return -1;
diff --git a/new/recompiler.py b/new/recompiler.py
--- a/new/recompiler.py
+++ b/new/recompiler.py
@@ -428,9 +428,12 @@
def _generate_cpy_struct_ctx(self, tp, name):
type_index = self._typesdict[tp]
- flags = '0'
+ flags = []
+ if tp.partial:
+ flags.append('CT_CUSTOM_FIELD_POS')
if isinstance(tp, model.UnionType):
- flags = 'CT_UNION'
+ flags.append('CT_UNION')
+ flags = ('|'.join(flags)) or '0'
if tp.fldtypes is not None:
c_field = [name]
for fldname, fldtype in zip(tp.fldnames, tp.fldtypes):
diff --git a/new/test_recompiler.py b/new/test_recompiler.py
--- a/new/test_recompiler.py
+++ b/new/test_recompiler.py
@@ -199,7 +199,7 @@
def test_verify_struct():
ffi = FFI()
- ffi.cdef("""struct foo_s { int b; short a; };
+ ffi.cdef("""struct foo_s { int b; short a; ...; };
struct bar_s { struct foo_s *f; };""")
lib = verify(ffi, 'test_verify_struct',
"""struct foo_s { short a; int b; };
@@ -213,6 +213,16 @@
q = ffi.new("struct bar_s *", {'f': p})
assert q.f == p
+def test_verify_exact_field_offset():
+ ffi = FFI()
+ ffi.cdef("""struct foo_s { int b; short a; };""")
+ lib = verify(ffi, 'test_verify_exact_field_offset',
+ """struct foo_s { short a; int b; };""")
+ e = py.test.raises(ffi.error, ffi.new, "struct foo_s *") # lazily
+ assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef "
+ 'says 0, but C compiler says 4). fix it or use "...;" '
+ "in the cdef for struct foo_s to make it flexible")
+
def test_type_caching():
ffi1 = FFI(); ffi1.cdef("struct foo_s;")
ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one!
@@ -260,8 +270,8 @@
assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code
# lazily build the fields and boom:
e = py.test.raises(ffi.error, ffi.new, "struct foo_s *")
- assert str(e.value) == ("struct foo_s field 'a' was declared in the "
- "cdef to be 20 bytes, but is actually 24 bytes")
+ assert str(e.value).startswith("struct foo_s: wrong size for field 'a' "
+ "(cdef says 20, but C compiler says 24)")
def test_open_array_in_struct():
ffi = FFI()
diff --git a/new/test_verify1.py b/new/test_verify1.py
--- a/new/test_verify1.py
+++ b/new/test_verify1.py
@@ -402,41 +402,45 @@
assert lib.bar(ffi.NULL) == 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; };")
+ def check(verified_code):
+ ffi = FFI()
+ ffi.cdef("struct foo_s { char x; int y; long *z; };")
+ ffi.verify(verified_code)
+ ffi.new("struct foo_s *")
+
+ check("struct foo_s { char x; int y; long *z; };")
#
if sys.platform != 'win32': # XXX fixme: only gives warnings
- py.test.raises(VerificationError, ffi.verify,
+ py.test.raises(VerificationError, check,
"struct foo_s { char x; int y; int *z; };")
#
- py.test.raises(VerificationError, ffi.verify,
- "struct foo_s { int y; long *z; };")
+ py.test.raises(VerificationError, check,
+ "struct foo_s { int y; long *z; };") # cdef'ed field x is missing
#
- e = py.test.raises(VerificationError, ffi.verify,
- "struct foo_s { int y; char x; long *z; };")
- assert str(e.value) == (
+ e = py.test.raises(FFI.error, check,
+ "struct foo_s { int y; char x; long *z; };")
+ assert str(e.value).startswith(
"struct foo_s: wrong offset for field 'x'"
- " (we have 0, but C compiler says 4)")
+ " (cdef says 0, but C compiler says 4)")
#
- e = py.test.raises(VerificationError, ffi.verify,
+ e = py.test.raises(FFI.error, check,
"struct foo_s { char x; int y; long *z; char extra; };")
- assert str(e.value) == (
+ assert str(e.value).startswith(
"struct foo_s: wrong total size"
- " (we have %d, but C compiler says %d)" % (
- ffi.sizeof("struct foo_s"),
- ffi.sizeof("struct foo_s") + ffi.sizeof("long*")))
+ " (cdef says %d, but C compiler says %d)" % (
+ 8 + FFI().sizeof('long *'),
+ 8 + FFI().sizeof('long *') * 2))
#
# 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; };")
+ check("struct foo_s { char x, extra; int y; long *z; };")
#
- e = py.test.raises(VerificationError, ffi.verify,
+ e = py.test.raises(FFI.error, check,
"struct foo_s { char x; short pad; short y; long *z; };")
- assert str(e.value) == (
+ assert str(e.value).startswith(
"struct foo_s: wrong size for field 'y'"
- " (we have 4, but C compiler says 2)")
+ " (cdef says 4, but C compiler says 2)")
def test_ffi_nonfull_struct():
ffi = FFI()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit