Browse Source

Merge branch 'compound-assign'

pull/1596/head
Daniel Grunwald 6 years ago
parent
commit
51d81f478a
  1. 80
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  2. 2
      ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs
  3. 4
      ICSharpCode.Decompiler/IL/Instructions.cs
  4. 7
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  5. 153
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  6. 2
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  7. 16
      ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs
  8. 3
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  9. 138
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  10. 10
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

80
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1326,7 +1326,13 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context)
{ {
var target = Translate(inst.Target); IType loadType = inst.Method.Parameters[0].Type;
ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, loadType);
} else {
target = Translate(inst.Target, loadType);
}
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) { if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) {
Debug.Assert(inst.Method.Parameters.Count == 2); Debug.Assert(inst.Method.Parameters.Count == 2);
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true);
@ -1342,7 +1348,7 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst) .WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); .WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult }));
} else { } else {
UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue); UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue);
Debug.Assert(op != null); Debug.Assert(op != null);
return new UnaryOperatorExpression(op.Value, target) return new UnaryOperatorExpression(op.Value, target)
@ -1421,10 +1427,15 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op) TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op)
{ {
var target = Translate(inst.Target); ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, inst.Type);
} else {
target = Translate(inst.Target, inst.Type);
}
TranslatedExpression resultExpr; TranslatedExpression resultExpr;
if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) { if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) {
Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract); Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract);
Debug.Assert(inst.Value.MatchLdcI(1)); Debug.Assert(inst.Value.MatchLdcI(1));
UnaryOperatorType unary; UnaryOperatorType unary;
@ -1485,8 +1496,13 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op) TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op)
{ {
Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue); Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue);
var target = Translate(inst.Target); ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, inst.Type);
} else {
target = Translate(inst.Target, inst.Type);
}
var value = Translate(inst.Value); var value = Translate(inst.Value);
// Shift operators in C# always expect type 'int' on the right-hand-side // Shift operators in C# always expect type 'int' on the right-hand-side
@ -1901,46 +1917,46 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context)
{ {
var target = Translate(inst.Target); var result = LdObj(inst.Target, inst.Type);
if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, inst.Type)) { //if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign())
TranslatedExpression result; // return result.ConvertTo(loadType, this);
return result.WithILInstruction(inst);
}
ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType)
{
var target = Translate(address);
if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) {
ExpressionWithResolveResult result;
if (target.Expression is DirectionExpression dirExpr) { if (target.Expression is DirectionExpression dirExpr) {
// we can dereference the managed reference by stripping away the 'ref' // we can dereference the managed reference by stripping away the 'ref'
result = target.UnwrapChild(dirExpr.Expression); result = target.UnwrapChild(dirExpr.Expression);
result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation
} else if (target.Type is PointerType pointerType) { } else if (target.Type is PointerType pointerType) {
if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) {
// We can dereference the pointer by stripping away the '&' // We can dereference the pointer by stripping away the '&'
result = target.UnwrapChild(uoe.Expression); result = target.UnwrapChild(uoe.Expression);
result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation
} else { } else {
// Dereference the existing pointer // Dereference the existing pointer
result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst)
.WithRR(new ResolveResult(pointerType.ElementType)); .WithRR(new ResolveResult(pointerType.ElementType));
} }
} else { } else {
// reference type behind non-DirectionExpression? // reference type behind non-DirectionExpression?
// this case should be impossible, but we can use a pointer cast // this case should be impossible, but we can use a pointer cast
// just to make sure // just to make sure
target = target.ConvertTo(new PointerType(inst.Type), this); target = target.ConvertTo(new PointerType(loadType), this);
return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst) .WithRR(new ResolveResult(loadType));
.WithRR(new ResolveResult(inst.Type));
} }
// we don't convert result to inst.Type, because the LdObj type // we don't convert result to inst.Type, because the LdObj type
// might be inaccurate (it's often System.Object for all reference types), // might be inaccurate (it's often System.Object for all reference types),
// and our parent node should already insert casts where necessary // and our parent node should already insert casts where necessary
if (target.Type.IsSmallIntegerType() && inst.Type.IsSmallIntegerType() && target.Type.GetSign() != inst.Type.GetSign())
return result.ConvertTo(inst.Type, this);
return result; return result;
} else { } else {
// We need to cast the pointer type: // We need to cast the pointer type:
target = target.ConvertTo(new PointerType(inst.Type), this); target = target.ConvertTo(new PointerType(loadType), this);
return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression)
.WithILInstruction(inst) .WithRR(new ResolveResult(loadType));
.WithRR(new ResolveResult(inst.Type));
} }
} }
@ -2194,8 +2210,6 @@ namespace ICSharpCode.Decompiler.CSharp
case BlockKind.CollectionInitializer: case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer: case BlockKind.ObjectInitializer:
return TranslateObjectAndCollectionInitializer(block); return TranslateObjectAndCollectionInitializer(block);
case BlockKind.PostfixOperator:
return TranslatePostfixOperator(block);
case BlockKind.CallInlineAssign: case BlockKind.CallInlineAssign:
return TranslateSetterCallAssignment(block); return TranslateSetterCallAssignment(block);
case BlockKind.CallWithNamedArgs: case BlockKind.CallWithNamedArgs:
@ -2530,19 +2544,6 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(stloc.Variable.Type)); .WithRR(new ResolveResult(stloc.Variable.Type));
} }
TranslatedExpression TranslatePostfixOperator(Block block)
{
var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value;
var inst = (block.Instructions.ElementAtOrDefault(1) as StLoc)?.Value as BinaryNumericInstruction;
if (targetInst == null || inst == null || (inst.Operator != BinaryNumericOperator.Add && inst.Operator != BinaryNumericOperator.Sub))
throw new ArgumentException("given Block is invalid!");
var op = inst.Operator == BinaryNumericOperator.Add ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
var target = Translate(targetInst);
return new UnaryOperatorExpression(op, target)
.WithILInstruction(block)
.WithRR(resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveUnaryOperator(op, target.ResolveResult));
}
/// <summary> /// <summary>
/// If expr is a constant integer expression, and its value fits into type, /// If expr is a constant integer expression, and its value fits into type,
/// convert the expression into the target type. /// convert the expression into the target type.
@ -3039,7 +3040,12 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context) protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context)
{ {
var target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); ExpressionWithResolveResult target;
if (inst.TargetKind == CompoundTargetKind.Address) {
target = LdObj(inst.Target, SpecialType.Dynamic);
} else {
target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo);
}
var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo);
var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value); var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value);

