Browse Source

Add control flow analysis.

pull/1/head
Daniel Grunwald 14 years ago
parent
commit
591c4c5bb8
  1. 185
      ICSharpCode.Decompiler/CecilExtensions.cs
  2. 56
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
  3. 180
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  4. 388
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  5. 184
      ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  6. 240
      ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
  7. 169
      ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
  8. 50
      ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
  9. 158
      ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
  10. 254
      ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs
  11. 190
      ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs
  12. 135
      ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
  13. 85
      ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs
  14. 254
      ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
  15. 196
      ICSharpCode.Decompiler/GraphVizGraph.cs
  16. 71
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  17. 31
      ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
  18. 10
      ILSpy.sln
  19. 192
      ILSpy/Disassembler/DisassemblerHelpers.cs
  20. 6
      ILSpy/ILSpy.csproj
  21. 1
      ILSpy/MainWindow.xaml
  22. 44
      ILSpy/MainWindow.xaml.cs
  23. 4
      ILSpy/MethodTreeNode.cs

185
ICSharpCode.Decompiler/CecilExtensions.cs

@ -0,0 +1,185 @@ @@ -0,0 +1,185 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Cecil helper methods.
/// </summary>
public static class CecilExtensions
{
#region GetPushDelta / GetPopDelta
public static int GetPushDelta(this Instruction instruction)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPush) {
case StackBehaviour.Push0:
return 0;
case StackBehaviour.Push1:
case StackBehaviour.Pushi:
case StackBehaviour.Pushi8:
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
return 1;
case StackBehaviour.Push1_push1:
return 2;
case StackBehaviour.Varpush:
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
return IsVoid (method.ReturnType) ? 0 : 1;
}
throw new NotSupportedException ();
}
public static int GetPopDelta(this Instruction instruction, MethodDefinition current, int currentStackSize)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPop) {
case StackBehaviour.Pop0:
return 0;
case StackBehaviour.Popi:
case StackBehaviour.Popref:
case StackBehaviour.Pop1:
return 1;
case StackBehaviour.Pop1_pop1:
case StackBehaviour.Popi_pop1:
case StackBehaviour.Popi_popi:
case StackBehaviour.Popi_popi8:
case StackBehaviour.Popi_popr4:
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
return 2;
case StackBehaviour.Popi_popi_popi:
case StackBehaviour.Popref_popi_popi:
case StackBehaviour.Popref_popi_popi8:
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
return 3;
case StackBehaviour.PopAll:
return currentStackSize;
case StackBehaviour.Varpop:
if (code == OpCodes.Ret)
return IsVoid (current.ReturnType) ? 0 : 1;
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
int count = method.HasParameters ? method.Parameters.Count : 0;
if (method.HasThis && code != OpCodes.Newobj)
++count;
return count;
}
throw new NotSupportedException ();
}
public static bool IsVoid(this TypeReference type)
{
return type.FullName == "System.Void" && !(type is TypeSpecification);
}
public static bool IsValueTypeOrVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
if (type is ArrayType)
return false;
return type.IsValueType || type.IsVoid();
}
#endregion
public static void WriteTo(this Instruction instruction, TextWriter writer)
{
writer.Write(OffsetToString(instruction.Offset));
writer.Write(": ");
writer.Write(instruction.OpCode.Name);
if(null != instruction.Operand) {
writer.Write(' ');
writer.Write(OperandToString(instruction.Operand));
}
}
public static void WriteTo(this ExceptionHandler exceptionHandler, TextWriter writer)
{
writer.Write("Try IL_{0:x4}-IL_{1:x4} ", exceptionHandler.TryStart.Offset, exceptionHandler.TryEnd.Offset);
writer.Write(exceptionHandler.HandlerType.ToString());
if (exceptionHandler.FilterStart != null) {
writer.Write(" IL_{0:x4}-IL_{1:x4} handler ", exceptionHandler.FilterStart.Offset, exceptionHandler.FilterEnd.Offset);
}
writer.Write(" IL_{0:x4}-IL_{1:x4} ", exceptionHandler.HandlerStart.Offset, exceptionHandler.HandlerEnd.Offset);
}
public static string OffsetToString(int offset)
{
return string.Format("IL_{0:x4}", offset);
}
public static string OperandToString(object operand)
{
if(null == operand) throw new ArgumentNullException("operand");
Instruction targetInstruction = operand as Instruction;
if(null != targetInstruction) {
return OffsetToString(targetInstruction.Offset);
}
Instruction [] targetInstructions = operand as Instruction [];
if(null != targetInstructions) {
return string.Join(", ", targetInstructions.Select(i => OffsetToString(i.Offset)));
}
VariableReference variableRef = operand as VariableReference;
if(null != variableRef) {
return variableRef.Index.ToString();
}
MethodReference methodRef = operand as MethodReference;
if(null != methodRef) {
return methodRef.ToString();
}
string s = operand as string;
if(null != s) {
return "\"" + s + "\"";
}
return operand.ToString();
}
}
}

56
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum JumpType
{
Normal,
JumpToExceptionHandler,
LeaveTry,
MutualProtection
}
public sealed class ControlFlowEdge
{
public readonly ControlFlowNode Source;
public readonly ControlFlowNode Target;
public readonly JumpType Type;
public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type)
{
this.Source = source;
this.Target = target;
this.Type = type;
}
public override string ToString()
{
switch (Type) {
case JumpType.Normal:
return "#" + Target.BlockIndex;
case JumpType.JumpToExceptionHandler:
return "e:#" + Target.BlockIndex;
default:
return Type.ToString() + ":#" + Target.BlockIndex;
}
}
}
}

180
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -0,0 +1,180 @@ @@ -0,0 +1,180 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains the control flow graph.
/// </summary>
public sealed class ControlFlowGraph
{
readonly ReadOnlyCollection<ControlFlowNode> nodes;
public ControlFlowNode EntryPoint {
get { return nodes[0]; }
}
public ControlFlowNode RegularExit {
get { return nodes[1]; }
}
public ControlFlowNode ExceptionalExit {
get { return nodes[2]; }
}
public ReadOnlyCollection<ControlFlowNode> Nodes {
get { return nodes; }
}
internal ControlFlowGraph(ControlFlowNode[] nodes)
{
this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes);
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
}
#if DEBUG
public GraphVizGraph ExportGraph()
{
GraphVizGraph graph = new GraphVizGraph();
foreach (ControlFlowNode node in nodes) {
graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" });
}
foreach (ControlFlowNode node in nodes) {
foreach (ControlFlowEdge edge in node.Outgoing) {
GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex);
switch (edge.Type) {
case JumpType.Normal:
break;
case JumpType.LeaveTry:
e.color = "red";
break;
default:
e.color = "gray";
//e.constraint = false;
break;
}
graph.AddEdge(e);
}
if (node.ImmediateDominator != null) {
graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false });
}
}
return graph;
}
#endif
internal void ResetVisited()
{
foreach (ControlFlowNode node in nodes) {
node.Visited = false;
}
}
public void ComputeDominance()
{
// A Simple, Fast Dominance Algorithm
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
EntryPoint.ImmediateDominator = EntryPoint;
bool changed = true;
while (changed) {
changed = false;
ResetVisited();
// for all nodes b except the entry point
EntryPoint.TraversePreOrder(
b => b.Successors,
b => {
if (b != EntryPoint) {
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited);
// for all other predecessors p of b
foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) {
newIdom = FindCommonDominator(p, newIdom);
}
}
if (b.ImmediateDominator != newIdom) {
b.ImmediateDominator = newIdom;
changed = true;
}
}
});
}
EntryPoint.ImmediateDominator = null;
foreach (ControlFlowNode node in nodes) {
if (node.ImmediateDominator != null)
node.ImmediateDominator.DominatorTreeChildren.Add(node);
}
}
static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2)
{
// Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>();
while (b1 != null && path1.Add(b1))
b1 = b1.ImmediateDominator;
while (b2 != null) {
if (path1.Contains(b2))
return b2;
else
b2 = b2.ImmediateDominator;
}
throw new Exception("No common dominator found!");
}
public void ComputeDominanceFrontier()
{
ResetVisited();
EntryPoint.TraversePostOrder(
b => b.DominatorTreeChildren,
n => {
//logger.WriteLine("Calculating dominance frontier for " + n.Name);
n.DominanceFrontier = new HashSet<ControlFlowNode>();
// DF_local computation
foreach (ControlFlowNode succ in n.Successors) {
if (succ.ImmediateDominator != n) {
//logger.WriteLine(" local: " + succ.Name);
n.DominanceFrontier.Add(succ);
}
}
// DF_up computation
foreach (ControlFlowNode child in n.DominatorTreeChildren) {
foreach (ControlFlowNode p in child.DominanceFrontier) {
if (p.ImmediateDominator != n) {
//logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
n.DominanceFrontier.Add(p);
}
}
}
});
}
}
}

