From bf1ca889f84bddea770a2495617cce681aee3357 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 10 Aug 2022 14:18:42 +0200 Subject: [PATCH 1/3] Explicit float and int conversion for builtin types --- src/runtime/Native/ITypeOffsets.cs | 1 + src/runtime/Native/TypeOffset.cs | 1 + src/runtime/Types/ClassBase.cs | 25 +++++++++++++++++++++++++ src/runtime/Types/OperatorMethod.cs | 1 + tests/test_conversion.py | 9 +++++++++ 5 files changed, 37 insertions(+) diff --git a/src/runtime/Native/ITypeOffsets.cs b/src/runtime/Native/ITypeOffsets.cs index 2c4fdf59a..0920a312a 100644 --- a/src/runtime/Native/ITypeOffsets.cs +++ b/src/runtime/Native/ITypeOffsets.cs @@ -22,6 +22,7 @@ interface ITypeOffsets int nb_true_divide { get; } int nb_and { get; } int nb_int { get; } + int nb_float { get; } int nb_or { get; } int nb_xor { get; } int nb_lshift { get; } diff --git a/src/runtime/Native/TypeOffset.cs b/src/runtime/Native/TypeOffset.cs index 59c5c3ee0..847f1766d 100644 --- a/src/runtime/Native/TypeOffset.cs +++ b/src/runtime/Native/TypeOffset.cs @@ -31,6 +31,7 @@ static partial class TypeOffset internal static int nb_or { get; private set; } internal static int nb_xor { get; private set; } internal static int nb_int { get; private set; } + internal static int nb_float { get; private set; } internal static int nb_lshift { get; private set; } internal static int nb_rshift { get; private set; } internal static int nb_remainder { get; private set; } diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index 1e3c325cc..a48c72f32 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -529,6 +529,13 @@ static NewReference tp_call_impl(BorrowedReference ob, BorrowedReference args, B return callBinder.Invoke(ob, args, kw); } + static NewReference DoConvert(BorrowedReference ob) + { + var self = (CLRObject)GetManagedObject(ob)!; + using var python = self.inst.ToPython(); + return python.NewReferenceOrNull(); + } + static IEnumerable GetCallImplementations(Type type) => type.GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == "__call__"); @@ -564,6 +571,24 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH { TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.mp_length, new Interop.B_P(MpLengthSlot.impl), slotsHolder); } + + switch (Type.GetTypeCode(type.Value)) + { + case TypeCode.Byte: + case TypeCode.SByte: + case TypeCode.UInt16: + case TypeCode.UInt32: + case TypeCode.UInt64: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvert), slotsHolder); + break; + case TypeCode.Double: + case TypeCode.Single: + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvert), slotsHolder); + break; + } } public virtual bool HasCustomNew() => this.GetType().GetMethod("tp_new") is not null; diff --git a/src/runtime/Types/OperatorMethod.cs b/src/runtime/Types/OperatorMethod.cs index 0c6127f65..88939a51d 100644 --- a/src/runtime/Types/OperatorMethod.cs +++ b/src/runtime/Types/OperatorMethod.cs @@ -53,6 +53,7 @@ static OperatorMethod() ["op_UnaryPlus"] = new SlotDefinition("__pos__", TypeOffset.nb_positive), ["__int__"] = new SlotDefinition("__int__", TypeOffset.nb_int), + ["__float__"] = new SlotDefinition("__float__", TypeOffset.nb_float), }; ComparisonOpMap = new Dictionary { diff --git a/tests/test_conversion.py b/tests/test_conversion.py index f8d8039c6..f5ceb45a1 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -720,3 +720,12 @@ def test_intptr_construction(): with pytest.raises(OverflowError): UIntPtr(v) +def test_explicit_conversion(): + from System import Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte + from System import Double, Single + + for t in [Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte]: + assert int(t(127)) == 127 + + for t in [Single, Double]: + assert float(t(0.125)) == 0.125 From 881b2b24e6a8e5ace4b99b78dbf569b2303e7a88 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 11 Aug 2022 09:28:02 +0200 Subject: [PATCH 2/3] Add conversions for int->float and bool->(int,float) --- src/runtime/Types/ClassBase.cs | 20 ++++++++++++++++++++ tests/test_conversion.py | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index a48c72f32..260427246 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -536,6 +536,20 @@ static NewReference DoConvert(BorrowedReference ob) return python.NewReferenceOrNull(); } + static NewReference DoConvertBooleanInt(BorrowedReference ob) + { + var self = (CLRObject)GetManagedObject(ob)!; + using var python = ((bool)self.inst ? 1 : 0).ToPython(); + return python.NewReferenceOrNull(); + } + + static NewReference DoConvertIntFloat(BorrowedReference ob) + { + var self = (CLRObject)GetManagedObject(ob)!; + using var python = (Convert.ToDouble(self.inst)).ToPython(); + return python.NewReferenceOrNull(); + } + static IEnumerable GetCallImplementations(Type type) => type.GetMethods(BindingFlags.Public | BindingFlags.Instance) .Where(m => m.Name == "__call__"); @@ -574,6 +588,11 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH switch (Type.GetTypeCode(type.Value)) { + case TypeCode.Boolean: + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvertBooleanInt), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertIntFloat), slotsHolder); + break; + case TypeCode.Byte: case TypeCode.SByte: case TypeCode.UInt16: @@ -583,6 +602,7 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH case TypeCode.Int32: case TypeCode.Int64: TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvert), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertIntFloat), slotsHolder); break; case TypeCode.Double: case TypeCode.Single: diff --git a/tests/test_conversion.py b/tests/test_conversion.py index f5ceb45a1..3772c95cc 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -721,11 +721,16 @@ def test_intptr_construction(): UIntPtr(v) def test_explicit_conversion(): - from System import Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte + from System import ( + Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte, Boolean + ) from System import Double, Single + assert int(Boolean(True)) == 1 + for t in [Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte]: assert int(t(127)) == 127 + assert float(t(127)) == 127.0 for t in [Single, Double]: assert float(t(0.125)) == 0.125 From 3cbdfdc6856783d23bc096409bf00f25aaa7bdee Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 11 Aug 2022 21:37:39 +0200 Subject: [PATCH 3/3] Use direct conversion functions and add more tests --- src/runtime/Types/ClassBase.cs | 39 +++++++++++++++++++++------------- tests/test_conversion.py | 13 +++++++++++- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index 260427246..943841e25 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -536,18 +536,28 @@ static NewReference DoConvert(BorrowedReference ob) return python.NewReferenceOrNull(); } + static NewReference DoConvertInt(BorrowedReference ob) + { + var self = (CLRObject)GetManagedObject(ob)!; + return Runtime.PyLong_FromLongLong(Convert.ToInt64(self.inst)); + } + + static NewReference DoConvertUInt(BorrowedReference ob) + { + var self = (CLRObject)GetManagedObject(ob)!; + return Runtime.PyLong_FromUnsignedLongLong(Convert.ToUInt64(self.inst)); + } + static NewReference DoConvertBooleanInt(BorrowedReference ob) { var self = (CLRObject)GetManagedObject(ob)!; - using var python = ((bool)self.inst ? 1 : 0).ToPython(); - return python.NewReferenceOrNull(); + return Runtime.PyInt_FromInt32((bool)self.inst ? 1 : 0); } - static NewReference DoConvertIntFloat(BorrowedReference ob) + static NewReference DoConvertFloat(BorrowedReference ob) { var self = (CLRObject)GetManagedObject(ob)!; - using var python = (Convert.ToDouble(self.inst)).ToPython(); - return python.NewReferenceOrNull(); + return Runtime.PyFloat_FromDouble(Convert.ToDouble(self.inst)); } static IEnumerable GetCallImplementations(Type type) @@ -589,24 +599,23 @@ public virtual void InitializeSlots(BorrowedReference pyType, SlotsHolder slotsH switch (Type.GetTypeCode(type.Value)) { case TypeCode.Boolean: - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvertBooleanInt), slotsHolder); - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertIntFloat), slotsHolder); + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvertInt), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertFloat), slotsHolder); break; - case TypeCode.Byte: - case TypeCode.SByte: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: - case TypeCode.Int16: - case TypeCode.Int32: - case TypeCode.Int64: - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvert), slotsHolder); - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertIntFloat), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_int, new Interop.B_N(DoConvertUInt), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertFloat), slotsHolder); break; case TypeCode.Double: case TypeCode.Single: - TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvert), slotsHolder); + TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.nb_float, new Interop.B_N(DoConvertFloat), slotsHolder); break; } } diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 3772c95cc..69e7ec63f 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -726,11 +726,22 @@ def test_explicit_conversion(): ) from System import Double, Single + assert int(Boolean(False)) == 0 assert int(Boolean(True)) == 1 - for t in [Int64, UInt64, Int32, UInt32, Int16, UInt16, Byte, SByte]: + for t in [UInt64, UInt32, UInt16, Byte]: assert int(t(127)) == 127 assert float(t(127)) == 127.0 + for t in [Int64, Int32, Int16, SByte]: + assert int(t(127)) == 127 + assert int(t(-127)) == -127 + assert float(t(127)) == 127.0 + assert float(t(-127)) == -127.0 + + assert int(Int64.MaxValue) == 2**63 - 1 + assert int(Int64.MinValue) == -2**63 + assert int(UInt64.MaxValue) == 2**64 - 1 + for t in [Single, Double]: assert float(t(0.125)) == 0.125