#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Linq;
namespace Python.Runtime
{
///
/// Common base class for all objects that are implemented in managed
/// code. It defines the common fields that associate CLR and Python
/// objects and common utilities to convert between those identities.
///
[Serializable]
internal abstract class ManagedType
{
internal enum TrackTypes
{
Untrack,
Extension,
Wrapper,
}
[NonSerialized]
internal GCHandle gcHandle; // Native handle
internal IntPtr pyHandle; // PyObject *
internal IntPtr tpHandle; // PyType *
internal BorrowedReference ObjectReference => new(pyHandle);
internal BorrowedReference TypeReference => new(tpHandle);
private static readonly Dictionary _managedObjs = new Dictionary();
internal void IncrRefCount()
{
Runtime.XIncref(pyHandle);
}
internal void DecrRefCount()
{
Runtime.XDecref(pyHandle);
}
internal long RefCount
{
get
{
var gs = Runtime.PyGILState_Ensure();
try
{
return Runtime.Refcount(pyHandle);
}
finally
{
Runtime.PyGILState_Release(gs);
}
}
}
internal GCHandle AllocGCHandle(TrackTypes track = TrackTypes.Untrack)
{
gcHandle = GCHandle.Alloc(this);
if (track != TrackTypes.Untrack)
{
_managedObjs.Add(this, track);
}
return gcHandle;
}
internal void FreeGCHandle()
{
_managedObjs.Remove(this);
if (gcHandle.IsAllocated)
{
gcHandle.Free();
gcHandle = default;
}
}
///
/// Given a Python object, return the associated managed object or null.
///
internal static ManagedType? GetManagedObject(BorrowedReference ob)
=> GetManagedObject(ob.DangerousGetAddress());
///
/// Given a Python object, return the associated managed object or null.
///
internal static ManagedType? GetManagedObject(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType)
{
tp = ob;
}
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.HasClrInstance) != 0)
{
var gc = TryGetGCHandle(new BorrowedReference(ob));
return (ManagedType)gc?.Target;
}
}
return null;
}
///
/// Given a Python object, return the associated managed object type or null.
///
internal static ManagedType? GetManagedObjectType(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
var flags = (TypeFlags)Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.HasClrInstance) != 0)
{
var gc = GetGCHandle(new BorrowedReference(tp), Runtime.CLRMetaType);
return (ManagedType)gc.Target;
}
}
return null;
}
internal static bool IsInstanceOfManagedType(BorrowedReference ob)
=> IsInstanceOfManagedType(ob.DangerousGetAddressOrNull());
internal static bool IsInstanceOfManagedType(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType)
{
tp = ob;
}
return IsManagedType(new BorrowedReference(tp));
}
return false;
}
internal static bool IsManagedType(BorrowedReference type)
{
var flags = (TypeFlags)Util.ReadCLong(type.DangerousGetAddress(), TypeOffset.tp_flags);
return (flags & TypeFlags.HasClrInstance) != 0;
}
public bool IsClrMetaTypeInstance()
{
Debug.Assert(Runtime.PyCLRMetaType != IntPtr.Zero);
Debug.Assert(pyHandle != IntPtr.Zero);
return Runtime.PyObject_TYPE(pyHandle) == Runtime.PyCLRMetaType;
}
internal static IDictionary GetManagedObjects()
{
return _managedObjs;
}
internal static void ClearTrackedObjects()
{
_managedObjs.Clear();
}
internal static int PyVisit(IntPtr ob, IntPtr visit, IntPtr arg)
{
if (ob == IntPtr.Zero)
{
return 0;
}
var visitFunc = NativeCall.GetDelegate(visit);
return visitFunc(ob, arg);
}
///
/// Wrapper for calling tp_clear
///
internal void CallTypeClear()
{
if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
{
return;
}
var clearPtr = Runtime.PyType_GetSlot(TypeReference, TypeSlotID.tp_clear);
if (clearPtr == IntPtr.Zero)
{
return;
}
var clearFunc = NativeCall.GetDelegate(clearPtr);
clearFunc(pyHandle);
}
///
/// Wrapper for calling tp_traverse
///
internal void CallTypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg)
{
if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
{
return;
}
var traversePtr = Runtime.PyType_GetSlot(TypeReference, TypeSlotID.tp_traverse);
if (traversePtr == IntPtr.Zero)
{
return;
}
var traverseFunc = NativeCall.GetDelegate(traversePtr);
var visiPtr = Marshal.GetFunctionPointerForDelegate(visitproc);
traverseFunc(pyHandle, visiPtr, arg);
}
protected void TypeClear()
{
ClearObjectDict(pyHandle);
}
internal void Save(InterDomainContext context)
{
OnSave(context);
}
internal void Load(InterDomainContext context)
{
OnLoad(context);
}
protected virtual void OnSave(InterDomainContext context) { }
protected virtual void OnLoad(InterDomainContext context) { }
protected static void ClearObjectDict(IntPtr ob)
{
IntPtr dict = GetObjectDict(ob);
if (dict == IntPtr.Zero)
{
return;
}
SetObjectDict(ob, IntPtr.Zero);
Runtime.XDecref(dict);
}
protected static IntPtr GetObjectDict(IntPtr ob)
{
IntPtr type = Runtime.PyObject_TYPE(ob);
int instanceDictOffset = Marshal.ReadInt32(type, TypeOffset.tp_dictoffset);
Debug.Assert(instanceDictOffset > 0);
return Marshal.ReadIntPtr(ob, instanceDictOffset);
}
protected static void SetObjectDict(IntPtr ob, IntPtr value)
{
IntPtr type = Runtime.PyObject_TYPE(ob);
int instanceDictOffset = Marshal.ReadInt32(type, TypeOffset.tp_dictoffset);
Debug.Assert(instanceDictOffset > 0);
Marshal.WriteIntPtr(ob, instanceDictOffset, value);
}
internal static void GetGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type, out IntPtr handle)
{
Debug.Assert(reflectedClrObject != null);
Debug.Assert(IsManagedType(type) || type == Runtime.CLRMetaType);
Debug.Assert(Runtime.PyObject_TypeCheck(reflectedClrObject, type));
int gcHandleOffset = Marshal.ReadInt32(type.DangerousGetAddress(), Offsets.tp_clr_inst_offset);
Debug.Assert(gcHandleOffset > 0);
handle = Marshal.ReadIntPtr(reflectedClrObject.DangerousGetAddress(), gcHandleOffset);
}
internal static GCHandle? TryGetGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type)
{
GetGCHandle(reflectedClrObject, type, out IntPtr handle);
return handle == IntPtr.Zero ? null : (GCHandle)handle;
}
internal static GCHandle? TryGetGCHandle(BorrowedReference reflectedClrObject)
{
BorrowedReference reflectedType = Runtime.PyObject_TYPE(reflectedClrObject);
return TryGetGCHandle(reflectedClrObject, reflectedType);
}
internal static GCHandle GetGCHandle(BorrowedReference reflectedClrObject)
=> TryGetGCHandle(reflectedClrObject) ?? throw new InvalidOperationException();
internal static GCHandle GetGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type)
=> TryGetGCHandle(reflectedClrObject, type) ?? throw new InvalidOperationException();
internal static void InitGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type, GCHandle handle)
{
Debug.Assert(TryGetGCHandle(reflectedClrObject) == null);
SetGCHandle(reflectedClrObject, type: type, handle);
}
internal static void InitGCHandle(BorrowedReference reflectedClrObject, GCHandle handle)
=> InitGCHandle(reflectedClrObject, Runtime.PyObject_TYPE(reflectedClrObject), handle);
internal static void SetGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type, GCHandle newHandle)
{
Debug.Assert(Runtime.PyObject_TypeCheck(reflectedClrObject, type));
int offset = Marshal.ReadInt32(type.DangerousGetAddress(), Offsets.tp_clr_inst_offset);
Debug.Assert(offset > 0);
Marshal.WriteIntPtr(reflectedClrObject.DangerousGetAddress(), offset, (IntPtr)newHandle);
}
internal static void SetGCHandle(BorrowedReference reflectedClrObject, GCHandle newHandle)
=> SetGCHandle(reflectedClrObject, Runtime.PyObject_TYPE(reflectedClrObject), newHandle);
internal static class Offsets
{
static Offsets()
{
int pyTypeSize = Marshal.ReadInt32(Runtime.PyTypeType, TypeOffset.tp_basicsize);
if (pyTypeSize < 0) throw new InvalidOperationException();
tp_clr_inst_offset = pyTypeSize;
tp_clr_inst = tp_clr_inst_offset + IntPtr.Size;
}
public static int tp_clr_inst_offset { get; }
public static int tp_clr_inst { get; }
}
}
}