summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--c/_cffi_backend.c25
-rw-r--r--cffi/_cffi_include.h48
-rw-r--r--cffi/recompiler.py30
-rw-r--r--cffi/vengine_cpy.py83
-rw-r--r--testing/cffi0/test_function.py13
-rw-r--r--testing/cffi0/test_verify.py10
-rw-r--r--testing/cffi1/test_recompiler.py10
7 files changed, 196 insertions, 23 deletions
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
index 212a019..6826780 100644
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -2991,6 +2991,10 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
CTypeDescrObject *fresult;
char *resultdata;
char *errormsg;
+ struct freeme_s {
+ struct freeme_s *next;
+ union_alignment alignment;
+ } *freeme = NULL;
if (!(cd->c_type->ct_flags & CT_FUNCTIONPTR)) {
PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable",
@@ -3111,7 +3115,21 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
else if (datasize < 0)
goto error;
else {
- tmpbuf = alloca(datasize);
+ if (datasize <= 512) {
+ tmpbuf = alloca(datasize);
+ }
+ else {
+ struct freeme_s *fp = (struct freeme_s *)PyObject_Malloc(
+ offsetof(struct freeme_s, alignment) +
+ (size_t)datasize);
+ if (fp == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+ fp->next = freeme;
+ freeme = fp;
+ tmpbuf = (char *)&fp->alignment;
+ }
memset(tmpbuf, 0, datasize);
*(char **)data = tmpbuf;
if (convert_array_from_object(tmpbuf, argtype, obj) < 0)
@@ -3156,6 +3174,11 @@ cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
/* fall-through */
error:
+ while (freeme != NULL) {
+ void *p = (void *)freeme;
+ freeme = freeme->next;
+ PyObject_Free(p);
+ }
if (buffer)
PyObject_Free(buffer);
if (fvarargs != NULL) {
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
index 2eeb49b..3129150 100644
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -269,6 +269,54 @@ _CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x)
return _cffi_from_c_wchar3216_t((int)x);
}
+union _cffi_union_alignment_u {
+ 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;
+};
+
+struct _cffi_freeme_s {
+ struct _cffi_freeme_s *next;
+ union _cffi_union_alignment_u alignment;
+};
+
+_CFFI_UNUSED_FN static int
+_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg,
+ char **output_data, Py_ssize_t datasize,
+ struct _cffi_freeme_s **freeme)
+{
+ char *p;
+ if (datasize < 0)
+ return -1;
+
+ p = *output_data;
+ if (p == NULL) {
+ struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
+ offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
+ if (fp == NULL)
+ return -1;
+ fp->next = *freeme;
+ *freeme = fp;
+ p = *output_data = (char *)&fp->alignment;
+ }
+ memset((void *)p, 0, (size_t)datasize);
+ return _cffi_convert_array_from_object(p, ctptr, arg);
+}
+
+_CFFI_UNUSED_FN static void
+_cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
+{
+ do {
+ void *p = (void *)freeme;
+ freeme = freeme->next;
+ PyObject_Free(p);
+ } while (freeme != NULL);
+}
/********** end CPython-specific section **********/
#else
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
index c3a3845..d66ff7f 100644
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -560,23 +560,24 @@ class Recompiler:
tovar, tp.get_c_name(''), errvalue))
self._prnt(' %s;' % errcode)
- def _extra_local_variables(self, tp, localvars):
+ def _extra_local_variables(self, tp, localvars, freelines):
if isinstance(tp, model.PointerType):
localvars.add('Py_ssize_t datasize')
+ localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
+ freelines.add('if (large_args_free != NULL)'
+ ' _cffi_free_array_arguments(large_args_free);')
def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
self._prnt(' datasize = _cffi_prepare_pointer_call_argument(')
self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % (
self._gettypenum(tp), fromvar, tovar))
self._prnt(' if (datasize != 0) {')
- self._prnt(' if (datasize < 0)')
- self._prnt(' %s;' % errcode)
- self._prnt(' %s = (%s)alloca((size_t)datasize);' % (
+ self._prnt(' %s = ((size_t)datasize) <= 640 ? '
+ '(%s)alloca((size_t)datasize) : NULL;' % (
tovar, tp.get_c_name('')))
- self._prnt(' memset((void *)%s, 0, (size_t)datasize);' % (tovar,))
- self._prnt(' if (_cffi_convert_array_from_object('
- '(char *)%s, _cffi_type(%d), %s) < 0)' % (
- tovar, self._gettypenum(tp), fromvar))
+ self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
+ '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
+ self._prnt(' datasize, &large_args_free) < 0)')
self._prnt(' %s;' % errcode)
self._prnt(' }')
@@ -699,9 +700,10 @@ class Recompiler:
prnt(' %s;' % arg)
#
localvars = set()
+ freelines = set()
for type in tp.args:
- self._extra_local_variables(type, localvars)
- for decl in localvars:
+ self._extra_local_variables(type, localvars, freelines)
+ for decl in sorted(localvars):
prnt(' %s;' % (decl,))
#
if not isinstance(tp.result, model.VoidType):
@@ -709,6 +711,7 @@ class Recompiler:
context = 'result of %s' % name
result_decl = ' %s;' % tp.result.get_c_name(' result', context)
prnt(result_decl)
+ prnt(' PyObject *pyresult;')
else:
result_decl = None
result_code = ''
@@ -742,9 +745,14 @@ class Recompiler:
if numargs == 0:
prnt(' (void)noarg; /* unused */')
if result_code:
- prnt(' return %s;' %
+ prnt(' pyresult = %s;' %
self._convert_expr_from_c(tp.result, 'result', 'result type'))
+ for freeline in freelines:
+ prnt(' ' + freeline)
+ prnt(' return pyresult;')
else:
+ for freeline in freelines:
+ prnt(' ' + freeline)
prnt(' Py_INCREF(Py_None);')
prnt(' return Py_None;')
prnt('}')
diff --git a/cffi/vengine_cpy.py b/cffi/vengine_cpy.py
index 536f11f..cb344ce 100644
--- a/cffi/vengine_cpy.py
+++ b/cffi/vengine_cpy.py
@@ -275,22 +275,23 @@ class VCPythonEngine(object):
tovar, tp.get_c_name(''), errvalue))
self._prnt(' %s;' % errcode)
- def _extra_local_variables(self, tp, localvars):
+ def _extra_local_variables(self, tp, localvars, freelines):
if isinstance(tp, model.PointerType):
localvars.add('Py_ssize_t datasize')
+ localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
+ freelines.add('if (large_args_free != NULL)'
+ ' _cffi_free_array_arguments(large_args_free);')
def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
self._prnt(' datasize = _cffi_prepare_pointer_call_argument(')
self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % (
self._gettypenum(tp), fromvar, tovar))
self._prnt(' if (datasize != 0) {')
- self._prnt(' if (datasize < 0)')
- self._prnt(' %s;' % errcode)
- self._prnt(' %s = alloca((size_t)datasize);' % (tovar,))
- self._prnt(' memset((void *)%s, 0, (size_t)datasize);' % (tovar,))
- self._prnt(' if (_cffi_convert_array_from_object('
- '(char *)%s, _cffi_type(%d), %s) < 0)' % (
- tovar, self._gettypenum(tp), fromvar))
+ self._prnt(' %s = ((size_t)datasize) <= 640 ? '
+ 'alloca((size_t)datasize) : NULL;' % (tovar,))
+ self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
+ '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
+ self._prnt(' datasize, &large_args_free) < 0)')
self._prnt(' %s;' % errcode)
self._prnt(' }')
@@ -369,15 +370,17 @@ class VCPythonEngine(object):
prnt(' %s;' % type.get_c_name(' x%d' % i, context))
#
localvars = set()
+ freelines = set()
for type in tp.args:
- self._extra_local_variables(type, localvars)
- for decl in localvars:
+ self._extra_local_variables(type, localvars, freelines)
+ for decl in sorted(localvars):
prnt(' %s;' % (decl,))
#
if not isinstance(tp.result, model.VoidType):
result_code = 'result = '
context = 'result of %s' % name
prnt(' %s;' % tp.result.get_c_name(' result', context))
+ prnt(' PyObject *pyresult;')
else:
result_code = ''
#
@@ -409,9 +412,14 @@ class VCPythonEngine(object):
if numargs == 0:
prnt(' (void)noarg; /* unused */')
if result_code:
- prnt(' return %s;' %
+ prnt(' pyresult = %s;' %
self._convert_expr_from_c(tp.result, 'result', 'result type'))
+ for freeline in freelines:
+ prnt(' ' + freeline)
+ prnt(' return pyresult;')
else:
+ for freeline in freelines:
+ prnt(' ' + freeline)
prnt(' Py_INCREF(Py_None);')
prnt(' return Py_None;')
prnt('}')
@@ -981,6 +989,59 @@ static PyObject *_cffi_setup(PyObject *self, PyObject *args)
return PyBool_FromLong(was_alive);
}
+union _cffi_union_alignment_u {
+ 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;
+};
+
+struct _cffi_freeme_s {
+ struct _cffi_freeme_s *next;
+ union _cffi_union_alignment_u alignment;
+};
+
+#ifdef __GNUC__
+ __attribute__((unused))
+#endif
+static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg,
+ char **output_data, Py_ssize_t datasize,
+ struct _cffi_freeme_s **freeme)
+{
+ char *p;
+ if (datasize < 0)
+ return -1;
+
+ p = *output_data;
+ if (p == NULL) {
+ struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc(
+ offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize);
+ if (fp == NULL)
+ return -1;
+ fp->next = *freeme;
+ *freeme = fp;
+ p = *output_data = (char *)&fp->alignment;
+ }
+ memset((void *)p, 0, (size_t)datasize);
+ return _cffi_convert_array_from_object(p, ctptr, arg);
+}
+
+#ifdef __GNUC__
+ __attribute__((unused))
+#endif
+static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme)
+{
+ do {
+ void *p = (void *)freeme;
+ freeme = freeme->next;
+ PyObject_Free(p);
+ } while (freeme != NULL);
+}
+
static int _cffi_init(void)
{
PyObject *module, *c_api_object = NULL;
diff --git a/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
index d1f9ce4..6312707 100644
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -520,3 +520,16 @@ class TestFunction(object):
assert str(e.value).startswith("library '")
assert str(e.value).endswith("' has already been closed")
ffi.dlclose(lib) # does not raise
+
+ def test_passing_large_list(self):
+ if self.Backend is CTypesBackend:
+ py.test.skip("the ctypes backend doesn't support this")
+ ffi = FFI(backend=self.Backend())
+ ffi.cdef("""
+ void getenv(char *);
+ """)
+ needs_dlopen_none()
+ m = ffi.dlopen(None)
+ arg = [b"F", b"O", b"O"] + [b"\x00"] * 20000000
+ x = m.getenv(arg)
+ assert x is None
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
index 52ce214..3a1c0b9 100644
--- a/testing/cffi0/test_verify.py
+++ b/testing/cffi0/test_verify.py
@@ -2550,3 +2550,13 @@ def test_arithmetic_in_cdef():
""".replace('?', str(a)))
# the verify() crashes if the values in the enum are different from
# the values we computed ourselves from the cdef()
+
+def test_passing_large_list():
+ ffi = FFI()
+ ffi.cdef("""void passing_large_list(long[]);""")
+ lib = ffi.verify("""
+ static void passing_large_list(long a[]) { }
+ """)
+ arg = list(range(20000000))
+ lib.passing_large_list(arg)
+ # assert did not segfault
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
index 1b837f8..78abaa0 100644
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -2445,3 +2445,13 @@ def test_struct_with_func_with_struct_arg():
};
""")
py.test.raises(RuntimeError, ffi.new, "struct BinaryTree *")
+
+def test_passing_large_list():
+ ffi = FFI()
+ ffi.cdef("""void passing_large_list(long[]);""")
+ lib = verify(ffi, "test_passing_large_list", """
+ static void passing_large_list(long a[]) { }
+ """)
+ arg = list(range(20000000))
+ lib.passing_large_list(arg)
+ # assert did not segfault