2
ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs

@ -382,6 +382,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// ASCII characters we allow directly in the output even though we don't use // ASCII characters we allow directly in the output even though we don't use
// other Unicode characters of the same category. // other Unicode characters of the same category.
return null; return null;
case '\ufffd':
return "\\u" + ((int)ch).ToString("x4");
default: default:
switch (char.GetUnicodeCategory(ch)) { switch (char.GetUnicodeCategory(ch)) {
case UnicodeCategory.ModifierLetter: case UnicodeCategory.ModifierLetter:

4
ICSharpCode.Decompiler/IL/Instructions.cs

@ -1092,7 +1092,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as UserDefinedCompoundAssign; var o = other as UserDefinedCompoundAssign;
return o != null && this.Method.Equals(o.Method) && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
} }
} }
} }
@ -1126,7 +1126,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{ {
var o = other as DynamicCompoundAssign; var o = other as DynamicCompoundAssign;
return o != null && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); return o != null && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match);
} }
} }
} }

7
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -329,13 +329,6 @@ namespace ICSharpCode.Decompiler.IL
ObjectInitializer, ObjectInitializer,
StackAllocInitializer, StackAllocInitializer,
/// <summary> /// <summary>
/// Block is used for postfix operator on local variable.
/// </summary>
/// <remarks>
/// Postfix operators on non-locals use CompoundAssignmentInstruction with CompoundAssignmentType.EvaluatesToOldValue.
/// </remarks>
PostfixOperator,
/// <summary>
/// Block is used for using the result of a property setter inline. /// Block is used for using the result of a property setter inline.
/// Example: <code>Use(this.Property = value);</code> /// Example: <code>Use(this.Property = value);</code>
/// This is only for inline assignments to property or indexers; other inline assignments work /// This is only for inline assignments to property or indexers; other inline assignments work

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

