[pypy-commit] creflect default: move some code around and translate gc_weakrefs.py into C.

arigo noreply at buildbot.pypy.org
Fri Dec 5 22:37:25 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r185:771ed4577187
Date: 2014-12-05 22:37 +0100
http://bitbucket.org/cffi/creflect/changeset/771ed4577187/

Log:	move some code around and translate gc_weakrefs.py into C.

diff --git a/zeffir/cdata.c b/zeffir/cdata.c
--- a/zeffir/cdata.c
+++ b/zeffir/cdata.c
@@ -1708,3 +1708,197 @@
     cd->c_weakreflist = NULL;
     return cd;
 }
+
+static int _my_PyObject_AsBool(PyObject *ob)
+{
+    /* convert and cast a Python object to a boolean.  Accept an integer
+       or a float object, up to a CData 'long double'. */
+    PyObject *io;
+    PyNumberMethods *nb;
+    int res;
+
+#if PY_MAJOR_VERSION < 3
+    if (PyInt_Check(ob)) {
+        return PyInt_AS_LONG(ob) != 0;
+    }
+    else
+#endif
+    if (PyLong_Check(ob)) {
+        return _PyLong_Sign(ob) != 0;
+    }
+    else if (PyFloat_Check(ob)) {
+        return PyFloat_AS_DOUBLE(ob) != 0.0;
+    }
+    else if (CData_Check(ob)) {
+        CDataObject *cd = (CDataObject *)ob;
+        if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
+            /*READ(cd->c_data, cd->c_type->ct_size)*/
+            if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
+                /* 'long double' objects: return the answer directly */
+                return read_raw_longdouble_data(cd->c_data) != 0.0;
+            }
+            else {
+                /* 'float'/'double' objects: return the answer directly */
+                return read_raw_float_data(cd->c_data,
+                                           cd->c_type->ct_size) != 0.0;
+            }
+        }
+    }
+    nb = ob->ob_type->tp_as_number;
+    if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) {
+        PyErr_SetString(PyExc_TypeError, "integer/float expected");
+        return -1;
+    }
+    if (nb->nb_float && !CData_Check(ob))
+        io = (*nb->nb_float) (ob);
+    else
+        io = (*nb->nb_int) (ob);
+    if (io == NULL)
+        return -1;
+
+    if (PyIntOrLong_Check(io) || PyFloat_Check(io)) {
+        res = _my_PyObject_AsBool(io);
+    }
+    else {
+        PyErr_SetString(PyExc_TypeError, "integer/float conversion failed");
+        res = -1;
+    }
+    Py_DECREF(io);
+    return res;
+}
+
+static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob)
+{
+    unsigned PY_LONG_LONG value;
+    CDataObject *cd;
+
+    if (CData_Check(ob) &&
+            ((CDataObject *)ob)->c_type->ct_flags & (CT_POINTER | CT_ARRAY)) {
+        value = (Py_intptr_t)((CDataObject *)ob)->c_data;
+    }
+    else if (PyString_Check(ob)) {
+        if (PyString_GET_SIZE(ob) != 1) {
+            PyErr_Format(PyExc_TypeError,
+                         "cannot cast string of length %zd to ctype '%s'",
+                         PyString_GET_SIZE(ob), ct->ct_name);
+            return NULL;
+        }
+        value = (unsigned char)PyString_AS_STRING(ob)[0];
+    }
+    else if (ct->ct_flags & CT_IS_BOOL) {
+        int res = _my_PyObject_AsBool(ob);
+        if (res < 0)
+            return NULL;
+        value = res;
+    }
+    else {
+        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return NULL;
+    }
+    if (ct->ct_flags & CT_IS_BOOL)
+        value = !!value;
+    cd = _new_casted_primitive(ct);
+    if (cd != NULL)
+        write_raw_integer_data(cd->c_data, value, ct->ct_size);
+    return cd;
+}
+
+static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob)
+{
+    CDataObject *cd;
+
+    if (ct->ct_flags & (CT_POINTER | CT_ARRAY) &&
+        ct->ct_size >= 0) {
+        /* cast to a pointer or to an array.
+           Note that casting to an array is an extension to the C language,
+           which seems to be necessary in order to sanely get a
+           <cdata 'int[3]'> at some address. */
+        unsigned PY_LONG_LONG value;
+
+        if (CData_Check(ob)) {
+            CDataObject *cdsrc = (CDataObject *)ob;
+            if (cdsrc->c_type->ct_flags & (CT_POINTER | CT_ARRAY)) {
+                return new_simple_cdata(cdsrc->c_data, ct);
+            }
+        }
+        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
+        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
+            return NULL;
+        return new_simple_cdata((char *)(Py_intptr_t)value, ct);
+    }
+    else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED
+                             |CT_PRIMITIVE_CHAR)) {
+        /* cast to an integer type or a char */
+        return (PyObject *)cast_to_integer_or_char(ct, ob);
+    }
+    else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
+        /* cast to a float */
+        double value;
+        PyObject *io;
+
+        if (CData_Check(ob)) {
+            CDataObject *cdsrc = (CDataObject *)ob;
+
+            if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY))
+                goto cannot_cast;
+            io = convert_to_object(cdsrc->c_data, cdsrc->c_type);
+            if (io == NULL)
+                return NULL;
+        }
+        else {
+            io = ob;
+            Py_INCREF(io);
+        }
+
+        if (PyBytes_Check(io)) {
+            if (PyBytes_GET_SIZE(io) != 1) {
+                Py_DECREF(io);
+                goto cannot_cast;
+            }
+            value = (unsigned char)PyBytes_AS_STRING(io)[0];
+        }
+        else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                 CData_Check(io) &&
+                 (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            char *data = ((CDataObject *)io)->c_data;
+            /*READ(data, sizeof(long double)*/
+            lvalue = read_raw_longdouble_data(data);
+            cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, lvalue);
+            return (PyObject *)cd;
+        }
+        else {
+            value = PyFloat_AsDouble(io);
+        }
+        Py_DECREF(io);
+        if (value == -1.0 && PyErr_Occurred())
+            return NULL;
+
+        cd = _new_casted_primitive(ct);
+        if (cd != NULL) {
+            if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+                write_raw_float_data(cd->c_data, value, ct->ct_size);
+            else
+                write_raw_longdouble_data(cd->c_data, (long double)value);
+        }
+        return (PyObject *)cd;
+    }
+    else {
+        PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'",
+                     ct->ct_name);
+        return NULL;
+    }
+
+ cannot_cast:
+    if (CData_Check(ob))
+        PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'",
+                     ((CDataObject *)ob)->c_type->ct_name, ct->ct_name);
+    else
+        PyErr_Format(PyExc_TypeError,
+                     "cannot cast %.200s object to ctype '%s'",
+                     Py_TYPE(ob)->tp_name, ct->ct_name);
+    return NULL;
+}
diff --git a/zeffir/cgc.c b/zeffir/cgc.c
new file mode 100644
--- /dev/null
+++ b/zeffir/cgc.c
@@ -0,0 +1,86 @@
+
+/* translated to C from cffi/gc_weakref.py */
+
+
+#define GCWREF_DATA(ffi)       ((ffi)->gc_wrefs[0])
+#define GCWREF_CALLBACK(ffi)   ((ffi)->gc_wrefs[1])
+
+
+static PyObject *zef_name_pop;
+
+static PyObject *gc_wref_remove(ZefFFIObject *ffi, PyObject *arg)
+{
+    PyObject *destructor, *cdata, *x;
+    PyObject *res = PyObject_CallMethodObjArgs(GCWREF_DATA(ffi),
+                                               zef_name_pop, arg, NULL);
+    if (res == NULL)
+        return NULL;
+
+    assert(PyTuple_Check(res));
+    destructor = PyTuple_GET_ITEM(res, 0);
+    cdata = PyTuple_GET_ITEM(res, 1);
+    x = PyObject_CallFunctionObjArgs(destructor, cdata, NULL);
+    Py_DECREF(res);
+    if (x == NULL)
+        return NULL;
+    Py_DECREF(x);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static PyMethodDef remove_callback = {
+    "remove", (PyCFunction)gc_wref_remove, METH_O
+};
+
+static PyObject *gc_weakrefs_build(ZefFFIObject *ffi, CDataObject *cd,
+                                   PyObject *destructor)
+{
+    PyObject *new_cdata, *ref = NULL, *tup = NULL;
+
+    if (GCWREF_DATA(ffi) == NULL) {
+        /* initialize */
+        PyObject *remove, *data;
+
+        if (zef_name_pop == NULL) {
+            zef_name_pop = PyString_InternFromString("pop");
+            if (zef_name_pop == NULL)
+                return NULL;
+        }
+        remove = PyCFunction_New(&remove_callback, (PyObject *)ffi);
+        if (remove == NULL)
+            return NULL;
+        data = PyDict_New();
+        if (data == NULL) {
+            Py_DECREF(remove);
+            return NULL;
+        }
+        GCWREF_DATA(ffi)     = data;
+        GCWREF_CALLBACK(ffi) = remove;
+    }
+
+    new_cdata = do_cast(cd->c_type, (PyObject *)cd);
+    if (new_cdata == NULL)
+        goto error;
+
+    ref = PyWeakref_NewRef(new_cdata, GCWREF_CALLBACK(ffi));
+    if (ref == NULL)
+        goto error;
+
+    tup = PyTuple_Pack(2, destructor, cd);
+    if (tup == NULL)
+        goto error;
+
+    if (PyDict_SetItem(GCWREF_DATA(ffi), ref, tup) < 0)
+        goto error;
+
+    Py_DECREF(tup);
+    Py_DECREF(ref);
+    return new_cdata;
+
+ error:
+    Py_XDECREF(new_cdata);
+    Py_XDECREF(ref);
+    Py_XDECREF(tup);
+    return NULL;
+}
diff --git a/zeffir/ffi_obj.c b/zeffir/ffi_obj.c
--- a/zeffir/ffi_obj.c
+++ b/zeffir/ffi_obj.c
@@ -17,18 +17,23 @@
 struct ZefFFIObject_s {
     PyObject_HEAD
     PyObject *types_dict;
+    PyObject *gc_wrefs[2];
 };
 
 static void ffi_dealloc(ZefFFIObject *ffi)
 {
     PyObject_GC_UnTrack(ffi);
     Py_DECREF(ffi->types_dict);
+    Py_XDECREF(ffi->gc_wrefs[0]);
+    Py_XDECREF(ffi->gc_wrefs[1]);
     PyObject_GC_Del(ffi);
 }
 
 static int ffi_traverse(ZefFFIObject *ffi, visitproc visit, void *arg)
 {
     Py_VISIT(ffi->types_dict);
+    Py_VISIT(ffi->gc_wrefs[0]);
+    Py_VISIT(ffi->gc_wrefs[1]);
     return 0;
 }
 
@@ -48,6 +53,8 @@
         return NULL;
     }
     ffi->types_dict = dict;
