Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r2124:22eff74bf6e8
Date: 2015-05-28 18:11 +0200
http://bitbucket.org/cffi/cffi/changeset/22eff74bf6e8/

Log:    ABI mode: allow constants of any type, which are looked up from the
        lib on their first access.

diff --git a/c/lib_obj.c b/c/lib_obj.c
--- a/c/lib_obj.c
+++ b/c/lib_obj.c
@@ -258,23 +258,30 @@
         if (ct == NULL)
             return NULL;
 
-        assert(g->address);
         if (ct->ct_size <= 0) {
             PyErr_SetString(PyExc_SystemError, "constant has no known size");
             return NULL;
         }
-        /* xxx the few bytes of memory we allocate here leak, but it's
-           a minor concern because it should only occur for
-           OP_CONSTANT.  There is one per real non-integer C constant
-           in a CFFI C extension module.  CPython never unloads its C
-           extension modules anyway.  Note that we used to do alloca(),
-           but see issue #198. */
-        data = PyMem_Malloc(ct->ct_size);
-        if (data == NULL) {
-            PyErr_NoMemory();
-            return NULL;
+        if (g->address == NULL) {
+            /* for dlopen() style */
+            data = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
+            if (data == NULL)
+                return NULL;
         }
-        ((void(*)(char*))g->address)(data);
+        else {
+            /* xxx the few bytes of memory we allocate here leak, but it's
+               a minor concern because it should only occur for
+               OP_CONSTANT.  There is one per real non-integer C constant
+               in a CFFI C extension module.  CPython never unloads its C
+               extension modules anyway.  Note that we used to do alloca(),
+               but see issue #198. */
+            data = PyMem_Malloc(ct->ct_size);
+            if (data == NULL) {
+                PyErr_NoMemory();
+                return NULL;
+            }
+            ((void(*)(char*))g->address)(data);
+        }
         x = convert_to_object(data, ct);
         Py_DECREF(ct);
         break;
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -11,7 +11,7 @@
 
 
 class GlobalExpr:
-    def __init__(self, name, address, type_op, size=0, check_value=None):
+    def __init__(self, name, address, type_op, size=0, check_value=0):
         self.name = name
         self.address = address
         self.type_op = type_op
@@ -23,11 +23,6 @@
             self.name, self.address, self.type_op.as_c_expr(), self.size)
 
     def as_python_expr(self):
-        if not isinstance(self.check_value, int_type):
-            raise ffiplatform.VerificationError(
-                "ffi.dlopen() will not be able to figure out the value of "
-                "constant %r (only integer constants are supported, and only "
-                "if their value are specified in the cdef)" % (self.name,))
         return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
                                self.check_value)
 
@@ -747,7 +742,7 @@
             meth_kind = OP_CPYTHON_BLTN_V   # 'METH_VARARGS'
         self._lsts["global"].append(
             GlobalExpr(name, '_cffi_f_%s' % name,
-                       CffiOp(meth_kind, type_index), check_value=0,
+                       CffiOp(meth_kind, type_index),
                        size='_cffi_d_%s' % name))
 
     # ----------
@@ -971,7 +966,7 @@
 
     def _generate_cpy_constant_collecttype(self, tp, name):
         is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
-        if not is_int:
+        if not is_int or self.target_is_python:
             self._do_collect_type(tp)
 
     def _generate_cpy_constant_decl(self, tp, name):
@@ -979,7 +974,8 @@
         self._generate_cpy_const(is_int, name, tp)
 
     def _generate_cpy_constant_ctx(self, tp, name):
-        if isinstance(tp, model.PrimitiveType) and tp.is_integer_type():
+        if (not self.target_is_python and
+                isinstance(tp, model.PrimitiveType) and tp.is_integer_type()):
             type_op = CffiOp(OP_CONSTANT_INT, -1)
         else:
             if not tp.sizeof_enabled():
@@ -1038,6 +1034,10 @@
 
     def _generate_cpy_macro_ctx(self, tp, name):
         if tp == '...':
+            if self.target_is_python:
+                raise ffiplatform.VerificationError(
+                    "cannot use the syntax '...' in '#define %s ...' when "
+                    "using the ABI mode" % (name,))
             check_value = None
         else:
             check_value = tp     # an integer
@@ -1070,7 +1070,7 @@
         else:
             size = 0
         self._lsts["global"].append(
-            GlobalExpr(name, '&%s' % name, type_op, size, 0))
+            GlobalExpr(name, '&%s' % name, type_op, size))
 
     # ----------
     # emitting the opcodes for individual types
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -6,6 +6,13 @@
 1.0.4
 =====
 
