Author: Armin Rigo <ar...@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); _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit