You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
23 KiB
713 lines
23 KiB
|
|
/* 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. The
|
|
original idea was to raise AttributeError for anything else, even
|
|
attrs like '__class__', but it breaks various things; now, standard
|
|
attrs are returned, but in the unlikely case where a user cdef()s
|
|
the same name, then the standard attr is hidden (and the various
|
|
things like introspection might break).
|
|
|
|
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;
|
|
void *direct_fn;
|
|
int type_index;
|
|
char doc[1];
|
|
};
|
|
|
|
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 */
|
|
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)
|
|
{
|
|
PyObject *y;
|
|
LibObject *lo;
|
|
PyCFunctionObject *fo;
|
|
|
|
if (!PyCFunction_Check(x))
|
|
return NULL;
|
|
y = PyCFunction_GET_SELF(x);
|
|
if (!LibObject_Check(y))
|
|
return NULL;
|
|
|
|
fo = (PyCFunctionObject *)x;
|
|
lo = (LibObject *)y;
|
|
if (lo->l_libname != fo->m_module)
|
|
return NULL;
|
|
|
|
return (struct CPyExtFunc_s *)(fo->m_ml);
|
|
}
|
|
|
|
static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf)
|
|
{
|
|
PyObject *tuple, *result;
|
|
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 PyObject *_cpyextfunc_type_index(PyObject *x)
|
|
{
|
|
struct CPyExtFunc_s *exf;
|
|
LibObject *lib;
|
|
|
|
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);
|
|
return _cpyextfunc_type(lib, exf);
|
|
}
|
|
|
|
static void cdlopen_close_ignore_errors(void *libhandle); /* forward */
|
|
static void *cdlopen_fetch(PyObject *libname, void *libhandle,
|
|
const char *symbol);
|
|
|
|
static void lib_dealloc(LibObject *lib)
|
|
{
|
|
PyObject_GC_UnTrack(lib);
|
|
cdlopen_close_ignore_errors(lib->l_libhandle);
|
|
Py_DECREF(lib->l_dict);
|
|
Py_DECREF(lib->l_libname);
|
|
Py_DECREF(lib->l_ffi);
|
|
PyObject_GC_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_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().
|
|
*/
|
|
PyObject *result = NULL;
|
|
CTypeDescrObject **pfargs = NULL;
|
|
CTypeDescrObject *fresult;
|
|
Py_ssize_t nargs = 0;
|
|
struct CPyExtFunc_s *xfunc;
|
|
int i, type_index = _CFFI_GETARG(g->type_op);
|
|
_cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types;
|
|
static const char *const format = ";\n\nCFFI C function from %s.lib";
|
|
const char *libname = PyText_AS_UTF8(lib->l_libname);
|
|
struct funcbuilder_s funcbuilder;
|
|
|
|
/* return type: */
|
|
fresult = realize_c_func_return_type(lib->l_types_builder, opcodes,
|
|
type_index);
|
|
if (fresult == NULL)
|
|
goto error;
|
|
|
|
/* argument types: */
|
|
/* note that if the arguments are already built, they have a
|
|
pointer in the 'opcodes' array, and GETOP() returns a
|
|
random even value. But OP_FUNCTION_END is odd, so the
|
|
condition below still works correctly. */
|
|
i = type_index + 1;
|
|
while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END)
|
|
i++;
|
|
pfargs = alloca(sizeof(CTypeDescrObject *) * (i - type_index - 1));
|
|
i = type_index + 1;
|
|
while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) {
|
|
CTypeDescrObject *ct = realize_c_type(lib->l_types_builder, opcodes, i);
|
|
if (ct == NULL)
|
|
goto error;
|
|
pfargs[nargs++] = ct;
|
|
i++;
|
|
}
|
|
|
|
memset(&funcbuilder, 0, sizeof(funcbuilder));
|
|
if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
|
|
goto error;
|
|
|
|
/* The few bytes of memory we allocate here appear to leak, but
|
|
this is not a real leak. Indeed, CPython never unloads its C
|
|
extension modules. There is only one PyMem_Malloc() per real
|
|
C function in a CFFI C extension module. That means that this
|
|
PyMem_Malloc() could also have been written with a static
|
|
global variable generated for each CPYTHON_BLTN defined in the
|
|
C extension, and the effect would be the same (but a bit more
|
|
complicated).
|
|
*/
|
|
xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s) +
|
|
funcbuilder.nb_bytes +
|
|
strlen(format) + strlen(libname));
|
|
if (xfunc == NULL) {
|
|
PyErr_NoMemory();
|
|
goto error;
|
|
}
|
|
memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s));
|
|
assert(g->address);
|
|
xfunc->md.ml_meth = (PyCFunction)g->address;
|
|
xfunc->md.ml_flags = flags;
|
|
xfunc->md.ml_name = g->name;
|
|
xfunc->md.ml_doc = xfunc->doc;
|
|
xfunc->direct_fn = g->size_or_direct_fn;
|
|
xfunc->type_index = type_index;
|
|
|
|
/* build the docstring */
|
|
funcbuilder.bufferp = xfunc->doc;
|
|
if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
|
|
goto error;
|
|
sprintf(funcbuilder.bufferp - 1, format, libname);
|
|
/* done building the docstring */
|
|
|
|
result = PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
|
|
/* fall-through */
|
|
error:
|
|
Py_XDECREF(fresult);
|
|
while (nargs > 0) {
|
|
--nargs;
|
|
Py_DECREF(pfargs[nargs]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
builder_c_t *types_builder = lib->l_types_builder;
|
|
const char *s = PyText_AsUTF8(name);
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
index = search_in_globals(&types_builder->ctx, s, strlen(s));
|
|
if (index < 0) {
|
|
|
|
if (types_builder->included_libs != NULL) {
|
|
Py_ssize_t i;
|
|
PyObject *included_ffis = types_builder->included_ffis;
|
|
PyObject *included_libs = types_builder->included_libs;
|
|
|
|
if (recursion > 100) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"recursion overflow in ffi.include() delegations");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < PyTuple_GET_SIZE(included_libs); i++) {
|
|
LibObject *lib1;
|
|
|
|
lib1 = (LibObject *)PyTuple_GET_ITEM(included_libs, i);
|
|
if (lib1 != NULL) {
|
|
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;
|
|
}
|
|
}
|
|
else {
|
|
FFIObject *ffi1;
|
|
|
|
ffi1 = (FFIObject *)PyTuple_GetItem(included_ffis, i);
|
|
if (ffi1 == NULL)
|
|
return NULL;
|
|
x = ffi_fetch_int_constant(ffi1, s, recursion + 1);
|
|
if (x != NULL)
|
|
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 = &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(types_builder, index);
|
|
break;
|
|
}
|
|
|
|
case _CFFI_OP_CONSTANT:
|
|
case _CFFI_OP_DLOPEN_CONST:
|
|
{
|
|
/* a constant which is not of integer type */
|
|
char *data;
|
|
ct = realize_c_type(types_builder, types_builder->ctx.types,
|
|
_CFFI_GETARG(g->type_op));
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
if (ct->ct_size <= 0) {
|
|
PyErr_Format(FFIError, "constant '%s' is of type '%s', "
|
|
"whose size is not known", s, ct->ct_name);
|
|
return NULL;
|
|
}
|
|
if (g->address == NULL) {
|
|
/* for dlopen() style */
|
|
assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_DLOPEN_CONST);
|
|
data = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
|
|
if (data == NULL)
|
|
return NULL;
|
|
}
|
|
else {
|
|
/* The few bytes of memory we allocate here appear to leak, but
|
|
this is not a real leak. Indeed, CPython never unloads its C
|
|
extension modules. There is only one PyMem_Malloc() per real
|
|
non-integer C constant in a CFFI C extension module. That
|
|
means that this PyMem_Malloc() could also have been written
|
|
with a static global variable generated for each OP_CONSTANT
|
|
defined in the C extension, and the effect would be the same
|
|
(but a bit more complicated).
|
|
|
|
Note that we used to do alloca(), but see issue #198. We
|
|
could still do alloca(), or explicit PyMem_Free(), in some
|
|
cases; but there is no point and it only makes the remaining
|
|
less-common cases more suspicious.
|
|
*/
|
|
assert(_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT);
|
|
data = PyMem_Malloc(ct->ct_size);
|
|
if (data == NULL) {
|
|
PyErr_NoMemory();
|
|
return NULL;
|
|
}
|
|
((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
|
|
(nowadays, only used by the ABI mode or backward
|
|
compatibility; see _CFFI_OP_GLOBAL_VAR_F for the API mode)
|
|
*/
|
|
Py_ssize_t g_size = (Py_ssize_t)g->size_or_direct_fn;
|
|
ct = realize_c_type(types_builder, 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 {
|
|
void *address = g->address;
|
|
if (address == NULL) {
|
|
/* for dlopen() style */
|
|
address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
|
|
if (address == NULL)
|
|
return NULL;
|
|
}
|
|
x = make_global_var(name, ct, address, NULL);
|
|
}
|
|
Py_DECREF(ct);
|
|
break;
|
|
}
|
|
|
|
case _CFFI_OP_GLOBAL_VAR_F:
|
|
ct = realize_c_type(types_builder, types_builder->ctx.types,
|
|
_CFFI_GETARG(g->type_op));
|
|
if (ct == NULL)
|
|
return NULL;
|
|
x = make_global_var(name, ct, NULL, (gs_fetch_addr_fn)g->address);
|
|
Py_DECREF(ct);
|
|
break;
|
|
|
|
case _CFFI_OP_DLOPEN_FUNC:
|
|
{
|
|
/* For dlopen(): the function of the given 'name'. We use
|
|
dlsym() to get the address of something in the dynamic
|
|
library, which we interpret as being exactly a function of
|
|
the specified type.
|
|
*/
|
|
PyObject *ct1;
|
|
void *address = cdlopen_fetch(lib->l_libname, lib->l_libhandle, s);
|
|
if (address == NULL)
|
|
return NULL;
|
|
|
|
ct1 = realize_c_type_or_func(types_builder,
|
|
types_builder->ctx.types,
|
|
_CFFI_GETARG(g->type_op));
|
|
if (ct1 == NULL)
|
|
return NULL;
|
|
|
|
assert(!CTypeDescr_Check(ct1)); /* must be a function */
|
|
x = new_simple_cdata(address, unwrap_fn_as_fnptr(ct1));
|
|
|
|
Py_DECREF(ct1);
|
|
break;
|
|
}
|
|
|
|
case _CFFI_OP_EXTERN_PYTHON:
|
|
/* for reading 'lib.bar' where bar is declared with extern "Python" */
|
|
ct = realize_c_type(types_builder, types_builder->ctx.types,
|
|
_CFFI_GETARG(g->type_op));
|
|
if (ct == NULL)
|
|
return NULL;
|
|
x = convert_to_object((char *)&g->size_or_direct_fn, ct);
|
|
Py_DECREF(ct);
|
|
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_dir1(LibObject *lib, int ignore_global_vars)
|
|
{
|
|
const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
|
|
int i, count = 0, total = lib->l_types_builder->ctx.num_globals;
|
|
PyObject *s, *lst = PyList_New(total);
|
|
if (lst == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; i < total; i++) {
|
|
if (ignore_global_vars) {
|
|
int op = _CFFI_GETOP(g[i].type_op);
|
|
if (op == _CFFI_OP_GLOBAL_VAR || op == _CFFI_OP_GLOBAL_VAR_F)
|
|
continue;
|
|
}
|
|
s = PyText_FromString(g[i].name);
|
|
if (s == NULL)
|
|
goto error;
|
|
PyList_SET_ITEM(lst, count, s);
|
|
count++;
|
|
}
|
|
if (PyList_SetSlice(lst, count, total, NULL) < 0)
|
|
goto error;
|
|
return lst;
|
|
|
|
error:
|
|
Py_DECREF(lst);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *_lib_dict(LibObject *lib)
|
|
{
|
|
const struct _cffi_global_s *g = lib->l_types_builder->ctx.globals;
|
|
int i, total = lib->l_types_builder->ctx.num_globals;
|
|
PyObject *name, *x, *d = PyDict_New();
|
|
if (d == NULL)
|
|
return NULL;
|
|
|
|
for (i = 0; i < total; i++) {
|
|
name = PyText_FromString(g[i].name);
|
|
if (name == NULL)
|
|
goto error;
|
|
|
|
LIB_GET_OR_CACHE_ADDR(x, lib, name, goto error);
|
|
|
|
if (PyDict_SetItem(d, name, x) < 0)
|
|
goto error;
|
|
Py_DECREF(name);
|
|
}
|
|
return d;
|
|
|
|
error:
|
|
Py_XDECREF(name);
|
|
Py_DECREF(d);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *lib_getattr(LibObject *lib, PyObject *name)
|
|
{
|
|
const char *p;
|
|
PyObject *x;
|
|
LIB_GET_OR_CACHE_ADDR(x, lib, name, goto missing);
|
|
|
|
if (GlobSupport_Check(x)) {
|
|
return read_global_var((GlobSupportObject *)x);
|
|
}
|
|
Py_INCREF(x);
|
|
return x;
|
|
|
|
missing:
|
|
/*** ATTRIBUTEERROR IS SET HERE ***/
|
|
p = PyText_AsUTF8(name);
|
|
if (p == NULL)
|
|
return NULL;
|
|
if (strcmp(p, "__all__") == 0) {
|
|
PyErr_Clear();
|
|
return _lib_dir1(lib, 1);
|
|
}
|
|
if (strcmp(p, "__dict__") == 0) {
|
|
PyErr_Clear();
|
|
return _lib_dict(lib);
|
|
}
|
|
if (strcmp(p, "__class__") == 0) {
|
|
PyErr_Clear();
|
|
x = (PyObject *)&PyModule_Type;
|
|
/* ^^^ used to be Py_TYPE(lib). But HAAAAAACK! That makes
|
|
help() behave correctly. I couldn't find a more reasonable
|
|
way. Urgh. */
|
|
Py_INCREF(x);
|
|
return x;
|
|
}
|
|
/* this hack is for Python 3.5, and also to give a more
|
|
module-like behavior */
|
|
if (strcmp(p, "__name__") == 0) {
|
|
PyErr_Clear();
|
|
return PyText_FromFormat("%s.lib", PyText_AS_UTF8(lib->l_libname));
|
|
}
|
|
#if PY_MAJOR_VERSION >= 3
|
|
if (strcmp(p, "__loader__") == 0 || strcmp(p, "__spec__") == 0) {
|
|
/* some more module-like behavior hacks */
|
|
PyErr_Clear();
|
|
Py_INCREF(Py_None);
|
|
return Py_None;
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
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(PyObject *self, PyObject *noarg)
|
|
{
|
|
return _lib_dir1((LibObject *)self, 0);
|
|
}
|
|
|
|
static PyMethodDef lib_methods[] = {
|
|
{"__dir__", 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 | Py_TPFLAGS_HAVE_GC, /* 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, const 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 = (LibObject *)PyType_GenericAlloc(&Lib_Type, 0);
|
|
if (lib == NULL)
|
|
goto err3;
|
|
|
|
lib->l_types_builder = &ffi->types_builder;
|
|
lib->l_dict = dict;
|
|
lib->l_libname = libname;
|
|
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 (on python 2) */
|
|
o_varname = PyText_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' returns a cdata */
|
|
PyObject *ct;
|
|
if (exf->direct_fn == NULL) {
|
|
Py_INCREF(x); /* backward compatibility */
|
|
return x;
|
|
}
|
|
ct = _cpyextfunc_type(lib, exf);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
x = new_simple_cdata(exf->direct_fn, (CTypeDescrObject *)ct);
|
|
Py_DECREF(ct);
|
|
return x;
|
|
}
|
|
if (CData_Check(x) && /* a constant functionptr cdata: 'f == &f' */
|
|
(((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;
|
|
}
|