From 1c21526b8c6ad39dc4c088c618e29f703666ca2a Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 8 Jul 2022 20:34:18 +0200 Subject: [PATCH 1/6] Add unit tests for (U)IntPtr conversions --- src/testing/conversiontest.cs | 7 +++++-- tests/test_conversion.py | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs index 7a00f139e..272bb74c2 100644 --- a/src/testing/conversiontest.cs +++ b/src/testing/conversiontest.cs @@ -1,5 +1,6 @@ namespace Python.Test { + using System; using System.Collections.Generic; /// @@ -26,6 +27,8 @@ public ConversionTest() public ulong UInt64Field = 0; public float SingleField = 0.0F; public double DoubleField = 0.0; + public IntPtr IntPtrField = IntPtr.Zero; + public UIntPtr UIntPtrField = UIntPtr.Zero; public decimal DecimalField = 0; public string StringField; public ShortEnum EnumField; @@ -42,7 +45,7 @@ public ConversionTest() } - + public interface ISpam { @@ -63,7 +66,7 @@ public string GetValue() return value; } } - + public class UnicodeString { public string value = "안녕"; diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 6693d8000..1f89b3e0c 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -25,7 +25,7 @@ def test_bool_conversion(): with pytest.raises(TypeError): ob.BooleanField = 1 - + with pytest.raises(TypeError): ob.BooleanField = 0 @@ -679,3 +679,20 @@ def test_iconvertible_conversion(): assert 1024 == change_type(1024, System.Int32) assert 1024 == change_type(1024, System.Int64) assert 1024 == change_type(1024, System.Int16) + +def test_intptr_conversion(): + from System import IntPtr, UIntPtr, Int64 + + ob = ConversionTest() + + assert ob.IntPtrField == IntPtr.Zero + assert ob.UIntPtrField == UIntPtr.Zero + + ob.IntPtrField = IntPtr(-1) + assert ob.IntPtrField == IntPtr(-1) + + ob.IntPtrField = IntPtr(Int64(1024)) + assert ob.IntPtrField == IntPtr(1024) + + ob.UIntPtrField = ob.IntPtrField + assert ob.UIntPtrField == UIntPtr(1024) From 004232f496645ee7377394737ea647cc739e8f62 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 9 Jul 2022 16:05:27 +0200 Subject: [PATCH 2/6] Special-case construction of (U)IntPtr --- src/runtime/Converter.cs | 30 ++++++++++++++++++++++++++++++ tests/test_conversion.py | 14 +++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index f86ba7900..5329db555 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -298,6 +298,36 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType, { case CLRObject co: object tmp = co.inst; + if (obType == typeof(IntPtr)) + { + switch (tmp) + { + case nint val: + result = new IntPtr(val); + return true; + case Int64 val: + result = new IntPtr(val); + return true; + case Int32 val: + result = new IntPtr(val); + return true; + } + } + if (obType == typeof(UIntPtr)) + { + switch (tmp) + { + case nuint val: + result = new UIntPtr(val); + return true; + case UInt64 val: + result = new UIntPtr(val); + return true; + case UInt32 val: + result = new UIntPtr(val); + return true; + } + } if (obType.IsInstanceOfType(tmp)) { result = tmp; diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 1f89b3e0c..97c357382 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -680,19 +680,19 @@ def test_iconvertible_conversion(): assert 1024 == change_type(1024, System.Int64) assert 1024 == change_type(1024, System.Int16) -def test_intptr_conversion(): - from System import IntPtr, UIntPtr, Int64 +def test_intptr_construction(): + from System import IntPtr, UIntPtr, Int64, UInt64 ob = ConversionTest() assert ob.IntPtrField == IntPtr.Zero assert ob.UIntPtrField == UIntPtr.Zero - ob.IntPtrField = IntPtr(-1) - assert ob.IntPtrField == IntPtr(-1) + ob.IntPtrField = IntPtr(Int64(-1)) + assert ob.IntPtrField.ToInt64() == -1 ob.IntPtrField = IntPtr(Int64(1024)) - assert ob.IntPtrField == IntPtr(1024) + assert ob.IntPtrField.ToInt64() == 1024 - ob.UIntPtrField = ob.IntPtrField - assert ob.UIntPtrField == UIntPtr(1024) + ob.UIntPtrField = UIntPtr(UInt64(1024)) + assert ob.UIntPtrField.ToUInt64() == 1024 From c88b1f6fbfe8cf585b15590122f13cd6199b9d79 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 9 Jul 2022 17:55:39 +0200 Subject: [PATCH 3/6] Move ptr construction to ClassObject --- src/runtime/Converter.cs | 32 +-------- src/runtime/Runtime.cs | 7 -- src/runtime/Types/ClassObject.cs | 117 +++++++++++++++++++++++++++---- 3 files changed, 103 insertions(+), 53 deletions(-) diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index 5329db555..e1820f05b 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -298,36 +298,6 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType, { case CLRObject co: object tmp = co.inst; - if (obType == typeof(IntPtr)) - { - switch (tmp) - { - case nint val: - result = new IntPtr(val); - return true; - case Int64 val: - result = new IntPtr(val); - return true; - case Int32 val: - result = new IntPtr(val); - return true; - } - } - if (obType == typeof(UIntPtr)) - { - switch (tmp) - { - case nuint val: - result = new UIntPtr(val); - return true; - case UInt64 val: - result = new UIntPtr(val); - return true; - case UInt32 val: - result = new UIntPtr(val); - return true; - } - } if (obType.IsInstanceOfType(tmp)) { result = tmp; @@ -391,7 +361,7 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType, // conversions (Python string -> managed string). if (obType == objectType) { - if (Runtime.IsStringType(value)) + if (Runtime.PyString_Check(value)) { return ToPrimitive(value, stringType, out result, setError); } diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index 1eeb96b54..7e6210f90 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -1281,13 +1281,6 @@ internal static bool PyFloat_Check(BorrowedReference ob) //==================================================================== // Python string API //==================================================================== - internal static bool IsStringType(BorrowedReference op) - { - BorrowedReference t = PyObject_TYPE(op); - return (t == PyStringType) - || (t == PyUnicodeType); - } - internal static bool PyString_Check(BorrowedReference ob) { return PyObject_TYPE(ob) == PyStringType; diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 474e9dd7b..f68cf8b15 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -70,22 +70,9 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo // Primitive types do not have constructors, but they look like // they do from Python. If the ClassObject represents one of the // convertible primitive types, just convert the arg directly. - if (type.IsPrimitive || type == typeof(string)) + if (type.IsPrimitive) { - if (Runtime.PyTuple_Size(args) != 1) - { - Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); - return default; - } - - BorrowedReference op = Runtime.PyTuple_GetItem(args, 0); - - if (!Converter.ToManaged(op, type, out var result, true)) - { - return default; - } - - return CLRObject.GetReference(result!, tp); + return NewPrimitive(tp, args, type); } if (type.IsAbstract) @@ -99,6 +86,11 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo return NewEnum(type, args, tp); } + if (type == typeof(string)) + { + return NewString(args, tp); + } + if (IsGenericNullable(type)) { // Nullable has special handling in .NET runtime. @@ -112,6 +104,101 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo return self.NewObjectToPython(obj, tp); } + /// + /// Construct a new .NET String object from Python args + /// + private static NewReference NewString(BorrowedReference args, BorrowedReference tp) + { + if (Runtime.PyTuple_Size(args) == 1) + { + BorrowedReference ob = Runtime.PyTuple_GetItem(args, 0); + if (Runtime.PyString_Check(ob)) + { + if (Runtime.GetManagedString(ob) is string val) + return CLRObject.GetReference(val, tp); + } + + // TODO: Initialise using constructors instead + + Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); + return default; + } + + return default; + } + + /// + /// Create a new Python object for a primitive type + /// + /// The primitive types are Boolean, Byte, SByte, Int16, UInt16, + /// Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, + /// and Single. + /// + /// All numeric types and Boolean can be handled by a simple + /// conversion, (U)IntPtr has to be handled separately as we + /// do not want to convert them automically to/from integers. + /// + /// .NET type to construct + /// Corresponding Python type + /// Constructor arguments + private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference args, Type type) + { + // TODO: Handle IntPtr + if (Runtime.PyTuple_Size(args) != 1) + { + Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments"); + return default; + } + + BorrowedReference op = Runtime.PyTuple_GetItem(args, 0); + object? result = null; + + if (type == typeof(IntPtr)) + { + if (ManagedType.GetManagedObject(op) is CLRObject clrObject) + { + switch (clrObject.inst) + { + case nint val: + result = new IntPtr(val); + break; + case Int64 val: + result = new IntPtr(val); + break; + case Int32 val: + result = new IntPtr(val); + break; + } + } + } + + if (type == typeof(UIntPtr)) + { + if (ManagedType.GetManagedObject(op) is CLRObject clrObject) + { + switch (clrObject.inst) + { + case nuint val: + result = new UIntPtr(val); + break; + case UInt64 val: + result = new UIntPtr(val); + break; + case UInt32 val: + result = new UIntPtr(val); + break; + } + } + } + + if (result == null && !Converter.ToManaged(op, type, out result, true)) + { + return default; + } + + return CLRObject.GetReference(result!, tp); + } + protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder) { TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.tp_new, new Interop.BBB_N(tp_new_impl), slotsHolder); From 65a6408ac772b3c071aa34ce977f54224743ce92 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 11 Jul 2022 11:41:10 +0200 Subject: [PATCH 4/6] Add int constructor for (U)IntPtr --- src/runtime/Types/ClassObject.cs | 18 +++++++++++++++++- tests/test_conversion.py | 3 +++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index f68cf8b15..97216f24b 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -129,7 +129,7 @@ private static NewReference NewString(BorrowedReference args, BorrowedReference /// /// Create a new Python object for a primitive type - /// + /// /// The primitive types are Boolean, Byte, SByte, Int16, UInt16, /// Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, /// and Single. @@ -170,6 +170,14 @@ private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference break; } } + else if (Runtime.PyInt_Check(op)) + { + long? num = Runtime.PyLong_AsLongLong(op); + if (num is long n) + { + result = new IntPtr(n); + } + } } if (type == typeof(UIntPtr)) @@ -189,6 +197,14 @@ private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference break; } } + else if (Runtime.PyInt_Check(op)) + { + ulong? num = Runtime.PyLong_AsUnsignedLongLong(op); + if (num is ulong n) + { + result = new UIntPtr(n); + } + } } if (result == null && !Converter.ToManaged(op, type, out result, true)) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 97c357382..678e09928 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -689,10 +689,13 @@ def test_intptr_construction(): assert ob.UIntPtrField == UIntPtr.Zero ob.IntPtrField = IntPtr(Int64(-1)) + assert ob.IntPtrField == IntPtr(-1) assert ob.IntPtrField.ToInt64() == -1 ob.IntPtrField = IntPtr(Int64(1024)) + assert ob.IntPtrField == IntPtr(1024) assert ob.IntPtrField.ToInt64() == 1024 ob.UIntPtrField = UIntPtr(UInt64(1024)) + assert ob.UIntPtrField == UIntPtr(1024) assert ob.UIntPtrField.ToUInt64() == 1024 From 775efd522d8e8cf022783bc8dd066e670b007fc2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 11 Jul 2022 13:00:56 +0200 Subject: [PATCH 5/6] Extend intptr conversion tests and exceptions --- tests/test_conversion.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 678e09928..2641489a3 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -682,20 +682,34 @@ def test_iconvertible_conversion(): def test_intptr_construction(): from System import IntPtr, UIntPtr, Int64, UInt64 + from ctypes import sizeof, c_void_p + + ptr_size = sizeof(c_void_p) + max_intptr = 2 ** (ptr_size * 8 - 1) - 1 + min_intptr = -max_intptr - 1 + max_uintptr = 2 ** (ptr_size * 8) - 1 + min_uintptr = 0 ob = ConversionTest() assert ob.IntPtrField == IntPtr.Zero assert ob.UIntPtrField == UIntPtr.Zero - ob.IntPtrField = IntPtr(Int64(-1)) - assert ob.IntPtrField == IntPtr(-1) - assert ob.IntPtrField.ToInt64() == -1 + for v in [0, -1, 1024, max_intptr, min_intptr]: + ob.IntPtrField = IntPtr(Int64(v)) + assert ob.IntPtrField == IntPtr(v) + assert ob.IntPtrField.ToInt64() == v + + for v in [min_intptr - 1, max_intptr + 1]: + with pytest.raises(TypeError): + IntPtr(v) + + for v in [0, 1024, min_uintptr, max_uintptr, max_intptr]: + ob.UIntPtrField = UIntPtr(UInt64(v)) + assert ob.UIntPtrField == UIntPtr(v) + assert ob.UIntPtrField.ToUInt64() == v - ob.IntPtrField = IntPtr(Int64(1024)) - assert ob.IntPtrField == IntPtr(1024) - assert ob.IntPtrField.ToInt64() == 1024 + for v in [min_uintptr - 1, max_uintptr + 1, min_intptr]: + with pytest.raises(TypeError): + UIntPtr(v) - ob.UIntPtrField = UIntPtr(UInt64(1024)) - assert ob.UIntPtrField == UIntPtr(1024) - assert ob.UIntPtrField.ToUInt64() == 1024 From ae5e0746693c569af4c3a023b8ba2998e9971c45 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 11 Jul 2022 13:22:10 +0200 Subject: [PATCH 6/6] Check (U)IntPtr size explicitly --- src/runtime/Runtime.cs | 5 +++++ src/runtime/Types/ClassObject.cs | 14 ++++++++++++-- tests/test_conversion.py | 4 ++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index 7e6210f90..20bef23d4 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -59,6 +59,11 @@ private static string GetDefaultDllName(Version version) internal static bool TypeManagerInitialized => _typesInitialized; internal static readonly bool Is32Bit = IntPtr.Size == 4; + // Available in newer .NET Core versions (>= 5) as IntPtr.MaxValue etc. + internal static readonly long IntPtrMaxValue = Is32Bit ? Int32.MaxValue : Int64.MaxValue; + internal static readonly long IntPtrMinValue = Is32Bit ? Int32.MinValue : Int64.MinValue; + internal static readonly ulong UIntPtrMaxValue = Is32Bit ? UInt32.MaxValue : UInt64.MaxValue; + // .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows) internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT; diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs index 97216f24b..70ec53b18 100644 --- a/src/runtime/Types/ClassObject.cs +++ b/src/runtime/Types/ClassObject.cs @@ -173,10 +173,15 @@ private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference else if (Runtime.PyInt_Check(op)) { long? num = Runtime.PyLong_AsLongLong(op); - if (num is long n) + if (num is long n && n >= Runtime.IntPtrMinValue && n <= Runtime.IntPtrMaxValue) { result = new IntPtr(n); } + else + { + Exceptions.SetError(Exceptions.OverflowError, "value not in range for IntPtr"); + return default; + } } } @@ -200,10 +205,15 @@ private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference else if (Runtime.PyInt_Check(op)) { ulong? num = Runtime.PyLong_AsUnsignedLongLong(op); - if (num is ulong n) + if (num is ulong n && n <= Runtime.UIntPtrMaxValue) { result = new UIntPtr(n); } + else + { + Exceptions.SetError(Exceptions.OverflowError, "value not in range for UIntPtr"); + return default; + } } } diff --git a/tests/test_conversion.py b/tests/test_conversion.py index 2641489a3..a5b4c6fd9 100644 --- a/tests/test_conversion.py +++ b/tests/test_conversion.py @@ -701,7 +701,7 @@ def test_intptr_construction(): assert ob.IntPtrField.ToInt64() == v for v in [min_intptr - 1, max_intptr + 1]: - with pytest.raises(TypeError): + with pytest.raises(OverflowError): IntPtr(v) for v in [0, 1024, min_uintptr, max_uintptr, max_intptr]: @@ -710,6 +710,6 @@ def test_intptr_construction(): assert ob.UIntPtrField.ToUInt64() == v for v in [min_uintptr - 1, max_uintptr + 1, min_intptr]: - with pytest.raises(TypeError): + with pytest.raises(OverflowError): UIntPtr(v) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy