[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/chid/pythonnet/develop/pythonnet/src/runtime/typemanager.cs [Back]  [Original]

// ==========================================================================
// This software is subject to the provisions of the Zope Public License,
// Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
// FOR A PARTICULAR PURPOSE.
// ==========================================================================

using System;
using System.Runtime.InteropServices;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Collections;
using System.Reflection;
using System.Threading;

namespace Python.Runtime {

    //=======================================================================
    // The TypeManager class is responsible for building binary-compatible
    // Python type objects that are implemented in managed code.
    //=======================================================================

    internal class TypeManager {

        static BindingFlags tbFlags;
        static Dictionary cache;
        static int obSize;

        static TypeManager() {
            tbFlags = BindingFlags.Public | BindingFlags.Static;
            obSize = 5 * IntPtr.Size;
            cache = new Dictionary(128);
        }


        //====================================================================
        // 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 = IntPtr.Zero;
            cache.TryGetValue(type, out handle);
            if (handle != IntPtr.Zero) {
                return handle;
            }
            handle = CreateType(type);
            cache[type] = handle;
            return handle;
        }


        //====================================================================
        // 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 = IntPtr.Zero;
            cache.TryGetValue(type, out handle);
            if (handle != IntPtr.Zero) {
                return handle;
            }
            handle = CreateType(obj, type);
            cache[type] = handle;
            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);

            // Set tp_basicsize to the size of our managed instance objects.
            Marshal.WriteIntPtr(type, TypeOffset.tp_basicsize, (IntPtr)obSize);

            IntPtr offset = (IntPtr)ObjectOffset.ob_dict;
            Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, offset);

            InitializeSlots(type, impl);

            int flags = TypeFlags.Default | TypeFlags.Managed |
                        TypeFlags.HeapType | TypeFlags.HaveGC;
            Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)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);

            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;
            // 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 (PYTHON25 || PYTHON26 || PYTHON27)
            if (clrType == typeof(System.Exception)) {
                base_ = Exceptions.Exception;
                Runtime.Incref(base_);
            } else
