]> granicus.if.org Git - python/commitdiff
bpo-30353: Fix pass by value for structs on 64-bit Cygwin/MinGW (GH-1559)
authorErik Bray <erik.m.bray@gmail.com>
Wed, 7 Jun 2017 17:42:24 +0000 (18:42 +0100)
committerVinay Sajip <vinay_sajip@yahoo.co.uk>
Wed, 7 Jun 2017 17:42:24 +0000 (18:42 +0100)
Lib/ctypes/test/test_as_parameter.py
Lib/ctypes/test/test_structures.py
Modules/_ctypes/_ctypes_test.c
Modules/_ctypes/callproc.c

index 2a3484bec01b99ac614ddb5e199ee4b9364b200a..a2640575a07452e5038a6ea6b798c9c4a21db949 100644 (file)
@@ -169,6 +169,10 @@ class BasicWrapTestCase(unittest.TestCase):
         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),
index 2e778fb1b4374075f3f4a7de86da0e709dc320a2..d90c71144c9a9b80589a9d5d87e43b074c551b8c 100644 (file)
@@ -417,6 +417,28 @@ class StructureTestCase(unittest.TestCase):
         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):
index fe0015c80136f35e9b5013a2cef2c9fb080b2db4..2255e57339301b4cf7ecf3e943ddac9eb817a91e 100644 (file)
@@ -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",
index 0b6faf96c68f6ac17e75f6aed25850f85f7b69f4..5439b939dc49c17535bdd85a62ca514ac884b7b8 100644 (file)
@@ -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
@@ -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;