diff --git a/src/runtime/BorrowedReference.cs b/src/runtime/BorrowedReference.cs
new file mode 100644
index 000000000..7dbc7a811
--- /dev/null
+++ b/src/runtime/BorrowedReference.cs
@@ -0,0 +1,22 @@
+namespace Python.Runtime
+{
+ using System;
+ ///
+ /// Represents a reference to a Python object, that is being lent, and
+ /// can only be safely used until execution returns to the caller.
+ ///
+ readonly ref struct BorrowedReference
+ {
+ readonly IntPtr pointer;
+ public bool IsNull => this.pointer == IntPtr.Zero;
+
+ /// Gets a raw pointer to the Python object
+ public IntPtr DangerousGetAddress()
+ => this.IsNull ? throw new NullReferenceException() : this.pointer;
+
+ BorrowedReference(IntPtr pointer)
+ {
+ this.pointer = pointer;
+ }
+ }
+}
diff --git a/src/runtime/NewReference.cs b/src/runtime/NewReference.cs
new file mode 100644
index 000000000..3b45f821f
--- /dev/null
+++ b/src/runtime/NewReference.cs
@@ -0,0 +1,39 @@
+namespace Python.Runtime
+{
+ using System;
+ ///
+ /// Represents a reference to a Python object, that is tracked by Python's reference counting.
+ ///
+ [NonCopyable]
+ ref struct NewReference
+ {
+ IntPtr pointer;
+ public bool IsNull => this.pointer == IntPtr.Zero;
+
+ /// Gets a raw pointer to the Python object
+ public IntPtr DangerousGetAddress()
+ => this.IsNull ? throw new NullReferenceException() : this.pointer;
+
+ ///
+ /// Returns wrapper around this reference, which now owns
+ /// the pointer. Sets the original reference to null, as it no longer owns it.
+ ///
+ public PyObject MoveToPyObject()
+ {
+ if (this.IsNull) throw new NullReferenceException();
+
+ var result = new PyObject(this.pointer);
+ this.pointer = IntPtr.Zero;
+ return result;
+ }
+ ///
+ /// Removes this reference to a Python object, and sets it to null.
+ ///
+ public void Dispose()
+ {
+ if (!this.IsNull)
+ Runtime.XDecref(this.pointer);
+ this.pointer = IntPtr.Zero;
+ }
+ }
+}
diff --git a/src/runtime/NonCopyableAttribute.cs b/src/runtime/NonCopyableAttribute.cs
new file mode 100644
index 000000000..63d36ab42
--- /dev/null
+++ b/src/runtime/NonCopyableAttribute.cs
@@ -0,0 +1,6 @@
+namespace Python.Runtime
+{
+ using System;
+ [AttributeUsage(AttributeTargets.Struct)]
+ class NonCopyableAttribute : Attribute { }
+}
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index c31d4bf91..fd4f3416a 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -129,6 +129,13 @@
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 0c2f912de..c79afee3e 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -83,6 +83,7 @@
+
@@ -119,6 +120,8 @@
+
+
diff --git a/src/runtime/assemblymanager.cs b/src/runtime/assemblymanager.cs
index 3085bb639..9d0296d47 100644
--- a/src/runtime/assemblymanager.cs
+++ b/src/runtime/assemblymanager.cs
@@ -145,7 +145,7 @@ internal static void UpdatePath()
probed.Clear();
for (var i = 0; i < count; i++)
{
- IntPtr item = Runtime.PyList_GetItem(list, i);
+ BorrowedReference item = Runtime.PyList_GetItem(list, i);
string path = Runtime.GetManagedString(item);
if (path != null)
{
@@ -492,4 +492,4 @@ internal static Type[] GetTypes(Assembly a)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index 8a7fc1930..4e8698da1 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -292,7 +292,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
for (int i = 0; i < pynkwargs; ++i)
{
var keyStr = Runtime.GetManagedString(Runtime.PyList_GetItem(keylist, i));
- kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i);
+ kwargDict[keyStr] = Runtime.PyList_GetItem(valueList, i).DangerousGetAddress();
}
Runtime.XDecref(keylist);
Runtime.XDecref(valueList);
diff --git a/src/runtime/pydict.cs b/src/runtime/pydict.cs
index 7237d1990..b396f4f3d 100644
--- a/src/runtime/pydict.cs
+++ b/src/runtime/pydict.cs
@@ -139,12 +139,20 @@ public PyObject Values()
///
public PyObject Items()
{
- IntPtr items = Runtime.PyDict_Items(obj);
- if (items == IntPtr.Zero)
+ var items = Runtime.PyDict_Items(this.obj);
+ try
{
- throw new PythonException();
+ if (items.IsNull)
+ {
+ throw new PythonException();
+ }
+
+ return items.MoveToPyObject();
+ }
+ finally
+ {
+ items.Dispose();
}
- return new PyObject(items);
}
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 7748bafa9..4cceea42d 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -1509,6 +1509,8 @@ internal static IntPtr PyUnicode_FromString(string s)
return PyUnicode_FromUnicode(s, s.Length);
}
+ internal static string GetManagedString(in BorrowedReference borrowedReference)
+ => GetManagedString(borrowedReference.DangerousGetAddress());
///
/// Function to access the internal PyUnicode/PyString object and
/// convert it to a managed string with the correct encoding.
@@ -1591,7 +1593,7 @@ internal static bool PyDict_Check(IntPtr ob)
internal static extern IntPtr PyDict_Values(IntPtr pointer);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- internal static extern IntPtr PyDict_Items(IntPtr pointer);
+ internal static extern NewReference PyDict_Items(IntPtr pointer);
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyDict_Copy(IntPtr pointer);
@@ -1631,13 +1633,13 @@ internal static IntPtr PyList_New(long size)
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr PyList_AsTuple(IntPtr pointer);
- internal static IntPtr PyList_GetItem(IntPtr pointer, long index)
+ internal static BorrowedReference PyList_GetItem(IntPtr pointer, long index)
{
return PyList_GetItem(pointer, new IntPtr(index));
}
[DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr PyList_GetItem(IntPtr pointer, IntPtr index);
+ private static extern BorrowedReference PyList_GetItem(IntPtr pointer, IntPtr index);
internal static int PyList_SetItem(IntPtr pointer, long index, IntPtr value)
{