diff --git a/src/core/IronPython.Modules/ModuleOps.cs b/src/core/IronPython.Modules/ModuleOps.cs index 27d912bdf..7050a46e8 100644 --- a/src/core/IronPython.Modules/ModuleOps.cs +++ b/src/core/IronPython.Modules/ModuleOps.cs @@ -21,7 +21,7 @@ namespace IronPython.Modules { /// - /// Provides helper functions which need to be called from generated code to implement various + /// Provides helper functions which need to be called from generated code to implement various /// portions of modules. /// public static partial class ModuleOps { @@ -110,7 +110,7 @@ public static CTypes.CData CheckSimpleCDataType(object o, object type) { if (res == null && PythonOps.TryGetBoundAttr(o, "_as_parameter_", out object asParam)) { res = asParam as CTypes._CFuncPtr; } - + if (res == null || res.NativeType != type) { throw ArgumentError(type, ((PythonType)type).Name, o); } @@ -298,9 +298,6 @@ public static IntPtr GetPointer(object value) { } if (value is int iVal) { - if(iVal > int.MaxValue) { - iVal = -1; - } return new IntPtr(iVal); } @@ -357,13 +354,8 @@ public static IntPtr GetObject(object value) { } public static long GetSignedLongLong(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - return res.Value; - } - - if (value is BigInteger) { - return (long)(BigInteger)value; + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return unchecked((long)(ulong)(bi & ulong.MaxValue)); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -373,14 +365,9 @@ public static long GetSignedLongLong(object value, object type) { throw PythonOps.TypeErrorForTypeMismatch("signed long long ", value); } - public static long GetUnsignedLongLong(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null && res.Value >= 0) { - return res.Value; - } - - if (value is BigInteger) { - return (long)(ulong)(BigInteger)value; + public static ulong GetUnsignedLongLong(object value, object type) { + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return (ulong)(bi & ulong.MaxValue); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -463,17 +450,8 @@ public static int GetSingleBits(object value) { } public static int GetSignedLong(object value, object type) { - if (value is int) { - return (int)value; - } - - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - return res.Value; - } - - if (value is BigInteger && ((BigInteger)value).AsUInt32(out uint unsigned)) { - return (int)unsigned; + if (TryToIntStrict(value, out BigInteger bi)) { + return unchecked((int)(uint)(bi & uint.MaxValue)); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -483,16 +461,9 @@ public static int GetSignedLong(object value, object type) { throw PythonOps.TypeErrorForTypeMismatch("signed long", value); } - public static int GetUnsignedLong(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - return res.Value; - } - - if (value is BigInteger) { - if (((BigInteger)value).AsUInt32(out uint ures)) { - return (int)ures; - } + public static uint GetUnsignedLong(object value, object type) { + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return (uint)(bi & uint.MaxValue); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -502,24 +473,23 @@ public static int GetUnsignedLong(object value, object type) { throw PythonOps.TypeErrorForTypeMismatch("unsigned long", value); } - public static int GetUnsignedInt(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null && res.Value >= 0) { - return res.Value; + public static uint GetUnsignedInt(object value, object type) { + if (TryToIntStrict(value, out BigInteger bi)) { + return (uint)(bi & uint.MaxValue); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { - return GetUnsignedInt(type, asParam); + return GetUnsignedInt(asParam, type); } throw PythonOps.TypeErrorForTypeMismatch("unsigned int", value); } public static int GetSignedInt(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - return res.Value; + if (TryToIntStrict(value, out BigInteger bi)) { + return unchecked((int)(bi & uint.MaxValue)); } + if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { return GetSignedInt(asParam, type); } @@ -528,13 +498,10 @@ public static int GetSignedInt(object value, object type) { } public static ushort GetUnsignedShort(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - int iVal = res.Value; - if (iVal >= ushort.MinValue && iVal <= ushort.MaxValue) { - return (ushort)iVal; - } + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return (ushort)(bi & ushort.MaxValue); } + if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { return GetUnsignedShort(asParam, type); } @@ -543,12 +510,8 @@ public static ushort GetUnsignedShort(object value, object type) { } public static short GetSignedShort(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - int iVal = res.Value; - return (short)iVal; - } else if (value is BigInteger bigInt) { - return (short)(int)(bigInt & 0xffff); + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return unchecked((short)(ushort)(bi & ushort.MaxValue)); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -563,9 +526,8 @@ public static int GetVariantBool(object value, object type) { } public static byte GetUnsignedByte(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - return (byte)res.Value; + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return (byte)(bi & byte.MaxValue); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -576,12 +538,8 @@ public static byte GetUnsignedByte(object value, object type) { } public static sbyte GetSignedByte(object value, object type) { - int? res = Converter.ImplicitConvertToInt32(value); - if (res != null) { - int iVal = res.Value; - if (iVal >= sbyte.MinValue && iVal <= sbyte.MaxValue) { - return (sbyte)iVal; - } + if (PythonOps.TryToInt(value, out BigInteger bi)) { + return unchecked((sbyte)(byte)(bi & byte.MaxValue)); } if (PythonOps.TryGetBoundAttr(value, "_as_parameter_", out object asParam)) { @@ -639,7 +597,7 @@ public static char GetWChar(object value, object type) { return GetWChar(asParam, type); } - throw PythonOps.TypeError("unicode string expected instead of {0} instance", PythonOps.GetPythonTypeName(value)); + throw PythonOps.TypeErrorForBadInstance("unicode string expected instead of {0} instance", value); } public static object IntPtrToObject(IntPtr address) { @@ -650,6 +608,22 @@ public static object IntPtrToObject(IntPtr address) { return res; } + internal static bool TryToIntStrict(object value, out BigInteger bi) { + // When IronPython upgrades to Python 3.10, this method becomes obsolete + // and can be replaced with PythonOps.TryToIndex(value, out bi) + if (IsFloatingPoint(value)) { + throw PythonOps.TypeErrorForBadInstance("int expected instead of {0}", value); + } + + return PythonOps.TryToInt(value, out bi); + } + + internal static bool IsFloatingPoint(object value) + => value is double or float +#if NETCOREAPP + or Half +#endif + ; } } #endif diff --git a/src/core/IronPython.Modules/_ctypes/SimpleCData.cs b/src/core/IronPython.Modules/_ctypes/SimpleCData.cs index 9bb1cb126..1babc2e66 100644 --- a/src/core/IronPython.Modules/_ctypes/SimpleCData.cs +++ b/src/core/IronPython.Modules/_ctypes/SimpleCData.cs @@ -10,6 +10,7 @@ using System.Runtime.CompilerServices; using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; using IronPython.Runtime; using IronPython.Runtime.Operations; @@ -41,12 +42,8 @@ public void __init__(CodeContext/*!*/ context, object value) { { if (value is IList t && t.Count == 1) { value = Bytes.FromByte(t[0]); - } else if (value is int i) { - try { - value = Bytes.FromByte(checked((byte)i)); - } catch (OverflowException) { - throw PythonOps.TypeError("one character bytes, bytearray or integer expected"); - } + } else if (Converter.TryConvertToByte(value, out byte b) && !ModuleOps.IsFloatingPoint(value)) { + value = Bytes.FromByte(b); } else { throw PythonOps.TypeError("one character bytes, bytearray or integer expected"); } @@ -60,25 +57,20 @@ public void __init__(CodeContext/*!*/ context, object value) { } } break; + case SimpleTypeKind.SignedByte: case SimpleTypeKind.SignedInt: case SimpleTypeKind.SignedLong: case SimpleTypeKind.SignedLongLong: case SimpleTypeKind.SignedShort: + case SimpleTypeKind.UnsignedByte: case SimpleTypeKind.UnsignedInt: case SimpleTypeKind.UnsignedLong: case SimpleTypeKind.UnsignedLongLong: case SimpleTypeKind.UnsignedShort: { - object __int__ = null; - if (value is float || value is double) { - throw PythonOps.TypeError("int expected instead of float"); - } - - if (!(value is int || value is BigInteger || PythonOps.TryGetBoundAttr(value, "__int__", out __int__))) { - throw PythonOps.TypeError("an integer is required"); - } - - if (__int__ != null) { - value = PythonOps.CallWithContext(context, __int__); + if (ModuleOps.TryToIntStrict(value, out BigInteger bi)) { + value = bi; + } else { + throw PythonOps.TypeErrorForBadInstance("an integer is required (got type {0})", value); } } break; @@ -92,7 +84,7 @@ public void __init__(CodeContext/*!*/ context, object value) { if (value is BigInteger x) { if (x > (BigInteger)double.MaxValue) { - throw PythonOps.OverflowError("long int too large to convert to float"); + throw PythonOps.OverflowError("Python int too large to convert to float"); } } diff --git a/src/core/IronPython.Modules/_ctypes/SimpleType.cs b/src/core/IronPython.Modules/_ctypes/SimpleType.cs index 8f4c4a74f..2af7309c2 100644 --- a/src/core/IronPython.Modules/_ctypes/SimpleType.cs +++ b/src/core/IronPython.Modules/_ctypes/SimpleType.cs @@ -251,19 +251,19 @@ object INativeType.SetValue(MemoryHolder/*!*/ owner, int offset, object value) { switch (_type) { case SimpleTypeKind.Boolean: owner.WriteByte(offset, ModuleOps.GetBoolean(value, this)); break; case SimpleTypeKind.Char: owner.WriteByte(offset, ModuleOps.GetChar(value, this)); break; - case SimpleTypeKind.SignedByte: owner.WriteByte(offset, (byte)ModuleOps.GetSignedByte(value, this)); break; + case SimpleTypeKind.SignedByte: owner.WriteByte(offset, unchecked((byte)ModuleOps.GetSignedByte(value, this))); break; case SimpleTypeKind.UnsignedByte: owner.WriteByte(offset, ModuleOps.GetUnsignedByte(value, this)); break; - case SimpleTypeKind.WChar: owner.WriteInt16(offset, (short)ModuleOps.GetWChar(value, this)); break; - case SimpleTypeKind.SignedShort: owner.WriteInt16(offset, ModuleOps.GetSignedShort(value, this), _swap); break; - case SimpleTypeKind.UnsignedShort: owner.WriteInt16(offset, (short)ModuleOps.GetUnsignedShort(value, this), _swap); break; - case SimpleTypeKind.VariantBool: owner.WriteInt16(offset, (short)ModuleOps.GetVariantBool(value, this), _swap); break; + case SimpleTypeKind.WChar: owner.WriteInt16(offset, unchecked((short)ModuleOps.GetWChar(value, this))); break; + case SimpleTypeKind.SignedShort: owner.WriteInt16(offset, ModuleOps.GetSignedShort(value, this), _swap); break; + case SimpleTypeKind.UnsignedShort: owner.WriteInt16(offset, unchecked((short)ModuleOps.GetUnsignedShort(value, this)), _swap); break; + case SimpleTypeKind.VariantBool: owner.WriteInt16(offset, unchecked((short)ModuleOps.GetVariantBool(value, this)), _swap); break; case SimpleTypeKind.SignedInt: owner.WriteInt32(offset, ModuleOps.GetSignedInt(value, this), _swap); break; - case SimpleTypeKind.UnsignedInt: owner.WriteInt32(offset, ModuleOps.GetUnsignedInt(value, this), _swap); break; - case SimpleTypeKind.UnsignedLong: owner.WriteInt32(offset, ModuleOps.GetUnsignedLong(value, this), _swap); break; + case SimpleTypeKind.UnsignedInt: owner.WriteInt32(offset, unchecked((int)ModuleOps.GetUnsignedInt(value, this)), _swap); break; + case SimpleTypeKind.UnsignedLong: owner.WriteInt32(offset, unchecked((int)ModuleOps.GetUnsignedLong(value, this)), _swap); break; case SimpleTypeKind.SignedLong: owner.WriteInt32(offset, ModuleOps.GetSignedLong(value, this), _swap); break; case SimpleTypeKind.Single: owner.WriteInt32(offset, ModuleOps.GetSingleBits(value), _swap); break; case SimpleTypeKind.Double: owner.WriteInt64(offset, ModuleOps.GetDoubleBits(value), _swap); break; - case SimpleTypeKind.UnsignedLongLong: owner.WriteInt64(offset, ModuleOps.GetUnsignedLongLong(value, this), _swap); break; + case SimpleTypeKind.UnsignedLongLong: owner.WriteInt64(offset, unchecked((long)ModuleOps.GetUnsignedLongLong(value, this)), _swap); break; case SimpleTypeKind.SignedLongLong: owner.WriteInt64(offset, ModuleOps.GetSignedLongLong(value, this), _swap); break; case SimpleTypeKind.Object: owner.WriteIntPtr(offset, ModuleOps.GetObject(value)); break; case SimpleTypeKind.Pointer: owner.WriteIntPtr(offset, ModuleOps.GetPointer(value)); break; @@ -626,23 +626,23 @@ void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, Li break; case SimpleTypeKind.UnsignedInt: case SimpleTypeKind.UnsignedLong: - EmitInt32ToObject(method, value); + EmitUIntToObject(method, value); break; case SimpleTypeKind.UnsignedLongLong: case SimpleTypeKind.SignedLongLong: - EmitInt64ToObject(method, value); + EmitXInt64ToObject(method, value); break; case SimpleTypeKind.Object: method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.IntPtrToObject))); break; case SimpleTypeKind.WCharPointer: - method.Emit(OpCodes.Call, typeof(Marshal).GetMethod("PtrToStringUni", new[] { typeof(IntPtr) })); + method.Emit(OpCodes.Call, typeof(Marshal).GetMethod("PtrToStringUni", [typeof(IntPtr)])); break; case SimpleTypeKind.CharPointer: method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.IntPtrToBytes))); break; case SimpleTypeKind.BStr: - method.Emit(OpCodes.Call, typeof(Marshal).GetMethod("PtrToStringBSTR", new[] { typeof(IntPtr) })); + method.Emit(OpCodes.Call, typeof(Marshal).GetMethod("PtrToStringBSTR", [typeof(IntPtr)])); break; case SimpleTypeKind.Char: method.Emit(OpCodes.Call, typeof(ModuleOps).GetMethod(nameof(ModuleOps.CharToBytes))); @@ -669,12 +669,13 @@ void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, Li method.MarkLabel(notNull); method.Emit(OpCodes.Ldloc, tmpLocal); - EmitInt32ToObject(method, new Local(tmpLocal)); + EmitUIntToObject(method, new Local(tmpLocal)); } else { LocalBuilder tmpLocal = method.DeclareLocal(typeof(long)); method.Emit(OpCodes.Conv_I8); method.Emit(OpCodes.Stloc, tmpLocal); method.Emit(OpCodes.Ldloc, tmpLocal); + method.Emit(OpCodes.Conv_U8); method.Emit(OpCodes.Ldc_I4_0); method.Emit(OpCodes.Conv_U8); @@ -684,7 +685,7 @@ void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, Li method.MarkLabel(notNull); method.Emit(OpCodes.Ldloc, tmpLocal); - EmitInt64ToObject(method, new Local(tmpLocal)); + EmitXInt64ToObject(method, new Local(tmpLocal)); } method.MarkLabel(done); @@ -709,10 +710,13 @@ void INativeType.EmitReverseMarshalling(ILGenerator method, LocalOrArg value, Li } } - private static void EmitInt64ToObject(ILGenerator method, LocalOrArg value) { + private static void EmitXInt64ToObject(ILGenerator method, LocalOrArg value) { Label done; Label bigInt = method.DefineLabel(); done = method.DefineLabel(); + if (value.Type == typeof(ulong)) { + method.Emit(OpCodes.Conv_I8); + } method.Emit(OpCodes.Ldc_I4, Int32.MaxValue); method.Emit(OpCodes.Conv_I8); method.Emit(OpCodes.Bgt, bigInt); @@ -729,13 +733,13 @@ private static void EmitInt64ToObject(ILGenerator method, LocalOrArg value) { method.MarkLabel(bigInt); value.Emit(method); - method.Emit(OpCodes.Call, typeof(BigInteger).GetMethod("op_Implicit", new[] { value.Type })); + method.Emit(OpCodes.Call, typeof(BigInteger).GetMethod("op_Implicit", [value.Type])); method.Emit(OpCodes.Box, typeof(BigInteger)); method.MarkLabel(done); } - private static void EmitInt32ToObject(ILGenerator method, LocalOrArg value) { + private static void EmitUIntToObject(ILGenerator method, LocalOrArg value) { Label intVal, done; intVal = method.DefineLabel(); done = method.DefineLabel(); @@ -745,7 +749,7 @@ private static void EmitInt32ToObject(ILGenerator method, LocalOrArg value) { method.Emit(OpCodes.Ble, intVal); value.Emit(method); - method.Emit(OpCodes.Call, typeof(BigInteger).GetMethod("op_Implicit", new[] { value.Type })); + method.Emit(OpCodes.Call, typeof(BigInteger).GetMethod("op_Implicit", [value.Type])); method.Emit(OpCodes.Box, typeof(BigInteger)); method.Emit(OpCodes.Br, done); diff --git a/src/core/IronPython.Modules/_opcode.cs b/src/core/IronPython.Modules/_opcode.cs index b90dcddd1..e5e948a46 100644 --- a/src/core/IronPython.Modules/_opcode.cs +++ b/src/core/IronPython.Modules/_opcode.cs @@ -4,6 +4,8 @@ #nullable enable +using System.Numerics; + using IronPython.Runtime; using IronPython.Runtime.Operations; @@ -155,10 +157,10 @@ public static int stack_effect(CodeContext context, int opcode, object? oparg = throw PythonOps.ValueError("stack_effect: opcode requires oparg but oparg was not specified"); } - if (!Converter.TryConvertToIndex(oparg, out ioparg)) { // supported since CPython 3.8 - ioparg = Converter.ImplicitConvertToInt32(oparg) ?? // warning since CPython 3.8, unsupported in 3.10 - throw PythonOps.TypeError($"an integer is required (got type {PythonOps.GetPythonTypeName(oparg)})"); + if (!PythonOps.TryToInt(oparg, out BigInteger bioparg)) { + throw PythonOps.TypeError($"an integer is required (got type {PythonOps.GetPythonTypeName(oparg)})"); } + ioparg = (int)bioparg; } else if (oparg != null) { throw PythonOps.ValueError("stack_effect: opcode does not permit oparg but oparg was specified"); } diff --git a/src/core/IronPython/Runtime/Operations/PythonOps.cs b/src/core/IronPython/Runtime/Operations/PythonOps.cs index a4ac3df39..0efee5179 100644 --- a/src/core/IronPython/Runtime/Operations/PythonOps.cs +++ b/src/core/IronPython/Runtime/Operations/PythonOps.cs @@ -877,6 +877,26 @@ internal static bool TryToIndex(object? o, out BigInteger index) { return false; } + // This method is like `TryToIndex` but it additionally supports objects with `__int__`, + // which is expected for Python 3.7- and is supported until Python 3.10. + internal static bool TryToInt(object? o, out BigInteger res) { + if (TryToIndex(o, out res)) { + // supported since Python 3.8 + return true; + } + if (PythonTypeOps.TryInvokeUnaryOperator(DefaultContext.Default, o, "__int__", out object objres)) { + // warning since Python 3.8, unsupported in 3.10 + res = objres switch { + int i => i, + BigInteger bi => bi, + Extensible ebi => ebi.Value, + _ => throw TypeError("__int__ returned non-int (type {0})", PythonOps.GetPythonTypeName(objres)) + }; + return true; + } + return false; + } + private static bool IndexObjectToInt(object o, out int res, out BigInteger longRes) { switch (o) { case int i: diff --git a/tests/suite/modules/type_related/test_ctypes.py b/tests/suite/modules/type_related/test_ctypes.py index f4c1fdf3e..baa14825e 100644 --- a/tests/suite/modules/type_related/test_ctypes.py +++ b/tests/suite/modules/type_related/test_ctypes.py @@ -11,8 +11,31 @@ import sys import gc import unittest +from decimal import Decimal + +from iptest import IronPythonTestCase, is_posix, is_cli, is_mono, is_netcoreapp, big, myint, run_test + +class MyInt: + def __init__(self, value): + self.value = value + def __int__(self): + return self.value + +class MyIndex: + def __init__(self, value): + self.value = value + def __index__(self): + return self.value + +class MyIntIndex: + def __init__(self, intValue, indexValue): + self.intValue = intValue + self.indexValue = indexValue + def __int__(self): + return self.intValue + def __index__(self): + return self.indexValue -from iptest import IronPythonTestCase, is_posix, is_cli, is_mono, big, myint, run_test class CTypesTest(IronPythonTestCase): export_error_msg = "Existing exports of data: object cannot be re-sized" if is_cli else "cannot resize an array that is exporting buffers" @@ -237,4 +260,164 @@ def test_loadlibrary_error(self): self.assertIsNone(cm.exception.filename) self.assertIsNone(cm.exception.filename2) + + def test_conversions_c_int(self): + # normal case + c_int_value = c_int(42) + self.assertEqual(c_int_value.value, 42) + + # bool case + c_int_value = c_int(True) + self.assertEqual(c_int_value.value, 1) + + # BigInteger case + c_int_value = c_int(big(42)) + self.assertEqual(c_int_value.value, 42) + + if is_cli or sys.version_info < (3, 10): + # __int__ supported + c_int_value = c_int(MyInt(42)) + self.assertEqual(c_int_value.value, 42) + c_int_value.value = MyInt(24) + self.assertEqual(c_int_value.value, 24) + else: + # __int__ not supported + self.assertRaises(TypeError, c_int, MyInt(42)) + with self.assertRaises(TypeError): + c_int_value.value = MyInt(42) + + if is_cli or sys.version_info >= (3, 8): + # __index__ supported + c_int_value = c_int(MyIndex(42)) + self.assertEqual(c_int_value.value, 42) + c_int_value.value = MyIndex(24) + self.assertEqual(c_int_value.value, 24) + + # __index__ takes priority over __int__ + c_int_value = c_int(MyIntIndex(44, 42)) + self.assertEqual(c_int_value.value, 42) + c_int_value.value = MyIntIndex(22, 24) + self.assertEqual(c_int_value.value, 24) + else: + # __index__ not supported + self.assertRaises(TypeError, c_int, MyIndex(42)) + with self.assertRaises(TypeError): + c_int_value.value = MyIndex(42) + + # str not supported + self.assertRaises(TypeError, c_int, "abc") + with self.assertRaises(TypeError): + c_int_value.value = "abc" + + # float not supported + self.assertRaises(TypeError, c_int, 42.6) + with self.assertRaises(TypeError): + c_int_value.value = 42.6 + + # System.Single not supported + if is_cli: + import System + self.assertRaises(TypeError, c_int, System.Single(42.6)) + with self.assertRaises(TypeError): + c_int_value.value = System.Single(42.6) + + # System.Half not supported + if is_netcoreapp: + import System, clr + half = clr.Convert(42.6, System.Half) + self.assertRaises(TypeError, c_int, half) + with self.assertRaises(TypeError): + c_int_value.value = System.Half(42.6) + + # Decimal is supported as long as __int__ is supported + if is_cli or sys.version_info < (3, 10): + c_int_value = c_int(Decimal(42.6)) + else: + self.assertRaises(TypeError, c_int, Decimal(42.6)) + + + def test_conversions_c_char(self): + # normal case (c_char is unsigned) + c_char_value = c_char(42) + self.assertEqual(c_char_value.value, b"*") + + c_char_value = c_char(b"*") + self.assertEqual(c_char_value.value, b"*") + + # bool case + c_cbyte_value = c_char(True) + self.assertEqual(c_cbyte_value.value, b"\x01") + + # BigInteger case + c_cbyte_value = c_char(big(42)) + self.assertEqual(c_cbyte_value.value, b"*") + + # out of range int not supported + self.assertRaises(TypeError, c_char, 256) + self.assertRaises(TypeError, c_char, -1) + with self.assertRaises(TypeError): + c_char_value.value = 256 + with self.assertRaises(TypeError): + c_char_value.value = -1 + + # longer bytes not supported + self.assertRaises(TypeError, c_char, b"abc") + with self.assertRaises(TypeError): + c_char_value.value = b"abc" + + # __int__ not supported + self.assertRaises(TypeError, c_char, MyInt(42)) + with self.assertRaises(TypeError): + c_char_value.value = MyInt(42) + + # __index__ not supported + self.assertRaises(TypeError, c_char, MyIndex(42)) + with self.assertRaises(TypeError): + c_char_value.value = MyIndex(42) + + # str not supported + self.assertRaises(TypeError, c_char, "a") + with self.assertRaises(TypeError): + c_char_value.value = "a" + + # float not supported + self.assertRaises(TypeError, c_char, 42.6) + with self.assertRaises(TypeError): + c_char_value.value = 42.6 + + + def test_conversions_overflow(self): + # Overflow is clipped to lowest bits + c_int_value = c_int((42 << 60) + 24) + self.assertEqual(c_int_value.value, 24) + c_int_value.value = (42 << 60) + 12 + self.assertEqual(c_int_value.value, 12) + + c_int_value = c_int((-42 << 60) - 24) + self.assertEqual(c_int_value.value, -24) + + c_longlong_value = c_longlong((42 << 80) + 24) + self.assertEqual(c_longlong_value.value, 24) + c_longlong_value.value = (42 << 80) + 12 + self.assertEqual(c_longlong_value.value, 12) + + c_short_value = c_short((42 << 20) + 24) + self.assertEqual(c_short_value.value, 24) + c_short_value.value = (42 << 20) + 12 + self.assertEqual(c_short_value.value, 12) + c_short_value.value = 32768 + self.assertEqual(c_short_value.value, -32768) + c_short_value.value = 32769 + self.assertEqual(c_short_value.value, -32767) + + c_byte_value = c_byte((42 << 10) + 4) + self.assertEqual(c_byte_value.value, 4) + c_byte_value.value = (42 << 10) + 2 + self.assertEqual(c_byte_value.value, 2) + c_byte_value.value = 128 + self.assertEqual(c_byte_value.value, -128) + c_byte_value.value = 129 + self.assertEqual(c_byte_value.value, -127) + + run_test(__name__)