Browse Source

Support compound assignment for native integers.

pull/2063/head
Daniel Grunwald 5 years ago
parent
commit
1d684102ac
  1. 28
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs
  2. 27
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs
  3. 24
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 21
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  5. 10
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

28
ICSharpCode.Decompiler.Tests/TestCases/Correctness/CompoundAssignment.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
UnsignedShiftRightStaticProperty();
DivideByBigValue();
Overflow();
IntPtr_CompoundAssign();
}
static void Test(int a, int b)
@ -104,6 +105,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -104,6 +105,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
}
IntPtr intPtrField = new IntPtr(IntPtr.Size == 8 ? long.MaxValue : int.MaxValue);
public IntPtr IntPtrProperty {
get {
Console.WriteLine("In {0}.get_IntPtrProperty", instanceNumber);
return intPtrField;
}
set {
Console.WriteLine("In {0}.set_IntPtrProperty, value={1}", instanceNumber, value);
intPtrField = value;
}
}
public static Dictionary<string, int> GetDict()
{
Console.WriteLine("In GetDict()");
@ -112,8 +126,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -112,8 +126,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static CompoundAssignment GetObject()
{
Console.WriteLine("In GetObject() (instance #)");
return new CompoundAssignment();
var obj = new CompoundAssignment();
Console.WriteLine("In GetObject() (instance #{0})", obj.instanceNumber);
return obj;
}
static string GetString()
@ -208,5 +223,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -208,5 +223,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
return val;
}
static void IntPtr_CompoundAssign()
{
Console.WriteLine("IntPtr_CompoundAssign:");
#if !MCS
GetObject().IntPtrProperty -= 2;
GetObject().IntPtrProperty += 2;
#endif
}
}
}

27
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs

@ -134,5 +134,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -134,5 +134,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
(nint)i64
};
}
public NativeInts GetInstance(int i)
{
return this;
}
public void CompoundAssign()
{
GetInstance(0).i += i32;
checked {
GetInstance(1).i += i32;
}
GetInstance(2).u *= 2u;
checked {
GetInstance(3).u *= 2u;
}
GetInstance(4).intptr += (nint)i32;
checked {
// Note: the cast is necessary here, without it we'd call IntPtr.op_Addition
// but that is always unchecked.
GetInstance(5).intptr += (nint)i32;
}
// multiplication results in compiler-error without the cast
GetInstance(6).intptr *= (nint)2;
GetInstance(7).i <<= i32;
}
}
}

24
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1577,10 +1577,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1577,10 +1577,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
} else {
IType targetType = NullableType.GetUnderlyingType(target.Type).GetEnumUnderlyingType();
if (NullableType.IsNullable(value.Type)) {
targetType = NullableType.Create(compilation, targetType);
}
value = value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion: true);
value = ConvertValue(value, targetType);
}
break;
case AssignmentOperatorType.Multiply:
@ -1590,10 +1587,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1590,10 +1587,7 @@ namespace ICSharpCode.Decompiler.CSharp
case AssignmentOperatorType.BitwiseOr:
case AssignmentOperatorType.ExclusiveOr: {
IType targetType = NullableType.GetUnderlyingType(target.Type);
if (NullableType.IsNullable(value.Type)) {
targetType = NullableType.Create(compilation, targetType);
}
value = value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion: true);
value = ConvertValue(value, targetType);
break;
}
}
@ -1605,6 +1599,20 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1605,6 +1599,20 @@ namespace ICSharpCode.Decompiler.CSharp
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
}
return resultExpr;
TranslatedExpression ConvertValue(TranslatedExpression value, IType targetType)
{
bool allowImplicitConversion = true;
if (targetType.GetStackType() == StackType.I) {
// Force explicit cast for (U)IntPtr, keep allowing implicit conversion only for n(u)int
allowImplicitConversion = targetType.IsCSharpNativeIntegerType();
targetType = targetType.GetSign() == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt;
}
if (NullableType.IsNullable(value.Type)) {
targetType = NullableType.Create(compilation, targetType);
}
return value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion);
}
}
TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op)

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

@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.IL @@ -128,7 +128,7 @@ namespace ICSharpCode.Decompiler.IL
/// Gets whether the instruction checks for overflow.
/// </summary>
public readonly bool CheckForOverflow;
/// <summary>
/// For integer operations that depend on the sign, specifies whether the operation
/// is signed or unsigned.
@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL @@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL
CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode)
: base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value)
{
Debug.Assert(IsBinaryCompatibleWithType(binary, type));
Debug.Assert(IsBinaryCompatibleWithType(binary, type, null));
this.CheckForOverflow = binary.CheckForOverflow;
this.Sign = binary.Sign;
this.LeftInputType = binary.LeftInputType;
@ -164,11 +164,11 @@ namespace ICSharpCode.Decompiler.IL @@ -164,11 +164,11 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType));
}
/// <summary>
/// Gets whether the specific binary instruction is compatible with a compound operation on the specified type.
/// </summary>
internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type)
internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type, DecompilerSettings settings)
{
if (binary.IsLifted) {
if (!NullableType.IsNullable(type))
@ -201,6 +201,19 @@ namespace ICSharpCode.Decompiler.IL @@ -201,6 +201,19 @@ namespace ICSharpCode.Decompiler.IL
default:
return false; // operator not supported on pointer types
}
} else if (type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) {
// "target.intptr *= 2;" is compiler error, but
// "target.intptr *= (nint)2;" works
if (settings != null && !settings.NativeIntegers) {
// But if native integers are not available, we cannot use compound assignment.
return false;
}
// The trick with casting the RHS to n(u)int doesn't work for shifts:
switch (binary.Operator) {
case BinaryNumericOperator.ShiftLeft:
case BinaryNumericOperator.ShiftRight:
return false;
}
}
if (binary.Sign != Sign.None) {
if (type.IsCSharpSmallIntegerType()) {

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

@ -198,9 +198,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -198,9 +198,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType)
static bool ValidateCompoundAssign(BinaryNumericInstruction binary, Conv conv, IType targetType, DecompilerSettings settings)
{
if (!NumericCompoundAssign.IsBinaryCompatibleWithType(binary, targetType))
if (!NumericCompoundAssign.IsBinaryCompatibleWithType(binary, targetType, settings))
return false;
if (conv != null && !(conv.TargetType == targetType.ToPrimitiveType() && conv.CheckForOverflow == binary.CheckForOverflow))
return false; // conv does not match binary operation
@ -305,7 +305,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -305,7 +305,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable))
return false;
if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
if (!ValidateCompoundAssign(binary, smallIntConv, targetType, context.Settings))
return false;
context.Step($"Compound assignment (binary.numeric)", compoundStore);
finalizeMatch?.Invoke(context);
@ -656,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -656,7 +656,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (binary != null && (binary.Right.MatchLdcI(1) || binary.Right.MatchLdcF4(1) || binary.Right.MatchLdcF8(1))) {
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
if (!ValidateCompoundAssign(binary, conv, targetType))
if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings))
return false;
stloc = binary.Left as StLoc;
} else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) {
@ -731,7 +731,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -731,7 +731,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub))
return false;
if (!ValidateCompoundAssign(binary, conv, targetType))
if (!ValidateCompoundAssign(binary, conv, targetType, context.Settings))
return false;
context.Step("TransformPostIncDecOperator (builtin)", inst);
finalizeMatch?.Invoke(context);

Loading…
Cancel
Save