#endif
            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.Incref(Runtime.PyCLRMetaType);

            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);

            InitializeSlots(type, impl.GetType());

            if (base_ != IntPtr.Zero) {
                Marshal.WriteIntPtr(type, TypeOffset.tp_base, base_);
                Runtime.Incref(base_);
            }

            int flags = TypeFlags.Default;
            flags |= TypeFlags.Managed;
            flags |= TypeFlags.HeapType;
            flags |= TypeFlags.BaseType;
            flags |= TypeFlags.HaveGC;
            Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)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 != null ? 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;
        }

        internal static IntPtr CreateSubType(IntPtr args) {

            IntPtr py_name = Runtime.PyTuple_GetItem(args, 0);
            IntPtr bases = Runtime.PyTuple_GetItem(args, 1);
            IntPtr dict = Runtime.PyTuple_GetItem(args, 2);
            IntPtr base_ = Runtime.PyTuple_GetItem(bases, 0);

            string name = Runtime.GetManagedString(py_name);
            IntPtr type = AllocateTypeObject(name);

            Marshal.WriteIntPtr(type,TypeOffset.ob_type,Runtime.PyCLRMetaType);
            Runtime.Incref(Runtime.PyCLRMetaType);

            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.Incref(base_);

            int flags = TypeFlags.Default;
            flags |= TypeFlags.Managed;
            flags |= TypeFlags.HeapType;
            flags |= TypeFlags.BaseType;
            flags |= TypeFlags.Subclass;
            flags |= TypeFlags.HaveGC;
            Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags);

            CopySlot(base_, type, TypeOffset.tp_traverse);
            CopySlot(base_, type, TypeOffset.tp_clear);
            CopySlot(base_, type, TypeOffset.tp_is_gc);

            Runtime.PyType_Ready(type);


            IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
            IntPtr mod = Runtime.PyString_FromString("CLR");
            Runtime.PyDict_SetItemString(tp_dict, "__module__", mod);

            // for now, move up hidden handle...
            IntPtr gc = Marshal.ReadIntPtr(base_, TypeOffset.magic());
            Marshal.WriteIntPtr(type, TypeOffset.magic(), gc);

            return type;
        }


        internal static IntPtr CreateMetaType(Type impl) {

            // 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.Incref(py_type);

            // Copy gc and other type slots from the base Python metatype.

            CopySlot(py_type, type, TypeOffset.tp_basicsize);
            CopySlot(py_type, type, TypeOffset.tp_itemsize);

            CopySlot(py_type, type, TypeOffset.tp_dictoffset);
            CopySlot(py_type, type, TypeOffset.tp_weaklistoffset);

            CopySlot(py_type, type, TypeOffset.tp_traverse);
            CopySlot(py_type, type, TypeOffset.tp_clear);
            CopySlot(py_type, type, TypeOffset.tp_is_gc);

            // Override type slots with those of the managed implementation.

            InitializeSlots(type, impl);

            int flags = TypeFlags.Default;
            flags |= TypeFlags.Managed;
            flags |= TypeFlags.HeapType;
            flags |= TypeFlags.HaveGC;
            Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)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);

            //DebugUtil.DumpType(type);

            return type;
        }


        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.Incref(base_);

            int flags = TypeFlags.Default;
            flags |= TypeFlags.Managed;
            flags |= TypeFlags.HeapType;
            flags |= TypeFlags.HaveGC;
            Marshal.WriteIntPtr(type, TypeOffset.tp_flags, (IntPtr)flags);

            CopySlot(base_, type, TypeOffset.tp_traverse);
            CopySlot(base_, type, TypeOffset.tp_clear);
            CopySlot(base_, type, TypeOffset.tp_is_gc);

            InitializeSlots(type, impl);

            Runtime.PyType_Ready(type);

            IntPtr tp_dict = Marshal.ReadIntPtr(type, TypeOffset.tp_dict);
            IntPtr mod = Runtime.PyString_FromString("CLR");
            Runtime.PyDict_SetItemString(tp_dict, "__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);

            // 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.PyString_FromString(name);
            IntPtr raw = Runtime.PyString_AS_STRING(temp);
            Marshal.WriteIntPtr(type, TypeOffset.tp_name, raw);
            Marshal.WriteIntPtr(type, TypeOffset.name, 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_getreadbuffer);
            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) {
            Hashtable seen = new Hashtable(8);
            Type offsetType = typeof(TypeOffset);

            while (impl != null) {
                MethodInfo[] methods = impl.GetMethods(tbFlags);
                for (int i = 0; i < methods.Length; i++) {
                    MethodInfo method = methods[i];
                    string name = method.Name;
                    if (! (name.StartsWith("tp_") ||
                           name.StartsWith("nb_") ||
                           name.StartsWith("sq_") ||
                           name.StartsWith("mp_") ||
                           name.StartsWith("bf_")
                           ) ) {
                        continue;
                    }

                    if (seen[name] != null) {
                        continue;
                    }
                    
                    FieldInfo fi = offsetType.GetField(name);
                    int offset = (int)fi.GetValue(offsetType);

                    IntPtr slot = Interop.GetThunk(method);
                    Marshal.WriteIntPtr(type, offset, slot);

                    seen[name] = 1;
                }

                impl = impl.BaseType;
            }

        }


        //====================================================================
        // 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;

            while (type != null) {
                MethodInfo[] methods = type.GetMethods(flags);
                for (int i = 0; i < methods.Length; i++) {
                    MethodInfo method = methods[i];
                    object[] attrs = method.GetCustomAttributes(marker, false);
                    if (attrs.Length > 0) {
                        string method_name = method.Name;
                        MethodInfo[] mi = new MethodInfo[1];
                        mi[0] = method;
                        MethodObject m = new TypeMethod(method_name, mi);
                        Runtime.PyDict_SetItemString(dict, method_name,
                                                     m.pyHandle);
                    }
                }
                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);
        }


    }


}

Web Proxy Viewer  |  New URL  |  Original Page