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
// Inlining must be first, because it doesn't trigger re-runs. // Inlining must be first, because it doesn't trigger re-runs.
// Any other transform that opens up new inlining opportunities should call RequestRerun(). // Any other transform that opens up new inlining opportunities should call RequestRerun().
new ExpressionTransforms(), new ExpressionTransforms(),
new DeconstructionTransform(),
new DynamicIsEventAssignmentTransform(), new DynamicIsEventAssignmentTransform(),
new TransformAssignment(), // inline and compound assignments new TransformAssignment(), // inline and compound assignments
new NullCoalescingTransform(), new NullCoalescingTransform(),

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

@ -210,7 +210,7 @@ namespace ICSharpCode.Decompiler.IL
return input.MatchLdLoc(out inputVariable) || input.MatchLdLoca(out inputVariable); 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; expectedType = null;
value = null; value = null;
@ -228,6 +228,12 @@ namespace ICSharpCode.Decompiler.IL
} }
else if (arg.MatchLdLoc(out var v)) 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 else
{ {
@ -251,6 +257,12 @@ namespace ICSharpCode.Decompiler.IL
} }
else if (target.MatchLdLoc(out var v)) 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 else
{ {
@ -292,9 +304,11 @@ namespace ICSharpCode.Decompiler.IL
} }
Debug.Assert(this.conversions.FinalInstruction is Nop); 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) 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!"); throw new InvalidOperationException("inst is not an assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable)); Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
} }

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

@ -18,13 +18,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Resources;
using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms namespace ICSharpCode.Decompiler.IL.Transforms
{ {
@ -345,11 +342,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
int previousIndex = -1; int previousIndex = -1;
int conversionStLocIndex = 0; int conversionStLocIndex = 0;
int startPos = pos; 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); int index = FindIndex(valueInst, out var tupleAccessAdjustment);
if (index <= previousIndex) if (index <= previousIndex)
return false; break;
AddMissingAssignmentsForConversions(index, ref delayedActions); AddMissingAssignmentsForConversions(index, ref delayedActions);
if (!(valueInst.MatchLdLoc(out var resultVariable) if (!(valueInst.MatchLdLoc(out var resultVariable)
&& conversions.TryGetValue(resultVariable, out var conversionInfo))) && conversions.TryGetValue(resultVariable, out var conversionInfo)))
@ -377,30 +375,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
pos++; pos++;
previousIndex = index; 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) => { AddMissingAssignmentsForConversions(int.MaxValue, ref delayedActions);
var freshVar = context.Function.RegisterVariable(VariableKind.StackSlot, v.Type);
deconstructInst.Assignments.Instructions.Add(new StLoc(freshVar, new LdLoc(v))); return true;
v.LoadInstructions[0].Variable = freshVar;
};
}
i++;
}
} }
else
{
return false;
return startPos != pos; }
void AddMissingAssignmentsForConversions(int index, ref Action<DeconstructInstruction> delayedActions) void AddMissingAssignmentsForConversions(int index, ref Action<DeconstructInstruction> delayedActions)
{ {
@ -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; targetType = null;
valueInst = null; valueInst = null;
@ -435,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.MatchStLoc(out var v, out var value) if (inst.MatchStLoc(out var v, out var value)
&& value is Block block && block.MatchInlineAssignBlock(out var call, out valueInst)) && 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; return false;
if (!(v.IsSingleDefinition && v.LoadCount == 0)) if (!(v.IsSingleDefinition && v.LoadCount == 0))
return false; return false;
@ -446,12 +432,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}; };
return true; 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 // OK - use the assignment as is
addAssignment = (DeconstructInstruction deconstructInst) => { addAssignment = (DeconstructInstruction deconstructInst) => {
deconstructInst.Assignments.Instructions.Add(inst); deconstructInst.Assignments.Instructions.Add(inst);
}; };
// Mark variable as assigned
if (inst is StLoc stloc)
{
assignmentVariables.Add(stloc.Variable);
}
return true; return true;
} }
else else

Loading…
Cancel
Save