@ -23,36 +23,101 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
public enum CompoundAssignmentType : byte public enum CompoundEvalMode : byte
{ {
/// <summary>
/// The compound.assign instruction will evaluate to the old value.
/// This mode is used only for post-increment/decrement.
/// </summary>
EvaluatesToOldValue, EvaluatesToOldValue,
/// <summary>
/// The compound.assign instruction will evaluate to the new value.
/// This mode is used for compound assignments and pre-increment/decrement.
/// </summary>
EvaluatesToNewValue EvaluatesToNewValue
} }
public enum CompoundTargetKind : byte
{
/// <summary>
/// The target is an instruction computing an address,
/// and the compound.assign will implicitly load/store from/to that address.
/// </summary>
Address,
/// <summary>
/// The Target must be a call to a property getter,
/// and the compound.assign will implicitly call the corresponding property setter.
/// </summary>
Property,
/// <summary>
/// The target is a dynamic call.
/// </summary>
Dynamic
}
public abstract partial class CompoundAssignmentInstruction : ILInstruction public abstract partial class CompoundAssignmentInstruction : ILInstruction
{ {
public readonly CompoundAssignmentType CompoundAssignmentType; public readonly CompoundEvalMode EvalMode;
/// <summary>
/// If TargetIsProperty is true, the Target must be a call to a property getter,
/// and the compound.assign will implicitly call the corresponding property setter.
/// Otherwise, the Target can be any instruction that evaluates to an address,
/// and the compound.assign will implicit load and store from/to that address.
/// </summary>
public readonly CompoundTargetKind TargetKind;
public CompoundAssignmentInstruction(OpCode opCode, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) public CompoundAssignmentInstruction(OpCode opCode, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value)
: base(opCode) : base(opCode)
{ {
this.CompoundAssignmentType = compoundAssignmentType; this.EvalMode = evalMode;
this.Target = target; this.Target = target;
this.TargetKind = targetKind;
this.Value = value; this.Value = value;
CheckValidTarget();
} }
internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) internal override void CheckInvariant(ILPhase phase)
{ {
switch (inst.OpCode) { base.CheckInvariant(phase);
// case OpCode.LdLoc: -- not valid -- does not mark the variable as written to CheckValidTarget();
case OpCode.LdObj: }
return true;
case OpCode.Call: [Conditional("DEBUG")]
case OpCode.CallVirt: void CheckValidTarget()
var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty; {
return owner != null && owner.CanSet; switch (TargetKind) {
default: case CompoundTargetKind.Address:
return false; Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
break;
case CompoundTargetKind.Property:
Debug.Assert(target.OpCode == OpCode.Call || target.OpCode == OpCode.CallVirt);
var owner = ((CallInstruction)target).Method.AccessorOwner as IProperty;
Debug.Assert(owner != null && owner.CanSet);
break;
case CompoundTargetKind.Dynamic:
Debug.Assert(target.OpCode == OpCode.DynamicGetMemberInstruction || target.OpCode == OpCode.DynamicGetIndexInstruction);
break;
}
}
protected void WriteSuffix(ITextOutput output)
{
switch (TargetKind) {
case CompoundTargetKind.Address:
output.Write(".address");
break;
case CompoundTargetKind.Property:
output.Write(".property");
break;
}
switch (EvalMode) {
case CompoundEvalMode.EvaluatesToNewValue:
output.Write(".new");
break;
case CompoundEvalMode.EvaluatesToOldValue:
output.Write(".old");
break;
} }
} }
} }
@ -82,8 +147,9 @@ namespace ICSharpCode.Decompiler.IL
public bool IsLifted { get; } public bool IsLifted { get; }
public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target,
: base(OpCode.NumericCompoundAssign, compoundAssignmentType, target, value) 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));
this.CheckForOverflow = binary.CheckForOverflow; this.CheckForOverflow = binary.CheckForOverflow;
@ -95,8 +161,7 @@ namespace ICSharpCode.Decompiler.IL
this.IsLifted = binary.IsLifted; this.IsLifted = binary.IsLifted;
this.type = type; this.type = type;
this.AddILRange(binary); this.AddILRange(binary);
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType));
} }
@ -176,16 +241,20 @@ namespace ICSharpCode.Decompiler.IL
WriteILRange(output, options); WriteILRange(output, options);
output.Write(OpCode); output.Write(OpCode);
output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator)); output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator));
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) if (CheckForOverflow) {
output.Write(".new");
else
output.Write(".old");
if (CheckForOverflow)
output.Write(".ovf"); output.Write(".ovf");
if (Sign == Sign.Unsigned) }
if (Sign == Sign.Unsigned) {
output.Write(".unsigned"); output.Write(".unsigned");
else if (Sign == Sign.Signed) } else if (Sign == Sign.Signed) {
output.Write(".signed"); output.Write(".signed");
}
output.Write('.');
output.Write(UnderlyingResultType.ToString().ToLowerInvariant());
if (IsLifted) {
output.Write(".lifted");
}
base.WriteSuffix(output);
output.Write('('); output.Write('(');
Target.WriteTo(output, options); Target.WriteTo(output, options);
output.Write(", "); output.Write(", ");
@ -199,13 +268,13 @@ namespace ICSharpCode.Decompiler.IL
public readonly IMethod Method; public readonly IMethod Method;
public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments
public UserDefinedCompoundAssign(IMethod method, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) public UserDefinedCompoundAssign(IMethod method, CompoundEvalMode evalMode,
: base(OpCode.UserDefinedCompoundAssign, compoundAssignmentType, target, value) ILInstruction target, CompoundTargetKind targetKind, ILInstruction value)
: base(OpCode.UserDefinedCompoundAssign, evalMode, target, targetKind, value)
{ {
this.Method = method; this.Method = method;
Debug.Assert(Method.IsOperator || IsStringConcat(method)); Debug.Assert(Method.IsOperator || IsStringConcat(method));
Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement")); Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement"));
Debug.Assert(IsValidCompoundAssignmentTarget(Target));
} }
public static bool IsStringConcat(IMethod method) public static bool IsStringConcat(IMethod method)
@ -219,11 +288,7 @@ namespace ICSharpCode.Decompiler.IL
{ {
WriteILRange(output, options); WriteILRange(output, options);
output.Write(OpCode); output.Write(OpCode);
base.WriteSuffix(output);
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue)
output.Write(".new");
else
output.Write(".old");
output.Write(' '); output.Write(' ');
Method.WriteTo(output); Method.WriteTo(output);
output.Write('('); output.Write('(');
@ -241,8 +306,11 @@ namespace ICSharpCode.Decompiler.IL
public CSharpArgumentInfo ValueArgumentInfo { get; } public CSharpArgumentInfo ValueArgumentInfo { get; }
public CSharpBinderFlags BinderFlags { get; } public CSharpBinderFlags BinderFlags { get; }
public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, ILInstruction target, CSharpArgumentInfo targetArgumentInfo, ILInstruction value, CSharpArgumentInfo valueArgumentInfo) public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags,
: base(OpCode.DynamicCompoundAssign, CompoundAssignmentTypeFromOperation(op), target, value) ILInstruction target, CSharpArgumentInfo targetArgumentInfo,
ILInstruction value, CSharpArgumentInfo valueArgumentInfo,
CompoundTargetKind targetKind = CompoundTargetKind.Dynamic)
: base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value)
{ {
if (!IsExpressionTypeSupported(op)) if (!IsExpressionTypeSupported(op))
throw new ArgumentOutOfRangeException("op"); throw new ArgumentOutOfRangeException("op");
@ -258,10 +326,7 @@ namespace ICSharpCode.Decompiler.IL
output.Write(OpCode); output.Write(OpCode);
output.Write("." + Operation.ToString().ToLower()); output.Write("." + Operation.ToString().ToLower());
DynamicInstruction.WriteBinderFlags(BinderFlags, output, options); DynamicInstruction.WriteBinderFlags(BinderFlags, output, options);
if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) base.WriteSuffix(output);
output.Write(".new");
else
output.Write(".old");
output.Write(' '); output.Write(' ');
DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo));
} }
@ -287,14 +352,14 @@ namespace ICSharpCode.Decompiler.IL
|| type == ExpressionType.SubtractAssignChecked; || type == ExpressionType.SubtractAssignChecked;
} }
static CompoundAssignmentType CompoundAssignmentTypeFromOperation(ExpressionType op) static CompoundEvalMode CompoundEvalModeFromOperation(ExpressionType op)
{ {
switch (op) { switch (op) {
case ExpressionType.PostIncrementAssign: case ExpressionType.PostIncrementAssign:
case ExpressionType.PostDecrementAssign: case ExpressionType.PostDecrementAssign:
return CompoundAssignmentType.EvaluatesToOldValue; return CompoundEvalMode.EvaluatesToOldValue;
default: default:
return CompoundAssignmentType.EvaluatesToNewValue; return CompoundEvalMode.EvaluatesToNewValue;
} }
} }
} }

