X Tutup
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 { /// /// Given a Python object, return the associated managed object or null. /// internal static ManagedType? GetManagedObject(BorrowedReference ob) { if (ob != null) { BorrowedReference tp = Runtime.PyObject_TYPE(ob); var flags = PyType.GetFlags(tp); if ((flags & TypeFlags.HasClrInstance) != 0) { var gc = TryGetGCHandle(ob, tp); return (ManagedType?)gc?.Target; } } return null; } internal static bool IsInstanceOfManagedType(BorrowedReference ob) { if (ob != null) { BorrowedReference tp = Runtime.PyObject_TYPE(ob); if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) { tp = ob; } return IsManagedType(tp); } return false; } internal static bool IsManagedType(BorrowedReference type) { var flags = PyType.GetFlags(type); return (flags & TypeFlags.HasClrInstance) != 0; } internal static BorrowedReference GetUnmanagedBaseType(BorrowedReference managedType) { Debug.Assert(managedType != null && IsManagedType(managedType)); do { managedType = PyType.GetBase(managedType); Debug.Assert(managedType != null); } while (IsManagedType(managedType)); return managedType; } internal unsafe static int PyVisit(BorrowedReference ob, IntPtr visit, IntPtr arg) { if (ob == null) { return 0; } var visitFunc = (delegate* unmanaged[Cdecl])(visit); return visitFunc(ob, arg); } internal static unsafe void DecrefTypeAndFree(StolenReference ob) { if (ob == null) throw new ArgumentNullException(nameof(ob)); var borrowed = new BorrowedReference(ob.DangerousGetAddress()); var type = Runtime.PyObject_TYPE(borrowed); var freePtr = Util.ReadIntPtr(type, TypeOffset.tp_free); Debug.Assert(freePtr != IntPtr.Zero); var free = (delegate* unmanaged[Cdecl])freePtr; free(ob); Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress())); } internal static int CallClear(BorrowedReference ob) => CallTypeClear(ob, Runtime.PyObject_TYPE(ob)); /// /// Wrapper for calling tp_clear /// internal static unsafe int CallTypeClear(BorrowedReference ob, BorrowedReference tp) { if (ob == null) throw new ArgumentNullException(nameof(ob)); if (tp == null) throw new ArgumentNullException(nameof(tp)); var clearPtr = Util.ReadIntPtr(tp, TypeOffset.tp_clear); if (clearPtr == IntPtr.Zero) { return 0; } var clearFunc = (delegate* unmanaged[Cdecl])clearPtr; return clearFunc(ob); } internal Dictionary? Save(BorrowedReference ob) { return OnSave(ob); } internal void Load(BorrowedReference ob, Dictionary? context) { OnLoad(ob, context); } protected virtual Dictionary? OnSave(BorrowedReference ob) => null; protected virtual void OnLoad(BorrowedReference ob, Dictionary? context) { } /// /// Initializes given object, or returns false and sets Python error on failure /// public virtual bool Init(BorrowedReference obj, BorrowedReference args, BorrowedReference kw) { // this just calls obj.__init__(*args, **kw) using var init = Runtime.PyObject_GetAttr(obj, PyIdentifier.__init__); Runtime.PyErr_Clear(); if (!init.IsNull()) { using var result = Runtime.PyObject_Call(init.Borrow(), args, kw); if (result.IsNull()) { return false; } } return true; } protected static void ClearObjectDict(BorrowedReference ob) { BorrowedReference type = Runtime.PyObject_TYPE(ob); int instanceDictOffset = Util.ReadInt32(type, TypeOffset.tp_dictoffset); // Debug.Assert(instanceDictOffset > 0); if (instanceDictOffset > 0) Runtime.Py_CLEAR(ob, instanceDictOffset); } protected static BorrowedReference GetObjectDict(BorrowedReference ob) { BorrowedReference type = Runtime.PyObject_TYPE(ob); int instanceDictOffset = Util.ReadInt32(type, TypeOffset.tp_dictoffset); Debug.Assert(instanceDictOffset > 0); return Util.ReadRef(ob, instanceDictOffset); } protected static void SetObjectDict(BorrowedReference ob, StolenReference value) { if (value.Pointer == IntPtr.Zero) throw new ArgumentNullException(nameof(value)); SetObjectDictNullable(ob, value.AnalyzerWorkaround()); } protected static void SetObjectDictNullable(BorrowedReference ob, StolenReference value) { BorrowedReference type = Runtime.PyObject_TYPE(ob); int instanceDictOffset = Util.ReadInt32(type, TypeOffset.tp_dictoffset); Debug.Assert(instanceDictOffset > 0); Runtime.ReplaceReference(ob, instanceDictOffset, value.AnalyzerWorkaround()); } internal static void GetGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type, out IntPtr handle) { Debug.Assert(reflectedClrObject != null); Debug.Assert(IsManagedType(type) || IsManagedType(reflectedClrObject)); Debug.Assert(Runtime.PyObject_TypeCheck(reflectedClrObject, type)); int gcHandleOffset = Util.ReadInt32(type, Offsets.tp_clr_inst_offset); Debug.Assert(gcHandleOffset > 0); handle = Util.ReadIntPtr(reflectedClrObject, 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(type != null); Debug.Assert(reflectedClrObject != null); Debug.Assert(IsManagedType(type) || IsManagedType(reflectedClrObject)); Debug.Assert(Runtime.PyObject_TypeCheck(reflectedClrObject, type)); int offset = Util.ReadInt32(type, Offsets.tp_clr_inst_offset); Debug.Assert(offset > 0); Util.WriteIntPtr(reflectedClrObject, offset, (IntPtr)newHandle); } internal static void SetGCHandle(BorrowedReference reflectedClrObject, GCHandle newHandle) => SetGCHandle(reflectedClrObject, Runtime.PyObject_TYPE(reflectedClrObject), newHandle); internal static bool TryFreeGCHandle(BorrowedReference reflectedClrObject) => TryFreeGCHandle(reflectedClrObject, Runtime.PyObject_TYPE(reflectedClrObject)); internal static bool TryFreeGCHandle(BorrowedReference reflectedClrObject, BorrowedReference type) { Debug.Assert(type != null); Debug.Assert(reflectedClrObject != null); Debug.Assert(IsManagedType(type) || IsManagedType(reflectedClrObject)); Debug.Assert(Runtime.PyObject_TypeCheck(reflectedClrObject, type)); int offset = Util.ReadInt32(type, Offsets.tp_clr_inst_offset); Debug.Assert(offset > 0); IntPtr raw = Util.ReadIntPtr(reflectedClrObject, offset); if (raw == IntPtr.Zero) return false; var handle = (GCHandle)raw; handle.Free(); Util.WriteIntPtr(reflectedClrObject, offset, IntPtr.Zero); return true; } internal static class Offsets { static Offsets() { int pyTypeSize = Util.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; } } } }
X Tutup