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.

141 lines
4.6 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using ICSharpCode.Decompiler.FlowAnalysis;
  6. using ICSharpCode.Decompiler.IL.ControlFlow;
  7. using ICSharpCode.Decompiler.Util;
  8. namespace ICSharpCode.Decompiler.IL.Transforms
  9. {
  10. /// <summary>
  11. /// Per-block IL transform.
  12. /// </summary>
  13. public interface IBlockTransform
  14. {
  15. /// <summary>
  16. /// Runs the transform on the specified block.
  17. ///
  18. /// Note: the transform may only modify the specified block and its descendants,
  19. /// as well as any sibling blocks that are dominated by the specified block.
  20. /// </summary>
  21. void Run(Block block, BlockTransformContext context);
  22. }
  23. /// <summary>
  24. /// Parameter class holding various arguments for <see cref="IBlockTransform.Run"/>.
  25. /// </summary>
  26. public class BlockTransformContext : ILTransformContext
  27. {
  28. /// <summary>
  29. /// The block to process.
  30. /// </summary>
  31. /// <remarks>
  32. /// Should be identical to the <c>block</c> parameter to <c>IBlockTransform.Run</c>.
  33. /// </remarks>
  34. public Block Block { get; set; }
  35. /// <summary>
  36. /// The control flow node corresponding to the block being processed.
  37. /// </summary>
  38. /// <remarks>
  39. /// Identical to <c>ControlFlowGraph.GetNode(Block)</c>.
  40. /// Note: the control flow graph is not up-to-date, but was created at the start of the
  41. /// block transforms (before loop detection).
  42. /// </remarks>
  43. public ControlFlowNode ControlFlowNode { get; set; }
  44. /// <summary>
  45. /// Gets the control flow graph.
  46. ///
  47. /// Note: the control flow graph is not up-to-date, but was created at the start of the
  48. /// block transforms (before loop detection).
  49. /// </summary>
  50. public ControlFlowGraph ControlFlowGraph { get; set; }
  51. /// <summary>
  52. /// Initially equal to Block.Instructions.Count indicating that nothing has been transformed yet.
  53. /// Set by <see cref="ConditionDetection"/> when another already transformed block is merged into
  54. /// the current block. Subsequent <see cref="IBlockTransform"/>s must update this value, for example,
  55. /// by resetting it to Block.Instructions.Count. <see cref="StatementTransform"/> will use this value to
  56. /// skip already transformed instructions.
  57. /// </summary>
  58. public int IndexOfFirstAlreadyTransformedInstruction { get; set; }
  59. public BlockTransformContext(ILTransformContext context) : base(context)
  60. {
  61. }
  62. }
  63. /// <summary>
  64. /// IL transform that runs a list of per-block transforms.
  65. /// </summary>
  66. public class BlockILTransform : IILTransform
  67. {
  68. public IList<IBlockTransform> PreOrderTransforms { get; } = new List<IBlockTransform>();
  69. public IList<IBlockTransform> PostOrderTransforms { get; } = new List<IBlockTransform>();
  70. bool running;
  71. public override string ToString()
  72. {
  73. return $"{nameof(BlockILTransform)} ({string.Join(", ", PreOrderTransforms.Concat(PostOrderTransforms).Select(t => t.GetType().Name))})";
  74. }
  75. public void Run(ILFunction function, ILTransformContext context)
  76. {
  77. if (running)
  78. throw new InvalidOperationException("Reentrancy detected. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant.");
  79. try
  80. {
  81. running = true;
  82. var blockContext = new BlockTransformContext(context);
  83. Debug.Assert(blockContext.Function == function);
  84. foreach (var container in function.Descendants.OfType<BlockContainer>().ToList())
  85. {
  86. context.CancellationToken.ThrowIfCancellationRequested();
  87. blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken);
  88. VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext);
  89. }
  90. }
  91. finally
  92. {
  93. running = false;
  94. }
  95. }
  96. /// <summary>
  97. /// Walks the dominator tree rooted at entryNode, calling the transforms on each block.
  98. /// </summary>
  99. void VisitBlock(ControlFlowNode entryNode, BlockTransformContext context)
  100. {
  101. IEnumerable<ControlFlowNode> Preorder(ControlFlowNode cfgNode)
  102. {
  103. // preorder processing:
  104. Block block = (Block)cfgNode.UserData;
  105. context.StepStartGroup(block.Label, block);
  106. context.ControlFlowNode = cfgNode;
  107. context.Block = block;
  108. context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
  109. block.RunTransforms(PreOrderTransforms, context);
  110. // process the children
  111. return cfgNode.DominatorTreeChildren;
  112. }
  113. foreach (var cfgNode in TreeTraversal.PostOrder(entryNode, Preorder))
  114. {
  115. // in post-order:
  116. Block block = (Block)cfgNode.UserData;
  117. context.ControlFlowNode = cfgNode;
  118. context.Block = block;
  119. context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
  120. block.RunTransforms(PostOrderTransforms, context);
  121. context.StepEndGroup();
  122. }
  123. }
  124. }
  125. }