Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
0c65f668d5
  1. 98
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 73
      ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs
  3. 91
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  4. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 422
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  6. 71
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  7. 3
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  8. 100
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

98
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -18,7 +18,7 @@ namespace Decompiler @@ -18,7 +18,7 @@ namespace Decompiler
MethodDefinition methodDef;
TypeSystem typeSystem;
DecompilerContext context;
HashSet<ILVariable> definedLocalVars = new HashSet<ILVariable>();
HashSet<ILVariable> localVariablesToDefine = new HashSet<ILVariable>(); // local variables that are missing a definition
public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context)
{
@ -65,6 +65,10 @@ namespace Decompiler @@ -65,6 +65,10 @@ namespace Decompiler
context.CancellationToken.ThrowIfCancellationRequested();
Ast.BlockStatement astBlock = TransformBlock(ilMethod);
CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments
foreach (ILVariable v in localVariablesToDefine) {
DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name);
}
return astBlock;
}
@ -72,6 +76,8 @@ namespace Decompiler @@ -72,6 +76,8 @@ namespace Decompiler
{
Ast.BlockStatement astBlock = new BlockStatement();
if (block != null) {
if (block.EntryGoto != null)
astBlock.AddStatement((Statement)TransformExpression(block.EntryGoto));
foreach(ILNode node in block.Body) {
astBlock.AddStatements(TransformNode(node));
}
@ -100,24 +106,6 @@ namespace Decompiler @@ -100,24 +106,6 @@ namespace Decompiler
yield return new Ast.ForStatement {
EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock)
};
/*
} else if (node is Branch) {
yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label };
Ast.BlockStatement trueBlock = new Ast.BlockStatement();
trueBlock.AddStatement(new Ast.GotoStatement(((Branch)node).TrueSuccessor.Label));
Ast.BlockStatement falseBlock = new Ast.BlockStatement();
falseBlock.AddStatement(new Ast.GotoStatement(((Branch)node).FalseSuccessor.Label));
Ast.IfElseStatement ifElseStmt = new Ast.IfElseStatement {
Condition = MakeBranchCondition((Branch)node),
TrueStatement = trueBlock,
FalseStatement = falseBlock
};
yield return ifElseStmt;
*/
} else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node;
if (conditionalNode.FalseBlock.Body.Any()) {
@ -144,6 +132,8 @@ namespace Decompiler @@ -144,6 +132,8 @@ namespace Decompiler
switchStmt.SwitchSections.Add(section);
}
yield return switchStmt;
if (ilSwitch.DefaultGoto != null)
yield return (Statement)TransformExpression(ilSwitch.DefaultGoto);
} else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
var tryCatchStmt = new Ast.TryCatchStatement();
@ -186,6 +176,23 @@ namespace Decompiler @@ -186,6 +176,23 @@ namespace Decompiler
Ast.Expression MakeBranchCondition(ILExpression expr)
{
switch(expr.Code) {
case ILCode.LogicNot:
return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(expr.Arguments[0]));
case ILCode.LogicAnd:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(expr.Arguments[1])
);
case ILCode.LogicOr:
return new Ast.BinaryOperatorExpression(
MakeBranchCondition(expr.Arguments[0]),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(expr.Arguments[1])
);
}
List<Ast.Expression> args = TransformExpressionArguments(expr);
Ast.Expression arg1 = args.Count >= 1 ? args[0] : null;
Ast.Expression arg2 = args.Count >= 2 ? args[1] : null;
@ -225,43 +232,9 @@ namespace Decompiler @@ -225,43 +232,9 @@ namespace Decompiler
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
case Code.Bne_Un:
return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2);
default: throw new Exception("Bad opcode");
}
/*
} else if (branch is ShortCircuitBranch) {
ShortCircuitBranch scBranch = (ShortCircuitBranch)branch;
switch(scBranch.Operator) {
case ShortCircuitOperator.LeftAndRight:
return new BinaryOperatorExpression(
MakeBranchCondition(scBranch.Left),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.LeftOrRight:
return new BinaryOperatorExpression(
MakeBranchCondition(scBranch.Left),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.NotLeftAndRight:
return new BinaryOperatorExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)),
BinaryOperatorType.ConditionalAnd,
MakeBranchCondition(scBranch.Right)
);
case ShortCircuitOperator.NotLeftOrRight:
return new BinaryOperatorExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(scBranch.Left)),
BinaryOperatorType.ConditionalOr,
MakeBranchCondition(scBranch.Right)
);
default:
throw new Exception("Bad operator");
}
} else {
throw new Exception("Bad type");
default:
throw new Exception("Bad opcode");
}
*/
}
AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args)
@ -607,19 +580,14 @@ namespace Decompiler @@ -607,19 +580,14 @@ namespace Decompiler
}
case Code.Rethrow: return new Ast.ThrowStatement();
case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) };
case Code.Starg:
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1);
case Code.Starg:
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand), arg1);
case Code.Stloc: {
ILVariable locVar = (ILVariable)operand;
if (!definedLocalVars.Contains(locVar)) {
definedLocalVars.Add(locVar);
return new Ast.VariableDeclarationStatement(
locVar.Type != null ? AstBuilder.ConvertType(locVar.Type) : new Ast.PrimitiveType("var"),
locVar.Name,
arg1);
} else {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
if (!localVariablesToDefine.Contains(locVar)) {
localVariablesToDefine.Add(locVar);
}
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name).WithAnnotation(locVar), arg1);
}
case Code.Stobj: return InlineAssembly(byteCode, args);
case Code.Switch: return InlineAssembly(byteCode, args);