388
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -0,0 +1,388 @@ @@ -0,0 +1,388 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public sealed class ControlFlowGraphBuilder
{
public static ControlFlowGraph Build(MethodBody methodBody)
{
return new ControlFlowGraphBuilder(methodBody).Build();
}
MethodBody methodBody;
int[] offsets; // array index = instruction index; value = IL offset
bool[] hasIncomingJumps; // array index = instruction index
List<ControlFlowNode> nodes = new List<ControlFlowNode>();
ControlFlowNode entryPoint;
ControlFlowNode regularExit ;
ControlFlowNode exceptionalExit;
//Stack<> activeExceptionHandlers = new Stack<ExceptionHandler>();
private ControlFlowGraphBuilder(MethodBody methodBody)
{
this.methodBody = methodBody;
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
hasIncomingJumps = new bool[methodBody.Instructions.Count];
entryPoint = new ControlFlowNode(0, ControlFlowNodeType.EntryPoint);
nodes.Add(entryPoint);
regularExit = new ControlFlowNode(1, ControlFlowNodeType.RegularExit);
nodes.Add(regularExit);
exceptionalExit = new ControlFlowNode(2, ControlFlowNodeType.ExceptionalExit);
nodes.Add(exceptionalExit);
Debug.Assert(nodes.Count == 3);
}
int GetInstructionIndex(Instruction inst)
{
int index = Array.BinarySearch(offsets, inst.Offset);
Debug.Assert(index >= 0);
return index;
}
public ControlFlowGraph Build()
{
CalculateHasIncomingJumps();
CreateNodes();
CreateRegularControlFlow();
CreateExceptionalControlFlow();
CopyFinallyBlocksIntoLeaveEdges();
return new ControlFlowGraph(nodes.ToArray());
}
void CalculateHasIncomingJumps()
{
foreach (Instruction inst in methodBody.Instructions) {
if (inst.OpCode.OperandType == OperandType.InlineBrTarget) {
hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
} else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])inst.Operand)
hasIncomingJumps[GetInstructionIndex(i)] = true;
}
}
foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
if (eh.FilterStart != null) {
hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
}
hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
}
}
void CreateNodes()
{
for (int i = 0; i < methodBody.Instructions.Count; i++) {
Instruction blockStart = methodBody.Instructions[i];
// try and see how big we can make that block:
for (; i + 1 < methodBody.Instructions.Count; i++) {
Instruction inst = methodBody.Instructions[i];
if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
break;
if (hasIncomingJumps[i + 1])
break;
}
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
}
foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
if (handler.HandlerType == ExceptionHandlerType.Filter)
throw new NotSupportedException();
ControlFlowNode endFinallyOrFaultNode = null;
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, ControlFlowNodeType.EndFinallyOrFault);
nodes.Add(endFinallyOrFaultNode);
}
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
}
}
void CreateRegularControlFlow()
{
CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
foreach (ControlFlowNode node in nodes) {
if (node.End != null) {
// create normal edges from one instruction to the next
if (!IsUnconditionalBranch(node.End.OpCode))
CreateEdge(node, node.End.Next, JumpType.Normal);
// create edges for branch instructions
if (node.End.OpCode.OperandType == OperandType.InlineBrTarget) {
if (node.End.OpCode == OpCodes.Leave) {
var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
else
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
} else {
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
}
} else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])node.End.Operand)
CreateEdge(node, i, JumpType.Normal);
}
// create edges for return instructions
if (node.End.OpCode.FlowControl == FlowControl.Return) {
switch (node.End.OpCode.Code) {
case Code.Ret:
CreateEdge(node, regularExit, JumpType.Normal);
break;
case Code.Endfinally:
ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.EndFinallyOrFaultNode == null)
throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
break;
default:
throw new NotSupportedException(node.End.OpCode.ToString());
}
}
}
}
}
void CreateExceptionalControlFlow()
{
foreach (ControlFlowNode node in nodes) {
if (node.End != null && CanThrowException(node.End.OpCode)) {
CreateEdge(node, FindInnermostExceptionHandler(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);
} 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, 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);
}
}
}
}
}
ControlFlowNode 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 exceptionalExit;
}
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
|| h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
{
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
}
}
return exceptionalExit;
}
void CopyFinallyBlocksIntoLeaveEdges()
{
// We need to process try-finally blocks inside-out.
// We'll do that by going through all instructions in reverse order
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
}
}
}
/// <summary>
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
/// Nodes pointing to the copied node are copied recursively to update those references, too.
/// This recursion stops at 'start'. The modified version of start is returned.
/// </summary>
ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
}
class CopyFinallySubGraphLogic
{
readonly ControlFlowGraphBuilder builder;
readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
readonly ControlFlowNode start;
readonly ControlFlowNode end;
readonly ControlFlowNode newEnd;
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
this.builder = builder;
this.start = start;
this.end = end;
this.newEnd = newEnd;
}
internal ControlFlowNode CopyFinallySubGraph()
{
foreach (ControlFlowNode n in end.Predecessors) {
CollectNodes(n);
}
foreach (var pair in oldToNew)
ReconstructEdges(pair.Key, pair.Value);
return GetNew(start);
}
void CollectNodes(ControlFlowNode node)
{
if (node == end || node == newEnd)
throw new InvalidOperationException("unexpected cycle involving finally construct");
if (!oldToNew.ContainsKey(node)) {
int newBlockIndex = builder.nodes.Count;
ControlFlowNode copy;
switch (node.NodeType) {
case ControlFlowNodeType.Normal:
copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
break;
case ControlFlowNodeType.FinallyOrFaultHandler:
copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
break;
default:
// other nodes shouldn't occur when copying finally blocks
throw new NotSupportedException(node.NodeType.ToString());
}
copy.CopyFrom = node;
builder.nodes.Add(copy);
oldToNew.Add(node, copy);
if (node != start) {
foreach (ControlFlowNode n in node.Predecessors) {
CollectNodes(n);
}
}
}
}
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
{
foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
}
}
ControlFlowNode GetNew(ControlFlowNode oldNode)
{
if (oldNode == end)
return newEnd;
ControlFlowNode newNode;
if (oldToNew.TryGetValue(oldNode, out newNode))
return newNode;
return oldNode;
}
}
#region CreateEdge methods
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
{
CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
}
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
{
ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
fromNode.Outgoing.Add(edge);
toNode.Incoming.Add(edge);
}
#endregion
#region OpCode info
static bool CanThrowException(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
return OpCodeInfo.Get(opcode).CanThrow;
}
static bool IsBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Cond_Branch:
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
static bool IsUnconditionalBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
case FlowControl.Cond_Branch:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
#endregion
}
}

