Fixed bpo-29565: Corrected ctypes passing of large structs by value o… · python/cpython@a86339b (original) (raw)

4 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -244,6 +244,7 @@ def callback(a, b, c, d, e):
244 244 def test_callback_large_struct(self):
245 245 class Check: pass
246 246
247 +# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
247 248 class X(Structure):
248 249 _fields_ = [
249 250 ('first', c_ulong),
@@ -255,6 +256,11 @@ def callback(check, s):
255 256 check.first = s.first
256 257 check.second = s.second
257 258 check.third = s.third
259 +# See issue #29565.
260 +# The structure should be passed by value, so
261 +# any changes to it should not be reflected in
262 +# the value passed
263 +s.first = s.second = s.third = 0x0badf00d
258 264
259 265 check = Check()
260 266 s = X()
@@ -275,6 +281,11 @@ def callback(check, s):
275 281 self.assertEqual(check.first, 0xdeadbeef)
276 282 self.assertEqual(check.second, 0xcafebabe)
277 283 self.assertEqual(check.third, 0x0bad1dea)
284 +# See issue #29565.
285 +# Ensure that the original struct is unchanged.
286 +self.assertEqual(s.first, check.first)
287 +self.assertEqual(s.second, check.second)
288 +self.assertEqual(s.third, check.third)
278 289
279 290 ################################################################
280 291
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):
@@ -391,6 +392,28 @@ class Z(Y):
391 392 (1, 0, 0, 0, 0, 0))
392 393 self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7))
393 394
395 +def test_pass_by_value(self):
396 +# This should mirror the structure in Modules/_ctypes/_ctypes_test.c
397 +class X(Structure):
398 +_fields_ = [
399 + ('first', c_ulong),
400 + ('second', c_ulong),
401 + ('third', c_ulong),
402 + ]
403 +
404 +s = X()
405 +s.first = 0xdeadbeef
406 +s.second = 0xcafebabe
407 +s.third = 0x0bad1dea
408 +dll = CDLL(_ctypes_test.__file__)
409 +func = dll._testfunc_large_struct_update_value
410 +func.argtypes = (X,)
411 +func.restype = None
412 +func(s)
413 +self.assertEqual(s.first, 0xdeadbeef)
414 +self.assertEqual(s.second, 0xcafebabe)
415 +self.assertEqual(s.third, 0x0bad1dea)
416 +
394 417 class PointerMemberTestCase(unittest.TestCase):
395 418
396 419 def test(self):
Original file line number Diff line number Diff line change
@@ -44,6 +44,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test))
44 44 func(in);
45 45 }
46 46
47 +/*
48 + * See issue 29565. Update a structure passed by value;
49 + * the caller should not see any change.
50 + */
51 +
52 +EXPORT(void)
53 +_testfunc_large_struct_update_value(Test in)
54 +{
55 +in.first = 0x0badf00d;
56 +in.second = 0x0badf00d;
57 +in.third = 0x0badf00d;
58 +}
59 +
47 60 EXPORT(void)testfunc_array(int values[4])
48 61 {
49 62 printf("testfunc_array %d %d %d %d\n",
Original file line number Diff line number Diff line change
@@ -239,6 +239,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
239 239 break;
240 240 #else
241 241 case FFI_SYSV:
242 +/* If a single argument takes more than 8 bytes,
243 + then a copy is passed by reference. */
244 +for (unsigned i = 0; i < cif->nargs; i++) {
245 +size_t z = cif->arg_types[i]->size;
246 +if (z > 8) {
247 +void *temp = alloca(z);
248 +memcpy(temp, avalue[i], z);
249 +avalue[i] = temp;
250 + }
251 + }
242 252 /*@-usedef@*/
243 253 return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
244 254 cif->flags, ecif.rvalue, fn);