Fixed bpo-29565: Corrected ctypes passing of large structs by value o… · python/cpython@3cc5817

4 files changed

lines changed

Original file line numberDiff line numberDiff line change

@@ -244,6 +244,7 @@ def callback(a, b, c, d, e):

244244

def test_callback_large_struct(self):

245245

class Check: pass

246246
247+

# This should mirror the structure in Modules/_ctypes/_ctypes_test.c

247248

class X(Structure):

248249

_fields_ = [

249250

('first', c_ulong),

@@ -255,6 +256,11 @@ def callback(check, s):

255256

check.first = s.first

256257

check.second = s.second

257258

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

258264
259265

check = Check()

260266

s = X()

@@ -275,6 +281,11 @@ def callback(check, s):

275281

self.assertEqual(check.first, 0xdeadbeef)

276282

self.assertEqual(check.second, 0xcafebabe)

277283

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)

278289
279290

################################################################

280291
Original file line numberDiff line numberDiff line change

@@ -3,6 +3,7 @@

33

from ctypes.test import need_symbol

44

from struct import calcsize

55

import _testcapi

6+

import _ctypes_test

67
78

class SubclassesTest(unittest.TestCase):

89

def test_subclass(self):

@@ -391,6 +392,28 @@ class Z(Y):

391392

(1, 0, 0, 0, 0, 0))

392393

self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7))

393394
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+
394417

class PointerMemberTestCase(unittest.TestCase):

395418
396419

def test(self):

Original file line numberDiff line numberDiff line change

@@ -44,6 +44,19 @@ _testfunc_cbk_large_struct(Test in, void (*func)(Test))

4444

func(in);

4545

}

4646
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+
4760

EXPORT(void)testfunc_array(int values[4])

4861

{

4962

printf("testfunc_array %d %d %d %d\n",

Original file line numberDiff line numberDiff line change

@@ -239,6 +239,16 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,

239239

break;

240240

#else

241241

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+

}

242252

/*@-usedef@*/

243253

return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,

244254

cif->flags, ecif.rvalue, fn);