Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8b54b34

Browse files
committed
improve deserialization resolution of ref, out and in parameters
Also add a test
1 parent 774348e commit 8b54b34

File tree

4 files changed

+160
-13
lines changed

4 files changed

+160
-13
lines changed

src/domain_tests/TestRunner.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,84 @@ def after_reload():
771771
print(bar.__repr__())
772772
",
773773
},
774+
775+
new TestCase
776+
{
777+
Name = "out_to_ref_param",
778+
DotNetBefore = @"
779+
namespace TestNamespace
780+
{
781+
782+
[System.Serializable]
783+
public class Data
784+
{
785+
public int num = -1;
786+
}
787+
788+
[System.Serializable]
789+
public class Cls
790+
{
791+
public static void MyFn (out Data a)
792+
{
793+
a = new Data();
794+
a.num = 9001;
795+
}
796+
}
797+
}",
798+
DotNetAfter = @"
799+
namespace TestNamespace
800+
{
801+
802+
[System.Serializable]
803+
public class Data
804+
{
805+
public int num = -1;
806+
}
807+
808+
[System.Serializable]
809+
public class Cls
810+
{
811+
public static void MyFn (ref Data a)
812+
{
813+
a.num = 7;
814+
}
815+
}
816+
}",
817+
PythonCode = @"
818+
import clr
819+
import sys
820+
clr.AddReference('DomainTests')
821+
import TestNamespace
822+
import System
823+
824+
def before_reload():
825+
826+
foo = TestNamespace.Data()
827+
bar = TestNamespace.Cls.MyFn(foo)
828+
assert bar.num == 9001
829+
# foo shouldn't have changed.
830+
assert foo.num == -1
831+
832+
833+
def after_reload():
834+
835+
try:
836+
# Now that the function takes a ref type, we must pass a valid object.
837+
bar = TestNamespace.Cls.MyFn(None)
838+
except System.NullReferenceException as e:
839+
print('caught expected exception')
840+
else:
841+
raise AssertionError('failed to raise')
842+
843+
foo = TestNamespace.Data()
844+
bar = TestNamespace.Cls.MyFn(foo)
845+
# foo should have changed
846+
assert foo.num == 7
847+
assert bar.num == 7
848+
# Pythonnet also returns a new object with `ref`-quialified parameters
849+
assert foo is not bar
850+
",
851+
},
774852
};
775853

776854
/// <summary>

src/domain_tests/test_domain_reload.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,7 @@ def test_rename_event():
5959

6060
@pytest.mark.xfail(reason="newly instanced object uses PyType_GenericAlloc")
6161
def test_construct_removed_class():
62-
_run_test("construct_removed_class")
62+
_run_test("construct_removed_class")
63+
64+
def test_out_to_ref_param():
65+
_run_test("out_to_ref_param")

src/runtime/StateSerialization/MaybeMethodBase.cs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,50 @@
11
using System;
22
using System.Reflection;
33
using System.Runtime.Serialization;
4-
using System.Runtime.Serialization.Formatters.Binary;
5-
using System.IO;
4+
using System.Linq;
65