+    ffi->gc_wrefs[0] = NULL;
+    ffi->gc_wrefs[1] = NULL;
     PyObject_GC_Track(ffi);
 
     return (PyObject *)ffi;
@@ -255,105 +262,9 @@
     return (PyObject *)cd;
 }
 
-static int _my_PyObject_AsBool(PyObject *ob)
-{
-    /* convert and cast a Python object to a boolean.  Accept an integer
-       or a float object, up to a CData 'long double'. */
-    PyObject *io;
-    PyNumberMethods *nb;
-    int res;
-
-#if PY_MAJOR_VERSION < 3
-    if (PyInt_Check(ob)) {
-        return PyInt_AS_LONG(ob) != 0;
-    }
-    else
-#endif
-    if (PyLong_Check(ob)) {
-        return _PyLong_Sign(ob) != 0;
-    }
-    else if (PyFloat_Check(ob)) {
-        return PyFloat_AS_DOUBLE(ob) != 0.0;
-    }
-    else if (CData_Check(ob)) {
-        CDataObject *cd = (CDataObject *)ob;
-        if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
-            /*READ(cd->c_data, cd->c_type->ct_size)*/
-            if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) {
-                /* 'long double' objects: return the answer directly */
-                return read_raw_longdouble_data(cd->c_data) != 0.0;
-            }
-            else {
-                /* 'float'/'double' objects: return the answer directly */
-                return read_raw_float_data(cd->c_data,
-                                           cd->c_type->ct_size) != 0.0;
-            }
-        }
-    }
-    nb = ob->ob_type->tp_as_number;
-    if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) {
-        PyErr_SetString(PyExc_TypeError, "integer/float expected");
-        return -1;
-    }
-    if (nb->nb_float && !CData_Check(ob))
-        io = (*nb->nb_float) (ob);
-    else
-        io = (*nb->nb_int) (ob);
-    if (io == NULL)
-        return -1;
-
-    if (PyIntOrLong_Check(io) || PyFloat_Check(io)) {
-        res = _my_PyObject_AsBool(io);
-    }
-    else {
-        PyErr_SetString(PyExc_TypeError, "integer/float conversion failed");
-        res = -1;
-    }
-    Py_DECREF(io);
-    return res;
-}
-
-static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob)
-{
-    unsigned PY_LONG_LONG value;
-    CDataObject *cd;
-
-    if (CData_Check(ob) &&
-            ((CDataObject *)ob)->c_type->ct_flags & (CT_POINTER | CT_ARRAY)) {
-        value = (Py_intptr_t)((CDataObject *)ob)->c_data;
-    }
-    else if (PyString_Check(ob)) {
-        if (PyString_GET_SIZE(ob) != 1) {
-            PyErr_Format(PyExc_TypeError,
-                         "cannot cast string of length %zd to ctype '%s'",
-                         PyString_GET_SIZE(ob), ct->ct_name);
-            return NULL;
-        }
-        value = (unsigned char)PyString_AS_STRING(ob)[0];
-    }
-    else if (ct->ct_flags & CT_IS_BOOL) {
-        int res = _my_PyObject_AsBool(ob);
-        if (res < 0)
-            return NULL;
-        value = res;
-    }
-    else {
-        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
-        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
-            return NULL;
-    }
-    if (ct->ct_flags & CT_IS_BOOL)
-        value = !!value;
-    cd = _new_casted_primitive(ct);
-    if (cd != NULL)
-        write_raw_integer_data(cd->c_data, value, ct->ct_size);
-    return cd;
-}
-
 static PyObject *ffi_cast(ZefFFIObject *self, PyObject *args)
 {
     CTypeDescrObject *ct;
-    CDataObject *cd;
     PyObject *ob, *arg;
     if (!PyArg_ParseTuple(args, "OO:cast", &arg, &ob))
         return NULL;
@@ -362,99 +273,7 @@
     if (ct == NULL)
         return NULL;
 
-    if (ct->ct_flags & (CT_POINTER | CT_ARRAY) &&
-        ct->ct_size >= 0) {
-        /* cast to a pointer or to an array.
-           Note that casting to an array is an extension to the C language,
-           which seems to be necessary in order to sanely get a
-           <cdata 'int[3]'> at some address. */
-        unsigned PY_LONG_LONG value;
-
-        if (CData_Check(ob)) {
-            CDataObject *cdsrc = (CDataObject *)ob;
-            if (cdsrc->c_type->ct_flags & (CT_POINTER | CT_ARRAY)) {
-                return new_simple_cdata(cdsrc->c_data, ct);
-            }
-        }
-        value = _my_PyLong_AsUnsignedLongLong(ob, 0);
-        if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred())
-            return NULL;
-        return new_simple_cdata((char *)(Py_intptr_t)value, ct);
-    }
-    else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED
-                             |CT_PRIMITIVE_CHAR)) {
-        /* cast to an integer type or a char */
-        return (PyObject *)cast_to_integer_or_char(ct, ob);
-    }
-    else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
-        /* cast to a float */
-        double value;
-        PyObject *io;
-
-        if (CData_Check(ob)) {
-            CDataObject *cdsrc = (CDataObject *)ob;
-
-            if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY))
-                goto cannot_cast;
-            io = convert_to_object(cdsrc->c_data, cdsrc->c_type);
-            if (io == NULL)
-                return NULL;
-        }
-        else {
-            io = ob;
-            Py_INCREF(io);
-        }
-
-        if (PyBytes_Check(io)) {
-            if (PyBytes_GET_SIZE(io) != 1) {
-                Py_DECREF(io);
-                goto cannot_cast;
-            }
-            value = (unsigned char)PyBytes_AS_STRING(io)[0];
-        }
-        else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
-                 CData_Check(io) &&
-                 (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
-            long double lvalue;
-            char *data = ((CDataObject *)io)->c_data;
-            /*READ(data, sizeof(long double)*/
-            lvalue = read_raw_longdouble_data(data);
-            cd = _new_casted_primitive(ct);
-            if (cd != NULL)
-                write_raw_longdouble_data(cd->c_data, lvalue);
-            return (PyObject *)cd;
-        }
-        else {
-            value = PyFloat_AsDouble(io);
-        }
-        Py_DECREF(io);
-        if (value == -1.0 && PyErr_Occurred())
-            return NULL;
-
-        cd = _new_casted_primitive(ct);
-        if (cd != NULL) {
-            if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
-                write_raw_float_data(cd->c_data, value, ct->ct_size);
-            else
-                write_raw_longdouble_data(cd->c_data, (long double)value);
-        }
-        return (PyObject *)cd;
-    }
-    else {
-        PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'",
-                     ct->ct_name);
-        return NULL;
-    }
-
- cannot_cast:
-    if (CData_Check(ob))
-        PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'",
-                     ((CDataObject *)ob)->c_type->ct_name, ct->ct_name);
-    else
-        PyErr_Format(PyExc_TypeError,
-                     "cannot cast %.200s object to ctype '%s'",
-                     Py_TYPE(ob)->tp_name, ct->ct_name);
-    return NULL;
+    return do_cast(ct, ob);
 }
 
 static PyObject *ffi_string(ZefFFIObject *self, PyObject *args)
