Browse Source

ControlFlowGraphBuilder bugfix: "try { try {} finally {} } catch {}"

pull/1/head
Daniel Grunwald 15 years ago
parent
commit
3d28650309
  1. 2
      ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  2. 4
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
  3. 1
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  4. 38
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  5. 17
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  6. 24
      ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
  7. 1
      ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs

2
ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -136,7 +136,7 @@ namespace ICSharpCode.Decompiler.Disassembler
}
output.Indent();
}
foreach (var node in s.Nodes.Concat(s.Children.Select(c => c.EntryPoint)).OrderBy(n => n.BlockIndex)) {
foreach (var node in s.Nodes.Concat(from c in s.Children select c.EntryPoint).OrderBy(c => c.Offset)) {
if (s.Nodes.Contains(node)) {
foreach (var inst in node.Instructions) {
inst.WriteTo(output);

4
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs

@ -39,10 +39,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -39,10 +39,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
LeaveTry,
/// <summary>
/// Jump from one catch block to its sibling (e.g. in "try {} catch (A) {} catch (B) {}")
/// </summary>
MutualProtection,
/// <summary>
/// Jump at endfinally (to any of the potential leave targets).
/// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge.
/// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction.

1
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -71,6 +71,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
case JumpType.Normal:
break;
case JumpType.LeaveTry:
case JumpType.EndFinally:
e.color = "red";
break;
default:

38
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -55,11 +55,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -55,11 +55,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
hasIncomingJumps = new bool[methodBody.Instructions.Count];
entryPoint = new ControlFlowNode(0, ControlFlowNodeType.EntryPoint);
entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
nodes.Add(entryPoint);
regularExit = new ControlFlowNode(1, ControlFlowNodeType.RegularExit);
regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
nodes.Add(regularExit);
exceptionalExit = new ControlFlowNode(2, ControlFlowNodeType.ExceptionalExit);
exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
nodes.Add(exceptionalExit);
Debug.Assert(nodes.Count == 3);
}
@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -140,7 +140,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
throw new NotSupportedException();
ControlFlowNode endFinallyOrFaultNode = null;
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, ControlFlowNodeType.EndFinallyOrFault);
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
nodes.Add(endFinallyOrFaultNode);
}
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
@ -206,28 +206,15 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -206,28 +206,15 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler);
CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), 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, FindInnermostExceptionHandlerNode(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler);
CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
}
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
}
}
// now create edges between catch handlers that mutually protect
for (int i = 0; i < methodBody.ExceptionHandlers.Count; i++) {
ExceptionHandler first = methodBody.ExceptionHandlers[i];
if (first.HandlerType == ExceptionHandlerType.Catch) {
for (int j = i + 1; j < methodBody.ExceptionHandlers.Count; j++) {
ExceptionHandler second = methodBody.ExceptionHandlers[j];
if (second.HandlerType == ExceptionHandlerType.Catch && second.TryStart == first.TryStart && second.TryEnd == first.TryEnd) {
CreateEdge(nodes.Single(n => n.ExceptionHandler == first), nodes.Single(n => n.ExceptionHandler == second), JumpType.MutualProtection);
}
}
}
}
}
ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
@ -260,6 +247,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -260,6 +247,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
}
return exceptionalExit;
}
ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
{
Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
|| exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
ExceptionHandler h = nodes[i].ExceptionHandler;
if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
return nodes[i];
}
return exceptionalExit;
}
#endregion
#region Step 5a: replace LeaveTry edges with EndFinally edges

17
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -77,6 +77,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -77,6 +77,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
public readonly int BlockIndex;
/// <summary>
/// Gets the IL offset of this node.
/// </summary>
public readonly int Offset;
/// <summary>
/// Type of the node.
/// </summary>
@ -94,6 +99,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -94,6 +99,13 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
public bool Visited;
/// <summary>
/// Gets whether this node is reachable. Requires that dominance is computed!
/// </summary>
public bool IsReachable {
get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; }
}
/// <summary>
/// Signalizes that this node is a copy of another node.
/// </summary>
@ -146,9 +158,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -146,9 +158,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// </summary>
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
internal ControlFlowNode(int blockIndex, ControlFlowNodeType nodeType)
internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType)
{
this.BlockIndex = blockIndex;
this.Offset = offset;
this.NodeType = nodeType;
}
@ -162,6 +175,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -162,6 +175,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
this.NodeType = ControlFlowNodeType.Normal;
this.Start = start;
this.End = end;
this.Offset = start.Offset;
}
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
@ -171,6 +185,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -171,6 +185,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
this.ExceptionHandler = exceptionHandler;
this.EndFinallyOrFaultNode = endFinallyOrFaultNode;
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
this.Offset = exceptionHandler.HandlerStart.Offset;
}
/// <summary>

24
ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

@ -18,9 +18,9 @@ @@ -18,9 +18,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
@ -59,15 +59,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -59,15 +59,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
current.Children.Add(tryBlock);
if (eh.FilterStart != null) {
var filterNodes = FindNodes(current, eh.FilterStart, eh.FilterEnd);
current.Nodes.ExceptWith(filterNodes);
ControlStructure filterBlock = new ControlStructure(
filterNodes,
g.Nodes.Single(n => n.Start == eh.HandlerStart),
ControlStructureType.Filter);
filterBlock.ExceptionHandler = eh;
MoveControlStructures(current, filterBlock, eh.FilterStart, eh.FilterEnd);
current.Children.Add(filterBlock);
throw new NotSupportedException();
}
var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
@ -77,9 +69,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -77,9 +69,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
current.Nodes.ExceptWith(handlerNodes);
ControlStructure handlerBlock = new ControlStructure(
handlerNodes,
g.Nodes.Single(n => n.Start == eh.HandlerStart),
ControlStructureType.Handler);
handlerNodes, handlerNode, ControlStructureType.Handler);
handlerBlock.ExceptionHandler = eh;
MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
current.Children.Add(handlerBlock);
@ -95,7 +85,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -95,7 +85,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
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) {
if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
result.Add(node);
}
}
@ -106,7 +96,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -106,7 +96,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
{
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) {
if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
current.Children.RemoveAt(i--);
target.Children.Add(child);
target.AllNodes.UnionWith(child.AllNodes);
@ -128,6 +118,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -128,6 +118,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
{
if (!current.EntryPoint.IsReachable)
return;
g.ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
FindLoops(current, current.EntryPoint);
@ -238,6 +230,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -238,6 +230,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
this.Nodes = nodes;
this.EntryPoint = entryPoint;
this.Type = type;

1
ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs

@ -163,7 +163,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -163,7 +163,6 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
throw new NotSupportedException("stacksize must be 0 in endfinally edge");
newStackSize = 0;
break;
case JumpType.MutualProtection:
case JumpType.JumpToExceptionHandler:
switch (edge.Target.NodeType) {
case ControlFlowNodeType.FinallyOrFaultHandler:

Loading…
Cancel
Save