Browse Source

Build exception handling blocks

pull/728/head
Daniel Grunwald 11 years ago
parent
commit
68ab914293
  1. 5
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 182
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 20
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 9
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  5. 5
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  6. 192
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  7. 57
      ICSharpCode.Decompiler/IL/ILReader.cs
  8. 2
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  9. 21
      ICSharpCode.Decompiler/IL/ILVariable.cs
  10. 74
      ICSharpCode.Decompiler/IL/Instructions.cs
  11. 14
      ICSharpCode.Decompiler/IL/Instructions.tt
  12. 2
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  13. 3
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  14. 1
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  15. 3
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  16. 7
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  17. 67
      ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs
  18. 41
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  19. 112
      ICSharpCode.Decompiler/IL/Instructions/TryCatch.cs
  20. 80
      ICSharpCode.Decompiler/IL/Instructions/TryFinally.cs
  21. 229
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  22. 66
      ICSharpCode.Decompiler/IL/TransformingVisitor.cs
  23. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  24. 34
      ICSharpCode.Decompiler/Util/Interval.cs
  25. 26
      ILSpy/Languages/ILAstLanguage.cs

5
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -101,8 +101,9 @@ namespace ICSharpCode.Decompiler.CSharp
throw new InvalidOperationException("Could not find method in NR type system"); throw new InvalidOperationException("Could not find method in NR type system");
var entityDecl = typeSystemAstBuilder.ConvertEntity(method); var entityDecl = typeSystemAstBuilder.ConvertEntity(method);
if (methodDefinition.HasBody) { if (methodDefinition.HasBody) {
var ilReader = new ILReader(methodDefinition.Body, CancellationToken); var ilReader = new ILReader();
var function = ilReader.CreateFunction(true); var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken);
function.Body = function.Body.AcceptVisitor(new TransformingVisitor());
var body = statementBuilder.ConvertAsBlock(function.Body); var body = statementBuilder.ConvertAsBlock(function.Body);
body.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); body.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
entityDecl.AddChild(body, Roles.Body); entityDecl.AddChild(body, Roles.Body);

182
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -16,7 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using Mono.Cecil;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
@ -40,7 +39,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.compilation = compilation; this.compilation = compilation;
} }
internal struct ConvertedExpression internal struct ConvertedExpression
{ {
public readonly Expression Expression; public readonly Expression Expression;
public readonly IType Type; public readonly IType Type;
@ -50,6 +49,23 @@ namespace ICSharpCode.Decompiler.CSharp
this.Expression = expression; this.Expression = expression;
this.Type = type; this.Type = type;
} }
public Expression ConvertTo(IType targetType)
{
if (targetType.IsKnownType(KnownTypeCode.Boolean))
return ConvertToBoolean();
return Expression;
}
public Expression ConvertToBoolean()
{
if (Type.IsKnownType(KnownTypeCode.Boolean) || Type.Kind == TypeKind.Unknown)
return Expression;
else if (Type.Kind == TypeKind.Pointer)
return new BinaryOperatorExpression(Expression, BinaryOperatorType.InEquality, new NullReferenceExpression());
else
return new BinaryOperatorExpression(Expression, BinaryOperatorType.InEquality, new PrimitiveExpression(0));
}
} }
public Expression Convert(ILInstruction inst) public Expression Convert(ILInstruction inst)
@ -59,34 +75,177 @@ namespace ICSharpCode.Decompiler.CSharp
return expr; return expr;
} }
public AstType ConvertType(TypeReference type) public AstType ConvertType(Mono.Cecil.TypeReference type)
{ {
if (type == null) if (type == null)
return null; return null;
return new SimpleType(type.Name); return new SimpleType(type.Name);
} }
IType GetType(Mono.Cecil.TypeReference type)
{
return SpecialType.UnknownType;
}
ConvertedExpression ConvertVariable(ILVariable variable)
{
var expr = new IdentifierExpression(variable.Name);
expr.AddAnnotation(variable);
return new ConvertedExpression(expr, GetType(variable.Type));
}
ConvertedExpression ConvertArgument(ILInstruction inst) ConvertedExpression ConvertArgument(ILInstruction inst)
{ {
var cexpr = inst.AcceptVisitor(this); var cexpr = inst.AcceptVisitor(this);
cexpr.Expression.AddAnnotation(inst); cexpr.Expression.AddAnnotation(inst);
return cexpr; return cexpr;
} }
ConvertedExpression IsType(IsInst inst)
{
var arg = ConvertArgument(inst.Argument);
return new ConvertedExpression(
new IsExpression(arg.Expression, ConvertType(inst.Type)),
compilation.FindType(TypeCode.Boolean));
}
protected internal override ConvertedExpression VisitIsInst(IsInst inst)
{
var arg = ConvertArgument(inst.Argument);
return new ConvertedExpression(
new AsExpression(arg.Expression, ConvertType(inst.Type)),
GetType(inst.Type));
}
protected internal override ConvertedExpression VisitNewObj(NewObj inst)
{
var oce = new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType));
oce.Arguments.AddRange(inst.Arguments.Select(i => ConvertArgument(i).Expression));
return new ConvertedExpression(oce, GetType(inst.Method.DeclaringType));
}
protected internal override ConvertedExpression VisitLdcI4(LdcI4 inst) protected internal override ConvertedExpression VisitLdcI4(LdcI4 inst)
{ {
return new ConvertedExpression( return new ConvertedExpression(
new PrimitiveExpression(inst.Value), new PrimitiveExpression(inst.Value),
compilation.FindType(KnownTypeCode.Int32)); compilation.FindType(KnownTypeCode.Int32));
}
protected internal override ConvertedExpression VisitLdcI8(LdcI8 inst)
{
return new ConvertedExpression(
new PrimitiveExpression(inst.Value),
compilation.FindType(KnownTypeCode.Int64));
}
protected internal override ConvertedExpression VisitLdcF(LdcF inst)
{
return new ConvertedExpression(
new PrimitiveExpression(inst.Value),
compilation.FindType(KnownTypeCode.Double));
}
protected internal override ConvertedExpression VisitLdStr(LdStr inst)
{
return new ConvertedExpression(
new PrimitiveExpression(inst.Value),
compilation.FindType(KnownTypeCode.String));
}
protected internal override ConvertedExpression VisitLdNull(LdNull inst)
{
return new ConvertedExpression(
new NullReferenceExpression(),
SpecialType.UnknownType);
} }
protected internal override ConvertedExpression VisitLogicNot(LogicNot inst) protected internal override ConvertedExpression VisitLogicNot(LogicNot inst)
{
return LogicNot(new ConvertedExpression(ConvertCondition(inst.Argument), compilation.FindType(KnownTypeCode.Boolean)));
}
ConvertedExpression LogicNot(ConvertedExpression expr)
{
return new ConvertedExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, expr.Expression),
compilation.FindType(KnownTypeCode.Boolean));
}
protected internal override ConvertedExpression VisitLdLoc(LdLoc inst)
{
return ConvertVariable(inst.Variable);
}
protected internal override ConvertedExpression VisitLdLoca(LdLoca inst)
{
var expr = ConvertVariable(inst.Variable);
return new ConvertedExpression(
new DirectionExpression(FieldDirection.Ref, expr.Expression),
new ByReferenceType(expr.Type));
}
protected internal override ConvertedExpression VisitStLoc(StLoc inst)
{
return Assignment(ConvertVariable(inst.Variable), ConvertArgument(inst.Value));
}
protected internal override ConvertedExpression VisitCeq(Ceq inst)
{
if (inst.Left.OpCode == OpCode.IsInst && inst.Right.OpCode == OpCode.LdNull) {
return LogicNot(IsType((IsInst)inst.Left));
} else if (inst.Right.OpCode == OpCode.IsInst && inst.Left.OpCode == OpCode.LdNull) {
return LogicNot(IsType((IsInst)inst.Right));
}
var left = ConvertArgument(inst.Left);
var right = ConvertArgument(inst.Right);
return new ConvertedExpression(
new BinaryOperatorExpression(left.Expression, BinaryOperatorType.Equality, right.Expression),
compilation.FindType(TypeCode.Boolean));
}
protected internal override ConvertedExpression VisitClt(Clt inst)
{
return Comparison(inst, BinaryOperatorType.LessThan);
}
protected internal override ConvertedExpression VisitCgt(Cgt inst)
{
return Comparison(inst, BinaryOperatorType.GreaterThan);
}
protected internal override ConvertedExpression VisitClt_Un(Clt_Un inst)
{
return Comparison(inst, BinaryOperatorType.LessThan, un: true);
}
protected internal override ConvertedExpression VisitCgt_Un(Cgt_Un inst)
{
return Comparison(inst, BinaryOperatorType.GreaterThan, un: true);
}
ConvertedExpression Comparison(BinaryComparisonInstruction inst, BinaryOperatorType op, bool un = false)
{
var left = ConvertArgument(inst.Left);
var right = ConvertArgument(inst.Right);
// TODO: ensure the arguments are signed
// or with _Un: ensure the arguments are unsigned; and that float comparisons are performed unordered
return new ConvertedExpression(
new BinaryOperatorExpression(left.Expression, op, right.Expression),
compilation.FindType(TypeCode.Boolean));
}
ConvertedExpression Assignment(ConvertedExpression left, ConvertedExpression right)
{ {
return new ConvertedExpression( return new ConvertedExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, ConvertCondition(inst.Argument)), new AssignmentExpression(left.Expression, right.ConvertTo(left.Type)),
compilation.FindType(KnownTypeCode.Boolean)); left.Type);
} }
Expression AddConversion(Expression expression, IType type)
{
return expression;
}
protected override ConvertedExpression Default(ILInstruction inst) protected override ConvertedExpression Default(ILInstruction inst)
{ {
return ErrorExpression("OpCode not supported: " + inst.OpCode); return ErrorExpression("OpCode not supported: " + inst.OpCode);
@ -102,12 +261,7 @@ namespace ICSharpCode.Decompiler.CSharp
public Expression ConvertCondition(ILInstruction condition) public Expression ConvertCondition(ILInstruction condition)
{ {
var expr = ConvertArgument(condition); var expr = ConvertArgument(condition);
if (expr.Type.IsKnownType(KnownTypeCode.Boolean) || expr.Type.Kind == TypeKind.Unknown) return expr.ConvertToBoolean();
return expr.Expression; }
else if (expr.Type.Kind == TypeKind.Pointer)
return new BinaryOperatorExpression(expr.Expression, BinaryOperatorType.InEquality, new NullReferenceExpression());
else
return new BinaryOperatorExpression(expr.Expression, BinaryOperatorType.InEquality, new PrimitiveExpression(0));
}
} }
} }

