* Add unit tests for (U)IntPtr conversions
* Special-case construction of (U)IntPtr
* Check (U)IntPtr size explicitly
This commit is contained in:
Benedikt Reinartz 2022-07-11 14:38:54 +02:00 коммит произвёл GitHub
Родитель 25f21f99b2
Коммит 332f8e79ef
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 174 добавлений и 26 удалений

Просмотреть файл

@ -361,7 +361,7 @@ namespace Python.Runtime
// conversions (Python string -> managed string).
if (obType == objectType)
{
if (Runtime.IsStringType(value))
if (Runtime.PyString_Check(value))
{
return ToPrimitive(value, stringType, out result, setError);
}

Просмотреть файл

@ -59,6 +59,11 @@ namespace Python.Runtime
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 @@ namespace Python.Runtime
//====================================================================
// 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;

Просмотреть файл

@ -70,22 +70,9 @@ namespace Python.Runtime
// 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 @@ namespace Python.Runtime
return NewEnum(type, args, tp);
}
if (type == typeof(string))
{
return NewString(args, tp);
}
if (IsGenericNullable(type))
{
// Nullable<T> has special handling in .NET runtime.
@ -112,6 +104,127 @@ namespace Python.Runtime
return self.NewObjectToPython(obj, tp);
}
/// <summary>
/// Construct a new .NET String object from Python args
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="type">.NET type to construct</param>
/// <param name="tp">Corresponding Python type</param>
/// <param name="args">Constructor arguments</param>
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);

Просмотреть файл

@ -1,5 +1,6 @@
namespace Python.Test
{
using System;
using System.Collections.Generic;
/// <summary>
@ -26,6 +27,8 @@ namespace Python.Test
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 @@ namespace Python.Test
}
public interface ISpam
{
@ -63,7 +66,7 @@ namespace Python.Test
return value;
}
}
public class UnicodeString
{
public string value = "안녕";

Просмотреть файл

@ -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)