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); |