From 834ef33929a145730fc9c96cad07a3a127e91d80 Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Fri, 20 Aug 2021 17:52:12 -0700 Subject: [PATCH 1/2] Fix #1523 Adds a preferred type parameter to GetTypeByAlias and then the preferred type gets passed in in methodbinder's TryComputeClrArgumentType --- src/runtime/converter.cs | 16 +++++++++++++--- src/runtime/methodbinder.cs | 2 +- src/testing/conversiontest.cs | 17 +++++++++++++++-- tests/test_conversion.py | 14 +++++++++++++- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/runtime/converter.cs b/src/runtime/converter.cs index 420fc9435..dd8598e33 100644 --- a/src/runtime/converter.cs +++ b/src/runtime/converter.cs @@ -29,6 +29,8 @@ private Converter() private static Type int64Type; private static Type boolType; private static Type typeType; + private static Type uint64Type; + private static Type uint32Type; static Converter() { @@ -43,13 +45,15 @@ static Converter() decimalType = typeof(Decimal); boolType = typeof(Boolean); typeType = typeof(Type); + uint64Type = typeof(UInt64); + uint32Type = typeof(UInt32); } /// /// Given a builtin Python type, return the corresponding CLR type. /// - internal static Type GetTypeByAlias(IntPtr op) + internal static Type GetTypeByAlias(IntPtr op, Type preferred=null) { if (op == Runtime.PyStringType) return stringType; @@ -57,12 +61,18 @@ internal static Type GetTypeByAlias(IntPtr op) if (op == Runtime.PyUnicodeType) return stringType; - if (op == Runtime.PyIntType) + if (op == Runtime.PyIntType && (preferred == null || preferred == int32Type)) return int32Type; - if (op == Runtime.PyLongType) + if (op == Runtime.PyIntType && (preferred == null || preferred == uint32Type)) + return uint32Type; + + if (op == Runtime.PyLongType && (preferred == null || preferred == int64Type)) return int64Type; + if (op == Runtime.PyLongType && (preferred == null || preferred == uint64Type)) + return uint64Type; + if (op == Runtime.PyFloatType) return doubleType; diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 1b7cc4736..090e34f10 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -725,7 +725,7 @@ static Type TryComputeClrArgumentType(Type parameterType, IntPtr argument, bool pyoptype = Runtime.PyObject_Type(argument); if (pyoptype != IntPtr.Zero) { - clrtype = Converter.GetTypeByAlias(pyoptype); + clrtype = Converter.GetTypeByAlias(pyoptype, parameterType); } Runtime.XDecref(pyoptype); } diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs index 1f9d64e1b..f67551bb7 100644 --- a/src/testing/conversiontest.cs +++ b/src/testing/conversiontest.cs @@ -42,7 +42,7 @@ public ConversionTest() } - + public interface ISpam { @@ -63,7 +63,7 @@ public string GetValue() return value; } } - + public class UnicodeString { public string value = "안녕"; @@ -78,4 +78,17 @@ public override string ToString() return value; } } + + public class MethodResolutionInt + { + public IEnumerable MethodA(ulong address, int size) + { + return new byte[10]; + } + + public int MethodA(string dummy, ulong address, int size) + { + return 0; + } + } } diff --git a/tests/test_conversion.py b/tests/test_conversion.py index eec2bcde6..8fa972a3e 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -4,7 +4,7 @@ import pytest import System -from Python.Test import ConversionTest, UnicodeString +from Python.Test import ConversionTest, UnicodeString, MethodResolutionInt from Python.Runtime import PyObjectConversions from Python.Runtime.Codecs import RawProxyEncoder @@ -681,3 +681,15 @@ def CanEncode(self, clr_type): l = ob.ListField l.Add(42) assert ob.ListField.Count == 1 + +def test_int_param_resolution_required(): + """Test resolution of `int` parameters when resolution is needed""" + + mri = MethodResolutionInt() + data = list(mri.MethodA(0x1000, 10)) + assert len(data) == 10 + assert data[0] == 0 + + data = list(mri.MethodA(0x100000000, 10)) + assert len(data) == 10 + assert data[0] == 0 From 01eee1dc6ae614a7da702b428fe44401ac0b70d7 Mon Sep 17 00:00:00 2001 From: Alex Earl Date: Mon, 23 Aug 2021 10:16:38 -0700 Subject: [PATCH 2/2] Sort the matching methods prior to final match This sorts matching methods so that non PyObject overloads are looked at first. The other option for this would be to change the test such that it determines the type before assuming that it is a tuple (See public static bool operator <=(OperableObject a, PyObject b) int TestOperator.cs) --- src/runtime/methodbinder.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs index 090e34f10..b72e3d963 100644 --- a/src/runtime/methodbinder.cs +++ b/src/runtime/methodbinder.cs @@ -330,6 +330,7 @@ public MatchedMethod(int kwargsMatched, int defaultsNeeded, object[] margs, int ManagedArgs = margs; Outs = outs; Method = mb; + HasPyObjectParameter = mb.GetParameters().Any(x => x.ParameterType == typeof(PyObject)); } public int KwargsMatched { get; } @@ -337,6 +338,7 @@ public MatchedMethod(int kwargsMatched, int defaultsNeeded, object[] margs, int public object[] ManagedArgs { get; } public int Outs { get; } public MethodBase Method { get; } + public bool HasPyObjectParameter { get; } } private readonly struct MismatchedMethod @@ -470,8 +472,15 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth var matchedMethod = new MatchedMethod(kwargsMatched, defaultsNeeded, margs, outs, mi); argMatchedMethods.Add(matchedMethod); } + if (argMatchedMethods.Count > 0) { + // order the matched methods such that PyObject overrides are last, we only care about + // this when there is more than one arg matched method + if (argMatchedMethods.Count > 1) + { + argMatchedMethods = argMatchedMethods.OrderBy(x => x.HasPyObjectParameter).ToList(); + } var bestKwargMatchCount = argMatchedMethods.Max(x => x.KwargsMatched); var fewestDefaultsRequired = argMatchedMethods.Where(x => x.KwargsMatched == bestKwargMatchCount).Min(x => x.DefaultsNeeded);