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 *
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(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType)
{
tp = ob;
}
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
IntPtr op = tp == ob
? Marshal.ReadIntPtr(tp, TypeOffset.magic())
: Marshal.ReadIntPtr(ob, ObjectOffset.magic(tp));
if (op == IntPtr.Zero)
{
return null;
}
var gc = (GCHandle)op;
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 = Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
tp = Marshal.ReadIntPtr(tp, TypeOffset.magic());
var gc = (GCHandle)tp;
return (ManagedType)gc.Target;
}
}
return null;
}
internal static ManagedType GetManagedObjectErr(IntPtr ob)
{
ManagedType result = GetManagedObject(ob);
if (result == null)
{
Exceptions.SetError(Exceptions.TypeError, "invalid argument, expected CLR type");
}
return result;
}
internal static bool IsManagedType(IntPtr ob)
{
if (ob != IntPtr.Zero)
{
IntPtr tp = Runtime.PyObject_TYPE(ob);
if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType)
{
tp = ob;
}
var flags = Util.ReadCLong(tp, TypeOffset.tp_flags);
if ((flags & TypeFlags.Managed) != 0)
{
return true;
}
}
return false;
}
public bool IsTypeObject()
{
return pyHandle == tpHandle;
}
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 = (Interop.ObjObjFunc)Marshal.GetDelegateForFunctionPointer(visit, typeof(Interop.ObjObjFunc));
return visitFunc(ob, arg);
}
///
/// Wrapper for calling tp_clear
///
internal void CallTypeClear()
{
if (tpHandle == IntPtr.Zero || pyHandle == IntPtr.Zero)
{
return;
}
var clearPtr = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_clear);
if (clearPtr == IntPtr.Zero)
{
return;
}
var clearFunc = (Interop.InquiryFunc)Marshal.GetDelegateForFunctionPointer(clearPtr, typeof(Interop.InquiryFunc));
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 = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_traverse);
if (traversePtr == IntPtr.Zero)
{
return;
}
var traverseFunc = (Interop.ObjObjArgFunc)Marshal.GetDelegateForFunctionPointer(traversePtr, typeof(Interop.ObjObjArgFunc));
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);
return Marshal.ReadIntPtr(ob, ObjectOffset.TypeDictOffset(type));
}
protected static void SetObjectDict(IntPtr ob, IntPtr value)
{
IntPtr type = Runtime.PyObject_TYPE(ob);
Marshal.WriteIntPtr(ob, ObjectOffset.TypeDictOffset(type), value);
}
}
}