diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs index f86ba7900..e1820f05b 100644 --- a/src/runtime/Converter.cs +++ b/src/runtime/Converter.cs @@ -361,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..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; @@ -1281,13 +1286,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..70ec53b18 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,127 @@ 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; + } + } + else if (Runtime.PyInt_Check(op)) + { + long? num = Runtime.PyLong_AsLongLong(op); + 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; + } + } + } + + 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; + } + } + else if (Runtime.PyInt_Check(op)) + { + ulong? num = Runtime.PyLong_AsUnsignedLongLong(op); + 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; + } + } + } + + 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); 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..a5b4c6fd9 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,37 @@ 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_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 + + 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(OverflowError): + 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 + + for v in [min_uintptr - 1, max_uintptr + 1, min_intptr]: + 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