@@ -429,24 +429,33 @@ private static void AddVirtualMethod(MethodInfo method, Type baseType, TypeBuild
429429 il . Emit ( OpCodes . Ldloc_0 ) ;
430430 il . Emit ( OpCodes . Ldc_I4 , i ) ;
431431 il . Emit ( OpCodes . Ldarg , i + 1 ) ;
432- if ( parameterTypes [ i ] . IsValueType )
432+ var type = parameterTypes [ i ] ;
433+ if ( type . IsByRef )
433434 {
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 ) ;
435441 }
436442 il . Emit ( OpCodes . Stelem , typeof ( object ) ) ;
437443 }
438444 il . Emit ( OpCodes . Ldloc_0 ) ;
445+
446+ il . Emit ( OpCodes . Ldtoken , method ) ;
439447#pragma warning disable CS0618 // PythonDerivedType is for internal use only
440448 if ( method . ReturnType == typeof ( void ) )
441449 {
442- il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethodVoid" ) ) ;
450+ il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethodVoid ) ) ) ;
443451 }
444452 else
445453 {
446454 il . Emit ( OpCodes . Call ,
447- typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethod" ) . MakeGenericMethod ( method . ReturnType ) ) ;
455+ typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethod ) ) . MakeGenericMethod ( method . ReturnType ) ) ;
448456 }
449457#pragma warning restore CS0618 // PythonDerivedType is for internal use only
458+ CodeGenerator . GenerateMarshalByRefsBack ( il , parameterTypes ) ;
450459 il . Emit ( OpCodes . Ret ) ;
451460 }
452461
@@ -500,35 +509,65 @@ private static void AddPythonMethod(string methodName, PyObject func, TypeBuilde
500509 argTypes . ToArray ( ) ) ;
501510
502511 ILGenerator il = methodBuilder . GetILGenerator ( ) ;
512+
503513 il . DeclareLocal ( typeof ( object [ ] ) ) ;
514+ il . DeclareLocal ( typeof ( RuntimeMethodHandle ) ) ;
515+
516+ // this
504517 il . Emit ( OpCodes . Ldarg_0 ) ;
518+
519+ // Python method to call
505520 il . Emit ( OpCodes . Ldstr , methodName ) ;
521+
522+ // original method name
506523 il . Emit ( OpCodes . Ldnull ) ; // don't fall back to the base type's method
524+
525+ // create args array
507526 il . Emit ( OpCodes . Ldc_I4 , argTypes . Count ) ;
508527 il . Emit ( OpCodes . Newarr , typeof ( object ) ) ;
509528 il . Emit ( OpCodes . Stloc_0 ) ;
529+
530+ // fill args array
510531 for ( var i = 0 ; i < argTypes . Count ; ++ i )
511532 {
512533 il . Emit ( OpCodes . Ldloc_0 ) ;
513534 il . Emit ( OpCodes . Ldc_I4 , i ) ;
514535 il . Emit ( OpCodes . Ldarg , i + 1 ) ;
515- if ( argTypes [ i ] . IsValueType )
536+ var type = argTypes [ i ] ;
537+ if ( type . IsByRef )
516538 {
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 ) ;
518545 }
519546 il . Emit ( OpCodes . Stelem , typeof ( object ) ) ;
520547 }
548+
549+ // args array
521550 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 ) ;
522556#pragma warning disable CS0618 // PythonDerivedType is for internal use only
557+
558+ // invoke the method
523559 if ( returnType == typeof ( void ) )
524560 {
525- il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethodVoid" ) ) ;
561+ il . Emit ( OpCodes . Call , typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethodVoid ) ) ) ;
526562 }
527563 else
528564 {
529565 il . Emit ( OpCodes . Call ,
530- typeof ( PythonDerivedType ) . GetMethod ( " InvokeMethod" ) . MakeGenericMethod ( returnType ) ) ;
566+ typeof ( PythonDerivedType ) . GetMethod ( nameof ( InvokeMethod ) ) . MakeGenericMethod ( returnType ) ) ;
531567 }
568+
569+ CodeGenerator . GenerateMarshalByRefsBack ( il , argTypes ) ;
570+
532571#pragma warning restore CS0618 // PythonDerivedType is for internal use only
533572 il . Emit ( OpCodes . Ret ) ;
534573 }
@@ -672,7 +711,8 @@ public class PythonDerivedType
672711 /// method binding (i.e. it has been overridden in the derived python
673712 /// class) it calls it, otherwise it calls the base method.
674713 /// </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 )
676716 {
677717 var self = GetPyObj ( obj ) ;
678718
@@ -698,8 +738,10 @@ public class PythonDerivedType
698738 }
699739
700740 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 > ( ) ;
703745 }
704746 }
705747 }
@@ -726,7 +768,7 @@ public class PythonDerivedType
726768 }
727769
728770 public static void InvokeMethodVoid ( IPythonDerivedType obj , string methodName , string origMethodName ,
729- object [ ] args )
771+ object ? [ ] args , RuntimeMethodHandle methodHandle )
730772 {
731773 var self = GetPyObj ( obj ) ;
732774 if ( null != self . Ref )
@@ -736,8 +778,7 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s
736778 try
737779 {
738780 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 ) ;
741782 if ( method . Reference != Runtime . None )
742783 {
743784 // 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
752793 }
753794
754795 PyObject py_result = method . Invoke ( pyargs ) ;
755- disposeList . Add ( py_result ) ;
796+ MarshalByRefsBack ( args , methodHandle , py_result , outsOffset : 0 ) ;
756797 return ;
757798 }
758799 }
@@ -779,6 +820,44 @@ public static void InvokeMethodVoid(IPythonDerivedType obj, string methodName, s
779820 args ) ;
780821 }
781822
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+
782861 public static T ? InvokeGetProperty < T > ( IPythonDerivedType obj , string propertyName )
783862 {
784863 var self = GetPyObj ( obj ) ;
0 commit comments