mirror of https://github.com/icsharpcode/ILSpy.git
23 changed files with 2943 additions and 140 deletions
@ -0,0 +1,185 @@
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler |
||||
{ |
||||
/// <summary>
|
||||
/// Cecil helper methods.
|
||||
/// </summary>
|
||||
public static class CecilExtensions |
||||
{ |
||||
#region GetPushDelta / GetPopDelta
|
||||
public static int GetPushDelta(this Instruction instruction) |
||||
{ |
||||
OpCode code = instruction.OpCode; |
||||
switch (code.StackBehaviourPush) { |
||||
case StackBehaviour.Push0: |
||||
return 0; |
||||
|
||||
case StackBehaviour.Push1: |
||||
case StackBehaviour.Pushi: |
||||
case StackBehaviour.Pushi8: |
||||
case StackBehaviour.Pushr4: |
||||
case StackBehaviour.Pushr8: |
||||
case StackBehaviour.Pushref: |
||||
return 1; |
||||
|
||||
case StackBehaviour.Push1_push1: |
||||
return 2; |
||||
|
||||
case StackBehaviour.Varpush: |
||||
if (code.FlowControl != FlowControl.Call) |
||||
break; |
||||
|
||||
IMethodSignature method = (IMethodSignature) instruction.Operand; |
||||
return IsVoid (method.ReturnType) ? 0 : 1; |
||||
} |
||||
|
||||
throw new NotSupportedException (); |
||||
} |
||||
|
||||
public static int GetPopDelta(this Instruction instruction, MethodDefinition current, int currentStackSize) |
||||
{ |
||||
OpCode code = instruction.OpCode; |
||||
switch (code.StackBehaviourPop) { |
||||
case StackBehaviour.Pop0: |
||||
return 0; |
||||
case StackBehaviour.Popi: |
||||
case StackBehaviour.Popref: |
||||
case StackBehaviour.Pop1: |
||||
return 1; |
||||
|
||||
case StackBehaviour.Pop1_pop1: |
||||
case StackBehaviour.Popi_pop1: |
||||
case StackBehaviour.Popi_popi: |
||||
case StackBehaviour.Popi_popi8: |
||||
case StackBehaviour.Popi_popr4: |
||||
case StackBehaviour.Popi_popr8: |
||||
case StackBehaviour.Popref_pop1: |
||||
case StackBehaviour.Popref_popi: |
||||
return 2; |
||||
|
||||
case StackBehaviour.Popi_popi_popi: |
||||
case StackBehaviour.Popref_popi_popi: |
||||
case StackBehaviour.Popref_popi_popi8: |
||||
case StackBehaviour.Popref_popi_popr4: |
||||
case StackBehaviour.Popref_popi_popr8: |
||||
case StackBehaviour.Popref_popi_popref: |
||||
return 3; |
||||
|
||||
case StackBehaviour.PopAll: |
||||
return currentStackSize; |
||||
|
||||
case StackBehaviour.Varpop: |
||||
if (code == OpCodes.Ret) |
||||
return IsVoid (current.ReturnType) ? 0 : 1; |
||||
|
||||
if (code.FlowControl != FlowControl.Call) |
||||
break; |
||||
|
||||
IMethodSignature method = (IMethodSignature) instruction.Operand; |
||||
int count = method.HasParameters ? method.Parameters.Count : 0; |
||||
if (method.HasThis && code != OpCodes.Newobj) |
||||
++count; |
||||
|
||||
return count; |
||||
} |
||||
|
||||
throw new NotSupportedException (); |
||||
} |
||||
|
||||
public static bool IsVoid(this TypeReference type) |
||||
{ |
||||
return type.FullName == "System.Void" && !(type is TypeSpecification); |
||||
} |
||||
|
||||
public static bool IsValueTypeOrVoid(this TypeReference type) |
||||
{ |
||||
while (type is OptionalModifierType || type is RequiredModifierType) |
||||
type = ((TypeSpecification)type).ElementType; |
||||
if (type is ArrayType) |
||||
return false; |
||||
return type.IsValueType || type.IsVoid(); |
||||
} |
||||
#endregion
|
||||
|
||||
public static void WriteTo(this Instruction instruction, TextWriter writer) |
||||
{ |
||||
writer.Write(OffsetToString(instruction.Offset)); |
||||
writer.Write(": "); |
||||
writer.Write(instruction.OpCode.Name); |
||||
if(null != instruction.Operand) { |
||||
writer.Write(' '); |
||||
writer.Write(OperandToString(instruction.Operand)); |
||||
} |
||||
} |
||||
|
||||
public static void WriteTo(this ExceptionHandler exceptionHandler, TextWriter writer) |
||||
{ |
||||
writer.Write("Try IL_{0:x4}-IL_{1:x4} ", exceptionHandler.TryStart.Offset, exceptionHandler.TryEnd.Offset); |
||||
writer.Write(exceptionHandler.HandlerType.ToString()); |
||||
if (exceptionHandler.FilterStart != null) { |
||||
writer.Write(" IL_{0:x4}-IL_{1:x4} handler ", exceptionHandler.FilterStart.Offset, exceptionHandler.FilterEnd.Offset); |
||||
} |
||||
writer.Write(" IL_{0:x4}-IL_{1:x4} ", exceptionHandler.HandlerStart.Offset, exceptionHandler.HandlerEnd.Offset); |
||||
} |
||||
|
||||
public static string OffsetToString(int offset) |
||||
{ |
||||
return string.Format("IL_{0:x4}", offset); |
||||
} |
||||
|
||||
public static string OperandToString(object operand) |
||||
{ |
||||
if(null == operand) throw new ArgumentNullException("operand"); |
||||
|
||||
Instruction targetInstruction = operand as Instruction; |
||||
if(null != targetInstruction) { |
||||
return OffsetToString(targetInstruction.Offset); |
||||
} |
||||
|
||||
Instruction [] targetInstructions = operand as Instruction []; |
||||
if(null != targetInstructions) { |
||||
return string.Join(", ", targetInstructions.Select(i => OffsetToString(i.Offset))); |
||||
} |
||||
|
||||
VariableReference variableRef = operand as VariableReference; |
||||
if(null != variableRef) { |
||||
return variableRef.Index.ToString(); |
||||
} |
||||
|
||||
MethodReference methodRef = operand as MethodReference; |
||||
if(null != methodRef) { |
||||
return methodRef.ToString(); |
||||
} |
||||
|
||||
string s = operand as string; |
||||
if(null != s) { |
||||
return "\"" + s + "\""; |
||||
} |
||||
|
||||
return operand.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public enum JumpType |
||||
{ |
||||
Normal, |
||||
JumpToExceptionHandler, |
||||
LeaveTry, |
||||
MutualProtection |
||||
} |
||||
|
||||
public sealed class ControlFlowEdge |
||||
{ |
||||
public readonly ControlFlowNode Source; |
||||
public readonly ControlFlowNode Target; |
||||
public readonly JumpType Type; |
||||
|
||||
public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type) |
||||
{ |
||||
this.Source = source; |
||||
this.Target = target; |
||||
this.Type = type; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
switch (Type) { |
||||
case JumpType.Normal: |
||||
return "#" + Target.BlockIndex; |
||||
case JumpType.JumpToExceptionHandler: |
||||
return "e:#" + Target.BlockIndex; |
||||
default: |
||||
return Type.ToString() + ":#" + Target.BlockIndex; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,180 @@
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Diagnostics; |
||||
using System.Globalization; |
||||
using System.IO; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
/// <summary>
|
||||
/// Contains the control flow graph.
|
||||
/// </summary>
|
||||
public sealed class ControlFlowGraph |
||||
{ |
||||
readonly ReadOnlyCollection<ControlFlowNode> nodes; |
||||
|
||||
public ControlFlowNode EntryPoint { |
||||
get { return nodes[0]; } |
||||
} |
||||
|
||||
public ControlFlowNode RegularExit { |
||||
get { return nodes[1]; } |
||||
} |
||||
|
||||
public ControlFlowNode ExceptionalExit { |
||||
get { return nodes[2]; } |
||||
} |
||||
|
||||
public ReadOnlyCollection<ControlFlowNode> Nodes { |
||||
get { return nodes; } |
||||
} |
||||
|
||||
internal ControlFlowGraph(ControlFlowNode[] nodes) |
||||
{ |
||||
this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes); |
||||
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); |
||||
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); |
||||
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); |
||||
} |
||||
|
||||
#if DEBUG
|
||||
public GraphVizGraph ExportGraph() |
||||
{ |
||||
GraphVizGraph graph = new GraphVizGraph(); |
||||
foreach (ControlFlowNode node in nodes) { |
||||
graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" }); |
||||
} |
||||
foreach (ControlFlowNode node in nodes) { |
||||
foreach (ControlFlowEdge edge in node.Outgoing) { |
||||
GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex); |
||||
switch (edge.Type) { |
||||
case JumpType.Normal: |
||||
break; |
||||
case JumpType.LeaveTry: |
||||
e.color = "red"; |
||||
break; |
||||
default: |
||||
e.color = "gray"; |
||||
//e.constraint = false;
|
||||
break; |
||||
} |
||||
graph.AddEdge(e); |
||||
} |
||||
if (node.ImmediateDominator != null) { |
||||
graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false }); |
||||
} |
||||
} |
||||
return graph; |
||||
} |
||||
#endif
|
||||
|
||||
internal void ResetVisited() |
||||
{ |
||||
foreach (ControlFlowNode node in nodes) { |
||||
node.Visited = false; |
||||
} |
||||
} |
||||
|
||||
public void ComputeDominance() |
||||
{ |
||||
// A Simple, Fast Dominance Algorithm
|
||||
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
|
||||
|
||||
EntryPoint.ImmediateDominator = EntryPoint; |
||||
bool changed = true; |
||||
while (changed) { |
||||
changed = false; |
||||
ResetVisited(); |
||||
|
||||
// for all nodes b except the entry point
|
||||
EntryPoint.TraversePreOrder( |
||||
b => b.Successors, |
||||
b => { |
||||
if (b != EntryPoint) { |
||||
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited); |
||||
// for all other predecessors p of b
|
||||
foreach (ControlFlowNode p in b.Predecessors) { |
||||
if (p != b && p.ImmediateDominator != null) { |
||||
newIdom = FindCommonDominator(p, newIdom); |
||||
} |
||||
} |
||||
if (b.ImmediateDominator != newIdom) { |
||||
b.ImmediateDominator = newIdom; |
||||
changed = true; |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
EntryPoint.ImmediateDominator = null; |
||||
foreach (ControlFlowNode node in nodes) { |
||||
if (node.ImmediateDominator != null) |
||||
node.ImmediateDominator.DominatorTreeChildren.Add(node); |
||||
} |
||||
} |
||||
|
||||
static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2) |
||||
{ |
||||
// Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
|
||||
HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>(); |
||||
while (b1 != null && path1.Add(b1)) |
||||
b1 = b1.ImmediateDominator; |
||||
while (b2 != null) { |
||||
if (path1.Contains(b2)) |
||||
return b2; |
||||
else |
||||
b2 = b2.ImmediateDominator; |
||||
} |
||||
throw new Exception("No common dominator found!"); |
||||
} |
||||
|
||||
public void ComputeDominanceFrontier() |
||||
{ |
||||
ResetVisited(); |
||||
|
||||
EntryPoint.TraversePostOrder( |
||||
b => b.DominatorTreeChildren, |
||||
n => { |
||||
//logger.WriteLine("Calculating dominance frontier for " + n.Name);
|
||||
n.DominanceFrontier = new HashSet<ControlFlowNode>(); |
||||
// DF_local computation
|
||||
foreach (ControlFlowNode succ in n.Successors) { |
||||
if (succ.ImmediateDominator != n) { |
||||
//logger.WriteLine(" local: " + succ.Name);
|
||||
n.DominanceFrontier.Add(succ); |
||||
} |
||||
} |
||||
// DF_up computation
|
||||
foreach (ControlFlowNode child in n.DominatorTreeChildren) { |
||||
foreach (ControlFlowNode p in child.DominanceFrontier) { |
||||
if (p.ImmediateDominator != n) { |
||||
//logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
|
||||
n.DominanceFrontier.Add(p); |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,388 @@
@@ -0,0 +1,388 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public sealed class ControlFlowGraphBuilder |
||||
{ |
||||
public static ControlFlowGraph Build(MethodBody methodBody) |
||||
{ |
||||
return new ControlFlowGraphBuilder(methodBody).Build(); |
||||
} |
||||
|
||||
MethodBody methodBody; |
||||
int[] offsets; // array index = instruction index; value = IL offset
|
||||
bool[] hasIncomingJumps; // array index = instruction index
|
||||
List<ControlFlowNode> nodes = new List<ControlFlowNode>(); |
||||
ControlFlowNode entryPoint; |
||||
ControlFlowNode regularExit ; |
||||
ControlFlowNode exceptionalExit; |
||||
//Stack<> activeExceptionHandlers = new Stack<ExceptionHandler>();
|
||||
|
||||
private ControlFlowGraphBuilder(MethodBody methodBody) |
||||
{ |
||||
this.methodBody = methodBody; |
||||
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray(); |
||||
hasIncomingJumps = new bool[methodBody.Instructions.Count]; |
||||
|
||||
entryPoint = new ControlFlowNode(0, ControlFlowNodeType.EntryPoint); |
||||
nodes.Add(entryPoint); |
||||
regularExit = new ControlFlowNode(1, ControlFlowNodeType.RegularExit); |
||||
nodes.Add(regularExit); |
||||
exceptionalExit = new ControlFlowNode(2, ControlFlowNodeType.ExceptionalExit); |
||||
nodes.Add(exceptionalExit); |
||||
Debug.Assert(nodes.Count == 3); |
||||
} |
||||
|
||||
int GetInstructionIndex(Instruction inst) |
||||
{ |
||||
int index = Array.BinarySearch(offsets, inst.Offset); |
||||
Debug.Assert(index >= 0); |
||||
return index; |
||||
} |
||||
|
||||
public ControlFlowGraph Build() |
||||
{ |
||||
CalculateHasIncomingJumps(); |
||||
CreateNodes(); |
||||
CreateRegularControlFlow(); |
||||
CreateExceptionalControlFlow(); |
||||
CopyFinallyBlocksIntoLeaveEdges(); |
||||
return new ControlFlowGraph(nodes.ToArray()); |
||||
} |
||||
|
||||
void CalculateHasIncomingJumps() |
||||
{ |
||||
foreach (Instruction inst in methodBody.Instructions) { |
||||
if (inst.OpCode.OperandType == OperandType.InlineBrTarget) { |
||||
hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true; |
||||
} else if (inst.OpCode.OperandType == OperandType.InlineSwitch) { |
||||
foreach (Instruction i in (Instruction[])inst.Operand) |
||||
hasIncomingJumps[GetInstructionIndex(i)] = true; |
||||
} |
||||
} |
||||
foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) { |
||||
if (eh.FilterStart != null) { |
||||
hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true; |
||||
} |
||||
hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true; |
||||
} |
||||
} |
||||
|
||||
void CreateNodes() |
||||
{ |
||||
for (int i = 0; i < methodBody.Instructions.Count; i++) { |
||||
Instruction blockStart = methodBody.Instructions[i]; |
||||
// try and see how big we can make that block:
|
||||
for (; i + 1 < methodBody.Instructions.Count; i++) { |
||||
Instruction inst = methodBody.Instructions[i]; |
||||
if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode)) |
||||
break; |
||||
if (hasIncomingJumps[i + 1]) |
||||
break; |
||||
} |
||||
|
||||
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i])); |
||||
} |
||||
foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) { |
||||
if (handler.HandlerType == ExceptionHandlerType.Filter) |
||||
throw new NotSupportedException(); |
||||
ControlFlowNode endFinallyOrFaultNode = null; |
||||
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) { |
||||
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, ControlFlowNodeType.EndFinallyOrFault); |
||||
nodes.Add(endFinallyOrFaultNode); |
||||
} |
||||
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode)); |
||||
} |
||||
} |
||||
|
||||
void CreateRegularControlFlow() |
||||
{ |
||||
CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal); |
||||
foreach (ControlFlowNode node in nodes) { |
||||
if (node.End != null) { |
||||
// create normal edges from one instruction to the next
|
||||
if (!IsUnconditionalBranch(node.End.OpCode)) |
||||
CreateEdge(node, node.End.Next, JumpType.Normal); |
||||
|
||||
// create edges for branch instructions
|
||||
if (node.End.OpCode.OperandType == OperandType.InlineBrTarget) { |
||||
if (node.End.OpCode == OpCodes.Leave) { |
||||
var handlerBlock = FindInnermostHandlerBlock(node.End.Offset); |
||||
if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler) |
||||
CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry); |
||||
else |
||||
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal); |
||||
} else { |
||||
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal); |
||||
} |
||||
} else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) { |
||||
foreach (Instruction i in (Instruction[])node.End.Operand) |
||||
CreateEdge(node, i, JumpType.Normal); |
||||
} |
||||
|
||||
// create edges for return instructions
|
||||
if (node.End.OpCode.FlowControl == FlowControl.Return) { |
||||
switch (node.End.OpCode.Code) { |
||||
case Code.Ret: |
||||
CreateEdge(node, regularExit, JumpType.Normal); |
||||
break; |
||||
case Code.Endfinally: |
||||
ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset); |
||||
if (handlerBlock.EndFinallyOrFaultNode == null) |
||||
throw new InvalidProgramException("Found endfinally in block " + handlerBlock); |
||||
CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal); |
||||
break; |
||||
default: |
||||
throw new NotSupportedException(node.End.OpCode.ToString()); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CreateExceptionalControlFlow() |
||||
{ |
||||
foreach (ControlFlowNode node in nodes) { |
||||
if (node.End != null && CanThrowException(node.End.OpCode)) { |
||||
CreateEdge(node, FindInnermostExceptionHandler(node.End.Offset), JumpType.JumpToExceptionHandler); |
||||
} |
||||
if (node.ExceptionHandler != null) { |
||||
if (node.EndFinallyOrFaultNode != null) { |
||||
// For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
|
||||
// This represents the exception bubbling up after finally block was executed.
|
||||
CreateEdge(node.EndFinallyOrFaultNode, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerEnd.Offset), JumpType.JumpToExceptionHandler); |
||||
} else { |
||||
// For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
|
||||
// This represents the exception bubbling up because it did not match the type of the catch block.
|
||||
CreateEdge(node, FindInnermostExceptionHandler(node.ExceptionHandler.HandlerStart.Offset), JumpType.JumpToExceptionHandler); |
||||
} |
||||
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal); |
||||
} |
||||
} |
||||
|
||||
// now create edges between catch handlers that mutually protect
|
||||
for (int i = 0; i < methodBody.ExceptionHandlers.Count; i++) { |
||||
ExceptionHandler first = methodBody.ExceptionHandlers[i]; |
||||
if (first.HandlerType == ExceptionHandlerType.Catch) { |
||||
for (int j = i + 1; j < methodBody.ExceptionHandlers.Count; j++) { |
||||
ExceptionHandler second = methodBody.ExceptionHandlers[j]; |
||||
if (second.HandlerType == ExceptionHandlerType.Catch && second.TryStart == first.TryStart && second.TryEnd == first.TryEnd) { |
||||
CreateEdge(nodes.Single(n => n.ExceptionHandler == first), nodes.Single(n => n.ExceptionHandler == second), JumpType.MutualProtection); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
ControlFlowNode FindInnermostExceptionHandler(int instructionOffsetInTryBlock) |
||||
{ |
||||
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) { |
||||
if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) { |
||||
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); |
||||
} |
||||
} |
||||
return exceptionalExit; |
||||
} |
||||
|
||||
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset) |
||||
{ |
||||
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) { |
||||
if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset |
||||
|| h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset) |
||||
{ |
||||
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null); |
||||
} |
||||
} |
||||
return exceptionalExit; |
||||
} |
||||
|
||||
void CopyFinallyBlocksIntoLeaveEdges() |
||||
{ |
||||
// We need to process try-finally blocks inside-out.
|
||||
// We'll do that by going through all instructions in reverse order
|
||||
for (int i = nodes.Count - 1; i >= 0; i--) { |
||||
ControlFlowNode node = nodes[i]; |
||||
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) { |
||||
Debug.Assert(node.End.OpCode == OpCodes.Leave); |
||||
|
||||
ControlFlowNode target = node.Outgoing[0].Target; |
||||
// remove the edge
|
||||
target.Incoming.Remove(node.Outgoing[0]); |
||||
node.Outgoing.Clear(); |
||||
|
||||
ControlFlowNode handler = FindInnermostExceptionHandler(node.End.Offset); |
||||
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler); |
||||
|
||||
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target); |
||||
CreateEdge(node, copy, JumpType.Normal); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
|
||||
/// Nodes pointing to the copied node are copied recursively to update those references, too.
|
||||
/// This recursion stops at 'start'. The modified version of start is returned.
|
||||
/// </summary>
|
||||
ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) |
||||
{ |
||||
return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph(); |
||||
} |
||||
|
||||
class CopyFinallySubGraphLogic |
||||
{ |
||||
readonly ControlFlowGraphBuilder builder; |
||||
readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>(); |
||||
readonly ControlFlowNode start; |
||||
readonly ControlFlowNode end; |
||||
readonly ControlFlowNode newEnd; |
||||
|
||||
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd) |
||||
{ |
||||
this.builder = builder; |
||||
this.start = start; |
||||
this.end = end; |
||||
this.newEnd = newEnd; |
||||
} |
||||
|
||||
internal ControlFlowNode CopyFinallySubGraph() |
||||
{ |
||||
foreach (ControlFlowNode n in end.Predecessors) { |
||||
CollectNodes(n); |
||||
} |
||||
foreach (var pair in oldToNew) |
||||
ReconstructEdges(pair.Key, pair.Value); |
||||
return GetNew(start); |
||||
} |
||||
|
||||
void CollectNodes(ControlFlowNode node) |
||||
{ |
||||
if (node == end || node == newEnd) |
||||
throw new InvalidOperationException("unexpected cycle involving finally construct"); |
||||
if (!oldToNew.ContainsKey(node)) { |
||||
int newBlockIndex = builder.nodes.Count; |
||||
ControlFlowNode copy; |
||||
switch (node.NodeType) { |
||||
case ControlFlowNodeType.Normal: |
||||
copy = new ControlFlowNode(newBlockIndex, node.Start, node.End); |
||||
break; |
||||
case ControlFlowNodeType.FinallyOrFaultHandler: |
||||
copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode); |
||||
break; |
||||
default: |
||||
// other nodes shouldn't occur when copying finally blocks
|
||||
throw new NotSupportedException(node.NodeType.ToString()); |
||||
} |
||||
copy.CopyFrom = node; |
||||
builder.nodes.Add(copy); |
||||
oldToNew.Add(node, copy); |
||||
|
||||
if (node != start) { |
||||
foreach (ControlFlowNode n in node.Predecessors) { |
||||
CollectNodes(n); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode) |
||||
{ |
||||
foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) { |
||||
builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type); |
||||
} |
||||
} |
||||
|
||||
ControlFlowNode GetNew(ControlFlowNode oldNode) |
||||
{ |
||||
if (oldNode == end) |
||||
return newEnd; |
||||
ControlFlowNode newNode; |
||||
if (oldToNew.TryGetValue(oldNode, out newNode)) |
||||
return newNode; |
||||
return oldNode; |
||||
} |
||||
} |
||||
|
||||
#region CreateEdge methods
|
||||
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type) |
||||
{ |
||||
CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type); |
||||
} |
||||
|
||||
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type) |
||||
{ |
||||
ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type); |
||||
fromNode.Outgoing.Add(edge); |
||||
toNode.Incoming.Add(edge); |
||||
} |
||||
#endregion
|
||||
|
||||
#region OpCode info
|
||||
static bool CanThrowException(OpCode opcode) |
||||
{ |
||||
if (opcode.OpCodeType == OpCodeType.Prefix) |
||||
return false; |
||||
return OpCodeInfo.Get(opcode).CanThrow; |
||||
} |
||||
|
||||
static bool IsBranch(OpCode opcode) |
||||
{ |
||||
if (opcode.OpCodeType == OpCodeType.Prefix) |
||||
return false; |
||||
switch (opcode.FlowControl) { |
||||
case FlowControl.Cond_Branch: |
||||
case FlowControl.Branch: |
||||
case FlowControl.Throw: |
||||
case FlowControl.Return: |
||||
return true; |
||||
case FlowControl.Next: |
||||
case FlowControl.Call: |
||||
return false; |
||||
default: |
||||
throw new NotSupportedException(opcode.FlowControl.ToString()); |
||||
} |
||||
} |
||||
|
||||
static bool IsUnconditionalBranch(OpCode opcode) |
||||
{ |
||||
if (opcode.OpCodeType == OpCodeType.Prefix) |
||||
return false; |
||||
switch (opcode.FlowControl) { |
||||
case FlowControl.Branch: |
||||
case FlowControl.Throw: |
||||
case FlowControl.Return: |
||||
return true; |
||||
case FlowControl.Next: |
||||
case FlowControl.Call: |
||||
case FlowControl.Cond_Branch: |
||||
return false; |
||||
default: |
||||
throw new NotSupportedException(opcode.FlowControl.ToString()); |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,184 @@
@@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
|
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public enum ControlFlowNodeType |
||||
{ |
||||
Normal, |
||||
EntryPoint, |
||||
RegularExit, |
||||
ExceptionalExit, |
||||
CatchHandler, |
||||
FinallyOrFaultHandler, |
||||
EndFinallyOrFault |
||||
} |
||||
|
||||
public sealed class ControlFlowNode |
||||
{ |
||||
public readonly int BlockIndex; |
||||
public readonly ControlFlowNodeType NodeType; |
||||
public readonly ControlFlowNode EndFinallyOrFaultNode; |
||||
|
||||
/// <summary>
|
||||
/// Visited flag that's used in various algorithms.
|
||||
/// </summary>
|
||||
internal bool Visited; |
||||
|
||||
/// <summary>
|
||||
/// Signalizes that this node is a copy of another node.
|
||||
/// </summary>
|
||||
public ControlFlowNode CopyFrom { get; internal set; } |
||||
|
||||
/// <summary>
|
||||
/// Gets the immediate dominator.
|
||||
/// </summary>
|
||||
public ControlFlowNode ImmediateDominator { get; internal set; } |
||||
|
||||
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>(); |
||||
|
||||
public HashSet<ControlFlowNode> DominanceFrontier; |
||||
|
||||
/// <summary>
|
||||
/// Start of code block represented by this node. Only set for nodetype == Normal.
|
||||
/// </summary>
|
||||
public readonly Instruction Start; |
||||
|
||||
/// <summary>
|
||||
/// End of the code block represented by this node. Only set for nodetype == Normal.
|
||||
/// </summary>
|
||||
public readonly Instruction End; |
||||
|
||||
/// <summary>
|
||||
/// Gets the exception handler associated with this node. Only set for nodetype == ExceptionHandler.
|
||||
/// </summary>
|
||||
public readonly ExceptionHandler ExceptionHandler; |
||||
|
||||
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>(); |
||||
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>(); |
||||
|
||||
internal ControlFlowNode(int blockIndex, ControlFlowNodeType nodeType) |
||||
{ |
||||
this.BlockIndex = blockIndex; |
||||
this.NodeType = nodeType; |
||||
} |
||||
|
||||
internal ControlFlowNode(int blockIndex, Instruction start, Instruction end) |
||||
{ |
||||
if (start == null) |
||||
throw new ArgumentNullException("start"); |
||||
if (end == null) |
||||
throw new ArgumentNullException("end"); |
||||
this.BlockIndex = blockIndex; |
||||
this.NodeType = ControlFlowNodeType.Normal; |
||||
this.Start = start; |
||||
this.End = end; |
||||
} |
||||
|
||||
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode) |
||||
{ |
||||
this.BlockIndex = blockIndex; |
||||
this.NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler; |
||||
this.ExceptionHandler = exceptionHandler; |
||||
this.EndFinallyOrFaultNode = endFinallyOrFaultNode; |
||||
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null)); |
||||
} |
||||
|
||||
public IEnumerable<ControlFlowNode> Predecessors { |
||||
get { |
||||
return Incoming.Select(e => e.Source); |
||||
} |
||||
} |
||||
|
||||
public IEnumerable<ControlFlowNode> Successors { |
||||
get { |
||||
return Outgoing.Select(e => e.Target); |
||||
} |
||||
} |
||||
|
||||
public IEnumerable<Instruction> Instructions { |
||||
get { |
||||
Instruction inst = Start; |
||||
if (inst != null) { |
||||
yield return inst; |
||||
while (inst != End) { |
||||
inst = inst.Next; |
||||
yield return inst; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction) |
||||
{ |
||||
if (Visited) |
||||
return; |
||||
Visited = true; |
||||
visitAction(this); |
||||
foreach (ControlFlowNode t in children(this)) |
||||
t.TraversePreOrder(children, visitAction); |
||||
} |
||||
|
||||
public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction) |
||||
{ |
||||
if (Visited) |
||||
return; |
||||
Visited = true; |
||||
foreach (ControlFlowNode t in children(this)) |
||||
t.TraversePostOrder(children, visitAction); |
||||
visitAction(this); |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringWriter writer = new StringWriter(); |
||||
switch (NodeType) { |
||||
case ControlFlowNodeType.Normal: |
||||
int endOffset = End.Next != null ? End.Next.Offset : End.Offset + End.GetSize(); |
||||
writer.Write("Block #{0}: IL_{1:x4} to IL_{2:x4}", BlockIndex, Start.Offset, endOffset); |
||||
break; |
||||
case ControlFlowNodeType.CatchHandler: |
||||
case ControlFlowNodeType.FinallyOrFaultHandler: |
||||
writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType); |
||||
ExceptionHandler.WriteTo(writer); |
||||
break; |
||||
default: |
||||
writer.Write("Block #{0}: {1}", BlockIndex, NodeType); |
||||
break; |
||||
} |
||||
// if (ImmediateDominator != null) {
|
||||
// writer.WriteLine();
|
||||
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
|
||||
// }
|
||||
foreach (Instruction inst in this.Instructions) { |
||||
writer.WriteLine(); |
||||
inst.WriteTo(writer); |
||||
} |
||||
return writer.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,240 @@
@@ -0,0 +1,240 @@
|
||||
// <file>
|
||||
// <copyright see="prj:///doc/copyright.txt"/>
|
||||
// <license see="prj:///doc/license.txt"/>
|
||||
// <author name="Daniel Grunwald"/>
|
||||
// <version>$Revision: 776 $</version>
|
||||
// </file>
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler |
||||
{ |
||||
/// <summary>
|
||||
/// Additional info about opcodes.
|
||||
/// </summary>
|
||||
sealed class OpCodeInfo |
||||
{ |
||||
static readonly OpCodeInfo[] knownOpCodes = { |
||||
#region Base Instructions
|
||||
new OpCodeInfo(OpCodes.Add) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.And) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Arglist) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Beq) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Bge) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Bgt) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ble) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Blt) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Br) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Break) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Call) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Calli) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ceq) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Cgt) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Clt) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false }, |
||||
// conv.<to type>
|
||||
new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false }, |
||||
// conv.ovf.<to type>
|
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true}, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true}, |
||||
// conv.ovf.<to type>.un
|
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true }, |
||||
|
||||
//new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
|
||||
new OpCodeInfo(OpCodes.Div) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true }, |
||||
new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false }, |
||||
//new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
|
||||
//new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
|
||||
new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true }, |
||||
new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false }, |
||||
// ldind.<type>
|
||||
new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true }, |
||||
// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
|
||||
new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true }, |
||||
new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Leave) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Localloc) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Mul) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Neg) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Nop) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Not) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Or) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Pop) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Rem) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Ret) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Shl) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Shr) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true }, |
||||
new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true }, |
||||
new OpCodeInfo(OpCodes.Sub) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true }, |
||||
new OpCodeInfo(OpCodes.Switch) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Xor) { CanThrow = false }, |
||||
#endregion
|
||||
#region Object model instructions
|
||||
// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
|
||||
new OpCodeInfo(OpCodes.Box), |
||||
new OpCodeInfo(OpCodes.Callvirt), |
||||
new OpCodeInfo(OpCodes.Castclass), // (output type not Input0 as type arguments may change)
|
||||
new OpCodeInfo(OpCodes.Cpobj), |
||||
new OpCodeInfo(OpCodes.Initobj) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Isinst) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldelem_Any), |
||||
// ldelem.<type>
|
||||
new OpCodeInfo(OpCodes.Ldelem_I) , |
||||
new OpCodeInfo(OpCodes.Ldelem_I1), |
||||
new OpCodeInfo(OpCodes.Ldelem_I2), |
||||
new OpCodeInfo(OpCodes.Ldelem_I4), |
||||
new OpCodeInfo(OpCodes.Ldelem_I8), |
||||
new OpCodeInfo(OpCodes.Ldelem_R4), |
||||
new OpCodeInfo(OpCodes.Ldelem_R8), |
||||
new OpCodeInfo(OpCodes.Ldelem_Ref), |
||||
new OpCodeInfo(OpCodes.Ldelem_U1), |
||||
new OpCodeInfo(OpCodes.Ldelem_U2), |
||||
new OpCodeInfo(OpCodes.Ldelem_U4), |
||||
new OpCodeInfo(OpCodes.Ldelema) , |
||||
new OpCodeInfo(OpCodes.Ldfld) , |
||||
new OpCodeInfo(OpCodes.Ldflda), |
||||
new OpCodeInfo(OpCodes.Ldlen) , |
||||
new OpCodeInfo(OpCodes.Ldobj) , |
||||
new OpCodeInfo(OpCodes.Ldsfld), |
||||
new OpCodeInfo(OpCodes.Ldsflda), |
||||
new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Ldvirtftn), |
||||
//new OpCodeInfo(OpCodes.Mkrefany), don't enable this without checking what it is
|
||||
new OpCodeInfo(OpCodes.Newarr), |
||||
new OpCodeInfo(OpCodes.Newobj), |
||||
//new OpCodeInfo(OpCodes.Mkrefany), don't enable this without checking what it is
|
||||
//new OpCodeInfo(OpCodes.Refanyval), don't enable this without checking what it is
|
||||
new OpCodeInfo(OpCodes.Rethrow), |
||||
new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false }, |
||||
new OpCodeInfo(OpCodes.Stelem_Any), |
||||
new OpCodeInfo(OpCodes.Stelem_I1), |
||||
new OpCodeInfo(OpCodes.Stelem_I2), |
||||
new OpCodeInfo(OpCodes.Stelem_I4), |
||||
new OpCodeInfo(OpCodes.Stelem_I8), |
||||
new OpCodeInfo(OpCodes.Stelem_R4), |
||||
new OpCodeInfo(OpCodes.Stelem_R8), |
||||
new OpCodeInfo(OpCodes.Stelem_Ref), |
||||
new OpCodeInfo(OpCodes.Stfld), |
||||
new OpCodeInfo(OpCodes.Stobj), |
||||
new OpCodeInfo(OpCodes.Stsfld), |
||||
new OpCodeInfo(OpCodes.Throw), |
||||
new OpCodeInfo(OpCodes.Unbox), |
||||
new OpCodeInfo(OpCodes.Unbox_Any), |
||||
#endregion
|
||||
}; |
||||
static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code); |
||||
|
||||
public static OpCodeInfo Get(OpCode opCode) |
||||
{ |
||||
return Get(opCode.Code); |
||||
} |
||||
|
||||
public static OpCodeInfo Get(Code code) |
||||
{ |
||||
OpCodeInfo info; |
||||
if (knownOpCodeDict.TryGetValue(code, out info)) |
||||
return info; |
||||
else |
||||
throw new NotSupportedException(code.ToString()); |
||||
} |
||||
|
||||
OpCode opcode; |
||||
|
||||
private OpCodeInfo(OpCode opcode) |
||||
{ |
||||
this.opcode = opcode; |
||||
this.CanThrow = true; |
||||
} |
||||
|
||||
public OpCode OpCode { get { return opcode; } } |
||||
|
||||
/// <summary>
|
||||
/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
|
||||
/// </summary>
|
||||
public bool IsMoveInstruction { get; private set; } |
||||
|
||||
/// <summary>
|
||||
/// Specifies whether this opcode is capable of throwing exceptions.
|
||||
/// </summary>
|
||||
public bool CanThrow { get; private set; } |
||||
} |
||||
} |
@ -0,0 +1,169 @@
@@ -0,0 +1,169 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
sealed class SimplifyByRefCalls |
||||
{ |
||||
public static bool MakeByRefCallsSimple(SsaForm ssaForm) |
||||
{ |
||||
SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm); |
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
for (int i = 0; i < block.Instructions.Count; i++) { |
||||
SsaInstruction inst = block.Instructions[i]; |
||||
if (inst.Instruction != null) { |
||||
switch (inst.Instruction.OpCode.Code) { |
||||
case Code.Call: |
||||
case Code.Callvirt: |
||||
instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand); |
||||
break; |
||||
case Code.Initobj: |
||||
instance.MakeInitObjCallSimple(block, ref i); |
||||
break; |
||||
case Code.Ldfld: |
||||
instance.MakeLoadFieldCallSimple(block, ref i); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
instance.RemoveRedundantInstructions(); |
||||
if (instance.couldSimplifySomething) |
||||
ssaForm.ComputeVariableUsage(); |
||||
return instance.couldSimplifySomething; |
||||
} |
||||
|
||||
readonly SsaForm ssaForm; |
||||
|
||||
bool couldSimplifySomething; |
||||
|
||||
// the list of ldloca instructions we will remove
|
||||
readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>(); |
||||
|
||||
private SimplifyByRefCalls(SsaForm ssaForm) |
||||
{ |
||||
this.ssaForm = ssaForm; |
||||
} |
||||
|
||||
void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod) |
||||
{ |
||||
SsaInstruction inst = block.Instructions[instructionIndexInBlock]; |
||||
for (int i = 0; i < inst.Operands.Length; i++) { |
||||
SsaVariable operand = inst.Operands[i]; |
||||
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { |
||||
// address is used for this method call only
|
||||
|
||||
Instruction loadAddressInstruction = operand.Definition.Instruction; |
||||
|
||||
// find target parameter type:
|
||||
bool isOut; |
||||
if (i == 0 && targetMethod.HasThis) { |
||||
isOut = false; |
||||
} else { |
||||
ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)]; |
||||
isOut = parameter.IsOut; |
||||
} |
||||
|
||||
SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction); |
||||
|
||||
// insert "Prepare" instruction on front
|
||||
SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall; |
||||
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( |
||||
block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode)); |
||||
|
||||
// insert "WriteAfterByRefOrOutCall" instruction after call
|
||||
block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction( |
||||
block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall)); |
||||
|
||||
couldSimplifySomething = true; |
||||
|
||||
// remove the loadAddressInstruction later
|
||||
// (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
|
||||
redundantLoadAddressInstructions.Add(operand.Definition); |
||||
} |
||||
} |
||||
} |
||||
|
||||
SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction) |
||||
{ |
||||
if (loadAddressInstruction.OpCode == OpCodes.Ldloca) { |
||||
return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand); |
||||
} else { |
||||
Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga); |
||||
return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand); |
||||
} |
||||
} |
||||
|
||||
static bool IsLoadAddress(SsaInstruction inst) |
||||
{ |
||||
return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga); |
||||
} |
||||
|
||||
void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock) |
||||
{ |
||||
SsaInstruction inst = block.Instructions[instructionIndexInBlock]; |
||||
Debug.Assert(inst.Operands.Length == 1); |
||||
SsaVariable operand = inst.Operands[0]; |
||||
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { |
||||
// replace instruction with special "InitObj" instruction
|
||||
block.Instructions[instructionIndexInBlock] = new SsaInstruction( |
||||
inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null, |
||||
specialOpCode: SpecialOpCode.InitObj, |
||||
typeOperand: (TypeReference)inst.Instruction.Operand); |
||||
|
||||
couldSimplifySomething = true; |
||||
|
||||
// remove the loadAddressInstruction later
|
||||
redundantLoadAddressInstructions.Add(operand.Definition); |
||||
} |
||||
} |
||||
|
||||
void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock) |
||||
{ |
||||
SsaInstruction inst = block.Instructions[instructionIndexInBlock]; |
||||
Debug.Assert(inst.Operands.Length == 1); |
||||
SsaVariable operand = inst.Operands[0]; |
||||
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) { |
||||
// insert special "PrepareForFieldAccess" instruction in front
|
||||
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction( |
||||
inst.ParentBlock, null, operand, |
||||
new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) }, |
||||
specialOpCode: SpecialOpCode.PrepareForFieldAccess)); |
||||
|
||||
couldSimplifySomething = true; |
||||
|
||||
// remove the loadAddressInstruction later
|
||||
redundantLoadAddressInstructions.Add(operand.Definition); |
||||
} |
||||
} |
||||
|
||||
void RemoveRedundantInstructions() |
||||
{ |
||||
foreach (SsaInstruction inst in redundantLoadAddressInstructions) { |
||||
inst.ParentBlock.Instructions.Remove(inst); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public sealed class SsaBlock |
||||
{ |
||||
public readonly List<SsaBlock> Successors = new List<SsaBlock>(); |
||||
public readonly List<SsaBlock> Predecessors = new List<SsaBlock>(); |
||||
public readonly ControlFlowNodeType NodeType; |
||||
public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>(); |
||||
public readonly int BlockIndex; |
||||
|
||||
internal SsaBlock(ControlFlowNode node) |
||||
{ |
||||
this.NodeType = node.NodeType; |
||||
this.BlockIndex = node.BlockIndex; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringWriter writer = new StringWriter(); |
||||
writer.Write("Block #{0} ({1})", BlockIndex, NodeType); |
||||
foreach (SsaInstruction inst in Instructions) { |
||||
writer.WriteLine(); |
||||
inst.WriteTo(writer); |
||||
} |
||||
return writer.ToString(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,158 @@
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public sealed class SsaForm |
||||
{ |
||||
readonly SsaVariable[] parameters; |
||||
readonly SsaVariable[] locals; |
||||
public readonly ReadOnlyCollection<SsaVariable> OriginalVariables; |
||||
public readonly ReadOnlyCollection<SsaBlock> Blocks; |
||||
readonly bool methodHasThis; |
||||
|
||||
public SsaBlock EntryPoint { |
||||
get { return this.Blocks[0]; } |
||||
} |
||||
|
||||
public SsaBlock RegularExit { |
||||
get { return this.Blocks[1]; } |
||||
} |
||||
|
||||
public SsaBlock ExceptionalExit { |
||||
get { return this.Blocks[2]; } |
||||
} |
||||
|
||||
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis) |
||||
{ |
||||
this.parameters = parameters; |
||||
this.locals = locals; |
||||
this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks); |
||||
this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList()); |
||||
this.methodHasThis = methodHasThis; |
||||
|
||||
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint); |
||||
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit); |
||||
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit); |
||||
for (int i = 0; i < this.OriginalVariables.Count; i++) { |
||||
this.OriginalVariables[i].OriginalVariableIndex = i; |
||||
} |
||||
} |
||||
|
||||
public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null) |
||||
{ |
||||
if (labelProvider == null) |
||||
labelProvider = b => b.ToString(); |
||||
GraphVizGraph graph = new GraphVizGraph(); |
||||
foreach (SsaBlock block in this.Blocks) { |
||||
graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" }); |
||||
} |
||||
foreach (SsaBlock block in this.Blocks) { |
||||
foreach (SsaBlock s in block.Successors) { |
||||
graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex)); |
||||
} |
||||
} |
||||
return graph; |
||||
} |
||||
|
||||
public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null) |
||||
{ |
||||
if (labelProvider == null) |
||||
labelProvider = v => v.ToString(); |
||||
GraphVizGraph graph = new GraphVizGraph(); |
||||
foreach (SsaVariable v in this.AllVariables) { |
||||
graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) }); |
||||
} |
||||
int instructionIndex = 0; |
||||
foreach (SsaBlock block in this.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
if (inst.Operands.Length == 0 && inst.Target == null) |
||||
continue; |
||||
string id = "instruction" + (++instructionIndex); |
||||
graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" }); |
||||
foreach (SsaVariable op in inst.Operands) |
||||
graph.AddEdge(new GraphVizEdge(op.Name, id)); |
||||
if (inst.Target != null) |
||||
graph.AddEdge(new GraphVizEdge(id, inst.Target.Name)); |
||||
} |
||||
} |
||||
return graph; |
||||
} |
||||
|
||||
public SsaVariable GetOriginalVariable(ParameterReference parameter) |
||||
{ |
||||
if (methodHasThis) |
||||
return parameters[parameter.Index + 1]; |
||||
else |
||||
return parameters[parameter.Index]; |
||||
} |
||||
|
||||
public SsaVariable GetOriginalVariable(VariableReference variable) |
||||
{ |
||||
return locals[variable.Index]; |
||||
} |
||||
|
||||
#region ComputeVariableUsage
|
||||
public void ComputeVariableUsage() |
||||
{ |
||||
// clear data from previous runs
|
||||
foreach (SsaBlock block in this.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
foreach (SsaVariable v in inst.Operands) { |
||||
if (v.Usage != null) |
||||
v.Usage.Clear(); |
||||
} |
||||
if (inst.Target != null && inst.Target.Usage != null) |
||||
inst.Target.Usage.Clear(); |
||||
} |
||||
} |
||||
foreach (SsaBlock block in this.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
foreach (SsaVariable v in inst.Operands) { |
||||
if (v.Usage == null) |
||||
v.Usage = new List<SsaInstruction>(); |
||||
v.Usage.Add(inst); |
||||
} |
||||
if (inst.Target != null && inst.Target.Usage == null) |
||||
inst.Target.Usage = new List<SsaInstruction>(); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
public IEnumerable<SsaVariable> AllVariables { |
||||
get { |
||||
return ( |
||||
from block in this.Blocks |
||||
from instruction in block.Instructions |
||||
where instruction.Target != null |
||||
select instruction.Target |
||||
).Distinct(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,254 @@
@@ -0,0 +1,254 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
/// <summary>
|
||||
/// Constructs "SsaForm" graph for a CFG.
|
||||
/// This class transforms the method from stack-based IL to a register-based IL language.
|
||||
/// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
|
||||
/// </summary>
|
||||
public sealed class SsaFormBuilder |
||||
{ |
||||
public static SsaForm Build(MethodDefinition method) |
||||
{ |
||||
if (method == null) |
||||
throw new ArgumentNullException("method"); |
||||
var cfg = ControlFlowGraphBuilder.Build(method.Body); |
||||
cfg.ComputeDominance(); |
||||
cfg.ComputeDominanceFrontier(); |
||||
var ssa = BuildRegisterIL(method, cfg); |
||||
TransformToSsa.Transform(cfg, ssa); |
||||
return ssa; |
||||
} |
||||
|
||||
public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg) |
||||
{ |
||||
if (method == null) |
||||
throw new ArgumentNullException("method"); |
||||
if (cfg == null) |
||||
throw new ArgumentNullException("cfg"); |
||||
return new SsaFormBuilder(method, cfg).Build(); |
||||
} |
||||
|
||||
readonly MethodDefinition method; |
||||
readonly ControlFlowGraph cfg; |
||||
|
||||
readonly SsaBlock[] blocks; // array index = block index
|
||||
readonly int[] stackSizeAtBlockStart; // array index = block index
|
||||
|
||||
readonly SsaVariable[] parameters; // array index = parameter number
|
||||
readonly SsaVariable[] locals; // array index = local number
|
||||
readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
|
||||
SsaForm ssaForm; |
||||
|
||||
private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg) |
||||
{ |
||||
this.method = method; |
||||
this.cfg = cfg; |
||||
|
||||
this.blocks = new SsaBlock[cfg.Nodes.Count]; |
||||
this.stackSizeAtBlockStart = new int[cfg.Nodes.Count]; |
||||
for (int i = 0; i < stackSizeAtBlockStart.Length; i++) { |
||||
stackSizeAtBlockStart[i] = -1; |
||||
} |
||||
stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0; |
||||
|
||||
this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)]; |
||||
if (method.HasThis) |
||||
parameters[0] = new SsaVariable(method.Body.ThisParameter); |
||||
for (int i = 0; i < method.Parameters.Count; i++) |
||||
parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]); |
||||
|
||||
this.locals = new SsaVariable[method.Body.Variables.Count]; |
||||
for (int i = 0; i < locals.Length; i++) |
||||
locals[i] = new SsaVariable(method.Body.Variables[i]); |
||||
|
||||
this.stackLocations = new SsaVariable[method.Body.MaxStackSize]; |
||||
for (int i = 0; i < stackLocations.Length; i++) { |
||||
stackLocations[i] = new SsaVariable(i); |
||||
} |
||||
} |
||||
|
||||
internal SsaForm Build() |
||||
{ |
||||
CreateGraphStructure(); |
||||
this.ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis); |
||||
CreateInstructions(cfg.EntryPoint.BlockIndex); |
||||
CreateSpecialInstructions(); |
||||
return ssaForm; |
||||
} |
||||
|
||||
void CreateGraphStructure() |
||||
{ |
||||
for (int i = 0; i < blocks.Length; i++) { |
||||
blocks[i] = new SsaBlock(cfg.Nodes[i]); |
||||
} |
||||
for (int i = 0; i < blocks.Length; i++) { |
||||
foreach (ControlFlowNode node in cfg.Nodes[i].Successors) { |
||||
blocks[i].Successors.Add(blocks[node.BlockIndex]); |
||||
blocks[node.BlockIndex].Predecessors.Add(blocks[i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CreateInstructions(int blockIndex) |
||||
{ |
||||
ControlFlowNode cfgNode = cfg.Nodes[blockIndex]; |
||||
SsaBlock block = blocks[blockIndex]; |
||||
|
||||
int stackSize = stackSizeAtBlockStart[blockIndex]; |
||||
Debug.Assert(stackSize >= 0); |
||||
|
||||
List<Instruction> prefixes = new List<Instruction>(); |
||||
foreach (Instruction inst in cfgNode.Instructions) { |
||||
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { |
||||
prefixes.Add(inst); |
||||
continue; |
||||
} |
||||
|
||||
int popCount = inst.GetPopDelta(method, stackSize); |
||||
stackSize -= popCount; |
||||
if (stackSize < 0) |
||||
throw new InvalidProgramException("IL stack underflow"); |
||||
|
||||
int pushCount = inst.GetPushDelta(); |
||||
if (stackSize + pushCount > stackLocations.Length) |
||||
throw new InvalidProgramException("IL stack overflow"); |
||||
|
||||
SsaVariable target; |
||||
SsaVariable[] operands; |
||||
DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands); |
||||
|
||||
Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null; |
||||
prefixes.Clear(); |
||||
|
||||
// ignore NOP instructions
|
||||
if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) { |
||||
block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray)); |
||||
} |
||||
stackSize += pushCount; |
||||
} |
||||
|
||||
foreach (ControlFlowEdge edge in cfgNode.Outgoing) { |
||||
int newStackSize; |
||||
switch (edge.Type) { |
||||
case JumpType.Normal: |
||||
newStackSize = stackSize; |
||||
break; |
||||
case JumpType.MutualProtection: |
||||
case JumpType.JumpToExceptionHandler: |
||||
switch (edge.Target.NodeType) { |
||||
case ControlFlowNodeType.FinallyOrFaultHandler: |
||||
newStackSize = 0; |
||||
break; |
||||
case ControlFlowNodeType.ExceptionalExit: |
||||
case ControlFlowNodeType.CatchHandler: |
||||
newStackSize = 1; |
||||
break; |
||||
default: |
||||
throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType); |
||||
} |
||||
break; |
||||
default: |
||||
throw new NotSupportedException("unsupported jump type: " + edge.Type); |
||||
} |
||||
|
||||
int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex]; |
||||
if (nextStackSize == -1) { |
||||
stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize; |
||||
CreateInstructions(edge.Target.BlockIndex); |
||||
} else if (nextStackSize != newStackSize) { |
||||
throw new InvalidProgramException("Stack size doesn't match"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands) |
||||
{ |
||||
switch (inst.OpCode.Code) { |
||||
case Code.Ldarg: |
||||
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) }; |
||||
target = stackLocations[stackSize]; |
||||
break; |
||||
case Code.Starg: |
||||
operands = new SsaVariable[] { stackLocations[stackSize] }; |
||||
target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand); |
||||
break; |
||||
case Code.Ldloc: |
||||
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) }; |
||||
target = stackLocations[stackSize]; |
||||
break; |
||||
case Code.Stloc: |
||||
operands = new SsaVariable[] { stackLocations[stackSize] }; |
||||
target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand); |
||||
break; |
||||
case Code.Dup: |
||||
operands = new SsaVariable[] { stackLocations[stackSize] }; |
||||
target = stackLocations[stackSize + 1]; |
||||
break; |
||||
default: |
||||
operands = new SsaVariable[popCount]; |
||||
for (int i = 0; i < popCount; i++) { |
||||
operands[i] = stackLocations[stackSize + i]; |
||||
} |
||||
|
||||
switch (pushCount) { |
||||
case 0: |
||||
target = null; |
||||
break; |
||||
case 1: |
||||
target = stackLocations[stackSize]; |
||||
break; |
||||
default: |
||||
throw new NotSupportedException("unsupported pushCount=" + pushCount); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void CreateSpecialInstructions() |
||||
{ |
||||
// Everything needs an initial write for the SSA transformation to work correctly.
|
||||
foreach (SsaVariable v in parameters) { |
||||
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter)); |
||||
} |
||||
foreach (SsaVariable v in locals) { |
||||
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized)); |
||||
} |
||||
foreach (SsaVariable v in stackLocations) { |
||||
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized)); |
||||
} |
||||
foreach (SsaBlock b in blocks) { |
||||
if (b.NodeType == ControlFlowNodeType.CatchHandler) { |
||||
b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null, |
||||
specialOpCode: SpecialOpCode.Exception, |
||||
typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,190 @@
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public enum SpecialOpCode |
||||
{ |
||||
/// <summary>
|
||||
/// No special op code: SsaInstruction has a normal IL instruction
|
||||
/// </summary>
|
||||
None, |
||||
/// <summary>
|
||||
/// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
|
||||
/// </summary>
|
||||
Phi, |
||||
/// <summary>
|
||||
/// Variable is read from before passing it by ref.
|
||||
/// This instruction constructs a managed reference to the variable.
|
||||
/// </summary>
|
||||
PrepareByRefCall, |
||||
/// <summary>
|
||||
/// This instruction constructs a managed reference to the variable.
|
||||
/// The variable is not really read from.
|
||||
/// </summary>
|
||||
PrepareByOutCall, |
||||
/// <summary>
|
||||
/// This instruction constructs a managed reference to the variable.
|
||||
/// The reference is used for a field access on a value type.
|
||||
/// </summary>
|
||||
PrepareForFieldAccess, |
||||
/// <summary>
|
||||
/// Variable is written to after passing it by ref or out.
|
||||
/// </summary>
|
||||
WriteAfterByRefOrOutCall, |
||||
/// <summary>
|
||||
/// Variable is not initialized.
|
||||
/// </summary>
|
||||
Uninitialized, |
||||
/// <summary>
|
||||
/// Value is passed in as parameter
|
||||
/// </summary>
|
||||
Parameter, |
||||
/// <summary>
|
||||
/// Value is a caught exception.
|
||||
/// TypeOperand is set to the exception type.
|
||||
/// </summary>
|
||||
Exception, |
||||
/// <summary>
|
||||
/// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
|
||||
/// but assigns to the target variable.
|
||||
/// TypeOperand is set to the type being created.
|
||||
/// </summary>
|
||||
InitObj |
||||
} |
||||
|
||||
public sealed class SsaInstruction |
||||
{ |
||||
public readonly SsaBlock ParentBlock; |
||||
public readonly SpecialOpCode SpecialOpCode; |
||||
|
||||
/// <summary>
|
||||
/// The original IL instruction.
|
||||
/// May be null for "invented" instructions (SpecialOpCode != None).
|
||||
/// </summary>
|
||||
public readonly Instruction Instruction; |
||||
|
||||
/// <summary>
|
||||
/// Prefixes in front of the IL instruction.
|
||||
/// </summary>
|
||||
public readonly Instruction[] Prefixes; |
||||
|
||||
/// <summary>
|
||||
/// Gets the type operand. This is used only in combination with some special opcodes.
|
||||
/// </summary>
|
||||
public readonly TypeReference TypeOperand; |
||||
|
||||
public SsaVariable Target; |
||||
public SsaVariable[] Operands; |
||||
|
||||
static readonly SsaVariable[] emptyVariableArray = {}; |
||||
static readonly Instruction[] emptyInstructionArray = {}; |
||||
|
||||
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands, |
||||
Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None, |
||||
TypeReference typeOperand = null) |
||||
{ |
||||
this.ParentBlock = parentBlock; |
||||
this.Instruction = instruction; |
||||
this.Prefixes = prefixes ?? emptyInstructionArray; |
||||
this.Target = target; |
||||
this.Operands = operands ?? emptyVariableArray; |
||||
this.SpecialOpCode = specialOpCode; |
||||
this.TypeOperand = typeOperand; |
||||
Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj)); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether this instruction is a simple assignment from one variable to another.
|
||||
/// </summary>
|
||||
public bool IsMoveInstruction { |
||||
get { |
||||
return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction; |
||||
} |
||||
} |
||||
|
||||
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar) |
||||
{ |
||||
for (int i = 0; i < this.Operands.Length; i++) { |
||||
if (this.Operands[i] == oldVar) |
||||
this.Operands[i] = newVar; |
||||
} |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringWriter w = new StringWriter(); |
||||
WriteTo(w); |
||||
return w.ToString(); |
||||
} |
||||
|
||||
public void WriteTo(TextWriter writer) |
||||
{ |
||||
foreach (Instruction prefix in this.Prefixes) { |
||||
prefix.WriteTo(writer); |
||||
writer.WriteLine(); |
||||
} |
||||
if (Instruction != null && Instruction.Offset >= 0) { |
||||
writer.Write(CecilExtensions.OffsetToString(Instruction.Offset)); |
||||
writer.Write(": "); |
||||
} |
||||
if (Target != null) { |
||||
writer.Write(Target.ToString()); |
||||
writer.Write(" = "); |
||||
} |
||||
if (IsMoveInstruction) { |
||||
writer.Write(Operands[0].ToString()); |
||||
if (Instruction != null) { |
||||
writer.Write(" (" + Instruction.OpCode.Name + ")"); |
||||
} |
||||
} else { |
||||
if (Instruction == null) { |
||||
writer.Write(SpecialOpCode.ToString()); |
||||
} else { |
||||
writer.Write(Instruction.OpCode.Name); |
||||
if(null != Instruction.Operand) { |
||||
writer.Write(' '); |
||||
writer.Write(CecilExtensions.OperandToString(Instruction.Operand)); |
||||
writer.Write(' '); |
||||
} |
||||
} |
||||
if (TypeOperand != null) { |
||||
writer.Write(' '); |
||||
writer.Write(TypeOperand.ToString()); |
||||
writer.Write(' '); |
||||
} |
||||
if (Operands.Length > 0) { |
||||
writer.Write('('); |
||||
for (int i = 0; i < Operands.Length; i++) { |
||||
if (i > 0) |
||||
writer.Write(", "); |
||||
writer.Write(Operands[i].ToString()); |
||||
} |
||||
writer.Write(')'); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
|
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
static class SsaOptimization |
||||
{ |
||||
public static void Optimize(SsaForm ssaForm) |
||||
{ |
||||
DirectlyStoreToVariables(ssaForm); |
||||
SimpleCopyPropagation(ssaForm); |
||||
RemoveDeadAssignments(ssaForm); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
|
||||
/// we optimize this to directly store in the target location.
|
||||
/// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
|
||||
/// after locals instead of keeping the temps as using only the simple copy propagation would do.
|
||||
/// </summary>
|
||||
public static void DirectlyStoreToVariables(SsaForm ssaForm) |
||||
{ |
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
block.Instructions.RemoveAll( |
||||
inst => { |
||||
if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) { |
||||
SsaVariable target = inst.Target; |
||||
SsaVariable temp = inst.Operands[0]; |
||||
if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) { |
||||
temp.Definition.Target = target; |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
}); |
||||
} |
||||
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
|
||||
} |
||||
|
||||
public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true) |
||||
{ |
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) { |
||||
if (inst.Target.IsStackLocation || !onlyForStackLocations) { |
||||
// replace all uses of 'target' with 'operands[0]'.
|
||||
foreach (SsaInstruction useInstruction in inst.Target.Usage) { |
||||
useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
|
||||
} |
||||
|
||||
public static void RemoveDeadAssignments(SsaForm ssaForm) |
||||
{ |
||||
HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>(); |
||||
// find variables that are used directly
|
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
if (!CanRemoveAsDeadCode(inst)) { |
||||
if (inst.Target != null) |
||||
liveVariables.Add(inst.Target); |
||||
foreach (SsaVariable op in inst.Operands) { |
||||
liveVariables.Add(op); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables); |
||||
// find variables that are used indirectly
|
||||
while (queue.Count > 0) { |
||||
SsaVariable v = queue.Dequeue(); |
||||
if (v.IsSingleAssignment) { |
||||
foreach (SsaVariable op in v.Definition.Operands) { |
||||
if (liveVariables.Add(op)) |
||||
queue.Enqueue(op); |
||||
} |
||||
} |
||||
} |
||||
// remove assignments to all unused variables
|
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
block.Instructions.RemoveAll( |
||||
inst => { |
||||
if (inst.Target != null && !liveVariables.Contains(inst.Target)) { |
||||
Debug.Assert(inst.Target.IsSingleAssignment); |
||||
return true; |
||||
} |
||||
return false; |
||||
}); |
||||
} |
||||
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
|
||||
} |
||||
|
||||
static bool CanRemoveAsDeadCode(SsaInstruction inst) |
||||
{ |
||||
if (inst.Target != null && !inst.Target.IsSingleAssignment) |
||||
return false; |
||||
switch (inst.SpecialOpCode) { |
||||
case SpecialOpCode.Phi: |
||||
case SpecialOpCode.Exception: |
||||
case SpecialOpCode.Parameter: |
||||
case SpecialOpCode.Uninitialized: |
||||
return true; |
||||
case SpecialOpCode.None: |
||||
return inst.IsMoveInstruction; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,85 @@
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
public sealed class SsaVariable |
||||
{ |
||||
public int OriginalVariableIndex; |
||||
public readonly string Name; |
||||
public readonly bool IsStackLocation; |
||||
|
||||
public readonly ParameterDefinition Parameter; |
||||
public readonly VariableDefinition Variable; |
||||
|
||||
public SsaVariable(ParameterDefinition p) |
||||
{ |
||||
this.Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name; |
||||
this.Parameter = p; |
||||
} |
||||
|
||||
public SsaVariable(VariableDefinition v) |
||||
{ |
||||
this.Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name; |
||||
this.Variable = v; |
||||
} |
||||
|
||||
public SsaVariable(int stackLocation) |
||||
{ |
||||
this.Name = "stack" + stackLocation; |
||||
this.IsStackLocation = true; |
||||
} |
||||
|
||||
public SsaVariable(SsaVariable original, string newName) |
||||
{ |
||||
this.Name = newName; |
||||
this.IsStackLocation = original.IsStackLocation; |
||||
this.OriginalVariableIndex = original.OriginalVariableIndex; |
||||
this.Parameter = original.Parameter; |
||||
this.Variable = original.Variable; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return Name; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether this variable has only a single assignment.
|
||||
/// This field is initialized in TransformToSsa step.
|
||||
/// </summary>
|
||||
public bool IsSingleAssignment; |
||||
|
||||
/// <summary>
|
||||
/// Gets the instruction defining the variable.
|
||||
/// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
|
||||
/// </summary>
|
||||
public SsaInstruction Definition; |
||||
|
||||
/// <summary>
|
||||
/// Gets the places where a variable is used.
|
||||
/// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
|
||||
/// </summary>
|
||||
public List<SsaInstruction> Usage; |
||||
} |
||||
} |
@ -0,0 +1,254 @@
@@ -0,0 +1,254 @@
|
||||
// Copyright (c) 2010 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Collections.ObjectModel; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.FlowAnalysis |
||||
{ |
||||
/// <summary>
|
||||
/// Convers a method to static single assignment form.
|
||||
/// </summary>
|
||||
sealed class TransformToSsa |
||||
{ |
||||
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true) |
||||
{ |
||||
TransformToSsa transform = new TransformToSsa(cfg, ssa); |
||||
transform.ConvertVariablesToSsa(); |
||||
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
|
||||
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) { |
||||
transform.ConvertVariablesToSsa(); |
||||
} |
||||
if (optimize) |
||||
SsaOptimization.Optimize(ssa); |
||||
} |
||||
|
||||
readonly ControlFlowGraph cfg; |
||||
readonly SsaForm ssaForm; |
||||
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
|
||||
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
|
||||
|
||||
private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm) |
||||
{ |
||||
this.cfg = cfg; |
||||
this.ssaForm = ssaForm; |
||||
this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count]; |
||||
this.addressTaken = new bool[ssaForm.OriginalVariables.Count]; |
||||
} |
||||
|
||||
#region CollectInformationAboutOriginalVariableUse
|
||||
void CollectInformationAboutOriginalVariableUse() |
||||
{ |
||||
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length); |
||||
for (int i = 0; i < writeToOriginalVariables.Length; i++) { |
||||
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i); |
||||
|
||||
addressTaken[i] = false; |
||||
// writeToOriginalVariables is only used when placing phi functions
|
||||
// we don't need to do that anymore for variables that are already in SSA form
|
||||
if (ssaForm.OriginalVariables[i].IsSingleAssignment) |
||||
writeToOriginalVariables[i] = null; |
||||
else |
||||
writeToOriginalVariables[i] = new List<SsaInstruction>(); |
||||
} |
||||
foreach (SsaBlock block in ssaForm.Blocks) { |
||||
foreach (SsaInstruction inst in block.Instructions) { |
||||
if (inst.Target != null ) { |
||||
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex]; |
||||
if (list != null) |
||||
list.Add(inst); |
||||
} |
||||
if (inst.Instruction != null) { |
||||
if (inst.Instruction.OpCode == OpCodes.Ldloca) { |
||||
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; |
||||
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) { |
||||
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region ConvertToSsa
|
||||
void ConvertVariablesToSsa() |
||||
{ |
||||
CollectInformationAboutOriginalVariableUse(); |
||||
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count]; |
||||
foreach (SsaVariable variable in ssaForm.OriginalVariables) { |
||||
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) { |
||||
PlacePhiFunctions(variable); |
||||
processVariable[variable.OriginalVariableIndex] = true; |
||||
} |
||||
} |
||||
RenameVariables(processVariable); |
||||
foreach (SsaVariable variable in ssaForm.OriginalVariables) { |
||||
if (!addressTaken[variable.OriginalVariableIndex]) { |
||||
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null); |
||||
} |
||||
} |
||||
ssaForm.ComputeVariableUsage(); |
||||
} |
||||
#endregion
|
||||
|
||||
#region PlacePhiFunctions
|
||||
void PlacePhiFunctions(SsaVariable variable) |
||||
{ |
||||
cfg.ResetVisited(); |
||||
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>(); |
||||
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>(); |
||||
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) { |
||||
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex]; |
||||
if (!cfgNode.Visited) { |
||||
cfgNode.Visited = true; |
||||
worklist.Enqueue(cfgNode); |
||||
} |
||||
} |
||||
while (worklist.Count > 0) { |
||||
ControlFlowNode cfgNode = worklist.Dequeue(); |
||||
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) { |
||||
// we don't need phi functions in the exit node
|
||||
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit) |
||||
continue; |
||||
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex]; |
||||
if (blocksWithPhi.Add(y)) { |
||||
// add a phi instruction in y
|
||||
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray(); |
||||
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi)); |
||||
if (!dfNode.Visited) { |
||||
dfNode.Visited = true; |
||||
worklist.Enqueue(dfNode); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region RenameVariable
|
||||
int tempVariableCounter = 1; |
||||
|
||||
void RenameVariables(bool[] processVariable) |
||||
{ |
||||
VariableRenamer r = new VariableRenamer(this, processVariable); |
||||
r.Visit(ssaForm.EntryPoint); |
||||
} |
||||
|
||||
sealed class VariableRenamer |
||||
{ |
||||
readonly TransformToSsa transform; |
||||
readonly ReadOnlyCollection<SsaVariable> inputVariables; |
||||
internal readonly Stack<SsaVariable>[] versionStacks; |
||||
int[] versionCounters; // specifies for each input variable the next version number
|
||||
|
||||
// processVariable = specifies for each input variable whether we should rename it
|
||||
public VariableRenamer(TransformToSsa transform, bool[] processVariable) |
||||
{ |
||||
this.transform = transform; |
||||
this.inputVariables = transform.ssaForm.OriginalVariables; |
||||
Debug.Assert(inputVariables.Count == processVariable.Length); |
||||
this.versionCounters = new int[inputVariables.Count]; |
||||
this.versionStacks = new Stack<SsaVariable>[inputVariables.Count]; |
||||
for (int i = 0; i < versionStacks.Length; i++) { |
||||
if (processVariable[i]) { |
||||
Debug.Assert(inputVariables[i].IsSingleAssignment == false); |
||||
// only create version stacks for the variables that we need to process and that weren't already processed earlier
|
||||
versionStacks[i] = new Stack<SsaVariable>(); |
||||
versionStacks[i].Push(inputVariables[i]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
SsaVariable MakeNewVersion(int variableIndex) |
||||
{ |
||||
int versionCounter = ++versionCounters[variableIndex]; |
||||
SsaVariable x = inputVariables[variableIndex]; |
||||
if (versionCounter == 1) { |
||||
return x; |
||||
} else { |
||||
if (x.IsStackLocation) { |
||||
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++)); |
||||
} else { |
||||
return new SsaVariable(x, x.Name + "_" + versionCounter); |
||||
} |
||||
} |
||||
} |
||||
|
||||
internal void Visit(SsaBlock block) |
||||
{ |
||||
// duplicate top of all stacks
|
||||
foreach (var stack in versionStacks) { |
||||
if (stack != null) |
||||
stack.Push(stack.Peek()); |
||||
} |
||||
|
||||
foreach (SsaInstruction s in block.Instructions) { |
||||
// replace all uses of variables being processed with their current version.
|
||||
if (s.SpecialOpCode != SpecialOpCode.Phi) { |
||||
for (int i = 0; i < s.Operands.Length; i++) { |
||||
var stack = versionStacks[s.Operands[i].OriginalVariableIndex]; |
||||
if (stack != null) |
||||
s.Operands[i] = stack.Peek(); |
||||
} |
||||
} |
||||
// if we're writing to a variable we should process:
|
||||
if (s.Target != null) { |
||||
int targetIndex = s.Target.OriginalVariableIndex; |
||||
if (versionStacks[targetIndex] != null) { |
||||
s.Target = MakeNewVersion(targetIndex); |
||||
s.Target.IsSingleAssignment = true; |
||||
s.Target.Definition = s; |
||||
|
||||
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
|
||||
// so now replace the top element
|
||||
versionStacks[targetIndex].Pop(); |
||||
versionStacks[targetIndex].Push(s.Target); |
||||
} |
||||
} |
||||
} |
||||
|
||||
foreach (SsaBlock succ in block.Successors) { |
||||
int j = succ.Predecessors.IndexOf(block); |
||||
Debug.Assert(j >= 0); |
||||
foreach (SsaInstruction f in succ.Instructions) { |
||||
if (f.SpecialOpCode == SpecialOpCode.Phi) { |
||||
var stack = versionStacks[f.Target.OriginalVariableIndex]; |
||||
if (stack != null) { |
||||
f.Operands[j] = stack.Peek(); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren) |
||||
Visit(transform.ssaForm.Blocks[child.BlockIndex]); |
||||
// restore stacks:
|
||||
foreach (var stack in versionStacks) { |
||||
if (stack != null) |
||||
stack.Pop(); |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -0,0 +1,196 @@
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Globalization; |
||||
using System.IO; |
||||
using System.Text.RegularExpressions; |
||||
|
||||
namespace ICSharpCode.Decompiler |
||||
{ |
||||
/// <summary>
|
||||
/// GraphViz graph.
|
||||
/// </summary>
|
||||
public sealed class GraphVizGraph |
||||
{ |
||||
List<GraphVizNode> nodes = new List<GraphVizNode>(); |
||||
List<GraphVizEdge> edges = new List<GraphVizEdge>(); |
||||
|
||||
public string rankdir; |
||||
|
||||
public void AddEdge(GraphVizEdge edge) |
||||
{ |
||||
edges.Add(edge); |
||||
} |
||||
|
||||
public void AddNode(GraphVizNode node) |
||||
{ |
||||
nodes.Add(node); |
||||
} |
||||
|
||||
public void Save(string fileName) |
||||
{ |
||||
using (StreamWriter writer = new StreamWriter(fileName)) |
||||
Save(writer); |
||||
} |
||||
|
||||
static string Escape(string text) |
||||
{ |
||||
if (Regex.IsMatch(text, @"^[\w\d]+$")) { |
||||
return text; |
||||
} else { |
||||
return "\"" + text.Replace("\\", "\\\\").Replace("\r", "").Replace("\n", "\\n").Replace("\"", "\\\"") + "\""; |
||||
} |
||||
} |
||||
|
||||
static void WriteGraphAttribute(TextWriter writer, string name, string value) |
||||
{ |
||||
if (value != null) |
||||
writer.WriteLine("{0}={1};", name, Escape(value)); |
||||
} |
||||
|
||||
internal static void WriteAttribute(TextWriter writer, string name, double? value, ref bool isFirst) |
||||
{ |
||||
if (value != null) { |
||||
WriteAttribute(writer, name, value.Value.ToString(CultureInfo.InvariantCulture), ref isFirst); |
||||
} |
||||
} |
||||
|
||||
internal static void WriteAttribute(TextWriter writer, string name, bool? value, ref bool isFirst) |
||||
{ |
||||
if (value != null) { |
||||
WriteAttribute(writer, name, value.Value ? "true" : "false", ref isFirst); |
||||
} |
||||
} |
||||
|
||||
internal static void WriteAttribute(TextWriter writer, string name, string value, ref bool isFirst) |
||||
{ |
||||
if (value != null) { |
||||
if (isFirst) |
||||
isFirst = false; |
||||
else |
||||
writer.Write(','); |
||||
writer.Write("{0}={1}", name, Escape(value)); |
||||
} |
||||
} |
||||
|
||||
public void Save(TextWriter writer) |
||||
{ |
||||
writer.WriteLine("digraph G {"); |
||||
WriteGraphAttribute(writer, "rankdir", rankdir); |
||||
foreach (GraphVizNode node in nodes) { |
||||
node.Save(writer); |
||||
} |
||||
foreach (GraphVizEdge edge in edges) { |
||||
edge.Save(writer); |
||||
} |
||||
writer.WriteLine("}"); |
||||
} |
||||
} |
||||
|
||||
public sealed class GraphVizEdge |
||||
{ |
||||
public readonly string Source, Target; |
||||
|
||||
/// <summary>edge stroke color</summary>
|
||||
public string color; |
||||
/// <summary>use edge to affect node ranking</summary>
|
||||
public bool? constraint; |
||||
|
||||
public string label; |
||||
|
||||
public string style; |
||||
|
||||
/// <summary>point size of label</summary>
|
||||
public int? fontsize; |
||||
|
||||
public GraphVizEdge(string source, string target) |
||||
{ |
||||
if (source == null) |
||||
throw new ArgumentNullException("source"); |
||||
if (target == null) |
||||
throw new ArgumentNullException("target"); |
||||
this.Source = source; |
||||
this.Target = target; |
||||
} |
||||
|
||||
public GraphVizEdge(int source, int target) |
||||
{ |
||||
this.Source = source.ToString(CultureInfo.InvariantCulture); |
||||
this.Target = target.ToString(CultureInfo.InvariantCulture); |
||||
} |
||||
|
||||
public void Save(TextWriter writer) |
||||
{ |
||||
writer.Write("{0} -> {1} [", Source, Target); |
||||
bool isFirst = true; |
||||
GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "style", style, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "color", color, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "constraint", constraint, ref isFirst); |
||||
writer.WriteLine("];"); |
||||
} |
||||
} |
||||
|
||||
public sealed class GraphVizNode |
||||
{ |
||||
public readonly string ID; |
||||
public string label; |
||||
|
||||
public string labelloc; |
||||
|
||||
/// <summary>point size of label</summary>
|
||||
public int? fontsize; |
||||
|
||||
/// <summary>minimum height in inches</summary>
|
||||
public double? height; |
||||
|
||||
/// <summary>space around label</summary>
|
||||
public string margin; |
||||
|
||||
/// <summary>node shape</summary>
|
||||
public string shape; |
||||
|
||||
public GraphVizNode(string id) |
||||
{ |
||||
if (id == null) |
||||
throw new ArgumentNullException("id"); |
||||
this.ID = id; |
||||
} |
||||
|
||||
public GraphVizNode(int id) |
||||
{ |
||||
this.ID = id.ToString(CultureInfo.InvariantCulture); |
||||
} |
||||
|
||||
public void Save(TextWriter writer) |
||||
{ |
||||
writer.Write(ID); |
||||
writer.Write(" ["); |
||||
bool isFirst = true; |
||||
GraphVizGraph.WriteAttribute(writer, "label", label, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "labelloc", labelloc, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "fontsize", fontsize, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "margin", margin, ref isFirst); |
||||
GraphVizGraph.WriteAttribute(writer, "shape", shape, ref isFirst); |
||||
writer.WriteLine("];"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build"> |
||||
<PropertyGroup> |
||||
<ProjectGuid>{984CC812-9470-4A13-AFF9-CC44068D666C}</ProjectGuid> |
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform> |
||||
<OutputType>Library</OutputType> |
||||
<RootNamespace>ICSharpCode.Decompiler</RootNamespace> |
||||
<AssemblyName>ICSharpCode.Decompiler</AssemblyName> |
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> |
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile> |
||||
<AppDesignerFolder>Properties</AppDesignerFolder> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Platform)' == 'x86' "> |
||||
<PlatformTarget>x86</PlatformTarget> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> |
||||
<OutputPath>bin\Debug\</OutputPath> |
||||
<DebugSymbols>True</DebugSymbols> |
||||
<DebugType>Full</DebugType> |
||||
<Optimize>False</Optimize> |
||||
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow> |
||||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
||||
</PropertyGroup> |
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> |
||||
<OutputPath>bin\Release\</OutputPath> |
||||
<DebugSymbols>False</DebugSymbols> |
||||
<DebugType>None</DebugType> |
||||
<Optimize>True</Optimize> |
||||
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> |
||||
<DefineConstants>TRACE</DefineConstants> |
||||
</PropertyGroup> |
||||
<ItemGroup> |
||||
<Reference Include="System" /> |
||||
<Reference Include="System.Core"> |
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework> |
||||
</Reference> |
||||
<Reference Include="System.Xml" /> |
||||
<Reference Include="System.Xml.Linq"> |
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework> |
||||
</Reference> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Compile Include="CecilExtensions.cs" /> |
||||
<Compile Include="FlowAnalysis\ControlFlowEdge.cs" /> |
||||
<Compile Include="FlowAnalysis\ControlFlowGraph.cs" /> |
||||
<Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" /> |
||||
<Compile Include="FlowAnalysis\ControlFlowNode.cs" /> |
||||
<Compile Include="FlowAnalysis\OpCodeInfo.cs" /> |
||||
<Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaBlock.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaForm.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaFormBuilder.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaInstruction.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaOptimization.cs" /> |
||||
<Compile Include="FlowAnalysis\SsaVariable.cs" /> |
||||
<Compile Include="FlowAnalysis\TransformToSsa.cs" /> |
||||
<Compile Include="GraphVizGraph.cs" /> |
||||
<Compile Include="Properties\AssemblyInfo.cs" /> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj"> |
||||
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project> |
||||
<Name>Mono.Cecil</Name> |
||||
</ProjectReference> |
||||
</ItemGroup> |
||||
<ItemGroup> |
||||
<Folder Include="FlowAnalysis" /> |
||||
</ItemGroup> |
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" /> |
||||
</Project> |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
#region Using directives
|
||||
|
||||
using System; |
||||
using System.Reflection; |
||||
using System.Runtime.InteropServices; |
||||
|
||||
#endregion
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ICSharpCode.Decompiler")] |
||||
[assembly: AssemblyDescription("")] |
||||
[assembly: AssemblyConfiguration("")] |
||||
[assembly: AssemblyCompany("")] |
||||
[assembly: AssemblyProduct("ICSharpCode.Decompiler")] |
||||
[assembly: AssemblyCopyright("Copyright 2011")] |
||||
[assembly: AssemblyTrademark("")] |
||||
[assembly: AssemblyCulture("")] |
||||
|
||||
// This sets the default COM visibility of types in the assembly to invisible.
|
||||
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
|
||||
[assembly: ComVisible(false)] |
||||
|
||||
// The assembly version has following format :
|
||||
//
|
||||
// Major.Minor.Build.Revision
|
||||
//
|
||||
// You can specify all the values or you can use the default the Revision and
|
||||
// Build Numbers by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.0.*")] |
Loading…
Reference in new issue