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