20
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -51,6 +51,11 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return new ExpressionStatement(exprBuilder.Convert(inst)); return new ExpressionStatement(exprBuilder.Convert(inst));
} }
protected internal override Statement VisitNop(Nop inst)
{
return new EmptyStatement();
}
protected internal override Statement VisitIfInstruction(IfInstruction inst) protected internal override Statement VisitIfInstruction(IfInstruction inst)
{ {
@ -92,13 +97,14 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
var tryCatch = new TryCatchStatement(); var tryCatch = new TryCatchStatement();
tryCatch.TryBlock = ConvertAsBlock(inst.TryBlock); tryCatch.TryBlock = ConvertAsBlock(inst.TryBlock);
foreach (var handler in inst.CatchHandlers) { foreach (var handler in inst.Handlers) {
var catchClause = new CatchClause(); var catchClause = new CatchClause();
if (handler.Variable != null) { if (handler.Variable != null) {
catchClause.Type = exprBuilder.ConvertType(handler.Variable.Type); catchClause.Type = exprBuilder.ConvertType(handler.Variable.Type);
catchClause.VariableName = handler.Variable.Name; catchClause.VariableName = handler.Variable.Name;
} }
catchClause.Condition = exprBuilder.ConvertCondition(handler.Filter); if (!handler.Filter.MatchLdcI4(1))
catchClause.Condition = exprBuilder.ConvertCondition(handler.Filter);
catchClause.Body = ConvertAsBlock(handler.Body); catchClause.Body = ConvertAsBlock(handler.Body);
tryCatch.CatchClauses.Add(catchClause); tryCatch.CatchClauses.Add(catchClause);
} }
@ -123,6 +129,16 @@ namespace ICSharpCode.Decompiler.CSharp
return tryCatch; return tryCatch;
} }
protected internal override Statement VisitBlock(Block block)
{
// Block without container
BlockStatement blockStatement = new BlockStatement();
foreach (var inst in block.Instructions) {
blockStatement.Add(Convert(inst));
}
return blockStatement;
}
protected internal override Statement VisitBlockContainer(BlockContainer container) protected internal override Statement VisitBlockContainer(BlockContainer container)
{ {
BlockStatement blockStatement = new BlockStatement(); BlockStatement blockStatement = new BlockStatement();

9
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -53,17 +53,20 @@ namespace ICSharpCode.Decompiler.Disassembler
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
{ {
writer.WriteReference(OffsetToString(instruction.Offset), instruction); if (instruction == null)
writer.Write("null");
else
writer.WriteReference(OffsetToString(instruction.Offset), instruction);
} }
public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer) public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer)
{ {
writer.Write("Try "); writer.Write(".try ");
WriteOffsetReference(writer, exceptionHandler.TryStart); WriteOffsetReference(writer, exceptionHandler.TryStart);
writer.Write('-'); writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.TryEnd); WriteOffsetReference(writer, exceptionHandler.TryEnd);
writer.Write(' '); writer.Write(' ');
writer.Write(exceptionHandler.HandlerType.ToString()); writer.Write(exceptionHandler.HandlerType.ToString().ToLowerInvariant());
if (exceptionHandler.FilterStart != null) { if (exceptionHandler.FilterStart != null) {
writer.Write(' '); writer.Write(' ');
WriteOffsetReference(writer, exceptionHandler.FilterStart); WriteOffsetReference(writer, exceptionHandler.FilterStart);

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -82,11 +82,12 @@
<Compile Include="IL\Instructions\ILFunction.cs" /> <Compile Include="IL\Instructions\ILFunction.cs" />
<Compile Include="IL\Instructions\ILInstruction.cs" /> <Compile Include="IL\Instructions\ILInstruction.cs" />
<Compile Include="IL\Instructions\InstructionCollection.cs" /> <Compile Include="IL\Instructions\InstructionCollection.cs" />
<Compile Include="IL\Instructions\LocalVarInstructions.cs" />
<Compile Include="IL\Instructions\MemoryInstructions.cs" /> <Compile Include="IL\Instructions\MemoryInstructions.cs" />
<Compile Include="IL\Instructions\PatternMatching.cs" />
<Compile Include="IL\Instructions\Return.cs" /> <Compile Include="IL\Instructions\Return.cs" />
<Compile Include="IL\Instructions\SimpleInstruction.cs" /> <Compile Include="IL\Instructions\SimpleInstruction.cs" />
<Compile Include="IL\Instructions\TryCatch.cs" /> <Compile Include="IL\Instructions\TryInstruction.cs" />
<Compile Include="IL\Instructions\TryFinally.cs" />
<Compile Include="IL\Instructions\UnaryInstruction.cs" /> <Compile Include="IL\Instructions\UnaryInstruction.cs" />
<Compile Include="IL\TransformingVisitor.cs" /> <Compile Include="IL\TransformingVisitor.cs" />
<Compile Include="TypesHierarchyHelpers.cs" /> <Compile Include="TypesHierarchyHelpers.cs" />

192
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -23,103 +23,140 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
{ {
class BlockBuilder class BlockBuilder
{ {
readonly Mono.Cecil.Cil.MethodBody body; readonly Mono.Cecil.Cil.MethodBody body;
readonly Stack<ILInstruction> instructionStack;
public BlockBuilder(Mono.Cecil.Cil.MethodBody body, bool instructionInlining) public BlockBuilder(Mono.Cecil.Cil.MethodBody body)
{ {
this.body = body; this.body = body;
this.instructionStack = (instructionInlining ? new Stack<ILInstruction>() : null);
} }
List<TryInstruction> tryInstructionList = new List<TryInstruction>();
Dictionary<int, BlockContainer> handlerContainers = new Dictionary<int, BlockContainer>();
void CreateContainerStructure()
{
List<TryCatch> tryCatchList = new List<TryCatch>();
foreach (var eh in body.ExceptionHandlers) {
var tryRange = new Interval(eh.TryStart.Offset, eh.TryEnd != null ? eh.TryEnd.Offset : body.CodeSize);
var handlerBlock = new BlockContainer();
handlerBlock.ILRange = new Interval(eh.HandlerStart.Offset, eh.HandlerEnd != null ? eh.HandlerEnd.Offset : body.CodeSize);
handlerBlock.Blocks.Add(new Block());
handlerContainers.Add(handlerBlock.ILRange.Start, handlerBlock);
if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Fault || eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally) {
var tryBlock = new BlockContainer();
tryBlock.ILRange = tryRange;
if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Finally)
tryInstructionList.Add(new TryFinally(tryBlock, handlerBlock));
else
tryInstructionList.Add(new TryFault(tryBlock, handlerBlock));
continue;
}
//
var tryCatch = tryCatchList.FirstOrDefault(tc => tc.TryBlock.ILRange == tryRange);
if (tryCatch == null) {
var tryBlock = new BlockContainer();
tryBlock.ILRange = tryRange;
tryCatch = new TryCatch(tryBlock);
tryCatchList.Add(tryCatch);
tryInstructionList.Add(tryCatch);
}
var variable = new ILVariable(VariableKind.Exception, eh.CatchType, handlerBlock.ILRange.Start);
handlerBlock.EntryPoint.Instructions.Add(new LdLoc(variable));
ILInstruction filter;
if (eh.HandlerType == Mono.Cecil.Cil.ExceptionHandlerType.Filter) {
var filterBlock = new BlockContainer();
filterBlock.ILRange = new Interval(eh.FilterStart.Offset, eh.HandlerStart.Offset);
filterBlock.Blocks.Add(new Block());
filterBlock.EntryPoint.Instructions.Add(new LdLoc(variable));
handlerContainers.Add(filterBlock.ILRange.Start, filterBlock);
filter = filterBlock;
} else {
filter = new LdcI4(1);
}
tryCatch.Handlers.Add(new TryCatchHandler(filter, handlerBlock, variable));
}
if (tryInstructionList.Count > 0) {
tryInstructionList = tryInstructionList.OrderBy(tc => tc.TryBlock.ILRange.Start).ThenByDescending(tc => tc.TryBlock.ILRange.End).ToList();
nextTry = tryInstructionList[0];
}
}
int currentTryIndex;
TryInstruction nextTry;
BlockContainer currentContainer; BlockContainer currentContainer;
Block currentBlock; Block currentBlock;
Stack<BlockContainer> containerStack = new Stack<BlockContainer>();
public BlockContainer CreateBlocks(List<ILInstruction> instructions, BitArray incomingBranches) public BlockContainer CreateBlocks(List<ILInstruction> instructions, BitArray incomingBranches)
{ {
currentContainer = new BlockContainer(); CreateContainerStructure();
var mainContainer = new BlockContainer();
incomingBranches[0] = true; // see entrypoint as incoming branch mainContainer.ILRange = new Interval(0, body.CodeSize);
currentContainer = mainContainer;
foreach (var inst in instructions) { foreach (var inst in instructions) {
int start = inst.ILRange.Start; int start = inst.ILRange.Start;
if (incomingBranches[start]) { if (currentBlock == null || incomingBranches[start]) {
// Finish up the previous block // Finish up the previous block
FinalizeCurrentBlock(start, fallthrough: true); FinalizeCurrentBlock(start, fallthrough: true);
// Create the new block // Leave nested containers if necessary
while (start >= currentContainer.ILRange.End) {
currentContainer = containerStack.Pop();
}
// Enter a handler if necessary
BlockContainer handlerContainer;
if (handlerContainers.TryGetValue(start, out handlerContainer)) {
containerStack.Push(currentContainer);
currentContainer = handlerContainer;
currentBlock = handlerContainer.EntryPoint;
} else {
// Create the new block
currentBlock = new Block();
currentContainer.Blocks.Add(currentBlock);
}
currentBlock.ILRange = new Interval(start, start);
}
while (nextTry != null && start == nextTry.TryBlock.ILRange.Start) {
currentBlock.Instructions.Add(nextTry);
containerStack.Push(currentContainer);
currentContainer = (BlockContainer)nextTry.TryBlock;
currentBlock = new Block(); currentBlock = new Block();
currentContainer.Blocks.Add(currentBlock); currentContainer.Blocks.Add(currentBlock);
currentBlock.ILRange = new Interval(start, start); currentBlock.ILRange = new Interval(start, start);
nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex);
} }
if (currentBlock != null) { currentBlock.Instructions.Add(inst);
if (instructionStack == null) { if (inst.HasFlag(InstructionFlags.EndPointUnreachable))
// inlining disabled FinalizeCurrentBlock(inst.ILRange.End, fallthrough: false);
currentBlock.Instructions.Add(inst);
} else {
bool finished;
var inlinedInst = inst.Inline(InstructionFlags.None, instructionStack, out finished);
if (inlinedInst.HasFlag(InstructionFlags.MayBranch)) {
// Values currently on the stack might be used on both sides of the branch,
// so we can't inline them.
FlushInstructionStack();
}
if (inlinedInst.ResultType == StackType.Void) {
// We cannot directly push instructions onto the stack if they don't produce
// a result.
if (finished && instructionStack.Count > 0) {
// Wrap the instruction on top of the stack into an inline block,
// and append our void-typed instruction to the end of that block.
var headInst = instructionStack.Pop();
var block = headInst as Block ?? new Block { Instructions = { headInst } };
block.Instructions.Add(inlinedInst);
instructionStack.Push(block);
} else {
// We can't move incomplete instructions into a nested block
// or the instruction stack was empty
FlushInstructionStack();
currentBlock.Instructions.Add(inst);
}
} else {
// Instruction has a result, so we can push it on the stack normally
instructionStack.Push(inlinedInst);
}
}
if (inst.HasFlag(InstructionFlags.EndPointUnreachable))
FinalizeCurrentBlock(inst.ILRange.End, fallthrough: false);
}
} }
FinalizeCurrentBlock(body.CodeSize, fallthrough: false); FinalizeCurrentBlock(body.CodeSize, fallthrough: false);
ConnectBranches(currentContainer); containerStack.Clear();
return currentContainer; ConnectBranches(mainContainer);
return mainContainer;
} }
private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough) private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough)
{ {
if (currentBlock == null) if (currentBlock == null)
return; return;
FlushInstructionStack();
currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, currentILOffset); currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, currentILOffset);
if (fallthrough) if (fallthrough)
currentBlock.Instructions.Add(new Branch(currentILOffset)); currentBlock.Instructions.Add(new Branch(currentILOffset));
currentBlock = null; currentBlock = null;
} }
private void FlushInstructionStack()
{
if (instructionStack != null && instructionStack.Count > 0) {
// Flush instruction stack
currentBlock.Instructions.AddRange(instructionStack.Reverse());
instructionStack.Clear();
}
}
Stack<BlockContainer> containerStack = new Stack<BlockContainer>();
void ConnectBranches(ILInstruction inst) void ConnectBranches(ILInstruction inst)
{ {
switch (inst.OpCode) { switch (inst.OpCode) {
@ -154,3 +191,42 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
} }
/* Inlining logic: } else {
bool finished;
var inlinedInst = inst.Inline(InstructionFlags.None, instructionStack, out finished);
if (inlinedInst.HasFlag(InstructionFlags.MayBranch)) {
// Values currently on the stack might be used on both sides of the branch,
// so we can't inline them.
FlushInstructionStack();
}
if (inlinedInst.ResultType == StackType.Void) {
// We cannot directly push instructions onto the stack if they don't produce
// a result.
if (finished && instructionStack.Count > 0) {
// Wrap the instruction on top of the stack into an inline block,
// and append our void-typed instruction to the end of that block.
var headInst = instructionStack.Pop();
var block = headInst as Block ?? new Block { Instructions = { headInst } };
block.Instructions.Add(inlinedInst);
instructionStack.Push(block);
} else {
// We can't move incomplete instructions into a nested block
// or the instruction stack was empty
FlushInstructionStack();
currentBlock.Instructions.Add(inst);
}
} else {
// Instruction has a result, so we can push it on the stack normally
instructionStack.Push(inlinedInst);
}
}
private void FlushInstructionStack()
{
if (instructionStack != null && instructionStack.Count > 0) {
// Flush instruction stack
currentBlock.Instructions.AddRange(instructionStack.Reverse());
instructionStack.Clear();
}
}*/

57
ICSharpCode.Decompiler/IL/ILReader.cs

@ -46,28 +46,32 @@ namespace ICSharpCode.Decompiler.IL
return new MetadataToken(reader.ReadUInt32()); return new MetadataToken(reader.ReadUInt32());
} }
readonly Cil.MethodBody body; Cil.MethodBody body;
readonly CancellationToken cancellationToken; TypeSystem typeSystem;
readonly TypeSystem typeSystem;
BlobReader reader; BlobReader reader;
readonly Stack<StackType> stack; Stack<StackType> stack;
ILVariable[] parameterVariables; ILVariable[] parameterVariables;
ILVariable[] localVariables; ILVariable[] localVariables;
BitArray isBranchTarget; BitArray isBranchTarget;
List<ILInstruction> instructionBuilder; List<ILInstruction> instructionBuilder;
public ILReader(Cil.MethodBody body, CancellationToken cancellationToken) // Dictionary that stores stacks for forward jumps
Dictionary<int, ImmutableArray<StackType>> branchStackDict;
void Init(Cil.MethodBody body)
{ {
if (body == null) if (body == null)
throw new ArgumentNullException("body"); throw new ArgumentNullException("body");
this.body = body; this.body = body;
this.cancellationToken = cancellationToken;
this.typeSystem = body.Method.Module.TypeSystem; this.typeSystem = body.Method.Module.TypeSystem;
this.reader = body.GetILReader(); this.reader = body.GetILReader();
this.stack = new Stack<StackType>(body.MaxStackSize); this.stack = new Stack<StackType>(body.MaxStackSize);
this.parameterVariables = InitParameterVariables(body); this.parameterVariables = InitParameterVariables(body);
this.localVariables = body.Variables.Select(v => new ILVariable(v)).ToArray(); this.localVariables = body.Variables.Select(v => new ILVariable(v)).ToArray();
this.instructionBuilder = new List<ILInstruction>();
this.isBranchTarget = new BitArray(body.CodeSize);
this.branchStackDict = new Dictionary<int, ImmutableArray<StackType>>();
} }
IMetadataTokenProvider ReadAndDecodeMetadataToken() IMetadataTokenProvider ReadAndDecodeMetadataToken()
@ -93,27 +97,18 @@ namespace ICSharpCode.Decompiler.IL
Debug.Fail(message); Debug.Fail(message);
} }
// Dictionary that stores stacks for forward jumps void ReadInstructions(Dictionary<int, ImmutableArray<StackType>> outputStacks, CancellationToken cancellationToken)
Dictionary<int, ImmutableArray<StackType>> branchStackDict = new Dictionary<int, ImmutableArray<StackType>>();
void ReadInstructions(Dictionary<int, ImmutableArray<StackType>> outputStacks)
{ {
instructionBuilder = new List<ILInstruction>();
isBranchTarget = new BitArray(body.CodeSize);
stack.Clear();
branchStackDict.Clear();
// Fill isBranchTarget and branchStackDict based on exception handlers // Fill isBranchTarget and branchStackDict based on exception handlers
foreach (var eh in body.ExceptionHandlers) { foreach (var eh in body.ExceptionHandlers) {
isBranchTarget[eh.TryStart.Offset] = true;
if (eh.FilterStart != null) { if (eh.FilterStart != null) {
isBranchTarget[eh.FilterStart.Offset] = true; isBranchTarget[eh.FilterStart.Offset] = true;
branchStackDict[eh.FilterStart.Offset] = ImmutableArray.Create(StackType.O); branchStackDict[eh.FilterStart.Offset] = ImmutableArray.Create(eh.CatchType.GetStackType());
} }
if (eh.HandlerStart != null) { if (eh.HandlerStart != null) {
isBranchTarget[eh.HandlerStart.Offset] = true; isBranchTarget[eh.HandlerStart.Offset] = true;
if (eh.HandlerType == Cil.ExceptionHandlerType.Catch || eh.HandlerType == Cil.ExceptionHandlerType.Filter) if (eh.HandlerType == Cil.ExceptionHandlerType.Catch || eh.HandlerType == Cil.ExceptionHandlerType.Filter)
branchStackDict[eh.HandlerStart.Offset] = ImmutableArray.Create(StackType.O); branchStackDict[eh.HandlerStart.Offset] = ImmutableArray.Create(eh.CatchType.GetStackType());
else else
branchStackDict[eh.HandlerStart.Offset] = ImmutableArray<StackType>.Empty; branchStackDict[eh.HandlerStart.Offset] = ImmutableArray<StackType>.Empty;
} }
@ -141,10 +136,14 @@ namespace ICSharpCode.Decompiler.IL
} }
} }
public void WriteTypedIL(ITextOutput output) /// <summary>
/// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout.
/// </summary>
public void WriteTypedIL(Cil.MethodBody body, ITextOutput output, CancellationToken cancellationToken = default(CancellationToken))
{ {
Init(body);
var outputStacks = new Dictionary<int, ImmutableArray<StackType>>(); var outputStacks = new Dictionary<int, ImmutableArray<StackType>>();
ReadInstructions(outputStacks); ReadInstructions(outputStacks, cancellationToken);
foreach (var inst in instructionBuilder) { foreach (var inst in instructionBuilder) {
output.Write(" ["); output.Write(" [");
bool isFirstElement = true; bool isFirstElement = true;
@ -169,11 +168,14 @@ namespace ICSharpCode.Decompiler.IL
new Disassembler.MethodBodyDisassembler(output, false, cancellationToken).WriteExceptionHandlers(body); new Disassembler.MethodBodyDisassembler(output, false, cancellationToken).WriteExceptionHandlers(body);
} }
public ILFunction CreateFunction(bool instructionInlining) /// <summary>
{ /// Decodes the specified method body and returns an ILFunction.
if (instructionBuilder == null) /// </summary>
ReadInstructions(null); public ILFunction ReadIL(Cil.MethodBody body, CancellationToken cancellationToken = default(CancellationToken))
var container = new BlockBuilder(body, instructionInlining).CreateBlocks(instructionBuilder, isBranchTarget); {
Init(body);
ReadInstructions(null, cancellationToken);
var container = new BlockBuilder(body).CreateBlocks(instructionBuilder, isBranchTarget);
var function = new ILFunction(body.Method, container); var function = new ILFunction(body.Method, container);
function.Variables.AddRange(parameterVariables); function.Variables.AddRange(parameterVariables);
function.Variables.AddRange(localVariables); function.Variables.AddRange(localVariables);
@ -751,6 +753,11 @@ namespace ICSharpCode.Decompiler.IL
int target = shortForm ? reader.ReadSByte() : reader.ReadInt32(); int target = shortForm ? reader.ReadSByte() : reader.ReadInt32();
target += reader.Position; target += reader.Position;
ILInstruction condition = Pop(); ILInstruction condition = Pop();
if (condition.ResultType == StackType.O) {
// introduce explicit comparison with null
condition = new Ceq(condition, new LdNull());
negate = !negate;
}
if (negate) { if (negate) {
condition = new LogicNot(condition); condition = new LogicNot(condition);
} }

2
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.IL
public static StackType GetStackType(this TypeReference typeRef) public static StackType GetStackType(this TypeReference typeRef)
{ {
return typeRef.SkipModifiers().MetadataType.GetStackType(); return typeRef.SkipModifiers().MetadataType.GetStackType();
} }
public static TypeReference SkipModifiers(this TypeReference typeRef) public static TypeReference SkipModifiers(this TypeReference typeRef)
{ {

21
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -31,6 +31,10 @@ namespace ICSharpCode.Decompiler.IL
{ {
Local, Local,
Parameter, Parameter,
/// <summary>
/// Variable created for exception handler
/// </summary>
Exception
} }
public class ILVariable public class ILVariable
@ -39,6 +43,21 @@ namespace ICSharpCode.Decompiler.IL
public readonly TypeReference Type; public readonly TypeReference Type;
public readonly int Index; public readonly int Index;
/// <summary>
/// Number of ldloc instructions referencing this variable.
/// </summary>
public int LoadCount;
/// <summary>
/// Number of stloc instructions referencing this variable.
/// </summary>
public int StoreCount;
/// <summary>
/// Number of ldloca instructions referencing this variable.
/// </summary>
public int AddressCount;
readonly object CecilObject; readonly object CecilObject;
public ILVariable(VariableKind kind, TypeReference type, int index) public ILVariable(VariableKind kind, TypeReference type, int index)
@ -60,6 +79,7 @@ namespace ICSharpCode.Decompiler.IL
: this(VariableKind.Parameter, p.ParameterType, p.Index) : this(VariableKind.Parameter, p.ParameterType, p.Index)
{ {
this.CecilObject = p; this.CecilObject = p;
this.StoreCount = 1; // count the initial store when the method is called with an argument
} }
public string Name { public string Name {
@ -85,6 +105,7 @@ namespace ICSharpCode.Decompiler.IL
output.WriteDefinition(this.Name, CecilObject ?? this, isLocal: true); output.WriteDefinition(this.Name, CecilObject ?? this, isLocal: true);
output.Write(" : "); output.Write(" : ");
Type.WriteTo(output); Type.WriteTo(output);
output.Write("({0} ldloc, {1} ldloca, {2} stloc)", LoadCount, AddressCount, StoreCount);
} }
internal void WriteTo(ITextOutput output) internal void WriteTo(ITextOutput output)

74
ICSharpCode.Decompiler/IL/Instructions.cs

@ -606,7 +606,7 @@ namespace ICSharpCode.Decompiler.IL
} }
/// <summary>Try-catch statement</summary> /// <summary>Try-catch statement</summary>
public sealed partial class TryCatch : ILInstruction public sealed partial class TryCatch : TryInstruction
{ {
public override T AcceptVisitor<T>(ILVisitor<T> visitor) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
@ -666,40 +666,9 @@ namespace ICSharpCode.Decompiler.IL
} }
/// <summary>Try-finally statement</summary> /// <summary>Try-finally statement</summary>
public sealed partial class TryFinally : ILInstruction public sealed partial class TryFinally : TryInstruction
{ {
public TryFinally(ILInstruction tryBlock, ILInstruction finallyBlock) : base(OpCode.TryFinally)
{
this.TryBlock = tryBlock;
this.FinallyBlock = finallyBlock;
}
ILInstruction tryBlock;
public ILInstruction TryBlock {
get { return this.tryBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.tryBlock, value);
}
}
ILInstruction finallyBlock;
public ILInstruction FinallyBlock {
get { return this.finallyBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.finallyBlock, value);
}
}
public override IEnumerable<ILInstruction> Children {
get {
yield return this.tryBlock;
yield return this.finallyBlock;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = this.tryBlock.AcceptVisitor(visitor);
this.FinallyBlock = this.finallyBlock.AcceptVisitor(visitor);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{ {
return visitor.VisitTryFinally(this); return visitor.VisitTryFinally(this);
@ -707,40 +676,9 @@ namespace ICSharpCode.Decompiler.IL
} }
/// <summary>Try-fault statement</summary> /// <summary>Try-fault statement</summary>
public sealed partial class TryFault : ILInstruction public sealed partial class TryFault : TryInstruction
{ {
public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFault)
{
this.TryBlock = tryBlock;
this.FaultBlock = faultBlock;
}
ILInstruction tryBlock;
public ILInstruction TryBlock {
get { return this.tryBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.tryBlock, value);
}
}
ILInstruction faultBlock;
public ILInstruction FaultBlock {
get { return this.faultBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.faultBlock, value);
}
}
public override IEnumerable<ILInstruction> Children {
get {
yield return this.tryBlock;
yield return this.faultBlock;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = this.tryBlock.AcceptVisitor(visitor);
this.FaultBlock = this.faultBlock.AcceptVisitor(visitor);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor) public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{ {
return visitor.VisitTryFault(this); return visitor.VisitTryFault(this);
@ -1560,7 +1498,7 @@ namespace ICSharpCode.Decompiler.IL
readonly TypeReference type; readonly TypeReference type;
/// <summary>Returns the type operand.</summary> /// <summary>Returns the type operand.</summary>
public TypeReference Type { get { return type; } } public TypeReference Type { get { return type; } }
public override StackType ResultType { get { return type.GetStackType(); } } public override StackType ResultType { get { return StackType.O; } }
public override void WriteTo(ITextOutput output) public override void WriteTo(ITextOutput output)
{ {
output.Write(OpCode); output.Write(OpCode);

14
ICSharpCode.Decompiler/IL/Instructions.tt

@ -72,22 +72,16 @@
new ChildInfo("falseInst"), new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo), }), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch", "Try-catch statement", new OpCode("try.catch", "Try-catch statement",
CustomConstructor, CustomComputeFlags, CustomWriteTo), BaseClass("TryInstruction"), CustomConstructor, CustomComputeFlags, CustomWriteTo),
new OpCode("try.catch.handler", "Catch handler within a try-catch statement", new OpCode("try.catch.handler", "Catch handler within a try-catch statement",
CustomChildren(new [] { CustomChildren(new [] {
new ChildInfo("filter") { IsArgument = true }, new ChildInfo("filter") { IsArgument = true },
new ChildInfo("body"), new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, ResultType("Void")), }), HasVariableOperand, CustomWriteTo, ResultType("Void")),
new OpCode("try.finally", "Try-finally statement", new OpCode("try.finally", "Try-finally statement",
CustomChildren(new [] { BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new ChildInfo("tryBlock"),
new ChildInfo("finallyBlock"),
}), CustomWriteTo, CustomComputeFlags),
new OpCode("try.fault", "Try-fault statement", new OpCode("try.fault", "Try-fault statement",
CustomChildren(new [] { BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new ChildInfo("tryBlock"),
new ChildInfo("faultBlock"),
}), CustomWriteTo, CustomComputeFlags),
new OpCode("debug.break", "Breakpoint instruction", new OpCode("debug.break", "Breakpoint instruction",
NoArguments, VoidResult, SideEffect), NoArguments, VoidResult, SideEffect),
new OpCode("ceq", "Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.", new OpCode("ceq", "Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.",
@ -154,7 +148,7 @@
new OpCode("castclass", "Casts an object to a class.", new OpCode("castclass", "Casts an object to a class.",
CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")), CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")),
new OpCode("isinst", "Test if object is instance of class or interface.", new OpCode("isinst", "Test if object is instance of class or interface.",
CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("type.GetStackType()")), CustomClassName("IsInst"), Unary, HasTypeOperand, ResultType("O")),
new OpCode("ldobj", "Indirect load (ref/pointer dereference).", new OpCode("ldobj", "Indirect load (ref/pointer dereference).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),

2
ICSharpCode.Decompiler/IL/Instructions/Block.cs

@ -63,7 +63,7 @@ namespace ICSharpCode.Decompiler.IL
output.WriteLine(); output.WriteLine();
} }
output.Unindent(); output.Unindent();
output.WriteLine("}"); output.Write("}");
} }
public override IEnumerable<ILInstruction> Children { public override IEnumerable<ILInstruction> Children {

3
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -54,9 +54,10 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in Blocks) { foreach (var inst in Blocks) {
inst.WriteTo(output); inst.WriteTo(output);
output.WriteLine(); output.WriteLine();
output.WriteLine();
} }
output.Unindent(); output.Unindent();
output.WriteLine("}"); output.Write("}");
} }
public override IEnumerable<ILInstruction> Children { public override IEnumerable<ILInstruction> Children {

1
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -49,6 +49,7 @@ namespace ICSharpCode.Decompiler.IL
output.WriteLine(); output.WriteLine();
body.WriteTo(output); body.WriteTo(output);
output.WriteLine();
output.Unindent(); output.Unindent();
output.WriteLine("}"); output.WriteLine("}");
} }

3
ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Represents a decoded IL instruction /// Represents a decoded IL instruction
/// </summary> /// </summary>
public abstract class ILInstruction public abstract partial class ILInstruction
{ {
public readonly OpCode OpCode; public readonly OpCode OpCode;
@ -217,6 +217,5 @@ namespace ICSharpCode.Decompiler.IL
newChild.ReleaseRef(); newChild.ReleaseRef();
InvalidateFlags(); InvalidateFlags();
} }
} }
} }

7
ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs

@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
@ -76,5 +77,11 @@ namespace ICSharpCode.Decompiler.IL
} }
return removed; return removed;
} }
public void ReplaceList(IEnumerable<T> newList)
{
Clear();
this.AddRange(newList);
}
} }
} }

