Skip to content

Commit 2498d47

Browse files
committed
always explicitly specify the way strings are marshaled
1 parent 51e5184 commit 2498d47

13 files changed

+336
-246
lines changed

src/embed_tests/References.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void Dispose()
2323
public void MoveToPyObject_SetsNull()
2424
{
2525
var dict = new PyDict();
26-
NewReference reference = Runtime.PyDict_Items(dict.Handle);
26+
NewReference reference = Runtime.PyDict_Items(dict.Reference);
2727
try
2828
{
2929
Assert.IsFalse(reference.IsNull());
@@ -41,7 +41,7 @@ public void MoveToPyObject_SetsNull()
4141
public void CanBorrowFromNewReference()
4242
{
4343
var dict = new PyDict();
44-
NewReference reference = Runtime.PyDict_Items(dict.Handle);
44+
NewReference reference = Runtime.PyDict_Items(dict.Reference);
4545
try
4646
{
4747
PythonException.ThrowIfIsNotZero(Runtime.PyList_Reverse(reference));

src/embed_tests/TestDomainReload.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ static void RunAssemblyAndUnload(string domainName)
332332
// assembly (and Python .NET) to reside
333333
var theProxy = CreateInstanceInstanceAndUnwrap<Proxy>(domain);
334334

335-
theProxy.Call("InitPython", ShutdownMode.Soft);
335+
theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Soft, PyRuntime.PythonDLL);
336336
// From now on use the Proxy to call into the new assembly
337337
theProxy.RunPython();
338338

@@ -400,7 +400,7 @@ static void RunDomainReloadSteps<T1, T2>() where T1 : CrossCaller where T2 : Cro
400400
try
401401
{
402402
var theProxy = CreateInstanceInstanceAndUnwrap<Proxy>(domain);
403-
theProxy.Call("InitPython", ShutdownMode.Reload);
403+
theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL);
404404

405405
var caller = CreateInstanceInstanceAndUnwrap<T1>(domain);
406406
arg = caller.Execute(arg);
@@ -418,7 +418,7 @@ static void RunDomainReloadSteps<T1, T2>() where T1 : CrossCaller where T2 : Cro
418418
try
419419
{
420420
var theProxy = CreateInstanceInstanceAndUnwrap<Proxy>(domain);
421-
theProxy.Call("InitPython", ShutdownMode.Reload);
421+
theProxy.Call(nameof(PythonRunner.InitPython), ShutdownMode.Reload, PyRuntime.PythonDLL);
422422

423423
var caller = CreateInstanceInstanceAndUnwrap<T2>(domain);
424424
caller.Execute(arg);
@@ -478,8 +478,9 @@ public static void RunPython()
478478

479479
private static IntPtr _state;
480480

481-
public static void InitPython(ShutdownMode mode)
481+
public static void InitPython(ShutdownMode mode, string dllName)
482482
{
483+
PyRuntime.PythonDLL = dllName;
483484
PythonEngine.Initialize(mode: mode);
484485
_state = PythonEngine.BeginAllowThreads();
485486
}

src/embed_tests/TestRuntime.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,13 +96,15 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
9696
// TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check.
9797
var threading = Runtime.Runtime.PyImport_ImportModule("threading");
9898
Exceptions.ErrorCheck(threading);
99-
var threadingDict = Runtime.Runtime.PyModule_GetDict(threading);
99+
var threadingDict = Runtime.Runtime.PyModule_GetDict(new BorrowedReference(threading));
100100
Exceptions.ErrorCheck(threadingDict);
101101
var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock");
102-
if (lockType == IntPtr.Zero)
102+
if (lockType.IsNull)
103103
throw new KeyNotFoundException("class 'Lock' was not found in 'threading'");
104104

105-
var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType, Runtime.Runtime.PyTuple_New(0));
105+
var args = Runtime.Runtime.PyTuple_New(0);
106+
var lockInstance = Runtime.Runtime.PyObject_CallObject(lockType.DangerousGetAddress(), args);
107+
Runtime.Runtime.XDecref(args);
106108
Exceptions.ErrorCheck(lockInstance);
107109

108110
Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance));

src/embed_tests/TestTypeManager.cs

Lines changed: 0 additions & 65 deletions
This file was deleted.

src/runtime/CustomMarshaler.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ public int GetNativeDataSize()
4141
/// </summary>
4242
internal class UcsMarshaler : MarshalerBase
4343
{
44+
internal static readonly int _UCS = Runtime.PyUnicode_GetMax() <= 0xFFFF ? 2 : 4;
45+
internal static readonly Encoding PyEncoding = _UCS == 2 ? Encoding.Unicode : Encoding.UTF32;
4446
private static readonly MarshalerBase Instance = new UcsMarshaler();
45-
private static readonly Encoding PyEncoding = Runtime.PyEncoding;
4647

4748
public override IntPtr MarshalManagedToNative(object managedObj)
4849
{
@@ -91,13 +92,13 @@ public static int GetUnicodeByteLength(IntPtr p)
9192
var len = 0;
9293
while (true)
9394
{
94-
int c = Runtime._UCS == 2
95+
int c = _UCS == 2
9596
? Marshal.ReadInt16(p, len * 2)
9697
: Marshal.ReadInt32(p, len * 4);
9798

9899
if (c == 0)
99100
{
100-
return len * Runtime._UCS;
101+
return len * _UCS;
101102
}
102103
checked
103104
{
@@ -147,7 +148,7 @@ public static string PtrToPy3UnicodePy2String(IntPtr p)
147148
internal class StrArrayMarshaler : MarshalerBase
148149
{
149150
private static readonly MarshalerBase Instance = new StrArrayMarshaler();
150-
private static readonly Encoding PyEncoding = Runtime.PyEncoding;
151+
private static readonly Encoding PyEncoding = UcsMarshaler.PyEncoding;
151152

152153
public override IntPtr MarshalManagedToNative(object managedObj)
153154
{
@@ -159,7 +160,7 @@ public override IntPtr MarshalManagedToNative(object managedObj)
159160
}
160161

161162
int totalStrLength = argv.Sum(arg => arg.Length + 1);
162-
int memSize = argv.Length * IntPtr.Size + totalStrLength * Runtime._UCS;
163+
int memSize = argv.Length * IntPtr.Size + totalStrLength * UcsMarshaler._UCS;
163164

164165
IntPtr mem = Marshal.AllocHGlobal(memSize);
165166
try

src/runtime/Python.Runtime.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,9 @@
3131
<ItemGroup>
3232
<PackageReference Include="System.Security.Permissions" Version="4.4.0" />
3333
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
34+
<PackageReference Include="Lost.NonCopyableAnalyzer" Version="0.7.0-m04">
35+
<PrivateAssets>all</PrivateAssets>
36+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
37+
</PackageReference>
3438
</ItemGroup>
3539
</Project>

src/runtime/exceptions.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,14 +194,16 @@ internal static void SetArgsAndCause(IntPtr ob)
194194
/// Shortcut for (pointer == NULL) -&gt; throw PythonException
195195
/// </summary>
196196
/// <param name="pointer">Pointer to a Python object</param>
197-
internal static void ErrorCheck(IntPtr pointer)
197+
internal static void ErrorCheck(BorrowedReference pointer)
198198
{
199-
if (pointer == IntPtr.Zero)
199+
if (pointer.IsNull)
200200
{
201201
throw new PythonException();
202202
}
203203
}
204204

205+
internal static void ErrorCheck(IntPtr pointer) => ErrorCheck(new BorrowedReference(pointer));
206+
205207
/// <summary>
206208
/// Shortcut for (pointer == NULL or ErrorOccurred()) -&gt; throw PythonException
207209
/// </summary>

src/runtime/native/PyCompilerFlags.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
3+
namespace Python.Runtime.Native
4+
{
5+
[Flags]
6+
enum PyCompilerFlags
7+
{
8+
SOURCE_IS_UTF8 = 0x0100,
9+
DONT_IMPLY_DEDENT = 0x0200,
10+
ONLY_AST = 0x0400,
11+
IGNORE_COOKIE = 0x0800,
12+
}
13+
}

src/runtime/native/StrPtr.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#nullable enable
2+
using System;
3+
using System.Runtime.InteropServices;
4+
using System.Text;
5+
6+
namespace Python.Runtime.Native
7+
{
8+
[StructLayout(LayoutKind.Sequential)]
9+
struct StrPtr : IDisposable
10+
{
11+
public IntPtr RawPointer { get; set; }
12+
unsafe byte* Bytes => (byte*)this.RawPointer;
13+
14+
public unsafe StrPtr(string value, Encoding encoding)
15+
{
16+
if (value is null) throw new ArgumentNullException(nameof(value));
17+
if (encoding is null) throw new ArgumentNullException(nameof(encoding));
18+
19+
var bytes = encoding.GetBytes(value);
20+
this.RawPointer = Marshal.AllocHGlobal(checked(bytes.Length + 1));
21+
try
22+
{
23+
Marshal.Copy(bytes, 0, this.RawPointer, bytes.Length);
24+
this.Bytes[bytes.Length] = 0;
25+
}
26+
catch
27+
{
28+
this.Dispose();
29+
throw;
30+
}
31+
}
32+
33+
public unsafe string? ToString(Encoding encoding)
34+
{
35+
if (encoding is null) throw new ArgumentNullException(nameof(encoding));
36+
if (this.RawPointer == IntPtr.Zero) return null;
37+
38+
return encoding.GetString((byte*)this.RawPointer, byteCount: checked((int)this.ByteCount));
39+
}
40+
41+
public unsafe nuint ByteCount
42+
{
43+
get
44+
{
45+
if (this.RawPointer == IntPtr.Zero) throw new NullReferenceException();
46+
47+
nuint zeroIndex = 0;
48+
while (this.Bytes[zeroIndex] != 0)
49+
{
50+
zeroIndex++;
51+
}
52+
return zeroIndex;
53+
}
54+
}
55+
56+
public void Dispose()
57+
{
58+
if (this.RawPointer == IntPtr.Zero)
59+
return;
60+
61+
Marshal.FreeHGlobal(this.RawPointer);
62+
this.RawPointer = IntPtr.Zero;
63+
}
64+
65+
internal static Encoding GetEncodingByPythonName(string pyEncodingName)
66+
{
67+
// https://stackoverflow.com/a/7798749/231238
68+
if (pyEncodingName == "mbcs") return Encoding.Default;
69+
70+
return Encoding.GetEncoding(pyEncodingName);
71+
}
72+
}
73+
}

src/runtime/pyint.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public PyInt(sbyte value) : this((int)value)
149149

150150
private static IntPtr FromString(string value)
151151
{
152-
IntPtr val = Runtime.PyInt_FromString(value, IntPtr.Zero, 0);
152+
IntPtr val = Runtime.PyLong_FromString(value, IntPtr.Zero, 0);
153153
PythonException.ThrowIfIsNull(val);
154154
return val;
155155
}

src/runtime/pyobject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public bool HasAttr(string name)
257257
{
258258
if (name == null) throw new ArgumentNullException(nameof(name));
259259

260-
return Runtime.PyObject_HasAttrString(obj, name) != 0;
260+
return Runtime.PyObject_HasAttrString(Reference, name) != 0;
261261
}
262262

263263

0 commit comments

Comments
 (0)