Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/ctypes/test/test_as_parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ class S2H(Structure):
s2h = dll.ret_2h_func(self.wrap(inp))
self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))

# Test also that the original struct was unmodified (i.e. was passed by
# value)
self.assertEqual((inp.x, inp.y), (99, 88))

def test_struct_return_8H(self):
class S8I(Structure):
_fields_ = [("a", c_int),
Expand Down
22 changes: 22 additions & 0 deletions Lib/ctypes/test/test_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,28 @@ class X(Structure):
self.assertEqual(s.second, 0xcafebabe)
self.assertEqual(s.third, 0x0bad1dea)

def test_pass_by_value_in_register(self):
class X(Structure):
_fields_ = [
('first', c_uint),
('second', c_uint)
]

s = X()
s.first = 0xdeadbeef
s.second = 0xcafebabe
dll = CDLL(_ctypes_test.__file__)
func = dll._testfunc_reg_struct_update_value
func.argtypes = (X,)
func.restype = None
func(s)
self.assertEqual(s.first, 0xdeadbeef)
self.assertEqual(s.second, 0xcafebabe)
got = X.in_dll(dll, "last_tfrsuv_arg")
self.assertEqual(s.first, got.first)
self.assertEqual(s.second, got.second)


class PointerMemberTestCase(unittest.TestCase):

def test(self):
Expand Down
18 changes: 18 additions & 0 deletions Modules/_ctypes/_ctypes_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ _testfunc_large_struct_update_value(Test in)
((volatile Test *)&in)->third = 0x0badf00d;
}

typedef struct {
unsigned int first;
unsigned int second;
} TestReg;


EXPORT(TestReg) last_tfrsuv_arg;


EXPORT(void)
_testfunc_reg_struct_update_value(TestReg in)
{
last_tfrsuv_arg = in;
((volatile TestReg *)&in)->first = 0x0badf00d;
((volatile TestReg *)&in)->second = 0x0badf00d;
}


EXPORT(void)testfunc_array(int values[4])
{
printf("testfunc_array %d %d %d %d\n",
Expand Down
23 changes: 21 additions & 2 deletions Modules/_ctypes/callproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,13 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
}
#endif

#if (defined(__x86_64__) && (defined(__MINGW64__) || defined(__CYGWIN__))) || \
defined(__aarch64__)
#define CTYPES_PASS_BY_REF_HACK
#define POW2(x) (((x & ~(x - 1)) == x) ? x : 0)
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
#endif

/*
* Requirements, must be ensured by the caller:
* - argtuple is tuple of arguments
Expand Down Expand Up @@ -1136,8 +1143,20 @@ PyObject *_ctypes_callproc(PPROC pProc,
}
for (i = 0; i < argcount; ++i) {
atypes[i] = args[i].ffi_type;
if (atypes[i]->type == FFI_TYPE_STRUCT
)
#ifdef CTYPES_PASS_BY_REF_HACK
size_t size = atypes[i]->size;
if (IS_PASS_BY_REF(size)) {
void *tmp = alloca(size);
if (atypes[i]->type == FFI_TYPE_STRUCT)
memcpy(tmp, args[i].value.p, size);
else
memcpy(tmp, (void*)&args[i].value, size);

avalues[i] = tmp;
}
else
#endif
if (atypes[i]->type == FFI_TYPE_STRUCT)
avalues[i] = (void *)args[i].value.p;
else
avalues[i] = (void *)&args[i].value;
Expand Down