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

Reply via email to