@@ -429,24 +429,33 @@ private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuild
429
429
il . Emit ( OpCodes . Ldloc_0 ) ;
430
430
il . Emit ( OpCodes . Ldc_I4 , i ) ;
431
431
il . Emit ( OpCodes . Ldarg , i + 1 ) ;
432
- if ( parameterTypes [ i ] . IsValueType )
432
+ var type = parameterTypes [ i ] ;
433
+ if ( type . IsByRef )
433
434
{
434
- il . Emit ( OpCodes . Box , parameterTypes [ i ] ) ;
435
+ type = type . GetElementType ( ) ;
436
+ il . Emit ( OpCodes . Ldobj , type ) ;
437
+ }
438
+ if ( type . IsValueType )
439
+ {
440
+ il . Emit ( OpCodes . Box , type ) ;
435
441
}
436
442
il . Emit ( OpCodes . Stelem , typeof ( object ) ) ;
437
443
}
438
444
il . Emit ( OpCodes . Ldloc_0 ) ;
445
+
446
+ il . Emit ( OpCodes . Ldtoken , method ) ;
439
447
#pragma warning disable CS0618 // PythonDerivedType is for internal use only
440
448
if ( method . ReturnType == typeof ( void ) )
441
449
{
442
- il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethodVoid" ) ) ;
450
+ il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethodVoid ) ) ) ;
443
451
}
444
452
else
445
453
{
446
454
il . Emit ( OpCodes . Call ,
447
- typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethod" ) . MakeGenericMethod ( method . ReturnType ) ) ;
455
+ typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethod ) ) . MakeGenericMethod ( method . ReturnType ) ) ;
448
456
}
449
457
#pragma warning restore CS0618 // PythonDerivedType is for internal use only
458
+ CodeGenerator . GenerateMarshalByRefsBack ( il , parameterTypes ) ;
450
459
il . Emit ( OpCodes . Ret ) ;
451
460
}
452
461
@@ -500,35 +509,65 @@ private static void AddPythonMethod(string methodName, PyObject func, TypeBuilde
500
509
argTypes . ToArray ( ) ) ;
501
510
502
511
ILGenerator il = methodBuilder . GetILGenerator ( ) ;
512
+
503
513
il . DeclareLocal ( typeof ( object [ ] ) ) ;
514
+ il . DeclareLocal ( typeof ( RuntimeMethodHandle ) ) ;
515
+
516
+ // this
504
517
il . Emit ( OpCodes . Ldarg_0 ) ;
518
+
519
+ // Python method to call
505
520
il . Emit ( OpCodes . Ldstr , methodName ) ;
521
+
522
+ // original method name
506
523
il . Emit ( OpCodes . Ldnull ) ; // don't fall back to the base type's method
524
+
525
+ // create args array
507
526
il . Emit ( OpCodes . Ldc_I4 , argTypes . Count ) ;
508
527
il . Emit ( OpCodes . Newarr , typeof ( object ) ) ;
509
528
il . Emit ( OpCodes . Stloc_0 ) ;
529
+
530
+ // fill args array
510
531
for ( var i = 0 ; i < argTypes . Count ; ++ i )
511
532
{
512
533
il . Emit ( OpCodes . Ldloc_0 ) ;
513
534
il . Emit ( OpCodes . Ldc_I4 , i ) ;
514
535
il . Emit ( OpCodes . Ldarg , i + 1 ) ;
515
- if ( argTypes [ i ] . IsValueType )
536
+ var type = argTypes [ i ] ;
537
+ if ( type . IsByRef )
516
538
{
517
- il . Emit ( OpCodes . Box , argTypes [ i ] ) ;
539
+ type = type . GetElementType ( ) ;
540
+ il . Emit ( OpCodes . Ldobj , type ) ;
541
+ }
542
+ if ( type . IsValueType )
543
+ {
544
+ il . Emit ( OpCodes . Box , type ) ;
518
545
}
519
546
il . Emit ( OpCodes . Stelem , typeof ( object ) ) ;
520
547
}
548
+
549
+ // args array
521
550
il . Emit ( OpCodes . Ldloc_0 ) ;
551
+
552
+ // method handle for the base method is null
553
+ il . Emit ( OpCodes . Ldloca_S , 1 ) ;
554
+ il . Emit ( OpCodes . Initobj , typeof ( RuntimeMethodHandle ) ) ;
555
+ il . Emit ( OpCodes . Ldloc_1 ) ;
522
556
#pragma warning disable CS0618 // PythonDerivedType is for internal use only
557
+
558
+ // invoke the method
523
559
if ( returnType == typeof ( void ) )
524
560
{
525
- il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethodVoid" ) ) ;
561
+ il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethodVoid ) ) ) ;
526
562
}
527
563
else
528
564
{
529
565
il . Emit ( OpCodes . Call ,
530
- typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethod" ) . MakeGenericMethod ( returnType ) ) ;
566
+ typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethod ) ) . MakeGenericMethod ( returnType ) ) ;
531
567
}
568
+
569
+ CodeGenerator . GenerateMarshalByRefsBack ( il , argTypes ) ;
570
+
532
571
#pragma warning restore CS0618 // PythonDerivedType is for internal use only
533
572
il . Emit ( OpCodes . Ret ) ;
534
573
}
@@ -672,7 +711,8 @@ public class PythonDerivedType
672
711
/// method binding (i.e. it has been overridden in the derived python
673
712
/// class) it calls it, otherwise it calls the base method.
674
713
/// </summary>
675
- public static T ? InvokeMethod < T > ( IPythonDerivedType obj , string methodName , string origMethodName , object [ ] args )
714
+ public static T ? InvokeMethod < T > ( IPythonDerivedType obj , string methodName , string origMethodName ,
715
+ object [ ] args , RuntimeMethodHandle methodHandle )
676
716
{
677
717
var self = GetPyObj ( obj ) ;
678
718
@@ -698,8 +738,10 @@ public class PythonDerivedType
698
738
}
699
739
700
740
PyObject py_result = method . Invoke ( pyargs ) ;
701
- disposeList . Add ( py_result ) ;
702
- return py_result . As < T > ( ) ;
741
+ PyTuple ? result_tuple = MarshalByRefsBack ( args , methodHandle , py_result , outsOffset : 1 ) ;
742
+ return result_tuple is not null
743
+ ? result_tuple [ 0 ] . As < T > ( )
744
+ : py_result . As < T > ( ) ;
703
745
}
704
746
}
705
747
}
@@ -726,7 +768,7 @@ public class PythonDerivedType
726
768
}
727
769
728
770
public static void InvokeMethodVoid ( IPythonDerivedType obj , string methodName , string origMethodName ,
729
- object [ ] args )
771
+ object ? [ ] args , RuntimeMethodHandle methodHandle )
730
772
{
731
773
var self = GetPyObj ( obj ) ;
732
774
if ( null != self . Ref )
@@ -736,8 +778,7 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s
736
778
try
737
779
{
738
780
using var pyself = new PyObject ( self . CheckRun ( ) ) ;
739
- PyObject method = pyself . GetAttr ( methodName , Runtime . None ) ;
740
- disposeList . Add ( method ) ;
781
+ using PyObject method = pyself . GetAttr ( methodName , Runtime . None ) ;
741
782
if ( method . Reference != Runtime . None )
742
783
{
743
784
// if the method hasn't been overridden then it will be a managed object
@@ -752,7 +793,7 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s
752
793
}
753
794
754
795
PyObject py_result = method . Invoke ( pyargs ) ;
755
- disposeList . Add ( py_result ) ;
796
+ MarshalByRefsBack ( args , methodHandle , py_result , outsOffset : 0 ) ;
756
797
return ;
757
798
}
758
799
}
@@ -779,6 +820,44 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s
779
820
args ) ;
780
821
}
781
822
823
+ /// <summary>
824
+ /// If the method has byref arguments, reinterprets Python return value
825
+ /// as a tuple of new values for those arguments, and updates corresponding
826
+ /// elements of <paramref name="args"/> array.
827
+ /// </summary>
828
+ private static PyTuple ? MarshalByRefsBack ( object ? [ ] args , RuntimeMethodHandle methodHandle , PyObject pyResult , int outsOffset )
829
+ {
830
+ if ( methodHandle == default ) return null ;
831
+
832
+ var originalMethod = MethodBase . GetMethodFromHandle ( methodHandle ) ;
833
+ var parameters = originalMethod . GetParameters ( ) ;
834
+ PyTuple ? outs = null ;
835
+ int byrefIndex = 0 ;
836
+ for ( int i = 0 ; i < parameters . Length ; ++ i )
837
+ {
838
+ Type type = parameters [ i ] . ParameterType ;
839
+ if ( ! type . IsByRef )
840
+ {
841
+ continue ;
842
+ }
843
+
844
+ type = type . GetElementType ( ) ;
845
+
846
+ if ( outs is null )
847
+ {
848
+ outs = new PyTuple ( pyResult ) ;
849
+ pyResult . Dispose ( ) ;
850
+ }
851
+
852
+ args [ i ] = outs [ byrefIndex + outsOffset ] . AsManagedObject ( type ) ;
853
+ byrefIndex ++ ;
854
+ }
855
+ if ( byrefIndex > 0 && outs ! . Length ( ) > byrefIndex + outsOffset )
856
+ throw new ArgumentException ( "Too many output parameters" ) ;
857
+
858
+ return outs ;
859
+ }
860
+
782
861
public static T ? InvokeGetProperty < T > ( IPythonDerivedType obj , string propertyName )
783
862
{
784
863
var self = GetPyObj ( obj ) ;
0 commit comments