184
ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -0,0 +1,184 @@ @@ -0,0 +1,184 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum ControlFlowNodeType
{
Normal,
EntryPoint,
RegularExit,
ExceptionalExit,
CatchHandler,
FinallyOrFaultHandler,
EndFinallyOrFault
}
public sealed class ControlFlowNode
{
public readonly int BlockIndex;
public readonly ControlFlowNodeType NodeType;
public readonly ControlFlowNode EndFinallyOrFaultNode;
/// <summary>
/// Visited flag that's used in various algorithms.
/// </summary>
internal bool Visited;
/// <summary>
/// Signalizes that this node is a copy of another node.
/// </summary>
public ControlFlowNode CopyFrom { get; internal set; }
/// <summary>
/// Gets the immediate dominator.
/// </summary>
public ControlFlowNode ImmediateDominator { get; internal set; }
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
public HashSet<ControlFlowNode> DominanceFrontier;
/// <summary>
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction Start;
/// <summary>
/// End of the code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction End;
/// <summary>
/// Gets the exception handler associated with this node. Only set for nodetype == ExceptionHandler.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
internal ControlFlowNode(int blockIndex, ControlFlowNodeType nodeType)
{
this.BlockIndex = blockIndex;
this.NodeType = nodeType;
}
internal ControlFlowNode(int blockIndex, Instruction start, Instruction end)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
this.BlockIndex = blockIndex;
this.NodeType = ControlFlowNodeType.Normal;
this.Start = start;
this.End = end;
}
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
{
this.BlockIndex = blockIndex;
this.NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler;
this.ExceptionHandler = exceptionHandler;
this.EndFinallyOrFaultNode = endFinallyOrFaultNode;
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
}
public IEnumerable<ControlFlowNode> Predecessors {
get {
return Incoming.Select(e => e.Source);
}
}
public IEnumerable<ControlFlowNode> Successors {
get {
return Outgoing.Select(e => e.Target);
}
}
public IEnumerable<Instruction> Instructions {
get {
Instruction inst = Start;
if (inst != null) {
yield return inst;
while (inst != End) {
inst = inst.Next;
yield return inst;
}
}
}
}
public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
visitAction(this);
foreach (ControlFlowNode t in children(this))
t.TraversePreOrder(children, visitAction);
}
public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
foreach (ControlFlowNode t in children(this))
t.TraversePostOrder(children, visitAction);
visitAction(this);
}
public override string ToString()
{
StringWriter writer = new StringWriter();
switch (NodeType) {
case ControlFlowNodeType.Normal:
int endOffset = End.Next != null ? End.Next.Offset : End.Offset + End.GetSize();
writer.Write("Block #{0}: IL_{1:x4} to IL_{2:x4}", BlockIndex, Start.Offset, endOffset);
break;
case ControlFlowNodeType.CatchHandler:
case ControlFlowNodeType.FinallyOrFaultHandler:
writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType);
ExceptionHandler.WriteTo(writer);
break;
default:
writer.Write("Block #{0}: {1}", BlockIndex, NodeType);
break;
}
// if (ImmediateDominator != null) {
// writer.WriteLine();
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
// }
foreach (Instruction inst in this.Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
}
}

240
ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs

@ -0,0 +1,240 @@ @@ -0,0 +1,240 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision: 776 $</version>
// </file>
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Additional info about opcodes.
/// </summary>
sealed class OpCodeInfo
{
static readonly OpCodeInfo[] knownOpCodes = {
#region Base Instructions
new OpCodeInfo(OpCodes.Add) { CanThrow = false },
new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.And) { CanThrow = false },
new OpCodeInfo(OpCodes.Arglist) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Br) { CanThrow = false },
new OpCodeInfo(OpCodes.Break) { CanThrow = true },
new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false },
new OpCodeInfo(OpCodes.Call) { CanThrow = true },
new OpCodeInfo(OpCodes.Calli) { CanThrow = true },
new OpCodeInfo(OpCodes.Ceq) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true },
new OpCodeInfo(OpCodes.Clt) { CanThrow = false },
new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false },
// conv.<to type>
new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false },
// conv.ovf.<to type>
new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true},
// conv.ovf.<to type>.un
new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true },
//new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
new OpCodeInfo(OpCodes.Div) { CanThrow = true },
new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false },
new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
//new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
//new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false },
// ldind.<type>
new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave) { CanThrow = false },
new OpCodeInfo(OpCodes.Localloc) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul) { CanThrow = false },
new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Neg) { CanThrow = false },
new OpCodeInfo(OpCodes.Nop) { CanThrow = false },
new OpCodeInfo(OpCodes.Not) { CanThrow = false },
new OpCodeInfo(OpCodes.Or) { CanThrow = false },
new OpCodeInfo(OpCodes.Pop) { CanThrow = false },
new OpCodeInfo(OpCodes.Rem) { CanThrow = true },
new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Ret) { CanThrow = false },
new OpCodeInfo(OpCodes.Shl) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true },
new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Sub) { CanThrow = false },
new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Switch) { CanThrow = false },
new OpCodeInfo(OpCodes.Xor) { CanThrow = false },
#endregion
#region Object model instructions
// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
new OpCodeInfo(OpCodes.Box),
new OpCodeInfo(OpCodes.Callvirt),
new OpCodeInfo(OpCodes.Castclass), // (output type not Input0 as type arguments may change)
new OpCodeInfo(OpCodes.Cpobj),
new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
new OpCodeInfo(OpCodes.Isinst) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldelem_Any),
// ldelem.<type>
new OpCodeInfo(OpCodes.Ldelem_I) ,
new OpCodeInfo(OpCodes.Ldelem_I1),
new OpCodeInfo(OpCodes.Ldelem_I2),
new OpCodeInfo(OpCodes.Ldelem_I4),
new OpCodeInfo(OpCodes.Ldelem_I8),
new OpCodeInfo(OpCodes.Ldelem_R4),
new OpCodeInfo(OpCodes.Ldelem_R8),
new OpCodeInfo(OpCodes.Ldelem_Ref),
new OpCodeInfo(OpCodes.Ldelem_U1),
new OpCodeInfo(OpCodes.Ldelem_U2),
new OpCodeInfo(OpCodes.Ldelem_U4),
new OpCodeInfo(OpCodes.Ldelema) ,
new OpCodeInfo(OpCodes.Ldfld) ,
new OpCodeInfo(OpCodes.Ldflda),
new OpCodeInfo(OpCodes.Ldlen) ,
new OpCodeInfo(OpCodes.Ldobj) ,
new OpCodeInfo(OpCodes.Ldsfld),
new OpCodeInfo(OpCodes.Ldsflda),
new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldvirtftn),
//new OpCodeInfo(OpCodes.Mkrefany), don't enable this without checking what it is
new OpCodeInfo(OpCodes.Newarr),
new OpCodeInfo(OpCodes.Newobj),
//new OpCodeInfo(OpCodes.Mkrefany), don't enable this without checking what it is
//new OpCodeInfo(OpCodes.Refanyval), don't enable this without checking what it is
new OpCodeInfo(OpCodes.Rethrow),
new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
new OpCodeInfo(OpCodes.Stelem_Any),
new OpCodeInfo(OpCodes.Stelem_I1),
new OpCodeInfo(OpCodes.Stelem_I2),
new OpCodeInfo(OpCodes.Stelem_I4),
new OpCodeInfo(OpCodes.Stelem_I8),
new OpCodeInfo(OpCodes.Stelem_R4),
new OpCodeInfo(OpCodes.Stelem_R8),
new OpCodeInfo(OpCodes.Stelem_Ref),
new OpCodeInfo(OpCodes.Stfld),
new OpCodeInfo(OpCodes.Stobj),
new OpCodeInfo(OpCodes.Stsfld),
new OpCodeInfo(OpCodes.Throw),
new OpCodeInfo(OpCodes.Unbox),
new OpCodeInfo(OpCodes.Unbox_Any),
#endregion
};
static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
public static OpCodeInfo Get(OpCode opCode)
{
return Get(opCode.Code);
}
public static OpCodeInfo Get(Code code)
{
OpCodeInfo info;
if (knownOpCodeDict.TryGetValue(code, out info))
return info;
else
throw new NotSupportedException(code.ToString());
}
OpCode opcode;
private OpCodeInfo(OpCode opcode)
{
this.opcode = opcode;
this.CanThrow = true;
}
public OpCode OpCode { get { return opcode; } }
/// <summary>
/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
/// </summary>
public bool IsMoveInstruction { get; private set; }
/// <summary>
/// Specifies whether this opcode is capable of throwing exceptions.
/// </summary>
public bool CanThrow { get; private set; }
}
}

