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.

168 lines
5.8 KiB

  1. // Copyright (c) 2017 Daniel Grunwald
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. #nullable enable
  19. using System;
  20. using System.Diagnostics;
  21. namespace ICSharpCode.Decompiler.IL.Transforms
  22. {
  23. /// <summary>
  24. /// IL transform that runs on a sequence of statements within a block.
  25. /// </summary>
  26. /// <remarks>
  27. /// Interleaving different statement-combining transforms on a per-statement level
  28. /// improves detection of nested constructs.
  29. /// For example, array initializers can assume that each element assignment was already
  30. /// reduced to a single statement, even if the element contains a high-level construct
  31. /// detected by a different transform (e.g. object initializer).
  32. /// </remarks>
  33. public interface IStatementTransform
  34. {
  35. /// <summary>
  36. /// Runs the transform on the statements within a block.
  37. ///
  38. /// Note: the transform may only modify block.Instructions[pos..].
  39. /// The transform will be called repeatedly for pos=block.Instructions.Count-1, pos=block.Instructions.Count-2, ..., pos=0.
  40. /// </summary>
  41. /// <param name="block">The current block.</param>
  42. /// <param name="pos">The starting position where the transform is allowed to work.</param>
  43. /// <param name="context">Additional parameters.</param>
  44. /// <remarks>
  45. /// Instructions prior to block.Instructions[pos] must not be modified.
  46. /// It is valid to read such instructions, but not recommended as those have not been transformed yet.
  47. ///
  48. /// This function is only called on control-flow blocks with unreachable end-point.
  49. /// Thus, the last instruction in the block always must have the EndPointUnreachable flag.
  50. /// ==> Instructions with reachable end can't be last. Some transforms use this to save some bounds checks.
  51. /// </remarks>
  52. void Run(Block block, int pos, StatementTransformContext context);
  53. }
  54. /// <summary>
  55. /// Parameter class holding various arguments for <see cref="IStatementTransform.Run"/>.
  56. /// </summary>
  57. public class StatementTransformContext : ILTransformContext
  58. {
  59. public BlockTransformContext BlockContext { get; }
  60. public StatementTransformContext(BlockTransformContext blockContext) : base(blockContext)
  61. {
  62. this.BlockContext = blockContext ?? throw new ArgumentNullException(nameof(blockContext));
  63. }
  64. /// <summary>
  65. /// Gets the block on which the transform is running.
  66. /// </summary>
  67. public Block Block => BlockContext.Block;
  68. internal bool rerunCurrentPosition;
  69. internal int? rerunPosition;
  70. /// <summary>
  71. /// After the current statement transform has completed,
  72. /// do not continue with the next statement transform at the same position.
  73. /// Instead, re-run all statement transforms (including the current transform) starting at the specified position.
  74. /// </summary>
  75. public void RequestRerun(int pos)
  76. {
  77. if (rerunPosition == null || pos > rerunPosition)
  78. {
  79. rerunPosition = pos;
  80. }
  81. }
  82. /// <summary>
  83. /// After the current statement transform has completed,
  84. /// repeat all statement transforms on the current position.
  85. /// </summary>
  86. public void RequestRerun()
  87. {
  88. rerunCurrentPosition = true;
  89. }
  90. }
  91. /// <summary>
  92. /// Block transform that runs a list of statement transforms.
  93. /// </summary>
  94. public class StatementTransform : IBlockTransform
  95. {
  96. readonly IStatementTransform[] children;
  97. public StatementTransform(params IStatementTransform[] children)
  98. {
  99. this.children = children;
  100. }
  101. public void Run(Block block, BlockTransformContext context)
  102. {
  103. var ctx = new StatementTransformContext(context);
  104. int pos = 0;
  105. ctx.rerunPosition = block.Instructions.Count - 1;
  106. while (pos >= 0)
  107. {
  108. if (ctx.rerunPosition != null)
  109. {
  110. Debug.Assert(ctx.rerunPosition >= pos);
  111. #if DEBUG
  112. for (; pos < ctx.rerunPosition; ++pos)
  113. {
  114. block.Instructions[pos].ResetDirty();
  115. }
  116. #else
  117. pos = ctx.rerunPosition.Value;
  118. #endif
  119. Debug.Assert(pos == ctx.rerunPosition);
  120. ctx.rerunPosition = null;
  121. }
  122. foreach (var transform in children)
  123. {
  124. transform.Run(block, pos, ctx);
  125. #if DEBUG
  126. block.Instructions[pos].CheckInvariant(ILPhase.Normal);
  127. for (int i = Math.Max(0, pos - 100); i < pos; ++i)
  128. {
  129. if (block.Instructions[i].IsDirty)
  130. {
  131. Debug.Fail($"{transform.GetType().Name} modified an instruction before pos");
  132. }
  133. }
  134. #endif
  135. if (ctx.rerunCurrentPosition)
  136. {
  137. ctx.rerunCurrentPosition = false;
  138. ctx.RequestRerun(pos);
  139. }
  140. if (ctx.rerunPosition != null)
  141. {
  142. break;
  143. }
  144. }
  145. if (ctx.rerunPosition == null)
  146. {
  147. pos--;
  148. }
  149. }
  150. // This invariant can be surprisingly expensive to check if the block has thousands
  151. // of instructions and is frequently modified by transforms (invalidating the flags each time)
  152. // so we'll check this only once at the end of the block.
  153. Debug.Assert(block.HasFlag(InstructionFlags.EndPointUnreachable));
  154. }
  155. }
  156. }