67
ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs

@ -0,0 +1,67 @@
// Copyright (c) 2014 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.IL
{
partial class LdLoc
{
protected override void Connected()
{
base.Connected();
variable.LoadCount++;
}
protected override void Disconnected()
{
variable.LoadCount--;
base.Disconnected();
}
}
partial class LdLoca
{
protected override void Connected()
{
base.Connected();
variable.AddressCount++;
}
protected override void Disconnected()
{
variable.AddressCount--;
base.Disconnected();
}
}
partial class StLoc
{
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
}
}

41
ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs

@ -0,0 +1,41 @@
// Copyright (c) 2014 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.IL
{
partial class ILInstruction
{
public bool MatchLdcI4(int val)
{
return OpCode == OpCode.LdcI4 && ((LdcI4)this).Value == val;
}
public bool MatchLdcI4(out int val)
{
var inst = this as LdcI4;
if (inst != null) {
val = inst.Value;
return true;
}
val = 0;
return false;
}
}
}

112
ICSharpCode.Decompiler/IL/Instructions/TryCatch.cs

@ -1,112 +0,0 @@
// Copyright (c) 2014 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;
namespace ICSharpCode.Decompiler.IL
{
partial class TryCatch
{
public readonly InstructionCollection<TryCatchHandler> CatchHandlers;
public TryCatch(ILInstruction tryBlock) : base(OpCode.TryCatch)
{
this.TryBlock = tryBlock;
this.CatchHandlers = new InstructionCollection<TryCatchHandler>(this);
}
ILInstruction tryBlock;
public ILInstruction TryBlock {
get { return this.tryBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.tryBlock, value);
}
}
public override void WriteTo(ITextOutput output)
{
output.Write("try ");
tryBlock.WriteTo(output);
foreach (var handler in CatchHandlers) {
output.Write(' ');
handler.WriteTo(output);
}
}
public override StackType ResultType {
get { return StackType.Void; }
}
protected override InstructionFlags ComputeFlags()
{
var flags = tryBlock.Flags;
foreach (var handler in CatchHandlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags);
return flags;
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
public override IEnumerable<ILInstruction> Children {
get {
yield return tryBlock;
foreach (var handler in CatchHandlers)
yield return handler;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = tryBlock.AcceptVisitor(visitor);
for (int i = 0; i < CatchHandlers.Count; i++) {
if (CatchHandlers[i].AcceptVisitor(visitor) != CatchHandlers[i])
throw new InvalidOperationException("Cannot transform a TryCatchHandler");
}
}
}
partial class TryCatchHandler
{
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
public override void WriteTo(ITextOutput output)
{
output.Write(" catch ");
if (variable != null) {
output.WriteDefinition(variable.Name, variable);
output.Write(" : ");
Disassembler.DisassemblerHelpers.WriteOperand(output, variable.Type);
}
output.Write(" if (");
filter.WriteTo(output);
output.Write(')');
output.Write(' ');
body.WriteTo(output);
}
}
}

80
ICSharpCode.Decompiler/IL/Instructions/TryFinally.cs

@ -1,80 +0,0 @@
// Copyright (c) 2014 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;
namespace ICSharpCode.Decompiler.IL
{
partial class TryFinally
{
public override void WriteTo(ITextOutput output)
{
output.Write("try ");
tryBlock.WriteTo(output);
output.Write(" finally ");
finallyBlock.WriteTo(output);
}
public override StackType ResultType {
get {
return tryBlock.ResultType;
}
}
protected override InstructionFlags ComputeFlags()
{
// if the endpoint of either the try or the finally is unreachable, the endpoint of the try-finally will be unreachable
return tryBlock.Flags | finallyBlock.Flags;
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
}
partial class TryFault
{
public override void WriteTo(ITextOutput output)
{
output.Write("try ");
tryBlock.WriteTo(output);
output.Write(" fault ");
faultBlock.WriteTo(output);
}
public override StackType ResultType {
get { return tryBlock.ResultType; }
}
protected override InstructionFlags ComputeFlags()
{
// The endpoint of the try-fault is unreachable only if both endpoints are unreachable
return IfInstruction.CombineFlags(tryBlock.Flags, faultBlock.Flags);
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
}
}

229
ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs

@ -0,0 +1,229 @@
// Copyright (c) 2014 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;
namespace ICSharpCode.Decompiler.IL
{
public abstract class TryInstruction : ILInstruction
{
protected TryInstruction(OpCode opCode, ILInstruction tryBlock) : base(opCode)
{
this.TryBlock = tryBlock;
}
ILInstruction tryBlock;
public ILInstruction TryBlock {
get { return this.tryBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.tryBlock, value);
}
}
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
}
partial class TryCatch : TryInstruction
{
public readonly InstructionCollection<TryCatchHandler> Handlers;
public TryCatch(ILInstruction tryBlock) : base(OpCode.TryCatch, tryBlock)
{
this.Handlers = new InstructionCollection<TryCatchHandler>(this);
}
public override void WriteTo(ITextOutput output)
{
output.Write(".try ");
TryBlock.WriteTo(output);
foreach (var handler in Handlers) {
output.Write(' ');
handler.WriteTo(output);
}
}
public override StackType ResultType {
get { return StackType.Void; }
}
protected override InstructionFlags ComputeFlags()
{
var flags = TryBlock.Flags;
foreach (var handler in Handlers)
flags = IfInstruction.CombineFlags(flags, handler.Flags);
return flags;
}
public override IEnumerable<ILInstruction> Children {
get {
yield return TryBlock;
foreach (var handler in Handlers)
yield return handler;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = TryBlock.AcceptVisitor(visitor);
for (int i = 0; i < Handlers.Count; i++) {
if (Handlers[i].AcceptVisitor(visitor) != Handlers[i])
throw new InvalidOperationException("Cannot transform a TryCatchHandler");
}
}
}
partial class TryCatchHandler
{
internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack<ILInstruction> instructionStack, out bool finished)
{
finished = false;
return this;
}
public override void WriteTo(ITextOutput output)
{
output.Write("catch ");
if (variable != null) {
output.WriteDefinition(variable.Name, variable);
output.Write(" : ");
Disassembler.DisassemblerHelpers.WriteOperand(output, variable.Type);
}
output.Write(" if (");
filter.WriteTo(output);
output.Write(')');
output.Write(' ');
body.WriteTo(output);
}
protected override void Connected()
{
base.Connected();
variable.StoreCount++;
}
protected override void Disconnected()
{
variable.StoreCount--;
base.Disconnected();
}
}
partial class TryFinally
{
public TryFinally(ILInstruction tryBlock, ILInstruction finallyBlock) : base(OpCode.TryFinally, tryBlock)
{
this.FinallyBlock = finallyBlock;
}
ILInstruction finallyBlock;
public ILInstruction FinallyBlock {
get { return this.finallyBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.finallyBlock, value);
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(".try ");
TryBlock.WriteTo(output);
output.Write(" finally ");
finallyBlock.WriteTo(output);
}
public override StackType ResultType {
get {
return TryBlock.ResultType;
}
}
protected override InstructionFlags ComputeFlags()
{
// if the endpoint of either the try or the finally is unreachable, the endpoint of the try-finally will be unreachable
return TryBlock.Flags | finallyBlock.Flags;
}
public override IEnumerable<ILInstruction> Children {
get {
yield return TryBlock;
yield return finallyBlock;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = TryBlock.AcceptVisitor(visitor);
this.FinallyBlock = finallyBlock.AcceptVisitor(visitor);
}
}
partial class TryFault
{
public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFinally, tryBlock)
{
this.FaultBlock = faultBlock;
}
ILInstruction faultBlock;
public ILInstruction FaultBlock {
get { return this.faultBlock; }
set {
ValidateChild(value);
SetChildInstruction(ref this.faultBlock, value);
}
}
public override void WriteTo(ITextOutput output)
{
output.Write(".try ");
TryBlock.WriteTo(output);
output.Write(" fault ");
faultBlock.WriteTo(output);
}
public override StackType ResultType {
get { return TryBlock.ResultType; }
}
protected override InstructionFlags ComputeFlags()
{
// The endpoint of the try-fault is unreachable only if both endpoints are unreachable
return IfInstruction.CombineFlags(TryBlock.Flags, faultBlock.Flags);
}
public override IEnumerable<ILInstruction> Children {
get {
yield return TryBlock;
yield return faultBlock;
}
}
public override void TransformChildren(ILVisitor<ILInstruction> visitor)
{
this.TryBlock = TryBlock.AcceptVisitor(visitor);
this.FaultBlock = faultBlock.AcceptVisitor(visitor);
}
}
}

66
ICSharpCode.Decompiler/IL/TransformingVisitor.cs

@ -17,7 +17,9 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL
@ -25,6 +27,12 @@ namespace ICSharpCode.Decompiler.IL
/// <summary> /// <summary>
/// Visitor that applies a list of transformations to the IL Ast. /// Visitor that applies a list of transformations to the IL Ast.
/// </summary> /// </summary>
/// <remarks>
/// The base class performs:
/// - variable inlining
/// - cleanup after branch inlining
/// - removal of unnecessary blocks and block containers
/// </remarks>
public class TransformingVisitor : ILVisitor<ILInstruction> public class TransformingVisitor : ILVisitor<ILInstruction>
{ {
protected override ILInstruction Default(ILInstruction inst) protected override ILInstruction Default(ILInstruction inst)
@ -33,6 +41,7 @@ namespace ICSharpCode.Decompiler.IL
return inst; return inst;
} }
/*
protected internal override ILInstruction VisitBranch(Branch inst) protected internal override ILInstruction VisitBranch(Branch inst)
{ {
// If this branch is the only edge to the target block, we can inline the target block here: // If this branch is the only edge to the target block, we can inline the target block here:
@ -41,6 +50,63 @@ namespace ICSharpCode.Decompiler.IL
} }
return base.VisitBranch(inst); return base.VisitBranch(inst);
} }
*/
protected bool removeNops;
protected internal override ILInstruction VisitBlock(Block block)
{
Stack<ILInstruction> stack = new Stack<ILInstruction>();
List<ILInstruction> output = new List<ILInstruction>();
for (int i = 0; i < block.Instructions.Count; i++) {
var inst = block.Instructions[i];
int stackCountBefore;
bool finished;
do {
inst = inst.AcceptVisitor(this);
stackCountBefore = stack.Count;
inst = inst.Inline(InstructionFlags.None, stack, out finished);
} while (stack.Count != stackCountBefore); // repeat transformations when something was inlined
if (inst.HasFlag(InstructionFlags.MayBranch)) {
// Values currently on the stack might be used on both sides of the branch,
// so we can't inline them.
FlushInstructionStack(stack, output);
}
if (inst.ResultType == StackType.Void) {
// We cannot directly push instructions onto the stack if they don't produce
// a result.
if (finished && stack.Count > 0) {
// Wrap the instruction on top of the stack into an inline block,
// and append our void-typed instruction to the end of that block.
var headInst = stack.Pop();
var nestedBlock = headInst as Block ?? new Block { Instructions = { headInst } };
nestedBlock.Instructions.Add(inst);
stack.Push(block);
} else {
// We can't move incomplete instructions into a nested block
// or the instruction stack was empty
FlushInstructionStack(stack, output);
output.Add(inst);
}
} else {
// Instruction has a result, so we can push it on the stack normally
stack.Push(inst);
}
}
FlushInstructionStack(stack, output);
if (!(block.Parent is BlockContainer)) {
if (output.Count == 1)
return output[0];
}
block.Instructions.ReplaceList(output);
return block;
}
void FlushInstructionStack(Stack<ILInstruction> stack, List<ILInstruction> output)
{
output.AddRange(stack.Reverse());
stack.Clear();
}
protected internal override ILInstruction VisitBlockContainer(BlockContainer container) protected internal override ILInstruction VisitBlockContainer(BlockContainer container)
{ {

1
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -87,6 +87,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="ExceptionHandling.cs" />
<Compile Include="Helpers\CodeAssert.cs" /> <Compile Include="Helpers\CodeAssert.cs" />
<Compile Include="Helpers\RemoveCompilerAttribute.cs" /> <Compile Include="Helpers\RemoveCompilerAttribute.cs" />
</ItemGroup> </ItemGroup>

34
ICSharpCode.Decompiler/Util/Interval.cs

@ -11,10 +11,8 @@ namespace ICSharpCode.Decompiler
/// Represents a half-open interval. /// Represents a half-open interval.
/// The start position is inclusive; but the end position is exclusive. /// The start position is inclusive; but the end position is exclusive.
/// </summary> /// </summary>
public struct Interval public struct Interval : IEquatable<Interval>
{ {
public static readonly Interval Empty = new Interval(0, 0);
/// <summary> /// <summary>
/// Gets the inclusive start of the interval. /// Gets the inclusive start of the interval.
/// </summary> /// </summary>
@ -43,6 +41,36 @@ namespace ICSharpCode.Decompiler
{ {
return string.Format("[{0}..{1})", Start, End); return string.Format("[{0}..{1})", Start, End);
} }
#region Equals and GetHashCode implementation
public override bool Equals(object obj)
{
return (obj is Interval) && Equals((Interval)obj);
}
public bool Equals(Interval other)
{
return this.Start == other.Start && this.End == other.End;
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked {
hashCode += 1000000007 * Start.GetHashCode();
hashCode += 1000000009 * End.GetHashCode();
}
return hashCode;
}
public static bool operator ==(Interval lhs, Interval rhs) {
return lhs.Equals(rhs);
}
public static bool operator !=(Interval lhs, Interval rhs) {
return !(lhs == rhs);
}
#endregion
} }
/// <summary> /// <summary>

26
ILSpy/Languages/ILAstLanguage.cs

@ -27,13 +27,12 @@ using Mono.Cecil;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
{ {
#if DEBUG #if DEBUG
/// <summary> /// <summary>
/// Represents the ILAst "language" used for debugging purposes. /// Represents the ILAst "language" used for debugging purposes.
/// </summary> /// </summary>
abstract class ILAstLanguage : Language abstract class ILAstLanguage : Language
{ {
bool inlineVariables = true;
//ILAstOptimizationStep? abortBeforeStep; //ILAstOptimizationStep? abortBeforeStep;
readonly string name; readonly string name;
@ -89,8 +88,8 @@ namespace ICSharpCode.ILSpy
internal static IEnumerable<ILAstLanguage> GetDebugLanguages() internal static IEnumerable<ILAstLanguage> GetDebugLanguages()
{ {
yield return new TypedIL(); yield return new TypedIL();
yield return new BlockIL(false); yield return new BlockIL(null);
yield return new BlockIL(true); yield return new BlockIL(new TransformingVisitor());
//yield return new ILAstLanguage { name = "ILAst (unoptimized)", inlineVariables = false }; //yield return new ILAstLanguage { name = "ILAst (unoptimized)", inlineVariables = false };
//string nextName = "ILAst (variable splitting)"; //string nextName = "ILAst (variable splitting)";
//foreach (ILAstOptimizationStep step in Enum.GetValues(typeof(ILAstOptimizationStep))) { //foreach (ILAstOptimizationStep step in Enum.GetValues(typeof(ILAstOptimizationStep))) {
@ -129,18 +128,18 @@ namespace ICSharpCode.ILSpy
base.DecompileMethod(method, output, options); base.DecompileMethod(method, output, options);
if (!method.HasBody) if (!method.HasBody)
return; return;
ILReader reader = new ILReader(method.Body, options.CancellationToken); ILReader reader = new ILReader();
reader.WriteTypedIL(output); reader.WriteTypedIL(method.Body, output, options.CancellationToken);
} }
} }
class BlockIL : ILAstLanguage class BlockIL : ILAstLanguage
{ {
readonly bool instructionInlining; readonly ILVisitor<ILInstruction> transformer;
public BlockIL(bool instructionInlining) : base(instructionInlining ? "ILAst (blocks+inlining)" : "ILAst (blocks)") public BlockIL(ILVisitor<ILInstruction> transformer) : base(transformer == null ? "ILAst (blocks)" : "ILAst (" + transformer.ToString() + ")")
{ {
this.instructionInlining = instructionInlining; this.transformer = transformer;
} }
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options)
@ -148,10 +147,13 @@ namespace ICSharpCode.ILSpy
base.DecompileMethod(method, output, options); base.DecompileMethod(method, output, options);
if (!method.HasBody) if (!method.HasBody)
return; return;
ILReader reader = new ILReader(method.Body, options.CancellationToken); ILReader reader = new ILReader();
reader.CreateFunction(instructionInlining).WriteTo(output); ILInstruction il = reader.ReadIL(method.Body, options.CancellationToken);
if (transformer != null)
il = il.AcceptVisitor(transformer);
il.WriteTo(output);
} }
} }
} }
#endif #endif
} }

Loading…
Cancel
Save