8000 call tp_clear (if any) of base unmanaged type when a reflected CLR ob… · pythonnet/pythonnet@2ef6b50 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2ef6b50

Browse files
committed
call tp_clear (if any) of base unmanaged type when a reflected CLR object instance is cleared
fixes #1476 : C# exceptions inherit from Python BaseException class, which has tp_clear. Prior to this change it was not called, leaving dangling references to BaseException.args and BaseException.__cause__.call tp_clear of base unmanaged type when a reflected CLR object instance is cleared fixes #1476 : C# exceptions inherit from Python BaseException class, which has tp_clear. Prior to this change it was not called, leaving dangling references to BaseException.args and BaseException.__cause__.
1 parent dc3c818 commit 2ef6b50

File tree

4 files changed

+56
-6
lines changed

4 files changed

+56
-6
lines changed

src/embed_tests/Inheritance.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Diagnostics;
4-
using System.Runtime.InteropServices;
53

64
using NUnit.Framework;
75

@@ -100,6 +98,18 @@ public void SetAdHocAttributes_WhenExtraBasePresent()
10098
int actual = scope.Eval<int>($"{nameof(instance)}.{nameof(Inherited.XProp)}");
10199
Assert.AreEqual(expected: Inherited.X, actual);
102100
}
101+
102+
// https://github.com/pythonnet/pythonnet/issues/1476
103+
[Test]
104+
public void BaseClearIsCalled()
105+
{
106+
using var scope = Py.CreateScope();
107+
scope.Set("exn", new Exception("42"));
108+
var msg = scope.Eval("exn.args[0]");
109+
Assert.AreEqual(2, msg.Refcount);
110+
scope.Set("exn", null);
111+
Assert.AreEqual(1, msg.Refcount);
112+
}
103113
}
104114

105115
class ExtraBaseTypeProvider : IPythonBaseTypeProvider

src/runtime/classbase.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Runtime.InteropServices;
45

56
namespace Python.Runtime
67
{
@@ -365,11 +366,30 @@ public static int tp_clear(IntPtr ob)
365366
if (!isTypeObject)
366367
{
367368
ClearObjectDict(ob);
369+
370+
int baseClearResult = BaseUnmanagedClear(ob);
371+
if (baseClearResult != 0)
372+
{
373+
return baseClearResult;
374+
}
368375
}
369376
if (self is not null) self.tpHandle = IntPtr.Zero;
370377
return 0;
371378
}
372379

380+
static unsafe int BaseUnmanagedClear(IntPtr ob)
381+
{
382+
var type = Runtime.PyObject_TYPE(new BorrowedReference(ob));
383+
var unmanagedBase = GetUnmanagedBaseType(type);
384+
var clearPtr = Marshal.ReadIntPtr(unmanagedBase.DangerousGetAddress(), TypeOffset.tp_clear);
385+
if (clearPtr == IntPtr.Zero)
386+
{
387+
return 0;
388+
}
389+
var clear = (delegate* unmanaged[Cdecl]<IntPtr, int>)clearPtr;
390+
return clear(ob);
391+
}
392+
373393
protected override void OnSave(InterDomainContext context)
374394
{
375395
base.OnSave(context);

src/runtime/managedtype.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,16 @@ internal static bool IsManagedType(BorrowedReference type)
149149
return (flags & TypeFlags.HasClrInstance) != 0;
150150
}
151151

152+
internal static BorrowedReference GetUnmanagedBaseType(BorrowedReference managedType)
153+
{
154+
Debug.Assert(managedType != null && IsManagedType(managedType));
155+
do
156+
{
157+
managedType = PyType.GetBase(managedType);
158+
} while (IsManagedType(managedType));
159+
return managedType;
160+
}
161+
152162
public bool IsClrMetaTypeInstance()
153163
{
154164
Debug.Assert(Runtime.PyCLRMetaType != IntPtr.Zero);

src/runtime/pytype.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System;
3+
using System.Diagnostics;
34
using System.Runtime.InteropServices;
45

56
using Python.Runtime.Native;
@@ -60,6 +61,11 @@ public static bool IsType(PyObject value)
6061

6162
return Runtime.PyType_Check(value.obj);
6263
}
64+
/// <summary>Checks if specified object is a Python type.</summary>
65+
internal static bool IsType(BorrowedReference value)
66+
{
67+
return Runtime.PyType_Check(value.DangerousGetAddress());
68+
}
6369

6470
/// <summary>
6571
/// Gets <see cref="PyType"/>, which represents the specified CLR type.
@@ -78,10 +84,7 @@ internal static PyType Get(Type clrType)
7884

7985
internal BorrowedReference BaseReference
8086
{
81-
get
82-
{
83-
return new(Marshal.ReadIntPtr(Handle, TypeOffset.tp_base));
84-
}
87+
get => GetBase(Reference);
8588
set
8689
{
8790
var old = BaseReference.DangerousGetAddressOrNull();
@@ -100,6 +103,13 @@ internal IntPtr GetSlot(TypeSlotID slot)
100103
return Exceptions.ErrorCheckIfNull(result);
101104
}
102105

106+
internal static BorrowedReference GetBase(BorrowedReference type)
107+
{
108+
Debug.Assert(IsType(type));
109+
IntPtr basePtr = Marshal.ReadInt 5B33 Ptr(type.DangerousGetAddress(), TypeOffset.tp_base);
110+
return new BorrowedReference(basePtr);
111+
}
112+
103113
private static IntPtr EnsureIsType(in StolenReference reference)
104114
{
105115
IntPtr address = reference.DangerousGetAddressOrNull();

0 commit comments

Comments
 (0)
0