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

Reply via email to