Skip to content

Call tp_clear of base unmanaged type #1541

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/embed_tests/Inheritance.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;

using NUnit.Framework;

Expand Down Expand Up @@ -100,6 +98,18 @@ public void SetAdHocAttributes_WhenExtraBasePresent()
int actual = scope.Eval<int>($"{nameof(instance)}.{nameof(Inherited.XProp)}");
Assert.AreEqual(expected: Inherited.X, actual);
}

// https://github.com/pythonnet/pythonnet/issues/1476
[Test]
public void BaseClearIsCalled()
{
using var scope = Py.CreateScope();
scope.Set("exn", new Exception("42"));
var msg = scope.Eval("exn.args[0]");
Assert.AreEqual(2, msg.Refcount);
scope.Set("exn", null);
Assert.AreEqual(1, msg.Refcount);
}
}

class ExtraBaseTypeProvider : IPythonBaseTypeProvider
Expand Down
20 changes: 20 additions & 0 deletions src/runtime/classbase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Python.Runtime
{
Expand Down Expand Up @@ -365,11 +366,30 @@ public static int tp_clear(IntPtr ob)
if (!isTypeObject)
{
ClearObjectDict(ob);

int baseClearResult = BaseUnmanagedClear(ob);
if (baseClearResult != 0)
{
return baseClearResult;
}
}
if (self is not null) self.tpHandle = IntPtr.Zero;
return 0;
}

static unsafe int BaseUnmanagedClear(IntPtr ob)
{
var type = Runtime.PyObject_TYPE(new BorrowedReference(ob));
var unmanagedBase = GetUnmanagedBaseType(type);
var clearPtr = Marshal.ReadIntPtr(unmanagedBase.DangerousGetAddress(), TypeOffset.tp_clear);
if (clearPtr == IntPtr.Zero)
{
return 0;
}
var clear = (delegate* unmanaged[Cdecl]<IntPtr, int>)clearPtr;
return clear(ob);
}

protected override void OnSave(InterDomainContext context)
{
base.OnSave(context);
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/managedtype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@ internal static bool IsManagedType(BorrowedReference type)
return (flags & TypeFlags.HasClrInstance) != 0;
}

internal static BorrowedReference GetUnmanagedBaseType(BorrowedReference managedType)
{
Debug.Assert(managedType != null && IsManagedType(managedType));
do
{
managedType = PyType.GetBase(managedType);
} while (IsManagedType(managedType));
return managedType;
}

public bool IsClrMetaTypeInstance()
{
Debug.Assert(Runtime.PyCLRMetaType != IntPtr.Zero);
Expand Down
18 changes: 14 additions & 4 deletions src/runtime/pytype.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

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

return Runtime.PyType_Check(value.obj);
}
/// <summary>Checks if specified object is a Python type.</summary>
internal static bool IsType(BorrowedReference value)
{
return Runtime.PyType_Check(value.DangerousGetAddress());
}

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

internal BorrowedReference BaseReference
{
get
{
return new(Marshal.ReadIntPtr(Handle, TypeOffset.tp_base));
}
get => GetBase(Reference);
set
{
var old = BaseReference.DangerousGetAddressOrNull();
Expand All @@ -100,6 +103,13 @@ internal IntPtr GetSlot(TypeSlotID slot)
return Exceptions.ErrorCheckIfNull(result);
}

internal static BorrowedReference GetBase(BorrowedReference type)
{
Debug.Assert(IsType(type));
IntPtr basePtr = Marshal.ReadIntPtr(type.DangerousGetAddress(), TypeOffset.tp_base);
return new BorrowedReference(basePtr);
}

private static IntPtr EnsureIsType(in StolenReference reference)
{
IntPtr address = reference.DangerousGetAddressOrNull();
Expand Down