@@ -198,12 +198,15 @@ internal static Type CreateDerivedType(string name,
198198
199199 // Override any properties explicitly overridden in python
200200 var pyProperties = new HashSet < string > ( ) ;
201+ var dictKeys = new HashSet < string > ( ) ;
201202 if ( py_dict != null && Runtime . PyDict_Check ( py_dict ) )
202203 {
203204 using var dict = new PyDict ( py_dict ) ;
204205 using var keys = dict . Keys ( ) ;
205206 foreach ( PyObject pyKey in keys )
206207 {
208+ var keyString = pyKey . As < string > ( ) ;
209+ dictKeys . Add ( keyString ) ;
207210 using var value = dict [ pyKey ] ;
208211 if ( value . HasAttr ( "_clr_property_type_" ) )
209212 {
@@ -239,11 +242,18 @@ internal static Type CreateDerivedType(string name,
239242 continue ;
240243 }
241244
245+ // if the name of the method is not in the dict keys, then the method is not explicitly
246+ // declared in the python code and we dont need to add it here.
247+ bool isDeclared = dictKeys . Contains ( method . Name ) ;
248+ if ( ! isDeclared )
249+ continue ;
250+
242251 // keep track of the virtual methods redirected to the python instance
243252 virtualMethods . Add ( method . Name ) ;
244253
254+
245255 // override the virtual method to call out to the python method, if there is one.
246- AddVirtualMethod ( method , baseType , typeBuilder ) ;
256+ AddVirtualMethod ( method , baseType , typeBuilder , isDeclared ) ;
247257 }
248258
249259 // Add any additional methods and properties explicitly exposed from Python.
@@ -271,35 +281,46 @@ internal static Type CreateDerivedType(string name,
271281 }
272282 }
273283
274- // add the destructor so the python object created in the constructor gets destroyed
275- MethodBuilder methodBuilder = typeBuilder . DefineMethod ( "Finalize" ,
276- MethodAttributes . Family |
277- MethodAttributes . Virtual |
278- MethodAttributes . HideBySig ,
279- CallingConventions . Standard ,
280- typeof ( void ) ,
281- Type . EmptyTypes ) ;
282- ILGenerator il = methodBuilder . GetILGenerator ( ) ;
283- il . Emit ( OpCodes . Ldarg_0 ) ;
284+
285+ // only add finalizer if it has not allready been added on a base type.
286+ // otherwise PyFinalize will be called multiple times for the same object,
287+ // causing an access violation exception on some platforms.
288+ // to see if this is the case, we can check if the base type is a IPythonDerivedType if so, it already
289+ // has the finalizer.
290+ if ( typeof ( IPythonDerivedType ) . IsAssignableFrom ( baseType ) == false )
291+ {
292+ // add the destructor so the python object created in the constructor gets destroyed
293+ MethodBuilder methodBuilder = typeBuilder . DefineMethod ( "Finalize" ,
294+ MethodAttributes . Family |
295+ MethodAttributes . Virtual |
296+ MethodAttributes . HideBySig ,
297+ CallingConventions . Standard ,
298+ typeof ( void ) ,
299+ Type . EmptyTypes ) ;
300+ ILGenerator il = methodBuilder . GetILGenerator ( ) ;
301+ il . Emit ( OpCodes . Ldarg_0 ) ;
284302#pragma warning disable CS0618 // PythonDerivedType is for internal use only
285- il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( PyFinalize ) ) ) ;
303+ il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( PyFinalize ) ) ) ;
286304#pragma warning restore CS0618 // PythonDerivedType is for internal use only
287305 il . Emit ( OpCodes . Ldarg_0 ) ;
288306 il . Emit ( OpCodes . Call , baseClass . GetMethod ( "Finalize" , BindingFlags . NonPublic | BindingFlags . Instance ) ) ;
289307 il . Emit ( OpCodes . Ret ) ;
308+ il . Emit ( OpCodes . Ldarg_0 ) ;
309+ il . Emit ( OpCodes . Call , baseClass . GetMethod ( "Finalize" , BindingFlags . NonPublic | BindingFlags . Instance ) ) ;
310+ il . Emit ( OpCodes . Ret ) ;
311+ }
290312
291313 Type type = typeBuilder . CreateType ( ) ;
292314
293- // scan the assembly so the newly added class can be imported
315+ // scan the assembly so the newly added class can be im ported
294316 Assembly assembly = Assembly . GetAssembly ( type ) ;
295317 AssemblyManager . ScanAssembly ( assembly ) ;
296318
297- // FIXME: assemblyBuilder not used
298- AssemblyBuilder assemblyBuilder = assemblyBuilders [ assemblyName ] ;
299-
300319 return type ;
301320 }
302321
322+
323+
303324 /// <summary>
304325 /// Add a constructor override that calls the python ctor after calling the base type constructor.
305326 /// </summary>
@@ -368,7 +389,8 @@ private static void AddConstructor(ConstructorInfo ctor, Type baseType, TypeBuil
368389 /// <param name="method">virtual method to be overridden</param>
369390 /// <param name="baseType">Python callable object</param>
370391 /// <param name="typeBuilder">TypeBuilder for the new type the method is to be added to</param>
371- private static void AddVirtualMethod ( MethodInfo method , Type baseType , TypeBuilder typeBuilder )
392+ /// <param name="isDeclared"></param>
393+ private static void AddVirtualMethod ( MethodInfo method , Type baseType , TypeBuilder typeBuilder , bool isDeclared )
372394 {
373395 ParameterInfo [ ] parameters = method . GetParameters ( ) ;
374396 Type [ ] parameterTypes = ( from param in parameters select param . ParameterType ) . ToArray ( ) ;
@@ -720,12 +742,15 @@ public class PythonDerivedType
720742 {
721743 var disposeList = new List < PyObject > ( ) ;
722744 PyGILState gs = Runtime . PyGILState_Ensure ( ) ;
745+
746+
723747 try
724748 {
725749 using var pyself = new PyObject ( self . CheckRun ( ) ) ;
726750 using PyObject method = pyself . GetAttr ( methodName , Runtime . None ) ;
727751 if ( method . Reference != Runtime . PyNone )
728752 {
753+
729754 // if the method hasn't been overridden then it will be a managed object
730755 ManagedType ? managedMethod = ManagedType . GetManagedObject ( method . Reference ) ;
731756 if ( null == managedMethod )
@@ -771,7 +796,8 @@ public class PythonDerivedType
771796 }
772797
773798 public static void InvokeMethodVoid ( IPythonDerivedType obj , string methodName , string origMethodName ,
774- object ? [ ] args , RuntimeMethodHandle methodHandle , RuntimeTypeHandle declaringTypeHandle )
799+ object ? [ ] args , RuntimeMethodHandle methodHandle , RuntimeTypeHandle declaringTypeHandle ,
800+ RuntimeTypeHandle callingTypeHandle , bool isDeclared )
775801 {
776802 var self = GetPyObj ( obj ) ;
777803 if ( null != self . Ref )
0 commit comments