@@ -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,43 @@ 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
287- il . Emit ( OpCodes . Ldarg_0 ) ;
288- il . Emit ( OpCodes . Call , baseClass . GetMethod ( "Finalize" , BindingFlags . NonPublic | BindingFlags . Instance ) ) ;
289- il . Emit ( OpCodes . Ret ) ;
305+ il . Emit ( OpCodes . Ldarg_0 ) ;
306+ il . Emit ( OpCodes . Call , baseClass . GetMethod ( "Finalize" , BindingFlags . NonPublic | BindingFlags . Instance ) ) ;
307+ il . Emit ( OpCodes . Ret ) ;
308+ }
290309
291310 Type type = typeBuilder . CreateType ( ) ;
292311
293- // scan the assembly so the newly added class can be imported
312+ // scan the assembly so the newly added class can be imported.
294313 Assembly assembly = Assembly . GetAssembly ( type ) ;
295314 AssemblyManager . ScanAssembly ( assembly ) ;
296315
297- // FIXME: assemblyBuilder not used
298- AssemblyBuilder assemblyBuilder = assemblyBuilders [ assemblyName ] ;
299-
300316 return type ;
301317 }
302318
319+
320+
303321 /// <summary>
304322 /// Add a constructor override that calls the python ctor after calling the base type constructor.
305323 /// </summary>
@@ -368,7 +386,8 @@ private static void AddConstructor(ConstructorInfo ctor, Type baseType, TypeBuil
368386 /// <param name="method">virtual method to be overridden</param>
369387 /// <param name="baseType">Python callable object</param>
370388 /// <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 )
389+ /// <param name="isDeclared"></param>
390+ private static void AddVirtualMethod ( MethodInfo method , Type baseType , TypeBuilder typeBuilder , bool isDeclared )
372391 {
373392 ParameterInfo [ ] parameters = method . GetParameters ( ) ;
374393 Type [ ] parameterTypes = ( from param in parameters select param . ParameterType ) . ToArray ( ) ;
@@ -720,12 +739,15 @@ public class PythonDerivedType
720739 {
721740 var disposeList = new List < PyObject > ( ) ;
722741 PyGILState gs = Runtime . PyGILState_Ensure ( ) ;
742+
743+
723744 try
724745 {
725746 using var pyself = new PyObject ( self . CheckRun ( ) ) ;
726747 using PyObject method = pyself . GetAttr ( methodName , Runtime . None ) ;
727748 if ( method . Reference != Runtime . PyNone )
728749 {
750+
729751 // if the method hasn't been overridden then it will be a managed object
730752 ManagedType ? managedMethod = ManagedType . GetManagedObject ( method . Reference ) ;
731753 if ( null == managedMethod )
0 commit comments