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.
1222 lines
43 KiB
1222 lines
43 KiB
|
|
/* An FFI object has methods like ffi.new(). It is also a container
|
|
for the type declarations (typedefs and structs) that you can use,
|
|
say in ffi.new().
|
|
|
|
CTypeDescrObjects are internally stored in the dict 'types_dict'.
|
|
The types_dict is lazily filled with CTypeDescrObjects made from
|
|
reading a _cffi_type_context_s structure.
|
|
|
|
In "modern" mode, the FFI instance is made by the C extension
|
|
module originally created by recompile(). The _cffi_type_context_s
|
|
structure comes from global data in the C extension module.
|
|
|
|
In "compatibility" mode, an FFI instance is created explicitly by
|
|
the user, and its _cffi_type_context_s is initially empty. You
|
|
need to call ffi.cdef() to add more information to it.
|
|
*/
|
|
|
|
#define FFI_COMPLEXITY_OUTPUT 1200 /* xxx should grow as needed */
|
|
|
|
#define FFIObject_Check(op) PyObject_TypeCheck(op, &FFI_Type)
|
|
#define LibObject_Check(ob) ((Py_TYPE(ob) == &Lib_Type))
|
|
|
|
struct FFIObject_s {
|
|
PyObject_HEAD
|
|
PyObject *gc_wrefs, *gc_wrefs_freelist;
|
|
PyObject *init_once_cache;
|
|
struct _cffi_parse_info_s info;
|
|
char ctx_is_static, ctx_is_nonempty;
|
|
builder_c_t types_builder;
|
|
};
|
|
|
|
static FFIObject *ffi_internal_new(PyTypeObject *ffitype,
|
|
const struct _cffi_type_context_s *static_ctx)
|
|
{
|
|
static _cffi_opcode_t internal_output[FFI_COMPLEXITY_OUTPUT];
|
|
|
|
FFIObject *ffi;
|
|
if (static_ctx != NULL) {
|
|
ffi = (FFIObject *)PyObject_GC_New(FFIObject, ffitype);
|
|
/* we don't call PyObject_GC_Track() here: from _cffi_init_module()
|
|
it is not needed, because in this case the ffi object is immortal */
|
|
}
|
|
else {
|
|
ffi = (FFIObject *)ffitype->tp_alloc(ffitype, 0);
|
|
}
|
|
if (ffi == NULL)
|
|
return NULL;
|
|
|
|
if (init_builder_c(&ffi->types_builder, static_ctx) < 0) {
|
|
Py_DECREF(ffi);
|
|
return NULL;
|
|
}
|
|
ffi->gc_wrefs = NULL;
|
|
ffi->gc_wrefs_freelist = NULL;
|
|
ffi->init_once_cache = NULL;
|
|
ffi->info.ctx = &ffi->types_builder.ctx;
|
|
ffi->info.output = internal_output;
|
|
ffi->info.output_size = FFI_COMPLEXITY_OUTPUT;
|
|
ffi->ctx_is_static = (static_ctx != NULL);
|
|
ffi->ctx_is_nonempty = (static_ctx != NULL);
|
|
return ffi;
|
|
}
|
|
|
|
static void ffi_dealloc(FFIObject *ffi)
|
|
{
|
|
PyObject_GC_UnTrack(ffi);
|
|
Py_XDECREF(ffi->gc_wrefs);
|
|
Py_XDECREF(ffi->gc_wrefs_freelist);
|
|
Py_XDECREF(ffi->init_once_cache);
|
|
|
|
free_builder_c(&ffi->types_builder, ffi->ctx_is_static);
|
|
|
|
Py_TYPE(ffi)->tp_free((PyObject *)ffi);
|
|
}
|
|
|
|
static int ffi_traverse(FFIObject *ffi, visitproc visit, void *arg)
|
|
{
|
|
Py_VISIT(ffi->types_builder.types_dict);
|
|
Py_VISIT(ffi->types_builder.included_ffis);
|
|
Py_VISIT(ffi->types_builder.included_libs);
|
|
Py_VISIT(ffi->gc_wrefs);
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *ffiobj_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
/* user-facing initialization code, for explicit FFI() calls */
|
|
return (PyObject *)ffi_internal_new(type, NULL);
|
|
}
|
|
|
|
/* forward, declared in cdlopen.c because it's mostly useful for this case */
|
|
static int ffiobj_init(PyObject *self, PyObject *args, PyObject *kwds);
|
|
|
|
static PyObject *ffi_fetch_int_constant(FFIObject *ffi, const char *name,
|
|
int recursion)
|
|
{
|
|
int index;
|
|
|
|
index = search_in_globals(&ffi->types_builder.ctx, name, strlen(name));
|
|
if (index >= 0) {
|
|
const struct _cffi_global_s *g;
|
|
g = &ffi->types_builder.ctx.globals[index];
|
|
|
|
switch (_CFFI_GETOP(g->type_op)) {
|
|
case _CFFI_OP_CONSTANT_INT:
|
|
case _CFFI_OP_ENUM:
|
|
return realize_global_int(&ffi->types_builder, index);
|
|
|
|
default:
|
|
PyErr_Format(FFIError,
|
|
"function, global variable or non-integer constant "
|
|
"'%.200s' must be fetched from its original 'lib' "
|
|
"object", name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (ffi->types_builder.included_ffis != NULL) {
|
|
Py_ssize_t i;
|
|
PyObject *included_ffis = ffi->types_builder.included_ffis;
|
|
|
|
if (recursion > 100) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"recursion overflow in ffi.include() delegations");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) {
|
|
FFIObject *ffi1;
|
|
PyObject *x;
|
|
|
|
ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i);
|
|
x = ffi_fetch_int_constant(ffi1, name, recursion + 1);
|
|
if (x != NULL || PyErr_Occurred())
|
|
return x;
|
|
}
|
|
}
|
|
return NULL; /* no exception set, means "not found" */
|
|
}
|
|
|
|
#define ACCEPT_STRING 1
|
|
#define ACCEPT_CTYPE 2
|
|
#define ACCEPT_CDATA 4
|
|
#define ACCEPT_ALL (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA)
|
|
#define CONSIDER_FN_AS_FNPTR 8
|
|
|
|
static CTypeDescrObject *_ffi_bad_type(FFIObject *ffi, const char *input_text)
|
|
{
|
|
size_t length = strlen(input_text);
|
|
char *extra;
|
|
|
|
if (length > 500) {
|
|
extra = "";
|
|
}
|
|
else {
|
|
char *p;
|
|
size_t i, num_spaces = ffi->info.error_location;
|
|
extra = alloca(length + num_spaces + 4);
|
|
p = extra;
|
|
*p++ = '\n';
|
|
for (i = 0; i < length; i++) {
|
|
if (' ' <= input_text[i] && input_text[i] < 0x7f)
|
|
*p++ = input_text[i];
|
|
else if (input_text[i] == '\t' || input_text[i] == '\n')
|
|
*p++ = ' ';
|
|
else
|
|
*p++ = '?';
|
|
}
|
|
*p++ = '\n';
|
|
memset(p, ' ', num_spaces);
|
|
p += num_spaces;
|
|
*p++ = '^';
|
|
*p++ = 0;
|
|
}
|
|
PyErr_Format(FFIError, "%s%s", ffi->info.error_message, extra);
|
|
return NULL;
|
|
}
|
|
|
|
static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg,
|
|
int accept)
|
|
{
|
|
/* Returns the CTypeDescrObject from the user-supplied 'arg'.
|
|
Does not return a new reference!
|
|
*/
|
|
if ((accept & ACCEPT_STRING) && PyText_Check(arg)) {
|
|
PyObject *types_dict = ffi->types_builder.types_dict;
|
|
PyObject *x = PyDict_GetItem(types_dict, arg);
|
|
|
|
if (x == NULL) {
|
|
const char *input_text = PyText_AS_UTF8(arg);
|
|
int err, index = parse_c_type(&ffi->info, input_text);
|
|
if (index < 0)
|
|
return _ffi_bad_type(ffi, input_text);
|
|
|
|
x = realize_c_type_or_func(&ffi->types_builder,
|
|
ffi->info.output, index);
|
|
if (x == NULL)
|
|
return NULL;
|
|
|
|
/* Cache under the name given by 'arg', in addition to the
|
|
fact that the same ct is probably already cached under
|
|
its standardized name. In a few cases, it is not, e.g.
|
|
if it is a primitive; for the purpose of this function,
|
|
the important point is the following line, which makes
|
|
sure that in any case the next _ffi_type() with the same
|
|
'arg' will succeed early, in PyDict_GetItem() above.
|
|
*/
|
|
err = PyDict_SetItem(types_dict, arg, x);
|
|
Py_DECREF(x); /* we know it was written in types_dict (unless out
|
|
of mem), so there is at least that ref left */
|
|
if (err < 0)
|
|
return NULL;
|
|
}
|
|
|
|
if (CTypeDescr_Check(x))
|
|
return (CTypeDescrObject *)x;
|
|
else if (accept & CONSIDER_FN_AS_FNPTR)
|
|
return unwrap_fn_as_fnptr(x);
|
|
else
|
|
return unexpected_fn_type(x);
|
|
}
|
|
else if ((accept & ACCEPT_CTYPE) && CTypeDescr_Check(arg)) {
|
|
return (CTypeDescrObject *)arg;
|
|
}
|
|
else if ((accept & ACCEPT_CDATA) && CData_Check(arg)) {
|
|
return ((CDataObject *)arg)->c_type;
|
|
}
|
|
#if PY_MAJOR_VERSION < 3
|
|
else if (PyUnicode_Check(arg)) {
|
|
CTypeDescrObject *result;
|
|
arg = PyUnicode_AsASCIIString(arg);
|
|
if (arg == NULL)
|
|
return NULL;
|
|
result = _ffi_type(ffi, arg, accept);
|
|
Py_DECREF(arg);
|
|
return result;
|
|
}
|
|
#endif
|
|
else {
|
|
const char *m1 = (accept & ACCEPT_STRING) ? "string" : "";
|
|
const char *m2 = (accept & ACCEPT_CTYPE) ? "ctype object" : "";
|
|
const char *m3 = (accept & ACCEPT_CDATA) ? "cdata object" : "";
|
|
const char *s12 = (*m1 && (*m2 || *m3)) ? " or " : "";
|
|
const char *s23 = (*m2 && *m3) ? " or " : "";
|
|
PyErr_Format(PyExc_TypeError, "expected a %s%s%s%s%s, got '%.200s'",
|
|
m1, s12, m2, s23, m3,
|
|
Py_TYPE(arg)->tp_name);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_sizeof_doc,
|
|
"Return the size in bytes of the argument.\n"
|
|
"It can be a string naming a C type, or a 'cdata' instance.");
|
|
|
|
static PyObject *ffi_sizeof(FFIObject *self, PyObject *arg)
|
|
{
|
|
Py_ssize_t size;
|
|
|
|
if (CData_Check(arg)) {
|
|
size = direct_sizeof_cdata((CDataObject *)arg);
|
|
}
|
|
else {
|
|
CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
size = ct->ct_size;
|
|
if (size < 0) {
|
|
PyErr_Format(FFIError, "don't know the size of ctype '%s'",
|
|
ct->ct_name);
|
|
return NULL;
|
|
}
|
|
}
|
|
return PyInt_FromSsize_t(size);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_alignof_doc,
|
|
"Return the natural alignment size in bytes of the argument.\n"
|
|
"It can be a string naming a C type, or a 'cdata' instance.");
|
|
|
|
static PyObject *ffi_alignof(FFIObject *self, PyObject *arg)
|
|
{
|
|
int align;
|
|
CTypeDescrObject *ct = _ffi_type(self, arg, ACCEPT_ALL);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
align = get_alignment(ct);
|
|
if (align < 0)
|
|
return NULL;
|
|
return PyInt_FromLong(align);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_typeof_doc,
|
|
"Parse the C type given as a string and return the\n"
|
|
"corresponding <ctype> object.\n"
|
|
"It can also be used on 'cdata' instance to get its C type.");
|
|
|
|
static PyObject *_cpyextfunc_type_index(PyObject *x); /* forward */
|
|
|
|
static PyObject *ffi_typeof(FFIObject *self, PyObject *arg)
|
|
{
|
|
PyObject *x = (PyObject *)_ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CDATA);
|
|
if (x != NULL) {
|
|
Py_INCREF(x);
|
|
}
|
|
else {
|
|
x = _cpyextfunc_type_index(arg);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_new_doc,
|
|
"Allocate an instance according to the specified C type and return a\n"
|
|
"pointer to it. The specified C type must be either a pointer or an\n"
|
|
"array: ``new('X *')`` allocates an X and returns a pointer to it,\n"
|
|
"whereas ``new('X[n]')`` allocates an array of n X'es and returns an\n"
|
|
"array referencing it (which works mostly like a pointer, like in C).\n"
|
|
"You can also use ``new('X[]', n)`` to allocate an array of a\n"
|
|
"non-constant length n.\n"
|
|
"\n"
|
|
"The memory is initialized following the rules of declaring a global\n"
|
|
"variable in C: by default it is zero-initialized, but an explicit\n"
|
|
"initializer can be given which can be used to fill all or part of the\n"
|
|
"memory.\n"
|
|
"\n"
|
|
"When the returned <cdata> object goes out of scope, the memory is\n"
|
|
"freed. In other words the returned <cdata> object has ownership of\n"
|
|
"the value of type 'cdecl' that it points to. This means that the raw\n"
|
|
"data can be used as long as this object is kept alive, but must not be\n"
|
|
"used for a longer time. Be careful about that when copying the\n"
|
|
"pointer to the memory somewhere else, e.g. into another structure.");
|
|
|
|
static PyObject *_ffi_new(FFIObject *self, PyObject *args, PyObject *kwds,
|
|
const cffi_allocator_t *allocator)
|
|
{
|
|
CTypeDescrObject *ct;
|
|
PyObject *arg, *init = Py_None;
|
|
static char *keywords[] = {"cdecl", "init", NULL};
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:new", keywords,
|
|
&arg, &init))
|
|
return NULL;
|
|
|
|
ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
return direct_newp(ct, init, allocator);
|
|
}
|
|
|
|
static PyObject *ffi_new(FFIObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
return _ffi_new(self, args, kwds, &default_allocator);
|
|
}
|
|
|
|
static PyObject *_ffi_new_with_allocator(PyObject *allocator, PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
cffi_allocator_t alloc1;
|
|
PyObject *my_alloc, *my_free;
|
|
my_alloc = PyTuple_GET_ITEM(allocator, 1);
|
|
my_free = PyTuple_GET_ITEM(allocator, 2);
|
|
alloc1.ca_alloc = (my_alloc == Py_None ? NULL : my_alloc);
|
|
alloc1.ca_free = (my_free == Py_None ? NULL : my_free);
|
|
alloc1.ca_dont_clear = (PyTuple_GET_ITEM(allocator, 3) == Py_False);
|
|
|
|
return _ffi_new((FFIObject *)PyTuple_GET_ITEM(allocator, 0),
|
|
args, kwds, &alloc1);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_new_allocator_doc,
|
|
"Return a new allocator, i.e. a function that behaves like ffi.new()\n"
|
|
"but uses the provided low-level 'alloc' and 'free' functions.\n"
|
|
"\n"
|
|
"'alloc' is called with the size as argument. If it returns NULL, a\n"
|
|
"MemoryError is raised. 'free' is called with the result of 'alloc'\n"
|
|
"as argument. Both can be either Python functions or directly C\n"
|
|
"functions. If 'free' is None, then no free function is called.\n"
|
|
"If both 'alloc' and 'free' are None, the default is used.\n"
|
|
"\n"
|
|
"If 'should_clear_after_alloc' is set to False, then the memory\n"
|
|
"returned by 'alloc' is assumed to be already cleared (or you are\n"
|
|
"fine with garbage); otherwise CFFI will clear it.");
|
|
|
|
static PyObject *ffi_new_allocator(FFIObject *self, PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
PyObject *allocator, *result;
|
|
PyObject *my_alloc = Py_None, *my_free = Py_None;
|
|
int should_clear_after_alloc = 1;
|
|
static char *keywords[] = {"alloc", "free", "should_clear_after_alloc",
|
|
NULL};
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:new_allocator", keywords,
|
|
&my_alloc, &my_free,
|
|
&should_clear_after_alloc))
|
|
return NULL;
|
|
|
|
if (my_alloc == Py_None && my_free != Py_None) {
|
|
PyErr_SetString(PyExc_TypeError, "cannot pass 'free' without 'alloc'");
|
|
return NULL;
|
|
}
|
|
|
|
allocator = PyTuple_Pack(4,
|
|
(PyObject *)self,
|
|
my_alloc,
|
|
my_free,
|
|
PyBool_FromLong(should_clear_after_alloc));
|
|
if (allocator == NULL)
|
|
return NULL;
|
|
|
|
{
|
|
static PyMethodDef md = {"allocator",
|
|
(PyCFunction)_ffi_new_with_allocator,
|
|
METH_VARARGS | METH_KEYWORDS};
|
|
result = PyCFunction_New(&md, allocator);
|
|
}
|
|
Py_DECREF(allocator);
|
|
return result;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_cast_doc,
|
|
"Similar to a C cast: returns an instance of the named C\n"
|
|
"type initialized with the given 'source'. The source is\n"
|
|
"casted between integers or pointers of any type.");
|
|
|
|
static PyObject *ffi_cast(FFIObject *self, PyObject *args)
|
|
{
|
|
CTypeDescrObject *ct;
|
|
PyObject *ob, *arg;
|
|
if (!PyArg_ParseTuple(args, "OO:cast", &arg, &ob))
|
|
return NULL;
|
|
|
|
ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
return do_cast(ct, ob);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_string_doc,
|
|
"Return a Python string (or unicode string) from the 'cdata'. If\n"
|
|
"'cdata' is a pointer or array of characters or bytes, returns the\n"
|
|
"null-terminated string. The returned string extends until the first\n"
|
|
"null character, or at most 'maxlen' characters. If 'cdata' is an\n"
|
|
"array then 'maxlen' defaults to its length.\n"
|
|
"\n"
|
|
"If 'cdata' is a pointer or array of wchar_t, returns a unicode string\n"
|
|
"following the same rules.\n"
|
|
"\n"
|
|
"If 'cdata' is a single character or byte or a wchar_t, returns it as a\n"
|
|
"string or unicode string.\n"
|
|
"\n"
|
|
"If 'cdata' is an enum, returns the value of the enumerator as a\n"
|
|
"string, or 'NUMBER' if the value is out of range.");
|
|
|
|
#define ffi_string b_string /* ffi_string() => b_string()
|
|
from _cffi_backend.c */
|
|
|
|
PyDoc_STRVAR(ffi_unpack_doc,
|
|
"Unpack an array of C data of the given length,\n"
|
|
"returning a Python string/unicode/list.\n"
|
|
"\n"
|
|
"If 'cdata' is a pointer to 'char', returns a byte string.\n"
|
|
"It does not stop at the first null. This is equivalent to:\n"
|
|
"ffi.buffer(cdata, length)[:]\n"
|
|
"\n"
|
|
"If 'cdata' is a pointer to 'wchar_t', returns a unicode string.\n"
|
|
"'length' is measured in wchar_t's; it is not the size in bytes.\n"
|
|
"\n"
|
|
"If 'cdata' is a pointer to anything else, returns a list of\n"
|
|
"'length' items. This is a faster equivalent to:\n"
|
|
"[cdata[i] for i in range(length)]");
|
|
|
|
#define ffi_unpack b_unpack /* ffi_unpack() => b_unpack()
|
|
from _cffi_backend.c */
|
|
|
|
|
|
PyDoc_STRVAR(ffi_offsetof_doc,
|
|
"Return the offset of the named field inside the given structure or\n"
|
|
"array, which must be given as a C type name. You can give several\n"
|
|
"field names in case of nested structures. You can also give numeric\n"
|
|
"values which correspond to array items, in case of an array type.");
|
|
|
|
static PyObject *ffi_offsetof(FFIObject *self, PyObject *args)
|
|
{
|
|
PyObject *arg;
|
|
CTypeDescrObject *ct;
|
|
Py_ssize_t i, offset;
|
|
|
|
if (PyTuple_Size(args) < 2) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"offsetof() expects at least 2 arguments");
|
|
return NULL;
|
|
}
|
|
|
|
arg = PyTuple_GET_ITEM(args, 0);
|
|
ct = _ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CTYPE);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
offset = 0;
|
|
for (i = 1; i < PyTuple_GET_SIZE(args); i++) {
|
|
Py_ssize_t ofs1;
|
|
ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i), i > 1, &ofs1);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
offset += ofs1;
|
|
}
|
|
return PyInt_FromSsize_t(offset);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_addressof_doc,
|
|
"Limited equivalent to the '&' operator in C:\n"
|
|
"\n"
|
|
"1. ffi.addressof(<cdata 'struct-or-union'>) returns a cdata that is a\n"
|
|
"pointer to this struct or union.\n"
|
|
"\n"
|
|
"2. ffi.addressof(<cdata>, field-or-index...) returns the address of a\n"
|
|
"field or array item inside the given structure or array, recursively\n"
|
|
"in case of nested structures or arrays.\n"
|
|
"\n"
|
|
"3. ffi.addressof(<library>, \"name\") returns the address of the named\n"
|
|
"function or global variable.");
|
|
|
|
static PyObject *address_of_global_var(PyObject *args); /* forward */
|
|
|
|
static PyObject *ffi_addressof(FFIObject *self, PyObject *args)
|
|
{
|
|
PyObject *arg, *z, *result;
|
|
CTypeDescrObject *ct;
|
|
Py_ssize_t i, offset = 0;
|
|
int accepted_flags;
|
|
|
|
if (PyTuple_Size(args) < 1) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"addressof() expects at least 1 argument");
|
|
return NULL;
|
|
}
|
|
|
|
arg = PyTuple_GET_ITEM(args, 0);
|
|
if (LibObject_Check(arg)) {
|
|
/* case 3 in the docstring */
|
|
return address_of_global_var(args);
|
|
}
|
|
|
|
ct = _ffi_type(self, arg, ACCEPT_CDATA);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
if (PyTuple_GET_SIZE(args) == 1) {
|
|
/* case 1 in the docstring */
|
|
accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY;
|
|
if ((ct->ct_flags & accepted_flags) == 0) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"expected a cdata struct/union/array object");
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
/* case 2 in the docstring */
|
|
accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER;
|
|
if ((ct->ct_flags & accepted_flags) == 0) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"expected a cdata struct/union/array/pointer object");
|
|
return NULL;
|
|
}
|
|
for (i = 1; i < PyTuple_GET_SIZE(args); i++) {
|
|
Py_ssize_t ofs1;
|
|
ct = direct_typeoffsetof(ct, PyTuple_GET_ITEM(args, i),
|
|
i > 1, &ofs1);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
offset += ofs1;
|
|
}
|
|
}
|
|
|
|
z = new_pointer_type(ct);
|
|
if (z == NULL)
|
|
return NULL;
|
|
|
|
result = new_simple_cdata(((CDataObject *)arg)->c_data + offset,
|
|
(CTypeDescrObject *)z);
|
|
Py_DECREF(z);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *_combine_type_name_l(CTypeDescrObject *ct,
|
|
size_t extra_text_len)
|
|
{
|
|
size_t base_name_len;
|
|
PyObject *result;
|
|
char *p;
|
|
|
|
base_name_len = strlen(ct->ct_name);
|
|
result = PyBytes_FromStringAndSize(NULL, base_name_len + extra_text_len);
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
p = PyBytes_AS_STRING(result);
|
|
memcpy(p, ct->ct_name, ct->ct_name_position);
|
|
p += ct->ct_name_position;
|
|
p += extra_text_len;
|
|
memcpy(p, ct->ct_name + ct->ct_name_position,
|
|
base_name_len - ct->ct_name_position);
|
|
return result;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_getctype_doc,
|
|
"Return a string giving the C type 'cdecl', which may be itself a\n"
|
|
"string or a <ctype> object. If 'replace_with' is given, it gives\n"
|
|
"extra text to append (or insert for more complicated C types), like a\n"
|
|
"variable name, or '*' to get actually the C type 'pointer-to-cdecl'.");
|
|
|
|
static PyObject *ffi_getctype(FFIObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
PyObject *c_decl, *res;
|
|
char *p, *replace_with = "";
|
|
int add_paren, add_space;
|
|
CTypeDescrObject *ct;
|
|
size_t replace_with_len;
|
|
static char *keywords[] = {"cdecl", "replace_with", NULL};
|
|
#if PY_MAJOR_VERSION >= 3
|
|
PyObject *u;
|
|
#endif
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s:getctype", keywords,
|
|
&c_decl, &replace_with))
|
|
return NULL;
|
|
|
|
ct = _ffi_type(self, c_decl, ACCEPT_STRING|ACCEPT_CTYPE);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
|
|
while (replace_with[0] != 0 && isspace(replace_with[0]))
|
|
replace_with++;
|
|
replace_with_len = strlen(replace_with);
|
|
while (replace_with_len > 0 && isspace(replace_with[replace_with_len - 1]))
|
|
replace_with_len--;
|
|
|
|
add_paren = (replace_with[0] == '*' &&
|
|
((ct->ct_flags & CT_ARRAY) != 0));
|
|
add_space = (!add_paren && replace_with_len > 0 &&
|
|
replace_with[0] != '[' && replace_with[0] != '(');
|
|
|
|
res = _combine_type_name_l(ct, replace_with_len + add_space + 2*add_paren);
|
|
if (res == NULL)
|
|
return NULL;
|
|
|
|
p = PyBytes_AS_STRING(res) + ct->ct_name_position;
|
|
if (add_paren)
|
|
*p++ = '(';
|
|
if (add_space)
|
|
*p++ = ' ';
|
|
memcpy(p, replace_with, replace_with_len);
|
|
if (add_paren)
|
|
p[replace_with_len] = ')';
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
/* bytes -> unicode string */
|
|
u = PyUnicode_DecodeLatin1(PyBytes_AS_STRING(res),
|
|
PyBytes_GET_SIZE(res),
|
|
NULL);
|
|
Py_DECREF(res);
|
|
res = u;
|
|
#endif
|
|
|
|
return res;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_new_handle_doc,
|
|
"Return a non-NULL cdata of type 'void *' that contains an opaque\n"
|
|
"reference to the argument, which can be any Python object. To cast it\n"
|
|
"back to the original object, use from_handle(). You must keep alive\n"
|
|
"the cdata object returned by new_handle()!");
|
|
|
|
static PyObject *ffi_new_handle(FFIObject *self, PyObject *arg)
|
|
{
|
|
/* g_ct_voidp is equal to <ctype 'void *'> */
|
|
return newp_handle(g_ct_voidp, arg);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_from_handle_doc,
|
|
"Cast a 'void *' back to a Python object. Must be used *only* on the\n"
|
|
"pointers returned by new_handle(), and *only* as long as the exact\n"
|
|
"cdata object returned by new_handle() is still alive (somewhere else\n"
|
|
"in the program). Failure to follow these rules will crash.");
|
|
|
|
#define ffi_from_handle b_from_handle /* ffi_from_handle => b_from_handle
|
|
from _cffi_backend.c */
|
|
|
|
PyDoc_STRVAR(ffi_from_buffer_doc,
|
|
"Return a <cdata 'char[]'> that points to the data of the given Python\n"
|
|
"object, which must support the buffer interface. Note that this is\n"
|
|
"not meant to be used on the built-in types str or unicode\n"
|
|
"(you can build 'char[]' arrays explicitly) but only on objects\n"
|
|
"containing large quantities of raw data in some other format, like\n"
|
|
"'array.array' or numpy arrays.");
|
|
|
|
static PyObject *ffi_from_buffer(FFIObject *self, PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
PyObject *cdecl1, *python_buf = NULL;
|
|
CTypeDescrObject *ct;
|
|
int require_writable = 0;
|
|
static char *keywords[] = {"cdecl", "python_buffer",
|
|
"require_writable", NULL};
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oi:from_buffer", keywords,
|
|
&cdecl1, &python_buf, &require_writable))
|
|
return NULL;
|
|
|
|
if (python_buf == NULL) {
|
|
python_buf = cdecl1;
|
|
ct = g_ct_chararray;
|
|
}
|
|
else {
|
|
ct = _ffi_type(self, cdecl1, ACCEPT_STRING|ACCEPT_CTYPE);
|
|
if (ct == NULL)
|
|
return NULL;
|
|
}
|
|
return direct_from_buffer(ct, python_buf, require_writable);
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_gc_doc,
|
|
"Return a new cdata object that points to the same data.\n"
|
|
"Later, when this new cdata object is garbage-collected,\n"
|
|
"'destructor(old_cdata_object)' will be called.\n"
|
|
"\n"
|
|
"The optional 'size' gives an estimate of the size, used to\n"
|
|
"trigger the garbage collection more eagerly. So far only used\n"
|
|
"on PyPy. It tells the GC that the returned object keeps alive\n"
|
|
"roughly 'size' bytes of external memory.");
|
|
|
|
#define ffi_gc b_gcp /* ffi_gc() => b_gcp()
|
|
from _cffi_backend.c */
|
|
|
|
PyDoc_STRVAR(ffi_def_extern_doc,
|
|
"A decorator. Attaches the decorated Python function to the C code\n"
|
|
"generated for the 'extern \"Python\"' function of the same name.\n"
|
|
"Calling the C function will then invoke the Python function.\n"
|
|
"\n"
|
|
"Optional arguments: 'name' is the name of the C function, if\n"
|
|
"different from the Python function; and 'error' and 'onerror'\n"
|
|
"handle what occurs if the Python function raises an exception\n"
|
|
"(see the docs for details).");
|
|
|
|
/* forward; see call_python.c */
|
|
static PyObject *_ffi_def_extern_decorator(PyObject *, PyObject *);
|
|
|
|
static PyObject *ffi_def_extern(FFIObject *self, PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
static PyMethodDef md = {"def_extern_decorator",
|
|
(PyCFunction)_ffi_def_extern_decorator, METH_O};
|
|
PyObject *name = Py_None, *error = Py_None;
|
|
PyObject *res, *onerror = Py_None;
|
|
static char *keywords[] = {"name", "error", "onerror", NULL};
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", keywords,
|
|
&name, &error, &onerror))
|
|
return NULL;
|
|
|
|
args = Py_BuildValue("(OOOO)", (PyObject *)self, name, error, onerror);
|
|
if (args == NULL)
|
|
return NULL;
|
|
|
|
res = PyCFunction_New(&md, args);
|
|
Py_DECREF(args);
|
|
return res;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_callback_doc,
|
|
"Return a callback object or a decorator making such a callback object.\n"
|
|
"'cdecl' must name a C function pointer type. The callback invokes the\n"
|
|
"specified 'python_callable' (which may be provided either directly or\n"
|
|
"via a decorator). Important: the callback object must be manually\n"
|
|
"kept alive for as long as the callback may be invoked from the C code.");
|
|
|
|
static PyObject *_ffi_callback_decorator(PyObject *outer_args, PyObject *fn)
|
|
{
|
|
PyObject *res, *old;
|
|
|
|
old = PyTuple_GET_ITEM(outer_args, 1);
|
|
PyTuple_SET_ITEM(outer_args, 1, fn);
|
|
res = b_callback(NULL, outer_args);
|
|
PyTuple_SET_ITEM(outer_args, 1, old);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *ffi_callback(FFIObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
PyObject *c_decl, *python_callable = Py_None, *error = Py_None;
|
|
PyObject *res, *onerror = Py_None;
|
|
static char *keywords[] = {"cdecl", "python_callable", "error",
|
|
"onerror", NULL};
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", keywords,
|
|
&c_decl, &python_callable, &error,
|
|
&onerror))
|
|
return NULL;
|
|
|
|
c_decl = (PyObject *)_ffi_type(self, c_decl, ACCEPT_STRING | ACCEPT_CTYPE |
|
|
CONSIDER_FN_AS_FNPTR);
|
|
if (c_decl == NULL)
|
|
return NULL;
|
|
|
|
args = Py_BuildValue("(OOOO)", c_decl, python_callable, error, onerror);
|
|
if (args == NULL)
|
|
return NULL;
|
|
|
|
if (python_callable != Py_None) {
|
|
res = b_callback(NULL, args);
|
|
}
|
|
else {
|
|
static PyMethodDef md = {"callback_decorator",
|
|
(PyCFunction)_ffi_callback_decorator, METH_O};
|
|
res = PyCFunction_New(&md, args);
|
|
}
|
|
Py_DECREF(args);
|
|
return res;
|
|
}
|
|
|
|
#ifdef MS_WIN32
|
|
PyDoc_STRVAR(ffi_getwinerror_doc,
|
|
"Return either the GetLastError() or the error number given by the\n"
|
|
"optional 'code' argument, as a tuple '(code, message)'.");
|
|
|
|
#define ffi_getwinerror b_getwinerror /* ffi_getwinerror() => b_getwinerror()
|
|
from misc_win32.h */
|
|
#endif
|
|
|
|
PyDoc_STRVAR(ffi_errno_doc, "the value of 'errno' from/to the C calls");
|
|
|
|
static PyObject *ffi_get_errno(PyObject *self, void *closure)
|
|
{
|
|
/* xxx maybe think about how to make the saved errno local
|
|
to an ffi instance */
|
|
return b_get_errno(NULL, NULL);
|
|
}
|
|
|
|
static int ffi_set_errno(PyObject *self, PyObject *newval, void *closure)
|
|
{
|
|
PyObject *x = b_set_errno(NULL, newval);
|
|
if (x == NULL)
|
|
return -1;
|
|
Py_DECREF(x);
|
|
return 0;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_dlopen_doc,
|
|
"Load and return a dynamic library identified by 'name'. The standard\n"
|
|
"C library can be loaded by passing None.\n"
|
|
"\n"
|
|
"Note that functions and types declared with 'ffi.cdef()' are not\n"
|
|
"linked to a particular library, just like C headers. In the library\n"
|
|
"we only look for the actual (untyped) symbols at the time of their\n"
|
|
"first access.");
|
|
|
|
PyDoc_STRVAR(ffi_dlclose_doc,
|
|
"Close a library obtained with ffi.dlopen(). After this call, access to\n"
|
|
"functions or variables from the library will fail (possibly with a\n"
|
|
"segmentation fault).");
|
|
|
|
static PyObject *ffi_dlopen(PyObject *self, PyObject *args); /* forward */
|
|
static PyObject *ffi_dlclose(PyObject *self, PyObject *args); /* forward */
|
|
|
|
PyDoc_STRVAR(ffi_int_const_doc,
|
|
"Get the value of an integer constant.\n"
|
|
"\n"
|
|
"'ffi.integer_const(\"xxx\")' is equivalent to 'lib.xxx' if xxx names an\n"
|
|
"integer constant. The point of this function is limited to use cases\n"
|
|
"where you have an 'ffi' object but not any associated 'lib' object.");
|
|
|
|
static PyObject *ffi_int_const(FFIObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
char *name;
|
|
PyObject *x;
|
|
static char *keywords[] = {"name", NULL};
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", keywords, &name))
|
|
return NULL;
|
|
|
|
x = ffi_fetch_int_constant(self, name, 0);
|
|
|
|
if (x == NULL && !PyErr_Occurred()) {
|
|
PyErr_Format(PyExc_AttributeError,
|
|
"integer constant '%.200s' not found", name);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_list_types_doc,
|
|
"Returns the user type names known to this FFI instance.\n"
|
|
"This returns a tuple containing three lists of names:\n"
|
|
"(typedef_names, names_of_structs, names_of_unions)");
|
|
|
|
static PyObject *ffi_list_types(FFIObject *self, PyObject *noargs)
|
|
{
|
|
Py_ssize_t i, n1 = self->types_builder.ctx.num_typenames;
|
|
Py_ssize_t n23 = self->types_builder.ctx.num_struct_unions;
|
|
PyObject *o, *lst[3] = {NULL, NULL, NULL}, *result = NULL;
|
|
|
|
lst[0] = PyList_New(n1);
|
|
if (lst[0] == NULL)
|
|
goto error;
|
|
lst[1] = PyList_New(0);
|
|
if (lst[1] == NULL)
|
|
goto error;
|
|
lst[2] = PyList_New(0);
|
|
if (lst[2] == NULL)
|
|
goto error;
|
|
|
|
for (i = 0; i < n1; i++) {
|
|
o = PyText_FromString(self->types_builder.ctx.typenames[i].name);
|
|
if (o == NULL)
|
|
goto error;
|
|
PyList_SET_ITEM(lst[0], i, o);
|
|
}
|
|
|
|
for (i = 0; i < n23; i++) {
|
|
const struct _cffi_struct_union_s *s;
|
|
int err, index;
|
|
|
|
s = &self->types_builder.ctx.struct_unions[i];
|
|
if (s->name[0] == '$')
|
|
continue;
|
|
|
|
o = PyText_FromString(s->name);
|
|
if (o == NULL)
|
|
goto error;
|
|
index = (s->flags & _CFFI_F_UNION) ? 2 : 1;
|
|
err = PyList_Append(lst[index], o);
|
|
Py_DECREF(o);
|
|
if (err < 0)
|
|
goto error;
|
|
}
|
|
result = PyTuple_Pack(3, lst[0], lst[1], lst[2]);
|
|
/* fall-through */
|
|
error:
|
|
Py_XDECREF(lst[2]);
|
|
Py_XDECREF(lst[1]);
|
|
Py_XDECREF(lst[0]);
|
|
return result;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_memmove_doc,
|
|
"ffi.memmove(dest, src, n) copies n bytes of memory from src to dest.\n"
|
|
"\n"
|
|
"Like the C function memmove(), the memory areas may overlap;\n"
|
|
"apart from that it behaves like the C function memcpy().\n"
|
|
"\n"
|
|
"'src' can be any cdata ptr or array, or any Python buffer object.\n"
|
|
"'dest' can be any cdata ptr or array, or a writable Python buffer\n"
|
|
"object. The size to copy, 'n', is always measured in bytes.\n"
|
|
"\n"
|
|
"Unlike other methods, this one supports all Python buffer including\n"
|
|
"byte strings and bytearrays---but it still does not support\n"
|
|
"non-contiguous buffers.");
|
|
|
|
#define ffi_memmove b_memmove /* ffi_memmove() => b_memmove()
|
|
from _cffi_backend.c */
|
|
|
|
PyDoc_STRVAR(ffi_init_once_doc,
|
|
"init_once(function, tag): run function() once. More precisely,\n"
|
|
"'function()' is called the first time we see a given 'tag'.\n"
|
|
"\n"
|
|
"The return value of function() is remembered and returned by the current\n"
|
|
"and all future init_once() with the same tag. If init_once() is called\n"
|
|
"from multiple threads in parallel, all calls block until the execution\n"
|
|
"of function() is done. If function() raises an exception, it is\n"
|
|
"propagated and nothing is cached.");
|
|
|
|
#if PY_MAJOR_VERSION < 3
|
|
/* PyCapsule_New is redefined to be PyCObject_FromVoidPtr in _cffi_backend,
|
|
which gives 2.6 compatibility; but the destructor signature is different */
|
|
static void _free_init_once_lock(void *lock)
|
|
{
|
|
PyThread_free_lock((PyThread_type_lock)lock);
|
|
}
|
|
#else
|
|
static void _free_init_once_lock(PyObject *capsule)
|
|
{
|
|
PyThread_type_lock lock;
|
|
lock = PyCapsule_GetPointer(capsule, "cffi_init_once_lock");
|
|
if (lock != NULL)
|
|
PyThread_free_lock(lock);
|
|
}
|
|
#endif
|
|
|
|
static PyObject *ffi_init_once(FFIObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
static char *keywords[] = {"func", "tag", NULL};
|
|
PyObject *cache, *func, *tag, *tup, *res, *x, *lockobj;
|
|
PyThread_type_lock lock;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", keywords, &func, &tag))
|
|
return NULL;
|
|
|
|
/* a lot of fun with reference counting and error checking
|
|
in this function */
|
|
|
|
/* atomically get or create a new dict (no GIL release) */
|
|
cache = self->init_once_cache;
|
|
if (cache == NULL) {
|
|
cache = PyDict_New();
|
|
if (cache == NULL)
|
|
return NULL;
|
|
self->init_once_cache = cache;
|
|
}
|
|
|
|
/* get the tuple from cache[tag], or make a new one: (False, lock) */
|
|
tup = PyDict_GetItem(cache, tag);
|
|
if (tup == NULL) {
|
|
lock = PyThread_allocate_lock();
|
|
if (lock == NULL)
|
|
return NULL;
|
|
x = PyCapsule_New(lock, "cffi_init_once_lock", _free_init_once_lock);
|
|
if (x == NULL) {
|
|
PyThread_free_lock(lock);
|
|
return NULL;
|
|
}
|
|
tup = PyTuple_Pack(2, Py_False, x);
|
|
Py_DECREF(x);
|
|
if (tup == NULL)
|
|
return NULL;
|
|
x = tup;
|
|
|
|
/* Possible corner case if 'tag' is an object overriding __eq__
|
|
in pure Python: the GIL may be released when we are running it.
|
|
We really need to call dict.setdefault(). */
|
|
tup = PyObject_CallMethod(cache, "setdefault", "OO", tag, x);
|
|
Py_DECREF(x);
|
|
if (tup == NULL)
|
|
return NULL;
|
|
|
|
Py_DECREF(tup); /* there is still a ref inside the dict */
|
|
}
|
|
|
|
res = PyTuple_GET_ITEM(tup, 1);
|
|
Py_INCREF(res);
|
|
|
|
if (PyTuple_GET_ITEM(tup, 0) == Py_True) {
|
|
/* tup == (True, result): return the result. */
|
|
return res;
|
|
}
|
|
|
|
/* tup == (False, lock) */
|
|
lockobj = res;
|
|
lock = (PyThread_type_lock)PyCapsule_GetPointer(lockobj,
|
|
"cffi_init_once_lock");
|
|
if (lock == NULL) {
|
|
Py_DECREF(lockobj);
|
|
return NULL;
|
|
}
|
|
|
|
Py_BEGIN_ALLOW_THREADS
|
|
PyThread_acquire_lock(lock, WAIT_LOCK);
|
|
Py_END_ALLOW_THREADS
|
|
|
|
x = PyDict_GetItem(cache, tag);
|
|
if (x != NULL && PyTuple_GET_ITEM(x, 0) == Py_True) {
|
|
/* the real result was put in the dict while we were waiting
|
|
for PyThread_acquire_lock() above */
|
|
res = PyTuple_GET_ITEM(x, 1);
|
|
Py_INCREF(res);
|
|
}
|
|
else {
|
|
res = PyObject_CallFunction(func, "");
|
|
if (res != NULL) {
|
|
tup = PyTuple_Pack(2, Py_True, res);
|
|
if (tup == NULL || PyDict_SetItem(cache, tag, tup) < 0) {
|
|
Py_XDECREF(tup);
|
|
Py_DECREF(res);
|
|
res = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
PyThread_release_lock(lock);
|
|
Py_DECREF(lockobj);
|
|
return res;
|
|
}
|
|
|
|
PyDoc_STRVAR(ffi_release_doc,
|
|
"Release now the resources held by a 'cdata' object from ffi.new(),\n"
|
|
"ffi.gc() or ffi.from_buffer(). The cdata object must not be used\n"
|
|
"afterwards.\n"
|
|
"\n"
|
|
"'ffi.release(cdata)' is equivalent to 'cdata.__exit__()'.\n"
|
|
"\n"
|
|
"Note that on CPython this method has no effect (so far) on objects\n"
|
|
"returned by ffi.new(), because the memory is allocated inline with the\n"
|
|
"cdata object and cannot be freed independently. It might be fixed in\n"
|
|
"future releases of cffi.");
|
|
|
|
#define ffi_release b_release /* ffi_release() => b_release()
|
|
from _cffi_backend.c */
|
|
|
|
|
|
#define METH_VKW (METH_VARARGS | METH_KEYWORDS)
|
|
static PyMethodDef ffi_methods[] = {
|
|
{"addressof", (PyCFunction)ffi_addressof, METH_VARARGS, ffi_addressof_doc},
|
|
{"alignof", (PyCFunction)ffi_alignof, METH_O, ffi_alignof_doc},
|
|
{"def_extern", (PyCFunction)ffi_def_extern, METH_VKW, ffi_def_extern_doc},
|
|
{"callback", (PyCFunction)ffi_callback, METH_VKW, ffi_callback_doc},
|
|
{"cast", (PyCFunction)ffi_cast, METH_VARARGS, ffi_cast_doc},
|
|
{"dlclose", (PyCFunction)ffi_dlclose, METH_VARARGS, ffi_dlclose_doc},
|
|
{"dlopen", (PyCFunction)ffi_dlopen, METH_VARARGS, ffi_dlopen_doc},
|
|
{"from_buffer",(PyCFunction)ffi_from_buffer,METH_VKW, ffi_from_buffer_doc},
|
|
{"from_handle",(PyCFunction)ffi_from_handle,METH_O, ffi_from_handle_doc},
|
|
{"gc", (PyCFunction)ffi_gc, METH_VKW, ffi_gc_doc},
|
|
{"getctype", (PyCFunction)ffi_getctype, METH_VKW, ffi_getctype_doc},
|
|
#ifdef MS_WIN32
|
|
{"getwinerror",(PyCFunction)ffi_getwinerror,METH_VKW, ffi_getwinerror_doc},
|
|
#endif
|
|
{"init_once", (PyCFunction)ffi_init_once, METH_VKW, ffi_init_once_doc},
|
|
{"integer_const",(PyCFunction)ffi_int_const,METH_VKW, ffi_int_const_doc},
|
|
{"list_types", (PyCFunction)ffi_list_types, METH_NOARGS, ffi_list_types_doc},
|
|
{"memmove", (PyCFunction)ffi_memmove, METH_VKW, ffi_memmove_doc},
|
|
{"new", (PyCFunction)ffi_new, METH_VKW, ffi_new_doc},
|
|
{"new_allocator",(PyCFunction)ffi_new_allocator,METH_VKW,ffi_new_allocator_doc},
|
|
{"new_handle", (PyCFunction)ffi_new_handle, METH_O, ffi_new_handle_doc},
|
|
{"offsetof", (PyCFunction)ffi_offsetof, METH_VARARGS, ffi_offsetof_doc},
|
|
{"release", (PyCFunction)ffi_release, METH_O, ffi_release_doc},
|
|
{"sizeof", (PyCFunction)ffi_sizeof, METH_O, ffi_sizeof_doc},
|
|
{"string", (PyCFunction)ffi_string, METH_VKW, ffi_string_doc},
|
|
{"typeof", (PyCFunction)ffi_typeof, METH_O, ffi_typeof_doc},
|
|
{"unpack", (PyCFunction)ffi_unpack, METH_VKW, ffi_unpack_doc},
|
|
{NULL}
|
|
};
|
|
|
|
static PyGetSetDef ffi_getsets[] = {
|
|
{"errno", ffi_get_errno, ffi_set_errno, ffi_errno_doc},
|
|
{NULL}
|
|
};
|
|
|
|
static PyTypeObject FFI_Type = {
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"CompiledFFI",
|
|
sizeof(FFIObject),
|
|
0,
|
|
(destructor)ffi_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
0, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
0, /* tp_hash */
|
|
0, /* tp_call */
|
|
0, /* tp_str */
|
|
PyObject_GenericGetAttr, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
|
|
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
0, /* tp_doc */
|
|
(traverseproc)ffi_traverse, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
ffi_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
ffi_getsets, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
ffiobj_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
ffiobj_new, /* tp_new */
|
|
PyObject_GC_Del, /* tp_free */
|
|
};
|
|
|
|
|
|
static PyObject *
|
|
_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s,
|
|
PyObject *included_ffis, int recursion)
|
|
{
|
|
Py_ssize_t i;
|
|
|
|
if (included_ffis == NULL)
|
|
return NULL;
|
|
|
|
if (recursion > 100) {
|
|
PyErr_SetString(PyExc_RuntimeError,
|
|
"recursion overflow in ffi.include() delegations");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < PyTuple_GET_SIZE(included_ffis); i++) {
|
|
FFIObject *ffi1;
|
|
const struct _cffi_struct_union_s *s1;
|
|
int sindex;
|
|
PyObject *x;
|
|
|
|
ffi1 = (FFIObject *)PyTuple_GET_ITEM(included_ffis, i);
|
|
sindex = search_in_struct_unions(&ffi1->types_builder.ctx, s->name,
|
|
strlen(s->name));
|
|
if (sindex < 0) /* not found at all */
|
|
continue;
|
|
s1 = &ffi1->types_builder.ctx.struct_unions[sindex];
|
|
if ((s1->flags & (_CFFI_F_EXTERNAL | _CFFI_F_UNION))
|
|
== (s->flags & _CFFI_F_UNION)) {
|
|
/* s1 is not external, and the same kind (struct or union) as s */
|
|
return _realize_c_struct_or_union(&ffi1->types_builder, sindex);
|
|
}
|
|
/* not found, look more recursively */
|
|
x = _fetch_external_struct_or_union(
|
|
s, ffi1->types_builder.included_ffis, recursion + 1);
|
|
if (x != NULL || PyErr_Occurred())
|
|
return x; /* either found, or got an error */
|
|
}
|
|
return NULL; /* not found at all, leave without an error */
|
|
}
|