Browse Source

Show exception handlers in IL assembly.

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
807a345d08
  1. 80
      ICSharpCode.Decompiler/DominanceLoopDetector.cs
  2. 32
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  3. 154
      ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
  4. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  5. 72
      ILSpy/Disassembler/ILLanguage.cs
  6. 4
      ILSpy/ExtensionMethods.cs
  7. 7
      ILSpy/ITextOutput.cs

80
ICSharpCode.Decompiler/DominanceLoopDetector.cs

@ -1,80 +0,0 @@ @@ -1,80 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Description of DominanceLoopDetector.
/// </summary>
public class DominanceLoopDetector
{
public static ControlStructure DetectLoops(ControlFlowGraph g)
{
ControlStructure root = new ControlStructure(
new HashSet<ControlFlowNode>(g.Nodes),
g.EntryPoint
);
DetectLoops(g, root);
g.ResetVisited();
return root;
}
static void DetectLoops(ControlFlowGraph g, ControlStructure current)
{
g.ResetVisited();
FindLoops(current, current.EntryPoint);
foreach (ControlStructure loop in current.Children)
DetectLoops(g, loop);
}
static void FindLoops(ControlStructure current, ControlFlowNode node)
{
if (node.Visited)
return;
node.Visited = true;
if (current.Nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& node != current.EntryPoint)
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(current, loopContents, node, node);
current.Nodes.ExceptWith(loopContents);
current.Children.Add(new ControlStructure(loopContents, node));
}
foreach (var edge in node.Outgoing) {
FindLoops(current, edge.Target);
}
}
static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
{
if (current.Nodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
foreach (var edge in node.Incoming) {
FindLoopContents(current, loopContents, loopHead, edge.Source);
}
}
}
}
public class ControlStructure
{
public List<ControlStructure> Children = new List<ControlStructure>();
public HashSet<ControlFlowNode> Nodes;
public ControlFlowNode EntryPoint;
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint)
{
this.Nodes = nodes;
this.EntryPoint = entryPoint;
}
}
}

32
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
{
for (int i = 0; i < methodBody.Instructions.Count; i++) {
Instruction blockStart = methodBody.Instructions[i];
ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
// try and see how big we can make that block:
for (; i + 1 < methodBody.Instructions.Count; i++) {
Instruction inst = methodBody.Instructions[i];
@ -105,6 +106,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -105,6 +106,12 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
break;
if (hasIncomingJumps[i + 1])
break;
if (inst.Next != null) {
// ensure that blocks never contain instructions from different try blocks
ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
if (instEH != blockStartEH)
break;
}
}
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
@ -170,17 +177,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -170,17 +177,17 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
{
foreach (ControlFlowNode node in nodes) {
if (node.End != null && CanThrowException(node.End.OpCode)) {
CreateEdge(node, FindInnermostExceptionHandler(node.End.Offset), JumpType.JumpToExceptionHandler);
CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
}
if (node.ExceptionHandler != null) {
if (node.EndFinallyOrFaultNode != null) {
// For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
// This represents the exception bubbling up after finally block was executed.
CreateEdge(node.EndFinallyOrFaultNode, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler);
CreateEdge(node.EndFinallyOrFaultNode, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler);
} else {
// For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
// This represents the exception bubbling up because it did not match the type of the catch block.
CreateEdge(node, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler);
CreateEdge(node, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler);
}
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
}
@ -200,14 +207,23 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -200,14 +207,23 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
}
ControlFlowNode FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
return h;
}
}
return exceptionalExit;
return null;
}
ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
{
ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
if (h != null)
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
else
return exceptionalExit;
}
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
@ -236,7 +252,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -236,7 +252,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset);
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
@ -258,7 +274,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -258,7 +274,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset);
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
CreateEdge(node, handler, JumpType.Normal);