169
ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
sealed class SimplifyByRefCalls
{
public static bool MakeByRefCallsSimple(SsaForm ssaForm)
{
SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm);
foreach (SsaBlock block in ssaForm.Blocks) {
for (int i = 0; i < block.Instructions.Count; i++) {
SsaInstruction inst = block.Instructions[i];
if (inst.Instruction != null) {
switch (inst.Instruction.OpCode.Code) {
case Code.Call:
case Code.Callvirt:
instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand);
break;
case Code.Initobj:
instance.MakeInitObjCallSimple(block, ref i);
break;
case Code.Ldfld:
instance.MakeLoadFieldCallSimple(block, ref i);
break;
}
}
}
}
instance.RemoveRedundantInstructions();
if (instance.couldSimplifySomething)
ssaForm.ComputeVariableUsage();
return instance.couldSimplifySomething;
}
readonly SsaForm ssaForm;
bool couldSimplifySomething;
// the list of ldloca instructions we will remove
readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>();
private SimplifyByRefCalls(SsaForm ssaForm)
{
this.ssaForm = ssaForm;
}
void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
for (int i = 0; i < inst.Operands.Length; i++) {
SsaVariable operand = inst.Operands[i];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// address is used for this method call only
Instruction loadAddressInstruction = operand.Definition.Instruction;
// find target parameter type:
bool isOut;
if (i == 0 && targetMethod.HasThis) {
isOut = false;
} else {
ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)];
isOut = parameter.IsOut;
}
SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction);
// insert "Prepare" instruction on front
SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall;
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode));
// insert "WriteAfterByRefOrOutCall" instruction after call
block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction(
block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
// (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
}
SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction)
{
if (loadAddressInstruction.OpCode == OpCodes.Ldloca) {
return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand);
} else {
Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga);
return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand);
}
}
static bool IsLoadAddress(SsaInstruction inst)
{
return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga);
}
void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// replace instruction with special "InitObj" instruction
block.Instructions[instructionIndexInBlock] = new SsaInstruction(
inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null,
specialOpCode: SpecialOpCode.InitObj,
typeOperand: (TypeReference)inst.Instruction.Operand);
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// insert special "PrepareForFieldAccess" instruction in front
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
inst.ParentBlock, null, operand,
new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) },
specialOpCode: SpecialOpCode.PrepareForFieldAccess));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void RemoveRedundantInstructions()
{
foreach (SsaInstruction inst in redundantLoadAddressInstructions) {
inst.ParentBlock.Instructions.Remove(inst);
}
}
}
}

50
ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public sealed class SsaBlock
{
public readonly List<SsaBlock> Successors = new List<SsaBlock>();
public readonly List<SsaBlock> Predecessors = new List<SsaBlock>();
public readonly ControlFlowNodeType NodeType;
public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>();
public readonly int BlockIndex;
internal SsaBlock(ControlFlowNode node)
{
this.NodeType = node.NodeType;
this.BlockIndex = node.BlockIndex;
}
public override string ToString()
{
StringWriter writer = new StringWriter();
writer.Write("Block #{0} ({1})", BlockIndex, NodeType);
foreach (SsaInstruction inst in Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
}
}

158
ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public sealed class SsaForm
{
readonly SsaVariable[] parameters;
readonly SsaVariable[] locals;
public readonly ReadOnlyCollection<SsaVariable> OriginalVariables;
public readonly ReadOnlyCollection<SsaBlock> Blocks;
readonly bool methodHasThis;
public SsaBlock EntryPoint {
get { return this.Blocks[0]; }
}
public SsaBlock RegularExit {
get { return this.Blocks[1]; }
}
public SsaBlock ExceptionalExit {
get { return this.Blocks[2]; }
}
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis)
{
this.parameters = parameters;
this.locals = locals;
this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks);
this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList());
this.methodHasThis = methodHasThis;
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
for (int i = 0; i < this.OriginalVariables.Count; i++) {
this.OriginalVariables[i].OriginalVariableIndex = i;
}
}
public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = b => b.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaBlock block in this.Blocks) {
graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" });
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaBlock s in block.Successors) {
graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex));
}
}
return graph;
}
public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = v => v.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaVariable v in this.AllVariables) {
graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) });
}
int instructionIndex = 0;
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Operands.Length == 0 && inst.Target == null)
continue;
string id = "instruction" + (++instructionIndex);
graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" });
foreach (SsaVariable op in inst.Operands)
graph.AddEdge(new GraphVizEdge(op.Name, id));
if (inst.Target != null)
graph.AddEdge(new GraphVizEdge(id, inst.Target.Name));
}
}
return graph;
}
public SsaVariable GetOriginalVariable(ParameterReference parameter)
{
if (methodHasThis)
return parameters[parameter.Index + 1];
else
return parameters[parameter.Index];
}
public SsaVariable GetOriginalVariable(VariableReference variable)
{
return locals[variable.Index];
}
#region ComputeVariableUsage
public void ComputeVariableUsage()
{
// clear data from previous runs
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage != null)
v.Usage.Clear();
}
if (inst.Target != null && inst.Target.Usage != null)
inst.Target.Usage.Clear();
}
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage == null)
v.Usage = new List<SsaInstruction>();
v.Usage.Add(inst);
}
if (inst.Target != null && inst.Target.Usage == null)
inst.Target.Usage = new List<SsaInstruction>();
}
}
}
#endregion
public IEnumerable<SsaVariable> AllVariables {
get {
return (
from block in this.Blocks
from instruction in block.Instructions
where instruction.Target != null
select instruction.Target
).Distinct();
}
}
}
}

