Skip to content

Commit bd54dfd

Browse files
committed
Run ctypes tests on macOS
1 parent 877b087 commit bd54dfd

8 files changed

Lines changed: 184 additions & 63 deletions

File tree

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_test.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,19 @@
88

99
using System;
1010
using System.IO;
11+
using System.Runtime.InteropServices;
12+
13+
using Mono.Unix.Native;
1114

1215
using IronPython.Runtime;
16+
using System.Runtime.Versioning;
1317

1418
[assembly: PythonModule("_ctypes_test", typeof(IronPython.Modules.CTypesTest))]
1519
namespace IronPython.Modules {
1620
public static class CTypesTest {
1721

22+
public static string __file__ = Path.Combine(FindRoot(), "tests", "suite", GetPydName());
23+
1824
private static string FindRoot() {
1925
// we start at the current directory and look up until we find the "src" directory
2026
var current = System.Reflection.Assembly.GetExecutingAssembly().Location;
@@ -30,7 +36,33 @@ private static string FindRoot() {
3036
return string.Empty;
3137
}
3238

33-
public static string __file__ = Path.Combine(FindRoot(), "tests", "suite", string.Format("_ctypes_test_{0}{1}.pyd", Environment.OSVersion.Platform == PlatformID.Win32NT ? "win" : Environment.OSVersion.Platform == PlatformID.MacOSX ? "macOS" : "linux", Environment.Is64BitProcess ? 64 : 32));
39+
private static string GetPydName() {
40+
string OS = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win"
41+
: RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "darwin"
42+
: "linux";
43+
44+
string arch;
45+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
46+
arch = IsArchitecutreArm64() ? "_arm64" : "_x86_64";
47+
} else {
48+
arch = Environment.Is64BitProcess ? "64" : "32";
49+
}
50+
51+
return string.Format("_ctypes_test_{0}{1}.pyd", OS, arch);
52+
}
53+
54+
[SupportedOSPlatform("linux")]
55+
[SupportedOSPlatform("macos")]
56+
private static bool IsArchitecutreArm64() {
57+
#if NETCOREAPP
58+
return RuntimeInformation.ProcessArchitecture == Architecture.Arm64;
59+
#else
60+
if (Syscall.uname(out Utsname info) == 0) {
61+
return info.machine is "arm64" or "aarch64";
62+
}
63+
return false;
64+
#endif
65+
}
3466
}
3567
}
3668

tests/IronPython.Tests/Cases/CPythonCasesManifest.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ WorkingDirectory=$(TEST_FILE_DIR)
44
Redirect=false
55
Timeout=120000 # 2 minute timeout
66

7-
[CPython.ctypes]
8-
RunCondition=NOT $(IS_OSX) # TODO: debug
9-
107
[CPython.ctypes.test_as_parameter]
118
Ignore=true
129

0 commit comments

Comments
 (0)