Author: Armin Rigo <[email protected]>
Branch:
Changeset: r2424:af8f2df87b10
Date: 2015-11-23 13:17 +0100
http://bitbucket.org/cffi/cffi/changeset/af8f2df87b10/
Log: Copy the PyPy behavior on CPython too: two calls to new_handle(x),
even with the same x, now return cdatas that compare as different.
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1611,7 +1611,7 @@
PyObject_GC_UnTrack(cd);
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
- PyObject *x = (PyObject *)(cd->c_data + 42);
+ PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
Py_DECREF(x);
}
else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */
@@ -1631,7 +1631,7 @@
static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg)
{
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
- PyObject *x = (PyObject *)(cd->c_data + 42);
+ PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
Py_VISIT(x);
}
else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */
@@ -1649,9 +1649,10 @@
static int cdataowninggc_clear(CDataObject *cd)
{
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
- PyObject *x = (PyObject *)(cd->c_data + 42);
+ CDataObject_own_structptr *cd1 = (CDataObject_own_structptr *)cd;
+ PyObject *x = cd1->structobj;
Py_INCREF(Py_None);
- cd->c_data = ((char *)Py_None) - 42;
+ cd1->structobj = Py_None;
Py_DECREF(x);
}
else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */
@@ -1839,7 +1840,7 @@
static PyObject *cdataowninggc_repr(CDataObject *cd)
{
if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */
- PyObject *x = (PyObject *)(cd->c_data + 42);
+ PyObject *x = ((CDataObject_own_structptr *)cd)->structobj;
return _cdata_repr2(cd, "handle to", x);
}
else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */
@@ -5609,10 +5610,26 @@
return Py_None;
}
+static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x)
+{
+ CDataObject_own_structptr *cd;
+ cd = (CDataObject_own_structptr
*)PyObject_GC_New(CDataObject_own_structptr,
+ &CDataOwningGC_Type);
+ if (cd == NULL)
+ return NULL;
+ Py_INCREF(ct_voidp);
+ cd->head.c_type = ct_voidp;
+ cd->head.c_data = (char *)cd;
+ cd->head.c_weakreflist = NULL;
+ Py_INCREF(x);
+ cd->structobj = x;
+ PyObject_GC_Track(cd);
+ return (PyObject *)cd;
+}
+
static PyObject *b_newp_handle(PyObject *self, PyObject *args)
{
CTypeDescrObject *ct;
- CDataObject *cd;
PyObject *x;
if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x))
return NULL;
@@ -5622,47 +5639,38 @@
return NULL;
}
- cd = (CDataObject *)PyObject_GC_New(CDataObject, &CDataOwningGC_Type);
- if (cd == NULL)
- return NULL;
- Py_INCREF(ct);
- cd->c_type = ct;
- Py_INCREF(x);
- cd->c_data = ((char *)x) - 42;
- cd->c_weakreflist = NULL;
- PyObject_GC_Track(cd);
- return (PyObject *)cd;
+ return newp_handle(ct, x);
}
static PyObject *b_from_handle(PyObject *self, PyObject *arg)
{
CTypeDescrObject *ct;
- char *raw;
+ CDataObject_own_structptr *orgcd;
PyObject *x;
if (!CData_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
return NULL;
}
ct = ((CDataObject *)arg)->c_type;
- raw = ((CDataObject *)arg)->c_data;
if (!(ct->ct_flags & CT_CAST_ANYTHING)) {
PyErr_Format(PyExc_TypeError,
"expected a 'cdata' object with a 'void *' out of "
"new_handle(), got '%s'", ct->ct_name);
return NULL;
}
- if (!raw) {
+ orgcd = (CDataObject_own_structptr *)((CDataObject *)arg)->c_data;
+ if (!orgcd) {
PyErr_SetString(PyExc_RuntimeError,
"cannot use from_handle() on NULL pointer");
return NULL;
}
- x = (PyObject *)(raw + 42);
- if (Py_REFCNT(x) <= 0) {
+ if (Py_REFCNT(orgcd) <= 0 || Py_TYPE(orgcd) != &CDataOwningGC_Type) {
Py_FatalError("ffi.from_handle() detected that the address passed "
"points to garbage. If it is really the result of "
"ffi.new_handle(), then the Python object has already "
"been garbage collected");
}
+ x = orgcd->structobj;
Py_INCREF(x);
return x;
}
diff --git a/c/ffi_obj.c b/c/ffi_obj.c
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -679,18 +679,8 @@
static PyObject *ffi_new_handle(FFIObject *self, PyObject *arg)
{
- CDataObject *cd;
-
- cd = (CDataObject *)PyObject_GC_New(CDataObject, &CDataOwningGC_Type);
- if (cd == NULL)
- return NULL;
- Py_INCREF(g_ct_voidp); // <ctype 'void *'>
- cd->c_type = g_ct_voidp;
- Py_INCREF(arg);
- cd->c_data = ((char *)arg) - 42;
- cd->c_weakreflist = NULL;
- PyObject_GC_Track(cd);
- return (PyObject *)cd;
+ /* g_ct_voidp is equal to <ctype 'void *'> */
+ return newp_handle(g_ct_voidp, arg);
}
PyDoc_STRVAR(ffi_from_handle_doc,
@@ -699,32 +689,8 @@
"cdata object returned by new_handle() is still alive (somewhere else\n"
"in the program). Failure to follow these rules will crash.");
-static PyObject *ffi_from_handle(PyObject *self, PyObject *arg)
-{
- CTypeDescrObject *ct;
- char *raw;
- PyObject *x;
- if (!CData_Check(arg)) {
- PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object");
- return NULL;
- }
- ct = ((CDataObject *)arg)->c_type;
- raw = ((CDataObject *)arg)->c_data;
- if (!(ct->ct_flags & CT_CAST_ANYTHING)) {
- PyErr_Format(PyExc_TypeError,
- "expected a 'cdata' object with a 'void *' out of "
- "new_handle(), got '%s'", ct->ct_name);
- return NULL;
- }
- if (!raw) {
- PyErr_SetString(PyExc_RuntimeError,
- "cannot use from_handle() on NULL pointer");
- return NULL;
- }
- x = (PyObject *)(raw + 42);
- Py_INCREF(x);
- return x;
-}
+#define ffi_from_handle b_from_handle /* ffi_from_handle => b_from_handle
+ from _cffi_backend.c */
PyDoc_STRVAR(ffi_from_buffer_doc,
"Return a <cdata 'char[]'> that points to the data of the given Python\n"
diff --git a/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -833,16 +833,18 @@
*Calling ffi.from_handle(p) is invalid and will likely crash if
the cdata object returned by new_handle() is not kept alive!*
-(In case you are wondering, this ``void *`` is not a ``PyObject *``
+(In case you are wondering, this ``void *`` is not the ``PyObject *``
pointer. This wouldn't make sense on PyPy anyway.)
The ``ffi.new_handle()/from_handle()`` functions *conceptually* work
like this:
-* ``new_handle()`` returns a cdata object that contains a reference to
- the Python object; we call them collectively the "handle" cdata
- objects. The ``void *`` value in this handle cdata object is random
- but unique.
+* ``new_handle()`` returns cdata objects that contains references to
+ the Python objects; we call them collectively the "handle" cdata
+ objects. The ``void *`` value in these handle cdata objects are
+ random but unique. *New in version 1.4:* two calls to
+ ``new_handle(x)`` are guaranteed to return cdata objects with
+ different ``void *`` values, even with the same ``x``.
* ``from_handle(p)`` searches all live "handle" cdata objects for the
one that has the same value ``p`` as its ``void *`` value. It then
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
--- a/testing/cffi1/test_ffi_obj.py
+++ b/testing/cffi1/test_ffi_obj.py
@@ -193,6 +193,11 @@
yp = ffi.new_handle([6, 4, 2])
assert ffi.from_handle(yp) == [6, 4, 2]
+def test_handle_unique():
+ ffi = _cffi1_backend.FFI()
+ assert ffi.new_handle(None) is not ffi.new_handle(None)
+ assert ffi.new_handle(None) != ffi.new_handle(None)
+
def test_ffi_cast():
ffi = _cffi1_backend.FFI()
assert ffi.cast("int(*)(int)", 0) == ffi.NULL
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit