[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/chid/pythonnet/master/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;
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 Hashtable cache;
	static int obSize;

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


	//====================================================================
	// 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. We

	    Object ob = cache[type];
	    if (ob != null) {
		return (IntPtr) ob;
	    }
	    IntPtr tp = CreateType(type);
	    cache[type] = tp;
	    return tp;
	}


	//====================================================================
	// 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 clrType) {
	    Object ob = cache[clrType];
	    if (ob != null) {
		return (IntPtr) ob;
	    }
	    IntPtr tp = CreateType(obj, clrType);
	    cache[clrType] = tp;
	    return tp;
	}


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