Issue #20160: Handled passing of large structs to callbacks correctly.

This commit is contained in:
Vinay Sajip 2016-08-05 21:24:27 +01:00
parent fb7929044a
commit 6f25003291
3 changed files with 65 additions and 2 deletions

View File

@ -1,3 +1,4 @@
import functools
import unittest
from ctypes import *
import _ctypes_test
@ -243,6 +244,40 @@ class SampleCallbacksTestCase(unittest.TestCase):
self.assertEqual(result,
callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5))
def test_callback_large_struct(self):
class Check: pass
class X(Structure):
_fields_ = [
('first', c_ulong),
('second', c_ulong),
('third', c_ulong),
]
def callback(check, s):
check.first = s.first
check.second = s.second
check.third = s.third
check = Check()
s = X()
s.first = 0xdeadbeef
s.second = 0xcafebabe
s.third = 0x0bad1dea
CALLBACK = CFUNCTYPE(None, X)
dll = CDLL(_ctypes_test.__file__)
func = dll._testfunc_cbk_large_struct
func.argtypes = (X, CALLBACK)
func.restype = None
# the function just calls the callback with the passed structure
func(s, CALLBACK(functools.partial(callback, check)))
self.assertEqual(check.first, s.first)
self.assertEqual(check.second, s.second)
self.assertEqual(check.third, s.third)
self.assertEqual(check.first, 0xdeadbeef)
self.assertEqual(check.second, 0xcafebabe)
self.assertEqual(check.third, 0x0bad1dea)
################################################################

View File

@ -26,6 +26,24 @@ _testfunc_cbk_reg_double(double a, double b, double c, double d, double e,
return func(a*a, b*b, c*c, d*d, e*e);
}
/*
* This structure should be the same as in test_callbacks.py and the
* method test_callback_large_struct. See issues 17310 and 20160: the
* structure must be larger than 8 bytes long.
*/
typedef struct {
unsigned long first;
unsigned long second;
unsigned long third;
} Test;
EXPORT(void)
_testfunc_cbk_large_struct(Test in, void (*func)(Test))
{
func(in);
}
EXPORT(void)testfunc_array(int values[4])
{
printf("testfunc_array %d %d %d %d\n",

View File

@ -340,7 +340,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
if ( cif->rtype->type == FFI_TYPE_STRUCT ) {
*rvalue = *(void **) argp;
argp += 4;
argp += sizeof(void *);
}
p_argv = avalue;
@ -351,13 +351,23 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
/* Align if necessary */
if ((sizeof(char *) - 1) & (size_t) argp) {
argp = (char *) ALIGN(argp, sizeof(char*));
argp = (char *) ALIGN(argp, sizeof(char*));
}
z = (*p_arg)->size;
/* because we're little endian, this is what it turns into. */
#ifdef _WIN64
if (z > 8) {
/* On Win64, if a single argument takes more than 8 bytes,
* then it is always passed by reference.
*/
*p_argv = *((void**) argp);
z = 8;
}
else
#endif
*p_argv = (void*) argp;
p_argv++;