Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 770fc01

Browse files
authored
Safe pointers (#1043)
* NewReference type and an example usage * BorrowedReference + example, that exposes dangerous pattern * make BorrowedReference readonly ref struct * BorrowedReference.Pointer is a private readonly field * renamed NewReference.ToPyObject to MoveToPyObject * removed public property Pointer from NewReference and replaced with DangerousGetAddress
1 parent 4271e57 commit 770fc01

9 files changed

+97
-10
lines changed

src/runtime/BorrowedReference.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
namespace Python.Runtime
2+
{
3+
using System;
4+
/// <summary>
5+
/// Represents a reference to a Python object, that is being lent, and
6+
/// can only be safely used until execution returns to the caller.
7+
/// </summary>
8+
readonly ref struct BorrowedReference
9+
{
10+
readonly IntPtr pointer;
11+
public bool IsNull => this.pointer == IntPtr.Zero;
12+
13+
/// <summary>Gets a raw pointer to the Python object</summary>
14+
public IntPtr DangerousGetAddress()
15+
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
16+
17+
BorrowedReference(IntPtr pointer)
18+
{
19+
this.pointer = pointer;
20+
}
21+
}
22+
}

src/runtime/NewReference.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace Python.Runtime
2+
{
3+
using System;
4+
/// <summary>
5+
/// Represents a reference to a Python object, that is tracked by Python's reference counting.
6+
/// </summary>
7+
[NonCopyable]
8+
ref struct NewReference
9+
{
10+
IntPtr pointer;
11+
public bool IsNull => this.pointer == IntPtr.Zero;
12+
13+
/// <summary>Gets a raw pointer to the Python object</summary>
14+
public IntPtr DangerousGetAddress()
15+
=> this.IsNull ? throw new NullReferenceException() : this.pointer;
16+
17+
/// <summary>
18+
/// Returns <see cref="PyObject"/> wrapper around this reference, which now owns
19+
/// the pointer. Sets the original reference to <c>null</c>, as it no longer owns it.
20+
/// </summary>
21+
public PyObject MoveToPyObject()
22+
{
23+
if (this.IsNull) throw new NullReferenceException();
24+
25+
var result = new PyObject(this.pointer);
26+
this.pointer = IntPtr.Zero;
27+
return result;
28+
}
29+
/// <summary>
30+
/// Removes this reference to a Python object, and sets it to <c>null</c>.
31+
/// </summary>
32+
public void Dispose()
33+
{
34+
if (!this.IsNull)
35+
Runtime.XDecref(this.pointer);
36+
this.pointer = IntPtr.Zero;
37+
}
38+
}
39+
}

src/runtime/NonCopyableAttribute.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Python.Runtime
2+
{
3+
using System;
4+
[AttributeUsage(AttributeTargets.Struct)]
5+
class NonCopyableAttribute : Attribute { }
6+
}

src/runtime/Python.Runtime.15.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@
129129
<PackageReference Include="Microsoft.TargetingPack.NETFramework.v4.5" Version="1.0.1" ExcludeAssets="All" PrivateAssets="All" />
130130
</ItemGroup>
131131

132+
<ItemGroup>
133+
<PackageReference Include="NonCopyableAnalyzer" Version="0.5.1">
134+
<PrivateAssets>all</PrivateAssets>
135+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
136+
</PackageReference>
137+
</ItemGroup>
138+
132139
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
133140

134141
<PropertyGroup>

src/runtime/Python.Runtime.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
</Compile>
8484
<Compile Include="arrayobject.cs" />
8585
<Compile Include="assemblymanager.cs" />
86+
<Compile Include="BorrowedReference.cs" />
8687
<Compile Include="classderived.cs" />
8788
<Compile Include="classbase.cs" />
8889
<Compile Include="classmanager.cs" />
@@ -119,6 +120,8 @@
119120
<Compile Include="moduleobject.cs" />
120121
<Compile Include="modulepropertyobject.cs" />
121122
<Compile Include="nativecall.cs" />
123+
<Compile Include="NewReference.cs" />
124+
<Compile Include="NonCopyableAttribute.cs" />
122125
<Compile Include="overload.cs" />
123126
<Compile Include="propertyobject.cs" />
124127
<Compile Include="pyansistring.cs" />

src/runtime/assemblymanager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ internal static void UpdatePath()
145145
probed.Clear();
146146
for (var i = 0; i < count; i++)
147147
{
148-
IntPtr item = Runtime.PyList_GetItem(list, i);
148+
BorrowedReference item = Runtime.PyList_GetItem(list, i);
149149
string path = Runtime.GetManagedString(item);
150150
if (path != null)
151151
{
@@ -492,4 +492,4 @@ internal static Type[] GetTypes(Assembly a)
492492
}
493493
}
494494
}
495-
}
495+
}

src/runtime/methodbinder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
292292
for (int i = 0; i < pynkwargs; ++i)
293293
{
294294
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
295-
kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
295+
kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i).DangerousGetAddress();
296296
}
297297
Runtime.XDecref(keylist);
298298
Runtime.XDecref(valueList);

src/runtime/pydict.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,20 @@ public PyObject Values()
139139
/// </remarks>
140140
public PyObject Items()
141141
{
142-
IntPtr items = Runtime.PyDict_Items(obj);
143-
if (items == IntPtr.Zero)
142+
var items = Runtime.PyDict_Items(this.obj);
143+
try
144144
{
145-
throw new PythonException();
145+
if (items.IsNull)
146+
{
147+
throw new PythonException();
148+
}
149+
150+
return items.MoveToPyObject();
151+
}
152+
finally
153+
{
154+
items.Dispose();
146155
}
147-
return new PyObject(items);
148156
}
149157

150158

src/runtime/runtime.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,8 @@ internal static IntPtr PyUnicode_FromString(string s)
15091509
return PyUnicode_FromUnicode(s, s.Length);
15101510
}
15111511

1512+
internal static string GetManagedString(in BorrowedReference borrowedReference)
1513+
=> GetManagedString(borrowedReference.DangerousGetAddress());
15121514
/// <summary>
15131515
/// Function to access the internal PyUnicode/PyString object and
15141516
/// convert it to a managed string with the correct encoding.
@@ -1591,7 +1593,7 @@ internal static bool PyDict_Check(IntPtr ob)
15911593
internal static extern IntPtr PyDict_Values(IntPtr pointer);
15921594

15931595
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1594-
internal static extern IntPtr PyDict_Items(IntPtr pointer);
1596+
internal static extern NewReference PyDict_Items(IntPtr pointer);
15951597

15961598
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
15971599
internal static extern IntPtr PyDict_Copy(IntPtr pointer);
@@ -1631,13 +1633,13 @@ internal static IntPtr PyList_New(long size)
16311633
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
16321634
internal static extern IntPtr PyList_AsTuple(IntPtr pointer);
16331635

1634-
internal static IntPtr PyList_GetItem(IntPtr pointer, long index)
1636+
internal static BorrowedReference PyList_GetItem(IntPtr pointer, long index)
16351637
{
16361638
return PyList_GetItem(pointer, new IntPtr(index));
16371639
}
16381640

16391641
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
1640-
private static extern IntPtr PyList_GetItem(IntPtr pointer, IntPtr index);
1642+
private static extern BorrowedReference PyList_GetItem(IntPtr pointer, IntPtr index);
16411643

16421644
internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value)
16431645
{

0 commit comments

Comments
 (0)