22using System . Collections ;
33using System . Collections . Generic ;
44using System . Linq ;
5- using System . Numerics ;
65using System . Reflection ;
76using System . Text ;
87
@@ -320,22 +319,46 @@ internal List<MethodInformation> GetMethods()
320319 /// See: https://github.com/jythontools/jython/blob/master/src/org/python/core/ReflectedArgs.java#L192
321320 /// </remarks>
322321 private static int GetPrecedence ( MethodInformation methodInformation )
322+ {
323+ return GetMatchedArgumentsPrecedence ( methodInformation , null , null ) ;
324+ }
325+
326+ /// <summary>
327+ /// Gets the precedence of a method's arguments, considering only those arguments that have been matched,
328+ /// that is, those that are not default values.
329+ /// </summary>
330+ private static int GetMatchedArgumentsPrecedence ( MethodInformation methodInformation , int ? matchedPositionalArgsCount , IEnumerable < string > matchedKwargsNames )
323331 {
324332 ParameterInfo [ ] pi = methodInformation . ParameterInfo ;
325333 var mi = methodInformation . MethodBase ;
326334 int val = mi . IsStatic ? 3000 : 0 ;
327- int num = pi . Length ;
335+ var isOperatorMethod = OperatorMethod . IsOperatorMethod ( methodInformation . MethodBase ) ;
328336
329337 val += mi . IsGenericMethod ? 1 : 0 ;
330- for ( var i = 0 ; i < num ; i ++ )
338+
339+ if ( ! matchedPositionalArgsCount . HasValue )
340+ {
341+ for ( var i = 0 ; i < pi . Length ; i ++ )
342+ {
343+ val += ArgPrecedence ( pi [ i ] . ParameterType , isOperatorMethod ) ;
344+ }
345+ }
346+ else
331347 {
332- val += ArgPrecedence ( pi [ i ] . ParameterType , methodInformation ) ;
348+ matchedKwargsNames ??= Array . Empty < string > ( ) ;
349+ for ( var i = 0 ; i < pi . Length ; i ++ )
350+ {
351+ if ( i < matchedPositionalArgsCount || matchedKwargsNames . Contains ( methodInformation . ParameterNames [ i ] ) )
352+ {
353+ val += ArgPrecedence ( pi [ i ] . ParameterType , isOperatorMethod ) ;
354+ }
355+ }
333356 }
334357
335358 var info = mi as MethodInfo ;
336359 if ( info != null )
337360 {
338- val += ArgPrecedence ( info . ReturnType , methodInformation ) ;
361+ val += ArgPrecedence ( info . ReturnType , isOperatorMethod ) ;
339362 if ( mi . DeclaringType == mi . ReflectedType )
340363 {
341364 val += methodInformation . IsOriginal ? 0 : 300000 ;
@@ -352,15 +375,15 @@ private static int GetPrecedence(MethodInformation methodInformation)
352375 /// <summary>
353376 /// Return a precedence value for a particular Type object.
354377 /// </summary>
355- internal static int ArgPrecedence ( Type t , MethodInformation mi )
378+ internal static int ArgPrecedence ( Type t , bool isOperatorMethod )
356379 {
357380 Type objectType = typeof ( object ) ;
358381 if ( t == objectType )
359382 {
360383 return 3000 ;
361384 }
362385
363- if ( t . IsAssignableFrom ( typeof ( PyObject ) ) && ! OperatorMethod . IsOperatorMethod ( mi . MethodBase ) )
386+ if ( t . IsAssignableFrom ( typeof ( PyObject ) ) && ! isOperatorMethod )
364387 {
365388 return - 1 ;
366389 }
@@ -372,7 +395,7 @@ internal static int ArgPrecedence(Type t, MethodInformation mi)
372395 {
373396 return 2500 ;
374397 }
375- return 100 + ArgPrecedence ( e , mi ) ;
398+ return 100 + ArgPrecedence ( e , isOperatorMethod ) ;
376399 }
377400
378401 TypeCode tc = Type . GetTypeCode ( t ) ;
@@ -452,6 +475,7 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe
452475 var methods = info == null ? GetMethods ( )
453476 : new List < MethodInformation > ( 1 ) { new MethodInformation ( info , true ) } ;
454477
478+ int pyArgCount = ( int ) Runtime . PyTuple_Size ( args ) ;
455479 var matches = new List < MatchedMethod > ( methods . Count ) ;
456480 List < MatchedMethod > matchesUsingImplicitConversion = null ;
457481
@@ -463,7 +487,6 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe
463487 var pi = methodInformation . ParameterInfo ;
464488 // Avoid accessing the parameter names property unless necessary
465489 var paramNames = hasNamedArgs ? methodInformation . ParameterNames : Array . Empty < string > ( ) ;
466- int pyArgCount = ( int ) Runtime . PyTuple_Size ( args ) ;
467490
468491 // Special case for operators
469492 bool isOperator = OperatorMethod . IsOperatorMethod ( mi ) ;
@@ -695,7 +718,7 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe
695718 }
696719 }
697720
698- var match = new MatchedMethod ( kwargsMatched , margs , outs , mi ) ;
721+ var match = new MatchedMethod ( kwargsMatched , margs , outs , methodInformation ) ;
699722 if ( usedImplicitConversion )
700723 {
701724 if ( matchesUsingImplicitConversion == null )
@@ -718,8 +741,17 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe
718741 // We favor matches that do not use implicit conversion
719742 var matchesTouse = matches . Count > 0 ? matches : matchesUsingImplicitConversion ;
720743
721- // The best match would be the one with the most named arguments matched
722- var bestMatch = matchesTouse . MaxBy ( x => x . KwargsMatched ) ;
744+ // The best match would be the one with the most named arguments matched.
745+ // But if multiple matches have the same max number of named arguments matched,
746+ // we solve the ambiguity by taking the one with the highest precedence but only
747+ // considering the actual arguments passed, ignoring the optional arguments for
748+ // which the default values were used
749+ var bestMatch = matchesTouse
750+ . GroupBy ( x => x . KwargsMatched )
751+ . OrderByDescending ( x => x . Key )
752+ . First ( )
753+ . MinBy ( x => GetMatchedArgumentsPrecedence ( x . MethodInformation , pyArgCount , kwArgDict ? . Keys ) ) ;
754+
723755 var margs = bestMatch . ManagedArgs ;
724756 var outs = bestMatch . Outs ;
725757 var mi = bestMatch . Method ;
@@ -1084,14 +1116,15 @@ private readonly struct MatchedMethod
10841116 public int KwargsMatched { get ; }
10851117 public object ? [ ] ManagedArgs { get ; }
10861118 public int Outs { get ; }
1087- public MethodBase Method { get ; }
1119+ public MethodInformation MethodInformation { get ; }
1120+ public MethodBase Method => MethodInformation . MethodBase ;
10881121
1089- public MatchedMethod ( int kwargsMatched , object ? [ ] margs , int outs , MethodBase mb )
1122+ public MatchedMethod ( int kwargsMatched , object ? [ ] margs , int outs , MethodInformation methodInformation )
10901123 {
10911124 KwargsMatched = kwargsMatched ;
10921125 ManagedArgs = margs ;
10931126 Outs = outs ;
1094- Method = mb ;
1127+ MethodInformation = methodInformation ;
10951128 }
10961129 }
10971130
0 commit comments