254
ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs "SsaForm" graph for a CFG.
/// This class transforms the method from stack-based IL to a register-based IL language.
/// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
/// </summary>
public sealed class SsaFormBuilder
{
public static SsaForm Build(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var cfg = ControlFlowGraphBuilder.Build(method.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
var ssa = BuildRegisterIL(method, cfg);
TransformToSsa.Transform(cfg, ssa);
return ssa;
}
public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg)
{
if (method == null)
throw new ArgumentNullException("method");
if (cfg == null)
throw new ArgumentNullException("cfg");
return new SsaFormBuilder(method, cfg).Build();
}
readonly MethodDefinition method;
readonly ControlFlowGraph cfg;
readonly SsaBlock[] blocks; // array index = block index
readonly int[] stackSizeAtBlockStart; // array index = block index
readonly SsaVariable[] parameters; // array index = parameter number
readonly SsaVariable[] locals; // array index = local number
readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
SsaForm ssaForm;
private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg)
{
this.method = method;
this.cfg = cfg;
this.blocks = new SsaBlock[cfg.Nodes.Count];
this.stackSizeAtBlockStart = new int[cfg.Nodes.Count];
for (int i = 0; i < stackSizeAtBlockStart.Length; i++) {
stackSizeAtBlockStart[i] = -1;
}
stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0;
this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)];
if (method.HasThis)
parameters[0] = new SsaVariable(method.Body.ThisParameter);
for (int i = 0; i < method.Parameters.Count; i++)
parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]);
this.locals = new SsaVariable[method.Body.Variables.Count];
for (int i = 0; i < locals.Length; i++)
locals[i] = new SsaVariable(method.Body.Variables[i]);
this.stackLocations = new SsaVariable[method.Body.MaxStackSize];
for (int i = 0; i < stackLocations.Length; i++) {
stackLocations[i] = new SsaVariable(i);
}
}
internal SsaForm Build()
{
CreateGraphStructure();
this.ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis);
CreateInstructions(cfg.EntryPoint.BlockIndex);
CreateSpecialInstructions();
return ssaForm;
}
void CreateGraphStructure()
{
for (int i = 0; i < blocks.Length; i++) {
blocks[i] = new SsaBlock(cfg.Nodes[i]);
}
for (int i = 0; i < blocks.Length; i++) {
foreach (ControlFlowNode node in cfg.Nodes[i].Successors) {
blocks[i].Successors.Add(blocks[node.BlockIndex]);
blocks[node.BlockIndex].Predecessors.Add(blocks[i]);
}
}
}
void CreateInstructions(int blockIndex)
{
ControlFlowNode cfgNode = cfg.Nodes[blockIndex];
SsaBlock block = blocks[blockIndex];
int stackSize = stackSizeAtBlockStart[blockIndex];
Debug.Assert(stackSize >= 0);
List<Instruction> prefixes = new List<Instruction>();
foreach (Instruction inst in cfgNode.Instructions) {
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
prefixes.Add(inst);
continue;
}
int popCount = inst.GetPopDelta(method, stackSize);
stackSize -= popCount;
if (stackSize < 0)
throw new InvalidProgramException("IL stack underflow");
int pushCount = inst.GetPushDelta();
if (stackSize + pushCount > stackLocations.Length)
throw new InvalidProgramException("IL stack overflow");
SsaVariable target;
SsaVariable[] operands;
DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands);
Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null;
prefixes.Clear();
// ignore NOP instructions
if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) {
block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray));
}
stackSize += pushCount;
}
foreach (ControlFlowEdge edge in cfgNode.Outgoing) {
int newStackSize;
switch (edge.Type) {
case JumpType.Normal:
newStackSize = stackSize;
break;
case JumpType.MutualProtection:
case JumpType.JumpToExceptionHandler:
switch (edge.Target.NodeType) {
case ControlFlowNodeType.FinallyOrFaultHandler:
newStackSize = 0;
break;
case ControlFlowNodeType.ExceptionalExit:
case ControlFlowNodeType.CatchHandler:
newStackSize = 1;
break;
default:
throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType);
}
break;
default:
throw new NotSupportedException("unsupported jump type: " + edge.Type);
}
int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex];
if (nextStackSize == -1) {
stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize;
CreateInstructions(edge.Target.BlockIndex);
} else if (nextStackSize != newStackSize) {
throw new InvalidProgramException("Stack size doesn't match");
}
}
}
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands)
{
switch (inst.OpCode.Code) {
case Code.Ldarg:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Starg:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand);
break;
case Code.Ldloc:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Stloc:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand);
break;
case Code.Dup:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = stackLocations[stackSize + 1];
break;
default:
operands = new SsaVariable[popCount];
for (int i = 0; i < popCount; i++) {
operands[i] = stackLocations[stackSize + i];
}
switch (pushCount) {
case 0:
target = null;
break;
case 1:
target = stackLocations[stackSize];
break;
default:
throw new NotSupportedException("unsupported pushCount=" + pushCount);
}
break;
}
}
void CreateSpecialInstructions()
{
// Everything needs an initial write for the SSA transformation to work correctly.
foreach (SsaVariable v in parameters) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter));
}
foreach (SsaVariable v in locals) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaVariable v in stackLocations) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaBlock b in blocks) {
if (b.NodeType == ControlFlowNodeType.CatchHandler) {
b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null,
specialOpCode: SpecialOpCode.Exception,
typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType));
}
}
}
}
}

190
ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs

@ -0,0 +1,190 @@ @@ -0,0 +1,190 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum SpecialOpCode
{
/// <summary>
/// No special op code: SsaInstruction has a normal IL instruction
/// </summary>
None,
/// <summary>
/// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
/// </summary>
Phi,
/// <summary>
/// Variable is read from before passing it by ref.
/// This instruction constructs a managed reference to the variable.
/// </summary>
PrepareByRefCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The variable is not really read from.
/// </summary>
PrepareByOutCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The reference is used for a field access on a value type.
/// </summary>
PrepareForFieldAccess,
/// <summary>
/// Variable is written to after passing it by ref or out.
/// </summary>
WriteAfterByRefOrOutCall,
/// <summary>
/// Variable is not initialized.
/// </summary>
Uninitialized,
/// <summary>
/// Value is passed in as parameter
/// </summary>
Parameter,
/// <summary>
/// Value is a caught exception.
/// TypeOperand is set to the exception type.
/// </summary>
Exception,
/// <summary>
/// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
/// but assigns to the target variable.
/// TypeOperand is set to the type being created.
/// </summary>
InitObj
}
public sealed class SsaInstruction
{
public readonly SsaBlock ParentBlock;
public readonly SpecialOpCode SpecialOpCode;
/// <summary>
/// The original IL instruction.
/// May be null for "invented" instructions (SpecialOpCode != None).
/// </summary>
public readonly Instruction Instruction;
/// <summary>
/// Prefixes in front of the IL instruction.
/// </summary>
public readonly Instruction[] Prefixes;
/// <summary>
/// Gets the type operand. This is used only in combination with some special opcodes.
/// </summary>
public readonly TypeReference TypeOperand;
public SsaVariable Target;
public SsaVariable[] Operands;
static readonly SsaVariable[] emptyVariableArray = {};
static readonly Instruction[] emptyInstructionArray = {};
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands,
Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None,
TypeReference typeOperand = null)
{
this.ParentBlock = parentBlock;
this.Instruction = instruction;
this.Prefixes = prefixes ?? emptyInstructionArray;
this.Target = target;
this.Operands = operands ?? emptyVariableArray;
this.SpecialOpCode = specialOpCode;
this.TypeOperand = typeOperand;
Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj));
}
/// <summary>
/// Gets whether this instruction is a simple assignment from one variable to another.
/// </summary>
public bool IsMoveInstruction {
get {
return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction;
}
}
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar)
{
for (int i = 0; i < this.Operands.Length; i++) {
if (this.Operands[i] == oldVar)
this.Operands[i] = newVar;
}
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(w);
return w.ToString();
}
public void WriteTo(TextWriter writer)
{
foreach (Instruction prefix in this.Prefixes) {
prefix.WriteTo(writer);
writer.WriteLine();
}
if (Instruction != null && Instruction.Offset >= 0) {
writer.Write(CecilExtensions.OffsetToString(Instruction.Offset));
writer.Write(": ");
}
if (Target != null) {
writer.Write(Target.ToString());
writer.Write(" = ");
}
if (IsMoveInstruction) {
writer.Write(Operands[0].ToString());
if (Instruction != null) {
writer.Write(" (" + Instruction.OpCode.Name + ")");
}
} else {
if (Instruction == null) {
writer.Write(SpecialOpCode.ToString());
} else {
writer.Write(Instruction.OpCode.Name);
if(null != Instruction.Operand) {
writer.Write(' ');
writer.Write(CecilExtensions.OperandToString(Instruction.Operand));
writer.Write(' ');
}
}
if (TypeOperand != null) {
writer.Write(' ');
writer.Write(TypeOperand.ToString());
writer.Write(' ');
}
if (Operands.Length > 0) {
writer.Write('(');
for (int i = 0; i < Operands.Length; i++) {
if (i > 0)
writer.Write(", ");
writer.Write(Operands[i].ToString());
}
writer.Write(')');
}
}
}
}
}

