Skip to content

Commit d55a914

Browse files
committed
fixed crash in SetError when PythonException does not have type or value
also: introduced StealingReference type for C API parameters
1 parent 8e70b5b commit d55a914

File tree

5 files changed

+79
-11
lines changed

5 files changed

+79
-11
lines changed

src/runtime/NewReference.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ ref struct NewReference
1515
public static implicit operator BorrowedReference(in NewReference reference)
1616
=> new BorrowedReference(reference.pointer);
1717

18+
/// <summary>
19+
/// Creates <see cref="NewReference"/> from a nullable <see cref="BorrowedReference"/>.
20+
/// Increments the reference count accordingly.
21+
/// </summary>
22+
[Pure]
23+
public static NewReference FromNullable(BorrowedReference reference)
24+
{
25+
if (reference.IsNull) return default;
26+
27+
IntPtr address = reference.DangerousGetAddress();
28+
Runtime.XIncref(address);
29+
return DangerousFromPointer(address);
30+
}
31+
1832
/// <summary>
1933
/// Returns <see cref="PyObject"/> wrapper around this reference, which now owns
2034
/// the pointer. Sets the original reference to <c>null</c>, as it no longer owns it.
@@ -44,6 +58,18 @@ public void Dispose()
4458
public static NewReference DangerousFromPointer(IntPtr pointer)
4559
=> new NewReference {pointer = pointer};
4660

61+
/// <summary>
62+
/// Creates <see cref="NewReference"/> from a raw pointer
63+
/// and writes <c>null</c> to the original location.
64+
/// </summary>
65+
[Pure]
66+
public static NewReference DangerousMoveFromPointer(ref IntPtr pointer)
67+
{
68+
var pointerValue = pointer;
69+
pointer = IntPtr.Zero;
70+
return DangerousFromPointer(pointerValue);
71+
}
72+
4773
public IntPtr DangerousMoveToPointer()
4874
{
4975
if (this.IsNull()) throw new NullReferenceException();
@@ -61,11 +87,11 @@ internal static bool IsNull(in NewReference reference)
6187
=> reference.pointer == IntPtr.Zero;
6288

6389
// TODO: return some static type
64-
internal IntPtr Steal()
90+
internal StealingReference Steal()
6591
{
6692
var result = this.pointer;
6793
this.pointer = IntPtr.Zero;
68-
return result;
94+
return StealingReference.DangerousFromPointer(result);
6995
}
7096
}
7197

src/runtime/StealingReference.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace Python.Runtime
2+
{
3+
using System;
4+
using System.Diagnostics.Contracts;
5+
6+
/// <summary>
7+
/// Represents a reference to a Python object, that is being stolen by a C API.
8+
/// </summary>
9+
[NonCopyable]
10+
ref struct StealingReference
11+
{
12+
IntPtr pointer;
13+
14+
/// <summary>
15+
/// Creates <see cref="StealingReference"/> from a raw pointer
16+
/// </summary>
17+
[Pure]
18+
public static StealingReference DangerousFromPointer(IntPtr pointer)
19+
=> new StealingReference { pointer = pointer};
20+
21+
public IntPtr DangerousGetAddressOrNull() => this.pointer;
22+
}
23+
}

src/runtime/exceptions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -283,10 +283,10 @@ public static void SetError(Exception e)
283283
var pe = e as PythonException;
284284
if (pe != null)
285285
{
286-
Runtime.XIncref(pe.PyType);
287-
Runtime.XIncref(pe.PyValue);
288-
Runtime.XIncref(pe.PyTB);
289-
Runtime.PyErr_Restore(pe.PyType, pe.PyValue, pe.PyTB);
286+
Runtime.PyErr_Restore(
287+
NewReference.FromNullable(pe.PythonType).Steal(),
288+
NewReference.FromNullable(pe.Value).Steal(),
289+
NewReference.FromNullable(pe.Traceback).Steal());
290290
return;
291291
}
292292

src/runtime/pythonexception.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,10 @@ static string TracebackHandleToString(BorrowedReference tracebackHandle) {
236236
public void Restore()
237237
{
238238
IntPtr gs = PythonEngine.AcquireLock();
239-
Runtime.PyErr_Restore(_pyType, _pyValue, _pyTB);
240-
_pyType = IntPtr.Zero;
241-
_pyValue = IntPtr.Zero;
242-
_pyTB = IntPtr.Zero;
239+
Runtime.PyErr_Restore(
240+
NewReference.DangerousMoveFromPointer(ref _pyType).Steal(),
241+
NewReference.DangerousMoveFromPointer(ref _pyValue).Steal(),
242+
NewReference.DangerousMoveFromPointer(ref _pyTB).Steal());
243243
PythonEngine.ReleaseLock(gs);
244244
}
245245

@@ -254,6 +254,11 @@ public IntPtr PyType
254254
get { return _pyType; }
255255
}
256256

257+
/// <summary>
258+
/// Type of the exception (nullable).
259+
/// </summary>
260+
internal BorrowedReference PythonType => new BorrowedReference(_pyType);
261+
257262
/// <summary>
258263
/// PyValue Property
259264
/// </summary>
@@ -265,6 +270,11 @@ public IntPtr PyValue
265270
get { return _pyValue; }
266271
}
267272

273+
/// <summary>
274+
/// Exception object value (nullable).
275+
/// </summary>
276+
internal BorrowedReference Value => new BorrowedReference(_pyValue);
277+
268278
/// <summary>
269279
/// PyTB Property
270280
/// </summary>
@@ -276,6 +286,11 @@ public IntPtr PyTB
276286
get { return _pyTB; }
277287
}
278288

289+
/// <summary>
290+
/// Traceback (nullable).
291+
/// </summary>
292+
internal BorrowedReference Traceback => new BorrowedReference(_pyTB);
293+
279294
/// <summary>
280295
/// Message Property
281296
/// </summary>

src/runtime/runtime.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1900,7 +1900,11 @@ internal static IntPtr PyMem_Realloc(IntPtr ptr, long size)
19001900
internal static void PyErr_Fetch(out NewReference type, out NewReference value, out NewReference traceback)
19011901
=> Delegates.PyErr_Fetch(out type, out value, out traceback);
19021902

1903-
internal static void PyErr_Restore(IntPtr ob, IntPtr val, IntPtr tb) => Delegates.PyErr_Restore(ob, val, tb);
1903+
internal static void PyErr_Restore(StealingReference ob, StealingReference val, StealingReference tb)
1904+
=> Delegates.PyErr_Restore(
1905+
ob.DangerousGetAddressOrNull(),
1906+
val.DangerousGetAddressOrNull(),
1907+
tb.DangerousGetAddressOrNull());
19041908

19051909
internal static void PyErr_Clear() => Delegates.PyErr_Clear();
19061910

0 commit comments

Comments
 (0)