diff --git a/src/runtime/interop.cs b/src/runtime/interop.cs index 95f3e5b9f..a56fd4a95 100644 --- a/src/runtime/interop.cs +++ b/src/runtime/interop.cs @@ -164,6 +164,11 @@ public static int TypeDictOffset(IntPtr type) return ManagedDataOffsets.DictOffset(type); } + public static int Size() + { + return size; + } + public static int Size(IntPtr pyType) { if (IsException(pyType)) diff --git a/src/runtime/interop34.cs b/src/runtime/interop34.cs index 6857ff2d0..178e62c92 100644 --- a/src/runtime/interop34.cs +++ b/src/runtime/interop34.cs @@ -131,9 +131,9 @@ public static int magic() public static int sq_inplace_repeat = 0; public static int bf_getbuffer = 0; public static int bf_releasebuffer = 0; - public static int name = 0; + public static int ht_name = 0; public static int ht_slots = 0; - public static int qualname = 0; + public static int ht_qualname = 0; public static int ht_cached_keys = 0; /* here are optional user slots, followed by the members. */ diff --git a/src/runtime/interop35.cs b/src/runtime/interop35.cs index a30bfa4fd..a8397a3bd 100644 --- a/src/runtime/interop35.cs +++ b/src/runtime/interop35.cs @@ -136,9 +136,9 @@ public static int magic() public static int sq_inplace_repeat = 0; public static int bf_getbuffer = 0; public static int bf_releasebuffer = 0; - public static int name = 0; + public static int ht_name = 0; public static int ht_slots = 0; - public static int qualname = 0; + public static int ht_qualname = 0; public static int ht_cached_keys = 0; /* here are optional user slots, followed by the members. */ diff --git a/src/runtime/interop36.cs b/src/runtime/interop36.cs index c46bcc2f5..7ba02e202 100644 --- a/src/runtime/interop36.cs +++ b/src/runtime/interop36.cs @@ -136,9 +136,9 @@ public static int magic() public static int sq_inplace_repeat = 0; public static int bf_getbuffer = 0; public static int bf_releasebuffer = 0; - public static int name = 0; + public static int ht_name = 0; public static int ht_slots = 0; - public static int qualname = 0; + public static int ht_qualname = 0; public static int ht_cached_keys = 0; /* here are optional user slots, followed by the members. */ diff --git a/src/runtime/interop37.cs b/src/runtime/interop37.cs index d5fc76ad3..34f92f56d 100644 --- a/src/runtime/interop37.cs +++ b/src/runtime/interop37.cs @@ -136,9 +136,9 @@ public static int magic() public static int sq_inplace_repeat = 0; public static int bf_getbuffer = 0; public static int bf_releasebuffer = 0; - public static int name = 0; + public static int ht_name = 0; public static int ht_slots = 0; - public static int qualname = 0; + public static int ht_qualname = 0; public static int ht_cached_keys = 0; /* here are optional user slots, followed by the members. */ diff --git a/src/runtime/interop38.cs b/src/runtime/interop38.cs index 9126bca6a..44cdcec40 100644 --- a/src/runtime/interop38.cs +++ b/src/runtime/interop38.cs @@ -139,9 +139,9 @@ public static int magic() public static int sq_inplace_repeat = 0; public static int bf_getbuffer = 0; public static int bf_releasebuffer = 0; - public static int name = 0; + public static int ht_name = 0; public static int ht_slots = 0; - public static int qualname = 0; + public static int ht_qualname = 0; public static int ht_cached_keys = 0; /* here are optional user slots, followed by the members. */ diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs index f63b1feae..350696ded 100644 --- a/src/runtime/runtime.cs +++ b/src/runtime/runtime.cs @@ -1726,6 +1726,9 @@ internal static IntPtr PyType_GenericAlloc(IntPtr type, long n) return PyType_GenericAlloc(type, new IntPtr(n)); } + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] + internal static extern NewReference PyType_FromSpec(ref TypeManager.PyTypeSpec spec); + [DllImport(_PythonDll, CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr PyType_GenericAlloc(IntPtr type, IntPtr n); diff --git a/src/runtime/typemanager.cs b/src/runtime/typemanager.cs index 04d40a2ba..167c6d541 100644 --- a/src/runtime/typemanager.cs +++ b/src/runtime/typemanager.cs @@ -81,23 +81,24 @@ internal static IntPtr GetTypeHandle(ManagedType obj, Type type) /// internal static IntPtr CreateType(Type impl) { - IntPtr type = AllocateTypeObject(impl.Name); - int ob_size = ObjectOffset.Size(type); + var slotArray = CreateSlotArray(impl); + int flags = TypeFlags.Default | TypeFlags.Managed | + TypeFlags.HeapType | TypeFlags.HaveGC; - // Set tp_basicsize to the size of our managed instance objects. - Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)ob_size); + IntPtr type = CreateTypeObject(impl.Name, ObjectOffset.Size(), flags, slotArray); + + if (ObjectOffset.Size() != ObjectOffset.Size(type)) + { + //should we reset the size and call PyType_Ready again?? + //how do we deal with the fact that size is based on whether + //the type is an exception type. Should CreateSlotArray + //return a tuple with both the slot array and a flag on + //whether the type array describes an exception or not? + } var offset = (IntPtr)ObjectOffset.TypeDictOffset(type); Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset); - InitializeSlots(type, impl); - - int flags = TypeFlags.Default | TypeFlags.Managed | - TypeFlags.HeapType | TypeFlags.HaveGC; - Util.WriteCLong(type, TypeOffset.tp_flags, flags); - - Runtime.PyType_Ready(type); - IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); IntPtr mod = Runtime.PyString_FromString("CLR"); Runtime.PyDict_SetItemString(dict, "__module__", mod); @@ -202,6 +203,17 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) return type; } + static PY_TYPE_SLOT InitializeSlot(TypeSlots slotNumber, MethodInfo method) + { + var thunk = Interop.GetThunk(method); + return new PY_TYPE_SLOT { slot = slotNumber, func = thunk.Address}; + } + + static PY_TYPE_SLOT InitializeSlot(TypeSlots slotNumber, IntPtr thunk) + { + return new PY_TYPE_SLOT { slot = slotNumber, func = thunk }; + } + static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method) { var thunk = Interop.GetThunk(method); @@ -422,6 +434,163 @@ internal static IntPtr BasicSubType(string name, IntPtr base_, Type impl) return type; } + internal enum TypeSlots : int + { + bf_getbuffer = 1, + bf_releasebuffer = 2, + mp_ass_subscript = 3, + mp_length = 4, + mp_subscript = 5, + nb_absolute = 6, + nb_add = 7, + nb_and = 8, + nb_bool = 9, + nb_divmod = 10, + nb_float = 11, + nb_floor_divide = 12, + nb_index = 13, + nb_inplace_add = 14, + nb_inplace_and = 15, + nb_inplace_floor_divide = 16, + nb_inplace_lshift = 17, + nb_inplace_multiply = 18, + nb_inplace_or = 19, + nb_inplace_power = 20, + nb_inplace_remainder = 21, + nb_inplace_rshift = 22, + nb_inplace_subtract = 23, + nb_inplace_true_divide = 24, + nb_inplace_xor = 25, + nb_int = 26, + nb_invert = 27, + nb_lshift = 28, + nb_multiply = 29, + nb_negative = 30, + nb_or = 31, + nb_positive = 32, + nb_power = 33, + nb_remainder = 34, + nb_rshift = 35, + nb_subtract = 36, + nb_true_divide = 37, + nb_xor = 38, + sq_ass_item = 39, + sq_concat = 40, + sq_contains = 41, + sq_inplace_concat = 42, + sq_inplace_repeat = 43, + sq_item = 44, + sq_length = 45, + sq_repeat = 46, + tp_alloc = 47, + tp_base = 48, + tp_bases = 49, + tp_call = 50, + tp_clear = 51, + tp_dealloc = 52, + tp_del = 53, + tp_descr_get = 54, + tp_descr_set = 55, + tp_doc = 56, + tp_getattr = 57, + tp_getattro = 58, + tp_hash = 59, + tp_init = 60, + tp_is_gc = 61, + tp_iter = 62, + tp_iternext = 63, + tp_methods = 64, + tp_new = 65, + tp_repr = 66, + tp_richcompare = 67, + tp_setattr = 68, + tp_setattro = 69, + tp_str = 70, + tp_traverse = 71, + tp_members = 72, + tp_getset = 73, + tp_free = 74, + nb_matrix_multiply = 75, + nb_inplace_matrix_multiply = 76, + am_await = 77, + am_aiter = 78, + am_anext = 79, + tp_finalize = 80, + } + + private static TypeSlots getSlotNumber(string methodName) + { + return (TypeSlots)Enum.Parse(typeof(TypeSlots), methodName); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PY_TYPE_SLOT + { + internal TypeSlots slot; //slot id, from typeslots.h + internal IntPtr func; //function pointer of the function implementing the slot + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PyTypeSpec + { + public IntPtr Name; + public int BasicSize; + public int ItemSize; + public int Flags; + public IntPtr Slots; + } + + internal static IntPtr CreateTypeObject(string name, int obSize, int obFlags, PY_TYPE_SLOT[] type_slots) + { + //convert type slot array to PyType_Slot* + int structSize = Marshal.SizeOf(typeof(PY_TYPE_SLOT)); + GCHandle pinnedArray = GCHandle.Alloc(type_slots, GCHandleType.Pinned); + IntPtr slotsPtr = pinnedArray.AddrOfPinnedObject(); + + //convert name to char* + byte[] ascii = System.Text.Encoding.ASCII.GetBytes(name); + GCHandle pinnedName = GCHandle.Alloc(ascii, GCHandleType.Pinned); + IntPtr namePtr = pinnedName.AddrOfPinnedObject(); + + var typeSpec = new PyTypeSpec + { + Name = namePtr, + BasicSize = obSize, + ItemSize = 0, + Flags = obFlags, + Slots = slotsPtr + }; + + var type = Runtime.PyType_FromSpec(ref typeSpec).DangerousGetAddress(); + pinnedArray.Free(); + pinnedName.Free(); + + // 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.ht_name, temp); + + Marshal.WriteIntPtr(type, TypeOffset.ht_qualname, temp); + + long ptr = type.ToInt64(); // 64-bit safe + + temp = new IntPtr(ptr + TypeOffset.nb_add); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_number, temp); + + temp = new IntPtr(ptr + TypeOffset.sq_length); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_sequence, temp); + + temp = new IntPtr(ptr + TypeOffset.mp_length); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_mapping, temp); + + temp = new IntPtr(ptr + TypeOffset.bf_getbuffer); + Marshal.WriteIntPtr(type, TypeOffset.tp_as_buffer, temp); + + return type; + } /// /// Utility method to allocate a type object & do basic initialization. @@ -436,9 +605,9 @@ internal static IntPtr AllocateTypeObject(string name) 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); + Marshal.WriteIntPtr(type, TypeOffset.ht_name, temp); - Marshal.WriteIntPtr(type, TypeOffset.qualname, temp); + Marshal.WriteIntPtr(type, TypeOffset.ht_qualname, temp); long ptr = type.ToInt64(); // 64-bit safe @@ -680,6 +849,79 @@ internal static void InitializeNativeCodePage() } #endregion + /// + /// Given a managed Type that provides the implementation for the type, + /// create a PY_TYPE_SLOT array to be used for PyType_FromSpec. + /// + internal static PY_TYPE_SLOT[] CreateSlotArray(Type impl) + { + // 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(); + var typeslots = new List(); + + while (impl != null) + { + MethodInfo[] methods = impl.GetMethods(tbFlags); + foreach (MethodInfo method in methods) + { + string name = method.Name; + if (!(name.StartsWith("tp_") || + name.StartsWith("nb_") || + name.StartsWith("sq_") || + name.StartsWith("mp_") || + name.StartsWith("bf_") + )) + { + continue; + } + + if (seen.Contains(name)) + { + continue; + } + + typeslots.Add(InitializeSlot(getSlotNumber(name), method)); + seen.Add(name); + } + + impl = impl.BaseType; + } + + var native = NativeCode.Active; + + // The garbage collection related slots always have to return 1 or 0 + // since .NET objects don't take part in Python's gc: + // tp_traverse (returns 0) + // tp_clear (returns 0) + // tp_is_gc (returns 1) + // These have to be defined, though, so by default we fill these with + // static C# functions from this class. + + var ret0 = Interop.GetThunk(((Func)Return0).Method).Address; + var ret1 = Interop.GetThunk(((Func)Return1).Method).Address; + + if (native != null) + { + // If we want to support domain reload, the C# implementation + // cannot be used as the assembly may get released before + // CPython calls these functions. Instead, for amd64 and x86 we + // load them into a separate code page that is leaked + // intentionally. + InitializeNativeCodePage(); + ret1 = NativeCodePage + native.Return1; + ret0 = NativeCodePage + native.Return0; + } + + typeslots.Add(InitializeSlot(getSlotNumber("tp_traverse"), ret0)); + typeslots.Add(InitializeSlot(getSlotNumber("tp_clear"), ret0)); + typeslots.Add(InitializeSlot(getSlotNumber("tp_is_gc"), ret1)); + + typeslots.Add(new PY_TYPE_SLOT { slot = 0, func = IntPtr.Zero }); + return typeslots.ToArray(); + } + /// /// Given a newly allocated Python type object and a managed Type that /// provides the implementation for the type, connect the type slots of pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy