[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/patstew/pythonnet/python3/src/runtime/classderived.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.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Python.Runtime
{

    /// 
    /// Managed class that provides the implementation for reflected types.
    /// Managed classes and value types are represented in Python by actual 
    /// Python type objects. Each of those type objects is associated with 
    /// an instance of ClassObject, which provides its implementation.
    /// 

    // interface used to idenfity which C# types were dynamically created as python subclasses
    public interface IPythonDerivedType
    {
    }

    internal class ClassDerivedObject : ClassObject
    {
        static private Dictionary assemblyBuilders;
        static private Dictionary moduleBuilders;

        static ClassDerivedObject()
        {
            assemblyBuilders = new Dictionary();
            moduleBuilders = new Dictionary();
        }

        internal ClassDerivedObject(Type tp)
            : base(tp)
        {
        }

        /// 
        /// Implements __new__ for derived classes of reflected classes.
        /// 
        new public static IntPtr tp_new(IntPtr tp, IntPtr args, IntPtr kw)
        {
            ClassDerivedObject cls = GetManagedObject(tp) as ClassDerivedObject;

            // call the managed constructor
            Object obj = cls.binder.InvokeRaw(IntPtr.Zero, args, kw);
            if (obj == null)
                return IntPtr.Zero;

            // return the pointer to the python object
            // (this indirectly calls ClassDerivedObject.ToPython)
            return Converter.ToPython(obj, cls.GetType());
        }

        new public static void tp_dealloc(IntPtr ob)
        {
            CLRObject self = (CLRObject)GetManagedObject(ob);

            // don't let the python GC destroy this object
            Runtime.PyObject_GC_UnTrack(self.pyHandle);

            // The python should now have a ref count of 0, but we don't actually want to
            // deallocate the object until the C# object that references it is destroyed.
            // So we don't call PyObject_GC_Del here and instead we set the python
            // reference to a weak reference so that the C# object can be collected.
            GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak);
            Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc);
            self.gcHandle.Free();
            self.gcHandle = gc;
        }

        // Called from Converter.ToPython for types that are python subclasses of managed types.
        // The referenced python object is returned instead of a new wrapper.
        internal static IntPtr ToPython(IPythonDerivedType obj)
        {
            // derived types have a __pyobj__ field that gets set to the python
            // object in the overriden constructor
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);

            Runtime.Incref(self.pyHandle);

            // when the C# constructor creates the python object it starts as a weak
            // reference with a reference count of 0. Now we're passing this object
            // to Python the reference count needs to be incremented and the reference
            // needs to be replaced with a strong reference to stop the C# object being
            // collected while Python still has a reference to it.
            if (Runtime.Refcount(self.pyHandle) == 1)
            {
                GCHandle gc = GCHandle.Alloc(self, GCHandleType.Normal);
                Marshal.WriteIntPtr(self.pyHandle, ObjectOffset.magic(self.tpHandle), (IntPtr)gc);
                self.gcHandle.Free();
                self.gcHandle = gc;

                // now the object has a python reference it's safe for the python GC to track it
                Runtime.PyObject_GC_Track(self.pyHandle);
            }

            return self.pyHandle;
        }

        /// 
        /// Creates a new managed type derived from a base type with any virtual
        /// methods overriden to call out to python if the associated python
        /// object has overriden the method.
        /// 
        internal static Type CreateDerivedType(string name,
                                               Type baseType,
                                               IntPtr py_dict,
                                               string namespaceStr,
                                               string assemblyName,
                                               string moduleName="Python.Runtime.Dynamic.dll")
        {
            if (null != namespaceStr)
                name = namespaceStr + "." + name;

            if (null == assemblyName)
                assemblyName = Assembly.GetExecutingAssembly().FullName;

            ModuleBuilder moduleBuilder = GetModuleBuilder(assemblyName, moduleName);
            TypeBuilder typeBuilder;

            Type baseClass = baseType;
            List interfaces = new List { typeof(IPythonDerivedType) };

            // if the base type is an interface then use System.Object as the base class
            // and add the base type to the list of interfaces this new class will implement.
            if (baseType.IsInterface)
            {
                interfaces.Add(baseType);
                baseClass = typeof(System.Object);
            }

            typeBuilder = moduleBuilder.DefineType(name,
                                                    TypeAttributes.Public | TypeAttributes.Class,
                                                    baseClass,
                                                    interfaces.ToArray());

            // add a field for storing the python object pointer
            FieldBuilder fb = typeBuilder.DefineField("__pyobj__", typeof(CLRObject), FieldAttributes.Public);

            // override any constructors
            ConstructorInfo[] constructors = baseClass.GetConstructors();
            foreach (ConstructorInfo ctor in constructors)
            {
                AddConstructor(ctor, baseType, typeBuilder);
            }

            // Override any properties explicitly overriden in python
            HashSet pyProperties = new HashSet();
            if (py_dict != IntPtr.Zero && Runtime.PyDict_Check(py_dict))
            {
                Runtime.Incref(py_dict);
                using (PyDict dict = new PyDict(py_dict))
                using (PyObject keys = dict.Keys())
                {
                    foreach (PyObject pyKey in keys)
                    {
                        using (PyObject value = dict[pyKey])
                        if (value.HasAttr("_clr_property_type_"))
                        {
                            string propertyName = pyKey.ToString();
                            pyProperties.Add(propertyName);

                            // Add the property to the type
                            AddPythonProperty(propertyName, value, typeBuilder);
                        }
                    }
                }
            }

            // override any virtual methods not already overriden by the properties above
            MethodInfo[] methods = baseType.GetMethods();
            HashSet virtualMethods = new HashSet();
            foreach (MethodInfo method in methods)
            {
                if (!method.Attributes.HasFlag(MethodAttributes.Virtual) | method.Attributes.HasFlag(MethodAttributes.Final))
                    continue;

                // skip if this property has already been overriden
                if ((method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))
                        && pyProperties.Contains(method.Name.Substring(4)))
                    continue;

                // keep track of the virtual methods redirected to the python instance
                virtualMethods.Add(method.Name);

                // override the virtual method to call out to the python method, if there is one.
                AddVirtualMethod(method, baseType, typeBuilder);
            }

            // Add any additional methods and properties explicitly exposed from Python.
            if (py_dict != IntPtr.Zero && Runtime.PyDict_Check(py_dict))
            {
                Runtime.Incref(py_dict);
                using (PyDict dict = new PyDict(py_dict))
                using (PyObject keys = dict.Keys())
                {
                    foreach (PyObject pyKey in keys)
                    {
                        using (PyObject value = dict[pyKey])
                        if (value.HasAttr("_clr_return_type_") && value.HasAttr("_clr_arg_types_"))
                        {
                            string methodName = pyKey.ToString();

                            // if this method has already been redirected to the python method skip it
                            if (virtualMethods.Contains(methodName))
                                continue;

                            // Add the method to the type
                            AddPythonMethod(methodName, value, typeBuilder);
                        }
                    }
                }
            }

            // add the destructor so the python object created in the constructor gets destroyed
            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Finalize",
                                                                    MethodAttributes.Family |
                                                                        MethodAttributes.Virtual |
                                                                        MethodAttributes.HideBySig,
                                                                        CallingConventions.Standard,
                                                                    typeof(void),
                                                                    Type.EmptyTypes);
            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("Finalize"));
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Call, baseClass.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance));
            il.Emit(OpCodes.Ret);

            Type type = typeBuilder.CreateType();

            // scan the assembly so the newly added class can be imported
            Assembly assembly = Assembly.GetAssembly(type);
            AssemblyManager.ScanAssembly(assembly);

            AssemblyBuilder assemblyBuilder = assemblyBuilders[assemblyName];

            return type;
        }

        /// 
        /// Add a constructor override that calls the python ctor after calling the base type constructor.
        /// 
        /// constructor to be called before calling the python ctor
        /// Python callable object
        /// TypeBuilder for the new type the ctor is to be added to
        private static void AddConstructor(ConstructorInfo ctor, Type baseType, TypeBuilder typeBuilder)
        {
            ParameterInfo[] parameters = ctor.GetParameters();
            Type[] parameterTypes = (from param in parameters select param.ParameterType).ToArray();

            // create a method for calling the original constructor
            string baseCtorName = "_" + baseType.Name + "__cinit__";
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(baseCtorName,
                                                                    MethodAttributes.Public |
                                                                        MethodAttributes.Final |
                                                                        MethodAttributes.HideBySig,
                                                                    typeof(void),
                                                                    parameterTypes);

            // emit the assembly for calling the original method using call instead of callvirt
            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            for (int i = 0; i < parameters.Length; ++i)
                il.Emit(OpCodes.Ldarg, i + 1);
            il.Emit(OpCodes.Call, ctor);
            il.Emit(OpCodes.Ret);

            // override the original method with a new one that dispatches to python
            ConstructorBuilder cb = typeBuilder.DefineConstructor(MethodAttributes.Public |
                                                                    MethodAttributes.ReuseSlot |
                                                                    MethodAttributes.HideBySig,
                                                                  ctor.CallingConvention,
                                                                  parameterTypes);
            il = cb.GetILGenerator();
            il.DeclareLocal(typeof(Object[]));
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldstr, baseCtorName);
            il.Emit(OpCodes.Ldc_I4, parameters.Length);
            il.Emit(OpCodes.Newarr, typeof(System.Object));
            il.Emit(OpCodes.Stloc_0);
            for (int i = 0; i < parameters.Length; ++i)
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldarg, i + 1);
                if (parameterTypes[i].IsValueType)
                    il.Emit(OpCodes.Box, parameterTypes[i]);
                il.Emit(OpCodes.Stelem, typeof(Object));
            }
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeCtor"));
            il.Emit(OpCodes.Ret);
        }

        /// 
        /// Add a virtual method override that checks for an override on the python instance
        /// and calls it, otherwise fall back to the base class method.
        /// 
        /// virtual method to be overriden
        /// Python callable object
        /// TypeBuilder for the new type the method is to be added to
        private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuilder typeBuilder)
        {

            ParameterInfo[] parameters = method.GetParameters();
            Type[] parameterTypes = (from param in parameters select param.ParameterType).ToArray();

            // create a method for calling the original method
            string baseMethodName = "_" + baseType.Name + "__" + method.Name;
            MethodBuilder methodBuilder = typeBuilder.DefineMethod(baseMethodName,
                                                                    MethodAttributes.Public |
                                                                        MethodAttributes.Final |
                                                                        MethodAttributes.HideBySig,
                                                                    method.ReturnType,
                                                                    parameterTypes);

            // emit the assembly for calling the original method using call instead of callvirt
            ILGenerator il = methodBuilder.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            for (int i = 0; i < parameters.Length; ++i)
                il.Emit(OpCodes.Ldarg, i + 1);
            il.Emit(OpCodes.Call, method);
            il.Emit(OpCodes.Ret);

            // override the original method with a new one that dispatches to python
            methodBuilder = typeBuilder.DefineMethod(method.Name,
                                                        MethodAttributes.Public |
                                                            MethodAttributes.ReuseSlot |
                                                            MethodAttributes.Virtual |
                                                            MethodAttributes.HideBySig,
                                                        method.CallingConvention,
                                                        method.ReturnType,
                                                        parameterTypes);
            il = methodBuilder.GetILGenerator();
            il.DeclareLocal(typeof(Object[]));
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldstr, method.Name);
            il.Emit(OpCodes.Ldstr, baseMethodName);
            il.Emit(OpCodes.Ldc_I4, parameters.Length);
            il.Emit(OpCodes.Newarr, typeof(System.Object));
            il.Emit(OpCodes.Stloc_0);
            for (int i = 0; i < parameters.Length; ++i)
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldc_I4, i);
                il.Emit(OpCodes.Ldarg, i + 1);
                if (parameterTypes[i].IsValueType)
                    il.Emit(OpCodes.Box, parameterTypes[i]);
                il.Emit(OpCodes.Stelem, typeof(Object));
            }
            il.Emit(OpCodes.Ldloc_0);
            if (method.ReturnType == typeof(void))
            {
                il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid"));
            }
            else
            {
                il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(method.ReturnType));
            }
            il.Emit(OpCodes.Ret);
        }

        /// 
        /// Python method may have the following function attributes set to control how they're exposed:
        ///   - _clr_return_type_    - method return type (required)
        ///   - _clr_arg_types_      - list of method argument types (required)
        ///   - _clr_method_name_    - method name, if different from the python method name (optional)
        /// 
        /// Method name to add to the type
        /// Python callable object
        /// TypeBuilder for the new type the method/property is to be added to
        private static void AddPythonMethod(string methodName, PyObject func, TypeBuilder typeBuilder)
        {
            if (func.HasAttr("_clr_method_name_"))
                methodName = func.GetAttr("_clr_method_name_").ToString();

            using (PyObject pyReturnType = func.GetAttr("_clr_return_type_"))
            using (PyObject pyArgTypes = func.GetAttr("_clr_arg_types_"))
            {
                Type returnType = pyReturnType.AsManagedObject(typeof(Type)) as Type;
                if (returnType == null)
                    returnType = typeof(void);

                if (!pyArgTypes.IsIterable())
                    throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types");

                List argTypes = new List();
                foreach (PyObject pyArgType in pyArgTypes)
                {
                    Type argType = pyArgType.AsManagedObject(typeof(Type)) as Type;
                    if (argType == null)
                        throw new ArgumentException("_clr_arg_types_ must be a list or tuple of CLR types");
                    argTypes.Add(argType);
                }

                // add the method to call back into python
                MethodAttributes methodAttribs = MethodAttributes.Public |
                                                    MethodAttributes.Virtual |
                                                    MethodAttributes.ReuseSlot |
                                                    MethodAttributes.HideBySig;

                MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodName,
                                                                        methodAttribs,
                                                                        returnType,
                                                                        argTypes.ToArray());

                ILGenerator il = methodBuilder.GetILGenerator();
                il.DeclareLocal(typeof(Object[]));
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldstr, methodName);
                il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method
                il.Emit(OpCodes.Ldc_I4, argTypes.Count);
                il.Emit(OpCodes.Newarr, typeof(System.Object));
                il.Emit(OpCodes.Stloc_0);
                for (int i = 0; i < argTypes.Count; ++i)
                {
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ldc_I4, i);
                    il.Emit(OpCodes.Ldarg, i + 1);
                    if (argTypes[i].IsValueType)
                        il.Emit(OpCodes.Box, argTypes[i]);
                    il.Emit(OpCodes.Stelem, typeof(Object));
                }
                il.Emit(OpCodes.Ldloc_0);
                if (returnType == typeof(void))
                {
                    il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethodVoid"));
                }
                else
                {
                    il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeMethod").MakeGenericMethod(returnType));
                }
                il.Emit(OpCodes.Ret);
            }
        }

        /// 
        /// Python properties may have the following function attributes set to control how they're exposed:
        ///   - _clr_property_type_     - property type (required)
        /// 
        /// Property name to add to the type
        /// Python property object
        /// TypeBuilder for the new type the method/property is to be added to
        private static void AddPythonProperty(string propertyName, PyObject func, TypeBuilder typeBuilder)
        {
            // add the method to call back into python
            MethodAttributes methodAttribs = MethodAttributes.Public |
                                                MethodAttributes.Virtual |
                                                MethodAttributes.ReuseSlot |
                                                MethodAttributes.HideBySig |
                                                MethodAttributes.SpecialName;

            using (PyObject pyPropertyType = func.GetAttr("_clr_property_type_"))
            {
                Type propertyType = pyPropertyType.AsManagedObject(typeof(Type)) as Type;
                if (propertyType == null)
                    throw new ArgumentException("_clr_property_type must be a CLR type");

                PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName,
                                                                             PropertyAttributes.None,
                                                                             propertyType,
                                                                             null);

                if (func.HasAttr("fget") && func.GetAttr("fget").IsTrue())
                {
                    MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_" + propertyName,
                                                                            methodAttribs,
                                                                            propertyType,
                                                                            null);

                    ILGenerator il = methodBuilder.GetILGenerator();
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldstr, propertyName);
                    il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeGetProperty").MakeGenericMethod(propertyType));
                    il.Emit(OpCodes.Ret);

                    propertyBuilder.SetGetMethod(methodBuilder);
                }

                if (func.HasAttr("fset") && func.GetAttr("fset").IsTrue())
                {
                    MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName,
                                                                            methodAttribs,
                                                                            null,
                                                                            new Type[]{propertyType});

                    ILGenerator il = methodBuilder.GetILGenerator();
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldstr, propertyName);
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeSetProperty").MakeGenericMethod(propertyType));
                    il.Emit(OpCodes.Ret);

                    propertyBuilder.SetSetMethod(methodBuilder);
                }
            }
        }

        private static ModuleBuilder GetModuleBuilder(string assemblyName, string moduleName)
        {
            // find or create a dynamic assembly and module
            AppDomain domain = AppDomain.CurrentDomain;
            ModuleBuilder moduleBuilder = null;

            if (moduleBuilders.ContainsKey(Tuple.Create(assemblyName, moduleName)))
            {
                moduleBuilder = moduleBuilders[Tuple.Create(assemblyName, moduleName)];
            }
            else
            {
                AssemblyBuilder assemblyBuilder = null;
                if (assemblyBuilders.ContainsKey(assemblyName))
                {
                    assemblyBuilder = assemblyBuilders[assemblyName];
                }
                else
                {
                    assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName),
                                                                   AssemblyBuilderAccess.Run);
                    assemblyBuilders[assemblyName] = assemblyBuilder;
                }

                moduleBuilder = assemblyBuilder.DefineDynamicModule(moduleName);
                moduleBuilders[Tuple.Create(assemblyName, moduleName)] = moduleBuilder;
            }

            return moduleBuilder;
        }
    }

    //
    // PythonDerivedType contains static methods used by the dynamically created
    // derived type that allow it to call back into python from overriden virtual
    // methods, and also handle the construction and destruction of the python
    // object.
    //
    // This has to be public as it's called from methods on dynamically built classes
    // potentially in other assemblies.
    //
    public class PythonDerivedType
    {
        //====================================================================
        // This is the implementaion of the overriden methods in the derived
        // type. It looks for a python method with the same name as the method
        // on the managed base class and if it exists and isn't the managed
        // method binding (ie it has been overriden in the derived python
        // class) it calls it, otherwise it calls the base method.
        //====================================================================
        public static T InvokeMethod(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args)
        {
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);

            if (null != self)
            {
                List disposeList = new List();
                IntPtr gs = Runtime.PyGILState_Ensure();
                try
                {
                    Runtime.Incref(self.pyHandle);
                    PyObject pyself = new PyObject(self.pyHandle);
                    disposeList.Add(pyself);

                    Runtime.Incref(Runtime.PyNone);
                    PyObject pynone = new PyObject(Runtime.PyNone);
                    disposeList.Add(pynone);

                    PyObject method = pyself.GetAttr(methodName, pynone);
                    disposeList.Add(method);
                    if (method.Handle != Runtime.PyNone)
                    {
                        // if the method hasn't been overriden then it will be a managed object
                        ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle);
                        if (null == managedMethod)
                        {
                            PyObject[] pyargs = new PyObject[args.Length];
                            for (int i = 0; i < args.Length; ++i)
                            {
                                pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i]));
                                disposeList.Add(pyargs[i]);
                            }

                            PyObject py_result = method.Invoke(pyargs);
                            disposeList.Add(py_result);
                            return (T)py_result.AsManagedObject(typeof(T));
                        }
                    }
                }
                finally
                {
                    foreach (PyObject x in disposeList) {
                        if (x != null)
                            x.Dispose();
                    }
                    Runtime.PyGILState_Release(gs);
                }
            }

            if (origMethodName == null)
                throw new NullReferenceException("Python object does not have a '" + methodName + "' method");

            return (T)obj.GetType().InvokeMember(origMethodName,
                                                 BindingFlags.InvokeMethod,
                                                 null,
                                                 obj,
                                                 args);
        }

        public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, string origMethodName, Object[] args)
        {
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);
            if (null != self)
            {
                List disposeList = new List();
                IntPtr gs = Runtime.PyGILState_Ensure();
                try
                {
                    Runtime.Incref(self.pyHandle);
                    PyObject pyself = new PyObject(self.pyHandle);
                    disposeList.Add(pyself);

                    Runtime.Incref(Runtime.PyNone);
                    PyObject pynone = new PyObject(Runtime.PyNone);
                    disposeList.Add(pynone);

                    PyObject method = pyself.GetAttr(methodName, pynone);
                    disposeList.Add(method);
                    if (method.Handle != Runtime.PyNone)
                    {
                        // if the method hasn't been overriden then it will be a managed object
                        ManagedType managedMethod = ManagedType.GetManagedObject(method.Handle);
                        if (null == managedMethod)
                        {
                            PyObject[] pyargs = new PyObject[args.Length];
                            for (int i = 0; i < args.Length; ++i)
                            {
                                pyargs[i] = new PyObject(Converter.ToPythonImplicit(args[i]));
                                disposeList.Add(pyargs[i]);
                            }

                            PyObject py_result = method.Invoke(pyargs);
                            disposeList.Add(py_result);
                            return;
                        }
                    }
                }
                finally
                {
                    foreach (PyObject x in disposeList) {
                        if (x != null)
                            x.Dispose();
                    }
                    Runtime.PyGILState_Release(gs);
                }
            }

            if (origMethodName == null)
                throw new NullReferenceException("Python object does not have a '" + methodName + "' method");

            obj.GetType().InvokeMember(origMethodName,
                                       BindingFlags.InvokeMethod,
                                       null,
                                       obj,
                                       args);
        }

        public static T InvokeGetProperty(IPythonDerivedType obj, string propertyName)
        {
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);

            if (null == self)
                throw new NullReferenceException("Instance must be specified when getting a property");

            IntPtr gs = Runtime.PyGILState_Ensure();
            try
            {
                Runtime.Incref(self.pyHandle);
                using (PyObject pyself = new PyObject(self.pyHandle))
                using (PyObject pyvalue = pyself.GetAttr(propertyName))
                    return (T)pyvalue.AsManagedObject(typeof(T));
            }
            finally
            {
                Runtime.PyGILState_Release(gs);
            }
        }

        public static void InvokeSetProperty(IPythonDerivedType obj, string propertyName, T value)
        {
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);

            if (null == self)
                throw new NullReferenceException("Instance must be specified when setting a property");

            IntPtr gs = Runtime.PyGILState_Ensure();
            try
            {
                Runtime.Incref(self.pyHandle);
                using (PyObject pyself = new PyObject(self.pyHandle))
                using (PyObject pyvalue = new PyObject(Converter.ToPythonImplicit(value)))
                    pyself.SetAttr(propertyName, pyvalue);
            }
            finally
            {
                Runtime.PyGILState_Release(gs);
            }
        }

        public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, Object[] args)
        {
            // call the base constructor
            obj.GetType().InvokeMember(origCtorName,
                                       BindingFlags.InvokeMethod,
                                       null,
                                       obj,
                                       args);

            List disposeList = new List();
            CLRObject self = null;
            IntPtr gs = Runtime.PyGILState_Ensure();
            try
            {
                // create the python object
                IntPtr type = TypeManager.GetTypeHandle(obj.GetType());
                self = new CLRObject(obj, type);

                // set __pyobj__ to self and deref the python object which will allow this
                // object to be collected.
                FieldInfo fi = obj.GetType().GetField("__pyobj__");
                fi.SetValue(obj, self);

                Runtime.Incref(self.pyHandle);
                PyObject pyself = new PyObject(self.pyHandle);
                disposeList.Add(pyself);

                Runtime.Incref(Runtime.PyNone);
                PyObject pynone = new PyObject(Runtime.PyNone);
                disposeList.Add(pynone);

                // call __init__
                PyObject init = pyself.GetAttr("__init__", pynone);
                disposeList.Add(init);
                if (init.Handle != Runtime.PyNone)
                {
                    // if __init__ hasn't been overriden then it will be a managed object
                    ManagedType managedMethod = ManagedType.GetManagedObject(init.Handle);
                    if (null == managedMethod)
                    {
                        PyObject[] pyargs = new PyObject[args.Length];
                        for (int i = 0; i < args.Length; ++i)
                        {
                            pyargs[i] = new PyObject(Converter.ToPython(args[i], args[i].GetType()));
                            disposeList.Add(pyargs[i]);
                        }

                        disposeList.Add(init.Invoke(pyargs));
                    }
                }
            }
            finally
            {
                foreach (PyObject x in disposeList) {
                    if (x != null)
                        x.Dispose();
                }

                // Decrement the python object's reference count.
                // This doesn't actually destroy the object, it just sets the reference to this object
                // to be a weak reference and it will be destroyed when the C# object is destroyed.
                if (null != self)
                    Runtime.Decref(self.pyHandle);

                Runtime.PyGILState_Release(gs);
            }
        }

        public static void Finalize(IPythonDerivedType obj)
        {
            FieldInfo fi = obj.GetType().GetField("__pyobj__");
            CLRObject self = (CLRObject)fi.GetValue(obj);

            // If python's been terminated then just free the gchandle.
            lock (Runtime.IsFinalizingLock)
            {
                if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing)
                {
                    self.gcHandle.Free();
                    return;
                }
            }

            // delete the python object in an asnyc task as we may not be able to acquire
            // the GIL immediately and we don't want to block the GC thread.
            var t = Task.Factory.StartNew(() =>
            {
                lock (Runtime.IsFinalizingLock)
                {
                    // If python's been terminated then just free the gchandle.
                    if (0 == Runtime.Py_IsInitialized() || Runtime.IsFinalizing)
                    {
                        self.gcHandle.Free();
                        return;
                    }

                    IntPtr gs = Runtime.PyGILState_Ensure();
                    try
                    {
                        // the C# object is being destroyed which must mean there are no more
                        // references to the Python object as well so now we can dealloc the
                        // python object.
                        IntPtr dict = Marshal.ReadIntPtr(self.pyHandle, ObjectOffset.DictOffset(self.pyHandle));
                        if (dict != IntPtr.Zero)
                            Runtime.Decref(dict);
                        Runtime.PyObject_GC_Del(self.pyHandle);
                        self.gcHandle.Free();
                    }
                    finally
                    {
                        Runtime.PyGILState_Release(gs);
                    }
                }
            });
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page