summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2014-07-05 16:47:52 +0200
committerArmin Rigo <arigo@tunes.org>2014-07-05 16:47:52 +0200
commitdec9ea315348924711debd8e16a0c155f02f130a (patch)
treebe2d9401b49044e8137da0e87427cfccbf86a0d6
parentd9f22a6bdecb46a0ef359ade64d5686e5591e40c (diff)
parentd006340d73490b19a70721fbf26cd2ba738df487 (diff)
downloadcffi-dec9ea315348924711debd8e16a0c155f02f130a.tar.gz
hg merge default
-rw-r--r--c/_cffi_backend.c30
-rw-r--r--c/check__thread.c1
-rw-r--r--c/minibuffer.h5
-rw-r--r--c/misc_win32.h38
-rw-r--r--c/test_c.py8
-rw-r--r--cffi/__init__.py4
-rw-r--r--cffi/api.py4
-rw-r--r--cffi/cparser.py113
-rw-r--r--cffi/ffiplatform.py1
-rw-r--r--cffi/vengine_cpy.py169
-rw-r--r--cffi/vengine_gen.py52
-rw-r--r--doc/source/conf.py2
-rw-r--r--doc/source/index.rst50
-rw-r--r--setup.py27
-rw-r--r--testing/backend_tests.py59
-rw-r--r--testing/test_function.py15
-rw-r--r--testing/test_parsing.py7
-rw-r--r--testing/test_verify.py88
18 files changed, 463 insertions, 210 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index a3a2515..4415264 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -5,7 +5,6 @@
#ifdef MS_WIN32
#include <windows.h>
#include "misc_win32.h"
-#include <malloc.h> /* for alloca() */
#else
#include <stddef.h>
#include <stdint.h>
@@ -13,9 +12,32 @@
#include <errno.h>
#include <ffi.h>
#include <sys/mman.h>
-#if (defined (__SVR4) && defined (__sun)) || defined(_AIX)
-# include <alloca.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;
+# 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)
+# include <alloca.h>
+# endif
#endif
#include "malloc_closure.h"
@@ -5482,7 +5504,7 @@ init_cffi_backend(void)
if (v == NULL || PyModule_AddObject(m, "_C_API", v) < 0)
INITERROR;
- v = PyText_FromString("0.8.2");
+ v = PyText_FromString("0.8.3");
if (v == NULL || PyModule_AddObject(m, "__version__", v) < 0)
INITERROR;
diff --git a/c/check__thread.c b/c/check__thread.c
deleted file mode 100644
index 6f3261c..0000000
--- a/c/check__thread.c
+++ /dev/null
@@ -1 +0,0 @@
-__thread int some_threadlocal_variable_42;
diff --git a/c/minibuffer.h b/c/minibuffer.h
index 289dc4a..92cf079 100644
--- a/c/minibuffer.h
+++ b/c/minibuffer.h
@@ -105,8 +105,9 @@ static PyObject *mb_str(MiniBufferObj *self)
static int mb_getbuf(MiniBufferObj *self, Py_buffer *view, int flags)
{
- return PyBuffer_FillInfo(view, NULL, self->mb_data, self->mb_size,
- /*readonly=*/0, PyBUF_CONTIG | PyBUF_FORMAT);
+ return PyBuffer_FillInfo(view, (PyObject *)self,
+ self->mb_data, self->mb_size,
+ /*readonly=*/0, flags);
}
static PySequenceMethods mb_as_sequence = {
diff --git a/c/misc_win32.h b/c/misc_win32.h
index 70195b2..47f9ea4 100644
--- a/c/misc_win32.h
+++ b/c/misc_win32.h
@@ -1,3 +1,4 @@
+#include <malloc.h> /* for alloca() */
/************************************************************/
/* errno and GetLastError support */
@@ -192,7 +193,27 @@ static void *dlopen(const char *filename, int flag)
static void *dlsym(void *handle, const char *symbol)
{
- return GetProcAddress((HMODULE)handle, 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 void dlclose(void *handle)
@@ -210,21 +231,6 @@ static const char *dlerror(void)
return buf;
}
-
-/************************************************************/
-/* types */
-
-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 unsigned char _Bool;
-
-
/************************************************************/
/* obscure */
diff --git a/c/test_c.py b/c/test_c.py
index 1864391..a42b9de 100644
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1102,7 +1102,7 @@ def test_load_and_call_function():
def test_read_variable():
## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
## https://bugs.pypy.org/issue1643
- if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
+ if not sys.platform.startswith("linux"):
py.test.skip("untested")
BVoidP = new_pointer_type(new_void_type())
ll = find_and_load_library('c')
@@ -1112,7 +1112,7 @@ def test_read_variable():
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 sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
+ if not sys.platform.startswith("linux"):
py.test.skip("untested")
BCharP = new_pointer_type(new_primitive_type("char"))
BArray = new_array_type(BCharP, None)
@@ -1124,7 +1124,7 @@ def test_read_variable_as_unknown_length_array():
def test_write_variable():
## FIXME: this test assumes glibc specific behavior, it's not compliant with C standard
## https://bugs.pypy.org/issue1643
- if sys.platform == 'win32' or sys.platform == 'darwin' or sys.platform.startswith('freebsd'):
+ if not sys.platform.startswith("linux"):
py.test.skip("untested")
BVoidP = new_pointer_type(new_void_type())
ll = find_and_load_library('c')
@@ -3199,4 +3199,4 @@ def test_packed_with_bitfields():
def test_version():
# this test is here mostly for PyPy
- assert __version__ == "0.8.2"
+ assert __version__ == "0.8.3"
diff --git a/cffi/__init__.py b/cffi/__init__.py
index fa9e86f..4c84250 100644
--- a/cffi/__init__.py
+++ b/cffi/__init__.py
@@ -4,5 +4,5 @@ __all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError',
from .api import FFI, CDefError, FFIError
from .ffiplatform import VerificationError, VerificationMissing
-__version__ = "0.8.2"
-__version_info__ = (0, 8, 2)
+__version__ = "0.8.3"
+__version_info__ = (0, 8, 3)
diff --git a/cffi/api.py b/cffi/api.py
index f44f086..0527a4b 100644
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -443,6 +443,10 @@ def _make_ffi_library(ffi, libname, flags):
for enumname, enumval in zip(tp.enumerators, tp.enumvalues):
if enumname not in library.__dict__:
library.__dict__[enumname] = enumval
+ for key, val in ffi._parser._int_constants.items():
+ if key not in library.__dict__:
+ library.__dict__[key] = val
+
copied_enums.append(True)
if name in library.__dict__:
return
diff --git a/cffi/cparser.py b/cffi/cparser.py
index 99998ac..a53d4c3 100644
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -24,6 +24,7 @@ _r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
_r_words = re.compile(r"\w+|\S")
_parser_cache = None
+_r_int_literal = re.compile(r"^0?x?[0-9a-f]+u?l?$", re.IGNORECASE)
def _get_parser():
global _parser_cache
@@ -99,6 +100,7 @@ class Parser(object):
self._structnode2type = weakref.WeakKeyDictionary()
self._override = False
self._packed = False
+ self._int_constants = {}
def _parse(self, csource):
csource, macros = _preprocess(csource)
@@ -128,9 +130,10 @@ class Parser(object):
finally:
if lock is not None:
lock.release()
- return ast, macros
+ # csource will be used to find buggy source text
+ return ast, macros, csource
- def convert_pycparser_error(self, e, csource):
+ def _convert_pycparser_error(self, e, csource):
# xxx look for ":NUM:" at the start of str(e) and try to interpret
# it as a line number
line = None
@@ -142,6 +145,12 @@ class Parser(object):
csourcelines = csource.splitlines()
if 1 <= linenum <= len(csourcelines):
line = csourcelines[linenum-1]
+ return line
+
+ def convert_pycparser_error(self, e, csource):
+ line = self._convert_pycparser_error(e, csource)
+
+ msg = str(e)
if line:
msg = 'cannot parse "%s"\n%s' % (line.strip(), msg)
else:
@@ -160,14 +169,9 @@ class Parser(object):
self._packed = prev_packed
def _internal_parse(self, csource):
- ast, macros = self._parse(csource)
+ ast, macros, csource = self._parse(csource)
# add the macros
- for key, value in macros.items():
- value = value.strip()
- if value != '...':
- raise api.CDefError('only supports the syntax "#define '
- '%s ..." for now (literally)' % key)
- self._declare('macro ' + key, value)
+ self._process_macros(macros)
# find the first "__dotdotdot__" and use that as a separator
# between the repeated typedefs and the real csource
iterator = iter(ast.ext)
@@ -175,27 +179,61 @@ class Parser(object):
if decl.name == '__dotdotdot__':
break
#
- for decl in iterator:
- if isinstance(decl, pycparser.c_ast.Decl):
- self._parse_decl(decl)
- elif isinstance(decl, pycparser.c_ast.Typedef):
- if not decl.name:
- raise api.CDefError("typedef does not declare any name",
- decl)
- if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType)
- and decl.type.type.names == ['__dotdotdot__']):
- realtype = model.unknown_type(decl.name)
- elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
- isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
- isinstance(decl.type.type.type,
- pycparser.c_ast.IdentifierType) and
- decl.type.type.type.names == ['__dotdotdot__']):
- realtype = model.unknown_ptr_type(decl.name)
+ try:
+ for decl in iterator:
+ if isinstance(decl, pycparser.c_ast.Decl):
+ self._parse_decl(decl)
+ elif isinstance(decl, pycparser.c_ast.Typedef):
+ if not decl.name:
+ raise api.CDefError("typedef does not declare any name",
+ decl)
+ if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType)
+ and decl.type.type.names == ['__dotdotdot__']):
+ realtype = model.unknown_type(decl.name)
+ elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
+ isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
+ isinstance(decl.type.type.type,
+ pycparser.c_ast.IdentifierType) and
+ decl.type.type.type.names == ['__dotdotdot__']):
+ realtype = model.unknown_ptr_type(decl.name)
+ else:
+ realtype = self._get_type(decl.type, name=decl.name)
+ self._declare('typedef ' + decl.name, realtype)
else:
- realtype = self._get_type(decl.type, name=decl.name)
- self._declare('typedef ' + decl.name, realtype)
+ raise api.CDefError("unrecognized construct", decl)
+ except api.FFIError as e:
+ msg = self._convert_pycparser_error(e, csource)
+ if msg:
+ e.args = (e.args[0] + "\n *** Err: %s" % msg,)
+ raise
+
+ def _add_constants(self, key, val):
+ if key in self._int_constants:
+ raise api.FFIError(
+ "multiple declarations of constant: %s" % (key,))
+ self._int_constants[key] = val
+
+ def _process_macros(self, macros):
+ for key, value in macros.items():
+ value = value.strip()
+ match = _r_int_literal.search(value)
+ if match is not None:
+ int_str = match.group(0).lower().rstrip("ul")
+
+ # "010" is not valid oct in py3
+ if (int_str.startswith("0") and
+ int_str != "0" and
+ not int_str.startswith("0x")):
+ int_str = "0o" + int_str[1:]
+
+ pyvalue = int(int_str, 0)
+ self._add_constants(key, pyvalue)
+ elif value == '...':
+ self._declare('macro ' + key, value)
else:
- raise api.CDefError("unrecognized construct", decl)
+ raise api.CDefError('only supports the syntax "#define '
+ '%s ..." (literally) or "#define '
+ '%s 0x1FF" for now' % (key, key))
def _parse_decl(self, decl):
node = decl.type
@@ -227,7 +265,7 @@ class Parser(object):
self._declare('variable ' + decl.name, tp)
def parse_type(self, cdecl):
- ast, macros = self._parse('void __dummy(\n%s\n);' % 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):
@@ -306,7 +344,8 @@ class Parser(object):
if ident == 'void':
return model.void_type
if ident == '__dotdotdot__':
- raise api.FFIError('bad usage of "..."')
+ raise api.FFIError(':%d: bad usage of "..."' %
+ typenode.coord.line)
return resolve_common_type(ident)
#
if isinstance(type, pycparser.c_ast.Struct):
@@ -333,7 +372,8 @@ class Parser(object):
return self._get_struct_union_enum_type('union', typenode, name,
nested=True)
#
- raise api.FFIError("bad or unsupported type declaration")
+ raise api.FFIError(":%d: bad or unsupported type declaration" %
+ typenode.coord.line)
def _parse_function_type(self, typenode, funcname=None):
params = list(getattr(typenode.args, 'params', []))
@@ -499,6 +539,10 @@ class Parser(object):
if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and
exprnode.op == '-'):
return -self._parse_constant(exprnode.expr)
+ # load previously defined int constant
+ if (isinstance(exprnode, pycparser.c_ast.ID) and
+ exprnode.name in self._int_constants):
+ return self._int_constants[exprnode.name]
#
if partial_length_ok:
if (isinstance(exprnode, pycparser.c_ast.ID) and
@@ -506,8 +550,8 @@ class Parser(object):
self._partial_length = True
return '...'
#
- raise api.FFIError("unsupported expression: expected a "
- "simple numeric constant")
+ raise api.FFIError(":%d: unsupported expression: expected a "
+ "simple numeric constant" % exprnode.coord.line)
def _build_enum_type(self, explicit_name, decls):
if decls is not None:
@@ -522,6 +566,7 @@ class Parser(object):
if enum.value is not None:
nextenumvalue = self._parse_constant(enum.value)
enumvalues.append(nextenumvalue)
+ self._add_constants(enum.name, nextenumvalue)
nextenumvalue += 1
enumvalues = tuple(enumvalues)
tp = model.EnumType(explicit_name, enumerators, enumvalues)
@@ -535,3 +580,5 @@ class Parser(object):
kind = name.split(' ', 1)[0]
if kind in ('typedef', 'struct', 'union', 'enum'):
self._declare(name, tp)
+ for k, v in other._int_constants.items():
+ self._add_constants(k, v)
diff --git a/cffi/ffiplatform.py b/cffi/ffiplatform.py
index 460ba90..4515d6c 100644
--- a/cffi/ffiplatform.py
+++ b/cffi/ffiplatform.py
@@ -38,6 +38,7 @@ def _build(tmpdir, ext):
import distutils.errors
#
dist = Distribution({'ext_modules': [ext]})
+ dist.parse_config_files()
options = dist.get_option_dict('build_ext')
options['force'] = ('ffiplatform', True)
options['build_lib'] = ('ffiplatform', tmpdir)
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
index d9af334..31793f0 100644
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -89,43 +89,54 @@ class VCPythonEngine(object):
# by generate_cpy_function_method().
prnt('static PyMethodDef _cffi_methods[] = {')
self._generate("method")
- prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS},')
- prnt(' {NULL, NULL} /* Sentinel */')
+ prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},')
+ prnt(' {NULL, NULL, 0, NULL} /* Sentinel */')
prnt('};')
prnt()
#
# standard init.
modname = self.verifier.get_module_name()
- if sys.version_info >= (3,):
- prnt('static struct PyModuleDef _cffi_module_def = {')
- prnt(' PyModuleDef_HEAD_INIT,')
- prnt(' "%s",' % modname)
- prnt(' NULL,')
- prnt(' -1,')
- prnt(' _cffi_methods,')
- prnt(' NULL, NULL, NULL, NULL')
- prnt('};')
- prnt()
- initname = 'PyInit_%s' % modname
- createmod = 'PyModule_Create(&_cffi_module_def)'
- errorcase = 'return NULL'
- finalreturn = 'return lib'
- else:
- initname = 'init%s' % modname
- createmod = 'Py_InitModule("%s", _cffi_methods)' % modname
- errorcase = 'return'
- finalreturn = 'return'
+ constants = self._chained_list_constants[False]
+ prnt('#if PY_MAJOR_VERSION >= 3')
+ prnt()
+ prnt('static struct PyModuleDef _cffi_module_def = {')
+ prnt(' PyModuleDef_HEAD_INIT,')
+ prnt(' "%s",' % modname)
+ prnt(' NULL,')
+ prnt(' -1,')
+ prnt(' _cffi_methods,')
+ prnt(' NULL, NULL, NULL, NULL')
+ prnt('};')
+ prnt()
+ prnt('PyMODINIT_FUNC')
+ prnt('PyInit_%s(void)' % modname)
+ prnt('{')
+ prnt(' PyObject *lib;')
+ prnt(' lib = PyModule_Create(&_cffi_module_def);')
+ prnt(' if (lib == NULL)')
+ prnt(' return NULL;')
+ prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,))
+ prnt(' Py_DECREF(lib);')
+ prnt(' return NULL;')
+ prnt(' }')
+ prnt(' return lib;')
+ prnt('}')
+ prnt()
+ prnt('#else')
+ prnt()
prnt('PyMODINIT_FUNC')
- prnt('%s(void)' % initname)
+ prnt('init%s(void)' % modname)
prnt('{')
prnt(' PyObject *lib;')
- prnt(' lib = %s;' % createmod)
- prnt(' if (lib == NULL || %s < 0)' % (
- self._chained_list_constants[False],))
- prnt(' %s;' % errorcase)
- prnt(' _cffi_init();')
- prnt(' %s;' % finalreturn)
+ prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname)
+ prnt(' if (lib == NULL)')
+ prnt(' return;')
+ prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,))
+ prnt(' return;')
+ prnt(' return;')
prnt('}')
+ prnt()
+ prnt('#endif')
def load_library(self):
# XXX review all usages of 'self' here!
@@ -394,7 +405,7 @@ class VCPythonEngine(object):
meth = 'METH_O'
else:
meth = 'METH_VARARGS'
- self._prnt(' {"%s", _cffi_f_%s, %s},' % (name, name, meth))
+ self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth))
_loading_cpy_function = _loaded_noop
@@ -481,8 +492,8 @@ class VCPythonEngine(object):
if tp.fldnames is None:
return # nothing to do with opaque structs
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
- self._prnt(' {"%s", %s, METH_NOARGS},' % (layoutfuncname,
- layoutfuncname))
+ self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname,
+ layoutfuncname))
def _loading_struct_or_union(self, tp, prefix, name, module):
if tp.fldnames is None:
@@ -589,13 +600,7 @@ class VCPythonEngine(object):
'variable type'),))
assert delayed
else:
- prnt(' if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name))
- prnt(' o = PyInt_FromLong((long)(%s));' % (name,))
- prnt(' else if ((%s) <= 0)' % (name,))
- prnt(' o = PyLong_FromLongLong((long long)(%s));' % (name,))
- prnt(' else')
- prnt(' o = PyLong_FromUnsignedLongLong('
- '(unsigned long long)(%s));' % (name,))
+ prnt(' o = _cffi_from_c_int_const(%s);' % name)
prnt(' if (o == NULL)')
prnt(' return -1;')
if size_too:
@@ -632,13 +637,18 @@ class VCPythonEngine(object):
# ----------
# enums
+ def _enum_funcname(self, prefix, name):
+ # "$enum_$1" => "___D_enum____D_1"
+ name = name.replace('$', '___D_')
+ return '_cffi_e_%s_%s' % (prefix, name)
+
def _generate_cpy_enum_decl(self, tp, name, prefix='enum'):
if tp.partial:
for enumerator in tp.enumerators:
self._generate_cpy_const(True, enumerator, delayed=False)
return
#
- funcname = '_cffi_e_%s_%s' % (prefix, name)
+ funcname = self._enum_funcname(prefix, name)
prnt = self._prnt
prnt('static int %s(PyObject *lib)' % funcname)
prnt('{')
@@ -760,17 +770,30 @@ cffimod_header = r'''
#include <Python.h>
#include <stddef.h>
-#ifdef MS_WIN32
-#include <malloc.h> /* for alloca() */
-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 unsigned char _Bool;
+/* 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;
+# 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)
+# include <alloca.h>
+# endif
#endif
#if PY_MAJOR_VERSION < 3
@@ -795,6 +818,15 @@ typedef unsigned char _Bool;
#define _cffi_to_c_double PyFloat_AsDouble
#define _cffi_to_c_float PyFloat_AsDouble
+#define _cffi_from_c_int_const(x) \
+ (((x) > 0) ? \
+ ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \
+ PyInt_FromLong((long)(x)) : \
+ PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \
+ ((long long)(x) >= (long long)LONG_MIN) ? \
+ PyInt_FromLong((long)(x)) : \
+ PyLong_FromLongLong((long long)(x)))
+
#define _cffi_from_c_int(x, type) \
(((type)-1) > 0 ? /* unsigned */ \
(sizeof(type) < sizeof(long) ? PyInt_FromLong(x) : \
@@ -804,14 +836,14 @@ typedef unsigned char _Bool;
PyLong_FromLongLong(x)))
#define _cffi_to_c_int(o, type) \
- (sizeof(type) == 1 ? (((type)-1) > 0 ? _cffi_to_c_u8(o) \
- : _cffi_to_c_i8(o)) : \
- sizeof(type) == 2 ? (((type)-1) > 0 ? _cffi_to_c_u16(o) \
- : _cffi_to_c_i16(o)) : \
- sizeof(type) == 4 ? (((type)-1) > 0 ? _cffi_to_c_u32(o) \
- : _cffi_to_c_i32(o)) : \
- sizeof(type) == 8 ? (((type)-1) > 0 ? _cffi_to_c_u64(o) \
- : _cffi_to_c_i64(o)) : \
+ (sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \
+ : (type)_cffi_to_c_i8(o)) : \
+ sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \
+ : (type)_cffi_to_c_i16(o)) : \
+ sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \
+ : (type)_cffi_to_c_i32(o)) : \
+ sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \
+ : (type)_cffi_to_c_i64(o)) : \
(Py_FatalError("unsupported size for type " #type), 0))
#define _cffi_to_c_i8 \
@@ -885,25 +917,32 @@ static PyObject *_cffi_setup(PyObject *self, PyObject *args)
return PyBool_FromLong(was_alive);
}
-static void _cffi_init(void)
+static int _cffi_init(void)
{
- PyObject *module = PyImport_ImportModule("_cffi_backend");
- PyObject *c_api_object;
+ PyObject *module, *c_api_object = NULL;
+ module = PyImport_ImportModule("_cffi_backend");
if (module == NULL)
- return;
+ goto failure;
c_api_object = PyObject_GetAttrString(module, "_C_API");
if (c_api_object == NULL)
- return;
+ goto failure;
if (!PyCapsule_CheckExact(c_api_object)) {
- Py_DECREF(c_api_object);
PyErr_SetNone(PyExc_ImportError);
- return;
+ goto failure;
}
memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"),
_CFFI_NUM_EXPORTS * sizeof(void *));
+
+ Py_DECREF(module);
Py_DECREF(c_api_object);
+ return 0;
+
+ failure:
+ Py_XDECREF(module);
+ Py_XDECREF(c_api_object);
+ return -1;
}
#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num))
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
index f8715c7..1a702c9 100644
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -249,10 +249,10 @@ class VGenericEngine(object):
prnt(' /* %s */' % str(e)) # cannot verify it, ignore
prnt('}')
self.export_symbols.append(layoutfuncname)
- prnt('ssize_t %s(ssize_t i)' % (layoutfuncname,))
+ prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,))
prnt('{')
prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname)
- prnt(' static ssize_t nums[] = {')
+ prnt(' static intptr_t nums[] = {')
prnt(' sizeof(%s),' % cname)
prnt(' offsetof(struct _cffi_aligncheck, y),')
for fname, ftype, fbitsize in tp.enumfields():
@@ -276,7 +276,7 @@ class VGenericEngine(object):
return # nothing to do with opaque structs
layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name)
#
- BFunc = self.ffi._typeof_locked("ssize_t(*)(ssize_t)")[0]
+ BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0]
function = module.load_function(BFunc, layoutfuncname)
layout = []
num = 0
@@ -410,13 +410,18 @@ class VGenericEngine(object):
# ----------
# enums
+ def _enum_funcname(self, prefix, name):
+ # "$enum_$1" => "___D_enum____D_1"
+ name = name.replace('$', '___D_')
+ return '_cffi_e_%s_%s' % (prefix, name)
+
def _generate_gen_enum_decl(self, tp, name, prefix='enum'):
if tp.partial:
for enumerator in tp.enumerators:
self._generate_gen_const(True, enumerator)
return
#
- funcname = '_cffi_e_%s_%s' % (prefix, name)
+ funcname = self._enum_funcname(prefix, name)
self.export_symbols.append(funcname)
prnt = self._prnt
prnt('int %s(char *out_error)' % funcname)
@@ -453,7 +458,7 @@ class VGenericEngine(object):
else:
BType = self.ffi._typeof_locked("char[]")[0]
BFunc = self.ffi._typeof_locked("int(*)(char*)")[0]
- funcname = '_cffi_e_%s_%s' % (prefix, name)
+ funcname = self._enum_funcname(prefix, name)
function = module.load_function(BFunc, funcname)
p = self.ffi.new(BType, 256)
if function(p) < 0:
@@ -547,20 +552,29 @@ cffimod_header = r'''
#include <errno.h>
#include <sys/types.h> /* XXX for ssize_t on some platforms */
-#ifdef _WIN32
-# include <Windows.h>
-# define snprintf _snprintf
-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 SSIZE_T ssize_t;
-typedef unsigned char _Bool;
-#else
+/* 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;
+# 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)
+# include <alloca.h>
+# endif
#endif
'''
diff --git a/doc/source/conf.py b/doc/source/conf.py
index 17bcbd3..29cdb4d 100644
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -47,7 +47,7 @@ copyright = u'2012, Armin Rigo, Maciej Fijalkowski'
# The short X.Y version.
version = '0.8'
# The full version, including alpha/beta/rc tags.
-release = '0.8.2'
+release = '0.8.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/doc/source/index.rst b/doc/source/index.rst
index 7f5120f..146faca 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1,31 +1,34 @@
CFFI documentation
================================
-Foreign Function Interface for Python calling C code. The aim of this project
-is to provide a convenient and reliable way of calling C code from Python.
-The interface is based on `LuaJIT's FFI`_ and follows a few principles:
+C Foreign Function Interface for Python. The goal is to provide a
+convenient and reliable way to call compiled C code from Python using
+interface declarations written in C.
-* The goal is to call C code from Python. You should be able to do so
- without learning a 3rd language: every alternative requires you to learn
- their own language (Cython_, SWIG_) or API (ctypes_). So we tried to
- assume that you know Python and C and minimize the extra bits of API that
- you need to learn.
+The interface is based on `LuaJIT's FFI`_, and follows a few principles:
+
+* The goal is to call C code from Python without learning a 3rd language:
+ existing alternatives require users to learn domain specific language
+ (Cython_, SWIG_) or API (ctypes_). The CFFI design requires users to know
+ only C and Python, minimizing the extra bits of API that need to be learned.
* Keep all the Python-related logic in Python so that you don't need to
write much C code (unlike `CPython native C extensions`_).
-* Work either at the level of the ABI (Application Binary Interface)
- or the API (Application Programming Interface). Usually, C
- libraries have a specified C API but often not an ABI (e.g. they may
+* The preferred way is to work at the level of the API (Application
+ Programming Interface): the C compiler is called from the declarations
+ you write to validate and link to the C language constructs.
+ Alternatively, it is also possible to work at the ABI level
+ (Application Binary Interface), the way ctypes_ work.
+ However, on non-Windows platforms, C libraries typically
+ have a specified C API but not an ABI (e.g. they may
document a "struct" as having at least these fields, but maybe more).
- (ctypes_ works at the ABI level, whereas Cython_ and `native C extensions`_
- work at the API level.)
-* We try to be complete. For now some C99 constructs are not supported,
+* Try to be complete. For now some C99 constructs are not supported,
but all C89 should be, including macros (and including macro "abuses",
which you can `manually wrap`_ in saner-looking C functions).
-* We attempt to support both PyPy and CPython, with a reasonable path
+* Attempt to support both PyPy and CPython, with a reasonable path
for other Python implementations like IronPython and Jython.
* Note that this project is **not** about embedding executable C code in
@@ -38,7 +41,7 @@ The interface is based on `LuaJIT's FFI`_ and follows a few principles:
.. _`CPython native C extensions`: http://docs.python.org/extending/extending.html
.. _`native C extensions`: http://docs.python.org/extending/extending.html
.. _`ctypes`: http://docs.python.org/library/ctypes.html
-.. _`Weave`: http://www.scipy.org/Weave
+.. _`Weave`: http://wiki.scipy.org/Weave
.. _`manually wrap`: `The verification step`_
@@ -85,13 +88,13 @@ Requirements:
Download and Installation:
-* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.2.tar.gz
+* http://pypi.python.org/packages/source/c/cffi/cffi-0.8.3.tar.gz
- Or grab the most current version by following the instructions below.
- - MD5: 37fc88c62f40d04e8a18192433f951ec
+ - MD5: ...
- - SHA: 75a6c433664a7a38d4d03cecbdc72cef4c3cceac
+ - SHA: ...
* Or get it from the `Bitbucket page`_:
``hg clone https://bitbucket.org/cffi/cffi``
@@ -851,7 +854,7 @@ like ``ffi.new("int[%d]" % x)``. Indeed, this is not recommended:
``ffi`` normally caches the string ``"int[]"`` to not need to re-parse
it all the time.
-.. versionadded:: 0.9
+.. versionadded:: 0.8.2
The ``ffi.cdef()`` call takes an optional argument ``packed``: if
True, then all structs declared within this cdef are "packed". This
has a meaning similar to ``__attribute__((packed))`` in GCC. It
@@ -1195,13 +1198,14 @@ an array.)
owned memory will not be freed as long as the buffer is alive.
Moreover buffer objects now support weakrefs to them.
-.. versionchanged:: 0.9
- Before version 0.9, ``bytes(buf)`` was supported in Python 3 to get
+.. versionchanged:: 0.8.2
+ Before version 0.8.2, ``bytes(buf)`` was supported in Python 3 to get
the content of the buffer, but on Python 2 it would return the repr
``<_cffi_backend.buffer object>``. This has been fixed. But you
should avoid using ``str(buf)``: it now gives inconsistent results
between Python 2 and Python 3 (this is similar to how ``str()``
- gives inconsistent results on regular byte strings).
+ gives inconsistent results on regular byte strings). Use ``buf[:]``
+ instead.
``ffi.typeof("C type" or cdata object)``: return an object of type
diff --git a/setup.py b/setup.py
index c3a6402..3b7e3e1 100644
--- a/setup.py
+++ b/setup.py
@@ -42,25 +42,14 @@ def _ask_pkg_config(resultlist, option, result_prefix='', sysroot=False):
resultlist[:] = res
def ask_supports_thread():
- if sys.platform == "darwin":
- sys.stderr.write("OS/X: confusion between 'cc' versus 'gcc'")
- sys.stderr.write(" (see issue 123)\n")
- sys.stderr.write("will not use '__thread' in the C code\n")
- return
- import distutils.errors
- from distutils.ccompiler import new_compiler
- compiler = new_compiler(force=1)
- try:
- compiler.compile(['c/check__thread.c'])
- except distutils.errors.CompileError:
- sys.stderr.write("the above error message can be safely ignored;\n")
- sys.stderr.write("will not use '__thread' in the C code\n")
- else:
+ from distutils.core import Distribution
+ config = Distribution().get_command_obj('config')
+ ok = config.try_compile('__thread int some_threadlocal_variable_42;')
+ if ok:
define_macros.append(('USE__THREAD', None))
- try:
- os.unlink('c/check__thread.o')
- except OSError:
- pass
+ else:
+ sys.stderr.write("Note: will not use '__thread' in the C code\n")
+ sys.stderr.write("The above error message can be safely ignored\n")
def use_pkg_config():
_ask_pkg_config(include_dirs, '--cflags-only-I', '-I', sysroot=True)
@@ -124,7 +113,7 @@ Contact
`Mailing list <https://groups.google.com/forum/#!forum/python-cffi>`_
""",
- version='0.8.2',
+ version='0.8.3',
packages=['cffi'],
zip_safe=False,
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
index 615e366..1ec806f 100644
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -865,25 +865,25 @@ class BackendTests:
def test_enum(self):
ffi = FFI(backend=self.Backend())
- ffi.cdef("enum foo { A, B, CC, D };")
- assert ffi.string(ffi.cast("enum foo", 0)) == "A"
- assert ffi.string(ffi.cast("enum foo", 2)) == "CC"
- assert ffi.string(ffi.cast("enum foo", 3)) == "D"
+ ffi.cdef("enum foo { A0, B0, CC0, D0 };")
+ assert ffi.string(ffi.cast("enum foo", 0)) == "A0"
+ assert ffi.string(ffi.cast("enum foo", 2)) == "CC0"
+ assert ffi.string(ffi.cast("enum foo", 3)) == "D0"
assert ffi.string(ffi.cast("enum foo", 4)) == "4"
- ffi.cdef("enum bar { A, B=-2, CC, D, E };")
- assert ffi.string(ffi.cast("enum bar", 0)) == "A"
- assert ffi.string(ffi.cast("enum bar", -2)) == "B"
- assert ffi.string(ffi.cast("enum bar", -1)) == "CC"
- assert ffi.string(ffi.cast("enum bar", 1)) == "E"
+ ffi.cdef("enum bar { A1, B1=-2, CC1, D1, E1 };")
+ assert ffi.string(ffi.cast("enum bar", 0)) == "A1"
+ assert ffi.string(ffi.cast("enum bar", -2)) == "B1"
+ assert ffi.string(ffi.cast("enum bar", -1)) == "CC1"
+ assert ffi.string(ffi.cast("enum bar", 1)) == "E1"
assert ffi.cast("enum bar", -2) != ffi.cast("enum bar", -2)
assert ffi.cast("enum foo", 0) != ffi.cast("enum bar", 0)
assert ffi.cast("enum bar", 0) != ffi.cast("int", 0)
- assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC>"
+ assert repr(ffi.cast("enum bar", -1)) == "<cdata 'enum bar' -1: CC1>"
assert repr(ffi.cast("enum foo", -1)) == ( # enums are unsigned, if
"<cdata 'enum foo' 4294967295>") # they contain no neg value
- ffi.cdef("enum baz { A=0x1000, B=0x2000 };")
- assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A"
- assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B"
+ ffi.cdef("enum baz { A2=0x1000, B2=0x2000 };")
+ assert ffi.string(ffi.cast("enum baz", 0x1000)) == "A2"
+ assert ffi.string(ffi.cast("enum baz", 0x2000)) == "B2"
def test_enum_in_struct(self):
ffi = FFI(backend=self.Backend())
@@ -1322,6 +1322,16 @@ class BackendTests:
e = ffi.cast("enum e", 0)
assert ffi.string(e) == "AA" # pick the first one arbitrarily
+ def test_enum_refer_previous_enum_value(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("enum e { AA, BB=2, CC=4, DD=BB, EE, FF=CC, GG=FF };")
+ assert ffi.string(ffi.cast("enum e", 2)) == "BB"
+ assert ffi.string(ffi.cast("enum e", 3)) == "EE"
+ assert ffi.sizeof("char[DD]") == 2
+ assert ffi.sizeof("char[EE]") == 3
+ assert ffi.sizeof("char[FF]") == 4
+ assert ffi.sizeof("char[GG]") == 4
+
def test_nested_anonymous_struct(self):
ffi = FFI(backend=self.Backend())
ffi.cdef("""
@@ -1543,6 +1553,7 @@ class BackendTests:
ffi2.include(ffi1)
p = ffi2.cast("enum foo", 1)
assert ffi2.string(p) == "FB"
+ assert ffi2.sizeof("char[FC]") == 2
def test_include_typedef_2(self):
backend = self.Backend()
@@ -1570,3 +1581,25 @@ class BackendTests:
assert s[0].a == b'X'
assert s[1].b == -4892220
assert s[1].a == b'Y'
+
+ def test_define_integer_constant(self):
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ #define DOT_0 0
+ #define DOT 100
+ #define DOT_OCT 0100l
+ #define DOT_HEX 0x100u
+ #define DOT_HEX2 0X10
+ #define DOT_UL 1000UL
+ enum foo {AA, BB=DOT, CC};
+ """)
+ lib = ffi.dlopen(None)
+ assert ffi.string(ffi.cast("enum foo", 100)) == "BB"
+ assert lib.DOT_0 == 0
+ assert lib.DOT == 100
+ assert lib.DOT_OCT == 0o100
+ assert lib.DOT_HEX == 0x100
+ assert lib.DOT_HEX2 == 0x10
+ assert lib.DOT_UL == 1000
+
+
diff --git a/testing/test_function.py b/testing/test_function.py
index aab35de..8edd4ae 100644
--- a/testing/test_function.py
+++ b/testing/test_function.py
@@ -402,3 +402,18 @@ class TestFunction(object):
if wr() is not None:
import gc; gc.collect()
assert wr() is None # 'data' does not leak
+
+ def test_windows_stdcall(self):
+ if sys.platform != 'win32':
+ py.test.skip("Windows-only test")
+ if self.Backend is CTypesBackend:
+ py.test.skip("not with the ctypes backend")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+ """)
+ m = ffi.dlopen("Kernel32.dll")
+ p_freq = ffi.new("LONGLONG *")
+ res = m.QueryPerformanceFrequency(p_freq)
+ assert res != 0
+ assert p_freq[0] != 0
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
index 3b59a69..594796a 100644
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -161,9 +161,10 @@ def test_remove_comments():
def test_define_not_supported_for_now():
ffi = FFI(backend=FakeBackend())
- e = py.test.raises(CDefError, ffi.cdef, "#define FOO 42")
- assert str(e.value) == \
- 'only supports the syntax "#define FOO ..." for now (literally)'
+ e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"')
+ assert str(e.value) == (
+ 'only supports the syntax "#define FOO ..." (literally)'
+ ' or "#define FOO 0x1FF" for now')
def test_unnamed_struct():
ffi = FFI(backend=FakeBackend())
diff --git a/testing/test_verify.py b/testing/test_verify.py
index ac0aaa4..9e4f51f 100644
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1,4 +1,4 @@
-import py
+import py, re
import sys, os, math, weakref
from cffi import FFI, VerificationError, VerificationMissing, model
from testing.support import *
@@ -29,6 +29,24 @@ else:
def setup_module():
import cffi.verifier
cffi.verifier.cleanup_tmpdir()
+ #
+ # check that no $ sign is produced in the C file; it used to be the
+ # case that anonymous enums would produce '$enum_$1', which was
+ # used as part of a function name. GCC accepts such names, but it's
+ # apparently non-standard.
+ _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE)
+ _r_string = re.compile(r'\".*?\"')
+ def _write_source_and_check(self, file=None):
+ base_write_source(self, file)
+ if file is None:
+ f = open(self.sourcefilename)
+ data = f.read()
+ f.close()
+ data = _r_comment.sub(' ', data)
+ data = _r_string.sub('"skipped"', data)
+ assert '$' not in data
+ base_write_source = cffi.verifier.Verifier._write_source
+ cffi.verifier.Verifier._write_source = _write_source_and_check
def test_module_type():
@@ -153,6 +171,9 @@ def test_longdouble_precision():
all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES
+if sys.platform == 'win32':
+ all_primitive_types = all_primitive_types.copy()
+ del all_primitive_types['ssize_t']
all_integer_types = sorted(tp for tp in all_primitive_types
if all_primitive_types[tp] == 'i')
all_float_types = sorted(tp for tp in all_primitive_types
@@ -1452,8 +1473,8 @@ def test_keepalive_ffi():
assert func() == 42
def test_FILE_stored_in_stdout():
- if sys.platform == 'win32':
- py.test.skip("MSVC: cannot assign to stdout")
+ if not sys.platform.startswith('linux'):
+ py.test.skip("likely, we cannot assign to stdout")
ffi = FFI()
ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);")
lib = ffi.verify("""
@@ -1636,8 +1657,8 @@ def test_callback_indirection():
ffi = FFI()
ffi.cdef("""
int (*python_callback)(int how_many, int *values);
- void *const c_callback; /* pass this ptr to C routines */
- int some_c_function(void *cb);
+ int (*const c_callback)(int,...); /* pass this ptr to C routines */
+ int some_c_function(int(*cb)(int,...));
""")
lib = ffi.verify("""
#include <stdarg.h>
@@ -1884,3 +1905,60 @@ def test_ptr_to_opaque():
p = lib.f2(42)
x = lib.f1(p)
assert x == 42
+
+def _run_in_multiple_threads(test1):
+ test1()
+ import sys
+ try:
+ import thread
+ except ImportError:
+ import _thread as thread
+ errors = []
+ def wrapper(lock):
+ try:
+ test1()
+ except:
+ errors.append(sys.exc_info())
+ lock.release()
+ locks = []
+ for i in range(10):
+ _lock = thread.allocate_lock()
+ _lock.acquire()
+ thread.start_new_thread(wrapper, (_lock,))
+ locks.append(_lock)
+ for _lock in locks:
+ _lock.acquire()
+ if errors:
+ raise errors[0][1]
+
+def test_errno_working_even_with_pypys_jit():
+ ffi = FFI()
+ ffi.cdef("int f(int);")
+ lib = ffi.verify("""
+ #include <errno.h>
+ int f(int x) { return (errno = errno + x); }
+ """)
+ @_run_in_multiple_threads
+ def test1():
+ ffi.errno = 0
+ for i in range(10000):
+ e = lib.f(1)
+ assert e == i + 1
+ assert ffi.errno == e
+ for i in range(10000):
+ ffi.errno = i
+ e = lib.f(42)
+ assert e == i + 42
+
+def test_getlasterror_working_even_with_pypys_jit():
+ if sys.platform != 'win32':
+ py.test.skip("win32-only test")
+ ffi = FFI()
+ ffi.cdef("void SetLastError(DWORD);")
+ lib = ffi.dlopen("Kernel32.dll")
+ @_run_in_multiple_threads
+ def test1():
+ for i in range(10000):
+ n = (1 << 29) + i
+ lib.SetLastError(n)
+ assert ffi.getwinerror()[0] == n