Browse Source

WIP

deconstruction-fixes
Siegfried Pammer 4 years ago
parent
commit
ad4684e1b0
  1. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 18
      ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs
  3. 47
      ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -138,6 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -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(),

18
ICSharpCode.Decompiler/IL/Instructions/DeconstructInstruction.cs

@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.IL @@ -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<ILVariable> assignedVariables, ICompilation typeSystem, out IType expectedType, out ILInstruction value)
{
expectedType = null;
value = null;
@ -228,6 +228,12 @@ namespace ICSharpCode.Decompiler.IL @@ -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 @@ -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 @@ -292,9 +304,11 @@ namespace ICSharpCode.Decompiler.IL
}
Debug.Assert(this.conversions.FinalInstruction is Nop);
HashSet<ILVariable> assignedVariables = new HashSet<ILVariable>(assignments.Instructions.OfType<StLoc>().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));
}

47
ICSharpCode.Decompiler/IL/Transforms/DeconstructionTransform.cs

@ -18,13 +18,10 @@ @@ -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 @@ -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<ILVariable> assignmentVariables = new HashSet<ILVariable>();
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 @@ -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<DeconstructInstruction> delayedActions)
{
@ -425,7 +411,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -425,7 +411,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool MatchAssignment(ILInstruction inst, out IType targetType, out ILInstruction valueInst, out Action<DeconstructInstruction> addAssignment)
bool MatchAssignment(ILInstruction inst, HashSet<ILVariable> assignmentVariables, out IType targetType, out ILInstruction valueInst, out Action<DeconstructInstruction> addAssignment)
{
targetType = null;
valueInst = null;
@ -435,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -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 @@ -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

Loading…
Cancel
Save