73
ICSharpCode.Decompiler/Ast/DeclareVariableInSmallestScope.cs

@ -0,0 +1,73 @@ @@ -0,0 +1,73 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
namespace Decompiler
{
/// <summary>
/// Helper class for declaring variables.
/// </summary>
public static class DeclareVariableInSmallestScope
{
static readonly ExpressionStatement assignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("ident", new IdentifierExpression()).ToExpression(),
new AnyNode("init").ToExpression()
));
/// <summary>
/// Declares a variable in the smallest required scope.
/// </summary>
/// <param name="node">The root of the subtree being searched for the best insertion position</param>
/// <param name="type">The type of the new variable</param>
/// <param name="name">The name of the new variable</param>
/// <param name="allowPassIntoLoops">Whether the variable is allowed to be placed inside a loop</param>
public static void DeclareVariable(AstNode node, AstType type, string name, bool allowPassIntoLoops = true)
{
AstNode pos = FindInsertPos(node, name, allowPassIntoLoops);
if (pos != null) {
Match m = assignmentPattern.Match(pos);
if (m != null && m.Get<IdentifierExpression>("ident").Single().Identifier == name) {
pos.ReplaceWith(new VariableDeclarationStatement(type, name, m.Get<Expression>("init").Single().Detach()));
} else {
pos.Parent.InsertChildBefore(pos, new VariableDeclarationStatement(type, name), BlockStatement.StatementRole);
}
}
}
static AstNode FindInsertPos(AstNode node, string name, bool allowPassIntoLoops)
{
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == name && ident.TypeArguments.Count == 0)
return node;
AstNode pos = null;
AstNode withinPos = null;
while (node != null) {
AstNode withinCurrent = FindInsertPos(node.FirstChild, name, allowPassIntoLoops);
if (withinCurrent != null) {
if (pos == null) {
pos = node;
withinPos = withinCurrent;
} else {
return pos;
}
}
node = node.NextSibling;
}
if (withinPos != null && withinPos.Role == BlockStatement.StatementRole && (allowPassIntoLoops || !IsLoop(pos)))
return withinPos;
else
return pos;
}
static bool IsLoop(AstNode node)
{
return node is ForStatement || node is ForeachStatement || node is DoWhileStatement || node is WhileStatement;
}
}
}