@@ -728,12 +547,24 @@
     return x;
 }
 
+static PyObject *ffi_gc(ZefFFIObject *self, PyObject *args)
+{
+    CDataObject *cd;
+    PyObject *destructor;
+
+    if (!PyArg_ParseTuple(args, "O!O:gc", &CData_Type, &cd, &destructor))
+        return NULL;
+
+    return gc_weakrefs_build(self, cd, destructor);
+}
+
 
 static PyMethodDef ffi_methods[] = {
     {"addressof",     (PyCFunction)ffi_addressof, METH_VARARGS},
     {"cast",          (PyCFunction)ffi_cast,      METH_VARARGS},
     {"close_library", ffi_close_library,          METH_VARARGS | METH_STATIC},
     {"from_handle",   (PyCFunction)ffi_from_handle,METH_O},
+    {"gc",            (PyCFunction)ffi_gc,        METH_VARARGS},
     {"getctype",      (PyCFunction)ffi_getctype,  METH_VARARGS},
     {"load_library",  (PyCFunction)ffi_load_library,METH_VARARGS|METH_KEYWORDS},
     {"offsetof",      (PyCFunction)ffi_offsetof,  METH_VARARGS},
diff --git a/zeffir/test/test_cgc.py b/zeffir/test/test_cgc.py
new file mode 100644
--- /dev/null
+++ b/zeffir/test/test_cgc.py
@@ -0,0 +1,18 @@
+import gc
+import support
+
+
+def test_simple_gc():
+    ffi = support.new_ffi()
+    p1 = ffi.cast("int *", 0x12345)
+    #
+    seen = []
+    q1 = ffi.gc(p1, seen.append)
+    del p1
+    for i in range(3):
+        gc.collect()
+    assert seen == []
+    del q1
+    for i in range(3):
+        gc.collect()
+    assert seen == [ffi.cast("int *", 0x12345)]
diff --git a/zeffir/zeffir.c b/zeffir/zeffir.c
--- a/zeffir/zeffir.c
+++ b/zeffir/zeffir.c
@@ -18,6 +18,7 @@
 #include "lib_obj.c"
 #include "cfunc.c"
 #include "ffi_obj.c"
+#include "cgc.c"
 #include "builder.c"
 #include "../creflect/creflect_cdecl.c"
 
diff --git a/zeffir/zeffir.h b/zeffir/zeffir.h
--- a/zeffir/zeffir.h
+++ b/zeffir/zeffir.h
@@ -27,3 +27,5 @@
                                             CTypeDescrObject *totype);
 static PyObject *combine_type_name_l(CTypeDescrObject *ct,
                                      size_t extra_text_len);
+static PyObject *gc_weakrefs_build(ZefFFIObject *ffi, CDataObject *cd,
+                                   PyObject *destructor);


More information about the pypy-commit mailing list