X Tutup
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Diagnostics; using Python.Runtime.Slots; 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; private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static; private static Dictionary cache = new Dictionary(); private static readonly Dictionary _slotsHolders = new Dictionary(); private static Dictionary _slotsImpls = new Dictionary(); // 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"); IntPtr type = SlotHelper.CreateObjectType(); subtype_traverse = Marshal.ReadIntPtr(type, TypeOffset.tp_traverse); subtype_clear = Marshal.ReadIntPtr(type, TypeOffset.tp_clear); Runtime.XDecref(type); } internal static void RemoveTypes() { foreach (var tpHandle in cache.Values) { SlotsHolder holder; if (_slotsHolders.TryGetValue(tpHandle, out holder)) { // If refcount > 1, it needs to reset the managed slot, // otherwise it can dealloc without any trick. if (Runtime.Refcount(tpHandle) > 1) { holder.ResetSlots(); } } Runtime.XDecref(tpHandle); } cache.Clear(); _slotsImpls.Clear(); _slotsHolders.Clear(); } internal static void SaveRuntimeData(RuntimeDataStorage storage) { foreach (var tpHandle in cache.Values) { Runtime.XIncref(tpHandle); } storage.AddValue("cache", cache); storage.AddValue("slots", _slotsImpls); } internal static void RestoreRuntimeData(RuntimeDataStorage storage) { Debug.Assert(cache == null || cache.Count == 0); storage.GetValue("slots", out _slotsImpls); storage.GetValue("cache", out cache); foreach (var entry in cache) { Type type = entry.Key; IntPtr handle = entry.Value; SlotsHolder holder = CreateSolotsHolder(handle); InitializeSlots(handle, _slotsImpls[type], holder); // FIXME: mp_length_slot.CanAssgin(clrType) } } /// /// Return value: Borrowed reference. /// 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 IntPtr GetTypeHandle(Type type) { // Note that these types are cached with a refcount of 1, so they // effectively exist until the CPython runtime is finalized. IntPtr handle; cache.TryGetValue(type, out handle); if (handle != IntPtr.Zero) { return handle; } handle = CreateType(type); cache[type] = handle; _slotsImpls.Add(type, type); return handle; } /// /// Return value: Borrowed reference. /// Get the handle of a Python type that reflects the given CLR type. /// The given ManagedType instance is a managed object that implements /// the appropriate semantics in Python for the reflected managed type. /// internal static IntPtr GetTypeHandle(ManagedType obj, Type type) { IntPtr handle; cache.TryGetValue(type, out handle); if (handle != IntPtr.Zero) { return handle; } handle = CreateType(obj, type); cache[type] = handle; _slotsImpls.Add(type, obj.GetType()); return handle; } /// /// 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 IntPtr CreateType(Type impl) { IntPtr type = AllocateTypeObject(impl.Name); int ob_size = ObjectOffset.Size(type); // Set tp_basicsize to the size of our managed instance objects. Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); var offset = (IntPtr)ObjectOffset.TypeDictOffset(type); Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); SlotsHolder slotsHolder = CreateSolotsHolder(type); InitializeSlots(type, impl, slotsHolder); int flags = TypeFlags.Default | TypeFlags.Managed | TypeFlags.HeapType | TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); if (Runtime.PyType_Ready(type) != 0) { throw new PythonException(); } IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); IntPtr mod = Runtime.PyString_FromString("CLR"); Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod); Runtime.XDecref(mod); InitMethods(type, impl); return type; } internal static IntPtr CreateType(ManagedType impl, Type clrType) { // Cleanup the type name to get rid of funny nested type names. string name = "CLR." + clrType.FullName; int i = name.LastIndexOf('+'); if (i > -1) { name = name.Substring(i + 1); } i = name.LastIndexOf('.'); if (i > -1) { name = name.Substring(i + 1); } IntPtr base_ = IntPtr.Zero; int ob_size = ObjectOffset.Size(Runtime.PyTypeType); // XXX Hack, use a different base class for System.Exception // Python 2.5+ allows new style class exceptions but they *must* // subclass BaseException (or better Exception). if (typeof(Exception).IsAssignableFrom(clrType)) { ob_size = ObjectOffset.Size(Exceptions.Exception); } int tp_dictoffset = ob_size + ManagedDataOffsets.ob_dict; if (clrType == typeof(Exception)) { base_ = Exceptions.Exception; } else if (clrType.BaseType != null) { ClassBase bc = ClassManager.GetClass(clrType.BaseType); base_ = bc.pyHandle; } IntPtr type = AllocateTypeObject(name); Marshal.WriteIntPtr(type, TypeOffset.ob_type, Runtime.PyCLRMetaType); Runtime.XIncref(Runtime.PyCLRMetaType); Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset); // we want to do this after the slot stuff above in case the class itself implements a slot method SlotsHolder slotsHolder = CreateSolotsHolder(type); InitializeSlots(type, impl.GetType(), slotsHolder); if (Marshal.ReadIntPtr(type, TypeOffset.mp_length) == IntPtr.Zero && mp_length_slot.CanAssign(clrType)) { InitializeSlot(type, TypeOffset.mp_length, mp_length_slot.Method, slotsHolder); } if (!typeof(IEnumerable).IsAssignableFrom(clrType) && !typeof(IEnumerator).IsAssignableFrom(clrType)) { // The tp_iter slot should only be set for enumerable types. Marshal.WriteIntPtr(type, TypeOffset.tp_iter, IntPtr.Zero); } // Only set mp_subscript and mp_ass_subscript for types with indexers if (impl is ClassBase cb) { if (!(impl is ArrayObject)) { if (cb.indexer == null || !cb.indexer.CanGet) { Marshal.WriteIntPtr(type, TypeOffset.mp_subscript, IntPtr.Zero); } if (cb.indexer == null || !cb.indexer.CanSet) { Marshal.WriteIntPtr(type, TypeOffset.mp_ass_subscript, IntPtr.Zero); } } } else { Marshal.WriteIntPtr(type, TypeOffset.mp_subscript, IntPtr.Zero); Marshal.WriteIntPtr(type, TypeOffset.mp_ass_subscript, IntPtr.Zero); } if (base_ != IntPtr.Zero) { Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); Runtime.XIncref(base_); } const int flags = TypeFlags.Default | TypeFlags.Managed | TypeFlags.HeapType | TypeFlags.BaseType | TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); // 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 (Runtime.PyType_Ready(type) != 0) { throw new PythonException(); } IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); string mn = clrType.Namespace ?? ""; IntPtr mod = Runtime.PyString_FromString(mn); Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod); Runtime.XDecref(mod); // Hide the gchandle of the implementation in a magic type slot. GCHandle gc = impl.AllocGCHandle(); Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc); // Set the handle attributes on the implementing instance. impl.tpHandle = type; impl.pyHandle = type; //DebugUtil.DumpType(type); return type; } internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) { // 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); // 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; var disposeList = new List(); try { var assemblyKey = new PyObject(Converter.ToPython("__assembly__", typeof(string))); disposeList.Add(assemblyKey); if (0 != Runtime.PyMapping_HasKey(py_dict, assemblyKey.Handle)) { var pyAssembly = new PyObject(Runtime.PyDict_GetItem(py_dict, assemblyKey.Handle)); Runtime.XIncref(pyAssembly.Handle); disposeList.Add(pyAssembly); if (!Converter.ToManagedValue(pyAssembly.Handle, typeof(string), out assembly, false)) { throw new InvalidCastException("Couldn't convert __assembly__ value to string"); } } var namespaceKey = new PyObject(Converter.ToPythonImplicit("__namespace__")); disposeList.Add(namespaceKey); if (0 != Runtime.PyMapping_HasKey(py_dict, namespaceKey.Handle)) { var pyNamespace = new PyObject(Runtime.PyDict_GetItem(py_dict, namespaceKey.Handle)); Runtime.XIncref(pyNamespace.Handle); disposeList.Add(pyNamespace); if (!Converter.ToManagedValue(pyNamespace.Handle, typeof(string), out namespaceStr, false)) { throw new InvalidCastException("Couldn't convert __namespace__ value to string"); } } } finally { foreach (PyObject o in disposeList) { o.Dispose(); } } // create the new managed type subclassing the base managed type var baseClass = ManagedType.GetManagedObject(py_base_type) as ClassBase; if (null == baseClass) { return Exceptions.RaiseTypeError("invalid base class, expected CLR class type"); } try { Type subType = ClassDerivedObject.CreateDerivedType(name, baseClass.type, py_dict, (string)namespaceStr, (string)assembly); // create the new ManagedType and python type ClassBase subClass = ClassManager.GetClass(subType); IntPtr py_type = GetTypeHandle(subClass, subType); // by default the class dict will have all the C# methods in it, but as this is a // derived class we want the python overrides in there instead if they exist. IntPtr cls_dict = Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict); Runtime.PyDict_Update(cls_dict, py_dict); Runtime.XIncref(py_type); // Update the __classcell__ if it exists var cell = new BorrowedReference(Runtime.PyDict_GetItemString(cls_dict, "__classcell__")); if (!cell.IsNull) { Runtime.PyCell_Set(cell, py_type); Runtime.PyDict_DelItemString(cls_dict, "__classcell__"); } return py_type; } catch (Exception e) { return Exceptions.RaiseTypeError(e.Message); } } internal static IntPtr WriteMethodDef(IntPtr mdef, IntPtr name, IntPtr func, int flags, IntPtr doc) { Marshal.WriteIntPtr(mdef, name); Marshal.WriteIntPtr(mdef, 1 * IntPtr.Size, func); Marshal.WriteInt32(mdef, 2 * IntPtr.Size, flags); Marshal.WriteIntPtr(mdef, 3 * IntPtr.Size, doc); return mdef + 4 * IntPtr.Size; } internal static IntPtr WriteMethodDef(IntPtr mdef, string name, IntPtr func, int flags = 0x0001, 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 IntPtr 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. IntPtr type = AllocateTypeObject("CLR Metatype"); IntPtr py_type = Runtime.PyTypeType; Marshal.WriteIntPtr(type, TypeOffset.tp_base, py_type); Runtime.XIncref(py_type); const int flags = TypeFlags.Default | TypeFlags.Managed | TypeFlags.HeapType | TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, 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 new PythonException(); } IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); IntPtr mod = Runtime.PyString_FromString("CLR"); Runtime.PyDict_SetItemString(dict, "__module__", mod); //DebugUtil.DumpType(type); return type; } internal static SlotsHolder SetupMetaSlots(Type impl, IntPtr type) { // Override type slots with those of the managed implementation. SlotsHolder 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); Marshal.WriteIntPtr(type, TypeOffset.tp_methods, mdefStart); // XXX: Hard code with mode check. if (Runtime.ShutdownMode != ShutdownMode.Reload) { slotsHolder.Set(TypeOffset.tp_methods, (t, offset) => { var p = Marshal.ReadIntPtr(t, offset); Runtime.PyMem_Free(p); Marshal.WriteIntPtr(t, offset, IntPtr.Zero); }); } return slotsHolder; } private static IntPtr AddCustomMetaMethod(string name, IntPtr type, IntPtr mdef, SlotsHolder slotsHolder) { MethodInfo mi = typeof(MetaType).GetMethod(name); ThunkInfo thunkInfo = Interop.GetThunk(mi, "BinaryFunc"); slotsHolder.KeeapAlive(thunkInfo); // XXX: Hard code with mode check. if (Runtime.ShutdownMode != ShutdownMode.Reload) { IntPtr mdefAddr = mdef; slotsHolder.AddDealloctor(() => { IntPtr tp_dict = Marshal.ReadIntPtr(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; } internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl) { // Utility to create a subtype of a std Python type, but with // a managed type able to override implementation IntPtr type = AllocateTypeObject(name); //Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize); //Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); //IntPtr offset = (IntPtr)ObjectOffset.ob_dict; //Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); //IntPtr dc = Runtime.PyDict_Copy(dict); //Marshal.WriteIntPtr(type, TypeOffset.tp_dict, dc); Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); Runtime.XIncref(base_); int flags = TypeFlags.Default; flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); CopySlot(base_, type, TypeOffset.tp_traverse); CopySlot(base_, type, TypeOffset.tp_clear); CopySlot(base_, type, TypeOffset.tp_is_gc); SlotsHolder slotsHolder = CreateSolotsHolder(type); InitializeSlots(type, impl, slotsHolder); if (Runtime.PyType_Ready(type) != 0) { throw new PythonException(); } IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); IntPtr mod = Runtime.PyString_FromString("CLR"); Runtime.PyDict_SetItem(tp_dict, PyIdentifier.__module__, mod); return type; } /// /// Utility method to allocate a type object & do basic initialization. /// internal static IntPtr AllocateTypeObject(string name) { IntPtr type = Runtime.PyType_GenericAlloc(Runtime.PyTypeType, 0); // 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. Marshal.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. IntPtr temp = Runtime.PyUnicode_FromString(name); IntPtr raw = Runtime.PyUnicode_AsUTF8(temp); Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw); Marshal.WriteIntPtr(type, TypeOffset.name, temp); Runtime.XIncref(temp); Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); temp = type + TypeOffset.nb_add; Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); temp = type + TypeOffset.sq_length; Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp); temp = type + TypeOffset.mp_length; Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); temp = type + TypeOffset.bf_getbuffer; Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); return type; } /// /// 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(IntPtr 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_") && !SlotTypes.IsSlotName(name)) { continue; } if (seen.Contains(name)) { continue; } InitializeSlot(type, Interop.GetThunk(method), name, slotsHolder); seen.Add(name); } impl = impl.BaseType; } foreach (string slot in _requiredSlots) { if (seen.Contains(slot)) { continue; } var offset = ManagedDataOffsets.GetSlotOffset(slot); Marshal.WriteIntPtr(type, offset, SlotsHolder.GetDefaultSlot(offset)); } } /// /// Helper for InitializeSlots. /// /// Initializes one slot to point to a function pointer. /// The function pointer might be a thunk for C#, or it may be /// an address in the NativeCodePage. /// /// Type being initialized. /// Function pointer. /// Name of the method. /// Can override the slot when it existed static void InitializeSlot(IntPtr type, IntPtr slot, string name, bool canOverride = true) { var offset = ManagedDataOffsets.GetSlotOffset(name); if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero) { return; } Marshal.WriteIntPtr(type, offset, slot); } static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolder slotsHolder = null, bool canOverride = true) { Type typeOffset = typeof(TypeOffset); FieldInfo fi = typeOffset.GetField(name); var offset = (int)fi.GetValue(typeOffset); if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero) { return; } Marshal.WriteIntPtr(type, offset, thunk.Address); if (slotsHolder != null) { slotsHolder.Set(offset, thunk); } } static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, SlotsHolder slotsHolder = null) { var thunk = Interop.GetThunk(method); Marshal.WriteIntPtr(type, slotOffset, thunk.Address); if (slotsHolder != null) { slotsHolder.Set(slotOffset, thunk); } } static bool IsSlotSet(IntPtr type, string name) { int offset = ManagedDataOffsets.GetSlotOffset(name); return Marshal.ReadIntPtr(type, offset) != IntPtr.Zero; } /// /// Given a newly allocated Python type object and a managed Type that /// implements it, initialize any methods defined by the Type that need /// to appear in the Python type __dict__ (based on custom attribute). /// private static void InitMethods(IntPtr pytype, Type type) { IntPtr dict = Marshal.ReadIntPtr(pytype, TypeOffset.tp_dict); Type marker = typeof(PythonMethodAttribute); BindingFlags flags = BindingFlags.Public | BindingFlags.Static; var addedMethods = new HashSet(); while (type != null) { MethodInfo[] methods = type.GetMethods(flags); foreach (MethodInfo method in methods) { if (!addedMethods.Contains(method.Name)) { object[] attrs = method.GetCustomAttributes(marker, false); if (attrs.Length > 0) { string method_name = method.Name; var mi = new MethodInfo[1]; mi[0] = method; MethodObject m = new TypeMethod(type, method_name, mi); Runtime.PyDict_SetItemString(dict, method_name, m.pyHandle); m.DecrRefCount(); addedMethods.Add(method_name); } } } type = type.BaseType; } } /// /// Utility method to copy slots from a given type to another type. /// internal static void CopySlot(IntPtr from, IntPtr to, int offset) { IntPtr fp = Marshal.ReadIntPtr(from, offset); Marshal.WriteIntPtr(to, offset, fp); } private static SlotsHolder CreateSolotsHolder(IntPtr type) { var holder = new SlotsHolder(type); _slotsHolders.Add(type, holder); return holder; } } class SlotsHolder { public delegate void Resetor(IntPtr type, int offset); private readonly IntPtr _type; private Dictionary _slots = new Dictionary(); private List _keepalive = new List(); private Dictionary _customResetors = new Dictionary(); private List _deallocators = new List(); private bool _alreadyReset = false; /// /// Create slots holder for holding the delegate of slots and be able to reset them. /// /// Steals a reference to target type public SlotsHolder(IntPtr type) { _type = type; } 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 void ResetSlots() { if (_alreadyReset) { return; } _alreadyReset = true; #if DEBUG IntPtr tp_name = Marshal.ReadIntPtr(_type, TypeOffset.tp_name); string typeName = Marshal.PtrToStringAnsi(tp_name); #endif foreach (var offset in _slots.Keys) { IntPtr ptr = GetDefaultSlot(offset); #if DEBUG //DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>"); #endif Marshal.WriteIntPtr(_type, offset, ptr); } 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 IntPtr handlePtr = Marshal.ReadIntPtr(_type, TypeOffset.magic()); if (handlePtr != IntPtr.Zero) { GCHandle handle = GCHandle.FromIntPtr(handlePtr); if (handle.IsAllocated) { handle.Free(); } Marshal.WriteIntPtr(_type, TypeOffset.magic(), IntPtr.Zero); } } 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 Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free); } else if (offset == TypeOffset.tp_free) { // PyObject_GC_Del return Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_free); } else if (offset == TypeOffset.tp_call) { return IntPtr.Zero; } else if (offset == TypeOffset.tp_new) { // PyType_GenericNew return Marshal.ReadIntPtr(Runtime.PySuper_Type, TypeOffset.tp_new); } else if (offset == TypeOffset.tp_getattro) { // PyObject_GenericGetAttr return Marshal.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_getattro); } else if (offset == TypeOffset.tp_setattro) { // PyObject_GenericSetAttr return Marshal.ReadIntPtr(Runtime.PyBaseObjectType, TypeOffset.tp_setattro); } return Marshal.ReadIntPtr(Runtime.PyTypeType, offset); } } static class SlotHelper { public static IntPtr CreateObjectType() { IntPtr globals = Runtime.PyDict_New(); if (Runtime.PyDict_SetItemString(globals, "__builtins__", Runtime.PyEval_GetBuiltins()) != 0) { Runtime.XDecref(globals); throw new PythonException(); } const string code = "class A(object): pass"; var resRef = Runtime.PyRun_String(code, RunFlagType.File, globals, globals); IntPtr res = resRef.DangerousGetAddress(); if (res == IntPtr.Zero) { try { throw new PythonException(); } finally { Runtime.XDecref(globals); } } resRef.Dispose(); IntPtr A = Runtime.PyDict_GetItemString(globals, "A"); Debug.Assert(A != IntPtr.Zero); Runtime.XIncref(A); Runtime.XDecref(globals); return A; } } static partial class SlotTypes { private static Dictionary _nameMap = new Dictionary(); static SlotTypes() { foreach (var type in Types) { FieldInfo[] fields = type.GetFields(); foreach (var fi in fields) { _nameMap[fi.Name] = type; } } } public static bool IsSlotName(string name) { return _nameMap.ContainsKey(name); } } }
X Tutup