Browse Source

Implement foreach loop detection in StatementBuilder.

pull/870/merge
Siegfried Pammer 8 years ago
parent
commit
fa25b5d2e8
  1. 65
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 6
      ICSharpCode.Decompiler/IL/DetectedLoop.cs
  3. 1
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  4. 45
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  5. 22
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

65
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -26,6 +26,8 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -26,6 +26,8 @@ using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System;
using System.Threading;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp
{
@ -254,14 +256,75 @@ namespace ICSharpCode.Decompiler.CSharp @@ -254,14 +256,75 @@ namespace ICSharpCode.Decompiler.CSharp
};
}
#region foreach construction
static readonly AssignmentExpression getEnumeratorPattern = new AssignmentExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), new InvocationExpression(new MemberReferenceExpression(new AnyNode("collection").ToExpression(), "GetEnumerator")));
static readonly InvocationExpression moveNextConditionPattern = new InvocationExpression(new MemberReferenceExpression(new NamedNode("enumerator", new IdentifierExpression(Pattern.AnyString)), "MoveNext"));
protected internal override Statement VisitUsingInstruction(UsingInstruction inst)
{
var resource = exprBuilder.Translate(inst.ResourceExpression);
var m = getEnumeratorPattern.Match(resource.Expression);
if (inst.Body is BlockContainer container && m.Success) {
var enumeratorVar = m.Get<IdentifierExpression>("enumerator").Single().GetILVariable();
var loop = DetectedLoop.DetectLoop(container);
if (loop.Kind == LoopKind.While && loop.Body is Block body) {
var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
var m2 = moveNextConditionPattern.Match(condition.Expression);
if (m2.Success) {
var enumeratorVar2 = m2.Get<IdentifierExpression>("enumerator").Single().GetILVariable();
if (enumeratorVar2 == enumeratorVar && BodyHasSingleGetCurrent(body, enumeratorVar, condition.ILInstructions.Single(),
out var singleGetter, out var needsUninlining, out var itemVariable)) {
var collectionExpr = m.Get<Expression>("collection").Single().Detach();
if (needsUninlining) {
itemVariable = currentFunction.RegisterVariable(
VariableKind.ForeachLocal, singleGetter.Method.ReturnType,
AssignVariableNames.GenerateVariableName(currentFunction, collectionExpr.Annotation<ILInstruction>(), "item")
);
singleGetter.ReplaceWith(new LdLoc(itemVariable));
body.Instructions.Insert(0, new StLoc(itemVariable, singleGetter));
} else {
itemVariable.Kind = VariableKind.ForeachLocal;
itemVariable.Name = AssignVariableNames.GenerateVariableName(currentFunction, collectionExpr.Annotation<ILInstruction>(), "item", itemVariable);
}
var whileLoop = (WhileStatement)ConvertAsBlock(inst.Body).Single();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
foreachBody.Statements.First().Detach();
return new ForeachStatement {
VariableType = exprBuilder.ConvertType(itemVariable.Type),
VariableName = itemVariable.Name,
InExpression = collectionExpr,
EmbeddedStatement = foreachBody
};
}
}
}
}
return new UsingStatement {
ResourceAcquisition = exprBuilder.Translate(inst.ResourceExpression),
ResourceAcquisition = resource,
EmbeddedStatement = ConvertAsBlock(inst.Body)
};
}
bool BodyHasSingleGetCurrent(Block body, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out bool needsUninlining, out ILVariable existingVariable)
{
singleGetter = null;
needsUninlining = false;
existingVariable = null;
var loads = (enumerator.LoadInstructions.OfType<ILInstruction>().Concat(enumerator.AddressInstructions.OfType<ILInstruction>())).Where(ld => !ld.IsDescendantOf(moveNextUsage)).ToArray();
if (loads.Length == 1 && ParentIsCurrentGetter(loads[0])) {
singleGetter = (CallInstruction)loads[0].Parent;
needsUninlining = !singleGetter.Parent.MatchStLoc(out existingVariable);
}
return singleGetter != null && singleGetter.IsDescendantOf(body.Instructions[0]) && ILInlining.CanUninline(singleGetter, body.Instructions[0]);
}
bool ParentIsCurrentGetter(ILInstruction inst)
{
return inst.Parent is CallVirt cv && cv.Method.IsAccessor &&
cv.Method.AccessorOwner is IProperty p && p.Getter.Equals(cv.Method);
}
#endregion
protected internal override Statement VisitPinnedRegion(PinnedRegion inst)
{
var fixedStmt = new FixedStatement();

6
ICSharpCode.Decompiler/IL/DetectedLoop.cs

@ -118,12 +118,10 @@ namespace ICSharpCode.Decompiler.IL @@ -118,12 +118,10 @@ namespace ICSharpCode.Decompiler.IL
Kind = LoopKind.For;
IncrementTarget = variable;
AdditionalBlocks = Container.Blocks.Skip(1).ToArray();
} else {
AdditionalBlocks = Container.Blocks.Skip(1).ToArray();
return this;
}
} else {
AdditionalBlocks = Container.Blocks.Skip(1).ToArray();
}
AdditionalBlocks = Container.Blocks.Skip(1).ToArray();
} else {
// do-while or while(true)-loop
if (Container.EntryPoint.IncomingEdgeCount == 2) {

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

@ -148,6 +148,7 @@ namespace ICSharpCode.Decompiler.IL @@ -148,6 +148,7 @@ namespace ICSharpCode.Decompiler.IL
if (string.IsNullOrWhiteSpace(name)) {
switch (kind) {
case VariableKind.Local:
case VariableKind.ForeachLocal:
name = "V_";
break;
case VariableKind.Parameter:

45
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -61,7 +61,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
reservedVariableNames = new Dictionary<string, int>();
loopCounters = CollectLoopCounters(function);
foreach (var p in function.Descendants.OfType<ILFunction>().Select(f => f.Method).SelectMany(m => m.Parameters))
AddExistingName(p.Name);
AddExistingName(reservedVariableNames, p.Name);
foreach (ILFunction f in function.Descendants.OfType<ILFunction>().Reverse()) {
PerformAssignment(f);
}
@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case VariableKind.Parameter: // ignore
break;
case VariableKind.InitializerTarget: // keep generated names
AddExistingName(v.Name);
AddExistingName(reservedVariableNames, v.Name);
break;
default:
if (v.HasGeneratedName || !IsValidName(v.Name)) {
@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -96,7 +96,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var v = inst.Variable;
if (!mapping.TryGetValue(v, out string name)) {
if (string.IsNullOrEmpty(v.Name))
v.Name = GenerateNameForVariable(v, function.Body);
v.Name = GenerateNameForVariable(v);
mapping.Add(v, v.Name);
} else {
v.Name = name;
@ -128,8 +128,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -128,8 +128,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
int number;
string nameWithoutDigits = SplitName(oldVariableName, out number);
string nameWithoutDigits = SplitName(oldVariableName, out int number);
if (!reservedVariableNames.ContainsKey(nameWithoutDigits)) {
reservedVariableNames.Add(nameWithoutDigits, number - 1);
@ -156,7 +155,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -156,7 +155,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return loopCounters;
}
string GenerateNameForVariable(ILVariable variable, ILInstruction methodBody)
string GenerateNameForVariable(ILVariable variable)
{
string proposedName = null;
if (variable.Type.IsKnownType(KnownTypeCode.Int32)) {
@ -192,8 +191,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -192,8 +191,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
// remove any numbers from the proposed name
int number;
proposedName = SplitName(proposedName, out number);
proposedName = SplitName(proposedName, out int number);
if (!reservedVariableNames.ContainsKey(proposedName)) {
reservedVariableNames.Add(proposedName, 0);
@ -291,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -291,7 +289,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return name;
}
void AddExistingName(string name)
static void AddExistingName(Dictionary<string, int> reservedVariableNames, string name)
{
if (string.IsNullOrEmpty(name))
return;
@ -336,5 +334,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -336,5 +334,34 @@ namespace ICSharpCode.Decompiler.IL.Transforms
else
return char.ToLower(name[0]) + name.Substring(1);
}
internal static string GenerateVariableName(ILFunction currentFunction, ILInstruction valueContext, string fallback, ILVariable existingVariable = null)
{
if (currentFunction == null)
throw new ArgumentNullException(nameof(currentFunction));
var reservedVariableNames = new Dictionary<string, int>();
foreach (var v in currentFunction.Descendants.OfType<ILFunction>().SelectMany(m => m.Variables)) {
if (v != existingVariable)
AddExistingName(reservedVariableNames, v.Name);
}
var proposedName = GetNameFromInstruction(valueContext);
if (string.IsNullOrEmpty(proposedName)) {
proposedName = fallback;
}
// remove any numbers from the proposed name
proposedName = SplitName(proposedName, out int number);
if (!reservedVariableNames.ContainsKey(proposedName)) {
reservedVariableNames.Add(proposedName, 0);
}
int count = ++reservedVariableNames[proposedName];
if (count > 1) {
return proposedName + count.ToString();
} else {
return proposedName;
}
}
}
}

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

@ -306,7 +306,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -306,7 +306,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction loadInst;
return FindLoadInNext(expr, v, expressionBeingMoved, out loadInst) == true;
}
/// <summary>
/// Gets whether arg can be un-inlined out of stmt.
/// </summary>
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
{
Debug.Assert(arg.IsDescendantOf(stmt));
for (ILInstruction inst = arg; inst != stmt; inst = inst.Parent) {
if (!inst.SlotInfo.CanInlineInto)
return false;
// Check whether re-ordering with predecessors is valid:
int childIndex = inst.ChildIndex;
for (int i = 0; i < childIndex; ++i) {
ILInstruction predecessor = inst.Parent.Children[i];
if (!SemanticHelper.MayReorder(arg, predecessor))
return false;
}
}
return true;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>

Loading…
Cancel
Save