From ad4684e1b0e445a6e9485fa44cdcc7bc555fd761 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 12 Dec 2021 00:53:44 +0100 Subject: [PATCH] WIP --- .../CSharp/CSharpDecompiler.cs | 1 + .../IL/Instructions/DeconstructInstruction.cs | 18 ++++++- .../IL/Transforms/DeconstructionTransform.cs | 47 ++++++++----------- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index b4e814627..903c42379 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -138,6 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp // Inlining must be first, because it doesn't trigger re-runs. // Any other transform that opens up new inlining opportunities should call RequestRerun(). new ExpressionTransforms(), + new DeconstructionTransform(), new DynamicIsEventAssignmentTransform(), new TransformAssignment(), // inline and compound assignments new NullCoalescingTransform(), diff --git a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs index d49a684de..5fe931fd1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs @@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.IL return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable); } - internal static bool IsAssignment(ILInstruction inst, ICompilation typeSystem, out IType expectedType, out ILInstruction value) + internal static bool IsAssignment(ILInstruction inst, HashSet assignedVariables, ICompilation typeSystem, out IType expectedType, out ILInstruction value) { expectedType = null; value = null; @@ -228,6 +228,12 @@ namespace ICSharpCode.Decompiler.IL } else if (arg.MatchLdLoc(out var v)) { + // Variables used as arguments of call-assignments must not be variables that are assigned to inside the deconstruction. + // Except for pattern locals, but these are definitely assigned in the assignment block. + if (v.Kind != VariableKind.PatternLocal && assignedVariables?.Contains(v) == true) + { + return false; + } } else { @@ -251,6 +257,12 @@ namespace ICSharpCode.Decompiler.IL } else if (target.MatchLdLoc(out var v)) { + // Variables used as arguments of call-assignments must not be variables that are assigned to inside the deconstruction. + // Except for pattern locals, but these are definitely assigned in the assignment block. + if (v.Kind != VariableKind.PatternLocal && assignedVariables?.Contains(v) == true) + { + return false; + } } else { @@ -292,9 +304,11 @@ namespace ICSharpCode.Decompiler.IL } Debug.Assert(this.conversions.FinalInstruction is Nop); + HashSet assignedVariables = new HashSet(assignments.Instructions.OfType().Select(stloc => stloc.Variable)); + foreach (var inst in assignments.Instructions) { - if (!(IsAssignment(inst, typeSystem: null, out _, out var value) && value.MatchLdLoc(out var inputVariable))) + if (!(IsAssignment(inst, assignedVariables, null, out _, out var value) && value.MatchLdLoc(out var inputVariable))) throw new InvalidOperationException("inst is not an assignment!"); Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable)); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs index 4559dd6a5..bbdffa57b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs @@ -18,13 +18,10 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; -using System.Resources; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -345,11 +342,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms int previousIndex = -1; int conversionStLocIndex = 0; int startPos = pos; - while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), out var targetType, out var valueInst, out var addAssignment)) + HashSet assignmentVariables = new HashSet(); + while (MatchAssignment(block.Instructions.ElementAtOrDefault(pos), assignmentVariables, out var targetType, out var valueInst, out var addAssignment)) { int index = FindIndex(valueInst, out var tupleAccessAdjustment); if (index <= previousIndex) - return false; + break; AddMissingAssignmentsForConversions(index, ref delayedActions); if (!(valueInst.MatchLdLoc(out var resultVariable) && conversions.TryGetValue(resultVariable, out var conversionInfo))) @@ -377,30 +375,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms pos++; previousIndex = index; } - AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions); - if (deconstructionResults != null) + if (startPos != pos) { - int i = previousIndex + 1; - while (i < deconstructionResults.Length) - { - var v = deconstructionResults[i]; - // this should only happen in release mode, where usually the last deconstruction element - // is not stored to a temporary, if it is used directly (and only once!) - // after the deconstruction. - if (v?.LoadCount == 1) - { - delayedActions += (DeconstructInstruction deconstructInst) => { - var freshVar = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type); - deconstructInst.Assignments.Instructions.Add(new StLoc(freshVar, new LdLoc(v))); - v.LoadInstructions[0].Variable = freshVar; - }; - } - i++; - } + AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions); + + return true; } + else + { + return false; - return startPos != pos; + } void AddMissingAssignmentsForConversions(int index, ref Action delayedActions) { @@ -425,7 +411,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool MatchAssignment(ILInstruction inst, out IType targetType, out ILInstruction valueInst, out Action addAssignment) + bool MatchAssignment(ILInstruction inst, HashSet assignmentVariables, out IType targetType, out ILInstruction valueInst, out Action addAssignment) { targetType = null; valueInst = null; @@ -435,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (inst.MatchStLoc(out var v, out var value) && value is Block block && block.MatchInlineAssignBlock(out var call, out valueInst)) { - if (!DeconstructInstruction.IsAssignment(call, context.TypeSystem, out targetType, out _)) + if (!DeconstructInstruction.IsAssignment(call, assignmentVariables, context.TypeSystem, out targetType, out _)) return false; if (!(v.IsSingleDefinition && v.LoadCount == 0)) return false; @@ -446,12 +432,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms }; return true; } - else if (DeconstructInstruction.IsAssignment(inst, context.TypeSystem, out targetType, out valueInst)) + else if (DeconstructInstruction.IsAssignment(inst, assignmentVariables, context.TypeSystem, out targetType, out valueInst)) { // OK - use the assignment as is addAssignment = (DeconstructInstruction deconstructInst) => { deconstructInst.Assignments.Instructions.Add(inst); }; + // Mark variable as assigned + if (inst is StLoc stloc) + { + assignmentVariables.Add(stloc.Variable); + } return true; } else