[pypy-commit] cffi release-1.3: merge default for release 1.3.1
arigo
noreply at buildbot.pypy.org
Wed Nov 18 10:55:32 EST 2015
Author: Armin Rigo <arigo at tunes.org>
Branch: release-1.3
Changeset: r2418:9a35705472a8
Date: 2015-11-18 15:46 +0100
http://bitbucket.org/cffi/cffi/changeset/9a35705472a8/
Log: merge default for release 1.3.1
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5861,6 +5861,9 @@
(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;
@@ -6187,6 +6190,7 @@
{"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},
@@ -6447,7 +6451,7 @@
if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
INITERROR;
- v = PyText_FromString("1.3.0");
+ v = PyText_FromString("1.3.1");
if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
INITERROR;
diff --git a/c/cffi1_module.c b/c/cffi1_module.c
--- a/c/cffi1_module.c
+++ b/c/cffi1_module.c
@@ -15,6 +15,7 @@
#include "cglob.c"
#include "lib_obj.c"
#include "cdlopen.c"
+#include "commontypes.c"
static int init_ffi_lib(PyObject *m)
diff --git a/c/commontypes.c b/c/commontypes.c
new file mode 100644
--- /dev/null
+++ b/c/commontypes.c
@@ -0,0 +1,215 @@
+/* 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 i, err;
+ 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
--- a/c/ffi_obj.c
+++ b/c/ffi_obj.c
@@ -361,7 +361,19 @@
&PyTuple_GET_ITEM(allocator, 1));
}
-PyDoc_STRVAR(ffi_new_allocator_doc, "XXX");
+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)
diff --git a/c/parse_c_type.c b/c/parse_c_type.c
--- a/c/parse_c_type.c
+++ b/c/parse_c_type.c
@@ -220,6 +220,8 @@
#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)
{
@@ -442,26 +444,34 @@
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;
-#define MAKE_SEARCH_FUNC(FIELD) \
- static \
- int search_in_##FIELD(const struct _cffi_type_context_s *ctx, \
- const char *search, size_t search_len) \
- { \
- int left = 0, right = ctx->num_##FIELD; \
- \
- while (left < right) { \
- int middle = (left + right) / 2; \
- const char *src = ctx->FIELD[middle].name; \
- 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; \
+ 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)
@@ -715,6 +725,7 @@
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);
@@ -725,6 +736,14 @@
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:
@@ -736,10 +755,15 @@
return parse_error(tok, "struct or union name expected");
n = search_in_struct_unions(tok->info->ctx, tok->p, tok->size);
- if (n < 0)
- return parse_error(tok, "undefined struct/union name");
- if (((tok->info->ctx->struct_unions[n].flags & _CFFI_F_UNION) != 0)
- ^ (kind == TOK_UNION))
+ 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);
@@ -770,7 +794,8 @@
static
-int parse_c_type(struct _cffi_parse_info_s *info, const char *input)
+int parse_c_type_from(struct _cffi_parse_info_s *info, size_t *output_index,
+ const char *input)
{
int result;
token_t token;
@@ -781,12 +806,26 @@
token.p = input;
token.size = 0;
token.output = info->output;
- token.output_index = 0;
+ 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
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -314,6 +314,16 @@
_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) {
@@ -330,9 +340,9 @@
(s->flags & _CFFI_F_UNION) ? "union " : "struct ",
s->name);
if (strcmp(name, "struct _IO_FILE") == 0)
- flags |= CT_IS_FILE;
-
- x = new_struct_or_union_type(name, flags);
+ 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;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -7,12 +7,12 @@
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
_setup_path()
from _cffi_backend import *
-from _cffi_backend import _testfunc, _get_types, __version__
+from _cffi_backend import _testfunc, _get_types, _get_common_types, __version__
# ____________________________________________________________
import sys
-assert __version__ == "1.3.0", ("This test_c.py file is for testing a version"
+assert __version__ == "1.3.1", ("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,):
@@ -3520,3 +3520,8 @@
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'
diff --git a/cffi/__init__.py b/cffi/__init__.py
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -4,8 +4,8 @@
from .api import FFI, CDefError, FFIError
from .ffiplatform import VerificationError, VerificationMissing
-__version__ = "1.3.0"
-__version_info__ = (1, 3, 0)
+__version__ = "1.3.1"
+__version_info__ = (1, 3, 1)
# The verifier module file names are based on the CRC32 of a string that
# contains the following version number. It may be older than __version__
diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
--- a/cffi/cffi_opcode.py
+++ b/cffi/cffi_opcode.py
@@ -110,6 +110,8 @@
_UNKNOWN_FLOAT_PRIM = -2
_UNKNOWN_LONG_DOUBLE = -3
+_IO_FILE_STRUCT = -1
+
PRIMITIVE_TO_INDEX = {
'char': PRIM_CHAR,
'short': PRIM_SHORT,
diff --git a/cffi/commontypes.py b/cffi/commontypes.py
--- a/cffi/commontypes.py
+++ b/cffi/commontypes.py
@@ -2,10 +2,17 @@
from . import api, model
-COMMON_TYPES = {
- 'FILE': model.unknown_type('FILE', '_IO_FILE'),
- 'bool': '_Bool',
- }
+COMMON_TYPES = {}
+
+try:
+ # fetch "bool" and all simple Windows types
+ from _cffi_backend import _get_common_types
+ _get_common_types(COMMON_TYPES)
+except ImportError:
+ pass
+
+COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE')
+COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above
for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
if _type.endswith('_t'):
@@ -14,212 +21,35 @@
_CACHE = {}
-def resolve_common_type(commontype):
+def resolve_common_type(parser, commontype):
try:
return _CACHE[commontype]
except KeyError:
- result = COMMON_TYPES.get(commontype, commontype)
- if not isinstance(result, str):
- pass # result is already a BaseType
- elif result.endswith(' *'):
- if result.startswith('const '):
- result = model.ConstPointerType(
- resolve_common_type(result[6:-2]))
- else:
- result = model.PointerType(resolve_common_type(result[:-2]))
- elif result in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
- result = model.PrimitiveType(result)
- elif result == 'set-unicode-needed':
+ cdecl = COMMON_TYPES.get(commontype, commontype)
+ if not isinstance(cdecl, str):
+ result, quals = cdecl, 0 # cdecl is already a BaseType
+ elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES:
+ result, quals = model.PrimitiveType(cdecl), 0
+ elif cdecl == 'set-unicode-needed':
raise api.FFIError("The Windows type %r is only available after "
"you call ffi.set_unicode()" % (commontype,))
else:
- if commontype == result:
+ if commontype == cdecl:
raise api.FFIError("Unsupported type: %r. Please file a bug "
"if you think it should be." % (commontype,))
- result = resolve_common_type(result) # recursively
+ result, quals = parser.parse_type_and_quals(cdecl) # recursive
+
assert isinstance(result, model.BaseTypeByIdentity)
- _CACHE[commontype] = result
- return result
+ _CACHE[commontype] = result, quals
+ return result, quals
# ____________________________________________________________
-# Windows common types
+# extra types for Windows (most of them are in commontypes.c)
-def win_common_types(maxsize):
- result = {}
- if maxsize < (1<<32):
- result.update({ # Windows 32-bits
- 'HALF_PTR': 'short',
- 'INT_PTR': 'int',
- 'LONG_PTR': 'long',
- 'UHALF_PTR': 'unsigned short',
- 'UINT_PTR': 'unsigned int',
- 'ULONG_PTR': 'unsigned long',
- })
- else:
- result.update({ # Windows 64-bits
- 'HALF_PTR': 'int',
- 'INT_PTR': 'long long',
- 'LONG_PTR': 'long long',
- 'UHALF_PTR': 'unsigned int',
- 'UINT_PTR': 'unsigned long long',
- 'ULONG_PTR': 'unsigned long long',
- })
- result.update({
- "BYTE": "unsigned char",
- "BOOL": "int",
- "CCHAR": "char",
- "CHAR": "char",
- "DWORD": "unsigned long",
- "DWORD32": "unsigned int",
- "DWORD64": "unsigned long long",
- "FLOAT": "float",
- "INT": "int",
- "INT8": "signed char",
- "INT16": "short",
- "INT32": "int",
- "INT64": "long long",
- "LONG": "long",
- "LONGLONG": "long long",
- "LONG32": "int",
- "LONG64": "long long",
- "WORD": "unsigned short",
- "PVOID": model.voidp_type,
- "ULONGLONG": "unsigned long long",
- "WCHAR": "wchar_t",
- "SHORT": "short",
- "UCHAR": "unsigned char",
- "UINT": "unsigned int",
- "UINT8": "unsigned char",
- "UINT16": "unsigned short",
- "UINT32": "unsigned int",
- "UINT64": "unsigned long long",
- "ULONG": "unsigned long",
- "ULONG32": "unsigned int",
- "ULONG64": "unsigned long long",
- "USHORT": "unsigned short",
-
- "SIZE_T": "ULONG_PTR",
- "SSIZE_T": "LONG_PTR",
- "ATOM": "WORD",
- "BOOLEAN": "BYTE",
- "COLORREF": "DWORD",
-
- "HANDLE": "PVOID",
- "DWORDLONG": "ULONGLONG",
- "DWORD_PTR": "ULONG_PTR",
- "HACCEL": "HANDLE",
-
- "HBITMAP": "HANDLE",
- "HBRUSH": "HANDLE",
- "HCOLORSPACE": "HANDLE",
- "HCONV": "HANDLE",
- "HCONVLIST": "HANDLE",
- "HDC": "HANDLE",
- "HDDEDATA": "HANDLE",
- "HDESK": "HANDLE",
- "HDROP": "HANDLE",
- "HDWP": "HANDLE",
- "HENHMETAFILE": "HANDLE",
- "HFILE": "int",
- "HFONT": "HANDLE",
- "HGDIOBJ": "HANDLE",
- "HGLOBAL": "HANDLE",
- "HHOOK": "HANDLE",
- "HICON": "HANDLE",
- "HCURSOR": "HICON",
- "HINSTANCE": "HANDLE",
- "HKEY": "HANDLE",
- "HKL": "HANDLE",
- "HLOCAL": "HANDLE",
- "HMENU": "HANDLE",
- "HMETAFILE": "HANDLE",
- "HMODULE": "HINSTANCE",
- "HMONITOR": "HANDLE",
- "HPALETTE": "HANDLE",
- "HPEN": "HANDLE",
- "HRESULT": "LONG",
- "HRGN": "HANDLE",
- "HRSRC": "HANDLE",
- "HSZ": "HANDLE",
- "WINSTA": "HANDLE",
- "HWND": "HANDLE",
-
- "LANGID": "WORD",
- "LCID": "DWORD",
- "LCTYPE": "DWORD",
- "LGRPID": "DWORD",
- "LPARAM": "LONG_PTR",
- "LPBOOL": "BOOL *",
- "LPBYTE": "BYTE *",
- "LPCOLORREF": "DWORD *",
- "LPCSTR": "const char *",
-
- "LPCVOID": model.const_voidp_type,
- "LPCWSTR": "const WCHAR *",
- "LPDWORD": "DWORD *",
- "LPHANDLE": "HANDLE *",
- "LPINT": "int *",
- "LPLONG": "long *",
- "LPSTR": "CHAR *",
- "LPWSTR": "WCHAR *",
- "LPVOID": model.voidp_type,
- "LPWORD": "WORD *",
- "LRESULT": "LONG_PTR",
- "PBOOL": "BOOL *",
- "PBOOLEAN": "BOOLEAN *",
- "PBYTE": "BYTE *",
- "PCHAR": "CHAR *",
- "PCSTR": "const CHAR *",
- "PCWSTR": "const WCHAR *",
- "PDWORD": "DWORD *",
- "PDWORDLONG": "DWORDLONG *",
- "PDWORD_PTR": "DWORD_PTR *",
- "PDWORD32": "DWORD32 *",
- "PDWORD64": "DWORD64 *",
- "PFLOAT": "FLOAT *",
- "PHALF_PTR": "HALF_PTR *",
- "PHANDLE": "HANDLE *",
- "PHKEY": "HKEY *",
- "PINT": "int *",
- "PINT_PTR": "INT_PTR *",
- "PINT8": "INT8 *",
- "PINT16": "INT16 *",
- "PINT32": "INT32 *",
- "PINT64": "INT64 *",
- "PLCID": "PDWORD",
- "PLONG": "LONG *",
- "PLONGLONG": "LONGLONG *",
- "PLONG_PTR": "LONG_PTR *",
- "PLONG32": "LONG32 *",
- "PLONG64": "LONG64 *",
- "PSHORT": "SHORT *",
- "PSIZE_T": "SIZE_T *",
- "PSSIZE_T": "SSIZE_T *",
- "PSTR": "CHAR *",
- "PUCHAR": "UCHAR *",
- "PUHALF_PTR": "UHALF_PTR *",
- "PUINT": "UINT *",
- "PUINT_PTR": "UINT_PTR *",
- "PUINT8": "UINT8 *",
- "PUINT16": "UINT16 *",
- "PUINT32": "UINT32 *",
- "PUINT64": "UINT64 *",
- "PULONG": "ULONG *",
- "PULONGLONG": "ULONGLONG *",
- "PULONG_PTR": "ULONG_PTR *",
- "PULONG32": "ULONG32 *",
- "PULONG64": "ULONG64 *",
- "PUSHORT": "USHORT *",
- "PWCHAR": "WCHAR *",
- "PWORD": "WORD *",
- "PWSTR": "WCHAR *",
- "QWORD": "unsigned long long",
- "SC_HANDLE": "HANDLE",
- "SC_LOCK": "LPVOID",
- "SERVICE_STATUS_HANDLE": "HANDLE",
-
+def win_common_types():
+ return {
"UNICODE_STRING": model.StructType(
"_UNICODE_STRING",
["Length",
@@ -232,10 +62,6 @@
"PUNICODE_STRING": "UNICODE_STRING *",
"PCUNICODE_STRING": "const UNICODE_STRING *",
- "USN": "LONGLONG",
- "VOID": model.void_type,
- "WPARAM": "UINT_PTR",
-
"TBYTE": "set-unicode-needed",
"TCHAR": "set-unicode-needed",
"LPCTSTR": "set-unicode-needed",
@@ -244,9 +70,7 @@
"PTSTR": "set-unicode-needed",
"PTBYTE": "set-unicode-needed",
"PTCHAR": "set-unicode-needed",
- })
- return result
-
+ }
if sys.platform == 'win32':
- COMMON_TYPES.update(win_common_types(sys.maxsize))
+ COMMON_TYPES.update(win_common_types())
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -29,6 +29,8 @@
_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
_r_cdecl = re.compile(r"\b__cdecl\b")
+_r_star_const_space = re.compile( # matches "* const "
+ r"[*]\s*((const|volatile|restrict)\b\s*)+")
def _get_parser():
global _parser_cache
@@ -36,6 +38,48 @@
_parser_cache = pycparser.CParser()
return _parser_cache
+def _workaround_for_old_pycparser(csource):
+ # Workaround for a pycparser issue (fixed between pycparser 2.10 and
+ # 2.14): "char*const***" gives us a wrong syntax tree, the same as
+ # for "char***(*const)". This means we can't tell the difference
+ # afterwards. But "char(*const(***))" gives us the right syntax
+ # tree. The issue only occurs if there are several stars in
+ # sequence with no parenthesis inbetween, just possibly qualifiers.
+ # Attempt to fix it by adding some parentheses in the source: each
+ # time we see "* const" or "* const *", we add an opening
+ # parenthesis before each star---the hard part is figuring out where
+ # to close them.
+ parts = []
+ while True:
+ match = _r_star_const_space.search(csource)
+ if not match:
+ break
+ #print repr(''.join(parts)+csource), '=>',
+ parts.append(csource[:match.start()])
+ parts.append('('); closing = ')'
+ parts.append(match.group()) # e.g. "* const "
+ endpos = match.end()
+ if csource.startswith('*', endpos):
+ parts.append('('); closing += ')'
+ level = 0
+ i = endpos
+ while i < len(csource):
+ c = csource[i]
+ if c == '(':
+ level += 1
+ elif c == ')':
+ if level == 0:
+ break
+ level -= 1
+ elif c in ',;=':
+ if level == 0:
+ break
+ i += 1
+ csource = csource[endpos:i] + closing + csource[i:]
+ #print repr(''.join(parts)+csource)
+ parts.append(csource)
+ return ''.join(parts)
+
def _preprocess(csource):
# Remove comments. NOTE: this only work because the cdef() section
# should not contain any string literal!
@@ -47,6 +91,10 @@
macrovalue = macrovalue.replace('\\\n', '').strip()
macros[macroname] = macrovalue
csource = _r_define.sub('', csource)
+ #
+ if pycparser.__version__ < '2.14':
+ csource = _workaround_for_old_pycparser(csource)
+ #
# BIG HACK: replace WINAPI or __stdcall with "volatile const".
# It doesn't make sense for the return type of a function to be
# "volatile volatile const", so we abuse it to detect __stdcall...
@@ -320,13 +368,15 @@
self._declare('variable ' + decl.name, tp, quals=quals)
def parse_type(self, cdecl):
+ return self.parse_type_and_quals(cdecl)[0]
+
+ def parse_type_and_quals(self, cdecl):
ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2]
assert not macros
exprnode = ast.ext[-1].type.args.params[0]
if isinstance(exprnode, pycparser.c_ast.ID):
raise api.CDefError("unknown identifier '%s'" % (exprnode.name,))
- tp, quals = self._get_type_and_quals(exprnode.type)
- return tp
+ return self._get_type_and_quals(exprnode.type)
def _declare(self, name, obj, included=False, quals=0):
if name in self._declarations:
@@ -348,6 +398,8 @@
pycparser.c_ast.PtrDecl)):
if 'const' in type.quals:
quals |= model.Q_CONST
+ if 'volatile' in type.quals:
+ quals |= model.Q_VOLATILE
if 'restrict' in type.quals:
quals |= model.Q_RESTRICT
return quals
@@ -422,7 +474,8 @@
if ident == '__dotdotdot__':
raise api.FFIError(':%d: bad usage of "..."' %
typenode.coord.line)
- return resolve_common_type(ident), quals
+ tp0, quals0 = resolve_common_type(self, ident)
+ return tp0, (quals | quals0)
#
if isinstance(type, pycparser.c_ast.Struct):
# 'struct foobar'
@@ -456,6 +509,13 @@
def _parse_function_type(self, typenode, funcname=None):
params = list(getattr(typenode.args, 'params', []))
+ for i, arg in enumerate(params):
+ if not hasattr(arg, 'type'):
+ raise api.CDefError("%s arg %d: unknown type '%s'"
+ " (if you meant to use the old C syntax of giving"
+ " untyped arguments, it is not supported)"
+ % (funcname or 'in expression', i + 1,
+ getattr(arg, 'name', '?')))
ellipsis = (
len(params) > 0 and
isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -7,10 +7,13 @@
# type qualifiers
Q_CONST = 0x01
Q_RESTRICT = 0x02
+Q_VOLATILE = 0x04
def qualify(quals, replace_with):
if quals & Q_CONST:
replace_with = ' const ' + replace_with.lstrip()
+ if quals & Q_VOLATILE:
+ replace_with = ' volatile ' + replace_with.lstrip()
if quals & Q_RESTRICT:
# It seems that __restrict is supported by gcc and msvc.
# If you hit some different compiler, add a #define in
@@ -511,12 +514,17 @@
if self.baseinttype is not None:
return self.baseinttype.get_cached_btype(ffi, finishlist)
#
+ from . import api
if self.enumvalues:
smallest_value = min(self.enumvalues)
largest_value = max(self.enumvalues)
else:
- smallest_value = 0
- largest_value = 0
+ import warnings
+ warnings.warn("%r has no values explicitly defined; next version "
+ "will refuse to guess which integer type it is "
+ "meant to be (unsigned/signed, int/long)"
+ % self._get_c_name())
+ smallest_value = largest_value = 0
if smallest_value < 0: # needs a signed type
sign = 1
candidate1 = PrimitiveType("int")
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
--- a/cffi/parse_c_type.h
+++ b/cffi/parse_c_type.h
@@ -83,6 +83,8 @@
#define _CFFI__UNKNOWN_FLOAT_PRIM (-2)
#define _CFFI__UNKNOWN_LONG_DOUBLE (-3)
+#define _CFFI__IO_FILE_STRUCT (-1)
+
struct _cffi_global_s {
const char *name;
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -253,7 +253,7 @@
Usually, the right thing to do is to call this method with True. Be
aware (particularly on Python 2) that, afterwards, you need to pass unicode
-strings as arguments instead of not byte strings. (Before cffi version 0.9,
+strings as arguments instead of byte strings. (Before cffi version 0.9,
``TCHAR`` and friends where hard-coded as unicode, but ``UNICODE`` was,
inconsistently, not defined by default.)
diff --git a/doc/source/conf.py b/doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -47,7 +47,7 @@
# The short X.Y version.
version = '1.3'
# The full version, including alpha/beta/rc tags.
-release = '1.3.0'
+release = '1.3.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/installation.rst b/doc/source/installation.rst
--- a/doc/source/installation.rst
+++ b/doc/source/installation.rst
@@ -51,11 +51,11 @@
Download and Installation:
-* http://pypi.python.org/packages/source/c/cffi/cffi-1.3.0.tar.gz
+* http://pypi.python.org/packages/source/c/cffi/cffi-1.3.1.tar.gz
- - MD5: a40ed8c8ac653c8fc7d5603711b06eaf
+ - MD5: ...
- - SHA: 54a0b2dbbc2f5d99131aa337e217b636652641a9
+ - SHA: ...
* Or grab the most current version from the `Bitbucket page`_:
``hg clone https://bitbucket.org/cffi/cffi``
diff --git a/doc/source/using.rst b/doc/source/using.rst
--- a/doc/source/using.rst
+++ b/doc/source/using.rst
@@ -514,6 +514,16 @@
discouraged: using this a style, we are more likely to forget the
callback object too early, when it is still in use.
+.. warning::
+
+ **SELinux** requires that the setting ``deny_execmem`` is left to
+ its default setting of ``off`` to use callbacks. A fix in cffi was
+ attempted (see the ``ffi_closure_alloc`` branch), but this branch is
+ not merged because it creates potential memory corruption with
+ ``fork()``. For more information, `see here.`__
+
+.. __: https://bugzilla.redhat.com/show_bug.cgi?id=1249685
+
*New in version 1.2:* If you want to be sure to catch all exceptions, use
``ffi.callback(..., onerror=func)``. If an exception occurs and
``onerror`` is specified, then ``onerror(exception, exc_value,
@@ -692,13 +702,16 @@
**ffi.from_buffer(python_buffer)**: return a ``<cdata 'char[]'>`` that
points to the data of the given Python object, which must support the
buffer interface. This is the opposite of ``ffi.buffer()``. It gives
-a (read-write) reference to the existing data, not a copy; for this
+a reference to the existing data, not a copy; for this
reason, and for PyPy compatibility, it does not work with the built-in
types str or unicode or bytearray (or buffers/memoryviews on them).
It is meant to be used on objects
containing large quantities of raw data, like ``array.array`` or numpy
arrays. It supports both the old buffer API (in Python 2.x) and the
-new memoryview API. The original object is kept alive (and, in case
+new memoryview API. Note that if you pass a read-only buffer object,
+you still get a regular ``<cdata 'char[]'>``; it is your responsibility
+not to write there if the original buffer doesn't expect you to.
+The original object is kept alive (and, in case
of memoryview, locked) as long as the cdata object returned by
``ffi.from_buffer()`` is alive. *New in version 0.9.*
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,6 +3,22 @@
======================
+v1.3.1
+======
+
+* The optional typedefs (``bool``, ``FILE`` and all Windows types) were
+ not always available from out-of-line FFI objects.
+
+* Opaque enums are phased out from the cdefs: they now give a warning,
+ instead of (possibly wrongly) being assumed equal to ``unsigned int``.
+ Please report if you get a reasonable use case for them.
+
+* Some parsing details, notably ``volatile`` is passed along like
+ ``const`` and ``restrict``. Also, older versions of pycparser
+ mis-parse some pointer-to-pointer types like ``char * const *``: the
+ "const" ends up at the wrong place. Added a workaround.
+
+
v1.3.0
======
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -144,7 +144,7 @@
`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
""",
- version='1.3.0',
+ version='1.3.1',
packages=['cffi'] if cpython else [],
package_data={'cffi': ['_cffi_include.h', 'parse_c_type.h']}
if cpython else {},
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -756,10 +756,11 @@
p = ffi.cast("long long", ffi.cast("wchar_t", -1))
if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned
assert int(p) == 0xffff
- elif platform.machine().startswith(('arm', 'aarch64')):
- assert int(p) == 0xffffffff # 4 bytes, unsigned
- else: # 4 bytes, signed
+ elif (sys.platform.startswith('linux') and
+ platform.machine().startswith('x86')): # known to be signed
assert int(p) == -1
+ else: # in general, it can be either signed or not
+ assert int(p) in [-1, 0xffffffff] # e.g. on arm, both cases occur
p = ffi.cast("int", u+'\u1234')
assert int(p) == 0x1234
@@ -1334,7 +1335,8 @@
# these depend on user-defined data, so should not be shared
assert ffi1.typeof("struct foo") is not ffi2.typeof("struct foo")
assert ffi1.typeof("union foo *") is not ffi2.typeof("union foo*")
- assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
+ # the following test is an opaque enum, which we no longer support
+ #assert ffi1.typeof("enum foo") is not ffi2.typeof("enum foo")
# sanity check: twice 'ffi1'
assert ffi1.typeof("struct foo*") is ffi1.typeof("struct foo *")
@@ -1346,6 +1348,17 @@
assert ffi.getctype("pe") == 'e *'
assert ffi.getctype("e1*") == 'e1 *'
+ def test_opaque_enum(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum foo;")
+ from cffi import __version_info__
+ if __version_info__ < (1, 4):
+ py.test.skip("re-enable me in version 1.4")
+ e = py.test.raises(CDefError, ffi.cast, "enum foo", -1)
+ assert str(e.value) == (
+ "'enum foo' has no values explicitly defined: refusing to guess "
+ "which integer type it is meant to be (unsigned/signed, int/long)")
+
def test_new_ctype(self):
ffi = FFI(backend=self.Backend())
p = ffi.new("int *")
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -486,7 +486,7 @@
ffi = FFI(backend=self.Backend())
ffi.cdef("double __stdcall sin(double x);") # stdcall ignored
m = ffi.dlopen(lib_m)
- if (sys.platform == 'win32' and sys.maxint < 2**32 and
+ if (sys.platform == 'win32' and sys.maxsize < 2**32 and
self.Backend is not CTypesBackend):
assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin))
else:
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
--- a/testing/cffi0/test_parsing.py
+++ b/testing/cffi0/test_parsing.py
@@ -261,7 +261,8 @@
ffi = FFI()
ffi.cdef("typedef int bool, *FILE;")
assert repr(ffi.cast("bool", 123)) == "<cdata 'int' 123>"
- assert repr(ffi.cast("FILE", 123)) == "<cdata 'int *' 0x7b>"
+ assert re.match(r"<cdata 'int [*]' 0[xX]?0*7[bB]>",
+ repr(ffi.cast("FILE", 123)))
ffi = FFI()
ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )"
@@ -272,6 +273,13 @@
ffi = FFI()
ffi.cdef("typedef _Bool bool; void f(bool);")
+def test_unknown_argument_type():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);")
+ assert str(e.value) == ("f arg 1: unknown type 'foobarbazzz' (if you meant"
+ " to use the old C syntax of giving untyped"
+ " arguments, it is not supported)")
+
def test_void_renamed_as_only_arg():
ffi = FFI()
ffi.cdef("typedef void void_t1;"
@@ -279,38 +287,16 @@
"typedef int (*func_t)(void_t);")
assert ffi.typeof("func_t").args == ()
-def test_win_common_types():
- from cffi.commontypes import COMMON_TYPES, _CACHE
- from cffi.commontypes import win_common_types, resolve_common_type
- #
- def clear_all(extra={}, old_dict=COMMON_TYPES.copy()):
- COMMON_TYPES.clear()
- COMMON_TYPES.update(old_dict)
- COMMON_TYPES.update(extra)
- _CACHE.clear()
- #
- for maxsize in [2**32-1, 2**64-1]:
- ct = win_common_types(maxsize)
- clear_all(ct)
- for key in sorted(ct):
- if ct[key] != 'set-unicode-needed':
- resolve_common_type(key)
- # assert did not crash
- # now try to use e.g. WPARAM (-> UINT_PTR -> unsigned 32/64-bit)
- for maxsize in [2**32-1, 2**64-1]:
- ct = win_common_types(maxsize)
- clear_all(ct)
- ffi = FFI()
- value = int(ffi.cast("WPARAM", -1))
- assert value == maxsize
- #
- clear_all()
-
def test_WPARAM_on_windows():
if sys.platform != 'win32':
py.test.skip("Only for Windows")
ffi = FFI()
ffi.cdef("void f(WPARAM);")
+ #
+ # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer
+ ffi = FFI()
+ value = int(ffi.cast("WPARAM", -42))
+ assert value == sys.maxsize * 2 - 40
def test__is_constant_globalvar():
for input, expected_output in [
@@ -360,6 +346,41 @@
assert lst[0] == lst[2]
assert lst[1] == lst[3]
+def test_const_pointer_to_pointer():
+ from cffi import model
+ ffi = FFI(backend=FakeBackend())
+ #
+ tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)")
+ assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+ tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))")
+ assert (str(tp), qual) == ("<char * * const *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))")
+ assert (str(tp), qual) == ("<char * const * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char const * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ #
+ tp, qual = ffi._parser.parse_type_and_quals("char * * * const const")
+ assert (str(tp), qual) == ("<char * * *>", model.Q_CONST)
+ tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *")
+ assert (str(tp), qual) == ("<char * * volatile *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *")
+ assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *")
+ assert (str(tp), qual) == ("<char volatile const * * *>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals("const char * * *")
+ assert (str(tp), qual) == ("<char const * * *>", 0)
+ #
+ tp, qual = ffi._parser.parse_type_and_quals(
+ "int(char*const*, short****const*)")
+ assert (str(tp), qual) == (
+ "<int()(char * const *, short * * * * const *)>", 0)
+ tp, qual = ffi._parser.parse_type_and_quals(
+ "char*const*(short*const****)")
+ assert (str(tp), qual) == (
+ "<char * const *()(short * const * * * *)>", 0)
+
def test_enum():
ffi = FFI()
ffi.cdef("""
diff --git a/testing/cffi1/test_commontypes.py b/testing/cffi1/test_commontypes.py
new file mode 100644
--- /dev/null
+++ b/testing/cffi1/test_commontypes.py
@@ -0,0 +1,34 @@
+import py, os, cffi, re
+import _cffi_backend
+
+
+def getlines():
+ try:
+ f = open(os.path.join(os.path.dirname(cffi.__file__),
+ '..', 'c', 'commontypes.c'))
+ except IOError:
+ py.test.skip("cannot find ../c/commontypes.c")
+ lines = [line for line in f.readlines() if line.strip().startswith('EQ(')]
+ f.close()
+ return lines
+
+def test_alphabetical_order():
+ lines = getlines()
+ assert lines == sorted(lines)
+
+def test_dependencies():
+ r = re.compile(r'EQ[(]"([^"]+)",(?:\s*"([A-Z0-9_]+)\s*[*]*"[)])?')
+ lines = getlines()
+ d = {}
+ for line in lines:
+ match = r.search(line)
+ if match is not None:
+ d[match.group(1)] = match.group(2)
+ for value in d.values():
+ if value:
+ assert value in d
+
+def test_get_common_types():
+ d = {}
+ _cffi_backend._get_common_types(d)
+ assert d["bool"] == "_Bool"
diff --git a/testing/cffi1/test_ffi_obj.py b/testing/cffi1/test_ffi_obj.py
--- a/testing/cffi1/test_ffi_obj.py
+++ b/testing/cffi1/test_ffi_obj.py
@@ -395,3 +395,23 @@
return ffi.NULL
alloc5 = ffi.new_allocator(myalloc5)
py.test.raises(MemoryError, alloc5, "int[5]")
+
+def test_bool_issue228():
+ ffi = _cffi1_backend.FFI()
+ fntype = ffi.typeof("int(*callback)(bool is_valid)")
+ assert repr(fntype.args[0]) == "<ctype '_Bool'>"
+
+def test_FILE_issue228():
+ fntype1 = _cffi1_backend.FFI().typeof("FILE *")
+ fntype2 = _cffi1_backend.FFI().typeof("FILE *")
+ assert repr(fntype1) == "<ctype 'FILE *'>"
+ assert fntype1 is fntype2
+
+def test_cast_from_int_type_to_bool():
+ ffi = _cffi1_backend.FFI()
+ for basetype in ['char', 'short', 'int', 'long', 'long long']:
+ for sign in ['signed', 'unsigned']:
+ type = '%s %s' % (sign, basetype)
+ assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
+ assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
+ assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
diff --git a/testing/cffi1/test_new_ffi_1.py b/testing/cffi1/test_new_ffi_1.py
--- a/testing/cffi1/test_new_ffi_1.py
+++ b/testing/cffi1/test_new_ffi_1.py
@@ -781,10 +781,11 @@
p = ffi.cast("long long", ffi.cast("wchar_t", -1))
if SIZE_OF_WCHAR == 2: # 2 bytes, unsigned
assert int(p) == 0xffff
- elif platform.machine().startswith(('arm', 'aarch64')):
- assert int(p) == 0xffffffff # 4 bytes, unsigned
- else: # 4 bytes, signed
+ elif (sys.platform.startswith('linux') and
+ platform.machine().startswith('x86')): # known to be signed
assert int(p) == -1
+ else: # in general, it can be either signed or not
+ assert int(p) in [-1, 0xffffffff] # e.g. on arm, both cases occur
p = ffi.cast("int", u+'\u1234')
assert int(p) == 0x1234
diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py
--- a/testing/cffi1/test_parse_c_type.py
+++ b/testing/cffi1/test_parse_c_type.py
@@ -19,8 +19,11 @@
ffi.cdef(header)
lib = ffi.verify(
- open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read(),
- include_dirs=[cffi_dir])
+ open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """
+static const char *get_common_type(const char *search, size_t search_len) {
+ return NULL;
+}
+""", include_dirs=[cffi_dir])
class ParseError(Exception):
pass
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1204,12 +1204,19 @@
assert foo_s.fields[1][1].type is ffi.typeof("void *")
def test_restrict_fields():
- if sys.platform == 'win32':
- py.test.skip("'__restrict__' probably not recognized")
ffi = FFI()
ffi.cdef("""struct foo_s { void * restrict b; };""")
lib = verify(ffi, 'test_restrict_fields', """
- struct foo_s { void * __restrict__ b; };""")
+ struct foo_s { void * __restrict b; };""")
+ foo_s = ffi.typeof("struct foo_s")
+ assert foo_s.fields[0][0] == 'b'
+ assert foo_s.fields[0][1].type is ffi.typeof("void *")
+
+def test_volatile_fields():
+ ffi = FFI()
+ ffi.cdef("""struct foo_s { void * volatile b; };""")
+ lib = verify(ffi, 'test_volatile_fields', """
+ struct foo_s { void * volatile b; };""")
foo_s = ffi.typeof("struct foo_s")
assert foo_s.fields[0][0] == 'b'
assert foo_s.fields[0][1].type is ffi.typeof("void *")
diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py
--- a/testing/cffi1/test_verify1.py
+++ b/testing/cffi1/test_verify1.py
@@ -1494,15 +1494,6 @@
assert lib.foo(0) == 1
py.test.raises(TypeError, lib.foo, 0.0)
-def test_cast_from_int_type_to_bool():
- ffi = FFI()
- for basetype in ['char', 'short', 'int', 'long', 'long long']:
- for sign in ['signed', 'unsigned']:
- type = '%s %s' % (sign, basetype)
- assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1
- assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1
- assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0
-
def test_addressof():
ffi = FFI()
ffi.cdef("""
@@ -2250,3 +2241,31 @@
assert p == lib.myarray + 4
p[1] = 82
assert lib.my_value == 82 # [5]
+
+def test_const_pointer_to_pointer():
+ ffi = FFI()
+ ffi.cdef("struct s { char *const *a; };")
+ ffi.verify("struct s { char *const *a; };")
+
+def test_share_FILE():
+ ffi1 = FFI()
+ ffi1.cdef("void do_stuff(FILE *);")
+ lib1 = ffi1.verify("void do_stuff(FILE *f) { (void)f; }")
+ ffi2 = FFI()
+ ffi2.cdef("FILE *barize(void);")
+ lib2 = ffi2.verify("FILE *barize(void) { return NULL; }")
+ lib1.do_stuff(lib2.barize())
+
+def test_win_common_types():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ ffi = FFI()
+ ffi.set_unicode(True)
+ ffi.verify("")
+ assert ffi.typeof("PBYTE") is ffi.typeof("unsigned char *")
+ if sys.maxsize > 2**32:
+ expected = "unsigned long long"
+ else:
+ expected = "unsigned int"
+ assert ffi.typeof("UINT_PTR") is ffi.typeof(expected)
+ assert ffi.typeof("PTSTR") is ffi.typeof("wchar_t *")
More information about the pypy-commit
mailing list