154
ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Description of DominanceLoopDetector.
/// </summary>
public class ControlStructureDetector
{
public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
{
ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
DetectExceptionHandling(root, g, exceptionHandlers);
DetectLoops(g, root);
g.ResetVisited();
return root;
}
#region Exception Handling
static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
{
foreach (ExceptionHandler eh in exceptionHandlers) {
ControlStructure tryBlock = new ControlStructure(
FindAndRemoveNodes(current, eh.TryStart, eh.TryEnd),
g.Nodes.Single(n => n.Start == eh.TryStart),
ControlStructureType.Try);
tryBlock.ExceptionHandler = eh;
MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
current.Children.Add(tryBlock);
if (eh.FilterStart != null) {
ControlStructure filterBlock = new ControlStructure(
FindAndRemoveNodes(current, eh.HandlerStart, eh.HandlerEnd),
g.Nodes.Single(n => n.Start == eh.HandlerStart),
ControlStructureType.Filter);
filterBlock.ExceptionHandler = eh;
MoveControlStructures(current, filterBlock, eh.FilterStart, eh.FilterEnd);
current.Children.Add(filterBlock);
}
ControlStructure handlerBlock = new ControlStructure(
FindAndRemoveNodes(current, eh.HandlerStart, eh.HandlerEnd),
g.Nodes.Single(n => n.Start == eh.HandlerStart),
ControlStructureType.Handler);
handlerBlock.ExceptionHandler = eh;
MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
current.Children.Add(handlerBlock);
}
}
/// <summary>
/// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
/// </summary>
static HashSet<ControlFlowNode> FindAndRemoveNodes(ControlStructure current, Instruction startInst, Instruction endInst)
{
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
int start = startInst.Offset;
int end = endInst.Offset;
foreach (var node in current.Nodes.ToArray()) {
if (node.Start != null && node.Start.Offset >= start && node.Start.Offset < end) {
current.Nodes.Remove(node);
result.Add(node);
}
}
return result;
}
static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
{
for (int i = 0; i < current.Children.Count; i++) {
var child = current.Children[i];
if (child.EntryPoint.Start.Offset >= startInst.Offset && child.EntryPoint.Start.Offset <= endInst.Offset) {
current.Children.RemoveAt(i--);
target.Children.Add(child);
}
}
}
#endregion
#region Loop Detection
static void DetectLoops(ControlFlowGraph g, ControlStructure current)
{
g.ResetVisited();
FindLoops(current, current.EntryPoint);
foreach (ControlStructure loop in current.Children)
DetectLoops(g, loop);
}
static void FindLoops(ControlStructure current, ControlFlowNode node)
{
if (node.Visited)
return;
node.Visited = true;
if (current.Nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(current, loopContents, node, node);
current.Nodes.ExceptWith(loopContents);
current.Children.Add(new ControlStructure(loopContents, node, ControlStructureType.Loop));
}
foreach (var edge in node.Outgoing) {
FindLoops(current, edge.Target);
}
}
static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
{
if (current.Nodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
foreach (var edge in node.Incoming) {
FindLoopContents(current, loopContents, loopHead, edge.Source);
}
}
}
#endregion
}
public enum ControlStructureType
{
Root,
Loop,
Try,
Handler,
Filter
}
public class ControlStructure
{
public readonly ControlStructureType Type;
public readonly List<ControlStructure> Children = new List<ControlStructure>();
public readonly HashSet<ControlFlowNode> Nodes;
public readonly ControlFlowNode EntryPoint;
public ExceptionHandler ExceptionHandler;
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
{
this.Nodes = nodes;
this.EntryPoint = entryPoint;
this.Type = type;
}
}
}

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -50,11 +50,11 @@ @@ -50,11 +50,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="CecilExtensions.cs" />
<Compile Include="DominanceLoopDetector.cs" />
<Compile Include="FlowAnalysis\ControlFlowEdge.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraph.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\ControlStructureDetector.cs" />
<Compile Include="FlowAnalysis\OpCodeInfo.cs" />
<Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" />
<Compile Include="FlowAnalysis\SsaBlock.cs" />

