@@ -15,6 +15,7 @@ internal class MethodPatcher
15
15
const string ORIGINAL_METHOD_PARAM = "__originalMethod" ;
16
16
const string ARGS_ARRAY_VAR = "__args" ;
17
17
const string RESULT_VAR = "__result" ;
18
+ const string RESULT_REF_VAR = "__resultRef" ;
18
19
const string STATE_VAR = "__state" ;
19
20
const string EXCEPTION_VAR = "__exception" ;
20
21
const string RUN_ORIGINAL_VAR = "__runOriginal" ;
@@ -76,6 +77,19 @@ internal MethodInfo CreateReplacement(out Dictionary<int, CodeInstruction> final
76
77
privateVars [ RESULT_VAR ] = resultVariable ;
77
78
}
78
79
80
+ if ( fixes . Any ( fix => fix . GetParameters ( ) . Any ( p => p . Name == RESULT_REF_VAR ) ) )
81
+ {
82
+ if ( returnType . IsByRef )
83
+ {
84
+ var resultRefVariable = il . DeclareLocal (
85
+ typeof ( RefResult < > ) . MakeGenericType ( returnType . GetElementType ( ) )
86
+ ) ;
87
+ emitter . Emit ( OpCodes . Ldnull ) ;
88
+ emitter . Emit ( OpCodes . Stloc , resultRefVariable ) ;
89
+ privateVars [ RESULT_REF_VAR ] = resultRefVariable ;
90
+ }
91
+ }
92
+
79
93
LocalBuilder argsArrayVariable = null ;
80
94
if ( fixes . Any ( fix => fix . GetParameters ( ) . Any ( p => p . Name == ARGS_ARRAY_VAR ) ) )
81
95
{
@@ -432,10 +446,11 @@ bool EmitOriginalBaseMethod()
432
446
return true ;
433
447
}
434
448
435
- void EmitCallParameter ( MethodInfo patch , Dictionary < string , LocalBuilder > variables , LocalBuilder runOriginalVariable , bool allowFirsParamPassthrough , out LocalBuilder tmpInstanceBoxingVar , out LocalBuilder tmpObjectVar , List < KeyValuePair < LocalBuilder , Type > > tmpBoxVars )
449
+ void EmitCallParameter ( MethodInfo patch , Dictionary < string , LocalBuilder > variables , LocalBuilder runOriginalVariable , bool allowFirsParamPassthrough , out LocalBuilder tmpInstanceBoxingVar , out LocalBuilder tmpObjectVar , out bool refResultUsed , List < KeyValuePair < LocalBuilder , Type > > tmpBoxVars )
436
450
{
437
451
tmpInstanceBoxingVar = null ;
438
452
tmpObjectVar = null ;
453
+ refResultUsed = false ;
439
454
440
455
var isInstance = original . IsStatic is false ;
441
456
var originalParameters = original . GetParameters ( ) ;
@@ -474,10 +489,10 @@ void EmitCallParameter(MethodInfo patch, Dictionary<string, LocalBuilder> variab
474
489
else
475
490
{
476
491
var paramType = patchParam . ParameterType ;
477
-
492
+
478
493
var parameterIsRef = paramType . IsByRef ;
479
494
var parameterIsObject = paramType == typeof ( object ) || paramType == typeof ( object ) . MakeByRefType ( ) ;
480
-
495
+
481
496
if ( AccessTools . IsStruct ( originalType ) )
482
497
{
483
498
if ( parameterIsObject )
@@ -571,7 +586,6 @@ void EmitCallParameter(MethodInfo patch, Dictionary<string, LocalBuilder> variab
571
586
// treat __result var special
572
587
if ( patchParam . Name == RESULT_VAR )
573
588
{
574
- var returnType = AccessTools . GetReturnedType ( original ) ;
575
589
if ( returnType == typeof ( void ) )
576
590
throw new Exception ( $ "Cannot get result from void method { original . FullDescription ( ) } ") ;
577
591
var resultType = patchParam . ParameterType ;
@@ -597,6 +611,25 @@ void EmitCallParameter(MethodInfo patch, Dictionary<string, LocalBuilder> variab
597
611
continue ;
598
612
}
599
613
614
+ // treat __resultRef delegate special
615
+ if ( patchParam . Name == RESULT_REF_VAR )
616
+ {
617
+ if ( ! returnType . IsByRef )
618
+ throw new Exception (
619
+ $ "Cannot use { RESULT_REF_VAR } with non-ref return type { returnType . FullName } of method { original . FullDescription ( ) } ") ;
620
+
621
+ var resultType = patchParam . ParameterType ;
622
+ var expectedTypeRef = typeof ( RefResult < > ) . MakeGenericType ( returnType . GetElementType ( ) ) . MakeByRefType ( ) ;
623
+ if ( resultType != expectedTypeRef )
624
+ throw new Exception (
625
+ $ "Wrong type of { RESULT_REF_VAR } for method { original . FullDescription ( ) } . Expected { expectedTypeRef . FullName } , got { resultType . FullName } ") ;
626
+
627
+ emitter . Emit ( OpCodes . Ldloca , variables [ RESULT_REF_VAR ] ) ;
628
+
629
+ refResultUsed = true ;
630
+ continue ;
631
+ }
632
+
600
633
// any other declared variables
601
634
if ( variables . TryGetValue ( patchParam . Name , out var localBuilder ) )
602
635
{
@@ -763,7 +796,7 @@ void AddPrefixes(Dictionary<string, LocalBuilder> variables, LocalBuilder runOri
763
796
}
764
797
765
798
var tmpBoxVars = new List < KeyValuePair < LocalBuilder , Type > > ( ) ;
766
- EmitCallParameter ( fix , variables , runOriginalVariable , false , out var tmpInstanceBoxingVar , out var tmpObjectVar , tmpBoxVars ) ;
799
+ EmitCallParameter ( fix , variables , runOriginalVariable , false , out var tmpInstanceBoxingVar , out var tmpObjectVar , out var refResultUsed , tmpBoxVars ) ;
767
800
emitter . Emit ( OpCodes . Call , fix ) ;
768
801
if ( fix . GetParameters ( ) . Any ( p => p . Name == ARGS_ARRAY_VAR ) )
769
802
RestoreArgumentArray ( variables ) ;
@@ -774,7 +807,22 @@ void AddPrefixes(Dictionary<string, LocalBuilder> variables, LocalBuilder runOri
774
807
emitter . Emit ( OpCodes . Unbox_Any , original . DeclaringType ) ;
775
808
emitter . Emit ( OpCodes . Stobj , original . DeclaringType ) ;
776
809
}
777
- if ( tmpObjectVar != null )
810
+ if ( refResultUsed )
811
+ {
812
+ var label = il . DefineLabel ( ) ;
813
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
814
+ emitter . Emit ( OpCodes . Brfalse_S , label ) ;
815
+
816
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
817
+ emitter . Emit ( OpCodes . Callvirt , AccessTools . Method ( variables [ RESULT_REF_VAR ] . LocalType , "Invoke" ) ) ;
818
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_VAR ] ) ;
819
+ emitter . Emit ( OpCodes . Ldnull ) ;
820
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_REF_VAR ] ) ;
821
+
822
+ emitter . MarkLabel ( label ) ;
823
+ emitter . Emit ( OpCodes . Nop ) ;
824
+ }
825
+ else if ( tmpObjectVar != null )
778
826
{
779
827
emitter . Emit ( OpCodes . Ldloc , tmpObjectVar ) ;
780
828
emitter . Emit ( OpCodes . Unbox_Any , AccessTools . GetReturnedType ( original ) ) ;
@@ -815,7 +863,7 @@ bool AddPostfixes(Dictionary<string, LocalBuilder> variables, LocalBuilder runOr
815
863
// throw new Exception("Methods without body cannot have postfixes. Use a transpiler instead.");
816
864
817
865
var tmpBoxVars = new List < KeyValuePair < LocalBuilder , Type > > ( ) ;
818
- EmitCallParameter ( fix , variables , runOriginalVariable , true , out var tmpInstanceBoxingVar , out var tmpObjectVar , tmpBoxVars ) ;
866
+ EmitCallParameter ( fix , variables , runOriginalVariable , true , out var tmpInstanceBoxingVar , out var tmpObjectVar , out var refResultUsed , tmpBoxVars ) ;
819
867
emitter . Emit ( OpCodes . Call , fix ) ;
820
868
if ( fix . GetParameters ( ) . Any ( p => p . Name == ARGS_ARRAY_VAR ) )
821
869
RestoreArgumentArray ( variables ) ;
@@ -826,7 +874,22 @@ bool AddPostfixes(Dictionary<string, LocalBuilder> variables, LocalBuilder runOr
826
874
emitter . Emit ( OpCodes . Unbox_Any , original . DeclaringType ) ;
827
875
emitter . Emit ( OpCodes . Stobj , original . DeclaringType ) ;
828
876
}
829
- if ( tmpObjectVar != null )
877
+ if ( refResultUsed )
878
+ {
879
+ var label = il . DefineLabel ( ) ;
880
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
881
+ emitter . Emit ( OpCodes . Brfalse_S , label ) ;
882
+
883
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
884
+ emitter . Emit ( OpCodes . Callvirt , AccessTools . Method ( variables [ RESULT_REF_VAR ] . LocalType , "Invoke" ) ) ;
885
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_VAR ] ) ;
886
+ emitter . Emit ( OpCodes . Ldnull ) ;
887
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_REF_VAR ] ) ;
888
+
889
+ emitter . MarkLabel ( label ) ;
890
+ emitter . Emit ( OpCodes . Nop ) ;
891
+ }
892
+ else if ( tmpObjectVar != null )
830
893
{
831
894
emitter . Emit ( OpCodes . Ldloc , tmpObjectVar ) ;
832
895
emitter . Emit ( OpCodes . Unbox_Any , AccessTools . GetReturnedType ( original ) ) ;
@@ -871,7 +934,7 @@ bool AddFinalizers(Dictionary<string, LocalBuilder> variables, LocalBuilder runO
871
934
emitter . MarkBlockBefore ( new ExceptionBlock ( ExceptionBlockType . BeginExceptionBlock ) , out var label ) ;
872
935
873
936
var tmpBoxVars = new List < KeyValuePair < LocalBuilder , Type > > ( ) ;
874
- EmitCallParameter ( fix , variables , runOriginalVariable , false , out var tmpInstanceBoxingVar , out var tmpObjectVar , tmpBoxVars ) ;
937
+ EmitCallParameter ( fix , variables , runOriginalVariable , false , out var tmpInstanceBoxingVar , out var tmpObjectVar , out var refResultUsed , tmpBoxVars ) ;
875
938
emitter . Emit ( OpCodes . Call , fix ) ;
876
939
if ( fix . GetParameters ( ) . Any ( p => p . Name == ARGS_ARRAY_VAR ) )
877
940
RestoreArgumentArray ( variables ) ;
@@ -882,7 +945,22 @@ bool AddFinalizers(Dictionary<string, LocalBuilder> variables, LocalBuilder runO
882
945
emitter . Emit ( OpCodes . Unbox_Any , original . DeclaringType ) ;
883
946
emitter . Emit ( OpCodes . Stobj , original . DeclaringType ) ;
884
947
}
885
- if ( tmpObjectVar != null )
948
+ if ( refResultUsed )
949
+ {
950
+ var label = il . DefineLabel ( ) ;
951
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
952
+ emitter . Emit ( OpCodes . Brfalse_S , label ) ;
953
+
954
+ emitter . Emit ( OpCodes . Ldloc , variables [ RESULT_REF_VAR ] ) ;
955
+ emitter . Emit ( OpCodes . Callvirt , AccessTools . Method ( variables [ RESULT_REF_VAR ] . LocalType , "Invoke" ) ) ;
956
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_VAR ] ) ;
957
+ emitter . Emit ( OpCodes . Ldnull ) ;
958
+ emitter . Emit ( OpCodes . Stloc , variables [ RESULT_REF_VAR ] ) ;
959
+
960
+ emitter . MarkLabel ( label ) ;
961
+ emitter . Emit ( OpCodes . Nop ) ;
962
+ }
963
+ else if ( tmpObjectVar != null )
886
964
{
887
965
emitter . Emit ( OpCodes . Ldloc , tmpObjectVar ) ;
888
966
emitter . Emit ( OpCodes . Unbox_Any , AccessTools . GetReturnedType ( original ) ) ;
0 commit comments