Skip to content

Commit d0988c2

Browse files
committed
1. In cases where the generic parameters do not match the arguments on overloaded methods, the method binder often picked the wrong method. This is because it picks the method when it looks at the generic parameters, before it knows what the arguments are. Now when it gets to evaluating the arguments, it uses the generic parameters in the picked method AND the arguments to try to re-bind to the proper method. This works with calls like this:
foo = bar.GetValue[str](int arg1, int arg2, str arg3) ...but I did NOT implement a fix for using the __overloads__ syntax. It is still broken (I'm not quite clear on when it is ever even necessary.) 2. Added unit tests for the above. 3. Added some text to the monoclr README.txt to describe how to compile the clr.so that is needed for MacOS X -- I still haven't been able to get everything working myself, so I'm not 100% on it, but the file was blank before, so I figure something is better that nothing.)
1 parent 1b6d6e6 commit d0988c2

File tree

5 files changed

+124
-29
lines changed

5 files changed

+124
-29
lines changed

pythonnet/src/monoclr/README.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
1. On MacOS X, create the following directory structure:
2+
3+
/PythonNET/src/monoclr
4+
5+
2. Copy the Makefile and setup.py to /PythonNET
6+
7+
3. Copy the C/C++ code and header files to /PythonNET/src/monoclr
8+
9+
4. In a terminal window, run:
10+
11+
$ python setup.py build
12+
13+
5. This creates the clr.so

pythonnet/src/runtime/methodbinder.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,55 @@ internal static MethodInfo MatchParameters(MethodInfo[] mi,Type[] tp) {
8787
}
8888
return null;
8989
}
90-
90+
91+
92+
//====================================================================
93+
// Given a sequence of MethodInfo and two sequences of type parameters,
94+
// return the MethodInfo that matches the signature and the closed generic.
95+
//====================================================================
96+
97+
internal static MethodInfo MatchSignatureAndParameters(MethodInfo[] mi, Type[] genericTp, Type[] sigTp)
98+
{
99+
int genericCount = genericTp.Length;
100+
int signatureCount = sigTp.Length;
101+
for (int i = 0; i < mi.Length; i++)
102+
{
103+
if (!mi[i].IsGenericMethodDefinition)
104+
{
105+
continue;
106+
}
107+
Type[] genericArgs = mi[i].GetGenericArguments();
108+
if (genericArgs.Length != genericCount)
109+
{
110+
continue;
111+
}
112+
ParameterInfo[] pi = mi[i].GetParameters();
113+
if (pi.Length != signatureCount)
114+
{
115+
continue;
116+
}
117+
for (int n = 0; n < pi.Length; n++)
118+
{
119+
if (sigTp[n] != pi[n].ParameterType)
120+
{
121+
break;
122+
}
123+
if (n == (pi.Length - 1))
124+
{
125+
MethodInfo match = mi[i];
126+
if (match.IsGenericMethodDefinition)
127+
{
128+
Type[] typeArgs = match.GetGenericArguments();
129+
return match.MakeGenericMethod(genericTp);
130+
}
131+
return match;
132+
}
133+
}
134+
}
135+
return null;
136+
}
137+
138+
91139
//====================================================================
92140
// Return the array of MethodInfo for this method. The result array
93141
// is arranged in order of precendence (done lazily to avoid doing it
@@ -264,7 +312,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw,
264312
MethodInfo mi = MethodBinder.MatchParameters(methodinfo, types);
265313
return Bind(inst, args, kw, mi, null);
266314
}
267-
return null;
315+
return null;
268316
}
269317