91
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -7,6 +7,7 @@ using System.Linq; @@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.PatternMatching;
using Mono.Cecil;
namespace Decompiler.Transforms
@ -174,63 +175,75 @@ namespace Decompiler.Transforms @@ -174,63 +175,75 @@ namespace Decompiler.Transforms
}
if (!ok)
continue;
Dictionary<string, Expression> dict = new Dictionary<string, Expression>();
Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>();
// Delete the variable declaration statement:
AstNode cur;
AstNode next = stmt.NextSibling;
AstNode cur = stmt.NextSibling;
stmt.Remove();
for (cur = next; cur != null; cur = next) {
next = cur.NextSibling;
// Delete any following statements as long as they assign simple variables to the display class:
// Test for the pattern:
// "variableName.MemberName = right;"
ExpressionStatement es = cur as ExpressionStatement;
if (es == null)
break;
AssignmentExpression ae = es.Expression as AssignmentExpression;
if (ae == null || ae.Operator != AssignmentOperatorType.Assign)
break;
MemberReferenceExpression left = ae.Left as MemberReferenceExpression;
if (left == null || !IsParameter(ae.Right))
break;
if (!(left.Target is IdentifierExpression) || (left.Target as IdentifierExpression).Identifier != variable.Name)
break;
dict[left.MemberName] = ae.Right;
es.Remove();
if (blockStatement.Parent.NodeType == NodeType.Member || blockStatement.Parent is Accessor) {
// Delete any following statements as long as they assign parameters to the display class
// Do parameter handling only for closures created in the top scope (direct child of method/accessor)
List<ParameterReference> parameterOccurrances = blockStatement.Descendants.OfType<IdentifierExpression>()
.Select(n => n.Annotation<ParameterReference>()).Where(p => p != null).ToList();
AstNode next;
for (; cur != null; cur = next) {
next = cur.NextSibling;
// Test for the pattern:
// "variableName.MemberName = right;"
ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }).ToExpression(),
new AnyNode("right").ToExpression()
)
);
Match m = closureFieldAssignmentPattern.Match(cur);
if (m != null) {
AstNode right = m.Get("right").Single();
bool isParameter = false;
if (right is ThisReferenceExpression) {
isParameter = true;
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrance except for 'right'
ParameterReference param = right.Annotation<ParameterReference>();
isParameter = parameterOccurrances.Count(c => c == param) == 1;
}
if (isParameter) {
dict[m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>()] = right;
cur.Remove();
} else {
break;
}
} else {
break;
}
}
}
// Now create variables for all fields of the display class (except for those that we already handled)
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>();
foreach (FieldDefinition field in type.Fields) {
if (dict.ContainsKey(field.Name))
if (dict.ContainsKey(field))
continue;
VariableDeclarationStatement newVarDecl = new VariableDeclarationStatement();
newVarDecl.Type = AstBuilder.ConvertType(field.FieldType, field);
newVarDecl.Variables.Add(new VariableInitializer(field.Name));
blockStatement.InsertChildBefore(cur, newVarDecl, BlockStatement.StatementRole);
dict[field.Name] = new IdentifierExpression(field.Name);
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name));
dict[field] = new IdentifierExpression(field.Name);
}
// Now figure out where the closure was accessed and use the simpler replacement expression there:
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
if (identExpr.Identifier == variable.Name) {
MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
Expression replacement;
if (dict.TryGetValue(mre.MemberName, out replacement)) {
AstNode replacement;
if (dict.TryGetValue(mre.Annotation<FieldReference>(), out replacement)) {
mre.ReplaceWith(replacement.Clone());
}
}
}
// Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
foreach (var tuple in variablesToDeclare) {
DeclareVariableInSmallestScope.DeclareVariable(blockStatement, tuple.Item1, tuple.Item2, allowPassIntoLoops: false);
}
}
return null;
}
bool IsParameter(Expression expr)
{
if (expr is ThisReferenceExpression)
return true;
IdentifierExpression ident = expr as IdentifierExpression;
return ident != null && ident.Annotation<ParameterReference>() != null;
}
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -52,6 +52,7 @@ @@ -52,6 +52,7 @@
<Compile Include="Ast\AstBuilder.cs" />
<Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\DeclareVariableInSmallestScope.cs" />
<Compile Include="Ast\CSharpCodeMapping.cs" />
<Compile Include="Ast\DecompilerContext.cs" />
<Compile Include="Ast\NameVariables.cs" />