+* Issue #175: in ABI mode: we now support any constant declaration,
+  instead of only integers whose value is given in the cdef.  Such "new"
+  constants, i.e. either non-integers or without a value given in the
+  cdef, must correspond to actual symbols in the lib.  At runtime they
+  are looked up the first time we access them.  This is useful if the
+  library defines ``extern const sometype somename;``.
+
 * Issue #198: in API mode, if you declare constants of a ``struct``
   type, what you saw from lib.CONSTANT was corrupted.
 
diff --git a/testing/cffi1/test_dlopen.py b/testing/cffi1/test_dlopen.py
--- a/testing/cffi1/test_dlopen.py
+++ b/testing/cffi1/test_dlopen.py
@@ -19,27 +19,20 @@
 )
 """
 
-def test_invalid_global_constant():
+def test_global_constant():
     ffi = FFI()
-    ffi.cdef("static const int BB;")
-    target = udir.join('test_invalid_global_constants.py')
-    e = py.test.raises(VerificationError, make_py_source, ffi,
-                       'test_invalid_global_constants', str(target))
-    assert str(e.value) == (
-        "ffi.dlopen() will not be able to figure out "
-        "the value of constant 'BB' (only integer constants are "
-        "supported, and only if their value are specified in the cdef)")
+    ffi.cdef("static const long BB; static const float BF = 12;")
+    target = udir.join('test_valid_global_constant.py')
+    make_py_source(ffi, 'test_valid_global_constant', str(target))
+    assert target.read() == r"""# auto-generated file
+import _cffi_backend
 
-def test_invalid_global_constant_2():
-    ffi = FFI()
-    ffi.cdef("static const float BB = 12;")
-    target = udir.join('test_invalid_global_constants_2.py')
-    e = py.test.raises(VerificationError, make_py_source, ffi,
-                       'test_invalid_global_constants_2', str(target))
-    assert str(e.value) == (
-        "ffi.dlopen() will not be able to figure out "
-        "the value of constant 'BB' (only integer constants are "
-        "supported, and only if their value are specified in the cdef)")
+ffi = _cffi_backend.FFI('test_valid_global_constant',
+    _version = 0x2601,
+    _types = b'\x00\x00\x0D\x01\x00\x00\x09\x01',
+    _globals = (b'\x00\x00\x01\x1DBB',0,b'\x00\x00\x00\x1DBF',0),
+)
+"""
 
 def test_invalid_global_constant_3():
     ffi = FFI()
@@ -53,10 +46,8 @@
     target = udir.join('test_invalid_dotdotdot_in_macro.py')
     e = py.test.raises(VerificationError, make_py_source, ffi,
                        'test_invalid_dotdotdot_in_macro', str(target))
-    assert str(e.value) == (
-        "ffi.dlopen() will not be able to figure out "
-        "the value of constant 'FOO' (only integer constants are "
-        "supported, and only if their value are specified in the cdef)")
+    assert str(e.value) == ("macro FOO: cannot use the syntax '...' in "
+                            "'#define FOO ...' when using the ABI mode")
 
 def test_typename():
     ffi = FFI()
diff --git a/testing/cffi1/test_re_python.py b/testing/cffi1/test_re_python.py
--- a/testing/cffi1/test_re_python.py
+++ b/testing/cffi1/test_re_python.py
@@ -14,6 +14,8 @@
     int add42(int x) { return x + 42; }
     int add43(int x, ...) { return x; }
     int globalvar42 = 1234;
+    const int globalconst42 = 4321;
+    const char *const globalconsthello = "hello";
     struct foo_s;
     typedef struct bar_s { int x; signed char a[]; } bar_t;
     enum foo_e { AA, BB, CC };
@@ -28,7 +30,8 @@
     ext = ffiplatform.get_extension(
         str(c_file),
         '_test_re_python',
-        export_symbols=['add42', 'add43', 'globalvar42']
+        export_symbols=['add42', 'add43', 'globalvar42',
+                        'globalconst42', 'globalconsthello']
     )
     outputfilename = ffiplatform.compile(str(tmpdir), ext)
     mod.extmod = outputfilename
@@ -43,6 +46,8 @@
     int add42(int);
     int add43(int, ...);
     int globalvar42;
+    const int globalconst42;
+    const char *const globalconsthello = "hello";
     int no_such_function(int);
     int no_such_globalvar;
     struct foo_s;
@@ -152,6 +157,18 @@
     p[0] -= 1
     assert lib.globalvar42 == 1238
 
+def test_global_const_int():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert lib.globalconst42 == 4321
+    py.test.raises(AttributeError, ffi.addressof, lib, 'globalconst42')
+
+def test_global_const_nonint():
+    from re_python_pysrc import ffi
+    lib = ffi.dlopen(extmod)
+    assert ffi.string(lib.globalconsthello, 8) == "hello"
+    py.test.raises(AttributeError, ffi.addressof, lib, 'globalconsthello')
+
 def test_rtld_constants():
     from re_python_pysrc import ffi
     ffi.RTLD_NOW    # check that we have the attributes
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to