270318
internal virtual IntPtr Invoke(IntPtr inst, IntPtr args, IntPtr kw) {

pythonnet/src/runtime/methodbinding.cs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -90,31 +90,48 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key) {
9090
//====================================================================
9191

9292
public static IntPtr tp_call(IntPtr ob, IntPtr args, IntPtr kw) {
93-
MethodBinding self = (MethodBinding)GetManagedObject(ob);
94-
95-
// This supports calling a method 'unbound', passing the instance
96-
// as the first argument. Note that this is not supported if any
97-
// of the overloads are static since we can't know if the intent
98-
// was to call the static method or the unbound instance method.
99-
100-
if ((self.target == IntPtr.Zero) && (!self.m.IsStatic())) {
101-
if (Runtime.PyTuple_Size(args) < 1) {
102-
Exceptions.SetError(Exceptions.TypeError,
103-
"not enough arguments"
104-
);
105-
return IntPtr.Zero;
106-
}
107-
int len = Runtime.PyTuple_Size(args);
108-
IntPtr uargs = Runtime.PyTuple_GetSlice(args, 1, len);
109-
IntPtr inst = Runtime.PyTuple_GetItem(args, 0);
110-
Runtime.Incref(inst);
111-
IntPtr r = self.m.Invoke(inst, uargs, kw, self.info);
112-
Runtime.Decref(inst);
113-
Runtime.Decref(uargs);
114-
return r;
115-
}
116-
117-
return self.m.Invoke(self.target, args, kw, self.info);
93+
MethodBinding self = (MethodBinding)GetManagedObject(ob);
94+
95+
// This works around a situation where the wrong generic method is picked,
96+
// for example this method in the tests: string Overloaded<T>(int arg1, int arg2, string arg3)
97+
if (self.info != null)
98+
{
99+
if (self.info.IsGenericMethod)
100+
{
101+
int len = Runtime.PyTuple_Size(args);
102+
Type[] sigTp = Runtime.PythonArgsToTypeArray(args, true);
103+
if (sigTp != null)
104+
{
105+
Type[] genericTp = self.info.GetGenericArguments();
106+
MethodInfo betterMatch = MethodBinder.MatchSignatureAndParameters(self.m.info, genericTp, sigTp);
107+
if (betterMatch != null) self.info = betterMatch;
108+
}
109+
}
110+
}
111+
112+
// This supports calling a method 'unbound', passing the instance
113+
// as the first argument. Note that this is not supported if any
114+
// of the overloads are static since we can't know if the intent
115+
// was to call the static method or the unbound instance method.
116+
117+
if ((self.target == IntPtr.Zero) && (!self.m.IsStatic()))
118+
{
119+
int len = Runtime.PyTuple_Size(args);
120+
if (len < 1)
121+
{
122+
Exceptions.SetError(Exceptions.TypeError, "not enough arguments");
123+
return IntPtr.Zero;
124+
}
125+
IntPtr uargs = Runtime.PyTuple_GetSlice(args, 1, len);
126+
IntPtr inst = Runtime.PyTuple_GetItem(args, 0);
127+
Runtime.Incref(inst);
128+
IntPtr r = self.m.Invoke(inst, uargs, kw, self.info);
129+
Runtime.Decref(inst);
130+
Runtime.Decref(uargs);
131+
return r;
132+
}
133+
134+
return self.m.Invoke(self.target, args, kw, self.info);
118135
}
119136

120137

pythonnet/src/testing/generictest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ public U Overloaded<Q, U>(Q arg1, U arg2) {
8888
return arg2;
8989
}
9090

91+
public string Overloaded<T>(int arg1, int arg2, string arg3) {
92+
return arg3;
93+
}
94+
9195
}
9296

9397
public class GenericStaticMethodTest<T> {
@@ -110,6 +114,10 @@ public static U Overloaded<Q, U>(Q arg1, U arg2) {
110114
return arg2;
111115
}
112116

117+
public static string Overloaded<T>(int arg1, int arg2, string arg3) {
118+
return arg3;
119+
}
120+
113121
}
114122

115123

pythonnet/src/tests/test_generic.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@ def testGenericMethodOverloadSelection(self):
378378
self.failUnless(value == True)
379379

380380
# public static U Overloaded<Q, U>(Q arg1, U arg2)
381-
#value = type.Overloaded[bool, str](True, "true")
382-
#self.failUnless(value == "true")
381+
value = type.Overloaded[bool, str](True, "true")
382+
self.failUnless(value == "true")
383383

384384
# public U Overloaded<Q, U>(Q arg1, U arg2)
385385
value = inst.Overloaded[bool, str](True, "true")
@@ -393,6 +393,14 @@ def testGenericMethodOverloadSelection(self):
393393
value = inst.Overloaded[str, bool]("true", True)
394394
self.failUnless(value == True)
395395

396+
# public static string Overloaded<T>(int arg1, int arg2, string arg3)
397+
value = type.Overloaded[str](123, 456, "success")
398+
self.failUnless(value == "success")
399+
400+
# public string Overloaded<T>(int arg1, int arg2, string arg3)
401+
value = inst.Overloaded[str](123, 456, "success")
402+
self.failUnless(value == "success")
403+
396404
def test():
397405
value = type.Overloaded[str, bool, int]("true", True, 1)
398406
self.failUnlessRaises(TypeError, test)
@@ -735,3 +743,4 @@ def main():
735743
if __name__ == '__main__':
736744
main()
737745

746+

0 commit comments

Comments
 (0)