Browse Source

Merge pull request #2994 from icsharpcode/unsigned-right-shift

pull/3012/head
Siegfried Pammer 2 years ago committed by GitHub
parent
commit
a54c5c6ba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 106
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  3. 225
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs
  4. 30
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs
  5. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  6. 49
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  8. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  9. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs
  10. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  11. 21
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs
  12. 7
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  13. 9
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs
  14. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs
  15. 5
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs
  16. 7
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  17. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs
  18. 6
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  19. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  20. 10
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  21. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  22. 4
      ICSharpCode.Decompiler/Output/IAmbience.cs
  23. 4
      ILSpy/Languages/CSharpLanguage.cs
  24. 9
      ILSpy/Properties/Resources.Designer.cs
  25. 3
      ILSpy/Properties/Resources.resx

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -312,6 +312,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -312,6 +312,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task Operators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{

106
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -201,6 +201,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -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();
@ -1685,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1685,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void ShortUnsignedRightShiftTest(short p, CustomClass c, CustomStruct2 s)
{
//X(p >>>= 5);
shortField >>>= 5;
ShortProp >>>= 5;
c.ShortField >>>= 5;
c.ShortProp >>>= 5;
s.ShortField >>>= 5;
s.ShortProp >>>= 5;
customClassField.ShortField >>>= 5;
customClassField.ShortProp >>>= 5;
otherCustomStructField.ShortField >>>= 5;
otherCustomStructField.ShortProp >>>= 5;
CustomClassProp.ShortField >>>= 5;
CustomClassProp.ShortProp >>>= 5;
GetClass().ShortField >>>= 5;
GetClass().ShortProp >>>= 5;
GetRefStruct().ShortField >>>= 5;
GetRefStruct().ShortProp >>>= 5;
GetRefShort() >>>= 5;
}
#endif
public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s)
{
//short l = 0;
@ -2047,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -2047,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void UshortUnsignedRightShiftTest(ushort p, CustomClass c, CustomStruct2 s)
{
//ushort l = 0;
//p >>>= 5;
//l >>>= 5;
ushortField >>>= 5;
UshortProp >>>= 5;
c.UshortField >>>= 5;
c.UshortProp >>>= 5;
s.UshortField >>>= 5;
s.UshortProp >>>= 5;
customClassField.UshortField >>>= 5;
customClassField.UshortProp >>>= 5;
otherCustomStructField.UshortField >>>= 5;
otherCustomStructField.UshortProp >>>= 5;
CustomClassProp.UshortField >>>= 5;
CustomClassProp.UshortProp >>>= 5;
GetClass().UshortField >>>= 5;
GetClass().UshortProp >>>= 5;
GetRefStruct().UshortField >>>= 5;
GetRefStruct().UshortProp >>>= 5;
GetRefUshort() >>>= 5;
}
#endif
public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s)
{
//ushort l = 0;
@ -2409,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -2409,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif
}
#if CS110
public static void IntUnsignedRightShiftTest(int p, CustomClass c, CustomStruct2 s)
{
X(p >>>= 5);
intField >>>= 5;
IntProp >>>= 5;
c.IntField >>>= 5;
c.IntProp >>>= 5;
s.IntField >>>= 5;
s.IntProp >>>= 5;
customClassField.IntField >>>= 5;
customClassField.IntProp >>>= 5;
otherCustomStructField.IntField >>>= 5;
otherCustomStructField.IntProp >>>= 5;
CustomClassProp.IntField >>>= 5;
CustomClassProp.IntProp >>>= 5;
GetClass().IntField >>>= 5;
GetClass().IntProp >>>= 5;
GetRefStruct().IntField >>>= 5;
GetRefStruct().IntProp >>>= 5;
GetRefInt() >>>= 5;
}
#endif
public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s)
{
//int l = 0;
@ -4219,6 +4299,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4219,6 +4299,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);

225
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs

