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.

146 lines
3.9 KiB

  1. // Copyright (c) 2016 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.Collections.Generic;
  21. using System.Diagnostics;
  22. using System.Linq;
  23. using ICSharpCode.Decompiler.Util;
  24. namespace ICSharpCode.Decompiler.IL.Transforms
  25. {
  26. /// <summary>
  27. /// Exception thrown when an IL transform runs into the <see cref="Stepper.StepLimit"/>.
  28. /// </summary>
  29. public class StepLimitReachedException : Exception
  30. {
  31. }
  32. /// <summary>
  33. /// Helper class that manages recording transform steps.
  34. /// </summary>
  35. public class Stepper
  36. {
  37. /// <summary>
  38. /// Gets whether stepping of built-in transforms is supported in this build of ICSharpCode.Decompiler.
  39. /// Usually only debug builds support transform stepping.
  40. /// </summary>
  41. public static bool SteppingAvailable {
  42. get {
  43. #if STEP
  44. return true;
  45. #else
  46. return false;
  47. #endif
  48. }
  49. }
  50. public IList<Node> Steps => steps;
  51. public int StepLimit { get; set; } = int.MaxValue;
  52. public bool IsDebug { get; set; }
  53. public class Node
  54. {
  55. public string Description { get; }
  56. public ILInstruction? Position { get; set; }
  57. /// <summary>
  58. /// BeginStep is inclusive.
  59. /// </summary>
  60. public int BeginStep { get; set; }
  61. /// <summary>
  62. /// EndStep is exclusive.
  63. /// </summary>
  64. public int EndStep { get; set; }
  65. public IList<Node> Children { get; } = new List<Node>();
  66. public Node(string description)
  67. {
  68. Description = description;
  69. }
  70. }
  71. readonly Stack<Node> groups;
  72. readonly IList<Node> steps;
  73. int step = 0;
  74. public Stepper()
  75. {
  76. steps = new List<Node>();
  77. groups = new Stack<Node>();
  78. }
  79. /// <summary>
  80. /// Call this method immediately before performing a transform step.
  81. /// Used for debugging the IL transforms. Has no effect in release mode.
  82. ///
  83. /// May throw <see cref="StepLimitReachedException"/> in debug mode.
  84. /// </summary>
  85. [DebuggerStepThrough]
  86. public void Step(string description, ILInstruction? near = null)
  87. {
  88. StepInternal(description, near);
  89. }
  90. [DebuggerStepThrough]
  91. private Node StepInternal(string description, ILInstruction? near)
  92. {
  93. if (step == StepLimit)
  94. {
  95. if (IsDebug)
  96. Debugger.Break();
  97. else
  98. throw new StepLimitReachedException();
  99. }
  100. var stepNode = new Node($"{step}: {description}") {
  101. Position = near,
  102. BeginStep = step,
  103. EndStep = step + 1
  104. };
  105. var p = groups.PeekOrDefault();
  106. if (p != null)
  107. p.Children.Add(stepNode);
  108. else
  109. steps.Add(stepNode);
  110. step++;
  111. return stepNode;
  112. }
  113. [DebuggerStepThrough]
  114. public void StartGroup(string description, ILInstruction? near = null)
  115. {
  116. groups.Push(StepInternal(description, near));
  117. }
  118. public void EndGroup(bool keepIfEmpty = false)
  119. {
  120. var node = groups.Pop();
  121. if (!keepIfEmpty && node.Children.Count == 0)
  122. {
  123. var col = groups.PeekOrDefault()?.Children ?? steps;
  124. Debug.Assert(col.Last() == node);
  125. col.RemoveAt(col.Count - 1);
  126. }
  127. node.EndStep = step;
  128. }
  129. }
  130. }