mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
4.4 KiB
133 lines
4.4 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
|
|
using ICSharpCode.Decompiler.FlowAnalysis; |
|
using ICSharpCode.Decompiler.IL.ControlFlow; |
|
|
|
namespace ICSharpCode.Decompiler.IL.Transforms |
|
{ |
|
/// <summary> |
|
/// Per-block IL transform. |
|
/// </summary> |
|
public interface IBlockTransform |
|
{ |
|
/// <summary> |
|
/// Runs the transform on the specified block. |
|
/// |
|
/// Note: the transform may only modify the specified block and its descendants, |
|
/// as well as any sibling blocks that are dominated by the specified block. |
|
/// </summary> |
|
void Run(Block block, BlockTransformContext context); |
|
} |
|
|
|
/// <summary> |
|
/// Parameter class holding various arguments for <see cref="IBlockTransform.Run"/>. |
|
/// </summary> |
|
public class BlockTransformContext : ILTransformContext |
|
{ |
|
/// <summary> |
|
/// The block to process. |
|
/// </summary> |
|
/// <remarks> |
|
/// Should be identical to the <c>block</c> parameter to <c>IBlockTransform.Run</c>. |
|
/// </remarks> |
|
public Block Block { get; set; } |
|
|
|
/// <summary> |
|
/// The control flow node corresponding to the block being processed. |
|
/// </summary> |
|
/// <remarks> |
|
/// Identical to <c>ControlFlowGraph.GetNode(Block)</c>. |
|
/// Note: the control flow graph is not up-to-date, but was created at the start of the |
|
/// block transforms (before loop detection). |
|
/// </remarks> |
|
public ControlFlowNode ControlFlowNode { get; set; } |
|
|
|
/// <summary> |
|
/// Gets the control flow graph. |
|
/// |
|
/// Note: the control flow graph is not up-to-date, but was created at the start of the |
|
/// block transforms (before loop detection). |
|
/// </summary> |
|
public ControlFlowGraph ControlFlowGraph { get; set; } |
|
|
|
/// <summary> |
|
/// Initially equal to Block.Instructions.Count indicating that nothing has been transformed yet. |
|
/// Set by <see cref="ConditionDetection"/> when another already transformed block is merged into |
|
/// the current block. Subsequent <see cref="IBlockTransform"/>s must update this value, for example, |
|
/// by resetting it to Block.Instructions.Count. <see cref="StatementTransform"/> will use this value to |
|
/// skip already transformed instructions. |
|
/// </summary> |
|
public int IndexOfFirstAlreadyTransformedInstruction { get; set; } |
|
|
|
public BlockTransformContext(ILTransformContext context) : base(context) |
|
{ |
|
} |
|
} |
|
|
|
|
|
/// <summary> |
|
/// IL transform that runs a list of per-block transforms. |
|
/// </summary> |
|
public class BlockILTransform : IILTransform |
|
{ |
|
public IList<IBlockTransform> PreOrderTransforms { get; } = new List<IBlockTransform>(); |
|
public IList<IBlockTransform> PostOrderTransforms { get; } = new List<IBlockTransform>(); |
|
|
|
bool running; |
|
|
|
public override string ToString() |
|
{ |
|
return $"{nameof(BlockILTransform)} ({string.Join(", ", PreOrderTransforms.Concat(PostOrderTransforms).Select(t => t.GetType().Name))})"; |
|
} |
|
|
|
public void Run(ILFunction function, ILTransformContext context) |
|
{ |
|
if (running) |
|
throw new InvalidOperationException("Reentrancy detected. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant."); |
|
try |
|
{ |
|
running = true; |
|
var blockContext = new BlockTransformContext(context); |
|
Debug.Assert(blockContext.Function == function); |
|
foreach (var container in function.Descendants.OfType<BlockContainer>().ToList()) |
|
{ |
|
context.CancellationToken.ThrowIfCancellationRequested(); |
|
blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); |
|
VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); |
|
} |
|
} |
|
finally |
|
{ |
|
running = false; |
|
} |
|
} |
|
|
|
void VisitBlock(ControlFlowNode cfgNode, BlockTransformContext context) |
|
{ |
|
Block block = (Block)cfgNode.UserData; |
|
context.StepStartGroup(block.Label, block); |
|
|
|
context.ControlFlowNode = cfgNode; |
|
context.Block = block; |
|
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; |
|
block.RunTransforms(PreOrderTransforms, context); |
|
|
|
// First, process the children in the dominator tree. |
|
// The ConditionDetection transform requires dominated blocks to |
|
// be already processed. |
|
foreach (var child in cfgNode.DominatorTreeChildren) |
|
{ |
|
VisitBlock(child, context); |
|
} |
|
|
|
context.ControlFlowNode = cfgNode; |
|
context.Block = block; |
|
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count; |
|
block.RunTransforms(PostOrderTransforms, context); |
|
context.StepEndGroup(); |
|
} |
|
} |
|
}
|
|
|