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. 17
      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
UnsignedShiftRightStaticProperty(); UnsignedShiftRightStaticProperty();
DivideByBigValue(); DivideByBigValue();
Overflow(); Overflow();
IntPtr_CompoundAssign();
} }
static void Test(int a, int b) static void Test(int a, int b)
@ -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() public static Dictionary<string, int> GetDict()
{ {
Console.WriteLine("In GetDict()"); Console.WriteLine("In GetDict()");
@ -112,8 +126,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
static CompoundAssignment GetObject() static CompoundAssignment GetObject()
{ {
Console.WriteLine("In GetObject() (instance #)"); var obj = new CompoundAssignment();
return new CompoundAssignment(); Console.WriteLine("In GetObject() (instance #{0})", obj.instanceNumber);
return obj;
} }
static string GetString() static string GetString()
@ -208,5 +223,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{ {
return val; 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
(nint)i64 (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
} }
} else { } else {
IType targetType = NullableType.GetUnderlyingType(target.Type).GetEnumUnderlyingType(); IType targetType = NullableType.GetUnderlyingType(target.Type).GetEnumUnderlyingType();
if (NullableType.IsNullable(value.Type)) { value = ConvertValue(value, targetType);
targetType = NullableType.Create(compilation, targetType);
}
value = value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion: true);
} }
break; break;
case AssignmentOperatorType.Multiply: case AssignmentOperatorType.Multiply:
@ -1590,10 +1587,7 @@ namespace ICSharpCode.Decompiler.CSharp
case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.BitwiseOr:
case AssignmentOperatorType.ExclusiveOr: { case AssignmentOperatorType.ExclusiveOr: {
IType targetType = NullableType.GetUnderlyingType(target.Type); IType targetType = NullableType.GetUnderlyingType(target.Type);
if (NullableType.IsNullable(value.Type)) { value = ConvertValue(value, targetType);
targetType = NullableType.Create(compilation, targetType);
}
value = value.ConvertTo(targetType, this, inst.CheckForOverflow, allowImplicitConversion: true);
break; break;
} }
} }
@ -1605,6 +1599,20 @@ namespace ICSharpCode.Decompiler.CSharp
resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
} }
return resultExpr; 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) TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op)

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

@ -151,7 +151,7 @@ namespace ICSharpCode.Decompiler.IL
CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode) CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode)
: base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value) : base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value)
{ {
Debug.Assert(IsBinaryCompatibleWithType(binary, type)); Debug.Assert(IsBinaryCompatibleWithType(binary, type, null));
this.CheckForOverflow = binary.CheckForOverflow; this.CheckForOverflow = binary.CheckForOverflow;
this.Sign = binary.Sign; this.Sign = binary.Sign;
this.LeftInputType = binary.LeftInputType; this.LeftInputType = binary.LeftInputType;
@ -168,7 +168,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Gets whether the specific binary instruction is compatible with a compound operation on the specified type. /// Gets whether the specific binary instruction is compatible with a compound operation on the specified type.
/// </summary> /// </summary>
internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type) internal static bool IsBinaryCompatibleWithType(BinaryNumericInstruction binary, IType type, DecompilerSettings settings)
{ {
if (binary.IsLifted) { if (binary.IsLifted) {
if (!NullableType.IsNullable(type)) if (!NullableType.IsNullable(type))
@ -201,6 +201,19 @@ namespace ICSharpCode.Decompiler.IL
default: default:
return false; // operator not supported on pointer types 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 (binary.Sign != Sign.None) {
if (type.IsCSharpSmallIntegerType()) { if (type.IsCSharpSmallIntegerType()) {

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

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

Loading…
Cancel
Save