Browse Source

TransformForeach: handle optional return statement after loop.

pull/870/merge
Siegfried Pammer 8 years ago
parent
commit
dfeb39f6ee
  1. 44
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  2. 16
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

44
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -271,14 +271,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -271,14 +271,14 @@ namespace ICSharpCode.Decompiler.CSharp
};
}
ForeachStatement TransformToForeach(UsingInstruction inst, out TranslatedExpression resource)
Statement TransformToForeach(UsingInstruction inst, out TranslatedExpression resource)
{
resource = exprBuilder.Translate(inst.ResourceExpression);
var m = getEnumeratorPattern.Match(resource.Expression);
if (!(inst.Body is BlockContainer container) || !m.Success)
return null;
var enumeratorVar = m.Get<IdentifierExpression>("enumerator").Single().GetILVariable();
var loop = DetectedLoop.DetectLoop(container);
var loop = DetectedLoop.DetectLoop(UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop));
if (loop.Kind != LoopKind.While || !(loop.Body is Block body))
return null;
var condition = exprBuilder.TranslateCondition(loop.Conditions.Single());
@ -319,15 +319,43 @@ namespace ICSharpCode.Decompiler.CSharp @@ -319,15 +319,43 @@ namespace ICSharpCode.Decompiler.CSharp
itemVariable.Kind = VariableKind.ForeachLocal;
itemVariable.Name = AssignVariableNames.GenerateVariableName(currentFunction, collectionExpr.Annotation<ILInstruction>(), "item", itemVariable);
}
var whileLoop = (WhileStatement)ConvertAsBlock(inst.Body).Single();
var whileLoop = (WhileStatement)ConvertAsBlock(inst.Body).First();
BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach();
foreachBody.Statements.First().Detach();
return new ForeachStatement {
var foreachStmt = new ForeachStatement {
VariableType = settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(itemVariable.Type),
VariableName = itemVariable.Name,
InExpression = collectionExpr.Detach(),
EmbeddedStatement = foreachBody
};
if (optionalReturnAfterLoop != null) {
return new BlockStatement {
Statements = {
foreachStmt,
optionalReturnAfterLoop.AcceptVisitor(this)
}
};
}
return foreachStmt;
}
BlockContainer UnwrapNestedContainerIfPossible(BlockContainer container, out Leave optionalReturnInst)
{
optionalReturnInst = null;
if (container.Blocks.Count != 1)
return container;
var nestedBlock = container.Blocks[0];
if (nestedBlock.Instructions.Count != 2 ||
!(nestedBlock.Instructions[0] is BlockContainer nestedContainer) ||
!(nestedBlock.Instructions[1] is Leave leave))
return container;
if (leave.MatchLeave(container))
return nestedContainer;
if (leave.IsLeavingFunction) {
optionalReturnInst = leave;
return nestedContainer;
}
return container;
}
bool BodyHasSingleGetCurrent(Block body, ILVariable enumerator, ILInstruction moveNextUsage, out CallInstruction singleGetter, out bool needsUninlining, out ILVariable existingVariable)
@ -504,7 +532,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -504,7 +532,13 @@ namespace ICSharpCode.Decompiler.CSharp
// skip the final 'leave' instruction and just fall out of the BlockStatement
continue;
}
blockStatement.Add(Convert(inst));
var stmt = Convert(inst);
if (stmt is BlockStatement b) {
foreach (var nested in b.Statements)
blockStatement.Add(nested.Detach());
} else {
blockStatement.Add(stmt);
}
}
if (block.FinalInstruction.OpCode != OpCode.Nop) {
blockStatement.Add(Convert(block.FinalInstruction));

16
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -92,24 +92,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -92,24 +92,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
context.Step("UsingTransform", body);
block.Instructions.RemoveAt(i);
block.Instructions[i - 1] = new UsingInstruction(resourceExpression, UnwrapNestedContainerIfPossible(body.TryBlock));
block.Instructions[i - 1] = new UsingInstruction(resourceExpression, body.TryBlock);
return true;
}
ILInstruction UnwrapNestedContainerIfPossible(ILInstruction tryBlock)
{
if (!(tryBlock is BlockContainer container))
return tryBlock;
if (container.Blocks.Count != 1)
return tryBlock;
var nestedBlock = container.Blocks[0];
if (nestedBlock.Instructions.Count != 2 ||
!(nestedBlock.Instructions[0] is BlockContainer nestedContainer) ||
!nestedBlock.Instructions[1].MatchLeave(container))
return tryBlock;
return nestedContainer;
}
bool MatchDisposeBlock(BlockContainer container, ILVariable objVar, bool usingNull)
{
var entryPoint = container.EntryPoint;

Loading…
Cancel
Save