summaryrefslogtreecommitdiff
path: root/c/lib_obj.c
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2015-05-12 11:07:25 +0200
committerArmin Rigo <arigo@tunes.org>2015-05-12 11:07:25 +0200
commitfe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b (patch)
tree5c80d5c2ee50bc8e99039c7483522e2febc08cb7 /c/lib_obj.c
parent34dbd9932de50a5de29f0fdaab9e9a06526d93a7 (diff)
downloadcffi-fe4bb73d2191ea7b2ee5586848e1c5bbbcbaa72b.tar.gz
the big Moving Files Around step
Diffstat (limited to 'c/lib_obj.c')
-rw-r--r--c/lib_obj.c495
1 files changed, 495 insertions, 0 deletions
diff --git a/c/lib_obj.c b/c/lib_obj.c
new file mode 100644
index 0000000..724317c
--- /dev/null
+++ b/c/lib_obj.c
@@ -0,0 +1,495 @@
+
+/* A Lib object is what is in the "lib" attribute of a C extension
+ module originally created by recompile().
+
+ A Lib object is special in the sense that it has a custom
+ __getattr__ which returns C globals, functions and constants. It
+ raises AttributeError for anything else, even attrs like '__class__'.
+
+ A Lib object has got a reference to the _cffi_type_context_s
+ structure, which is used to create lazily the objects returned by
+ __getattr__.
+*/
+
+struct CPyExtFunc_s {
+ PyMethodDef md;
+ int type_index;
+};
+static const char cpyextfunc_doc[] =
+ "direct call to the C function of the same name";
+
+struct LibObject_s {
+ PyObject_HEAD
+ builder_c_t *l_types_builder; /* same as the one on the ffi object */
+ PyObject *l_dict; /* content, built lazily */
+ PyObject *l_libname; /* some string that gives the name of the lib */
+ PyObject *l_includes; /* tuple of LibObjects included here */
+ FFIObject *l_ffi; /* reference back to the ffi object */
+ void *l_libhandle; /* the dlopen()ed handle, if any */
+};
+
+static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x)
+{
+ struct CPyExtFunc_s *exf;
+
+ if (!PyCFunction_Check(x))
+ return NULL;
+ if (!LibObject_Check(PyCFunction_GET_SELF(x)))
+ return NULL;
+
+ exf = (struct CPyExtFunc_s *)(((PyCFunctionObject *)x) -> m_ml);
+ if (exf->md.ml_doc != cpyextfunc_doc)
+ return NULL;
+
+ return exf;
+}
+
+static PyObject *_cpyextfunc_type_index(PyObject *x)
+{
+ struct CPyExtFunc_s *exf;
+ LibObject *lib;
+ PyObject *tuple, *result;
+
+ assert(PyErr_Occurred());
+ exf = _cpyextfunc_get(x);
+ if (exf == NULL)
+ return NULL; /* still the same exception is set */
+
+ PyErr_Clear();
+
+ lib = (LibObject *)PyCFunction_GET_SELF(x);
+ tuple = realize_c_type_or_func(lib->l_types_builder,
+ lib->l_types_builder->ctx.types,
+ exf->type_index);
+ if (tuple == NULL)
+ return NULL;
+
+ /* 'tuple' is a tuple of length 1 containing the real CT_FUNCTIONPTR
+ object */
+ result = PyTuple_GetItem(tuple, 0);
+ Py_XINCREF(result);
+ Py_DECREF(tuple);
+ return result;
+}
+
+static void cdlopen_close_ignore_errors(void *libhandle); /* forward */
+static void *cdlopen_fetch(PyObject *libname, void *libhandle, char *symbol);
+
+static void lib_dealloc(LibObject *lib)
+{
+ cdlopen_close_ignore_errors(lib->l_libhandle);
+ Py_DECREF(lib->l_dict);
+ Py_DECREF(lib->l_libname);
+ Py_XDECREF(lib->l_includes);
+ Py_DECREF(lib->l_ffi);
+ PyObject_Del(lib);
+}
+
+static int lib_traverse(LibObject *lib, visitproc visit, void *arg)
+{
+ Py_VISIT(lib->l_dict);
+ Py_VISIT(lib->l_libname);
+ Py_VISIT(lib->l_includes);
+ Py_VISIT(lib->l_ffi);
+ return 0;
+}
+
+static PyObject *lib_repr(LibObject *lib)
+{
+ return PyText_FromFormat("<Lib object for '%.200s'>",
+ PyText_AS_UTF8(lib->l_libname));
+}
+
+static PyObject *lib_build_cpython_func(LibObject *lib,
+ const struct _cffi_global_s *g,
+ const char *s, int flags)
+{
+ /* First make sure the argument types and return type are really
+ built. The C extension code can then assume that they are,
+ by calling _cffi_type().
+ */
+ CTypeDescrObject *ct;
+ struct CPyExtFunc_s *xfunc;
+ int i, type_index = _CFFI_GETARG(g->type_op);
+ _cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types;
+ assert(_CFFI_GETOP(opcodes[type_index]) == _CFFI_OP_FUNCTION);
+
+ /* return type: */
+ ct = realize_c_type(lib->l_types_builder, opcodes,
+ _CFFI_GETARG(opcodes[type_index]));
+ if (ct == NULL)
+ return NULL;
+ Py_DECREF(ct);
+
+ /* argument types: */
+ i = type_index + 1;
+ while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) {
+ ct = realize_c_type(lib->l_types_builder, opcodes, i);
+ if (ct == NULL)
+ return NULL;
+ Py_DECREF(ct);
+ i++;
+ }
+
+ /* xxx the few bytes of memory we allocate here leak, but it's a
+ minor concern because it should only occur for CPYTHON_BLTN.
+ There is one per real C function in a CFFI C extension module.
+ CPython never unloads its C extension modules anyway.
+ */
+ xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s));
+ if (xfunc == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s));
+ xfunc->md.ml_meth = (PyCFunction)g->address;
+ xfunc->md.ml_flags = flags;
+ xfunc->md.ml_name = g->name;
+ xfunc->md.ml_doc = cpyextfunc_doc;
+ xfunc->type_index = type_index;
+
+ return PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
+}
+
+static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name,
+ int recursion)
+{
+ /* does not return a new reference! */
+ PyObject *x;
+ int index;
+ const struct _cffi_global_s *g;
+ CTypeDescrObject *ct;
+ char *s = PyText_AsUTF8(name);
+ if (s == NULL)
+ return NULL;
+
+ index = search_in_globals(&lib->l_types_builder->ctx, s, strlen(s));
+ if (index < 0) {
+
+ if (lib->l_includes != NULL) {
+ Py_ssize_t i;
+
+ if (recursion > 100) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "recursion overflow in ffi.include() delegations");
+ return NULL;
+ }
+
+ for (i = 0; i < PyTuple_GET_SIZE(lib->l_includes); i++) {
+ LibObject *lib1;
+ lib1 = (LibObject *)PyTuple_GET_ITEM(lib->l_includes, i);
+ x = PyDict_GetItem(lib1->l_dict, name);
+ if (x != NULL) {
+ Py_INCREF(x);
+ goto found;
+ }
+ x = lib_build_and_cache_attr(lib1, name, recursion + 1);
+ if (x != NULL) {
+ Py_INCREF(x);
+ goto found;
+ }
+ if (PyErr_Occurred())
+ return NULL;
+ }
+ }
+
+ if (recursion > 0)
+ return NULL; /* no error set, continue looking elsewhere */
+
+ PyErr_Format(PyExc_AttributeError,
+ "cffi library '%.200s' has no function, constant "
+ "or global variable named '%.200s'",
+ PyText_AS_UTF8(lib->l_libname), s);
+ return NULL;
+ }
+
+ g = &lib->l_types_builder->ctx.globals[index];
+
+ switch (_CFFI_GETOP(g->type_op)) {
+
+ case _CFFI_OP_CPYTHON_BLTN_V:
+ x = lib_build_cpython_func(lib, g, s, METH_VARARGS);
+ break;
+
+ case _CFFI_OP_CPYTHON_BLTN_N:
+ x = lib_build_cpython_func(lib, g, s, METH_NOARGS);
+ break;
+
+ case _CFFI_OP_CPYTHON_BLTN_O:
+ x = lib_build_cpython_func(lib, g, s, METH_O);
+ break;
+
+ case _CFFI_OP_CONSTANT_INT:
+ case _CFFI_OP_ENUM:
+ {
+ /* a constant integer whose value, in an "unsigned long long",
+ is obtained by calling the function at g->address */
+ x = realize_global_int(lib->l_types_builder, index);
+ break;
+ }
+
+ case _CFFI_OP_CONSTANT:
+ {
+ /* a constant which is not of integer type */
+ char *data;
+ ct = realize_c_type(lib->l_types_builder,
+ lib->l_types_builder->ctx.types,
+ _CFFI_GETARG(g->type_op));
+ if (ct == NULL)
+ return NULL;
+
+ assert(ct->ct_size > 0);
+ data = alloca(ct->ct_size);
+ ((void(*)(char*))g->address)(data);
+ x = convert_to_object(data, ct);
+ Py_DECREF(ct);
+ break;
+ }
+
+ case _CFFI_OP_GLOBAL_VAR:
+ /* global variable of the exact type specified here */
+ ct = realize_c_type(lib->l_types_builder,
+ lib->l_types_builder->ctx.types,
+ _CFFI_GETARG(g->type_op));
+ if (ct == NULL)
+ return NULL;
+ if (g->size != ct->ct_size && g->size != 0 && ct->ct_size > 0) {
+ PyErr_Format(FFIError,
+ "global variable '%.200s' should be %zd bytes "
+ "according to the cdef, but is actually %zd",
+ s, ct->ct_size, g->size);
+ x = NULL;
+ }
+ else {
+ x = make_global_var(ct, g->address);
+ }
+ Py_DECREF(ct);
+ break;
+
+ case _CFFI_OP_DLOPEN:
+ {
+ /* For dlopen(): the function or global variable of the given
+ 'name'. We use dlsym() to get the address of something in
+ the dynamic library, which we interpret as being exactly of
+ the specified type. If this type is a function (not a
+ function pointer), then we assume it is a regular function
+ in the dynamic library; otherwise, we assume it is a global
+ variable.
+ */
+ PyObject *ct1;
+ void *address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
+ if (address == NULL)
+ return NULL;
+
+ ct1 = realize_c_type_or_func(lib->l_types_builder,
+ lib->l_types_builder->ctx.types,
+ _CFFI_GETARG(g->type_op));
+ if (ct1 == NULL)
+ return NULL;
+
+ if (CTypeDescr_Check(ct1))
+ x = make_global_var((CTypeDescrObject *)ct1, address);
+ else
+ x = new_simple_cdata(address, unwrap_fn_as_fnptr(ct1));
+
+ Py_DECREF(ct1);
+ break;
+ }
+
+ default:
+ PyErr_Format(PyExc_NotImplementedError, "in lib_build_attr: op=%d",
+ (int)_CFFI_GETOP(g->type_op));
+ return NULL;
+ }
+
+ found:
+ if (x != NULL) {
+ int err = PyDict_SetItem(lib->l_dict, name, x);
+ Py_DECREF(x);
+ if (err < 0) /* else there is still one ref left in the dict */
+ return NULL;
+ }
+ return x;
+}
+
+#define LIB_GET_OR_CACHE_ADDR(x, lib, name, error) \
+ do { \
+ x = PyDict_GetItem(lib->l_dict, name); \
+ if (x == NULL) { \
+ x = lib_build_and_cache_attr(lib, name, 0); \
+ if (x == NULL) { \
+ error; \
+ } \
+ } \
+ } while (0)
+
+static PyObject *lib_getattr(LibObject *lib, PyObject *name)
+{
+ PyObject *x;
+ LIB_GET_OR_CACHE_ADDR(x, lib, name, return NULL);
+
+ if (GlobSupport_Check(x)) {
+ return read_global_var((GlobSupportObject *)x);
+ }
+ Py_INCREF(x);
+ return x;
+}
+
+static int lib_setattr(LibObject *lib, PyObject *name, PyObject *val)
+{
+ PyObject *x;
+ LIB_GET_OR_CACHE_ADDR(x, lib, name, return -1);
+
+ if (val == NULL) {
+ PyErr_SetString(PyExc_AttributeError, "C attribute cannot be deleted");
+ return -1;
+ }
+
+ if (GlobSupport_Check(x)) {
+ return write_global_var((GlobSupportObject *)x, val);
+ }
+
+ PyErr_Format(PyExc_AttributeError,
+ "cannot write to function or constant '%.200s'",
+ PyText_Check(name) ? PyText_AS_UTF8(name) : "?");
+ return -1;
+}
+
+static PyObject *lib_dir(LibObject *lib, PyObject *noarg)
+{
+ const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
+ int i, total = lib->l_types_builder->ctx.num_globals;
+ PyObject *lst = PyList_New(total);
+ if (lst == NULL)
+ return NULL;
+
+ for (i = 0; i < total; i++) {
+ PyObject *s = PyText_FromString(g[i].name);
+ if (s == NULL) {
+ Py_DECREF(lst);
+ return NULL;
+ }
+ PyList_SET_ITEM(lst, i, s);
+ }
+ return lst;
+}
+
+static PyMethodDef lib_methods[] = {
+ {"__dir__", (PyCFunction)lib_dir, METH_NOARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyTypeObject Lib_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "CompiledLib",
+ sizeof(LibObject),
+ 0,
+ (destructor)lib_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)lib_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ (getattrofunc)lib_getattr, /* tp_getattro */
+ (setattrofunc)lib_setattr, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)lib_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ lib_methods, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof(LibObject, l_dict), /* tp_dictoffset */
+};
+
+static LibObject *lib_internal_new(FFIObject *ffi, char *module_name,
+ void *dlopen_libhandle)
+{
+ LibObject *lib;
+ PyObject *libname, *dict;
+
+ libname = PyText_FromString(module_name);
+ if (libname == NULL)
+ goto err1;
+
+ dict = PyDict_New();
+ if (dict == NULL)
+ goto err2;
+
+ lib = PyObject_New(LibObject, &Lib_Type);
+ if (lib == NULL)
+ goto err3;
+
+ lib->l_types_builder = &ffi->types_builder;
+ lib->l_dict = dict;
+ lib->l_libname = libname;
+ lib->l_includes = NULL;
+ Py_INCREF(ffi);
+ lib->l_ffi = ffi;
+ lib->l_libhandle = dlopen_libhandle;
+ return lib;
+
+ err3:
+ Py_DECREF(dict);
+ err2:
+ Py_DECREF(libname);
+ err1:
+ cdlopen_close_ignore_errors(dlopen_libhandle);
+ return NULL;
+}
+
+static PyObject *address_of_global_var(PyObject *args)
+{
+ LibObject *lib;
+ PyObject *x, *o_varname;
+ char *varname;
+
+ if (!PyArg_ParseTuple(args, "O!s", &Lib_Type, &lib, &varname))
+ return NULL;
+
+ /* rebuild a string from 'varname', to do typechecks and to force
+ a unicode back to a plain string */
+ o_varname = PyString_FromString(varname);
+ if (o_varname == NULL)
+ return NULL;
+
+ LIB_GET_OR_CACHE_ADDR(x, lib, o_varname, goto error);
+ Py_DECREF(o_varname);
+ if (GlobSupport_Check(x)) {
+ return cg_addressof_global_var((GlobSupportObject *)x);
+ }
+ else {
+ struct CPyExtFunc_s *exf = _cpyextfunc_get(x);
+ if (exf != NULL || /* an OP_CPYTHON_BLTN: '&func' is 'func' in C */
+ ((CData_Check(x) && /* or, a constant functionptr cdata: same */
+ (((CDataObject *)x)->c_type->ct_flags & CT_FUNCTIONPTR) != 0))) {
+ Py_INCREF(x);
+ return x;
+ }
+ else {
+ PyErr_Format(PyExc_AttributeError,
+ "cannot take the address of the constant '%.200s'",
+ varname);
+ return NULL;
+ }
+ }
+
+ error:
+ Py_DECREF(o_varname);
+ return NULL;
+}