| /* ffi.dlopen() interface with dlopen()/dlsym()/dlclose() */ | |
| static void *cdlopen_fetch(PyObject *libname, void *libhandle, | |
| const char *symbol) | |
| { | |
| void *address; | |
| if (libhandle == NULL) { | |
| PyErr_Format(FFIError, "library '%s' has been closed", | |
| PyText_AS_UTF8(libname)); | |
| return NULL; | |
| } | |
| dlerror(); /* clear error condition */ | |
| address = dlsym(libhandle, symbol); | |
| if (address == NULL) { | |
| const char *error = dlerror(); | |
| PyErr_Format(FFIError, "symbol '%s' not found in library '%s': %s", | |
| symbol, PyText_AS_UTF8(libname), error); | |
| } | |
| return address; | |
| } | |
| static void cdlopen_close_ignore_errors(void *libhandle) | |
| { | |
| if (libhandle != NULL) | |
| dlclose(libhandle); | |
| } | |
| static int cdlopen_close(PyObject *libname, void *libhandle) | |
| { | |
| if (libhandle != NULL && dlclose(libhandle) != 0) { | |
| const char *error = dlerror(); | |
| PyErr_Format(FFIError, "closing library '%s': %s", | |
| PyText_AS_UTF8(libname), error); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static PyObject *ffi_dlopen(PyObject *self, PyObject *args) | |
| { | |
| const char *modname; | |
| PyObject *temp, *result = NULL; | |
| void *handle; | |
| int auto_close; | |
| handle = b_do_dlopen(args, &modname, &temp, &auto_close); | |
| if (handle != NULL) | |
| { | |
| result = (PyObject *)lib_internal_new((FFIObject *)self, | |
| modname, handle, auto_close); | |
| } | |
| Py_XDECREF(temp); | |
| return result; | |
| } | |
| static PyObject *ffi_dlclose(PyObject *self, PyObject *args) | |
| { | |
| LibObject *lib; | |
| void *libhandle; | |
| if (!PyArg_ParseTuple(args, "O!", &Lib_Type, &lib)) | |
| return NULL; | |
| libhandle = lib->l_libhandle; | |
| if (libhandle != NULL) | |
| { | |
| lib->l_libhandle = NULL; | |
| /* Clear the dict to force further accesses to do cdlopen_fetch() | |
| again, and fail because the library was closed. */ | |
| PyDict_Clear(lib->l_dict); | |
| if (cdlopen_close(lib->l_libname, libhandle) < 0) | |
| return NULL; | |
| } | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| static Py_ssize_t cdl_4bytes(char *src) | |
| { | |
| /* read 4 bytes in little-endian order; return it as a signed integer */ | |
| signed char *ssrc = (signed char *)src; | |
| unsigned char *usrc = (unsigned char *)src; | |
| return (ssrc[0] << 24) | (usrc[1] << 16) | (usrc[2] << 8) | usrc[3]; | |
| } | |
| static _cffi_opcode_t cdl_opcode(char *src) | |
| { | |
| return (_cffi_opcode_t)cdl_4bytes(src); | |
| } | |
| typedef struct { | |
| unsigned long long value; | |
| int neg; | |
| } cdl_intconst_t; | |
| static int _cdl_realize_global_int(struct _cffi_getconst_s *gc) | |
| { | |
| /* The 'address' field of 'struct _cffi_global_s' is set to point | |
| to this function in case ffiobj_init() sees constant integers. | |
| This fishes around after the 'ctx->globals' array, which is | |
| initialized to contain another array, this time of | |
| 'cdl_intconst_t' structures. We get the nth one and it tells | |
| us what to return. | |
| */ | |
| cdl_intconst_t *ic; | |
| ic = (cdl_intconst_t *)(gc->ctx->globals + gc->ctx->num_globals); | |
| ic += gc->gindex; | |
| gc->value = ic->value; | |
| return ic->neg; | |
| } | |
| static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds) | |
| { | |
| FFIObject *ffi; | |
| static char *keywords[] = {"module_name", "_version", "_types", | |
| "_globals", "_struct_unions", "_enums", | |
| "_typenames", "_includes", NULL}; | |
| char *ffiname = "?", *types = NULL, *building = NULL; | |
| Py_ssize_t version = -1; | |
| Py_ssize_t types_len = 0; | |
| PyObject *globals = NULL, *struct_unions = NULL, *enums = NULL; | |
| PyObject *typenames = NULL, *includes = NULL; | |
| if (!PyArg_ParseTupleAndKeywords(args, kwds, | |
| "|sns#O!O!O!O!O!:FFI", keywords, | |
| &ffiname, &version, &types, &types_len, | |
| &PyTuple_Type, &globals, | |
| &PyTuple_Type, &struct_unions, | |
| &PyTuple_Type, &enums, | |
| &PyTuple_Type, &typenames, | |
| &PyTuple_Type, &includes)) | |
| return -1; | |
| ffi = (FFIObject *)self; | |
| if (ffi->ctx_is_nonempty) { | |
| PyErr_SetString(PyExc_ValueError, | |
| "cannot call FFI.__init__() more than once"); | |
| return -1; | |
| } | |
| ffi->ctx_is_nonempty = 1; | |
| if (version == -1 && types_len == 0) | |
| return 0; | |
| if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { | |
| PyErr_Format(PyExc_ImportError, | |
| "cffi out-of-line Python module '%s' has unknown " | |
| "version %p", ffiname, (void *)version); | |
| return -1; | |
| } | |
| if (types_len > 0) { | |
| /* unpack a string of 4-byte entries into an array of _cffi_opcode_t */ | |
| _cffi_opcode_t *ntypes; | |
| Py_ssize_t i, n = types_len / 4; | |
| building = PyMem_Malloc(n * sizeof(_cffi_opcode_t)); | |
| if (building == NULL) | |
| goto error; | |
| ntypes = (_cffi_opcode_t *)building; | |
| for (i = 0; i < n; i++) { | |
| ntypes[i] = cdl_opcode(types); | |
| types += 4; | |
| } | |
| ffi->types_builder.ctx.types = ntypes; | |
| ffi->types_builder.ctx.num_types = n; | |
| building = NULL; | |
| } | |
| if (globals != NULL) { | |
| /* unpack a tuple alternating strings and ints, each two together | |
| describing one global_s entry with no specified address or size. | |
| The int is only used with integer constants. */ | |
| struct _cffi_global_s *nglobs; | |
| cdl_intconst_t *nintconsts; | |
| Py_ssize_t i, n = PyTuple_GET_SIZE(globals) / 2; | |
| i = n * (sizeof(struct _cffi_global_s) + sizeof(cdl_intconst_t)); | |
| building = PyMem_Malloc(i); | |
| if (building == NULL) | |
| goto error; | |
| memset(building, 0, i); | |
| nglobs = (struct _cffi_global_s *)building; | |
| nintconsts = (cdl_intconst_t *)(nglobs + n); | |
| for (i = 0; i < n; i++) { | |
| char *g = PyBytes_AS_STRING(PyTuple_GET_ITEM(globals, i * 2)); | |
| nglobs[i].type_op = cdl_opcode(g); g += 4; | |
| nglobs[i].name = g; | |
| if (_CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_CONSTANT_INT || | |
| _CFFI_GETOP(nglobs[i].type_op) == _CFFI_OP_ENUM) { | |
| PyObject *o = PyTuple_GET_ITEM(globals, i * 2 + 1); | |
| nglobs[i].address = &_cdl_realize_global_int; | |
| #if PY_MAJOR_VERSION < 3 | |
| if (PyInt_Check(o)) { | |
| nintconsts[i].neg = PyInt_AS_LONG(o) <= 0; | |
| nintconsts[i].value = (long long)PyInt_AS_LONG(o); | |
| } | |
| else | |
| #endif | |
| { | |
| nintconsts[i].neg = PyObject_RichCompareBool(o, Py_False, | |
| Py_LE); | |
| nintconsts[i].value = PyLong_AsUnsignedLongLongMask(o); | |
| if (PyErr_Occurred()) | |
| goto error; | |
| } | |
| } | |
| } | |
| ffi->types_builder.ctx.globals = nglobs; | |
| ffi->types_builder.ctx.num_globals = n; | |
| building = NULL; | |
| } | |
| if (struct_unions != NULL) { | |
| /* unpack a tuple of struct/unions, each described as a sub-tuple; | |
| the item 0 of each sub-tuple describes the struct/union, and | |
| the items 1..N-1 describe the fields, if any */ | |
| struct _cffi_struct_union_s *nstructs; | |
| struct _cffi_field_s *nfields; | |
| Py_ssize_t i, n = PyTuple_GET_SIZE(struct_unions); | |
| Py_ssize_t nf = 0; /* total number of fields */ | |
| for (i = 0; i < n; i++) { | |
| nf += PyTuple_GET_SIZE(PyTuple_GET_ITEM(struct_unions, i)) - 1; | |
| } | |
| i = (n * sizeof(struct _cffi_struct_union_s) + | |
| nf * sizeof(struct _cffi_field_s)); | |
| building = PyMem_Malloc(i); | |
| if (building == NULL) | |
| goto error; | |
| memset(building, 0, i); | |
| nstructs = (struct _cffi_struct_union_s *)building; | |
| nfields = (struct _cffi_field_s *)(nstructs + n); | |
| nf = 0; | |
| for (i = 0; i < n; i++) { | |
| /* 'desc' is the tuple of strings (desc_struct, desc_field_1, ..) */ | |
| PyObject *desc = PyTuple_GET_ITEM(struct_unions, i); | |
| Py_ssize_t j, nf1 = PyTuple_GET_SIZE(desc) - 1; | |
| char *s = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, 0)); | |
| /* 's' is the first string, describing the struct/union */ | |
| nstructs[i].type_index = cdl_4bytes(s); s += 4; | |
| nstructs[i].flags = cdl_4bytes(s); s += 4; | |
| nstructs[i].name = s; | |
| if (nstructs[i].flags & (_CFFI_F_OPAQUE | _CFFI_F_EXTERNAL)) { | |
| nstructs[i].size = (size_t)-1; | |
| nstructs[i].alignment = -1; | |
| nstructs[i].first_field_index = -1; | |
| nstructs[i].num_fields = 0; | |
| assert(nf1 == 0); | |
| } | |
| else { | |
| nstructs[i].size = (size_t)-2; | |
| nstructs[i].alignment = -2; | |
| nstructs[i].first_field_index = nf; | |
| nstructs[i].num_fields = nf1; | |
| } | |
| for (j = 0; j < nf1; j++) { | |
| char *f = PyBytes_AS_STRING(PyTuple_GET_ITEM(desc, j + 1)); | |
| /* 'f' is one of the other strings beyond the first one, | |
| describing one field each */ | |
| nfields[nf].field_type_op = cdl_opcode(f); f += 4; | |
| nfields[nf].field_offset = (size_t)-1; | |
| if (_CFFI_GETOP(nfields[nf].field_type_op) != _CFFI_OP_NOOP) { | |
| nfields[nf].field_size = cdl_4bytes(f); f += 4; | |
| } | |
| else { | |
| nfields[nf].field_size = (size_t)-1; | |
| } | |
| nfields[nf].name = f; | |
| nf++; | |
| } | |
| } | |
| ffi->types_builder.ctx.struct_unions = nstructs; | |
| ffi->types_builder.ctx.fields = nfields; | |
| ffi->types_builder.ctx.num_struct_unions = n; | |
| building = NULL; | |
| } | |
| if (enums != NULL) { | |
| /* unpack a tuple of strings, each of which describes one enum_s | |
| entry */ | |
| struct _cffi_enum_s *nenums; | |
| Py_ssize_t i, n = PyTuple_GET_SIZE(enums); | |
| i = n * sizeof(struct _cffi_enum_s); | |
| building = PyMem_Malloc(i); | |
| if (building == NULL) | |
| goto error; | |
| memset(building, 0, i); | |
| nenums = (struct _cffi_enum_s *)building; | |
| for (i = 0; i < n; i++) { | |
| char *e = PyBytes_AS_STRING(PyTuple_GET_ITEM(enums, i)); | |
| /* 'e' is a string describing the enum */ | |
| nenums[i].type_index = cdl_4bytes(e); e += 4; | |
| nenums[i].type_prim = cdl_4bytes(e); e += 4; | |
| nenums[i].name = e; e += strlen(e) + 1; | |
| nenums[i].enumerators = e; | |
| } | |
| ffi->types_builder.ctx.enums = nenums; | |
| ffi->types_builder.ctx.num_enums = n; | |
| building = NULL; | |
| } | |
| if (typenames != NULL) { | |
| /* unpack a tuple of strings, each of which describes one typename_s | |
| entry */ | |
| struct _cffi_typename_s *ntypenames; | |
| Py_ssize_t i, n = PyTuple_GET_SIZE(typenames); | |
| i = n * sizeof(struct _cffi_typename_s); | |
| building = PyMem_Malloc(i); | |
| if (building == NULL) | |
| goto error; | |
| memset(building, 0, i); | |
| ntypenames = (struct _cffi_typename_s *)building; | |
| for (i = 0; i < n; i++) { | |
| char *t = PyBytes_AS_STRING(PyTuple_GET_ITEM(typenames, i)); | |
| /* 't' is a string describing the typename */ | |
| ntypenames[i].type_index = cdl_4bytes(t); t += 4; | |
| ntypenames[i].name = t; | |
| } | |
| ffi->types_builder.ctx.typenames = ntypenames; | |
| ffi->types_builder.ctx.num_typenames = n; | |
| building = NULL; | |
| } | |
| if (includes != NULL) { | |
| PyObject *included_libs; | |
| included_libs = PyTuple_New(PyTuple_GET_SIZE(includes)); | |
| if (included_libs == NULL) | |
| return -1; | |
| Py_INCREF(includes); | |
| ffi->types_builder.included_ffis = includes; | |
| ffi->types_builder.included_libs = included_libs; | |
| } | |
| /* Above, we took directly some "char *" strings out of the strings, | |
| typically from somewhere inside tuples. Keep them alive by | |
| incref'ing the whole input arguments. */ | |
| Py_INCREF(args); | |
| Py_XINCREF(kwds); | |
| ffi->types_builder._keepalive1 = args; | |
| ffi->types_builder._keepalive2 = kwds; | |
| return 0; | |
| error: | |
| if (building != NULL) | |
| PyMem_Free(building); | |
| if (!PyErr_Occurred()) | |
| PyErr_NoMemory(); | |
| return -1; | |
| } |