bpo-29565: Corrected ctypes passing of large structs by value on Wind… · python/cpython@3243f8c (original) (raw)

4 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -250,6 +250,7 @@ def callback(a, b, c, d, e):
250 250 def test_callback_large_struct(self):
251 251 class Check: pass
252 252
253 +# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
253 254 class X(Structure):
254 255 _fields_ = [
255 256 ('first', c_ulong),
@@ -261,6 +262,11 @@ def callback(check, s):
261 262 check.first = s.first
262 263 check.second = s.second
263 264 check.third = s.third
265 +# See issue #29565.
266 +# The structure should be passed by value, so
267 +# any changes to it should not be reflected in
268 +# the value passed
269 +s.first = s.second = s.third = 0x0badf00d
264 270
265 271 check = Check()
266 272 s = X()
@@ -281,6 +287,11 @@ def callback(check, s):
281 287 self.assertEqual(check.first, 0xdeadbeef)
282 288 self.assertEqual(check.second, 0xcafebabe)
283 289 self.assertEqual(check.third, 0x0bad1dea)
290 +# See issue #29565.
291 +# Ensure that the original struct is unchanged.
292 +self.assertEqual(s.first, check.first)
293 +self.assertEqual(s.second, check.second)
294 +self.assertEqual(s.third, check.third)
284 295
285 296 ################################################################
286 297
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
3 3 from ctypes.test import need_symbol
4 4 from struct import calcsize
5 5 import _testcapi
6 +import _ctypes_test
6 7
7 8 class SubclassesTest(unittest.TestCase):
8 9 def test_subclass(self):
@@ -401,6 +402,28 @@ class Z(Y):
401 402 (1, 0, 0, 0, 0, 0))
402 403 self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7))
403 404
405 +def test_pass_by_value(self):
406 +# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
407 +class X(Structure):
408 +_fields_ = [
409 + ('first', c_ulong),
410 + ('second', c_ulong),
411 + ('third', c_ulong),
412 + ]
413 +
414 +s = X()
415 +s.first = 0xdeadbeef
416 +s.second = 0xcafebabe
417 +s.third = 0x0bad1dea
418 +dll = CDLL(_ctypes_test.__file__)
419 +func = dll._testfunc_large_struct_update_value
420 +func.argtypes = (X,)
421 +func.restype = None
422 +func(s)
423 +self.assertEqual(s.first, 0xdeadbeef)
424 +self.assertEqual(s.second, 0xcafebabe)
425 +self.assertEqual(s.third, 0x0bad1dea)
426 +
404 427 class PointerMemberTestCase(unittest.TestCase):
405 428
406 429 def test(self):
Original file line number Diff line number Diff line change
@@ -52,6 +52,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test))
52 52 func(in);
53 53 }
54 54
55 +/*
56 + * See issue 29565. Update a structure passed by value;
57 + * the caller should not see any change.
58 + */
59 +
60 +EXPORT(void)
61 +_testfunc_large_struct_update_value(Test in)
62 +{
63 +in.first = 0x0badf00d;
64 +in.second = 0x0badf00d;
65 +in.third = 0x0badf00d;
66 +}
67 +
55 68 EXPORT(void)testfunc_array(int values[4])
56 69 {
57 70 printf("testfunc_array %d %d %d %d\n",
Original file line number Diff line number Diff line change
@@ -220,6 +220,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
220 220 break;
221 221 #else
222 222 case FFI_SYSV:
223 +/* If a single argument takes more than 8 bytes,
224 + then a copy is passed by reference. */
225 +for (unsigned i = 0; i < cif->nargs; i++) {
226 +size_t z = cif->arg_types[i]->size;
227 +if (z > 8) {
228 +void *temp = alloca(z);
229 +memcpy(temp, avalue[i], z);
230 +avalue[i] = temp;
231 + }
232 + }
223 233 /*@-usedef@*/
224 234 return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
225 235 cif->flags, ecif.rvalue, fn);