From 63b32fef35002e75cc56a2bb9ddd0ffa134fc67c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 13 Jul 2014 17:29:46 +0200 Subject: [PATCH] Create ILFunction top-level node --- .../CSharp/CSharpDecompiler.cs | 4 +- .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/IL/BlockBuilder.cs | 37 +++++++++- ICSharpCode.Decompiler/IL/ILReader.cs | 14 ++-- ICSharpCode.Decompiler/IL/ILVariable.cs | 12 +++- ICSharpCode.Decompiler/IL/Instructions.cs | 34 +++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 4 ++ .../IL/Instructions/Block.cs | 27 +++++++- .../IL/Instructions/BlockContainer.cs | 36 ++++++---- .../IL/Instructions/Branch.cs | 59 ++++++++++++++-- .../IL/Instructions/ILFunction.cs | 69 +++++++++++++++++++ .../IL/Instructions/InstructionCollection.cs | 15 ++++ .../IL/Instructions/MemoryInstructions.cs | 20 +++++- .../IL/TransformingVisitor.cs | 64 +++++++++++++++++ ILSpy/Languages/ILAstLanguage.cs | 8 +-- 15 files changed, 365 insertions(+), 40 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs create mode 100644 ICSharpCode.Decompiler/IL/TransformingVisitor.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index fa8dc7fe0..9554546e7 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -102,8 +102,8 @@ namespace ICSharpCode.Decompiler.CSharp var entityDecl = typeSystemAstBuilder.ConvertEntity(method); if (methodDefinition.HasBody) { var ilReader = new ILReader(methodDefinition.Body, CancellationToken); - var inst = ilReader.CreateBlocks(true); - var body = statementBuilder.ConvertAsBlock(inst); + var function = ilReader.CreateFunction(true); + var body = statementBuilder.ConvertAsBlock(function.Body); body.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }); entityDecl.AddChild(body, Roles.Body); } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 7701aca96..86c9d5f16 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -79,6 +79,7 @@ + @@ -87,6 +88,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/BlockBuilder.cs b/ICSharpCode.Decompiler/IL/BlockBuilder.cs index 4ae090fa5..5ad97abf8 100644 --- a/ICSharpCode.Decompiler/IL/BlockBuilder.cs +++ b/ICSharpCode.Decompiler/IL/BlockBuilder.cs @@ -43,7 +43,6 @@ namespace ICSharpCode.Decompiler.IL public BlockContainer CreateBlocks(List instructions, BitArray incomingBranches) { currentContainer = new BlockContainer(); - currentContainer.AddRef(); // mark the root node incomingBranches[0] = true; // see entrypoint as incoming branch @@ -95,6 +94,7 @@ namespace ICSharpCode.Decompiler.IL } } FinalizeCurrentBlock(body.CodeSize, fallthrough: false); + ConnectBranches(currentContainer); return currentContainer; } @@ -117,5 +117,40 @@ namespace ICSharpCode.Decompiler.IL instructionStack.Clear(); } } + + Stack containerStack = new Stack(); + + void ConnectBranches(ILInstruction inst) + { + switch (inst.OpCode) { + case OpCode.Branch: + var branch = (Branch)inst; + branch.TargetBlock = FindBranchTarget(branch.TargetILOffset); + break; + case OpCode.BlockContainer: + var container = (BlockContainer)inst; + containerStack.Push(container); + container.EntryPoint.IncomingEdgeCount++; // count the entry edge + foreach (var block in container.Blocks) + ConnectBranches(block); + containerStack.Pop(); + break; + default: + foreach (var child in inst.Children) + ConnectBranches(child); + break; + } + } + + Block FindBranchTarget(int targetILOffset) + { + foreach (var container in containerStack) { + foreach (var block in container.Blocks) { + if (block.ILRange.Start == targetILOffset) + return block; + } + } + throw new InvalidOperationException("Could not find block for branch target"); + } } } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index c474c566b..32a346d58 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -169,16 +169,16 @@ namespace ICSharpCode.Decompiler.IL new Disassembler.MethodBodyDisassembler(output, false, cancellationToken).WriteExceptionHandlers(body); } - public void WriteBlocks(ITextOutput output, bool instructionInlining) - { - CreateBlocks(instructionInlining).WriteTo(output); - } - - internal BlockContainer CreateBlocks(bool instructionInlining) + public ILFunction CreateFunction(bool instructionInlining) { if (instructionBuilder == null) ReadInstructions(null); - return new BlockBuilder(body, instructionInlining).CreateBlocks(instructionBuilder, isBranchTarget); + var container = new BlockBuilder(body, instructionInlining).CreateBlocks(instructionBuilder, isBranchTarget); + var function = new ILFunction(body.Method, container); + function.Variables.AddRange(parameterVariables); + function.Variables.AddRange(localVariables); + function.AddRef(); // mark the root node + return function; } ILInstruction Neg() diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 69022f3b3..da9794740 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -23,6 +23,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using ICSharpCode.Decompiler.Disassembler; namespace ICSharpCode.Decompiler.IL { @@ -78,10 +79,17 @@ namespace ICSharpCode.Decompiler.IL return Kind.ToString(); } } - + + internal void WriteDefinitionTo(ITextOutput output) + { + output.WriteDefinition(this.Name, CecilObject ?? this, isLocal: true); + output.Write(" : "); + Type.WriteTo(output); + } + internal void WriteTo(ITextOutput output) { - output.WriteReference(this.ToString(), CecilObject ?? this, isLocal: true); + output.WriteReference(this.Name, CecilObject ?? this, isLocal: true); } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 26da44846..890a619a5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -36,6 +36,8 @@ namespace ICSharpCode.Decompiler.IL /// Ignore the arguments and produce void. Used to prevent the end result of an instruction from being pushed to the evaluation stack. Void, /// A container of IL blocks. + ILFunction, + /// A container of IL blocks. BlockContainer, /// A block of IL instructions. Block, @@ -328,6 +330,33 @@ namespace ICSharpCode.Decompiler.IL } } + /// A container of IL blocks. + public sealed partial class ILFunction : ILInstruction + { + ILInstruction body; + public ILInstruction Body { + get { return this.body; } + set { + ValidateChild(value); + SetChildInstruction(ref this.body, value); + } + } + public override IEnumerable Children { + get { + yield return this.body; + } + } + public override void TransformChildren(ILVisitor visitor) + { + this.Body = this.body.AcceptVisitor(visitor); + } + public override StackType ResultType { get { return StackType.O; } } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitILFunction(this); + } + } + /// A container of IL blocks. public sealed partial class BlockContainer : ILInstruction { @@ -2037,6 +2066,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitILFunction(ILFunction function) + { + return Default(function); + } protected internal virtual T VisitBlockContainer(BlockContainer container) { return Default(container); @@ -2362,6 +2395,7 @@ namespace ICSharpCode.Decompiler.IL "pop", "peek", "void", + "ILFunction", "BlockContainer", "Block", "logic.not", diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 43874cc07..58ca764f7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -39,6 +39,10 @@ Peeking, NoArguments, ResultTypeParam), new OpCode("void", "Ignore the arguments and produce void. Used to prevent the end result of an instruction from being pushed to the evaluation stack.", VoidResult, Unary), + new OpCode("ILFunction", "A container of IL blocks.", + CustomChildren(new [] { + new ChildInfo("body") + }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")), new OpCode("BlockContainer", "A container of IL blocks.", VoidResult, CustomConstructor, CustomVariableName("container")), new OpCode("Block", "A block of IL instructions.", diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index 4c061895e..6442692a3 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -1,4 +1,22 @@ -using System; +// 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; using System.Diagnostics; using System.Linq; @@ -11,6 +29,8 @@ namespace ICSharpCode.Decompiler.IL { public readonly InstructionCollection Instructions; + public int IncomingEdgeCount; + public Block() : base(OpCode.Block) { this.Instructions = new InstructionCollection(this); @@ -32,7 +52,10 @@ namespace ICSharpCode.Decompiler.IL public override void WriteTo(ITextOutput output) { - output.WriteDefinition("Block " + Label, this); + output.Write("Block "); + output.WriteDefinition(Label, this); + if (Parent is BlockContainer) + output.Write(" (incoming: {0})", IncomingEdgeCount); output.WriteLine(" {"); output.Indent(); foreach (var inst in Instructions) { diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 7ca8cfcd8..777d37dc2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -1,4 +1,22 @@ -using System; +// 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; using System.Diagnostics; using System.Linq; @@ -16,7 +34,7 @@ namespace ICSharpCode.Decompiler.IL /// partial class BlockContainer : ILInstruction { - public readonly IList Blocks; + public readonly InstructionCollection Blocks; public Block EntryPoint { get { @@ -29,19 +47,7 @@ namespace ICSharpCode.Decompiler.IL this.Blocks = new InstructionCollection(this); } - /* - public override bool IsPeeking { get { return EntryPoint.IsPeeking; } } - - public override bool NoResult { get { return true; } } - - public override void TransformChildren(Func transformFunc) - { - for (int i = 0; i < Blocks.Count; i++) { - if (transformFunc(Blocks[i]) != Blocks[i]) - throw new InvalidOperationException("Cannot replace blocks"); - } - } - */public override void WriteTo(ITextOutput output) + public override void WriteTo(ITextOutput output) { output.WriteLine("BlockContainer {"); output.Indent(); diff --git a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs index 7501e9631..141a0de3f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Branch.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Branch.cs @@ -1,4 +1,22 @@ -using System; +// 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; using System.Diagnostics; using System.Linq; @@ -9,7 +27,8 @@ namespace ICSharpCode.Decompiler.IL { partial class Branch : SimpleInstruction { - public readonly int TargetILOffset; + readonly int targetILOffset; + Block targetBlock; /// /// Pops the specified number of arguments from the evaluation stack during the branching operation. @@ -20,18 +39,48 @@ namespace ICSharpCode.Decompiler.IL public Branch(int targetILOffset) : base(OpCode.Branch) { - this.TargetILOffset = targetILOffset; + this.targetILOffset = targetILOffset; + } + + public int TargetILOffset { + get { return targetBlock != null ? targetBlock.ILRange.Start : targetILOffset; } + } + + public Block TargetBlock { + get { return targetBlock; } + set { + if (targetBlock != null && IsConnected) + targetBlock.IncomingEdgeCount--; + targetBlock = value; + if (targetBlock != null && IsConnected) + targetBlock.IncomingEdgeCount++; + } + } + + protected override void Connected() + { + base.Connected(); + if (targetBlock != null) + targetBlock.IncomingEdgeCount++; + } + + protected override void Disconnected() + { + base.Disconnected(); + if (targetBlock != null) + targetBlock.IncomingEdgeCount--; } public string TargetLabel { - get { return CecilExtensions.OffsetToString(TargetILOffset); } + get { return targetBlock != null ? targetBlock.Label : CecilExtensions.OffsetToString(TargetILOffset); } } + public override void WriteTo(ITextOutput output) { output.Write(OpCode); output.Write(' '); - output.WriteReference(TargetLabel, TargetILOffset, isLocal: true); + output.WriteReference(TargetLabel, (object)targetBlock ?? TargetILOffset, isLocal: true); if (PopCount != 0) { output.Write(" (pop "); output.Write(PopCount.ToString()); diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs new file mode 100644 index 000000000..1e5554d66 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -0,0 +1,69 @@ +// 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; +using Mono.Cecil; +using ICSharpCode.Decompiler.Disassembler; + +namespace ICSharpCode.Decompiler.IL +{ + partial class ILFunction + { + public readonly MethodDefinition Method; + public readonly IList Variables = new List(); + + public ILFunction(MethodDefinition method, ILInstruction body) : base(OpCode.ILFunction) + { + this.Body = body; + this.Method = method; + } + + public override void WriteTo(ITextOutput output) + { + output.Write(OpCode); + output.Write(' '); + Method.WriteTo(output); + output.WriteLine(" {"); + output.Indent(); + + foreach (var variable in Variables) { + variable.WriteDefinitionTo(output); + output.WriteLine(); + } + output.WriteLine(); + body.WriteTo(output); + + output.Unindent(); + output.WriteLine("}"); + } + + protected override InstructionFlags ComputeFlags() + { + // Creating a lambda may throw OutOfMemoryException + return InstructionFlags.MayThrow; + } + + internal override ILInstruction Inline(InstructionFlags flagsBefore, Stack instructionStack, out bool finished) + { + // To the outside, lambda creation looks like a constant + finished = true; + return this; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs index fb66bdcdc..08c86f000 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs @@ -18,6 +18,7 @@ using System; using System.Collections.ObjectModel; +using System.Linq; namespace ICSharpCode.Decompiler.IL { @@ -61,5 +62,19 @@ namespace ICSharpCode.Decompiler.IL parentInstruction.AddChildInstruction(item); base.SetItem(index, item); } + + public int RemoveAll(Predicate predicate) + { + int removed = 0; + for (int i = 0; i < this.Count;) { + if (predicate(this[i])) { + RemoveAt(i); + removed++; + } else { + i++; + } + } + return removed; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs index f26a5c237..d68d4bf63 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/MemoryInstructions.cs @@ -1,4 +1,22 @@ -using ICSharpCode.Decompiler.Disassembler; +// 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 ICSharpCode.Decompiler.Disassembler; using Mono.Cecil; using System; using System.Collections.Generic; diff --git a/ICSharpCode.Decompiler/IL/TransformingVisitor.cs b/ICSharpCode.Decompiler/IL/TransformingVisitor.cs new file mode 100644 index 000000000..7f5bbdfa0 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/TransformingVisitor.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler.IL +{ + /// + /// Visitor that applies a list of transformations to the IL Ast. + /// + public class TransformingVisitor : ILVisitor + { + protected override ILInstruction Default(ILInstruction inst) + { + inst.TransformChildren(this); + 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: + if (inst.TargetBlock.IncomingEdgeCount == 1 && inst.PopCount == 0) { + return inst.TargetBlock; + } + return base.VisitBranch(inst); + } + + protected internal override ILInstruction VisitBlockContainer(BlockContainer container) + { + container.TransformChildren(this); + + // VisitBranch() 'steals' blocks from containers. Remove all blocks that were stolen from the block list: + Debug.Assert(container.EntryPoint.IncomingEdgeCount > 0); + container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0); + + // If the container only contains a single block, and the block contents do not jump back to the block start, + // we can remove the container. + if (container.Blocks.Count == 1 && container.EntryPoint.IncomingEdgeCount == 1) { + // If the block has only one instruction, we can remove the block too + if (container.EntryPoint.Instructions.Count == 1) + return container.EntryPoint.Instructions[0]; + return container.EntryPoint; + } + return container; + } + } +} diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index 8c28ccd66..529975dbe 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -99,10 +99,8 @@ namespace ICSharpCode.ILSpy //} } - public override string FileExtension - { - get - { + public override string FileExtension { + get { return ".il"; } } @@ -151,7 +149,7 @@ namespace ICSharpCode.ILSpy if (!method.HasBody) return; ILReader reader = new ILReader(method.Body, options.CancellationToken); - reader.WriteBlocks(output, instructionInlining); + reader.CreateFunction(instructionInlining).WriteTo(output); } } }