diff --git a/src/perf_tests/Python.PerformanceTests.csproj b/src/perf_tests/Python.PerformanceTests.csproj index 4ee604bf2..8ea99d9b2 100644 --- a/src/perf_tests/Python.PerformanceTests.csproj +++ b/src/perf_tests/Python.PerformanceTests.csproj @@ -13,7 +13,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + compile @@ -25,7 +25,7 @@ - + diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs index bf852112c..b88a6a6b6 100644 --- a/src/runtime/ClassManager.cs +++ b/src/runtime/ClassManager.cs @@ -220,6 +220,11 @@ internal static ClassBase CreateClass(Type type) impl = new LookUpObject(type); } + else if (type.IsEnum) + { + impl = new EnumObject(type); + } + else { impl = new ClassObject(type); diff --git a/src/runtime/Properties/AssemblyInfo.cs b/src/runtime/Properties/AssemblyInfo.cs index a7c2c1a3d..4bada6682 100644 --- a/src/runtime/Properties/AssemblyInfo.cs +++ b/src/runtime/Properties/AssemblyInfo.cs @@ -4,5 +4,5 @@ [assembly: InternalsVisibleTo("Python.EmbeddingTest, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] [assembly: InternalsVisibleTo("Python.Test, PublicKey=00240000048000009400000006020000002400005253413100040000110000005ffd8f49fb44ab0641b3fd8d55e749f716e6dd901032295db641eb98ee46063cbe0d4a1d121ef0bc2af95f8a7438d7a80a3531316e6b75c2dae92fb05a99f03bf7e0c03980e1c3cfb74ba690aca2f3339ef329313bcc5dccced125a4ffdc4531dcef914602cd5878dc5fbb4d4c73ddfbc133f840231343e013762884d6143189")] -[assembly: AssemblyVersion("2.0.46")] -[assembly: AssemblyFileVersion("2.0.46")] +[assembly: AssemblyVersion("2.0.47")] +[assembly: AssemblyFileVersion("2.0.47")] diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj index 558466d26..b60b36e6b 100644 --- a/src/runtime/Python.Runtime.csproj +++ b/src/runtime/Python.Runtime.csproj @@ -5,7 +5,7 @@ Python.Runtime Python.Runtime QuantConnect.pythonnet - 2.0.46 + 2.0.47 false LICENSE https://github.com/pythonnet/pythonnet diff --git a/src/runtime/Types/ClassBase.cs b/src/runtime/Types/ClassBase.cs index ded315952..590c870b5 100644 --- a/src/runtime/Types/ClassBase.cs +++ b/src/runtime/Types/ClassBase.cs @@ -156,42 +156,7 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc try { int cmp = co1Comp.CompareTo(co2Inst); - - BorrowedReference pyCmp; - if (cmp < 0) - { - if (op == Runtime.Py_LT || op == Runtime.Py_LE) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - else if (cmp == 0) - { - if (op == Runtime.Py_LE || op == Runtime.Py_GE) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - else - { - if (op == Runtime.Py_GE || op == Runtime.Py_GT) - { - pyCmp = Runtime.PyTrue; - } - else - { - pyCmp = Runtime.PyFalse; - } - } - return new NewReference(pyCmp); + return new NewReference(GetComparisonResult(op, cmp)); } catch (ArgumentException e) { @@ -202,7 +167,53 @@ public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReferenc } } - private static bool TryGetSecondCompareOperandInstance(BorrowedReference left, BorrowedReference right, out CLRObject co1, out object co2Inst) + /// + /// Get the result of a comparison operation based on the operator and the comparison result. + /// + /// + /// This method is used to determine the result of a comparison operation, excluding equality and inequality. + /// + protected static BorrowedReference GetComparisonResult(int op, int comparisonResult) + { + BorrowedReference pyCmp; + if (comparisonResult < 0) + { + if (op == Runtime.Py_LT || op == Runtime.Py_LE) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + else if (comparisonResult == 0) + { + if (op == Runtime.Py_LE || op == Runtime.Py_GE) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + else + { + if (op == Runtime.Py_GE || op == Runtime.Py_GT) + { + pyCmp = Runtime.PyTrue; + } + else + { + pyCmp = Runtime.PyFalse; + } + } + + return pyCmp; + } + + protected static bool TryGetSecondCompareOperandInstance(BorrowedReference left, BorrowedReference right, out CLRObject co1, out object co2Inst) { co2Inst = null; diff --git a/src/runtime/Types/DelegateObject.cs b/src/runtime/Types/DelegateObject.cs index 43a75aba7..a469e6a52 100644 --- a/src/runtime/Types/DelegateObject.cs +++ b/src/runtime/Types/DelegateObject.cs @@ -103,7 +103,7 @@ public static NewReference tp_call(BorrowedReference ob, BorrowedReference args, /// /// Implements __cmp__ for reflected delegate types. /// - public new static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) + public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) { if (op != Runtime.Py_EQ && op != Runtime.Py_NE) { diff --git a/src/runtime/Types/EnumObject.cs b/src/runtime/Types/EnumObject.cs new file mode 100644 index 000000000..8c146ff50 --- /dev/null +++ b/src/runtime/Types/EnumObject.cs @@ -0,0 +1,218 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Python.Runtime +{ + /// + /// Managed class that provides the implementation for reflected enum types. + /// + [Serializable] + internal class EnumObject : ClassBase + { + internal EnumObject(Type type) : base(type) + { + } + + /// + /// Standard comparison implementation for instances of enum types. + /// + public static NewReference tp_richcompare(BorrowedReference ob, BorrowedReference other, int op) + { + object rightInstance; + CLRObject leftClrObject; + int comparisonResult; + + switch (op) + { + case Runtime.Py_EQ: + case Runtime.Py_NE: + var pytrue = Runtime.PyTrue; + var pyfalse = Runtime.PyFalse; + + // swap true and false for NE + if (op != Runtime.Py_EQ) + { + pytrue = Runtime.PyFalse; + pyfalse = Runtime.PyTrue; + } + + if (ob == other) + { + return new NewReference(pytrue); + } + + if (!TryGetSecondCompareOperandInstance(ob, other, out leftClrObject, out rightInstance)) + { + return new NewReference(pyfalse); + } + + if (rightInstance != null && + TryCompare(leftClrObject.inst as Enum, rightInstance, out comparisonResult) && + comparisonResult == 0) + { + return new NewReference(pytrue); + } + else + { + return new NewReference(pyfalse); + } + + case Runtime.Py_LT: + case Runtime.Py_LE: + case Runtime.Py_GT: + case Runtime.Py_GE: + if (!TryGetSecondCompareOperandInstance(ob, other, out leftClrObject, out rightInstance)) + { + return Exceptions.RaiseTypeError("Cannot get managed object"); + } + + if (rightInstance == null) + { + return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} to None"); + } + + try + { + if (!TryCompare(leftClrObject.inst as Enum, rightInstance, out comparisonResult)) + { + return Exceptions.RaiseTypeError($"Cannot compare {leftClrObject.inst.GetType()} with {rightInstance.GetType()}"); + } + + return new NewReference(GetComparisonResult(op, comparisonResult)); + } + catch (ArgumentException e) + { + return Exceptions.RaiseTypeError(e.Message); + } + + default: + return new NewReference(Runtime.PyNotImplemented); + } + } + + /// + /// Tries comparing the give enum to the right operand by converting it to the appropriate type if possible + /// + /// True if the right operand was converted to a supported type and the comparison was performed successfully + private static bool TryCompare(Enum left, object right, out int result) + { + result = int.MinValue; + var conversionSuccessful = true; + var leftType = left.GetType(); + var rightType = right.GetType(); + + // Same enum comparison: + if (leftType == rightType) + { + result = left.CompareTo(right); + } + // Comparison with other enum types + else if (rightType.IsEnum) + { + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + result = Compare(left, right as Enum, leftIsUnsigned); + } + else if (right is string rightString) + { + result = left.ToString().CompareTo(rightString); + } + else + { + var leftIsUnsigned = leftType.GetEnumUnderlyingType() == typeof(UInt64); + switch (right) + { + case double rightDouble: + result = Compare(left, rightDouble, leftIsUnsigned); + break; + case long rightLong: + result = Compare(left, rightLong, leftIsUnsigned); + break; + case ulong rightULong: + result = Compare(left, rightULong, leftIsUnsigned); + break; + case int rightInt: + result = Compare(left, (long)rightInt, leftIsUnsigned); + break; + case uint rightUInt: + result = Compare(left, (ulong)rightUInt, leftIsUnsigned); + break; + default: + conversionSuccessful = false; + break; + } + } + + return conversionSuccessful; + } + + #region Comparison against integers + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(long a, ulong b) + { + if (a < 0) return -1; + return ((ulong)a).CompareTo(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, long b, bool isUnsigned) + { + + if (isUnsigned) + { + return -Compare(b, Convert.ToUInt64(a)); + } + return Convert.ToInt64(a).CompareTo(b); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, ulong b, bool inUnsigned) + { + if (inUnsigned) + { + return Convert.ToUInt64(a).CompareTo(b); + } + return Compare(Convert.ToInt64(a), b); + } + + #endregion + + #region Comparison against doubles + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, double b, bool isUnsigned) + { + if (isUnsigned) + { + var uIntA = Convert.ToUInt64(a); + if (uIntA < b) return -1; + if (uIntA > b) return 1; + return 0; + } + + var intA = Convert.ToInt64(a); + if (intA < b) return -1; + if (intA > b) return 1; + return 0; + } + + #endregion + + #region Comparison against other enum types + + /// + /// We support comparing enums of different types by comparing their underlying values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int Compare(Enum a, Enum b, bool isUnsigned) + { + if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) + { + return Compare(a, Convert.ToUInt64(b), isUnsigned); + } + return Compare(a, Convert.ToInt64(b), isUnsigned); + } + + #endregion + } +} diff --git a/src/runtime/Util/OpsHelper.cs b/src/runtime/Util/OpsHelper.cs index 89ce79e20..135a67163 100644 --- a/src/runtime/Util/OpsHelper.cs +++ b/src/runtime/Util/OpsHelper.cs @@ -156,428 +156,5 @@ public static double op_Division(double a, T b) } #endregion - - #region Int comparison operators - - public static bool op_Equality(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) == uvalue; - } - return Convert.ToInt64(a) == b; - } - - public static bool op_Equality(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b == uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) == b; - } - - public static bool op_Equality(long a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Equality(ulong a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, long b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(T a, ulong b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(long a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_Inequality(ulong a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_LessThan(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) > uvalue; - } - return Convert.ToInt64(a) < b; - } - - public static bool op_LessThan(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b > uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) < b; - } - - public static bool op_LessThan(long a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_LessThan(ulong a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) < uvalue; - } - return Convert.ToInt64(a) > b; - } - - public static bool op_GreaterThan(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b < uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) > b; - } - - public static bool op_GreaterThan(long a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_GreaterThan(ulong a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) >= uvalue; - } - return Convert.ToInt64(a) <= b; - } - - public static bool op_LessThanOrEqual(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) <= b; - } - - public static bool op_LessThanOrEqual(long a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_LessThanOrEqual(ulong a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, long b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b >= 0 && ((ulong)b) <= uvalue; - } - return Convert.ToInt64(a) >= b; - } - - public static bool op_GreaterThanOrEqual(T a, ulong b) - { - if (IsUnsigned) - { - var uvalue = Convert.ToUInt64(a); - return b <= uvalue; - } - var ivalue = Convert.ToInt64(a); - return ivalue >= 0 && ((ulong)ivalue) >= b; - } - - public static bool op_GreaterThanOrEqual(long a, T b) - { - return op_LessThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(ulong a, T b) - { - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region Double comparison operators - - public static bool op_Equality(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) == b; - } - return Convert.ToInt64(a) == b; - } - - public static bool op_Equality(double a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, double b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(double a, T b) - { - return !op_Equality(b, a); - } - - public static bool op_LessThan(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) < b; - } - return Convert.ToInt64(a) < b; - } - - public static bool op_LessThan(double a, T b) - { - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) > b; - } - return Convert.ToInt64(a) > b; - } - - public static bool op_GreaterThan(double a, T b) - { - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) <= b; - } - return Convert.ToInt64(a) <= b; - } - - public static bool op_LessThanOrEqual(double a, T b) - { - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, double b) - { - if (IsUnsigned) - { - return Convert.ToUInt64(a) >= b; - } - return Convert.ToInt64(a) >= b; - } - - public static bool op_GreaterThanOrEqual(double a, T b) - { - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region String comparison operators - public static bool op_Equality(T a, string b) - { - return a.ToString().Equals(b, StringComparison.InvariantCultureIgnoreCase); - } - public static bool op_Equality(string a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, string b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(string a, T b) - { - return !op_Equality(b, a); - } - - #endregion - - #region Enum comparison operators - - public static bool op_Equality(T a, Enum b) - { - if (b == null) - { - return false; - } - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_Equality(a, Convert.ToUInt64(b)); - } - return op_Equality(a, Convert.ToInt64(b)); - } - - public static bool op_Equality(Enum a, T b) - { - return op_Equality(b, a); - } - - public static bool op_Inequality(T a, Enum b) - { - return !op_Equality(a, b); - } - - public static bool op_Inequality(Enum a, T b) - { - return !op_Equality(b, a); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ThrowOnNull(object obj, string @operator) - { - if (obj == null) - { - using (Py.GIL()) - { - Exceptions.RaiseTypeError($"'{@operator}' not supported between instances of '{typeof(T).Name}' and null/None"); - PythonException.ThrowLastAsClrException(); - } - } - } - - public static bool op_LessThan(T a, Enum b) - { - ThrowOnNull(b, "<"); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_LessThan(a, Convert.ToUInt64(b)); - } - return op_LessThan(a, Convert.ToInt64(b)); - } - - public static bool op_LessThan(Enum a, T b) - { - ThrowOnNull(a, "<"); - return op_GreaterThan(b, a); - } - - public static bool op_GreaterThan(T a, Enum b) - { - ThrowOnNull(b, ">"); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_GreaterThan(a, Convert.ToUInt64(b)); - } - return op_GreaterThan(a, Convert.ToInt64(b)); - } - - public static bool op_GreaterThan(Enum a, T b) - { - ThrowOnNull(a, ">"); - return op_LessThan(b, a); - } - - public static bool op_LessThanOrEqual(T a, Enum b) - { - ThrowOnNull(b, "<="); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_LessThanOrEqual(a, Convert.ToUInt64(b)); - } - return op_LessThanOrEqual(a, Convert.ToInt64(b)); - } - - public static bool op_LessThanOrEqual(Enum a, T b) - { - ThrowOnNull(a, "<="); - return op_GreaterThanOrEqual(b, a); - } - - public static bool op_GreaterThanOrEqual(T a, Enum b) - { - ThrowOnNull(b, ">="); - - if (b.GetType().GetEnumUnderlyingType() == typeof(UInt64)) - { - return op_GreaterThanOrEqual(a, Convert.ToUInt64(b)); - } - return op_GreaterThanOrEqual(a, Convert.ToInt64(b)); - } - - public static bool op_GreaterThanOrEqual(Enum a, T b) - { - ThrowOnNull(a, ">="); - return op_LessThanOrEqual(b, a); - } - - #endregion - - #region Object equality operators - - public static bool op_Equality(T a, object b) - { - return false; - } - - public static bool op_Equality(object a, T b) - { - return false; - } - - public static bool op_Inequality(T a, object b) - { - return true; - } - - public static bool op_Inequality(object a, T b) - { - return true; - } - - #endregion } }