X Tutup
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Diagnostics; using Python.Runtime.Native; using Python.Runtime.StateSerialization; namespace Python.Runtime { /// /// The TypeManager class is responsible for building binary-compatible /// Python type objects that are implemented in managed code. /// internal class TypeManager { internal static IntPtr subtype_traverse; internal static IntPtr subtype_clear; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. /// initialized in rather than in constructor internal static IPythonBaseTypeProvider pythonBaseTypeProvider; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static; private static readonly Dictionary cache = new(); static readonly Dictionary _slotsHolders = new(PythonReferenceComparer.Instance); // Slots which must be set private static readonly string[] _requiredSlots = new string[] { "tp_traverse", "tp_clear", }; internal static void Initialize() { Debug.Assert(cache.Count == 0, "Cache should be empty", "Some errors may occurred on last shutdown"); using (var plainType = SlotHelper.CreateObjectType()) { subtype_traverse = Util.ReadIntPtr(plainType.Borrow(), TypeOffset.tp_traverse); subtype_clear = Util.ReadIntPtr(plainType.Borrow(), TypeOffset.tp_clear); } pythonBaseTypeProvider = PythonEngine.InteropConfiguration.pythonBaseTypeProviders; } internal static void RemoveTypes() { if (Runtime.HostedInPython) { foreach (var holder in _slotsHolders) { // If refcount > 1, it needs to reset the managed slot, // otherwise it can dealloc without any trick. if (holder.Key.Refcount > 1) { holder.Value.ResetSlots(); } } } foreach (var type in cache.Values) { type.Dispose(); } cache.Clear(); _slotsHolders.Clear(); } internal static TypeManagerState SaveRuntimeData() => new() { Cache = cache, }; internal static void RestoreRuntimeData(TypeManagerState storage) { Debug.Assert(cache == null || cache.Count == 0); var typeCache = storage.Cache; foreach (var entry in typeCache) { var type = entry.Key.Value; cache![type] = entry.Value; SlotsHolder holder = CreateSlotsHolder(entry.Value); InitializeSlots(entry.Value, type, holder); Runtime.PyType_Modified(entry.Value); } } internal static PyType GetType(Type type) { // Note that these types are cached with a refcount of 1, so they // effectively exist until the CPython runtime is finalized. if (!cache.TryGetValue(type, out var pyType)) { pyType = CreateType(type); cache[type] = pyType; } return pyType; } /// /// Given a managed Type derived from ExtensionType, get the handle to /// a Python type object that delegates its implementation to the Type /// object. These Python type instances are used to implement internal /// descriptor and utility types like ModuleObject, PropertyObject, etc. /// internal static BorrowedReference GetTypeReference(Type type) => GetType(type).Reference; /// /// The following CreateType implementations do the necessary work to /// create Python types to represent managed extension types, reflected /// types, subclasses of reflected types and the managed metatype. The /// dance is slightly different for each kind of type due to different /// behavior needed and the desire to have the existing Python runtime /// do as much of the allocation and initialization work as possible. /// internal static unsafe PyType CreateType(Type impl) { // TODO: use PyType(TypeSpec) constructor PyType type = AllocateTypeObject(impl.Name, metatype: Runtime.PyCLRMetaType); BorrowedReference base_ = impl == typeof(CLRModule) ? Runtime.PyModuleType : Runtime.PyBaseObjectType; type.BaseReference = base_; int newFieldOffset = InheritOrAllocateStandardFields(type, base_); int tp_clr_inst_offset = newFieldOffset; newFieldOffset += IntPtr.Size; int ob_size = newFieldOffset; // Set tp_basicsize to the size of our managed instance objects. Util.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); Util.WriteInt32(type, ManagedType.Offsets.tp_clr_inst_offset, tp_clr_inst_offset); Util.WriteIntPtr(type, TypeOffset.tp_new, (IntPtr)Runtime.Delegates.PyType_GenericNew); SlotsHolder slotsHolder = CreateSlotsHolder(type); InitializeSlots(type, impl, slotsHolder); type.Flags = TypeFlags.Default | TypeFlags.HasClrInstance | TypeFlags.HeapType | TypeFlags.HaveGC; if (Runtime.PyType_Ready(type) != 0) { throw PythonException.ThrowLastAsClrException(); } using (var dict = Runtime.PyObject_GenericGetDict(type.Reference)) using (var mod = Runtime.PyString_FromString("CLR")) { Runtime.PyDict_SetItem(dict.Borrow(), PyIdentifier.__module__, mod.Borrow()); } // The type has been modified after PyType_Ready has been called // Refresh the type Runtime.PyType_Modified(type.Reference); return type; } internal static void InitializeClassCore(Type clrType, PyType pyType, ClassBase impl) { if (pyType.BaseReference != null) { return; } // Hide the gchandle of the implementation in a magic type slot. GCHandle gc = GCHandle.Alloc(impl); ManagedType.InitGCHandle(pyType, Runtime.CLRMetaType, gc); using var baseTuple = GetBaseTypeTuple(clrType); InitializeBases(pyType, baseTuple); // core fields must be initialized in partially constructed classes, // otherwise it would be impossible to manipulate GCHandle and check type size InitializeCoreFields(pyType); } internal static string GetPythonTypeName(Type clrType) { var result = new System.Text.StringBuilder(); GetPythonTypeName(clrType, target: result); return result.ToString(); } static void GetPythonTypeName(Type clrType, System.Text.StringBuilder target) { if (clrType.IsGenericType) { string fullName = clrType.GetGenericTypeDefinition().FullName; int argCountIndex = fullName.LastIndexOf('`'); if (argCountIndex >= 0) { string nonGenericFullName = fullName.Substring(0, argCountIndex); string nonGenericName = CleanupFullName(nonGenericFullName); target.Append(nonGenericName); var arguments = clrType.GetGenericArguments(); target.Append('['); for (int argIndex = 0; argIndex < arguments.Length; argIndex++) { if (argIndex != 0) { target.Append(','); } GetPythonTypeName(arguments[argIndex], target); } target.Append(']'); int nestedStart = fullName.IndexOf('+'); while (nestedStart >= 0) { target.Append('.'); int nextNested = fullName.IndexOf('+', nestedStart + 1); if (nextNested < 0) { target.Append(fullName.Substring(nestedStart + 1)); } else { target.Append(fullName.Substring(nestedStart + 1, length: nextNested - nestedStart - 1)); } nestedStart = nextNested; } return; } } string name = CleanupFullName(clrType.FullName); target.Append(name); } static string CleanupFullName(string fullTypeName) { // Cleanup the type name to get rid of funny nested type names. string name = "clr." + fullTypeName; int i = name.LastIndexOf('+'); if (i > -1) { name = name.Substring(i + 1); } i = name.LastIndexOf('.'); if (i > -1) { name = name.Substring(i + 1); } return name; } static BorrowedReference InitializeBases(PyType pyType, PyTuple baseTuple) { Debug.Assert(baseTuple.Length() > 0); var primaryBase = baseTuple[0].Reference; pyType.BaseReference = primaryBase; if (baseTuple.Length() > 1) { Util.WriteIntPtr(pyType, TypeOffset.tp_bases, baseTuple.NewReferenceOrNull().DangerousMoveToPointer()); } return primaryBase; } static void InitializeCoreFields(PyType type) { int newFieldOffset = InheritOrAllocateStandardFields(type); if (ManagedType.IsManagedType(type.BaseReference)) { int baseClrInstOffset = Util.ReadInt32(type.BaseReference, ManagedType.Offsets.tp_clr_inst_offset); Util.WriteInt32(type, ManagedType.Offsets.tp_clr_inst_offset, baseClrInstOffset); } else { Util.WriteInt32(type, ManagedType.Offsets.tp_clr_inst_offset, newFieldOffset); newFieldOffset += IntPtr.Size; } int ob_size = newFieldOffset; Util.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); Util.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); } internal static void InitializeClass(PyType type, ClassBase impl, Type clrType) { // we want to do this after the slot stuff above in case the class itself implements a slot method SlotsHolder slotsHolder = CreateSlotsHolder(type); InitializeSlots(type, impl.GetType(), slotsHolder); impl.InitializeSlots(type, slotsHolder); OperatorMethod.FixupSlots(type, clrType); // Leverage followup initialization from the Python runtime. Note // that the type of the new type must PyType_Type at the time we // call this, else PyType_Ready will skip some slot initialization. if (!type.IsReady && Runtime.PyType_Ready(type) != 0) { throw PythonException.ThrowLastAsClrException(); } var dict = Util.ReadRef(type, TypeOffset.tp_dict); string mn = clrType.Namespace ?? ""; using (var mod = Runtime.PyString_FromString(mn)) Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod.Borrow()); Runtime.PyType_Modified(type.Reference); //DebugUtil.DumpType(type); } static int InheritOrAllocateStandardFields(BorrowedReference type) { var @base = Util.ReadRef(type, TypeOffset.tp_base); return InheritOrAllocateStandardFields(type, @base); } static int InheritOrAllocateStandardFields(BorrowedReference typeRef, BorrowedReference @base) { IntPtr baseAddress = @base.DangerousGetAddress(); IntPtr type = typeRef.DangerousGetAddress(); int baseSize = Util.ReadInt32(@base, TypeOffset.tp_basicsize); int newFieldOffset = baseSize; void InheritOrAllocate(int typeField) { int value = Marshal.ReadInt32(baseAddress, typeField); if (value == 0) { Marshal.WriteIntPtr(type, typeField, new IntPtr(newFieldOffset)); newFieldOffset += IntPtr.Size; } else { Marshal.WriteIntPtr(type, typeField, new IntPtr(value)); } } InheritOrAllocate(TypeOffset.tp_dictoffset); InheritOrAllocate(TypeOffset.tp_weaklistoffset); return newFieldOffset; } static PyTuple GetBaseTypeTuple(Type clrType) { var bases = pythonBaseTypeProvider .GetBaseTypes(clrType, new PyType[0]) ?.ToArray(); if (bases is null || bases.Length == 0) { throw new InvalidOperationException("At least one base type must be specified"); } var nonBases = bases.Where(@base => !@base.Flags.HasFlag(TypeFlags.BaseType)).ToList(); if (nonBases.Count > 0) { throw new InvalidProgramException("The specified Python type(s) can not be inherited from: " + string.Join(", ", nonBases)); } return new PyTuple(bases); } internal static NewReference CreateSubType(BorrowedReference py_name, ClassBase py_base_type, IList interfaces, BorrowedReference dictRef) { // Utility to create a subtype of a managed type with the ability for the // a python subtype able to override the managed implementation string? name = Runtime.GetManagedString(py_name); if (name is null) { Exceptions.SetError(Exceptions.ValueError, "Class name must not be None"); return default; } // the derived class can have class attributes __assembly__ and __module__ which // control the name of the assembly and module the new type is created in. object? assembly = null; object? namespaceStr = null; using (var assemblyKey = new PyString("__assembly__")) { var assemblyPtr = Runtime.PyDict_GetItemWithError(dictRef, assemblyKey.Reference); if (assemblyPtr.IsNull) { if (Exceptions.ErrorOccurred()) return default; } else if (!Converter.ToManagedValue(assemblyPtr, typeof(string), out assembly, true)) { return Exceptions.RaiseTypeError("Couldn't convert __assembly__ value to string"); } using var namespaceKey = new PyString("__namespace__"); var pyNamespace = Runtime.PyDict_GetItemWithError(dictRef, namespaceKey.Reference); if (pyNamespace.IsNull) { if (Exceptions.ErrorOccurred()) return default; } else if (!Converter.ToManagedValue(pyNamespace, typeof(string), out namespaceStr, true)) { return Exceptions.RaiseTypeError("Couldn't convert __namespace__ value to string"); } } // create the new managed type subclassing the base managed type return ReflectedClrType.CreateSubclass(py_base_type, interfaces, name, ns: (string?)namespaceStr, assembly: (string?)assembly, dict: dictRef); } internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, PyMethodFlags flags, IntPtr doc) { Marshal.WriteIntPtr(mdef, name); Marshal.WriteIntPtr(mdef, 1 * IntPtr.Size, func); Marshal.WriteInt32(mdef, 2 * IntPtr.Size, (int)flags); Marshal.WriteIntPtr(mdef, 3 * IntPtr.Size, doc); return mdef + 4 * IntPtr.Size; } internal static IntPtr WriteMethodDef(IntPtr mdef, string name, IntPtr func, PyMethodFlags flags = PyMethodFlags.VarArgs, string? doc = null) { IntPtr namePtr = Marshal.StringToHGlobalAnsi(name); IntPtr docPtr = doc != null ? Marshal.StringToHGlobalAnsi(doc) : IntPtr.Zero; return WriteMethodDef(mdef, namePtr, func, flags, docPtr); } internal static IntPtr WriteMethodDefSentinel(IntPtr mdef) { return WriteMethodDef(mdef, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero); } internal static void FreeMethodDef(IntPtr mdef) { unsafe { var def = (PyMethodDef*)mdef; if (def->ml_name != IntPtr.Zero) { Marshal.FreeHGlobal(def->ml_name); def->ml_name = IntPtr.Zero; } if (def->ml_doc != IntPtr.Zero) { Marshal.FreeHGlobal(def->ml_doc); def->ml_doc = IntPtr.Zero; } } } internal static PyType CreateMetatypeWithGCHandleOffset() { var py_type = new PyType(Runtime.PyTypeType, prevalidated: true); int size = Util.ReadInt32(Runtime.PyTypeType, TypeOffset.tp_basicsize) + IntPtr.Size // tp_clr_inst_offset ; var slots = new[] { new TypeSpec.Slot(TypeSlotID.tp_traverse, subtype_traverse), new TypeSpec.Slot(TypeSlotID.tp_clear, subtype_clear) }; var result = new PyType( new TypeSpec( "clr._internal.GCOffsetBase", basicSize: size, slots: slots, TypeFlags.Default | TypeFlags.HeapType | TypeFlags.HaveGC ), bases: new PyTuple(new[] { py_type }) ); return result; } internal static PyType CreateMetaType(Type impl, out SlotsHolder slotsHolder) { // The managed metatype is functionally little different than the // standard Python metatype (PyType_Type). It overrides certain of // the standard type slots, and has to subclass PyType_Type for // certain functions in the C runtime to work correctly with it. PyType gcOffsetBase = CreateMetatypeWithGCHandleOffset(); PyType type = AllocateTypeObject("CLRMetatype", metatype: gcOffsetBase); Util.WriteRef(type, TypeOffset.tp_base, new NewReference(gcOffsetBase).Steal()); nint size = Util.ReadInt32(gcOffsetBase, TypeOffset.tp_basicsize) + IntPtr.Size // tp_clr_inst ; Util.WriteIntPtr(type, TypeOffset.tp_basicsize, size); Util.WriteInt32(type, ManagedType.Offsets.tp_clr_inst_offset, ManagedType.Offsets.tp_clr_inst); const TypeFlags flags = TypeFlags.Default | TypeFlags.HeapType | TypeFlags.HaveGC | TypeFlags.HasClrInstance; Util.WriteCLong(type, TypeOffset.tp_flags, (int)flags); // Slots will inherit from TypeType, it's not neccesary for setting them. // Inheried slots: // tp_basicsize, tp_itemsize, // tp_dictoffset, tp_weaklistoffset, // tp_traverse, tp_clear, tp_is_gc, etc. slotsHolder = SetupMetaSlots(impl, type); if (Runtime.PyType_Ready(type) != 0) { throw PythonException.ThrowLastAsClrException(); } BorrowedReference dict = Util.ReadRef(type, TypeOffset.tp_dict); using (var mod = Runtime.PyString_FromString("clr._internal")) Runtime.PyDict_SetItemString(dict, "__module__", mod.Borrow()); // The type has been modified after PyType_Ready has been called // Refresh the type Runtime.PyType_Modified(type); //DebugUtil.DumpType(type); return type; } internal static SlotsHolder SetupMetaSlots(Type impl, PyType type) { // Override type slots with those of the managed implementation. var slotsHolder = new SlotsHolder(type); InitializeSlots(type, impl, slotsHolder); // We need space for 3 PyMethodDef structs. int mdefSize = (MetaType.CustomMethods.Length + 1) * Marshal.SizeOf(typeof(PyMethodDef)); IntPtr mdef = Runtime.PyMem_Malloc(mdefSize); IntPtr mdefStart = mdef; foreach (var methodName in MetaType.CustomMethods) { mdef = AddCustomMetaMethod(methodName, type, mdef, slotsHolder); } mdef = WriteMethodDefSentinel(mdef); Debug.Assert((long)(mdefStart + mdefSize) <= (long)mdef); Util.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart); // XXX: Hard code with mode check. if (Runtime.HostedInPython) { slotsHolder.Set(TypeOffset.tp_methods, (t, offset) => { var p = Util.ReadIntPtr(t, offset); Runtime.PyMem_Free(p); Util.WriteIntPtr(t, offset, IntPtr.Zero); }); } return slotsHolder; } private static IntPtr AddCustomMetaMethod(string name, PyType type, IntPtr mdef, SlotsHolder slotsHolder) { MethodInfo mi = typeof(MetaType).GetMethod(name); ThunkInfo thunkInfo = Interop.GetThunk(mi); slotsHolder.KeeapAlive(thunkInfo); // XXX: Hard code with mode check. if (Runtime.HostedInPython) { IntPtr mdefAddr = mdef; slotsHolder.AddDealloctor(() => { var tp_dict = Util.ReadRef(type, TypeOffset.tp_dict); if (Runtime.PyDict_DelItemString(tp_dict, name) != 0) { Runtime.PyErr_Print(); Debug.Fail($"Cannot remove {name} from metatype"); } FreeMethodDef(mdefAddr); }); } mdef = WriteMethodDef(mdef, name, thunkInfo.Address); return mdef; } /// /// Utility method to allocate a type object & do basic initialization. /// internal static PyType AllocateTypeObject(string name, PyType metatype) { var newType = Runtime.PyType_GenericAlloc(metatype, 0); var type = new PyType(newType.StealOrThrow()); // Clr type would not use __slots__, // and the PyMemberDef after PyHeapTypeObject will have other uses(e.g. type handle), // thus set the ob_size to 0 for avoiding slots iterations. Util.WriteIntPtr(type, TypeOffset.ob_size, IntPtr.Zero); // Cheat a little: we'll set tp_name to the internal char * of // the Python version of the type name - otherwise we'd have to // allocate the tp_name and would have no way to free it. using var temp = Runtime.PyString_FromString(name); IntPtr raw = Runtime.PyUnicode_AsUTF8(temp.BorrowOrThrow()); Util.WriteIntPtr(type, TypeOffset.tp_name, raw); Util.WriteRef(type, TypeOffset.name, new NewReference(temp).Steal()); Util.WriteRef(type, TypeOffset.qualname, temp.Steal()); // Ensure that tp_traverse and tp_clear are always set, since their // existence is enforced in newer Python versions in PyType_Ready Util.WriteIntPtr(type, TypeOffset.tp_traverse, subtype_traverse); Util.WriteIntPtr(type, TypeOffset.tp_clear, subtype_clear); // This is a new mechanism in Python 3.14. We should eventually use it to implement // a nicer type check, but for now we just need to ensure that it is set to NULL. if (TypeOffset.ht_token != -1) Util.WriteIntPtr(type, TypeOffset.ht_token, IntPtr.Zero); InheritSubstructs(type.Reference.DangerousGetAddress()); return type; } /// /// Inherit substructs, that are not inherited by default: /// https://docs.python.org/3/c-api/typeobj.html#c.PyTypeObject.tp_as_number /// static void InheritSubstructs(IntPtr type) { IntPtr substructAddress = type + TypeOffset.nb_add; Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, substructAddress); substructAddress = type + TypeOffset.sq_length; Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, substructAddress); substructAddress = type + TypeOffset.mp_length; Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, substructAddress); substructAddress = type + TypeOffset.bf_getbuffer; Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, substructAddress); } /// /// Given a newly allocated Python type object and a managed Type that /// provides the implementation for the type, connect the type slots of /// the Python object to the managed methods of the implementing Type. /// internal static void InitializeSlots(PyType type, Type impl, SlotsHolder? slotsHolder = null) { // We work from the most-derived class up; make sure to get // the most-derived slot and not to override it with a base // class's slot. var seen = new HashSet(); while (impl != null) { MethodInfo[] methods = impl.GetMethods(tbFlags); foreach (MethodInfo method in methods) { string name = method.Name; if (!name.StartsWith("tp_") && !TypeOffset.IsSupportedSlotName(name)) { Debug.Assert(!name.Contains("_") || name.StartsWith("_") || method.IsSpecialName); continue; } if (seen.Contains(name)) { continue; } InitializeSlot(type, Interop.GetThunk(method), name, slotsHolder); seen.Add(name); } var initSlot = impl.GetMethod("InitializeSlots", BindingFlags.Static | BindingFlags.Public); initSlot?.Invoke(null, parameters: new object?[] { type, seen, slotsHolder }); impl = impl.BaseType; } SetRequiredSlots(type, seen); } private static void SetRequiredSlots(PyType type, HashSet seen) { foreach (string slot in _requiredSlots) { if (seen.Contains(slot)) { continue; } var offset = TypeOffset.GetSlotOffset(slot); Util.WriteIntPtr(type, offset, SlotsHolder.GetDefaultSlot(offset)); } } static void InitializeSlot(BorrowedReference type, ThunkInfo thunk, string name, SlotsHolder? slotsHolder) { if (!Enum.TryParse(name, out _)) { throw new NotSupportedException("Bad slot name " + name); } int offset = TypeOffset.GetSlotOffset(name); InitializeSlot(type, offset, thunk, slotsHolder); } static void InitializeSlot(BorrowedReference type, int slotOffset, MethodInfo method, SlotsHolder slotsHolder) { var thunk = Interop.GetThunk(method); InitializeSlot(type, slotOffset, thunk, slotsHolder); } internal static void InitializeSlot(BorrowedReference type, int slotOffset, Delegate impl, SlotsHolder slotsHolder) { var thunk = Interop.GetThunk(impl); InitializeSlot(type, slotOffset, thunk, slotsHolder); } internal static void InitializeSlotIfEmpty(BorrowedReference type, int slotOffset, Delegate impl, SlotsHolder slotsHolder) { if (slotsHolder.IsHolding(slotOffset)) return; InitializeSlot(type, slotOffset, impl, slotsHolder); } static void InitializeSlot(BorrowedReference type, int slotOffset, ThunkInfo thunk, SlotsHolder? slotsHolder) { Util.WriteIntPtr(type, slotOffset, thunk.Address); if (slotsHolder != null) { slotsHolder.Set(slotOffset, thunk); } } /// /// Utility method to copy slots from a given type to another type. /// internal static void CopySlot(BorrowedReference from, BorrowedReference to, int offset) { IntPtr fp = Util.ReadIntPtr(from, offset); Util.WriteIntPtr(to, offset, fp); } internal static SlotsHolder CreateSlotsHolder(PyType type) { type = new PyType(type); var holder = new SlotsHolder(type); _slotsHolders.Add(type, holder); return holder; } } class SlotsHolder { public delegate void Resetor(PyType type, int offset); private readonly Dictionary _slots = new(); private readonly List _keepalive = new(); private readonly Dictionary _customResetors = new(); private readonly List _deallocators = new(); private bool _alreadyReset = false; private readonly PyType Type; public string?[] Holds => _slots.Keys.Select(TypeOffset.GetSlotName).ToArray(); /// /// Create slots holder for holding the delegate of slots and be able to reset them. /// /// Steals a reference to target type public SlotsHolder(PyType type) { this.Type = type; } public bool IsHolding(int offset) => _slots.ContainsKey(offset); public ICollection Slots => _slots.Keys; public void Set(int offset, ThunkInfo thunk) { _slots[offset] = thunk; } public void Set(int offset, Resetor resetor) { _customResetors[offset] = resetor; } public void AddDealloctor(Action deallocate) { _deallocators.Add(deallocate); } public void KeeapAlive(ThunkInfo thunk) { _keepalive.Add(thunk); } public static void ResetSlots(BorrowedReference type, IEnumerable slots) { foreach (int offset in slots) { IntPtr ptr = GetDefaultSlot(offset); #if DEBUG //DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>"); #endif Util.WriteIntPtr(type, offset, ptr); } } public void ResetSlots() { if (_alreadyReset) { return; } _alreadyReset = true; #if DEBUG IntPtr tp_name = Util.ReadIntPtr(Type, TypeOffset.tp_name); string typeName = Marshal.PtrToStringAnsi(tp_name); #endif ResetSlots(Type, _slots.Keys); foreach (var action in _deallocators) { action(); } foreach (var pair in _customResetors) { int offset = pair.Key; var resetor = pair.Value; resetor?.Invoke(Type, offset); } _customResetors.Clear(); _slots.Clear(); _keepalive.Clear(); _deallocators.Clear(); // Custom reset if (Type != Runtime.CLRMetaType) { var metatype = Runtime.PyObject_TYPE(Type); ManagedType.TryFreeGCHandle(Type, metatype); } Runtime.PyType_Modified(Type); } public static IntPtr GetDefaultSlot(int offset) { if (offset == TypeOffset.tp_clear) { return TypeManager.subtype_clear; } else if (offset == TypeOffset.tp_traverse) { return TypeManager.subtype_traverse; } else if (offset == TypeOffset.tp_dealloc) { // tp_free of PyTypeType is point to PyObejct_GC_Del. return Util.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free); } else if (offset == TypeOffset.tp_free) { // PyObject_GC_Del return Util.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free); } else if (offset == TypeOffset.tp_call) { return IntPtr.Zero; } else if (offset == TypeOffset.tp_new) { // PyType_GenericNew return Util.ReadIntPtr(Runtime.PySuper_Type, TypeOffset.tp_new); } else if (offset == TypeOffset.tp_getattro) { // PyObject_GenericGetAttr return Util.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_getattro); } else if (offset == TypeOffset.tp_setattro) { // PyObject_GenericSetAttr return Util.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_setattro); } return Util.ReadIntPtr(Runtime.PyTypeType, offset); } } static class SlotHelper { public static NewReference CreateObjectType() { using var globals = Runtime.PyDict_New(); if (Runtime.PyDict_SetItemString(globals.Borrow(), "__builtins__", Runtime.PyEval_GetBuiltins()) != 0) { globals.Dispose(); throw PythonException.ThrowLastAsClrException(); } const string code = "class A(object): pass"; using var resRef = Runtime.PyRun_String(code, RunFlagType.File, globals.Borrow(), globals.Borrow()); if (resRef.IsNull()) { globals.Dispose(); throw PythonException.ThrowLastAsClrException(); } resRef.Dispose(); BorrowedReference A = Runtime.PyDict_GetItemString(globals.Borrow(), "A"); return new NewReference(A); } } }
X Tutup