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