Skip to content

Commit dd66f45

Browse files
authored
Run ctypes tests on macOS (#1913)
* Run ctypes tests on macOS * Fix marshalling of short integers * Disable failing ctypes tests * Enable cypes test on linux aarch64 * Reorder `using` * Enable `test_bitfields_ctypes_stdlib` on macOS
1 parent 877b087 commit dd66f45

14 files changed

+211
-75
lines changed

src/core/IronPython.Modules/ModuleOps.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using IronPython.Runtime.Exceptions;
1818
using IronPython.Runtime.Operations;
1919
using IronPython.Runtime.Types;
20+
using System.Runtime.CompilerServices;
2021

2122
namespace IronPython.Modules {
2223
/// <summary>
@@ -526,12 +527,12 @@ public static int GetSignedInt(object value, object type) {
526527
throw PythonOps.TypeErrorForTypeMismatch("signed int", value);
527528
}
528529

529-
public static short GetUnsignedShort(object value, object type) {
530+
public static ushort GetUnsignedShort(object value, object type) {
530531
int? res = Converter.ImplicitConvertToInt32(value);
531532
if (res != null) {
532533
int iVal = res.Value;
533534
if (iVal >= ushort.MinValue && iVal <= ushort.MaxValue) {
534-
return (short)(ushort)iVal;
535+
return (ushort)iVal;
535536
}
536537
}
537538
if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) {
@@ -574,12 +575,12 @@ public static byte GetUnsignedByte(object value, object type) {
574575
throw PythonOps.TypeErrorForTypeMismatch("unsigned byte", value);
575576
}
576577

577-
public static byte GetSignedByte(object value, object type) {
578+
public static sbyte GetSignedByte(object value, object type) {
578579
int? res = Converter.ImplicitConvertToInt32(value);
579580
if (res != null) {
580581
int iVal = res.Value;
581582
if (iVal >= sbyte.MinValue && iVal <= sbyte.MaxValue) {
582-
return (byte)(sbyte)iVal;
583+
return (sbyte)iVal;
583584
}
584585
}
585586

src/core/IronPython.Modules/_ctypes/MemoryHolder.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public MemoryHolder(int size) {
4545
try {
4646
} finally {
4747
_size = size;
48-
_data = NativeFunctions.Calloc(new IntPtr(size));
48+
_data = NativeFunctions.Calloc(checked((nuint)size));
4949
if (_data == IntPtr.Zero) {
5050
GC.SuppressFinalize(this);
5151
throw new OutOfMemoryException();
@@ -289,8 +289,8 @@ public void WriteIntPtr(int offset, MemoryHolder address) {
289289
/// <summary>
290290
/// Copies the data in data into this MemoryHolder.
291291
/// </summary>
292-
public void CopyFrom(IntPtr source, IntPtr size) {
293-
NativeFunctions.MemCopy(_data, source, size);
292+
public void CopyFrom(IntPtr source, nint size) {
293+
NativeFunctions.MemCopy(_data, source, checked((nuint)size));
294294
GC.KeepAlive(this);
295295
}
296296

@@ -319,7 +319,7 @@ public MemoryHolder GetSubBlock(int offset) {
319319
/// operation.
320320
/// </summary>
321321
public void CopyTo(MemoryHolder/*!*/ destAddress, int writeOffset, int size) {
322-
NativeFunctions.MemCopy(destAddress._data.Add(writeOffset), _data, new IntPtr(size));
322+
NativeFunctions.MemCopy(destAddress._data.Add(writeOffset), _data, checked((nuint)size));
323323
GC.KeepAlive(destAddress);
324324
GC.KeepAlive(this);
325325
}

src/core/IronPython.Modules/_ctypes/NativeFunctions.cs

Lines changed: 127 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Runtime.ConstrainedExecution;
1010
using System.Runtime.InteropServices;
11+
using System.Runtime.Versioning;
1112

1213
//[assembly: PythonModule("_ctypes", typeof(IronPython.Modules.CTypes))]
1314
namespace IronPython.Modules {
@@ -19,60 +20,96 @@ internal static class NativeFunctions {
1920
private static MoveMemoryDelegate _moveMem = MoveMemory;
2021

2122
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
22-
private delegate IntPtr SetMemoryDelegate(IntPtr dest, byte value, IntPtr length);
23+
private delegate IntPtr SetMemoryDelegate(IntPtr dest, byte value, nuint length);
2324
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
24-
private delegate IntPtr MoveMemoryDelegate(IntPtr dest, IntPtr src, IntPtr length);
25+
private delegate IntPtr MoveMemoryDelegate(IntPtr dest, IntPtr src, nuint length);
2526

27+
[SupportedOSPlatform("windows")]
28+
[DllImport("kernel32.dll", SetLastError = true)]
29+
private static extern IntPtr LoadLibrary(string lpFileName);
30+
31+
[SupportedOSPlatform("windows")]
2632
[DllImport("kernel32.dll")]
2733
[return: MarshalAs(UnmanagedType.Bool)]
2834
public static extern bool FreeLibrary(IntPtr hModule);
2935

30-
[DllImport("kernel32.dll", SetLastError = true)]
31-
private static extern IntPtr LoadLibrary(string lpFileName);
32-
36+
[SupportedOSPlatform("windows")]
3337
[DllImport("kernel32.dll")]
3438
public static extern void SetLastError(int errorCode);
3539

40+
[SupportedOSPlatform("windows")]
3641
[DllImport("kernel32.dll")]
3742
public static extern int GetLastError();
43+
[SupportedOSPlatform("windows")]
3844

45+
[SupportedOSPlatform("windows")]
3946
[DllImport("kernel32.dll")]
4047
private static extern IntPtr GetProcAddress(IntPtr module, string lpFileName);
4148

49+
[SupportedOSPlatform("windows")]
4250
[DllImport("kernel32.dll")]
4351
private static extern IntPtr GetProcAddress(IntPtr module, IntPtr ordinal);
4452

53+
[SupportedOSPlatform("linux")]
4554
[DllImport("libc")]
46-
private static extern void memcpy(IntPtr dst, IntPtr src, IntPtr length);
55+
private static extern unsafe void memcpy(void* dst, void* src, nuint length);
4756

48-
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", ExactSpelling = true)]
49-
private static extern void CopyMemory(IntPtr destination, IntPtr source, IntPtr length);
57+
[SupportedOSPlatform("macos")]
58+
[DllImport("libSystem.dylib", EntryPoint = "memcpy")]
59+
private static extern unsafe void memcpy_darwin(void* dst, void* src, nuint length);
5060

51-
public static void MemCopy(IntPtr destination, IntPtr source, IntPtr length) {
52-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
53-
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
54-
memcpy(destination, source, length);
61+
[SupportedOSPlatform("windows")]
62+
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", ExactSpelling = true)]
63+
private static extern unsafe void CopyMemory(void* destination, void* source, nuint length);
64+
65+
public static unsafe void MemCopy(IntPtr destination, IntPtr source, nuint length) {
66+
void* dst = (void*)destination;
67+
void* src = (void*)source;
68+
#if NET7_0_OR_GREATER
69+
NativeMemory.Copy(source: src, destination: dst, length);
70+
#else
71+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
72+
memcpy(dst, src, length);
73+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
74+
memcpy_darwin(dst, src, length);
75+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
76+
CopyMemory(dst, src, length);
5577
} else {
56-
CopyMemory(destination, source, length);
78+
throw new PlatformNotSupportedException();
5779
}
80+
#endif
5881
}
5982

6083
// unix entry points, VM needs to map the filenames.
84+
[SupportedOSPlatform("linux")]
6185
[DllImport("libc")]
6286
private static extern IntPtr dlopen(string filename, int flags);
6387

88+
[SupportedOSPlatform("linux")]
6489
[DllImport("libdl", EntryPoint = "dlopen")]
6590
private static extern IntPtr dlopen_dl(string filename, int flags);
6691

92+
[SupportedOSPlatform("macos")]
93+
[DllImport("libSystem.dylib", EntryPoint = "dlopen")]
94+
private static extern IntPtr dlopen_darwin(string filename, int flags);
95+
96+
[SupportedOSPlatform("linux")]
6797
[DllImport("libc")]
68-
private static extern IntPtr dlsym(IntPtr handle, string symbol);
98+
private static extern unsafe void* dlsym(IntPtr handle, string symbol);
6999

100+
[SupportedOSPlatform("linux")]
70101
[DllImport("libdl", EntryPoint = "dlsym")]
71-
private static extern IntPtr dlsym_dl(IntPtr handle, string symbol);
102+
private static extern unsafe void* dlsym_dl(IntPtr handle, string symbol);
72103

104+
[SupportedOSPlatform("macos")]
105+
[DllImport("libSystem.dylib", EntryPoint = "dlsym")]
106+
private static extern unsafe void* dlsym_darwin(IntPtr handle, string symbol);
107+
108+
[SupportedOSPlatform("linux")]
73109
[DllImport("libc")]
74110
private static extern IntPtr gnu_get_libc_version();
75111

112+
[SupportedOSPlatform("linux")]
76113
private static bool GetGNULibCVersion(out int major, out int minor) {
77114
major = minor = 0;
78115
try {
@@ -91,6 +128,7 @@ private static bool GetGNULibCVersion(out int major, out int minor) {
91128

92129
private const int RTLD_NOW = 2;
93130

131+
[SupportedOSPlatform("linux")]
94132
private static bool UseLibDL() {
95133
if (!_useLibDL.HasValue) {
96134
bool success = GetGNULibCVersion(out int major, out int minor);
@@ -107,45 +145,53 @@ public static IntPtr LoadDLL(string filename, int flags) {
107145

108146
if (flags == 0) flags = RTLD_NOW;
109147

110-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && UseLibDL()) {
111-
return dlopen_dl(filename, flags);
148+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
149+
return UseLibDL() ? dlopen_dl(filename, flags) : dlopen(filename, flags);
150+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
151+
return dlopen_darwin(filename, flags);
152+
} else {
153+
throw new PlatformNotSupportedException();
112154
}
113-
114-
return dlopen(filename, flags);
115155
}
116156

117-
public static IntPtr LoadFunction(IntPtr module, string functionName) {
157+
public static unsafe IntPtr LoadFunction(IntPtr module, string functionName) {
118158
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
119159
return GetProcAddress(module, functionName);
160+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
161+
return (IntPtr)(UseLibDL() ? dlsym_dl(module, functionName) : dlsym(module, functionName));
162+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
163+
return (IntPtr)dlsym_darwin(module, functionName);
164+
} else {
165+
throw new PlatformNotSupportedException();
120166
}
121-
122-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && UseLibDL()) {
123-
return dlsym_dl(module, functionName);
124-
}
125-
126-
return dlsym(module, functionName);
127167
}
128168

129169
public static IntPtr LoadFunction(IntPtr module, IntPtr ordinal) {
130-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
131-
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
132-
return IntPtr.Zero;
170+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
171+
return GetProcAddress(module, ordinal);
133172
}
134-
135-
return GetProcAddress(module, ordinal);
173+
return IntPtr.Zero;
136174
}
137175

138176
/// <summary>
139177
/// Allocates memory that's zero-filled
140178
/// </summary>
141179
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
142-
public static IntPtr Calloc(IntPtr size) {
143-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
144-
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
145-
return calloc((IntPtr)1, size);
180+
public static unsafe IntPtr Calloc(nuint size) {
181+
#if NET7_0_OR_GREATER
182+
return new IntPtr(NativeMemory.AllocZeroed(size));
183+
#else
184+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
185+
return (IntPtr)calloc(1, size);
186+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
187+
return (IntPtr)calloc_darwin(1, size);
188+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
189+
const uint LMEM_ZEROINIT = 0x0040;
190+
return (IntPtr)LocalAlloc(LMEM_ZEROINIT, size);
191+
} else {
192+
throw new PlatformNotSupportedException();
146193
}
147-
148-
return LocalAlloc(LMEM_ZEROINIT, size);
194+
#endif
149195
}
150196

151197
public static IntPtr GetMemMoveAddress() {
@@ -156,40 +202,67 @@ public static IntPtr GetMemSetAddress() {
156202
return Marshal.GetFunctionPointerForDelegate(_setMem);
157203
}
158204

205+
[SupportedOSPlatform("windows")]
159206
[DllImport("kernel32.dll"), ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
160-
private static extern IntPtr LocalAlloc(uint flags, IntPtr size);
207+
private static extern unsafe void* LocalAlloc(uint flags, nuint size);
161208

209+
[SupportedOSPlatform("linux")]
162210
[DllImport("libc")]
163-
private static extern IntPtr calloc(IntPtr num, IntPtr size);
211+
private static extern unsafe void* calloc(nuint num, nuint size);
164212

165-
private const int LMEM_ZEROINIT = 0x0040;
213+
214+
[SupportedOSPlatform("macos")]
215+
[DllImport("libSystem.dylib", EntryPoint = "calloc")]
216+
private static extern unsafe void* calloc_darwin(nuint num, nuint size);
166217

218+
[SupportedOSPlatform("windows")]
167219
[DllImport("kernel32.dll")]
168-
private static extern void RtlMoveMemory(IntPtr Destination, IntPtr src, IntPtr length);
220+
private static extern unsafe void RtlMoveMemory(void* dest, void* src, nuint length);
169221

222+
[SupportedOSPlatform("linux")]
170223
[DllImport("libc")]
171-
private static extern IntPtr memmove(IntPtr dst, IntPtr src, IntPtr length);
224+
private static extern unsafe void* memmove(void* dest, void* src, nuint length);
225+
226+
[SupportedOSPlatform("macos")]
227+
[DllImport("libSystem.dylib", EntryPoint = "memmove")]
228+
private static extern unsafe void* memmove_darwin(void* dest, void* src, nuint count);
172229

173230
/// <summary>
174-
/// Helper function for implementing memset. Could be more efficient if we
175-
/// could P/Invoke or call some otherwise native code to do this.
231+
/// Helper function for implementing memset.
176232
/// </summary>
177-
private static IntPtr MemSet(IntPtr dest, byte value, IntPtr length) {
178-
IntPtr end = dest.Add(length.ToInt32());
179-
for (IntPtr cur = dest; cur != end; cur = new IntPtr(cur.ToInt64() + 1)) {
180-
Marshal.WriteByte(cur, value);
233+
private static unsafe IntPtr MemSet(IntPtr dest, byte value, nuint length) {
234+
#if NET7_0_OR_GREATER
235+
NativeMemory.Fill((void*)dest, length, value);
236+
#else
237+
const int blockSize = 1 << 30; // 1 GiB
238+
byte* cur = (byte*)dest;
239+
while (length > 0) {
240+
int to_fill = length < blockSize ? (int)length : blockSize;
241+
new Span<byte>(cur, to_fill).Fill(value);
242+
length -= (nuint)to_fill;
243+
cur += to_fill;
181244
}
245+
#endif
182246
return dest;
183247
}
184248

185-
private static IntPtr MoveMemory(IntPtr dest, IntPtr src, IntPtr length) {
186-
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
187-
RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
188-
memmove(dest, src, length);
249+
private static unsafe IntPtr MoveMemory(IntPtr destination, IntPtr source, nuint length) {
250+
void* dst = (void*)destination;
251+
void* src = (void*)source;
252+
#if NET7_0_OR_GREATER
253+
NativeMemory.Copy((void*)src, (void*)dst, length);
254+
#else
255+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
256+
memmove(dst, src, length);
257+
} else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
258+
memmove_darwin(dst, src, length);
259+
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
260+
RtlMoveMemory(dst, src, length);
189261
} else {
190-
RtlMoveMemory(dest, src, length);
262+
throw new PlatformNotSupportedException();
191263
}
192-
return dest;
264+
#endif
265+
return destination;
193266
}
194267
}
195268
}

src/core/IronPython.Modules/_ctypes/SimpleType.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,11 +251,11 @@ object INativeType.SetValue(MemoryHolder/*!*/ owner, int offset, object value) {
251251
switch (_type) {
252252
case SimpleTypeKind.Boolean: owner.WriteByte(offset, ModuleOps.GetBoolean(value, this)); break;
253253
case SimpleTypeKind.Char: owner.WriteByte(offset, ModuleOps.GetChar(value, this)); break;
254-
case SimpleTypeKind.SignedByte: owner.WriteByte(offset, ModuleOps.GetSignedByte(value, this)); break;
254+
case SimpleTypeKind.SignedByte: owner.WriteByte(offset, (byte)ModuleOps.GetSignedByte(value, this)); break;
255255
case SimpleTypeKind.UnsignedByte: owner.WriteByte(offset, ModuleOps.GetUnsignedByte(value, this)); break;
256256
case SimpleTypeKind.WChar: owner.WriteInt16(offset, (short)ModuleOps.GetWChar(value, this)); break;
257257
case SimpleTypeKind.SignedShort: owner.WriteInt16(offset, ModuleOps.GetSignedShort(value, this), _swap); break;
258-
case SimpleTypeKind.UnsignedShort: owner.WriteInt16(offset, ModuleOps.GetUnsignedShort(value, this), _swap); break;
258+
case SimpleTypeKind.UnsignedShort: owner.WriteInt16(offset, (short)ModuleOps.GetUnsignedShort(value, this), _swap); break;
259259
case SimpleTypeKind.VariantBool: owner.WriteInt16(offset, (short)ModuleOps.GetVariantBool(value, this), _swap); break;
260260
case SimpleTypeKind.SignedInt: owner.WriteInt32(offset, ModuleOps.GetSignedInt(value, this), _swap); break;
261261
case SimpleTypeKind.UnsignedInt: owner.WriteInt32(offset, ModuleOps.GetUnsignedInt(value, this), _swap); break;

0 commit comments

Comments
 (0)