From d3f8912d53c8a395ad45a71b648e86e70cf6c4a3 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 16 Jun 2022 00:54:15 +0200 Subject: [PATCH] Handle special-cases with addressof: - Never introduce casts for ldobj-address-chains - Add special-case to TransformExpressionTrees: transform addressof(ldloc) to ldloca - Classify foreach and using variables as readonly lvalues --- .../CSharp/ExpressionBuilder.cs | 19 ++++++++++++++++++- .../IL/Transforms/ILInlining.cs | 5 ++++- .../IL/Transforms/TransformExpressionTrees.cs | 8 ++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index e6a8584b7..cd3918ff6 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -3784,7 +3784,9 @@ namespace ICSharpCode.Decompiler.CSharp // on a mutable lvalue, we would end up modifying the original lvalue, not just the copy. // We solve this by introducing a "redundant" cast. Casts are classified as rvalue // and ensure that the C# compiler will also create a copy. - if (classification == ExpressionClassification.MutableLValue && value.Expression is not CastExpression) + if (classification == ExpressionClassification.MutableLValue + && !CanIgnoreCopy() + && value.Expression is not CastExpression) { value = new CastExpression(ConvertType(inst.Type), value.Expression) .WithoutILInstruction() @@ -3793,6 +3795,21 @@ namespace ICSharpCode.Decompiler.CSharp return new DirectionExpression(FieldDirection.Ref, value) .WithILInstruction(inst) .WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); + + bool CanIgnoreCopy() + { + ILInstruction loadAddress = inst; + while (loadAddress.Parent is LdFlda parent) + { + loadAddress = parent; + } + if (loadAddress.Parent is LdObj) + { + // Ignore copy, never introduce a cast + return true; + } + return false; + } } protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 21c6e9d45..ecdb7d2b4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -416,7 +416,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms { case OpCode.LdLoc: case OpCode.StLoc: - if (((IInstructionWithVariableOperand)inst).Variable.IsRefReadOnly) + ILVariable v = ((IInstructionWithVariableOperand)inst).Variable; + if (v.IsRefReadOnly + || v.Kind == VariableKind.ForeachLocal + || v.Kind == VariableKind.UsingLocal) { return ExpressionClassification.ReadonlyLValue; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 997a5dcbe..eef43cb7d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -637,9 +637,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms { case StackType.Ref: if (target.ResultType == StackType.Ref) + { return target; + } + else if (target is LdLoc ldloc) + { + return new LdLoca(ldloc.Variable).WithILRange(ldloc); + } else + { return new AddressOf(target, expectedType); + } case StackType.O: if (targetType.IsReferenceType == false) {