@@ -188,7 +188,8 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType)
188
188
if ( typeof ( ICollection ) . IsAssignableFrom ( clrType ) || clrType . GetInterfaces ( ) . Any ( x => x . IsGenericType && x . GetGenericTypeDefinition ( ) == typeof ( ICollection < > ) ) )
189
189
{
190
190
var method = typeof ( mp_length_slot ) . GetMethod ( nameof ( mp_length_slot . mp_length ) ) ;
191
- InitializeSlot ( type , TypeOffset . mp_length , method , slotsHolder ) ;
191
+ var thunk = Interop . GetThunk ( method ) ;
192
+ InitializeSlot ( type , thunk , "__len__" , slotsHolder ) ;
192
193
}
193
194
194
195
if ( base_ != IntPtr . Zero )
@@ -354,6 +355,28 @@ internal static void FreeMethodDef(IntPtr mdef)
354
355
}
355
356
}
356
357
358
+ /// <summary>
359
+ /// Adds a deallocator for a type's method. At deallocation, the deallocator will remove the
360
+ /// method from the type's Dict and deallocate the PyMethodDef object.
361
+ /// </summary>
362
+ /// <param name="t">The type to add the deallocator to.</param>
363
+ /// <param name="mdef">The pointer to the PyMethodDef structure.</param>
364
+ /// <param name="name">The name of the slot.</param>
365
+ /// <param name="slotsHolder">The SlotsHolder holding the deallocator/.</param>
366
+ internal static void AddDeallocator ( IntPtr t , IntPtr mdef , string name , SlotsHolder slotsHolder )
367
+ {
368
+ slotsHolder . AddDealloctor ( ( ) =>
369
+ {
370
+ //IntPtr t = type;
371
+ IntPtr tp_dict = Marshal . ReadIntPtr ( t , TypeOffset . tp_dict ) ;
372
+ if ( Runtime . PyDict_DelItemString ( tp_dict , name ) != 0 )
373
+ {
374
+ Runtime . PyErr_Print ( ) ;
375
+ }
376
+ FreeMethodDef ( mdef ) ;
377
+ } ) ;
378
+ }
379
+
357
380
internal static IntPtr CreateMetaType ( Type impl , out SlotsHolder slotsHolder )
358
381
{
359
382
// The managed metatype is functionally little different than the
@@ -389,54 +412,31 @@ internal static IntPtr CreateMetaType(Type impl, out SlotsHolder slotsHolder)
389
412
Debug . Assert ( 4 * IntPtr . Size == Marshal . SizeOf ( typeof ( PyMethodDef ) ) ) ;
390
413
IntPtr mdefStart = mdef ;
391
414
ThunkInfo thunk = Interop . GetThunk ( typeof ( MetaType ) . GetMethod ( "__instancecheck__" ) , "BinaryFunc" ) ;
392
- slotsHolder . KeeapAlive ( thunk . Target ) ;
393
-
394
- {
395
- IntPtr mdefAddr = mdef ;
396
- slotsHolder . AddDealloctor ( ( ) =>
397
- {
398
- IntPtr t = type ;
399
- IntPtr tp_dict = Marshal . ReadIntPtr ( t , TypeOffset . tp_dict ) ;
400
- if ( Runtime . PyDict_DelItemString ( tp_dict , "__instancecheck__" ) != 0 )
401
- {
402
- Runtime . PyErr_Print ( ) ;
403
- }
404
- FreeMethodDef ( mdefAddr ) ;
405
- } ) ;
406
- }
415
+ slotsHolder . KeepAlive ( thunk . Target ) ;
416
+ // Add deallocator before writing the method def, as after WriteMethodDef, mdef
417
+ // will not have the same value.
418
+ AddDeallocator ( type , mdef , "__instancecheck__" , slotsHolder ) ;
407
419
mdef = WriteMethodDef (
408
420
mdef ,
409
421
"__instancecheck__" ,
410
422
thunk . Address
411
423
) ;
412
424
413
425
thunk = Interop . GetThunk ( typeof ( MetaType ) . GetMethod ( "__subclasscheck__" ) , "BinaryFunc" ) ;
414
- slotsHolder . KeeapAlive ( thunk . Target ) ;
415
- {
416
- IntPtr mdefAddr = mdef ;
417
- slotsHolder . AddDealloctor ( ( ) =>
418
- {
419
- IntPtr t = type ;
420
- IntPtr tp_dict = Marshal . ReadIntPtr ( t , TypeOffset . tp_dict ) ;
421
- if ( Runtime . PyDict_DelItemString ( tp_dict , "__subclasscheck__" ) != 0 )
422
- {
423
- Runtime . PyErr_Print ( ) ;
424
- }
425
- FreeMethodDef ( mdefAddr ) ;
426
- } ) ;
427
- }
426
+ slotsHolder . KeepAlive ( thunk . Target ) ;
427
+ AddDeallocator ( type , mdef , "__subclasscheck__" , slotsHolder ) ;
428
428
429
429
mdef = WriteMethodDef (
430
430
mdef ,
431
431
"__subclasscheck__" ,
432
432
thunk . Address
433
433
) ;
434
434
435
- // FIXME: mdef is not used
435
+ // Pad the last field with zeroes to terminate the array
436
436
mdef = WriteMethodDefSentinel ( mdef ) ;
437
437
438
438
Marshal . WriteIntPtr ( type , TypeOffset . tp_methods , mdefStart ) ;
439
- slotsHolder . Add ( TypeOffset . tp_methods , ( t , offset ) =>
439
+ slotsHolder . Set ( TypeOffset . tp_methods , ( t , offset ) =>
440
440
{
441
441
var p = Marshal . ReadIntPtr ( t , offset ) ;
442
442
Runtime . PyMem_Free ( p ) ;
@@ -818,9 +818,8 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
818
818
// These have to be defined, though, so by default we fill these with
819
819
// static C# functions from this class.
820
820
821
- // var ret0 = Interop.GetThunk(((Func<IntPtr, int>)Return0).Method).Address;
822
- // var ret1 = Interop.GetThunk(((Func<IntPtr, int>)Return1).Method).Address;
823
-
821
+ IntPtr ret0 = IntPtr . Zero ;
822
+ IntPtr ret1 = IntPtr . Zero ;
824
823
if ( native != null )
825
824
{
826
825
// If we want to support domain reload, the C# implementation
@@ -829,35 +828,18 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
829
828
// load them into a separate code page that is leaked
830
829
// intentionally.
831
830
InitializeNativeCodePage ( ) ;
832
- IntPtr ret1 = NativeCodePage + native . Return1 ;
833
- IntPtr ret0 = NativeCodePage + native . Return0 ;
834
- InitializeSlot ( type , ret0 , "tp_traverse" , canOverride : false ) ;
835
- InitializeSlot ( type , ret0 , "tp_clear" , canOverride : false ) ;
836
- InitializeSlot ( type , ret1 , "tp_is_gc" , canOverride : false ) ;
831
+ ret1 = NativeCodePage + native . Return1 ;
832
+ ret0 = NativeCodePage + native . Return0 ;
837
833
}
838
834
else
839
835
{
840
- if ( ! IsSlotSet ( type , "tp_traverse" ) )
841
- {
842
- var thunkRet0 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return0 ) . Method ) ;
843
- var offset = GetSlotOffset ( "tp_traverse" ) ;
844
- Marshal . WriteIntPtr ( type , offset , thunkRet0 . Address ) ;
845
- if ( slotsHolder != null )
846
- {
847
- slotsHolder . Add ( offset , thunkRet0 ) ;
848
- }
849
- }
850
- if ( ! IsSlotSet ( type , "tp_clear" ) )
851
- {
852
- var thunkRet0 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return0 ) . Method ) ;
853
- var offset = GetSlotOffset ( "tp_clear" ) ;
854
- Marshal . WriteIntPtr ( type , offset , thunkRet0 . Address ) ;
855
- if ( slotsHolder != null )
856
- {
857
- slotsHolder . Add ( offset , thunkRet0 ) ;
858
- }
859
- }
836
+ ret1 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return1 ) . Method ) . Address ;
837
+ ret0 = Interop . GetThunk ( ( ( Func < IntPtr , int > ) Return0 ) . Method ) . Address ;
860
838
}
839
+
840
+ InitializeSlot ( type , ret0 , "tp_traverse" ) ;
841
+ InitializeSlot ( type , ret0 , "tp_clear" ) ;
842
+ InitializeSlot ( type , ret1 , "tp_is_gc" ) ;
861
843
}
862
844
863
845
static int Return1 ( IntPtr _ ) => 1 ;
@@ -873,11 +855,11 @@ internal static void InitializeSlots(IntPtr type, Type impl, SlotsHolder slotsHo
873
855
/// </summary>
874
856
/// <param name="type">Type being initialized.</param>
875
857
/// <param name="slot">Function pointer.</param>
876
- /// <param name="canOverride">Can override the slot when it existed </param>
877
- static void InitializeSlot ( IntPtr type , IntPtr slot , string name , bool canOverride = true )
858
+ /// <param name="name">Name of the slot to initialize </param>
859
+ static void InitializeSlot ( IntPtr type , IntPtr slot , string name )
878
860
{
879
861
var offset = GetSlotOffset ( name ) ;
880
- if ( ! canOverride && Marshal . ReadIntPtr ( type , offset ) != IntPtr . Zero )
862
+ if ( Marshal . ReadIntPtr ( type , offset ) != IntPtr . Zero )
881
863
{
882
864
return ;
883
865
}
@@ -901,16 +883,6 @@ static void InitializeSlot(IntPtr type, ThunkInfo thunk, string name, SlotsHolde
901
883
}
902
884
}
903
885
904
- static void InitializeSlot ( IntPtr type , int slotOffset , MethodInfo method , SlotsHolder slotsHolder = null )
905
- {
906
- var thunk = Interop . GetThunk ( method ) ;
907
- Marshal . WriteIntPtr ( type , slotOffset , thunk . Address ) ;
908
- if ( slotsHolder != null )
909
- {
910
- slotsHolder . Add ( slotOffset , thunk ) ;
911
- }
912
- }
913
-
914
886
static int GetSlotOffset ( string name )
915
887
{
916
888
Type typeOffset = typeof ( TypeOffset ) ;
@@ -981,88 +953,102 @@ private static SlotsHolder CreateSlotsHolder(IntPtr type)
981
953
982
954
class SlotsHolder
983
955
{
984
- public delegate void Resetor ( IntPtr type , int offset ) ;
956
+ /// <summary>
957
+ /// Delegate called to customize a (Python) Type slot reset
958
+ /// </summary>
959
+ /// <param name="type">The type that will have a slot reset</param>
960
+ /// <param name="offset">The offset of the slot</param>
961
+ public delegate void ResetSlotAction ( IntPtr type , int offset ) ;
985
962
986
- private readonly IntPtr _type ;
987
- private Dictionary < int , ThunkInfo > _slots = new Dictionary < int , ThunkInfo > ( ) ;
988
- private List < Delegate > _keepalive = new List < Delegate > ( ) ;
989
- private Dictionary < int , Resetor > _customRestors = new Dictionary < int , Resetor > ( ) ;
990
- private List < Action > _deallocators = new List < Action > ( ) ;
991
- private bool _alredyReset = false ;
963
+ private readonly IntPtr type ;
964
+ private Dictionary < int , ThunkInfo > slots = new Dictionary < int , ThunkInfo > ( ) ;
965
+ private List < Delegate > keepalive = new List < Delegate > ( ) ;
966
+ private Dictionary < int , ResetSlotAction > customResetors = new Dictionary < int , ResetSlotAction > ( ) ;
967
+ private List < Action > deallocators = new List < Action > ( ) ;
968
+ private bool alreadyReset = false ;
992
969
993
970
/// <summary>
994
971
/// Create slots holder for holding the delegate of slots and be able to reset them.
995
972
/// </summary>
996
973
/// <param name="type">Steals a reference to target type</param>
997
974
public SlotsHolder ( IntPtr type )
998
975
{
999
- _type = type ;
976
+ this . type = type ;
1000
977
}
1001
978
1002
979
public void Add ( int offset , ThunkInfo thunk )
1003
980
{
1004
- _slots . Add ( offset , thunk ) ;
981
+ slots . Add ( offset , thunk ) ;
1005
982
}
1006
983
1007
- public void Add ( int offset , Resetor resetor )
984
+ public void Set ( int offset , ResetSlotAction resetor )
1008
985
{
1009
- _customRestors [ offset ] = resetor ;
986
+ customResetors [ offset ] = resetor ;
1010
987
}
1011
988
1012
989
public void AddDealloctor ( Action deallocate )
1013
990
{
1014
- _deallocators . Add ( deallocate ) ;
991
+ deallocators . Add ( deallocate ) ;
1015
992
}
1016
993
1017
- public void KeeapAlive ( Delegate d )
994
+ /// <summary>
995
+ /// Add a delegate to keep it from being garbage collected.
996
+ /// </summary>
997
+ /// <param name="d">The delegate to add</param>
998
+ public void KeepAlive ( Delegate d )
1018
999
{
1019
- _keepalive . Add ( d ) ;
1000
+ keepalive . Add ( d ) ;
1020
1001
}
1021
1002
1022
1003
public void ResetSlots ( )
1023
1004
{
1024
- if ( _alredyReset )
1005
+ if ( alreadyReset )
1025
1006
{
1026
1007
return ;
1027
1008
}
1028
- _alredyReset = true ;
1029
- IntPtr tp_name = Marshal . ReadIntPtr ( _type , TypeOffset . tp_name ) ;
1009
+ alreadyReset = true ;
1010
+ IntPtr tp_name = Marshal . ReadIntPtr ( type , TypeOffset . tp_name ) ;
1030
1011
string typeName = Marshal . PtrToStringAnsi ( tp_name ) ;
1031
- foreach ( var offset in _slots . Keys )
1012
+ foreach ( var offset in slots . Keys )
1032
1013
{
1033
1014
IntPtr ptr = GetDefaultSlot ( offset ) ;
1034
1015
//DebugUtil.Print($"Set slot<{TypeOffsetHelper.GetSlotNameByOffset(offset)}> to 0x{ptr.ToString("X")} at {typeName}<0x{_type}>");
1035
- Marshal . WriteIntPtr ( _type , offset , ptr ) ;
1016
+ Marshal . WriteIntPtr ( type , offset , ptr ) ;
1036
1017
}
1037
1018
1038
- foreach ( var action in _deallocators )
1019
+ foreach ( var action in deallocators )
1039
1020
{
1040
1021
action ( ) ;
1041
1022
}
1042
1023
1043
- foreach ( var pair in _customRestors )
1024
+ foreach ( var pair in customResetors )
1044
1025
{
1045
1026
int offset = pair . Key ;
1046
1027
var resetor = pair . Value ;
1047
- resetor ? . Invoke ( _type , offset ) ;
1028
+ resetor ? . Invoke ( type , offset ) ;
1048
1029
}
1049
1030
1050
- _customRestors . Clear ( ) ;
1051
- _slots . Clear ( ) ;
1052
- _keepalive . Clear ( ) ;
1053
- _deallocators . Clear ( ) ;
1031
+ customResetors . Clear ( ) ;
1032
+ slots . Clear ( ) ;
1033
+ keepalive . Clear ( ) ;
1034
+ deallocators . Clear ( ) ;
1054
1035
1055
1036
// Custom reset
1056
- IntPtr tp_base = Marshal . ReadIntPtr ( _type , TypeOffset . tp_base ) ;
1037
+ IntPtr tp_base = Marshal . ReadIntPtr ( type , TypeOffset . tp_base ) ;
1057
1038
Runtime . XDecref ( tp_base ) ;
1058
- Marshal . WriteIntPtr ( _type , TypeOffset . tp_base , IntPtr . Zero ) ;
1039
+ Marshal . WriteIntPtr ( type , TypeOffset . tp_base , IntPtr . Zero ) ;
1059
1040
1060
- IntPtr tp_bases = Marshal . ReadIntPtr ( _type , TypeOffset . tp_bases ) ;
1041
+ IntPtr tp_bases = Marshal . ReadIntPtr ( type , TypeOffset . tp_bases ) ;
1061
1042
Runtime . XDecref ( tp_bases ) ;
1062
1043
tp_bases = Runtime . PyTuple_New ( 0 ) ;
1063
- Marshal . WriteIntPtr ( _type , TypeOffset . tp_bases , tp_bases ) ;
1044
+ Marshal . WriteIntPtr ( type , TypeOffset . tp_bases , tp_bases ) ;
1064
1045
}
1065
1046
1047
+ /// <summary>
1048
+ /// Returns the default C function pointer for the slot to reset.
1049
+ /// </summary>
1050
+ /// <param name="offset">The offset of the slot.</param>
1051
+ /// <returns>The default C function pointer of the slot.</returns>
1066
1052
private static IntPtr GetDefaultSlot ( int offset )
1067
1053
{
1068
1054
if ( offset == TypeOffset . tp_clear
@@ -1072,7 +1058,7 @@ private static IntPtr GetDefaultSlot(int offset)
1072
1058
}
1073
1059
else if ( offset == TypeOffset . tp_dealloc )
1074
1060
{
1075
- // tp_free of PyTypeType is point to PyObejct_GC_Del .
1061
+ // tp_free of PyTypeType is point to PyObject_GC_Del .
1076
1062
return Marshal . ReadIntPtr ( Runtime . PyTypeType , TypeOffset . tp_free ) ;
1077
1063
}
1078
1064
else if ( offset == TypeOffset . tp_free )
0 commit comments