Skip to content

Commit 0b58886

Browse files
committed
Issue #20160: Merged fix from 3.4.
2 parents b858af6 + a0b2568 commit 0b58886

3 files changed

Lines changed: 65 additions & 2 deletions

File tree

‎Lib/ctypes/test/test_callbacks.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import functools
12
import unittest
23
from ctypes import *
34
from ctypes.test import need_symbol
@@ -240,6 +241,40 @@ def callback(a, b, c, d, e):
240241
self.assertEqual(result,
241242
callback(1.1*1.1, 2.2*2.2, 3.3*3.3, 4.4*4.4, 5.5*5.5))
242243

244+
def test_callback_large_struct(self):
245+
class Check: pass
246+
247+
class X(Structure):
248+
_fields_ = [
249+
('first', c_ulong),
250+
('second', c_ulong),
251+
('third', c_ulong),
252+
]
253+
254+
def callback(check, s):
255+
check.first = s.first
256+
check.second = s.second
257+
check.third = s.third
258+
259+
check = Check()
260+
s = X()
261+
s.first = 0xdeadbeef
262+
s.second = 0xcafebabe
263+
s.third = 0x0bad1dea
264+
265+
CALLBACK = CFUNCTYPE(None, X)
266+
dll = CDLL(_ctypes_test.__file__)
267+
func = dll._testfunc_cbk_large_struct
268+
func.argtypes = (X, CALLBACK)
269+
func.restype = None
270+
# the function just calls the callback with the passed structure
271+
func(s, CALLBACK(functools.partial(callback, check)))
272+
self.assertEqual(check.first, s.first)
273+
self.assertEqual(check.second, s.second)
274+
self.assertEqual(check.third, s.third)
275+
self.assertEqual(check.first, 0xdeadbeef)
276+
self.assertEqual(check.second, 0xcafebabe)
277+
self.assertEqual(check.third, 0x0bad1dea)
243278

244279
################################################################
245280

‎Modules/_ctypes/_ctypes_test.c‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,24 @@ _testfunc_cbk_reg_double(double a, double b, double c, double d, double e,
2626
return func(a*a, b*b, c*c, d*d, e*e);
2727
}
2828

29+
/*
30+
* This structure should be the same as in test_callbacks.py and the
31+
* method test_callback_large_struct. See issues 17310 and 20160: the
32+
* structure must be larger than 8 bytes long.
33+
*/
34+
35+
typedef struct {
36+
unsigned long first;
37+
unsigned long second;
38+
unsigned long third;
39+
} Test;
40+
41+
EXPORT(void)
42+
_testfunc_cbk_large_struct(Test in, void (*func)(Test))
43+
{
44+
func(in);
45+
}
46+
2947
EXPORT(void)testfunc_array(int values[4])
3048
{
3149
printf("testfunc_array %d %d %d %d\n",

‎Modules/_ctypes/libffi_msvc/ffi.c‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
378378

379379
if ( cif->rtype->type == FFI_TYPE_STRUCT ) {
380380
*rvalue = *(void **) argp;
381-
argp += 4;
381+
argp += sizeof(void *);
382382
}
383383

384384
p_argv = avalue;
@@ -389,13 +389,23 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
389389

390390
/* Align if necessary */
391391
if ((sizeof(char *) - 1) & (size_t) argp) {
392-
argp = (char *) ALIGN(argp, sizeof(char*));
392+
argp = (char *) ALIGN(argp, sizeof(char*));
393393
}
394394

395395
z = (*p_arg)->size;
396396

397397
/* because we're little endian, this is what it turns into. */
398398

399+
#ifdef _WIN64
400+
if (z > 8) {
401+
/* On Win64, if a single argument takes more than 8 bytes,
402+
* then it is always passed by reference.
403+
*/
404+
*p_argv = *((void**) argp);
405+
z = 8;
406+
}
407+
else
408+
#endif
399409
*p_argv = (void*) argp;
400410

401411
p_argv++;

0 commit comments

Comments
 (0)