135
ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs

@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
static class SsaOptimization
{
public static void Optimize(SsaForm ssaForm)
{
DirectlyStoreToVariables(ssaForm);
SimpleCopyPropagation(ssaForm);
RemoveDeadAssignments(ssaForm);
}
/// <summary>
/// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
/// we optimize this to directly store in the target location.
/// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
/// after locals instead of keeping the temps as using only the simple copy propagation would do.
/// </summary>
public static void DirectlyStoreToVariables(SsaForm ssaForm)
{
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) {
SsaVariable target = inst.Target;
SsaVariable temp = inst.Operands[0];
if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) {
temp.Definition.Target = target;
return true;
}
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true)
{
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) {
if (inst.Target.IsStackLocation || !onlyForStackLocations) {
// replace all uses of 'target' with 'operands[0]'.
foreach (SsaInstruction useInstruction in inst.Target.Usage) {
useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]);
}
}
}
}
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void RemoveDeadAssignments(SsaForm ssaForm)
{
HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>();
// find variables that are used directly
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (!CanRemoveAsDeadCode(inst)) {
if (inst.Target != null)
liveVariables.Add(inst.Target);
foreach (SsaVariable op in inst.Operands) {
liveVariables.Add(op);
}
}
}
}
Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables);
// find variables that are used indirectly
while (queue.Count > 0) {
SsaVariable v = queue.Dequeue();
if (v.IsSingleAssignment) {
foreach (SsaVariable op in v.Definition.Operands) {
if (liveVariables.Add(op))
queue.Enqueue(op);
}
}
}
// remove assignments to all unused variables
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Target != null && !liveVariables.Contains(inst.Target)) {
Debug.Assert(inst.Target.IsSingleAssignment);
return true;
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
static bool CanRemoveAsDeadCode(SsaInstruction inst)
{
if (inst.Target != null && !inst.Target.IsSingleAssignment)
return false;
switch (inst.SpecialOpCode) {
case SpecialOpCode.Phi:
case SpecialOpCode.Exception:
case SpecialOpCode.Parameter:
case SpecialOpCode.Uninitialized:
return true;
case SpecialOpCode.None:
return inst.IsMoveInstruction;
default:
return false;
}
}
}
}

85
ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public sealed class SsaVariable
{
public int OriginalVariableIndex;
public readonly string Name;
public readonly bool IsStackLocation;
public readonly ParameterDefinition Parameter;
public readonly VariableDefinition Variable;
public SsaVariable(ParameterDefinition p)
{
this.Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name;
this.Parameter = p;
}
public SsaVariable(VariableDefinition v)
{
this.Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name;
this.Variable = v;
}
public SsaVariable(int stackLocation)
{
this.Name = "stack" + stackLocation;
this.IsStackLocation = true;
}
public SsaVariable(SsaVariable original, string newName)
{
this.Name = newName;
this.IsStackLocation = original.IsStackLocation;
this.OriginalVariableIndex = original.OriginalVariableIndex;
this.Parameter = original.Parameter;
this.Variable = original.Variable;
}
public override string ToString()
{
return Name;
}
/// <summary>
/// Gets whether this variable has only a single assignment.
/// This field is initialized in TransformToSsa step.
/// </summary>
public bool IsSingleAssignment;
/// <summary>
/// Gets the instruction defining the variable.
/// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
/// </summary>
public SsaInstruction Definition;
/// <summary>
/// Gets the places where a variable is used.
/// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
/// </summary>
public List<SsaInstruction> Usage;
}
}

254
ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Convers a method to static single assignment form.
/// </summary>
sealed class TransformToSsa
{
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true)
{
TransformToSsa transform = new TransformToSsa(cfg, ssa);
transform.ConvertVariablesToSsa();
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) {
transform.ConvertVariablesToSsa();
}
if (optimize)
SsaOptimization.Optimize(ssa);
}
readonly ControlFlowGraph cfg;
readonly SsaForm ssaForm;
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
{
this.cfg = cfg;
this.ssaForm = ssaForm;
this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
this.addressTaken = new bool[ssaForm.OriginalVariables.Count];
}
#region CollectInformationAboutOriginalVariableUse
void CollectInformationAboutOriginalVariableUse()
{
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length);
for (int i = 0; i < writeToOriginalVariables.Length; i++) {
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i);
addressTaken[i] = false;
// writeToOriginalVariables is only used when placing phi functions
// we don't need to do that anymore for variables that are already in SSA form
if (ssaForm.OriginalVariables[i].IsSingleAssignment)
writeToOriginalVariables[i] = null;
else
writeToOriginalVariables[i] = new List<SsaInstruction>();
}
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Target != null ) {
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex];
if (list != null)
list.Add(inst);
}
if (inst.Instruction != null) {
if (inst.Instruction.OpCode == OpCodes.Ldloca) {
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) {
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
}
}
}
}
}
#endregion
#region ConvertToSsa
void ConvertVariablesToSsa()
{
CollectInformationAboutOriginalVariableUse();
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count];
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) {
PlacePhiFunctions(variable);
processVariable[variable.OriginalVariableIndex] = true;
}
}
RenameVariables(processVariable);
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!addressTaken[variable.OriginalVariableIndex]) {
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null);
}
}
ssaForm.ComputeVariableUsage();
}
#endregion
#region PlacePhiFunctions
void PlacePhiFunctions(SsaVariable variable)
{
cfg.ResetVisited();
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>();
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>();
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) {
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex];
if (!cfgNode.Visited) {
cfgNode.Visited = true;
worklist.Enqueue(cfgNode);
}
}
while (worklist.Count > 0) {
ControlFlowNode cfgNode = worklist.Dequeue();
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) {
// we don't need phi functions in the exit node
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit)
continue;
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex];
if (blocksWithPhi.Add(y)) {
// add a phi instruction in y
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray();
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi));
if (!dfNode.Visited) {
dfNode.Visited = true;
worklist.Enqueue(dfNode);
}
}
}
}
}
#endregion
#region RenameVariable
int tempVariableCounter = 1;
void RenameVariables(bool[] processVariable)
{
VariableRenamer r = new VariableRenamer(this, processVariable);
r.Visit(ssaForm.EntryPoint);
}
sealed class VariableRenamer
{
readonly TransformToSsa transform;
readonly ReadOnlyCollection<SsaVariable> inputVariables;
internal readonly Stack<SsaVariable>[] versionStacks;
int[] versionCounters; // specifies for each input variable the next version number
// processVariable = specifies for each input variable whether we should rename it
public VariableRenamer(TransformToSsa transform, bool[] processVariable)
{
this.transform = transform;
this.inputVariables = transform.ssaForm.OriginalVariables;
Debug.Assert(inputVariables.Count == processVariable.Length);
this.versionCounters = new int[inputVariables.Count];
this.versionStacks = new Stack<SsaVariable>[inputVariables.Count];
for (int i = 0; i < versionStacks.Length; i++) {
if (processVariable[i]) {
Debug.Assert(inputVariables[i].IsSingleAssignment == false);
// only create version stacks for the variables that we need to process and that weren't already processed earlier
versionStacks[i] = new Stack<SsaVariable>();
versionStacks[i].Push(inputVariables[i]);
}
}
}
SsaVariable MakeNewVersion(int variableIndex)
{
int versionCounter = ++versionCounters[variableIndex];
SsaVariable x = inputVariables[variableIndex];
if (versionCounter == 1) {
return x;
} else {
if (x.IsStackLocation) {
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++));
} else {
return new SsaVariable(x, x.Name + "_" + versionCounter);
}
}
}
internal void Visit(SsaBlock block)
{
// duplicate top of all stacks
foreach (var stack in versionStacks) {
if (stack != null)
stack.Push(stack.Peek());
}
foreach (SsaInstruction s in block.Instructions) {
// replace all uses of variables being processed with their current version.
if (s.SpecialOpCode != SpecialOpCode.Phi) {
for (int i = 0; i < s.Operands.Length; i++) {
var stack = versionStacks[s.Operands[i].OriginalVariableIndex];
if (stack != null)
s.Operands[i] = stack.Peek();
}
}
// if we're writing to a variable we should process:
if (s.Target != null) {
int targetIndex = s.Target.OriginalVariableIndex;
if (versionStacks[targetIndex] != null) {
s.Target = MakeNewVersion(targetIndex);
s.Target.IsSingleAssignment = true;
s.Target.Definition = s;
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
// so now replace the top element
versionStacks[targetIndex].Pop();
versionStacks[targetIndex].Push(s.Target);
}
}
}
foreach (SsaBlock succ in block.Successors) {
int j = succ.Predecessors.IndexOf(block);
Debug.Assert(j >= 0);
foreach (SsaInstruction f in succ.Instructions) {
if (f.SpecialOpCode == SpecialOpCode.Phi) {
var stack = versionStacks[f.Target.OriginalVariableIndex];
if (stack != null) {
f.Operands[j] = stack.Peek();
}
}
}
}
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren)
Visit(transform.ssaForm.Blocks[child.BlockIndex]);
// restore stacks:
foreach (var stack in versionStacks) {
if (stack != null)
stack.Pop();
}
}
}
#endregion
}
}

