internal IntPtr tpHandle; // PyType * /// <summary> /// Given a Python object, return the associated managed object or null. /// </summary> internal static ManagedType GetManagedObject(IntPtr ob) { if (ob != IntPtr.Zero) { IntPtr tp = Runtime.PyObject_TYPE(ob); if (tp == Runtime.PyTypeType || tp == Runtime.PyCLRMetaType) { tp = ob; } var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Managed) != 0) { IntPtr op = tp == ob ? Marshal.ReadIntPtr(tp, TypeOffset.magic()) : Marshal.ReadIntPtr(ob, ObjectOffset.magic(ob)); if (op == IntPtr.Zero) { return(null); } var gc = (GCHandle)op; return((ManagedType)gc.Target); } } return(null); }
/// <summary> /// Dealloc implementation. This is called when a Python type generated /// by this metatype is no longer referenced from the Python runtime. /// </summary> public static void tp_dealloc(IntPtr tp) { // Fix this when we dont cheat on the handle for subclasses! var flags = Util.ReadCLong(tp, TypeOffset.tp_flags); if ((flags & TypeFlags.Subclass) == 0) { IntPtr gc = Marshal.ReadIntPtr(tp, TypeOffset.magic()); ((GCHandle)gc).Free(); } IntPtr op = Marshal.ReadIntPtr(tp, TypeOffset.ob_type); Runtime.XDecref(op); // Delegate the rest of finalization the Python metatype. Note // that the PyType_Type implementation of tp_dealloc will call // tp_free on the type of the type being deallocated - in this // case our CLR metatype. That is why we implement tp_free. op = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_dealloc); NativeCall.Void_Call_1(op, tp); }
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); int tp_dictoffset = ObjectOffset.DictOffset(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); tp_dictoffset = ObjectOffset.DictOffset(Exceptions.Exception); } 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); InitializeSlots(type, impl.GetType()); if (base_ != IntPtr.Zero) { Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_); Runtime.XIncref(base_); } int flags = TypeFlags.Default; flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.BaseType; flags |= 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. Runtime.PyType_Ready(type); IntPtr dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict); string mn = clrType.Namespace ?? ""; IntPtr mod = Runtime.PyString_FromString(mn); Runtime.PyDict_SetItemString(dict, "__module__", mod); // Hide the gchandle of the implementation in a magic type slot. GCHandle gc = GCHandle.Alloc(impl); Marshal.WriteIntPtr(type, TypeOffset.magic(), (IntPtr)gc); // Set the handle attributes on the implementing instance. impl.tpHandle = Runtime.PyCLRMetaType; impl.gcHandle = gc; impl.pyHandle = type; //DebugUtil.DumpType(type); return(type); }
/// <summary> /// Metatype __new__ implementation. This is called to create a new /// class / type when a reflected class is subclassed. /// </summary> public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw) { var len = Runtime.PyTuple_Size(args); if (len < 3) { return(Exceptions.RaiseTypeError("invalid argument list")); } IntPtr name = Runtime.PyTuple_GetItem(args, 0); IntPtr bases = Runtime.PyTuple_GetItem(args, 1); IntPtr dict = Runtime.PyTuple_GetItem(args, 2); // We do not support multiple inheritance, so the bases argument // should be a 1-item tuple containing the type we are subtyping. // That type must itself have a managed implementation. We check // that by making sure its metatype is the CLR metatype. if (Runtime.PyTuple_Size(bases) != 1) { return(Exceptions.RaiseTypeError("cannot use multiple inheritance with managed classes")); } IntPtr base_type = Runtime.PyTuple_GetItem(bases, 0); IntPtr mt = Runtime.PyObject_TYPE(base_type); if (!(mt == PyCLRMetaType || mt == Runtime.PyTypeType)) { return(Exceptions.RaiseTypeError("invalid metatype")); } // Ensure that the reflected type is appropriate for subclassing, // disallowing subclassing of delegates, enums and array types. var cb = GetManagedObject(base_type) as ClassBase; if (cb != null) { if (!cb.CanSubclass()) { return(Exceptions.RaiseTypeError("delegates, enums and array types cannot be subclassed")); } } IntPtr slots = Runtime.PyDict_GetItemString(dict, "__slots__"); if (slots != IntPtr.Zero) { return(Exceptions.RaiseTypeError("subclasses of managed classes do not support __slots__")); } // If __assembly__ or __namespace__ are in the class dictionary then create // a managed sub type. // This creates a new managed type that can be used from .net to call back // into python. if (IntPtr.Zero != dict) { Runtime.XIncref(dict); using (var clsDict = new PyDict(dict)) { if (clsDict.HasKey("__assembly__") || clsDict.HasKey("__namespace__")) { return(TypeManager.CreateSubType(name, base_type, dict)); } } } // otherwise just create a basic type without reflecting back into the managed side. IntPtr func = Marshal.ReadIntPtr(Runtime.PyTypeType, TypeOffset.tp_new); IntPtr type = NativeCall.Call_3(func, tp, args, kw); if (type == IntPtr.Zero) { return(IntPtr.Zero); } int flags = TypeFlags.Default; flags |= TypeFlags.Managed; flags |= TypeFlags.HeapType; flags |= TypeFlags.BaseType; flags |= TypeFlags.Subclass; flags |= TypeFlags.HaveGC; Util.WriteCLong(type, TypeOffset.tp_flags, flags); TypeManager.CopySlot(base_type, type, TypeOffset.tp_dealloc); // Hmm - the standard subtype_traverse, clear look at ob_size to // do things, so to allow gc to work correctly we need to move // our hidden handle out of ob_size. Then, in theory we can // comment this out and still not crash. TypeManager.CopySlot(base_type, type, TypeOffset.tp_traverse); TypeManager.CopySlot(base_type, type, TypeOffset.tp_clear); // for now, move up hidden handle... IntPtr gc = Marshal.ReadIntPtr(base_type, TypeOffset.magic()); Marshal.WriteIntPtr(type, TypeOffset.magic(), gc); return(type); }