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. 172
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 18
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  4. 7
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  5. 5
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  6. 182
      ICSharpCode.Decompiler/IL/BlockBuilder.cs
  7. 55
      ICSharpCode.Decompiler/IL/ILReader.cs
  8. 21
      ICSharpCode.Decompiler/IL/ILVariable.cs
  9. 74
      ICSharpCode.Decompiler/IL/Instructions.cs
  10. 14
      ICSharpCode.Decompiler/IL/Instructions.tt
  11. 2
      ICSharpCode.Decompiler/IL/Instructions/Block.cs
  12. 3
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  13. 1
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  14. 3
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  15. 7
      ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs
  16. 67
      ICSharpCode.Decompiler/IL/Instructions/LocalVarInstructions.cs
  17. 41
      ICSharpCode.Decompiler/IL/Instructions/PatternMatching.cs
  18. 112
      ICSharpCode.Decompiler/IL/Instructions/TryCatch.cs
  19. 80
      ICSharpCode.Decompiler/IL/Instructions/TryFinally.cs
  20. 229
      ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs
  21. 66
      ICSharpCode.Decompiler/IL/TransformingVisitor.cs
  22. 1
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  23. 34
      ICSharpCode.Decompiler/Util/Interval.cs
  24. 26
      ILSpy/Languages/ILAstLanguage.cs

5
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

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

172
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using Mono.Cecil;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem;
@ -50,6 +49,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -50,6 +49,23 @@ namespace ICSharpCode.Decompiler.CSharp
this.Expression = expression;
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)
@ -59,13 +75,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -59,13 +75,25 @@ namespace ICSharpCode.Decompiler.CSharp
return expr;
}
public AstType ConvertType(TypeReference type)
public AstType ConvertType(Mono.Cecil.TypeReference type)
{
if (type == null)
return null;
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)
{
var cexpr = inst.AcceptVisitor(this);
@ -73,6 +101,29 @@ namespace ICSharpCode.Decompiler.CSharp @@ -73,6 +101,29 @@ namespace ICSharpCode.Decompiler.CSharp
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)
{
return new ConvertedExpression(
@ -80,13 +131,121 @@ namespace ICSharpCode.Decompiler.CSharp @@ -80,13 +131,121 @@ namespace ICSharpCode.Decompiler.CSharp
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)
{
return LogicNot(new ConvertedExpression(ConvertCondition(inst.Argument), compilation.FindType(KnownTypeCode.Boolean)));
}
ConvertedExpression LogicNot(ConvertedExpression expr)
{
return new ConvertedExpression(
new UnaryOperatorExpression(UnaryOperatorType.Not, ConvertCondition(inst.Argument)),
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(
new AssignmentExpression(left.Expression, right.ConvertTo(left.Type)),
left.Type);
}
Expression AddConversion(Expression expression, IType type)
{
return expression;
}
protected override ConvertedExpression Default(ILInstruction inst)
{
return ErrorExpression("OpCode not supported: " + inst.OpCode);
@ -102,12 +261,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -102,12 +261,7 @@ namespace ICSharpCode.Decompiler.CSharp
public Expression ConvertCondition(ILInstruction condition)
{
var expr = ConvertArgument(condition);
if (expr.Type.IsKnownType(KnownTypeCode.Boolean) || expr.Type.Kind == TypeKind.Unknown)
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));
return expr.ConvertToBoolean();
}
}
}

