From 47107d1c194acd8e99e8035e378780fd1021cd07 Mon Sep 17 00:00:00 2001 From: Victor Nova Date: Thu, 1 Apr 2021 23:17:17 -0700 Subject: [PATCH] use PyType instances instead of raw pointers in TypeManager type cache and ConstructorBinding instances --- src/runtime/classbase.cs | 4 +- src/runtime/classderived.cs | 6 +- src/runtime/classmanager.cs | 14 +-- src/runtime/clrobject.cs | 2 + src/runtime/constructorbinding.cs | 20 ++-- src/runtime/exceptions.cs | 9 ++ src/runtime/extensiontype.cs | 10 +- src/runtime/managedtype.cs | 35 +++---- src/runtime/metatype.cs | 4 +- src/runtime/pytype.cs | 13 +++ src/runtime/runtime.cs | 22 ++--- src/runtime/runtime_data.cs | 3 +- src/runtime/typemanager.cs | 157 ++++++++++++------------------ 13 files changed, 142 insertions(+), 157 deletions(-) diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs index bf6a8034d..55f5c5b8f 100644 --- a/src/runtime/classbase.cs +++ b/src/runtime/classbase.cs @@ -373,7 +373,7 @@ public static int tp_clear(IntPtr ob) protected override void OnSave(InterDomainContext context) { base.OnSave(context); - if (pyHandle != tpHandle) + if (!this.IsClrMetaTypeInstance()) { IntPtr dict = GetObjectDict(pyHandle); Runtime.XIncref(dict); @@ -384,7 +384,7 @@ protected override void OnSave(InterDomainContext context) protected override void OnLoad(InterDomainContext context) { base.OnLoad(context); - if (pyHandle != tpHandle) + if (!this.IsClrMetaTypeInstance()) { IntPtr dict = context.Storage.GetValue("dict"); SetObjectDict(pyHandle, dict); diff --git a/src/runtime/classderived.cs b/src/runtime/classderived.cs index f8b97b397..cc2397225 100644 --- a/src/runtime/classderived.cs +++ b/src/runtime/classderived.cs @@ -122,11 +122,13 @@ internal static IntPtr ToPython(IPythonDerivedType obj) /// internal static Type CreateDerivedType(string name, Type baseType, - IntPtr py_dict, + BorrowedReference dictRef, string namespaceStr, string assemblyName, string moduleName = "Python.Runtime.Dynamic.dll") { + // TODO: clean up + IntPtr py_dict = dictRef.DangerousGetAddress(); if (null != namespaceStr) { name = namespaceStr + "." + name; @@ -824,7 +826,7 @@ public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, objec try { // create the python object - IntPtr type = TypeManager.GetTypeHandle(obj.GetType()); + BorrowedReference type = TypeManager.GetTypeReference(obj.GetType()); self = new CLRObject(obj, type); // set __pyobj__ to self and deref the python object which will allow this diff --git a/src/runtime/classmanager.cs b/src/runtime/classmanager.cs index 306962f56..e3c5b2a3b 100644 --- a/src/runtime/classmanager.cs +++ b/src/runtime/classmanager.cs @@ -117,7 +117,7 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage) // Python object's dictionary tool; thus raising an AttributeError // instead of a TypeError. // Classes are re-initialized on in RestoreRuntimeData. - var dict = new BorrowedReference(Marshal.ReadIntPtr(cls.Value.tpHandle, TypeOffset.tp_dict)); + using var dict = Runtime.PyObject_GenericGetDict(cls.Value.TypeReference); foreach (var member in cls.Value.dotNetMembers) { // No need to decref the member, the ClassBase instance does @@ -135,7 +135,7 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage) } } // We modified the Type object, notify it we did. - Runtime.PyType_Modified(cls.Value.tpHandle); + Runtime.PyType_Modified(cls.Value.TypeReference); } } @@ -155,7 +155,7 @@ internal static Dictionary RestoreRuntimeData(R // re-init the class InitClassBase(pair.Key.Value, pair.Value); // We modified the Type object, notify it we did. - Runtime.PyType_Modified(pair.Value.tpHandle); + Runtime.PyType_Modified(pair.Value.TypeReference); var context = contexts[pair.Value.pyHandle]; pair.Value.Load(context); loadedObjs.Add(pair.Value, context); @@ -266,10 +266,10 @@ private static void InitClassBase(Type type, ClassBase impl) // point to the managed methods providing the implementation. - IntPtr tp = TypeManager.GetTypeHandle(impl, type); + var pyType = TypeManager.GetType(impl, type); // Finally, initialize the class __dict__ and return the object. - var dict = new BorrowedReference(Marshal.ReadIntPtr(tp, TypeOffset.tp_dict)); + using var dict = Runtime.PyObject_GenericGetDict(pyType.Reference); if (impl.dotNetMembers == null) @@ -312,7 +312,7 @@ private static void InitClassBase(Type type, ClassBase impl) // Implement Overloads on the class object if (!CLRModule._SuppressOverloads) { - var ctors = new ConstructorBinding(type, tp, co.binder); + var ctors = new ConstructorBinding(type, pyType, co.binder); // ExtensionType types are untracked, so don't Incref() them. // TODO: deprecate __overloads__ soon... Runtime.PyDict_SetItem(dict, PyIdentifier.__overloads__, ctors.ObjectReference); @@ -332,7 +332,7 @@ private static void InitClassBase(Type type, ClassBase impl) // The type has been modified after PyType_Ready has been called // Refresh the type - Runtime.PyType_Modified(tp); + Runtime.PyType_Modified(pyType.Reference); } internal static bool ShouldBindMethod(MethodBase mb) diff --git a/src/runtime/clrobject.cs b/src/runtime/clrobject.cs index 46cd896e2..142fade25 100644 --- a/src/runtime/clrobject.cs +++ b/src/runtime/clrobject.cs @@ -26,6 +26,8 @@ internal CLRObject(object ob, IntPtr tp) if (ob is Exception e) Exceptions.SetArgsAndCause(e, py); } + internal CLRObject(object ob, BorrowedReference tp) : this(ob, tp.DangerousGetAddress()) { } + protected CLRObject() { } diff --git a/src/runtime/constructorbinding.cs b/src/runtime/constructorbinding.cs index 803823e39..6706c2b48 100644 --- a/src/runtime/constructorbinding.cs +++ b/src/runtime/constructorbinding.cs @@ -23,16 +23,16 @@ namespace Python.Runtime internal class ConstructorBinding : ExtensionType { private MaybeType type; // The managed Type being wrapped in a ClassObject - private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. + private PyType typeToCreate; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; [NonSerialized] private IntPtr repr; - public ConstructorBinding(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder) + public ConstructorBinding(Type type, PyType typeToCreate, ConstructorBinder ctorBinder) { this.type = type; - this.pyTypeHndl = pyTypeHndl; // steal a type reference + this.typeToCreate = typeToCreate; this.ctorBinder = ctorBinder; repr = IntPtr.Zero; } @@ -110,7 +110,7 @@ public static IntPtr mp_subscript(IntPtr op, IntPtr key) { return Exceptions.RaiseTypeError("No match found for constructor signature"); } - var boundCtor = new BoundContructor(tp, self.pyTypeHndl, self.ctorBinder, ci); + var boundCtor = new BoundContructor(tp, self.typeToCreate, self.ctorBinder, ci); return boundCtor.pyHandle; } @@ -169,7 +169,7 @@ public static int tp_clear(IntPtr ob) public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg) { var self = (ConstructorBinding)GetManagedObject(ob); - int res = PyVisit(self.pyTypeHndl, visit, arg); + int res = PyVisit(self.typeToCreate.Handle, visit, arg); if (res != 0) return res; res = PyVisit(self.repr, visit, arg); @@ -190,15 +190,15 @@ public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg) internal class BoundContructor : ExtensionType { private Type type; // The managed Type being wrapped in a ClassObject - private IntPtr pyTypeHndl; // The python type tells GetInstHandle which Type to create. + private PyType typeToCreate; // The python type tells GetInstHandle which Type to create. private ConstructorBinder ctorBinder; private ConstructorInfo ctorInfo; private IntPtr repr; - public BoundContructor(Type type, IntPtr pyTypeHndl, ConstructorBinder ctorBinder, ConstructorInfo ci) + public BoundContructor(Type type, PyType typeToCreate, ConstructorBinder ctorBinder, ConstructorInfo ci) { this.type = type; - this.pyTypeHndl = pyTypeHndl; // steal a type reference + this.typeToCreate = typeToCreate; this.ctorBinder = ctorBinder; ctorInfo = ci; repr = IntPtr.Zero; @@ -229,7 +229,7 @@ public static IntPtr tp_call(IntPtr op, IntPtr args, IntPtr kw) } // Instantiate the python object that wraps the result of the method call // and return the PyObject* to it. - return CLRObject.GetInstHandle(obj, self.pyTypeHndl); + return CLRObject.GetInstHandle(obj, self.typeToCreate.Reference).DangerousMoveToPointer(); } /// @@ -272,7 +272,7 @@ public static int tp_clear(IntPtr ob) public static int tp_traverse(IntPtr ob, IntPtr visit, IntPtr arg) { var self = (BoundContructor)GetManagedObject(ob); - int res = PyVisit(self.pyTypeHndl, visit, arg); + int res = PyVisit(self.typeToCreate.Handle, visit, arg); if (res != 0) return res; res = PyVisit(self.repr, visit, arg); diff --git a/src/runtime/exceptions.cs b/src/runtime/exceptions.cs index 40e018fe6..163b0a11e 100644 --- a/src/runtime/exceptions.cs +++ b/src/runtime/exceptions.cs @@ -206,6 +206,15 @@ internal static void ErrorOccurredCheck(IntPtr pointer) } } + internal static IntPtr ErrorCheckIfNull(IntPtr pointer) + { + if (pointer == IntPtr.Zero && ErrorOccurred()) + { + throw new PythonException(); + } + return pointer; + } + /// /// ExceptionMatches Method /// diff --git a/src/runtime/extensiontype.cs b/src/runtime/extensiontype.cs index 554837c46..34a82fe37 100644 --- a/src/runtime/extensiontype.cs +++ b/src/runtime/extensiontype.cs @@ -18,7 +18,7 @@ public ExtensionType() // The Python instance object is related to an instance of a // particular concrete subclass with a hidden CLR gchandle. - IntPtr tp = TypeManager.GetTypeHandle(GetType()); + BorrowedReference tp = TypeManager.GetTypeReference(GetType()); //int rc = (int)Marshal.ReadIntPtr(tp, TypeOffset.ob_refcnt); //if (rc > 1050) @@ -27,11 +27,11 @@ public ExtensionType() // DebugUtil.DumpType(tp); //} - IntPtr py = Runtime.PyType_GenericAlloc(tp, 0); + NewReference py = Runtime.PyType_GenericAlloc(tp, 0); - // Steals a ref to tpHandle. - tpHandle = tp; - pyHandle = py; + // Borrowed reference. Valid as long as pyHandle is valid. + tpHandle = tp.DangerousGetAddress(); + pyHandle = py.DangerousMoveToPointer(); #if DEBUG GetGCHandle(ObjectReference, TypeReference, out var existing); diff --git a/src/runtime/managedtype.cs b/src/runtime/managedtype.cs index 41408abc7..e2f7a171f 100644 --- a/src/runtime/managedtype.cs +++ b/src/runtime/managedtype.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Diagnostics; @@ -27,8 +28,8 @@ internal enum TrackTypes internal IntPtr pyHandle; // PyObject * internal IntPtr tpHandle; // PyType * - internal BorrowedReference ObjectReference => new BorrowedReference(pyHandle); - internal BorrowedReference TypeReference => new BorrowedReference(tpHandle); + internal BorrowedReference ObjectReference => new(pyHandle); + internal BorrowedReference TypeReference => new(tpHandle); private static readonly Dictionary _managedObjs = new Dictionary(); @@ -78,12 +79,12 @@ internal void FreeGCHandle() } } - internal static ManagedType GetManagedObject(BorrowedReference ob) + 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) + internal static ManagedType? GetManagedObject(IntPtr ob) { if (ob != IntPtr.Zero) { @@ -106,7 +107,7 @@ internal static ManagedType GetManagedObject(IntPtr ob) /// /// Given a Python object, return the associated managed object type or null. /// - internal static ManagedType GetManagedObjectType(IntPtr ob) + internal static ManagedType? GetManagedObjectType(IntPtr ob) { if (ob != IntPtr.Zero) { @@ -121,18 +122,6 @@ internal static ManagedType GetManagedObjectType(IntPtr ob) 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 IsInstanceOfManagedType(BorrowedReference ob) => IsInstanceOfManagedType(ob.DangerousGetAddressOrNull()); internal static bool IsInstanceOfManagedType(IntPtr ob) @@ -156,6 +145,13 @@ internal static bool IsManagedType(BorrowedReference type) 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; @@ -185,7 +181,8 @@ internal void CallTypeClear() { return; } - var clearPtr = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_clear); + + var clearPtr = Runtime.PyType_GetSlot(TypeReference, TypeSlotID.tp_clear); if (clearPtr == IntPtr.Zero) { return; @@ -203,7 +200,7 @@ internal void CallTypeTraverse(Interop.ObjObjFunc visitproc, IntPtr arg) { return; } - var traversePtr = Marshal.ReadIntPtr(tpHandle, TypeOffset.tp_traverse); + var traversePtr = Runtime.PyType_GetSlot(TypeReference, TypeSlotID.tp_traverse); if (traversePtr == IntPtr.Zero) { return; diff --git a/src/runtime/metatype.cs b/src/runtime/metatype.cs index 1fde7dd78..a25df30e5 100644 --- a/src/runtime/metatype.cs +++ b/src/runtime/metatype.cs @@ -132,7 +132,7 @@ public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) { - return TypeManager.CreateSubType(name, base_type, dict); + return TypeManager.CreateSubType(name, base_type, clsDict.Reference); } } } @@ -266,7 +266,7 @@ public static int tp_setattro(IntPtr tp, IntPtr name, IntPtr value) } int res = Runtime.PyObject_GenericSetAttr(tp, name, value); - Runtime.PyType_Modified(tp); + Runtime.PyType_Modified(new BorrowedReference(tp)); return res; } diff --git a/src/runtime/pytype.cs b/src/runtime/pytype.cs index 8bc08b76d..e3d95db8d 100644 --- a/src/runtime/pytype.cs +++ b/src/runtime/pytype.cs @@ -6,6 +6,7 @@ namespace Python.Runtime { + [Serializable] public class PyType : PyObject { /// Creates heap type object from the . @@ -13,6 +14,12 @@ public PyType(TypeSpec spec, PyTuple? bases = null) : base(FromSpec(spec, bases) /// Wraps an existing type object. public PyType(PyObject o) : base(FromObject(o)) { } + internal PyType(BorrowedReference reference) : base(reference) + { + if (!Runtime.PyType_Check(this.Handle)) + throw new ArgumentException("object is not a type"); + } + /// Checks if specified object is a Python type. public static bool IsType(PyObject value) { @@ -21,6 +28,12 @@ public static bool IsType(PyObject value) return Runtime.PyType_Check(value.obj); } + internal IntPtr GetSlot(TypeSlotID slot) + { + IntPtr result = Runtime.PyType_GetSlot(this.Reference, slot); + return Exceptions.ErrorCheckIfNull(result); + } + private static BorrowedReference FromObject(PyObject o) { if (o is null) throw new ArgumentNullException(nameof(o)); diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index 232dd8708..8a3ad9231 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -1991,7 +1991,7 @@ internal static bool PyType_Check(IntPtr ob) } - internal static void PyType_Modified(IntPtr type) => Delegates.PyType_Modified(type); + internal static void PyType_Modified(BorrowedReference type) => Delegates.PyType_Modified(type); internal static bool PyType_IsSubtype(BorrowedReference t1, IntPtr ofType) => PyType_IsSubtype(t1, new BorrowedReference(ofType)); internal static bool PyType_IsSubtype(BorrowedReference t1, BorrowedReference t2) => Delegates.PyType_IsSubtype(t1, t2); @@ -2014,14 +2014,10 @@ internal static bool PyType_IsSameAsOrSubtype(BorrowedReference type, BorrowedRe internal static IntPtr PyType_GenericNew(IntPtr type, IntPtr args, IntPtr kw) => Delegates.PyType_GenericNew(type, args, kw); - internal static IntPtr PyType_GenericAlloc(IntPtr type, long n) - { - return PyType_GenericAlloc(type, new IntPtr(n)); - } - + internal static IntPtr PyType_GenericAlloc(IntPtr type, nint n) => PyType_GenericAlloc(new BorrowedReference(type), n).DangerousMoveToPointer(); + internal static NewReference PyType_GenericAlloc(BorrowedReference type, nint n) => Delegates.PyType_GenericAlloc(type, n); - private static IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n) => Delegates.PyType_GenericAlloc(type, n); - + internal static IntPtr PyType_GetSlot(BorrowedReference type, TypeSlotID slot) => Delegates.PyType_GetSlot(type, slot); internal static NewReference PyType_FromSpecWithBases(in NativeTypeSpec spec, BorrowedReference bases) => Delegates.PyType_FromSpecWithBases(in spec, bases); /// @@ -2489,10 +2485,10 @@ static Delegates() PySys_SetArgvEx = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetArgvEx), GetUnmanagedDll(_PythonDll)); PySys_GetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_GetObject), GetUnmanagedDll(_PythonDll)); PySys_SetObject = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PySys_SetObject), GetUnmanagedDll(_PythonDll)); - PyType_Modified = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Modified), GetUnmanagedDll(_PythonDll)); + PyType_Modified = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Modified), GetUnmanagedDll(_PythonDll)); PyType_IsSubtype = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_IsSubtype), GetUnmanagedDll(_PythonDll)); PyType_GenericNew = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericNew), GetUnmanagedDll(_PythonDll)); - PyType_GenericAlloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericAlloc), GetUnmanagedDll(_PythonDll)); + PyType_GenericAlloc = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GenericAlloc), GetUnmanagedDll(_PythonDll)); PyType_Ready = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_Ready), GetUnmanagedDll(_PythonDll)); _PyType_Lookup = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(_PyType_Lookup), GetUnmanagedDll(_PythonDll)); PyObject_GenericGetAttr = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyObject_GenericGetAttr), GetUnmanagedDll(_PythonDll)); @@ -2534,6 +2530,7 @@ static Delegates() PyException_SetCause = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyException_SetCause), GetUnmanagedDll(_PythonDll)); PyThreadState_SetAsyncExcLLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); PyThreadState_SetAsyncExcLP64 = (delegate* unmanaged[Cdecl])GetFunctionByName("PyThreadState_SetAsyncExc", GetUnmanagedDll(_PythonDll)); + PyType_GetSlot = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_GetSlot), GetUnmanagedDll(_PythonDll)); PyType_FromSpecWithBases = (delegate* unmanaged[Cdecl])GetFunctionByName(nameof(PyType_FromSpecWithBases), GetUnmanagedDll(PythonDLL)); try @@ -2774,10 +2771,10 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PySys_SetArgvEx { get; } internal static delegate* unmanaged[Cdecl] PySys_GetObject { get; } internal static delegate* unmanaged[Cdecl] PySys_SetObject { get; } - internal static delegate* unmanaged[Cdecl] PyType_Modified { get; } + internal static delegate* unmanaged[Cdecl] PyType_Modified { get; } internal static delegate* unmanaged[Cdecl] PyType_IsSubtype { get; } internal static delegate* unmanaged[Cdecl] PyType_GenericNew { get; } - internal static delegate* unmanaged[Cdecl] PyType_GenericAlloc { get; } + internal static delegate* unmanaged[Cdecl] PyType_GenericAlloc { get; } internal static delegate* unmanaged[Cdecl] PyType_Ready { get; } internal static delegate* unmanaged[Cdecl] _PyType_Lookup { get; } internal static delegate* unmanaged[Cdecl] PyObject_GenericGetAttr { get; } @@ -2819,6 +2816,7 @@ static Delegates() internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLLP64 { get; } internal static delegate* unmanaged[Cdecl] PyThreadState_SetAsyncExcLP64 { get; } internal static delegate* unmanaged[Cdecl] PyObject_GenericGetDict { get; } + internal static delegate* unmanaged[Cdecl] PyType_GetSlot { get; } internal static delegate* unmanaged[Cdecl] PyType_FromSpecWithBases { get; } internal static delegate* unmanaged[Cdecl] _Py_NewReference { get; } } diff --git a/src/runtime/runtime_data.cs b/src/runtime/runtime_data.cs index 29cea4181..832e5bbec 100644 --- a/src/runtime/runtime_data.cs +++ b/src/runtime/runtime_data.cs @@ -119,12 +119,13 @@ private static void RestoreRuntimeDataImpl() var formatter = CreateFormatter(); var storage = (RuntimeDataStorage)formatter.Deserialize(ms); + PyCLRMetaType = MetaType.RestoreRuntimeData(storage.GetStorage("meta")); + var objs = RestoreRuntimeDataObjects(storage.GetStorage("objs")); RestoreRuntimeDataModules(storage.GetStorage("modules")); TypeManager.RestoreRuntimeData(storage.GetStorage("types")); var clsObjs = ClassManager.RestoreRuntimeData(storage.GetStorage("classes")); ImportHook.RestoreRuntimeData(storage.GetStorage("import")); - PyCLRMetaType = MetaType.RestoreRuntimeData(storage.GetStorage("meta")); foreach (var item in objs) { diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 20b95d85c..9707568b5 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -21,7 +21,7 @@ internal class TypeManager internal static IntPtr subtype_clear; private const BindingFlags tbFlags = BindingFlags.Public | BindingFlags.Static; - private static Dictionary cache = new Dictionary(); + private static Dictionary cache = new(); private static readonly Dictionary _slotsHolders = new Dictionary(); private static Dictionary _slotsImpls = new Dictionary(); @@ -45,19 +45,19 @@ internal static void Initialize() internal static void RemoveTypes() { - foreach (var tpHandle in cache.Values) + foreach (var type in cache.Values) { SlotsHolder holder; - if (_slotsHolders.TryGetValue(tpHandle, out holder)) + if (_slotsHolders.TryGetValue(type.Handle, out holder)) { // If refcount > 1, it needs to reset the managed slot, // otherwise it can dealloc without any trick. - if (Runtime.Refcount(tpHandle) > 1) + if (Runtime.Refcount(type.Handle) > 1) { holder.ResetSlots(); } } - Runtime.XDecref(tpHandle); + type.Dispose(); } cache.Clear(); _slotsImpls.Clear(); @@ -68,7 +68,7 @@ internal static void SaveRuntimeData(RuntimeDataStorage storage) { foreach (var tpHandle in cache.Values) { - Runtime.XIncref(tpHandle); + Runtime.XIncref(tpHandle.Handle); } storage.AddValue("cache", cache); storage.AddValue("slots", _slotsImpls); @@ -78,45 +78,33 @@ internal static void RestoreRuntimeData(RuntimeDataStorage storage) { Debug.Assert(cache == null || cache.Count == 0); storage.GetValue("slots", out _slotsImpls); - storage.GetValue>("cache", out var _cache); + storage.GetValue>("cache", out var _cache); foreach (var entry in _cache) { if (!entry.Key.Valid) { - Runtime.XDecref(entry.Value); + entry.Value.Dispose(); continue; } Type type = entry.Key.Value;; - IntPtr handle = entry.Value; - cache[type] = handle; - SlotsHolder holder = CreateSolotsHolder(handle); - InitializeSlots(handle, _slotsImpls[type], holder); + cache[type] = entry.Value; + SlotsHolder holder = CreateSolotsHolder(entry.Value.Handle); + InitializeSlots(entry.Value.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. - /// - [Obsolete] - internal static IntPtr GetTypeHandle(Type type) + 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. - IntPtr handle; - cache.TryGetValue(type, out handle); - if (handle != IntPtr.Zero) + if (!cache.TryGetValue(type, out var pyType)) { - return handle; + pyType = CreateType(type); + cache[type] = pyType; + _slotsImpls.Add(type, type); } - handle = CreateType(type); - cache[type] = handle; - _slotsImpls.Add(type, type); - return handle; + return pyType; } /// /// Given a managed Type derived from ExtensionType, get the handle to @@ -124,28 +112,23 @@ internal static IntPtr GetTypeHandle(Type 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) - => new BorrowedReference(GetTypeHandle(type)); + internal static BorrowedReference GetTypeReference(Type type) => GetType(type).Reference; /// - /// Return value: Borrowed reference. - /// Get the handle of a Python type that reflects the given CLR type. + /// Get the 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(ClassBase obj, Type type) + internal static PyType GetType(ClassBase obj, Type type) { - IntPtr handle; - cache.TryGetValue(type, out handle); - if (handle != IntPtr.Zero) + if (!cache.TryGetValue(type, out var pyType)) { - return handle; + pyType = CreateType(obj, type); + cache[type] = pyType; + _slotsImpls.Add(type, obj.GetType()); } - handle = CreateType(obj, type); - cache[type] = handle; - _slotsImpls.Add(type, obj.GetType()); - return handle; + return pyType; } @@ -157,7 +140,7 @@ internal static IntPtr GetTypeHandle(ClassBase obj, Type type) /// 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 IntPtr CreateType(Type impl) + internal static unsafe PyType CreateType(Type impl) { IntPtr type = AllocateTypeObject(impl.Name, metatype: Runtime.PyCLRMetaType); IntPtr base_ = impl == typeof(CLRModule) @@ -187,21 +170,27 @@ internal static unsafe IntPtr CreateType(Type impl) throw new PythonException(); } - var dict = new BorrowedReference(Marshal.ReadIntPtr(type, TypeOffset.tp_dict)); + // TODO: use PyType(TypeSpec) constructor + var pyType = new PyType(new BorrowedReference(type)); + Runtime.XDecref(type); + + NewReference dict = Runtime.PyObject_GenericGetDict(pyType.Reference); var mod = NewReference.DangerousFromPointer(Runtime.PyString_FromString("CLR")); Runtime.PyDict_SetItem(dict, PyIdentifier.__module__, mod); mod.Dispose(); - InitMethods(type, impl); + InitMethods(dict, impl); + + dict.Dispose(); // The type has been modified after PyType_Ready has been called // Refresh the type - Runtime.PyType_Modified(type); - return type; + Runtime.PyType_Modified(pyType.Reference); + return pyType; } - internal static IntPtr CreateType(ClassBase impl, Type clrType) + internal static PyType CreateType(ClassBase impl, Type clrType) { // Cleanup the type name to get rid of funny nested type names. string name = $"clr.{clrType.FullName}"; @@ -322,8 +311,9 @@ internal static IntPtr CreateType(ClassBase impl, Type clrType) impl.pyHandle = type; //DebugUtil.DumpType(type); - - return type; + var pyType = new PyType(new BorrowedReference(type)); + Runtime.XDecref(type); + return pyType; } static int InheritOrAllocateStandardFields(IntPtr type, IntPtr @base) @@ -351,9 +341,8 @@ void InheritOrAllocate(int typeField) return newFieldOffset; } - internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) + internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, BorrowedReference dictRef) { - var dictRef = new BorrowedReference(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); @@ -400,18 +389,18 @@ internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr { Type subType = ClassDerivedObject.CreateDerivedType(name, baseClass.type.Value, - py_dict, + dictRef, (string)namespaceStr, (string)assembly); // create the new ManagedType and python type ClassBase subClass = ClassManager.GetClass(subType); - IntPtr py_type = GetTypeHandle(subClass, subType); + IntPtr py_type = GetType(subClass, subType).Handle; // 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. var cls_dict = new BorrowedReference(Marshal.ReadIntPtr(py_type, TypeOffset.tp_dict)); - ThrowIfIsNotZero(Runtime.PyDict_Update(cls_dict, new BorrowedReference(py_dict))); + ThrowIfIsNotZero(Runtime.PyDict_Update(cls_dict, dictRef)); Runtime.XIncref(py_type); // Update the __classcell__ if it exists BorrowedReference cell = Runtime.PyDict_GetItemString(cls_dict, "__classcell__"); @@ -513,7 +502,7 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder) // The type has been modified after PyType_Ready has been called // Refresh the type - Runtime.PyType_Modified(type); + Runtime.PyType_Modified(new BorrowedReference(type)); //DebugUtil.DumpType(type); return type; @@ -576,7 +565,6 @@ private static IntPtr AddCustomMetaMethod(string name, IntPtr type, IntPtr mdef, return mdef; } - /// /// Utility method to allocate a type object & do basic initialization. /// @@ -599,6 +587,8 @@ internal static IntPtr AllocateTypeObject(string name, IntPtr metatype) Runtime.XIncref(temp); Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); + + #warning dead code? temp = type + TypeOffset.nb_add; Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); @@ -610,6 +600,7 @@ internal static IntPtr AllocateTypeObject(string name, IntPtr metatype) temp = type + TypeOffset.bf_getbuffer; Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); + return type; } @@ -661,45 +652,24 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo } } - /// - /// 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) + static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolder slotsHolder) { - var offset = TypeOffset.GetSlotOffset(name); - if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero) + if (!Enum.TryParse(name, out var id)) { - return; + throw new NotSupportedException("Bad slot name " + name); } - Marshal.WriteIntPtr(type, offset, slot); + int offset = TypeOffset.GetSlotOffset(name); + InitializeSlot(type, offset, thunk, slotsHolder); } - static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolder slotsHolder = null, bool canOverride = true) + static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, SlotsHolder slotsHolder) { - int offset = TypeOffset.GetSlotOffset(name); - - if (!canOverride && Marshal.ReadIntPtr(type, offset) != IntPtr.Zero) - { - return; - } - Marshal.WriteIntPtr(type, offset, thunk.Address); - if (slotsHolder != null) - { - slotsHolder.Set(offset, thunk); - } + var thunk = Interop.GetThunk(method); + InitializeSlot(type, slotOffset, thunk, slotsHolder); } - static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, SlotsHolder slotsHolder = null) + static void InitializeSlot(IntPtr type, int slotOffset, ThunkInfo thunk, SlotsHolder slotsHolder) { - var thunk = Interop.GetThunk(method); Marshal.WriteIntPtr(type, slotOffset, thunk.Address); if (slotsHolder != null) { @@ -707,20 +677,13 @@ static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method, Slots } } - static bool IsSlotSet(IntPtr type, string name) - { - int offset = TypeOffset.GetSlotOffset(name); - return Marshal.ReadIntPtr(type, offset) != IntPtr.Zero; - } - /// - /// Given a newly allocated Python type object and a managed Type that + /// Given a dict of 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) + private static void InitMethods(BorrowedReference typeDict, Type type) { - IntPtr dict = Marshal.ReadIntPtr(pytype, TypeOffset.tp_dict); Type marker = typeof(PythonMethodAttribute); BindingFlags flags = BindingFlags.Public | BindingFlags.Static; @@ -740,7 +703,7 @@ private static void InitMethods(IntPtr pytype, Type type) var mi = new MethodInfo[1]; mi[0] = method; MethodObject m = new TypeMethod(type, method_name, mi); - Runtime.PyDict_SetItemString(dict, method_name, m.pyHandle); + Runtime.PyDict_SetItemString(typeDict, method_name, m.ObjectReference); m.DecrRefCount(); addedMethods.Add(method_name); }