diff options
author | Kevin Cheng <kevcheng@google.com> | 2019-04-18 11:31:16 -0700 |
---|---|---|
committer | Kevin Cheng <kevcheng@google.com> | 2019-05-02 13:59:40 -0700 |
commit | 757c264bc10ebc71074ee3f5fb66d670667a09bc (patch) | |
tree | 26c7f7b74c752db99d9b0ac1f94fc592aca1e53a /c | |
parent | 99013222844839c42437f16eace25f4e6a8a8b20 (diff) | |
download | cffi-757c264bc10ebc71074ee3f5fb66d670667a09bc.tar.gz |
Add in cffi 1.12.2 (e0c7666)
Since this is a mercurial repo, d/led zip of src:
https://bitbucket.org/cffi/cffi/get/v1.12.2.zip
Also add in misc METADATA/NOTICE/Android.bp/etc files.
Bug: 122778810
Test: None
Change-Id: I36c58ed07a2cdd4d9d11831908175a5c988f33c1
Diffstat (limited to 'c')
37 files changed, 20702 insertions, 0 deletions
diff --git a/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 b/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 Binary files differnew file mode 100644 index 0000000..59e65c0 --- /dev/null +++ b/c/.libs_cffi_backend/libffi-45372312.so.6.0.4 diff --git a/c/Android.bp b/c/Android.bp new file mode 100644 index 0000000..baced02 --- /dev/null +++ b/c/Android.bp @@ -0,0 +1,53 @@ +python_library { + name: "py-cffi-backend", + host_supported: true, + srcs: [ + "_dummy_file_cffi_backend.py", + ], + version: { + py2: { + enabled: true, + }, + py3: { + enabled: true, + }, + }, + data: [ + ":py-cffi-backend-files" + ], +} + +filegroup { + name: "py-cffi-backend-files", + srcs: [ + "_cffi_backend.so", + ], +} + +python_library { + name: "py-cffi-backend-libffi", + host_supported: true, + srcs: [ + "_dummy_file_libffi.py", + ], + version: { + py2: { + enabled: true, + }, + py3: { + enabled: true, + }, + }, + data: [ + ":py-cffi-backend-libffi-files" + ], +} + +filegroup { + name: "py-cffi-backend-libffi-files", + srcs: [ + ".libs_cffi_backend/libffi-45372312.so.6.0.4", + ], +} + + diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c new file mode 100644 index 0000000..c39866a --- /dev/null +++ b/c/_cffi_backend.c @@ -0,0 +1,7686 @@ +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "structmember.h" + +#define CFFI_VERSION "1.12.2" + +#ifdef MS_WIN32 +#include <windows.h> +#include "misc_win32.h" +#else +#include <stddef.h> +#include <stdint.h> +#include <dlfcn.h> +#include <errno.h> +#include <ffi.h> +#include <sys/mman.h> +#endif + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py */ +#if defined(_MSC_VER) +# include <malloc.h> /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include <stdint.h> +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ + typedef unsigned char _Bool; +# endif +#else +# include <stdint.h> +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include <alloca.h> +# endif +#endif + + +/* Define the following macro ONLY if you trust libffi's version of + * ffi_closure_alloc() more than the code in malloc_closure.h. + * IMPORTANT: DO NOT ENABLE THIS ON LINUX, unless you understand exactly + * why I recommend against it and decide that you trust it more than my + * analysis below. + * + * There are two versions of this code: one inside libffi itself, and + * one inside malloc_closure.h here. Both should be fine as long as the + * Linux distribution does _not_ enable extra security features. If it + * does, then the code in malloc_closure.h will cleanly crash because + * there is no reasonable way to obtain a read-write-execute memory + * page. On the other hand, the code in libffi will appear to + * work---but will actually randomly crash after a fork() if the child + * does not immediately call exec(). This second crash is of the kind + * that can be turned into an attack vector by a motivated attacker. + * So, _enabling_ extra security features _opens_ an attack vector. + * That sounds like a horribly bad idea to me, and is the reason for why + * I prefer CFFI crashing cleanly. + * + * Currently, we use libffi's ffi_closure_alloc() only on NetBSD. It is + * known that on the NetBSD kernel, a different strategy is used which + * should not be open to the fork() bug. + */ +#ifdef __NetBSD__ +# define CFFI_TRUST_LIBFFI +#endif + +#ifndef CFFI_TRUST_LIBFFI +# include "malloc_closure.h" +#endif + + +#if PY_MAJOR_VERSION >= 3 +# define STR_OR_BYTES "bytes" +# define PyText_Type PyUnicode_Type +# define PyText_Check PyUnicode_Check +# define PyTextAny_Check PyUnicode_Check +# define PyText_FromFormat PyUnicode_FromFormat +# define PyText_AsUTF8 _PyUnicode_AsString /* PyUnicode_AsUTF8 in Py3.3 */ +# define PyText_AS_UTF8 _PyUnicode_AsString +# if PY_VERSION_HEX >= 0x03030000 +# define PyText_GetSize PyUnicode_GetLength +# else +# define PyText_GetSize PyUnicode_GetSize +# endif +# define PyText_FromString PyUnicode_FromString +# define PyText_FromStringAndSize PyUnicode_FromStringAndSize +# define PyText_InternInPlace PyUnicode_InternInPlace +# define PyText_InternFromString PyUnicode_InternFromString +# define PyIntOrLong_Check PyLong_Check +#else +# define STR_OR_BYTES "str" +# define PyText_Type PyString_Type +# define PyText_Check PyString_Check +# define PyTextAny_Check(op) (PyString_Check(op) || PyUnicode_Check(op)) +# define PyText_FromFormat PyString_FromFormat +# define PyText_AsUTF8 PyString_AsString +# define PyText_AS_UTF8 PyString_AS_STRING +# define PyText_GetSize PyString_Size +# define PyText_FromString PyString_FromString +# define PyText_FromStringAndSize PyString_FromStringAndSize +# define PyText_InternInPlace PyString_InternInPlace +# define PyText_InternFromString PyString_InternFromString +# define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op)) +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +# define PyInt_FromSsize_t PyLong_FromSsize_t +# define PyInt_AsSsize_t PyLong_AsSsize_t +# define PyInt_AsLong PyLong_AsLong +#endif + +#if PY_MAJOR_VERSION >= 3 +/* This is the default on Python3 and constant has been removed. */ +# define Py_TPFLAGS_CHECKTYPES 0 +#endif + +#if PY_MAJOR_VERSION < 3 +# undef PyCapsule_GetPointer +# undef PyCapsule_New +# define PyCapsule_GetPointer(capsule, name) \ + (PyCObject_AsVoidPtr(capsule)) +# define PyCapsule_New(pointer, name, destructor) \ + (PyCObject_FromVoidPtr(pointer, destructor)) +#endif + +/************************************************************/ + +/* base type flag: exactly one of the following: */ +#define CT_PRIMITIVE_SIGNED 0x001 /* signed integer */ +#define CT_PRIMITIVE_UNSIGNED 0x002 /* unsigned integer */ +#define CT_PRIMITIVE_CHAR 0x004 /* char, wchar_t, charN_t */ +#define CT_PRIMITIVE_FLOAT 0x008 /* float, double, long double */ +#define CT_POINTER 0x010 /* pointer, excluding ptr-to-func */ +#define CT_ARRAY 0x020 /* array */ +#define CT_STRUCT 0x040 /* struct */ +#define CT_UNION 0x080 /* union */ +#define CT_FUNCTIONPTR 0x100 /* pointer to function */ +#define CT_VOID 0x200 /* void */ +#define CT_PRIMITIVE_COMPLEX 0x400 /* float _Complex, double _Complex */ + +/* other flags that may also be set in addition to the base flag: */ +#define CT_IS_VOIDCHAR_PTR 0x00001000 +#define CT_PRIMITIVE_FITS_LONG 0x00002000 +#define CT_IS_OPAQUE 0x00004000 +#define CT_IS_ENUM 0x00008000 +#define CT_IS_PTR_TO_OWNED 0x00010000 /* only owned if CDataOwning_Type */ +#define CT_CUSTOM_FIELD_POS 0x00020000 +#define CT_IS_LONGDOUBLE 0x00040000 +#define CT_IS_BOOL 0x00080000 +#define CT_IS_FILE 0x00100000 +#define CT_IS_VOID_PTR 0x00200000 +#define CT_WITH_VAR_ARRAY 0x00400000 +/* unused 0x00800000 */ +#define CT_LAZY_FIELD_LIST 0x01000000 +#define CT_WITH_PACKED_CHANGE 0x02000000 +#define CT_IS_SIGNED_WCHAR 0x04000000 +#define CT_PRIMITIVE_ANY (CT_PRIMITIVE_SIGNED | \ + CT_PRIMITIVE_UNSIGNED | \ + CT_PRIMITIVE_CHAR | \ + CT_PRIMITIVE_FLOAT | \ + CT_PRIMITIVE_COMPLEX) + +typedef struct _ctypedescr { + PyObject_VAR_HEAD + + struct _ctypedescr *ct_itemdescr; /* ptrs and arrays: the item type */ + PyObject *ct_stuff; /* structs: dict of the fields + arrays: ctypedescr of the ptr type + function: tuple(abi, ctres, ctargs..) + enum: pair {"name":x},{x:"name"} + ptrs: lazily, ctypedescr of array */ + void *ct_extra; /* structs: first field (not a ref!) + function types: cif_description + primitives: prebuilt "cif" object */ + + PyObject *ct_weakreflist; /* weakref support */ + + PyObject *ct_unique_key; /* key in unique_cache (a string, but not + human-readable) */ + + Py_ssize_t ct_size; /* size of instances, or -1 if unknown */ + Py_ssize_t ct_length; /* length of arrays, or -1 if unknown; + or alignment of primitive and struct types; + always -1 for pointers */ + int ct_flags; /* CT_xxx flags */ + + int ct_name_position; /* index in ct_name of where to put a var name */ + char ct_name[1]; /* string, e.g. "int *" for pointers to ints */ +} CTypeDescrObject; + +typedef struct { + PyObject_HEAD + CTypeDescrObject *c_type; + char *c_data; + PyObject *c_weakreflist; +} CDataObject; + +typedef struct cfieldobject_s { + PyObject_HEAD + CTypeDescrObject *cf_type; + Py_ssize_t cf_offset; + short cf_bitshift; /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */ + short cf_bitsize; + unsigned char cf_flags; /* BF_... */ + struct cfieldobject_s *cf_next; +} CFieldObject; +#define BS_REGULAR (-1) /* a regular field, not with bitshift */ +#define BS_EMPTY_ARRAY (-2) /* a field declared 'type[0]' or 'type[]' */ +#define BF_IGNORE_IN_CTOR 0x01 /* union field not in the first place */ + +static PyTypeObject CTypeDescr_Type; +static PyTypeObject CField_Type; +static PyTypeObject CData_Type; +static PyTypeObject CDataOwning_Type; +static PyTypeObject CDataOwningGC_Type; +static PyTypeObject CDataGCP_Type; + +#define CTypeDescr_Check(ob) (Py_TYPE(ob) == &CTypeDescr_Type) +#define CData_Check(ob) (Py_TYPE(ob) == &CData_Type || \ + Py_TYPE(ob) == &CDataOwning_Type || \ + Py_TYPE(ob) == &CDataOwningGC_Type || \ + Py_TYPE(ob) == &CDataGCP_Type) +#define CDataOwn_Check(ob) (Py_TYPE(ob) == &CDataOwning_Type || \ + Py_TYPE(ob) == &CDataOwningGC_Type) + +typedef union { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +} union_alignment; + +typedef struct { + CDataObject head; + union_alignment alignment; +} CDataObject_casted_primitive; + +typedef struct { + CDataObject head; + union_alignment alignment; +} CDataObject_own_nolength; + +typedef struct { + CDataObject head; + Py_ssize_t length; + union_alignment alignment; +} CDataObject_own_length; + +typedef struct { + CDataObject head; + PyObject *structobj; +} CDataObject_own_structptr; + +typedef struct { + CDataObject head; + Py_ssize_t length; /* same as CDataObject_own_length up to here */ + Py_buffer *bufferview; +} CDataObject_owngc_frombuf; + +typedef struct { + CDataObject head; + Py_ssize_t length; /* same as CDataObject_own_length up to here */ + PyObject *origobj; + PyObject *destructor; +} CDataObject_gcp; + +typedef struct { + CDataObject head; + ffi_closure *closure; +} CDataObject_closure; + +typedef struct { + ffi_cif cif; + /* the following information is used when doing the call: + - a buffer of size 'exchange_size' is malloced + - the arguments are converted from Python objects to raw data + - the i'th raw data is stored at 'buffer + exchange_offset_arg[1+i]' + - the call is done + - the result is read back from 'buffer + exchange_offset_arg[0]' */ + Py_ssize_t exchange_size; + Py_ssize_t exchange_offset_arg[1]; +} cif_description_t; + +#define ADD_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) + ((size_t)(y)))) +#define MUL_WRAPAROUND(x, y) ((Py_ssize_t)(((size_t)(x)) * ((size_t)(y)))) + + +/* whenever running Python code, the errno is saved in this thread-local + variable */ +#ifndef MS_WIN32 +# include "misc_thread_posix.h" +#endif + +#include "minibuffer.h" + +#if PY_MAJOR_VERSION >= 3 +# include "file_emulator.h" +#endif + +#ifdef PyUnicode_KIND /* Python >= 3.3 */ +# include "wchar_helper_3.h" +#else +# include "wchar_helper.h" +#endif + +#include "../cffi/_cffi_errors.h" + +typedef struct _cffi_allocator_s { + PyObject *ca_alloc, *ca_free; + int ca_dont_clear; +} cffi_allocator_t; +static const cffi_allocator_t default_allocator = { NULL, NULL, 0 }; +static PyObject *FFIError; +static PyObject *unique_cache; + +/************************************************************/ + +static CTypeDescrObject * +ctypedescr_new(int name_size) +{ + CTypeDescrObject *ct = PyObject_GC_NewVar(CTypeDescrObject, + &CTypeDescr_Type, + name_size); + if (ct == NULL) + return NULL; + + ct->ct_itemdescr = NULL; + ct->ct_stuff = NULL; + ct->ct_weakreflist = NULL; + ct->ct_unique_key = NULL; + PyObject_GC_Track(ct); + return ct; +} + +static CTypeDescrObject * +ctypedescr_new_on_top(CTypeDescrObject *ct_base, const char *extra_text, + int extra_position) +{ + int base_name_len = strlen(ct_base->ct_name); + int extra_name_len = strlen(extra_text); + CTypeDescrObject *ct = ctypedescr_new(base_name_len + extra_name_len + 1); + char *p; + if (ct == NULL) + return NULL; + + Py_INCREF(ct_base); + ct->ct_itemdescr = ct_base; + ct->ct_name_position = ct_base->ct_name_position + extra_position; + + p = ct->ct_name; + memcpy(p, ct_base->ct_name, ct_base->ct_name_position); + p += ct_base->ct_name_position; + memcpy(p, extra_text, extra_name_len); + p += extra_name_len; + memcpy(p, ct_base->ct_name + ct_base->ct_name_position, + base_name_len - ct_base->ct_name_position + 1); + + return ct; +} + +static PyObject * +ctypedescr_repr(CTypeDescrObject *ct) +{ + return PyText_FromFormat("<ctype '%s'>", ct->ct_name); +} + +static void +ctypedescr_dealloc(CTypeDescrObject *ct) +{ + PyObject_GC_UnTrack(ct); + if (ct->ct_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) ct); + + if (ct->ct_unique_key != NULL) { + /* revive dead object temporarily for DelItem */ + Py_REFCNT(ct) = 43; + PyDict_DelItem(unique_cache, ct->ct_unique_key); + assert(Py_REFCNT(ct) == 42); + Py_REFCNT(ct) = 0; + Py_DECREF(ct->ct_unique_key); + } + Py_XDECREF(ct->ct_itemdescr); + Py_XDECREF(ct->ct_stuff); + if (ct->ct_flags & CT_FUNCTIONPTR) + PyObject_Free(ct->ct_extra); + Py_TYPE(ct)->tp_free((PyObject *)ct); +} + +static int +ctypedescr_traverse(CTypeDescrObject *ct, visitproc visit, void *arg) +{ + Py_VISIT(ct->ct_itemdescr); + Py_VISIT(ct->ct_stuff); + return 0; +} + +static int +ctypedescr_clear(CTypeDescrObject *ct) +{ + Py_CLEAR(ct->ct_itemdescr); + Py_CLEAR(ct->ct_stuff); + return 0; +} + + +static PyObject *nosuchattr(const char *attr) +{ + PyErr_SetString(PyExc_AttributeError, attr); + return NULL; +} + +static PyObject *ctypeget_kind(CTypeDescrObject *ct, void *context) +{ + char *result; + if (ct->ct_flags & CT_PRIMITIVE_ANY) { + if (ct->ct_flags & CT_IS_ENUM) + result = "enum"; + else + result = "primitive"; + } + else if (ct->ct_flags & CT_POINTER) { + result = "pointer"; + } + else if (ct->ct_flags & CT_ARRAY) { + result = "array"; + } + else if (ct->ct_flags & CT_VOID) { + result = "void"; + } + else if (ct->ct_flags & CT_STRUCT) { + result = "struct"; + } + else if (ct->ct_flags & CT_UNION) { + result = "union"; + } + else if (ct->ct_flags & CT_FUNCTIONPTR) { + result = "function"; + } + else + result = "?"; + + return PyText_FromString(result); +} + +static PyObject *ctypeget_cname(CTypeDescrObject *ct, void *context) +{ + return PyText_FromString(ct->ct_name); +} + +static PyObject *ctypeget_item(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & (CT_POINTER | CT_ARRAY)) { + Py_INCREF(ct->ct_itemdescr); + return (PyObject *)ct->ct_itemdescr; + } + return nosuchattr("item"); +} + +static PyObject *ctypeget_length(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_ARRAY) { + if (ct->ct_length >= 0) { + return PyInt_FromSsize_t(ct->ct_length); + } + else { + Py_INCREF(Py_None); + return Py_None; + } + } + return nosuchattr("length"); +} + +static PyObject * +get_field_name(CTypeDescrObject *ct, CFieldObject *cf); /* forward */ + +/* returns 0 if the struct ctype is opaque, 1 if it is not, or -1 if + an exception occurs */ +#define force_lazy_struct(ct) \ + ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct)) + +static int do_realize_lazy_struct(CTypeDescrObject *ct); +/* forward, implemented in realize_c_type.c */ + +static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & (CT_STRUCT | CT_UNION)) { + if (!(ct->ct_flags & CT_IS_OPAQUE)) { + CFieldObject *cf; + PyObject *res; + if (force_lazy_struct(ct) < 0) + return NULL; + res = PyList_New(0); + if (res == NULL) + return NULL; + for (cf = (CFieldObject *)ct->ct_extra; + cf != NULL; cf = cf->cf_next) { + PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf), + (PyObject *)cf); + int err = (o != NULL) ? PyList_Append(res, o) : -1; + Py_XDECREF(o); + if (err < 0) { + Py_DECREF(res); + return NULL; + } + } + return res; + } + else { + Py_INCREF(Py_None); + return Py_None; + } + } + return nosuchattr("fields"); +} + +static PyObject *ctypeget_args(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_FUNCTIONPTR) { + PyObject *t = ct->ct_stuff; + return PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t)); + } + return nosuchattr("args"); +} + +static PyObject *ctypeget_result(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_FUNCTIONPTR) { + PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); + Py_XINCREF(res); + return res; + } + return nosuchattr("result"); +} + +static PyObject *ctypeget_ellipsis(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_FUNCTIONPTR) { + PyObject *res = ct->ct_extra ? Py_False : Py_True; + Py_INCREF(res); + return res; + } + return nosuchattr("ellipsis"); +} + +static PyObject *ctypeget_abi(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_FUNCTIONPTR) { + PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); + Py_XINCREF(res); + return res; + } + return nosuchattr("abi"); +} + +static PyObject *ctypeget_elements(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_IS_ENUM) { + PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1); + if (res) res = PyDict_Copy(res); + return res; + } + return nosuchattr("elements"); +} + +static PyObject *ctypeget_relements(CTypeDescrObject *ct, void *context) +{ + if (ct->ct_flags & CT_IS_ENUM) { + PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0); + if (res) res = PyDict_Copy(res); + return res; + } + return nosuchattr("relements"); +} + +static PyGetSetDef ctypedescr_getsets[] = { + {"kind", (getter)ctypeget_kind, NULL, "kind"}, + {"cname", (getter)ctypeget_cname, NULL, "C name"}, + {"item", (getter)ctypeget_item, NULL, "pointer to, or array of"}, + {"length", (getter)ctypeget_length, NULL, "array length or None"}, + {"fields", (getter)ctypeget_fields, NULL, "struct or union fields"}, + {"args", (getter)ctypeget_args, NULL, "function argument types"}, + {"result", (getter)ctypeget_result, NULL, "function result type"}, + {"ellipsis", (getter)ctypeget_ellipsis, NULL, "function has '...'"}, + {"abi", (getter)ctypeget_abi, NULL, "function ABI"}, + {"elements", (getter)ctypeget_elements, NULL, "enum elements"}, + {"relements", (getter)ctypeget_relements, NULL, "enum elements, reverse"}, + {NULL} /* sentinel */ +}; + +static PyObject * +ctypedescr_dir(PyObject *ct, PyObject *noarg) +{ + int err; + struct PyGetSetDef *gsdef; + PyObject *res = PyList_New(0); + if (res == NULL) + return NULL; + + for (gsdef = ctypedescr_getsets; gsdef->name; gsdef++) { + PyObject *x = PyObject_GetAttrString(ct, gsdef->name); + if (x == NULL) { + PyErr_Clear(); + } + else { + Py_DECREF(x); + x = PyText_FromString(gsdef->name); + err = (x != NULL) ? PyList_Append(res, x) : -1; + Py_XDECREF(x); + if (err < 0) { + Py_DECREF(res); + return NULL; + } + } + } + return res; +} + +static PyMethodDef ctypedescr_methods[] = { + {"__dir__", ctypedescr_dir, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject CTypeDescr_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CTypeDescr", + offsetof(CTypeDescrObject, ct_name), + sizeof(char), + (destructor)ctypedescr_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)ctypedescr_repr, /* 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, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)ctypedescr_traverse, /* tp_traverse */ + (inquiry)ctypedescr_clear, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(CTypeDescrObject, ct_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + ctypedescr_methods, /* tp_methods */ + 0, /* tp_members */ + ctypedescr_getsets, /* tp_getset */ +}; + +/************************************************************/ + +static PyObject * +get_field_name(CTypeDescrObject *ct, CFieldObject *cf) +{ + Py_ssize_t i = 0; + PyObject *d_key, *d_value; + while (PyDict_Next(ct->ct_stuff, &i, &d_key, &d_value)) { + if (d_value == (PyObject *)cf) + return d_key; + } + Py_FatalError("_cffi_backend: get_field_name()"); + return NULL; +} + +static void +cfield_dealloc(CFieldObject *cf) +{ + Py_DECREF(cf->cf_type); + PyObject_Del(cf); +} + +#undef OFF +#define OFF(x) offsetof(CFieldObject, x) + +static PyMemberDef cfield_members[] = { + {"type", T_OBJECT, OFF(cf_type), READONLY}, + {"offset", T_PYSSIZET, OFF(cf_offset), READONLY}, + {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY}, + {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY}, + {"flags", T_UBYTE, OFF(cf_flags), READONLY}, + {NULL} /* Sentinel */ +}; +#undef OFF + +static PyTypeObject CField_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CField", + sizeof(CFieldObject), + 0, + (destructor)cfield_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, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + cfield_members, /* tp_members */ +}; + +/************************************************************/ + +static int +CDataObject_Or_PyFloat_Check(PyObject *ob) +{ + return (PyFloat_Check(ob) || + (CData_Check(ob) && + (((CDataObject *)ob)->c_type->ct_flags & CT_PRIMITIVE_FLOAT))); +} + +static PY_LONG_LONG +_my_PyLong_AsLongLong(PyObject *ob) +{ + /* (possibly) convert and cast a Python object to a long long. + Like PyLong_AsLongLong(), this version accepts a Python int too, and + does convertions from other types of objects. The difference is that + this version refuses floats. */ +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(ob)) { + return PyInt_AS_LONG(ob); + } + else +#endif + if (PyLong_Check(ob)) { + return PyLong_AsLongLong(ob); + } + else { + PyObject *io; + PY_LONG_LONG res; + PyNumberMethods *nb = ob->ob_type->tp_as_number; + + if (CDataObject_Or_PyFloat_Check(ob) || + nb == NULL || nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return -1; + } + io = (*nb->nb_int) (ob); + if (io == NULL) + return -1; + + if (PyIntOrLong_Check(io)) { + res = _my_PyLong_AsLongLong(io); + } + else { + PyErr_SetString(PyExc_TypeError, "integer conversion failed"); + res = -1; + } + Py_DECREF(io); + return res; + } +} + +static unsigned PY_LONG_LONG +_my_PyLong_AsUnsignedLongLong(PyObject *ob, int strict) +{ + /* (possibly) convert and cast a Python object to an unsigned long long. + Like PyLong_AsLongLong(), this version accepts a Python int too, and + does convertions from other types of objects. If 'strict', complains + with OverflowError and refuses floats. If '!strict', rounds floats + and masks the result. */ +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(ob)) { + long value1 = PyInt_AS_LONG(ob); + if (strict && value1 < 0) + goto negative; + return (unsigned PY_LONG_LONG)(PY_LONG_LONG)value1; + } + else +#endif + if (PyLong_Check(ob)) { + if (strict) { + if (_PyLong_Sign(ob) < 0) + goto negative; + return PyLong_AsUnsignedLongLong(ob); + } + else { + return PyLong_AsUnsignedLongLongMask(ob); + } + } + else { + PyObject *io; + unsigned PY_LONG_LONG res; + PyNumberMethods *nb = ob->ob_type->tp_as_number; + + if ((strict && CDataObject_Or_PyFloat_Check(ob)) || + nb == NULL || nb->nb_int == NULL) { + PyErr_SetString(PyExc_TypeError, "an integer is required"); + return (unsigned PY_LONG_LONG)-1; + } + io = (*nb->nb_int) (ob); + if (io == NULL) + return (unsigned PY_LONG_LONG)-1; + + if (PyIntOrLong_Check(io)) { + res = _my_PyLong_AsUnsignedLongLong(io, strict); + } + else { + PyErr_SetString(PyExc_TypeError, "integer conversion failed"); + res = (unsigned PY_LONG_LONG)-1; + } + Py_DECREF(io); + return res; + } + + negative: + PyErr_SetString(PyExc_OverflowError, + "can't convert negative number to unsigned"); + return (unsigned PY_LONG_LONG)-1; +} + +#define _read_raw_data(type) \ + do { \ + if (size == sizeof(type)) { \ + type r; \ + memcpy(&r, target, sizeof(type)); \ + return r; \ + } \ + } while(0) + +static PY_LONG_LONG +read_raw_signed_data(char *target, int size) +{ + _read_raw_data(signed char); + _read_raw_data(short); + _read_raw_data(int); + _read_raw_data(long); + _read_raw_data(PY_LONG_LONG); + Py_FatalError("read_raw_signed_data: bad integer size"); + return 0; +} + +static unsigned PY_LONG_LONG +read_raw_unsigned_data(char *target, int size) +{ + _read_raw_data(unsigned char); + _read_raw_data(unsigned short); + _read_raw_data(unsigned int); + _read_raw_data(unsigned long); + _read_raw_data(unsigned PY_LONG_LONG); + Py_FatalError("read_raw_unsigned_data: bad integer size"); + return 0; +} + +#ifdef __GNUC__ +/* This is a workaround for what I think is a GCC bug on several + platforms. See issue #378. */ +__attribute__((noinline)) +#endif +void _cffi_memcpy(char *target, const void *src, size_t size) +{ + memcpy(target, src, size); +} + +#define _write_raw_data(type) \ + do { \ + if (size == sizeof(type)) { \ + type r = (type)source; \ + _cffi_memcpy(target, &r, sizeof(type)); \ + return; \ + } \ + } while(0) + +static void +write_raw_integer_data(char *target, unsigned PY_LONG_LONG source, int size) +{ + _write_raw_data(unsigned char); + _write_raw_data(unsigned short); + _write_raw_data(unsigned int); + _write_raw_data(unsigned long); + _write_raw_data(unsigned PY_LONG_LONG); + Py_FatalError("write_raw_integer_data: bad integer size"); +} + +static double +read_raw_float_data(char *target, int size) +{ + _read_raw_data(float); + _read_raw_data(double); + Py_FatalError("read_raw_float_data: bad float size"); + return 0; +} + +static long double +read_raw_longdouble_data(char *target) +{ + int size = sizeof(long double); + _read_raw_data(long double); + Py_FatalError("read_raw_longdouble_data: bad long double size"); + return 0; +} + +static Py_complex +read_raw_complex_data(char *target, int size) +{ + Py_complex r = {0.0, 0.0}; + if (size == 2*sizeof(float)) { + float real_part, imag_part; + memcpy(&real_part, target + 0, sizeof(float)); + memcpy(&imag_part, target + sizeof(float), sizeof(float)); + r.real = real_part; + r.imag = imag_part; + return r; + } + if (size == 2*sizeof(double)) { + memcpy(&r, target, 2*sizeof(double)); + return r; + } + Py_FatalError("read_raw_complex_data: bad complex size"); + return r; +} + +static void +write_raw_float_data(char *target, double source, int size) +{ + _write_raw_data(float); + _write_raw_data(double); + Py_FatalError("write_raw_float_data: bad float size"); +} + +static void +write_raw_longdouble_data(char *target, long double source) +{ + int size = sizeof(long double); + _write_raw_data(long double); +} + +#define _write_raw_complex_data(type) \ + do { \ + if (size == 2*sizeof(type)) { \ + type r = (type)source.real; \ + type i = (type)source.imag; \ + _cffi_memcpy(target, &r, sizeof(type)); \ + _cffi_memcpy(target+sizeof(type), &i, sizeof(type)); \ + return; \ + } \ + } while(0) + +static void +write_raw_complex_data(char *target, Py_complex source, int size) +{ + _write_raw_complex_data(float); + _write_raw_complex_data(double); + Py_FatalError("write_raw_complex_data: bad complex size"); +} + +static PyObject * +new_simple_cdata(char *data, CTypeDescrObject *ct) +{ + CDataObject *cd = PyObject_New(CDataObject, &CData_Type); + if (cd == NULL) + return NULL; + Py_INCREF(ct); + cd->c_data = data; + cd->c_type = ct; + cd->c_weakreflist = NULL; + return (PyObject *)cd; +} + +static PyObject * +new_sized_cdata(char *data, CTypeDescrObject *ct, Py_ssize_t length) +{ + CDataObject_own_length *scd; + + scd = (CDataObject_own_length *)PyObject_Malloc( + offsetof(CDataObject_own_length, alignment)); + if (PyObject_Init((PyObject *)scd, &CData_Type) == NULL) + return NULL; + Py_INCREF(ct); + scd->head.c_type = ct; + scd->head.c_data = data; + scd->head.c_weakreflist = NULL; + scd->length = length; + return (PyObject *)scd; +} + +static CDataObject *_new_casted_primitive(CTypeDescrObject *ct); /*forward*/ + +static PyObject * +convert_to_object(char *data, CTypeDescrObject *ct) +{ + if (!(ct->ct_flags & CT_PRIMITIVE_ANY)) { + /* non-primitive types (check done just for performance) */ + if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { + char *ptrdata = *(char **)data; + /*READ(data, sizeof(char *))*/ + return new_simple_cdata(ptrdata, ct); + } + else if (ct->ct_flags & CT_IS_OPAQUE) { + PyErr_Format(PyExc_TypeError, "cdata '%s' is opaque", + ct->ct_name); + return NULL; + } + else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { + return new_simple_cdata(data, ct); + } + else if (ct->ct_flags & CT_ARRAY) { + if (ct->ct_length < 0) { + /* we can't return a <cdata 'int[]'> here, because we don't + know the length to give it. As a compromize, returns + <cdata 'int *'> in this case. */ + ct = (CTypeDescrObject *)ct->ct_stuff; + } + return new_simple_cdata(data, ct); + } + } + else if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { + PY_LONG_LONG value; + /*READ(data, ct->ct_size)*/ + value = read_raw_signed_data(data, ct->ct_size); + if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) + return PyInt_FromLong((long)value); + else + return PyLong_FromLongLong(value); + } + else if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { + unsigned PY_LONG_LONG value; + /*READ(data, ct->ct_size)*/ + value = read_raw_unsigned_data(data, ct->ct_size); + + if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) { + if (ct->ct_flags & CT_IS_BOOL) { + PyObject *x; + switch ((int)value) { + case 0: x = Py_False; break; + case 1: x = Py_True; break; + default: + PyErr_Format(PyExc_ValueError, + "got a _Bool of value %d, expected 0 or 1", + (int)value); + return NULL; + } + Py_INCREF(x); + return x; + } + return PyInt_FromLong((long)value); + } + else + return PyLong_FromUnsignedLongLong(value); + } + else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { + /*READ(data, ct->ct_size)*/ + if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) { + double value = read_raw_float_data(data, ct->ct_size); + return PyFloat_FromDouble(value); + } + else { + long double value = read_raw_longdouble_data(data); + CDataObject *cd = _new_casted_primitive(ct); + if (cd != NULL) + write_raw_longdouble_data(cd->c_data, value); + return (PyObject *)cd; + } + } + else if (ct->ct_flags & CT_PRIMITIVE_CHAR) { + /*READ(data, ct->ct_size)*/ + switch (ct->ct_size) { + case sizeof(char): + return PyBytes_FromStringAndSize(data, 1); + case 2: + return _my_PyUnicode_FromChar16((cffi_char16_t *)data, 1); + case 4: + return _my_PyUnicode_FromChar32((cffi_char32_t *)data, 1); + } + } + else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { + Py_complex value = read_raw_complex_data(data, ct->ct_size); + return PyComplex_FromCComplex(value); + } + + PyErr_Format(PyExc_SystemError, + "convert_to_object: '%s'", ct->ct_name); + return NULL; +} + +static PyObject * +convert_to_object_bitfield(char *data, CFieldObject *cf) +{ + CTypeDescrObject *ct = cf->cf_type; + /*READ(data, ct->ct_size)*/ + + if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { + unsigned PY_LONG_LONG value, valuemask, shiftforsign; + PY_LONG_LONG result; + + value = (unsigned PY_LONG_LONG)read_raw_signed_data(data, ct->ct_size); + valuemask = (1ULL << cf->cf_bitsize) - 1ULL; + shiftforsign = 1ULL << (cf->cf_bitsize - 1); + value = ((value >> cf->cf_bitshift) + shiftforsign) & valuemask; + result = ((PY_LONG_LONG)value) - (PY_LONG_LONG)shiftforsign; + + if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) + return PyInt_FromLong((long)result); + else + return PyLong_FromLongLong(result); + } + else { + unsigned PY_LONG_LONG value, valuemask; + + value = read_raw_unsigned_data(data, ct->ct_size); + valuemask = (1ULL << cf->cf_bitsize) - 1ULL; + value = (value >> cf->cf_bitshift) & valuemask; + + if (ct->ct_flags & CT_PRIMITIVE_FITS_LONG) + return PyInt_FromLong((long)value); + else + return PyLong_FromUnsignedLongLong(value); + } +} + +static int _convert_overflow(PyObject *init, const char *ct_name) +{ + PyObject *s; + if (PyErr_Occurred()) /* already an exception pending */ + return -1; + s = PyObject_Str(init); + if (s == NULL) + return -1; + PyErr_Format(PyExc_OverflowError, "integer %s does not fit '%s'", + PyText_AS_UTF8(s), ct_name); + Py_DECREF(s); + return -1; +} + +static int _convert_to_char(PyObject *init) +{ + if (PyBytes_Check(init) && PyBytes_GET_SIZE(init) == 1) { + return (unsigned char)(PyBytes_AS_STRING(init)[0]); + } + if (CData_Check(init) && + (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && + (((CDataObject *)init)->c_type->ct_size == sizeof(char))) { + char *data = ((CDataObject *)init)->c_data; + /*READ(data, 1)*/ + return *(unsigned char *)data; + } + PyErr_Format(PyExc_TypeError, + "initializer for ctype 'char' must be a "STR_OR_BYTES + " of length 1, not %.200s", Py_TYPE(init)->tp_name); + return -1; +} + +static cffi_char16_t _convert_to_char16_t(PyObject *init) +{ + char err_got[80]; + err_got[0] = 0; + + if (PyUnicode_Check(init)) { + cffi_char16_t ordinal; + if (_my_PyUnicode_AsSingleChar16(init, &ordinal, err_got) == 0) + return ordinal; + } + if (CData_Check(init) && + (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && + (((CDataObject *)init)->c_type->ct_size == 2)) { + char *data = ((CDataObject *)init)->c_data; + /*READ(data, 2)*/ + return *(cffi_char16_t *)data; + } + PyErr_Format(PyExc_TypeError, + "initializer for ctype 'char16_t' must be a unicode string " + "of length 1, not %.200s", + err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); + return (cffi_char16_t)-1; +} + +static cffi_char32_t _convert_to_char32_t(PyObject *init) +{ + char err_got[80]; + err_got[0] = 0; + + if (PyUnicode_Check(init)) { + cffi_char32_t ordinal; + if (_my_PyUnicode_AsSingleChar32(init, &ordinal, err_got) == 0) + return ordinal; + } + if (CData_Check(init) && + (((CDataObject *)init)->c_type->ct_flags & CT_PRIMITIVE_CHAR) && + (((CDataObject *)init)->c_type->ct_size == 4)) { + char *data = ((CDataObject *)init)->c_data; + /*READ(data, 4)*/ + return *(cffi_char32_t *)data; + } + PyErr_Format(PyExc_TypeError, + "initializer for ctype 'char32_t' must be a unicode string " + "of length 1, not %.200s", + err_got[0] == 0 ? Py_TYPE(init)->tp_name : err_got); + return (cffi_char32_t)-1; +} + +static int _convert_error(PyObject *init, CTypeDescrObject *ct, + const char *expected) +{ + if (CData_Check(init)) { + CTypeDescrObject *ct2 = ((CDataObject *)init)->c_type; + if (strcmp(ct->ct_name, ct2->ct_name) != 0) + PyErr_Format(PyExc_TypeError, + "initializer for ctype '%s' must be a %s, " + "not cdata '%s'", + ct->ct_name, expected, ct2->ct_name); + else if (ct != ct2) { + /* in case we'd give the error message "initializer for + ctype 'A' must be a pointer to same type, not cdata + 'B'", but with A=B, then give instead a different error + message to try to clear up the confusion */ + PyErr_Format(PyExc_TypeError, + "initializer for ctype '%s' appears indeed to be '%s'," + " but the types are different (check that you are not" + " e.g. mixing up different ffi instances)", + ct->ct_name, ct2->ct_name); + } + else + { + PyErr_Format(PyExc_SystemError, + "initializer for ctype '%s' is correct, but we get " + "an internal mismatch--please report a bug", + ct->ct_name); + } + } + else + PyErr_Format(PyExc_TypeError, + "initializer for ctype '%s' must be a %s, " + "not %.200s", + ct->ct_name, expected, Py_TYPE(init)->tp_name); + return -1; +} + +static int /* forward */ +convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init); +static int /* forward */ +convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init); + +static Py_ssize_t +get_new_array_length(CTypeDescrObject *ctitem, PyObject **pvalue) +{ + PyObject *value = *pvalue; + + if (PyList_Check(value) || PyTuple_Check(value)) { + return PySequence_Fast_GET_SIZE(value); + } + else if (PyBytes_Check(value)) { + /* from a string, we add the null terminator */ + return PyBytes_GET_SIZE(value) + 1; + } + else if (PyUnicode_Check(value)) { + /* from a unicode, we add the null terminator */ + int length; + if (ctitem->ct_size == 2) + length = _my_PyUnicode_SizeAsChar16(value); + else + length = _my_PyUnicode_SizeAsChar32(value); + return length + 1; + } + else { + Py_ssize_t explicitlength; + explicitlength = PyNumber_AsSsize_t(value, PyExc_OverflowError); + if (explicitlength < 0) { + if (PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) + PyErr_Format(PyExc_TypeError, + "expected new array length or list/tuple/str, " + "not %.200s", Py_TYPE(value)->tp_name); + } + else + PyErr_SetString(PyExc_ValueError, "negative array length"); + return -1; + } + *pvalue = Py_None; + return explicitlength; + } +} + +static int +convert_field_from_object(char *data, CFieldObject *cf, PyObject *value) +{ + data += cf->cf_offset; + if (cf->cf_bitshift >= 0) + return convert_from_object_bitfield(data, cf, value); + else + return convert_from_object(data, cf->cf_type, value); +} + +static int +convert_vfield_from_object(char *data, CFieldObject *cf, PyObject *value, + Py_ssize_t *optvarsize) +{ + /* a special case for var-sized C99 arrays */ + if ((cf->cf_type->ct_flags & CT_ARRAY) && cf->cf_type->ct_size < 0) { + Py_ssize_t varsizelength = get_new_array_length( + cf->cf_type->ct_itemdescr, &value); + if (varsizelength < 0) + return -1; + if (optvarsize != NULL) { + /* in this mode, the only purpose of this function is to compute + the real size of the structure from a var-sized C99 array */ + Py_ssize_t size, itemsize; + assert(data == NULL); + itemsize = cf->cf_type->ct_itemdescr->ct_size; + size = ADD_WRAPAROUND(cf->cf_offset, + MUL_WRAPAROUND(itemsize, varsizelength)); + if (size < 0 || + ((size - cf->cf_offset) / itemsize) != varsizelength) { + PyErr_SetString(PyExc_OverflowError, + "array size would overflow a Py_ssize_t"); + return -1; + } + if (size > *optvarsize) + *optvarsize = size; + return 0; + } + /* if 'value' was only an integer, get_new_array_length() returns + it and convert 'value' to be None. Detect if this was the case, + and if so, stop here, leaving the content uninitialized + (it should be zero-initialized from somewhere else). */ + if (value == Py_None) + return 0; + } + if (optvarsize == NULL) + return convert_field_from_object(data, cf, value); + else + return 0; +} + +static int +must_be_array_of_zero_or_one(const char *data, Py_ssize_t n) +{ + Py_ssize_t i; + for (i = 0; i < n; i++) { + if (((unsigned char)data[i]) > 1) { + PyErr_SetString(PyExc_ValueError, + "an array of _Bool can only contain \\x00 or \\x01"); + return -1; + } + } + return 0; +} + +static Py_ssize_t +get_array_length(CDataObject *cd) +{ + if (cd->c_type->ct_length < 0) + return ((CDataObject_own_length *)cd)->length; + else + return cd->c_type->ct_length; +} + +static int +convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init) +{ + /* used by convert_from_object(), and also to decode lists/tuples/unicodes + passed as function arguments. 'ct' is an CT_ARRAY in the first case + and a CT_POINTER in the second case. */ + const char *expected; + CTypeDescrObject *ctitem = ct->ct_itemdescr; + + if (PyList_Check(init) || PyTuple_Check(init)) { + PyObject **items; + Py_ssize_t i, n; + n = PySequence_Fast_GET_SIZE(init); + if (ct->ct_length >= 0 && n > ct->ct_length) { + PyErr_Format(PyExc_IndexError, + "too many initializers for '%s' (got %zd)", + ct->ct_name, n); + return -1; + } + items = PySequence_Fast_ITEMS(init); + for (i=0; i<n; i++) { + if (convert_from_object(data, ctitem, items[i]) < 0) + return -1; + data += ctitem->ct_size; + } + return 0; + } + else if ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) || + ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) + && (ctitem->ct_size == sizeof(char)))) { + if (ctitem->ct_size == sizeof(char)) { + char *srcdata; + Py_ssize_t n; + if (!PyBytes_Check(init)) { + expected = STR_OR_BYTES" or list or tuple"; + goto cannot_convert; + } + n = PyBytes_GET_SIZE(init); + if (ct->ct_length >= 0 && n > ct->ct_length) { + PyErr_Format(PyExc_IndexError, + "initializer "STR_OR_BYTES" is too long for '%s' " + "(got %zd characters)", ct->ct_name, n); + return -1; + } + if (n != ct->ct_length) + n++; + srcdata = PyBytes_AS_STRING(init); + if (ctitem->ct_flags & CT_IS_BOOL) + if (must_be_array_of_zero_or_one(srcdata, n) < 0) + return -1; + memcpy(data, srcdata, n); + return 0; + } + else { + Py_ssize_t n; + if (!PyUnicode_Check(init)) { + expected = "unicode or list or tuple"; + goto cannot_convert; + } + + if (ctitem->ct_size == 4) + n = _my_PyUnicode_SizeAsChar32(init); + else + n = _my_PyUnicode_SizeAsChar16(init); + + if (ct->ct_length >= 0 && n > ct->ct_length) { + PyErr_Format(PyExc_IndexError, + "initializer unicode is too long for '%s' " + "(got %zd characters)", ct->ct_name, n); + return -1; + } + if (n != ct->ct_length) + n++; + if (ctitem->ct_size == 4) + return _my_PyUnicode_AsChar32(init, (cffi_char32_t *)data, n); + else + return _my_PyUnicode_AsChar16(init, (cffi_char16_t *)data, n); + } + } + else { + expected = "list or tuple"; + goto cannot_convert; + } + + cannot_convert: + if ((ct->ct_flags & CT_ARRAY) && CData_Check(init)) + { + CDataObject *cd = (CDataObject *)init; + if (cd->c_type == ct) + { + Py_ssize_t n = get_array_length(cd); + memcpy(data, cd->c_data, n * ctitem->ct_size); + return 0; + } + } + return _convert_error(init, ct, expected); +} + +static int +convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init, + Py_ssize_t *optvarsize) +{ + /* does not accept 'init' being already a CData */ + const char *expected; + + if (force_lazy_struct(ct) <= 0) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_TypeError, "'%s' is opaque", ct->ct_name); + return -1; + } + + if (PyList_Check(init) || PyTuple_Check(init)) { + PyObject **items = PySequence_Fast_ITEMS(init); + Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init); + CFieldObject *cf = (CFieldObject *)ct->ct_extra; + + for (i=0; i<n; i++) { + while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR)) + cf = cf->cf_next; + if (cf == NULL) { + PyErr_Format(PyExc_ValueError, + "too many initializers for '%s' (got %zd)", + ct->ct_name, n); + return -1; + } + if (convert_vfield_from_object(data, cf, items[i], optvarsize) < 0) + return -1; + cf = cf->cf_next; + } + return 0; + } + if (PyDict_Check(init)) { + PyObject *d_key, *d_value; + Py_ssize_t i = 0; + CFieldObject *cf; + + while (PyDict_Next(init, &i, &d_key, &d_value)) { + cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, d_key); + if (cf == NULL) { + PyErr_SetObject(PyExc_KeyError, d_key); + return -1; + } + if (convert_vfield_from_object(data, cf, d_value, optvarsize) < 0) + return -1; + } + return 0; + } + expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata" + : "list or tuple or dict"; + return _convert_error(init, ct, expected); +} + +#ifdef __GNUC__ +# if __GNUC__ >= 4 +/* Don't go inlining this huge function. Needed because occasionally + it gets inlined in places where is causes a warning: call to + __builtin___memcpy_chk will always overflow destination buffer + (which is places where the 'ct' should never represent such a large + primitive type anyway). */ +__attribute__((noinline)) +# endif +#endif +static int +convert_from_object(char *data, CTypeDescrObject *ct, PyObject *init) +{ + const char *expected; + char buf[sizeof(PY_LONG_LONG)]; + + /*if (ct->ct_size > 0)*/ + /*WRITE(data, ct->ct_size)*/ + + if (ct->ct_flags & CT_ARRAY) { + return convert_array_from_object(data, ct, init); + } + if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { + char *ptrdata; + CTypeDescrObject *ctinit; + + if (!CData_Check(init)) { + expected = "cdata pointer"; + goto cannot_convert; + } + ctinit = ((CDataObject *)init)->c_type; + if (!(ctinit->ct_flags & (CT_POINTER|CT_FUNCTIONPTR))) { + if (ctinit->ct_flags & CT_ARRAY) + ctinit = (CTypeDescrObject *)ctinit->ct_stuff; + else { + expected = "pointer or array"; + goto cannot_convert; + } + } + if (ctinit != ct) { + int combined_flags = ct->ct_flags | ctinit->ct_flags; + if (combined_flags & CT_IS_VOID_PTR) + ; /* accept "void *" as either source or target */ + else if (combined_flags & CT_IS_VOIDCHAR_PTR) { + /* for backward compatibility, accept "char *" as either + source of target. This is not what C does, though, + so emit a warning that will eventually turn into an + error. The warning is turned off if both types are + pointers to single bytes. */ + char *msg = (ct->ct_flags & CT_IS_VOIDCHAR_PTR ? + "implicit cast to 'char *' from a different pointer type: " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" : + "implicit cast from 'char *' to a different pointer type: " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)"); + if ((ct->ct_flags & ctinit->ct_flags & CT_POINTER) && + ct->ct_itemdescr->ct_size == 1 && + ctinit->ct_itemdescr->ct_size == 1) { + /* no warning */ + } + else if (PyErr_WarnEx(PyExc_UserWarning, msg, 1)) + return -1; + } + else { + expected = "pointer to same type"; + goto cannot_convert; + } + } + ptrdata = ((CDataObject *)init)->c_data; + + *(char **)data = ptrdata; + return 0; + } + if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { + PY_LONG_LONG value = _my_PyLong_AsLongLong(init); + if (value == -1 && PyErr_Occurred()) + return -1; + write_raw_integer_data(buf, value, ct->ct_size); + if (value != read_raw_signed_data(buf, ct->ct_size)) + goto overflow; + write_raw_integer_data(data, value, ct->ct_size); + return 0; + } + if (ct->ct_flags & CT_PRIMITIVE_UNSIGNED) { + unsigned PY_LONG_LONG value = _my_PyLong_AsUnsignedLongLong(init, 1); + if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + return -1; + if (ct->ct_flags & CT_IS_BOOL) { + if (value > 1ULL) /* value != 0 && value != 1 */ + goto overflow; + } + else { + write_raw_integer_data(buf, value, ct->ct_size); + if (value != read_raw_unsigned_data(buf, ct->ct_size)) + goto overflow; + } + write_raw_integer_data(data, value, ct->ct_size); + return 0; + } + if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { + double value; + if ((ct->ct_flags & CT_IS_LONGDOUBLE) && + CData_Check(init) && + (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + long double lvalue; + char *initdata = ((CDataObject *)init)->c_data; + /*READ(initdata, sizeof(long double))*/ + lvalue = read_raw_longdouble_data(initdata); + write_raw_longdouble_data(data, lvalue); + return 0; + } + value = PyFloat_AsDouble(init); + if (value == -1.0 && PyErr_Occurred()) + return -1; + if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) + write_raw_float_data(data, value, ct->ct_size); + else + write_raw_longdouble_data(data, (long double)value); + return 0; + } + if (ct->ct_flags & CT_PRIMITIVE_CHAR) { + switch (ct->ct_size) { + case sizeof(char): { + int res = _convert_to_char(init); + if (res < 0) + return -1; + data[0] = res; + return 0; + } + case 2: { + cffi_char16_t res = _convert_to_char16_t(init); + if (res == (cffi_char16_t)-1 && PyErr_Occurred()) + return -1; + *(cffi_char16_t *)data = res; + return 0; + } + case 4: { + cffi_char32_t res = _convert_to_char32_t(init); + if (res == (cffi_char32_t)-1 && PyErr_Occurred()) + return -1; + *(cffi_char32_t *)data = res; + return 0; + } + } + } + if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { + + if (CData_Check(init)) { + if (((CDataObject *)init)->c_type == ct && ct->ct_size >= 0) { + memcpy(data, ((CDataObject *)init)->c_data, ct->ct_size); + return 0; + } + } + return convert_struct_from_object(data, ct, init, NULL); + } + if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { + Py_complex value = PyComplex_AsCComplex(init); + if (PyErr_Occurred()) + return -1; + write_raw_complex_data(data, value, ct->ct_size); + return 0; + } + PyErr_Format(PyExc_SystemError, + "convert_from_object: '%s'", ct->ct_name); + return -1; + + overflow: + return _convert_overflow(init, ct->ct_name); + + cannot_convert: + return _convert_error(init, ct, expected); +} + +static int +convert_from_object_bitfield(char *data, CFieldObject *cf, PyObject *init) +{ + CTypeDescrObject *ct = cf->cf_type; + PY_LONG_LONG fmin, fmax, value = PyLong_AsLongLong(init); + unsigned PY_LONG_LONG rawfielddata, rawvalue, rawmask; + if (value == -1 && PyErr_Occurred()) + return -1; + + if (ct->ct_flags & CT_PRIMITIVE_SIGNED) { + fmin = -(1LL << (cf->cf_bitsize-1)); + fmax = (1LL << (cf->cf_bitsize-1)) - 1LL; + if (fmax == 0) + fmax = 1; /* special case to let "int x:1" receive "1" */ + } + else { + fmin = 0LL; + fmax = (PY_LONG_LONG)((1ULL << cf->cf_bitsize) - 1ULL); + } + if (value < fmin || value > fmax) { + /* phew, PyErr_Format does not support "%lld" in Python 2.6 */ + PyObject *svalue = NULL, *sfmin = NULL, *sfmax = NULL; + PyObject *lfmin = NULL, *lfmax = NULL; + svalue = PyObject_Str(init); + if (svalue == NULL) goto skip; + lfmin = PyLong_FromLongLong(fmin); + if (lfmin == NULL) goto skip; + sfmin = PyObject_Str(lfmin); + if (sfmin == NULL) goto skip; + lfmax = PyLong_FromLongLong(fmax); + if (lfmax == NULL) goto skip; + sfmax = PyObject_Str(lfmax); + if (sfmax == NULL) goto skip; + PyErr_Format(PyExc_OverflowError, + "value %s outside the range allowed by the " + "bit field width: %s <= x <= %s", + PyText_AS_UTF8(svalue), + PyText_AS_UTF8(sfmin), + PyText_AS_UTF8(sfmax)); + skip: + Py_XDECREF(svalue); + Py_XDECREF(sfmin); + Py_XDECREF(sfmax); + Py_XDECREF(lfmin); + Py_XDECREF(lfmax); + return -1; + } + + rawmask = ((1ULL << cf->cf_bitsize) - 1ULL) << cf->cf_bitshift; + rawvalue = ((unsigned PY_LONG_LONG)value) << cf->cf_bitshift; + /*WRITE(data, ct->ct_size)*/ + rawfielddata = read_raw_unsigned_data(data, ct->ct_size); + rawfielddata = (rawfielddata & ~rawmask) | (rawvalue & rawmask); + write_raw_integer_data(data, rawfielddata, ct->ct_size); + return 0; +} + +static int +get_alignment(CTypeDescrObject *ct) +{ + int align; + retry: + if ((ct->ct_flags & (CT_PRIMITIVE_ANY|CT_STRUCT|CT_UNION)) && + !(ct->ct_flags & CT_IS_OPAQUE)) { + align = ct->ct_length; + if (align == -1 && (ct->ct_flags & CT_LAZY_FIELD_LIST)) { + force_lazy_struct(ct); + align = ct->ct_length; + } + } + else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { + struct aligncheck_ptr { char x; char *y; }; + align = offsetof(struct aligncheck_ptr, y); + } + else if (ct->ct_flags & CT_ARRAY) { + ct = ct->ct_itemdescr; + goto retry; + } + else { + PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown alignment", + ct->ct_name); + return -1; + } + + if ((align < 1) || (align & (align-1))) { + PyErr_Format(PyExc_SystemError, + "found for ctype '%s' bogus alignment '%d'", + ct->ct_name, align); + return -1; + } + return align; +} + +static void cdata_dealloc(CDataObject *cd) +{ + if (cd->c_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) cd); + + Py_DECREF(cd->c_type); +#ifndef CFFI_MEM_LEAK /* never release anything, tests only */ + Py_TYPE(cd)->tp_free((PyObject *)cd); +#endif +} + +static void cdataowning_dealloc(CDataObject *cd) +{ + assert(!(cd->c_type->ct_flags & (CT_IS_VOID_PTR | CT_FUNCTIONPTR))); + + if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { + Py_DECREF(((CDataObject_own_structptr *)cd)->structobj); + } +#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) + if (cd->c_type->ct_flags & (CT_PRIMITIVE_ANY | CT_STRUCT | CT_UNION)) { + assert(cd->c_type->ct_size >= 0); + memset(cd->c_data, 0xDD, cd->c_type->ct_size); + } + else if (cd->c_type->ct_flags & CT_ARRAY) { + Py_ssize_t x = get_array_length(cd); + assert(x >= 0); + x *= cd->c_type->ct_itemdescr->ct_size; + assert(x >= 0); + memset(cd->c_data, 0xDD, x); + } +#endif + cdata_dealloc(cd); +} + +static void cdataowninggc_dealloc(CDataObject *cd) +{ + assert(!(cd->c_type->ct_flags & (CT_IS_PTR_TO_OWNED | + CT_PRIMITIVE_ANY | + CT_STRUCT | CT_UNION))); + PyObject_GC_UnTrack(cd); + + if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ + PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; + Py_DECREF(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ + ffi_closure *closure = ((CDataObject_closure *)cd)->closure; + PyObject *args = (PyObject *)(closure->user_data); + Py_XDECREF(args); +#ifdef CFFI_TRUST_LIBFFI + ffi_closure_free(closure); +#else + cffi_closure_free(closure); +#endif + } + else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + PyBuffer_Release(view); + PyObject_Free(view); + } + cdata_dealloc(cd); +} + +static int cdataowninggc_traverse(CDataObject *cd, visitproc visit, void *arg) +{ + if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ + PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; + Py_VISIT(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ + ffi_closure *closure = ((CDataObject_closure *)cd)->closure; + PyObject *args = (PyObject *)(closure->user_data); + Py_VISIT(args); + } + else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + Py_VISIT(view->obj); + } + return 0; +} + +static int cdataowninggc_clear(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ + CDataObject_own_structptr *cd1 = (CDataObject_own_structptr *)cd; + PyObject *x = cd1->structobj; + Py_INCREF(Py_None); + cd1->structobj = Py_None; + Py_DECREF(x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ + ffi_closure *closure = ((CDataObject_closure *)cd)->closure; + PyObject *args = (PyObject *)(closure->user_data); + closure->user_data = NULL; + Py_XDECREF(args); + } + else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + PyBuffer_Release(view); + } + return 0; +} + +/* forward */ +static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, + char *objdescr, PyObject *obj, + char *extra_error_line); + + +static void gcp_finalize(PyObject *destructor, PyObject *origobj) +{ + /* NOTE: this decrements the reference count of the two arguments */ + + if (destructor != NULL) { + PyObject *result; + PyObject *error_type, *error_value, *error_traceback; + + /* Save the current exception */ + PyErr_Fetch(&error_type, &error_value, &error_traceback); + + result = PyObject_CallFunctionObjArgs(destructor, origobj, NULL); + if (result != NULL) { + Py_DECREF(result); + } + else { + PyObject *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + /* Don't use error capture here, because it is very much + * like errors at __del__(), and these ones are not captured + * either */ + /* ecap = _cffi_start_error_capture(); */ + _my_PyErr_WriteUnraisable(t, v, tb, "From callback for ffi.gc ", + origobj, NULL); + /* _cffi_stop_error_capture(ecap); */ + } + Py_DECREF(destructor); + + /* Restore the saved exception */ + PyErr_Restore(error_type, error_value, error_traceback); + } + Py_XDECREF(origobj); +} + +static void cdatagcp_finalize(CDataObject_gcp *cd) +{ + PyObject *destructor = cd->destructor; + PyObject *origobj = cd->origobj; + cd->destructor = NULL; + cd->origobj = NULL; + gcp_finalize(destructor, origobj); +} + +static void cdatagcp_dealloc(CDataObject_gcp *cd) +{ + PyObject *destructor = cd->destructor; + PyObject *origobj = cd->origobj; + cdata_dealloc((CDataObject *)cd); + + gcp_finalize(destructor, origobj); +} + +static int cdatagcp_traverse(CDataObject_gcp *cd, visitproc visit, void *arg) +{ + Py_VISIT(cd->destructor); + Py_VISIT(cd->origobj); + return 0; +} + +static PyObject *cdata_float(CDataObject *cd); /*forward*/ + +static PyObject *convert_cdata_to_enum_string(CDataObject *cd, int both) +{ + PyObject *d_key, *d_value; + CTypeDescrObject *ct = cd->c_type; + + assert(ct->ct_flags & CT_IS_ENUM); + d_key = convert_to_object(cd->c_data, ct); + if (d_key == NULL) + return NULL; + + d_value = PyDict_GetItem(PyTuple_GET_ITEM(ct->ct_stuff, 1), d_key); + if (d_value != NULL) { + if (both) { + PyObject *o = PyObject_Str(d_key); + if (o == NULL) + d_value = NULL; + else { + d_value = PyText_FromFormat("%s: %s", + PyText_AS_UTF8(o), + PyText_AS_UTF8(d_value)); + Py_DECREF(o); + } + } + else + Py_INCREF(d_value); + } + else + d_value = PyObject_Str(d_key); + Py_DECREF(d_key); + return d_value; +} + +static PyObject *cdata_repr(CDataObject *cd) +{ + char *extra; + PyObject *result, *s; + + if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { + if (cd->c_type->ct_flags & CT_IS_ENUM) { + s = convert_cdata_to_enum_string(cd, 1); + } + else if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { + long double lvalue; + char buffer[128]; /* big enough */ + /*READ(cd->c_data, sizeof(long double)*/ + lvalue = read_raw_longdouble_data(cd->c_data); + sprintf(buffer, "%LE", lvalue); + s = PyText_FromString(buffer); + } + else { + PyObject *o = convert_to_object(cd->c_data, cd->c_type); + if (o == NULL) + return NULL; + s = PyObject_Repr(o); + Py_DECREF(o); + } + } + else if ((cd->c_type->ct_flags & CT_ARRAY) && cd->c_type->ct_length < 0) { + s = PyText_FromFormat("sliced length %zd", get_array_length(cd)); + } + else { + if (cd->c_data != NULL) { + s = PyText_FromFormat("%p", cd->c_data); + } + else + s = PyText_FromString("NULL"); + } + if (s == NULL) + return NULL; + /* it's slightly confusing to get "<cdata 'struct foo' 0x...>" because the + struct foo is not owned. Trying to make it clearer, write in this + case "<cdata 'struct foo &' 0x...>". */ + if (cd->c_type->ct_flags & (CT_STRUCT|CT_UNION)) + extra = " &"; + else + extra = ""; + result = PyText_FromFormat("<cdata '%s%s' %s>", + cd->c_type->ct_name, extra, + PyText_AsUTF8(s)); + Py_DECREF(s); + return result; +} + +static PyObject *_cdata_repr2(CDataObject *cd, char *text, PyObject *x) +{ + PyObject *res, *s = PyObject_Repr(x); + if (s == NULL) + return NULL; + res = PyText_FromFormat("<cdata '%s' %s %s>", + cd->c_type->ct_name, text, PyText_AsUTF8(s)); + Py_DECREF(s); + return res; +} + +static Py_ssize_t _cdata_var_byte_size(CDataObject *cd) +{ + /* If 'cd' is a 'struct foo' or 'struct foo *' allocated with + ffi.new(), and if the struct foo contains a varsize array, + then return the real allocated size. Otherwise, return -1. */ + if (!CDataOwn_Check(cd)) + return -1; + + if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { + cd = (CDataObject *)((CDataObject_own_structptr *)cd)->structobj; + } + if (cd->c_type->ct_flags & CT_WITH_VAR_ARRAY) { + return ((CDataObject_own_length *)cd)->length; + } + return -1; +} + +static PyObject *cdataowning_repr(CDataObject *cd) +{ + Py_ssize_t size = _cdata_var_byte_size(cd); + if (size < 0) { + if (cd->c_type->ct_flags & CT_POINTER) + size = cd->c_type->ct_itemdescr->ct_size; + else if (cd->c_type->ct_flags & CT_ARRAY) + size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + else + size = cd->c_type->ct_size; + } + return PyText_FromFormat("<cdata '%s' owning %zd bytes>", + cd->c_type->ct_name, size); +} + +static PyObject *cdataowninggc_repr(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_IS_VOID_PTR) { /* a handle */ + PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; + return _cdata_repr2(cd, "handle to", x); + } + else if (cd->c_type->ct_flags & CT_FUNCTIONPTR) { /* a callback */ + ffi_closure *closure = ((CDataObject_closure *)cd)->closure; + PyObject *args = (PyObject *)closure->user_data; + if (args == NULL) + return cdata_repr(cd); + else + return _cdata_repr2(cd, "calling", PyTuple_GET_ITEM(args, 1)); + } + else if (cd->c_type->ct_flags & CT_ARRAY) { /* from_buffer */ + Py_buffer *view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + Py_ssize_t buflen = get_array_length(cd); + return PyText_FromFormat( + "<cdata '%s' buffer len %zd from '%.200s' object>", + cd->c_type->ct_name, + buflen, + view->obj ? Py_TYPE(view->obj)->tp_name : "(null)"); + } + return cdataowning_repr(cd); +} + +static int cdata_nonzero(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) { + if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED | + CT_PRIMITIVE_CHAR)) + return read_raw_unsigned_data(cd->c_data, cd->c_type->ct_size) != 0; + + if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { + if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) + return read_raw_longdouble_data(cd->c_data) != 0.0; + return read_raw_float_data(cd->c_data, cd->c_type->ct_size) != 0.0; + } + if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { + Py_complex value = read_raw_complex_data(cd->c_data, + cd->c_type->ct_size); + return value.real != 0.0 || value.imag != 0.0; + } + } + return cd->c_data != NULL; +} + +static PyObject *cdata_int(CDataObject *cd) +{ + if ((cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) + == (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_FITS_LONG)) { + /* this case is to handle enums, but also serves as a slight + performance improvement for some other primitive types */ + long value; + /*READ(cd->c_data, cd->c_type->ct_size)*/ + value = (long)read_raw_signed_data(cd->c_data, cd->c_type->ct_size); + return PyInt_FromLong(value); + } + if (cd->c_type->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) { + return convert_to_object(cd->c_data, cd->c_type); + } + else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { + /*READ(cd->c_data, cd->c_type->ct_size)*/ + switch (cd->c_type->ct_size) { + case sizeof(char): + return PyInt_FromLong((unsigned char)cd->c_data[0]); + case 2: + return PyInt_FromLong((long)*(cffi_char16_t *)cd->c_data); + case 4: + if (cd->c_type->ct_flags & CT_IS_SIGNED_WCHAR) + return PyInt_FromLong((long)*(int32_t *)cd->c_data); + else if (sizeof(long) > 4) + return PyInt_FromLong(*(uint32_t *)cd->c_data); + else + return PyLong_FromUnsignedLong(*(uint32_t *)cd->c_data); + } + } + else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { + PyObject *o = cdata_float(cd); +#if PY_MAJOR_VERSION < 3 + PyObject *r = o ? PyNumber_Int(o) : NULL; +#else + PyObject *r = o ? PyNumber_Long(o) : NULL; +#endif + Py_XDECREF(o); + return r; + } + PyErr_Format(PyExc_TypeError, "int() not supported on cdata '%s'", + cd->c_type->ct_name); + return NULL; +} + +#if PY_MAJOR_VERSION < 3 +static PyObject *cdata_long(CDataObject *cd) +{ + PyObject *res = cdata_int(cd); + if (res != NULL && PyInt_CheckExact(res)) { + PyObject *o = PyLong_FromLong(PyInt_AS_LONG(res)); + Py_DECREF(res); + res = o; + } + return res; +} +#endif + +static PyObject *cdata_float(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { + double value; + /*READ(cd->c_data, cd->c_type->ct_size)*/ + if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + value = read_raw_float_data(cd->c_data, cd->c_type->ct_size); + } + else { + value = (double)read_raw_longdouble_data(cd->c_data); + } + return PyFloat_FromDouble(value); + } + PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'", + cd->c_type->ct_name); + return NULL; +} + +static PyObject *cdata_richcompare(PyObject *v, PyObject *w, int op) +{ + int v_is_ptr, w_is_ptr; + PyObject *pyres; + + assert(CData_Check(v)); + + /* Comparisons involving a primitive cdata work differently than + * comparisons involving a struct/array/pointer. + * + * If v or w is a struct/array/pointer, then the other must be too + * (otherwise we return NotImplemented and leave the case to + * Python). If both are, then we compare the addresses. + * + * If v and/or w is a primitive cdata, then we convert the cdata(s) + * to regular Python objects and redo the comparison there. + */ + + v_is_ptr = !(((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY); + w_is_ptr = CData_Check(w) && + !(((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY); + + if (v_is_ptr && w_is_ptr) { + int res; + char *v_cdata = ((CDataObject *)v)->c_data; + char *w_cdata = ((CDataObject *)w)->c_data; + + switch (op) { + case Py_EQ: res = (v_cdata == w_cdata); break; + case Py_NE: res = (v_cdata != w_cdata); break; + case Py_LT: res = (v_cdata < w_cdata); break; + case Py_LE: res = (v_cdata <= w_cdata); break; + case Py_GT: res = (v_cdata > w_cdata); break; + case Py_GE: res = (v_cdata >= w_cdata); break; + default: res = -1; + } + pyres = res ? Py_True : Py_False; + } + else if (v_is_ptr || w_is_ptr) { + pyres = Py_NotImplemented; + } + else { + PyObject *aa[2]; + int i; + + aa[0] = v; Py_INCREF(v); + aa[1] = w; Py_INCREF(w); + pyres = NULL; + + for (i = 0; i < 2; i++) { + v = aa[i]; + if (!CData_Check(v)) + continue; + w = convert_to_object(((CDataObject *)v)->c_data, + ((CDataObject *)v)->c_type); + if (w == NULL) + goto error; + if (CData_Check(w)) { + Py_DECREF(w); + PyErr_Format(PyExc_NotImplementedError, + "cannot use <cdata '%s'> in a comparison", + ((CDataObject *)v)->c_type->ct_name); + goto error; + } + aa[i] = w; + Py_DECREF(v); + } + pyres = PyObject_RichCompare(aa[0], aa[1], op); + error: + Py_DECREF(aa[1]); + Py_DECREF(aa[0]); + return pyres; + } + + Py_INCREF(pyres); + return pyres; +} + +static long cdata_hash(CDataObject *v) +{ + if (((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) { + PyObject *vv = convert_to_object(((CDataObject *)v)->c_data, + ((CDataObject *)v)->c_type); + if (vv == NULL) + return -1; + if (!CData_Check(vv)) { + long hash = PyObject_Hash(vv); + Py_DECREF(vv); + return hash; + } + Py_DECREF(vv); + } + return _Py_HashPointer(v->c_data); +} + +static Py_ssize_t +cdata_length(CDataObject *cd) +{ + if (cd->c_type->ct_flags & CT_ARRAY) { + return get_array_length(cd); + } + PyErr_Format(PyExc_TypeError, "cdata of type '%s' has no len()", + cd->c_type->ct_name); + return -1; +} + +static char * +_cdata_get_indexed_ptr(CDataObject *cd, PyObject *key) +{ + Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + + if (cd->c_type->ct_flags & CT_POINTER) { + if (CDataOwn_Check(cd)) { + if (i != 0) { + PyErr_Format(PyExc_IndexError, + "cdata '%s' can only be indexed by 0", + cd->c_type->ct_name); + return NULL; + } + } + else { + if (cd->c_data == NULL) { + PyErr_Format(PyExc_RuntimeError, + "cannot dereference null pointer from cdata '%s'", + cd->c_type->ct_name); + return NULL; + } + } + } + else if (cd->c_type->ct_flags & CT_ARRAY) { + if (i < 0) { + PyErr_SetString(PyExc_IndexError, + "negative index"); + return NULL; + } + if (i >= get_array_length(cd)) { + PyErr_Format(PyExc_IndexError, + "index too large for cdata '%s' (expected %zd < %zd)", + cd->c_type->ct_name, + i, get_array_length(cd)); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", + cd->c_type->ct_name); + return NULL; + } + return cd->c_data + i * cd->c_type->ct_itemdescr->ct_size; +} + +static PyObject * +new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length); /* forward */ + +static CTypeDescrObject * +_cdata_getslicearg(CDataObject *cd, PySliceObject *slice, Py_ssize_t bounds[]) +{ + Py_ssize_t start, stop; + CTypeDescrObject *ct; + + start = PyInt_AsSsize_t(slice->start); + if (start == -1 && PyErr_Occurred()) { + if (slice->start == Py_None) + PyErr_SetString(PyExc_IndexError, "slice start must be specified"); + return NULL; + } + stop = PyInt_AsSsize_t(slice->stop); + if (stop == -1 && PyErr_Occurred()) { + if (slice->stop == Py_None) + PyErr_SetString(PyExc_IndexError, "slice stop must be specified"); + return NULL; + } + if (slice->step != Py_None) { + PyErr_SetString(PyExc_IndexError, "slice with step not supported"); + return NULL; + } + if (start > stop) { + PyErr_SetString(PyExc_IndexError, "slice start > stop"); + return NULL; + } + + ct = cd->c_type; + if (ct->ct_flags & CT_ARRAY) { + if (start < 0) { + PyErr_SetString(PyExc_IndexError, + "negative index"); + return NULL; + } + if (stop > get_array_length(cd)) { + PyErr_Format(PyExc_IndexError, + "index too large (expected %zd <= %zd)", + stop, get_array_length(cd)); + return NULL; + } + ct = (CTypeDescrObject *)ct->ct_stuff; + } + else if (!(ct->ct_flags & CT_POINTER)) { + PyErr_Format(PyExc_TypeError, "cdata of type '%s' cannot be indexed", + ct->ct_name); + return NULL; + } + + bounds[0] = start; + bounds[1] = stop - start; + return ct; +} + +static PyObject * +cdata_slice(CDataObject *cd, PySliceObject *slice) +{ + char *cdata; + Py_ssize_t bounds[2]; + CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); + if (ct == NULL) + return NULL; + + if (ct->ct_stuff == NULL) { + ct->ct_stuff = new_array_type(ct, -1); + if (ct->ct_stuff == NULL) + return NULL; + } + ct = (CTypeDescrObject *)ct->ct_stuff; + + cdata = cd->c_data + ct->ct_itemdescr->ct_size * bounds[0]; + return new_sized_cdata(cdata, ct, bounds[1]); +} + +static int +cdata_ass_slice(CDataObject *cd, PySliceObject *slice, PyObject *v) +{ + Py_ssize_t bounds[2], i, length, itemsize; + PyObject *it, *item; + PyObject *(*iternext)(PyObject *); + char *cdata; + int err; + CTypeDescrObject *ct = _cdata_getslicearg(cd, slice, bounds); + if (ct == NULL) + return -1; + ct = ct->ct_itemdescr; + itemsize = ct->ct_size; + cdata = cd->c_data + itemsize * bounds[0]; + length = bounds[1]; + + if (CData_Check(v)) { + CTypeDescrObject *ctv = ((CDataObject *)v)->c_type; + if ((ctv->ct_flags & CT_ARRAY) && (ctv->ct_itemdescr == ct) && + (get_array_length((CDataObject *)v) == length)) { + /* fast path: copying from exactly the correct type */ + memmove(cdata, ((CDataObject *)v)->c_data, itemsize * length); + return 0; + } + } + + /* A fast path for <char[]>[0:N] = b"somestring" or bytearray, which + also adds support for Python 3: otherwise, you get integers while + enumerating the string, and you can't set them to characters :-/ + */ + if ((ct->ct_flags & CT_PRIMITIVE_CHAR) && itemsize == sizeof(char)) { + char *src; + Py_ssize_t srclen; + if (PyBytes_Check(v)) { + srclen = PyBytes_GET_SIZE(v); + src = PyBytes_AS_STRING(v); + } + else if (PyByteArray_Check(v)) { + srclen = PyByteArray_GET_SIZE(v); + src = PyByteArray_AS_STRING(v); + } + else + goto other_types; + + if (srclen != length) { + PyErr_Format(PyExc_ValueError, + "need a string of length %zd, got %zd", + length, srclen); + return -1; + } + memcpy(cdata, src, length); + return 0; + } + other_types: + + it = PyObject_GetIter(v); + if (it == NULL) + return -1; + iternext = *it->ob_type->tp_iternext; + + for (i = 0; i < length; i++) { + item = iternext(it); + if (item == NULL) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_ValueError, + "need %zd values to unpack, got %zd", + length, i); + goto error; + } + err = convert_from_object(cdata, ct, item); + Py_DECREF(item); + if (err < 0) + goto error; + + cdata += itemsize; + } + item = iternext(it); + if (item != NULL) { + Py_DECREF(item); + PyErr_Format(PyExc_ValueError, + "got more than %zd values to unpack", length); + } + error: + Py_DECREF(it); + return PyErr_Occurred() ? -1 : 0; +} + +static PyObject * +cdataowning_subscript(CDataObject *cd, PyObject *key) +{ + char *c; + if (PySlice_Check(key)) + return cdata_slice(cd, (PySliceObject *)key); + + c = _cdata_get_indexed_ptr(cd, key); + /* use 'mp_subscript' instead of 'sq_item' because we don't want + negative indexes to be corrected automatically */ + if (c == NULL && PyErr_Occurred()) + return NULL; + + if (cd->c_type->ct_flags & CT_IS_PTR_TO_OWNED) { + PyObject *res = ((CDataObject_own_structptr *)cd)->structobj; + Py_INCREF(res); + return res; + } + else { + return convert_to_object(c, cd->c_type->ct_itemdescr); + } +} + +static PyObject * +cdata_subscript(CDataObject *cd, PyObject *key) +{ + char *c; + if (PySlice_Check(key)) + return cdata_slice(cd, (PySliceObject *)key); + + c = _cdata_get_indexed_ptr(cd, key); + /* use 'mp_subscript' instead of 'sq_item' because we don't want + negative indexes to be corrected automatically */ + if (c == NULL && PyErr_Occurred()) + return NULL; + return convert_to_object(c, cd->c_type->ct_itemdescr); +} + +static int +cdata_ass_sub(CDataObject *cd, PyObject *key, PyObject *v) +{ + char *c; + CTypeDescrObject *ctitem; + if (PySlice_Check(key)) + return cdata_ass_slice(cd, (PySliceObject *)key, v); + + c = _cdata_get_indexed_ptr(cd, key); + ctitem = cd->c_type->ct_itemdescr; + /* use 'mp_ass_subscript' instead of 'sq_ass_item' because we don't want + negative indexes to be corrected automatically */ + if (c == NULL && PyErr_Occurred()) + return -1; + if (v == NULL) { + PyErr_SetString(PyExc_TypeError, + "'del x[n]' not supported for cdata objects"); + return -1; + } + return convert_from_object(c, ctitem, v); +} + +static PyObject * +_cdata_add_or_sub(PyObject *v, PyObject *w, int sign) +{ + Py_ssize_t i, itemsize; + CDataObject *cd; + CTypeDescrObject *ctptr; + + if (!CData_Check(v)) { + PyObject *swap; + assert(CData_Check(w)); + if (sign != 1) + goto not_implemented; + swap = v; + v = w; + w = swap; + } + + i = PyNumber_AsSsize_t(w, PyExc_OverflowError); + if (i == -1 && PyErr_Occurred()) + return NULL; + i *= sign; + + cd = (CDataObject *)v; + if (cd->c_type->ct_flags & CT_POINTER) + ctptr = cd->c_type; + else if (cd->c_type->ct_flags & CT_ARRAY) { + ctptr = (CTypeDescrObject *)cd->c_type->ct_stuff; + } + else { + PyErr_Format(PyExc_TypeError, "cannot add a cdata '%s' and a number", + cd->c_type->ct_name); + return NULL; + } + itemsize = ctptr->ct_itemdescr->ct_size; + if (itemsize < 0) { + if (ctptr->ct_flags & CT_IS_VOID_PTR) { + itemsize = 1; + } + else { + PyErr_Format(PyExc_TypeError, + "ctype '%s' points to items of unknown size", + cd->c_type->ct_name); + return NULL; + } + } + return new_simple_cdata(cd->c_data + i * itemsize, ctptr); + + not_implemented: + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; +} + +static PyObject * +cdata_add(PyObject *v, PyObject *w) +{ + return _cdata_add_or_sub(v, w, +1); +} + +static PyObject * +cdata_sub(PyObject *v, PyObject *w) +{ + if (CData_Check(v) && CData_Check(w)) { + CDataObject *cdv = (CDataObject *)v; + CDataObject *cdw = (CDataObject *)w; + CTypeDescrObject *ct = cdw->c_type; + Py_ssize_t diff, itemsize; + + if (ct->ct_flags & CT_ARRAY) /* ptr_to_T - array_of_T: ok */ + ct = (CTypeDescrObject *)ct->ct_stuff; + + if (ct != cdv->c_type || !(ct->ct_flags & CT_POINTER) || + (ct->ct_itemdescr->ct_size <= 0 && + !(ct->ct_flags & CT_IS_VOID_PTR))) { + PyErr_Format(PyExc_TypeError, + "cannot subtract cdata '%s' and cdata '%s'", + cdv->c_type->ct_name, ct->ct_name); + return NULL; + } + itemsize = ct->ct_itemdescr->ct_size; + diff = cdv->c_data - cdw->c_data; + if (itemsize > 1) { + if (diff % itemsize) { + PyErr_SetString(PyExc_ValueError, + "pointer subtraction: the distance between the two " + "pointers is not a multiple of the item size"); + return NULL; + } + diff = diff / itemsize; + } +#if PY_MAJOR_VERSION < 3 + return PyInt_FromSsize_t(diff); +#else + return PyLong_FromSsize_t(diff); +#endif + } + + return _cdata_add_or_sub(v, w, -1); +} + +static void +_cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr) +{ + const char *text; + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return; + PyErr_Clear(); + text = PyText_AsUTF8(attr); + if (text == NULL) + return; + PyErr_Format(PyExc_AttributeError, errmsg, cd->c_type->ct_name, text); +} + +static PyObject * +cdata_getattro(CDataObject *cd, PyObject *attr) +{ + CFieldObject *cf; + CTypeDescrObject *ct = cd->c_type; + char *errmsg = "cdata '%s' has no attribute '%s'"; + PyObject *x; + + if (ct->ct_flags & CT_POINTER) + ct = ct->ct_itemdescr; + + if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { + switch (force_lazy_struct(ct)) { + case 1: + cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); + if (cf != NULL) { + /* read the field 'cf' */ + char *data = cd->c_data + cf->cf_offset; + Py_ssize_t array_len, size; + + if (cf->cf_bitshift == BS_REGULAR) { + return convert_to_object(data, cf->cf_type); + } + else if (cf->cf_bitshift != BS_EMPTY_ARRAY) { + return convert_to_object_bitfield(data, cf); + } + + /* variable-length array: */ + /* if reading variable length array from variable length + struct, calculate array type from allocated length */ + size = _cdata_var_byte_size(cd) - cf->cf_offset; + if (size >= 0) { + array_len = size / cf->cf_type->ct_itemdescr->ct_size; + return new_sized_cdata(data, cf->cf_type, array_len); + } + return new_simple_cdata(data, + (CTypeDescrObject *)cf->cf_type->ct_stuff); + } + errmsg = "cdata '%s' has no field '%s'"; + break; + case -1: + return NULL; + default: + errmsg = "cdata '%s' points to an opaque type: cannot read fields"; + break; + } + } + x = PyObject_GenericGetAttr((PyObject *)cd, attr); + if (x == NULL) + _cdata_attr_errmsg(errmsg, cd, attr); + return x; +} + +static int +cdata_setattro(CDataObject *cd, PyObject *attr, PyObject *value) +{ + CFieldObject *cf; + CTypeDescrObject *ct = cd->c_type; + char *errmsg = "cdata '%s' has no attribute '%s'"; + int x; + + if (ct->ct_flags & CT_POINTER) + ct = ct->ct_itemdescr; + + if (ct->ct_flags & (CT_STRUCT|CT_UNION)) { + switch (force_lazy_struct(ct)) { + case 1: + cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, attr); + if (cf != NULL) { + /* write the field 'cf' */ + if (value != NULL) { + return convert_field_from_object(cd->c_data, cf, value); + } + else { + PyErr_SetString(PyExc_AttributeError, + "cannot delete struct field"); + return -1; + } + } + errmsg = "cdata '%s' has no field '%s'"; + break; + case -1: + return -1; + default: + errmsg = "cdata '%s' points to an opaque type: cannot write fields"; + break; + } + } + x = PyObject_GenericSetAttr((PyObject *)cd, attr, value); + if (x < 0) + _cdata_attr_errmsg(errmsg, cd, attr); + return x; +} + +static PyObject * +convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/ + +static cif_description_t * +fb_prepare_cif(PyObject *fargs, CTypeDescrObject *, ffi_abi); /*forward*/ + +static PyObject *new_primitive_type(const char *name); /*forward*/ + +static CTypeDescrObject *_get_ct_int(void) +{ + static CTypeDescrObject *ct_int = NULL; + if (ct_int == NULL) { + ct_int = (CTypeDescrObject *)new_primitive_type("int"); + } + return ct_int; +} + +static Py_ssize_t +_prepare_pointer_call_argument(CTypeDescrObject *ctptr, PyObject *init, + char **output_data) +{ + /* 'ctptr' is here a pointer type 'ITEM *'. Accept as argument an + initializer for an array 'ITEM[]'. This includes the case of + passing a Python byte string to a 'char *' argument. + + This function returns -1 if an error occurred, + 0 if conversion succeeded (into *output_data), + or N > 0 if conversion would require N bytes of storage. + */ + Py_ssize_t length, datasize; + CTypeDescrObject *ctitem; + + if (CData_Check(init)) + goto convert_default; + + ctitem = ctptr->ct_itemdescr; + /* XXX some code duplication, how to avoid it? */ + if (PyBytes_Check(init)) { + /* from a string: just returning the string here is fine. + We assume that the C code won't modify the 'char *' data. */ + if ((ctptr->ct_flags & CT_IS_VOIDCHAR_PTR) || + ((ctitem->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED)) + && (ctitem->ct_size == sizeof(char)))) { +#if defined(CFFI_MEM_DEBUG) || defined(CFFI_MEM_LEAK) + length = PyBytes_GET_SIZE(init) + 1; +#else + *output_data = PyBytes_AS_STRING(init); + if (ctitem->ct_flags & CT_IS_BOOL) + if (must_be_array_of_zero_or_one(*output_data, + PyBytes_GET_SIZE(init)) < 0) + return -1; + return 0; +#endif + } + else + goto convert_default; + } + else if (PyList_Check(init) || PyTuple_Check(init)) { + length = PySequence_Fast_GET_SIZE(init); + } + else if (PyUnicode_Check(init)) { + /* from a unicode, we add the null terminator */ + if (ctitem->ct_size == 2) + length = _my_PyUnicode_SizeAsChar16(init); + else + length = _my_PyUnicode_SizeAsChar32(init); + length += 1; + } + else if ((ctitem->ct_flags & CT_IS_FILE) && PyFile_Check(init)) { + *output_data = (char *)PyFile_AsFile(init); + if (*output_data == NULL && PyErr_Occurred()) + return -1; + return 0; + } + else { + /* refuse to receive just an integer (and interpret it + as the array size) */ + goto convert_default; + } + + if (ctitem->ct_size <= 0) + goto convert_default; + datasize = MUL_WRAPAROUND(length, ctitem->ct_size); + if ((datasize / ctitem->ct_size) != length) { + PyErr_SetString(PyExc_OverflowError, + "array size would overflow a Py_ssize_t"); + return -1; + } + if (datasize <= 0) + datasize = 1; + return datasize; + + convert_default: + return convert_from_object((char *)output_data, ctptr, init); +} + +static PyObject* +cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds) +{ + char *buffer; + void** buffer_array; + cif_description_t *cif_descr; + Py_ssize_t i, nargs, nargs_declared; + PyObject *signature, *res = NULL, *fvarargs; + CTypeDescrObject *fresult; + char *resultdata; + char *errormsg; + + if (!(cd->c_type->ct_flags & CT_FUNCTIONPTR)) { + PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable", + cd->c_type->ct_name); + return NULL; + } + if (kwds != NULL && PyDict_Size(kwds) != 0) { + PyErr_SetString(PyExc_TypeError, + "a cdata function cannot be called with keyword arguments"); + return NULL; + } + signature = cd->c_type->ct_stuff; + nargs = PyTuple_Size(args); + if (nargs < 0) + return NULL; + nargs_declared = PyTuple_GET_SIZE(signature) - 2; + fresult = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 1); + fvarargs = NULL; + buffer = NULL; + + cif_descr = (cif_description_t *)cd->c_type->ct_extra; + + if (cif_descr != NULL) { + /* regular case: this function does not take '...' arguments */ + if (nargs != nargs_declared) { + errormsg = "'%s' expects %zd arguments, got %zd"; + bad_number_of_arguments: + PyErr_Format(PyExc_TypeError, errormsg, + cd->c_type->ct_name, nargs_declared, nargs); + goto error; + } + } + else { + /* call of a variadic function */ + ffi_abi fabi; + if (nargs < nargs_declared) { + errormsg = "'%s' expects at least %zd arguments, got %zd"; + goto bad_number_of_arguments; + } + fvarargs = PyTuple_New(nargs); + if (fvarargs == NULL) + goto error; + for (i = 0; i < nargs_declared; i++) { + PyObject *o = PyTuple_GET_ITEM(signature, 2 + i); + Py_INCREF(o); + PyTuple_SET_ITEM(fvarargs, i, o); + } + for (i = nargs_declared; i < nargs; i++) { + PyObject *obj = PyTuple_GET_ITEM(args, i); + CTypeDescrObject *ct; + + if (CData_Check(obj)) { + ct = ((CDataObject *)obj)->c_type; + if (ct->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_UNSIGNED | + CT_PRIMITIVE_SIGNED)) { + if (ct->ct_size < (Py_ssize_t)sizeof(int)) { + ct = _get_ct_int(); + if (ct == NULL) + goto error; + } + } + else if (ct->ct_flags & CT_ARRAY) { + ct = (CTypeDescrObject *)ct->ct_stuff; + } + Py_INCREF(ct); + } + else { + PyErr_Format(PyExc_TypeError, + "argument %zd passed in the variadic part " + "needs to be a cdata object (got %.200s)", + i + 1, Py_TYPE(obj)->tp_name); + goto error; + } + PyTuple_SET_ITEM(fvarargs, i, (PyObject *)ct); + } +#if PY_MAJOR_VERSION < 3 + fabi = PyInt_AS_LONG(PyTuple_GET_ITEM(signature, 0)); +#else + fabi = PyLong_AS_LONG(PyTuple_GET_ITEM(signature, 0)); +#endif + cif_descr = fb_prepare_cif(fvarargs, fresult, fabi); + if (cif_descr == NULL) + goto error; + } + + buffer = PyObject_Malloc(cif_descr->exchange_size); + if (buffer == NULL) { + PyErr_NoMemory(); + goto error; + } + + buffer_array = (void **)buffer; + + for (i=0; i<nargs; i++) { + CTypeDescrObject *argtype; + char *data = buffer + cif_descr->exchange_offset_arg[1 + i]; + PyObject *obj = PyTuple_GET_ITEM(args, i); + + buffer_array[i] = data; + + if (i < nargs_declared) + argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(signature, 2 + i); + else + argtype = (CTypeDescrObject *)PyTuple_GET_ITEM(fvarargs, i); + + if (argtype->ct_flags & CT_POINTER) { + char *tmpbuf; + Py_ssize_t datasize = _prepare_pointer_call_argument( + argtype, obj, (char **)data); + if (datasize == 0) + ; /* successfully filled '*data' */ + else if (datasize < 0) + goto error; + else { + tmpbuf = alloca(datasize); + memset(tmpbuf, 0, datasize); + *(char **)data = tmpbuf; + if (convert_array_from_object(tmpbuf, argtype, obj) < 0) + goto error; + } + } + else if (convert_from_object(data, argtype, obj) < 0) + goto error; + } + + resultdata = buffer + cif_descr->exchange_offset_arg[0]; + /*READ(cd->c_data, sizeof(void(*)(void)))*/ + + Py_BEGIN_ALLOW_THREADS + restore_errno(); + ffi_call(&cif_descr->cif, (void (*)(void))(cd->c_data), + resultdata, buffer_array); + save_errno(); + Py_END_ALLOW_THREADS + + if (fresult->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED)) { +#ifdef WORDS_BIGENDIAN + /* For results of precisely these types, libffi has a strange + rule that they will be returned as a whole 'ffi_arg' if they + are smaller. The difference only matters on big-endian. */ + if (fresult->ct_size < sizeof(ffi_arg)) + resultdata += (sizeof(ffi_arg) - fresult->ct_size); +#endif + res = convert_to_object(resultdata, fresult); + } + else if (fresult->ct_flags & CT_VOID) { + res = Py_None; + Py_INCREF(res); + } + else if (fresult->ct_flags & CT_STRUCT) { + res = convert_struct_to_owning_object(resultdata, fresult); + } + else { + res = convert_to_object(resultdata, fresult); + } + /* fall-through */ + + error: + if (buffer) + PyObject_Free(buffer); + if (fvarargs != NULL) { + Py_DECREF(fvarargs); + if (cif_descr != NULL) /* but only if fvarargs != NULL, if variadic */ + PyObject_Free(cif_descr); + } + return res; +} + +static PyObject *cdata_dir(PyObject *cd, PyObject *noarg) +{ + CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; + + /* replace the type 'pointer-to-t' with just 't' */ + if (ct->ct_flags & CT_POINTER) { + ct = ct->ct_itemdescr; + } + if ((ct->ct_flags & (CT_STRUCT | CT_UNION)) && + !(ct->ct_flags & CT_IS_OPAQUE)) { + + /* for non-opaque structs or unions */ + if (force_lazy_struct(ct) < 0) + return NULL; + return PyDict_Keys(ct->ct_stuff); + } + else { + return PyList_New(0); /* empty list for the other cases */ + } +} + +static PyObject *cdata_complex(PyObject *cd_, PyObject *noarg) +{ + CDataObject *cd = (CDataObject *)cd_; + + if (cd->c_type->ct_flags & CT_PRIMITIVE_COMPLEX) { + Py_complex value = read_raw_complex_data(cd->c_data, cd->c_type->ct_size); + PyObject *op = PyComplex_FromCComplex(value); + return op; + } + /* <cdata 'float'> or <cdata 'int'> cannot be directly converted by + calling complex(), just like <cdata 'int'> cannot be directly + converted by calling float() */ + + PyErr_Format(PyExc_TypeError, "complex() not supported on cdata '%s'", + cd->c_type->ct_name); + return NULL; +} + +static int explicit_release_case(PyObject *cd) +{ + CTypeDescrObject *ct = ((CDataObject *)cd)->c_type; + if (Py_TYPE(cd) == &CDataOwning_Type) { + if ((ct->ct_flags & (CT_POINTER | CT_ARRAY)) != 0) /* ffi.new() */ + return 0; + } + else if (Py_TYPE(cd) == &CDataOwningGC_Type) { + if (ct->ct_flags & CT_ARRAY) /* ffi.from_buffer() */ + return 1; + } + else if (Py_TYPE(cd) == &CDataGCP_Type) { + return 2; /* ffi.gc() */ + } + PyErr_SetString(PyExc_ValueError, + "only 'cdata' object from ffi.new(), ffi.gc(), ffi.from_buffer() " + "or ffi.new_allocator()() can be used with the 'with' keyword or " + "ffi.release()"); + return -1; +} + +static PyObject *cdata_enter(PyObject *cd, PyObject *noarg) +{ + if (explicit_release_case(cd) < 0) /* only to check the ctype */ + return NULL; + Py_INCREF(cd); + return cd; +} + +static PyObject *cdata_exit(PyObject *cd, PyObject *args) +{ + /* 'args' ignored */ + CTypeDescrObject *ct; + Py_buffer *view; + switch (explicit_release_case(cd)) + { + case 0: /* ffi.new() */ + /* no effect on CPython: raw memory is allocated with the + same malloc() as the object itself, so it can't be + released independently. If we use a custom allocator, + then it's implemented with ffi.gc(). */ + ct = ((CDataObject *)cd)->c_type; + if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { + PyObject *x = ((CDataObject_own_structptr *)cd)->structobj; + if (Py_TYPE(x) == &CDataGCP_Type) { + /* this is a special case for + ffi.new_allocator()("struct-or-union") */ + cdatagcp_finalize((CDataObject_gcp *)x); + } + } + break; + + case 1: /* ffi.from_buffer() */ + view = ((CDataObject_owngc_frombuf *)cd)->bufferview; + PyBuffer_Release(view); + break; + + case 2: /* ffi.gc() or ffi.new_allocator()("not-struct-nor-union") */ + /* call the destructor immediately */ + cdatagcp_finalize((CDataObject_gcp *)cd); + break; + + default: + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *cdata_iter(CDataObject *); + +static PyNumberMethods CData_as_number = { + (binaryfunc)cdata_add, /*nb_add*/ + (binaryfunc)cdata_sub, /*nb_subtract*/ + 0, /*nb_multiply*/ +#if PY_MAJOR_VERSION < 3 + 0, /*nb_divide*/ +#endif + 0, /*nb_remainder*/ + 0, /*nb_divmod*/ + 0, /*nb_power*/ + 0, /*nb_negative*/ + 0, /*nb_positive*/ + 0, /*nb_absolute*/ + (inquiry)cdata_nonzero, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ +#if PY_MAJOR_VERSION < 3 + 0, /*nb_coerce*/ +#endif + (unaryfunc)cdata_int, /*nb_int*/ +#if PY_MAJOR_VERSION < 3 + (unaryfunc)cdata_long, /*nb_long*/ +#else + 0, +#endif + (unaryfunc)cdata_float, /*nb_float*/ + 0, /*nb_oct*/ + 0, /*nb_hex*/ +}; + +static PyMappingMethods CData_as_mapping = { + (lenfunc)cdata_length, /*mp_length*/ + (binaryfunc)cdata_subscript, /*mp_subscript*/ + (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ +}; + +static PyMappingMethods CDataOwn_as_mapping = { + (lenfunc)cdata_length, /*mp_length*/ + (binaryfunc)cdataowning_subscript, /*mp_subscript*/ + (objobjargproc)cdata_ass_sub, /*mp_ass_subscript*/ +}; + +static PyMethodDef cdata_methods[] = { + {"__dir__", cdata_dir, METH_NOARGS}, + {"__complex__", cdata_complex, METH_NOARGS}, + {"__enter__", cdata_enter, METH_NOARGS}, + {"__exit__", cdata_exit, METH_VARARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject CData_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CData", + sizeof(CDataObject), + 0, + (destructor)cdata_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)cdata_repr, /* tp_repr */ + &CData_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + &CData_as_mapping, /* tp_as_mapping */ + (hashfunc)cdata_hash, /* tp_hash */ + (ternaryfunc)cdata_call, /* tp_call */ + 0, /* tp_str */ + (getattrofunc)cdata_getattro, /* tp_getattro */ + (setattrofunc)cdata_setattro, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + cdata_richcompare, /* tp_richcompare */ + offsetof(CDataObject, c_weakreflist), /* tp_weaklistoffset */ + (getiterfunc)cdata_iter, /* tp_iter */ + 0, /* tp_iternext */ + cdata_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + PyObject_Del, /* tp_free */ +}; + +static PyTypeObject CDataOwning_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CDataOwn", + sizeof(CDataObject), + 0, + (destructor)cdataowning_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)cdataowning_repr, /* tp_repr */ + 0, /* inherited */ /* tp_as_number */ + 0, /* tp_as_sequence */ + &CDataOwn_as_mapping, /* tp_as_mapping */ + 0, /* inherited */ /* tp_hash */ + 0, /* inherited */ /* tp_call */ + 0, /* tp_str */ + 0, /* inherited */ /* tp_getattro */ + 0, /* inherited */ /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* inherited */ /* tp_richcompare */ + 0, /* inherited */ /* tp_weaklistoffset */ + 0, /* inherited */ /* tp_iter */ + 0, /* tp_iternext */ + 0, /* inherited */ /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &CData_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + free, /* tp_free */ +}; + +static PyTypeObject CDataOwningGC_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CDataOwnGC", + sizeof(CDataObject_owngc_frombuf), + 0, + (destructor)cdataowninggc_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)cdataowninggc_repr, /* tp_repr */ + 0, /* inherited */ /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* inherited */ /* tp_as_mapping */ + 0, /* inherited */ /* tp_hash */ + 0, /* inherited */ /* tp_call */ + 0, /* tp_str */ + 0, /* inherited */ /* tp_getattro */ + 0, /* inherited */ /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ + | Py_TPFLAGS_HAVE_GC, + 0, /* tp_doc */ + (traverseproc)cdataowninggc_traverse, /* tp_traverse */ + (inquiry)cdataowninggc_clear, /* tp_clear */ + 0, /* inherited */ /* tp_richcompare */ + 0, /* inherited */ /* tp_weaklistoffset */ + 0, /* inherited */ /* tp_iter */ + 0, /* tp_iternext */ + 0, /* inherited */ /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &CDataOwning_Type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + PyObject_GC_Del, /* tp_free */ +}; + +static PyTypeObject CDataGCP_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CDataGCP", + sizeof(CDataObject_gcp), + 0, + (destructor)cdatagcp_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* inherited */ /* tp_repr */ + 0, /* inherited */ /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* inherited */ /* tp_as_mapping */ + 0, /* inherited */ /* tp_hash */ + 0, /* inherited */ /* tp_call */ + 0, /* tp_str */ + 0, /* inherited */ /* tp_getattro */ + 0, /* inherited */ /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES /* tp_flags */ +#ifdef Py_TPFLAGS_HAVE_FINALIZE + | Py_TPFLAGS_HAVE_FINALIZE +#endif + | Py_TPFLAGS_HAVE_GC, + 0, /* tp_doc */ + (traverseproc)cdatagcp_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* inherited */ /* tp_richcompare */ + 0, /* inherited */ /* tp_weaklistoffset */ + 0, /* inherited */ /* tp_iter */ + 0, /* tp_iternext */ + 0, /* inherited */ /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + &CData_Type, /* tp_base */ +#ifdef Py_TPFLAGS_HAVE_FINALIZE /* CPython >= 3.4 */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* inherited */ /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* version_tag */ + (destructor)cdatagcp_finalize, /* tp_finalize */ +#endif +}; + +/************************************************************/ + +typedef struct { + PyObject_HEAD + char *di_next, *di_stop; + CDataObject *di_object; + CTypeDescrObject *di_itemtype; +} CDataIterObject; + +static PyObject * +cdataiter_next(CDataIterObject *it) +{ + char *result = it->di_next; + if (result != it->di_stop) { + it->di_next = result + it->di_itemtype->ct_size; + return convert_to_object(result, it->di_itemtype); + } + return NULL; +} + +static void +cdataiter_dealloc(CDataIterObject *it) +{ + Py_DECREF(it->di_object); + PyObject_Del(it); +} + +static PyTypeObject CDataIter_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.CDataIter", /* tp_name */ + sizeof(CDataIterObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)cdataiter_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, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)cdataiter_next, /* tp_iternext */ +}; + +static PyObject * +cdata_iter(CDataObject *cd) +{ + CDataIterObject *it; + + if (!(cd->c_type->ct_flags & CT_ARRAY)) { + PyErr_Format(PyExc_TypeError, "cdata '%s' does not support iteration", + cd->c_type->ct_name); + return NULL; + } + + it = PyObject_New(CDataIterObject, &CDataIter_Type); + if (it == NULL) + return NULL; + + Py_INCREF(cd); + it->di_object = cd; + it->di_itemtype = cd->c_type->ct_itemdescr; + it->di_next = cd->c_data; + it->di_stop = cd->c_data + get_array_length(cd) * it->di_itemtype->ct_size; + return (PyObject *)it; +} + +/************************************************************/ + +static CDataObject *allocate_owning_object(Py_ssize_t size, + CTypeDescrObject *ct, + int dont_clear) +{ + /* note: objects with &CDataOwning_Type are always allocated with + either a plain malloc() or calloc(), and freed with free(). */ + CDataObject *cd; + if (dont_clear) + cd = malloc(size); + else + cd = calloc(size, 1); + if (PyObject_Init((PyObject *)cd, &CDataOwning_Type) == NULL) + return NULL; + + Py_INCREF(ct); + cd->c_type = ct; + cd->c_weakreflist = NULL; + return cd; +} + +static PyObject * +convert_struct_to_owning_object(char *data, CTypeDescrObject *ct) +{ + /* also accepts unions, for the API mode */ + CDataObject *cd; + Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment); + Py_ssize_t datasize = ct->ct_size; + + if (datasize < 0) { + PyErr_SetString(PyExc_TypeError, + "return type is an opaque structure or union"); + return NULL; + } + if (ct->ct_flags & CT_WITH_VAR_ARRAY) { + PyErr_SetString(PyExc_TypeError, + "return type is a struct/union with a varsize array member"); + return NULL; + } + cd = allocate_owning_object(dataoffset + datasize, ct, /*dont_clear=*/1); + if (cd == NULL) + return NULL; + cd->c_data = ((char *)cd) + dataoffset; + + memcpy(cd->c_data, data, datasize); + return (PyObject *)cd; +} + +static CDataObject *allocate_gcp_object(CDataObject *origobj, + CTypeDescrObject *ct, + PyObject *destructor) +{ + CDataObject_gcp *cd = PyObject_GC_New(CDataObject_gcp, &CDataGCP_Type); + if (cd == NULL) + return NULL; + + Py_XINCREF(destructor); + Py_INCREF(origobj); + Py_INCREF(ct); + cd->head.c_data = origobj->c_data; + cd->head.c_type = ct; + cd->head.c_weakreflist = NULL; + cd->origobj = (PyObject *)origobj; + cd->destructor = destructor; + + PyObject_GC_Track(cd); + return (CDataObject *)cd; +} + +static CDataObject *allocate_with_allocator(Py_ssize_t basesize, + Py_ssize_t datasize, + CTypeDescrObject *ct, + const cffi_allocator_t *allocator) +{ + CDataObject *cd; + + if (allocator->ca_alloc == NULL) { + cd = allocate_owning_object(basesize + datasize, ct, + allocator->ca_dont_clear); + if (cd == NULL) + return NULL; + cd->c_data = ((char *)cd) + basesize; + } + else { + PyObject *res = PyObject_CallFunction(allocator->ca_alloc, "n", datasize); + if (res == NULL) + return NULL; + + if (!CData_Check(res)) { + PyErr_Format(PyExc_TypeError, + "alloc() must return a cdata object (got %.200s)", + Py_TYPE(res)->tp_name); + Py_DECREF(res); + return NULL; + } + cd = (CDataObject *)res; + if (!(cd->c_type->ct_flags & (CT_POINTER|CT_ARRAY))) { + PyErr_Format(PyExc_TypeError, + "alloc() must return a cdata pointer, not '%s'", + cd->c_type->ct_name); + Py_DECREF(res); + return NULL; + } + if (!cd->c_data) { + PyErr_SetString(PyExc_MemoryError, "alloc() returned NULL"); + Py_DECREF(res); + return NULL; + } + + cd = allocate_gcp_object(cd, ct, allocator->ca_free); + Py_DECREF(res); + if (!allocator->ca_dont_clear) + memset(cd->c_data, 0, datasize); + } + return cd; +} + +static PyObject *direct_newp(CTypeDescrObject *ct, PyObject *init, + const cffi_allocator_t *allocator) +{ + CTypeDescrObject *ctitem; + CDataObject *cd; + Py_ssize_t dataoffset, datasize, explicitlength; + + explicitlength = -1; + if (ct->ct_flags & CT_POINTER) { + dataoffset = offsetof(CDataObject_own_nolength, alignment); + ctitem = ct->ct_itemdescr; + datasize = ctitem->ct_size; + if (datasize < 0) { + PyErr_Format(PyExc_TypeError, + "cannot instantiate ctype '%s' of unknown size", + ctitem->ct_name); + return NULL; + } + if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) + datasize *= 2; /* forcefully add another character: a null */ + + if (ctitem->ct_flags & (CT_STRUCT | CT_UNION)) { + if (force_lazy_struct(ctitem) < 0) /* for CT_WITH_VAR_ARRAY */ + return NULL; + + if (ctitem->ct_flags & CT_WITH_VAR_ARRAY) { + assert(ct->ct_flags & CT_IS_PTR_TO_OWNED); + dataoffset = offsetof(CDataObject_own_length, alignment); + + if (init != Py_None) { + Py_ssize_t optvarsize = datasize; + if (convert_struct_from_object(NULL, ctitem, init, + &optvarsize) < 0) + return NULL; + datasize = optvarsize; + } + } + } + } + else if (ct->ct_flags & CT_ARRAY) { + dataoffset = offsetof(CDataObject_own_nolength, alignment); + datasize = ct->ct_size; + if (datasize < 0) { + explicitlength = get_new_array_length(ct->ct_itemdescr, &init); + if (explicitlength < 0) + return NULL; + ctitem = ct->ct_itemdescr; + dataoffset = offsetof(CDataObject_own_length, alignment); + datasize = MUL_WRAPAROUND(explicitlength, ctitem->ct_size); + if (explicitlength > 0 && + (datasize / explicitlength) != ctitem->ct_size) { + PyErr_SetString(PyExc_OverflowError, + "array size would overflow a Py_ssize_t"); + return NULL; + } + } + } + else { + PyErr_Format(PyExc_TypeError, + "expected a pointer or array ctype, got '%s'", + ct->ct_name); + return NULL; + } + + if (ct->ct_flags & CT_IS_PTR_TO_OWNED) { + /* common case of ptr-to-struct (or ptr-to-union): for this case + we build two objects instead of one, with the memory-owning + one being really the struct (or union) and the returned one + having a strong reference to it */ + CDataObject *cds; + + cds = allocate_with_allocator(dataoffset, datasize, ct->ct_itemdescr, + allocator); + if (cds == NULL) + return NULL; + + cd = allocate_owning_object(sizeof(CDataObject_own_structptr), ct, + /*dont_clear=*/1); + if (cd == NULL) { + Py_DECREF(cds); + return NULL; + } + /* store the only reference to cds into cd */ + ((CDataObject_own_structptr *)cd)->structobj = (PyObject *)cds; + /* store information about the allocated size of the struct */ + if (dataoffset == offsetof(CDataObject_own_length, alignment)) { + ((CDataObject_own_length *)cds)->length = datasize; + } + assert(explicitlength < 0); + + cd->c_data = cds->c_data; + } + else { + cd = allocate_with_allocator(dataoffset, datasize, ct, allocator); + if (cd == NULL) + return NULL; + + if (explicitlength >= 0) + ((CDataObject_own_length*)cd)->length = explicitlength; + } + + if (init != Py_None) { + if (convert_from_object(cd->c_data, + (ct->ct_flags & CT_POINTER) ? ct->ct_itemdescr : ct, init) < 0) { + Py_DECREF(cd); + return NULL; + } + } + return (PyObject *)cd; +} + +static PyObject *b_newp(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *init = Py_None; + if (!PyArg_ParseTuple(args, "O!|O:newp", &CTypeDescr_Type, &ct, &init)) + return NULL; + return direct_newp(ct, init, &default_allocator); +} + +static int +_my_PyObject_AsBool(PyObject *ob) +{ + /* convert and cast a Python object to a boolean. Accept an integer + or a float object, up to a CData 'long double'. */ + PyObject *io; + PyNumberMethods *nb; + int res; + +#if PY_MAJOR_VERSION < 3 + if (PyInt_Check(ob)) { + return PyInt_AS_LONG(ob) != 0; + } + else +#endif + if (PyLong_Check(ob)) { + return _PyLong_Sign(ob) != 0; + } + else if (PyFloat_Check(ob)) { + return PyFloat_AS_DOUBLE(ob) != 0.0; + } + else if (CData_Check(ob)) { + CDataObject *cd = (CDataObject *)ob; + if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) { + /*READ(cd->c_data, cd->c_type->ct_size)*/ + if (cd->c_type->ct_flags & CT_IS_LONGDOUBLE) { + /* 'long double' objects: return the answer directly */ + return read_raw_longdouble_data(cd->c_data) != 0.0; + } + else { + /* 'float'/'double' objects: return the answer directly */ + return read_raw_float_data(cd->c_data, + cd->c_type->ct_size) != 0.0; + } + } + } + nb = ob->ob_type->tp_as_number; + if (nb == NULL || (nb->nb_float == NULL && nb->nb_int == NULL)) { + PyErr_SetString(PyExc_TypeError, "integer/float expected"); + return -1; + } + if (nb->nb_float && !CData_Check(ob)) + io = (*nb->nb_float) (ob); + else + io = (*nb->nb_int) (ob); + if (io == NULL) + return -1; + + if (PyIntOrLong_Check(io) || PyFloat_Check(io)) { + res = _my_PyObject_AsBool(io); + } + else { + PyErr_SetString(PyExc_TypeError, "integer/float conversion failed"); + res = -1; + } + Py_DECREF(io); + return res; +} + +static CDataObject *_new_casted_primitive(CTypeDescrObject *ct) +{ + int dataoffset = offsetof(CDataObject_casted_primitive, alignment); + CDataObject *cd = (CDataObject *)PyObject_Malloc(dataoffset + ct->ct_size); + if (PyObject_Init((PyObject *)cd, &CData_Type) == NULL) + return NULL; + Py_INCREF(ct); + cd->c_type = ct; + cd->c_data = ((char*)cd) + dataoffset; + cd->c_weakreflist = NULL; + return cd; +} + +static CDataObject *cast_to_integer_or_char(CTypeDescrObject *ct, PyObject *ob) +{ + unsigned PY_LONG_LONG value; + CDataObject *cd; + + if (CData_Check(ob) && + ((CDataObject *)ob)->c_type->ct_flags & + (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { + value = (Py_intptr_t)((CDataObject *)ob)->c_data; + } +#if PY_MAJOR_VERSION < 3 + else if (PyString_Check(ob)) { + if (PyString_GET_SIZE(ob) != 1) { + PyErr_Format(PyExc_TypeError, + "cannot cast string of length %zd to ctype '%s'", + PyString_GET_SIZE(ob), ct->ct_name); + return NULL; + } + value = (unsigned char)PyString_AS_STRING(ob)[0]; + } +#endif + else if (PyUnicode_Check(ob)) { + char err_buf[80]; + cffi_char32_t ordinal; + if (_my_PyUnicode_AsSingleChar32(ob, &ordinal, err_buf) < 0) { + PyErr_Format(PyExc_TypeError, + "cannot cast %s to ctype '%s'", err_buf, ct->ct_name); + return NULL; + } + /* the types char16_t and char32_t are both unsigned. However, + wchar_t might be signed. In theory it does not matter, + because 'ordinal' comes from a regular Python unicode. */ +#ifdef HAVE_WCHAR_H + if (ct->ct_flags & CT_IS_SIGNED_WCHAR) + value = (wchar_t)ordinal; + else +#endif + value = ordinal; + } + else if (PyBytes_Check(ob)) { + int res = _convert_to_char(ob); + if (res < 0) + return NULL; + value = (unsigned char)res; + } + else if (ct->ct_flags & CT_IS_BOOL) { + int res = _my_PyObject_AsBool(ob); + if (res < 0) + return NULL; + value = res; + } + else { + value = _my_PyLong_AsUnsignedLongLong(ob, 0); + if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + return NULL; + } + if (ct->ct_flags & CT_IS_BOOL) + value = !!value; + cd = _new_casted_primitive(ct); + if (cd != NULL) + write_raw_integer_data(cd->c_data, value, ct->ct_size); + return cd; +} + +/* returns -1 if cannot cast, 0 if we don't get a value, 1 if we do */ +static int check_bytes_for_float_compatible(PyObject *io, double *out_value) +{ + if (PyBytes_Check(io)) { + if (PyBytes_GET_SIZE(io) != 1) + goto error; + *out_value = (unsigned char)PyBytes_AS_STRING(io)[0]; + return 1; + } + else if (PyUnicode_Check(io)) { + char ignored[80]; + cffi_char32_t ordinal; + if (_my_PyUnicode_AsSingleChar32(io, &ordinal, ignored) < 0) + goto error; + /* the signness of the 32-bit version of wide chars should not + * matter here, because 'ordinal' comes from a normal Python + * unicode string */ + *out_value = ordinal; + return 1; + } + *out_value = 0; /* silence a gcc warning if this function is inlined */ + return 0; + + error: + Py_DECREF(io); + *out_value = 0; /* silence a gcc warning if this function is inlined */ + return -1; +} + +static PyObject *do_cast(CTypeDescrObject *ct, PyObject *ob) +{ + CDataObject *cd; + + if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY) && + ct->ct_size >= 0) { + /* cast to a pointer, to a funcptr, or to an array. + Note that casting to an array is an extension to the C language, + which seems to be necessary in order to sanely get a + <cdata 'int[3]'> at some address. */ + unsigned PY_LONG_LONG value; + + if (CData_Check(ob)) { + CDataObject *cdsrc = (CDataObject *)ob; + if (cdsrc->c_type->ct_flags & + (CT_POINTER|CT_FUNCTIONPTR|CT_ARRAY)) { + return new_simple_cdata(cdsrc->c_data, ct); + } + } + if ((ct->ct_flags & CT_POINTER) && + (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && + PyFile_Check(ob)) { + FILE *f = PyFile_AsFile(ob); + if (f == NULL && PyErr_Occurred()) + return NULL; + return new_simple_cdata((char *)f, ct); + } + value = _my_PyLong_AsUnsignedLongLong(ob, 0); + if (value == (unsigned PY_LONG_LONG)-1 && PyErr_Occurred()) + return NULL; + return new_simple_cdata((char *)(Py_intptr_t)value, ct); + } + else if (ct->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED + |CT_PRIMITIVE_CHAR)) { + /* cast to an integer type or a char */ + return (PyObject *)cast_to_integer_or_char(ct, ob); + } + else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) { + /* cast to a float */ + double value; + PyObject *io; + int res; + + if (CData_Check(ob)) { + CDataObject *cdsrc = (CDataObject *)ob; + + if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) + goto cannot_cast; + io = convert_to_object(cdsrc->c_data, cdsrc->c_type); + if (io == NULL) + return NULL; + } + else { + io = ob; + Py_INCREF(io); + } + + res = check_bytes_for_float_compatible(io, &value); + if (res == -1) + goto cannot_cast; + if (res == 0) { + if ((ct->ct_flags & CT_IS_LONGDOUBLE) && + CData_Check(io) && + (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + long double lvalue; + char *data = ((CDataObject *)io)->c_data; + /*READ(data, sizeof(long double)*/ + lvalue = read_raw_longdouble_data(data); + Py_DECREF(io); + cd = _new_casted_primitive(ct); + if (cd != NULL) + write_raw_longdouble_data(cd->c_data, lvalue); + return (PyObject *)cd; + } + value = PyFloat_AsDouble(io); + } + Py_DECREF(io); + if (value == -1.0 && PyErr_Occurred()) + return NULL; + + cd = _new_casted_primitive(ct); + if (cd != NULL) { + if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) + write_raw_float_data(cd->c_data, value, ct->ct_size); + else + write_raw_longdouble_data(cd->c_data, (long double)value); + } + return (PyObject *)cd; + } + else if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) { + /* cast to a complex */ + Py_complex value; + PyObject *io; + int res; + + if (CData_Check(ob)) { + CDataObject *cdsrc = (CDataObject *)ob; + + if (!(cdsrc->c_type->ct_flags & CT_PRIMITIVE_ANY)) + goto cannot_cast; + io = convert_to_object(cdsrc->c_data, cdsrc->c_type); + if (io == NULL) + return NULL; + } + else { + io = ob; + Py_INCREF(io); + } + + res = check_bytes_for_float_compatible(io, &value.real); + if (res == -1) + goto cannot_cast; + if (res == 1) { + // got it from string + value.imag = 0.0; + } else { + value = PyComplex_AsCComplex(io); + } + Py_DECREF(io); + if (PyErr_Occurred()) { + return NULL; + } + cd = _new_casted_primitive(ct); + if (cd != NULL) { + write_raw_complex_data(cd->c_data, value, ct->ct_size); + } + return (PyObject *)cd; + } + else { + PyErr_Format(PyExc_TypeError, "cannot cast to ctype '%s'", + ct->ct_name); + return NULL; + } + + cannot_cast: + if (CData_Check(ob)) + PyErr_Format(PyExc_TypeError, "cannot cast ctype '%s' to ctype '%s'", + ((CDataObject *)ob)->c_type->ct_name, ct->ct_name); + else + PyErr_Format(PyExc_TypeError, + "cannot cast %.200s object to ctype '%s'", + Py_TYPE(ob)->tp_name, ct->ct_name); + return NULL; +} + +static PyObject *b_cast(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *ob; + if (!PyArg_ParseTuple(args, "O!O:cast", &CTypeDescr_Type, &ct, &ob)) + return NULL; + + return do_cast(ct, ob); +} + +/************************************************************/ + +typedef struct { + PyObject_HEAD + void *dl_handle; + char *dl_name; +} DynLibObject; + +static void dl_dealloc(DynLibObject *dlobj) +{ + if (dlobj->dl_handle != NULL) + dlclose(dlobj->dl_handle); + free(dlobj->dl_name); + PyObject_Del(dlobj); +} + +static PyObject *dl_repr(DynLibObject *dlobj) +{ + return PyText_FromFormat("<clibrary '%s'>", dlobj->dl_name); +} + +static int dl_check_closed(DynLibObject *dlobj) +{ + if (dlobj->dl_handle == NULL) + { + PyErr_Format(PyExc_ValueError, "library '%s' has already been closed", + dlobj->dl_name); + return -1; + } + return 0; +} + +static PyObject *dl_load_function(DynLibObject *dlobj, PyObject *args) +{ + CTypeDescrObject *ct; + char *funcname; + void *funcptr; + + if (!PyArg_ParseTuple(args, "O!s:load_function", + &CTypeDescr_Type, &ct, &funcname)) + return NULL; + + if (dl_check_closed(dlobj) < 0) + return NULL; + + if (!(ct->ct_flags & (CT_FUNCTIONPTR | CT_POINTER | CT_ARRAY))) { + PyErr_Format(PyExc_TypeError, + "function or pointer or array cdata expected, got '%s'", + ct->ct_name); + return NULL; + } + dlerror(); /* clear error condition */ + funcptr = dlsym(dlobj->dl_handle, funcname); + if (funcptr == NULL) { + const char *error = dlerror(); + PyErr_Format(PyExc_AttributeError, + "function/symbol '%s' not found in library '%s': %s", + funcname, dlobj->dl_name, error); + return NULL; + } + + if ((ct->ct_flags & CT_ARRAY) && ct->ct_length < 0) { + ct = (CTypeDescrObject *)ct->ct_stuff; + } + return new_simple_cdata(funcptr, ct); +} + +static PyObject *dl_read_variable(DynLibObject *dlobj, PyObject *args) +{ + CTypeDescrObject *ct; + char *varname; + char *data; + + if (!PyArg_ParseTuple(args, "O!s:read_variable", + &CTypeDescr_Type, &ct, &varname)) + return NULL; + + if (dl_check_closed(dlobj) < 0) + return NULL; + + dlerror(); /* clear error condition */ + data = dlsym(dlobj->dl_handle, varname); + if (data == NULL) { + const char *error = dlerror(); + if (error != NULL) { + PyErr_Format(PyExc_KeyError, + "variable '%s' not found in library '%s': %s", + varname, dlobj->dl_name, error); + return NULL; + } + } + return convert_to_object(data, ct); +} + +static PyObject *dl_write_variable(DynLibObject *dlobj, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *value; + char *varname; + char *data; + + if (!PyArg_ParseTuple(args, "O!sO:write_variable", + &CTypeDescr_Type, &ct, &varname, &value)) + return NULL; + + if (dl_check_closed(dlobj) < 0) + return NULL; + + dlerror(); /* clear error condition */ + data = dlsym(dlobj->dl_handle, varname); + if (data == NULL) { + const char *error = dlerror(); + PyErr_Format(PyExc_KeyError, + "variable '%s' not found in library '%s': %s", + varname, dlobj->dl_name, error); + return NULL; + } + if (convert_from_object(data, ct, value) < 0) + return NULL; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *dl_close_lib(DynLibObject *dlobj, PyObject *no_args) +{ + if (dlobj->dl_handle != NULL) + { + dlclose(dlobj->dl_handle); + dlobj->dl_handle = NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef dl_methods[] = { + {"load_function", (PyCFunction)dl_load_function, METH_VARARGS}, + {"read_variable", (PyCFunction)dl_read_variable, METH_VARARGS}, + {"write_variable", (PyCFunction)dl_write_variable, METH_VARARGS}, + {"close_lib", (PyCFunction)dl_close_lib, METH_NOARGS}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject dl_type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.Library", /* tp_name */ + sizeof(DynLibObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)dl_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)dl_repr, /* 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, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + dl_methods, /* tp_methods */ +}; + +static void *b_do_dlopen(PyObject *args, const char **p_printable_filename, + PyObject **p_temp) +{ + /* Logic to call the correct version of dlopen(). Returns NULL in case of error. + Otherwise, '*p_printable_filename' will point to a printable char version of + the filename (maybe utf-8-encoded). '*p_temp' will be set either to NULL or + to a temporary object that must be freed after looking at printable_filename. + */ + void *handle; + char *filename_or_null; + int flags = 0; + *p_temp = NULL; + + if (PyTuple_GET_SIZE(args) == 0 || PyTuple_GET_ITEM(args, 0) == Py_None) { + PyObject *dummy; + if (!PyArg_ParseTuple(args, "|Oi:load_library", + &dummy, &flags)) + return NULL; + filename_or_null = NULL; + *p_printable_filename = "<None>"; + } + else + { + PyObject *s = PyTuple_GET_ITEM(args, 0); +#ifdef MS_WIN32 + Py_UNICODE *filenameW; + if (PyArg_ParseTuple(args, "u|i:load_library", &filenameW, &flags)) + { +#if PY_MAJOR_VERSION < 3 + s = PyUnicode_AsUTF8String(s); + if (s == NULL) + return NULL; + *p_temp = s; +#endif + *p_printable_filename = PyText_AsUTF8(s); + if (*p_printable_filename == NULL) + return NULL; + + handle = dlopenW(filenameW); + goto got_handle; + } + PyErr_Clear(); +#endif + if (!PyArg_ParseTuple(args, "et|i:load_library", + Py_FileSystemDefaultEncoding, &filename_or_null, &flags)) + return NULL; +#if PY_MAJOR_VERSION < 3 + if (PyUnicode_Check(s)) + { + s = PyUnicode_AsUTF8String(s); + if (s == NULL) + return NULL; + *p_temp = s; + } +#endif + *p_printable_filename = PyText_AsUTF8(s); + if (*p_printable_filename == NULL) + return NULL; + } + if ((flags & (RTLD_NOW | RTLD_LAZY)) == 0) + flags |= RTLD_NOW; + + handle = dlopen(filename_or_null, flags); + +#ifdef MS_WIN32 + got_handle: +#endif + if (handle == NULL) { + const char *error = dlerror(); + PyErr_Format(PyExc_OSError, "cannot load library '%s': %s", + *p_printable_filename, error); + return NULL; + } + return handle; +} + +static PyObject *b_load_library(PyObject *self, PyObject *args) +{ + const char *printable_filename; + PyObject *temp; + void *handle; + DynLibObject *dlobj = NULL; + + handle = b_do_dlopen(args, &printable_filename, &temp); + if (handle == NULL) + goto error; + + dlobj = PyObject_New(DynLibObject, &dl_type); + if (dlobj == NULL) { + dlclose(handle); + goto error; + } + dlobj->dl_handle = handle; + dlobj->dl_name = strdup(printable_filename); + + error: + Py_XDECREF(temp); + return (PyObject *)dlobj; +} + +/************************************************************/ + +static PyObject *get_unique_type(CTypeDescrObject *x, + const void *unique_key[], long keylength) +{ + /* Replace the CTypeDescrObject 'x' with a standardized one. + This either just returns x, or x is decrefed and a new reference + to the already-existing equivalent is returned. + + In this function, 'x' always contains a reference that must be + either decrefed or returned. + + Keys: + void ["void"] + primitive [&static_struct] + pointer [ctype] + array [ctype, length] + funcptr [ctresult, ellipsis+abi, num_args, ctargs...] + */ + PyObject *key, *y; + void *pkey; + + key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *)); + if (key == NULL) + goto error; + + pkey = PyBytes_AS_STRING(key); + memcpy(pkey, unique_key, keylength * sizeof(void *)); + + y = PyDict_GetItem(unique_cache, key); + if (y != NULL) { + Py_DECREF(key); + Py_INCREF(y); + Py_DECREF(x); + return y; + } + if (PyDict_SetItem(unique_cache, key, (PyObject *)x) < 0) { + Py_DECREF(key); + goto error; + } + /* Haaaack for our reference count hack: gcmodule.c must not see this + dictionary. The problem is that any PyDict_SetItem() notices that + 'x' is tracked and re-tracks the unique_cache dictionary. So here + we re-untrack it again... */ + PyObject_GC_UnTrack(unique_cache); + + assert(x->ct_unique_key == NULL); + x->ct_unique_key = key; /* the key will be freed in ctypedescr_dealloc() */ + /* the 'value' in unique_cache doesn't count as 1, but don't use + Py_DECREF(x) here because it will confuse debug builds into thinking + there was an extra DECREF in total. */ + ((PyObject *)x)->ob_refcnt--; + return (PyObject *)x; + + error: + Py_DECREF(x); + return NULL; +} + +/* according to the C standard, these types should be equivalent to the + _Complex types for the purposes of storage (not arguments in calls!) */ +typedef float cffi_float_complex_t[2]; +typedef double cffi_double_complex_t[2]; + +static PyObject *new_primitive_type(const char *name) +{ +#define ENUM_PRIMITIVE_TYPES \ + EPTYPE(c, char, CT_PRIMITIVE_CHAR) \ + EPTYPE(s, short, CT_PRIMITIVE_SIGNED ) \ + EPTYPE(i, int, CT_PRIMITIVE_SIGNED ) \ + EPTYPE(l, long, CT_PRIMITIVE_SIGNED ) \ + EPTYPE(ll, long long, CT_PRIMITIVE_SIGNED ) \ + EPTYPE(sc, signed char, CT_PRIMITIVE_SIGNED ) \ + EPTYPE(uc, unsigned char, CT_PRIMITIVE_UNSIGNED ) \ + EPTYPE(us, unsigned short, CT_PRIMITIVE_UNSIGNED ) \ + EPTYPE(ui, unsigned int, CT_PRIMITIVE_UNSIGNED ) \ + EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED ) \ + EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED ) \ + EPTYPE(f, float, CT_PRIMITIVE_FLOAT ) \ + EPTYPE(d, double, CT_PRIMITIVE_FLOAT ) \ + EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE ) \ + EPTYPE2(fc, "float _Complex", cffi_float_complex_t, CT_PRIMITIVE_COMPLEX ) \ + EPTYPE2(dc, "double _Complex", cffi_double_complex_t, CT_PRIMITIVE_COMPLEX ) \ + ENUM_PRIMITIVE_TYPES_WCHAR \ + EPTYPE2(c16, "char16_t", cffi_char16_t, CT_PRIMITIVE_CHAR ) \ + EPTYPE2(c32, "char32_t", cffi_char32_t, CT_PRIMITIVE_CHAR ) \ + EPTYPE(b, _Bool, CT_PRIMITIVE_UNSIGNED | CT_IS_BOOL ) \ + /* the following types are not primitive in the C sense */ \ + EPTYPE(i8, int8_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(u8, uint8_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(i16, int16_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(u16, uint16_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(i32, int32_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(u32, uint32_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(i64, int64_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(u64, uint64_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il8, int_least8_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul8, uint_least8_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il16, int_least16_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul16, uint_least16_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il32, int_least32_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul32, uint_least32_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(il64, int_least64_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(ul64, uint_least64_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if8, int_fast8_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf8, uint_fast8_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if16, int_fast16_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf16, uint_fast16_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if32, int_fast32_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf32, uint_fast32_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(if64, int_fast64_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(uf64, uint_fast64_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(ip, intptr_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(up, uintptr_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(im, intmax_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(um, uintmax_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE(pd, ptrdiff_t, CT_PRIMITIVE_SIGNED) \ + EPTYPE(sz, size_t, CT_PRIMITIVE_UNSIGNED) \ + EPTYPE2(ssz, "ssize_t", Py_ssize_t, CT_PRIMITIVE_SIGNED) + +#ifdef HAVE_WCHAR_H +# define ENUM_PRIMITIVE_TYPES_WCHAR \ + EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR | \ + (((wchar_t)-1) > 0 ? 0 : CT_IS_SIGNED_WCHAR)) +#else +# define ENUM_PRIMITIVE_TYPES_WCHAR /* nothing */ +#endif + +#define EPTYPE(code, typename, flags) EPTYPE2(code, #typename, typename, flags) + +#define EPTYPE2(code, export_name, typename, flags) \ + struct aligncheck_##code { char x; typename y; }; + ENUM_PRIMITIVE_TYPES +#undef EPTYPE2 + + CTypeDescrObject *td; + static const struct descr_s { const char *name; int size, align, flags; } + types[] = { +#define EPTYPE2(code, export_name, typename, flags) \ + { export_name, \ + sizeof(typename), \ + offsetof(struct aligncheck_##code, y), \ + flags \ + }, + ENUM_PRIMITIVE_TYPES +#undef EPTYPE2 +#undef EPTYPE +#undef ENUM_PRIMITIVE_TYPES_WCHAR +#undef ENUM_PRIMITIVE_TYPES + { NULL } + }; + const struct descr_s *ptypes; + const void *unique_key[1]; + int name_size; + ffi_type *ffitype; + + for (ptypes=types; ; ptypes++) { + if (ptypes->name == NULL) { +#ifndef HAVE_WCHAR_H + if (strcmp(name, "wchar_t")) + PyErr_SetString(PyExc_NotImplementedError, name); + else +#endif + PyErr_SetString(PyExc_KeyError, name); + return NULL; + } + if (strcmp(name, ptypes->name) == 0) + break; + } + + if (ptypes->flags & CT_PRIMITIVE_SIGNED) { + switch (ptypes->size) { + case 1: ffitype = &ffi_type_sint8; break; + case 2: ffitype = &ffi_type_sint16; break; + case 4: ffitype = &ffi_type_sint32; break; + case 8: ffitype = &ffi_type_sint64; break; + default: goto bad_ffi_type; + } + } + else if (ptypes->flags & CT_PRIMITIVE_FLOAT) { + if (strcmp(ptypes->name, "float") == 0) + ffitype = &ffi_type_float; + else if (strcmp(ptypes->name, "double") == 0) + ffitype = &ffi_type_double; + else if (strcmp(ptypes->name, "long double") == 0) { + /* assume that if sizeof(double) == sizeof(long double), then + the two types are equivalent for C. libffi bugs on Win64 + if a function's return type is ffi_type_longdouble... */ + if (sizeof(double) == sizeof(long double)) + ffitype = &ffi_type_double; + else + ffitype = &ffi_type_longdouble; + } + else + goto bad_ffi_type; + } + else if (ptypes->flags & CT_PRIMITIVE_COMPLEX) { + /* As of March 2017, still no libffi support for complex. + It fails silently if we try to use ffi_type_complex_float + or ffi_type_complex_double. Better not use it at all. + */ + ffitype = NULL; + } + else { + switch (ptypes->size) { + case 1: ffitype = &ffi_type_uint8; break; + case 2: ffitype = &ffi_type_uint16; break; + case 4: ffitype = &ffi_type_uint32; break; + case 8: ffitype = &ffi_type_uint64; break; + default: goto bad_ffi_type; + } + } + + name_size = strlen(ptypes->name) + 1; + td = ctypedescr_new(name_size); + if (td == NULL) + return NULL; + + memcpy(td->ct_name, name, name_size); + td->ct_size = ptypes->size; + td->ct_length = ptypes->align; + td->ct_extra = ffitype; + td->ct_flags = ptypes->flags; + if (td->ct_flags & (CT_PRIMITIVE_SIGNED | CT_PRIMITIVE_CHAR)) { + if (td->ct_size <= (Py_ssize_t)sizeof(long)) + td->ct_flags |= CT_PRIMITIVE_FITS_LONG; + } + else if (td->ct_flags & CT_PRIMITIVE_UNSIGNED) { + if (td->ct_size < (Py_ssize_t)sizeof(long)) + td->ct_flags |= CT_PRIMITIVE_FITS_LONG; + } + td->ct_name_position = strlen(td->ct_name); + unique_key[0] = ptypes; + return get_unique_type(td, unique_key, 1); + + bad_ffi_type: + PyErr_Format(PyExc_NotImplementedError, + "primitive type '%s' has size %d; " + "the supported sizes are 1, 2, 4, 8", + name, (int)ptypes->size); + return NULL; +} + +static PyObject *b_new_primitive_type(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:new_primitive_type", &name)) + return NULL; + return new_primitive_type(name); +} + +static PyObject *new_pointer_type(CTypeDescrObject *ctitem) +{ + CTypeDescrObject *td; + const char *extra; + const void *unique_key[1]; + + if (ctitem->ct_flags & CT_ARRAY) + extra = "(*)"; /* obscure case: see test_array_add */ + else + extra = " *"; + td = ctypedescr_new_on_top(ctitem, extra, 2); + if (td == NULL) + return NULL; + + td->ct_size = sizeof(void *); + td->ct_length = -1; + td->ct_flags = CT_POINTER; + if (ctitem->ct_flags & (CT_STRUCT|CT_UNION)) + td->ct_flags |= CT_IS_PTR_TO_OWNED; + if (ctitem->ct_flags & CT_VOID) + td->ct_flags |= CT_IS_VOID_PTR; + if ((ctitem->ct_flags & CT_VOID) || + ((ctitem->ct_flags & CT_PRIMITIVE_CHAR) && + ctitem->ct_size == sizeof(char))) + td->ct_flags |= CT_IS_VOIDCHAR_PTR; /* 'void *' or 'char *' only */ + unique_key[0] = ctitem; + return get_unique_type(td, unique_key, 1); +} + +static PyObject *b_new_pointer_type(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ctitem; + if (!PyArg_ParseTuple(args, "O!:new_pointer_type", + &CTypeDescr_Type, &ctitem)) + return NULL; + return new_pointer_type(ctitem); +} + +static PyObject *b_new_array_type(PyObject *self, PyObject *args) +{ + PyObject *lengthobj; + Py_ssize_t length; + CTypeDescrObject *ctptr; + + if (!PyArg_ParseTuple(args, "O!O:new_array_type", + &CTypeDescr_Type, &ctptr, &lengthobj)) + return NULL; + + if (lengthobj == Py_None) { + length = -1; + } + else { + length = PyNumber_AsSsize_t(lengthobj, PyExc_OverflowError); + if (length < 0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, "negative array length"); + return NULL; + } + } + return new_array_type(ctptr, length); +} + +static PyObject * +new_array_type(CTypeDescrObject *ctptr, Py_ssize_t length) +{ + CTypeDescrObject *td, *ctitem; + char extra_text[32]; + Py_ssize_t arraysize; + int flags = CT_ARRAY; + const void *unique_key[2]; + + if (!(ctptr->ct_flags & CT_POINTER)) { + PyErr_SetString(PyExc_TypeError, "first arg must be a pointer ctype"); + return NULL; + } + ctitem = ctptr->ct_itemdescr; + if (ctitem->ct_size < 0) { + PyErr_Format(PyExc_ValueError, "array item of unknown size: '%s'", + ctitem->ct_name); + return NULL; + } + + if (length < 0) { + sprintf(extra_text, "[]"); + length = -1; + arraysize = -1; + } + else { + sprintf(extra_text, "[%llu]", (unsigned PY_LONG_LONG)length); + arraysize = MUL_WRAPAROUND(length, ctitem->ct_size); + if (length > 0 && (arraysize / length) != ctitem->ct_size) { + PyErr_SetString(PyExc_OverflowError, + "array size would overflow a Py_ssize_t"); + return NULL; + } + } + td = ctypedescr_new_on_top(ctitem, extra_text, 0); + if (td == NULL) + return NULL; + + Py_INCREF(ctptr); + td->ct_stuff = (PyObject *)ctptr; + td->ct_size = arraysize; + td->ct_length = length; + td->ct_flags = flags; + unique_key[0] = ctptr; + unique_key[1] = (void *)length; + return get_unique_type(td, unique_key, 2); +} + +static PyObject *new_void_type(void) +{ + int name_size = strlen("void") + 1; + const void *unique_key[1]; + CTypeDescrObject *td = ctypedescr_new(name_size); + if (td == NULL) + return NULL; + + memcpy(td->ct_name, "void", name_size); + td->ct_size = -1; + td->ct_flags = CT_VOID | CT_IS_OPAQUE; + td->ct_name_position = strlen("void"); + unique_key[0] = "void"; + return get_unique_type(td, unique_key, 1); +} + +static PyObject *b_new_void_type(PyObject *self, PyObject *args) +{ + return new_void_type(); +} + +static PyObject *new_struct_or_union_type(const char *name, int flag) +{ + int namelen = strlen(name); + CTypeDescrObject *td = ctypedescr_new(namelen + 1); + if (td == NULL) + return NULL; + + td->ct_size = -1; + td->ct_length = -1; + td->ct_flags = flag | CT_IS_OPAQUE; + td->ct_extra = NULL; + memcpy(td->ct_name, name, namelen + 1); + td->ct_name_position = namelen; + return (PyObject *)td; +} + +static PyObject *b_new_struct_type(PyObject *self, PyObject *args) +{ + char *name; + int flag; + if (!PyArg_ParseTuple(args, "s:new_struct_type", &name)) + return NULL; + + flag = CT_STRUCT; + if (strcmp(name, "struct _IO_FILE") == 0 || strcmp(name, "FILE") == 0) + flag |= CT_IS_FILE; + return new_struct_or_union_type(name, flag); +} + +static PyObject *b_new_union_type(PyObject *self, PyObject *args) +{ + char *name; + if (!PyArg_ParseTuple(args, "s:new_union_type", &name)) + return NULL; + return new_struct_or_union_type(name, CT_UNION); +} + +static CFieldObject * +_add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype, + Py_ssize_t offset, int bitshift, int fbitsize, int flags) +{ + int err; + Py_ssize_t prev_size; + CFieldObject *cf = PyObject_New(CFieldObject, &CField_Type); + if (cf == NULL) + return NULL; + + Py_INCREF(ftype); + cf->cf_type = ftype; + cf->cf_offset = offset; + cf->cf_bitshift = bitshift; + cf->cf_bitsize = fbitsize; + cf->cf_flags = flags; + + Py_INCREF(fname); + PyText_InternInPlace(&fname); + prev_size = PyDict_Size(interned_fields); + err = PyDict_SetItem(interned_fields, fname, (PyObject *)cf); + Py_DECREF(fname); + Py_DECREF(cf); + if (err < 0) + return NULL; + + if (PyDict_Size(interned_fields) != prev_size + 1) { + PyErr_Format(PyExc_KeyError, "duplicate field name '%s'", + PyText_AS_UTF8(fname)); + return NULL; + } + return cf; /* borrowed reference */ +} + +#define SF_MSVC_BITFIELDS 0x01 +#define SF_GCC_ARM_BITFIELDS 0x02 +#define SF_GCC_X86_BITFIELDS 0x10 + +#define SF_GCC_BIG_ENDIAN 0x04 +#define SF_GCC_LITTLE_ENDIAN 0x40 + +#define SF_PACKED 0x08 +#define SF_STD_FIELD_POS 0x80 + +#ifdef MS_WIN32 +# define SF_DEFAULT_PACKING 8 +#else +# define SF_DEFAULT_PACKING 0x40000000 /* a huge power of two */ +#endif + +static int complete_sflags(int sflags) +{ + /* add one of the SF_xxx_BITFIELDS flags if none is specified */ + if (!(sflags & (SF_MSVC_BITFIELDS | SF_GCC_ARM_BITFIELDS | + SF_GCC_X86_BITFIELDS))) { +#ifdef MS_WIN32 + sflags |= SF_MSVC_BITFIELDS; +#else +# if defined(__arm__) || defined(__aarch64__) + sflags |= SF_GCC_ARM_BITFIELDS; +# else + sflags |= SF_GCC_X86_BITFIELDS; +# endif +#endif + } + /* add one of SF_GCC_xx_ENDIAN if none is specified */ + if (!(sflags & (SF_GCC_BIG_ENDIAN | SF_GCC_LITTLE_ENDIAN))) { + int _check_endian = 1; + if (*(char *)&_check_endian == 0) + sflags |= SF_GCC_BIG_ENDIAN; + else + sflags |= SF_GCC_LITTLE_ENDIAN; + } + return sflags; +} + +static int detect_custom_layout(CTypeDescrObject *ct, int sflags, + Py_ssize_t cdef_value, + Py_ssize_t compiler_value, + const char *msg1, const char *txt, + const char *msg2) +{ + if (compiler_value != cdef_value) { + if (sflags & SF_STD_FIELD_POS) { + PyErr_Format(FFIError, + "%s: %s%s%s (cdef says %zd, but C compiler says %zd)." + " fix it or use \"...;\" in the cdef for %s to " + "make it flexible", + ct->ct_name, msg1, txt, msg2, + cdef_value, compiler_value, + ct->ct_name); + return -1; + } + ct->ct_flags |= CT_CUSTOM_FIELD_POS; + } + return 0; +} + +static PyObject *b_complete_struct_or_union(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *fields, *interned_fields, *ignored; + int is_union, alignment; + Py_ssize_t boffset, i, nb_fields, boffsetmax, alignedsize, boffsetorg; + Py_ssize_t totalsize = -1; + int totalalignment = -1; + CFieldObject **previous; + int prev_bitfield_size, prev_bitfield_free; + int sflags = 0, fflags; + int pack = 0; + + if (!PyArg_ParseTuple(args, "O!O!|Oniii:complete_struct_or_union", + &CTypeDescr_Type, &ct, + &PyList_Type, &fields, + &ignored, &totalsize, &totalalignment, &sflags, + &pack)) + return NULL; + + sflags = complete_sflags(sflags); + if (sflags & SF_PACKED) + pack = 1; + else if (pack <= 0) + pack = SF_DEFAULT_PACKING; + else + sflags |= SF_PACKED; + + if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) == + (CT_STRUCT|CT_IS_OPAQUE)) { + is_union = 0; + } + else if ((ct->ct_flags & (CT_UNION|CT_IS_OPAQUE)) == + (CT_UNION|CT_IS_OPAQUE)) { + is_union = 1; + } + else { + PyErr_SetString(PyExc_TypeError, + "first arg must be a non-initialized struct or union ctype"); + return NULL; + } + ct->ct_flags &= ~(CT_CUSTOM_FIELD_POS | CT_WITH_PACKED_CHANGE); + + alignment = 1; + boffset = 0; /* this number is in *bits*, not bytes! */ + boffsetmax = 0; /* the maximum value of boffset, in bits too */ + prev_bitfield_size = 0; + prev_bitfield_free = 0; + nb_fields = PyList_GET_SIZE(fields); + interned_fields = PyDict_New(); + if (interned_fields == NULL) + return NULL; + + previous = (CFieldObject **)&ct->ct_extra; + + for (i=0; i<nb_fields; i++) { + PyObject *fname; + CTypeDescrObject *ftype; + int fbitsize = -1, falign, falignorg, do_align; + Py_ssize_t foffset = -1; + + if (!PyArg_ParseTuple(PyList_GET_ITEM(fields, i), "O!O!|in:list item", + &PyText_Type, &fname, + &CTypeDescr_Type, &ftype, + &fbitsize, &foffset)) + goto error; + + if (ftype->ct_size < 0) { + if ((ftype->ct_flags & CT_ARRAY) && fbitsize < 0 + && (i == nb_fields - 1 || foffset != -1)) { + ct->ct_flags |= CT_WITH_VAR_ARRAY; + } + else { + PyErr_Format(PyExc_TypeError, + "field '%s.%s' has ctype '%s' of unknown size", + ct->ct_name, PyText_AS_UTF8(fname), + ftype->ct_name); + goto error; + } + } + + if (is_union) + boffset = 0; /* reset each field at offset 0 */ + + /* update the total alignment requirement, but skip it if the + field is an anonymous bitfield or if SF_PACKED */ + falignorg = get_alignment(ftype); + if (falignorg < 0) + goto error; + falign = (pack < falignorg) ? pack : falignorg; + + do_align = 1; + if (!(sflags & SF_GCC_ARM_BITFIELDS) && fbitsize >= 0) { + if (!(sflags & SF_MSVC_BITFIELDS)) { + /* GCC: anonymous bitfields (of any size) don't cause alignment */ + do_align = PyText_GetSize(fname) > 0; + } + else { + /* MSVC: zero-sized bitfields don't cause alignment */ + do_align = fbitsize > 0; + } + } + if (alignment < falign && do_align) + alignment = falign; + + fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0; + + if (fbitsize < 0) { + /* not a bitfield: common case */ + int bs_flag; + + if ((ftype->ct_flags & CT_ARRAY) && ftype->ct_length <= 0) + bs_flag = BS_EMPTY_ARRAY; + else + bs_flag = BS_REGULAR; + + /* align this field to its own 'falign' by inserting padding */ + boffsetorg = (boffset + falignorg*8-1) & ~(falignorg*8-1); /*bits!*/ + boffset = (boffset + falign*8-1) & ~(falign*8-1); /* bits! */ + if (boffsetorg != boffset) { + ct->ct_flags |= CT_WITH_PACKED_CHANGE; + } + + if (foffset >= 0) { + /* a forced field position: ignore the offset just computed, + except to know if we must set CT_CUSTOM_FIELD_POS */ + if (detect_custom_layout(ct, sflags, boffset / 8, foffset, + "wrong offset for field '", + PyText_AS_UTF8(fname), "'") < 0) + goto error; + boffset = foffset * 8; + } + + if (PyText_GetSize(fname) == 0 && + ftype->ct_flags & (CT_STRUCT|CT_UNION)) { + /* a nested anonymous struct or union */ + CFieldObject *cfsrc = (CFieldObject *)ftype->ct_extra; + for (; cfsrc != NULL; cfsrc = cfsrc->cf_next) { + /* broken complexity in the call to get_field_name(), + but we'll assume you never do that with nested + anonymous structures with thousand of fields */ + *previous = _add_field(interned_fields, + get_field_name(ftype, cfsrc), + cfsrc->cf_type, + boffset / 8 + cfsrc->cf_offset, + cfsrc->cf_bitshift, + cfsrc->cf_bitsize, + cfsrc->cf_flags | fflags); + if (*previous == NULL) + goto error; + previous = &(*previous)->cf_next; + } + /* always forbid such structures from being passed by value */ + ct->ct_flags |= CT_CUSTOM_FIELD_POS; + } + else { + *previous = _add_field(interned_fields, fname, ftype, + boffset / 8, bs_flag, -1, fflags); + if (*previous == NULL) + goto error; + previous = &(*previous)->cf_next; + } + if (ftype->ct_size >= 0) + boffset += ftype->ct_size * 8; + prev_bitfield_size = 0; + } + else { + /* this is the case of a bitfield */ + Py_ssize_t field_offset_bytes; + int bits_already_occupied, bitshift; + + if (foffset >= 0) { + PyErr_Format(PyExc_TypeError, + "field '%s.%s' is a bitfield, " + "but a fixed offset is specified", + ct->ct_name, PyText_AS_UTF8(fname)); + goto error; + } + + if (!(ftype->ct_flags & (CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED | + CT_PRIMITIVE_CHAR))) { + PyErr_Format(PyExc_TypeError, + "field '%s.%s' declared as '%s' cannot be a bit field", + ct->ct_name, PyText_AS_UTF8(fname), + ftype->ct_name); + goto error; + } + if (fbitsize > 8 * ftype->ct_size) { + PyErr_Format(PyExc_TypeError, + "bit field '%s.%s' is declared '%s:%d', which " + "exceeds the width of the type", + ct->ct_name, PyText_AS_UTF8(fname), + ftype->ct_name, fbitsize); + goto error; + } + + /* compute the starting position of the theoretical field + that covers a complete 'ftype', inside of which we will + locate the real bitfield */ + field_offset_bytes = boffset / 8; + field_offset_bytes &= ~(falign - 1); + + if (fbitsize == 0) { + if (PyText_GetSize(fname) > 0) { + PyErr_Format(PyExc_TypeError, + "field '%s.%s' is declared with :0", + ct->ct_name, PyText_AS_UTF8(fname)); + goto error; + } + if (!(sflags & SF_MSVC_BITFIELDS)) { + /* GCC's notion of "ftype :0;" */ + + /* pad boffset to a value aligned for "ftype" */ + if (boffset > field_offset_bytes * 8) { + field_offset_bytes += falign; + assert(boffset < field_offset_bytes * 8); + } + boffset = field_offset_bytes * 8; + } + else { + /* MSVC's notion of "ftype :0;" */ + + /* Mostly ignored. It seems they only serve as + separator between other bitfields, to force them + into separate words. */ + } + prev_bitfield_size = 0; + } + else { + if (!(sflags & SF_MSVC_BITFIELDS)) { + /* GCC's algorithm */ + + /* Can the field start at the offset given by 'boffset'? It + can if it would entirely fit into an aligned ftype field. */ + bits_already_occupied = boffset - (field_offset_bytes * 8); + + if (bits_already_occupied + fbitsize > 8 * ftype->ct_size) { + /* it would not fit, we need to start at the next + allowed position */ + if ((sflags & SF_PACKED) && + (bits_already_occupied & 7)) { + PyErr_Format(PyExc_NotImplementedError, + "with 'packed', gcc would compile field " + "'%s.%s' to reuse some bits in the previous " + "field", ct->ct_name, PyText_AS_UTF8(fname)); + goto error; + } + field_offset_bytes += falign; + assert(boffset < field_offset_bytes * 8); + boffset = field_offset_bytes * 8; + bitshift = 0; + } + else { + bitshift = bits_already_occupied; + assert(bitshift >= 0); + } + boffset += fbitsize; + } + else { + /* MSVC's algorithm */ + + /* A bitfield is considered as taking the full width + of their declared type. It can share some bits + with the previous field only if it was also a + bitfield and used a type of the same size. */ + if (prev_bitfield_size == ftype->ct_size && + prev_bitfield_free >= fbitsize) { + /* yes: reuse */ + bitshift = 8 * prev_bitfield_size - prev_bitfield_free; + } + else { + /* no: start a new full field */ + boffset = (boffset + falign*8-1) & ~(falign*8-1); /*align*/ + boffset += ftype->ct_size * 8; + bitshift = 0; + prev_bitfield_size = ftype->ct_size; + prev_bitfield_free = 8 * prev_bitfield_size; + } + prev_bitfield_free -= fbitsize; + field_offset_bytes = boffset / 8 - ftype->ct_size; + } + + if (sflags & SF_GCC_BIG_ENDIAN) + bitshift = 8 * ftype->ct_size - fbitsize - bitshift; + + *previous = _add_field(interned_fields, fname, ftype, + field_offset_bytes, bitshift, fbitsize, + fflags); + if (*previous == NULL) + goto error; + previous = &(*previous)->cf_next; + } + } + + if (boffset > boffsetmax) + boffsetmax = boffset; + } + *previous = NULL; + + /* Like C, if the size of this structure would be zero, we compute it + as 1 instead. But for ctypes support, we allow the manually- + specified totalsize to be zero in this case. */ + boffsetmax = (boffsetmax + 7) / 8; /* bits -> bytes */ + alignedsize = (boffsetmax + alignment - 1) & ~(alignment-1); + if (alignedsize == 0) + alignedsize = 1; + + if (totalsize < 0) { + totalsize = alignedsize; + } + else { + if (detect_custom_layout(ct, sflags, alignedsize, + totalsize, "wrong total size", "", "") < 0) + goto error; + if (totalsize < boffsetmax) { + PyErr_Format(PyExc_TypeError, + "%s cannot be of size %zd: there are fields at least " + "up to %zd", ct->ct_name, totalsize, boffsetmax); + goto error; + } + } + if (totalalignment < 0) { + totalalignment = alignment; + } + else { + if (detect_custom_layout(ct, sflags, alignment, totalalignment, + "wrong total alignment", "", "") < 0) + goto error; + } + + ct->ct_size = totalsize; + ct->ct_length = totalalignment; + ct->ct_stuff = interned_fields; + ct->ct_flags &= ~CT_IS_OPAQUE; + + Py_INCREF(Py_None); + return Py_None; + + error: + ct->ct_extra = NULL; + Py_DECREF(interned_fields); + return NULL; +} + +struct funcbuilder_s { + Py_ssize_t nb_bytes; + char *bufferp; + ffi_type **atypes; + ffi_type *rtype; + Py_ssize_t nargs; + CTypeDescrObject *fct; +}; + +static void *fb_alloc(struct funcbuilder_s *fb, Py_ssize_t size) +{ + if (fb->bufferp == NULL) { + fb->nb_bytes += size; + return NULL; + } + else { + char *result = fb->bufferp; + fb->bufferp += size; + return result; + } +} + +#define SUPPORTED_IN_API_MODE \ + " are only supported as %s if the function is " \ + "'API mode' and non-variadic (i.e. declared inside ffibuilder" \ + ".cdef()+ffibuilder.set_source() and not taking a final '...' " \ + "argument)" + +static ffi_type *fb_unsupported(CTypeDescrObject *ct, const char *place, + const char *detail) +{ + PyErr_Format(PyExc_NotImplementedError, + "ctype '%s' not supported as %s. %s. " + "Such structs" SUPPORTED_IN_API_MODE, + ct->ct_name, place, detail, place); + return NULL; +} + +static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct, + int is_result_type) +{ + const char *place = is_result_type ? "return value" : "argument"; + + if (ct->ct_flags & (CT_PRIMITIVE_ANY & ~CT_PRIMITIVE_COMPLEX)) { + return (ffi_type *)ct->ct_extra; + } + else if (ct->ct_flags & (CT_POINTER|CT_FUNCTIONPTR)) { + return &ffi_type_pointer; + } + else if ((ct->ct_flags & CT_VOID) && is_result_type) { + return &ffi_type_void; + } + + if (ct->ct_size <= 0) { + PyErr_Format(PyExc_TypeError, + ct->ct_size < 0 ? "ctype '%s' has incomplete type" + : "ctype '%s' has size 0", + ct->ct_name); + return NULL; + } + if (ct->ct_flags & CT_STRUCT) { + ffi_type *ffistruct, *ffifield; + ffi_type **elements; + Py_ssize_t i, n, nflat; + CFieldObject *cf; + + /* We can't pass a struct that was completed by verify(). + Issue: assume verify() is given "struct { long b; ...; }". + Then it will complete it in the same way whether it is actually + "struct { long a, b; }" or "struct { double a; long b; }". + But on 64-bit UNIX, these two structs are passed by value + differently: e.g. on x86-64, "b" ends up in register "rsi" in + the first case and "rdi" in the second case. + + Another reason for CT_CUSTOM_FIELD_POS would be anonymous + nested structures: we lost the information about having it + here, so better safe (and forbid it) than sorry (and maybe + crash). Note: it seems we only get in this case with + ffi.verify(). + */ + if (force_lazy_struct(ct) < 0) + return NULL; + if (ct->ct_flags & CT_CUSTOM_FIELD_POS) { + /* these NotImplementedErrors may be caught and ignored until + a real call is made to a function of this type */ + return fb_unsupported(ct, place, + "It is a struct declared with \"...;\", but the C " + "calling convention may depend on the missing fields; " + "or, it contains anonymous struct/unions"); + } + /* Another reason: __attribute__((packed)) is not supported by libffi. + */ + if (ct->ct_flags & CT_WITH_PACKED_CHANGE) { + return fb_unsupported(ct, place, + "It is a 'packed' structure, with a different layout than " + "expected by libffi"); + } + + n = PyDict_Size(ct->ct_stuff); + nflat = 0; + + /* walk the fields, expanding arrays into repetitions; first, + only count how many flattened fields there are */ + cf = (CFieldObject *)ct->ct_extra; + for (i=0; i<n; i++) { + Py_ssize_t flat; + CTypeDescrObject *ct1; + assert(cf != NULL); + if (cf->cf_bitshift >= 0) { + return fb_unsupported(ct, place, + "It is a struct with bit fields, which libffi does not " + "support"); + } + flat = 1; + ct1 = cf->cf_type; + while (ct1->ct_flags & CT_ARRAY) { + flat *= ct1->ct_length; + ct1 = ct1->ct_itemdescr; + } + if (flat <= 0) { + return fb_unsupported(ct, place, + "It is a struct with a zero-length array, which libffi " + "does not support"); + } + nflat += flat; + cf = cf->cf_next; + } + assert(cf == NULL); + + /* next, allocate and fill the flattened list */ + elements = fb_alloc(fb, (nflat + 1) * sizeof(ffi_type*)); + nflat = 0; + cf = (CFieldObject *)ct->ct_extra; + for (i=0; i<n; i++) { + Py_ssize_t j, flat = 1; + CTypeDescrObject *ct = cf->cf_type; + while (ct->ct_flags & CT_ARRAY) { + flat *= ct->ct_length; + ct = ct->ct_itemdescr; + } + ffifield = fb_fill_type(fb, ct, 0); + if (PyErr_Occurred()) + return NULL; + if (elements != NULL) { + for (j=0; j<flat; j++) + elements[nflat++] = ffifield; + } + cf = cf->cf_next; + } + + /* finally, allocate the FFI_TYPE_STRUCT */ + ffistruct = fb_alloc(fb, sizeof(ffi_type)); + if (ffistruct != NULL) { + elements[nflat] = NULL; + ffistruct->size = ct->ct_size; + ffistruct->alignment = ct->ct_length; + ffistruct->type = FFI_TYPE_STRUCT; + ffistruct->elements = elements; + } + return ffistruct; + } + else if (ct->ct_flags & CT_UNION) { + PyErr_Format(PyExc_NotImplementedError, + "ctype '%s' not supported as %s by libffi. " + "Unions" SUPPORTED_IN_API_MODE, + ct->ct_name, place, place); + return NULL; + } + else { + char *extra = ""; + if (ct->ct_flags & CT_PRIMITIVE_COMPLEX) + extra = " (the support for complex types inside libffi " + "is mostly missing at this point, so CFFI only " + "supports complex types as arguments or return " + "value in API-mode functions)"; + + PyErr_Format(PyExc_NotImplementedError, + "ctype '%s' (size %zd) not supported as %s%s", + ct->ct_name, ct->ct_size, place, extra); + return NULL; + } +} + +#define ALIGN_ARG(n) ((n) + 7) & ~7 + +static int fb_build(struct funcbuilder_s *fb, PyObject *fargs, + CTypeDescrObject *fresult) +{ + Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs); + Py_ssize_t exchange_offset; + cif_description_t *cif_descr; + + /* ffi buffer: start with a cif_description */ + cif_descr = fb_alloc(fb, sizeof(cif_description_t) + + nargs * sizeof(Py_ssize_t)); + + /* ffi buffer: next comes an array of 'ffi_type*', one per argument */ + fb->atypes = fb_alloc(fb, nargs * sizeof(ffi_type*)); + fb->nargs = nargs; + + /* ffi buffer: next comes the result type */ + fb->rtype = fb_fill_type(fb, fresult, 1); + if (PyErr_Occurred()) + return -1; + if (cif_descr != NULL) { + /* exchange data size */ + /* first, enough room for an array of 'nargs' pointers */ + exchange_offset = nargs * sizeof(void*); + exchange_offset = ALIGN_ARG(exchange_offset); + cif_descr->exchange_offset_arg[0] = exchange_offset; + /* then enough room for the result --- which means at least + sizeof(ffi_arg), according to the ffi docs */ + i = fb->rtype->size; + if (i < (Py_ssize_t)sizeof(ffi_arg)) + i = sizeof(ffi_arg); + exchange_offset += i; + } + else + exchange_offset = 0; /* not used */ + + /* loop over the arguments */ + for (i=0; i<nargs; i++) { + CTypeDescrObject *farg; + ffi_type *atype; + + farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i); + /* convert arrays to pointers */ + if (farg->ct_flags & CT_ARRAY) + farg = (CTypeDescrObject *)farg->ct_stuff; + + /* ffi buffer: fill in the ffi for the i'th argument */ + assert(farg != NULL); + atype = fb_fill_type(fb, farg, 0); + if (PyErr_Occurred()) + return -1; + + if (fb->atypes != NULL) { + fb->atypes[i] = atype; + /* exchange data size */ + exchange_offset = ALIGN_ARG(exchange_offset); + cif_descr->exchange_offset_arg[1 + i] = exchange_offset; + exchange_offset += atype->size; + } + } + + if (cif_descr != NULL) { + /* exchange data size */ + /* we also align it to the next multiple of 8, in an attempt to + work around bugs(?) of libffi like #241 */ + cif_descr->exchange_size = ALIGN_ARG(exchange_offset); + } + return 0; +} + +#undef ALIGN_ARG + +static void fb_cat_name(struct funcbuilder_s *fb, const char *piece, + int piecelen) +{ + if (fb->bufferp == NULL) { + fb->nb_bytes += piecelen; + } + else { + memcpy(fb->bufferp, piece, piecelen); + fb->bufferp += piecelen; + } +} + +static int fb_build_name(struct funcbuilder_s *fb, const char *repl, + CTypeDescrObject **pfargs, Py_ssize_t nargs, + CTypeDescrObject *fresult, int ellipsis) +{ + Py_ssize_t i; + fb->nargs = nargs; + + /* name: the function type name we build here is, like in C, made + as follows: + + RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL + */ + fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position); + if (repl[0] != '(' && + fresult->ct_name[fresult->ct_name_position - 1] != '*') + fb_cat_name(fb, " ", 1); /* add a space */ + fb_cat_name(fb, repl, strlen(repl)); + if (fb->fct) { + i = strlen(repl) - 1; /* between '(*' and ')' */ + assert(repl[i] == ')'); + fb->fct->ct_name_position = fresult->ct_name_position + i; + } + fb_cat_name(fb, "(", 1); + + /* loop over the arguments */ + for (i=0; i<nargs; i++) { + CTypeDescrObject *farg; + + farg = pfargs[i]; + if (!CTypeDescr_Check(farg)) { + PyErr_SetString(PyExc_TypeError, "expected a tuple of ctypes"); + return -1; + } + /* name: concatenate the name of the i'th argument's type */ + if (i > 0) + fb_cat_name(fb, ", ", 2); + fb_cat_name(fb, farg->ct_name, strlen(farg->ct_name)); + } + + /* name: add the '...' if needed */ + if (ellipsis) { + if (nargs > 0) + fb_cat_name(fb, ", ", 2); + fb_cat_name(fb, "...", 3); + } + + /* name: concatenate the tail of the result type */ + fb_cat_name(fb, ")", 1); + fb_cat_name(fb, fresult->ct_name + fresult->ct_name_position, + strlen(fresult->ct_name) - fresult->ct_name_position + 1); + return 0; +} + +static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb, + PyObject *fargs, + CTypeDescrObject *fresult, + int ellipsis, int fabi) +{ + CTypeDescrObject *fct, **pfargs; + Py_ssize_t nargs; + char *repl = "(*)"; + + fb->nb_bytes = 0; + fb->bufferp = NULL; + fb->fct = NULL; + + pfargs = (CTypeDescrObject **)&PyTuple_GET_ITEM(fargs, 0); + nargs = PyTuple_GET_SIZE(fargs); +#if defined(MS_WIN32) && !defined(_WIN64) + if (fabi == FFI_STDCALL) + repl = "(__stdcall *)"; +#endif + + /* compute the total size needed for the name */ + if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) + return NULL; + + /* allocate the function type */ + fct = ctypedescr_new(fb->nb_bytes); + if (fct == NULL) + return NULL; + fb->fct = fct; + + /* call again fb_build_name() to really build the ct_name */ + fb->bufferp = fct->ct_name; + if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0) + goto error; + assert(fb->bufferp == fct->ct_name + fb->nb_bytes); + + fct->ct_extra = NULL; + fct->ct_size = sizeof(void(*)(void)); + fct->ct_flags = CT_FUNCTIONPTR; + return fct; + + error: + Py_DECREF(fct); + return NULL; +} + +static cif_description_t *fb_prepare_cif(PyObject *fargs, + CTypeDescrObject *fresult, + ffi_abi fabi) +{ + char *buffer; + cif_description_t *cif_descr; + struct funcbuilder_s funcbuffer; + + funcbuffer.nb_bytes = 0; + funcbuffer.bufferp = NULL; + + /* compute the total size needed in the buffer for libffi */ + if (fb_build(&funcbuffer, fargs, fresult) < 0) + return NULL; + + /* allocate the buffer */ + buffer = PyObject_Malloc(funcbuffer.nb_bytes); + if (buffer == NULL) { + PyErr_NoMemory(); + return NULL; + } + + /* call again fb_build() to really build the libffi data structures */ + funcbuffer.bufferp = buffer; + if (fb_build(&funcbuffer, fargs, fresult) < 0) + goto error; + assert(funcbuffer.bufferp == buffer + funcbuffer.nb_bytes); + + cif_descr = (cif_description_t *)buffer; + if (ffi_prep_cif(&cif_descr->cif, fabi, funcbuffer.nargs, + funcbuffer.rtype, funcbuffer.atypes) != FFI_OK) { + PyErr_SetString(PyExc_SystemError, + "libffi failed to build this function type"); + goto error; + } + return cif_descr; + + error: + PyObject_Free(buffer); + return NULL; +} + +static PyObject *new_function_type(PyObject *fargs, /* tuple */ + CTypeDescrObject *fresult, + int ellipsis, int fabi) +{ + PyObject *fabiobj; + CTypeDescrObject *fct; + struct funcbuilder_s funcbuilder; + Py_ssize_t i; + const void **unique_key; + + if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) || + (fresult->ct_flags & CT_ARRAY)) { + char *msg; + if (fresult->ct_flags & CT_IS_OPAQUE) + msg = "result type '%s' is opaque"; + else + msg = "invalid result type: '%s'"; + PyErr_Format(PyExc_TypeError, msg, fresult->ct_name); + return NULL; + } + + fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis, fabi); + if (fct == NULL) + return NULL; + + if (!ellipsis) { + /* Functions with '...' varargs are stored without a cif_descr + at all. The cif is computed on every call from the actual + types passed in. For all other functions, the cif_descr + is computed here. */ + cif_description_t *cif_descr; + + cif_descr = fb_prepare_cif(fargs, fresult, fabi); + if (cif_descr == NULL) { + if (PyErr_ExceptionMatches(PyExc_NotImplementedError)) { + PyErr_Clear(); /* will get the exception if we see an + actual call */ + } + else + goto error; + } + + fct->ct_extra = (char *)cif_descr; + } + + /* build the signature, given by a tuple of ctype objects */ + fct->ct_stuff = PyTuple_New(2 + funcbuilder.nargs); + if (fct->ct_stuff == NULL) + goto error; + fabiobj = PyInt_FromLong(fabi); + if (fabiobj == NULL) + goto error; + PyTuple_SET_ITEM(fct->ct_stuff, 0, fabiobj); + + Py_INCREF(fresult); + PyTuple_SET_ITEM(fct->ct_stuff, 1, (PyObject *)fresult); + for (i=0; i<funcbuilder.nargs; i++) { + PyObject *o = PyTuple_GET_ITEM(fargs, i); + /* convert arrays into pointers */ + if (((CTypeDescrObject *)o)->ct_flags & CT_ARRAY) + o = ((CTypeDescrObject *)o)->ct_stuff; + Py_INCREF(o); + PyTuple_SET_ITEM(fct->ct_stuff, 2 + i, o); + } + + /* [ctresult, ellipsis+abi, num_args, ctargs...] */ + unique_key = alloca((3 + funcbuilder.nargs) * sizeof(void *)); + unique_key[0] = fresult; + unique_key[1] = (const void *)(Py_ssize_t)((fabi << 1) | !!ellipsis); + unique_key[2] = (const void *)(Py_ssize_t)(funcbuilder.nargs); + for (i=0; i<funcbuilder.nargs; i++) + unique_key[3 + i] = PyTuple_GET_ITEM(fct->ct_stuff, 2 + i); + return get_unique_type(fct, unique_key, 3 + funcbuilder.nargs); + + error: + Py_DECREF(fct); + return NULL; +} + +static PyObject *b_new_function_type(PyObject *self, PyObject *args) +{ + PyObject *fargs; + CTypeDescrObject *fresult; + int ellipsis = 0, fabi = FFI_DEFAULT_ABI; + + if (!PyArg_ParseTuple(args, "O!O!|ii:new_function_type", + &PyTuple_Type, &fargs, + &CTypeDescr_Type, &fresult, + &ellipsis, + &fabi)) + return NULL; + + return new_function_type(fargs, fresult, ellipsis, fabi); +} + +static int convert_from_object_fficallback(char *result, + CTypeDescrObject *ctype, + PyObject *pyobj, + int encode_result_for_libffi) +{ + /* work work work around a libffi irregularity: for integer return + types we have to fill at least a complete 'ffi_arg'-sized result + buffer. */ + if (ctype->ct_size < (Py_ssize_t)sizeof(ffi_arg)) { + if (ctype->ct_flags & CT_VOID) { + if (pyobj == Py_None) { + return 0; + } + else { + PyErr_SetString(PyExc_TypeError, + "callback with the return type 'void' must return None"); + return -1; + } + } + if (!encode_result_for_libffi) + goto skip; + if (ctype->ct_flags & CT_PRIMITIVE_SIGNED) { + PY_LONG_LONG value; + /* It's probably fine to always zero-extend, but you never + know: maybe some code somewhere expects a negative + 'short' result to be returned into EAX as a 32-bit + negative number. Better safe than sorry. This code + is about that case. Let's ignore this for enums. + */ + /* do a first conversion only to detect overflows. This + conversion produces stuff that is otherwise ignored. */ + if (convert_from_object(result, ctype, pyobj) < 0) + return -1; + /* manual inlining and tweaking of convert_from_object() + in order to write a whole 'ffi_arg'. */ + value = _my_PyLong_AsLongLong(pyobj); + if (value == -1 && PyErr_Occurred()) + return -1; + write_raw_integer_data(result, value, sizeof(ffi_arg)); + return 0; + } + else if (ctype->ct_flags & (CT_PRIMITIVE_CHAR | CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED | + CT_POINTER | CT_FUNCTIONPTR)) { + /* zero extension: fill the '*result' with zeros, and (on big- + endian machines) correct the 'result' pointer to write to. + We also do that for pointers, even though we're normally not + in this branch because ctype->ct_size == sizeof(ffi_arg) for + pointers---except on some architectures like x32 (issue #372). + */ + memset(result, 0, sizeof(ffi_arg)); +#ifdef WORDS_BIGENDIAN + result += (sizeof(ffi_arg) - ctype->ct_size); +#endif + } + } + skip: + return convert_from_object(result, ctype, pyobj); +} + +static void _my_PyErr_WriteUnraisable(PyObject *t, PyObject *v, PyObject *tb, + char *objdescr, PyObject *obj, + char *extra_error_line) +{ + /* like PyErr_WriteUnraisable(), but write a full traceback */ + PyObject *f; +#if PY_MAJOR_VERSION >= 3 + /* jump through hoops to ensure the tb is attached to v, on Python 3 */ + PyErr_NormalizeException(&t, &v, &tb); + if (tb == NULL) { + tb = Py_None; + Py_INCREF(tb); + } + PyException_SetTraceback(v, tb); +#endif + f = PySys_GetObject("stderr"); + if (f != NULL) { + if (obj != NULL) { + PyFile_WriteString(objdescr, f); + PyFile_WriteObject(obj, f, 0); + PyFile_WriteString(":\n", f); + } + if (extra_error_line != NULL) + PyFile_WriteString(extra_error_line, f); + PyErr_Display(t, v, tb); + } + Py_XDECREF(t); + Py_XDECREF(v); + Py_XDECREF(tb); +} + +static void general_invoke_callback(int decode_args_from_libffi, + void *result, char *args, void *userdata) +{ + PyObject *cb_args = (PyObject *)userdata; + CTypeDescrObject *ct = (CTypeDescrObject *)PyTuple_GET_ITEM(cb_args, 0); + PyObject *signature = ct->ct_stuff; + PyObject *py_ob = PyTuple_GET_ITEM(cb_args, 1); + PyObject *py_args = NULL; + PyObject *py_res = NULL; + PyObject *py_rawerr; + PyObject *onerror_cb; + Py_ssize_t i, n; + char *extra_error_line = NULL; + +#define SIGNATURE(i) ((CTypeDescrObject *)PyTuple_GET_ITEM(signature, i)) + + Py_INCREF(cb_args); + + n = PyTuple_GET_SIZE(signature) - 2; + py_args = PyTuple_New(n); + if (py_args == NULL) + goto error; + + for (i=0; i<n; i++) { + char *a_src; + PyObject *a; + CTypeDescrObject *a_ct = SIGNATURE(2 + i); + + if (decode_args_from_libffi) { + a_src = ((void **)args)[i]; + } + else { + a_src = args + i * 8; + if (a_ct->ct_flags & (CT_IS_LONGDOUBLE | CT_STRUCT | CT_UNION)) + a_src = *(char **)a_src; + } + a = convert_to_object(a_src, a_ct); + if (a == NULL) + goto error; + PyTuple_SET_ITEM(py_args, i, a); + } + + py_res = PyObject_Call(py_ob, py_args, NULL); + if (py_res == NULL) + goto error; + if (convert_from_object_fficallback(result, SIGNATURE(1), py_res, + decode_args_from_libffi) < 0) { + extra_error_line = "Trying to convert the result back to C:\n"; + goto error; + } + done: + Py_XDECREF(py_args); + Py_XDECREF(py_res); + Py_DECREF(cb_args); + return; + + error: + if (SIGNATURE(1)->ct_size > 0) { + py_rawerr = PyTuple_GET_ITEM(cb_args, 2); + memcpy(result, PyBytes_AS_STRING(py_rawerr), + PyBytes_GET_SIZE(py_rawerr)); + } + onerror_cb = PyTuple_GET_ITEM(cb_args, 3); + if (onerror_cb == Py_None) { + PyObject *ecap, *t, *v, *tb; + PyErr_Fetch(&t, &v, &tb); + ecap = _cffi_start_error_capture(); + _my_PyErr_WriteUnraisable(t, v, tb, "From cffi callback ", py_ob, + extra_error_line); + _cffi_stop_error_capture(ecap); + } + else { + PyObject *exc1, *val1, *tb1, *res1, *exc2, *val2, *tb2; + PyErr_Fetch(&exc1, &val1, &tb1); + PyErr_NormalizeException(&exc1, &val1, &tb1); + res1 = PyObject_CallFunctionObjArgs(onerror_cb, + exc1 ? exc1 : Py_None, + val1 ? val1 : Py_None, + tb1 ? tb1 : Py_None, + NULL); + if (res1 != NULL) { + if (res1 != Py_None) + convert_from_object_fficallback(result, SIGNATURE(1), res1, + decode_args_from_libffi); + Py_DECREF(res1); + } + if (!PyErr_Occurred()) { + Py_XDECREF(exc1); + Py_XDECREF(val1); + Py_XDECREF(tb1); + } + else { + /* double exception! print a double-traceback... */ + PyObject *ecap; + PyErr_Fetch(&exc2, &val2, &tb2); + ecap = _cffi_start_error_capture(); + _my_PyErr_WriteUnraisable(exc1, val1, tb1, + "From cffi callback ", py_ob, + extra_error_line); + extra_error_line = ("\nDuring the call to 'onerror', " + "another exception occurred:\n\n"); + _my_PyErr_WriteUnraisable(exc2, val2, tb2, + NULL, NULL, extra_error_line); + _cffi_stop_error_capture(ecap); + } + } + goto done; + +#undef SIGNATURE +} + +static void invoke_callback(ffi_cif *cif, void *result, void **args, + void *userdata) +{ + save_errno(); + { + PyGILState_STATE state = gil_ensure(); + general_invoke_callback(1, result, (char *)args, userdata); + gil_release(state); + } + restore_errno(); +} + +static PyObject *prepare_callback_info_tuple(CTypeDescrObject *ct, + PyObject *ob, + PyObject *error_ob, + PyObject *onerror_ob, + int decode_args_from_libffi) +{ + CTypeDescrObject *ctresult; + PyObject *py_rawerr, *infotuple; + Py_ssize_t size; + + if (!(ct->ct_flags & CT_FUNCTIONPTR)) { + PyErr_Format(PyExc_TypeError, "expected a function ctype, got '%s'", + ct->ct_name); + return NULL; + } + if (!PyCallable_Check(ob)) { + PyErr_Format(PyExc_TypeError, + "expected a callable object, not %.200s", + Py_TYPE(ob)->tp_name); + return NULL; + } + if (onerror_ob != Py_None && !PyCallable_Check(onerror_ob)) { + PyErr_Format(PyExc_TypeError, + "expected a callable object for 'onerror', not %.200s", + Py_TYPE(onerror_ob)->tp_name); + return NULL; + } + + ctresult = (CTypeDescrObject *)PyTuple_GET_ITEM(ct->ct_stuff, 1); + size = ctresult->ct_size; + if (size < (Py_ssize_t)sizeof(ffi_arg)) + size = sizeof(ffi_arg); + py_rawerr = PyBytes_FromStringAndSize(NULL, size); + if (py_rawerr == NULL) + return NULL; + memset(PyBytes_AS_STRING(py_rawerr), 0, size); + if (error_ob != Py_None) { + if (convert_from_object_fficallback( + PyBytes_AS_STRING(py_rawerr), ctresult, error_ob, + decode_args_from_libffi) < 0) { + Py_DECREF(py_rawerr); + return NULL; + } + } + infotuple = Py_BuildValue("OOOO", ct, ob, py_rawerr, onerror_ob); + Py_DECREF(py_rawerr); + +#ifdef WITH_THREAD + /* We must setup the GIL here, in case the callback is invoked in + some other non-Pythonic thread. This is the same as ctypes. */ + PyEval_InitThreads(); +#endif + + return infotuple; +} + +static PyObject *b_callback(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + CDataObject_closure *cd; + PyObject *ob, *error_ob = Py_None, *onerror_ob = Py_None; + PyObject *infotuple; + cif_description_t *cif_descr; + ffi_closure *closure; + void *closure_exec; + + if (!PyArg_ParseTuple(args, "O!O|OO:callback", &CTypeDescr_Type, &ct, &ob, + &error_ob, &onerror_ob)) + return NULL; + + infotuple = prepare_callback_info_tuple(ct, ob, error_ob, onerror_ob, 1); + if (infotuple == NULL) + return NULL; + +#ifdef CFFI_TRUST_LIBFFI + closure = ffi_closure_alloc(sizeof(ffi_closure), &closure_exec); +#else + closure = cffi_closure_alloc(); + closure_exec = closure; +#endif + if (closure == NULL) { + Py_DECREF(infotuple); + return NULL; + } + cd = PyObject_GC_New(CDataObject_closure, &CDataOwningGC_Type); + if (cd == NULL) + goto error; + Py_INCREF(ct); + cd->head.c_type = ct; + cd->head.c_data = (char *)closure_exec; + cd->head.c_weakreflist = NULL; + cd->closure = closure; + PyObject_GC_Track(cd); + + cif_descr = (cif_description_t *)ct->ct_extra; + if (cif_descr == NULL) { + PyErr_Format(PyExc_NotImplementedError, + "%s: callback with unsupported argument or " + "return type or with '...'", ct->ct_name); + goto error; + } +#ifdef CFFI_TRUST_LIBFFI + if (ffi_prep_closure_loc(closure, &cif_descr->cif, + invoke_callback, infotuple, closure_exec) != FFI_OK) { +#else + if (ffi_prep_closure(closure, &cif_descr->cif, + invoke_callback, infotuple) != FFI_OK) { +#endif + PyErr_SetString(PyExc_SystemError, + "libffi failed to build this callback"); + goto error; + } + if (closure->user_data != infotuple) { + /* Issue #266. Should not occur, but could, if we are using + at runtime a version of libffi compiled with a different + 'ffi_closure' structure than the one we expect from ffi.h + (e.g. difference in details of the platform): a difference + in FFI_TRAMPOLINE_SIZE means that the 'user_data' field + ends up somewhere else, and so the test above fails. + */ + PyErr_SetString(PyExc_SystemError, + "ffi_prep_closure(): bad user_data (it seems that the " + "version of the libffi library seen at runtime is " + "different from the 'ffi.h' file seen at compile-time)"); + goto error; + } + return (PyObject *)cd; + + error: + closure->user_data = NULL; + if (cd == NULL) { +#ifdef CFFI_TRUST_LIBFFI + ffi_closure_free(closure); +#else + cffi_closure_free(closure); +#endif + } + else + Py_DECREF(cd); + Py_XDECREF(infotuple); + return NULL; +} + +static PyObject *b_new_enum_type(PyObject *self, PyObject *args) +{ + char *ename; + PyObject *enumerators, *enumvalues; + PyObject *dict1 = NULL, *dict2 = NULL, *combined = NULL, *tmpkey = NULL; + int name_size; + CTypeDescrObject *td, *basetd; + Py_ssize_t i, n; + + if (!PyArg_ParseTuple(args, "sO!O!O!:new_enum_type", + &ename, + &PyTuple_Type, &enumerators, + &PyTuple_Type, &enumvalues, + &CTypeDescr_Type, &basetd)) + return NULL; + + n = PyTuple_GET_SIZE(enumerators); + if (n != PyTuple_GET_SIZE(enumvalues)) { + PyErr_SetString(PyExc_ValueError, + "tuple args must have the same size"); + return NULL; + } + + if (!(basetd->ct_flags & (CT_PRIMITIVE_SIGNED|CT_PRIMITIVE_UNSIGNED))) { + PyErr_SetString(PyExc_TypeError, + "expected a primitive signed or unsigned base type"); + return NULL; + } + + dict1 = PyDict_New(); + if (dict1 == NULL) + goto error; + dict2 = PyDict_New(); + if (dict2 == NULL) + goto error; + + for (i=n; --i >= 0; ) { + long long lvalue; + PyObject *value = PyTuple_GET_ITEM(enumvalues, i); + tmpkey = PyTuple_GET_ITEM(enumerators, i); + Py_INCREF(tmpkey); + if (!PyText_Check(tmpkey)) { +#if PY_MAJOR_VERSION < 3 + if (PyUnicode_Check(tmpkey)) { + const char *text = PyText_AsUTF8(tmpkey); + if (text == NULL) + goto error; + Py_DECREF(tmpkey); + tmpkey = PyString_FromString(text); + if (tmpkey == NULL) + goto error; + } + else +#endif + { + PyErr_SetString(PyExc_TypeError, + "enumerators must be a list of strings"); + goto error; + } + } + if (convert_from_object((char*)&lvalue, basetd, value) < 0) + goto error; /* out-of-range or badly typed 'value' */ + if (PyDict_SetItem(dict1, tmpkey, value) < 0) + goto error; + if (PyDict_SetItem(dict2, value, tmpkey) < 0) + goto error; + Py_DECREF(tmpkey); + tmpkey = NULL; + } + + combined = PyTuple_Pack(2, dict1, dict2); + if (combined == NULL) + goto error; + + Py_CLEAR(dict2); + Py_CLEAR(dict1); + + name_size = strlen(ename) + 1; + td = ctypedescr_new(name_size); + if (td == NULL) + goto error; + + memcpy(td->ct_name, ename, name_size); + td->ct_stuff = combined; + td->ct_size = basetd->ct_size; + td->ct_length = basetd->ct_length; /* alignment */ + td->ct_extra = basetd->ct_extra; /* ffi type */ + td->ct_flags = basetd->ct_flags | CT_IS_ENUM; + td->ct_name_position = name_size - 1; + return (PyObject *)td; + + error: + Py_XDECREF(tmpkey); + Py_XDECREF(combined); + Py_XDECREF(dict2); + Py_XDECREF(dict1); + return NULL; +} + +static PyObject *b_alignof(PyObject *self, PyObject *arg) +{ + int align; + if (!CTypeDescr_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "expected a 'ctype' object"); + return NULL; + } + align = get_alignment((CTypeDescrObject *)arg); + if (align < 0) + return NULL; + return PyInt_FromLong(align); +} + +static Py_ssize_t direct_sizeof_cdata(CDataObject *cd) +{ + Py_ssize_t size; + if (cd->c_type->ct_flags & CT_ARRAY) + size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + else { + size = -1; + if (cd->c_type->ct_flags & (CT_STRUCT | CT_UNION)) + size = _cdata_var_byte_size(cd); + if (size < 0) + size = cd->c_type->ct_size; + } + return size; +} + +static PyObject *b_sizeof(PyObject *self, PyObject *arg) +{ + Py_ssize_t size; + + if (CData_Check(arg)) { + size = direct_sizeof_cdata((CDataObject *)arg); + } + else if (CTypeDescr_Check(arg)) { + size = ((CTypeDescrObject *)arg)->ct_size; + if (size < 0) { + PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown size", + ((CTypeDescrObject *)arg)->ct_name); + return NULL; + } + } + else { + PyErr_SetString(PyExc_TypeError, + "expected a 'cdata' or 'ctype' object"); + return NULL; + } + return PyInt_FromSsize_t(size); +} + +static PyObject *b_typeof(PyObject *self, PyObject *arg) +{ + PyObject *res; + + if (!CData_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); + return NULL; + } + res = (PyObject *)((CDataObject *)arg)->c_type; + Py_INCREF(res); + return res; +} + +static CTypeDescrObject *direct_typeoffsetof(CTypeDescrObject *ct, + PyObject *fieldname, + int following, Py_ssize_t *offset) +{ + /* Does not return a new reference! */ + CTypeDescrObject *res; + CFieldObject *cf; + + if (PyTextAny_Check(fieldname)) { + if (!following && (ct->ct_flags & CT_POINTER)) + ct = ct->ct_itemdescr; + if (!(ct->ct_flags & (CT_STRUCT|CT_UNION))) { + PyErr_SetString(PyExc_TypeError, + "with a field name argument, expected a " + "struct or union ctype"); + return NULL; + } + if (force_lazy_struct(ct) <= 0) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_TypeError, "struct/union is opaque"); + return NULL; + } + cf = (CFieldObject *)PyDict_GetItem(ct->ct_stuff, fieldname); + if (cf == NULL) { + PyErr_SetObject(PyExc_KeyError, fieldname); + return NULL; + } + if (cf->cf_bitshift >= 0) { + PyErr_SetString(PyExc_TypeError, "not supported for bitfields"); + return NULL; + } + res = cf->cf_type; + *offset = cf->cf_offset; + } + else { + Py_ssize_t index = PyInt_AsSsize_t(fieldname); + if (index < 0 && PyErr_Occurred()) { + PyErr_SetString(PyExc_TypeError, + "field name or array index expected"); + return NULL; + } + + if (!(ct->ct_flags & (CT_ARRAY|CT_POINTER)) || + ct->ct_itemdescr->ct_size < 0) { + PyErr_SetString(PyExc_TypeError, "with an integer argument, " + "expected an array ctype or a " + "pointer to non-opaque"); + return NULL; + } + res = ct->ct_itemdescr; + *offset = MUL_WRAPAROUND(index, ct->ct_itemdescr->ct_size); + if ((*offset / ct->ct_itemdescr->ct_size) != index) { + PyErr_SetString(PyExc_OverflowError, + "array offset would overflow a Py_ssize_t"); + return NULL; + } + } + return res; +} + +static PyObject *b_typeoffsetof(PyObject *self, PyObject *args) +{ + PyObject *res, *fieldname; + CTypeDescrObject *ct; + Py_ssize_t offset; + int following = 0; + + if (!PyArg_ParseTuple(args, "O!O|i:typeoffsetof", + &CTypeDescr_Type, &ct, &fieldname, &following)) + return NULL; + + res = (PyObject *)direct_typeoffsetof(ct, fieldname, following, &offset); + if (res == NULL) + return NULL; + + return Py_BuildValue("(On)", res, offset); +} + +static PyObject *b_rawaddressof(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + CDataObject *cd; + Py_ssize_t offset; + int accepted_flags; + + if (!PyArg_ParseTuple(args, "O!O!n:rawaddressof", + &CTypeDescr_Type, &ct, + &CData_Type, &cd, + &offset)) + return NULL; + + accepted_flags = CT_STRUCT | CT_UNION | CT_ARRAY | CT_POINTER; + if ((cd->c_type->ct_flags & accepted_flags) == 0) { + PyErr_SetString(PyExc_TypeError, + "expected a cdata struct/union/array/pointer object"); + return NULL; + } + if ((ct->ct_flags & CT_POINTER) == 0) { + PyErr_SetString(PyExc_TypeError, + "expected a pointer ctype"); + return NULL; + } + return new_simple_cdata(cd->c_data + offset, ct); +} + +static PyObject *b_getcname(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + char *replace_with, *p, *s; + Py_ssize_t namelen, replacelen; + + if (!PyArg_ParseTuple(args, "O!s:getcname", + &CTypeDescr_Type, &ct, &replace_with)) + return NULL; + + namelen = strlen(ct->ct_name); + replacelen = strlen(replace_with); + s = p = alloca(namelen + replacelen + 1); + memcpy(p, ct->ct_name, ct->ct_name_position); + p += ct->ct_name_position; + memcpy(p, replace_with, replacelen); + p += replacelen; + memcpy(p, ct->ct_name + ct->ct_name_position, + namelen - ct->ct_name_position); + + return PyText_FromStringAndSize(s, namelen + replacelen); +} + +static PyObject *b_string(PyObject *self, PyObject *args, PyObject *kwds) +{ + CDataObject *cd; + Py_ssize_t maxlen = -1; + static char *keywords[] = {"cdata", "maxlen", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:string", keywords, + &CData_Type, &cd, &maxlen)) + return NULL; + + if (cd->c_type->ct_itemdescr != NULL && + cd->c_type->ct_itemdescr->ct_flags & (CT_PRIMITIVE_CHAR | + CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED) && + !(cd->c_type->ct_itemdescr->ct_flags & CT_IS_BOOL)) { + Py_ssize_t length = maxlen; + if (cd->c_data == NULL) { + PyObject *s = cdata_repr(cd); + if (s != NULL) { + PyErr_Format(PyExc_RuntimeError, + "cannot use string() on %s", + PyText_AS_UTF8(s)); + Py_DECREF(s); + } + return NULL; + } + if (length < 0 && cd->c_type->ct_flags & CT_ARRAY) { + length = get_array_length(cd); + } + if (cd->c_type->ct_itemdescr->ct_size == sizeof(char)) { + const char *start = cd->c_data; + if (length < 0) { + /*READ(start, 1)*/ + length = strlen(start); + /*READ(start, length)*/ + } + else { + const char *end; + /*READ(start, length)*/ + end = (const char *)memchr(start, 0, length); + if (end != NULL) + length = end - start; + } + return PyBytes_FromStringAndSize(start, length); + } + else if (cd->c_type->ct_itemdescr->ct_flags & CT_PRIMITIVE_CHAR) { + switch (cd->c_type->ct_itemdescr->ct_size) { + case 2: { + const cffi_char16_t *start = (cffi_char16_t *)cd->c_data; + if (length < 0) { + /*READ(start, 2)*/ + length = 0; + while (start[length]) + length++; + /*READ(start, 2 * length)*/ + } + else { + /*READ(start, 2 * length)*/ + maxlen = length; + length = 0; + while (length < maxlen && start[length]) + length++; + } + return _my_PyUnicode_FromChar16(start, length); + } + case 4: { + const cffi_char32_t *start = (cffi_char32_t *)cd->c_data; + if (length < 0) { + /*READ(start, 4)*/ + length = 0; + while (start[length]) + length++; + /*READ(start, 4 * length)*/ + } + else { + /*READ(start, 4 * length)*/ + maxlen = length; + length = 0; + while (length < maxlen && start[length]) + length++; + } + return _my_PyUnicode_FromChar32(start, length); + } + } + } + } + else if (cd->c_type->ct_flags & CT_IS_ENUM) { + return convert_cdata_to_enum_string(cd, 0); + } + else if (cd->c_type->ct_flags & CT_IS_BOOL) { + /* fall through to TypeError */ + } + else if (cd->c_type->ct_flags & (CT_PRIMITIVE_CHAR | + CT_PRIMITIVE_SIGNED | + CT_PRIMITIVE_UNSIGNED)) { + /*READ(cd->c_data, cd->c_type->ct_size)*/ + if (cd->c_type->ct_size == sizeof(char)) + return PyBytes_FromStringAndSize(cd->c_data, 1); + else if (cd->c_type->ct_flags & CT_PRIMITIVE_CHAR) { + switch (cd->c_type->ct_size) { + case 2: + return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data, 1); + case 4: + return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data, 1); + } + } + } + PyErr_Format(PyExc_TypeError, "string(): unexpected cdata '%s' argument", + cd->c_type->ct_name); + return NULL; +} + +static PyObject *b_unpack(PyObject *self, PyObject *args, PyObject *kwds) +{ + CDataObject *cd; + CTypeDescrObject *ctitem; + Py_ssize_t i, length, itemsize; + PyObject *result; + char *src; + int casenum; + static char *keywords[] = {"cdata", "length", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!n:unpack", keywords, + &CData_Type, &cd, &length)) + return NULL; + + if (!(cd->c_type->ct_flags & (CT_ARRAY|CT_POINTER))) { + PyErr_Format(PyExc_TypeError, + "expected a pointer or array, got '%s'", + cd->c_type->ct_name); + return NULL; + } + if (length < 0) { + PyErr_SetString(PyExc_ValueError, "'length' cannot be negative"); + return NULL; + } + if (cd->c_data == NULL) { + PyObject *s = cdata_repr(cd); + if (s != NULL) { + PyErr_Format(PyExc_RuntimeError, + "cannot use unpack() on %s", + PyText_AS_UTF8(s)); + Py_DECREF(s); + } + return NULL; + } + + /* byte- and unicode strings */ + ctitem = cd->c_type->ct_itemdescr; + if (ctitem->ct_flags & CT_PRIMITIVE_CHAR) { + switch (ctitem->ct_size) { + case sizeof(char): + return PyBytes_FromStringAndSize(cd->c_data, length); + case 2: + return _my_PyUnicode_FromChar16((cffi_char16_t *)cd->c_data,length); + case 4: + return _my_PyUnicode_FromChar32((cffi_char32_t *)cd->c_data,length); + } + } + + /* else, the result is a list. This implementation should be + equivalent to but much faster than '[p[i] for i in range(length)]'. + (Note that on PyPy, 'list(p[0:length])' should be equally fast, + but arguably, finding out that there *is* such an unexpected way + to write things down is the real problem.) + */ + result = PyList_New(length); + if (result == NULL) + return NULL; + + src = cd->c_data; + itemsize = ctitem->ct_size; + if (itemsize < 0) { + Py_DECREF(result); + PyErr_Format(PyExc_ValueError, "'%s' points to items of unknown size", + cd->c_type->ct_name); + return NULL; + } + + /* Determine some common fast-paths for the loop below. The case -1 + is the fall-back, which always gives the right answer. */ + +#define ALIGNMENT_CHECK(align) \ + (((align) & ((align) - 1)) == 0 && \ + (((uintptr_t)src) & ((align) - 1)) == 0) + + casenum = -1; + + if ((ctitem->ct_flags & CT_PRIMITIVE_ANY) && + ALIGNMENT_CHECK(ctitem->ct_length)) { + /* Source data is fully aligned; we can directly read without + memcpy(). The unaligned case is expected to be rare; in + this situation it is ok to fall back to the general + convert_to_object() in the loop. For now we also use this + fall-back for types that are too large. + */ + if (ctitem->ct_flags & CT_PRIMITIVE_SIGNED) { + if (itemsize == sizeof(long)) casenum = 3; + else if (itemsize == sizeof(int)) casenum = 2; + else if (itemsize == sizeof(short)) casenum = 1; + else if (itemsize == sizeof(signed char)) casenum = 0; + } + else if (ctitem->ct_flags & CT_PRIMITIVE_UNSIGNED) { + /* Note: we never pick case 6 if sizeof(int) == sizeof(long), + so that case 6 below can assume that the 'unsigned int' result + would always fit in a 'signed long'. */ + if (ctitem->ct_flags & CT_IS_BOOL) casenum = 11; + else if (itemsize == sizeof(unsigned long)) casenum = 7; + else if (itemsize == sizeof(unsigned int)) casenum = 6; + else if (itemsize == sizeof(unsigned short)) casenum = 5; + else if (itemsize == sizeof(unsigned char)) casenum = 4; + } + else if (ctitem->ct_flags & CT_PRIMITIVE_FLOAT) { + if (itemsize == sizeof(double)) casenum = 9; + else if (itemsize == sizeof(float)) casenum = 8; + } + } + else if (ctitem->ct_flags & (CT_POINTER | CT_FUNCTIONPTR)) { + casenum = 10; /* any pointer */ + } +#undef ALIGNMENT_CHECK + + for (i = 0; i < length; i++) { + PyObject *x; + switch (casenum) { + /* general case */ + default: x = convert_to_object(src, ctitem); break; + + /* special cases for performance only */ + case 0: x = PyInt_FromLong(*(signed char *)src); break; + case 1: x = PyInt_FromLong(*(short *)src); break; + case 2: x = PyInt_FromLong(*(int *)src); break; + case 3: x = PyInt_FromLong(*(long *)src); break; + case 4: x = PyInt_FromLong(*(unsigned char *)src); break; + case 5: x = PyInt_FromLong(*(unsigned short *)src); break; + case 6: x = PyInt_FromLong((long)*(unsigned int *)src); break; + case 7: x = PyLong_FromUnsignedLong(*(unsigned long *)src); break; + case 8: x = PyFloat_FromDouble(*(float *)src); break; + case 9: x = PyFloat_FromDouble(*(double *)src); break; + case 10: x = new_simple_cdata(*(char **)src, ctitem); break; + case 11: + switch (*(unsigned char *)src) { + case 0: x = Py_False; Py_INCREF(x); break; + case 1: x = Py_True; Py_INCREF(x); break; + default: x = convert_to_object(src, ctitem); /* error */ + } + break; + } + if (x == NULL) { + Py_DECREF(result); + return NULL; + } + PyList_SET_ITEM(result, i, x); + src += itemsize; + } + return result; +} + +static PyObject * +b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + /* this is the constructor of the type implemented in minibuffer.h */ + CDataObject *cd; + Py_ssize_t size = -1; + static char *keywords[] = {"cdata", "size", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|n:buffer", keywords, + &CData_Type, &cd, &size)) + return NULL; + + if (size < 0) + size = _cdata_var_byte_size(cd); + + if (cd->c_type->ct_flags & CT_POINTER) { + if (size < 0) + size = cd->c_type->ct_itemdescr->ct_size; + } + else if (cd->c_type->ct_flags & CT_ARRAY) { + if (size < 0) + size = get_array_length(cd) * cd->c_type->ct_itemdescr->ct_size; + } + else { + PyErr_Format(PyExc_TypeError, + "expected a pointer or array cdata, got '%s'", + cd->c_type->ct_name); + return NULL; + } + if (size < 0) { + PyErr_Format(PyExc_TypeError, + "don't know the size pointed to by '%s'", + cd->c_type->ct_name); + return NULL; + } + /*WRITE(cd->c_data, size)*/ + return minibuffer_new(cd->c_data, size, (PyObject *)cd); +} + +static PyObject *b_get_errno(PyObject *self, PyObject *noarg) +{ + int err; + restore_errno_only(); + err = errno; + errno = 0; + return PyInt_FromLong(err); +} + +static PyObject *b_set_errno(PyObject *self, PyObject *arg) +{ + long ival = PyInt_AsLong(arg); + if (ival == -1 && PyErr_Occurred()) + return NULL; + else if (ival < INT_MIN || ival > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, "errno value too large"); + return NULL; + } + errno = (int)ival; + save_errno_only(); + errno = 0; + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *newp_handle(CTypeDescrObject *ct_voidp, PyObject *x) +{ + CDataObject_own_structptr *cd; + cd = (CDataObject_own_structptr *)PyObject_GC_New(CDataObject_own_structptr, + &CDataOwningGC_Type); + if (cd == NULL) + return NULL; + Py_INCREF(ct_voidp); + cd->head.c_type = ct_voidp; + cd->head.c_data = (char *)cd; + cd->head.c_weakreflist = NULL; + Py_INCREF(x); + cd->structobj = x; + PyObject_GC_Track(cd); + return (PyObject *)cd; +} + +static PyObject *b_newp_handle(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *x; + if (!PyArg_ParseTuple(args, "O!O", &CTypeDescr_Type, &ct, &x)) + return NULL; + + if (!(ct->ct_flags & CT_IS_VOID_PTR)) { + PyErr_Format(PyExc_TypeError, "needs 'void *', got '%s'", ct->ct_name); + return NULL; + } + + return newp_handle(ct, x); +} + +static PyObject *b_from_handle(PyObject *self, PyObject *arg) +{ + CTypeDescrObject *ct; + CDataObject_own_structptr *orgcd; + PyObject *x; + if (!CData_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); + return NULL; + } + ct = ((CDataObject *)arg)->c_type; + if (!(ct->ct_flags & CT_IS_VOIDCHAR_PTR)) { + PyErr_Format(PyExc_TypeError, + "expected a 'cdata' object with a 'void *' out of " + "new_handle(), got '%s'", ct->ct_name); + return NULL; + } + orgcd = (CDataObject_own_structptr *)((CDataObject *)arg)->c_data; + if (!orgcd) { + PyErr_SetString(PyExc_RuntimeError, + "cannot use from_handle() on NULL pointer"); + return NULL; + } + if (Py_REFCNT(orgcd) <= 0 || Py_TYPE(orgcd) != &CDataOwningGC_Type) { + Py_FatalError("ffi.from_handle() detected that the address passed " + "points to garbage. If it is really the result of " + "ffi.new_handle(), then the Python object has already " + "been garbage collected"); + } + x = orgcd->structobj; + Py_INCREF(x); + return x; +} + +static int _my_PyObject_GetContiguousBuffer(PyObject *x, Py_buffer *view, + int writable_only) +{ +#if PY_MAJOR_VERSION < 3 + /* Some objects only support the buffer interface and CPython doesn't + translate it into the memoryview interface, mess. Hack a very + minimal content for 'view'. Don't care if the other fields are + uninitialized: we only call PyBuffer_Release(), which only reads + 'view->obj'. */ + PyBufferProcs *pb = x->ob_type->tp_as_buffer; + if (pb && !pb->bf_releasebuffer) { + /* we used to try all three in some vaguely sensible order, + i.e. first the write. But trying to call the write on a + read-only buffer fails with TypeError. So we use a less- + sensible order now. See test_from_buffer_more_cases. + + If 'writable_only', we only try bf_getwritebuffer. + */ + readbufferproc proc = NULL; + if (!writable_only) { + proc = (readbufferproc)pb->bf_getreadbuffer; + if (!proc) + proc = (readbufferproc)pb->bf_getcharbuffer; + } + if (!proc) + proc = (readbufferproc)pb->bf_getwritebuffer; + + if (proc && pb->bf_getsegcount) { + if ((*pb->bf_getsegcount)(x, NULL) != 1) { + PyErr_SetString(PyExc_TypeError, + "expected a single-segment buffer object"); + return -1; + } + view->len = (*proc)(x, 0, &view->buf); + if (view->len < 0) + return -1; + view->obj = x; + Py_INCREF(x); + return 0; + } + } +#endif + + if (PyObject_GetBuffer(x, view, writable_only ? PyBUF_WRITABLE + : PyBUF_SIMPLE) < 0) + return -1; + + if (!PyBuffer_IsContiguous(view, 'A')) { + PyBuffer_Release(view); + PyErr_SetString(PyExc_TypeError, "contiguous buffer expected"); + return -1; + } + return 0; +} + +static PyObject *direct_from_buffer(CTypeDescrObject *ct, PyObject *x, + int require_writable) +{ + CDataObject *cd; + Py_buffer *view; + Py_ssize_t arraylength; + + if (!(ct->ct_flags & CT_ARRAY)) { + PyErr_Format(PyExc_TypeError, "expected an array ctype, got '%s'", + ct->ct_name); + return NULL; + } + + /* PyPy 5.7 can obtain buffers for string (python 2) + or bytes (python 3). from_buffer(u"foo") is disallowed. + */ + if (PyUnicode_Check(x)) { + PyErr_SetString(PyExc_TypeError, + "from_buffer() cannot return the address " + "of a unicode object"); + return NULL; + } + + view = PyObject_Malloc(sizeof(Py_buffer)); + if (view == NULL) { + PyErr_NoMemory(); + return NULL; + } + if (_my_PyObject_GetContiguousBuffer(x, view, require_writable) < 0) + goto error1; + + if (ct->ct_length >= 0) { + /* it's an array with a fixed length; make sure that the + buffer contains enough bytes. */ + if (view->len < ct->ct_size) { + PyErr_Format(PyExc_ValueError, + "buffer is too small (%zd bytes) for '%s' (%zd bytes)", + view->len, ct->ct_name, ct->ct_size); + goto error2; + } + arraylength = ct->ct_length; + } + else { + /* it's an open 'array[]' */ + if (ct->ct_itemdescr->ct_size == 1) { + /* fast path, performance only */ + arraylength = view->len; + } + else if (ct->ct_itemdescr->ct_size > 0) { + /* give it as many items as fit the buffer. Ignore a + partial last element. */ + arraylength = view->len / ct->ct_itemdescr->ct_size; + } + else { + /* it's an array 'empty[]'. Unsupported obscure case: + the problem is that setting the length of the result + to anything large (like SSIZE_T_MAX) is dangerous, + because if someone tries to loop over it, it will + turn effectively into an infinite loop. */ + PyErr_Format(PyExc_ZeroDivisionError, + "from_buffer('%s', ..): the actual length of the array " + "cannot be computed", ct->ct_name); + goto error2; + } + } + + cd = (CDataObject *)PyObject_GC_New(CDataObject_owngc_frombuf, + &CDataOwningGC_Type); + if (cd == NULL) + goto error2; + + Py_INCREF(ct); + cd->c_type = ct; + cd->c_data = view->buf; + cd->c_weakreflist = NULL; + ((CDataObject_owngc_frombuf *)cd)->length = arraylength; + ((CDataObject_owngc_frombuf *)cd)->bufferview = view; + PyObject_GC_Track(cd); + return (PyObject *)cd; + + error2: + PyBuffer_Release(view); + error1: + PyObject_Free(view); + return NULL; +} + +static PyObject *b_from_buffer(PyObject *self, PyObject *args) +{ + CTypeDescrObject *ct; + PyObject *x; + int require_writable = 0; + + if (!PyArg_ParseTuple(args, "O!O|i", &CTypeDescr_Type, &ct, &x, + &require_writable)) + return NULL; + + return direct_from_buffer(ct, x, require_writable); +} + +static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only) +{ + if (CData_Check(x)) { + CTypeDescrObject *ct = ((CDataObject *)x)->c_type; + if (!(ct->ct_flags & (CT_POINTER|CT_ARRAY))) { + PyErr_Format(PyExc_TypeError, + "expected a pointer or array ctype, got '%s'", + ct->ct_name); + return -1; + } + view->buf = ((CDataObject *)x)->c_data; + view->obj = NULL; + return 0; + } + else { + return _my_PyObject_GetContiguousBuffer(x, view, writable_only); + } +} + +static PyObject *b_memmove(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *dest_obj, *src_obj; + Py_buffer dest_view, src_view; + Py_ssize_t n; + static char *keywords[] = {"dest", "src", "n", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOn", keywords, + &dest_obj, &src_obj, &n)) + return NULL; + if (n < 0) { + PyErr_SetString(PyExc_ValueError, "negative size"); + return NULL; + } + + if (_fetch_as_buffer(src_obj, &src_view, 0) < 0) { + return NULL; + } + if (_fetch_as_buffer(dest_obj, &dest_view, 1) < 0) { + PyBuffer_Release(&src_view); + return NULL; + } + + memmove(dest_view.buf, src_view.buf, n); + + PyBuffer_Release(&dest_view); + PyBuffer_Release(&src_view); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *b__get_types(PyObject *self, PyObject *noarg) +{ + return PyTuple_Pack(2, (PyObject *)&CData_Type, + (PyObject *)&CTypeDescr_Type); +} + +/* forward, in commontypes.c */ +static PyObject *b__get_common_types(PyObject *self, PyObject *arg); + +static PyObject *b_gcp(PyObject *self, PyObject *args, PyObject *kwds) +{ + CDataObject *cd; + CDataObject *origobj; + PyObject *destructor; + Py_ssize_t ignored; /* for pypy */ + static char *keywords[] = {"cdata", "destructor", "size", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!O|n:gc", keywords, + &CData_Type, &origobj, &destructor, + &ignored)) + return NULL; + + if (destructor == Py_None) { + if (!PyObject_TypeCheck(origobj, &CDataGCP_Type)) { + PyErr_SetString(PyExc_TypeError, + "Can remove destructor only on a object " + "previously returned by ffi.gc()"); + return NULL; + } + Py_CLEAR(((CDataObject_gcp *)origobj)->destructor); + Py_RETURN_NONE; + } + + cd = allocate_gcp_object(origobj, origobj->c_type, destructor); + return (PyObject *)cd; +} + +static PyObject *b_release(PyObject *self, PyObject *arg) +{ + if (!CData_Check(arg)) { + PyErr_SetString(PyExc_TypeError, "expected a 'cdata' object"); + return NULL; + } + return cdata_exit(arg, NULL); +} + +/************************************************************/ + +static char _testfunc0(char a, char b) +{ + return a + b; +} +static long _testfunc1(int a, long b) +{ + return (long)a + b; +} +static PY_LONG_LONG _testfunc2(PY_LONG_LONG a, PY_LONG_LONG b) +{ + return a + b; +} +static double _testfunc3(float a, double b) +{ + return a + b; +} +static float _testfunc4(float a, double b) +{ + return (float)(a + b); +} +static void _testfunc5(void) +{ + errno = errno + 15; +} +static int *_testfunc6(int *x) +{ + static int y; + y = *x - 1000; + return &y; +} +struct _testfunc7_s { unsigned char a1; short a2; }; +static short _testfunc7(struct _testfunc7_s inlined) +{ + return inlined.a1 + inlined.a2; +} +static int _testfunc9(int num, ...) +{ + va_list vargs; + int i, total = 0; + va_start(vargs, num); + for (i=0; i<num; i++) { + int value = va_arg(vargs, int); + if (value == 0) + value = -66666666; + total += value; + } + va_end(vargs); + return total; +} + +static struct _testfunc7_s _testfunc10(int n) +{ + struct _testfunc7_s result; + result.a1 = n; + result.a2 = n * n; + return result; +} + +struct _testfunc11_s { int a1, a2; }; +static struct _testfunc11_s _testfunc11(int n) +{ + struct _testfunc11_s result; + result.a1 = n; + result.a2 = n * n; + return result; +} + +struct _testfunc12_s { double a1; }; +static struct _testfunc12_s _testfunc12(int n) +{ + struct _testfunc12_s result; + result.a1 = n; + return result; +} + +struct _testfunc13_s { int a1, a2, a3; }; +static struct _testfunc13_s _testfunc13(int n) +{ + struct _testfunc13_s result; + result.a1 = n; + result.a2 = n * n; + result.a3 = n * n * n; + return result; +} + +struct _testfunc14_s { float a1; }; +static struct _testfunc14_s _testfunc14(int n) +{ + struct _testfunc14_s result; + result.a1 = (float)n; + return result; +} + +struct _testfunc15_s { float a1; int a2; }; +static struct _testfunc15_s _testfunc15(int n) +{ + struct _testfunc15_s result; + result.a1 = (float)n; + result.a2 = n * n; + return result; +} + +struct _testfunc16_s { float a1, a2; }; +static struct _testfunc16_s _testfunc16(int n) +{ + struct _testfunc16_s result; + result.a1 = (float)n; + result.a2 = -(float)n; + return result; +} + +struct _testfunc17_s { int a1; float a2; }; +static struct _testfunc17_s _testfunc17(int n) +{ + struct _testfunc17_s result; + result.a1 = n; + result.a2 = (float)n * (float)n; + return result; +} + +static int _testfunc18(struct _testfunc17_s *ptr) +{ + return ptr->a1 + (int)ptr->a2; +} + +static long double _testfunc19(long double x, int count) +{ + int i; + for (i=0; i<count; i++) { + x = 4*x - x*x; + } + return x; +} + +static short _testfunc20(struct _testfunc7_s *ptr) +{ + return ptr->a1 + ptr->a2; +} + +struct _testfunc21_s { int a, b, c, d, e, f, g, h, i, j; }; +static int _testfunc21(struct _testfunc21_s inlined) +{ + return ((inlined.a << 0) + + (inlined.b << 1) + + (inlined.c << 2) + + (inlined.d << 3) + + (inlined.e << 4) + + (inlined.f << 5) + + (inlined.g << 6) + + (inlined.h << 7) + + (inlined.i << 8) + + (inlined.j << 9)); +} + +struct _testfunc22_s { int a[10]; }; +static struct _testfunc22_s _testfunc22(struct _testfunc22_s s1, + struct _testfunc22_s s2) +{ + struct _testfunc22_s result; + int i; + for (i=0; i<10; i++) + result.a[i] = s1.a[i] - s2.a[i]; + return result; +} + +static int _testfunc23(char *p) +{ + if (p) + return 1000 * p[0]; + return -42; +} + +#if 0 /* libffi doesn't properly support complexes currently */ + /* also, MSVC might not support _Complex... */ + /* if this is enabled one day, remember to also add _Complex + * arguments in addition to return values. */ +static float _Complex _testfunc24(float a, float b) +{ + return a + I*2.0*b; +} +static double _Complex _testfunc25(double a, double b) +{ + return a + I*2.0*b; +} +#endif + +static PyObject *b__testfunc(PyObject *self, PyObject *args) +{ + /* for testing only */ + int i; + void *f; + if (!PyArg_ParseTuple(args, "i:_testfunc", &i)) + return NULL; + switch (i) { + case 0: f = &_testfunc0; break; + case 1: f = &_testfunc1; break; + case 2: f = &_testfunc2; break; + case 3: f = &_testfunc3; break; + case 4: f = &_testfunc4; break; + case 5: f = &_testfunc5; break; + case 6: f = &_testfunc6; break; + case 7: f = &_testfunc7; break; + case 8: f = stderr; break; + case 9: f = &_testfunc9; break; + case 10: f = &_testfunc10; break; + case 11: f = &_testfunc11; break; + case 12: f = &_testfunc12; break; + case 13: f = &_testfunc13; break; + case 14: f = &_testfunc14; break; + case 15: f = &_testfunc15; break; + case 16: f = &_testfunc16; break; + case 17: f = &_testfunc17; break; + case 18: f = &_testfunc18; break; + case 19: f = &_testfunc19; break; + case 20: f = &_testfunc20; break; + case 21: f = &_testfunc21; break; + case 22: f = &_testfunc22; break; + case 23: f = &_testfunc23; break; +#if 0 + case 24: f = &_testfunc24; break; + case 25: f = &_testfunc25; break; +#endif + default: + PyErr_SetNone(PyExc_ValueError); + return NULL; + } + return PyLong_FromVoidPtr(f); +} + +#if PY_MAJOR_VERSION < 3 +static Py_ssize_t _test_segcountproc(PyObject *o, Py_ssize_t *ignored) +{ + return 1; +} +static Py_ssize_t _test_getreadbuf(PyObject *o, Py_ssize_t i, void **r) +{ + static char buf[] = "RDB"; + *r = buf; + return 3; +} +static Py_ssize_t _test_getwritebuf(PyObject *o, Py_ssize_t i, void **r) +{ + static char buf[] = "WRB"; + *r = buf; + return 3; +} +static Py_ssize_t _test_getcharbuf(PyObject *o, Py_ssize_t i, char **r) +{ + static char buf[] = "CHB"; + *r = buf; + return 3; +} +#endif +static int _test_getbuf(PyObject *self, Py_buffer *view, int flags) +{ + static char buf[] = "GTB"; + return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/0, flags); +} +static int _test_getbuf_ro(PyObject *self, Py_buffer *view, int flags) +{ + static char buf[] = "ROB"; + return PyBuffer_FillInfo(view, self, buf, 3, /*readonly=*/1, flags); +} + + +static PyObject *b__testbuff(PyObject *self, PyObject *args) +{ + /* for testing only */ + int methods; + PyTypeObject *obj; + if (!PyArg_ParseTuple(args, "O!i|_testbuff", &PyType_Type, &obj, &methods)) + return NULL; + + assert(obj->tp_as_buffer != NULL); + +#if PY_MAJOR_VERSION < 3 + obj->tp_as_buffer->bf_getsegcount = &_test_segcountproc; + obj->tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER; + obj->tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; + if (methods & 1) obj->tp_as_buffer->bf_getreadbuffer = &_test_getreadbuf; + if (methods & 2) obj->tp_as_buffer->bf_getwritebuffer = &_test_getwritebuf; + if (methods & 4) obj->tp_as_buffer->bf_getcharbuffer = &_test_getcharbuf; +#endif + if (methods & 8) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf; + if (methods & 16) obj->tp_as_buffer->bf_getbuffer = &_test_getbuf_ro; + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject *b_init_cffi_1_0_external_module(PyObject *, PyObject *); +/* forward, see cffi1_module.c */ + + +static PyMethodDef FFIBackendMethods[] = { + {"load_library", b_load_library, METH_VARARGS}, + {"new_primitive_type", b_new_primitive_type, METH_VARARGS}, + {"new_pointer_type", b_new_pointer_type, METH_VARARGS}, + {"new_array_type", b_new_array_type, METH_VARARGS}, + {"new_void_type", b_new_void_type, METH_NOARGS}, + {"new_struct_type", b_new_struct_type, METH_VARARGS}, + {"new_union_type", b_new_union_type, METH_VARARGS}, + {"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS}, + {"new_function_type", b_new_function_type, METH_VARARGS}, + {"new_enum_type", b_new_enum_type, METH_VARARGS}, + {"newp", b_newp, METH_VARARGS}, + {"cast", b_cast, METH_VARARGS}, + {"callback", b_callback, METH_VARARGS}, + {"alignof", b_alignof, METH_O}, + {"sizeof", b_sizeof, METH_O}, + {"typeof", b_typeof, METH_O}, + {"typeoffsetof", b_typeoffsetof, METH_VARARGS}, + {"rawaddressof", b_rawaddressof, METH_VARARGS}, + {"getcname", b_getcname, METH_VARARGS}, + {"string", (PyCFunction)b_string, METH_VARARGS | METH_KEYWORDS}, + {"unpack", (PyCFunction)b_unpack, METH_VARARGS | METH_KEYWORDS}, + {"get_errno", b_get_errno, METH_NOARGS}, + {"set_errno", b_set_errno, METH_O}, + {"newp_handle", b_newp_handle, METH_VARARGS}, + {"from_handle", b_from_handle, METH_O}, + {"from_buffer", b_from_buffer, METH_VARARGS}, + {"memmove", (PyCFunction)b_memmove, METH_VARARGS | METH_KEYWORDS}, + {"gcp", (PyCFunction)b_gcp, METH_VARARGS | METH_KEYWORDS}, + {"release", b_release, METH_O}, +#ifdef MS_WIN32 + {"getwinerror", (PyCFunction)b_getwinerror, METH_VARARGS | METH_KEYWORDS}, +#endif + {"_get_types", b__get_types, METH_NOARGS}, + {"_get_common_types", b__get_common_types, METH_O}, + {"_testfunc", b__testfunc, METH_VARARGS}, + {"_testbuff", b__testbuff, METH_VARARGS}, + {"_init_cffi_1_0_external_module", b_init_cffi_1_0_external_module, METH_O}, + {NULL, NULL} /* Sentinel */ +}; + +/************************************************************/ +/* Functions used by '_cffi_N.so', the generated modules */ + +#define _cffi_to_c_SIGNED_FN(RETURNTYPE, SIZE) \ +static RETURNTYPE _cffi_to_c_i##SIZE(PyObject *obj) { \ + PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); \ + if ((tmp > (PY_LONG_LONG)((1ULL<<(SIZE-1)) - 1)) || \ + (tmp < (PY_LONG_LONG)(0ULL-(1ULL<<(SIZE-1))))) \ + if (!PyErr_Occurred()) \ + return (RETURNTYPE)_convert_overflow(obj, #SIZE "-bit int"); \ + return (RETURNTYPE)tmp; \ +} + +#define _cffi_to_c_UNSIGNED_FN(RETURNTYPE, SIZE) \ +static RETURNTYPE _cffi_to_c_u##SIZE(PyObject *obj) { \ + unsigned PY_LONG_LONG tmp = _my_PyLong_AsUnsignedLongLong(obj, 1); \ + if (tmp > ~(((unsigned PY_LONG_LONG)-2) << (SIZE-1))) \ + if (!PyErr_Occurred()) \ + return (RETURNTYPE)_convert_overflow(obj, \ + #SIZE "-bit unsigned int"); \ + return (RETURNTYPE)tmp; \ +} + +_cffi_to_c_SIGNED_FN(int, 8) +_cffi_to_c_SIGNED_FN(int, 16) +_cffi_to_c_SIGNED_FN(int, 32) +_cffi_to_c_SIGNED_FN(PY_LONG_LONG, 64) +_cffi_to_c_UNSIGNED_FN(int, 8) +_cffi_to_c_UNSIGNED_FN(int, 16) +_cffi_to_c_UNSIGNED_FN(unsigned int, 32) +_cffi_to_c_UNSIGNED_FN(unsigned PY_LONG_LONG, 64) + +static PyObject *_cffi_from_c_pointer(char *ptr, CTypeDescrObject *ct) +{ + return convert_to_object((char *)&ptr, ct); +} + +static char *_cffi_to_c_pointer(PyObject *obj, CTypeDescrObject *ct) +{ + char *result; + if (convert_from_object((char *)&result, ct, obj) < 0) { + if ((ct->ct_flags & CT_POINTER) && + (ct->ct_itemdescr->ct_flags & CT_IS_FILE) && + PyFile_Check(obj)) { + PyErr_Clear(); + return (char *)PyFile_AsFile(obj); + } + return NULL; + } + return result; +} + +static long double _cffi_to_c_long_double(PyObject *obj) +{ + if (CData_Check(obj) && + (((CDataObject *)obj)->c_type->ct_flags & CT_IS_LONGDOUBLE)) { + char *data = ((CDataObject *)obj)->c_data; + /*READ(data, sizeof(long double))*/ + return read_raw_longdouble_data(data); + } + else + return PyFloat_AsDouble(obj); +} + +static _Bool _cffi_to_c__Bool(PyObject *obj) +{ + PY_LONG_LONG tmp = _my_PyLong_AsLongLong(obj); + if (tmp == 0) + return 0; + else if (tmp == 1) + return 1; + else if (PyErr_Occurred()) + return (_Bool)-1; + else + return (_Bool)_convert_overflow(obj, "_Bool"); +} + +static PyObject *_cffi_get_struct_layout(Py_ssize_t nums[]) +{ + PyObject *result; + int count = 0; + while (nums[count] >= 0) + count++; + + result = PyList_New(count); + if (result == NULL) + return NULL; + + while (--count >= 0) { + PyObject *o = PyInt_FromSsize_t(nums[count]); + if (o == NULL) { + Py_DECREF(result); + return NULL; + } + PyList_SET_ITEM(result, count, o); + } + return result; +} + +static PyObject *_cffi_from_c_char(char x) { + return PyBytes_FromStringAndSize(&x, 1); +} + +/* backward-compatibility hack: instead of _cffi_to_c_char16_t() and + * _cffi_to_c_char32_t(), we have _cffi_to_c_wchar_t() handling whatever + * size is wchar_t, and _cffi_to_c_wchar3216_t() handling the opposite. + */ +#ifdef HAVE_WCHAR_H +typedef wchar_t cffi_wchar_t; +#else +typedef uint16_t cffi_wchar_t; /* random pick... */ +#endif + +static cffi_wchar_t _cffi_to_c_wchar_t(PyObject *init) +{ + if (sizeof(cffi_wchar_t) == 2) + return (cffi_wchar_t)_convert_to_char16_t(init); + else + return (cffi_wchar_t)_convert_to_char32_t(init); +} +static PyObject *_cffi_from_c_wchar_t(cffi_wchar_t x) { + if (sizeof(cffi_wchar_t) == 2) { + cffi_char16_t input = x; + return _my_PyUnicode_FromChar16(&input, 1); + } + else { + cffi_char32_t input = x; + return _my_PyUnicode_FromChar32(&input, 1); + } +} +static int _cffi_to_c_wchar3216_t(PyObject *init) +{ + if (sizeof(cffi_wchar_t) == 4) + return (int)_convert_to_char16_t(init); + else + return (int)_convert_to_char32_t(init); +} +static PyObject *_cffi_from_c_wchar3216_t(int x) { + if (sizeof(cffi_wchar_t) == 4) { + cffi_char16_t input = x; + return _my_PyUnicode_FromChar16(&input, 1); + } + else { + cffi_char32_t input = x; + return _my_PyUnicode_FromChar32(&input, 1); + } +} + +struct _cffi_externpy_s; /* forward declaration */ +static void cffi_call_python(struct _cffi_externpy_s *, char *args); + +static void *cffi_exports[] = { + NULL, + _cffi_to_c_i8, + _cffi_to_c_u8, + _cffi_to_c_i16, + _cffi_to_c_u16, + _cffi_to_c_i32, + _cffi_to_c_u32, + _cffi_to_c_i64, + _cffi_to_c_u64, + _convert_to_char, + _cffi_from_c_pointer, + _cffi_to_c_pointer, + _cffi_get_struct_layout, + restore_errno, + save_errno, + _cffi_from_c_char, + convert_to_object, + convert_from_object, + convert_struct_to_owning_object, + _cffi_to_c_wchar_t, + _cffi_from_c_wchar_t, + _cffi_to_c_long_double, + _cffi_to_c__Bool, + _prepare_pointer_call_argument, + convert_array_from_object, + cffi_call_python, + _cffi_to_c_wchar3216_t, + _cffi_from_c_wchar3216_t, +}; + +static struct { const char *name; int value; } all_dlopen_flags[] = { + { "RTLD_LAZY", RTLD_LAZY }, + { "RTLD_NOW", RTLD_NOW }, + { "RTLD_GLOBAL", RTLD_GLOBAL }, +#ifdef RTLD_LOCAL + { "RTLD_LOCAL", RTLD_LOCAL }, +#else + { "RTLD_LOCAL", 0 }, +#endif +#ifdef RTLD_NODELETE + { "RTLD_NODELETE", RTLD_NODELETE }, +#endif +#ifdef RTLD_NOLOAD + { "RTLD_NOLOAD", RTLD_NOLOAD }, +#endif +#ifdef RTLD_DEEPBIND + { "RTLD_DEEPBIND", RTLD_DEEPBIND }, +#endif + { NULL, 0 } +}; + + +/************************************************************/ + +#include "cffi1_module.c" + +/************************************************************/ + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef FFIBackendModuleDef = { + PyModuleDef_HEAD_INIT, + "_cffi_backend", + NULL, + -1, + FFIBackendMethods, + NULL, NULL, NULL, NULL +}; +#define INITERROR return NULL + +PyMODINIT_FUNC +PyInit__cffi_backend(void) +#else +#define INITERROR return + +PyMODINIT_FUNC +init_cffi_backend(void) +#endif +{ + PyObject *m, *v; + int i; + static char init_done = 0; + + v = PySys_GetObject("version"); + if (v == NULL || !PyText_Check(v) || + strncmp(PyText_AS_UTF8(v), PY_VERSION, 3) != 0) { + PyErr_Format(PyExc_ImportError, + "this module was compiled for Python %c%c%c", + PY_VERSION[0], PY_VERSION[1], PY_VERSION[2]); + INITERROR; + } + +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&FFIBackendModuleDef); +#else + m = Py_InitModule("_cffi_backend", FFIBackendMethods); +#endif + + if (m == NULL) + INITERROR; + + if (unique_cache == NULL) { + unique_cache = PyDict_New(); + if (unique_cache == NULL) + INITERROR; + } + + if (PyType_Ready(&dl_type) < 0) + INITERROR; + if (PyType_Ready(&CTypeDescr_Type) < 0) + INITERROR; + if (PyType_Ready(&CField_Type) < 0) + INITERROR; + if (PyType_Ready(&CData_Type) < 0) + INITERROR; + if (PyType_Ready(&CDataOwning_Type) < 0) + INITERROR; + if (PyType_Ready(&CDataOwningGC_Type) < 0) + INITERROR; + if (PyType_Ready(&CDataGCP_Type) < 0) + INITERROR; + if (PyType_Ready(&CDataIter_Type) < 0) + INITERROR; + if (PyType_Ready(&MiniBuffer_Type) < 0) + INITERROR; + + if (!init_done) { + v = PyText_FromString("_cffi_backend"); + if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, + "__module__", v) < 0) + INITERROR; + v = PyText_FromString("<cdata>"); + if (v == NULL || PyDict_SetItemString(CData_Type.tp_dict, + "__name__", v) < 0) + INITERROR; + init_done = 1; + } + + /* this is for backward compatibility only */ + v = PyCapsule_New((void *)cffi_exports, "cffi", NULL); + if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0) + INITERROR; + + v = PyText_FromString(CFFI_VERSION); + if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0) + INITERROR; + + if (PyModule_AddIntConstant(m, "FFI_DEFAULT_ABI", FFI_DEFAULT_ABI) < 0 || +#if defined(MS_WIN32) && !defined(_WIN64) + PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 || +#endif + PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 || + +#ifdef MS_WIN32 +# ifdef _WIN64 + PyModule_AddIntConstant(m, "_WIN", 64) < 0 || /* win64 */ +# else + PyModule_AddIntConstant(m, "_WIN", 32) < 0 || /* win32 */ +# endif +#endif + 0) + INITERROR; + + for (i = 0; all_dlopen_flags[i].name != NULL; i++) { + if (PyModule_AddIntConstant(m, + all_dlopen_flags[i].name, + all_dlopen_flags[i].value) < 0) + INITERROR; + } + + Py_INCREF(&MiniBuffer_Type); + if (PyModule_AddObject(m, "buffer", (PyObject *)&MiniBuffer_Type) < 0) + INITERROR; + + init_cffi_tls(); + if (PyErr_Occurred()) + INITERROR; + init_cffi_tls_zombie(); + if (PyErr_Occurred()) + INITERROR; + + if (init_ffi_lib(m) < 0) + INITERROR; + +#if PY_MAJOR_VERSION >= 3 + if (init_file_emulator() < 0) + INITERROR; + return m; +#endif +} diff --git a/c/_cffi_backend.so b/c/_cffi_backend.so Binary files differnew file mode 100644 index 0000000..7055ae6 --- /dev/null +++ b/c/_cffi_backend.so diff --git a/c/_dummy_file_cffi_backend.py b/c/_dummy_file_cffi_backend.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/c/_dummy_file_cffi_backend.py diff --git a/c/_dummy_file_libffi.py b/c/_dummy_file_libffi.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/c/_dummy_file_libffi.py diff --git a/c/call_python.c b/c/call_python.c new file mode 100644 index 0000000..8fdcb90 --- /dev/null +++ b/c/call_python.c @@ -0,0 +1,278 @@ +#if PY_VERSION_HEX >= 0x03080000 +# define Py_BUILD_CORE +/* for access to the fields of PyInterpreterState */ +# include "internal/pycore_pystate.h" +# undef Py_BUILD_CORE +#endif + +static PyObject *_get_interpstate_dict(void) +{ + /* Hack around to return a dict that is subinterpreter-local. + Does not return a new reference. Returns NULL in case of + error, but without setting any exception. (If called late + during shutdown, we *can't* set an exception!) + */ + static PyObject *attr_name = NULL; + PyThreadState *tstate; + PyObject *d, *builtins; + int err; + + tstate = PyThreadState_GET(); + if (tstate == NULL) { + /* no thread state! */ + return NULL; + } + + builtins = tstate->interp->builtins; + if (builtins == NULL) { + /* subinterpreter was cleared already, or is being cleared right now, + to a point that is too much for us to continue */ + return NULL; + } + + /* from there on, we know the (sub-)interpreter is still valid */ + + if (attr_name == NULL) { + attr_name = PyText_InternFromString("__cffi_backend_extern_py"); + if (attr_name == NULL) + goto error; + } + + d = PyDict_GetItem(builtins, attr_name); + if (d == NULL) { + d = PyDict_New(); + if (d == NULL) + goto error; + err = PyDict_SetItem(builtins, attr_name, d); + Py_DECREF(d); /* if successful, there is one ref left in builtins */ + if (err < 0) + goto error; + } + return d; + + error: + PyErr_Clear(); /* typically a MemoryError */ + return NULL; +} + +static PyObject *_ffi_def_extern_decorator(PyObject *outer_args, PyObject *fn) +{ + const char *s; + PyObject *error, *onerror, *infotuple, *old1; + int index, err; + const struct _cffi_global_s *g; + struct _cffi_externpy_s *externpy; + CTypeDescrObject *ct; + FFIObject *ffi; + builder_c_t *types_builder; + PyObject *name = NULL; + PyObject *interpstate_dict; + PyObject *interpstate_key; + + if (!PyArg_ParseTuple(outer_args, "OzOO", &ffi, &s, &error, &onerror)) + return NULL; + + if (s == NULL) { + name = PyObject_GetAttrString(fn, "__name__"); + if (name == NULL) + return NULL; + s = PyText_AsUTF8(name); + if (s == NULL) { + Py_DECREF(name); + return NULL; + } + } + + types_builder = &ffi->types_builder; + index = search_in_globals(&types_builder->ctx, s, strlen(s)); + if (index < 0) + goto not_found; + g = &types_builder->ctx.globals[index]; + if (_CFFI_GETOP(g->type_op) != _CFFI_OP_EXTERN_PYTHON) + goto not_found; + Py_XDECREF(name); + + ct = realize_c_type(types_builder, types_builder->ctx.types, + _CFFI_GETARG(g->type_op)); + if (ct == NULL) + return NULL; + + infotuple = prepare_callback_info_tuple(ct, fn, error, onerror, 0); + Py_DECREF(ct); + if (infotuple == NULL) + return NULL; + + /* don't directly attach infotuple to externpy: in the presence of + subinterpreters, each time we switch to a different + subinterpreter and call the C function, it will notice the + change and look up infotuple from the interpstate_dict. + */ + interpstate_dict = _get_interpstate_dict(); + if (interpstate_dict == NULL) { + Py_DECREF(infotuple); + return PyErr_NoMemory(); + } + + externpy = (struct _cffi_externpy_s *)g->address; + interpstate_key = PyLong_FromVoidPtr((void *)externpy); + if (interpstate_key == NULL) { + Py_DECREF(infotuple); + return NULL; + } + + err = PyDict_SetItem(interpstate_dict, interpstate_key, infotuple); + Py_DECREF(interpstate_key); + Py_DECREF(infotuple); /* interpstate_dict owns the last ref */ + if (err < 0) + return NULL; + + /* force _update_cache_to_call_python() to be called the next time + the C function invokes cffi_call_python, to update the cache */ + old1 = externpy->reserved1; + externpy->reserved1 = Py_None; /* a non-NULL value */ + Py_INCREF(Py_None); + Py_XDECREF(old1); + + /* return the function object unmodified */ + Py_INCREF(fn); + return fn; + + not_found: + PyErr_Format(FFIError, "ffi.def_extern('%s'): no 'extern \"Python\"' " + "function with this name", s); + Py_XDECREF(name); + return NULL; +} + + +static int _update_cache_to_call_python(struct _cffi_externpy_s *externpy) +{ + PyObject *interpstate_dict, *interpstate_key, *infotuple, *old1, *new1; + PyObject *old2; + + interpstate_dict = _get_interpstate_dict(); + if (interpstate_dict == NULL) + return 4; /* oops, shutdown issue? */ + + interpstate_key = PyLong_FromVoidPtr((void *)externpy); + if (interpstate_key == NULL) + goto error; + + infotuple = PyDict_GetItem(interpstate_dict, interpstate_key); + Py_DECREF(interpstate_key); + if (infotuple == NULL) + return 3; /* no ffi.def_extern() from this subinterpreter */ + + new1 = PyThreadState_GET()->interp->modules; + Py_INCREF(new1); + Py_INCREF(infotuple); + old1 = (PyObject *)externpy->reserved1; + old2 = (PyObject *)externpy->reserved2; + externpy->reserved1 = new1; /* holds a reference */ + externpy->reserved2 = infotuple; /* holds a reference (issue #246) */ + Py_XDECREF(old1); + Py_XDECREF(old2); + + return 0; /* no error */ + + error: + PyErr_Clear(); + return 2; /* out of memory? */ +} + +#if (defined(WITH_THREAD) && !defined(_MSC_VER) && \ + !defined(__amd64__) && !defined(__x86_64__) && \ + !defined(__i386__) && !defined(__i386)) +# if defined(HAVE_SYNC_SYNCHRONIZE) +# define read_barrier() __sync_synchronize() +# elif defined(_AIX) +# define read_barrier() __lwsync() +# elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) +# include <mbarrier.h> +# define read_barrier() __compiler_barrier() +# elif defined(__hpux) +# define read_barrier() _Asm_mf() +# else +# define read_barrier() /* missing */ +# warning "no definition for read_barrier(), missing synchronization for\ + multi-thread initialization in embedded mode" +# endif +#else +# define read_barrier() (void)0 +#endif + +static void cffi_call_python(struct _cffi_externpy_s *externpy, char *args) +{ + /* Invoked by the helpers generated from extern "Python" in the cdef. + + 'externpy' is a static structure that describes which of the + extern "Python" functions is called. It has got fields 'name' and + 'type_index' describing the function, and more reserved fields + that are initially zero. These reserved fields are set up by + ffi.def_extern(), which invokes _ffi_def_extern_decorator() above. + + 'args' is a pointer to an array of 8-byte entries. Each entry + contains an argument. If an argument is less than 8 bytes, only + the part at the beginning of the entry is initialized. If an + argument is 'long double' or a struct/union, then it is passed + by reference. + + 'args' is also used as the place to write the result to + (directly, even if more than 8 bytes). In all cases, 'args' is + at least 8 bytes in size. + */ + int err = 0; + + /* This read barrier is needed for _embedding.h. It is paired + with the write_barrier() there. Without this barrier, we can + in theory see the following situation: the Python + initialization code already ran (in another thread), and the + '_cffi_call_python' function pointer directed execution here; + but any number of other data could still be seen as + uninitialized below. For example, 'externpy' would still + contain NULLs even though it was correctly set up, or + 'interpreter_lock' (the GIL inside CPython) would still be seen + as NULL, or 'autoInterpreterState' (used by + PyGILState_Ensure()) would be NULL or contain bogus fields. + */ + read_barrier(); + + save_errno(); + + /* We need the infotuple here. We could always go through + _update_cache_to_call_python(), but to avoid the extra dict + lookups, we cache in (reserved1, reserved2) the last seen pair + (interp->modules, infotuple). The first item in this tuple is + a random PyObject that identifies the subinterpreter. + */ + if (externpy->reserved1 == NULL) { + /* Not initialized! We didn't call @ffi.def_extern() on this + externpy object from any subinterpreter at all. */ + err = 1; + } + else { + PyGILState_STATE state = gil_ensure(); + if (externpy->reserved1 != PyThreadState_GET()->interp->modules) { + /* Update the (reserved1, reserved2) cache. This will fail + if we didn't call @ffi.def_extern() in this particular + subinterpreter. */ + err = _update_cache_to_call_python(externpy); + } + if (!err) { + general_invoke_callback(0, args, args, externpy->reserved2); + } + gil_release(state); + } + if (err) { + static const char *msg[] = { + "no code was attached to it yet with @ffi.def_extern()", + "got internal exception (out of memory?)", + "@ffi.def_extern() was not called in the current subinterpreter", + "got internal exception (shutdown issue?)", + }; + fprintf(stderr, "extern \"Python\": function %s() called, " + "but %s. Returning 0.\n", externpy->name, msg[err-1]); + memset(args, 0, externpy->size_of_result); + } + restore_errno(); +} diff --git a/c/cdlopen.c b/c/cdlopen.c new file mode 100644 index 0000000..ad33bbd --- /dev/null +++ b/c/cdlopen.c @@ -0,0 +1,361 @@ +/* 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; + + handle = b_do_dlopen(args, &modname, &temp); + if (handle != NULL) + { + result = (PyObject *)lib_internal_new((FFIObject *)self, + modname, handle); + } + 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; +} diff --git a/c/cffi1_module.c b/c/cffi1_module.c new file mode 100644 index 0000000..2b98e8e --- /dev/null +++ b/c/cffi1_module.c @@ -0,0 +1,231 @@ + +#include "parse_c_type.c" +#include "realize_c_type.c" + +#define CFFI_VERSION_MIN 0x2601 +#define CFFI_VERSION_CHAR16CHAR32 0x2801 +#define CFFI_VERSION_MAX 0x28FF + +typedef struct FFIObject_s FFIObject; +typedef struct LibObject_s LibObject; + +static PyTypeObject FFI_Type; /* forward */ +static PyTypeObject Lib_Type; /* forward */ + +#include "ffi_obj.c" +#include "cglob.c" +#include "lib_obj.c" +#include "cdlopen.c" +#include "commontypes.c" +#include "call_python.c" + + +static int init_ffi_lib(PyObject *m) +{ + PyObject *x; + int i, res; + static char init_done = 0; + + if (PyType_Ready(&FFI_Type) < 0) + return -1; + if (PyType_Ready(&Lib_Type) < 0) + return -1; + + if (!init_done) { + if (init_global_types_dict(FFI_Type.tp_dict) < 0) + return -1; + + FFIError = PyErr_NewException("ffi.error", NULL, NULL); + if (FFIError == NULL) + return -1; + if (PyDict_SetItemString(FFI_Type.tp_dict, "error", FFIError) < 0) + return -1; + if (PyDict_SetItemString(FFI_Type.tp_dict, "CType", + (PyObject *)&CTypeDescr_Type) < 0) + return -1; + if (PyDict_SetItemString(FFI_Type.tp_dict, "CData", + (PyObject *)&CData_Type) < 0) + return -1; + if (PyDict_SetItemString(FFI_Type.tp_dict, "buffer", + (PyObject *)&MiniBuffer_Type) < 0) + return -1; + + for (i = 0; all_dlopen_flags[i].name != NULL; i++) { + x = PyInt_FromLong(all_dlopen_flags[i].value); + if (x == NULL) + return -1; + res = PyDict_SetItemString(FFI_Type.tp_dict, + all_dlopen_flags[i].name, x); + Py_DECREF(x); + if (res < 0) + return -1; + } + init_done = 1; + } + + x = (PyObject *)&FFI_Type; + Py_INCREF(x); + if (PyModule_AddObject(m, "FFI", x) < 0) + return -1; + x = (PyObject *)&Lib_Type; + Py_INCREF(x); + if (PyModule_AddObject(m, "Lib", x) < 0) + return -1; + + return 0; +} + +static int make_included_tuples(char *module_name, + const char *const *ctx_includes, + PyObject **included_ffis, + PyObject **included_libs) +{ + Py_ssize_t num = 0; + const char *const *p_include; + + if (ctx_includes == NULL) + return 0; + + for (p_include = ctx_includes; *p_include; p_include++) { + num++; + } + *included_ffis = PyTuple_New(num); + *included_libs = PyTuple_New(num); + if (*included_ffis == NULL || *included_libs == NULL) + goto error; + + num = 0; + for (p_include = ctx_includes; *p_include; p_include++) { + PyObject *included_ffi, *included_lib; + PyObject *m = PyImport_ImportModule(*p_include); + if (m == NULL) + goto import_error; + + included_ffi = PyObject_GetAttrString(m, "ffi"); + PyTuple_SET_ITEM(*included_ffis, num, included_ffi); + + included_lib = (included_ffi == NULL) ? NULL : + PyObject_GetAttrString(m, "lib"); + PyTuple_SET_ITEM(*included_libs, num, included_lib); + + Py_DECREF(m); + if (included_lib == NULL) + goto import_error; + + if (!FFIObject_Check(included_ffi) || + !LibObject_Check(included_lib)) + goto import_error; + num++; + } + return 0; + + import_error: + PyErr_Format(PyExc_ImportError, + "while loading %.200s: failed to import ffi, lib from %.200s", + module_name, *p_include); + error: + Py_XDECREF(*included_ffis); *included_ffis = NULL; + Py_XDECREF(*included_libs); *included_libs = NULL; + return -1; +} + +static PyObject *_my_Py_InitModule(char *module_name) +{ +#if PY_MAJOR_VERSION >= 3 + struct PyModuleDef *module_def, local_module_def = { + PyModuleDef_HEAD_INIT, + module_name, + NULL, + -1, + NULL, NULL, NULL, NULL, NULL + }; + /* note: the 'module_def' is allocated dynamically and leaks, + but anyway the C extension module can never be unloaded */ + module_def = PyMem_Malloc(sizeof(struct PyModuleDef)); + if (module_def == NULL) + return PyErr_NoMemory(); + *module_def = local_module_def; + return PyModule_Create(module_def); +#else + return Py_InitModule(module_name, NULL); +#endif +} + +static PyObject *b_init_cffi_1_0_external_module(PyObject *self, PyObject *arg) +{ + PyObject *m, *modules_dict; + FFIObject *ffi; + LibObject *lib; + Py_ssize_t version, num_exports; + char *module_name, *exports, *module_name_with_lib; + void **raw; + const struct _cffi_type_context_s *ctx; + + raw = (void **)PyLong_AsVoidPtr(arg); + if (raw == NULL) + return NULL; + + module_name = (char *)raw[0]; + version = (Py_ssize_t)raw[1]; + exports = (char *)raw[2]; + ctx = (const struct _cffi_type_context_s *)raw[3]; + + if (version < CFFI_VERSION_MIN || version > CFFI_VERSION_MAX) { + if (!PyErr_Occurred()) + PyErr_Format(PyExc_ImportError, + "cffi extension module '%s' uses an unknown version tag %p. " + "This module might need a more recent version of cffi " + "than the one currently installed, which is %s", + module_name, (void *)version, CFFI_VERSION); + return NULL; + } + + /* initialize the exports array */ + num_exports = 25; + if (ctx->flags & 1) /* set to mean that 'extern "Python"' is used */ + num_exports = 26; + if (version >= CFFI_VERSION_CHAR16CHAR32) + num_exports = 28; + memcpy(exports, (char *)cffi_exports, num_exports * sizeof(void *)); + + /* make the module object */ + m = _my_Py_InitModule(module_name); + if (m == NULL) + return NULL; + + /* build the FFI and Lib object inside this new module */ + ffi = ffi_internal_new(&FFI_Type, ctx); + Py_XINCREF(ffi); /* make the ffi object really immortal */ + if (ffi == NULL || PyModule_AddObject(m, "ffi", (PyObject *)ffi) < 0) + return NULL; + + lib = lib_internal_new(ffi, module_name, NULL); + if (lib == NULL || PyModule_AddObject(m, "lib", (PyObject *)lib) < 0) + return NULL; + + if (make_included_tuples(module_name, ctx->includes, + &ffi->types_builder.included_ffis, + &lib->l_types_builder->included_libs) < 0) + return NULL; + + /* add manually 'module_name.lib' in sys.modules: + see test_import_from_lib */ + modules_dict = PySys_GetObject("modules"); + if (!modules_dict) + return NULL; + module_name_with_lib = alloca(strlen(module_name) + 5); + strcpy(module_name_with_lib, module_name); + strcat(module_name_with_lib, ".lib"); + if (PyDict_SetItemString(modules_dict, module_name_with_lib, + (PyObject *)lib) < 0) + return NULL; + +#if PY_MAJOR_VERSION >= 3 + /* add manually 'module_name' in sys.modules: it seems that + Py_InitModule() is not enough to do that */ + if (PyDict_SetItemString(modules_dict, module_name, m) < 0) + return NULL; +#endif + + return m; +} diff --git a/c/cglob.c b/c/cglob.c new file mode 100644 index 0000000..9ee4025 --- /dev/null +++ b/c/cglob.c @@ -0,0 +1,113 @@ + +typedef void *(*gs_fetch_addr_fn)(void); + +typedef struct { + PyObject_HEAD + + PyObject *gs_name; + CTypeDescrObject *gs_type; + char *gs_data; + gs_fetch_addr_fn gs_fetch_addr; + +} GlobSupportObject; + +static void glob_support_dealloc(GlobSupportObject *gs) +{ + Py_DECREF(gs->gs_name); + Py_DECREF(gs->gs_type); + PyObject_Del(gs); +} + +static PyTypeObject GlobSupport_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "FFIGlobSupport", + sizeof(GlobSupportObject), + 0, + (destructor)glob_support_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, /* tp_flags */ +}; + +#define GlobSupport_Check(ob) (Py_TYPE(ob) == &GlobSupport_Type) + +static PyObject *make_global_var(PyObject *name, CTypeDescrObject *type, + char *addr, gs_fetch_addr_fn fetch_addr) +{ + GlobSupportObject *gs = PyObject_New(GlobSupportObject, &GlobSupport_Type); + if (gs == NULL) + return NULL; + + Py_INCREF(name); + Py_INCREF(type); + gs->gs_name = name; + gs->gs_type = type; + gs->gs_data = addr; + gs->gs_fetch_addr = fetch_addr; + return (PyObject *)gs; +} + +static void *fetch_global_var_addr(GlobSupportObject *gs) +{ + void *data; + if (gs->gs_data != NULL) { + data = gs->gs_data; + } + else { + Py_BEGIN_ALLOW_THREADS + restore_errno(); + data = gs->gs_fetch_addr(); + save_errno(); + Py_END_ALLOW_THREADS + } + if (data == NULL) { + PyErr_Format(FFIError, "global variable '%s' is at address NULL", + PyText_AS_UTF8(gs->gs_name)); + return NULL; + } + return data; +} + +static PyObject *read_global_var(GlobSupportObject *gs) +{ + void *data = fetch_global_var_addr(gs); + if (data == NULL) + return NULL; + return convert_to_object(data, gs->gs_type); +} + +static int write_global_var(GlobSupportObject *gs, PyObject *obj) +{ + void *data = fetch_global_var_addr(gs); + if (data == NULL) + return -1; + return convert_from_object(data, gs->gs_type, obj); +} + +static PyObject *cg_addressof_global_var(GlobSupportObject *gs) +{ + void *data; + PyObject *x, *ptrtype = new_pointer_type(gs->gs_type); + if (ptrtype == NULL) + return NULL; + + data = fetch_global_var_addr(gs); + if (data != NULL) + x = new_simple_cdata(data, (CTypeDescrObject *)ptrtype); + else + x = NULL; + Py_DECREF(ptrtype); + return x; +} diff --git a/c/commontypes.c b/c/commontypes.c new file mode 100644 index 0000000..a41c2fd --- /dev/null +++ b/c/commontypes.c @@ -0,0 +1,216 @@ +/* This file must be kept in alphabetical order. See test_commontypes.py */ + +#define EQ(key, value) key "\0" value /* string concatenation */ +#ifdef _WIN64 +# define W32_64(X,Y) Y +# else +# define W32_64(X,Y) X +# endif + + +static const char *common_simple_types[] = { + +#ifdef MS_WIN32 /* Windows types */ + EQ("ATOM", "WORD"), + EQ("BOOL", "int"), + EQ("BOOLEAN", "BYTE"), + EQ("BYTE", "unsigned char"), + EQ("CCHAR", "char"), + EQ("CHAR", "char"), + EQ("COLORREF", "DWORD"), + EQ("DWORD", "unsigned long"), + EQ("DWORD32", "unsigned int"), + EQ("DWORD64", "unsigned long long"), + EQ("DWORDLONG", "ULONGLONG"), + EQ("DWORD_PTR", "ULONG_PTR"), +#endif + + EQ("FILE", "struct _IO_FILE"), + +#ifdef MS_WIN32 /* more Windows types */ + EQ("FLOAT", "float"), + EQ("HACCEL", "HANDLE"), + EQ("HALF_PTR", W32_64("short","int")), + EQ("HANDLE", "PVOID"), + EQ("HBITMAP", "HANDLE"), + EQ("HBRUSH", "HANDLE"), + EQ("HCOLORSPACE", "HANDLE"), + EQ("HCONV", "HANDLE"), + EQ("HCONVLIST", "HANDLE"), + EQ("HCURSOR", "HICON"), + EQ("HDC", "HANDLE"), + EQ("HDDEDATA", "HANDLE"), + EQ("HDESK", "HANDLE"), + EQ("HDROP", "HANDLE"), + EQ("HDWP", "HANDLE"), + EQ("HENHMETAFILE", "HANDLE"), + EQ("HFILE", "int"), + EQ("HFONT", "HANDLE"), + EQ("HGDIOBJ", "HANDLE"), + EQ("HGLOBAL", "HANDLE"), + EQ("HHOOK", "HANDLE"), + EQ("HICON", "HANDLE"), + EQ("HINSTANCE", "HANDLE"), + EQ("HKEY", "HANDLE"), + EQ("HKL", "HANDLE"), + EQ("HLOCAL", "HANDLE"), + EQ("HMENU", "HANDLE"), + EQ("HMETAFILE", "HANDLE"), + EQ("HMODULE", "HINSTANCE"), + EQ("HMONITOR", "HANDLE"), + EQ("HPALETTE", "HANDLE"), + EQ("HPEN", "HANDLE"), + EQ("HRESULT", "LONG"), + EQ("HRGN", "HANDLE"), + EQ("HRSRC", "HANDLE"), + EQ("HSZ", "HANDLE"), + EQ("HWND", "HANDLE"), + EQ("INT", "int"), + EQ("INT16", "short"), + EQ("INT32", "int"), + EQ("INT64", "long long"), + EQ("INT8", "signed char"), + EQ("INT_PTR", W32_64("int","long long")), + EQ("LANGID", "WORD"), + EQ("LCID", "DWORD"), + EQ("LCTYPE", "DWORD"), + EQ("LGRPID", "DWORD"), + EQ("LONG", "long"), + EQ("LONG32", "int"), + EQ("LONG64", "long long"), + EQ("LONGLONG", "long long"), + EQ("LONG_PTR", W32_64("long","long long")), + EQ("LPARAM", "LONG_PTR"), + EQ("LPBOOL", "BOOL *"), + EQ("LPBYTE", "BYTE *"), + EQ("LPCOLORREF", "DWORD *"), + EQ("LPCSTR", "const char *"), + EQ("LPCVOID", "const void *"), + EQ("LPCWSTR", "const WCHAR *"), + EQ("LPDWORD", "DWORD *"), + EQ("LPHANDLE", "HANDLE *"), + EQ("LPINT", "int *"), + EQ("LPLONG", "long *"), + EQ("LPSTR", "CHAR *"), + EQ("LPVOID", "void *"), + EQ("LPWORD", "WORD *"), + EQ("LPWSTR", "WCHAR *"), + EQ("LRESULT", "LONG_PTR"), + EQ("PBOOL", "BOOL *"), + EQ("PBOOLEAN", "BOOLEAN *"), + EQ("PBYTE", "BYTE *"), + EQ("PCHAR", "CHAR *"), + EQ("PCSTR", "const CHAR *"), + EQ("PCWSTR", "const WCHAR *"), + EQ("PDWORD", "DWORD *"), + EQ("PDWORD32", "DWORD32 *"), + EQ("PDWORD64", "DWORD64 *"), + EQ("PDWORDLONG", "DWORDLONG *"), + EQ("PDWORD_PTR", "DWORD_PTR *"), + EQ("PFLOAT", "FLOAT *"), + EQ("PHALF_PTR", "HALF_PTR *"), + EQ("PHANDLE", "HANDLE *"), + EQ("PHKEY", "HKEY *"), + EQ("PINT", "int *"), + EQ("PINT16", "INT16 *"), + EQ("PINT32", "INT32 *"), + EQ("PINT64", "INT64 *"), + EQ("PINT8", "INT8 *"), + EQ("PINT_PTR", "INT_PTR *"), + EQ("PLCID", "PDWORD"), + EQ("PLONG", "LONG *"), + EQ("PLONG32", "LONG32 *"), + EQ("PLONG64", "LONG64 *"), + EQ("PLONGLONG", "LONGLONG *"), + EQ("PLONG_PTR", "LONG_PTR *"), + EQ("PSHORT", "SHORT *"), + EQ("PSIZE_T", "SIZE_T *"), + EQ("PSSIZE_T", "SSIZE_T *"), + EQ("PSTR", "CHAR *"), + EQ("PUCHAR", "UCHAR *"), + EQ("PUHALF_PTR", "UHALF_PTR *"), + EQ("PUINT", "UINT *"), + EQ("PUINT16", "UINT16 *"), + EQ("PUINT32", "UINT32 *"), + EQ("PUINT64", "UINT64 *"), + EQ("PUINT8", "UINT8 *"), + EQ("PUINT_PTR", "UINT_PTR *"), + EQ("PULONG", "ULONG *"), + EQ("PULONG32", "ULONG32 *"), + EQ("PULONG64", "ULONG64 *"), + EQ("PULONGLONG", "ULONGLONG *"), + EQ("PULONG_PTR", "ULONG_PTR *"), + EQ("PUSHORT", "USHORT *"), + EQ("PVOID", "void *"), + EQ("PWCHAR", "WCHAR *"), + EQ("PWORD", "WORD *"), + EQ("PWSTR", "WCHAR *"), + EQ("QWORD", "unsigned long long"), + EQ("SC_HANDLE", "HANDLE"), + EQ("SC_LOCK", "LPVOID"), + EQ("SERVICE_STATUS_HANDLE", "HANDLE"), + EQ("SHORT", "short"), + EQ("SIZE_T", "ULONG_PTR"), + EQ("SSIZE_T", "LONG_PTR"), + EQ("UCHAR", "unsigned char"), + EQ("UHALF_PTR", W32_64("unsigned short","unsigned int")), + EQ("UINT", "unsigned int"), + EQ("UINT16", "unsigned short"), + EQ("UINT32", "unsigned int"), + EQ("UINT64", "unsigned long long"), + EQ("UINT8", "unsigned char"), + EQ("UINT_PTR", W32_64("unsigned int","unsigned long long")), + EQ("ULONG", "unsigned long"), + EQ("ULONG32", "unsigned int"), + EQ("ULONG64", "unsigned long long"), + EQ("ULONGLONG", "unsigned long long"), + EQ("ULONG_PTR", W32_64("unsigned long","unsigned long long")), + EQ("USHORT", "unsigned short"), + EQ("USN", "LONGLONG"), + EQ("VOID", "void"), + EQ("WCHAR", "wchar_t"), + EQ("WINSTA", "HANDLE"), + EQ("WORD", "unsigned short"), + EQ("WPARAM", "UINT_PTR"), +#endif + + EQ("bool", "_Bool"), +}; + + +#undef EQ +#undef W32_64 + +#define num_common_simple_types \ + (sizeof(common_simple_types) / sizeof(common_simple_types[0])) + + +static const char *get_common_type(const char *search, size_t search_len) +{ + const char *entry; + int index = search_sorted(common_simple_types, sizeof(const char *), + num_common_simple_types, search, search_len); + if (index < 0) + return NULL; + + entry = common_simple_types[index]; + return entry + strlen(entry) + 1; +} + +static PyObject *b__get_common_types(PyObject *self, PyObject *arg) +{ + int err; + size_t i; + for (i = 0; i < num_common_simple_types; i++) { + const char *s = common_simple_types[i]; + PyObject *o = PyText_FromString(s + strlen(s) + 1); + if (o == NULL) + return NULL; + err = PyDict_SetItemString(arg, s, o); + Py_DECREF(o); + if (err < 0) + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} diff --git a/c/ffi_obj.c b/c/ffi_obj.c new file mode 100644 index 0000000..1e8cc6f --- /dev/null +++ b/c/ffi_obj.c @@ -0,0 +1,1221 @@ + +/* 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 */ +} diff --git a/c/file_emulator.h b/c/file_emulator.h new file mode 100644 index 0000000..82a34c0 --- /dev/null +++ b/c/file_emulator.h @@ -0,0 +1,93 @@ + +/* Emulation of PyFile_Check() and PyFile_AsFile() for Python 3. */ + +static PyObject *PyIOBase_TypeObj; + +static int init_file_emulator(void) +{ + if (PyIOBase_TypeObj == NULL) { + PyObject *io = PyImport_ImportModule("_io"); + if (io == NULL) + return -1; + PyIOBase_TypeObj = PyObject_GetAttrString(io, "_IOBase"); + if (PyIOBase_TypeObj == NULL) + return -1; + } + return 0; +} + + +#define PyFile_Check(p) PyObject_IsInstance(p, PyIOBase_TypeObj) + + +static void _close_file_capsule(PyObject *ob_capsule) +{ + FILE *f = (FILE *)PyCapsule_GetPointer(ob_capsule, "FILE"); + if (f != NULL) + fclose(f); +} + + +static FILE *PyFile_AsFile(PyObject *ob_file) +{ + PyObject *ob, *ob_capsule = NULL, *ob_mode = NULL; + FILE *f; + int fd; + const char *mode; + + ob = PyObject_CallMethod(ob_file, "flush", NULL); + if (ob == NULL) + goto fail; + Py_DECREF(ob); + + ob_capsule = PyObject_GetAttrString(ob_file, "__cffi_FILE"); + if (ob_capsule == NULL) { + PyErr_Clear(); + + fd = PyObject_AsFileDescriptor(ob_file); + if (fd < 0) + goto fail; + + ob_mode = PyObject_GetAttrString(ob_file, "mode"); + if (ob_mode == NULL) + goto fail; + mode = PyText_AsUTF8(ob_mode); + if (mode == NULL) + goto fail; + + fd = dup(fd); + if (fd < 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto fail; + } + + f = fdopen(fd, mode); + if (f == NULL) { + close(fd); + PyErr_SetFromErrno(PyExc_OSError); + goto fail; + } + setbuf(f, NULL); /* non-buffered */ + Py_DECREF(ob_mode); + ob_mode = NULL; + + ob_capsule = PyCapsule_New(f, "FILE", _close_file_capsule); + if (ob_capsule == NULL) { + fclose(f); + goto fail; + } + + if (PyObject_SetAttrString(ob_file, "__cffi_FILE", ob_capsule) < 0) + goto fail; + } + else { + f = PyCapsule_GetPointer(ob_capsule, "FILE"); + } + Py_DECREF(ob_capsule); /* assumes still at least one reference */ + return f; + + fail: + Py_XDECREF(ob_mode); + Py_XDECREF(ob_capsule); + return NULL; +} diff --git a/c/lib_obj.c b/c/lib_obj.c new file mode 100644 index 0000000..7cd40ec --- /dev/null +++ b/c/lib_obj.c @@ -0,0 +1,712 @@ + +/* 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; +} diff --git a/c/libffi_msvc/LICENSE b/c/libffi_msvc/LICENSE new file mode 100644 index 0000000..f591795 --- /dev/null +++ b/c/libffi_msvc/LICENSE @@ -0,0 +1,20 @@ +libffi - Copyright (c) 1996-2003 Red Hat, Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +``Software''), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/c/libffi_msvc/README b/c/libffi_msvc/README new file mode 100644 index 0000000..97a12cf --- /dev/null +++ b/c/libffi_msvc/README @@ -0,0 +1,502 @@ +This directory contains the libffi package, which is not part of GCC but +shipped with GCC as convenience. + +Copied without changes from CPython 2.7 head (e04e1f253ed8). + +Status +====== + +libffi-2.00 has not been released yet! This is a development snapshot! + +libffi-1.20 was released on October 5, 1998. Check the libffi web +page for updates: <URL:http://sources.redhat.com/libffi/>. + + +What is libffi? +=============== + +Compilers for high level languages generate code that follow certain +conventions. These conventions are necessary, in part, for separate +compilation to work. One such convention is the "calling +convention". The "calling convention" is essentially a set of +assumptions made by the compiler about where function arguments will +be found on entry to a function. A "calling convention" also specifies +where the return value for a function is found. + +Some programs may not know at the time of compilation what arguments +are to be passed to a function. For instance, an interpreter may be +told at run-time about the number and types of arguments used to call +a given function. Libffi can be used in such programs to provide a +bridge from the interpreter program to compiled code. + +The libffi library provides a portable, high level programming +interface to various calling conventions. This allows a programmer to +call any function specified by a call interface description at run +time. + +Ffi stands for Foreign Function Interface. A foreign function +interface is the popular name for the interface that allows code +written in one language to call code written in another language. The +libffi library really only provides the lowest, machine dependent +layer of a fully featured foreign function interface. A layer must +exist above libffi that handles type conversions for values passed +between the two languages. + + +Supported Platforms and Prerequisites +===================================== + +Libffi has been ported to: + + SunOS 4.1.3 & Solaris 2.x (SPARC-V8, SPARC-V9) + + Irix 5.3 & 6.2 (System V/o32 & n32) + + Intel x86 - Linux (System V ABI) + + Alpha - Linux and OSF/1 + + m68k - Linux (System V ABI) + + PowerPC - Linux (System V ABI, Darwin, AIX) + + ARM - Linux (System V ABI) + +Libffi has been tested with the egcs 1.0.2 gcc compiler. Chances are +that other versions will work. Libffi has also been built and tested +with the SGI compiler tools. + +On PowerPC, the tests failed (see the note below). + +You must use GNU make to build libffi. SGI's make will not work. +Sun's probably won't either. + +If you port libffi to another platform, please let me know! I assume +that some will be easy (x86 NetBSD), and others will be more difficult +(HP). + + +Installing libffi +================= + +[Note: before actually performing any of these installation steps, + you may wish to read the "Platform Specific Notes" below.] + +First you must configure the distribution for your particular +system. Go to the directory you wish to build libffi in and run the +"configure" program found in the root directory of the libffi source +distribution. + +You may want to tell configure where to install the libffi library and +header files. To do that, use the --prefix configure switch. Libffi +will install under /usr/local by default. + +If you want to enable extra run-time debugging checks use the the +--enable-debug configure switch. This is useful when your program dies +mysteriously while using libffi. + +Another useful configure switch is --enable-purify-safety. Using this +will add some extra code which will suppress certain warnings when you +are using Purify with libffi. Only use this switch when using +Purify, as it will slow down the library. + +Configure has many other options. Use "configure --help" to see them all. + +Once configure has finished, type "make". Note that you must be using +GNU make. SGI's make will not work. Sun's probably won't either. +You can ftp GNU make from prep.ai.mit.edu:/pub/gnu. + +To ensure that libffi is working as advertised, type "make test". + +To install the library and header files, type "make install". + + +Using libffi +============ + + The Basics + ---------- + +Libffi assumes that you have a pointer to the function you wish to +call and that you know the number and types of arguments to pass it, +as well as the return type of the function. + +The first thing you must do is create an ffi_cif object that matches +the signature of the function you wish to call. The cif in ffi_cif +stands for Call InterFace. To prepare a call interface object, use the +following function: + +ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, + unsigned int nargs, + ffi_type *rtype, ffi_type **atypes); + + CIF is a pointer to the call interface object you wish + to initialize. + + ABI is an enum that specifies the calling convention + to use for the call. FFI_DEFAULT_ABI defaults + to the system's native calling convention. Other + ABI's may be used with care. They are system + specific. + + NARGS is the number of arguments this function accepts. + libffi does not yet support vararg functions. + + RTYPE is a pointer to an ffi_type structure that represents + the return type of the function. Ffi_type objects + describe the types of values. libffi provides + ffi_type objects for many of the native C types: + signed int, unsigned int, signed char, unsigned char, + etc. There is also a pointer ffi_type object and + a void ffi_type. Use &ffi_type_void for functions that + don't return values. + + ATYPES is a vector of ffi_type pointers. ARGS must be NARGS long. + If NARGS is 0, this is ignored. + + +ffi_prep_cif will return a status code that you are responsible +for checking. It will be one of the following: + + FFI_OK - All is good. + + FFI_BAD_TYPEDEF - One of the ffi_type objects that ffi_prep_cif + came across is bad. + + +Before making the call, the VALUES vector should be initialized +with pointers to the appropriate argument values. + +To call the the function using the initialized ffi_cif, use the +ffi_call function: + +void ffi_call(ffi_cif *cif, void *fn, void *rvalue, void **avalues); + + CIF is a pointer to the ffi_cif initialized specifically + for this function. + + FN is a pointer to the function you want to call. + + RVALUE is a pointer to a chunk of memory that is to hold the + result of the function call. Currently, it must be + at least one word in size (except for the n32 version + under Irix 6.x, which must be a pointer to an 8 byte + aligned value (a long long). It must also be at least + word aligned (depending on the return type, and the + system's alignment requirements). If RTYPE is + &ffi_type_void, this is ignored. If RVALUE is NULL, + the return value is discarded. + + AVALUES is a vector of void* that point to the memory locations + holding the argument values for a call. + If NARGS is 0, this is ignored. + + +If you are expecting a return value from FN it will have been stored +at RVALUE. + + + + An Example + ---------- + +Here is a trivial example that calls puts() a few times. + + #include <stdio.h> + #include <ffi.h> + + int main() + { + ffi_cif cif; + ffi_type *args[1]; + void *values[1]; + char *s; + int rc; + + /* Initialize the argument info vectors */ + args[0] = &ffi_type_uint; + values[0] = &s; + + /* Initialize the cif */ + if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, + &ffi_type_uint, args) == FFI_OK) + { + s = "Hello World!"; + ffi_call(&cif, puts, &rc, values); + /* rc now holds the result of the call to puts */ + + /* values holds a pointer to the function's arg, so to + call puts() again all we need to do is change the + value of s */ + s = "This is cool!"; + ffi_call(&cif, puts, &rc, values); + } + + return 0; + } + + + + Aggregate Types + --------------- + +Although libffi has no special support for unions or bit-fields, it is +perfectly happy passing structures back and forth. You must first +describe the structure to libffi by creating a new ffi_type object +for it. Here is the definition of ffi_type: + + typedef struct _ffi_type + { + unsigned size; + short alignment; + short type; + struct _ffi_type **elements; + } ffi_type; + +All structures must have type set to FFI_TYPE_STRUCT. You may set +size and alignment to 0. These will be calculated and reset to the +appropriate values by ffi_prep_cif(). + +elements is a NULL terminated array of pointers to ffi_type objects +that describe the type of the structure elements. These may, in turn, +be structure elements. + +The following example initializes a ffi_type object representing the +tm struct from Linux's time.h: + + struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + /* Those are for future use. */ + long int __tm_gmtoff__; + __const char *__tm_zone__; + }; + + { + ffi_type tm_type; + ffi_type *tm_type_elements[12]; + int i; + + tm_type.size = tm_type.alignment = 0; + tm_type.elements = &tm_type_elements; + + for (i = 0; i < 9; i++) + tm_type_elements[i] = &ffi_type_sint; + + tm_type_elements[9] = &ffi_type_slong; + tm_type_elements[10] = &ffi_type_pointer; + tm_type_elements[11] = NULL; + + /* tm_type can now be used to represent tm argument types and + return types for ffi_prep_cif() */ + } + + + +Platform Specific Notes +======================= + + Intel x86 + --------- + +There are no known problems with the x86 port. + + Sun SPARC - SunOS 4.1.3 & Solaris 2.x + ------------------------------------- + +You must use GNU Make to build libffi on Sun platforms. + + MIPS - Irix 5.3 & 6.x + --------------------- + +Irix 6.2 and better supports three different calling conventions: o32, +n32 and n64. Currently, libffi only supports both o32 and n32 under +Irix 6.x, but only o32 under Irix 5.3. Libffi will automatically be +configured for whichever calling convention it was built for. + +By default, the configure script will try to build libffi with the GNU +development tools. To build libffi with the SGI development tools, set +the environment variable CC to either "cc -32" or "cc -n32" before +running configure under Irix 6.x (depending on whether you want an o32 +or n32 library), or just "cc" for Irix 5.3. + +With the n32 calling convention, when returning structures smaller +than 16 bytes, be sure to provide an RVALUE that is 8 byte aligned. +Here's one way of forcing this: + + double struct_storage[2]; + my_small_struct *s = (my_small_struct *) struct_storage; + /* Use s for RVALUE */ + +If you don't do this you are liable to get spurious bus errors. + +"long long" values are not supported yet. + +You must use GNU Make to build libffi on SGI platforms. + + ARM - System V ABI + ------------------ + +The ARM port was performed on a NetWinder running ARM Linux ELF +(2.0.31) and gcc 2.8.1. + + + + PowerPC System V ABI + -------------------- + +There are two `System V ABI's which libffi implements for PowerPC. +They differ only in how small structures are returned from functions. + +In the FFI_SYSV version, structures that are 8 bytes or smaller are +returned in registers. This is what GCC does when it is configured +for solaris, and is what the System V ABI I have (dated September +1995) says. + +In the FFI_GCC_SYSV version, all structures are returned the same way: +by passing a pointer as the first argument to the function. This is +what GCC does when it is configured for linux or a generic sysv +target. + +EGCS 1.0.1 (and probably other versions of EGCS/GCC) also has a +inconsistency with the SysV ABI: When a procedure is called with many +floating-point arguments, some of them get put on the stack. They are +all supposed to be stored in double-precision format, even if they are +only single-precision, but EGCS stores single-precision arguments as +single-precision anyway. This causes one test to fail (the `many +arguments' test). + + +What's With The Crazy Comments? +=============================== + +You might notice a number of cryptic comments in the code, delimited +by /*@ and @*/. These are annotations read by the program LCLint, a +tool for statically checking C programs. You can read all about it at +<http://larch-www.lcs.mit.edu:8001/larch/lclint/index.html>. + + +History +======= + +1.20 Oct-5-98 + Raffaele Sena produces ARM port. + +1.19 Oct-5-98 + Fixed x86 long double and long long return support. + m68k bug fixes from Andreas Schwab. + Patch for DU assembler compatibility for the Alpha from Richard + Henderson. + +1.18 Apr-17-98 + Bug fixes and MIPS configuration changes. + +1.17 Feb-24-98 + Bug fixes and m68k port from Andreas Schwab. PowerPC port from + Geoffrey Keating. Various bug x86, Sparc and MIPS bug fixes. + +1.16 Feb-11-98 + Richard Henderson produces Alpha port. + +1.15 Dec-4-97 + Fixed an n32 ABI bug. New libtool, auto* support. + +1.14 May-13-97 + libtool is now used to generate shared and static libraries. + Fixed a minor portability problem reported by Russ McManus + <mcmanr@eq.gs.com>. + +1.13 Dec-2-96 + Added --enable-purify-safety to keep Purify from complaining + about certain low level code. + Sparc fix for calling functions with < 6 args. + Linux x86 a.out fix. + +1.12 Nov-22-96 + Added missing ffi_type_void, needed for supporting void return + types. Fixed test case for non MIPS machines. Cygnus Support + is now Cygnus Solutions. + +1.11 Oct-30-96 + Added notes about GNU make. + +1.10 Oct-29-96 + Added configuration fix for non GNU compilers. + +1.09 Oct-29-96 + Added --enable-debug configure switch. Clean-ups based on LCLint + feedback. ffi_mips.h is always installed. Many configuration + fixes. Fixed ffitest.c for sparc builds. + +1.08 Oct-15-96 + Fixed n32 problem. Many clean-ups. + +1.07 Oct-14-96 + Gordon Irlam rewrites v8.S again. Bug fixes. + +1.06 Oct-14-96 + Gordon Irlam improved the sparc port. + +1.05 Oct-14-96 + Interface changes based on feedback. + +1.04 Oct-11-96 + Sparc port complete (modulo struct passing bug). + +1.03 Oct-10-96 + Passing struct args, and returning struct values works for + all architectures/calling conventions. Expanded tests. + +1.02 Oct-9-96 + Added SGI n32 support. Fixed bugs in both o32 and Linux support. + Added "make test". + +1.01 Oct-8-96 + Fixed float passing bug in mips version. Restructured some + of the code. Builds cleanly with SGI tools. + +1.00 Oct-7-96 + First release. No public announcement. + + +Authors & Credits +================= + +libffi was written by Anthony Green <green@cygnus.com>. + +Portions of libffi were derived from Gianni Mariani's free gencall +library for Silicon Graphics machines. + +The closure mechanism was designed and implemented by Kresten Krab +Thorup. + +The Sparc port was derived from code contributed by the fine folks at +Visible Decisions Inc <http://www.vdi.com>. Further enhancements were +made by Gordon Irlam at Cygnus Solutions <http://www.cygnus.com>. + +The Alpha port was written by Richard Henderson at Cygnus Solutions. + +Andreas Schwab ported libffi to m68k Linux and provided a number of +bug fixes. + +Geoffrey Keating ported libffi to the PowerPC. + +Raffaele Sena ported libffi to the ARM. + +Jesper Skov and Andrew Haley both did more than their fair share of +stepping through the code and tracking down bugs. + +Thanks also to Tom Tromey for bug fixes and configuration help. + +Thanks to Jim Blandy, who provided some useful feedback on the libffi +interface. + +If you have a problem, or have found a bug, please send a note to +green@cygnus.com. diff --git a/c/libffi_msvc/README.ctypes b/c/libffi_msvc/README.ctypes new file mode 100644 index 0000000..17e8a40 --- /dev/null +++ b/c/libffi_msvc/README.ctypes @@ -0,0 +1,7 @@ +The purpose is to hack the libffi sources so that they can be compiled +with MSVC, and to extend them so that they have the features I need +for ctypes. + +I retrieved the libffi sources from the gcc cvs repository on +2004-01-27. Then I did 'configure' in a 'build' subdirectory on a x86 +linux system, and copied the files I found useful. diff --git a/c/libffi_msvc/ffi.c b/c/libffi_msvc/ffi.c new file mode 100644 index 0000000..836f171 --- /dev/null +++ b/c/libffi_msvc/ffi.c @@ -0,0 +1,486 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 1996, 1998, 1999, 2001 Red Hat, Inc. + Copyright (c) 2002 Ranjit Mathew + Copyright (c) 2002 Bo Thorsen + Copyright (c) 2002 Roger Sayle + + x86 Foreign Function Interface + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#include <ffi.h> +#include <ffi_common.h> + +#include <stdlib.h> + +/* ffi_prep_args is called by the assembly routine once stack space + has been allocated for the function's arguments */ + +extern void Py_FatalError(const char *msg); + +/*@-exportheader@*/ +void ffi_prep_args(char *stack, extended_cif *ecif) +/*@=exportheader@*/ +{ + register unsigned int i; + register void **p_argv; + register char *argp; + register ffi_type **p_arg; + + argp = stack; + if (ecif->cif->flags == FFI_TYPE_STRUCT) + { + *(void **) argp = ecif->rvalue; + argp += sizeof(void *); + } + + p_argv = ecif->avalue; + + for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; + i != 0; + i--, p_arg++) + { + size_t z; + + /* Align if necessary */ + if ((sizeof(void *) - 1) & (size_t) argp) + argp = (char *) ALIGN(argp, sizeof(void *)); + + z = (*p_arg)->size; + if (z < sizeof(int)) + { + z = sizeof(int); + switch ((*p_arg)->type) + { + case FFI_TYPE_SINT8: + *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv); + break; + + case FFI_TYPE_UINT8: + *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv); + break; + + case FFI_TYPE_SINT16: + *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv); + break; + + case FFI_TYPE_UINT16: + *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv); + break; + + case FFI_TYPE_SINT32: + *(signed int *) argp = (signed int)*(SINT32 *)(* p_argv); + break; + + case FFI_TYPE_UINT32: + *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); + break; + + case FFI_TYPE_STRUCT: + *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv); + break; + + default: + FFI_ASSERT(0); + } + } +#ifdef _WIN64 + else if (z > 8) + { + /* On Win64, if a single argument takes more than 8 bytes, + then it is always passed by reference. */ + *(void **)argp = *p_argv; + z = 8; + } +#endif + else + { + memcpy(argp, *p_argv, z); + } + p_argv++; + argp += z; + } + + if (argp - stack > (long)ecif->cif->bytes) + { + Py_FatalError("FFI BUG: not enough stack space for arguments"); + } + return; +} + +/* Perform machine dependent cif processing */ +ffi_status ffi_prep_cif_machdep(ffi_cif *cif) +{ + /* Set the return type flag */ + switch (cif->rtype->type) + { + case FFI_TYPE_VOID: + case FFI_TYPE_SINT64: + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: + case FFI_TYPE_LONGDOUBLE: + cif->flags = (unsigned) cif->rtype->type; + break; + + case FFI_TYPE_STRUCT: + /* MSVC returns small structures in registers. Put in cif->flags + the value FFI_TYPE_STRUCT only if the structure is big enough; + otherwise, put the 4- or 8-bytes integer type. */ + if (cif->rtype->size <= 4) + cif->flags = FFI_TYPE_INT; + else if (cif->rtype->size <= 8) + cif->flags = FFI_TYPE_SINT64; + else + cif->flags = FFI_TYPE_STRUCT; + break; + + case FFI_TYPE_UINT64: +#ifdef _WIN64 + case FFI_TYPE_POINTER: +#endif + cif->flags = FFI_TYPE_SINT64; + break; + + default: + cif->flags = FFI_TYPE_INT; + break; + } + + return FFI_OK; +} + +#ifdef _WIN32 +extern int +ffi_call_x86(void (*)(char *, extended_cif *), + /*@out@*/ extended_cif *, + unsigned, unsigned, + /*@out@*/ unsigned *, + void (*fn)()); +#endif + +#ifdef _WIN64 +extern int +ffi_call_AMD64(void (*)(char *, extended_cif *), + /*@out@*/ extended_cif *, + unsigned, unsigned, + /*@out@*/ unsigned *, + void (*fn)()); +#endif + +int +ffi_call(/*@dependent@*/ ffi_cif *cif, + void (*fn)(), + /*@out@*/ void *rvalue, + /*@dependent@*/ void **avalue) +{ + extended_cif ecif; + + ecif.cif = cif; + ecif.avalue = avalue; + + /* If the return value is a struct and we don't have a return */ + /* value address then we need to make one */ + + if ((rvalue == NULL) && + (cif->flags == FFI_TYPE_STRUCT)) + { + /*@-sysunrecog@*/ + ecif.rvalue = alloca(cif->rtype->size); + /*@=sysunrecog@*/ + } + else + ecif.rvalue = rvalue; + + + switch (cif->abi) + { +#if !defined(_WIN64) + case FFI_SYSV: + case FFI_STDCALL: + return ffi_call_x86(ffi_prep_args, &ecif, cif->bytes, + cif->flags, ecif.rvalue, fn); + break; +#else + case FFI_SYSV: + /*@-usedef@*/ + return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes, + cif->flags, ecif.rvalue, fn); + /*@=usedef@*/ + break; +#endif + + default: + FFI_ASSERT(0); + break; + } + return -1; /* theller: Hrm. */ +} + + +/** private members **/ + +static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, + void** args, ffi_cif* cif); +/* This function is jumped to by the trampoline */ + +#ifdef _WIN64 +void * +#else +static void __fastcall +#endif +ffi_closure_SYSV (ffi_closure *closure, char *argp) +{ + // this is our return value storage + long double res; + + // our various things... + ffi_cif *cif; + void **arg_area; + unsigned short rtype; + void *resp = (void*)&res; + void *args = argp + sizeof(void *); + + cif = closure->cif; + arg_area = (void**) alloca (cif->nargs * sizeof (void*)); + + /* this call will initialize ARG_AREA, such that each + * element in that array points to the corresponding + * value on the stack; and if the function returns + * a structure, it will re-set RESP to point to the + * structure return address. */ + + ffi_prep_incoming_args_SYSV(args, (void**)&resp, arg_area, cif); + + (closure->fun) (cif, resp, arg_area, closure->user_data); + + rtype = cif->flags; + +#if defined(_WIN32) && !defined(_WIN64) +#ifdef _MSC_VER + /* now, do a generic return based on the value of rtype */ + if (rtype == FFI_TYPE_INT) + { + _asm mov eax, resp ; + _asm mov eax, [eax] ; + } + else if (rtype == FFI_TYPE_FLOAT) + { + _asm mov eax, resp ; + _asm fld DWORD PTR [eax] ; +// asm ("flds (%0)" : : "r" (resp) : "st" ); + } + else if (rtype == FFI_TYPE_DOUBLE) + { + _asm mov eax, resp ; + _asm fld QWORD PTR [eax] ; +// asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); + } + else if (rtype == FFI_TYPE_LONGDOUBLE) + { +// asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); + } + else if (rtype == FFI_TYPE_SINT64) + { + _asm mov edx, resp ; + _asm mov eax, [edx] ; + _asm mov edx, [edx + 4] ; +// asm ("movl 0(%0),%%eax;" +// "movl 4(%0),%%edx" +// : : "r"(resp) +// : "eax", "edx"); + } +#else + /* now, do a generic return based on the value of rtype */ + if (rtype == FFI_TYPE_INT) + { + asm ("movl (%0),%%eax" : : "r" (resp) : "eax"); + } + else if (rtype == FFI_TYPE_FLOAT) + { + asm ("flds (%0)" : : "r" (resp) : "st" ); + } + else if (rtype == FFI_TYPE_DOUBLE) + { + asm ("fldl (%0)" : : "r" (resp) : "st", "st(1)" ); + } + else if (rtype == FFI_TYPE_LONGDOUBLE) + { + asm ("fldt (%0)" : : "r" (resp) : "st", "st(1)" ); + } + else if (rtype == FFI_TYPE_SINT64) + { + asm ("movl 0(%0),%%eax;" + "movl 4(%0),%%edx" + : : "r"(resp) + : "eax", "edx"); + } +#endif +#endif + +#ifdef _WIN64 + /* The result is returned in rax. This does the right thing for + result types except for floats; we have to 'mov xmm0, rax' in the + caller to correct this. + */ + return *(void **)resp; +#endif +} + +/*@-exportheader@*/ +static void +ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, + void **avalue, ffi_cif *cif) +/*@=exportheader@*/ +{ + register unsigned int i; + register void **p_argv; + register char *argp; + register ffi_type **p_arg; + + argp = stack; + + if ( cif->flags == FFI_TYPE_STRUCT ) { + *rvalue = *(void **) argp; + argp += 4; + } + + p_argv = avalue; + + for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) + { + size_t z; + + /* Align if necessary */ + if ((sizeof(char *) - 1) & (size_t) argp) { + argp = (char *) ALIGN(argp, sizeof(char*)); + } + + z = (*p_arg)->size; + + /* because we're little endian, this is what it turns into. */ + +#ifdef _WIN64 + if (z > 8) + { + /* On Win64, if a single argument takes more than 8 bytes, + then it is always passed by reference. */ + *p_argv = *((void**) argp); + z = 8; + } + else +#endif + *p_argv = (void*) argp; + + p_argv++; + argp += z; + } + + return; +} + +/* the cif must already be prep'ed */ +extern void ffi_closure_OUTER(); + +ffi_status +ffi_prep_closure_loc (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void *codeloc) +{ + short bytes; + char *tramp; +#ifdef _WIN64 + int mask = 0; +#endif + FFI_ASSERT (cif->abi == FFI_SYSV); + + if (cif->abi == FFI_SYSV) + bytes = 0; +#if !defined(_WIN64) + else if (cif->abi == FFI_STDCALL) + bytes = cif->bytes; +#endif + else + return FFI_BAD_ABI; + + tramp = &closure->tramp[0]; + +#define BYTES(text) memcpy(tramp, text, sizeof(text)), tramp += sizeof(text)-1 +#define POINTER(x) *(void**)tramp = (void*)(x), tramp += sizeof(void*) +#define SHORT(x) *(short*)tramp = x, tramp += sizeof(short) +#define INT(x) *(int*)tramp = x, tramp += sizeof(int) + +#ifdef _WIN64 + if (cif->nargs >= 1 && + (cif->arg_types[0]->type == FFI_TYPE_FLOAT + || cif->arg_types[0]->type == FFI_TYPE_DOUBLE)) + mask |= 1; + if (cif->nargs >= 2 && + (cif->arg_types[1]->type == FFI_TYPE_FLOAT + || cif->arg_types[1]->type == FFI_TYPE_DOUBLE)) + mask |= 2; + if (cif->nargs >= 3 && + (cif->arg_types[2]->type == FFI_TYPE_FLOAT + || cif->arg_types[2]->type == FFI_TYPE_DOUBLE)) + mask |= 4; + if (cif->nargs >= 4 && + (cif->arg_types[3]->type == FFI_TYPE_FLOAT + || cif->arg_types[3]->type == FFI_TYPE_DOUBLE)) + mask |= 8; + + /* 41 BB ---- mov r11d,mask */ + BYTES("\x41\xBB"); INT(mask); + + /* 48 B8 -------- mov rax, closure */ + BYTES("\x48\xB8"); POINTER(closure); + + /* 49 BA -------- mov r10, ffi_closure_OUTER */ + BYTES("\x49\xBA"); POINTER(ffi_closure_OUTER); + + /* 41 FF E2 jmp r10 */ + BYTES("\x41\xFF\xE2"); + +#else + + /* mov ecx, closure */ + BYTES("\xb9"); POINTER(closure); + + /* mov edx, esp */ + BYTES("\x8b\xd4"); + + /* call ffi_closure_SYSV */ + BYTES("\xe8"); POINTER((char*)&ffi_closure_SYSV - (tramp + 4)); + + /* ret bytes */ + BYTES("\xc2"); + SHORT(bytes); + +#endif + + if (tramp - &closure->tramp[0] > FFI_TRAMPOLINE_SIZE) + Py_FatalError("FFI_TRAMPOLINE_SIZE too small in " __FILE__); + + closure->cif = cif; + closure->user_data = user_data; + closure->fun = fun; + return FFI_OK; +} diff --git a/c/libffi_msvc/ffi.h b/c/libffi_msvc/ffi.h new file mode 100644 index 0000000..97cdb59 --- /dev/null +++ b/c/libffi_msvc/ffi.h @@ -0,0 +1,322 @@ +/* -----------------------------------------------------------------*-C-*- + libffi 2.00-beta - Copyright (c) 1996-2003 Red Hat, Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + ----------------------------------------------------------------------- */ + +/* ------------------------------------------------------------------- + The basic API is described in the README file. + + The raw API is designed to bypass some of the argument packing + and unpacking on architectures for which it can be avoided. + + The closure API allows interpreted functions to be packaged up + inside a C function pointer, so that they can be called as C functions, + with no understanding on the client side that they are interpreted. + It can also be used in other cases in which it is necessary to package + up a user specified parameter and a function pointer as a single + function pointer. + + The closure API must be implemented in order to get its functionality, + e.g. for use by gij. Routines are provided to emulate the raw API + if the underlying platform doesn't allow faster implementation. + + More details on the raw and cloure API can be found in: + + http://gcc.gnu.org/ml/java/1999-q3/msg00138.html + + and + + http://gcc.gnu.org/ml/java/1999-q3/msg00174.html + -------------------------------------------------------------------- */ + +#ifndef LIBFFI_H +#define LIBFFI_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Specify which architecture libffi is configured for. */ +//XXX #define X86 + +/* ---- System configuration information --------------------------------- */ + +#include <ffitarget.h> + +#ifndef LIBFFI_ASM + +#include <stddef.h> +#include <limits.h> + +/* LONG_LONG_MAX is not always defined (not if STRICT_ANSI, for example). + But we can find it either under the correct ANSI name, or under GNU + C's internal name. */ +#ifdef LONG_LONG_MAX +# define FFI_LONG_LONG_MAX LONG_LONG_MAX +#else +# ifdef LLONG_MAX +# define FFI_LONG_LONG_MAX LLONG_MAX +# else +# ifdef __GNUC__ +# define FFI_LONG_LONG_MAX __LONG_LONG_MAX__ +# endif +# ifdef _MSC_VER +# define FFI_LONG_LONG_MAX _I64_MAX +# endif +# endif +#endif + +#if SCHAR_MAX == 127 +# define ffi_type_uchar ffi_type_uint8 +# define ffi_type_schar ffi_type_sint8 +#else + #error "char size not supported" +#endif + +#if SHRT_MAX == 32767 +# define ffi_type_ushort ffi_type_uint16 +# define ffi_type_sshort ffi_type_sint16 +#elif SHRT_MAX == 2147483647 +# define ffi_type_ushort ffi_type_uint32 +# define ffi_type_sshort ffi_type_sint32 +#else + #error "short size not supported" +#endif + +#if INT_MAX == 32767 +# define ffi_type_uint ffi_type_uint16 +# define ffi_type_sint ffi_type_sint16 +#elif INT_MAX == 2147483647 +# define ffi_type_uint ffi_type_uint32 +# define ffi_type_sint ffi_type_sint32 +#elif INT_MAX == 9223372036854775807 +# define ffi_type_uint ffi_type_uint64 +# define ffi_type_sint ffi_type_sint64 +#else + #error "int size not supported" +#endif + +#define ffi_type_ulong ffi_type_uint64 +#define ffi_type_slong ffi_type_sint64 +#if LONG_MAX == 2147483647 +# if FFI_LONG_LONG_MAX != 9223372036854775807 + #error "no 64-bit data type supported" +# endif +#elif LONG_MAX != 9223372036854775807 + #error "long size not supported" +#endif + +/* The closure code assumes that this works on pointers, i.e. a size_t */ +/* can hold a pointer. */ + +typedef struct _ffi_type +{ + size_t size; + unsigned short alignment; + unsigned short type; + /*@null@*/ struct _ffi_type **elements; +} ffi_type; + +/* These are defined in types.c */ +extern ffi_type ffi_type_void; +extern ffi_type ffi_type_uint8; +extern ffi_type ffi_type_sint8; +extern ffi_type ffi_type_uint16; +extern ffi_type ffi_type_sint16; +extern ffi_type ffi_type_uint32; +extern ffi_type ffi_type_sint32; +extern ffi_type ffi_type_uint64; +extern ffi_type ffi_type_sint64; +extern ffi_type ffi_type_float; +extern ffi_type ffi_type_double; +extern ffi_type ffi_type_longdouble; +extern ffi_type ffi_type_pointer; + + +typedef enum { + FFI_OK = 0, + FFI_BAD_TYPEDEF, + FFI_BAD_ABI +} ffi_status; + +typedef unsigned FFI_TYPE; + +typedef struct { + ffi_abi abi; + unsigned nargs; + /*@dependent@*/ ffi_type **arg_types; + /*@dependent@*/ ffi_type *rtype; + unsigned bytes; + unsigned flags; +#ifdef FFI_EXTRA_CIF_FIELDS + FFI_EXTRA_CIF_FIELDS; +#endif +} ffi_cif; + +/* ---- Definitions for the raw API -------------------------------------- */ + +#ifdef _WIN64 +#define FFI_SIZEOF_ARG 8 +#else +#define FFI_SIZEOF_ARG 4 +#endif + +typedef union { + ffi_sarg sint; + ffi_arg uint; + float flt; + char data[FFI_SIZEOF_ARG]; + void* ptr; +} ffi_raw; + +void ffi_raw_call (/*@dependent@*/ ffi_cif *cif, + void (*fn)(), + /*@out@*/ void *rvalue, + /*@dependent@*/ ffi_raw *avalue); + +void ffi_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); +void ffi_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); +size_t ffi_raw_size (ffi_cif *cif); + +/* This is analogous to the raw API, except it uses Java parameter */ +/* packing, even on 64-bit machines. I.e. on 64-bit machines */ +/* longs and doubles are followed by an empty 64-bit word. */ + +void ffi_java_raw_call (/*@dependent@*/ ffi_cif *cif, + void (*fn)(), + /*@out@*/ void *rvalue, + /*@dependent@*/ ffi_raw *avalue); + +void ffi_java_ptrarray_to_raw (ffi_cif *cif, void **args, ffi_raw *raw); +void ffi_java_raw_to_ptrarray (ffi_cif *cif, ffi_raw *raw, void **args); +size_t ffi_java_raw_size (ffi_cif *cif); + +/* ---- Definitions for closures ----------------------------------------- */ + +#if FFI_CLOSURES + +typedef struct { + char tramp[FFI_TRAMPOLINE_SIZE]; + ffi_cif *cif; + void (*fun)(ffi_cif*,void*,void**,void*); + void *user_data; +} ffi_closure; + +void ffi_closure_free(void *); +void *ffi_closure_alloc (size_t size, void **code); + +ffi_status +ffi_prep_closure_loc (ffi_closure*, + ffi_cif *, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data, + void *codeloc); + +/* AR: for cffi we need the following API, and not the _loc version */ +#define ffi_prep_closure(a,b,c,d) ffi_prep_closure_loc(a,b,c,d,a) + +typedef struct { + char tramp[FFI_TRAMPOLINE_SIZE]; + + ffi_cif *cif; + +#if !FFI_NATIVE_RAW_API + + /* if this is enabled, then a raw closure has the same layout + as a regular closure. We use this to install an intermediate + handler to do the transaltion, void** -> ffi_raw*. */ + + void (*translate_args)(ffi_cif*,void*,void**,void*); + void *this_closure; + +#endif + + void (*fun)(ffi_cif*,void*,ffi_raw*,void*); + void *user_data; + +} ffi_raw_closure; + +ffi_status +ffi_prep_raw_closure (ffi_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_raw*,void*), + void *user_data); + +ffi_status +ffi_prep_java_raw_closure (ffi_raw_closure*, + ffi_cif *cif, + void (*fun)(ffi_cif*,void*,ffi_raw*,void*), + void *user_data); + +#endif /* FFI_CLOSURES */ + +/* ---- Public interface definition -------------------------------------- */ + +ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, + ffi_abi abi, + unsigned int nargs, + /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, + /*@dependent@*/ ffi_type **atypes); + +int +ffi_call(/*@dependent@*/ ffi_cif *cif, + void (*fn)(), + /*@out@*/ void *rvalue, + /*@dependent@*/ void **avalue); + +/* Useful for eliminating compiler warnings */ +#define FFI_FN(f) ((void (*)())f) + +/* ---- Definitions shared with assembly code ---------------------------- */ + +#endif + +/* If these change, update src/mips/ffitarget.h. */ +#define FFI_TYPE_VOID 0 +#define FFI_TYPE_INT 1 +#define FFI_TYPE_FLOAT 2 +#define FFI_TYPE_DOUBLE 3 +#if 1 +#define FFI_TYPE_LONGDOUBLE 4 +#else +#define FFI_TYPE_LONGDOUBLE FFI_TYPE_DOUBLE +#endif +#define FFI_TYPE_UINT8 5 +#define FFI_TYPE_SINT8 6 +#define FFI_TYPE_UINT16 7 +#define FFI_TYPE_SINT16 8 +#define FFI_TYPE_UINT32 9 +#define FFI_TYPE_SINT32 10 +#define FFI_TYPE_UINT64 11 +#define FFI_TYPE_SINT64 12 +#define FFI_TYPE_STRUCT 13 +#define FFI_TYPE_POINTER 14 + +/* This should always refer to the last type code (for sanity checks) */ +#define FFI_TYPE_LAST FFI_TYPE_POINTER + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/c/libffi_msvc/ffi_common.h b/c/libffi_msvc/ffi_common.h new file mode 100644 index 0000000..43fb83b --- /dev/null +++ b/c/libffi_msvc/ffi_common.h @@ -0,0 +1,77 @@ +/* ----------------------------------------------------------------------- + ffi_common.h - Copyright (c) 1996 Red Hat, Inc. + + Common internal definitions and macros. Only necessary for building + libffi. + ----------------------------------------------------------------------- */ + +#ifndef FFI_COMMON_H +#define FFI_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <fficonfig.h> +#include <malloc.h> + +/* Check for the existence of memcpy. */ +#if STDC_HEADERS +# include <string.h> +#else +# ifndef HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#if defined(FFI_DEBUG) +#include <stdio.h> +#endif + +#ifdef FFI_DEBUG +/*@exits@*/ void ffi_assert(/*@temp@*/ char *expr, /*@temp@*/ char *file, int line); +void ffi_stop_here(void); +void ffi_type_test(/*@temp@*/ /*@out@*/ ffi_type *a, /*@temp@*/ char *file, int line); + +#define FFI_ASSERT(x) ((x) ? (void)0 : ffi_assert(#x, __FILE__,__LINE__)) +#define FFI_ASSERT_AT(x, f, l) ((x) ? 0 : ffi_assert(#x, (f), (l))) +#define FFI_ASSERT_VALID_TYPE(x) ffi_type_test (x, __FILE__, __LINE__) +#else +#define FFI_ASSERT(x) +#define FFI_ASSERT_AT(x, f, l) +#define FFI_ASSERT_VALID_TYPE(x) +#endif + +#define ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1) + +/* Perform machine dependent cif processing */ +ffi_status ffi_prep_cif_machdep(ffi_cif *cif); + +/* Extended cif, used in callback from assembly routine */ +typedef struct +{ + /*@dependent@*/ ffi_cif *cif; + /*@dependent@*/ void *rvalue; + /*@dependent@*/ void **avalue; +} extended_cif; + +/* Terse sized type definitions. */ +typedef unsigned int UINT8 __attribute__((__mode__(__QI__))); +typedef signed int SINT8 __attribute__((__mode__(__QI__))); +typedef unsigned int UINT16 __attribute__((__mode__(__HI__))); +typedef signed int SINT16 __attribute__((__mode__(__HI__))); +typedef unsigned int UINT32 __attribute__((__mode__(__SI__))); +typedef signed int SINT32 __attribute__((__mode__(__SI__))); +typedef unsigned int UINT64 __attribute__((__mode__(__DI__))); +typedef signed int SINT64 __attribute__((__mode__(__DI__))); + +typedef float FLOAT32; + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/c/libffi_msvc/fficonfig.h b/c/libffi_msvc/fficonfig.h new file mode 100644 index 0000000..c14f653 --- /dev/null +++ b/c/libffi_msvc/fficonfig.h @@ -0,0 +1,96 @@ +/* fficonfig.h. Originally created by configure, now hand_maintained for MSVC. */ + +/* fficonfig.h. Generated automatically by configure. */ +/* fficonfig.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define this for MSVC, but not for mingw32! */ +#ifdef _MSC_VER +#define __attribute__(x) /* */ +#endif +#define alloca _alloca + +/*----------------------------------------------------------------*/ + +/* Define if using alloca.c. */ +/* #undef C_ALLOCA */ + +/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. + This function is required for alloca.c support on those systems. */ +/* #undef CRAY_STACKSEG_END */ + +/* Define if you have alloca, as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define if you have <alloca.h> and it should be used (not on Ultrix). */ +/* #define HAVE_ALLOCA_H 1 */ + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at run-time. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown + */ +/* #undef STACK_DIRECTION */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if read-only mmap of a plain file works. */ +//#define HAVE_MMAP_FILE 1 + +/* Define if mmap of /dev/zero works. */ +//#define HAVE_MMAP_DEV_ZERO 1 + +/* Define if mmap with MAP_ANON(YMOUS) works. */ +//#define HAVE_MMAP_ANON 1 + +/* The number of bytes in type double */ +#define SIZEOF_DOUBLE 8 + +/* The number of bytes in type long double */ +#define SIZEOF_LONG_DOUBLE 12 + +/* Define if you have the long double type and it is bigger than a double */ +#define HAVE_LONG_DOUBLE 1 + +/* whether byteorder is bigendian */ +/* #undef WORDS_BIGENDIAN */ + +/* Define if the host machine stores words of multi-word integers in + big-endian order. */ +/* #undef HOST_WORDS_BIG_ENDIAN */ + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#define BYTEORDER 1234 + +/* Define if your assembler and linker support unaligned PC relative relocs. */ +/* #undef HAVE_AS_SPARC_UA_PCREL */ + +/* Define if your assembler supports .register. */ +/* #undef HAVE_AS_REGISTER_PSEUDO_OP */ + +/* Define if .eh_frame sections should be read-only. */ +/* #undef HAVE_RO_EH_FRAME */ + +/* Define to the flags needed for the .section .eh_frame directive. */ +/* #define EH_FRAME_FLAGS "aw" */ + +/* Define to the flags needed for the .section .eh_frame directive. */ +/* #define EH_FRAME_FLAGS "aw" */ + +/* Define this if you want extra debugging. */ +/* #undef FFI_DEBUG */ + +/* Define this is you do not want support for aggregate types. */ +/* #undef FFI_NO_STRUCTS */ + +/* Define this is you do not want support for the raw API. */ +/* #undef FFI_NO_RAW_API */ + +/* Define this if you are using Purify and want to suppress spurious messages. */ +/* #undef USING_PURIFY */ + diff --git a/c/libffi_msvc/ffitarget.h b/c/libffi_msvc/ffitarget.h new file mode 100644 index 0000000..85f5ee8 --- /dev/null +++ b/c/libffi_msvc/ffitarget.h @@ -0,0 +1,85 @@ +/* -----------------------------------------------------------------*-C-*- + ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc. + Target configuration macros for x86 and x86-64. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + ----------------------------------------------------------------------- */ + +#ifndef LIBFFI_TARGET_H +#define LIBFFI_TARGET_H + +/* ---- System specific configurations ----------------------------------- */ + +#if defined (X86_64) && defined (__i386__) +#undef X86_64 +#define X86 +#endif + +/* ---- Generic type definitions ----------------------------------------- */ + +#ifndef LIBFFI_ASM +#ifndef _WIN64 +typedef unsigned long ffi_arg; +#else +typedef unsigned __int64 ffi_arg; +#endif +typedef signed long ffi_sarg; + +typedef enum ffi_abi { + FFI_FIRST_ABI = 0, + + /* ---- Intel x86 Win32 ---------- */ + FFI_SYSV, +#ifndef _WIN64 + FFI_STDCALL, +#endif + /* TODO: Add fastcall support for the sake of completeness */ + FFI_DEFAULT_ABI = FFI_SYSV, + + /* ---- Intel x86 and AMD x86-64 - */ +/* #if !defined(X86_WIN32) && (defined(__i386__) || defined(__x86_64__)) */ +/* FFI_SYSV, */ +/* FFI_UNIX64,*/ /* Unix variants all use the same ABI for x86-64 */ +/* #ifdef __i386__ */ +/* FFI_DEFAULT_ABI = FFI_SYSV, */ +/* #else */ +/* FFI_DEFAULT_ABI = FFI_UNIX64, */ +/* #endif */ +/* #endif */ + + FFI_LAST_ABI = FFI_DEFAULT_ABI + 1 +} ffi_abi; +#endif + +/* ---- Definitions for closures ----------------------------------------- */ + +#define FFI_CLOSURES 1 + +#ifdef _WIN64 +#define FFI_TRAMPOLINE_SIZE 29 +#define FFI_NATIVE_RAW_API 0 +#else +#define FFI_TRAMPOLINE_SIZE 15 +#define FFI_NATIVE_RAW_API 1 /* x86 has native raw api support */ +#endif + +#endif + diff --git a/c/libffi_msvc/prep_cif.c b/c/libffi_msvc/prep_cif.c new file mode 100644 index 0000000..5dacfff --- /dev/null +++ b/c/libffi_msvc/prep_cif.c @@ -0,0 +1,181 @@ +/* ----------------------------------------------------------------------- + prep_cif.c - Copyright (c) 1996, 1998 Red Hat, Inc. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#include <ffi.h> +#include <ffi_common.h> +#include <stdlib.h> + + +/* Round up to FFI_SIZEOF_ARG. */ + +#define STACK_ARG_SIZE(x) ALIGN(x, FFI_SIZEOF_ARG) + +/* Perform machine independent initialization of aggregate type + specifications. */ + +static ffi_status initialize_aggregate(/*@out@*/ ffi_type *arg) +{ + ffi_type **ptr; + + FFI_ASSERT(arg != NULL); + + /*@-usedef@*/ + + FFI_ASSERT(arg->elements != NULL); + FFI_ASSERT(arg->size == 0); + FFI_ASSERT(arg->alignment == 0); + + ptr = &(arg->elements[0]); + + while ((*ptr) != NULL) + { + if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) + return FFI_BAD_TYPEDEF; + + /* Perform a sanity check on the argument type */ + FFI_ASSERT_VALID_TYPE(*ptr); + + arg->size = ALIGN(arg->size, (*ptr)->alignment); + arg->size += (*ptr)->size; + + arg->alignment = (arg->alignment > (*ptr)->alignment) ? + arg->alignment : (*ptr)->alignment; + + ptr++; + } + + /* Structure size includes tail padding. This is important for + structures that fit in one register on ABIs like the PowerPC64 + Linux ABI that right justify small structs in a register. + It's also needed for nested structure layout, for example + struct A { long a; char b; }; struct B { struct A x; char y; }; + should find y at an offset of 2*sizeof(long) and result in a + total size of 3*sizeof(long). */ + arg->size = ALIGN (arg->size, arg->alignment); + + if (arg->size == 0) + return FFI_BAD_TYPEDEF; + else + return FFI_OK; + + /*@=usedef@*/ +} + +/* Perform machine independent ffi_cif preparation, then call + machine dependent routine. */ + +ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif, + ffi_abi abi, unsigned int nargs, + /*@dependent@*/ /*@out@*/ /*@partial@*/ ffi_type *rtype, + /*@dependent@*/ ffi_type **atypes) +{ + unsigned bytes = 0; + unsigned int i; + ffi_type **ptr; + + FFI_ASSERT(cif != NULL); + FFI_ASSERT((abi > FFI_FIRST_ABI) && (abi <= FFI_DEFAULT_ABI)); + + cif->abi = abi; + cif->arg_types = atypes; + cif->nargs = nargs; + cif->rtype = rtype; + + cif->flags = 0; + + /* Initialize the return type if necessary */ + /*@-usedef@*/ + if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK)) + return FFI_BAD_TYPEDEF; + /*@=usedef@*/ + + /* Perform a sanity check on the return type */ + FFI_ASSERT_VALID_TYPE(cif->rtype); + + /* x86-64 and s390 stack space allocation is handled in prep_machdep. */ +#if !defined M68K && !defined __x86_64__ && !defined S390 + /* Make space for the return structure pointer */ + if (cif->rtype->type == FFI_TYPE_STRUCT +#ifdef _WIN32 + && (cif->rtype->size > 8) /* MSVC returns small structs in registers */ +#endif +#ifdef SPARC + && (cif->abi != FFI_V9 || cif->rtype->size > 32) +#endif + ) + bytes = STACK_ARG_SIZE(sizeof(void*)); +#endif + + for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) + { + + /* Initialize any uninitialized aggregate type definitions */ + if (((*ptr)->size == 0) && (initialize_aggregate((*ptr)) != FFI_OK)) + return FFI_BAD_TYPEDEF; + + /* Perform a sanity check on the argument type, do this + check after the initialization. */ + FFI_ASSERT_VALID_TYPE(*ptr); + +#if !defined __x86_64__ && !defined S390 +#ifdef SPARC + if (((*ptr)->type == FFI_TYPE_STRUCT + && ((*ptr)->size > 16 || cif->abi != FFI_V9)) + || ((*ptr)->type == FFI_TYPE_LONGDOUBLE + && cif->abi != FFI_V9)) + bytes += sizeof(void*); + else +#endif + { +#if !defined(_MSC_VER) && !defined(__MINGW32__) + /* Don't know if this is a libffi bug or not. At least on + Windows with MSVC, function call parameters are *not* + aligned in the same way as structure fields are, they are + only aligned in integer boundaries. + + This doesn't do any harm for cdecl functions and closures, + since the caller cleans up the stack, but it is wrong for + stdcall functions where the callee cleans. + */ + + /* Add any padding if necessary */ + if (((*ptr)->alignment - 1) & bytes) + bytes = ALIGN(bytes, (*ptr)->alignment); + +#endif + bytes += STACK_ARG_SIZE((*ptr)->size); + } +#endif + } + +#ifdef _WIN64 + /* Function call needs at least 40 bytes stack size, on win64 AMD64 */ + if (bytes < 40) + bytes = 40; +#endif + + cif->bytes = bytes; + + /* Perform machine dependent cif processing */ + return ffi_prep_cif_machdep(cif); +} diff --git a/c/libffi_msvc/types.c b/c/libffi_msvc/types.c new file mode 100644 index 0000000..4433ac2 --- /dev/null +++ b/c/libffi_msvc/types.c @@ -0,0 +1,104 @@ +/* ----------------------------------------------------------------------- + types.c - Copyright (c) 1996, 1998 Red Hat, Inc. + + Predefined ffi_types needed by libffi. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +#include <ffi.h> +#include <ffi_common.h> + +/* Type definitions */ + +#define FFI_INTEGRAL_TYPEDEF(n, s, a, t) ffi_type ffi_type_##n = { s, a, t, NULL } +#define FFI_AGGREGATE_TYPEDEF(n, e) ffi_type ffi_type_##n = { 0, 0, FFI_TYPE_STRUCT, e } + +/* Size and alignment are fake here. They must not be 0. */ +FFI_INTEGRAL_TYPEDEF(void, 1, 1, FFI_TYPE_VOID); + +FFI_INTEGRAL_TYPEDEF(uint8, 1, 1, FFI_TYPE_UINT8); +FFI_INTEGRAL_TYPEDEF(sint8, 1, 1, FFI_TYPE_SINT8); +FFI_INTEGRAL_TYPEDEF(uint16, 2, 2, FFI_TYPE_UINT16); +FFI_INTEGRAL_TYPEDEF(sint16, 2, 2, FFI_TYPE_SINT16); +FFI_INTEGRAL_TYPEDEF(uint32, 4, 4, FFI_TYPE_UINT32); +FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32); +FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT); + +#if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \ + || defined IA64 || defined _WIN64 + +FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER); + +#else + +FFI_INTEGRAL_TYPEDEF(pointer, 4, 4, FFI_TYPE_POINTER); + +#endif + +#if defined X86 || defined X86_WIN32 || defined ARM || defined M68K + +FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); +FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); + +#elif defined SH + +FFI_INTEGRAL_TYPEDEF(uint64, 8, 4, FFI_TYPE_UINT64); +FFI_INTEGRAL_TYPEDEF(sint64, 8, 4, FFI_TYPE_SINT64); + +#else + +FFI_INTEGRAL_TYPEDEF(uint64, 8, 8, FFI_TYPE_UINT64); +FFI_INTEGRAL_TYPEDEF(sint64, 8, 8, FFI_TYPE_SINT64); + +#endif + + +#if defined X86 || defined X86_WIN32 || defined M68K + +FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); +FFI_INTEGRAL_TYPEDEF(longdouble, 12, 4, FFI_TYPE_LONGDOUBLE); + +#elif defined ARM || defined SH || defined POWERPC_AIX || defined POWERPC_DARWIN + +FFI_INTEGRAL_TYPEDEF(double, 8, 4, FFI_TYPE_DOUBLE); +FFI_INTEGRAL_TYPEDEF(longdouble, 8, 4, FFI_TYPE_LONGDOUBLE); + +#elif defined SPARC + +FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); +#ifdef SPARC64 +FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); +#else +FFI_INTEGRAL_TYPEDEF(longdouble, 16, 8, FFI_TYPE_LONGDOUBLE); +#endif + +#elif defined X86_64 + +FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); +FFI_INTEGRAL_TYPEDEF(longdouble, 16, 16, FFI_TYPE_LONGDOUBLE); + +#else + +FFI_INTEGRAL_TYPEDEF(double, 8, 8, FFI_TYPE_DOUBLE); +FFI_INTEGRAL_TYPEDEF(longdouble, 8, 8, FFI_TYPE_LONGDOUBLE); + +#endif + diff --git a/c/libffi_msvc/win32.c b/c/libffi_msvc/win32.c new file mode 100644 index 0000000..d1149a8 --- /dev/null +++ b/c/libffi_msvc/win32.c @@ -0,0 +1,162 @@ +/* ----------------------------------------------------------------------- + win32.S - Copyright (c) 1996, 1998, 2001, 2002 Red Hat, Inc. + Copyright (c) 2001 John Beniton + Copyright (c) 2002 Ranjit Mathew + + + X86 Foreign Function Interface + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + ``Software''), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + ----------------------------------------------------------------------- */ + +/* theller: almost verbatim translation from gas syntax to MSVC inline + assembler code. */ + +/* theller: ffi_call_x86 now returns an integer - the difference of the stack + pointer before and after the function call. If everything is ok, zero is + returned. If stdcall functions are passed the wrong number of arguments, + the difference will be nonzero. */ + +#include <ffi.h> +#include <ffi_common.h> + +__declspec(naked) int +ffi_call_x86(void (* prepfunc)(char *, extended_cif *), /* 8 */ + extended_cif *ecif, /* 12 */ + unsigned bytes, /* 16 */ + unsigned flags, /* 20 */ + unsigned *rvalue, /* 24 */ + void (*fn)()) /* 28 */ +{ + _asm { + push ebp + mov ebp, esp + + push esi // NEW: this register must be preserved across function calls +// XXX SAVE ESP NOW! + mov esi, esp // save stack pointer before the call + +// Make room for all of the new args. + mov ecx, [ebp+16] + sub esp, ecx // sub esp, bytes + + mov eax, esp + +// Place all of the ffi_prep_args in position + push [ebp + 12] // ecif + push eax + call [ebp + 8] // prepfunc + +// Return stack to previous state and call the function + add esp, 8 +// FIXME: Align the stack to a 128-bit boundary to avoid +// potential performance hits. + call [ebp + 28] + +// Load ecif->cif->abi + mov ecx, [ebp + 12] + mov ecx, [ecx]ecif.cif + mov ecx, [ecx]ecif.cif.abi + + cmp ecx, FFI_STDCALL + je noclean +// STDCALL: Remove the space we pushed for the args + mov ecx, [ebp + 16] + add esp, ecx +// CDECL: Caller has already cleaned the stack +noclean: +// Check that esp has the same value as before! + sub esi, esp + +// Load %ecx with the return type code + mov ecx, [ebp + 20] + +// If the return value pointer is NULL, assume no return value. +/* + Intel asm is weird. We have to explicitely specify 'DWORD PTR' in the nexr instruction, + otherwise only one BYTE will be compared (instead of a DWORD)! + */ + cmp DWORD PTR [ebp + 24], 0 + jne sc_retint + +// Even if there is no space for the return value, we are +// obliged to handle floating-point values. + cmp ecx, FFI_TYPE_FLOAT + jne sc_noretval +// fstp %st(0) + fstp st(0) + + jmp sc_epilogue + +sc_retint: + cmp ecx, FFI_TYPE_INT + jne sc_retfloat +// # Load %ecx with the pointer to storage for the return value + mov ecx, [ebp + 24] + mov [ecx + 0], eax + jmp sc_epilogue + +sc_retfloat: + cmp ecx, FFI_TYPE_FLOAT + jne sc_retdouble +// Load %ecx with the pointer to storage for the return value + mov ecx, [ebp+24] +// fstps (%ecx) + fstp DWORD PTR [ecx] + jmp sc_epilogue + +sc_retdouble: + cmp ecx, FFI_TYPE_DOUBLE + jne sc_retlongdouble +// movl 24(%ebp),%ecx + mov ecx, [ebp+24] + fstp QWORD PTR [ecx] + jmp sc_epilogue + + jmp sc_retlongdouble // avoid warning about unused label +sc_retlongdouble: + cmp ecx, FFI_TYPE_LONGDOUBLE + jne sc_retint64 +// Load %ecx with the pointer to storage for the return value + mov ecx, [ebp+24] +// fstpt (%ecx) + fstp QWORD PTR [ecx] /* XXX ??? */ + jmp sc_epilogue + +sc_retint64: + cmp ecx, FFI_TYPE_SINT64 + jne sc_retstruct +// Load %ecx with the pointer to storage for the return value + mov ecx, [ebp+24] + mov [ecx+0], eax + mov [ecx+4], edx + +sc_retstruct: +// Nothing to do! + +sc_noretval: +sc_epilogue: + mov eax, esi + pop esi // NEW restore: must be preserved across function calls + mov esp, ebp + pop ebp + ret + } +} diff --git a/c/libffi_msvc/win64.asm b/c/libffi_msvc/win64.asm new file mode 100644 index 0000000..301188b --- /dev/null +++ b/c/libffi_msvc/win64.asm @@ -0,0 +1,156 @@ +PUBLIC ffi_call_AMD64 + +EXTRN __chkstk:NEAR +EXTRN ffi_closure_SYSV:NEAR + +_TEXT SEGMENT + +;;; ffi_closure_OUTER will be called with these registers set: +;;; rax points to 'closure' +;;; r11 contains a bit mask that specifies which of the +;;; first four parameters are float or double +;;; +;;; It must move the parameters passed in registers to their stack location, +;;; call ffi_closure_SYSV for the actual work, then return the result. +;;; +ffi_closure_OUTER PROC FRAME + ;; save actual arguments to their stack space. + test r11, 1 + jne first_is_float + mov QWORD PTR [rsp+8], rcx + jmp second +first_is_float: + movlpd QWORD PTR [rsp+8], xmm0 + +second: + test r11, 2 + jne second_is_float + mov QWORD PTR [rsp+16], rdx + jmp third +second_is_float: + movlpd QWORD PTR [rsp+16], xmm1 + +third: + test r11, 4 + jne third_is_float + mov QWORD PTR [rsp+24], r8 + jmp forth +third_is_float: + movlpd QWORD PTR [rsp+24], xmm2 + +forth: + test r11, 8 + jne forth_is_float + mov QWORD PTR [rsp+32], r9 + jmp done +forth_is_float: + movlpd QWORD PTR [rsp+32], xmm3 + +done: +.ALLOCSTACK 40 + sub rsp, 40 +.ENDPROLOG + mov rcx, rax ; context is first parameter + mov rdx, rsp ; stack is second parameter + add rdx, 40 ; correct our own area + mov rax, ffi_closure_SYSV + call rax ; call the real closure function + ;; Here, code is missing that handles float return values + add rsp, 40 + movd xmm0, rax ; In case the closure returned a float. + ret 0 +ffi_closure_OUTER ENDP + + +;;; ffi_call_AMD64 + +stack$ = 0 +prepfunc$ = 32 +ecif$ = 40 +bytes$ = 48 +flags$ = 56 +rvalue$ = 64 +fn$ = 72 + +ffi_call_AMD64 PROC FRAME + + mov QWORD PTR [rsp+32], r9 + mov QWORD PTR [rsp+24], r8 + mov QWORD PTR [rsp+16], rdx + mov QWORD PTR [rsp+8], rcx +.PUSHREG rbp + push rbp +.ALLOCSTACK 48 + sub rsp, 48 ; 00000030H +.SETFRAME rbp, 32 + lea rbp, QWORD PTR [rsp+32] +.ENDPROLOG + + mov eax, DWORD PTR bytes$[rbp] + add rax, 15 + and rax, -16 + call __chkstk + sub rsp, rax + lea rax, QWORD PTR [rsp+32] + mov QWORD PTR stack$[rbp], rax + + mov rdx, QWORD PTR ecif$[rbp] + mov rcx, QWORD PTR stack$[rbp] + call QWORD PTR prepfunc$[rbp] + + mov rsp, QWORD PTR stack$[rbp] + + movlpd xmm3, QWORD PTR [rsp+24] + movd r9, xmm3 + + movlpd xmm2, QWORD PTR [rsp+16] + movd r8, xmm2 + + movlpd xmm1, QWORD PTR [rsp+8] + movd rdx, xmm1 + + movlpd xmm0, QWORD PTR [rsp] + movd rcx, xmm0 + + call QWORD PTR fn$[rbp] +ret_int$: + cmp DWORD PTR flags$[rbp], 1 ; FFI_TYPE_INT + jne ret_float$ + + mov rcx, QWORD PTR rvalue$[rbp] + mov DWORD PTR [rcx], eax + jmp SHORT ret_nothing$ + +ret_float$: + cmp DWORD PTR flags$[rbp], 2 ; FFI_TYPE_FLOAT + jne SHORT ret_double$ + + mov rax, QWORD PTR rvalue$[rbp] + movlpd QWORD PTR [rax], xmm0 + jmp SHORT ret_nothing$ + +ret_double$: + cmp DWORD PTR flags$[rbp], 3 ; FFI_TYPE_DOUBLE + jne SHORT ret_int64$ + + mov rax, QWORD PTR rvalue$[rbp] + movlpd QWORD PTR [rax], xmm0 + jmp SHORT ret_nothing$ + +ret_int64$: + cmp DWORD PTR flags$[rbp], 12 ; FFI_TYPE_SINT64 + jne ret_nothing$ + + mov rcx, QWORD PTR rvalue$[rbp] + mov QWORD PTR [rcx], rax + jmp SHORT ret_nothing$ + +ret_nothing$: + xor eax, eax + + lea rsp, QWORD PTR [rbp+16] + pop rbp + ret 0 +ffi_call_AMD64 ENDP +_TEXT ENDS +END diff --git a/c/libffi_msvc/win64.obj b/c/libffi_msvc/win64.obj Binary files differnew file mode 100644 index 0000000..38d3cd1 --- /dev/null +++ b/c/libffi_msvc/win64.obj diff --git a/c/malloc_closure.h b/c/malloc_closure.h new file mode 100644 index 0000000..bebb93d --- /dev/null +++ b/c/malloc_closure.h @@ -0,0 +1,176 @@ +/* + * This file is from CPython's Modules/_ctypes/malloc_closure.c + * and has received some edits. + */ + +#include <ffi.h> +#ifdef MS_WIN32 +#include <windows.h> +#else +#include <sys/mman.h> +#include <unistd.h> +# if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +# define MAP_ANONYMOUS MAP_ANON +# endif +#endif + +/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. + + This is, apparently, an undocumented change to ffi_prep_closure(): + depending on the Linux kernel we're running on, we must give it a + mmap that is either PROT_READ|PROT_WRITE|PROT_EXEC or only + PROT_READ|PROT_WRITE. In the latter case, just trying to obtain a + mmap with PROT_READ|PROT_WRITE|PROT_EXEC would kill our process(!), + but in that situation libffi is fine with only PROT_READ|PROT_WRITE. + There is nothing in the libffi API to know that, though, so we have + to guess by parsing /proc/self/status. "Meh." + */ +#ifdef __linux__ +#include <stdlib.h> + +static int emutramp_enabled = -1; + +static int +emutramp_enabled_check (void) +{ + char *buf = NULL; + size_t len = 0; + FILE *f; + int ret; + f = fopen ("/proc/self/status", "r"); + if (f == NULL) + return 0; + ret = 0; + + while (getline (&buf, &len, f) != -1) + if (!strncmp (buf, "PaX:", 4)) + { + char emutramp; + if (sscanf (buf, "%*s %*c%c", &emutramp) == 1) + ret = (emutramp == 'E'); + break; + } + free (buf); + fclose (f); + return ret; +} + +#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \ + : (emutramp_enabled = emutramp_enabled_check ())) +#else +#define is_emutramp_enabled() 0 +#endif + + +/* 'allocate_num_pages' is dynamically adjusted starting from one + page. It grows by a factor of PAGE_ALLOCATION_GROWTH_RATE. This is + meant to handle both the common case of not needing a lot of pages, + and the rare case of needing many of them. Systems in general have a + limit of how many mmap'd blocks can be open. +*/ + +#define PAGE_ALLOCATION_GROWTH_RATE 1.3 + +static Py_ssize_t allocate_num_pages = 0; + +/* #define MALLOC_CLOSURE_DEBUG */ /* enable for some debugging output */ + +/******************************************************************/ + +union mmaped_block { + ffi_closure closure; + union mmaped_block *next; +}; + +static union mmaped_block *free_list = 0; +static Py_ssize_t _pagesize = 0; + +static void more_core(void) +{ + union mmaped_block *item; + Py_ssize_t count, i; + +/* determine the pagesize */ +#ifdef MS_WIN32 + if (!_pagesize) { + SYSTEM_INFO systeminfo; + GetSystemInfo(&systeminfo); + _pagesize = systeminfo.dwPageSize; + } +#else + if (!_pagesize) { +#ifdef _SC_PAGESIZE + _pagesize = sysconf(_SC_PAGESIZE); +#else + _pagesize = getpagesize(); +#endif + } +#endif + if (_pagesize <= 0) + _pagesize = 4096; + + /* bump 'allocate_num_pages' */ + allocate_num_pages = 1 + ( + (Py_ssize_t)(allocate_num_pages * PAGE_ALLOCATION_GROWTH_RATE)); + + /* calculate the number of mmaped_blocks to allocate */ + count = (allocate_num_pages * _pagesize) / sizeof(union mmaped_block); + + /* allocate a memory block */ +#ifdef MS_WIN32 + item = (union mmaped_block *)VirtualAlloc(NULL, + count * sizeof(union mmaped_block), + MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + if (item == NULL) + return; +#else + { + int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + if (is_emutramp_enabled ()) + prot &= ~PROT_EXEC; + item = (union mmaped_block *)mmap(NULL, + allocate_num_pages * _pagesize, + prot, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0); + if (item == (void *)MAP_FAILED) + return; + } +#endif + +#ifdef MALLOC_CLOSURE_DEBUG + printf("block at %p allocated (%ld bytes), %ld mmaped_blocks\n", + item, (long)(allocate_num_pages * _pagesize), (long)count); +#endif + /* put them into the free list */ + for (i = 0; i < count; ++i) { + item->next = free_list; + free_list = item; + ++item; + } +} + +/******************************************************************/ + +/* put the item back into the free list */ +static void cffi_closure_free(ffi_closure *p) +{ + union mmaped_block *item = (union mmaped_block *)p; + item->next = free_list; + free_list = item; +} + +/* return one item from the free list, allocating more if needed */ +static ffi_closure *cffi_closure_alloc(void) +{ + union mmaped_block *item; + if (!free_list) + more_core(); + if (!free_list) + return NULL; + item = free_list; + free_list = item->next; + return &item->closure; +} diff --git a/c/minibuffer.h b/c/minibuffer.h new file mode 100644 index 0000000..f3f5ca1 --- /dev/null +++ b/c/minibuffer.h @@ -0,0 +1,408 @@ + +/* Implementation of a C object with the 'buffer' or 'memoryview' + * interface at C-level (as approriate for the version of Python we're + * compiling for), but only a minimal but *consistent* part of the + * 'buffer' interface at application level. + */ + +typedef struct { + PyObject_HEAD + char *mb_data; + Py_ssize_t mb_size; + PyObject *mb_keepalive; + PyObject *mb_weakreflist; /* weakref support */ +} MiniBufferObj; + +static Py_ssize_t mb_length(MiniBufferObj *self) +{ + return self->mb_size; +} + +static PyObject *mb_item(MiniBufferObj *self, Py_ssize_t idx) +{ + if (idx < 0 || idx >= self->mb_size ) { + PyErr_SetString(PyExc_IndexError, "buffer index out of range"); + return NULL; + } + return PyBytes_FromStringAndSize(self->mb_data + idx, 1); +} + +static PyObject *mb_slice(MiniBufferObj *self, + Py_ssize_t left, Py_ssize_t right) +{ + Py_ssize_t size = self->mb_size; + if (left < 0) left = 0; + if (right > size) right = size; + if (left > right) left = right; + return PyBytes_FromStringAndSize(self->mb_data + left, right - left); +} + +static int mb_ass_item(MiniBufferObj *self, Py_ssize_t idx, PyObject *other) +{ + if (idx < 0 || idx >= self->mb_size) { + PyErr_SetString(PyExc_IndexError, + "buffer assignment index out of range"); + return -1; + } + if (PyBytes_Check(other) && PyBytes_GET_SIZE(other) == 1) { + self->mb_data[idx] = PyBytes_AS_STRING(other)[0]; + return 0; + } + else { + PyErr_Format(PyExc_TypeError, + "must assign a "STR_OR_BYTES + " of length 1, not %.200s", Py_TYPE(other)->tp_name); + return -1; + } +} + +/* forward: from _cffi_backend.c */ +static int _fetch_as_buffer(PyObject *x, Py_buffer *view, int writable_only); + +static int mb_ass_slice(MiniBufferObj *self, + Py_ssize_t left, Py_ssize_t right, PyObject *other) +{ + Py_ssize_t count; + Py_ssize_t size = self->mb_size; + Py_buffer src_view; + + if (_fetch_as_buffer(other, &src_view, 0) < 0) + return -1; + + if (left < 0) left = 0; + if (right > size) right = size; + if (left > right) left = right; + + count = right - left; + if (count != src_view.len) { + PyBuffer_Release(&src_view); + PyErr_SetString(PyExc_ValueError, + "right operand length must match slice length"); + return -1; + } + memcpy(self->mb_data + left, src_view.buf, count); + PyBuffer_Release(&src_view); + return 0; +} + +#if PY_MAJOR_VERSION < 3 +static Py_ssize_t mb_getdata(MiniBufferObj *self, Py_ssize_t idx, void **pp) +{ + *pp = self->mb_data; + return self->mb_size; +} + +static Py_ssize_t mb_getsegcount(MiniBufferObj *self, Py_ssize_t *lenp) +{ + if (lenp) + *lenp = self->mb_size; + return 1; +} + +static PyObject *mb_str(MiniBufferObj *self) +{ + /* Python 2: we want str(buffer) to behave like buffer[:], because + that's what bytes(buffer) does on Python 3 and there is no way + we can prevent this. */ + return PyString_FromStringAndSize(self->mb_data, self->mb_size); +} +#endif + +static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags) +{ + return PyBuffer_FillInfo(view, (PyObject *)self, + self->mb_data, self->mb_size, + /*readonly=*/0, flags); +} + +static PySequenceMethods mb_as_sequence = { + (lenfunc)mb_length, /*sq_length*/ + (binaryfunc)0, /*sq_concat*/ + (ssizeargfunc)0, /*sq_repeat*/ + (ssizeargfunc)mb_item, /*sq_item*/ + (ssizessizeargfunc)mb_slice, /*sq_slice*/ + (ssizeobjargproc)mb_ass_item, /*sq_ass_item*/ + (ssizessizeobjargproc)mb_ass_slice, /*sq_ass_slice*/ +}; + +static PyBufferProcs mb_as_buffer = { +#if PY_MAJOR_VERSION < 3 + (readbufferproc)mb_getdata, + (writebufferproc)mb_getdata, + (segcountproc)mb_getsegcount, + (charbufferproc)mb_getdata, +#endif + (getbufferproc)mb_getbuf, + (releasebufferproc)0, +}; + +static void +mb_dealloc(MiniBufferObj *ob) +{ + PyObject_GC_UnTrack(ob); + if (ob->mb_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)ob); + Py_XDECREF(ob->mb_keepalive); + Py_TYPE(ob)->tp_free((PyObject *)ob); +} + +static int +mb_traverse(MiniBufferObj *ob, visitproc visit, void *arg) +{ + Py_VISIT(ob->mb_keepalive); + return 0; +} + +static int +mb_clear(MiniBufferObj *ob) +{ + Py_CLEAR(ob->mb_keepalive); + return 0; +} + +static PyObject * +mb_richcompare(PyObject *self, PyObject *other, int op) +{ + Py_ssize_t self_size, other_size; + Py_buffer self_bytes, other_bytes; + PyObject *res; + Py_ssize_t minsize; + int cmp, rc; + + /* Bytes can be compared to anything that supports the (binary) + buffer API. Except that a comparison with Unicode is always an + error, even if the comparison is for equality. */ + rc = PyObject_IsInstance(self, (PyObject*)&PyUnicode_Type); + if (!rc) + rc = PyObject_IsInstance(other, (PyObject*)&PyUnicode_Type); + if (rc < 0) + return NULL; + if (rc) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + if (PyObject_GetBuffer(self, &self_bytes, PyBUF_SIMPLE) != 0) { + PyErr_Clear(); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + + } + self_size = self_bytes.len; + + if (PyObject_GetBuffer(other, &other_bytes, PyBUF_SIMPLE) != 0) { + PyErr_Clear(); + PyBuffer_Release(&self_bytes); + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + + } + other_size = other_bytes.len; + + if (self_size != other_size && (op == Py_EQ || op == Py_NE)) { + /* Shortcut: if the lengths differ, the objects differ */ + cmp = (op == Py_NE); + } + else { + minsize = self_size; + if (other_size < minsize) + minsize = other_size; + + cmp = memcmp(self_bytes.buf, other_bytes.buf, minsize); + /* In ISO C, memcmp() guarantees to use unsigned bytes! */ + + if (cmp == 0) { + if (self_size < other_size) + cmp = -1; + else if (self_size > other_size) + cmp = 1; + } + + switch (op) { + case Py_LT: cmp = cmp < 0; break; + case Py_LE: cmp = cmp <= 0; break; + case Py_EQ: cmp = cmp == 0; break; + case Py_NE: cmp = cmp != 0; break; + case Py_GT: cmp = cmp > 0; break; + case Py_GE: cmp = cmp >= 0; break; + } + } + + res = cmp ? Py_True : Py_False; + PyBuffer_Release(&self_bytes); + PyBuffer_Release(&other_bytes); + Py_INCREF(res); + return res; +} + +#if PY_MAJOR_VERSION >= 3 +/* pfffffffffffff pages of copy-paste from listobject.c */ + +/* pfffffffffffff#2: the PySlice_GetIndicesEx() *macro* should not + be called, because C extension modules compiled with it differ + on ABI between 3.6.0, 3.6.1 and 3.6.2. */ +#if PY_VERSION_HEX < 0x03070000 && defined(PySlice_GetIndicesEx) && !defined(PYPY_VERSION) +#undef PySlice_GetIndicesEx +#endif + +static PyObject *mb_subscript(MiniBufferObj *self, PyObject *item) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i; + i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return NULL; + if (i < 0) + i += self->mb_size; + return mb_item(self, i); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->mb_size, + &start, &stop, &step, &slicelength) < 0) + return NULL; + + if (step == 1) + return mb_slice(self, start, stop); + else { + PyErr_SetString(PyExc_TypeError, + "buffer doesn't support slicing with step != 1"); + return NULL; + } + } + else { + PyErr_Format(PyExc_TypeError, + "buffer indices must be integers, not %.200s", + item->ob_type->tp_name); + return NULL; + } +} +static int +mb_ass_subscript(MiniBufferObj* self, PyObject* item, PyObject* value) +{ + if (PyIndex_Check(item)) { + Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); + if (i == -1 && PyErr_Occurred()) + return -1; + if (i < 0) + i += self->mb_size; + return mb_ass_item(self, i, value); + } + else if (PySlice_Check(item)) { + Py_ssize_t start, stop, step, slicelength; + + if (PySlice_GetIndicesEx(item, self->mb_size, + &start, &stop, &step, &slicelength) < 0) { + return -1; + } + + if (step == 1) + return mb_ass_slice(self, start, stop, value); + else { + PyErr_SetString(PyExc_TypeError, + "buffer doesn't support slicing with step != 1"); + return -1; + } + } + else { + PyErr_Format(PyExc_TypeError, + "buffer indices must be integers, not %.200s", + item->ob_type->tp_name); + return -1; + } +} + +static PyMappingMethods mb_as_mapping = { + (lenfunc)mb_length, /*mp_length*/ + (binaryfunc)mb_subscript, /*mp_subscript*/ + (objobjargproc)mb_ass_subscript, /*mp_ass_subscript*/ +}; +#endif + +#if PY_MAJOR_VERSION >= 3 +# define MINIBUF_TPFLAGS 0 +#else +# define MINIBUF_TPFLAGS (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_NEWBUFFER) +#endif + +PyDoc_STRVAR(ffi_buffer_doc, +"ffi.buffer(cdata[, byte_size]):\n" +"Return a read-write buffer object that references the raw C data\n" +"pointed to by the given 'cdata'. The 'cdata' must be a pointer or an\n" +"array. Can be passed to functions expecting a buffer, or directly\n" +"manipulated with:\n" +"\n" +" buf[:] get a copy of it in a regular string, or\n" +" buf[idx] as a single character\n" +" buf[:] = ...\n" +" buf[idx] = ... change the content"); + +static PyObject * /* forward, implemented in _cffi_backend.c */ +b_buffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + + +static PyTypeObject MiniBuffer_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.buffer", + sizeof(MiniBufferObj), + 0, + (destructor)mb_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + &mb_as_sequence, /* tp_as_sequence */ +#if PY_MAJOR_VERSION < 3 + 0, /* tp_as_mapping */ +#else + &mb_as_mapping, /* tp_as_mapping */ +#endif + 0, /* tp_hash */ + 0, /* tp_call */ +#if PY_MAJOR_VERSION < 3 + (reprfunc)mb_str, /* tp_str */ +#else + 0, /* tp_str */ +#endif + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + &mb_as_buffer, /* tp_as_buffer */ + (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + MINIBUF_TPFLAGS), /* tp_flags */ + ffi_buffer_doc, /* tp_doc */ + (traverseproc)mb_traverse, /* tp_traverse */ + (inquiry)mb_clear, /* tp_clear */ + (richcmpfunc)mb_richcompare, /* tp_richcompare */ + offsetof(MiniBufferObj, mb_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + b_buffer_new, /* tp_new */ + 0, /* tp_free */ +}; + +static PyObject *minibuffer_new(char *data, Py_ssize_t size, + PyObject *keepalive) +{ + MiniBufferObj *ob = PyObject_GC_New(MiniBufferObj, &MiniBuffer_Type); + if (ob != NULL) { + ob->mb_data = data; + ob->mb_size = size; + ob->mb_keepalive = keepalive; Py_INCREF(keepalive); + ob->mb_weakreflist = NULL; + PyObject_GC_Track(ob); + } + return (PyObject *)ob; +} diff --git a/c/misc_thread_common.h b/c/misc_thread_common.h new file mode 100644 index 0000000..66e2835 --- /dev/null +++ b/c/misc_thread_common.h @@ -0,0 +1,371 @@ +#ifndef WITH_THREAD +# error "xxx no-thread configuration not tested, please report if you need that" +#endif +#include "pythread.h" + + +struct cffi_tls_s { + /* The current thread's ThreadCanaryObj. This is only non-null in + case cffi builds the thread state here. It remains null if this + thread had already a thread state provided by CPython. */ + struct thread_canary_s *local_thread_canary; + +#ifndef USE__THREAD + /* The saved errno. If the C compiler supports '__thread', then + we use that instead. */ + int saved_errno; +#endif + +#ifdef MS_WIN32 + /* The saved lasterror, on Windows. */ + int saved_lasterror; +#endif +}; + +static struct cffi_tls_s *get_cffi_tls(void); /* in misc_thread_posix.h + or misc_win32.h */ + + +/* We try to keep the PyThreadState around in a thread not started by + * Python but where cffi callbacks occur. If we didn't do that, then + * the standard logic in PyGILState_Ensure() and PyGILState_Release() + * would create a new PyThreadState and completely free it for every + * single call. For some applications, this is a huge slow-down. + * + * As shown by issue #362, it is quite messy to do. The current + * solution is to keep the PyThreadState alive by incrementing its + * 'gilstate_counter'. We detect thread shut-down, and we put the + * PyThreadState inside a list of zombies (we can't free it + * immediately because we don't have the GIL at that point in time). + * We also detect other pieces of code (notably Py_Finalize()) which + * clear and free PyThreadStates under our feet, using ThreadCanaryObj. + */ + +#define TLS_ZOM_LOCK() PyThread_acquire_lock(cffi_zombie_lock, WAIT_LOCK) +#define TLS_ZOM_UNLOCK() PyThread_release_lock(cffi_zombie_lock) +static PyThread_type_lock cffi_zombie_lock = NULL; + + +/* A 'canary' object is created in a thread when there is a callback + invoked, and that thread has no PyThreadState so far. It is an + object of reference count equal to 1, which is stored in the + PyThreadState->dict. Two things can occur then: + + 1. The PyThreadState can be forcefully cleared by Py_Finalize(). + Then thread_canary_dealloc() is called, and we have to cancel + the hacks we did to keep the PyThreadState alive. + + 2. The thread finishes. In that case, we put the canary in a list + of zombies, and at some convenient time later when we have the + GIL, we free all PyThreadStates in the zombie list. + + Some more fun comes from the fact that thread_canary_dealloc() can + be called at a point where the canary is in the zombie list already. + Also, the various pieces are freed at specific points in time, and + we must make sure not to access already-freed structures: + + - the struct cffi_tls_s is valid until the thread shuts down, and + then it is freed by cffi_thread_shutdown(). + + - the canary is a normal Python object, but we have a borrowed + reference to it from cffi_tls_s.local_thread_canary. + */ + +typedef struct thread_canary_s { + PyObject_HEAD + struct thread_canary_s *zombie_prev, *zombie_next; + PyThreadState *tstate; + struct cffi_tls_s *tls; +} ThreadCanaryObj; + +static PyTypeObject ThreadCanary_Type; /* forward */ +static ThreadCanaryObj cffi_zombie_head; + +static void +_thread_canary_detach_with_lock(ThreadCanaryObj *ob) +{ + /* must be called with both the GIL and TLS_ZOM_LOCK. */ + ThreadCanaryObj *p, *n; + p = ob->zombie_prev; + n = ob->zombie_next; + p->zombie_next = n; + n->zombie_prev = p; + ob->zombie_prev = NULL; + ob->zombie_next = NULL; +} + +static void +thread_canary_dealloc(ThreadCanaryObj *ob) +{ + /* this ThreadCanaryObj is being freed: if it is in the zombie + chained list, remove it. Thread-safety: 'zombie_next' amd + 'local_thread_canary' accesses need to be protected with + the TLS_ZOM_LOCK. + */ + TLS_ZOM_LOCK(); + if (ob->zombie_next != NULL) { + //fprintf(stderr, "thread_canary_dealloc(%p): ZOMBIE\n", ob); + _thread_canary_detach_with_lock(ob); + } + else { + //fprintf(stderr, "thread_canary_dealloc(%p): not a zombie\n", ob); + } + + if (ob->tls != NULL) { + //fprintf(stderr, "thread_canary_dealloc(%p): was local_thread_canary\n", ob); + assert(ob->tls->local_thread_canary == ob); + ob->tls->local_thread_canary = NULL; + } + TLS_ZOM_UNLOCK(); + + PyObject_Del((PyObject *)ob); +} + +static void +thread_canary_make_zombie(ThreadCanaryObj *ob) +{ + /* This must be called without the GIL, but with the TLS_ZOM_LOCK. + It must be called at most once for a given ThreadCanaryObj. */ + ThreadCanaryObj *last; + + //fprintf(stderr, "thread_canary_make_zombie(%p)\n", ob); + if (ob->zombie_next) + Py_FatalError("cffi: ThreadCanaryObj is already a zombie"); + last = cffi_zombie_head.zombie_prev; + ob->zombie_next = &cffi_zombie_head; + ob->zombie_prev = last; + last->zombie_next = ob; + cffi_zombie_head.zombie_prev = ob; +} + +static void +thread_canary_free_zombies(void) +{ + /* This must be called with the GIL. */ + if (cffi_zombie_head.zombie_next == &cffi_zombie_head) + return; /* fast path */ + + while (1) { + ThreadCanaryObj *ob; + PyThreadState *tstate = NULL; + + TLS_ZOM_LOCK(); + ob = cffi_zombie_head.zombie_next; + if (ob != &cffi_zombie_head) { + tstate = ob->tstate; + //fprintf(stderr, "thread_canary_free_zombie(%p) tstate=%p\n", ob, tstate); + _thread_canary_detach_with_lock(ob); + if (tstate == NULL) + Py_FatalError("cffi: invalid ThreadCanaryObj->tstate"); + } + TLS_ZOM_UNLOCK(); + + if (tstate == NULL) + break; + PyThreadState_Clear(tstate); /* calls thread_canary_dealloc on 'ob', + but now ob->zombie_next == NULL. */ + PyThreadState_Delete(tstate); + //fprintf(stderr, "thread_canary_free_zombie: cleared and deleted tstate=%p\n", tstate); + } + //fprintf(stderr, "thread_canary_free_zombie: end\n"); +} + +static void +thread_canary_register(PyThreadState *tstate) +{ + /* called with the GIL; 'tstate' is the current PyThreadState. */ + ThreadCanaryObj *canary; + PyObject *tdict; + struct cffi_tls_s *tls; + int err; + + /* first free the zombies, if any */ + thread_canary_free_zombies(); + + tls = get_cffi_tls(); + if (tls == NULL) + goto ignore_error; + + tdict = PyThreadState_GetDict(); + if (tdict == NULL) + goto ignore_error; + + canary = PyObject_New(ThreadCanaryObj, &ThreadCanary_Type); + //fprintf(stderr, "thread_canary_register(%p): tstate=%p tls=%p\n", canary, tstate, tls); + if (canary == NULL) + goto ignore_error; + canary->zombie_prev = NULL; + canary->zombie_next = NULL; + canary->tstate = tstate; + canary->tls = tls; + + err = PyDict_SetItemString(tdict, "cffi.thread.canary", (PyObject *)canary); + Py_DECREF(canary); + if (err < 0) + goto ignore_error; + + /* thread-safety: we have the GIL here, and 'tstate' is the one that + corresponds to our own thread. We are allocating a new 'canary' + and setting it up for our own thread, both in 'tdict' (which owns + the reference) and in 'tls->local_thread_canary' (which doesn't). */ + assert(Py_REFCNT(canary) == 1); + tls->local_thread_canary = canary; + tstate->gilstate_counter++; + /* ^^^ this means 'tstate' will never be automatically freed by + PyGILState_Release() */ + return; + + ignore_error: + PyErr_Clear(); +} + +static PyTypeObject ThreadCanary_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_cffi_backend.thread_canary", + sizeof(ThreadCanaryObj), + 0, + (destructor)thread_canary_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 */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ +}; + +static void init_cffi_tls_zombie(void) +{ + cffi_zombie_head.zombie_next = &cffi_zombie_head; + cffi_zombie_head.zombie_prev = &cffi_zombie_head; + cffi_zombie_lock = PyThread_allocate_lock(); + if (cffi_zombie_lock == NULL) + PyErr_SetString(PyExc_SystemError, "can't allocate cffi_zombie_lock"); +} + +static void cffi_thread_shutdown(void *p) +{ + /* this function is called from misc_thread_posix or misc_win32 + when a thread is about to end. */ + struct cffi_tls_s *tls = (struct cffi_tls_s *)p; + + /* thread-safety: this field 'local_thread_canary' can be reset + to NULL in parallel, protected by TLS_ZOM_LOCK. */ + TLS_ZOM_LOCK(); + if (tls->local_thread_canary != NULL) { + tls->local_thread_canary->tls = NULL; + thread_canary_make_zombie(tls->local_thread_canary); + } + TLS_ZOM_UNLOCK(); + //fprintf(stderr, "thread_shutdown(%p)\n", tls); + free(tls); +} + +/* USE__THREAD is defined by setup.py if it finds that it is + syntactically valid to use "__thread" with this C compiler. */ +#ifdef USE__THREAD + +static __thread int cffi_saved_errno = 0; +static void save_errno_only(void) { cffi_saved_errno = errno; } +static void restore_errno_only(void) { errno = cffi_saved_errno; } + +#else + +static void save_errno_only(void) +{ + int saved = errno; + struct cffi_tls_s *tls = get_cffi_tls(); + if (tls != NULL) + tls->saved_errno = saved; +} + +static void restore_errno_only(void) +{ + struct cffi_tls_s *tls = get_cffi_tls(); + if (tls != NULL) + errno = tls->saved_errno; +} + +#endif + + +/* MESS. We can't use PyThreadState_GET(), because that calls + PyThreadState_Get() which fails an assert if the result is NULL. + + * in Python 2.7 and <= 3.4, the variable _PyThreadState_Current + is directly available, so use that. + + * in Python 3.5, the variable is available too, but it might be + the case that the headers don't define it (this changed in 3.5.1). + In case we're compiling with 3.5.x with x >= 1, we need to + manually define this variable. + + * in Python >= 3.6 there is _PyThreadState_UncheckedGet(). + It was added in 3.5.2 but should never be used in 3.5.x + because it is not available in 3.5.0 or 3.5.1. +*/ +#if PY_VERSION_HEX >= 0x03050100 && PY_VERSION_HEX < 0x03060000 +PyAPI_DATA(void *volatile) _PyThreadState_Current; +#endif + +static PyThreadState *get_current_ts(void) +{ +#if PY_VERSION_HEX >= 0x03060000 + return _PyThreadState_UncheckedGet(); +#elif defined(_Py_atomic_load_relaxed) + return (PyThreadState*)_Py_atomic_load_relaxed(&_PyThreadState_Current); +#else + return (PyThreadState*)_PyThreadState_Current; /* assume atomic read */ +#endif +} + +static PyGILState_STATE gil_ensure(void) +{ + /* Called at the start of a callback. Replacement for + PyGILState_Ensure(). + */ + PyGILState_STATE result; + PyThreadState *ts = PyGILState_GetThisThreadState(); + + if (ts != NULL) { + ts->gilstate_counter++; + if (ts != get_current_ts()) { + /* common case: 'ts' is our non-current thread state and + we have to make it current and acquire the GIL */ + PyEval_RestoreThread(ts); + return PyGILState_UNLOCKED; + } + else { + return PyGILState_LOCKED; + } + } + else { + /* no thread state here so far. */ + result = PyGILState_Ensure(); + assert(result == PyGILState_UNLOCKED); + + ts = PyGILState_GetThisThreadState(); + assert(ts != NULL); + assert(ts == get_current_ts()); + assert(ts->gilstate_counter >= 1); + + /* Use the ThreadCanary mechanism to keep 'ts' alive until the + thread really shuts down */ + thread_canary_register(ts); + + return result; + } +} + +static void gil_release(PyGILState_STATE oldstate) +{ + PyGILState_Release(oldstate); +} diff --git a/c/misc_thread_posix.h b/c/misc_thread_posix.h new file mode 100644 index 0000000..bcc0177 --- /dev/null +++ b/c/misc_thread_posix.h @@ -0,0 +1,49 @@ +/* + Logic for a better replacement of PyGILState_Ensure(). + + This version is ready to handle the case of a non-Python-started + thread in which we do a large number of calls to CFFI callbacks. If + we were to rely on PyGILState_Ensure() for that, we would constantly + be creating and destroying PyThreadStates---it is slow, and + PyThreadState_Delete() will actually walk the list of all thread + states, making it O(n). :-( + + This version only creates one PyThreadState object the first time we + see a given thread, and keep it alive until the thread is really + shut down, using a destructor on the tls key. +*/ + +#include <pthread.h> +#include "misc_thread_common.h" + + +static pthread_key_t cffi_tls_key; + +static void init_cffi_tls(void) +{ + if (pthread_key_create(&cffi_tls_key, &cffi_thread_shutdown) != 0) + PyErr_SetString(PyExc_OSError, "pthread_key_create() failed"); +} + +static struct cffi_tls_s *_make_cffi_tls(void) +{ + void *p = calloc(1, sizeof(struct cffi_tls_s)); + if (p == NULL) + return NULL; + if (pthread_setspecific(cffi_tls_key, p) != 0) { + free(p); + return NULL; + } + return p; +} + +static struct cffi_tls_s *get_cffi_tls(void) +{ + void *p = pthread_getspecific(cffi_tls_key); + if (p == NULL) + p = _make_cffi_tls(); + return (struct cffi_tls_s *)p; +} + +#define save_errno save_errno_only +#define restore_errno restore_errno_only diff --git a/c/misc_win32.h b/c/misc_win32.h new file mode 100644 index 0000000..07b76c1 --- /dev/null +++ b/c/misc_win32.h @@ -0,0 +1,241 @@ +#include <malloc.h> /* for alloca() */ + + +/************************************************************/ +/* errno and GetLastError support */ + +#include "misc_thread_common.h" + +static DWORD cffi_tls_index = TLS_OUT_OF_INDEXES; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, + DWORD reason_for_call, + LPVOID reserved) +{ + LPVOID p; + + switch (reason_for_call) { + + case DLL_THREAD_DETACH: + if (cffi_tls_index != TLS_OUT_OF_INDEXES) { + p = TlsGetValue(cffi_tls_index); + if (p != NULL) { + TlsSetValue(cffi_tls_index, NULL); + cffi_thread_shutdown(p); + } + } + break; + + default: + break; + } + return TRUE; +} + +static void init_cffi_tls(void) +{ + if (cffi_tls_index == TLS_OUT_OF_INDEXES) { + cffi_tls_index = TlsAlloc(); + if (cffi_tls_index == TLS_OUT_OF_INDEXES) + PyErr_SetString(PyExc_WindowsError, "TlsAlloc() failed"); + } +} + +static struct cffi_tls_s *get_cffi_tls(void) +{ + LPVOID p = TlsGetValue(cffi_tls_index); + + if (p == NULL) { + p = malloc(sizeof(struct cffi_tls_s)); + if (p == NULL) + return NULL; + memset(p, 0, sizeof(struct cffi_tls_s)); + TlsSetValue(cffi_tls_index, p); + } + return (struct cffi_tls_s *)p; +} + +#ifdef USE__THREAD +# error "unexpected USE__THREAD on Windows" +#endif + +static void save_errno(void) +{ + int current_err = errno; + int current_lasterr = GetLastError(); + struct cffi_tls_s *p = get_cffi_tls(); + if (p != NULL) { + p->saved_errno = current_err; + p->saved_lasterror = current_lasterr; + } + /* else: cannot report the error */ +} + +static void restore_errno(void) +{ + struct cffi_tls_s *p = get_cffi_tls(); + if (p != NULL) { + SetLastError(p->saved_lasterror); + errno = p->saved_errno; + } + /* else: cannot report the error */ +} + +/************************************************************/ + + +#if PY_MAJOR_VERSION >= 3 +static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) +{ + int err = -1; + int len; + WCHAR *s_buf = NULL; /* Free via LocalFree */ + PyObject *v, *message; + static char *keywords[] = {"code", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) + return NULL; + + if (err == -1) { + struct cffi_tls_s *p = get_cffi_tls(); + if (p == NULL) + return PyErr_NoMemory(); + err = p->saved_lasterror; + } + + len = FormatMessageW( + /* Error API error */ + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, /* no message source */ + err, + MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), /* Default language */ + (LPWSTR) &s_buf, + 0, /* size not used */ + NULL); /* no args */ + if (len==0) { + /* Only seen this in out of mem situations */ + message = PyUnicode_FromFormat("Windows Error 0x%X", err); + } else { + /* remove trailing cr/lf and dots */ + while (len > 0 && (s_buf[len-1] <= L' ' || s_buf[len-1] == L'.')) + s_buf[--len] = L'\0'; + message = PyUnicode_FromWideChar(s_buf, len); + } + if (message != NULL) + v = Py_BuildValue("(iO)", err, message); + else + v = NULL; + LocalFree(s_buf); + return v; +} +#else +static PyObject *b_getwinerror(PyObject *self, PyObject *args, PyObject *kwds) +{ + int err = -1; + int len; + char *s; + char *s_buf = NULL; /* Free via LocalFree */ + char s_small_buf[40]; /* Room for "Windows Error 0xFFFFFFFFFFFFFFFF" */ + PyObject *v; + static char *keywords[] = {"code", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", keywords, &err)) + return NULL; + + if (err == -1) { + struct cffi_tls_s *p = get_cffi_tls(); + if (p == NULL) + return PyErr_NoMemory(); + err = p->saved_lasterror; + } + + len = FormatMessage( + /* Error API error */ + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, /* no message source */ + err, + MAKELANGID(LANG_NEUTRAL, + SUBLANG_DEFAULT), /* Default language */ + (LPTSTR) &s_buf, + 0, /* size not used */ + NULL); /* no args */ + if (len==0) { + /* Only seen this in out of mem situations */ + sprintf(s_small_buf, "Windows Error 0x%X", err); + s = s_small_buf; + s_buf = NULL; + } else { + s = s_buf; + /* remove trailing cr/lf and dots */ + while (len > 0 && (s[len-1] <= ' ' || s[len-1] == '.')) + s[--len] = '\0'; + } + v = Py_BuildValue("(is)", err, s); + LocalFree(s_buf); + return v; +} +#endif + + +/************************************************************/ +/* Emulate dlopen()&co. from the Windows API */ + +#define RTLD_LAZY 0 +#define RTLD_NOW 0 +#define RTLD_GLOBAL 0 +#define RTLD_LOCAL 0 + +static void *dlopen(const char *filename, int flag) +{ + return (void *)LoadLibraryA(filename); +} + +static void *dlopenW(const wchar_t *filename) +{ + return (void *)LoadLibraryW(filename); +} + +static void *dlsym(void *handle, const char *symbol) +{ + void *address = GetProcAddress((HMODULE)handle, symbol); +#ifndef MS_WIN64 + if (!address) { + /* If 'symbol' is not found, then try '_symbol@N' for N in + (0, 4, 8, 12, ..., 124). Unlike ctypes, we try to do that + for any symbol, although in theory it should only be done + for __stdcall functions. + */ + int i; + char *mangled_name = alloca(1 + strlen(symbol) + 1 + 3 + 1); + if (!mangled_name) + return NULL; + for (i = 0; i < 32; i++) { + sprintf(mangled_name, "_%s@%d", symbol, i * 4); + address = GetProcAddress((HMODULE)handle, mangled_name); + if (address) + break; + } + } +#endif + return address; +} + +static int dlclose(void *handle) +{ + return FreeLibrary((HMODULE)handle) ? 0 : -1; +} + +static const char *dlerror(void) +{ + static char buf[32]; + DWORD dw = GetLastError(); + if (dw == 0) + return NULL; + sprintf(buf, "error 0x%x", (unsigned int)dw); + return buf; +} diff --git a/c/parse_c_type.c b/c/parse_c_type.c new file mode 100644 index 0000000..698ef64 --- /dev/null +++ b/c/parse_c_type.c @@ -0,0 +1,847 @@ +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> + +#define _CFFI_INTERNAL +#include "../cffi/parse_c_type.h" + + +enum token_e { + TOK_STAR='*', + TOK_OPEN_PAREN='(', + TOK_CLOSE_PAREN=')', + TOK_OPEN_BRACKET='[', + TOK_CLOSE_BRACKET=']', + TOK_COMMA=',', + + TOK_START=256, + TOK_END, + TOK_ERROR, + TOK_IDENTIFIER, + TOK_INTEGER, + TOK_DOTDOTDOT, + + /* keywords */ + TOK__BOOL, + TOK_CHAR, + TOK__COMPLEX, + TOK_CONST, + TOK_DOUBLE, + TOK_ENUM, + TOK_FLOAT, + //TOK__IMAGINARY, + TOK_INT, + TOK_LONG, + TOK_SHORT, + TOK_SIGNED, + TOK_STRUCT, + TOK_UNION, + TOK_UNSIGNED, + TOK_VOID, + TOK_VOLATILE, + + TOK_CDECL, + TOK_STDCALL, +}; + +typedef struct { + struct _cffi_parse_info_s *info; + const char *input, *p; + size_t size; // the next token is at 'p' and of length 'size' + enum token_e kind; + _cffi_opcode_t *output; + size_t output_index; +} token_t; + +static int is_space(char x) +{ + return (x == ' ' || x == '\f' || x == '\n' || x == '\r' || + x == '\t' || x == '\v'); +} + +static int is_ident_first(char x) +{ + return (('A' <= x && x <= 'Z') || ('a' <= x && x <= 'z') || x == '_' || + x == '$'); /* '$' in names is supported here, for the struct + names invented by cparser */ +} + +static int is_digit(char x) +{ + return ('0' <= x && x <= '9'); +} + +static int is_hex_digit(char x) +{ + return (('0' <= x && x <= '9') || + ('A' <= x && x <= 'F') || + ('a' <= x && x <= 'f')); +} + +static int is_ident_next(char x) +{ + return (is_ident_first(x) || is_digit(x)); +} + +static char get_following_char(token_t *tok) +{ + const char *p = tok->p + tok->size; + if (tok->kind == TOK_ERROR) + return 0; + while (is_space(*p)) + p++; + return *p; +} + +static int number_of_commas(token_t *tok) +{ + const char *p = tok->p; + int result = 0; + int nesting = 0; + while (1) { + switch (*p++) { + case ',': result += !nesting; break; + case '(': nesting++; break; + case ')': if ((--nesting) < 0) return result; break; + case 0: return result; + default: break; + } + } +} + +static void next_token(token_t *tok) +{ + const char *p = tok->p + tok->size; + if (tok->kind == TOK_ERROR) + return; + while (!is_ident_first(*p)) { + if (is_space(*p)) { + p++; + } + else if (is_digit(*p)) { + tok->kind = TOK_INTEGER; + tok->p = p; + tok->size = 1; + if (p[1] == 'x' || p[1] == 'X') + tok->size = 2; + while (is_hex_digit(p[tok->size])) + tok->size++; + return; + } + else if (p[0] == '.' && p[1] == '.' && p[2] == '.') { + tok->kind = TOK_DOTDOTDOT; + tok->p = p; + tok->size = 3; + return; + } + else if (*p) { + tok->kind = *p; + tok->p = p; + tok->size = 1; + return; + } + else { + tok->kind = TOK_END; + tok->p = p; + tok->size = 0; + return; + } + } + tok->kind = TOK_IDENTIFIER; + tok->p = p; + tok->size = 1; + while (is_ident_next(p[tok->size])) + tok->size++; + + switch (*p) { + case '_': + if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL; + if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL; + if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL; + if (tok->size == 8 && !memcmp(p,"_Complex",8)) tok->kind = TOK__COMPLEX; + break; + case 'c': + if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR; + if (tok->size == 5 && !memcmp(p, "const", 5)) tok->kind = TOK_CONST; + break; + case 'd': + if (tok->size == 6 && !memcmp(p, "double", 6)) tok->kind = TOK_DOUBLE; + break; + case 'e': + if (tok->size == 4 && !memcmp(p, "enum", 4)) tok->kind = TOK_ENUM; + break; + case 'f': + if (tok->size == 5 && !memcmp(p, "float", 5)) tok->kind = TOK_FLOAT; + break; + case 'i': + if (tok->size == 3 && !memcmp(p, "int", 3)) tok->kind = TOK_INT; + break; + case 'l': + if (tok->size == 4 && !memcmp(p, "long", 4)) tok->kind = TOK_LONG; + break; + case 's': + if (tok->size == 5 && !memcmp(p, "short", 5)) tok->kind = TOK_SHORT; + if (tok->size == 6 && !memcmp(p, "signed", 6)) tok->kind = TOK_SIGNED; + if (tok->size == 6 && !memcmp(p, "struct", 6)) tok->kind = TOK_STRUCT; + break; + case 'u': + if (tok->size == 5 && !memcmp(p, "union", 5)) tok->kind = TOK_UNION; + if (tok->size == 8 && !memcmp(p,"unsigned",8)) tok->kind = TOK_UNSIGNED; + break; + case 'v': + if (tok->size == 4 && !memcmp(p, "void", 4)) tok->kind = TOK_VOID; + if (tok->size == 8 && !memcmp(p,"volatile",8)) tok->kind = TOK_VOLATILE; + break; + } +} + +static int parse_error(token_t *tok, const char *msg) +{ + if (tok->kind != TOK_ERROR) { + tok->kind = TOK_ERROR; + tok->info->error_location = tok->p - tok->input; + tok->info->error_message = msg; + } + return -1; +} + +static int write_ds(token_t *tok, _cffi_opcode_t ds) +{ + size_t index = tok->output_index; + if (index >= tok->info->output_size) { + parse_error(tok, "internal type complexity limit reached"); + return -1; + } + tok->output[index] = ds; + tok->output_index = index + 1; + return index; +} + +#define MAX_SSIZE_T (((size_t)-1) >> 1) + +static int parse_complete(token_t *tok); +static const char *get_common_type(const char *search, size_t search_len); +static int parse_common_type_replacement(token_t *tok, const char *replacement); + +static int parse_sequel(token_t *tok, int outer) +{ + /* Emit opcodes for the "sequel", which is the optional part of a + type declaration that follows the type name, i.e. everything + with '*', '[ ]', '( )'. Returns the entry point index pointing + the innermost opcode (the one that corresponds to the complete + type). The 'outer' argument is the index of the opcode outside + this "sequel". + */ + int check_for_grouping, abi=0; + _cffi_opcode_t result, *p_current; + + header: + switch (tok->kind) { + case TOK_STAR: + outer = write_ds(tok, _CFFI_OP(_CFFI_OP_POINTER, outer)); + next_token(tok); + goto header; + case TOK_CONST: + /* ignored for now */ + next_token(tok); + goto header; + case TOK_VOLATILE: + /* ignored for now */ + next_token(tok); + goto header; + case TOK_CDECL: + case TOK_STDCALL: + /* must be in a function; checked below */ + abi = tok->kind; + next_token(tok); + goto header; + default: + break; + } + + check_for_grouping = 1; + if (tok->kind == TOK_IDENTIFIER) { + next_token(tok); /* skip a potential variable name */ + check_for_grouping = 0; + } + + result = 0; + p_current = &result; + + while (tok->kind == TOK_OPEN_PAREN) { + next_token(tok); + + if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) { + abi = tok->kind; + next_token(tok); + } + + if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR || + tok->kind == TOK_CONST || + tok->kind == TOK_VOLATILE || + tok->kind == TOK_OPEN_BRACKET)) { + /* just parentheses for grouping. Use a OP_NOOP to simplify */ + int x; + assert(p_current == &result); + x = tok->output_index; + p_current = tok->output + x; + + write_ds(tok, _CFFI_OP(_CFFI_OP_NOOP, 0)); + + x = parse_sequel(tok, x); + result = _CFFI_OP(_CFFI_GETOP(0), x); + } + else { + /* function type */ + int arg_total, base_index, arg_next, flags=0; + + if (abi == TOK_STDCALL) { + flags = 2; + /* note that an ellipsis below will overwrite this flags, + which is the goal: variadic functions are always cdecl */ + } + abi = 0; + + if (tok->kind == TOK_VOID && get_following_char(tok) == ')') { + next_token(tok); + } + + /* (over-)estimate 'arg_total'. May return 1 when it is really 0 */ + arg_total = number_of_commas(tok) + 1; + + *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); + p_current = tok->output + tok->output_index; + + base_index = write_ds(tok, _CFFI_OP(_CFFI_OP_FUNCTION, 0)); + if (base_index < 0) + return -1; + /* reserve (arg_total + 1) slots for the arguments and the + final FUNCTION_END */ + for (arg_next = 0; arg_next <= arg_total; arg_next++) + if (write_ds(tok, _CFFI_OP(0, 0)) < 0) + return -1; + + arg_next = base_index + 1; + + if (tok->kind != TOK_CLOSE_PAREN) { + while (1) { + int arg; + _cffi_opcode_t oarg; + + if (tok->kind == TOK_DOTDOTDOT) { + flags = 1; /* ellipsis */ + next_token(tok); + break; + } + arg = parse_complete(tok); + switch (_CFFI_GETOP(tok->output[arg])) { + case _CFFI_OP_ARRAY: + case _CFFI_OP_OPEN_ARRAY: + arg = _CFFI_GETARG(tok->output[arg]); + /* fall-through */ + case _CFFI_OP_FUNCTION: + oarg = _CFFI_OP(_CFFI_OP_POINTER, arg); + break; + default: + oarg = _CFFI_OP(_CFFI_OP_NOOP, arg); + break; + } + assert(arg_next - base_index <= arg_total); + tok->output[arg_next++] = oarg; + if (tok->kind != TOK_COMMA) + break; + next_token(tok); + } + } + tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags); + } + + if (tok->kind != TOK_CLOSE_PAREN) + return parse_error(tok, "expected ')'"); + next_token(tok); + } + + if (abi != 0) + return parse_error(tok, "expected '('"); + + while (tok->kind == TOK_OPEN_BRACKET) { + *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); + p_current = tok->output + tok->output_index; + + next_token(tok); + if (tok->kind != TOK_CLOSE_BRACKET) { + size_t length; + int gindex; + char *endptr; + + switch (tok->kind) { + + case TOK_INTEGER: + errno = 0; + if (sizeof(length) > sizeof(unsigned long)) { +#ifdef MS_WIN32 +# ifdef _WIN64 + length = _strtoui64(tok->p, &endptr, 0); +# else + abort(); /* unreachable */ +# endif +#else + length = strtoull(tok->p, &endptr, 0); +#endif + } + else + length = strtoul(tok->p, &endptr, 0); + if (endptr != tok->p + tok->size) + return parse_error(tok, "invalid number"); + if (errno == ERANGE || length > MAX_SSIZE_T) + return parse_error(tok, "number too large"); + break; + + case TOK_IDENTIFIER: + gindex = search_in_globals(tok->info->ctx, tok->p, tok->size); + if (gindex >= 0) { + const struct _cffi_global_s *g; + g = &tok->info->ctx->globals[gindex]; + if (_CFFI_GETOP(g->type_op) == _CFFI_OP_CONSTANT_INT || + _CFFI_GETOP(g->type_op) == _CFFI_OP_ENUM) { + int neg; + struct _cffi_getconst_s gc; + gc.ctx = tok->info->ctx; + gc.gindex = gindex; + neg = ((int(*)(struct _cffi_getconst_s*))g->address) + (&gc); + if (neg == 0 && gc.value > MAX_SSIZE_T) + return parse_error(tok, + "integer constant too large"); + if (neg == 0 || gc.value == 0) { + length = (size_t)gc.value; + break; + } + if (neg != 1) + return parse_error(tok, "disagreement about" + " this constant's value"); + } + } + /* fall-through to the default case */ + default: + return parse_error(tok, "expected a positive integer constant"); + } + + next_token(tok); + + write_ds(tok, _CFFI_OP(_CFFI_OP_ARRAY, 0)); + write_ds(tok, (_cffi_opcode_t)length); + } + else + write_ds(tok, _CFFI_OP(_CFFI_OP_OPEN_ARRAY, 0)); + + if (tok->kind != TOK_CLOSE_BRACKET) + return parse_error(tok, "expected ']'"); + next_token(tok); + } + + *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), outer); + return _CFFI_GETARG(result); +} + +static int search_sorted(const char *const *base, + size_t item_size, int array_len, + const char *search, size_t search_len) +{ + int left = 0, right = array_len; + const char *baseptr = (const char *)base; + + while (left < right) { + int middle = (left + right) / 2; + const char *src = *(const char *const *)(baseptr + middle * item_size); + int diff = strncmp(src, search, search_len); + if (diff == 0 && src[search_len] == '\0') + return middle; + else if (diff >= 0) + right = middle; + else + left = middle + 1; + } + return -1; +} + +#define MAKE_SEARCH_FUNC(FIELD) \ + static \ + int search_in_##FIELD(const struct _cffi_type_context_s *ctx, \ + const char *search, size_t search_len) \ + { \ + return search_sorted(&ctx->FIELD->name, sizeof(*ctx->FIELD), \ + ctx->num_##FIELD, search, search_len); \ + } + +MAKE_SEARCH_FUNC(globals) +MAKE_SEARCH_FUNC(struct_unions) +MAKE_SEARCH_FUNC(typenames) +MAKE_SEARCH_FUNC(enums) + +#undef MAKE_SEARCH_FUNC + + +static +int search_standard_typename(const char *p, size_t size) +{ + if (size < 6 || p[size-2] != '_' || p[size-1] != 't') + return -1; + + switch (p[4]) { + + case '1': + if (size == 8 && !memcmp(p, "uint16", 6)) return _CFFI_PRIM_UINT16; + if (size == 8 && !memcmp(p, "char16", 6)) return _CFFI_PRIM_CHAR16; + break; + + case '2': + if (size == 7 && !memcmp(p, "int32", 5)) return _CFFI_PRIM_INT32; + break; + + case '3': + if (size == 8 && !memcmp(p, "uint32", 6)) return _CFFI_PRIM_UINT32; + if (size == 8 && !memcmp(p, "char32", 6)) return _CFFI_PRIM_CHAR32; + break; + + case '4': + if (size == 7 && !memcmp(p, "int64", 5)) return _CFFI_PRIM_INT64; + break; + + case '6': + if (size == 8 && !memcmp(p, "uint64", 6)) return _CFFI_PRIM_UINT64; + if (size == 7 && !memcmp(p, "int16", 5)) return _CFFI_PRIM_INT16; + break; + + case '8': + if (size == 7 && !memcmp(p, "uint8", 5)) return _CFFI_PRIM_UINT8; + break; + + case 'a': + if (size == 8 && !memcmp(p, "intmax", 6)) return _CFFI_PRIM_INTMAX; + break; + + case 'e': + if (size == 7 && !memcmp(p, "ssize", 5)) return _CFFI_PRIM_SSIZE; + break; + + case 'f': + if (size == 11 && !memcmp(p, "int_fast8", 9)) return _CFFI_PRIM_INT_FAST8; + if (size == 12 && !memcmp(p, "int_fast16", 10)) return _CFFI_PRIM_INT_FAST16; + if (size == 12 && !memcmp(p, "int_fast32", 10)) return _CFFI_PRIM_INT_FAST32; + if (size == 12 && !memcmp(p, "int_fast64", 10)) return _CFFI_PRIM_INT_FAST64; + break; + + case 'i': + if (size == 9 && !memcmp(p, "ptrdiff", 7)) return _CFFI_PRIM_PTRDIFF; + break; + + case 'l': + if (size == 12 && !memcmp(p, "int_least8", 10)) return _CFFI_PRIM_INT_LEAST8; + if (size == 13 && !memcmp(p, "int_least16", 11)) return _CFFI_PRIM_INT_LEAST16; + if (size == 13 && !memcmp(p, "int_least32", 11)) return _CFFI_PRIM_INT_LEAST32; + if (size == 13 && !memcmp(p, "int_least64", 11)) return _CFFI_PRIM_INT_LEAST64; + break; + + case 'm': + if (size == 9 && !memcmp(p, "uintmax", 7)) return _CFFI_PRIM_UINTMAX; + break; + + case 'p': + if (size == 9 && !memcmp(p, "uintptr", 7)) return _CFFI_PRIM_UINTPTR; + break; + + case 'r': + if (size == 7 && !memcmp(p, "wchar", 5)) return _CFFI_PRIM_WCHAR; + break; + + case 't': + if (size == 8 && !memcmp(p, "intptr", 6)) return _CFFI_PRIM_INTPTR; + break; + + case '_': + if (size == 6 && !memcmp(p, "size", 4)) return _CFFI_PRIM_SIZE; + if (size == 6 && !memcmp(p, "int8", 4)) return _CFFI_PRIM_INT8; + if (size >= 12) { + switch (p[10]) { + case '1': + if (size == 14 && !memcmp(p, "uint_least16", 12)) return _CFFI_PRIM_UINT_LEAST16; + break; + case '2': + if (size == 13 && !memcmp(p, "uint_fast32", 11)) return _CFFI_PRIM_UINT_FAST32; + break; + case '3': + if (size == 14 && !memcmp(p, "uint_least32", 12)) return _CFFI_PRIM_UINT_LEAST32; + break; + case '4': + if (size == 13 && !memcmp(p, "uint_fast64", 11)) return _CFFI_PRIM_UINT_FAST64; + break; + case '6': + if (size == 14 && !memcmp(p, "uint_least64", 12)) return _CFFI_PRIM_UINT_LEAST64; + if (size == 13 && !memcmp(p, "uint_fast16", 11)) return _CFFI_PRIM_UINT_FAST16; + break; + case '8': + if (size == 13 && !memcmp(p, "uint_least8", 11)) return _CFFI_PRIM_UINT_LEAST8; + break; + case '_': + if (size == 12 && !memcmp(p, "uint_fast8", 10)) return _CFFI_PRIM_UINT_FAST8; + break; + default: + break; + } + } + break; + + default: + break; + } + return -1; +} + + +static int parse_complete(token_t *tok) +{ + unsigned int t0; + _cffi_opcode_t t1; + _cffi_opcode_t t1complex; + int modifiers_length, modifiers_sign; + + qualifiers: + switch (tok->kind) { + case TOK_CONST: + /* ignored for now */ + next_token(tok); + goto qualifiers; + case TOK_VOLATILE: + /* ignored for now */ + next_token(tok); + goto qualifiers; + default: + ; + } + + modifiers_length = 0; + modifiers_sign = 0; + modifiers: + switch (tok->kind) { + + case TOK_SHORT: + if (modifiers_length != 0) + return parse_error(tok, "'short' after another 'short' or 'long'"); + modifiers_length--; + next_token(tok); + goto modifiers; + + case TOK_LONG: + if (modifiers_length < 0) + return parse_error(tok, "'long' after 'short'"); + if (modifiers_length >= 2) + return parse_error(tok, "'long long long' is too long"); + modifiers_length++; + next_token(tok); + goto modifiers; + + case TOK_SIGNED: + if (modifiers_sign) + return parse_error(tok, "multiple 'signed' or 'unsigned'"); + modifiers_sign++; + next_token(tok); + goto modifiers; + + case TOK_UNSIGNED: + if (modifiers_sign) + return parse_error(tok, "multiple 'signed' or 'unsigned'"); + modifiers_sign--; + next_token(tok); + goto modifiers; + + default: + break; + } + + t1complex = 0; + + if (modifiers_length || modifiers_sign) { + + switch (tok->kind) { + + case TOK_VOID: + case TOK__BOOL: + case TOK_FLOAT: + case TOK_STRUCT: + case TOK_UNION: + case TOK_ENUM: + case TOK__COMPLEX: + return parse_error(tok, "invalid combination of types"); + + case TOK_DOUBLE: + if (modifiers_sign != 0 || modifiers_length != 1) + return parse_error(tok, "invalid combination of types"); + next_token(tok); + t0 = _CFFI_PRIM_LONGDOUBLE; + break; + + case TOK_CHAR: + if (modifiers_length != 0) + return parse_error(tok, "invalid combination of types"); + modifiers_length = -2; + /* fall-through */ + case TOK_INT: + next_token(tok); + /* fall-through */ + default: + if (modifiers_sign >= 0) + switch (modifiers_length) { + case -2: t0 = _CFFI_PRIM_SCHAR; break; + case -1: t0 = _CFFI_PRIM_SHORT; break; + case 1: t0 = _CFFI_PRIM_LONG; break; + case 2: t0 = _CFFI_PRIM_LONGLONG; break; + default: t0 = _CFFI_PRIM_INT; break; + } + else + switch (modifiers_length) { + case -2: t0 = _CFFI_PRIM_UCHAR; break; + case -1: t0 = _CFFI_PRIM_USHORT; break; + case 1: t0 = _CFFI_PRIM_ULONG; break; + case 2: t0 = _CFFI_PRIM_ULONGLONG; break; + default: t0 = _CFFI_PRIM_UINT; break; + } + } + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, t0); + } + else { + switch (tok->kind) { + case TOK_INT: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_INT); + break; + case TOK_CHAR: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_CHAR); + break; + case TOK_VOID: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_VOID); + break; + case TOK__BOOL: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_BOOL); + break; + case TOK_FLOAT: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOAT); + t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_FLOATCOMPLEX); + break; + case TOK_DOUBLE: + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLE); + t1complex = _CFFI_OP(_CFFI_OP_PRIMITIVE, _CFFI_PRIM_DOUBLECOMPLEX); + break; + case TOK_IDENTIFIER: + { + const char *replacement; + int n = search_in_typenames(tok->info->ctx, tok->p, tok->size); + if (n >= 0) { + t1 = _CFFI_OP(_CFFI_OP_TYPENAME, n); + break; + } + n = search_standard_typename(tok->p, tok->size); + if (n >= 0) { + t1 = _CFFI_OP(_CFFI_OP_PRIMITIVE, n); + break; + } + replacement = get_common_type(tok->p, tok->size); + if (replacement != NULL) { + n = parse_common_type_replacement(tok, replacement); + if (n < 0) + return parse_error(tok, "internal error, please report!"); + t1 = _CFFI_OP(_CFFI_OP_NOOP, n); + break; + } + return parse_error(tok, "undefined type name"); + } + case TOK_STRUCT: + case TOK_UNION: + { + int n, kind = tok->kind; + next_token(tok); + if (tok->kind != TOK_IDENTIFIER) + return parse_error(tok, "struct or union name expected"); + + n = search_in_struct_unions(tok->info->ctx, tok->p, tok->size); + if (n < 0) { + if (kind == TOK_STRUCT && tok->size == 8 && + !memcmp(tok->p, "_IO_FILE", 8)) + n = _CFFI__IO_FILE_STRUCT; + else + return parse_error(tok, "undefined struct/union name"); + } + else if (((tok->info->ctx->struct_unions[n].flags & _CFFI_F_UNION) + != 0) ^ (kind == TOK_UNION)) + return parse_error(tok, "wrong kind of tag: struct vs union"); + + t1 = _CFFI_OP(_CFFI_OP_STRUCT_UNION, n); + break; + } + case TOK_ENUM: + { + int n; + next_token(tok); + if (tok->kind != TOK_IDENTIFIER) + return parse_error(tok, "enum name expected"); + + n = search_in_enums(tok->info->ctx, tok->p, tok->size); + if (n < 0) + return parse_error(tok, "undefined enum name"); + + t1 = _CFFI_OP(_CFFI_OP_ENUM, n); + break; + } + default: + return parse_error(tok, "identifier expected"); + } + next_token(tok); + } + if (tok->kind == TOK__COMPLEX) + { + if (t1complex == 0) + return parse_error(tok, "_Complex type combination unsupported"); + t1 = t1complex; + next_token(tok); + } + + return parse_sequel(tok, write_ds(tok, t1)); +} + + +static +int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index, + const char *input) +{ + int result; + token_t token; + + token.info = info; + token.kind = TOK_START; + token.input = input; + token.p = input; + token.size = 0; + token.output = info->output; + token.output_index = *output_index; + + next_token(&token); + result = parse_complete(&token); + + *output_index = token.output_index; + if (token.kind != TOK_END) + return parse_error(&token, "unexpected symbol"); + return result; +} + +static +int parse_c_type(struct _cffi_parse_info_s *info, const char *input) +{ + size_t output_index = 0; + return parse_c_type_from(info, &output_index, input); +} + +static +int parse_common_type_replacement(token_t *tok, const char *replacement) +{ + return parse_c_type_from(tok->info, &tok->output_index, replacement); +} diff --git a/c/realize_c_type.c b/c/realize_c_type.c new file mode 100644 index 0000000..082c488 --- /dev/null +++ b/c/realize_c_type.c @@ -0,0 +1,797 @@ + +typedef struct { + struct _cffi_type_context_s ctx; /* inlined substructure */ + PyObject *types_dict; + PyObject *included_ffis; + PyObject *included_libs; + PyObject *_keepalive1; + PyObject *_keepalive2; +} builder_c_t; + + +static PyObject *all_primitives[_CFFI__NUM_PRIM]; +static CTypeDescrObject *g_ct_voidp, *g_ct_chararray; + +static PyObject *build_primitive_type(int num); /* forward */ + +#define primitive_in_range(num) ((num) >= 0 && (num) < _CFFI__NUM_PRIM) +#define get_primitive_type(num) \ + ((primitive_in_range(num) && all_primitives[num] != NULL) ? \ + all_primitives[num] : build_primitive_type(num)) + +static int init_global_types_dict(PyObject *ffi_type_dict) +{ + int err; + PyObject *ct_void, *ct_char, *ct2, *pnull; + /* XXX some leaks in case these functions fail, but well, + MemoryErrors during importing an extension module are kind + of bad anyway */ + + ct_void = get_primitive_type(_CFFI_PRIM_VOID); // 'void' + if (ct_void == NULL) + return -1; + + ct2 = new_pointer_type((CTypeDescrObject *)ct_void); // 'void *' + if (ct2 == NULL) + return -1; + g_ct_voidp = (CTypeDescrObject *)ct2; + + ct_char = get_primitive_type(_CFFI_PRIM_CHAR); // 'char' + if (ct_char == NULL) + return -1; + + ct2 = new_pointer_type((CTypeDescrObject *)ct_char); // 'char *' + if (ct2 == NULL) + return -1; + + ct2 = new_array_type((CTypeDescrObject *)ct2, -1); // 'char[]' + if (ct2 == NULL) + return -1; + g_ct_chararray = (CTypeDescrObject *)ct2; + + pnull = new_simple_cdata(NULL, g_ct_voidp); + if (pnull == NULL) + return -1; + err = PyDict_SetItemString(ffi_type_dict, "NULL", pnull); + Py_DECREF(pnull); + return err; +} + +static void free_builder_c(builder_c_t *builder, int ctx_is_static) +{ + if (!ctx_is_static) { + size_t i; + const void *mem[] = {builder->ctx.types, + builder->ctx.globals, + builder->ctx.struct_unions, + //builder->ctx.fields: allocated with struct_unions + builder->ctx.enums, + builder->ctx.typenames}; + for (i = 0; i < sizeof(mem) / sizeof(*mem); i++) { + if (mem[i] != NULL) + PyMem_Free((void *)mem[i]); + } + } + Py_XDECREF(builder->included_ffis); + Py_XDECREF(builder->included_libs); + Py_XDECREF(builder->types_dict); + Py_XDECREF(builder->_keepalive1); + Py_XDECREF(builder->_keepalive2); +} + +static int init_builder_c(builder_c_t *builder, + const struct _cffi_type_context_s *ctx) +{ + PyObject *ldict = PyDict_New(); + if (ldict == NULL) + return -1; + + if (ctx) + builder->ctx = *ctx; + else + memset(&builder->ctx, 0, sizeof(builder->ctx)); + + builder->types_dict = ldict; + builder->included_ffis = NULL; + builder->included_libs = NULL; + builder->_keepalive1 = NULL; + builder->_keepalive2 = NULL; + return 0; +} + +static PyObject *build_primitive_type(int num) +{ + /* XXX too many translations between here and new_primitive_type() */ + static const char *primitive_name[] = { + NULL, + "_Bool", + "char", + "signed char", + "unsigned char", + "short", + "unsigned short", + "int", + "unsigned int", + "long", + "unsigned long", + "long long", + "unsigned long long", + "float", + "double", + "long double", + "wchar_t", + "int8_t", + "uint8_t", + "int16_t", + "uint16_t", + "int32_t", + "uint32_t", + "int64_t", + "uint64_t", + "intptr_t", + "uintptr_t", + "ptrdiff_t", + "size_t", + "ssize_t", + "int_least8_t", + "uint_least8_t", + "int_least16_t", + "uint_least16_t", + "int_least32_t", + "uint_least32_t", + "int_least64_t", + "uint_least64_t", + "int_fast8_t", + "uint_fast8_t", + "int_fast16_t", + "uint_fast16_t", + "int_fast32_t", + "uint_fast32_t", + "int_fast64_t", + "uint_fast64_t", + "intmax_t", + "uintmax_t", + "float _Complex", + "double _Complex", + "char16_t", + "char32_t", + }; + PyObject *x; + + assert(sizeof(primitive_name) == sizeof(*primitive_name) * _CFFI__NUM_PRIM); + if (num == _CFFI_PRIM_VOID) { + x = new_void_type(); + } + else if (primitive_in_range(num) && primitive_name[num] != NULL) { + x = new_primitive_type(primitive_name[num]); + } + else if (num == _CFFI__UNKNOWN_PRIM) { + PyErr_SetString(FFIError, "primitive integer type with an unexpected " + "size (or not an integer type at all)"); + return NULL; + } + else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) { + PyErr_SetString(FFIError, "primitive floating-point type with an " + "unexpected size (or not a float type at all)"); + return NULL; + } + else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) { + PyErr_SetString(FFIError, "primitive floating-point type is " + "'long double', not supported for now with " + "the syntax 'typedef double... xxx;'"); + return NULL; + } + else { + PyErr_Format(PyExc_NotImplementedError, "prim=%d", num); + return NULL; + } + + all_primitives[num] = x; + return x; +} + +static PyObject *realize_global_int(builder_c_t *builder, int gindex) +{ + int neg; + char got[64]; + unsigned long long value; + struct _cffi_getconst_s gc; + const struct _cffi_global_s *g = &builder->ctx.globals[gindex]; + gc.ctx = &builder->ctx; + gc.gindex = gindex; + /* note: we cast g->address to this function type; we do the same + in parse_c_type:parse_sequel() too. Note that the called function + may be declared simply with "unsigned long long *" as argument, + which is fine as it is the first field in _cffi_getconst_s. */ + assert(&gc.value == (unsigned long long *)&gc); + neg = ((int(*)(struct _cffi_getconst_s *))g->address)(&gc); + value = gc.value; + + switch (neg) { + + case 0: + if (value <= (unsigned long long)LONG_MAX) + return PyInt_FromLong((long)value); + else + return PyLong_FromUnsignedLongLong(value); + + case 1: + if ((long long)value >= (long long)LONG_MIN) + return PyInt_FromLong((long)value); + else + return PyLong_FromLongLong((long long)value); + + default: + break; + } + if (neg == 2) + sprintf(got, "%llu (0x%llx)", value, value); + else + sprintf(got, "%lld", (long long)value); + PyErr_Format(FFIError, "the C compiler says '%.200s' is equal to %s, " + "but the cdef disagrees", g->name, got); + return NULL; +} + +static CTypeDescrObject * +unwrap_fn_as_fnptr(PyObject *x) +{ + assert(PyTuple_Check(x)); + return (CTypeDescrObject *)PyTuple_GET_ITEM(x, 0); +} + +static CTypeDescrObject * +unexpected_fn_type(PyObject *x) +{ + CTypeDescrObject *ct = unwrap_fn_as_fnptr(x); + char *text1 = ct->ct_name; + char *text2 = text1 + ct->ct_name_position + 1; + assert(text2[-3] == '('); + text2[-3] = '\0'; + PyErr_Format(FFIError, "the type '%s%s' is a function type, not a " + "pointer-to-function type", text1, text2); + text2[-3] = '('; + return NULL; +} + +static PyObject * +realize_c_type_or_func(builder_c_t *builder, + _cffi_opcode_t opcodes[], int index); /* forward */ + + +/* Interpret an opcodes[] array. If opcodes == ctx->types, store all + the intermediate types back in the opcodes[]. Returns a new + reference. +*/ +static CTypeDescrObject * +realize_c_type(builder_c_t *builder, _cffi_opcode_t opcodes[], int index) +{ + PyObject *x = realize_c_type_or_func(builder, opcodes, index); + if (x == NULL || CTypeDescr_Check(x)) + return (CTypeDescrObject *)x; + else { + unexpected_fn_type(x); + Py_DECREF(x); + return NULL; + } +} + +static void _realize_name(char *target, const char *prefix, const char *srcname) +{ + /* "xyz" => "struct xyz" + "$xyz" => "xyz" + "$1" => "struct $1" + */ + if (srcname[0] == '$' && srcname[1] != '$' && + !('0' <= srcname[1] && srcname[1] <= '9')) { + strcpy(target, &srcname[1]); + } + else { + strcpy(target, prefix); + strcat(target, srcname); + } +} + +static void _unrealize_name(char *target, const char *srcname) +{ + /* reverse of _realize_name() */ + if (strncmp(srcname, "struct ", 7) == 0) { + strcpy(target, &srcname[7]); + } + else if (strncmp(srcname, "union ", 6) == 0) { + strcpy(target, &srcname[6]); + } + else if (strncmp(srcname, "enum ", 5) == 0) { + strcpy(target, &srcname[5]); + } + else { + strcpy(target, "$"); + strcat(target, srcname); + } +} + +static PyObject * /* forward */ +_fetch_external_struct_or_union(const struct _cffi_struct_union_s *s, + PyObject *included_ffis, int recursion); + +static PyObject * +_realize_c_struct_or_union(builder_c_t *builder, int sindex) +{ + PyObject *x; + _cffi_opcode_t op2; + const struct _cffi_struct_union_s *s; + + if (sindex == _CFFI__IO_FILE_STRUCT) { + /* returns a single global cached opaque type */ + static PyObject *file_struct = NULL; + if (file_struct == NULL) + file_struct = new_struct_or_union_type("FILE", + CT_STRUCT | CT_IS_FILE); + Py_XINCREF(file_struct); + return file_struct; + } + + s = &builder->ctx.struct_unions[sindex]; + op2 = builder->ctx.types[s->type_index]; + if ((((uintptr_t)op2) & 1) == 0) { + x = (PyObject *)op2; /* found already in the "primary" slot */ + Py_INCREF(x); + } + else { + CTypeDescrObject *ct = NULL; + + if (!(s->flags & _CFFI_F_EXTERNAL)) { + int flags = (s->flags & _CFFI_F_UNION) ? CT_UNION : CT_STRUCT; + char *name = alloca(8 + strlen(s->name)); + _realize_name(name, + (s->flags & _CFFI_F_UNION) ? "union " : "struct ", + s->name); + if (strcmp(name, "struct _IO_FILE") == 0) + x = _realize_c_struct_or_union(builder, _CFFI__IO_FILE_STRUCT); + else + x = new_struct_or_union_type(name, flags); + if (x == NULL) + return NULL; + + if (!(s->flags & _CFFI_F_OPAQUE)) { + assert(s->first_field_index >= 0); + ct = (CTypeDescrObject *)x; + ct->ct_size = (Py_ssize_t)s->size; + ct->ct_length = s->alignment; /* may be -1 */ + ct->ct_flags &= ~CT_IS_OPAQUE; + ct->ct_flags |= CT_LAZY_FIELD_LIST; + ct->ct_extra = builder; + } + else + assert(s->first_field_index < 0); + } + else { + assert(s->first_field_index < 0); + x = _fetch_external_struct_or_union(s, builder->included_ffis, 0); + if (x == NULL) { + if (!PyErr_Occurred()) + PyErr_Format(FFIError, "'%s %.200s' should come from " + "ffi.include() but was not found", + (s->flags & _CFFI_F_UNION) ? "union" + : "struct", s->name); + return NULL; + } + if (!(s->flags & _CFFI_F_OPAQUE)) { + if (((CTypeDescrObject *)x)->ct_flags & CT_IS_OPAQUE) { + const char *prefix = (s->flags & _CFFI_F_UNION) ? "union" + : "struct"; + PyErr_Format(PyExc_NotImplementedError, + "'%s %.200s' is opaque in the ffi.include(), " + "but no longer in the ffi doing the include " + "(workaround: don't use ffi.include() but " + "duplicate the declarations of everything " + "using %s %.200s)", + prefix, s->name, prefix, s->name); + Py_DECREF(x); + return NULL; + } + } + } + + /* Update the "primary" OP_STRUCT_UNION slot */ + assert((((uintptr_t)x) & 1) == 0); + assert(builder->ctx.types[s->type_index] == op2); + Py_INCREF(x); + builder->ctx.types[s->type_index] = x; + + if (ct != NULL && s->size == (size_t)-2) { + /* oops, this struct is unnamed and we couldn't generate + a C expression to get its size. We have to rely on + complete_struct_or_union() to compute it now. */ + if (do_realize_lazy_struct(ct) < 0) { + builder->ctx.types[s->type_index] = op2; + return NULL; + } + } + } + return x; +} + +static PyObject * +realize_c_type_or_func(builder_c_t *builder, + _cffi_opcode_t opcodes[], int index) +{ + PyObject *x, *y, *z; + _cffi_opcode_t op = opcodes[index]; + Py_ssize_t length = -1; + + if ((((uintptr_t)op) & 1) == 0) { + x = (PyObject *)op; + Py_INCREF(x); + return x; + } + + switch (_CFFI_GETOP(op)) { + + case _CFFI_OP_PRIMITIVE: + x = get_primitive_type(_CFFI_GETARG(op)); + Py_XINCREF(x); + break; + + case _CFFI_OP_POINTER: + y = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); + if (y == NULL) + return NULL; + if (CTypeDescr_Check(y)) { + x = new_pointer_type((CTypeDescrObject *)y); + } + else { + assert(PyTuple_Check(y)); /* from _CFFI_OP_FUNCTION */ + x = PyTuple_GET_ITEM(y, 0); + Py_INCREF(x); + } + Py_DECREF(y); + break; + + case _CFFI_OP_ARRAY: + length = (Py_ssize_t)opcodes[index + 1]; + /* fall-through */ + case _CFFI_OP_OPEN_ARRAY: + y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); + if (y == NULL) + return NULL; + z = new_pointer_type((CTypeDescrObject *)y); + Py_DECREF(y); + if (z == NULL) + return NULL; + x = new_array_type((CTypeDescrObject *)z, length); + Py_DECREF(z); + break; + + case _CFFI_OP_STRUCT_UNION: + x = _realize_c_struct_or_union(builder, _CFFI_GETARG(op)); + break; + + case _CFFI_OP_ENUM: + { + const struct _cffi_enum_s *e; + _cffi_opcode_t op2; + + e = &builder->ctx.enums[_CFFI_GETARG(op)]; + op2 = builder->ctx.types[e->type_index]; + if ((((uintptr_t)op2) & 1) == 0) { + x = (PyObject *)op2; + Py_INCREF(x); + } + else { + PyObject *enumerators = NULL, *enumvalues = NULL, *tmp; + Py_ssize_t i, j, n = 0; + const char *p; + int gindex; + PyObject *args; + PyObject *basetd = get_primitive_type(e->type_prim); + if (basetd == NULL) + return NULL; + + if (*e->enumerators != '\0') { + n++; + for (p = e->enumerators; *p != '\0'; p++) + n += (*p == ','); + } + enumerators = PyTuple_New(n); + if (enumerators == NULL) + return NULL; + + enumvalues = PyTuple_New(n); + if (enumvalues == NULL) { + Py_DECREF(enumerators); + return NULL; + } + + p = e->enumerators; + for (i = 0; i < n; i++) { + j = 0; + while (p[j] != ',' && p[j] != '\0') + j++; + tmp = PyText_FromStringAndSize(p, j); + if (tmp == NULL) + break; + PyTuple_SET_ITEM(enumerators, i, tmp); + + gindex = search_in_globals(&builder->ctx, p, j); + assert(gindex >= 0); + assert(builder->ctx.globals[gindex].type_op == + _CFFI_OP(_CFFI_OP_ENUM, -1)); + + tmp = realize_global_int(builder, gindex); + if (tmp == NULL) + break; + PyTuple_SET_ITEM(enumvalues, i, tmp); + + p += j + 1; + } + + args = NULL; + if (!PyErr_Occurred()) { + char *name = alloca(6 + strlen(e->name)); + _realize_name(name, "enum ", e->name); + args = Py_BuildValue("(sOOO)", name, enumerators, + enumvalues, basetd); + } + Py_DECREF(enumerators); + Py_DECREF(enumvalues); + if (args == NULL) + return NULL; + + x = b_new_enum_type(NULL, args); + Py_DECREF(args); + if (x == NULL) + return NULL; + + /* Update the "primary" _CFFI_OP_ENUM slot, which + may be the same or a different slot than the "current" one */ + assert((((uintptr_t)x) & 1) == 0); + assert(builder->ctx.types[e->type_index] == op2); + Py_INCREF(x); + builder->ctx.types[e->type_index] = x; + + /* Done, leave without updating the "current" slot because + it may be done already above. If not, never mind, the + next call to realize_c_type() will do it. */ + return x; + } + break; + } + + case _CFFI_OP_FUNCTION: + { + PyObject *fargs; + int i, base_index, num_args, ellipsis, abi; + + y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op)); + if (y == NULL) + return NULL; + + base_index = index + 1; + num_args = 0; + /* 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. */ + while (_CFFI_GETOP(opcodes[base_index + num_args]) != + _CFFI_OP_FUNCTION_END) + num_args++; + + ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01; + abi = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE; + switch (abi) { + case 0: + abi = FFI_DEFAULT_ABI; + break; + case 2: +#if defined(MS_WIN32) && !defined(_WIN64) + abi = FFI_STDCALL; +#else + abi = FFI_DEFAULT_ABI; +#endif + break; + default: + PyErr_Format(FFIError, "abi number %d not supported", abi); + Py_DECREF(y); + return NULL; + } + + fargs = PyTuple_New(num_args); + if (fargs == NULL) { + Py_DECREF(y); + return NULL; + } + + for (i = 0; i < num_args; i++) { + z = (PyObject *)realize_c_type(builder, opcodes, base_index + i); + if (z == NULL) { + Py_DECREF(fargs); + Py_DECREF(y); + return NULL; + } + PyTuple_SET_ITEM(fargs, i, z); + } + + z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi); + Py_DECREF(fargs); + Py_DECREF(y); + if (z == NULL) + return NULL; + + x = PyTuple_Pack(1, z); /* hack: hide the CT_FUNCTIONPTR. it will + be revealed again by the OP_POINTER */ + Py_DECREF(z); + break; + } + + case _CFFI_OP_NOOP: + x = realize_c_type_or_func(builder, opcodes, _CFFI_GETARG(op)); + break; + + case _CFFI_OP_TYPENAME: + { + /* essential: the TYPENAME opcode resolves the type index looked + up in the 'ctx->typenames' array, but it does so in 'ctx->types' + instead of in 'opcodes'! */ + int type_index = builder->ctx.typenames[_CFFI_GETARG(op)].type_index; + x = realize_c_type_or_func(builder, builder->ctx.types, type_index); + break; + } + + default: + PyErr_Format(PyExc_NotImplementedError, "op=%d", (int)_CFFI_GETOP(op)); + return NULL; + } + + if (x != NULL && opcodes == builder->ctx.types && opcodes[index] != x) { + assert((((uintptr_t)x) & 1) == 0); + assert((((uintptr_t)opcodes[index]) & 1) == 1); + Py_INCREF(x); + opcodes[index] = x; + } + return x; +}; + +static CTypeDescrObject * +realize_c_func_return_type(builder_c_t *builder, + _cffi_opcode_t opcodes[], int index) +{ + PyObject *x; + _cffi_opcode_t op = opcodes[index]; + + if ((((uintptr_t)op) & 1) == 0) { + /* already built: assert that it is a function and fish + for the return type */ + x = (PyObject *)op; + assert(PyTuple_Check(x)); /* from _CFFI_OP_FUNCTION */ + x = PyTuple_GET_ITEM(x, 0); + assert(CTypeDescr_Check(x)); + assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR); + x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1); + assert(CTypeDescr_Check(x)); + Py_INCREF(x); + return (CTypeDescrObject *)x; + } + else { + assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION); + return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index])); + } +} + +static int do_realize_lazy_struct(CTypeDescrObject *ct) +{ + /* This is called by force_lazy_struct() in _cffi_backend.c */ + assert(ct->ct_flags & (CT_STRUCT | CT_UNION)); + + if (ct->ct_flags & CT_LAZY_FIELD_LIST) { + builder_c_t *builder; + char *p; + int n, i, sflags; + const struct _cffi_struct_union_s *s; + const struct _cffi_field_s *fld; + PyObject *fields, *args, *res; + + assert(!(ct->ct_flags & CT_IS_OPAQUE)); + + builder = ct->ct_extra; + assert(builder != NULL); + + p = alloca(2 + strlen(ct->ct_name)); + _unrealize_name(p, ct->ct_name); + + n = search_in_struct_unions(&builder->ctx, p, strlen(p)); + if (n < 0) + Py_FatalError("lost a struct/union!"); + + s = &builder->ctx.struct_unions[n]; + fld = &builder->ctx.fields[s->first_field_index]; + + /* XXX painfully build all the Python objects that are the args + to b_complete_struct_or_union() */ + + fields = PyList_New(s->num_fields); + if (fields == NULL) + return -1; + + for (i = 0; i < s->num_fields; i++, fld++) { + _cffi_opcode_t op = fld->field_type_op; + int fbitsize = -1; + PyObject *f; + CTypeDescrObject *ctf; + + switch (_CFFI_GETOP(op)) { + + case _CFFI_OP_BITFIELD: + assert(fld->field_size >= 0); + fbitsize = (int)fld->field_size; + /* fall-through */ + case _CFFI_OP_NOOP: + ctf = realize_c_type(builder, builder->ctx.types, + _CFFI_GETARG(op)); + break; + + default: + Py_DECREF(fields); + PyErr_Format(PyExc_NotImplementedError, "field op=%d", + (int)_CFFI_GETOP(op)); + return -1; + } + + if (ctf != NULL && fld->field_offset == (size_t)-1) { + /* unnamed struct, with field positions and sizes entirely + determined by complete_struct_or_union() and not checked. + Or, bitfields (field_size >= 0), similarly not checked. */ + assert(fld->field_size == (size_t)-1 || fbitsize >= 0); + } + else if (ctf == NULL || detect_custom_layout(ct, SF_STD_FIELD_POS, + ctf->ct_size, fld->field_size, + "wrong size for field '", + fld->name, "'") < 0) { + Py_DECREF(fields); + return -1; + } + + f = Py_BuildValue("(sOin)", fld->name, ctf, + fbitsize, (Py_ssize_t)fld->field_offset); + if (f == NULL) { + Py_DECREF(fields); + return -1; + } + PyList_SET_ITEM(fields, i, f); + } + + sflags = 0; + if (s->flags & _CFFI_F_CHECK_FIELDS) + sflags |= SF_STD_FIELD_POS; + if (s->flags & _CFFI_F_PACKED) + sflags |= SF_PACKED; + + args = Py_BuildValue("(OOOnii)", ct, fields, Py_None, + (Py_ssize_t)s->size, + s->alignment, + sflags); + Py_DECREF(fields); + if (args == NULL) + return -1; + + ct->ct_extra = NULL; + ct->ct_flags |= CT_IS_OPAQUE; + res = b_complete_struct_or_union(NULL, args); + ct->ct_flags &= ~CT_IS_OPAQUE; + Py_DECREF(args); + + if (res == NULL) { + ct->ct_extra = builder; + return -1; + } + + assert(ct->ct_stuff != NULL); + ct->ct_flags &= ~CT_LAZY_FIELD_LIST; + Py_DECREF(res); + return 1; + } + else { + assert(ct->ct_flags & CT_IS_OPAQUE); + return 0; + } +} diff --git a/c/test_c.py b/c/test_c.py new file mode 100644 index 0000000..da5f751 --- /dev/null +++ b/c/test_c.py @@ -0,0 +1,4256 @@ +import py +def _setup_path(): + import os, sys + if '__pypy__' in sys.builtin_module_names: + py.test.skip("_cffi_backend.c: not tested on top of pypy, " + "use pypy/module/_cffi_backend/test/ instead.") + sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) +_setup_path() +from _cffi_backend import * +from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__ + +# ____________________________________________________________ + +import sys +assert __version__ == "1.12.2", ("This test_c.py file is for testing a version" + " of cffi that differs from the one that we" + " get from 'import _cffi_backend'") +if sys.version_info < (3,): + type_or_class = "type" + mandatory_b_prefix = '' + mandatory_u_prefix = 'u' + bytechr = chr + bitem2bchr = lambda x: x + class U(object): + def __add__(self, other): + return eval('u'+repr(other).replace(r'\\u', r'\u') + .replace(r'\\U', r'\U')) + u = U() + str2bytes = str + strict_compare = False +else: + type_or_class = "class" + long = int + unicode = str + unichr = chr + mandatory_b_prefix = 'b' + mandatory_u_prefix = '' + bytechr = lambda n: bytes([n]) + bitem2bchr = bytechr + u = "" + str2bytes = lambda s: bytes(s, "ascii") + strict_compare = True + +def size_of_int(): + BInt = new_primitive_type("int") + return sizeof(BInt) + +def size_of_long(): + BLong = new_primitive_type("long") + return sizeof(BLong) + +def size_of_ptr(): + BInt = new_primitive_type("int") + BPtr = new_pointer_type(BInt) + return sizeof(BPtr) + + +def find_and_load_library(name, flags=RTLD_NOW): + import ctypes.util + if name is None: + path = None + else: + path = ctypes.util.find_library(name) + if path is None and name == 'c': + assert sys.platform == 'win32' + assert sys.version_info >= (3,) + py.test.skip("dlopen(None) cannot work on Windows with Python 3") + return load_library(path, flags) + +def test_load_library(): + x = find_and_load_library('c') + assert repr(x).startswith("<clibrary '") + x = find_and_load_library('c', RTLD_NOW | RTLD_GLOBAL) + assert repr(x).startswith("<clibrary '") + x = find_and_load_library('c', RTLD_LAZY) + assert repr(x).startswith("<clibrary '") + +def test_all_rtld_symbols(): + import sys + FFI_DEFAULT_ABI # these symbols must be defined + FFI_CDECL + RTLD_LAZY + RTLD_NOW + RTLD_GLOBAL + RTLD_LOCAL + if sys.platform.startswith("linux"): + RTLD_NODELETE + RTLD_NOLOAD + RTLD_DEEPBIND + +def test_new_primitive_type(): + py.test.raises(KeyError, new_primitive_type, "foo") + p = new_primitive_type("signed char") + assert repr(p) == "<ctype 'signed char'>" + +def check_dir(p, expected): + got = [name for name in dir(p) if not name.startswith('_')] + assert got == sorted(expected) + +def test_inspect_primitive_type(): + p = new_primitive_type("signed char") + assert p.kind == "primitive" + assert p.cname == "signed char" + check_dir(p, ['cname', 'kind']) + +def test_cast_to_signed_char(): + p = new_primitive_type("signed char") + x = cast(p, -65 + 17*256) + assert repr(x) == "<cdata 'signed char' -65>" + assert repr(type(x)) == "<%s '_cffi_backend.CData'>" % type_or_class + assert int(x) == -65 + x = cast(p, -66 + (1<<199)*256) + assert repr(x) == "<cdata 'signed char' -66>" + assert int(x) == -66 + assert (x == cast(p, -66)) is True + assert (x != cast(p, -66)) is False + q = new_primitive_type("short") + assert (x == cast(q, -66)) is True + assert (x != cast(q, -66)) is False + +def test_sizeof_type(): + py.test.raises(TypeError, sizeof, 42.5) + p = new_primitive_type("short") + assert sizeof(p) == 2 + +def test_integer_types(): + for name in ['signed char', 'short', 'int', 'long', 'long long']: + p = new_primitive_type(name) + size = sizeof(p) + min = -(1 << (8*size-1)) + max = (1 << (8*size-1)) - 1 + assert int(cast(p, min)) == min + assert int(cast(p, max)) == max + assert int(cast(p, min - 1)) == max + assert int(cast(p, max + 1)) == min + py.test.raises(TypeError, cast, p, None) + assert long(cast(p, min - 1)) == max + assert int(cast(p, b'\x08')) == 8 + assert int(cast(p, u+'\x08')) == 8 + for name in ['char', 'short', 'int', 'long', 'long long']: + p = new_primitive_type('unsigned ' + name) + size = sizeof(p) + max = (1 << (8*size)) - 1 + assert int(cast(p, 0)) == 0 + assert int(cast(p, max)) == max + assert int(cast(p, -1)) == max + assert int(cast(p, max + 1)) == 0 + assert long(cast(p, -1)) == max + assert int(cast(p, b'\xFE')) == 254 + assert int(cast(p, u+'\xFE')) == 254 + +def test_no_float_on_int_types(): + p = new_primitive_type('long') + py.test.raises(TypeError, float, cast(p, 42)) + py.test.raises(TypeError, complex, cast(p, 42)) + +def test_float_types(): + INF = 1E200 * 1E200 + for name in ["float", "double"]: + p = new_primitive_type(name) + assert bool(cast(p, 0)) is False # since 1.7 + assert bool(cast(p, -0.0)) is False # since 1.7 + assert bool(cast(p, 1e-42)) is True + assert bool(cast(p, -1e-42)) is True + assert bool(cast(p, INF)) + assert bool(cast(p, -INF)) + assert bool(cast(p, float("nan"))) + assert int(cast(p, -150)) == -150 + assert int(cast(p, 61.91)) == 61 + assert long(cast(p, 61.91)) == 61 + assert type(int(cast(p, 61.91))) is int + assert type(int(cast(p, 1E22))) is long + assert type(long(cast(p, 61.91))) is long + assert type(long(cast(p, 1E22))) is long + py.test.raises(OverflowError, int, cast(p, INF)) + py.test.raises(OverflowError, int, cast(p, -INF)) + assert float(cast(p, 1.25)) == 1.25 + assert float(cast(p, INF)) == INF + assert float(cast(p, -INF)) == -INF + if name == "float": + assert float(cast(p, 1.1)) != 1.1 # rounding error + assert float(cast(p, 1E200)) == INF # limited range + + assert cast(p, -1.1) == cast(p, -1.1) + assert repr(float(cast(p, -0.0))) == '-0.0' + assert float(cast(p, b'\x09')) == 9.0 + assert float(cast(p, u+'\x09')) == 9.0 + assert float(cast(p, True)) == 1.0 + py.test.raises(TypeError, cast, p, None) + +def test_complex_types(): + INF = 1E200 * 1E200 + for name in ["float", "double"]: + p = new_primitive_type(name + " _Complex") + assert bool(cast(p, 0)) is False + assert bool(cast(p, INF)) + assert bool(cast(p, -INF)) + assert bool(cast(p, 0j)) is False + assert bool(cast(p, INF*1j)) + assert bool(cast(p, -INF*1j)) + # "can't convert complex to float", like CPython's "float(0j)" + py.test.raises(TypeError, int, cast(p, -150)) + py.test.raises(TypeError, long, cast(p, -150)) + py.test.raises(TypeError, float, cast(p, -150)) + assert complex(cast(p, 1.25)) == 1.25 + assert complex(cast(p, 1.25j)) == 1.25j + assert complex(cast(p, complex(0,INF))) == complex(0,INF) + assert complex(cast(p, -INF)) == -INF + if name == "float": + assert complex(cast(p, 1.1j)) != 1.1j # rounding error + assert complex(cast(p, 1E200+3j)) == INF+3j # limited range + assert complex(cast(p, complex(3,1E200))) == complex(3,INF) # limited range + + assert cast(p, -1.1j) == cast(p, -1.1j) + assert repr(complex(cast(p, -0.0)).real) == '-0.0' + #assert repr(complex(cast(p, -0j))) == '-0j' # http://bugs.python.org/issue29602 + assert complex(cast(p, b'\x09')) == 9.0 + 0j + assert complex(cast(p, u+'\x09')) == 9.0 + 0j + assert complex(cast(p, True)) == 1.0 + 0j + py.test.raises(TypeError, cast, p, None) + # + py.test.raises(TypeError, cast, new_primitive_type(name), 1+0j) + # + for basetype in ["char", "int", "uint64_t", "float", + "double", "long double"]: + baseobj = cast(new_primitive_type(basetype), 65) + py.test.raises(TypeError, complex, baseobj) + # + BArray = new_array_type(new_pointer_type(p), 10) + x = newp(BArray, None) + x[5] = 12.34 + 56.78j + assert type(x[5]) is complex + assert abs(x[5] - (12.34 + 56.78j)) < 1e-5 + assert (x[5] == 12.34 + 56.78j) == (name == "double") # rounding error + # + class Foo: + def __complex__(self): + return 2 + 3j + assert complex(Foo()) == 2 + 3j + assert complex(cast(p, Foo())) == 2 + 3j + py.test.raises(TypeError, cast, new_primitive_type("int"), 1+0j) + +def test_character_type(): + p = new_primitive_type("char") + assert bool(cast(p, 'A')) is True + assert bool(cast(p, '\x00')) is False # since 1.7 + assert cast(p, '\x00') == cast(p, -17*256) + assert int(cast(p, 'A')) == 65 + assert long(cast(p, 'A')) == 65 + assert type(int(cast(p, 'A'))) is int + assert type(long(cast(p, 'A'))) is long + assert str(cast(p, 'A')) == repr(cast(p, 'A')) + assert repr(cast(p, 'A')) == "<cdata 'char' %s'A'>" % mandatory_b_prefix + assert repr(cast(p, 255)) == r"<cdata 'char' %s'\xff'>" % mandatory_b_prefix + assert repr(cast(p, 0)) == r"<cdata 'char' %s'\x00'>" % mandatory_b_prefix + +def test_pointer_type(): + p = new_primitive_type("int") + assert repr(p) == "<ctype 'int'>" + p = new_pointer_type(p) + assert repr(p) == "<ctype 'int *'>" + p = new_pointer_type(p) + assert repr(p) == "<ctype 'int * *'>" + p = new_pointer_type(p) + assert repr(p) == "<ctype 'int * * *'>" + +def test_inspect_pointer_type(): + p1 = new_primitive_type("int") + p2 = new_pointer_type(p1) + assert p2.kind == "pointer" + assert p2.cname == "int *" + assert p2.item is p1 + check_dir(p2, ['cname', 'kind', 'item']) + p3 = new_pointer_type(p2) + assert p3.item is p2 + +def test_pointer_to_int(): + BInt = new_primitive_type("int") + py.test.raises(TypeError, newp, BInt) + py.test.raises(TypeError, newp, BInt, None) + BPtr = new_pointer_type(BInt) + p = newp(BPtr) + assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() + p = newp(BPtr, None) + assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() + p = newp(BPtr, 5000) + assert repr(p) == "<cdata 'int *' owning %d bytes>" % size_of_int() + q = cast(BPtr, p) + assert repr(q).startswith("<cdata 'int *' 0x") + assert p == q + assert hash(p) == hash(q) + e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), None) + assert str(e.value) == ( + "expected new array length or list/tuple/str, not NoneType") + +def test_pointer_bool(): + BInt = new_primitive_type("int") + BPtr = new_pointer_type(BInt) + p = cast(BPtr, 0) + assert bool(p) is False + p = cast(BPtr, 42) + assert bool(p) is True + +def test_pointer_to_pointer(): + BInt = new_primitive_type("int") + BPtr = new_pointer_type(BInt) + BPtrPtr = new_pointer_type(BPtr) + p = newp(BPtrPtr, None) + assert repr(p) == "<cdata 'int * *' owning %d bytes>" % size_of_ptr() + +def test_reading_pointer_to_int(): + BInt = new_primitive_type("int") + BPtr = new_pointer_type(BInt) + p = newp(BPtr, None) + assert p[0] == 0 + p = newp(BPtr, 5000) + assert p[0] == 5000 + py.test.raises(IndexError, "p[1]") + py.test.raises(IndexError, "p[-1]") + +def test_reading_pointer_to_float(): + BFloat = new_primitive_type("float") + py.test.raises(TypeError, newp, BFloat, None) + BPtr = new_pointer_type(BFloat) + p = newp(BPtr, None) + assert p[0] == 0.0 and type(p[0]) is float + p = newp(BPtr, 1.25) + assert p[0] == 1.25 and type(p[0]) is float + p = newp(BPtr, 1.1) + assert p[0] != 1.1 and abs(p[0] - 1.1) < 1E-5 # rounding errors + +def test_cast_float_to_int(): + for type in ["int", "unsigned int", "long", "unsigned long", + "long long", "unsigned long long"]: + p = new_primitive_type(type) + assert int(cast(p, 4.2)) == 4 + py.test.raises(TypeError, newp, new_pointer_type(p), 4.2) + +def test_newp_integer_types(): + for name in ['signed char', 'short', 'int', 'long', 'long long']: + p = new_primitive_type(name) + pp = new_pointer_type(p) + size = sizeof(p) + min = -(1 << (8*size-1)) + max = (1 << (8*size-1)) - 1 + assert newp(pp, min)[0] == min + assert newp(pp, max)[0] == max + py.test.raises(OverflowError, newp, pp, min - 2 ** 32) + py.test.raises(OverflowError, newp, pp, min - 2 ** 64) + py.test.raises(OverflowError, newp, pp, max + 2 ** 32) + py.test.raises(OverflowError, newp, pp, max + 2 ** 64) + py.test.raises(OverflowError, newp, pp, min - 1) + py.test.raises(OverflowError, newp, pp, max + 1) + py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 32) + py.test.raises(OverflowError, newp, pp, min - 1 - 2 ** 64) + py.test.raises(OverflowError, newp, pp, max + 1) + py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 32) + py.test.raises(OverflowError, newp, pp, max + 1 + 2 ** 64) + py.test.raises(TypeError, newp, pp, 1.0) + for name in ['char', 'short', 'int', 'long', 'long long']: + p = new_primitive_type('unsigned ' + name) + pp = new_pointer_type(p) + size = sizeof(p) + max = (1 << (8*size)) - 1 + assert newp(pp, 0)[0] == 0 + assert newp(pp, max)[0] == max + py.test.raises(OverflowError, newp, pp, -1) + py.test.raises(OverflowError, newp, pp, max + 1) + +def test_reading_pointer_to_char(): + BChar = new_primitive_type("char") + py.test.raises(TypeError, newp, BChar, None) + BPtr = new_pointer_type(BChar) + p = newp(BPtr, None) + assert p[0] == b'\x00' + p = newp(BPtr, b'A') + assert p[0] == b'A' + py.test.raises(TypeError, newp, BPtr, 65) + py.test.raises(TypeError, newp, BPtr, b"foo") + py.test.raises(TypeError, newp, BPtr, u+"foo") + c = cast(BChar, b'A') + assert str(c) == repr(c) + assert int(c) == ord(b'A') + py.test.raises(TypeError, cast, BChar, b'foo') + py.test.raises(TypeError, cast, BChar, u+'foo') + e = py.test.raises(TypeError, newp, new_array_type(BPtr, None), 12.3) + assert str(e.value) == ( + "expected new array length or list/tuple/str, not float") + +def test_reading_pointer_to_pointer(): + BVoidP = new_pointer_type(new_void_type()) + BCharP = new_pointer_type(new_primitive_type("char")) + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + BIntPtrPtr = new_pointer_type(BIntPtr) + q = newp(BIntPtr, 42) + assert q[0] == 42 + p = newp(BIntPtrPtr, None) + assert p[0] is not None + assert p[0] == cast(BVoidP, 0) + assert p[0] == cast(BCharP, 0) + assert p[0] != None + assert repr(p[0]) == "<cdata 'int *' NULL>" + p[0] = q + assert p[0] != cast(BVoidP, 0) + assert p[0] != cast(BCharP, 0) + assert p[0][0] == 42 + q[0] += 1 + assert p[0][0] == 43 + p = newp(BIntPtrPtr, q) + assert p[0][0] == 43 + +def test_load_standard_library(): + if sys.platform == "win32": + py.test.raises(OSError, find_and_load_library, None) + return + x = find_and_load_library(None) + BVoidP = new_pointer_type(new_void_type()) + assert x.load_function(BVoidP, 'strcpy') + py.test.raises(AttributeError, x.load_function, + BVoidP, 'xxx_this_function_does_not_exist') + # the next one is from 'libm', not 'libc', but we assume + # that it is already loaded too, so it should work + assert x.load_function(BVoidP, 'sqrt') + # + x.close_lib() + py.test.raises(ValueError, x.load_function, BVoidP, 'sqrt') + x.close_lib() + +def test_no_len_on_nonarray(): + p = new_primitive_type("int") + py.test.raises(TypeError, len, cast(p, 42)) + +def test_cmp_none(): + p = new_primitive_type("int") + x = cast(p, 42) + assert (x == None) is False + assert (x != None) is True + assert (x == ["hello"]) is False + assert (x != ["hello"]) is True + y = cast(p, 0) + assert (y == None) is False + +def test_invalid_indexing(): + p = new_primitive_type("int") + x = cast(p, 42) + py.test.raises(TypeError, "x[0]") + +def test_default_str(): + BChar = new_primitive_type("char") + x = cast(BChar, 42) + assert str(x) == repr(x) + BInt = new_primitive_type("int") + x = cast(BInt, 42) + assert str(x) == repr(x) + BArray = new_array_type(new_pointer_type(BInt), 10) + x = newp(BArray, None) + assert str(x) == repr(x) + +def test_default_unicode(): + BInt = new_primitive_type("int") + x = cast(BInt, 42) + assert unicode(x) == unicode(repr(x)) + BArray = new_array_type(new_pointer_type(BInt), 10) + x = newp(BArray, None) + assert unicode(x) == unicode(repr(x)) + +def test_cast_from_cdataint(): + BInt = new_primitive_type("int") + x = cast(BInt, 0) + y = cast(new_pointer_type(BInt), x) + assert bool(y) is False + # + x = cast(BInt, 42) + y = cast(BInt, x) + assert int(y) == 42 + y = cast(new_primitive_type("char"), x) + assert int(y) == 42 + y = cast(new_primitive_type("float"), x) + assert float(y) == 42.0 + # + z = cast(BInt, 42.5) + assert int(z) == 42 + z = cast(BInt, y) + assert int(z) == 42 + +def test_void_type(): + p = new_void_type() + assert p.kind == "void" + assert p.cname == "void" + check_dir(p, ['kind', 'cname']) + +def test_array_type(): + p = new_primitive_type("int") + assert repr(p) == "<ctype 'int'>" + # + py.test.raises(TypeError, new_array_type, new_pointer_type(p), "foo") + py.test.raises(ValueError, new_array_type, new_pointer_type(p), -42) + # + p1 = new_array_type(new_pointer_type(p), None) + assert repr(p1) == "<ctype 'int[]'>" + py.test.raises(ValueError, new_array_type, new_pointer_type(p1), 42) + # + p1 = new_array_type(new_pointer_type(p), 42) + p2 = new_array_type(new_pointer_type(p1), 25) + assert repr(p2) == "<ctype 'int[25][42]'>" + p2 = new_array_type(new_pointer_type(p1), None) + assert repr(p2) == "<ctype 'int[][42]'>" + # + py.test.raises(OverflowError, + new_array_type, new_pointer_type(p), sys.maxsize+1) + py.test.raises(OverflowError, + new_array_type, new_pointer_type(p), sys.maxsize // 3) + +def test_inspect_array_type(): + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), None) + assert p1.kind == "array" + assert p1.cname == "int[]" + assert p1.item is p + assert p1.length is None + check_dir(p1, ['cname', 'kind', 'item', 'length']) + p1 = new_array_type(new_pointer_type(p), 42) + assert p1.kind == "array" + assert p1.cname == "int[42]" + assert p1.item is p + assert p1.length == 42 + check_dir(p1, ['cname', 'kind', 'item', 'length']) + +def test_array_instance(): + LENGTH = 1423 + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), LENGTH) + a = newp(p1, None) + assert repr(a) == "<cdata 'int[%d]' owning %d bytes>" % ( + LENGTH, LENGTH * size_of_int()) + assert len(a) == LENGTH + for i in range(LENGTH): + assert a[i] == 0 + py.test.raises(IndexError, "a[LENGTH]") + py.test.raises(IndexError, "a[-1]") + for i in range(LENGTH): + a[i] = i * i + 1 + for i in range(LENGTH): + assert a[i] == i * i + 1 + e = py.test.raises(IndexError, "a[LENGTH+100] = 500") + assert ('(expected %d < %d)' % (LENGTH+100, LENGTH)) in str(e.value) + py.test.raises(TypeError, int, a) + +def test_array_of_unknown_length_instance(): + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), None) + py.test.raises(TypeError, newp, p1, None) + py.test.raises(ValueError, newp, p1, -42) + a = newp(p1, 42) + assert len(a) == 42 + for i in range(42): + a[i] -= i + for i in range(42): + assert a[i] == -i + py.test.raises(IndexError, "a[42]") + py.test.raises(IndexError, "a[-1]") + py.test.raises(IndexError, "a[42] = 123") + py.test.raises(IndexError, "a[-1] = 456") + +def test_array_of_unknown_length_instance_with_initializer(): + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), None) + a = newp(p1, list(range(42))) + assert len(a) == 42 + a = newp(p1, tuple(range(142))) + assert len(a) == 142 + +def test_array_initializer(): + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), None) + a = newp(p1, list(range(100, 142))) + for i in range(42): + assert a[i] == 100 + i + # + p2 = new_array_type(new_pointer_type(p), 43) + a = newp(p2, tuple(range(100, 142))) + for i in range(42): + assert a[i] == 100 + i + assert a[42] == 0 # extra uninitialized item + +def test_array_add(): + p = new_primitive_type("int") + p1 = new_array_type(new_pointer_type(p), 5) # int[5] + p2 = new_array_type(new_pointer_type(p1), 3) # int[3][5] + a = newp(p2, [list(range(n, n+5)) for n in [100, 200, 300]]) + assert repr(a) == "<cdata 'int[3][5]' owning %d bytes>" % ( + 3*5*size_of_int(),) + assert repr(a + 0).startswith("<cdata 'int(*)[5]' 0x") + assert 0 + a == a + 0 != 1 + a == a + 1 + assert repr(a[0]).startswith("<cdata 'int[5]' 0x") + assert repr((a + 0)[0]).startswith("<cdata 'int[5]' 0x") + assert repr(a[0] + 0).startswith("<cdata 'int *' 0x") + assert type(a[0][0]) is int + assert type((a[0] + 0)[0]) is int + +def test_array_sub(): + BInt = new_primitive_type("int") + BArray = new_array_type(new_pointer_type(BInt), 5) # int[5] + a = newp(BArray, None) + p = a + 1 + assert p - a == 1 + assert p - (a+0) == 1 + assert a == (p - 1) + BPtr = new_pointer_type(new_primitive_type("short")) + q = newp(BPtr, None) + py.test.raises(TypeError, "p - q") + py.test.raises(TypeError, "q - p") + py.test.raises(TypeError, "a - q") + e = py.test.raises(TypeError, "q - a") + assert str(e.value) == "cannot subtract cdata 'short *' and cdata 'int *'" + +def test_ptr_sub_unaligned(): + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + a = cast(BIntPtr, 1240) + for bi in range(1430, 1438): + b = cast(BIntPtr, bi) + if ((bi - 1240) % size_of_int()) == 0: + assert b - a == (bi - 1240) // size_of_int() + assert a - b == (1240 - bi) // size_of_int() + else: + py.test.raises(ValueError, "b - a") + py.test.raises(ValueError, "a - b") + +def test_cast_primitive_from_cdata(): + p = new_primitive_type("int") + n = cast(p, cast(p, -42)) + assert int(n) == -42 + # + p = new_primitive_type("unsigned int") + n = cast(p, cast(p, 42)) + assert int(n) == 42 + # + p = new_primitive_type("long long") + n = cast(p, cast(p, -(1<<60))) + assert int(n) == -(1<<60) + # + p = new_primitive_type("unsigned long long") + n = cast(p, cast(p, 1<<63)) + assert int(n) == 1<<63 + # + p = new_primitive_type("float") + n = cast(p, cast(p, 42.5)) + assert float(n) == 42.5 + # + p = new_primitive_type("char") + n = cast(p, cast(p, "A")) + assert int(n) == ord("A") + +def test_new_primitive_from_cdata(): + p = new_primitive_type("int") + p1 = new_pointer_type(p) + n = newp(p1, cast(p, -42)) + assert n[0] == -42 + # + p = new_primitive_type("unsigned int") + p1 = new_pointer_type(p) + n = newp(p1, cast(p, 42)) + assert n[0] == 42 + # + p = new_primitive_type("float") + p1 = new_pointer_type(p) + n = newp(p1, cast(p, 42.5)) + assert n[0] == 42.5 + # + p = new_primitive_type("char") + p1 = new_pointer_type(p) + n = newp(p1, cast(p, "A")) + assert n[0] == b"A" + +def test_cast_between_pointers(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntA = new_array_type(BIntP, None) + a = newp(BIntA, [40, 41, 42, 43, 44]) + BShortP = new_pointer_type(new_primitive_type("short")) + b = cast(BShortP, a) + c = cast(BIntP, b) + assert c[3] == 43 + BLongLong = new_primitive_type("long long") + d = cast(BLongLong, c) + e = cast(BIntP, d) + assert e[3] == 43 + f = cast(BIntP, int(d)) + assert f[3] == 43 + # + b = cast(BShortP, 0) + assert not b + c = cast(BIntP, b) + assert not c + assert int(cast(BLongLong, c)) == 0 + +def test_alignof(): + BInt = new_primitive_type("int") + assert alignof(BInt) == sizeof(BInt) + BPtr = new_pointer_type(BInt) + assert alignof(BPtr) == sizeof(BPtr) + BArray = new_array_type(BPtr, None) + assert alignof(BArray) == alignof(BInt) + +def test_new_struct_type(): + BStruct = new_struct_type("foo") + assert repr(BStruct) == "<ctype 'foo'>" + BStruct = new_struct_type("struct foo") + assert repr(BStruct) == "<ctype 'struct foo'>" + BPtr = new_pointer_type(BStruct) + assert repr(BPtr) == "<ctype 'struct foo *'>" + py.test.raises(ValueError, sizeof, BStruct) + py.test.raises(ValueError, alignof, BStruct) + +def test_new_union_type(): + BUnion = new_union_type("union foo") + assert repr(BUnion) == "<ctype 'union foo'>" + BPtr = new_pointer_type(BUnion) + assert repr(BPtr) == "<ctype 'union foo *'>" + +def test_complete_struct(): + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + assert BStruct.kind == "struct" + assert BStruct.cname == "struct foo" + assert BStruct.fields is None + check_dir(BStruct, ['cname', 'kind', 'fields']) + # + complete_struct_or_union(BStruct, [('a1', BLong, -1), + ('a2', BChar, -1), + ('a3', BShort, -1)]) + d = BStruct.fields + assert len(d) == 3 + assert d[0][0] == 'a1' + assert d[0][1].type is BLong + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == sizeof(BLong) + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert d[2][0] == 'a3' + assert d[2][1].type is BShort + assert d[2][1].offset == sizeof(BLong) + sizeof(BShort) + assert d[2][1].bitshift == -1 + assert d[2][1].bitsize == -1 + assert sizeof(BStruct) == 2 * sizeof(BLong) + assert alignof(BStruct) == alignof(BLong) + +def test_complete_union(): + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BUnion = new_union_type("union foo") + assert BUnion.kind == "union" + assert BUnion.cname == "union foo" + assert BUnion.fields is None + complete_struct_or_union(BUnion, [('a1', BLong, -1), + ('a2', BChar, -1)]) + d = BUnion.fields + assert len(d) == 2 + assert d[0][0] == 'a1' + assert d[0][1].type is BLong + assert d[0][1].offset == 0 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == 0 + assert sizeof(BUnion) == sizeof(BLong) + assert alignof(BUnion) == alignof(BLong) + +def test_struct_instance(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + p = cast(BStructPtr, 42) + e = py.test.raises(AttributeError, "p.a1") # opaque + assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " + "cannot read fields") + e = py.test.raises(AttributeError, "p.a1 = 10") # opaque + assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: " + "cannot write fields") + + complete_struct_or_union(BStruct, [('a1', BInt, -1), + ('a2', BInt, -1)]) + p = newp(BStructPtr, None) + s = p[0] + assert s.a1 == 0 + s.a2 = 123 + assert s.a1 == 0 + assert s.a2 == 123 + py.test.raises(OverflowError, "s.a1 = sys.maxsize+1") + assert s.a1 == 0 + e = py.test.raises(AttributeError, "p.foobar") + assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" + e = py.test.raises(AttributeError, "p.foobar = 42") + assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'" + e = py.test.raises(AttributeError, "s.foobar") + assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" + e = py.test.raises(AttributeError, "s.foobar = 42") + assert str(e.value) == "cdata 'struct foo' has no field 'foobar'" + j = cast(BInt, 42) + e = py.test.raises(AttributeError, "j.foobar") + assert str(e.value) == "cdata 'int' has no attribute 'foobar'" + e = py.test.raises(AttributeError, "j.foobar = 42") + assert str(e.value) == "cdata 'int' has no attribute 'foobar'" + j = cast(new_pointer_type(BInt), 42) + e = py.test.raises(AttributeError, "j.foobar") + assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" + e = py.test.raises(AttributeError, "j.foobar = 42") + assert str(e.value) == "cdata 'int *' has no attribute 'foobar'" + pp = newp(new_pointer_type(BStructPtr), p) + e = py.test.raises(AttributeError, "pp.a1") + assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" + e = py.test.raises(AttributeError, "pp.a1 = 42") + assert str(e.value) == "cdata 'struct foo * *' has no attribute 'a1'" + +def test_union_instance(): + BInt = new_primitive_type("int") + BUInt = new_primitive_type("unsigned int") + BUnion = new_union_type("union bar") + complete_struct_or_union(BUnion, [('a1', BInt, -1), ('a2', BUInt, -1)]) + p = newp(new_pointer_type(BUnion), [-42]) + bigval = -42 + (1 << (8*size_of_int())) + assert p.a1 == -42 + assert p.a2 == bigval + p = newp(new_pointer_type(BUnion), {'a2': bigval}) + assert p.a1 == -42 + assert p.a2 == bigval + py.test.raises(OverflowError, newp, new_pointer_type(BUnion), + {'a1': bigval}) + p = newp(new_pointer_type(BUnion), []) + assert p.a1 == p.a2 == 0 + +def test_struct_pointer(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BInt, -1), + ('a2', BInt, -1)]) + p = newp(BStructPtr, None) + assert p.a1 == 0 # read/write via the pointer (C equivalent: '->') + p.a2 = 123 + assert p.a1 == 0 + assert p.a2 == 123 + +def test_struct_init_list(): + BVoidP = new_pointer_type(new_void_type()) + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BInt, -1), + ('a2', BInt, -1), + ('a3', BInt, -1), + ('p4', BIntPtr, -1)]) + s = newp(BStructPtr, [123, 456]) + assert s.a1 == 123 + assert s.a2 == 456 + assert s.a3 == 0 + assert s.p4 == cast(BVoidP, 0) + assert s.p4 != 0 + # + s = newp(BStructPtr, {'a2': 41122, 'a3': -123}) + assert s.a1 == 0 + assert s.a2 == 41122 + assert s.a3 == -123 + assert s.p4 == cast(BVoidP, 0) + # + py.test.raises(KeyError, newp, BStructPtr, {'foobar': 0}) + # + p = newp(BIntPtr, 14141) + s = newp(BStructPtr, [12, 34, 56, p]) + assert s.p4 == p + assert s.p4 + # + s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)]) + assert s.p4 == cast(BVoidP, 0) + assert not s.p4 + # + py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None]) + +def test_array_in_struct(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BArrayInt5 = new_array_type(new_pointer_type(BInt), 5) + complete_struct_or_union(BStruct, [('a1', BArrayInt5, -1)]) + s = newp(new_pointer_type(BStruct), [[20, 24, 27, 29, 30]]) + assert s.a1[2] == 27 + assert repr(s.a1).startswith("<cdata 'int[5]' 0x") + +def test_offsetof(): + def offsetof(BType, fieldname): + return typeoffsetof(BType, fieldname)[1] + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + py.test.raises(TypeError, offsetof, BInt, "abc") + py.test.raises(TypeError, offsetof, BStruct, "abc") + complete_struct_or_union(BStruct, [('abc', BInt, -1), ('def', BInt, -1)]) + assert offsetof(BStruct, 'abc') == 0 + assert offsetof(BStruct, 'def') == size_of_int() + py.test.raises(KeyError, offsetof, BStruct, "ghi") + assert offsetof(new_pointer_type(BStruct), "def") == size_of_int() + +def test_function_type(): + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False) + assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" + BFunc2 = new_function_type((), BFunc, False) + assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>" + +def test_inspect_function_type(): + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False) + assert BFunc.kind == "function" + assert BFunc.cname == "int(*)(int, int)" + assert BFunc.args == (BInt, BInt) + assert BFunc.result is BInt + assert BFunc.ellipsis is False + assert BFunc.abi == FFI_DEFAULT_ABI + +def test_function_type_taking_struct(): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BShort, -1)]) + BFunc = new_function_type((BStruct,), BShort, False) + assert repr(BFunc) == "<ctype 'short(*)(struct foo)'>" + +def test_function_void_result(): + BVoid = new_void_type() + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BVoid, False) + assert repr(BFunc) == "<ctype 'void(*)(int, int)'>" + +def test_function_void_arg(): + BVoid = new_void_type() + BInt = new_primitive_type("int") + py.test.raises(TypeError, new_function_type, (BVoid,), BInt, False) + +def test_call_function_0(): + BSignedChar = new_primitive_type("signed char") + BFunc0 = new_function_type((BSignedChar, BSignedChar), BSignedChar, False) + f = cast(BFunc0, _testfunc(0)) + assert f(40, 2) == 42 + assert f(-100, -100) == -200 + 256 + py.test.raises(OverflowError, f, 128, 0) + py.test.raises(OverflowError, f, 0, 128) + +def test_call_function_0_pretend_bool_result(): + BSignedChar = new_primitive_type("signed char") + BBool = new_primitive_type("_Bool") + BFunc0 = new_function_type((BSignedChar, BSignedChar), BBool, False) + f = cast(BFunc0, _testfunc(0)) + assert f(40, -39) is True + assert f(40, -40) is False + py.test.raises(ValueError, f, 40, 2) + +def test_call_function_1(): + BInt = new_primitive_type("int") + BLong = new_primitive_type("long") + BFunc1 = new_function_type((BInt, BLong), BLong, False) + f = cast(BFunc1, _testfunc(1)) + assert f(40, 2) == 42 + assert f(-100, -100) == -200 + int_max = (1 << (8*size_of_int()-1)) - 1 + long_max = (1 << (8*size_of_long()-1)) - 1 + if int_max == long_max: + assert f(int_max, 1) == - int_max - 1 + else: + assert f(int_max, 1) == int_max + 1 + +def test_call_function_2(): + BLongLong = new_primitive_type("long long") + BFunc2 = new_function_type((BLongLong, BLongLong), BLongLong, False) + f = cast(BFunc2, _testfunc(2)) + longlong_max = (1 << (8*sizeof(BLongLong)-1)) - 1 + assert f(longlong_max - 42, 42) == longlong_max + assert f(43, longlong_max - 42) == - longlong_max - 1 + +def test_call_function_3(): + BFloat = new_primitive_type("float") + BDouble = new_primitive_type("double") + BFunc3 = new_function_type((BFloat, BDouble), BDouble, False) + f = cast(BFunc3, _testfunc(3)) + assert f(1.25, 5.1) == 1.25 + 5.1 # exact + res = f(1.3, 5.1) + assert res != 6.4 and abs(res - 6.4) < 1E-5 # inexact + +def test_call_function_4(): + BFloat = new_primitive_type("float") + BDouble = new_primitive_type("double") + BFunc4 = new_function_type((BFloat, BDouble), BFloat, False) + f = cast(BFunc4, _testfunc(4)) + res = f(1.25, 5.1) + assert res != 6.35 and abs(res - 6.35) < 1E-5 # inexact + +def test_call_function_5(): + BVoid = new_void_type() + BFunc5 = new_function_type((), BVoid, False) + f = cast(BFunc5, _testfunc(5)) + f() # did not crash + +def test_call_function_6(): + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + BFunc6 = new_function_type((BIntPtr,), BIntPtr, False) + f = cast(BFunc6, _testfunc(6)) + x = newp(BIntPtr, 42) + res = f(x) + assert typeof(res) is BIntPtr + assert res[0] == 42 - 1000 + # + BIntArray = new_array_type(BIntPtr, None) + BFunc6bis = new_function_type((BIntArray,), BIntPtr, False) + f = cast(BFunc6bis, _testfunc(6)) + # + res = f([142]) + assert typeof(res) is BIntPtr + assert res[0] == 142 - 1000 + # + res = f((143,)) + assert typeof(res) is BIntPtr + assert res[0] == 143 - 1000 + # + x = newp(BIntArray, [242]) + res = f(x) + assert typeof(res) is BIntPtr + assert res[0] == 242 - 1000 + # + py.test.raises(TypeError, f, 123456) + py.test.raises(TypeError, f, "foo") + py.test.raises(TypeError, f, u+"bar") + +def test_call_function_7(): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BShort, -1)]) + BFunc7 = new_function_type((BStruct,), BShort, False) + f = cast(BFunc7, _testfunc(7)) + res = f({'a1': b'A', 'a2': -4042}) + assert res == -4042 + ord(b'A') + # + x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) + res = f(x[0]) + assert res == -4042 + ord(b'A') + +def test_call_function_20(): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BShort, -1)]) + BFunc20 = new_function_type((BStructPtr,), BShort, False) + f = cast(BFunc20, _testfunc(20)) + x = newp(BStructPtr, {'a1': b'A', 'a2': -4042}) + # can't pass a 'struct foo' + py.test.raises(TypeError, f, x[0]) + +def test_call_function_21(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a', BInt, -1), + ('b', BInt, -1), + ('c', BInt, -1), + ('d', BInt, -1), + ('e', BInt, -1), + ('f', BInt, -1), + ('g', BInt, -1), + ('h', BInt, -1), + ('i', BInt, -1), + ('j', BInt, -1)]) + BFunc21 = new_function_type((BStruct,), BInt, False) + f = cast(BFunc21, _testfunc(21)) + res = f(list(range(13, 3, -1))) + lst = [(n << i) for (i, n) in enumerate(range(13, 3, -1))] + assert res == sum(lst) + +def test_call_function_22(): + BInt = new_primitive_type("int") + BArray10 = new_array_type(new_pointer_type(BInt), 10) + BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BArray10, -1)]) + BFunc22 = new_function_type((BStruct, BStruct), BStruct, False) + f = cast(BFunc22, _testfunc(22)) + p1 = newp(BStructP, {'a': list(range(100, 110))}) + p2 = newp(BStructP, {'a': list(range(1000, 1100, 10))}) + res = f(p1[0], p2[0]) + for i in range(10): + assert res.a[i] == p1.a[i] - p2.a[i] + +def test_call_function_23(): + BVoid = new_void_type() # declaring the function as int(void*) + BVoidP = new_pointer_type(BVoid) + BInt = new_primitive_type("int") + BFunc23 = new_function_type((BVoidP,), BInt, False) + f = cast(BFunc23, _testfunc(23)) + res = f(b"foo") + assert res == 1000 * ord(b'f') + res = f(cast(BVoidP, 0)) # NULL + assert res == -42 + py.test.raises(TypeError, f, None) + py.test.raises(TypeError, f, 0) + py.test.raises(TypeError, f, 0.0) + +def test_call_function_23_bis(): + # declaring the function as int(unsigned char*) + BUChar = new_primitive_type("unsigned char") + BUCharP = new_pointer_type(BUChar) + BInt = new_primitive_type("int") + BFunc23 = new_function_type((BUCharP,), BInt, False) + f = cast(BFunc23, _testfunc(23)) + res = f(b"foo") + assert res == 1000 * ord(b'f') + +def test_call_function_23_bool_array(): + # declaring the function as int(_Bool*) + BBool = new_primitive_type("_Bool") + BBoolP = new_pointer_type(BBool) + BInt = new_primitive_type("int") + BFunc23 = new_function_type((BBoolP,), BInt, False) + f = cast(BFunc23, _testfunc(23)) + res = f(b"\x01\x01") + assert res == 1000 + py.test.raises(ValueError, f, b"\x02\x02") + +def test_cannot_pass_struct_with_array_of_length_0(): + BInt = new_primitive_type("int") + BArray0 = new_array_type(new_pointer_type(BInt), 0) + BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BArray0)]) + BFunc = new_function_type((BStruct,), BInt, False) + py.test.raises(NotImplementedError, cast(BFunc, 123), cast(BStructP, 123)) + BFunc2 = new_function_type((BInt,), BStruct, False) + py.test.raises(NotImplementedError, cast(BFunc2, 123), 123) + +def test_call_function_9(): + BInt = new_primitive_type("int") + BFunc9 = new_function_type((BInt,), BInt, True) # vararg + f = cast(BFunc9, _testfunc(9)) + assert f(0) == 0 + assert f(1, cast(BInt, 42)) == 42 + assert f(2, cast(BInt, 40), cast(BInt, 2)) == 42 + py.test.raises(TypeError, f, 1, 42) + py.test.raises(TypeError, f, 2, None) + # promotion of chars and shorts to ints + BSChar = new_primitive_type("signed char") + BUChar = new_primitive_type("unsigned char") + BSShort = new_primitive_type("short") + assert f(3, cast(BSChar, -3), cast(BUChar, 200), cast(BSShort, -5)) == 192 + +def test_call_function_24(): + BFloat = new_primitive_type("float") + BFloatComplex = new_primitive_type("float _Complex") + BFunc3 = new_function_type((BFloat, BFloat), BFloatComplex, False) + if 0: # libffi returning nonsense silently, so logic disabled for now + f = cast(BFunc3, _testfunc(24)) + result = f(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact + else: + f = cast(BFunc3, _testfunc(9)) + py.test.raises(NotImplementedError, f, 12.3, 34.5) + +def test_call_function_25(): + BDouble = new_primitive_type("double") + BDoubleComplex = new_primitive_type("double _Complex") + BFunc3 = new_function_type((BDouble, BDouble), BDoubleComplex, False) + if 0: # libffi returning nonsense silently, so logic disabled for now + f = cast(BFunc3, _testfunc(25)) + result = f(1.25, 5.1) + assert type(result) == complex + assert result.real == 1.25 # exact + assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-10) # inexact + else: + f = cast(BFunc3, _testfunc(9)) + py.test.raises(NotImplementedError, f, 12.3, 34.5) + +def test_cannot_call_with_a_autocompleted_struct(): + BSChar = new_primitive_type("signed char") + BDouble = new_primitive_type("double") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('c', BDouble, -1, 8), + ('a', BSChar, -1, 2), + ('b', BSChar, -1, 0)]) + BFunc = new_function_type((BStruct,), BDouble) # internally not callable + dummy_func = cast(BFunc, 42) + e = py.test.raises(NotImplementedError, dummy_func, "?") + msg = ("ctype 'struct foo' not supported as argument. It is a struct " + 'declared with "...;", but the C calling convention may depend ' + "on the missing fields; or, it contains anonymous struct/unions. " + "Such structs are only supported as argument if the function is " + "'API mode' and non-variadic (i.e. declared inside ffibuilder." + "cdef()+ffibuilder.set_source() and not taking a final '...' " + "argument)") + assert str(e.value) == msg + +def test_new_charp(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + x = newp(BCharA, 42) + assert len(x) == 42 + x = newp(BCharA, b"foobar") + assert len(x) == 7 + +def test_load_and_call_function(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BLong = new_primitive_type("long") + BFunc = new_function_type((BCharP,), BLong, False) + ll = find_and_load_library('c') + strlen = ll.load_function(BFunc, "strlen") + input = newp(new_array_type(BCharP, None), b"foobar") + assert strlen(input) == 6 + # + assert strlen(b"foobarbaz") == 9 + # + BVoidP = new_pointer_type(new_void_type()) + strlenaddr = ll.load_function(BVoidP, "strlen") + assert strlenaddr == cast(BVoidP, strlen) + +def test_read_variable(): + ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard + ## https://bugs.pypy.org/issue1643 + if not sys.platform.startswith("linux"): + py.test.skip("untested") + BVoidP = new_pointer_type(new_void_type()) + ll = find_and_load_library('c') + stderr = ll.read_variable(BVoidP, "stderr") + assert stderr == cast(BVoidP, _testfunc(8)) + # + ll.close_lib() + py.test.raises(ValueError, ll.read_variable, BVoidP, "stderr") + +def test_read_variable_as_unknown_length_array(): + ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard + ## https://bugs.pypy.org/issue1643 + if not sys.platform.startswith("linux"): + py.test.skip("untested") + BCharP = new_pointer_type(new_primitive_type("char")) + BArray = new_array_type(BCharP, None) + ll = find_and_load_library('c') + stderr = ll.read_variable(BArray, "stderr") + assert repr(stderr).startswith("<cdata 'char *' 0x") + # ^^ and not 'char[]', which is basically not allowed and would crash + +def test_write_variable(): + ## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard + ## https://bugs.pypy.org/issue1643 + if not sys.platform.startswith("linux"): + py.test.skip("untested") + BVoidP = new_pointer_type(new_void_type()) + ll = find_and_load_library('c') + stderr = ll.read_variable(BVoidP, "stderr") + ll.write_variable(BVoidP, "stderr", cast(BVoidP, 0)) + assert ll.read_variable(BVoidP, "stderr") is not None + assert not ll.read_variable(BVoidP, "stderr") + ll.write_variable(BVoidP, "stderr", stderr) + assert ll.read_variable(BVoidP, "stderr") == stderr + # + ll.close_lib() + py.test.raises(ValueError, ll.write_variable, BVoidP, "stderr", stderr) + +def test_callback(): + BInt = new_primitive_type("int") + def make_callback(): + def cb(n): + return n + 1 + BFunc = new_function_type((BInt,), BInt, False) + return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope + f = make_callback() + assert f(-142) == -141 + assert repr(f).startswith( + "<cdata 'int(*)(int)' calling <function ") + assert "cb at 0x" in repr(f) + e = py.test.raises(TypeError, f) + assert str(e.value) == "'int(*)(int)' expects 1 arguments, got 0" + +def test_callback_exception(): + try: + import cStringIO + except ImportError: + import io as cStringIO # Python 3 + import linecache + def matches(istr, ipattern): + str, pattern = istr, ipattern + while '$' in pattern: + i = pattern.index('$') + assert str[:i] == pattern[:i] + j = str.find(pattern[i+1], i) + assert i + 1 <= j <= str.find('\n', i) + str = str[j:] + pattern = pattern[i+1:] + assert str == pattern + return True + def check_value(x): + if x == 10000: + raise ValueError(42) + def Zcb1(x): + check_value(x) + return x * 3 + BShort = new_primitive_type("short") + BFunc = new_function_type((BShort,), BShort, False) + f = callback(BFunc, Zcb1, -42) + # + seen = [] + oops_result = None + def oops(*args): + seen.append(args) + return oops_result + ff = callback(BFunc, Zcb1, -42, oops) + # + orig_stderr = sys.stderr + orig_getline = linecache.getline + try: + linecache.getline = lambda *args: 'LINE' # hack: speed up PyPy tests + sys.stderr = cStringIO.StringIO() + assert f(100) == 300 + assert sys.stderr.getvalue() == '' + assert f(10000) == -42 + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Traceback (most recent call last): + File "$", line $, in Zcb1 + $ + File "$", line $, in check_value + $ +ValueError: 42 +""") + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + assert f(bigvalue) == -42 + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Trying to convert the result back to C: +OverflowError: integer 60000 does not fit 'short' +""") + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + assert len(seen) == 0 + assert ff(bigvalue) == -42 + assert sys.stderr.getvalue() == "" + assert len(seen) == 1 + exc, val, tb = seen[0] + assert exc is OverflowError + assert str(val) == "integer 60000 does not fit 'short'" + # + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + del seen[:] + oops_result = 81 + assert ff(bigvalue) == 81 + oops_result = None + assert sys.stderr.getvalue() == "" + assert len(seen) == 1 + exc, val, tb = seen[0] + assert exc is OverflowError + assert str(val) == "integer 60000 does not fit 'short'" + # + sys.stderr = cStringIO.StringIO() + bigvalue = 20000 + del seen[:] + oops_result = "xy" # not None and not an int! + assert ff(bigvalue) == -42 + oops_result = None + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Trying to convert the result back to C: +OverflowError: integer 60000 does not fit 'short' + +During the call to 'onerror', another exception occurred: + +TypeError: $integer$ +""") + # + sys.stderr = cStringIO.StringIO() + seen = "not a list" # this makes the oops() function crash + assert ff(bigvalue) == -42 + assert matches(sys.stderr.getvalue(), """\ +From cffi callback <function$Zcb1 at 0x$>: +Trying to convert the result back to C: +OverflowError: integer 60000 does not fit 'short' + +During the call to 'onerror', another exception occurred: + +Traceback (most recent call last): + File "$", line $, in oops + $ +AttributeError: 'str' object has no attribute 'append' +""") + finally: + sys.stderr = orig_stderr + linecache.getline = orig_getline + +def test_callback_return_type(): + for rettype in ["signed char", "short", "int", "long", "long long", + "unsigned char", "unsigned short", "unsigned int", + "unsigned long", "unsigned long long"]: + BRet = new_primitive_type(rettype) + def cb(n): + return n + 1 + BFunc = new_function_type((BRet,), BRet) + f = callback(BFunc, cb, 42) + assert f(41) == 42 + if rettype.startswith("unsigned "): + min = 0 + max = (1 << (8*sizeof(BRet))) - 1 + else: + min = -(1 << (8*sizeof(BRet)-1)) + max = (1 << (8*sizeof(BRet)-1)) - 1 + assert f(min) == min + 1 + assert f(max - 1) == max + assert f(max) == 42 + +def test_a_lot_of_callbacks(): + BIGNUM = 10000 + if 'PY_DOT_PY' in globals(): BIGNUM = 100 # tests on py.py + # + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt,), BInt, False) + def make_callback(m): + def cb(n): + return n + m + return callback(BFunc, cb, 42) # 'cb' and 'BFunc' go out of scope + # + flist = [make_callback(i) for i in range(BIGNUM)] + for i, f in enumerate(flist): + assert f(-142) == -142 + i + +def test_callback_receiving_tiny_struct(): + BSChar = new_primitive_type("signed char") + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BSChar, -1), + ('b', BSChar, -1)]) + def cb(s): + return s.a + 10 * s.b + BFunc = new_function_type((BStruct,), BInt) + f = callback(BFunc, cb) + p = newp(BStructPtr, [-2, -4]) + n = f(p[0]) + assert n == -42 + +def test_callback_returning_tiny_struct(): + BSChar = new_primitive_type("signed char") + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BSChar, -1), + ('b', BSChar, -1)]) + def cb(n): + return newp(BStructPtr, [-n, -3*n])[0] + BFunc = new_function_type((BInt,), BStruct) + f = callback(BFunc, cb) + s = f(10) + assert typeof(s) is BStruct + assert repr(s) == "<cdata 'struct foo' owning 2 bytes>" + assert s.a == -10 + assert s.b == -30 + +def test_callback_receiving_struct(): + BSChar = new_primitive_type("signed char") + BInt = new_primitive_type("int") + BDouble = new_primitive_type("double") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BSChar, -1), + ('b', BDouble, -1)]) + def cb(s): + return s.a + int(s.b) + BFunc = new_function_type((BStruct,), BInt) + f = callback(BFunc, cb) + p = newp(BStructPtr, [-2, 44.444]) + n = f(p[0]) + assert n == 42 + +def test_callback_returning_struct(): + BSChar = new_primitive_type("signed char") + BInt = new_primitive_type("int") + BDouble = new_primitive_type("double") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BSChar, -1), + ('b', BDouble, -1)]) + def cb(n): + return newp(BStructPtr, [-n, 1E-42])[0] + BFunc = new_function_type((BInt,), BStruct) + f = callback(BFunc, cb) + s = f(10) + assert typeof(s) is BStruct + assert repr(s) in ["<cdata 'struct foo' owning 12 bytes>", + "<cdata 'struct foo' owning 16 bytes>"] + assert s.a == -10 + assert s.b == 1E-42 + +def test_callback_receiving_big_struct(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BInt, -1), + ('b', BInt, -1), + ('c', BInt, -1), + ('d', BInt, -1), + ('e', BInt, -1), + ('f', BInt, -1), + ('g', BInt, -1), + ('h', BInt, -1), + ('i', BInt, -1), + ('j', BInt, -1)]) + def cb(s): + for i, name in enumerate("abcdefghij"): + assert getattr(s, name) == 13 - i + return 42 + BFunc = new_function_type((BStruct,), BInt) + f = callback(BFunc, cb) + p = newp(BStructPtr, list(range(13, 3, -1))) + n = f(p[0]) + assert n == 42 + +def test_callback_returning_big_struct(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a', BInt, -1), + ('b', BInt, -1), + ('c', BInt, -1), + ('d', BInt, -1), + ('e', BInt, -1), + ('f', BInt, -1), + ('g', BInt, -1), + ('h', BInt, -1), + ('i', BInt, -1), + ('j', BInt, -1)]) + def cb(): + return newp(BStructPtr, list(range(13, 3, -1)))[0] + BFunc = new_function_type((), BStruct) + f = callback(BFunc, cb) + s = f() + assert typeof(s) is BStruct + assert repr(s) in ["<cdata 'struct foo' owning 40 bytes>", + "<cdata 'struct foo' owning 80 bytes>"] + for i, name in enumerate("abcdefghij"): + assert getattr(s, name) == 13 - i + +def test_callback_returning_void(): + BVoid = new_void_type() + BFunc = new_function_type((), BVoid, False) + def cb(): + seen.append(42) + f = callback(BFunc, cb) + seen = [] + f() + assert seen == [42] + py.test.raises(TypeError, callback, BFunc, cb, -42) + +def test_enum_type(): + BUInt = new_primitive_type("unsigned int") + BEnum = new_enum_type("foo", (), (), BUInt) + assert repr(BEnum) == "<ctype 'foo'>" + assert BEnum.kind == "enum" + assert BEnum.cname == "foo" + assert BEnum.elements == {} + # + BInt = new_primitive_type("int") + BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) + assert BEnum.kind == "enum" + assert BEnum.cname == "enum foo" + assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} + # 'elements' is not the real dict, but merely a copy + BEnum.elements[2] = '??' + assert BEnum.elements == {-20: 'ab', 0: 'def', 1: 'c'} + # + BEnum = new_enum_type("enum bar", ('ab', 'cd'), (5, 5), BUInt) + assert BEnum.elements == {5: 'ab'} + assert BEnum.relements == {'ab': 5, 'cd': 5} + +def test_cast_to_enum(): + BInt = new_primitive_type("int") + BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) + assert sizeof(BEnum) == sizeof(BInt) + e = cast(BEnum, 0) + assert repr(e) == "<cdata 'enum foo' 0: def>" + assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>" + assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>" + assert string(e) == 'def' + assert string(cast(BEnum, -20)) == 'ab' + assert int(cast(BEnum, 1)) == 1 + assert int(cast(BEnum, 0)) == 0 + assert int(cast(BEnum, -242 + 2**128)) == -242 + assert string(cast(BEnum, -242 + 2**128)) == '-242' + # + BUInt = new_primitive_type("unsigned int") + BEnum = new_enum_type("enum bar", ('def', 'c', 'ab'), (0, 1, 20), BUInt) + e = cast(BEnum, -1) + assert repr(e) == "<cdata 'enum bar' 4294967295>" # unsigned int + # + BLong = new_primitive_type("long") + BEnum = new_enum_type("enum baz", (), (), BLong) + assert sizeof(BEnum) == sizeof(BLong) + e = cast(BEnum, -1) + assert repr(e) == "<cdata 'enum baz' -1>" + +def test_enum_with_non_injective_mapping(): + BInt = new_primitive_type("int") + BEnum = new_enum_type("enum foo", ('ab', 'cd'), (7, 7), BInt) + e = cast(BEnum, 7) + assert repr(e) == "<cdata 'enum foo' 7: ab>" + assert string(e) == 'ab' + +def test_enum_in_struct(): + BInt = new_primitive_type("int") + BEnum = new_enum_type("enum foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) + BStruct = new_struct_type("struct bar") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BEnum, -1)]) + p = newp(BStructPtr, [-20]) + assert p.a1 == -20 + p = newp(BStructPtr, [12]) + assert p.a1 == 12 + e = py.test.raises(TypeError, newp, BStructPtr, [None]) + msg = str(e.value) + assert ("an integer is required" in msg or # CPython + "unsupported operand type for int(): 'NoneType'" in msg or # old PyPys + "expected integer, got NoneType object" in msg) # newer PyPys + py.test.raises(TypeError, 'p.a1 = "def"') + if sys.version_info < (3,): + BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,), BInt) + assert string(cast(BEnum2, 5)) == 'abc' + assert type(string(cast(BEnum2, 5))) is str + +def test_enum_overflow(): + max_uint = 2 ** (size_of_int()*8) - 1 + max_int = max_uint // 2 + max_ulong = 2 ** (size_of_long()*8) - 1 + max_long = max_ulong // 2 + for BPrimitive in [new_primitive_type("int"), + new_primitive_type("unsigned int"), + new_primitive_type("long"), + new_primitive_type("unsigned long")]: + for x in [max_uint, max_int, max_ulong, max_long]: + for testcase in [x, x+1, -x-1, -x-2]: + if int(cast(BPrimitive, testcase)) == testcase: + # fits + BEnum = new_enum_type("foo", ("AA",), (testcase,), + BPrimitive) + assert int(cast(BEnum, testcase)) == testcase + else: + # overflows + py.test.raises(OverflowError, new_enum_type, + "foo", ("AA",), (testcase,), BPrimitive) + +def test_callback_returning_enum(): + BInt = new_primitive_type("int") + BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20), BInt) + def cb(n): + if n & 1: + return cast(BEnum, n) + else: + return n + BFunc = new_function_type((BInt,), BEnum) + f = callback(BFunc, cb) + assert f(0) == 0 + assert f(1) == 1 + assert f(-20) == -20 + assert f(20) == 20 + assert f(21) == 21 + +def test_callback_returning_enum_unsigned(): + BInt = new_primitive_type("int") + BUInt = new_primitive_type("unsigned int") + BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20), BUInt) + def cb(n): + if n & 1: + return cast(BEnum, n) + else: + return n + BFunc = new_function_type((BInt,), BEnum) + f = callback(BFunc, cb) + assert f(0) == 0 + assert f(1) == 1 + assert f(-21) == 2**32 - 21 + assert f(20) == 20 + assert f(21) == 21 + +def test_callback_returning_char(): + BInt = new_primitive_type("int") + BChar = new_primitive_type("char") + def cb(n): + return bytechr(n) + BFunc = new_function_type((BInt,), BChar) + f = callback(BFunc, cb) + assert f(0) == b'\x00' + assert f(255) == b'\xFF' + +def _hacked_pypy_uni4(): + pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] + return 'PY_DOT_PY' in globals() and not pyuni4 + +def test_callback_returning_wchar_t(): + BInt = new_primitive_type("int") + BWChar = new_primitive_type("wchar_t") + def cb(n): + if n == -1: + return u+'\U00012345' + if n == -2: + raise ValueError + return unichr(n) + BFunc = new_function_type((BInt,), BWChar) + f = callback(BFunc, cb) + assert f(0) == unichr(0) + assert f(255) == unichr(255) + assert f(0x1234) == u+'\u1234' + if sizeof(BWChar) == 4 and not _hacked_pypy_uni4(): + assert f(-1) == u+'\U00012345' + assert f(-2) == u+'\x00' # and an exception printed to stderr + +def test_struct_with_bitfields(): + BLong = new_primitive_type("long") + BStruct = new_struct_type("struct foo") + LONGBITS = 8 * sizeof(BLong) + complete_struct_or_union(BStruct, [('a1', BLong, 1), + ('a2', BLong, 2), + ('a3', BLong, 3), + ('a4', BLong, LONGBITS - 5)]) + d = BStruct.fields + assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0 + assert d[3][1].offset == sizeof(BLong) + def f(m, r): + if sys.byteorder == 'little': + return r + else: + return LONGBITS - m - r + assert d[0][1].bitshift == f(1, 0) + assert d[0][1].bitsize == 1 + assert d[1][1].bitshift == f(2, 1) + assert d[1][1].bitsize == 2 + assert d[2][1].bitshift == f(3, 3) + assert d[2][1].bitsize == 3 + assert d[3][1].bitshift == f(LONGBITS - 5, 0) + assert d[3][1].bitsize == LONGBITS - 5 + assert sizeof(BStruct) == 2 * sizeof(BLong) + assert alignof(BStruct) == alignof(BLong) + +def test_bitfield_instance(): + BInt = new_primitive_type("int") + BUnsignedInt = new_primitive_type("unsigned int") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BInt, 1), + ('a2', BUnsignedInt, 2), + ('a3', BInt, 3)]) + p = newp(new_pointer_type(BStruct), None) + p.a1 = -1 + assert p.a1 == -1 + p.a1 = 0 + py.test.raises(OverflowError, "p.a1 = 2") + assert p.a1 == 0 + # + p.a1 = -1 + p.a2 = 3 + p.a3 = -4 + py.test.raises(OverflowError, "p.a3 = 4") + e = py.test.raises(OverflowError, "p.a3 = -5") + assert str(e.value) == ("value -5 outside the range allowed by the " + "bit field width: -4 <= x <= 3") + assert p.a1 == -1 and p.a2 == 3 and p.a3 == -4 + # + # special case for convenience: "int x:1", while normally signed, + # allows also setting the value "1" (it still gets read back as -1) + p.a1 = 1 + assert p.a1 == -1 + e = py.test.raises(OverflowError, "p.a1 = -2") + assert str(e.value) == ("value -2 outside the range allowed by the " + "bit field width: -1 <= x <= 1") + +def test_bitfield_instance_init(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BInt, 1)]) + p = newp(new_pointer_type(BStruct), [-1]) + assert p.a1 == -1 + p = newp(new_pointer_type(BStruct), {'a1': -1}) + assert p.a1 == -1 + # + BUnion = new_union_type("union bar") + complete_struct_or_union(BUnion, [('a1', BInt, 1)]) + p = newp(new_pointer_type(BUnion), [-1]) + assert p.a1 == -1 + +def test_weakref(): + import _weakref + BInt = new_primitive_type("int") + BPtr = new_pointer_type(BInt) + rlist = [_weakref.ref(BInt), + _weakref.ref(newp(BPtr, 42)), + _weakref.ref(cast(BPtr, 42)), + _weakref.ref(cast(BInt, 42)), + _weakref.ref(buffer(newp(BPtr, 42))), + ] + for i in range(5): + import gc; gc.collect() + if [r() for r in rlist] == [None for r in rlist]: + break + +def test_no_inheritance(): + BInt = new_primitive_type("int") + try: + class foo(type(BInt)): pass + except TypeError: + pass + else: + raise AssertionError + x = cast(BInt, 42) + try: + class foo(type(x)): pass + except TypeError: + pass + else: + raise AssertionError + +def test_assign_string(): + BChar = new_primitive_type("char") + BArray1 = new_array_type(new_pointer_type(BChar), 5) + BArray2 = new_array_type(new_pointer_type(BArray1), 5) + a = newp(BArray2, [b"abc", b"de", b"ghij"]) + assert string(a[1]) == b"de" + assert string(a[2]) == b"ghij" + a[2] = b"." + assert string(a[2]) == b"." + a[2] = b"12345" + assert string(a[2]) == b"12345" + e = py.test.raises(IndexError, 'a[2] = b"123456"') + assert 'char[5]' in str(e.value) + assert 'got 6 characters' in str(e.value) + +def test_add_error(): + x = cast(new_primitive_type("int"), 42) + py.test.raises(TypeError, "x + 1") + py.test.raises(TypeError, "x - 1") + +def test_void_errors(): + py.test.raises(ValueError, alignof, new_void_type()) + py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None) + +def test_too_many_items(): + BChar = new_primitive_type("char") + BArray = new_array_type(new_pointer_type(BChar), 5) + py.test.raises(IndexError, newp, BArray, tuple(b'123456')) + py.test.raises(IndexError, newp, BArray, list(b'123456')) + py.test.raises(IndexError, newp, BArray, b'123456') + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, []) + py.test.raises(TypeError, newp, new_pointer_type(BStruct), b'') + py.test.raises(ValueError, newp, new_pointer_type(BStruct), [b'1']) + +def test_more_type_errors(): + BInt = new_primitive_type("int") + BChar = new_primitive_type("char") + BArray = new_array_type(new_pointer_type(BChar), 5) + py.test.raises(TypeError, newp, BArray, 12.34) + BArray = new_array_type(new_pointer_type(BInt), 5) + py.test.raises(TypeError, newp, BArray, 12.34) + BFloat = new_primitive_type("float") + py.test.raises(TypeError, cast, BFloat, newp(BArray, None)) + +def test_more_overflow_errors(): + BUInt = new_primitive_type("unsigned int") + py.test.raises(OverflowError, newp, new_pointer_type(BUInt), -1) + py.test.raises(OverflowError, newp, new_pointer_type(BUInt), 2**32) + +def test_newp_copying(): + """Test that we can do newp(<type>, <cdata of the given type>) for most + types, including same-type arrays. + """ + BInt = new_primitive_type("int") + p = newp(new_pointer_type(BInt), cast(BInt, 42)) + assert p[0] == 42 + # + BUInt = new_primitive_type("unsigned int") + p = newp(new_pointer_type(BUInt), cast(BUInt, 42)) + assert p[0] == 42 + # + BChar = new_primitive_type("char") + p = newp(new_pointer_type(BChar), cast(BChar, '!')) + assert p[0] == b'!' + # + BFloat = new_primitive_type("float") + p = newp(new_pointer_type(BFloat), cast(BFloat, 12.25)) + assert p[0] == 12.25 + # + BStruct = new_struct_type("struct foo_s") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BInt, -1)]) + s1 = newp(BStructPtr, [42]) + p1 = newp(new_pointer_type(BStructPtr), s1) + assert p1[0] == s1 + # + BArray = new_array_type(new_pointer_type(BInt), None) + a1 = newp(BArray, [1, 2, 3, 4]) + py.test.raises(TypeError, newp, BArray, a1) + BArray6 = new_array_type(new_pointer_type(BInt), 6) + a1 = newp(BArray6, [10, 20, 30]) + a2 = newp(BArray6, a1) + assert list(a2) == [10, 20, 30, 0, 0, 0] + # + s1 = newp(BStructPtr, [42]) + s2 = newp(BStructPtr, s1[0]) + assert s2.a1 == 42 + # + BUnion = new_union_type("union foo_u") + BUnionPtr = new_pointer_type(BUnion) + complete_struct_or_union(BUnion, [('a1', BInt, -1)]) + u1 = newp(BUnionPtr, [42]) + u2 = newp(BUnionPtr, u1[0]) + assert u2.a1 == 42 + # + BFunc = new_function_type((BInt,), BUInt) + p1 = cast(BFunc, 42) + p2 = newp(new_pointer_type(BFunc), p1) + assert p2[0] == p1 + +def test_string(): + BChar = new_primitive_type("char") + assert string(cast(BChar, 42)) == b'*' + assert string(cast(BChar, 0)) == b'\x00' + BCharP = new_pointer_type(BChar) + BArray = new_array_type(BCharP, 10) + a = newp(BArray, b"hello") + assert len(a) == 10 + assert string(a) == b"hello" + p = a + 2 + assert string(p) == b"llo" + assert string(newp(new_array_type(BCharP, 4), b"abcd")) == b"abcd" + py.test.raises(RuntimeError, string, cast(BCharP, 0)) + assert string(a, 4) == b"hell" + assert string(a, 5) == b"hello" + assert string(a, 6) == b"hello" + +def test_string_byte(): + BByte = new_primitive_type("signed char") + assert string(cast(BByte, 42)) == b'*' + assert string(cast(BByte, 0)) == b'\x00' + BArray = new_array_type(new_pointer_type(BByte), None) + a = newp(BArray, [65, 66, 67]) + assert type(string(a)) is bytes and string(a) == b'ABC' + # + BByte = new_primitive_type("unsigned char") + assert string(cast(BByte, 42)) == b'*' + assert string(cast(BByte, 0)) == b'\x00' + BArray = new_array_type(new_pointer_type(BByte), None) + a = newp(BArray, [65, 66, 67]) + assert type(string(a)) is bytes and string(a) == b'ABC' + if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): + assert string(a, 8).startswith(b'ABC') # may contain additional garbage + +def test_string_wchar(): + for typename in ["wchar_t", "char16_t", "char32_t"]: + _test_string_wchar_variant(typename) + +def _test_string_wchar_variant(typename): + BWChar = new_primitive_type(typename) + assert string(cast(BWChar, 42)) == u+'*' + assert string(cast(BWChar, 0x4253)) == u+'\u4253' + assert string(cast(BWChar, 0)) == u+'\x00' + BArray = new_array_type(new_pointer_type(BWChar), None) + a = newp(BArray, [u+'A', u+'B', u+'C']) + assert type(string(a)) is unicode and string(a) == u+'ABC' + if 'PY_DOT_PY' not in globals() and sys.version_info < (3,): + try: + # may contain additional garbage + assert string(a, 8).startswith(u+'ABC') + except ValueError: # garbage contains values > 0x10FFFF + assert sizeof(BWChar) == 4 + +def test_string_typeerror(): + BShort = new_primitive_type("short") + BArray = new_array_type(new_pointer_type(BShort), None) + a = newp(BArray, [65, 66, 67]) + py.test.raises(TypeError, string, a) + +def test_bug_convert_to_ptr(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BDouble = new_primitive_type("double") + x = cast(BDouble, 42) + py.test.raises(TypeError, newp, new_pointer_type(BCharP), x) + +def test_set_struct_fields(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharArray10 = new_array_type(BCharP, 10) + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BCharArray10, -1)]) + p = newp(BStructPtr, None) + assert string(p.a1) == b'' + p.a1 = b'foo' + assert string(p.a1) == b'foo' + assert list(p.a1) == [b'f', b'o', b'o'] + [b'\x00'] * 7 + p.a1 = [b'x', b'y'] + assert string(p.a1) == b'xyo' + +def test_invalid_function_result_types(): + BFunc = new_function_type((), new_void_type()) + BArray = new_array_type(new_pointer_type(BFunc), 5) # works + new_function_type((), BFunc) # works + new_function_type((), new_primitive_type("int")) + new_function_type((), new_pointer_type(BFunc)) + BUnion = new_union_type("union foo_u") + complete_struct_or_union(BUnion, []) + BFunc = new_function_type((), BUnion) + py.test.raises(NotImplementedError, cast(BFunc, 123)) + py.test.raises(TypeError, new_function_type, (), BArray) + +def test_struct_return_in_func(): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BFloat = new_primitive_type("float") + BDouble = new_primitive_type("double") + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo_s") + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BShort, -1)]) + BFunc10 = new_function_type((BInt,), BStruct) + f = cast(BFunc10, _testfunc(10)) + s = f(40) + assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>" + assert s.a1 == bytechr(40) + assert s.a2 == 40 * 40 + # + BStruct11 = new_struct_type("struct test11") + complete_struct_or_union(BStruct11, [('a1', BInt, -1), + ('a2', BInt, -1)]) + BFunc11 = new_function_type((BInt,), BStruct11) + f = cast(BFunc11, _testfunc(11)) + s = f(40) + assert repr(s) == "<cdata 'struct test11' owning 8 bytes>" + assert s.a1 == 40 + assert s.a2 == 40 * 40 + # + BStruct12 = new_struct_type("struct test12") + complete_struct_or_union(BStruct12, [('a1', BDouble, -1), + ]) + BFunc12 = new_function_type((BInt,), BStruct12) + f = cast(BFunc12, _testfunc(12)) + s = f(40) + assert repr(s) == "<cdata 'struct test12' owning 8 bytes>" + assert s.a1 == 40.0 + # + BStruct13 = new_struct_type("struct test13") + complete_struct_or_union(BStruct13, [('a1', BInt, -1), + ('a2', BInt, -1), + ('a3', BInt, -1)]) + BFunc13 = new_function_type((BInt,), BStruct13) + f = cast(BFunc13, _testfunc(13)) + s = f(40) + assert repr(s) == "<cdata 'struct test13' owning 12 bytes>" + assert s.a1 == 40 + assert s.a2 == 40 * 40 + assert s.a3 == 40 * 40 * 40 + # + BStruct14 = new_struct_type("struct test14") + complete_struct_or_union(BStruct14, [('a1', BFloat, -1), + ]) + BFunc14 = new_function_type((BInt,), BStruct14) + f = cast(BFunc14, _testfunc(14)) + s = f(40) + assert repr(s) == "<cdata 'struct test14' owning 4 bytes>" + assert s.a1 == 40.0 + # + BStruct15 = new_struct_type("struct test15") + complete_struct_or_union(BStruct15, [('a1', BFloat, -1), + ('a2', BInt, -1)]) + BFunc15 = new_function_type((BInt,), BStruct15) + f = cast(BFunc15, _testfunc(15)) + s = f(40) + assert repr(s) == "<cdata 'struct test15' owning 8 bytes>" + assert s.a1 == 40.0 + assert s.a2 == 40 * 40 + # + BStruct16 = new_struct_type("struct test16") + complete_struct_or_union(BStruct16, [('a1', BFloat, -1), + ('a2', BFloat, -1)]) + BFunc16 = new_function_type((BInt,), BStruct16) + f = cast(BFunc16, _testfunc(16)) + s = f(40) + assert repr(s) == "<cdata 'struct test16' owning 8 bytes>" + assert s.a1 == 40.0 + assert s.a2 == -40.0 + # + BStruct17 = new_struct_type("struct test17") + complete_struct_or_union(BStruct17, [('a1', BInt, -1), + ('a2', BFloat, -1)]) + BFunc17 = new_function_type((BInt,), BStruct17) + f = cast(BFunc17, _testfunc(17)) + s = f(40) + assert repr(s) == "<cdata 'struct test17' owning 8 bytes>" + assert s.a1 == 40 + assert s.a2 == 40.0 * 40.0 + # + BStruct17Ptr = new_pointer_type(BStruct17) + BFunc18 = new_function_type((BStruct17Ptr,), BInt) + f = cast(BFunc18, _testfunc(18)) + x = f([[40, 2.5]]) + assert x == 42 + x = f([{'a2': 43.1}]) + assert x == 43 + +def test_cast_with_functionptr(): + BFunc = new_function_type((), new_void_type()) + BFunc2 = new_function_type((), new_primitive_type("short")) + BCharP = new_pointer_type(new_primitive_type("char")) + BIntP = new_pointer_type(new_primitive_type("int")) + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BFunc, -1)]) + newp(BStructPtr, [cast(BFunc, 0)]) + newp(BStructPtr, [cast(BCharP, 0)]) + py.test.raises(TypeError, newp, BStructPtr, [cast(BIntP, 0)]) + py.test.raises(TypeError, newp, BStructPtr, [cast(BFunc2, 0)]) + +def test_wchar(): + _test_wchar_variant("wchar_t") + if sys.platform.startswith("linux"): + BWChar = new_primitive_type("wchar_t") + assert sizeof(BWChar) == 4 + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) + +def test_char16(): + BChar16 = new_primitive_type("char16_t") + assert sizeof(BChar16) == 2 + _test_wchar_variant("char16_t") + assert int(cast(BChar16, -1)) == 0xffff # always unsigned + +def test_char32(): + BChar32 = new_primitive_type("char32_t") + assert sizeof(BChar32) == 4 + _test_wchar_variant("char32_t") + assert int(cast(BChar32, -1)) == 0xffffffff # always unsigned + +def _test_wchar_variant(typename): + BWChar = new_primitive_type(typename) + BInt = new_primitive_type("int") + pyuni4 = {1: True, 2: False}[len(u+'\U00012345')] + wchar4 = {2: False, 4: True}[sizeof(BWChar)] + assert str(cast(BWChar, 0x45)) == "<cdata '%s' %s'E'>" % ( + typename, mandatory_u_prefix) + assert str(cast(BWChar, 0x1234)) == "<cdata '%s' %s'\u1234'>" % ( + typename, mandatory_u_prefix) + if not _hacked_pypy_uni4(): + if wchar4: + x = cast(BWChar, 0x12345) + assert str(x) == "<cdata '%s' %s'\U00012345'>" % ( + typename, mandatory_u_prefix) + assert int(x) == 0x12345 + else: + x = cast(BWChar, 0x18345) + assert str(x) == "<cdata '%s' %s'\u8345'>" % ( + typename, mandatory_u_prefix) + assert int(x) == 0x8345 + # + BWCharP = new_pointer_type(BWChar) + BStruct = new_struct_type("struct foo_s") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BWChar, -1), + ('a2', BWCharP, -1)]) + s = newp(BStructPtr) + s.a1 = u+'\x00' + assert s.a1 == u+'\x00' + py.test.raises(TypeError, "s.a1 = b'a'") + py.test.raises(TypeError, "s.a1 = bytechr(0xFF)") + s.a1 = u+'\u1234' + assert s.a1 == u+'\u1234' + if pyuni4: + if wchar4: + s.a1 = u+'\U00012345' + assert s.a1 == u+'\U00012345' + elif wchar4: + if not _hacked_pypy_uni4(): + s.a1 = cast(BWChar, 0x12345) + assert s.a1 == u+'\ud808\udf45' + s.a1 = u+'\ud807\udf44' + assert s.a1 == u+'\U00011f44' + else: + py.test.raises(TypeError, "s.a1 = u+'\U00012345'") + # + BWCharArray = new_array_type(BWCharP, None) + a = newp(BWCharArray, u+'hello \u1234 world') + assert len(a) == 14 # including the final null + assert string(a) == u+'hello \u1234 world' + a[13] = u+'!' + assert string(a) == u+'hello \u1234 world!' + assert str(a) == repr(a) + assert a[6] == u+'\u1234' + a[6] = u+'-' + assert string(a) == u+'hello - world!' + assert str(a) == repr(a) + # + if wchar4 and not _hacked_pypy_uni4(): + u1 = u+'\U00012345\U00012346\U00012347' + a = newp(BWCharArray, u1) + assert len(a) == 4 + assert string(a) == u1 + assert len(list(a)) == 4 + expected = [u+'\U00012345', u+'\U00012346', u+'\U00012347', unichr(0)] + assert list(a) == expected + got = [a[i] for i in range(4)] + assert got == expected + py.test.raises(IndexError, 'a[4]') + # + w = cast(BWChar, 'a') + assert repr(w) == "<cdata '%s' %s'a'>" % (typename, mandatory_u_prefix) + assert str(w) == repr(w) + assert string(w) == u+'a' + assert int(w) == ord('a') + w = cast(BWChar, 0x1234) + assert repr(w) == "<cdata '%s' %s'\u1234'>" % (typename, mandatory_u_prefix) + assert str(w) == repr(w) + assert string(w) == u+'\u1234' + assert int(w) == 0x1234 + w = cast(BWChar, u+'\u8234') + assert repr(w) == "<cdata '%s' %s'\u8234'>" % (typename, mandatory_u_prefix) + assert str(w) == repr(w) + assert string(w) == u+'\u8234' + assert int(w) == 0x8234 + w = cast(BInt, u+'\u1234') + assert repr(w) == "<cdata 'int' 4660>" + if wchar4 and not _hacked_pypy_uni4(): + w = cast(BWChar, u+'\U00012345') + assert repr(w) == "<cdata '%s' %s'\U00012345'>" % ( + typename, mandatory_u_prefix) + assert str(w) == repr(w) + assert string(w) == u+'\U00012345' + assert int(w) == 0x12345 + w = cast(BInt, u+'\U00012345') + assert repr(w) == "<cdata 'int' 74565>" + py.test.raises(TypeError, cast, BInt, u+'') + py.test.raises(TypeError, cast, BInt, u+'XX') + assert int(cast(BInt, u+'a')) == ord('a') + # + a = newp(BWCharArray, u+'hello - world') + p = cast(BWCharP, a) + assert string(p) == u+'hello - world' + p[6] = u+'\u2345' + assert string(p) == u+'hello \u2345 world' + # + s = newp(BStructPtr, [u+'\u1234', p]) + assert s.a1 == u+'\u1234' + assert s.a2 == p + assert str(s.a2) == repr(s.a2) + assert string(s.a2) == u+'hello \u2345 world' + # + q = cast(BWCharP, 0) + assert str(q) == repr(q) + py.test.raises(RuntimeError, string, q) + # + def cb(p): + assert repr(p).startswith("<cdata '%s *' 0x" % typename) + return len(string(p)) + BFunc = new_function_type((BWCharP,), BInt, False) + f = callback(BFunc, cb, -42) + assert f(u+'a\u1234b') == 3 + # + if wchar4 and not pyuni4 and not _hacked_pypy_uni4(): + # try out-of-range wchar_t values + x = cast(BWChar, 1114112) + py.test.raises(ValueError, string, x) + x = cast(BWChar, -1) + py.test.raises(ValueError, string, x) + +def test_wchar_variants_mix(): + BWChar = new_primitive_type("wchar_t") + BChar16 = new_primitive_type("char16_t") + BChar32 = new_primitive_type("char32_t") + assert int(cast(BChar32, cast(BChar16, -2))) == 0xfffe + assert int(cast(BWChar, cast(BChar16, -2))) == 0xfffe + assert int(cast(BChar16, cast(BChar32, 0x0001f345))) == 0xf345 + assert int(cast(BChar16, cast(BWChar, 0x0001f345))) == 0xf345 + # + BChar16A = new_array_type(new_pointer_type(BChar16), None) + BChar32A = new_array_type(new_pointer_type(BChar32), None) + x = cast(BChar32, 'A') + py.test.raises(TypeError, newp, BChar16A, [x]) + x = cast(BChar16, 'A') + py.test.raises(TypeError, newp, BChar32A, [x]) + # + a = newp(BChar16A, u+'\U00012345') + assert len(a) == 3 + a = newp(BChar32A, u+'\U00012345') + assert len(a) == 2 # even if the Python unicode string above is 2 chars + +def test_keepalive_struct(): + # exception to the no-keepalive rule: p=newp(BStructPtr) returns a + # pointer owning the memory, and p[0] returns a pointer to the + # struct that *also* owns the memory + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1), + ('a2', new_primitive_type("int"), -1), + ('a3', new_primitive_type("int"), -1)]) + p = newp(BStructPtr) + assert repr(p) == "<cdata 'struct foo *' owning 12 bytes>" + q = p[0] + assert repr(q) == "<cdata 'struct foo' owning 12 bytes>" + q.a1 = 123456 + assert p.a1 == 123456 + r = cast(BStructPtr, p) + assert repr(r[0]).startswith("<cdata 'struct foo &' 0x") + del p + import gc; gc.collect() + assert q.a1 == 123456 + assert repr(q) == "<cdata 'struct foo' owning 12 bytes>" + assert q.a1 == 123456 + +def test_nokeepalive_struct(): + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + BStructPtrPtr = new_pointer_type(BStructPtr) + complete_struct_or_union(BStruct, [('a1', new_primitive_type("int"), -1)]) + p = newp(BStructPtr) + pp = newp(BStructPtrPtr) + pp[0] = p + s = pp[0][0] + assert repr(s).startswith("<cdata 'struct foo &' 0x") + +def test_owning_repr(): + BInt = new_primitive_type("int") + BArray = new_array_type(new_pointer_type(BInt), None) # int[] + p = newp(BArray, 7) + assert repr(p) == "<cdata 'int[]' owning 28 bytes>" + assert sizeof(p) == 28 + # + BArray = new_array_type(new_pointer_type(BInt), 7) # int[7] + p = newp(BArray, None) + assert repr(p) == "<cdata 'int[7]' owning 28 bytes>" + assert sizeof(p) == 28 + +def test_cannot_dereference_void(): + BVoidP = new_pointer_type(new_void_type()) + p = cast(BVoidP, 123456) + py.test.raises(TypeError, "p[0]") + p = cast(BVoidP, 0) + py.test.raises((TypeError, RuntimeError), "p[0]") + +def test_iter(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, None) # int[] + p = newp(BArray, 7) + assert list(p) == list(iter(p)) == [0] * 7 + # + py.test.raises(TypeError, iter, cast(BInt, 5)) + py.test.raises(TypeError, iter, cast(BIntP, 123456)) + +def test_cmp(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BVoidP = new_pointer_type(new_void_type()) + p = newp(BIntP, 123) + q = cast(BInt, 124) + assert (p == q) is False + assert (p != q) is True + assert (q == p) is False + assert (q != p) is True + if strict_compare: + py.test.raises(TypeError, "p < q") + py.test.raises(TypeError, "p <= q") + py.test.raises(TypeError, "q < p") + py.test.raises(TypeError, "q <= p") + py.test.raises(TypeError, "p > q") + py.test.raises(TypeError, "p >= q") + r = cast(BVoidP, p) + assert (p < r) is False + assert (p <= r) is True + assert (p == r) is True + assert (p != r) is False + assert (p > r) is False + assert (p >= r) is True + s = newp(BIntP, 125) + assert (p == s) is False + assert (p != s) is True + assert (p < s) is (p <= s) is (s > p) is (s >= p) + assert (p > s) is (p >= s) is (s < p) is (s <= p) + assert (p < s) ^ (p > s) + +def test_buffer(): + try: + import __builtin__ + except ImportError: + import builtins as __builtin__ + BShort = new_primitive_type("short") + s = newp(new_pointer_type(BShort), 100) + assert sizeof(s) == size_of_ptr() + assert sizeof(BShort) == 2 + assert len(buffer(s)) == 2 + # + BChar = new_primitive_type("char") + BCharArray = new_array_type(new_pointer_type(BChar), None) + c = newp(BCharArray, b"hi there") + # + buf = buffer(c) + assert repr(buf).startswith('<_cffi_backend.buffer object at 0x') + assert bytes(buf) == b"hi there\x00" + assert type(buf) is buffer + if sys.version_info < (3,): + assert str(buf) == "hi there\x00" + assert unicode(buf) == u+"hi there\x00" + else: + assert str(buf) == repr(buf) + # --mb_length-- + assert len(buf) == len(b"hi there\x00") + # --mb_item-- + for i in range(-12, 12): + try: + expected = b"hi there\x00"[i] + except IndexError: + py.test.raises(IndexError, "buf[i]") + else: + assert buf[i] == bitem2bchr(expected) + # --mb_slice-- + assert buf[:] == b"hi there\x00" + for i in range(-12, 12): + assert buf[i:] == b"hi there\x00"[i:] + assert buf[:i] == b"hi there\x00"[:i] + for j in range(-12, 12): + assert buf[i:j] == b"hi there\x00"[i:j] + # --misc-- + assert list(buf) == list(map(bitem2bchr, b"hi there\x00")) + # --mb_as_buffer-- + if hasattr(__builtin__, 'buffer'): # Python <= 2.7 + py.test.raises(TypeError, __builtin__.buffer, c) + bf1 = __builtin__.buffer(buf) + assert len(bf1) == len(buf) and bf1[3] == "t" + if hasattr(__builtin__, 'memoryview'): # Python >= 2.7 + py.test.raises(TypeError, memoryview, c) + mv1 = memoryview(buf) + assert len(mv1) == len(buf) and mv1[3] in (b"t", ord(b"t")) + # --mb_ass_item-- + expected = list(map(bitem2bchr, b"hi there\x00")) + for i in range(-12, 12): + try: + expected[i] = bytechr(i & 0xff) + except IndexError: + py.test.raises(IndexError, "buf[i] = bytechr(i & 0xff)") + else: + buf[i] = bytechr(i & 0xff) + assert list(buf) == expected + # --mb_ass_slice-- + buf[:] = b"hi there\x00" + assert list(buf) == list(c) == list(map(bitem2bchr, b"hi there\x00")) + py.test.raises(ValueError, 'buf[:] = b"shorter"') + py.test.raises(ValueError, 'buf[:] = b"this is much too long!"') + buf[4:2] = b"" # no effect, but should work + assert buf[:] == b"hi there\x00" + buf[:2] = b"HI" + assert buf[:] == b"HI there\x00" + buf[:2] = b"hi" + expected = list(map(bitem2bchr, b"hi there\x00")) + x = 0 + for i in range(-12, 12): + for j in range(-12, 12): + start = i if i >= 0 else i + len(buf) + stop = j if j >= 0 else j + len(buf) + start = max(0, min(len(buf), start)) + stop = max(0, min(len(buf), stop)) + sample = bytechr(x & 0xff) * (stop - start) + x += 1 + buf[i:j] = sample + expected[i:j] = map(bitem2bchr, sample) + assert list(buf) == expected + +def test_getcname(): + BUChar = new_primitive_type("unsigned char") + BArray = new_array_type(new_pointer_type(BUChar), 123) + assert getcname(BArray, "<-->") == "unsigned char<-->[123]" + +def test_errno(): + BVoid = new_void_type() + BFunc5 = new_function_type((), BVoid) + f = cast(BFunc5, _testfunc(5)) + set_errno(50) + f() + assert get_errno() == 65 + f(); f() + assert get_errno() == 95 + +def test_errno_callback(): + if globals().get('PY_DOT_PY') == '2.5': + py.test.skip("cannot run this test on py.py with Python 2.5") + set_errno(95) + def cb(): + e = get_errno() + set_errno(e - 6) + BVoid = new_void_type() + BFunc5 = new_function_type((), BVoid) + f = callback(BFunc5, cb) + f() + assert get_errno() == 89 + f(); f() + assert get_errno() == 77 + +def test_cast_to_array(): + # not valid in C! extension to get a non-owning <cdata 'int[3]'> + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, 3) + x = cast(BArray, 0) + assert repr(x) == "<cdata 'int[3]' NULL>" + +def test_cast_invalid(): + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, []) + p = cast(new_pointer_type(BStruct), 123456) + s = p[0] + py.test.raises(TypeError, cast, BStruct, s) + +def test_bug_float_convertion(): + BDouble = new_primitive_type("double") + BDoubleP = new_pointer_type(BDouble) + py.test.raises(TypeError, newp, BDoubleP, "foobar") + +def test_bug_delitem(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + x = newp(BCharP) + py.test.raises(TypeError, "del x[0]") + +def test_bug_delattr(): + BLong = new_primitive_type("long") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BLong, -1)]) + x = newp(new_pointer_type(BStruct)) + py.test.raises(AttributeError, "del x.a1") + +def test_variable_length_struct(): + py.test.skip("later") + BLong = new_primitive_type("long") + BArray = new_array_type(new_pointer_type(BLong), None) + BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BLong, -1), + ('a2', BArray, -1)]) + assert sizeof(BStruct) == size_of_long() + assert alignof(BStruct) == alignof(BLong) + # + py.test.raises(TypeError, newp, BStructP, None) + x = newp(BStructP, 5) + assert sizeof(x) == 6 * size_of_long() + x[4] = 123 + assert x[4] == 123 + py.test.raises(IndexError, "x[5]") + assert len(x.a2) == 5 + # + py.test.raises(TypeError, newp, BStructP, [123]) + x = newp(BStructP, [123, 5]) + assert x.a1 == 123 + assert len(x.a2) == 5 + assert list(x.a2) == [0] * 5 + # + x = newp(BStructP, {'a2': 5}) + assert x.a1 == 0 + assert len(x.a2) == 5 + assert list(x.a2) == [0] * 5 + # + x = newp(BStructP, [123, (4, 5)]) + assert x.a1 == 123 + assert len(x.a2) == 2 + assert list(x.a2) == [4, 5] + # + x = newp(BStructP, {'a2': (4, 5)}) + assert x.a1 == 0 + assert len(x.a2) == 2 + assert list(x.a2) == [4, 5] + +def test_autocast_int(): + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + BLongLong = new_primitive_type("long long") + BULongLong = new_primitive_type("unsigned long long") + BULongLongPtr = new_pointer_type(BULongLong) + x = newp(BIntPtr, cast(BInt, 42)) + assert x[0] == 42 + x = newp(BIntPtr, cast(BLongLong, 42)) + assert x[0] == 42 + x = newp(BIntPtr, cast(BULongLong, 42)) + assert x[0] == 42 + x = newp(BULongLongPtr, cast(BInt, 42)) + assert x[0] == 42 + py.test.raises(OverflowError, newp, BULongLongPtr, cast(BInt, -42)) + x = cast(BInt, cast(BInt, 42)) + assert int(x) == 42 + x = cast(BInt, cast(BLongLong, 42)) + assert int(x) == 42 + x = cast(BInt, cast(BULongLong, 42)) + assert int(x) == 42 + x = cast(BULongLong, cast(BInt, 42)) + assert int(x) == 42 + x = cast(BULongLong, cast(BInt, -42)) + assert int(x) == 2 ** 64 - 42 + x = cast(BIntPtr, cast(BInt, 42)) + assert int(cast(BInt, x)) == 42 + +def test_autocast_float(): + BFloat = new_primitive_type("float") + BDouble = new_primitive_type("float") + BFloatPtr = new_pointer_type(BFloat) + x = newp(BFloatPtr, cast(BDouble, 12.5)) + assert x[0] == 12.5 + x = cast(BFloat, cast(BDouble, 12.5)) + assert float(x) == 12.5 + +def test_longdouble(): + py_py = 'PY_DOT_PY' in globals() + BInt = new_primitive_type("int") + BLongDouble = new_primitive_type("long double") + BLongDoublePtr = new_pointer_type(BLongDouble) + BLongDoubleArray = new_array_type(BLongDoublePtr, None) + a = newp(BLongDoubleArray, 1) + x = a[0] + if not py_py: + assert repr(x).startswith("<cdata 'long double' 0.0") + assert float(x) == 0.0 + assert int(x) == 0 + # + b = newp(BLongDoubleArray, [1.23]) + x = b[0] + if not py_py: + assert repr(x).startswith("<cdata 'long double' 1.23") + assert float(x) == 1.23 + assert int(x) == 1 + # + BFunc19 = new_function_type((BLongDouble, BInt), BLongDouble) + f = cast(BFunc19, _testfunc(19)) + start = lstart = 1.5 + for i in range(107): + start = 4 * start - start * start + lstart = f(lstart, 1) + lother = f(1.5, 107) + if not py_py: + assert float(lstart) == float(lother) + assert repr(lstart) == repr(lother) + if sizeof(BLongDouble) > sizeof(new_primitive_type("double")): + assert float(lstart) != start + assert repr(lstart).startswith("<cdata 'long double' ") + # + c = newp(BLongDoubleArray, [lstart]) + x = c[0] + assert float(f(lstart, 107)) == float(f(x, 107)) + +def test_get_array_of_length_zero(): + for length in [0, 5, 10]: + BLong = new_primitive_type("long") + BLongP = new_pointer_type(BLong) + BArray0 = new_array_type(BLongP, length) + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BArray0, -1)]) + p = newp(BStructPtr, None) + if length == 0: + assert repr(p.a1).startswith("<cdata 'long *' 0x") + else: + assert repr(p.a1).startswith("<cdata 'long[%d]' 0x" % length) + +def test_nested_anonymous_struct(): + BInt = new_primitive_type("int") + BChar = new_primitive_type("char") + BStruct = new_struct_type("struct foo") + BInnerStruct = new_struct_type("struct foo") + complete_struct_or_union(BInnerStruct, [('a1', BInt, -1), + ('a2', BChar, -1)]) + complete_struct_or_union(BStruct, [('', BInnerStruct, -1), + ('a3', BChar, -1)]) + assert sizeof(BInnerStruct) == sizeof(BInt) * 2 # with alignment + assert sizeof(BStruct) == sizeof(BInt) * 3 # 'a3' is placed after + d = BStruct.fields + assert len(d) == 3 + assert d[0][0] == 'a1' + assert d[0][1].type is BInt + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == sizeof(BInt) + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert d[2][0] == 'a3' + assert d[2][1].type is BChar + assert d[2][1].offset == sizeof(BInt) * 2 + assert d[2][1].bitshift == -1 + assert d[2][1].bitsize == -1 + +def test_nested_anonymous_struct_2(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BInnerUnion = new_union_type("union bar") + complete_struct_or_union(BInnerUnion, [('a1', BInt, -1), + ('a2', BInt, -1)]) + complete_struct_or_union(BStruct, [('b1', BInt, -1), + ('', BInnerUnion, -1), + ('b2', BInt, -1)]) + assert sizeof(BInnerUnion) == sizeof(BInt) + assert sizeof(BStruct) == sizeof(BInt) * 3 + fields = [(name, fld.offset, fld.flags) for (name, fld) in BStruct.fields] + assert fields == [ + ('b1', 0 * sizeof(BInt), 0), + ('a1', 1 * sizeof(BInt), 0), + ('a2', 1 * sizeof(BInt), 1), + ('b2', 2 * sizeof(BInt), 0), + ] + +def test_sizeof_union(): + # a union has the largest alignment of its members, and a total size + # that is the largest of its items *possibly further aligned* if + # another smaller item has a larger alignment... + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + assert sizeof(BShort) == alignof(BShort) == 2 + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BChar), + ('a2', BChar), + ('a3', BChar)]) + assert sizeof(BStruct) == 3 and alignof(BStruct) == 1 + BUnion = new_union_type("union u") + complete_struct_or_union(BUnion, [('s', BStruct), + ('i', BShort)]) + assert sizeof(BUnion) == 4 + assert alignof(BUnion) == 2 + +def test_unaligned_struct(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('b', BInt, -1, 1)], + None, 5, 1) + +def test_CData_CType(): + CData, CType = _get_types() + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + nullchr = cast(BChar, 0) + chrref = newp(BCharP, None) + assert isinstance(nullchr, CData) + assert isinstance(chrref, CData) + assert not isinstance(BChar, CData) + assert not isinstance(nullchr, CType) + assert not isinstance(chrref, CType) + assert isinstance(BChar, CType) + +def test_no_cdata_float(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BUInt = new_primitive_type("unsigned int") + BUIntP = new_pointer_type(BUInt) + BFloat = new_primitive_type("float") + py.test.raises(TypeError, newp, BIntP, cast(BFloat, 0.0)) + py.test.raises(TypeError, newp, BUIntP, cast(BFloat, 0.0)) + +def test_bool(): + BBool = new_primitive_type("_Bool") + BBoolP = new_pointer_type(BBool) + assert int(cast(BBool, False)) == 0 + assert int(cast(BBool, True)) == 1 + assert bool(cast(BBool, False)) is False # since 1.7 + assert bool(cast(BBool, True)) is True + assert int(cast(BBool, 3)) == 1 + assert int(cast(BBool, long(3))) == 1 + assert int(cast(BBool, long(10)**4000)) == 1 + assert int(cast(BBool, -0.1)) == 1 + assert int(cast(BBool, -0.0)) == 0 + assert int(cast(BBool, '\x00')) == 0 + assert int(cast(BBool, '\xff')) == 1 + assert newp(BBoolP, False)[0] == 0 + assert newp(BBoolP, True)[0] == 1 + assert newp(BBoolP, 0)[0] == 0 + assert newp(BBoolP, 1)[0] == 1 + py.test.raises(TypeError, newp, BBoolP, 1.0) + py.test.raises(TypeError, newp, BBoolP, '\x00') + py.test.raises(OverflowError, newp, BBoolP, 2) + py.test.raises(OverflowError, newp, BBoolP, -1) + BCharP = new_pointer_type(new_primitive_type("char")) + p = newp(BCharP, b'\x01') + q = cast(BBoolP, p) + assert q[0] is True + p = newp(BCharP, b'\x00') + q = cast(BBoolP, p) + assert q[0] is False + py.test.raises(TypeError, string, cast(BBool, False)) + BDouble = new_primitive_type("double") + assert int(cast(BBool, cast(BDouble, 0.1))) == 1 + assert int(cast(BBool, cast(BDouble, 0.0))) == 0 + BBoolA = new_array_type(BBoolP, None) + p = newp(BBoolA, b'\x01\x00') + assert p[0] is True + assert p[1] is False + +def test_bool_forbidden_cases(): + BBool = new_primitive_type("_Bool") + BBoolP = new_pointer_type(BBool) + BBoolA = new_array_type(BBoolP, None) + BCharP = new_pointer_type(new_primitive_type("char")) + p = newp(BCharP, b'X') + q = cast(BBoolP, p) + py.test.raises(ValueError, "q[0]") + py.test.raises(TypeError, newp, BBoolP, b'\x00') + assert newp(BBoolP, 0)[0] is False + assert newp(BBoolP, 1)[0] is True + py.test.raises(OverflowError, newp, BBoolP, 2) + py.test.raises(OverflowError, newp, BBoolP, -1) + py.test.raises(ValueError, newp, BBoolA, b'\x00\x01\x02') + py.test.raises(OverflowError, newp, BBoolA, [0, 1, 2]) + py.test.raises(TypeError, string, newp(BBoolP, 1)) + py.test.raises(TypeError, string, newp(BBoolA, [1])) + +def test_typeoffsetof(): + BChar = new_primitive_type("char") + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BChar, -1), + ('a3', BChar, -1)]) + py.test.raises(TypeError, typeoffsetof, BStructPtr, None) + py.test.raises(TypeError, typeoffsetof, BStruct, None) + assert typeoffsetof(BStructPtr, 'a1') == (BChar, 0) + assert typeoffsetof(BStruct, 'a1') == (BChar, 0) + assert typeoffsetof(BStructPtr, 'a2') == (BChar, 1) + assert typeoffsetof(BStruct, 'a3') == (BChar, 2) + assert typeoffsetof(BStructPtr, 'a2', 0) == (BChar, 1) + assert typeoffsetof(BStruct, u+'a3') == (BChar, 2) + py.test.raises(TypeError, typeoffsetof, BStructPtr, 'a2', 1) + py.test.raises(KeyError, typeoffsetof, BStructPtr, 'a4') + py.test.raises(KeyError, typeoffsetof, BStruct, 'a5') + py.test.raises(TypeError, typeoffsetof, BStruct, 42) + py.test.raises(TypeError, typeoffsetof, BChar, 'a1') + +def test_typeoffsetof_array(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, None) + py.test.raises(TypeError, typeoffsetof, BArray, None) + py.test.raises(TypeError, typeoffsetof, BArray, 'a1') + assert typeoffsetof(BArray, 51) == (BInt, 51 * size_of_int()) + assert typeoffsetof(BIntP, 51) == (BInt, 51 * size_of_int()) + assert typeoffsetof(BArray, -51) == (BInt, -51 * size_of_int()) + MAX = sys.maxsize // size_of_int() + assert typeoffsetof(BArray, MAX) == (BInt, MAX * size_of_int()) + assert typeoffsetof(BIntP, MAX) == (BInt, MAX * size_of_int()) + py.test.raises(OverflowError, typeoffsetof, BArray, MAX + 1) + +def test_typeoffsetof_no_bitfield(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BInt, 4)]) + py.test.raises(TypeError, typeoffsetof, BStruct, 'a1') + +def test_rawaddressof(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BStruct = new_struct_type("struct foo") + BStructPtr = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('a1', BChar, -1), + ('a2', BChar, -1), + ('a3', BChar, -1)]) + p = newp(BStructPtr) + assert repr(p) == "<cdata 'struct foo *' owning 3 bytes>" + s = p[0] + assert repr(s) == "<cdata 'struct foo' owning 3 bytes>" + a = rawaddressof(BStructPtr, s, 0) + assert repr(a).startswith("<cdata 'struct foo *' 0x") + py.test.raises(TypeError, rawaddressof, BStruct, s, 0) + b = rawaddressof(BCharP, s, 0) + assert b == cast(BCharP, p) + c = rawaddressof(BStructPtr, a, 0) + assert c == a + py.test.raises(TypeError, rawaddressof, BStructPtr, cast(BChar, '?'), 0) + # + d = rawaddressof(BCharP, s, 1) + assert d == cast(BCharP, p) + 1 + # + e = cast(BCharP, 109238) + f = rawaddressof(BCharP, e, 42) + assert f == e + 42 + # + BCharA = new_array_type(BCharP, None) + e = newp(BCharA, 50) + f = rawaddressof(BCharP, e, 42) + assert f == e + 42 + +def test_newp_signed_unsigned_char(): + BCharArray = new_array_type( + new_pointer_type(new_primitive_type("char")), None) + p = newp(BCharArray, b"foo") + assert len(p) == 4 + assert list(p) == [b"f", b"o", b"o", b"\x00"] + # + BUCharArray = new_array_type( + new_pointer_type(new_primitive_type("unsigned char")), None) + p = newp(BUCharArray, b"fo\xff") + assert len(p) == 4 + assert list(p) == [ord("f"), ord("o"), 0xff, 0] + # + BSCharArray = new_array_type( + new_pointer_type(new_primitive_type("signed char")), None) + p = newp(BSCharArray, b"fo\xff") + assert len(p) == 4 + assert list(p) == [ord("f"), ord("o"), -1, 0] + +def test_newp_from_bytearray_doesnt_work(): + BCharArray = new_array_type( + new_pointer_type(new_primitive_type("char")), None) + py.test.raises(TypeError, newp, BCharArray, bytearray(b"foo")) + p = newp(BCharArray, 5) + buffer(p)[:] = bytearray(b"foo.\x00") + assert len(p) == 5 + assert list(p) == [b"f", b"o", b"o", b".", b"\x00"] + p[1:3] = bytearray(b"XY") + assert list(p) == [b"f", b"X", b"Y", b".", b"\x00"] + +def test_string_assignment_to_byte_array(): + BByteArray = new_array_type( + new_pointer_type(new_primitive_type("unsigned char")), None) + p = newp(BByteArray, 5) + p[0:3] = bytearray(b"XYZ") + assert list(p) == [ord("X"), ord("Y"), ord("Z"), 0, 0] + +# XXX hack +if sys.version_info >= (3,): + try: + import posix, io + posix.fdopen = io.open + except ImportError: + pass # win32 + +def test_FILE(): + if sys.platform == "win32": + py.test.skip("testing FILE not implemented") + # + BFILE = new_struct_type("struct _IO_FILE") + BFILEP = new_pointer_type(BFILE) + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BInt = new_primitive_type("int") + BFunc = new_function_type((BCharP, BFILEP), BInt, False) + BFunc2 = new_function_type((BFILEP, BCharP), BInt, True) + ll = find_and_load_library('c') + fputs = ll.load_function(BFunc, "fputs") + fscanf = ll.load_function(BFunc2, "fscanf") + # + import posix + fdr, fdw = posix.pipe() + fr1 = posix.fdopen(fdr, 'rb', 256) + fw1 = posix.fdopen(fdw, 'wb', 256) + # + fw1.write(b"X") + res = fputs(b"hello world\n", fw1) + assert res >= 0 + fw1.flush() # should not be needed + # + p = newp(new_array_type(BCharP, 100), None) + res = fscanf(fr1, b"%s\n", p) + assert res == 1 + assert string(p) == b"Xhello" + fr1.close() + fw1.close() + +def test_FILE_only_for_FILE_arg(): + if sys.platform == "win32": + py.test.skip("testing FILE not implemented") + # + B_NOT_FILE = new_struct_type("struct NOT_FILE") + B_NOT_FILEP = new_pointer_type(B_NOT_FILE) + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BInt = new_primitive_type("int") + BFunc = new_function_type((BCharP, B_NOT_FILEP), BInt, False) + ll = find_and_load_library('c') + fputs = ll.load_function(BFunc, "fputs") + # + import posix + fdr, fdw = posix.pipe() + fr1 = posix.fdopen(fdr, 'r') + fw1 = posix.fdopen(fdw, 'w') + # + e = py.test.raises(TypeError, fputs, b"hello world\n", fw1) + assert str(e.value).startswith( + "initializer for ctype 'struct NOT_FILE *' must " + "be a cdata pointer, not ") + +def test_FILE_object(): + if sys.platform == "win32": + py.test.skip("testing FILE not implemented") + # + BFILE = new_struct_type("FILE") + BFILEP = new_pointer_type(BFILE) + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BInt = new_primitive_type("int") + BFunc = new_function_type((BCharP, BFILEP), BInt, False) + BFunc2 = new_function_type((BFILEP,), BInt, False) + ll = find_and_load_library('c') + fputs = ll.load_function(BFunc, "fputs") + fileno = ll.load_function(BFunc2, "fileno") + # + import posix + fdr, fdw = posix.pipe() + fw1 = posix.fdopen(fdw, 'wb', 256) + # + fw1p = cast(BFILEP, fw1) + fw1.write(b"X") + fw1.flush() + res = fputs(b"hello\n", fw1p) + assert res >= 0 + res = fileno(fw1p) + assert (res == fdw) == (sys.version_info < (3,)) + fw1.close() + # + data = posix.read(fdr, 256) + assert data == b"Xhello\n" + posix.close(fdr) + +def test_errno_saved(): + set_errno(42) + # a random function that will reset errno to 0 (at least on non-windows) + import os; os.stat('.') + # + res = get_errno() + assert res == 42 + +def test_GetLastError(): + if sys.platform != "win32": + py.test.skip("GetLastError(): only for Windows") + # + lib = find_and_load_library('KERNEL32.DLL') + BInt = new_primitive_type("int") + BVoid = new_void_type() + BFunc1 = new_function_type((BInt,), BVoid, False) + BFunc2 = new_function_type((), BInt, False) + SetLastError = lib.load_function(BFunc1, "SetLastError") + GetLastError = lib.load_function(BFunc2, "GetLastError") + # + SetLastError(42) + # a random function that will reset the real GetLastError() to 0 + import nt; nt.stat('.') + # + res = GetLastError() + assert res == 42 + # + SetLastError(2) + code, message = getwinerror() + assert code == 2 + assert message == "The system cannot find the file specified" + # + code, message = getwinerror(1155) + assert code == 1155 + assert message == ("No application is associated with the " + "specified file for this operation") + +def test_nonstandard_integer_types(): + for typename in ['int8_t', 'uint8_t', 'int16_t', 'uint16_t', 'int32_t', + 'uint32_t', 'int64_t', 'uint64_t', 'intptr_t', + 'uintptr_t', 'ptrdiff_t', 'size_t', 'ssize_t', + 'int_least8_t', 'uint_least8_t', + 'int_least16_t', 'uint_least16_t', + 'int_least32_t', 'uint_least32_t', + 'int_least64_t', 'uint_least64_t', + 'int_fast8_t', 'uint_fast8_t', + 'int_fast16_t', 'uint_fast16_t', + 'int_fast32_t', 'uint_fast32_t', + 'int_fast64_t', 'uint_fast64_t', + 'intmax_t', 'uintmax_t']: + new_primitive_type(typename) # works + +def test_cannot_convert_unicode_to_charp(): + BCharP = new_pointer_type(new_primitive_type("char")) + BCharArray = new_array_type(BCharP, None) + py.test.raises(TypeError, newp, BCharArray, u+'foobar') + +def test_buffer_keepalive(): + BCharP = new_pointer_type(new_primitive_type("char")) + BCharArray = new_array_type(BCharP, None) + buflist = [] + for i in range(20): + c = newp(BCharArray, str2bytes("hi there %d" % i)) + buflist.append(buffer(c)) + import gc; gc.collect() + for i in range(20): + buf = buflist[i] + assert buf[:] == str2bytes("hi there %d\x00" % i) + +def test_slice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + assert len(c) == 5 + assert repr(c) == "<cdata 'int[]' owning 20 bytes>" + d = c[1:4] + assert len(d) == 3 + assert repr(d) == "<cdata 'int[]' sliced length 3>" + d[0] = 123 + d[2] = 456 + assert c[1] == 123 + assert c[3] == 456 + assert d[2] == 456 + py.test.raises(IndexError, "d[3]") + py.test.raises(IndexError, "d[-1]") + +def test_slice_ptr(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + d = (c+1)[0:2] + assert len(d) == 2 + assert repr(d) == "<cdata 'int[]' sliced length 2>" + d[1] += 50 + assert c[2] == 50 + +def test_slice_array_checkbounds(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + c[0:5] + assert len(c[5:5]) == 0 + py.test.raises(IndexError, "c[-1:1]") + cp = c + 0 + cp[-1:1] + +def test_nonstandard_slice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + e = py.test.raises(IndexError, "c[:5]") + assert str(e.value) == "slice start must be specified" + e = py.test.raises(IndexError, "c[4:]") + assert str(e.value) == "slice stop must be specified" + e = py.test.raises(IndexError, "c[1:2:3]") + assert str(e.value) == "slice with step not supported" + e = py.test.raises(IndexError, "c[1:2:1]") + assert str(e.value) == "slice with step not supported" + e = py.test.raises(IndexError, "c[4:2]") + assert str(e.value) == "slice start > stop" + e = py.test.raises(IndexError, "c[6:6]") + assert str(e.value) == "index too large (expected 6 <= 5)" + +def test_setslice(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + c[1:3] = [100, 200] + assert list(c) == [0, 100, 200, 0, 0] + cp = c + 3 + cp[-1:1] = [300, 400] + assert list(c) == [0, 100, 300, 400, 0] + cp[-1:1] = iter([500, 600]) + assert list(c) == [0, 100, 500, 600, 0] + py.test.raises(ValueError, "cp[-1:1] = [1000]") + assert list(c) == [0, 100, 1000, 600, 0] + py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)") + assert list(c) == [0, 100, 700, 800, 0] + +def test_setslice_array(): + BIntP = new_pointer_type(new_primitive_type("int")) + BIntArray = new_array_type(BIntP, None) + c = newp(BIntArray, 5) + d = newp(BIntArray, [10, 20, 30]) + c[1:4] = d + assert list(c) == [0, 10, 20, 30, 0] + # + BShortP = new_pointer_type(new_primitive_type("short")) + BShortArray = new_array_type(BShortP, None) + d = newp(BShortArray, [40, 50]) + c[1:3] = d + assert list(c) == [0, 40, 50, 30, 0] + +def test_cdata_name_module_doc(): + p = new_primitive_type("signed char") + x = cast(p, 17) + assert x.__module__ == '_cffi_backend' + assert x.__name__ == '<cdata>' + assert hasattr(x, '__doc__') + +def test_different_types_of_ptr_equality(): + BVoidP = new_pointer_type(new_void_type()) + BIntP = new_pointer_type(new_primitive_type("int")) + x = cast(BVoidP, 12345) + assert x == cast(BIntP, 12345) + assert x != cast(BIntP, 12344) + assert hash(x) == hash(cast(BIntP, 12345)) + +def test_new_handle(): + import _weakref + BVoidP = new_pointer_type(new_void_type()) + BCharP = new_pointer_type(new_primitive_type("char")) + class mylist(list): + pass + o = mylist([2, 3, 4]) + x = newp_handle(BVoidP, o) + assert repr(x) == "<cdata 'void *' handle to [2, 3, 4]>" + assert x + assert from_handle(x) is o + assert from_handle(cast(BCharP, x)) is o + wr = _weakref.ref(o) + del o + import gc; gc.collect() + assert wr() is not None + assert from_handle(x) == list((2, 3, 4)) + assert from_handle(cast(BCharP, x)) == list((2, 3, 4)) + del x + for i in range(3): + if wr() is not None: + import gc; gc.collect() + assert wr() is None + py.test.raises(RuntimeError, from_handle, cast(BCharP, 0)) + +def test_new_handle_cycle(): + import _weakref + BVoidP = new_pointer_type(new_void_type()) + class A(object): + pass + o = A() + o.cycle = newp_handle(BVoidP, o) + wr = _weakref.ref(o) + del o + for i in range(3): + if wr() is not None: + import gc; gc.collect() + assert wr() is None + +def _test_bitfield_details(flag): + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + BInt = new_primitive_type("int") + BUInt = new_primitive_type("unsigned int") + BStruct = new_struct_type("struct foo1") + complete_struct_or_union(BStruct, [('a', BChar, -1), + ('b1', BInt, 9), + ('b2', BUInt, 7), + ('c', BChar, -1)], -1, -1, -1, flag) + if not (flag & SF_MSVC_BITFIELDS): # gcc, any variant + assert typeoffsetof(BStruct, 'c') == (BChar, 3) + assert sizeof(BStruct) == 4 + else: # msvc + assert typeoffsetof(BStruct, 'c') == (BChar, 8) + assert sizeof(BStruct) == 12 + assert alignof(BStruct) == 4 + # + p = newp(new_pointer_type(BStruct), None) + p.a = b'A' + p.b1 = -201 + p.b2 = 99 + p.c = b'\x9D' + raw = buffer(p)[:] + if sys.byteorder == 'little': + if flag & SF_MSVC_BITFIELDS: + assert raw == b'A\x00\x00\x007\xC7\x00\x00\x9D\x00\x00\x00' + elif flag & SF_GCC_LITTLE_ENDIAN: + assert raw == b'A7\xC7\x9D' + elif flag & SF_GCC_BIG_ENDIAN: + assert raw == b'A\xE3\x9B\x9D' + else: + raise AssertionError("bad flag") + else: + if flag & SF_MSVC_BITFIELDS: + assert raw == b'A\x00\x00\x00\x00\x00\xC77\x9D\x00\x00\x00' + elif flag & SF_GCC_LITTLE_ENDIAN: + assert raw == b'A\xC77\x9D' + elif flag & SF_GCC_BIG_ENDIAN: + assert raw == b'A\x9B\xE3\x9D' + else: + raise AssertionError("bad flag") + # + BStruct = new_struct_type("struct foo2") + complete_struct_or_union(BStruct, [('a', BChar, -1), + ('', BShort, 9), + ('c', BChar, -1)], -1, -1, -1, flag) + assert typeoffsetof(BStruct, 'c') == (BChar, 4) + if flag & SF_MSVC_BITFIELDS: + assert sizeof(BStruct) == 6 + assert alignof(BStruct) == 2 + elif flag & SF_GCC_X86_BITFIELDS: + assert sizeof(BStruct) == 5 + assert alignof(BStruct) == 1 + elif flag & SF_GCC_ARM_BITFIELDS: + assert sizeof(BStruct) == 6 + assert alignof(BStruct) == 2 + else: + raise AssertionError("bad flag") + # + BStruct = new_struct_type("struct foo2") + complete_struct_or_union(BStruct, [('a', BChar, -1), + ('', BInt, 0), + ('', BInt, 0), + ('c', BChar, -1)], -1, -1, -1, flag) + if flag & SF_MSVC_BITFIELDS: + assert typeoffsetof(BStruct, 'c') == (BChar, 1) + assert sizeof(BStruct) == 2 + assert alignof(BStruct) == 1 + elif flag & SF_GCC_X86_BITFIELDS: + assert typeoffsetof(BStruct, 'c') == (BChar, 4) + assert sizeof(BStruct) == 5 + assert alignof(BStruct) == 1 + elif flag & SF_GCC_ARM_BITFIELDS: + assert typeoffsetof(BStruct, 'c') == (BChar, 4) + assert sizeof(BStruct) == 8 + assert alignof(BStruct) == 4 + else: + raise AssertionError("bad flag") + + +SF_MSVC_BITFIELDS = 0x01 +SF_GCC_ARM_BITFIELDS = 0x02 +SF_GCC_X86_BITFIELDS = 0x10 + +SF_GCC_BIG_ENDIAN = 0x04 +SF_GCC_LITTLE_ENDIAN = 0x40 + +SF_PACKED = 0x08 + +def test_bitfield_as_x86_gcc(): + _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_LITTLE_ENDIAN) + +def test_bitfield_as_msvc(): + _test_bitfield_details(flag=SF_MSVC_BITFIELDS|SF_GCC_LITTLE_ENDIAN) + +def test_bitfield_as_arm_gcc(): + _test_bitfield_details(flag=SF_GCC_ARM_BITFIELDS|SF_GCC_LITTLE_ENDIAN) + +def test_bitfield_as_ppc_gcc(): + # PowerPC uses the same format as X86, but is big-endian + _test_bitfield_details(flag=SF_GCC_X86_BITFIELDS|SF_GCC_BIG_ENDIAN) + + +def test_struct_array_no_length(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, None) + BStruct = new_struct_type("foo") + py.test.raises(TypeError, complete_struct_or_union, + BStruct, [('x', BArray), + ('y', BInt)]) + # + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('x', BInt), + ('y', BArray)]) + assert sizeof(BStruct) == size_of_int() + d = BStruct.fields + assert len(d) == 2 + assert d[0][0] == 'x' + assert d[0][1].type is BInt + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'y' + assert d[1][1].type is BArray + assert d[1][1].offset == size_of_int() + assert d[1][1].bitshift == -2 + assert d[1][1].bitsize == -1 + # + p = newp(new_pointer_type(BStruct)) + p.x = 42 + assert p.x == 42 + assert typeof(p.y) is BArray + assert len(p.y) == 0 + assert p.y == cast(BIntP, p) + 1 + # + p = newp(new_pointer_type(BStruct), [100]) + assert p.x == 100 + assert len(p.y) == 0 + # + # Tests for + # ffi.new("struct_with_var_array *", [field.., [the_array_items..]]) + # ffi.new("struct_with_var_array *", [field.., array_size]) + plist = [] + for i in range(20): + if i % 2 == 0: + p = newp(new_pointer_type(BStruct), [100, [200, i, 400]]) + else: + p = newp(new_pointer_type(BStruct), [100, 3]) + p.y[1] = i + p.y[0] = 200 + assert p.y[2] == 0 + p.y[2] = 400 + assert len(p.y) == 3 + assert len(p[0].y) == 3 + assert len(buffer(p)) == sizeof(BInt) * 4 + assert sizeof(p[0]) == sizeof(BInt) * 4 + plist.append(p) + for i in range(20): + p = plist[i] + assert p.x == 100 + assert p.y[0] == 200 + assert p.y[1] == i + assert p.y[2] == 400 + assert list(p.y) == [200, i, 400] + # + # the following assignment works, as it normally would, for any array field + p.y = [501, 601] + assert list(p.y) == [501, 601, 400] + p[0].y = [500, 600] + assert list(p[0].y) == [500, 600, 400] + assert repr(p) == "<cdata 'foo *' owning %d bytes>" % ( + sizeof(BStruct) + 3 * sizeof(BInt),) + assert repr(p[0]) == "<cdata 'foo' owning %d bytes>" % ( + sizeof(BStruct) + 3 * sizeof(BInt),) + assert sizeof(p[0]) == sizeof(BStruct) + 3 * sizeof(BInt) + # + # from a non-owning pointer, we can't get the length + q = cast(new_pointer_type(BStruct), p) + assert q.y[0] == 500 + assert q[0].y[0] == 500 + py.test.raises(TypeError, len, q.y) + py.test.raises(TypeError, len, q[0].y) + assert typeof(q.y) is BIntP + assert typeof(q[0].y) is BIntP + assert sizeof(q[0]) == sizeof(BStruct) + # + # error cases + py.test.raises(IndexError, "p.y[4]") + py.test.raises(TypeError, "p.y = cast(BIntP, 0)") + py.test.raises(TypeError, "p.y = 15") + py.test.raises(TypeError, "p.y = None") + # + # accepting this may be specified by the C99 standard, + # or a GCC strangeness... + BStruct2 = new_struct_type("bar") + complete_struct_or_union(BStruct2, [('f', BStruct), + ('n', BInt)]) + p = newp(new_pointer_type(BStruct2), {'n': 42}) + assert p.n == 42 + # + # more error cases + py.test.raises(TypeError, newp, new_pointer_type(BStruct), [100, None]) + BArray4 = new_array_type(BIntP, 4) + BStruct4 = new_struct_type("test4") + complete_struct_or_union(BStruct4, [('a', BArray4)]) # not varsized + py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [None]) + py.test.raises(TypeError, newp, new_pointer_type(BStruct4), [4]) + p = newp(new_pointer_type(BStruct4), [[10, 20, 30]]) + assert p.a[0] == 10 + assert p.a[1] == 20 + assert p.a[2] == 30 + assert p.a[3] == 0 + +def test_struct_array_no_length_explicit_position(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BArray = new_array_type(BIntP, None) + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('x', BArray, -1, 0), # actually 3 items + ('y', BInt, -1, 12)]) + p = newp(new_pointer_type(BStruct), [[10, 20], 30]) + assert p.x[0] == 10 + assert p.x[1] == 20 + assert p.x[2] == 0 + assert p.y == 30 + p = newp(new_pointer_type(BStruct), {'x': [40], 'y': 50}) + assert p.x[0] == 40 + assert p.x[1] == 0 + assert p.x[2] == 0 + assert p.y == 50 + p = newp(new_pointer_type(BStruct), {'y': 60}) + assert p.x[0] == 0 + assert p.x[1] == 0 + assert p.x[2] == 0 + assert p.y == 60 + # + # This "should" work too, allocating a larger structure + # (a bit strange in this case, but useful in general) + plist = [] + for i in range(20): + p = newp(new_pointer_type(BStruct), [[10, 20, 30, 40, 50, 60, 70]]) + plist.append(p) + for i in range(20): + p = plist[i] + assert p.x[0] == 10 + assert p.x[1] == 20 + assert p.x[2] == 30 + assert p.x[3] == 40 == p.y + assert p.x[4] == 50 + assert p.x[5] == 60 + assert p.x[6] == 70 + +def test_struct_array_not_aligned(): + # struct a { int x; char y; char z[]; }; + # ends up of size 8, but 'z' is at offset 5 + BChar = new_primitive_type("char") + BInt = new_primitive_type("int") + BCharP = new_pointer_type(BChar) + BArray = new_array_type(BCharP, None) + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('x', BInt), + ('y', BChar), + ('z', BArray)]) + assert sizeof(BStruct) == 2 * size_of_int() + def offsetof(BType, fieldname): + return typeoffsetof(BType, fieldname)[1] + base = offsetof(BStruct, 'z') + assert base == size_of_int() + 1 + # + p = newp(new_pointer_type(BStruct), {'z': 3}) + assert sizeof(p[0]) == base + 3 + q = newp(new_pointer_type(BStruct), {'z': size_of_int()}) + assert sizeof(q) == size_of_ptr() + assert sizeof(q[0]) == base + size_of_int() + assert len(p.z) == 3 + assert len(p[0].z) == 3 + assert len(q.z) == size_of_int() + assert len(q[0].z) == size_of_int() + +def test_ass_slice(): + BChar = new_primitive_type("char") + BArray = new_array_type(new_pointer_type(BChar), None) + p = newp(BArray, b"foobar") + p[2:5] = [b"*", b"Z", b"T"] + p[1:3] = b"XY" + assert list(p) == [b"f", b"X", b"Y", b"Z", b"T", b"r", b"\x00"] + py.test.raises(TypeError, "p[1:5] = u+'XYZT'") + py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") + # + for typename in ["wchar_t", "char16_t", "char32_t"]: + BUniChar = new_primitive_type(typename) + BArray = new_array_type(new_pointer_type(BUniChar), None) + p = newp(BArray, u+"foobar") + p[2:5] = [u+"*", u+"Z", u+"T"] + p[1:3] = u+"XY" + assert list(p) == [u+"f", u+"X", u+"Y", u+"Z", u+"T", u+"r", u+"\x00"] + py.test.raises(TypeError, "p[1:5] = b'XYZT'") + py.test.raises(TypeError, "p[1:5] = [1, 2, 3, 4]") + +def test_void_p_arithmetic(): + BVoid = new_void_type() + BInt = new_primitive_type("intptr_t") + p = cast(new_pointer_type(BVoid), 100000) + assert int(cast(BInt, p)) == 100000 + assert int(cast(BInt, p + 42)) == 100042 + assert int(cast(BInt, p - (-42))) == 100042 + assert (p + 42) - p == 42 + q = cast(new_pointer_type(new_primitive_type("char")), 100000) + py.test.raises(TypeError, "p - q") + py.test.raises(TypeError, "q - p") + py.test.raises(TypeError, "p + cast(new_primitive_type('int'), 42)") + py.test.raises(TypeError, "p - cast(new_primitive_type('int'), 42)") + +def test_sizeof_sliced_array(): + BInt = new_primitive_type("int") + BArray = new_array_type(new_pointer_type(BInt), 10) + p = newp(BArray, None) + assert sizeof(p[2:9]) == 7 * sizeof(BInt) + +def test_packed(): + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BShort = new_primitive_type("short") + for extra_args in [(SF_PACKED,), (0, 1)]: + BStruct = new_struct_type("struct foo") + complete_struct_or_union(BStruct, [('a1', BLong, -1), + ('a2', BChar, -1), + ('a3', BShort, -1)], + None, -1, -1, *extra_args) + d = BStruct.fields + assert len(d) == 3 + assert d[0][0] == 'a1' + assert d[0][1].type is BLong + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'a2' + assert d[1][1].type is BChar + assert d[1][1].offset == sizeof(BLong) + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert d[2][0] == 'a3' + assert d[2][1].type is BShort + assert d[2][1].offset == sizeof(BLong) + sizeof(BChar) + assert d[2][1].bitshift == -1 + assert d[2][1].bitsize == -1 + assert sizeof(BStruct) == sizeof(BLong) + sizeof(BChar) + sizeof(BShort) + assert alignof(BStruct) == 1 + # + BStruct2 = new_struct_type("struct foo") + complete_struct_or_union(BStruct2, [('b1', BChar, -1), + ('b2', BLong, -1)], + None, -1, -1, 0, 2) + d = BStruct2.fields + assert len(d) == 2 + assert d[0][0] == 'b1' + assert d[0][1].type is BChar + assert d[0][1].offset == 0 + assert d[0][1].bitshift == -1 + assert d[0][1].bitsize == -1 + assert d[1][0] == 'b2' + assert d[1][1].type is BLong + assert d[1][1].offset == 2 + assert d[1][1].bitshift == -1 + assert d[1][1].bitsize == -1 + assert sizeof(BStruct2) == 2 + sizeof(BLong) + assert alignof(BStruct2) == 2 + +def test_packed_with_bitfields(): + if sys.platform == "win32": + py.test.skip("testing gcc behavior") + BLong = new_primitive_type("long") + BChar = new_primitive_type("char") + BStruct = new_struct_type("struct foo") + py.test.raises(NotImplementedError, + complete_struct_or_union, + BStruct, [('a1', BLong, 30), + ('a2', BChar, 5)], + None, -1, -1, SF_PACKED) + +def test_from_buffer(): + import array + a = array.array('H', [10000, 20000, 30000]) + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + c = from_buffer(BCharA, a) + assert typeof(c) is BCharA + assert len(c) == 6 + assert repr(c) == "<cdata 'char[]' buffer len 6 from 'array.array' object>" + p = new_pointer_type(new_primitive_type("unsigned short")) + cast(p, c)[1] += 500 + assert list(a) == [10000, 20500, 30000] + +def test_from_buffer_not_str_unicode(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + p1 = from_buffer(BCharA, b"foo") + assert p1 == from_buffer(BCharA, b"foo") + import gc; gc.collect() + assert p1 == from_buffer(BCharA, b"foo") + py.test.raises(TypeError, from_buffer, BCharA, u+"foo") + try: + from __builtin__ import buffer + except ImportError: + pass + else: + # Python 2 only + contents = from_buffer(BCharA, buffer(b"foo")) + assert len(contents) == len(p1) + for i in range(len(contents)): + assert contents[i] == p1[i] + p4 = buffer(u+"foo") + contents = from_buffer(BCharA, buffer(u+"foo")) + assert len(contents) == len(p4) + for i in range(len(contents)): + assert contents[i] == p4[i] + try: + from __builtin__ import memoryview + except ImportError: + pass + else: + contents = from_buffer(BCharA, memoryview(b"foo")) + assert len(contents) == len(p1) + for i in range(len(contents)): + assert contents[i] == p1[i] + + +def test_from_buffer_bytearray(): + a = bytearray(b"xyz") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + p = from_buffer(BCharA, a) + assert typeof(p) is BCharA + assert len(p) == 3 + assert repr(p) == "<cdata 'char[]' buffer len 3 from 'bytearray' object>" + assert p[2] == b"z" + p[2] = b"." + assert a[2] == ord(".") + a[2] = ord("?") + assert p[2] == b"?" + +def test_from_buffer_more_cases(): + try: + from _cffi_backend import _testbuff + except ImportError: + py.test.skip("not for pypy") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + # + def check1(bufobj, expected): + c = from_buffer(BCharA, bufobj) + assert typeof(c) is BCharA + if sys.version_info >= (3,): + expected = [bytes(c, "ascii") for c in expected] + assert list(c) == list(expected) + # + def check(methods, expected, expected_for_memoryview=None): + if sys.version_info >= (3,): + if methods <= 7: + return + if expected_for_memoryview is not None: + expected = expected_for_memoryview + class X(object): + pass + _testbuff(X, methods) + bufobj = X() + check1(bufobj, expected) + try: + from __builtin__ import buffer + bufobjb = buffer(bufobj) + except (TypeError, ImportError): + pass + else: + check1(bufobjb, expected) + try: + bufobjm = memoryview(bufobj) + except (TypeError, NameError): + pass + else: + check1(bufobjm, expected_for_memoryview or expected) + # + check(1, "RDB") + check(2, "WRB") + check(4, "CHB") + check(8, "GTB") + check(16, "ROB") + # + check(1 | 2, "RDB") + check(1 | 4, "RDB") + check(2 | 4, "CHB") + check(1 | 8, "RDB", "GTB") + check(1 | 16, "RDB", "ROB") + check(2 | 8, "WRB", "GTB") + check(2 | 16, "WRB", "ROB") + check(4 | 8, "CHB", "GTB") + check(4 | 16, "CHB", "ROB") + +def test_from_buffer_require_writable(): + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + p1 = from_buffer(BCharA, b"foo", False) + assert p1 == from_buffer(BCharA, b"foo", False) + py.test.raises((TypeError, BufferError), from_buffer, BCharA, b"foo", True) + ba = bytearray(b"foo") + p1 = from_buffer(BCharA, ba, True) + p1[0] = b"g" + assert ba == b"goo" + +def test_from_buffer_types(): + BInt = new_primitive_type("int") + BIntP = new_pointer_type(BInt) + BIntA = new_array_type(BIntP, None) + lst = [-12345678, 87654321, 489148] + bytestring = buffer(newp(BIntA, lst))[:] + b'XYZ' + # + p1 = from_buffer(BIntA, bytestring) # int[] + assert typeof(p1) is BIntA + assert len(p1) == 3 + assert p1[0] == lst[0] + assert p1[1] == lst[1] + assert p1[2] == lst[2] + py.test.raises(IndexError, "p1[3]") + py.test.raises(IndexError, "p1[-1]") + # + py.test.raises(TypeError, from_buffer, BInt, bytestring) + py.test.raises(TypeError, from_buffer, BIntP, bytestring) + # + BIntA2 = new_array_type(BIntP, 2) + p2 = from_buffer(BIntA2, bytestring) # int[2] + assert typeof(p2) is BIntA2 + assert len(p2) == 2 + assert p2[0] == lst[0] + assert p2[1] == lst[1] + py.test.raises(IndexError, "p2[2]") + py.test.raises(IndexError, "p2[-1]") + assert p2 == p1 + # + BIntA4 = new_array_type(BIntP, 4) # int[4]: too big + py.test.raises(ValueError, from_buffer, BIntA4, bytestring) + # + BStruct = new_struct_type("foo") + complete_struct_or_union(BStruct, [('a1', BInt, -1), + ('a2', BInt, -1)]) + BStructP = new_pointer_type(BStruct) + BStructA = new_array_type(BStructP, None) + p1 = from_buffer(BStructA, bytestring) # struct[] + assert len(p1) == 1 + assert typeof(p1) is BStructA + assert p1[0].a1 == lst[0] + assert p1[0].a2 == lst[1] + py.test.raises(IndexError, "p1[1]") + # + BEmptyStruct = new_struct_type("empty") + complete_struct_or_union(BEmptyStruct, [], Ellipsis, 0) + assert sizeof(BEmptyStruct) == 0 + BEmptyStructP = new_pointer_type(BEmptyStruct) + BEmptyStructA = new_array_type(BEmptyStructP, None) + py.test.raises(ZeroDivisionError, from_buffer, # empty[] + BEmptyStructA, bytestring) + # + BEmptyStructA5 = new_array_type(BEmptyStructP, 5) + p1 = from_buffer(BEmptyStructA5, bytestring) # struct empty[5] + assert typeof(p1) is BEmptyStructA5 + assert len(p1) == 5 + assert cast(BIntP, p1) == from_buffer(BIntA, bytestring) + +def test_memmove(): + Short = new_primitive_type("short") + ShortA = new_array_type(new_pointer_type(Short), None) + Char = new_primitive_type("char") + CharA = new_array_type(new_pointer_type(Char), None) + p = newp(ShortA, [-1234, -2345, -3456, -4567, -5678]) + memmove(p, p + 1, 4) + assert list(p) == [-2345, -3456, -3456, -4567, -5678] + p[2] = 999 + memmove(p + 2, p, 6) + assert list(p) == [-2345, -3456, -2345, -3456, 999] + memmove(p + 4, newp(CharA, b"\x71\x72"), 2) + if sys.byteorder == 'little': + assert list(p) == [-2345, -3456, -2345, -3456, 0x7271] + else: + assert list(p) == [-2345, -3456, -2345, -3456, 0x7172] + +def test_memmove_buffer(): + import array + Short = new_primitive_type("short") + ShortA = new_array_type(new_pointer_type(Short), None) + a = array.array('H', [10000, 20000, 30000]) + p = newp(ShortA, 5) + memmove(p, a, 6) + assert list(p) == [10000, 20000, 30000, 0, 0] + memmove(p + 1, a, 6) + assert list(p) == [10000, 10000, 20000, 30000, 0] + b = array.array('h', [-1000, -2000, -3000]) + memmove(b, a, 4) + assert b.tolist() == [10000, 20000, -3000] + assert a.tolist() == [10000, 20000, 30000] + p[0] = 999 + p[1] = 998 + p[2] = 997 + p[3] = 996 + p[4] = 995 + memmove(b, p, 2) + assert b.tolist() == [999, 20000, -3000] + memmove(b, p + 2, 4) + assert b.tolist() == [997, 996, -3000] + p[2] = -p[2] + p[3] = -p[3] + memmove(b, p + 2, 6) + assert b.tolist() == [-997, -996, 995] + +def test_memmove_readonly_readwrite(): + SignedChar = new_primitive_type("signed char") + SignedCharA = new_array_type(new_pointer_type(SignedChar), None) + p = newp(SignedCharA, 5) + memmove(p, b"abcde", 3) + assert list(p) == [ord("a"), ord("b"), ord("c"), 0, 0] + memmove(p, bytearray(b"ABCDE"), 2) + assert list(p) == [ord("A"), ord("B"), ord("c"), 0, 0] + py.test.raises((TypeError, BufferError), memmove, b"abcde", p, 3) + ba = bytearray(b"xxxxx") + memmove(dest=ba, src=p, n=3) + assert ba == bytearray(b"ABcxx") + memmove(ba, b"EFGH", 4) + assert ba == bytearray(b"EFGHx") + +def test_memmove_sign_check(): + SignedChar = new_primitive_type("signed char") + SignedCharA = new_array_type(new_pointer_type(SignedChar), None) + p = newp(SignedCharA, 5) + py.test.raises(ValueError, memmove, p, p + 1, -1) # not segfault + +def test_memmove_bad_cdata(): + BInt = new_primitive_type("int") + p = cast(BInt, 42) + py.test.raises(TypeError, memmove, p, bytearray(b'a'), 1) + py.test.raises(TypeError, memmove, bytearray(b'a'), p, 1) + +def test_dereference_null_ptr(): + BInt = new_primitive_type("int") + BIntPtr = new_pointer_type(BInt) + p = cast(BIntPtr, 0) + py.test.raises(RuntimeError, "p[0]") + py.test.raises(RuntimeError, "p[0] = 42") + py.test.raises(RuntimeError, "p[42]") + py.test.raises(RuntimeError, "p[42] = -1") + +def test_mixup(): + BStruct1 = new_struct_type("foo") + BStruct2 = new_struct_type("foo") # <= same name as BStruct1 + BStruct3 = new_struct_type("bar") + BStruct1Ptr = new_pointer_type(BStruct1) + BStruct2Ptr = new_pointer_type(BStruct2) + BStruct3Ptr = new_pointer_type(BStruct3) + BStruct1PtrPtr = new_pointer_type(BStruct1Ptr) + BStruct2PtrPtr = new_pointer_type(BStruct2Ptr) + BStruct3PtrPtr = new_pointer_type(BStruct3Ptr) + pp1 = newp(BStruct1PtrPtr) + pp2 = newp(BStruct2PtrPtr) + pp3 = newp(BStruct3PtrPtr) + pp1[0] = pp1[0] + e = py.test.raises(TypeError, "pp3[0] = pp1[0]") + assert str(e.value).startswith("initializer for ctype 'bar *' must be a ") + assert str(e.value).endswith(", not cdata 'foo *'") + e = py.test.raises(TypeError, "pp2[0] = pp1[0]") + assert str(e.value) == ("initializer for ctype 'foo *' appears indeed to " + "be 'foo *', but the types are different (check " + "that you are not e.g. mixing up different ffi " + "instances)") + +def test_stdcall_function_type(): + assert FFI_CDECL == FFI_DEFAULT_ABI + try: + stdcall = FFI_STDCALL + except NameError: + stdcall = FFI_DEFAULT_ABI + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False, stdcall) + if stdcall != FFI_DEFAULT_ABI: + assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>" + else: + assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" + +def test_get_common_types(): + d = {} + _get_common_types(d) + assert d['bool'] == '_Bool' + +def test_unpack(): + BChar = new_primitive_type("char") + BArray = new_array_type(new_pointer_type(BChar), 10) # char[10] + p = newp(BArray, b"abc\x00def") + p0 = p + assert unpack(p, 10) == b"abc\x00def\x00\x00\x00" + assert unpack(p+1, 5) == b"bc\x00de" + + for typename in ["wchar_t", "char16_t", "char32_t"]: + BWChar = new_primitive_type(typename) + BArray = new_array_type(new_pointer_type(BWChar), 10) # wchar_t[10] + p = newp(BArray, u"abc\x00def") + assert unpack(p, 10) == u"abc\x00def\x00\x00\x00" + + for typename, samples in [ + ("uint8_t", [0, 2**8-1]), + ("uint16_t", [0, 2**16-1]), + ("uint32_t", [0, 2**32-1]), + ("uint64_t", [0, 2**64-1]), + ("int8_t", [-2**7, 2**7-1]), + ("int16_t", [-2**15, 2**15-1]), + ("int32_t", [-2**31, 2**31-1]), + ("int64_t", [-2**63, 2**63-1]), + ("_Bool", [False, True]), + ("float", [0.0, 10.5]), + ("double", [12.34, 56.78]), + ]: + BItem = new_primitive_type(typename) + BArray = new_array_type(new_pointer_type(BItem), 10) + p = newp(BArray, samples) + result = unpack(p, len(samples)) + assert result == samples + for i in range(len(samples)): + assert result[i] == p[i] and type(result[i]) is type(p[i]) + assert (type(result[i]) is bool) == (type(samples[i]) is bool) + # + BInt = new_primitive_type("int") + py.test.raises(TypeError, unpack, p) + py.test.raises(TypeError, unpack, b"foobar", 6) + py.test.raises(TypeError, unpack, cast(BInt, 42), 1) + # + BPtr = new_pointer_type(BInt) + random_ptr = cast(BPtr, -424344) + other_ptr = cast(BPtr, 54321) + BArray = new_array_type(new_pointer_type(BPtr), None) + lst = unpack(newp(BArray, [random_ptr, other_ptr]), 2) + assert lst == [random_ptr, other_ptr] + # + BFunc = new_function_type((BInt, BInt), BInt, False) + BFuncPtr = new_pointer_type(BFunc) + lst = unpack(newp(new_array_type(BFuncPtr, None), 2), 2) + assert len(lst) == 2 + assert not lst[0] and not lst[1] + assert typeof(lst[0]) is BFunc + # + BStruct = new_struct_type("foo") + BStructPtr = new_pointer_type(BStruct) + e = py.test.raises(ValueError, unpack, cast(BStructPtr, 42), 5) + assert str(e.value) == "'foo *' points to items of unknown size" + complete_struct_or_union(BStruct, [('a1', BInt, -1), + ('a2', BInt, -1)]) + array_of_structs = newp(new_array_type(BStructPtr, None), [[4,5], [6,7]]) + lst = unpack(array_of_structs, 2) + assert typeof(lst[0]) is BStruct + assert lst[0].a1 == 4 and lst[1].a2 == 7 + # + py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 0) + py.test.raises(RuntimeError, unpack, cast(new_pointer_type(BChar), 0), 10) + # + py.test.raises(ValueError, unpack, p0, -1) + py.test.raises(ValueError, unpack, p, -1) + +def test_cdata_dir(): + BInt = new_primitive_type("int") + p = cast(BInt, 42) + check_dir(p, []) + p = newp(new_array_type(new_pointer_type(BInt), None), 5) + check_dir(p, []) + BStruct = new_struct_type("foo") + p = cast(new_pointer_type(BStruct), 0) + check_dir(p, []) # opaque + complete_struct_or_union(BStruct, [('a2', BInt, -1), + ('a1', BInt, -1)]) + check_dir(p, ['a1', 'a2']) # always sorted + p = newp(new_pointer_type(BStruct), None) + check_dir(p, ['a1', 'a2']) + check_dir(p[0], ['a1', 'a2']) + pp = newp(new_pointer_type(new_pointer_type(BStruct)), p) + check_dir(pp, []) + check_dir(pp[0], ['a1', 'a2']) + check_dir(pp[0][0], ['a1', 'a2']) + +def test_char_pointer_conversion(): + import warnings + assert __version__.startswith("1."), ( + "the warning will be an error if we ever release cffi 2.x") + BCharP = new_pointer_type(new_primitive_type("char")) + BIntP = new_pointer_type(new_primitive_type("int")) + BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) + z1 = cast(BCharP, 0) + z2 = cast(BIntP, 0) + z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + newp(new_pointer_type(BIntP), z1) # warn + assert len(w) == 1 + newp(new_pointer_type(BVoidP), z1) # fine + assert len(w) == 1 + newp(new_pointer_type(BCharP), z2) # warn + assert len(w) == 2 + newp(new_pointer_type(BVoidP), z2) # fine + assert len(w) == 2 + newp(new_pointer_type(BCharP), z3) # fine + assert len(w) == 2 + newp(new_pointer_type(BIntP), z3) # fine + assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 + # check that the warnings are associated with lines in this file + assert w[1].lineno == w[0].lineno + 4 + +def test_primitive_comparison(): + def assert_eq(a, b): + assert (a == b) is True + assert (b == a) is True + assert (a != b) is False + assert (b != a) is False + assert (a < b) is False + assert (a <= b) is True + assert (a > b) is False + assert (a >= b) is True + assert (b < a) is False + assert (b <= a) is True + assert (b > a) is False + assert (b >= a) is True + assert hash(a) == hash(b) + def assert_lt(a, b, check_hash=True): + assert (a == b) is False + assert (b == a) is False + assert (a != b) is True + assert (b != a) is True + assert (a < b) is True + assert (a <= b) is True + assert (a > b) is False + assert (a >= b) is False + assert (b < a) is False + assert (b <= a) is False + assert (b > a) is True + assert (b >= a) is True + if check_hash: + assert hash(a) != hash(b) # (or at least, it is unlikely) + def assert_gt(a, b, check_hash=True): + assert_lt(b, a, check_hash) + def assert_ne(a, b): + assert (a == b) is False + assert (b == a) is False + assert (a != b) is True + assert (b != a) is True + if strict_compare: + py.test.raises(TypeError, "a < b") + py.test.raises(TypeError, "a <= b") + py.test.raises(TypeError, "a > b") + py.test.raises(TypeError, "a >= b") + py.test.raises(TypeError, "b < a") + py.test.raises(TypeError, "b <= a") + py.test.raises(TypeError, "b > a") + py.test.raises(TypeError, "b >= a") + elif a < b: + assert_lt(a, b) + else: + assert_lt(b, a) + assert_eq(5, 5) + assert_lt(3, 5) + assert_ne('5', 5) + # + t1 = new_primitive_type("char") + t2 = new_primitive_type("int") + t3 = new_primitive_type("unsigned char") + t4 = new_primitive_type("unsigned int") + t5 = new_primitive_type("float") + t6 = new_primitive_type("double") + assert_eq(cast(t1, 65), b'A') + assert_lt(cast(t1, 64), b'\x99') + assert_gt(cast(t1, 200), b'A') + assert_ne(cast(t1, 65), 65) + assert_eq(cast(t2, -25), -25) + assert_lt(cast(t2, -25), -24) + assert_gt(cast(t2, -25), -26) + assert_eq(cast(t3, 65), 65) + assert_ne(cast(t3, 65), b'A') + assert_ne(cast(t3, 65), cast(t1, 65)) + assert_gt(cast(t4, -1), -1, check_hash=False) + assert_gt(cast(t4, -1), cast(t2, -1), check_hash=False) + assert_gt(cast(t4, -1), 99999) + assert_eq(cast(t4, -1), 256 ** size_of_int() - 1) + assert_eq(cast(t5, 3.0), 3) + assert_eq(cast(t5, 3.5), 3.5) + assert_lt(cast(t5, 3.3), 3.3) # imperfect rounding + assert_eq(cast(t6, 3.3), 3.3) + assert_eq(cast(t5, 3.5), cast(t6, 3.5)) + assert_lt(cast(t5, 3.1), cast(t6, 3.1)) # imperfect rounding + assert_eq(cast(t5, 7.0), cast(t3, 7)) + assert_lt(cast(t5, 3.1), 3.101) + assert_gt(cast(t5, 3.1), 3) + +def test_explicit_release_new(): + # release() on a ffi.new() object has no effect on CPython, but + # really releases memory on PyPy. We can't test that effect + # though, because a released cdata is not marked. + BIntP = new_pointer_type(new_primitive_type("int")) + p = newp(BIntP) + p[0] = 42 + py.test.raises(IndexError, "p[1]") + release(p) + # here, reading p[0] might give garbage or segfault... + release(p) # no effect + # + BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('p', BIntP, -1)]) + pstruct = newp(BStructP) + assert pstruct.p == cast(BIntP, 0) + release(pstruct) + # here, reading pstruct.p might give garbage or segfault... + release(pstruct) # no effect + +def test_explicit_release_new_contextmgr(): + BIntP = new_pointer_type(new_primitive_type("int")) + with newp(BIntP) as p: + p[0] = 42 + assert p[0] == 42 + # here, reading p[0] might give garbage or segfault... + release(p) # no effect + +def test_explicit_release_badtype(): + BIntP = new_pointer_type(new_primitive_type("int")) + p = cast(BIntP, 12345) + py.test.raises(ValueError, release, p) + py.test.raises(ValueError, release, p) + BStruct = new_struct_type("struct foo") + BStructP = new_pointer_type(BStruct) + complete_struct_or_union(BStruct, [('p', BIntP, -1)]) + pstruct = newp(BStructP) + py.test.raises(ValueError, release, pstruct[0]) + +def test_explicit_release_badtype_contextmgr(): + BIntP = new_pointer_type(new_primitive_type("int")) + p = cast(BIntP, 12345) + py.test.raises(ValueError, "with p: pass") + py.test.raises(ValueError, "with p: pass") + +def test_explicit_release_gc(): + BIntP = new_pointer_type(new_primitive_type("int")) + seen = [] + intp1 = newp(BIntP, 12345) + p1 = cast(BIntP, intp1) + p = gcp(p1, seen.append) + assert seen == [] + release(p) + assert seen == [p1] + assert p1[0] == 12345 + assert p[0] == 12345 # true so far, but might change to raise RuntimeError + release(p) # no effect + +def test_explicit_release_gc_contextmgr(): + BIntP = new_pointer_type(new_primitive_type("int")) + seen = [] + intp1 = newp(BIntP, 12345) + p1 = cast(BIntP, intp1) + p = gcp(p1, seen.append) + with p: + assert p[0] == 12345 + assert seen == [] + assert seen == [p1] + assert p1[0] == 12345 + assert p[0] == 12345 # true so far, but might change to raise RuntimeError + release(p) # no effect + +def test_explicit_release_from_buffer(): + a = bytearray(b"xyz") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + p = from_buffer(BCharA, a) + assert p[2] == b"z" + release(p) + assert p[2] == b"z" # true so far, but might change to raise RuntimeError + release(p) # no effect + +def test_explicit_release_from_buffer_contextmgr(): + a = bytearray(b"xyz") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + p = from_buffer(BCharA, a) + with p: + assert p[2] == b"z" + assert p[2] == b"z" # true so far, but might change to raise RuntimeError + release(p) # no effect + +def test_explicit_release_bytearray_on_cpython(): + if '__pypy__' in sys.builtin_module_names: + py.test.skip("pypy's bytearray are never locked") + a = bytearray(b"xyz") + BChar = new_primitive_type("char") + BCharP = new_pointer_type(BChar) + BCharA = new_array_type(BCharP, None) + a += b't' * 10 + p = from_buffer(BCharA, a) + py.test.raises(BufferError, "a += b'u' * 100") + release(p) + a += b'v' * 100 + release(p) # no effect + a += b'w' * 1000 + assert a == bytearray(b"xyz" + b't' * 10 + b'v' * 100 + b'w' * 1000) diff --git a/c/wchar_helper.h b/c/wchar_helper.h new file mode 100644 index 0000000..8e6ea58 --- /dev/null +++ b/c/wchar_helper.h @@ -0,0 +1,246 @@ +/* + * wchar_t helpers + */ + +typedef uint16_t cffi_char16_t; +typedef uint32_t cffi_char32_t; + + +#if Py_UNICODE_SIZE == 2 + +/* Before Python 2.7, PyUnicode_FromWideChar is not able to convert + wchar_t values greater than 65535 into two-unicode-characters surrogates. + But even the Python 2.7 version doesn't detect wchar_t values that are + out of range(1114112), and just returns nonsense. + + From cffi 1.11 we can't use it anyway, because we need a version + with char32_t input types. +*/ +static PyObject * +_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) +{ + PyObject *unicode; + register Py_ssize_t i; + Py_ssize_t alloc; + const cffi_char32_t *orig_w; + + alloc = size; + orig_w = w; + for (i = size; i > 0; i--) { + if (*w > 0xFFFF) + alloc++; + w++; + } + w = orig_w; + unicode = PyUnicode_FromUnicode(NULL, alloc); + if (!unicode) + return NULL; + + /* Copy the wchar_t data into the new object */ + { + register Py_UNICODE *u; + u = PyUnicode_AS_UNICODE(unicode); + for (i = size; i > 0; i--) { + if (*w > 0xFFFF) { + cffi_char32_t ordinal; + if (*w > 0x10FFFF) { + PyErr_Format(PyExc_ValueError, + "char32_t out of range for " + "conversion to unicode: 0x%x", (int)*w); + Py_DECREF(unicode); + return NULL; + } + ordinal = *w++; + ordinal -= 0x10000; + *u++ = 0xD800 | (ordinal >> 10); + *u++ = 0xDC00 | (ordinal & 0x3FF); + } + else + *u++ = *w++; + } + } + return unicode; +} + +static PyObject * +_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) +{ + return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); +} + +#else /* Py_UNICODE_SIZE == 4 */ + +static PyObject * +_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) +{ + return PyUnicode_FromUnicode((const Py_UNICODE *)w, size); +} + +static PyObject * +_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) +{ + /* 'size' is the length of the 'w' array */ + PyObject *result = PyUnicode_FromUnicode(NULL, size); + + if (result != NULL) { + Py_UNICODE *u_base = PyUnicode_AS_UNICODE(result); + Py_UNICODE *u = u_base; + + if (size == 1) { /* performance only */ + *u = (cffi_char32_t)*w; + } + else { + while (size > 0) { + cffi_char32_t ch = *w++; + size--; + if (0xD800 <= ch && ch <= 0xDBFF && size > 0) { + cffi_char32_t ch2 = *w; + if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { + ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; + w++; + size--; + } + } + *u++ = ch; + } + if (PyUnicode_Resize(&result, u - u_base) < 0) { + Py_DECREF(result); + return NULL; + } + } + } + return result; +} + +#endif + + +#define IS_SURROGATE(u) (0xD800 <= (u)[0] && (u)[0] <= 0xDBFF && \ + 0xDC00 <= (u)[1] && (u)[1] <= 0xDFFF) +#define AS_SURROGATE(u) (0x10000 + (((u)[0] - 0xD800) << 10) + \ + ((u)[1] - 0xDC00)) + +static int +_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, + char *err_got) +{ + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + if (PyUnicode_GET_SIZE(unicode) != 1) { + sprintf(err_got, "unicode string of length %zd", + PyUnicode_GET_SIZE(unicode)); + return -1; + } +#if Py_UNICODE_SIZE == 4 + if (((unsigned int)u[0]) > 0xFFFF) + { + sprintf(err_got, "larger-than-0xFFFF character"); + return -1; + } +#endif + *result = (cffi_char16_t)u[0]; + return 0; +} + +static int +_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, + char *err_got) +{ + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + if (PyUnicode_GET_SIZE(unicode) == 1) { + *result = (cffi_char32_t)u[0]; + return 0; + } +#if Py_UNICODE_SIZE == 2 + if (PyUnicode_GET_SIZE(unicode) == 2 && IS_SURROGATE(u)) { + *result = AS_SURROGATE(u); + return 0; + } +#endif + sprintf(err_got, "unicode string of length %zd", + PyUnicode_GET_SIZE(unicode)); + return -1; +} + +static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) +{ + Py_ssize_t length = PyUnicode_GET_SIZE(unicode); + Py_ssize_t result = length; + +#if Py_UNICODE_SIZE == 4 + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + Py_ssize_t i; + + for (i=0; i<length; i++) { + if (u[i] > 0xFFFF) + result++; + } +#endif + return result; +} + +static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) +{ + Py_ssize_t length = PyUnicode_GET_SIZE(unicode); + Py_ssize_t result = length; + +#if Py_UNICODE_SIZE == 2 + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + Py_ssize_t i; + + for (i=0; i<length-1; i++) { + if (IS_SURROGATE(u+i)) + result--; + } +#endif + return result; +} + +static int _my_PyUnicode_AsChar16(PyObject *unicode, + cffi_char16_t *result, + Py_ssize_t resultlen) +{ + Py_ssize_t len = PyUnicode_GET_SIZE(unicode); + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + Py_ssize_t i; + for (i=0; i<len; i++) { +#if Py_UNICODE_SIZE == 2 + cffi_char16_t ordinal = u[i]; +#else + cffi_char32_t ordinal = u[i]; + if (ordinal > 0xFFFF) { + if (ordinal > 0x10FFFF) { + PyErr_Format(PyExc_ValueError, + "unicode character out of range for " + "conversion to char16_t: 0x%x", (int)ordinal); + return -1; + } + ordinal -= 0x10000; + *result++ = 0xD800 | (ordinal >> 10); + *result++ = 0xDC00 | (ordinal & 0x3FF); + continue; + } +#endif + *result++ = ordinal; + } + return 0; +} + +static int _my_PyUnicode_AsChar32(PyObject *unicode, + cffi_char32_t *result, + Py_ssize_t resultlen) +{ + Py_UNICODE *u = PyUnicode_AS_UNICODE(unicode); + Py_ssize_t i; + for (i=0; i<resultlen; i++) { + cffi_char32_t ordinal = *u; +#if Py_UNICODE_SIZE == 2 + if (IS_SURROGATE(u)) { + ordinal = AS_SURROGATE(u); + u++; + } +#endif + result[i] = ordinal; + u++; + } + return 0; +} diff --git a/c/wchar_helper_3.h b/c/wchar_helper_3.h new file mode 100644 index 0000000..f15464e --- /dev/null +++ b/c/wchar_helper_3.h @@ -0,0 +1,149 @@ +/* + * wchar_t helpers, version CPython >= 3.3. + * + * CPython 3.3 added support for sys.maxunicode == 0x10FFFF on all + * platforms, even ones with wchar_t limited to 2 bytes. As such, + * this code here works from the outside like wchar_helper.h in the + * case Py_UNICODE_SIZE == 4, but the implementation is very different. + */ + +typedef uint16_t cffi_char16_t; +typedef uint32_t cffi_char32_t; + + +static PyObject * +_my_PyUnicode_FromChar32(const cffi_char32_t *w, Py_ssize_t size) +{ + return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, w, size); +} + +static PyObject * +_my_PyUnicode_FromChar16(const cffi_char16_t *w, Py_ssize_t size) +{ + /* are there any surrogate pairs, and if so, how many? */ + Py_ssize_t i, count_surrogates = 0; + for (i = 0; i < size - 1; i++) { + if (0xD800 <= w[i] && w[i] <= 0xDBFF && + 0xDC00 <= w[i+1] && w[i+1] <= 0xDFFF) + count_surrogates++; + } + if (count_surrogates == 0) { + /* no, fast path */ + return PyUnicode_FromKindAndData(PyUnicode_2BYTE_KIND, w, size); + } + else + { + PyObject *result = PyUnicode_New(size - count_surrogates, 0x10FFFF); + Py_UCS4 *data; + assert(PyUnicode_KIND(result) == PyUnicode_4BYTE_KIND); + data = PyUnicode_4BYTE_DATA(result); + + for (i = 0; i < size; i++) + { + cffi_char32_t ch = w[i]; + if (0xD800 <= ch && ch <= 0xDBFF && i < size - 1) { + cffi_char32_t ch2 = w[i + 1]; + if (0xDC00 <= ch2 && ch2 <= 0xDFFF) { + ch = (((ch & 0x3FF)<<10) | (ch2 & 0x3FF)) + 0x10000; + i++; + } + } + *data++ = ch; + } + return result; + } +} + +static int +_my_PyUnicode_AsSingleChar16(PyObject *unicode, cffi_char16_t *result, + char *err_got) +{ + cffi_char32_t ch; + if (PyUnicode_GET_LENGTH(unicode) != 1) { + sprintf(err_got, "unicode string of length %zd", + PyUnicode_GET_LENGTH(unicode)); + return -1; + } + ch = PyUnicode_READ_CHAR(unicode, 0); + + if (ch > 0xFFFF) + { + sprintf(err_got, "larger-than-0xFFFF character"); + return -1; + } + *result = (cffi_char16_t)ch; + return 0; +} + +static int +_my_PyUnicode_AsSingleChar32(PyObject *unicode, cffi_char32_t *result, + char *err_got) +{ + if (PyUnicode_GET_LENGTH(unicode) != 1) { + sprintf(err_got, "unicode string of length %zd", + PyUnicode_GET_LENGTH(unicode)); + return -1; + } + *result = PyUnicode_READ_CHAR(unicode, 0); + return 0; +} + +static Py_ssize_t _my_PyUnicode_SizeAsChar16(PyObject *unicode) +{ + Py_ssize_t length = PyUnicode_GET_LENGTH(unicode); + Py_ssize_t result = length; + unsigned int kind = PyUnicode_KIND(unicode); + + if (kind == PyUnicode_4BYTE_KIND) + { + Py_UCS4 *data = PyUnicode_4BYTE_DATA(unicode); + Py_ssize_t i; + for (i = 0; i < length; i++) { + if (data[i] > 0xFFFF) + result++; + } + } + return result; +} + +static Py_ssize_t _my_PyUnicode_SizeAsChar32(PyObject *unicode) +{ + return PyUnicode_GET_LENGTH(unicode); +} + +static int _my_PyUnicode_AsChar16(PyObject *unicode, + cffi_char16_t *result, + Py_ssize_t resultlen) +{ + Py_ssize_t len = PyUnicode_GET_LENGTH(unicode); + unsigned int kind = PyUnicode_KIND(unicode); + void *data = PyUnicode_DATA(unicode); + Py_ssize_t i; + + for (i = 0; i < len; i++) { + cffi_char32_t ordinal = PyUnicode_READ(kind, data, i); + if (ordinal > 0xFFFF) { + if (ordinal > 0x10FFFF) { + PyErr_Format(PyExc_ValueError, + "unicode character out of range for " + "conversion to char16_t: 0x%x", (int)ordinal); + return -1; + } + ordinal -= 0x10000; + *result++ = 0xD800 | (ordinal >> 10); + *result++ = 0xDC00 | (ordinal & 0x3FF); + } + else + *result++ = ordinal; + } + return 0; +} + +static int _my_PyUnicode_AsChar32(PyObject *unicode, + cffi_char32_t *result, + Py_ssize_t resultlen) +{ + if (PyUnicode_AsUCS4(unicode, (Py_UCS4 *)result, resultlen, 0) == NULL) + return -1; + return 0; +} |