18
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -52,6 +52,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -52,6 +52,11 @@ namespace ICSharpCode.Decompiler.CSharp
return new ExpressionStatement(exprBuilder.Convert(inst));
}
protected internal override Statement VisitNop(Nop inst)
{
return new EmptyStatement();
}
protected internal override Statement VisitIfInstruction(IfInstruction inst)
{
var condition = exprBuilder.ConvertCondition(inst.Condition);
@ -92,12 +97,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,12 +97,13 @@ namespace ICSharpCode.Decompiler.CSharp
{
var tryCatch = new TryCatchStatement();
tryCatch.TryBlock = ConvertAsBlock(inst.TryBlock);
foreach (var handler in inst.CatchHandlers) {
foreach (var handler in inst.Handlers) {
var catchClause = new CatchClause();
if (handler.Variable != null) {
catchClause.Type = exprBuilder.ConvertType(handler.Variable.Type);
catchClause.VariableName = handler.Variable.Name;
}
if (!handler.Filter.MatchLdcI4(1))
catchClause.Condition = exprBuilder.ConvertCondition(handler.Filter);
catchClause.Body = ConvertAsBlock(handler.Body);
tryCatch.CatchClauses.Add(catchClause);
@ -123,6 +129,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -123,6 +129,16 @@ namespace ICSharpCode.Decompiler.CSharp
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)
{
BlockStatement blockStatement = new BlockStatement();

7
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

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

5
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

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

182
ICSharpCode.Decompiler/IL/BlockBuilder.cs

@ -23,103 +23,140 @@ using System.Diagnostics; @@ -23,103 +23,140 @@ using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.IL
{
class BlockBuilder
{
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.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;
Block currentBlock;
Stack<BlockContainer> containerStack = new Stack<BlockContainer>();
public BlockContainer CreateBlocks(List<ILInstruction> instructions, BitArray incomingBranches)
{
currentContainer = new BlockContainer();
incomingBranches[0] = true; // see entrypoint as incoming branch
CreateContainerStructure();
var mainContainer = new BlockContainer();
mainContainer.ILRange = new Interval(0, body.CodeSize);
currentContainer = mainContainer;
foreach (var inst in instructions) {
int start = inst.ILRange.Start;
if (incomingBranches[start]) {
if (currentBlock == null || incomingBranches[start]) {
// Finish up the previous block
FinalizeCurrentBlock(start, fallthrough: true);
// 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);
}
if (currentBlock != null) {
if (instructionStack == null) {
// inlining disabled
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();
while (nextTry != null && start == nextTry.TryBlock.ILRange.Start) {
currentBlock.Instructions.Add(nextTry);
containerStack.Push(currentContainer);
currentContainer = (BlockContainer)nextTry.TryBlock;
currentBlock = new Block();
currentContainer.Blocks.Add(currentBlock);
currentBlock.ILRange = new Interval(start, start);
nextTry = tryInstructionList.ElementAtOrDefault(++currentTryIndex);
}
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);
ConnectBranches(currentContainer);
return currentContainer;
containerStack.Clear();
ConnectBranches(mainContainer);
return mainContainer;
}
private void FinalizeCurrentBlock(int currentILOffset, bool fallthrough)
{
if (currentBlock == null)
return;
FlushInstructionStack();
currentBlock.ILRange = new Interval(currentBlock.ILRange.Start, currentILOffset);
if (fallthrough)
currentBlock.Instructions.Add(new Branch(currentILOffset));
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)
{
switch (inst.OpCode) {
@ -154,3 +191,42 @@ namespace ICSharpCode.Decompiler.IL @@ -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();
}
}*/

55
ICSharpCode.Decompiler/IL/ILReader.cs

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

21
ICSharpCode.Decompiler/IL/ILVariable.cs

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

74
ICSharpCode.Decompiler/IL/Instructions.cs

@ -606,7 +606,7 @@ namespace ICSharpCode.Decompiler.IL @@ -606,7 +606,7 @@ namespace ICSharpCode.Decompiler.IL
}
/// <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)
@ -666,40 +666,9 @@ namespace ICSharpCode.Decompiler.IL @@ -666,40 +666,9 @@ namespace ICSharpCode.Decompiler.IL
}
/// <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)
{
return visitor.VisitTryFinally(this);
@ -707,40 +676,9 @@ namespace ICSharpCode.Decompiler.IL @@ -707,40 +676,9 @@ namespace ICSharpCode.Decompiler.IL
}
/// <summary>Try-fault statement</summary>
public sealed partial class TryFault : ILInstruction
{
public TryFault(ILInstruction tryBlock, ILInstruction faultBlock) : base(OpCode.TryFault)
public sealed partial class TryFault : TryInstruction
{
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)
{
return visitor.VisitTryFault(this);
@ -1560,7 +1498,7 @@ namespace ICSharpCode.Decompiler.IL @@ -1560,7 +1498,7 @@ namespace ICSharpCode.Decompiler.IL
readonly TypeReference type;
/// <summary>Returns the type operand.</summary>
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)
{
output.Write(OpCode);

14
ICSharpCode.Decompiler/IL/Instructions.tt

@ -72,22 +72,16 @@ @@ -72,22 +72,16 @@
new ChildInfo("falseInst"),
}), CustomConstructor, CustomComputeFlags, CustomWriteTo),
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",
CustomChildren(new [] {
new ChildInfo("filter") { IsArgument = true },
new ChildInfo("body"),
}), HasVariableOperand, CustomWriteTo, ResultType("Void")),
new OpCode("try.finally", "Try-finally statement",
CustomChildren(new [] {
new ChildInfo("tryBlock"),
new ChildInfo("finallyBlock"),
}), CustomWriteTo, CustomComputeFlags),
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("try.fault", "Try-fault statement",
CustomChildren(new [] {
new ChildInfo("tryBlock"),
new ChildInfo("faultBlock"),
}), CustomWriteTo, CustomComputeFlags),
BaseClass("TryInstruction"), CustomConstructor, CustomWriteTo, CustomComputeFlags),
new OpCode("debug.break", "Breakpoint instruction",
NoArguments, VoidResult, SideEffect),
new OpCode("ceq", "Compare equal. Returns 1 (of type I4) if two numbers or object references are equal; 0 otherwise.",
@ -154,7 +148,7 @@ @@ -154,7 +148,7 @@
new OpCode("castclass", "Casts an object to a class.",
CustomClassName("CastClass"), Unary, HasTypeOperand, MayThrow, ResultType("type.GetStackType()")),
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).",
CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess,
SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")),

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

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

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

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

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

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

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

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

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

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

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

@ -0,0 +1,67 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -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 @@ @@ -17,7 +17,9 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.IL
@ -25,6 +27,12 @@ namespace ICSharpCode.Decompiler.IL @@ -25,6 +27,12 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Visitor that applies a list of transformations to the IL Ast.
/// </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>
{
protected override ILInstruction Default(ILInstruction inst)
@ -33,6 +41,7 @@ namespace ICSharpCode.Decompiler.IL @@ -33,6 +41,7 @@ namespace ICSharpCode.Decompiler.IL
return 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:
@ -41,6 +50,63 @@ namespace ICSharpCode.Decompiler.IL @@ -41,6 +50,63 @@ namespace ICSharpCode.Decompiler.IL
}
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)
{

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

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

34
ICSharpCode.Decompiler/Util/Interval.cs

@ -11,10 +11,8 @@ namespace ICSharpCode.Decompiler @@ -11,10 +11,8 @@ namespace ICSharpCode.Decompiler
/// Represents a half-open interval.
/// The start position is inclusive; but the end position is exclusive.
/// </summary>
public struct Interval
public struct Interval : IEquatable<Interval>
{
public static readonly Interval Empty = new Interval(0, 0);
/// <summary>
/// Gets the inclusive start of the interval.
/// </summary>
@ -43,6 +41,36 @@ namespace ICSharpCode.Decompiler @@ -43,6 +41,36 @@ namespace ICSharpCode.Decompiler
{
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>

26
ILSpy/Languages/ILAstLanguage.cs

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

Loading…
Cancel
Save