From 3c46271a1123d3c8a02fcb5151bcd239754776f7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 2 Jun 2023 21:37:42 +0200 Subject: [PATCH] Add support for unsigned right shift. Only for user-defined operators so far; builtin right shifts still cast to the appropriate type. --- .../Pretty/CompoundAssignmentTest.cs | 32 +++++++++++++++++++ .../TestCases/Pretty/Operators.cs | 10 ++++++ .../CSharp/CSharpDecompiler.cs | 1 + .../CSharp/ExpressionBuilder.cs | 7 ++-- .../CSharp/OutputVisitor/CSharpAmbience.cs | 1 + .../OutputVisitor/CSharpOutputVisitor.cs | 1 + .../GenericGrammarAmbiguityVisitor.cs | 3 ++ .../OutputVisitor/InsertParenthesesVisitor.cs | 1 + .../CSharp/Resolver/CSharpOperators.cs | 21 ++++++++++++ .../CSharp/Resolver/CSharpResolver.cs | 7 +++- .../Expressions/AssignmentExpression.cs | 9 ++++++ .../Expressions/BinaryOperatorExpression.cs | 5 +++ .../Syntax/TypeMembers/OperatorDeclaration.cs | 5 +++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 7 ++++ .../ReplaceMethodCallsWithOperators.cs | 6 ++-- ICSharpCode.Decompiler/DecompilerSettings.cs | 21 +++++++++++- .../IL/Transforms/TransformAssignment.cs | 2 +- ICSharpCode.Decompiler/Output/IAmbience.cs | 4 +++ ILSpy/Languages/CSharpLanguage.cs | 4 +++ ILSpy/Properties/Resources.Designer.cs | 9 ++++++ ILSpy/Properties/Resources.resx | 3 ++ 21 files changed, 152 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index e9c391ae3..abc6bb7e5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -201,6 +201,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { throw new NotImplementedException(); } +#if CS110 + public static CustomStruct operator >>>(CustomStruct lhs, int rhs) + { + throw new NotImplementedException(); + } +#endif public static CustomStruct operator &(CustomStruct lhs, CustomStruct rhs) { throw new NotImplementedException(); @@ -4219,6 +4225,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty #endif } +#if CS110 + public static void CustomStructUnsignedRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s) + { + //CustomStruct l = default(CustomStruct); + //p >>>= 5; + //l >>>= 5; + customStructField >>>= 5; + CustomStructProp >>>= 5; + c.CustomStructField >>>= 5; + c.CustomStructProp >>>= 5; + s.CustomStructField >>>= 5; + s.CustomStructProp >>>= 5; + customClassField.CustomStructField >>>= 5; + customClassField.CustomStructProp >>>= 5; + otherCustomStructField.CustomStructField >>>= 5; + otherCustomStructField.CustomStructProp >>>= 5; + CustomClassProp.CustomStructField >>>= 5; + CustomClassProp.CustomStructProp >>>= 5; + GetClass().CustomStructField >>>= 5; + GetClass().CustomStructProp >>>= 5; + GetRefStruct().CustomStructField >>>= 5; + GetRefStruct().CustomStructProp >>>= 5; + GetRefCustomStruct() >>>= 5; + } +#endif + public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s) { //CustomStruct l = default(CustomStruct); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs index 35e4a4a18..0e73a7952 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs @@ -72,6 +72,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return null; } +#if CS110 + public static AllOperators operator >>>(AllOperators a, int b) + { + return null; + } +#endif + public static AllOperators operator ~(AllOperators a) { return null; @@ -170,6 +177,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty c = a ^ b; c = a << 5; c = a >> 5; +#if CS110 + c = a >>> 5; +#endif c = ~a; c = !a; c = -a; diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 86ae6bf22..08d815d91 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -530,6 +530,7 @@ namespace ICSharpCode.Decompiler.CSharp typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors; typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses; typeSystemAstBuilder.SupportRecordStructs = settings.RecordStructs; + typeSystemAstBuilder.SupportUnsignedRightShift = settings.UnsignedRightShift; typeSystemAstBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal; return typeSystemAstBuilder; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 0972105f1..91a86192a 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1729,6 +1729,7 @@ namespace ICSharpCode.Decompiler.CSharp case BinaryOperatorType.ExclusiveOr: case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: return false; default: return true; @@ -1804,7 +1805,7 @@ namespace ICSharpCode.Decompiler.CSharp else if (inst.Method.Parameters.Count == 2) { var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this); - AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name); + AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name, settings); Debug.Assert(op != null); return new AssignmentExpression(target, op.Value, value) @@ -1822,7 +1823,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name) + internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -1846,6 +1847,8 @@ namespace ICSharpCode.Decompiler.CSharp return AssignmentOperatorType.ShiftLeft; case "op_RightShift": return AssignmentOperatorType.ShiftRight; + case "op_UnsignedRightShift" when settings.UnsignedRightShift: + return AssignmentOperatorType.UnsignedShiftRight; default: return null; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index 2e04d0575..207613994 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -237,6 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astBuilder.SupportInitAccessors = (ConversionFlags & ConversionFlags.SupportInitAccessors) != 0; astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0; astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0; + astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0; return astBuilder; } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index ce48d69ca..9a4788f70 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -826,6 +826,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor break; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: spacePolicy = policy.SpaceAroundShiftOperator; break; case BinaryOperatorType.NullCoalescing: diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs index 74e99d8c0..e7f372f96 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs @@ -92,6 +92,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2: genericNestingLevel -= 2; break; + case BinaryOperatorType.UnsignedShiftRight when genericNestingLevel >= 3: + genericNestingLevel -= 3; + break; default: return true; // stop visiting, no ambiguity found } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index e279cf021..2fb3a4546 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -122,6 +122,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor return PrecedenceLevel.Additive; case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftRight: + case BinaryOperatorType.UnsignedShiftRight: return PrecedenceLevel.Shift; case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThanOrEqual: diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs index b3f453157..fff06f134 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs @@ -674,6 +674,27 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } } } + + OperatorMethod[]? unsignedShiftRightOperators; + + public OperatorMethod[] UnsignedShiftRightOperators { + get { + OperatorMethod[]? ops = LazyInit.VolatileRead(ref unsignedShiftRightOperators); + if (ops != null) + { + return ops; + } + else + { + return LazyInit.GetOrSet(ref unsignedShiftRightOperators, Lift( + new LambdaBinaryOperatorMethod(this, (a, b) => (int)((uint)a >> b)), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b), + new LambdaBinaryOperatorMethod(this, (a, b) => (long)((ulong)a >> b)), + new LambdaBinaryOperatorMethod(this, (a, b) => a >> b) + )); + } + } + } #endregion #region Equality operators diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index 1e6d5df8b..f235e0a98 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -685,7 +685,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { isNullable = true; } - if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) + if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight || op == BinaryOperatorType.UnsignedShiftRight) { // special case: the shift operators allow "var x = null << null", producing int?. if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) @@ -805,6 +805,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver case BinaryOperatorType.ShiftRight: methodGroup = operators.ShiftRightOperators; break; + case BinaryOperatorType.UnsignedShiftRight: + methodGroup = operators.UnsignedShiftRightOperators; + break; case BinaryOperatorType.Equality: case BinaryOperatorType.InEquality: case BinaryOperatorType.LessThan: @@ -1256,6 +1259,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver return "op_LeftShift"; case BinaryOperatorType.ShiftRight: return "op_RightShift"; + case BinaryOperatorType.UnsignedShiftRight: + return "op_UnsignedRightShift"; case BinaryOperatorType.Equality: return "op_Equality"; case BinaryOperatorType.InEquality: diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index acd1038a8..378b33bfa 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole ModulusRole = new TokenRole("%="); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<="); public readonly static TokenRole ShiftRightRole = new TokenRole(">>="); + public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>="); public readonly static TokenRole BitwiseAndRole = new TokenRole("&="); public readonly static TokenRole BitwiseOrRole = new TokenRole("|="); public readonly static TokenRole ExclusiveOrRole = new TokenRole("^="); @@ -129,6 +130,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ShiftLeftRole; case AssignmentOperatorType.ShiftRight: return ShiftRightRole; + case AssignmentOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case AssignmentOperatorType.BitwiseAnd: return BitwiseAndRole; case AssignmentOperatorType.BitwiseOr: @@ -164,6 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return BinaryOperatorType.ShiftLeft; case AssignmentOperatorType.ShiftRight: return BinaryOperatorType.ShiftRight; + case AssignmentOperatorType.UnsignedShiftRight: + return BinaryOperatorType.UnsignedShiftRight; case AssignmentOperatorType.BitwiseAnd: return BinaryOperatorType.BitwiseAnd; case AssignmentOperatorType.BitwiseOr: @@ -195,6 +200,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ExpressionType.LeftShiftAssign; case AssignmentOperatorType.ShiftRight: return ExpressionType.RightShiftAssign; + case AssignmentOperatorType.UnsignedShiftRight: + return ExpressionType.Extension; case AssignmentOperatorType.BitwiseAnd: return ExpressionType.AndAssign; case AssignmentOperatorType.BitwiseOr: @@ -259,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ShiftLeft, /// left >>= right ShiftRight, + /// left >>>= right + UnsignedShiftRight, /// left &= right BitwiseAnd, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs index f64752e06..294b9f584 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs @@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public readonly static TokenRole ModulusRole = new TokenRole("%"); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<"); public readonly static TokenRole ShiftRightRole = new TokenRole(">>"); + public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>"); public readonly static TokenRole NullCoalescingRole = new TokenRole("??"); public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; @@ -151,6 +152,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return ShiftLeftRole; case BinaryOperatorType.ShiftRight: return ShiftRightRole; + case BinaryOperatorType.UnsignedShiftRight: + return UnsignedShiftRightRole; case BinaryOperatorType.NullCoalescing: return NullCoalescingRole; case BinaryOperatorType.Range: @@ -262,6 +265,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ShiftLeft, /// left >> right ShiftRight, + /// left >>> right + UnsignedShiftRight, /// left ?? right NullCoalescing, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs index c0f8236b8..4d8677ca7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs @@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax ExclusiveOr, LeftShift, RightShift, + UnsignedRightShift, Equality, Inequality, GreaterThan, @@ -94,6 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole ExclusiveOrRole = new TokenRole("^"); public static readonly TokenRole LeftShiftRole = new TokenRole("<<"); public static readonly TokenRole RightShiftRole = new TokenRole(">>"); + public static readonly TokenRole UnsignedRightShiftRole = new TokenRole(">>>"); public static readonly TokenRole EqualityRole = new TokenRole("=="); public static readonly TokenRole InequalityRole = new TokenRole("!="); public static readonly TokenRole GreaterThanRole = new TokenRole(">"); @@ -127,6 +129,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" }; names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" }; names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" }; + names[(int)OperatorType.UnsignedRightShift] = new string[] { ">>>", "op_UnsignedRightShift" }; names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" }; names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" }; names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" }; @@ -230,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return LeftShiftRole; case OperatorType.RightShift: return RightShiftRole; + case OperatorType.UnsignedRightShift: + return UnsignedRightShiftRole; case OperatorType.Equality: return EqualityRole; case OperatorType.Inequality: diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3751c4454..17b896710 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -225,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// public bool SupportRecordStructs { get; set; } + /// + /// Controls whether C# 11 "operator >>>" is supported. + /// + public bool SupportUnsignedRightShift { get; set; } + /// /// Controls whether all fully qualified type names should be prefixed with "global::". /// @@ -2217,6 +2222,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax OperatorType? opType = OperatorDeclaration.GetOperatorType(op.Name); if (opType == null) return ConvertMethod(op); + if (opType == OperatorType.UnsignedRightShift && !SupportUnsignedRightShift) + return ConvertMethod(op); OperatorDeclaration decl = new OperatorDeclaration(); decl.Modifiers = GetMemberModifiers(op); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index f2f7a355f..738fcd46a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms break; } - BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name); + BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name, context.Settings); if (bop != null && arguments.Length == 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression @@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) + static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name, DecompilerSettings settings) { switch (name) { @@ -374,6 +374,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return BinaryOperatorType.ShiftLeft; case "op_RightShift": return BinaryOperatorType.ShiftRight; + case "op_UnsignedRightShift" when settings.UnsignedRightShift: + return BinaryOperatorType.UnsignedShiftRight; case "op_Equality": return BinaryOperatorType.Equality; case "op_Inequality": diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 2a8e2a4ae..dac595efa 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -154,12 +154,13 @@ namespace ICSharpCode.Decompiler requiredMembers = false; numericIntPtr = false; utf8StringLiterals = false; + unsignedRightShift = false; } } public CSharp.LanguageVersion GetMinimumRequiredVersion() { - if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals) + if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift) return CSharp.LanguageVersion.CSharp11_0; if (fileScopedNamespaces || recordStructs) return CSharp.LanguageVersion.CSharp10_0; @@ -1199,6 +1200,24 @@ namespace ICSharpCode.Decompiler } } + bool unsignedRightShift = true; + + /// + /// Gets/Sets whether to use C# 11.0 unsigned right shift operator. + /// + [Category("C# 11.0 / VS 2022.4")] + [Description("DecompilerSettings.UnsignedRightShift")] + public bool UnsignedRightShift { + get { return unsignedRightShift; } + set { + if (unsignedRightShift != value) + { + unsignedRightShift = value; + OnPropertyChanged(); + } + } + } + bool showXmlDocumentation = true; /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 7407179b4..849ac8840 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { - if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null) + if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name, context.Settings) == null) return false; rhs = operatorCall.Arguments[1]; } diff --git a/ICSharpCode.Decompiler/Output/IAmbience.cs b/ICSharpCode.Decompiler/Output/IAmbience.cs index d496204b7..74bbcf072 100644 --- a/ICSharpCode.Decompiler/Output/IAmbience.cs +++ b/ICSharpCode.Decompiler/Output/IAmbience.cs @@ -109,6 +109,10 @@ namespace ICSharpCode.Decompiler.Output /// Support record structs. /// SupportRecordStructs = 0x40000, + /// + /// Support >>> as unsigned right shift operator. + /// + SupportUnsignedRightShift = 0x80000, StandardConversionFlags = ShowParameterNames | ShowAccessibility | diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index a5cd1e1a0..4486366a9 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -749,6 +749,10 @@ namespace ICSharpCode.ILSpy { flags |= ConversionFlags.SupportRecordStructs; } + if (settings.UnsignedRightShift) + { + flags |= ConversionFlags.SupportUnsignedRightShift; + } if (settings.InitAccessors) { flags |= ConversionFlags.SupportInitAccessors; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 0e93e78f5..f5396a9e2 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1280,6 +1280,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Unsigned right shift (>>>). + /// + public static string DecompilerSettings_UnsignedRightShift { + get { + return ResourceManager.GetString("DecompilerSettings.UnsignedRightShift", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use discards. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index ffa61461d..1368de08d 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -450,6 +450,9 @@ Are you sure you want to continue? Switch expressions + + Unsigned right shift (>>>) + Use discards