422
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -22,48 +22,47 @@ namespace Decompiler.ControlFlow @@ -22,48 +22,47 @@ namespace Decompiler.ControlFlow
public class ILAstOptimizer
{
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
Dictionary<ILLabel, int> labelRefCount;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
SplitToMovableBlocks(block);
SplitToBasicBlocks(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) {
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, block.EntryPoint);
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, true);
}
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) {
UpdateLabelRefCounts(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, block.EntryPoint);
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
// OrderNodes(method);
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenNestedMovableBlocks(method);
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return;
SimpleGotoRemoval(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return;
RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(context, method);
}
class ILMoveableBlock: ILBlock
{
public int OriginalOrder;
}
int nextBlockIndex = 0;
/// <summary>
@ -71,26 +70,29 @@ namespace Decompiler.ControlFlow @@ -71,26 +70,29 @@ namespace Decompiler.ControlFlow
/// The method adds necessary branches to make control flow between blocks
/// explicit and thus order independent.
/// </summary>
void SplitToMovableBlocks(ILBlock block)
void SplitToBasicBlocks(ILBlock block)
{
// Remve no-ops
// TODO: Assign the no-op range to someting
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList();
List<ILNode> moveableBlocks = new List<ILNode>();
List<ILNode> basicBlocks = new List<ILNode>();
ILMoveableBlock moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
moveableBlocks.Add(moveableBlock);
block.EntryPoint = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder };
moveableBlock.Body.Add(block.EntryPoint);
ILBasicBlock basicBlock = new ILBasicBlock() {
EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) }
};
basicBlocks.Add(basicBlock);
block.EntryGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
if (block.Body.Count > 0) {
moveableBlock.Body.Add(block.Body[0]);
basicBlock.Body.Add(block.Body[0]);
for (int i = 1; i < block.Body.Count; i++) {
ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i];
bool added = false;
// Insert split
if ((currNode is ILLabel && !(lastNode is ILLabel)) ||
lastNode is ILTryCatchBlock ||
@ -98,24 +100,30 @@ namespace Decompiler.ControlFlow @@ -98,24 +100,30 @@ namespace Decompiler.ControlFlow
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() ||
(currNode is ILExpression) && ((ILExpression)currNode).IsBranch())
{
ILBlock lastBlock = moveableBlock;
moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) };
moveableBlocks.Add(moveableBlock);
ILBasicBlock lastBlock = basicBlock;
basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
if (currNode is ILLabel) {
// Reuse the first label
basicBlock.EntryLabel = (ILLabel)currNode;
added = true;
} else {
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) };
}
// Explicit branch from one block to other
// (unless the last expression was unconditional branch)
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) {
ILLabel blockLabel = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder };
lastBlock.Body.Add(new ILExpression(ILCode.Br, blockLabel));
moveableBlock.Body.Add(blockLabel);
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
}
}
moveableBlock.Body.Add(currNode);
if (!added)
basicBlock.Body.Add(currNode);
}
}
block.Body = moveableBlocks;
block.Body = basicBlocks;
return;
}
@ -175,7 +183,7 @@ namespace Decompiler.ControlFlow @@ -175,7 +183,7 @@ namespace Decompiler.ControlFlow
return new ControlFlowGraph(cfNodes.ToArray());
}
List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, bool excludeEntryPoint)
List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
{
List<ILNode> result = new List<ILNode>();
@ -184,18 +192,22 @@ namespace Decompiler.ControlFlow @@ -184,18 +192,22 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue();
if (nodes.Contains(node)
if (scope.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& (node != entryPoint || !excludeEntryPoint))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(nodes, loopContents, node, node);
FindLoopContents(scope, loopContents, node, node);
// Move the content into loop block
nodes.ExceptWith(loopContents);
scope.ExceptWith(loopContents);
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) };
((ILBlock)node.UserData).Body.Insert(0, entryLabel);
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } });
((ILBasicBlock)node.UserData).Body.Insert(0, entryLabel);
result.Add(new ILLoop() {
ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) {
EntryGoto = new ILExpression(ILCode.Br, entryLabel)
}
});
}
// Using the dominator tree should ensure we find the the widest loop first
@ -205,23 +217,34 @@ namespace Decompiler.ControlFlow @@ -205,23 +217,34 @@ namespace Decompiler.ControlFlow
}
// Add whatever is left
foreach(var node in nodes) {
foreach(var node in scope) {
result.Add((ILNode)node.UserData);
}
return result;
}
static void FindLoopContents(HashSet<ControlFlowNode> nodes, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode)
static void FindLoopContents(HashSet<ControlFlowNode> scope, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode addNode)
{
if (nodes.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) {
if (scope.Contains(addNode) && loopHead.Dominates(addNode) && loopContents.Add(addNode)) {
foreach (var edge in addNode.Incoming) {
FindLoopContents(nodes, loopContents, loopHead, edge.Source);
FindLoopContents(scope, loopContents, loopHead, edge.Source);
}
}
}
List<ILNode> FindConditions(HashSet<ControlFlowNode> nodes, ControlFlowNode entryNode)
void UpdateLabelRefCounts(ILBlock method)
{
labelRefCount = new Dictionary<ILLabel, int>();
foreach(ILLabel label in method.GetSelfAndChildrenRecursive<ILLabel>()) {
labelRefCount[label] = 0;
}
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) {
labelRefCount[target]++;
}
}
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode)
{
List<ILNode> result = new List<ILNode>();
@ -236,41 +259,32 @@ namespace Decompiler.ControlFlow @@ -236,41 +259,32 @@ namespace Decompiler.ControlFlow
agenda.Remove(node);
// Find a block that represents a simple condition
if (nodes.Contains(node)) {
if (scope.Contains(node)) {
ILMoveableBlock block = node.UserData as ILMoveableBlock;
ILBasicBlock block = node.UserData as ILBasicBlock;
if (block != null && block.Body.Count == 3) {
if (block != null && block.Body.Count == 1) {
ILLabel label = block.Body[0] as ILLabel;
ILExpression condBranch = block.Body[1] as ILExpression;
ILExpression statBranch = block.Body[2] as ILExpression;
ILExpression condBranch = block.Body[0] as ILExpression;
// Switch
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch };
if (condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0) {
// Replace the two branches with a conditional structure - this preserves the node label
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(ilSwitch);
ILSwitch ilSwitch = new ILSwitch() {
Condition = condBranch,
DefaultGoto = block.FallthoughGoto
};
ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue((ILLabel)block.FallthoughGoto.Operand, out fallTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier);
if (fallTarget != null)
frontiers.UnionWith(fallTarget.DominanceFrontier);
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
}
@ -279,10 +293,12 @@ namespace Decompiler.ControlFlow @@ -279,10 +293,12 @@ namespace Decompiler.ControlFlow
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel };
ILBlock caseBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, condLabel)
};
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
}
ilSwitch.CaseBlocks.Add(caseBlock);
@ -291,61 +307,61 @@ namespace Decompiler.ControlFlow @@ -291,61 +307,61 @@ namespace Decompiler.ControlFlow
// The labels will not be used - kill them
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node);
result.Add(new ILBasicBlock() {
EntryLabel = block.EntryLabel, // Keep the entry label
Body = { ilSwitch }
});
scope.Remove(node);
}
// Two-way branch
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget);
ILCondition ilCond;
HashSet<ControlFlowNode> matchedNodes;
ILLabel condEntryLabel;
if (TryMatchCondition(scope, new ControlFlowNode[] {}, node, out ilCond, out matchedNodes, out condEntryLabel)) {
ILCondition condition = new ILCondition() {
Condition = condBranch,
TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand },
FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand }
};
// The branch labels will not be used - kill them
foreach(ILExpression expr in ilCond.Condition.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.GetBranchTargets().Any()) {
expr.Operand = null;
}
}
// Replace the two branches with a conditional structure - this preserves the node label
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(condition);
ControlFlowNode trueTarget = null;
labelToCfNode.TryGetValue((ILLabel)ilCond.TrueBlock.EntryGoto.Operand, out trueTarget);
ControlFlowNode falseTarget = null;
labelToCfNode.TryGetValue((ILLabel)ilCond.FalseBlock.EntryGoto.Operand, out falseTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
if (trueTarget != null)
frontiers.UnionWith(trueTarget.DominanceFrontier);
if (falseTarget != null)
frontiers.UnionWith(falseTarget.DominanceFrontier);
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget));
if (trueTarget != null && !frontiers.Contains(trueTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);
scope.ExceptWith(content);
ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget));
}
if (statTarget != null && !frontiers.Contains(statTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget);
nodes.ExceptWith(content);
condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget));
if (falseTarget != null && !frontiers.Contains(falseTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget);
scope.ExceptWith(content);
ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget));
}
// The label will not be used - kill it
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node);
result.Add(new ILBasicBlock() {
EntryLabel = condEntryLabel, // Keep the entry label
Body = { ilCond }
});
scope.ExceptWith(matchedNodes);
}
}
// Add the node now so that we have good ordering
if (nodes.Contains(node)) {
if (scope.Contains(node)) {
result.Add((ILNode)node.UserData);
nodes.Remove(node);
scope.Remove(node);
}
}
@ -356,14 +372,115 @@ namespace Decompiler.ControlFlow @@ -356,14 +372,115 @@ namespace Decompiler.ControlFlow
}
// Add whatever is left
foreach(var node in nodes) {
foreach(var node in scope) {
result.Add((ILNode)node.UserData);
}
return result;
}
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> nodes, ControlFlowNode head)
bool TryMatchCondition(HashSet<ControlFlowNode> scope, IEnumerable<ControlFlowNode> scopeExcept, ControlFlowNode head, out ILCondition condition, out HashSet<ControlFlowNode> matchedNodes, out ILLabel entryLabel)
{
condition = null;
matchedNodes = null;
entryLabel = null;
if (!scope.Contains(head) || scopeExcept.Contains(head))
return false;
ILBasicBlock basicBlock = head.UserData as ILBasicBlock;
if (basicBlock == null || basicBlock.Body.Count != 1)
return false;
ILExpression condBranch = basicBlock.Body[0] as ILExpression;
if (condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0) {
// We have found a two-way condition
condition = new ILCondition() {
Condition = condBranch,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, condBranch.Operand) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, basicBlock.FallthoughGoto.Operand) }
};
// We are done with the node so "remove" it from scope
scopeExcept = scopeExcept.Union(new[] {head});
matchedNodes = new HashSet<ControlFlowNode>() { head };
entryLabel = basicBlock.EntryLabel;
// Optimize short-circut expressions
while(true) {
// Consider condition.TrueBlock
{
ILLabel nextLabel = (ILLabel)condition.TrueBlock.EntryGoto.Operand;
ControlFlowNode nextTarget;
labelToCfNode.TryGetValue(nextLabel, out nextTarget);
ILCondition nextCond;
HashSet<ControlFlowNode> nextMatchedNodes;
ILLabel nextEnteryLabel;
if (nextTarget != null &&
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) &&
labelRefCount[nextEnteryLabel] == 1)
{
if (condition.FalseBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicAnd, null, condition.Condition, nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
if (condition.FalseBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicOr, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
}
}
// Consider condition.FalseBlock
{
ILLabel nextLabel = (ILLabel)condition.FalseBlock.EntryGoto.Operand;
ControlFlowNode nextTarget;
labelToCfNode.TryGetValue(nextLabel, out nextTarget);
ILCondition nextCond;
HashSet<ControlFlowNode> nextMatchedNodes;
ILLabel nextEnteryLabel;
if (nextTarget != null &&
TryMatchCondition(scope, scopeExcept, nextTarget, out nextCond, out nextMatchedNodes, out nextEnteryLabel) &&
labelRefCount[nextEnteryLabel] == 1)
{
if (condition.TrueBlock.EntryGoto.Operand == nextCond.FalseBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicAnd, null, new ILExpression(ILCode.LogicNot, null, condition.Condition), nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
if (condition.TrueBlock.EntryGoto.Operand == nextCond.TrueBlock.EntryGoto.Operand) {
condition.Condition = new ILExpression(ILCode.LogicOr, null, condition.Condition, nextCond.Condition);
condition.TrueBlock = nextCond.TrueBlock;
condition.FalseBlock = nextCond.FalseBlock;
scopeExcept = scopeExcept.Union(nextMatchedNodes);
matchedNodes.UnionWith(nextMatchedNodes);
continue;
}
}
}
break;
}
return true;
}
return false;
}
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head)
{
var exitNodes = head.DominanceFrontier.SelectMany(n => n.Predecessors);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(exitNodes);
@ -373,121 +490,42 @@ namespace Decompiler.ControlFlow @@ -373,121 +490,42 @@ namespace Decompiler.ControlFlow
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (nodes.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);
}
}
}
if (nodes.Contains(head))
if (scope.Contains(head))
result.Add(head);
return result;
}
/*
public enum ShortCircuitOperator
{
LeftAndRight,
LeftOrRight,
NotLeftAndRight,
NotLeftOrRight,
}
static bool TryOptimizeShortCircuit(Node head)
{
if ((head is BasicBlock) &&
(head as BasicBlock).BranchBasicBlock != null &&
(head as BasicBlock).FallThroughBasicBlock != null) {
head.Parent.MergeChilds<SimpleBranch>(head);
return true;
}
Branch top = head as Branch;
if (top == null) return false;
Branch left = head.FloatUpToNeighbours(top.TrueSuccessor) as Branch;
Branch right = head.FloatUpToNeighbours(top.FalseSuccessor) as Branch;
// A & B
if (left != null &&
left.Predecessors.Count == 1 &&
left.FalseSuccessor == top.FalseSuccessor) {
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left);
scBranch.Operator = ShortCircuitOperator.LeftAndRight;
return true;
}
// ~A | B
if (left != null &&
left.Predecessors.Count == 1 &&
left.TrueSuccessor == top.FalseSuccessor) {
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left);
scBranch.Operator = ShortCircuitOperator.NotLeftOrRight;
return true;
}
// A | B
if (right != null &&
right.Predecessors.Count == 1 &&
right.TrueSuccessor == top.TrueSuccessor) {
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right);
scBranch.Operator = ShortCircuitOperator.LeftOrRight;
return true;
}
// ~A & B
if (right != null &&
right.Predecessors.Count == 1 &&
right.FalseSuccessor == top.TrueSuccessor) {
ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right);
scBranch.Operator = ShortCircuitOperator.NotLeftAndRight;
return true;
}
return false;
}
*/
void OrderNodes(ILBlock ast)
{
// Order movable nodes
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList();
ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 };
foreach(ILBlock block in blocks) {
block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList();
}
}
/// <summary>
/// Flattens all nested movable blocks, except the the top level 'node' argument
/// Flattens all nested basic blocks, except the the top level 'node' argument
/// </summary>
void FlattenNestedMovableBlocks(ILNode node)
void FlattenBasicBlocks(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
List<ILNode> flatBody = new List<ILNode>();
if (block.EntryPoint != null) {
flatBody.Add(new ILExpression(ILCode.Br, block.EntryPoint));
block.EntryPoint = null;
}
foreach (ILNode child in block.Body) {
FlattenNestedMovableBlocks(child);
if (child is ILMoveableBlock) {
flatBody.AddRange(((ILMoveableBlock)child).Body);
foreach (ILNode child in block.GetChildren()) {
FlattenBasicBlocks(child);
if (child is ILBasicBlock) {
flatBody.AddRange(child.GetChildren());
} else {
flatBody.Add(child);
}
}
block.EntryGoto = null;
block.Body = flatBody;
} else if (node is ILExpression) {
// Optimization - no need to check expressions
} else if (node != null) {
// Recursively find all ILBlocks
foreach(ILNode child in node.GetChildren()) {
FlattenNestedMovableBlocks(child);
FlattenBasicBlocks(child);
}
}
}

71
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -3,13 +3,13 @@ using System.Collections.Generic; @@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Decompiler.ControlFlow;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.CSharp;
using Cecil = Mono.Cecil;
namespace Decompiler
@ -38,8 +38,7 @@ namespace Decompiler @@ -38,8 +38,7 @@ namespace Decompiler
public class ILBlock: ILNode
{
// TODO: This should really be a goto, not a label
public ILLabel EntryPoint;
public ILExpression EntryGoto;
public List<ILNode> Body;
@ -55,8 +54,8 @@ namespace Decompiler @@ -55,8 +54,8 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren()
{
if (EntryPoint != null)
yield return EntryPoint;
if (this.EntryGoto != null)
yield return this.EntryGoto;
foreach(ILNode child in this.Body) {
yield return child;
}
@ -64,9 +63,33 @@ namespace Decompiler @@ -64,9 +63,33 @@ namespace Decompiler
public override void WriteTo(ITextOutput output)
{
if (EntryPoint != null)
EntryPoint.WriteTo(output);
foreach(ILNode child in this.Body) {
foreach(ILNode child in this.GetChildren()) {
child.WriteTo(output);
output.WriteLine();
}
}
}
public class ILBasicBlock: ILNode
{
public ILLabel EntryLabel;
public List<ILNode> Body = new List<ILNode>();
public ILExpression FallthoughGoto;
public override IEnumerable<ILNode> GetChildren()
{
if (this.EntryLabel != null)
yield return this.EntryLabel;
foreach (ILNode child in this.Body) {
yield return child;
}
if (this.FallthoughGoto != null)
yield return this.FallthoughGoto;
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in this.GetChildren()) {
child.WriteTo(output);
output.WriteLine();
}
@ -119,7 +142,8 @@ namespace Decompiler @@ -119,7 +142,8 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren()
{
yield return this.TryBlock;
if (this.TryBlock != null)
yield return this.TryBlock;
foreach (var catchBlock in this.CatchBlocks) {
yield return catchBlock;
}
@ -188,6 +212,11 @@ namespace Decompiler @@ -188,6 +212,11 @@ namespace Decompiler
this.ILRanges = new List<ILRange>(1);
}
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
}
public bool IsBranch()
{
return this.Operand is ILLabel || this.Operand is ILLabel[];
@ -225,11 +254,6 @@ namespace Decompiler @@ -225,11 +254,6 @@ namespace Decompiler
return ranges;
}
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
}
public override void WriteTo(ITextOutput output)
{
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) {
@ -283,7 +307,8 @@ namespace Decompiler @@ -283,7 +307,8 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren()
{
yield return ContentBlock;
if (this.ContentBlock != null)
yield return ContentBlock;
}
public override void WriteTo(ITextOutput output)
@ -304,10 +329,12 @@ namespace Decompiler @@ -304,10 +329,12 @@ namespace Decompiler
public override IEnumerable<ILNode> GetChildren()
{
yield return Condition;
yield return TrueBlock;
if (FalseBlock != null)
yield return FalseBlock;
if (this.Condition != null)
yield return this.Condition;
if (this.TrueBlock != null)
yield return this.TrueBlock;
if (this.FalseBlock != null)
yield return this.FalseBlock;
}
public override void WriteTo(ITextOutput output)
@ -333,13 +360,17 @@ namespace Decompiler @@ -333,13 +360,17 @@ namespace Decompiler
{
public ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>();
public ILExpression DefaultGoto;
public override IEnumerable<ILNode> GetChildren()
{
yield return Condition;
if (this.Condition != null)
yield return this.Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
if (this.DefaultGoto != null)
yield return this.DefaultGoto;
}
public override void WriteTo(ITextOutput output)

3
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -255,6 +255,9 @@ namespace Decompiler @@ -255,6 +255,9 @@ namespace Decompiler
// Virtual codes - defined for convenience
Ldexception, // Operand holds the CatchType for catch handler, null for filter
LogicNot,
LogicAnd,
LogicOr
}
public static class ILCodeUtil

100
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -20,40 +20,74 @@ namespace Decompiler @@ -20,40 +20,74 @@ namespace Decompiler
/// </remarks>
public class TypeAnalysis
{
public static void Run(DecompilerContext context, ILNode node)
public static void Run(DecompilerContext context, ILBlock method)
{
TypeAnalysis ta = new TypeAnalysis();
ta.context = context;
ta.module = context.CurrentMethod.Module;
ta.typeSystem = ta.module.TypeSystem;
ta.InferTypes(node);
ta.method = method;
ta.InferTypes(method);
ta.InferRemainingStores();
}
DecompilerContext context;
TypeSystem typeSystem;
ILBlock method;
ModuleDefinition module;
List<ILExpression> storedToGeneratedVariables = new List<ILExpression>();
HashSet<ILVariable> inferredVariables = new HashSet<ILVariable>();
void InferTypes(ILNode node)
{
foreach (ILNode child in node.GetChildren()) {
ILExpression expr = child as ILExpression;
if (expr != null) {
ILVariable v = expr.Operand as ILVariable;
if (v != null && v.IsGenerated && v.Type == null && expr.Code == ILCode.Stloc) {
// don't deal with this node or its children yet,
// wait for the expected type to be inferred first
storedToGeneratedVariables.Add(expr);
continue;
}
bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null);
if (expr.InferredType == null || anyArgumentIsMissingType)
expr.InferredType = InferTypeForExpression(expr, null, forceInferChildren: anyArgumentIsMissingType);
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable v = expr.Operand as ILVariable;
if (v != null && v.IsGenerated && v.Type == null && expr.Code == ILCode.Stloc && !inferredVariables.Contains(v) && HasSingleLoad(v)) {
// Don't deal with this node or its children yet,
// wait for the expected type to be inferred first.
// This happens with the arg_... variables introduced by the ILAst - we skip inferring the whole statement,
// and first infer the statement that reads from the arg_... variable.
// The ldloc inference will write the expected type to the variable, and the next InferRemainingStores() pass
// will then infer this statement with the correct expected type.
storedToGeneratedVariables.Add(expr);
return;
}
bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null);
if (expr.InferredType == null || anyArgumentIsMissingType)
expr.InferredType = InferTypeForExpression(expr, null, forceInferChildren: anyArgumentIsMissingType);
}
foreach (ILNode child in node.GetChildren()) {
InferTypes(child);
}
}
bool HasSingleLoad(ILVariable v)
{
int loads = 0;
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Operand == v) {
if (expr.Code == ILCode.Ldloc)
loads++;
else if (expr.Code != ILCode.Stloc)
return false;
}
}
return loads == 1;
}
void InferRemainingStores()
{
while (storedToGeneratedVariables.Count > 0) {
List<ILExpression> stored = storedToGeneratedVariables;
storedToGeneratedVariables = new List<ILExpression>();
foreach (ILExpression expr in stored)
InferTypes(expr);
if (!(storedToGeneratedVariables.Count < stored.Count))
throw new InvalidOperationException("Infinite loop in type analysis detected.");
}
}
/// <summary>
/// Infers the C# type of <paramref name="expr"/>.
/// </summary>
@ -70,14 +104,44 @@ namespace Decompiler @@ -70,14 +104,44 @@ namespace Decompiler
TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{
switch (expr.Code) {
case ILCode.LogicNot:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
}
return typeSystem.Boolean;
case ILCode.LogicAnd:
case ILCode.LogicOr:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
}
return typeSystem.Boolean;
}
switch ((Code)expr.Code) {
#region Variable load/store
case Code.Stloc:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type);
{
ILVariable v = (ILVariable)expr.Operand;
if (forceInferChildren || v.Type == null) {
TypeReference t = InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type);
if (v.Type == null)
v.Type = t;
}
}
return null;
case Code.Ldloc:
return ((ILVariable)expr.Operand).Type;
{
ILVariable v = (ILVariable)expr.Operand;
if (v.Type == null) {
v.Type = expectedType;
// Mark the variable as inferred. This is necessary because expectedType might be null
// (e.g. the only use of an arg_*-Variable is a pop statement),
// so we can't tell from v.Type whether it was already inferred.
inferredVariables.Add(v);
}
return v.Type;
}
case Code.Starg:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType);

Loading…
Cancel
Save