Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r1869:1b74d35a49ed
Date: 2015-04-28 11:31 +0200
http://bitbucket.org/cffi/cffi/changeset/1b74d35a49ed/
Log: Check again the value of #define constants if they are not defined
with '...'.
diff --git a/_cffi1/_cffi_include.h b/_cffi1/_cffi_include.h
--- a/_cffi1/_cffi_include.h
+++ b/_cffi1/_cffi_include.h
@@ -170,6 +170,10 @@
(size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) :
\
0)
+#define _cffi_check_int(got, got_nonpos, expected) \
+ ((got_nonpos) == (expected <= 0) && \
+ (got) == (unsigned long long)expected)
+
static int _cffi_init(void)
{
diff --git a/_cffi1/realize_c_type.c b/_cffi1/realize_c_type.c
--- a/_cffi1/realize_c_type.c
+++ b/_cffi1/realize_c_type.c
@@ -186,24 +186,37 @@
static PyObject *realize_global_int(const struct _cffi_global_s *g)
{
- PyObject *x;
unsigned long long value;
/* note: we cast g->address to this function type; we do the same
in parse_c_type:parse_sequel() too */
int neg = ((int(*)(unsigned long long*))g->address)(&value);
- if (!neg) {
+
+ switch (neg) {
+
+ case 0:
if (value <= (unsigned long long)LONG_MAX)
- x = PyInt_FromLong((long)value);
+ return PyInt_FromLong((long)value);
else
- x = PyLong_FromUnsignedLongLong(value);
+ return PyLong_FromUnsignedLongLong(value);
+
+ case 1:
+ if ((long long)value >= (long long)LONG_MIN)
+ return PyInt_FromLong((long)value);
+ else
+ return PyLong_FromLongLong((long long)value);
+
+ default:
+ break;
}
- else {
- if ((long long)value >= (long long)LONG_MIN)
- x = PyInt_FromLong((long)value);
- else
- x = PyLong_FromLongLong((long long)value);
- }
- return x;
+
+ char got[64];
+ if (neg == 2)
+ sprintf(got, "%llu (0x%llx)", value, value);
+ else
+ sprintf(got, "%lld", (long long)value);
+ PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, "
+ "but the cdef disagrees", g->name, got);
+ return NULL;
}
static PyObject *
@@ -462,6 +475,8 @@
while (p[j] != ',' && p[j] != '\0')
j++;
tmp = PyString_FromStringAndSize(p, j);
+ if (tmp == NULL)
+ break;
PyTuple_SET_ITEM(enumerators, i, tmp);
gindex = search_in_globals(&builder->ctx, p, j);
@@ -470,6 +485,8 @@
assert(g->type_op == _CFFI_OP(_CFFI_OP_ENUM, -1));
tmp = realize_global_int(g);
+ if (tmp == NULL)
+ break;
PyTuple_SET_ITEM(enumvalues, i, tmp);
p += j + 1;
diff --git a/_cffi1/recompiler.py b/_cffi1/recompiler.py
--- a/_cffi1/recompiler.py
+++ b/_cffi1/recompiler.py
@@ -615,7 +615,8 @@
# ----------
# constants, declared with "static const ..."
- def _generate_cpy_const(self, is_int, name, tp=None, category='const'):
+ def _generate_cpy_const(self, is_int, name, tp=None, category='const',
+ check_value=None):
if (category, name) in self._seen_constants:
raise ffiplatform.VerificationError(
"duplicate declaration of %s '%s'" % (category, name))
@@ -626,11 +627,16 @@
if is_int:
prnt('static int %s(unsigned long long *o)' % funcname)
prnt('{')
+ prnt(' int n = (%s) <= 0;' % (name,))
prnt(' *o = (unsigned long long)((%s) << 0);'
' /* check that we get an integer */' % (name,))
- prnt(' return (%s) <= 0;' % (name,))
+ if check_value is not None:
+ prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,))
+ prnt(' n |= 2;')
+ prnt(' return n;')
prnt('}')
else:
+ assert check_value is None
prnt('static void %s(char *o)' % funcname)
prnt('{')
prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name))
@@ -695,9 +701,11 @@
pass
def _generate_cpy_macro_decl(self, tp, name):
- # for now, we ignore the value (if != ',,,') given in the cdef
- # and always trust the value coming from the C compiler
- self._generate_cpy_const(True, name)
+ if tp == '...':
+ check_value = None
+ else:
+ check_value = tp # an integer
+ self._generate_cpy_const(True, name, check_value=check_value)
def _generate_cpy_macro_ctx(self, tp, name):
self._lsts["global"].append(
diff --git a/_cffi1/test_recompiler.py b/_cffi1/test_recompiler.py
--- a/_cffi1/test_recompiler.py
+++ b/_cffi1/test_recompiler.py
@@ -175,17 +175,40 @@
assert lib.FOOBAR == -6912
py.test.raises(AttributeError, "lib.FOOBAR = 2")
-def test_macro_check_value_ok():
+def test_macro_check_value():
+ # the value '-0x80000000' in C sources does not have a clear meaning
+ # to me; it appears to have a different effect than '-2147483648'...
+ vals = ['42', '-42', '0x80000000', '-2147483648',
+ '0', '9223372036854775809ULL',
+ '-9223372036854775807LL']
ffi = FFI()
- ffi.cdef("#define FOOBAR 42")
- lib = verify(ffi, 'test_macro_check_value_ok', "#define FOOBAR 42")
- assert lib.FOOBAR == 42
+ cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i])
+ for i in range(len(vals))
+ for j in range(len(vals))]
+ ffi.cdef('\n'.join(cdef_lines))
-def test_macro_check_value_fail():
- ffi = FFI()
- ffi.cdef("#define FOOBAR 42")
- lib = verify(ffi, 'test_macro_check_value_fail', "#define FOOBAR 43")
- assert lib.FOOBAR == 43 # for now, we don't check the cdef value
+ verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i]
+ for i in range(len(vals))
+ for j in range(len(vals))]
+ lib = verify(ffi, 'test_macro_check_value_ok',
+ '\n'.join(verify_lines))
+ #
+ for j in range(len(vals)):
+ c_got = int(vals[j].replace('U', '').replace('L', ''), 0)
+ c_compiler_msg = str(c_got)
+ if c_got > 0:
+ c_compiler_msg += ' (0x%x)' % (c_got,)
+ #
+ for i in range(len(vals)):
+ attrname = 'FOO_%d_%d' % (i, j)
+ if i == j:
+ x = getattr(lib, attrname)
+ assert x == c_got
+ else:
+ e = py.test.raises(ffi.error, getattr, lib, attrname)
+ assert str(e.value) == (
+ "the C compiler says '%s' is equal to "
+ "%s, but the cdef disagrees" % (attrname, c_compiler_msg))
def test_constant():
ffi = FFI()
diff --git a/_cffi1/test_verify1.py b/_cffi1/test_verify1.py
--- a/_cffi1/test_verify1.py
+++ b/_cffi1/test_verify1.py
@@ -2171,4 +2171,6 @@
ffi = FFI()
ffi.cdef("#define FOO 123")
lib = ffi.verify("#define FOO 124") # used to complain
- assert lib.FOO == 124
+ e = py.test.raises(ffi.error, "lib.FOO")
+ assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c),"
+ " but the cdef disagrees")
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -23,7 +23,7 @@
_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
_r_words = re.compile(r"\w+|\S")
_parser_cache = None
-_r_int_literal = re.compile(r"^0?x?[0-9a-f]+u?l?$", re.IGNORECASE)
+_r_int_literal = re.compile(r"^0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
def _get_parser():
global _parser_cache
@@ -218,6 +218,9 @@
def _process_macros(self, macros):
for key, value in macros.items():
value = value.strip()
+ neg = value.startswith('-')
+ if neg:
+ value = value[1:].strip()
match = _r_int_literal.search(value)
if match is not None:
int_str = match.group(0).lower().rstrip("ul")
@@ -229,6 +232,7 @@
int_str = "0o" + int_str[1:]
pyvalue = int(int_str, 0)
+ if neg: pyvalue = -pyvalue
self._add_constants(key, pyvalue)
self._declare('macro ' + key, pyvalue)
elif value == '...':
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit