[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/saroad2/pythonnet/master/src/runtime/managedtype.cs [Back]  [Original]

#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;  }
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page