2
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -276,6 +276,8 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
internal void RecombineVariables(ILVariable variable1, ILVariable variable2) internal void RecombineVariables(ILVariable variable1, ILVariable variable2)
{ {
if (variable1 == variable2)
return;
Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2)); Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2));
foreach (var ldloc in variable2.LoadInstructions.ToArray()) { foreach (var ldloc in variable2.LoadInstructions.ToArray()) {
ldloc.Variable = variable1; ldloc.Variable = variable1;

16
ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs

@ -434,18 +434,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary> /// <summary>
/// Returns true if the instruction is stloc v(add(ldloc v, arg)) /// Returns true if the instruction is stloc v(add(ldloc v, arg))
/// or stloc v(compound.assign(ldloc v, arg)) /// or compound.assign(ldloca v, arg)
/// </summary> /// </summary>
public static bool MatchIncrement(ILInstruction inst, out ILVariable variable) public static bool MatchIncrement(ILInstruction inst, out ILVariable variable)
{ {
if (!inst.MatchStLoc(out variable, out var value)) if (inst.MatchStLoc(out variable, out var value)) {
return false; if (value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) {
if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { return left.MatchLdLoc(variable);
if (value is CompoundAssignmentInstruction cai) { }
left = cai.Target; } else if (inst is CompoundAssignmentInstruction cai) {
} else return false; return cai.TargetKind == CompoundTargetKind.Address && cai.Target.MatchLdLoca(out variable);
} }
return left.MatchLdLoc(variable); return false;
} }
/// <summary> /// <summary>

3
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -424,10 +424,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; // inline into (left slot of) user-defined && or || operator return true; // inline into (left slot of) user-defined && or || operator
case OpCode.DynamicGetMemberInstruction: case OpCode.DynamicGetMemberInstruction:
case OpCode.DynamicGetIndexInstruction: case OpCode.DynamicGetIndexInstruction:
case OpCode.LdObj:
if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign)
return true; // inline into dynamic compound assignments return true; // inline into dynamic compound assignments
break; break;
case OpCode.DynamicCompoundAssign:
return true;
case OpCode.ArrayToPointer: case OpCode.ArrayToPointer:
case OpCode.LocAllocSpan: case OpCode.LocAllocSpan:
return true; // inline size-expressions into localloc.span return true; // inline size-expressions into localloc.span

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

@ -44,8 +44,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
if (context.Settings.IntroduceIncrementAndDecrement) { if (context.Settings.IntroduceIncrementAndDecrement) {
if (TransformPostIncDecOperatorWithInlineStore(block, pos) if (TransformPostIncDecOperatorWithInlineStore(block, pos)
|| TransformPostIncDecOperator(block, pos) || TransformPostIncDecOperator(block, pos)) {
|| TransformPostIncDecOperatorLocal(block, pos)) {
// again, new top-level stloc might need inlining: // again, new top-level stloc might need inlining:
context.RequestRerun(); context.RequestRerun();
return; return;
@ -276,18 +275,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} }
ILInstruction newInst; ILInstruction newInst;
if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) {
if (!IsMatchingCompoundLoad(binary.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable))
return false; return false;
if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) if (!ValidateCompoundAssign(binary, smallIntConv, targetType))
return false; return false;
context.Step($"Compound assignment (binary.numeric)", compoundStore); context.Step($"Compound assignment (binary.numeric)", compoundStore);
newInst = new NumericCompoundAssign( newInst = new NumericCompoundAssign(
binary, binary.Left, binary.Right, binary, target, targetKind, binary.Right,
targetType, CompoundAssignmentType.EvaluatesToNewValue); targetType, CompoundEvalMode.EvaluatesToNewValue);
} else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) {
if (operatorCall.Arguments.Count == 0) if (operatorCall.Arguments.Count == 0)
return false; return false;
if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable))
return false; return false;
ILInstruction rhs; ILInstruction rhs;
if (operatorCall.Arguments.Count == 2) { if (operatorCall.Arguments.Count == 2) {
@ -305,24 +304,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (operatorCall.IsLifted) if (operatorCall.IsLifted)
return false; // TODO: add tests and think about whether nullables need special considerations return false; // TODO: add tests and think about whether nullables need special considerations
context.Step($"Compound assignment (user-defined binary)", compoundStore); context.Step($"Compound assignment (user-defined binary)", compoundStore);
newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundAssignmentType.EvaluatesToNewValue, newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue,
operatorCall.Arguments[0], rhs); target, targetKind, rhs);
} else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) {
if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable))
return false; return false;
context.Step($"Compound assignment (dynamic binary)", compoundStore); context.Step($"Compound assignment (dynamic binary)", compoundStore);
newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, dynamicBinaryOp.Left, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo); newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind);
} else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) {
// setterValue is a string.Concat() invocation // setterValue is a string.Concat() invocation
if (concatCall.Arguments.Count != 2) if (concatCall.Arguments.Count != 2)
return false; // for now we only support binary compound assignments return false; // for now we only support binary compound assignments
if (!targetType.IsKnownType(KnownTypeCode.String)) if (!targetType.IsKnownType(KnownTypeCode.String))
return false; return false;
if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable))
return false; return false;
context.Step($"Compound assignment (string concatenation)", compoundStore); context.Step($"Compound assignment (string concatenation)", compoundStore);
newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundAssignmentType.EvaluatesToNewValue, newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue,
concatCall.Arguments[0], concatCall.Arguments[1]); target, targetKind, concatCall.Arguments[1]);
} else { } else {
return false; return false;
} }
@ -428,48 +427,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true; return true;
} }
/// <code>
/// stloc s(ldloc l)
/// stloc l(binary.op(ldloc s, ldc.i4 1))
/// -->
/// stloc s(block {
/// stloc s2(ldloc l)
/// stloc l(binary.op(ldloc s2, ldc.i4 1))
/// final: ldloc s2
/// })
/// </code>
bool TransformPostIncDecOperatorLocal(Block block, int pos)
{
var inst = block.Instructions[pos] as StLoc;
var nextInst = block.Instructions.ElementAtOrDefault(pos + 1) as StLoc;
if (inst == null || nextInst == null || !inst.Value.MatchLdLoc(out var loadVar) || !ILVariableEqualityComparer.Instance.Equals(loadVar, nextInst.Variable))
return false;
var binary = nextInst.Value as BinaryNumericInstruction;
if (inst.Variable.Kind != VariableKind.StackSlot || nextInst.Variable.Kind == VariableKind.StackSlot || binary == null)
return false;
if (binary.IsLifted)
return false;
if ((binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub) || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1))
return false;
context.Step($"TransformPostIncDecOperatorLocal", inst);
if (loadVar != nextInst.Variable) {
// load and store are two different variables, that were split from the same variable
context.Function.RecombineVariables(loadVar, nextInst.Variable);
}
var tempStore = context.Function.RegisterVariable(VariableKind.StackSlot, inst.Variable.Type);
var assignment = new Block(BlockKind.PostfixOperator);
assignment.Instructions.Add(new StLoc(tempStore, new LdLoc(loadVar)));
assignment.Instructions.Add(new StLoc(loadVar, new BinaryNumericInstruction(binary.Operator, new LdLoc(tempStore), new LdcI4(1), binary.CheckForOverflow, binary.Sign)));
assignment.FinalInstruction = new LdLoc(tempStore);
inst.Value = assignment;
block.Instructions.RemoveAt(pos + 1); // remove nextInst
return true;
}
/// <summary> /// <summary>
/// Gets whether 'inst' is a possible store for use as a compound store. /// Gets whether 'inst' is a possible store for use as a compound store.
/// </summary> /// </summary>
static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value, ICompilation compilation) /// <remarks>
/// Output parameters:
/// storeType: The type of the value being stored.
/// value: The value being stored (will be analyzed further to detect compound assignments)
///
/// Every IsCompoundStore() call should be followed by an IsMatchingCompoundLoad() call.
/// </remarks>
static bool IsCompoundStore(ILInstruction inst, out IType storeType,
out ILInstruction value, ICompilation compilation)
{ {
value = null; value = null;
storeType = null; storeType = null;
@ -506,23 +475,45 @@ namespace ICSharpCode.Decompiler.IL.Transforms
storeType = call.Method.Parameters.Last().Type; storeType = call.Method.Parameters.Last().Type;
value = call.Arguments.Last(); value = call.Arguments.Last();
return IsSameMember(call.Method, (call.Method.AccessorOwner as IProperty)?.Setter); return IsSameMember(call.Method, (call.Method.AccessorOwner as IProperty)?.Setter);
} else if (inst is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.Parameter)) {
storeType = stloc.Variable.Type;
value = stloc.Value;
return true;
} else { } else {
return false; return false;
} }
} }
static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, ILVariable forbiddenVariable) static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store,
out ILInstruction target, out CompoundTargetKind targetKind,
ILFunction contextFunction = null,
ILVariable forbiddenVariable = null)
{ {
target = null;
targetKind = 0;
if (load is LdObj ldobj && store is StObj stobj) { if (load is LdObj ldobj && store is StObj stobj) {
Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags));
if (!SemanticHelper.IsPure(ldobj.Target.Flags)) if (!SemanticHelper.IsPure(ldobj.Target.Flags))
return false; return false;
if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(ldobj.Target)) if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(ldobj.Target))
return false; return false;
target = ldobj.Target;
targetKind = CompoundTargetKind.Address;
return ldobj.Target.Match(stobj.Target).Success; return ldobj.Target.Match(stobj.Target).Success;
} else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) { } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) {
if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load))
return false; return false;
target = load;
targetKind = CompoundTargetKind.Property;
return true;
} else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) {
if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable))
return false;
if (contextFunction == null)
return false; // locals only supported for the callers that specify the context
target = new LdLoca(ldloc.Variable).WithILRange(ldloc);
targetKind = CompoundTargetKind.Address;
contextFunction.RecombineVariables(ldloc.Variable, stloc.Variable);
return true; return true;
} else { } else {
return false; return false;
@ -551,8 +542,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos)
{ {
var store = block.Instructions[pos]; var store = block.Instructions[pos];
if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) {
return false; return false;
}
StLoc stloc; StLoc stloc;
var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction; var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction;
if (binary != null && binary.Right.MatchLdcI(1)) { if (binary != null && binary.Right.MatchLdcI(1)) {
@ -574,28 +566,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; return false;
if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot))
return false; return false;
if (!IsMatchingCompoundLoad(stloc.Value, store, stloc.Variable)) if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, forbiddenVariable: stloc.Variable))
return false; return false;
if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem))
return false; return false;
context.Step("TransformPostIncDecOperatorWithInlineStore", store); context.Step("TransformPostIncDecOperatorWithInlineStore", store);
if (binary != null) { if (binary != null) {
block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign(
binary, stloc.Value, binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue)); binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue));
} else { } else {
Call operatorCall = (Call)value; Call operatorCall = (Call)value;
block.Instructions[pos] = new StLoc(stloc.Variable, new UserDefinedCompoundAssign( block.Instructions[pos] = new StLoc(stloc.Variable, new UserDefinedCompoundAssign(
operatorCall.Method, CompoundAssignmentType.EvaluatesToOldValue, stloc.Value, new LdcI4(1))); operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)));
} }
return true; return true;
} }
/// <code> /// <code>
/// stloc l(ldobj(target)) /// stloc tmp(ldobj(target))
/// stobj(target, binary.op(ldloc l, ldc.i4 1)) /// stobj(target, binary.op(ldloc tmp, ldc.i4 1))
/// target is pure and does not use 'l', 'stloc does not truncate' /// target is pure and does not use 'tmp', 'stloc does not truncate'
/// --> /// -->
/// stloc l(compound.op.old(ldobj(target), ldc.i4 1)) /// stloc tmp(compound.op.old(ldobj(target), ldc.i4 1))
/// </code>
/// This is usually followed by inlining or eliminating 'tmp'.
///
/// Local variables use a similar pattern, also detected by this function:
/// <code>
/// stloc tmp(ldloc target)
/// stloc target(binary.op(ldloc tmp, ldc.i4 1))
/// -->
/// stloc tmp(compound.op.old(ldloca target, ldc.i4 1))
/// </code> /// </code>
/// <remarks> /// <remarks>
/// This pattern occurs with legacy csc for static fields, and with Roslyn for most post-increments. /// This pattern occurs with legacy csc for static fields, and with Roslyn for most post-increments.
@ -606,26 +607,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var store = block.Instructions.ElementAtOrDefault(i + 1); var store = block.Instructions.ElementAtOrDefault(i + 1);
if (inst == null || store == null) if (inst == null || store == null)
return false; return false;
var tmpVar = inst.Variable;
if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem))
return false; return false;
if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) { if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) {
// 'stloc l' is implicitly truncating the value // 'stloc tmp' is implicitly truncating the value
return false; return false;
} }
if (!IsMatchingCompoundLoad(inst.Value, store, inst.Variable)) if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, context.Function, forbiddenVariable: inst.Variable))
return false; return false;
if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) {
if (!binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI(1)) if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1))
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))
return false; return false;
context.Step("TransformPostIncDecOperator (builtin)", inst); context.Step("TransformPostIncDecOperator (builtin)", inst);
inst.Value = new NumericCompoundAssign(binary, inst.Value, binary.Right, inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right,
targetType, CompoundAssignmentType.EvaluatesToOldValue); targetType, CompoundEvalMode.EvaluatesToOldValue);
} 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) {
if (!operatorCall.Arguments[0].MatchLdLoc(inst.Variable)) if (!operatorCall.Arguments[0].MatchLdLoc(tmpVar))
return false; return false;
if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement"))
return false; return false;
@ -633,7 +635,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false; // TODO: add tests and think about whether nullables need special considerations return false; // TODO: add tests and think about whether nullables need special considerations
context.Step("TransformPostIncDecOperator (user-defined)", inst); context.Step("TransformPostIncDecOperator (user-defined)", inst);
inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, inst.Value = new UserDefinedCompoundAssign(operatorCall.Method,
CompoundAssignmentType.EvaluatesToOldValue, inst.Value, new LdcI4(1)); CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1));
} else { } else {
return false; return false;
} }

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

@ -305,15 +305,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Debug.Fail("LdFlda pattern not supported!"); Debug.Fail("LdFlda pattern not supported!");
} }
} }
protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst)
{
base.VisitNumericCompoundAssign(inst);
// NumericCompoundAssign is only valid when used with fields: -> replace it with a BinaryNumericInstruction.
if (inst.Target.MatchLdLoc(out var v)) {
inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign).WithILRange(inst)));
}
}
} }
} }

Loading…
Cancel
Save