72
ILSpy/Disassembler/ILLanguage.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis;
@ -35,10 +36,8 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -35,10 +36,8 @@ namespace ICSharpCode.ILSpy.Disassembler
public override void Decompile(MethodDefinition method, ITextOutput output)
{
output.WriteComment("// Method begins at RVA 0x{0:x4}", method.RVA);
output.WriteLine();
output.WriteComment("// Code size {0} (0x{0:x})", method.Body.CodeSize);
output.WriteLine();
output.WriteCommentLine("// Method begins at RVA 0x{0:x4}", method.RVA);
output.WriteCommentLine("// Code size {0} (0x{0:x})", method.Body.CodeSize);
output.WriteLine(".maxstack {0}", method.Body.MaxStackSize);
if (method.DeclaringType.Module.Assembly.EntryPoint == method)
output.WriteLine (".entrypoint");
@ -58,13 +57,14 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -58,13 +57,14 @@ namespace ICSharpCode.ILSpy.Disassembler
output.Unindent();
output.WriteLine(")");
}
output.WriteLine();
if (detectControlStructure) {
method.Body.SimplifyMacros();
var cfg = ControlFlowGraphBuilder.Build(method.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
var s = DominanceLoopDetector.DetectLoops(cfg);
var s = ControlStructureDetector.DetectStructure(cfg, method.Body.ExceptionHandlers);
WriteStructure(output, s);
} else {
foreach (var inst in method.Body.Instructions) {
@ -81,9 +81,43 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -81,9 +81,43 @@ namespace ICSharpCode.ILSpy.Disassembler
void WriteStructure(ITextOutput output, ControlStructure s)
{
output.WriteComment("// loop start");
output.WriteLine();
output.Indent();
if (s.Type != ControlStructureType.Root) {
switch (s.Type) {
case ControlStructureType.Loop:
output.WriteCommentLine("// loop start");
break;
case ControlStructureType.Try:
output.WriteLine(".try {");
break;
case ControlStructureType.Handler:
switch (s.ExceptionHandler.HandlerType) {
case Mono.Cecil.Cil.ExceptionHandlerType.Catch:
case Mono.Cecil.Cil.ExceptionHandlerType.Filter:
output.Write("catch");
if (s.ExceptionHandler.CatchType != null) {
output.Write(' ');
s.ExceptionHandler.CatchType.WriteTo(output);
}
output.WriteLine(" {");
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Finally:
output.WriteLine("finally {");
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Fault:
output.WriteLine("fault {");
break;
default:
throw new NotSupportedException();
}
break;
case ControlStructureType.Filter:
output.WriteLine("filter {");
break;
default:
throw new NotSupportedException();
}
output.Indent();
}
foreach (var node in s.Nodes.Concat(s.Children.Select(c => c.EntryPoint)).OrderBy(n => n.BlockIndex)) {
if (s.Nodes.Contains(node)) {
foreach (var inst in node.Instructions) {
@ -94,9 +128,25 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -94,9 +128,25 @@ namespace ICSharpCode.ILSpy.Disassembler
WriteStructure(output, s.Children.Single(c => c.EntryPoint == node));
}
}
output.Unindent();
output.WriteComment("// loop end");
output.WriteLine();
if (s.Type != ControlStructureType.Root) {
output.Unindent();
switch (s.Type) {
case ControlStructureType.Loop:
output.WriteCommentLine("// end loop");
break;
case ControlStructureType.Try:
output.WriteLine("} // end .try");
break;
case ControlStructureType.Handler:
output.WriteLine("} // end handler");
break;
case ControlStructureType.Filter:
output.WriteLine("} // end filter");
break;
default:
throw new NotSupportedException();
}
}
}
}
}

4
ILSpy/ExtensionMethods.cs

@ -50,9 +50,9 @@ namespace ICSharpCode.ILSpy @@ -50,9 +50,9 @@ namespace ICSharpCode.ILSpy
output.WriteLine(string.Format(format, args));
}
public static void WriteComment(this ITextOutput output, string format, params object[] args)
public static void WriteCommentLine(this ITextOutput output, string format, params object[] args)
{
output.WriteComment(string.Format(format, args));
output.WriteCommentLine(string.Format(format, args));
}
/// <summary>

7
ILSpy/ITextOutput.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy
void Unindent();
void Write(char ch);
void Write(string text);
void WriteComment(string comment);
void WriteCommentLine(string comment);
void WriteLine();
void WriteDefinition(string text, object definition);
void WriteReference(string text, object reference);
@ -101,10 +101,11 @@ namespace ICSharpCode.ILSpy @@ -101,10 +101,11 @@ namespace ICSharpCode.ILSpy
b.Append(text);
}
public void WriteComment(string comment)
public void WriteCommentLine(string comment)
{
WriteIndent();
b.Append(comment);
b.AppendLine(comment);
needsIndent = true;
}
public void WriteLine()

Loading…
Cancel
Save