@@ -512,12 +512,8 @@ public static NativeCode Active
512512 return I386 ;
513513 case MachineType . x86_64 :
514514 return X86_64 ;
515- case MachineType . armv7l :
516- return Armv7l ;
517- case MachineType . armv8 :
518- return Armv8 ;
519515 default :
520- throw new NotImplementedException ( $ "No support for { Runtime . MachineName } " ) ;
516+ return null ;
521517 }
522518 }
523519 }
@@ -552,34 +548,6 @@ public static NativeCode Active
552548 /// <see cref="NativeCode.X86_64"/>
553549 /// </summary>
554550 public static readonly NativeCode I386 = X86_64 ;
555-
556- public static readonly NativeCode Armv7l = new NativeCode ( )
557- {
558- Return0 = 0 ,
559- Return1 = 0x08 ,
560- Code = new byte [ ]
561- {
562- 0xe3 , 0xa0 , 0x00 , 0x00 , // mov r0, #0
563- 0xe1 , 0x2f , 0xff , 0x1e , // bx lr
564-
565- 0xe3 , 0xa0 , 0x00 , 0x01 , // mov r0, #1
566- 0xe1 , 0x2f , 0xff , 0x1e , // bx lr
567- }
568- } ;
569-
570- public static readonly NativeCode Armv8 = new NativeCode ( )
571- {
572- Return0 = 0 ,
573- Return1 = 0x08 ,
574- Code = new byte [ ]
575- {
576- 0x52 , 0x80 , 0x00 , 0x00 , // mov w0, #0x0
577- 0xd6 , 0x5f , 0x03 , 0xc0 , // ret
578-
579- 0x52 , 0x80 , 0x00 , 0x20 , // mov w0, #0x1
580- 0xd6 , 0x5f , 0x03 , 0xc0 , // ret
581- }
582- } ;
583551 }
584552
585553 /// <summary>
@@ -702,7 +670,7 @@ internal static void InitializeNativeCodePage()
702670 Marshal . Copy ( NativeCode . Active . Code , 0 , NativeCodePage , codeLength ) ;
703671 mapper . SetReadExec ( NativeCodePage , codeLength ) ;
704672 }
705- #endregion
673+ #endregion
706674
707675 /// <summary>
708676 /// Given a newly allocated Python type object and a managed Type that
@@ -745,21 +713,40 @@ internal static void InitializeSlots(IntPtr type, Type impl)
745713 impl = impl . BaseType ;
746714 }
747715
748- // See the TestDomainReload test: there was a crash related to
749- // the gc-related slots. They always return 0 or 1 because we don't
750- // really support gc:
716+ var native = NativeCode . Active ;
717+
718+ // The garbage collection related slots always have to return 1 or 0
719+ // since .NET objects don't take part in Python's gc:
751720 // tp_traverse (returns 0)
752721 // tp_clear (returns 0)
753722 // tp_is_gc (returns 1)
754- // We can't do without: python really wants those slots to exist.
755- // We can't implement those in C# because the application domain
756- // can be shut down and the memory released.
757- InitializeNativeCodePage ( ) ;
758- InitializeSlot ( type , NativeCodePage + NativeCode . Active . Return0 , "tp_traverse" ) ;
759- InitializeSlot ( type , NativeCodePage + NativeCode . Active . Return0 , "tp_clear" ) ;
760- InitializeSlot ( type , NativeCodePage + NativeCode . Active . Return1 , "tp_is_gc" ) ;
723+ // These have to be defined, though, so by default we fill these with
724+ // static C# functions from this class.
725+
726+ var ret0 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return0 ) . Method ) ;
727+ var ret1 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return1 ) . Method ) ;
728+
729+ if ( native != null )
730+ {
731+ // If we want to support domain reload, the C# implementation
732+ // cannot be used as the assembly may get released before
733+ // CPython calls these functions. Instead, for amd64 and x86 we
734+ // load them into a separate code page that is leaked
735+ // intentionally.
736+ InitializeNativeCodePage ( ) ;
737+ ret1 = NativeCodePage + native . Return1 ;
738+ ret0 = NativeCodePage + native . Return0 ;
739+ }
740+
741+ InitializeSlot ( type , ret0 , "tp_traverse" ) ;
742+ InitializeSlot ( type , ret0 , "tp_clear" ) ;
743+ InitializeSlot ( type , ret1 , "tp_is_gc" ) ;
761744 }
762745
746+ static int Return1 ( IntPtr _ ) => 1 ;
747+
748+ static int Return0 ( IntPtr _ ) => 0 ;
749+
763750 /// <summary>
764751 /// Helper for InitializeSlots.
765752 ///
0 commit comments