@ -0,0 +1,225 @@ @@ -0,0 +1,225 @@
// Copyright (c) Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class AllOperators
{
public static AllOperators operator +(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator -(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator *(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator /(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator %(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator &(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator |(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator ^(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator <<(AllOperators a, int b)
{
return null;
}
public static AllOperators operator >>(AllOperators a, int b)
{
return null;
}
#if CS110
public static AllOperators operator >>>(AllOperators a, int b)
{
return null;
}
#endif
public static AllOperators operator ~(AllOperators a)
{
return null;
}
public static AllOperators operator !(AllOperators a)
{
return null;
}
public static AllOperators operator -(AllOperators a)
{
return null;
}
public static AllOperators operator +(AllOperators a)
{
return null;
}
public static AllOperators operator ++(AllOperators a)
{
return null;
}
public static AllOperators operator --(AllOperators a)
{
return null;
}
public static bool operator true(AllOperators a)
{
return false;
}
public static bool operator false(AllOperators a)
{
return false;
}
public static bool operator ==(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator !=(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator <(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator >(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator <=(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator >=(AllOperators a, AllOperators b)
{
return false;
}
public static implicit operator AllOperators(int a)
{
return null;
}
public static explicit operator int(AllOperators a)
{
return 0;
}
}
public class UseAllOperators
{
private AllOperators a = new AllOperators();
private AllOperators b = new AllOperators();
private AllOperators c;
public void Test()
{
c = a + b;
c = a - b;
c = a * b;
c = a / b;
c = a % b;
c = a & b;
c = a | b;
c = a ^ b;
c = a << 5;
c = a >> 5;
#if CS110
c = a >>> 5;
#endif
c = ~a;
c = !a;
c = -a;
c = +a;
c = ++a;
c = --a;
if (a)
{
Console.WriteLine("a");
}
if (!a)
{
Console.WriteLine("!a");
}
if (a == b)
{
Console.WriteLine("a == b");
}
if (a != b)
{
Console.WriteLine("a != b");
}
if (a < b)
{
Console.WriteLine("a < b");
}
if (a > b)
{
Console.WriteLine("a > b");
}
if (a <= b)
{
Console.WriteLine("a <= b");
}
if (a >= b)
{
Console.WriteLine("a >= b");
}
int num = (int)a;
a = num;
}
}
}

30
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs

@ -106,9 +106,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -106,9 +106,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public uint RShiftByteWithZeroExtension(byte num)
{
// zero extend -> cast to unsigned -> unsigned shift
return (uint)num >> 8;
}
public int RShiftByteWithZeroExtensionReturnAsSigned(byte num)
{
#if CS110
// zero extend -> unsigned shift
return num >>> 8;
#else
// zero extend -> cast to unsigned -> unsigned shift -> cast to signed
return (int)((uint)num >> 8);
#endif
}
public int RShiftByteAsSByte(byte num)
{
return (sbyte)num >> 8;
@ -121,9 +133,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -121,9 +133,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public uint RShiftSByteWithZeroExtension(sbyte num)
{
return (uint)num >> 8;
return (uint)((byte)num >> 4);
}
public uint RShiftSByteWithSignExtension(sbyte num)
{
// sign extend -> cast to unsigned -> unsigned shift
return (uint)num >> 4;
}
public int RShiftSByteWithSignExtensionReturnAsSigned(sbyte num)
{
#if CS110
// sign extend -> unsigned shift
return num >>> 4;
#else
// sign extend -> cast to unsigned -> unsigned shift -> cast to signed
return (int)((uint)num >> 4);
#endif
}
public int RShiftSByteAsByte(sbyte num)
{
return (byte)num >> 8;

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -530,6 +530,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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;
}

49
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1208,9 +1208,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1208,9 +1208,9 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.BitXor:
return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context);
case BinaryNumericOperator.ShiftLeft:
return HandleShift(inst, BinaryOperatorType.ShiftLeft);
return HandleShift(inst, BinaryOperatorType.ShiftLeft, context);
case BinaryNumericOperator.ShiftRight:
return HandleShift(inst, BinaryOperatorType.ShiftRight);
return HandleShift(inst, BinaryOperatorType.ShiftRight, context);
default:
throw new ArgumentOutOfRangeException();
}
@ -1729,13 +1729,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1729,13 +1729,14 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryOperatorType.ExclusiveOr:
case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight:
case BinaryOperatorType.UnsignedShiftRight:
return false;
default:
return true;
}
}
TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op)
TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context)
{
var left = Translate(inst.Left);
var right = Translate(inst.Right);
@ -1744,10 +1745,27 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1744,10 +1745,27 @@ namespace ICSharpCode.Decompiler.CSharp
Sign sign = inst.Sign;
var leftUType = NullableType.GetUnderlyingType(left.Type);
if (leftUType.IsCSharpSmallIntegerType() && sign != Sign.Unsigned && inst.UnderlyingResultType == StackType.I4)
bool couldUseUnsignedRightShift = (
sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift
&& (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType())
// If we need to cast to unsigned anyway, don't use >>> operator.
&& context.TypeHint.GetSign() != Sign.Unsigned
);
if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 &&
(sign != Sign.Unsigned || couldUseUnsignedRightShift))
{
// With small integer types, C# will promote to int and perform signed shifts.
// We thus don't need any casts in this case.
// The >>> operator also promotes to signed int, but then performs an unsigned shift.
if (sign == Sign.Unsigned)
{
op = BinaryOperatorType.UnsignedShiftRight;
}
}
else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize() && leftUType.GetSign() == Sign.Signed)
{
// Use C# 11 unsigned right shift operator. We don't need any casts in this case.
op = BinaryOperatorType.UnsignedShiftRight;
}
else
{
@ -1804,7 +1822,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1804,7 +1822,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 +1840,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1822,7 +1840,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name)
internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings)
{
switch (name)
{
@ -1846,6 +1864,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1846,6 +1864,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;
}
@ -1887,7 +1907,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1887,7 +1907,22 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.ShiftLeft:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft);
case BinaryNumericOperator.ShiftRight:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight);
if (inst.Sign == Sign.Unsigned && inst.Type.GetSign() == Sign.Signed)
{
Debug.Assert(settings.UnsignedRightShift);
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else if (inst.Sign == Sign.Unsigned && inst.Type.IsCSharpSmallIntegerType() && settings.UnsignedRightShift)
{
// For small unsigned integer types promoted to signed int, the sign bit will be zero,
// so there is no difference between signed and unsigned shift.
// However the IL still indicates which C# operator was used, so preserve that if the setting allows us to.
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else
{
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight);
}
default:
throw new ArgumentOutOfRangeException();
}

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -237,6 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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;
}

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -826,6 +826,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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:

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs

@ -92,6 +92,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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
}

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -122,6 +122,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -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:

21
ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs

@ -674,6 +674,27 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -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<int, int>(this, (a, b) => (int)((uint)a >> b)),
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a >> b),
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => (long)((ulong)a >> b)),
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a >> b)
));
}
}
}
#endregion
#region Equality operators

