Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix (U)IntPtr construction#1861

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
filmor merged 6 commits intopythonnet:releasefromfilmor:fix-intptr-conversion
Jul 11, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletionsrc/runtime/Converter.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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);
}
Expand Down
12 changes: 5 additions & 7 deletionssrc/runtime/Runtime.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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;

Expand DownExpand Up@@ -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;
Expand Down
143 changes: 128 additions & 15 deletionssrc/runtime/Types/ClassObject.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand All@@ -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<T> has special handling in .NET runtime.
Expand All@@ -112,6 +104,127 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

dead TODO?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

True, I'll remove it.

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))
Copy link
Member

@lostmsulostmsuJul 11, 2022
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think we need a test forIntPtr constructor call with this path (e.g. parameter is neither CLR object nor Pythonint)

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

What do you expect to happen? It fails to convert, this is essentially the "old" code path. I'll add the test, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@filmor actually, nothing specific. Just want to ensure it does not error in a weird way and the error is actually helpful.

{
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);
Expand Down
7 changes: 5 additions & 2 deletionssrc/testing/conversiontest.cs
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
namespace Python.Test
{
using System;
using System.Collections.Generic;

/// <summary>
Expand All@@ -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;
Expand All@@ -42,7 +45,7 @@ public ConversionTest()

}



public interface ISpam
{
Expand All@@ -63,7 +66,7 @@ public string GetValue()
return value;
}
}

public class UnicodeString
{
public string value = "안녕";
Expand Down
36 changes: 35 additions & 1 deletiontests/test_conversion.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -25,7 +25,7 @@ def test_bool_conversion():

with pytest.raises(TypeError):
ob.BooleanField = 1

with pytest.raises(TypeError):
ob.BooleanField = 0

Expand DownExpand Up@@ -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)


[8]ページ先頭

©2009-2025 Movatter.jp