| /* 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 */ | |
| int l_auto_close; /* if we must dlclose() this handle */ | |
| }; | |
| 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); | |
| if (lib->l_auto_close) | |
| 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) | |
| "_cffi_backend.Lib", | |
| 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, int auto_close) | |
| { | |
| 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; | |
| lib->l_auto_close = auto_close; | |
| return lib; | |
| err3: | |
| Py_DECREF(dict); | |
| err2: | |
| Py_DECREF(libname); | |
| err1: | |
| if (auto_close) | |
| 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; | |
| } |