.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

257 lines
9.2 KiB

// 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
{
/// <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.EndFinally:
if (stackSize != 0)
throw new NotSupportedException("stacksize must be 0 in endfinally edge");
newStackSize = 0;
break;
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));
}
}
}
}
}