7
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -685,7 +685,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -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 @@ -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 @@ -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:

9
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs

@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -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 @@ -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 @@ -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 @@ -259,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ShiftLeft,
/// <summary>left >>= right</summary>
ShiftRight,
/// <summary>left >>>= right</summary>
UnsignedShiftRight,
/// <summary>left &amp;= right</summary>
BitwiseAnd,

6
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs

@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -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:
@ -205,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -205,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case BinaryOperatorType.NullCoalescing:
return ExpressionType.Coalesce;
case BinaryOperatorType.Range:
case BinaryOperatorType.UnsignedShiftRight:
return ExpressionType.Extension;
default:
throw new NotSupportedException("Invalid value for BinaryOperatorType");
@ -262,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -262,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ShiftLeft,
/// <summary>left &gt;&gt; right</summary>
ShiftRight,
/// <summary>left &gt;&gt;&gt; right</summary>
UnsignedShiftRight,
/// <summary>left ?? right</summary>
NullCoalescing,

5
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs

@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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 @@ -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 @@ -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 @@ -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:

7
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -225,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -225,6 +225,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary>
public bool SupportRecordStructs { get; set; }
/// <summary>
/// Controls whether C# 11 "operator >>>" is supported.
/// </summary>
public bool SupportUnsignedRightShift { get; set; }
/// <summary>
/// Controls whether all fully qualified type names should be prefixed with "global::".
/// </summary>
@ -2217,6 +2222,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -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);

