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 { /// /// Per-block IL transform. /// public interface IBlockTransform { /// /// 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. /// void Run(Block block, BlockTransformContext context); } /// /// Parameter class holding various arguments for . /// public class BlockTransformContext : ILTransformContext { /// /// The block to process. /// /// /// Should be identical to the block parameter to IBlockTransform.Run. /// public Block Block { get; set; } /// /// The control flow node corresponding to the block being processed. /// /// /// Identical to ControlFlowGraph.GetNode(Block). /// Note: the control flow graph is not up-to-date, but was created at the start of the /// block transforms (before loop detection). /// public ControlFlowNode ControlFlowNode { get; set; } /// /// 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). /// public ControlFlowGraph ControlFlowGraph { get; set; } public BlockTransformContext(ILTransformContext context) : base(context) { } } /// /// IL transform that runs a list of per-block transforms. /// public class BlockILTransform : IILTransform { public IList PreOrderTransforms { get; } = new List(); public IList PostOrderTransforms { get; } = new List(); 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().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; 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; block.RunTransforms(PostOrderTransforms, context); context.StepEndGroup(); } } }