76
namespace Python.Runtime
87
{
98
[Serializable]
109
internal struct MaybeMethodBase<T> : ISerializable where T: MethodBase
1110
{
11+
[Serializable]
12+
struct ParameterHelper : IEquatable<ParameterInfo>
13+
{
14+
public enum TypeModifier
15+
{
16+
None,
17+
In,
18+
Out,
19+
Ref
20+
}
21+
public readonly string Name;
22+
public readonly TypeModifier Modifier;
23+
24+
public ParameterHelper(ParameterInfo tp)
25+
{
26+
Name = tp.ParameterType.AssemblyQualifiedName;
27+
Modifier = TypeModifier.None;
28+
29+
if (tp.IsIn)
30+
{
31+
Modifier = TypeModifier.In;
32+
}
33+
else if (tp.IsOut)
34+
{
35+
Modifier = TypeModifier.Out;
36+
}
37+
else if (tp.ParameterType.IsByRef)
38+
{
39+
Modifier = TypeModifier.Ref;
40+
}
41+
}
42+
43+
public bool Equals(ParameterInfo other)
44+
{
45+
return this.Equals(new ParameterHelper(other));
46+
}
47+
}
1248
public static implicit operator MaybeMethodBase<T> (T ob) => new MaybeMethodBase<T>(ob);
1349

1450
string name;
@@ -64,11 +100,11 @@ internal MaybeMethodBase(SerializationInfo serializationInfo, StreamingContext c
64100
var tp = Type.GetType(serializationInfo.GetString("t"));
65101
// Get the method's parameters types
66102
var field_name = serializationInfo.GetString("f");
67-
var param = (string[])serializationInfo.GetValue("p", typeof(string[]));
103+
var param = (ParameterHelper[])serializationInfo.GetValue("p", typeof(ParameterHelper[]));
68104
Type[] types = new Type[param.Length];
69105
for (int i = 0; i < param.Length; i++)
70106
{
71-
types[i] = Type.GetType(param[i]);
107+
types[i] = Type.GetType(param[i].Name);
72108
}
73109
// Try to get the method
74110
MethodBase mb = tp.GetMethod(field_name, ClassManager.BindingFlags, binder:null, types:types, modifiers:null);
@@ -81,7 +117,29 @@ internal MaybeMethodBase(SerializationInfo serializationInfo, StreamingContext c
81117
// Do like in ClassManager.GetClassInfo
82118
if(mb != null && ClassManager.ShouldBindMethod(mb))
83119
{
84-
info = mb;
120+
// One more step: Changing:
121+
// void MyFn (ref int a)
122+
// to:
123+
// void MyFn (out int a)
124+
// will still find the fucntion correctly as, `in`, `out` and `ref`
125+
// are all represented as a reference type. Query the method we got
126+
// and validate the parameters
127+
bool matches = true;
128+
if (param.Length != 0)
129+
{
130+
foreach (var item in Enumerable.Zip(param, mb.GetParameters(), (x, y) => new {x, y}))
131+
{
132+
if (!item.x.Equals(item.y))
133+
{
134+
matches = false;
135+
break;
136+
}
137+
}
138+
}
139+
if (matches)
140+
{
141+
info = mb;
142+
}
85143
}
86144
}
87145
catch (Exception e)
@@ -97,13 +155,8 @@ public void GetObjectData(SerializationInfo serializationInfo, StreamingContext
97155
{
98156
serializationInfo.AddValue("f", info.Name);
99157
serializationInfo.AddValue("t", info.ReflectedType.AssemblyQualifiedName);
100-
var p = info.GetParameters();
101-
string[] types = new string[p.Length];
102-
for (int i = 0; i < p.Length; i++)
103-
{
104-
types[i] = p[i].ParameterType.AssemblyQualifiedName;
105-
}
106-
serializationInfo.AddValue("p", types, typeof(string[]));
158+
ParameterHelper[] parameters = (from p in info.GetParameters() select new ParameterHelper(p)).ToArray();
159+
serializationInfo.AddValue("p", parameters, typeof(ParameterHelper[]));
107160
}
108161
}
109162
}

src/runtime/methodbinder.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,19 @@ int IComparer<MaybeMethodBase>.Compare(MaybeMethodBase m1, MaybeMethodBase m2)
866866
{
867867
MethodBase me1 = m1.Valid ? m1.Value : null;
868868
MethodBase me2 = m2.Valid ? m2.Value : null;
869+
if (me1 == null && me2 == null)
870+
{
871+
return 0;
872+
}
873+
else if (me1 == null)
874+
{
875+
return -1;
876+
}
877+
else if (me2 == null)
878+
{
879+
return 1;
880+
}
881+
869882
if (me1.DeclaringType != me2.DeclaringType)
870883
{
871884
// m2's type derives from m1's type, favor m2

0 commit comments

Comments
 (0)