X Tutup
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; using Python.Runtime.Native; #pragma warning disable CS0618 // Type or member is obsolete. OK for internal use using static Python.Runtime.PythonDerivedType; #pragma warning restore CS0618 // Type or member is obsolete 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 identify which C# types were dynamically created as python subclasses /// public interface IPythonDerivedType { } [Serializable] internal class ClassDerivedObject : ClassObject { private static Dictionary assemblyBuilders; private static Dictionary, ModuleBuilder> moduleBuilders; static ClassDerivedObject() { assemblyBuilders = new Dictionary(); moduleBuilders = new Dictionary, ModuleBuilder>(); } public static void Reset() { assemblyBuilders = new Dictionary(); moduleBuilders = new Dictionary, ModuleBuilder>(); } internal ClassDerivedObject(Type tp) : base(tp) { } protected override NewReference NewObjectToPython(object obj, BorrowedReference tp) { var self = base.NewObjectToPython(obj, tp); SetPyObj((IPythonDerivedType)obj, self.Borrow()); // 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. Runtime.XDecref(self.Steal()); return Converter.ToPython(obj, type.Value); } protected override void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder) { // Python derived types rely on base tp_new and overridden __init__ } public new static void tp_dealloc(NewReference ob) { var self = (CLRObject?)GetManagedObject(ob.Borrow()); // don't let the python GC destroy this object Runtime.PyObject_GC_UnTrack(ob.Borrow()); // self may be null after Shutdown begun if (self is not null) { // 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 oldHandle = GetGCHandle(ob.Borrow()); GCHandle gc = GCHandle.Alloc(self, GCHandleType.Weak); SetGCHandle(ob.Borrow(), gc); oldHandle.Free(); } } /// /// No-op clear. Real cleanup happens in /// public new static int tp_clear(BorrowedReference ob) => 0; /// /// 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 NewReference ToPython(IPythonDerivedType obj) { // derived types have a __pyobj__ field that gets set to the python // object in the overridden constructor BorrowedReference self; try { self = GetPyObj(obj).CheckRun(); } catch (RuntimeShutdownException e) { Exceptions.SetError(e); return default; } var result = new NewReference(self); // 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) == 1) { Runtime._Py_NewReference(self); GCHandle weak = GetGCHandle(self); var clrObject = GetManagedObject(self); GCHandle gc = GCHandle.Alloc(clrObject, GCHandleType.Normal); SetGCHandle(self, gc); weak.Free(); // now the object has a python reference it's safe for the python GC to track it Runtime.PyObject_GC_Track(self); } return result; } /// /// Creates a new managed type derived from a base type with any virtual /// methods overridden to call out to python if the associated python /// object has overridden the method. /// internal static Type CreateDerivedType(string name, Type baseType, IList typeInterfaces, BorrowedReference py_dict, string? namespaceStr, string? assemblyName, string moduleName = "Python.Runtime.Dynamic.dll") { // TODO: clean up if (null != namespaceStr) { name = namespaceStr + "." + name; } if (null == assemblyName) { assemblyName = "Python.Runtime.Dynamic"; } ModuleBuilder moduleBuilder = GetModuleBuilder(assemblyName, moduleName); Type baseClass = baseType; var interfaces = new HashSet { typeof(IPythonDerivedType) }; foreach(var interfaceType in typeInterfaces) interfaces.Add(interfaceType); // 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(object); } TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, baseClass, interfaces.ToArray()); // add a field for storing the python object pointer // FIXME: fb not used FieldBuilder fb = typeBuilder.DefineField(PyObjName, #pragma warning disable CS0618 // Type or member is obsolete. OK for internal use. typeof(UnsafeReferenceWithRun), #pragma warning restore CS0618 // Type or member is obsolete FieldAttributes.Private); // override any constructors ConstructorInfo[] constructors = baseClass.GetConstructors(); foreach (ConstructorInfo ctor in constructors) { AddConstructor(ctor, baseType, typeBuilder); } // Override any properties explicitly overridden in python var pyProperties = new HashSet(); if (py_dict != null && Runtime.PyDict_Check(py_dict)) { using var dict = new PyDict(py_dict); using var keys = dict.Keys(); foreach (PyObject pyKey in keys) { using var 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); } pyKey.Dispose(); } } // override any virtual not already overridden by the properties above // also override any interface method. var methods = baseType.GetMethods().Concat(interfaces.SelectMany(x => x.GetMethods())); var virtualMethods = new HashSet(); foreach (MethodInfo method in methods) { if (!method.Attributes.HasFlag(MethodAttributes.Virtual) | method.Attributes.HasFlag(MethodAttributes.Final) // overriding generic virtual methods is not supported // so a call to that should be deferred to the base class method. || method.IsGenericMethod) { continue; } // skip if this property has already been overridden 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 != null && Runtime.PyDict_Check(py_dict)) { using var dict = new PyDict(py_dict); using var keys = dict.Keys(); foreach (PyObject pyKey in keys) { using var 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); } pyKey.Dispose(); } } // 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); #pragma warning disable CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(PyFinalize))); #pragma warning restore CS0618 // PythonDerivedType is for internal use only 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); // FIXME: assemblyBuilder not used 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 (var 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(object)); il.Emit(OpCodes.Stloc_0); for (var 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); #pragma warning disable CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeCtor))); #pragma warning restore CS0618 // PythonDerivedType is for internal use only 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 overridden /// 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(); // If the method isn't abstract create a method for calling the original method string? baseMethodName = null; if (!method.IsAbstract) { baseMethodName = "_" + baseType.Name + "__" + method.Name; MethodBuilder baseMethodBuilder = 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 baseIl = baseMethodBuilder.GetILGenerator(); baseIl.Emit(OpCodes.Ldarg_0); for (var i = 0; i < parameters.Length; ++i) { baseIl.Emit(OpCodes.Ldarg, i + 1); } baseIl.Emit(OpCodes.Call, method); baseIl.Emit(OpCodes.Ret); } // override the original method with a new one that dispatches to python MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.ReuseSlot | MethodAttributes.Virtual | MethodAttributes.HideBySig, method.CallingConvention, method.ReturnType, parameterTypes); ILGenerator il = methodBuilder.GetILGenerator(); il.DeclareLocal(typeof(object[])); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, method.Name); // don't fall back to the base type's method if it's abstract if (null != baseMethodName) { il.Emit(OpCodes.Ldstr, baseMethodName); } else { il.Emit(OpCodes.Ldnull); } il.Emit(OpCodes.Ldc_I4, parameters.Length); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc_0); for (var i = 0; i < parameters.Length; ++i) { il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); var type = parameterTypes[i]; if (type.IsByRef) { type = type.GetElementType(); il.Emit(OpCodes.Ldobj, type); } if (type.IsValueType) { il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Stelem, typeof(object)); } il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldtoken, method); il.Emit(OpCodes.Ldtoken, method.DeclaringType); #pragma warning disable CS0618 // PythonDerivedType is for internal use only if (method.ReturnType == typeof(void)) { il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); } else { il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(method.ReturnType)); } #pragma warning restore CS0618 // PythonDerivedType is for internal use only CodeGenerator.GenerateMarshalByRefsBack(il, parameterTypes); 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) { const string methodNameAttribute = "_clr_method_name_"; if (func.HasAttr(methodNameAttribute)) { using PyObject pyMethodName = func.GetAttr(methodNameAttribute); methodName = pyMethodName.As() ?? throw new ArgumentNullException(methodNameAttribute); } using var pyReturnType = func.GetAttr("_clr_return_type_"); using var pyArgTypes = func.GetAttr("_clr_arg_types_"); using var pyArgTypesIter = PyIter.GetIter(pyArgTypes); var returnType = pyReturnType.AsManagedObject(typeof(Type)) as Type; if (returnType == null) { returnType = typeof(void); } var argTypes = new List(); foreach (PyObject pyArgType in pyArgTypesIter) { var 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); pyArgType.Dispose(); } // 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.DeclareLocal(typeof(RuntimeMethodHandle)); il.DeclareLocal(typeof(RuntimeTypeHandle)); // this il.Emit(OpCodes.Ldarg_0); // Python method to call il.Emit(OpCodes.Ldstr, methodName); // original method name il.Emit(OpCodes.Ldnull); // don't fall back to the base type's method // create args array il.Emit(OpCodes.Ldc_I4, argTypes.Count); il.Emit(OpCodes.Newarr, typeof(object)); il.Emit(OpCodes.Stloc_0); // fill args array for (var i = 0; i < argTypes.Count; ++i) { il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ldc_I4, i); il.Emit(OpCodes.Ldarg, i + 1); var type = argTypes[i]; if (type.IsByRef) { type = type.GetElementType(); il.Emit(OpCodes.Ldobj, type); } if (type.IsValueType) { il.Emit(OpCodes.Box, type); } il.Emit(OpCodes.Stelem, typeof(object)); } // args array il.Emit(OpCodes.Ldloc_0); // method handle for the base method is null il.Emit(OpCodes.Ldloca_S, 1); il.Emit(OpCodes.Initobj, typeof(RuntimeMethodHandle)); il.Emit(OpCodes.Ldloc_1); // type handle is also not required il.Emit(OpCodes.Ldloca_S, 2); il.Emit(OpCodes.Initobj, typeof(RuntimeTypeHandle)); il.Emit(OpCodes.Ldloc_2); #pragma warning disable CS0618 // PythonDerivedType is for internal use only // invoke the method if (returnType == typeof(void)) { il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethodVoid))); } else { il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod(nameof(InvokeMethod)).MakeGenericMethod(returnType)); } CodeGenerator.GenerateMarshalByRefsBack(il, argTypes); #pragma warning restore CS0618 // PythonDerivedType is for internal use only 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 var pyPropertyType = func.GetAttr("_clr_property_type_"); var 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")) { using var pyfget = func.GetAttr("fget"); if (pyfget.IsTrue()) { MethodBuilder methodBuilder = typeBuilder.DefineMethod("get_" + propertyName, methodAttribs, propertyType, null); ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, propertyName); #pragma warning disable CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeGetProperty").MakeGenericMethod(propertyType)); #pragma warning restore CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Ret); propertyBuilder.SetGetMethod(methodBuilder); } } if (func.HasAttr("fset")) { using var pyset = func.GetAttr("fset"); if (pyset.IsTrue()) { MethodBuilder methodBuilder = typeBuilder.DefineMethod("set_" + propertyName, methodAttribs, null, new[] { propertyType }); ILGenerator il = methodBuilder.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, propertyName); il.Emit(OpCodes.Ldarg_1); #pragma warning disable CS0618 // PythonDerivedType is for internal use only il.Emit(OpCodes.Call, typeof(PythonDerivedType).GetMethod("InvokeSetProperty").MakeGenericMethod(propertyType)); #pragma warning restore CS0618 // PythonDerivedType is for internal use only 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; if (moduleBuilders.ContainsKey(Tuple.Create(assemblyName, moduleName))) { moduleBuilder = moduleBuilders[Tuple.Create(assemblyName, moduleName)]; } else { AssemblyBuilder assemblyBuilder; 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 overridden 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. /// [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete(Util.InternalUseOnly)] public class PythonDerivedType { internal const string PyObjName = "__pyobj__"; internal const BindingFlags PyObjFlags = BindingFlags.Instance | BindingFlags.NonPublic; /// /// This is the implementation of the overridden 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 (i.e. it has been overridden 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, RuntimeMethodHandle methodHandle, RuntimeTypeHandle declaringTypeHandle) { var self = GetPyObj(obj); if (null != self.Ref) { var disposeList = new List(); PyGILState gs = Runtime.PyGILState_Ensure(); try { using var pyself = new PyObject(self.CheckRun()); using PyObject method = pyself.GetAttr(methodName, Runtime.None); if (method.Reference != Runtime.PyNone) { // if the method hasn't been overridden then it will be a managed object ManagedType? managedMethod = ManagedType.GetManagedObject(method.Reference); if (null == managedMethod) { var pyargs = new PyObject[args.Length]; for (var i = 0; i < args.Length; ++i) { pyargs[i] = Converter.ToPythonImplicit(args[i]).MoveToPyObject(); disposeList.Add(pyargs[i]); } PyObject py_result = method.Invoke(pyargs); var clrMethod = methodHandle != default ? MethodBase.GetMethodFromHandle(methodHandle, declaringTypeHandle) : null; PyTuple? result_tuple = MarshalByRefsBack(args, clrMethod, py_result, outsOffset: 1); return result_tuple is not null ? result_tuple[0].As() : py_result.As(); } } } finally { foreach (PyObject x in disposeList) { x?.Dispose(); } Runtime.PyGILState_Release(gs); } } if (origMethodName == null) { throw new NotImplementedException("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, RuntimeMethodHandle methodHandle, RuntimeTypeHandle declaringTypeHandle) { var self = GetPyObj(obj); if (null != self.Ref) { var disposeList = new List(); PyGILState gs = Runtime.PyGILState_Ensure(); try { using var pyself = new PyObject(self.CheckRun()); using PyObject method = pyself.GetAttr(methodName, Runtime.None); if (method.Reference != Runtime.None) { // if the method hasn't been overridden then it will be a managed object ManagedType? managedMethod = ManagedType.GetManagedObject(method); if (null == managedMethod) { var pyargs = new PyObject[args.Length]; for (var i = 0; i < args.Length; ++i) { pyargs[i] = Converter.ToPythonImplicit(args[i]).MoveToPyObject(); disposeList.Add(pyargs[i]); } PyObject py_result = method.Invoke(pyargs); var clrMethod = methodHandle != default ? MethodBase.GetMethodFromHandle(methodHandle, declaringTypeHandle) : null; MarshalByRefsBack(args, clrMethod, py_result, outsOffset: 0); return; } } } finally { foreach (PyObject x in disposeList) { x?.Dispose(); } Runtime.PyGILState_Release(gs); } } if (origMethodName == null) { throw new NotImplementedException($"Python object does not have a '{methodName}' method"); } obj.GetType().InvokeMember(origMethodName, BindingFlags.InvokeMethod, null, obj, args); } /// /// If the method has byref arguments, reinterprets Python return value /// as a tuple of new values for those arguments, and updates corresponding /// elements of array. /// private static PyTuple? MarshalByRefsBack(object?[] args, MethodBase? method, PyObject pyResult, int outsOffset) { if (method is null) return null; var parameters = method.GetParameters(); PyTuple? outs = null; int byrefIndex = 0; for (int i = 0; i < parameters.Length; ++i) { Type type = parameters[i].ParameterType; if (!type.IsByRef) { continue; } type = type.GetElementType(); if (outs is null) { outs = new PyTuple(pyResult); pyResult.Dispose(); } args[i] = outs[byrefIndex + outsOffset].AsManagedObject(type); byrefIndex++; } if (byrefIndex > 0 && outs!.Length() > byrefIndex + outsOffset) throw new ArgumentException("Too many output parameters"); return outs; } public static T? InvokeGetProperty(IPythonDerivedType obj, string propertyName) { var self = GetPyObj(obj); if (null == self.Ref) { throw new NullReferenceException("Instance must be specified when getting a property"); } PyGILState gs = Runtime.PyGILState_Ensure(); try { using var pyself = new PyObject(self.CheckRun()); using var pyvalue = pyself.GetAttr(propertyName); return pyvalue.As(); } finally { Runtime.PyGILState_Release(gs); } } public static void InvokeSetProperty(IPythonDerivedType obj, string propertyName, T value) { var self = GetPyObj(obj); if (null == self.Ref) { throw new NullReferenceException("Instance must be specified when setting a property"); } PyGILState gs = Runtime.PyGILState_Ensure(); try { using var pyself = new PyObject(self.CheckRun()); using var pyvalue = Converter.ToPythonImplicit(value).MoveToPyObject(); pyself.SetAttr(propertyName, pyvalue); } finally { Runtime.PyGILState_Release(gs); } } public static void InvokeCtor(IPythonDerivedType obj, string origCtorName, object[] args) { var selfRef = GetPyObj(obj); if (selfRef.Ref == null) { // this might happen when the object is created from .NET using var _ = Py.GIL(); // In the end we 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. using var self = CLRObject.GetReference(obj, obj.GetType()); SetPyObj(obj, self.Borrow()); } // call the base constructor obj.GetType().InvokeMember(origCtorName, BindingFlags.InvokeMethod, null, obj, args); } public static void PyFinalize(IPythonDerivedType obj) { // the C# object is being destroyed which must mean there are no more // references to the Python object as well var self = GetPyObj(obj); Finalizer.Instance.AddDerivedFinalizedObject(ref self.RawObj, self.Run); } internal static void Finalize(IntPtr derived) { var @ref = NewReference.DangerousFromPointer(derived); ClassBase.tp_clear(@ref.Borrow()); var type = Runtime.PyObject_TYPE(@ref.Borrow()); if (!Runtime.HostedInPython || Runtime.TypeManagerInitialized) { // rare case when it's needed // matches correspdonging PyObject_GC_UnTrack // in ClassDerivedObject.tp_dealloc Runtime.PyObject_GC_Del(@ref.Steal()); // must decref our type Runtime.XDecref(StolenReference.DangerousFromPointer(type.DangerousGetAddress())); } } internal static FieldInfo? GetPyObjField(Type type) => type.GetField(PyObjName, PyObjFlags); internal static UnsafeReferenceWithRun GetPyObj(IPythonDerivedType obj) { FieldInfo fi = GetPyObjField(obj.GetType())!; return (UnsafeReferenceWithRun)fi.GetValue(obj); } internal static void SetPyObj(IPythonDerivedType obj, BorrowedReference pyObj) { FieldInfo fi = GetPyObjField(obj.GetType())!; fi.SetValue(obj, new UnsafeReferenceWithRun(pyObj)); } } }
X Tutup