22using System . Collections ;
33using System . Collections . Generic ;
44using System . Linq ;
5- using System . Numerics ;
65using System . Reflection ;
76using System . Text ;
87
@@ -320,18 +319,40 @@ 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 ;
328-
329335 var isOperatorMethod = OperatorMethod . IsOperatorMethod ( methodInformation . MethodBase ) ;
330336
331337 val += mi . IsGenericMethod ? 1 : 0 ;
332- 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
333347 {
334- val += ArgPrecedence ( pi [ i ] . ParameterType , isOperatorMethod ) ;
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+ }
335356 }
336357
337358 var info = mi as MethodInfo ;
@@ -351,32 +372,6 @@ private static int GetPrecedence(MethodInformation methodInformation)
351372 return val ;
352373 }
353374
354- /// <summary>
355- /// Gets the precedence of a method's arguments, considering only those arguments that have been matched,
356- /// that is, those that are not default values.
357- /// </summary>
358- private static int GetMatchedArgumentsPrecedence ( MethodInformation method , int matchedPositionalArgsCount , IEnumerable < string > matchedKwargsNames )
359- {
360- var isOperatorMethod = OperatorMethod . IsOperatorMethod ( method . MethodBase ) ;
361- var pi = method . ParameterInfo ;
362- var val = 0 ;
363- for ( var i = 0 ; i < pi . Length ; i ++ )
364- {
365- if ( i < matchedPositionalArgsCount || matchedKwargsNames . Contains ( method . ParameterNames [ i ] ) )
366- {
367- val += ArgPrecedence ( pi [ i ] . ParameterType , isOperatorMethod ) ;
368- }
369- }
370-
371- var mi = method . MethodBase ;
372- var info = mi as MethodInfo ;
373- if ( info != null )
374- {
375- val += ArgPrecedence ( info . ReturnType , isOperatorMethod ) ;
376- }
377- return val ;
378- }
379-
380375 /// <summary>
381376 /// Return a precedence value for a particular Type object.
382377 /// </summary>
@@ -390,7 +385,7 @@ internal static int ArgPrecedence(Type t, bool isOperatorMethod)
390385
391386 if ( t . IsAssignableFrom ( typeof ( PyObject ) ) && ! isOperatorMethod )
392387 {
393- return - 3000 ;
388+ return - 1 ;
394389 }
395390
396391 if ( t . IsArray )
@@ -746,26 +741,16 @@ internal Binding Bind(BorrowedReference inst, BorrowedReference args, BorrowedRe
746741 // We favor matches that do not use implicit conversion
747742 var matchesTouse = matches . Count > 0 ? matches : matchesUsingImplicitConversion ;
748743
749- // The best match would be the one with the most named arguments matched
750- var maxKwargsMatched = matchesTouse . Max ( x => x . KwargsMatched ) ;
751- // Don't materialize the enumerable, just enumerate twice if necessary to avoid creating a collection instance.
752- var bestMatches = matchesTouse . Where ( x => x . KwargsMatched == maxKwargsMatched ) ;
753- var bestMatchesCount = bestMatches . Count ( ) ;
754-
755- MatchedMethod bestMatch ;
756- // Multiple best matches, we can still resolve the ambiguity because
757- // some method might take precedence if it received PyObject instances.
758- // So let's get the best match by the precedence of the actual passed arguments,
759- // without considering optional arguments without a passed value
760- if ( bestMatchesCount > 1 )
761- {
762- bestMatch = bestMatches . MinBy ( x => GetMatchedArgumentsPrecedence ( methods . First ( m => m . MethodBase == x . Method ) , pyArgCount ,
763- kwArgDict ? . Keys ?? Enumerable . Empty < string > ( ) ) ) ;
764- }
765- else
766- {
767- bestMatch = bestMatches . First ( ) ;
768- }
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 ( methods . First ( m => m . MethodBase == x . Method ) , pyArgCount , kwArgDict ? . Keys ) ) ;
769754
770755 var margs = bestMatch . ManagedArgs ;
771756 var outs = bestMatch . Outs ;
0 commit comments