196
ICSharpCode.Decompiler/GraphVizGraph.cs

@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// GraphViz graph.
/// </summary>
public sealed class GraphVizGraph
{
List<GraphVizNode> nodes = new List<GraphVizNode>();
List<GraphVizEdge> edges = new List<GraphVizEdge>();
public string rankdir;
public void AddEdge(GraphVizEdge edge)
{
edges.Add(edge);
}
public void AddNode(GraphVizNode node)
{
nodes.Add(node);
}
public void Save(string fileName)
{
using (StreamWriter writer = new StreamWriter(fileName))
Save(writer);
}
static string Escape(string text)
{
if (Regex.IsMatch(text, @"^[\w\d]+$")) {
return text;
} else {
return "\"" + text.Replace("\\", "\\\\").Replace("\r", "").Replace("\n", "\\n").Replace("\"", "\\\"") + "\"";
}
}
static void WriteGraphAttribute(TextWriter writer, string name, string value)
{
if (value != null)
writer.WriteLine("{0}={1};", name, Escape(value));
}
internal static void WriteAttribute(TextWriter writer, string name, double? value, ref bool isFirst)
{
if (value != null) {
WriteAttribute(writer, name, value.Value.ToString(CultureInfo.InvariantCulture), ref isFirst);
}
}
internal static void WriteAttribute(TextWriter writer, string name, bool? value, ref bool isFirst)
{
if (value != null) {
WriteAttribute(writer, name, value.Value ? "true" : "false", ref isFirst);
}
}
internal static void WriteAttribute(TextWriter writer, string name, string value, ref bool isFirst)
{
if (value != null) {
if (isFirst)
isFirst = false;
else
writer.Write(',');
writer.Write("{0}={1}", name, Escape(value));
}
}
public void Save(TextWriter writer)
{
writer.WriteLine("digraph G {");
WriteGraphAttribute(writer, "rankdir", rankdir);
foreach (GraphVizNode node in nodes) {
node.Save(writer);
}
foreach (GraphVizEdge edge in edges) {
edge.Save(writer);
}
writer.WriteLine("}");
}
}
public sealed class GraphVizEdge
{
public readonly string Source, Target;
/// <summary>edge stroke color</summary>
public string color;
/// <summary>use edge to affect node ranking</summary>
public bool? constraint;
public string label;
public string style;
/// <summary>point size of label</summary>
public int? fontsize;
public GraphVizEdge(string source, string target)
{
if (source == null)
throw new ArgumentNullException("source");
if (target == null)
throw new ArgumentNullException("target");
this.Source = source;
this.Target = target;
}
public GraphVizEdge(int source, int target)
{
this.Source = source.ToString(CultureInfo.InvariantCulture);
this.Target = target.ToString(CultureInfo.InvariantCulture);
}
public void Save(TextWriter writer)
{
writer.Write("{0} -> {1} [", Source, Target);
bool isFirst = true;
GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "style", style, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "color", color, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "constraint", constraint, ref isFirst);
writer.WriteLine("];");
}
}
public sealed class GraphVizNode
{
public readonly string ID;
public string label;
public string labelloc;
/// <summary>point size of label</summary>
public int? fontsize;
/// <summary>minimum height in inches</summary>
public double? height;
/// <summary>space around label</summary>
public string margin;
/// <summary>node shape</summary>
public string shape;
public GraphVizNode(string id)
{
if (id == null)
throw new ArgumentNullException("id");
this.ID = id;
}
public GraphVizNode(int id)
{
this.ID = id.ToString(CultureInfo.InvariantCulture);
}
public void Save(TextWriter writer)
{
writer.Write(ID);
writer.Write(" [");
bool isFirst = true;
GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "labelloc", labelloc, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "margin", margin, ref isFirst);
GraphVizGraph.WriteAttribute(writer, "shape", shape, ref isFirst);
writer.WriteLine("];");
}
}
}

71
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -0,0 +1,71 @@ @@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{984CC812-9470-4A13-AFF9-CC44068D666C}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<AppDesignerFolder>Properties</AppDesignerFolder>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>True</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CecilExtensions.cs" />
<Compile Include="FlowAnalysis\ControlFlowEdge.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraph.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\OpCodeInfo.cs" />
<Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" />
<Compile Include="FlowAnalysis\SsaBlock.cs" />
<Compile Include="FlowAnalysis\SsaForm.cs" />
<Compile Include="FlowAnalysis\SsaFormBuilder.cs" />
<Compile Include="FlowAnalysis\SsaInstruction.cs" />
<Compile Include="FlowAnalysis\SsaOptimization.cs" />
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="GraphVizGraph.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="FlowAnalysis" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