2
ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs

@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.UnsignedShiftRight:
return AssignmentOperatorType.UnsignedShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:

6
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -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 @@ -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 @@ -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":

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -154,12 +154,13 @@ namespace ICSharpCode.Decompiler @@ -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 @@ -1199,6 +1200,24 @@ namespace ICSharpCode.Decompiler
}
}
bool unsignedRightShift = true;
/// <summary>
/// Gets/Sets whether to use C# 11.0 unsigned right shift operator.
/// </summary>
[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;
/// <summary>

10
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL @@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL
return false; // operator not supported on pointer types
}
}
else if (type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr))
else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt)
{
// If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr):
// "target.intptr *= 2;" is compiler error, but
// "target.intptr *= (nint)2;" works
if (settings != null && !settings.NativeIntegers)
@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL @@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL
}
if (binary.Sign != Sign.None)
{
bool signMismatchAllowed = (binary.Sign == Sign.Unsigned && binary.Operator == BinaryNumericOperator.ShiftRight && (settings == null || settings.UnsignedRightShift));
if (type.IsCSharpSmallIntegerType())
{
// C# will use numeric promotion to int, binary op must be signed
if (binary.Sign != Sign.Signed)
if (binary.Sign != Sign.Signed && !signMismatchAllowed)
return false;
}
else
{
// C# will use sign from type
if (type.GetSign() != binary.Sign)
// C# will use sign from type; except for right shift with C# 11 >>> operator.
if (type.GetSign() != binary.Sign && !signMismatchAllowed)
return false;
}
}

2
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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];
}

4
ICSharpCode.Decompiler/Output/IAmbience.cs

@ -109,6 +109,10 @@ namespace ICSharpCode.Decompiler.Output @@ -109,6 +109,10 @@ namespace ICSharpCode.Decompiler.Output
/// Support <c>record</c> structs.
/// </summary>
SupportRecordStructs = 0x40000,
/// <summary>
/// Support <c>&gt;&gt;&gt;</c> as unsigned right shift operator.
/// </summary>
SupportUnsignedRightShift = 0x80000,
StandardConversionFlags = ShowParameterNames |
ShowAccessibility |

4
ILSpy/Languages/CSharpLanguage.cs

@ -749,6 +749,10 @@ namespace ICSharpCode.ILSpy @@ -749,6 +749,10 @@ namespace ICSharpCode.ILSpy
{
flags |= ConversionFlags.SupportRecordStructs;
}
if (settings.UnsignedRightShift)
{
flags |= ConversionFlags.SupportUnsignedRightShift;
}
if (settings.InitAccessors)
{
flags |= ConversionFlags.SupportInitAccessors;

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1280,6 +1280,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1280,6 +1280,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Unsigned right shift (&gt;&gt;&gt;).
/// </summary>
public static string DecompilerSettings_UnsignedRightShift {
get {
return ResourceManager.GetString("DecompilerSettings.UnsignedRightShift", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use discards.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -450,6 +450,9 @@ Are you sure you want to continue?</value> @@ -450,6 +450,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.SwitchExpressions" xml:space="preserve">
<value>Switch expressions</value>
</data>
<data name="DecompilerSettings.UnsignedRightShift" xml:space="preserve">
<value>Unsigned right shift (&gt;&gt;&gt;)</value>
</data>
<data name="DecompilerSettings.UseDiscards" xml:space="preserve">
<value>Use discards</value>
</data>

Loading…
Cancel
Save