mirror of https://github.com/icsharpcode/ILSpy.git
23 changed files with 2943 additions and 140 deletions
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
|
} |
||||||
|
} |
||||||
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; } |
||||||
|
} |
||||||
|
} |
||||||
@ -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); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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(')'); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
@ -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
|
||||||
|
} |
||||||
|
} |
||||||
@ -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("];"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -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> |
||||||
@ -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.*")] |
||||||
Loading…
Reference in new issue