31
ICSharpCode.Decompiler/Properties/AssemblyInfo.cs

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
#region Using directives
using System;
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ICSharpCode.Decompiler")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ICSharpCode.Decompiler")]
[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all the values or you can use the default the Revision and
// Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]

10
ILSpy.sln

@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mo @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "Mono.Cecil\Mo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
@ -46,5 +48,13 @@ Global @@ -46,5 +48,13 @@ Global
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|x86.ActiveCfg = Release|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.Build.0 = Release|Any CPU
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|x86.Build.0 = Debug|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|x86.ActiveCfg = Debug|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.Build.0 = Debug|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.ActiveCfg = Debug|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.Build.0 = Release|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.ActiveCfg = Release|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.Build.0 = Release|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.ActiveCfg = Release|x86
EndGlobalSection
EndGlobal

192
ILSpy/Disassembler/CecilExtensions.cs → ILSpy/Disassembler/DisassemblerHelpers.cs

@ -1,20 +1,31 @@ @@ -1,20 +1,31 @@
// 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)
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.Decompiler;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.ILSpy.Disassembler
{
static class CecilExtensions
static class DisassemblerHelpers
{
#region Debug output(ToString helpers)
public static string OffsetToString(int offset)
{
return string.Format("IL_{0:x4}", offset);
}
public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer)
{
writer.Write("Try IL_{0:x4}-IL_{1:x4} ", exceptionHandler.TryStart.Offset, exceptionHandler.TryEnd.Offset);
@ -27,7 +38,7 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -27,7 +38,7 @@ namespace ICSharpCode.ILSpy.Disassembler
public static void WriteTo(this Instruction instruction, ITextOutput writer)
{
writer.Write(OffsetToString(instruction.Offset));
writer.Write(CecilExtensions.OffsetToString(instruction.Offset));
writer.Write(": ");
writer.Write(instruction.OpCode.Name);
if(null != instruction.Operand) {
@ -36,50 +47,12 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -36,50 +47,12 @@ namespace ICSharpCode.ILSpy.Disassembler
}
}
public static void WriteOperand(ITextOutput writer, object operand)
{
if(null == operand) throw new ArgumentNullException("operand");
Instruction targetInstruction = operand as Instruction;
if(null != targetInstruction) {
writer.Write(OffsetToString(targetInstruction.Offset));
return;
}
Instruction [] targetInstructions = operand as Instruction [];
if(null != targetInstructions) {
WriteLabelList(writer, targetInstructions);
return;
}
VariableReference variableRef = operand as VariableReference;
if(null != variableRef) {
writer.Write(variableRef.Index.ToString());
return;
}
MethodReference methodRef = operand as MethodReference;
if(null != methodRef) {
WriteMethodReference(writer, methodRef);
return;
}
string s = operand as string;
if(null != s) {
writer.Write("\"" + s + "\"");
return;
}
s = ToInvariantCultureString(operand);
writer.Write(s);
}
static void WriteLabelList(ITextOutput writer, Instruction[] instructions)
{
writer.Write("(");
for(int i = 0; i < instructions.Length; i++) {
if(i != 0) writer.Write(", ");
writer.Write(OffsetToString(instructions [i].Offset));
writer.Write(CecilExtensions.OffsetToString(instructions [i].Offset));
}
writer.Write(")");
}
@ -122,100 +95,43 @@ namespace ICSharpCode.ILSpy.Disassembler @@ -122,100 +95,43 @@ namespace ICSharpCode.ILSpy.Disassembler
}
return typeName;
}
#endregion
#region GetPushDelta / GetPopDelta
public static int GetPushDelta(this Instruction instruction)
public static void WriteOperand(ITextOutput writer, object operand)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPush) {
case StackBehaviour.Push0:
return 0;
case StackBehaviour.Push1:
case StackBehaviour.Pushi:
case StackBehaviour.Pushi8:
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
return 1;
case StackBehaviour.Push1_push1:
return 2;
case StackBehaviour.Varpush:
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
return IsVoid (method.ReturnType) ? 0 : 1;
if(null == operand) throw new ArgumentNullException("operand");
Instruction targetInstruction = operand as Instruction;
if(null != targetInstruction) {
writer.Write(CecilExtensions.OffsetToString(targetInstruction.Offset));
return;
}
throw new NotSupportedException ();
}
public static int GetPopDelta(this Instruction instruction, MethodDefinition current, int currentStackSize)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPop) {
case StackBehaviour.Pop0:
return 0;
case StackBehaviour.Popi:
case StackBehaviour.Popref:
case StackBehaviour.Pop1:
return 1;
case StackBehaviour.Pop1_pop1:
case StackBehaviour.Popi_pop1:
case StackBehaviour.Popi_popi:
case StackBehaviour.Popi_popi8:
case StackBehaviour.Popi_popr4:
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
return 2;
case StackBehaviour.Popi_popi_popi:
case StackBehaviour.Popref_popi_popi:
case StackBehaviour.Popref_popi_popi8:
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
return 3;
case StackBehaviour.PopAll:
return currentStackSize;
case StackBehaviour.Varpop:
if (code == OpCodes.Ret)
return IsVoid (current.ReturnType) ? 0 : 1;
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
int count = method.HasParameters ? method.Parameters.Count : 0;
if (method.HasThis && code != OpCodes.Newobj)
++count;
return count;
Instruction [] targetInstructions = operand as Instruction [];
if(null != targetInstructions) {
WriteLabelList(writer, targetInstructions);
return;
}
throw new NotSupportedException ();
}
public static bool IsVoid(this TypeReference type)
{
return type.FullName == "System.Void" && !(type is TypeSpecification);
}
public static bool IsValueTypeOrVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
if (type is ArrayType)
return false;
return type.IsValueType || type.IsVoid();
VariableReference variableRef = operand as VariableReference;
if(null != variableRef) {
writer.Write(variableRef.Index.ToString());
return;
}
MethodReference methodRef = operand as MethodReference;
if(null != methodRef) {
WriteMethodReference(writer, methodRef);
return;
}
string s = operand as string;
if(null != s) {
writer.Write("\"" + s + "\"");
return;
}
s = ToInvariantCultureString(operand);
writer.Write(s);
}
#endregion

6
ILSpy/ILSpy.csproj

@ -75,7 +75,7 @@ @@ -75,7 +75,7 @@
<Compile Include="BaseTypesTreeNode.cs" />
<Compile Include="CueBannerService.cs" />
<Compile Include="Decompiler\CSharpLanguage.cs" />
<Compile Include="Disassembler\CecilExtensions.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />
<Compile Include="Disassembler\ILLanguage.cs" />
<Compile Include="EventTreeNode.cs" />
<Compile Include="ExtensionMethods.cs" />
@ -149,6 +149,10 @@ @@ -149,6 +149,10 @@
<Folder Include="themes" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
<ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>

1
ILSpy/MainWindow.xaml

@ -44,6 +44,7 @@ @@ -44,6 +44,7 @@
</MenuItem>
</Menu>
<ToolBar
Name="toolBar"
DockPanel.Dock="Top">
<ToolBar.Resources>
<!-- Make images transparent if menu command is disabled -->

44
ILSpy/MainWindow.xaml.cs

@ -24,9 +24,11 @@ using System.Reflection; @@ -24,9 +24,11 @@ using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.TreeView;
using Microsoft.Win32;
using Mono.Cecil.Rocks;
namespace ICSharpCode.ILSpy
{
@ -87,7 +89,47 @@ namespace ICSharpCode.ILSpy @@ -87,7 +89,47 @@ namespace ICSharpCode.ILSpy
for (int i = 1; i < args.Length; i++) {
assemblyList.OpenAssembly(args[i]);
}
#if DEBUG
toolBar.Items.Add(new Separator());
Button cfg = new Button() { Content = "CFG" };
cfg.Click += new RoutedEventHandler(cfg_Click);
toolBar.Items.Add(cfg);
Button ssa = new Button() { Content = "SSA" };
ssa.Click += new RoutedEventHandler(ssa_Click);
toolBar.Items.Add(ssa);
#endif
}
#region Debugging CFG
#if DEBUG
void cfg_Click(object sender, RoutedEventArgs e)
{
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
node.MethodDefinition.Body.SimplifyMacros();
ShowGraph(node.MethodDefinition.Name + "-cfg", ControlFlowGraphBuilder.Build(node.MethodDefinition.Body).ExportGraph());
}
}
void ssa_Click(object sender, RoutedEventArgs e)
{
MethodTreeNode node = treeView.SelectedItem as MethodTreeNode;
if (node != null && node.MethodDefinition.HasBody) {
node.MethodDefinition.Body.SimplifyMacros();
ShowGraph(node.MethodDefinition.Name + "-cfg", SsaFormBuilder.Build(node.MethodDefinition).ExportBlockGraph());
}
}
void ShowGraph(string name, GraphVizGraph graph)
{
string fileName = Path.Combine(Path.GetTempPath(), name);
graph.Save(fileName + ".gv");
Process.Start("dot", "\"" + fileName + ".gv\" -Tpng -o \"" + fileName + ".png\"").WaitForExit();
Process.Start(fileName + ".png");
}
#endif
#endregion
void OpenCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{

4
ILSpy/MethodTreeNode.cs

@ -30,6 +30,10 @@ namespace ICSharpCode.ILSpy @@ -30,6 +30,10 @@ namespace ICSharpCode.ILSpy
{
MethodDefinition method;
public MethodDefinition MethodDefinition {
get { return method; }
}
public MethodTreeNode(MethodDefinition method)
{
if (method == null)

Loading…
Cancel
Save