/ICSharpCode.Decompiler/Disassembler/ILStructure.cs

http://github.com/icsharpcode/ILSpy · C# · 285 lines · 189 code · 22 blank · 74 comment · 43 complexity · ad04394326c798d9475d33e8993dcfeb MD5 · raw file

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  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. using System.Collections.Generic;
  19. using System.Diagnostics;
  20. using System.Linq;
  21. using System.Reflection.Metadata;
  22. using ICSharpCode.Decompiler.Metadata;
  23. using ICSharpCode.Decompiler.Util;
  24. namespace ICSharpCode.Decompiler.Disassembler
  25. {
  26. /// <summary>
  27. /// Specifies the type of an IL structure.
  28. /// </summary>
  29. public enum ILStructureType
  30. {
  31. /// <summary>
  32. /// The root block of the method
  33. /// </summary>
  34. Root,
  35. /// <summary>
  36. /// A nested control structure representing a loop.
  37. /// </summary>
  38. Loop,
  39. /// <summary>
  40. /// A nested control structure representing a try block.
  41. /// </summary>
  42. Try,
  43. /// <summary>
  44. /// A nested control structure representing a catch, finally, or fault block.
  45. /// </summary>
  46. Handler,
  47. /// <summary>
  48. /// A nested control structure representing an exception filter block.
  49. /// </summary>
  50. Filter
  51. }
  52. /// <summary>
  53. /// An IL structure.
  54. /// </summary>
  55. public class ILStructure
  56. {
  57. public readonly PEFile Module;
  58. public readonly MethodDefinitionHandle MethodHandle;
  59. public readonly GenericContext GenericContext;
  60. public readonly ILStructureType Type;
  61. /// <summary>
  62. /// Start position of the structure.
  63. /// </summary>
  64. public readonly int StartOffset;
  65. /// <summary>
  66. /// End position of the structure. (exclusive)
  67. /// </summary>
  68. public readonly int EndOffset;
  69. /// <summary>
  70. /// The exception handler associated with the Try, Filter or Handler block.
  71. /// </summary>
  72. public readonly ExceptionRegion ExceptionHandler;
  73. /// <summary>
  74. /// The loop's entry point.
  75. /// </summary>
  76. public readonly int LoopEntryPointOffset;
  77. /// <summary>
  78. /// The list of child structures.
  79. /// </summary>
  80. public readonly List<ILStructure> Children = new List<ILStructure>();
  81. public ILStructure(PEFile module, MethodDefinitionHandle handle, GenericContext genericContext, MethodBodyBlock body)
  82. : this(module, handle, genericContext, ILStructureType.Root, 0, body.GetILReader().Length)
  83. {
  84. // Build the tree of exception structures:
  85. for (int i = 0; i < body.ExceptionRegions.Length; i++) {
  86. ExceptionRegion eh = body.ExceptionRegions[i];
  87. if (!body.ExceptionRegions.Take(i).Any(oldEh => oldEh.TryOffset == eh.TryOffset && oldEh.TryLength == eh.TryLength))
  88. AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Try, eh.TryOffset, eh.TryOffset + eh.TryLength, eh));
  89. if (eh.Kind == ExceptionRegionKind.Filter)
  90. AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Filter, eh.FilterOffset, eh.HandlerOffset, eh));
  91. AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Handler, eh.HandlerOffset, eh.HandlerOffset + eh.HandlerLength, eh));
  92. }
  93. // Very simple loop detection: look for backward branches
  94. (var allBranches, var isAfterUnconditionalBranch) = FindAllBranches(body.GetILReader());
  95. // We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;")
  96. for (int i = allBranches.Count - 1; i >= 0; i--) {
  97. int loopEnd = allBranches[i].Source.End;
  98. int loopStart = allBranches[i].Target;
  99. if (loopStart < loopEnd) {
  100. // We found a backward branch. This is a potential loop.
  101. // Check that is has only one entry point:
  102. int entryPoint = -1;
  103. // entry point is first instruction in loop if prev inst isn't an unconditional branch
  104. if (loopStart > 0 && !isAfterUnconditionalBranch[loopStart])
  105. entryPoint = allBranches[i].Target;
  106. bool multipleEntryPoints = false;
  107. foreach (var branch in allBranches) {
  108. if (branch.Source.Start < loopStart || branch.Source.Start >= loopEnd) {
  109. if (loopStart <= branch.Target && branch.Target < loopEnd) {
  110. // jump from outside the loop into the loop
  111. if (entryPoint < 0)
  112. entryPoint = branch.Target;
  113. else if (branch.Target != entryPoint)
  114. multipleEntryPoints = true;
  115. }
  116. }
  117. }
  118. if (!multipleEntryPoints) {
  119. AddNestedStructure(new ILStructure(module, handle, genericContext, ILStructureType.Loop, loopStart, loopEnd, entryPoint));
  120. }
  121. }
  122. }
  123. SortChildren();
  124. }
  125. public ILStructure(PEFile module, MethodDefinitionHandle handle, GenericContext genericContext, ILStructureType type, int startOffset, int endOffset, ExceptionRegion handler = default)
  126. {
  127. Debug.Assert(startOffset < endOffset);
  128. this.Module = module;
  129. this.MethodHandle = handle;
  130. this.GenericContext = genericContext;
  131. this.Type = type;
  132. this.StartOffset = startOffset;
  133. this.EndOffset = endOffset;
  134. this.ExceptionHandler = handler;
  135. }
  136. public ILStructure(PEFile module, MethodDefinitionHandle handle, GenericContext genericContext, ILStructureType type, int startOffset, int endOffset, int loopEntryPoint)
  137. {
  138. Debug.Assert(startOffset < endOffset);
  139. this.Module = module;
  140. this.MethodHandle = handle;
  141. this.GenericContext = genericContext;
  142. this.Type = type;
  143. this.StartOffset = startOffset;
  144. this.EndOffset = endOffset;
  145. this.LoopEntryPointOffset = loopEntryPoint;
  146. }
  147. bool AddNestedStructure(ILStructure newStructure)
  148. {
  149. // special case: don't consider the loop-like structure of "continue;" statements to be nested loops
  150. if (this.Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == this.StartOffset)
  151. return false;
  152. // use <= for end-offset comparisons because both end and EndOffset are exclusive
  153. Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset);
  154. foreach (ILStructure child in this.Children) {
  155. if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) {
  156. return child.AddNestedStructure(newStructure);
  157. } else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) {
  158. // child and newStructure overlap
  159. if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) {
  160. // Invalid nesting, can't build a tree. -> Don't add the new structure.
  161. return false;
  162. }
  163. }
  164. }
  165. // Move existing structures into the new structure:
  166. for (int i = 0; i < this.Children.Count; i++) {
  167. ILStructure child = this.Children[i];
  168. if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) {
  169. this.Children.RemoveAt(i--);
  170. newStructure.Children.Add(child);
  171. }
  172. }
  173. // Add the structure here:
  174. this.Children.Add(newStructure);
  175. return true;
  176. }
  177. struct Branch
  178. {
  179. public Interval Source;
  180. public int Target;
  181. public Branch(int start, int end, int target)
  182. {
  183. this.Source = new Interval(start, end);
  184. this.Target = target;
  185. }
  186. public override string ToString()
  187. {
  188. return $"[Branch Source={Source}, Target={Target}]";
  189. }
  190. }
  191. /// <summary>
  192. /// Finds all branches. Returns list of source offset->target offset mapping.
  193. /// Multiple entries for the same source offset are possible (switch statements).
  194. /// The result is sorted by source offset.
  195. /// </summary>
  196. (List<Branch> Branches, BitSet IsAfterUnconditionalBranch) FindAllBranches(BlobReader body)
  197. {
  198. var result = new List<Branch>();
  199. var bitset = new BitSet(body.Length + 1);
  200. body.Reset();
  201. int target;
  202. while (body.RemainingBytes > 0) {
  203. var offset = body.Offset;
  204. int endOffset;
  205. var thisOpCode = body.DecodeOpCode();
  206. switch (thisOpCode.GetOperandType()) {
  207. case OperandType.BrTarget:
  208. case OperandType.ShortBrTarget:
  209. target = ILParser.DecodeBranchTarget(ref body, thisOpCode);
  210. endOffset = body.Offset;
  211. result.Add(new Branch(offset, endOffset, target));
  212. bitset[endOffset] = IsUnconditionalBranch(thisOpCode);
  213. break;
  214. case OperandType.Switch:
  215. var targets = ILParser.DecodeSwitchTargets(ref body);
  216. foreach (int t in targets)
  217. result.Add(new Branch(offset, body.Offset, t));
  218. break;
  219. default:
  220. ILParser.SkipOperand(ref body, thisOpCode);
  221. bitset[body.Offset] = IsUnconditionalBranch(thisOpCode);
  222. break;
  223. }
  224. }
  225. return (result, bitset);
  226. }
  227. static bool IsUnconditionalBranch(ILOpCode opCode)
  228. {
  229. switch (opCode) {
  230. case ILOpCode.Br:
  231. case ILOpCode.Br_s:
  232. case ILOpCode.Ret:
  233. case ILOpCode.Endfilter:
  234. case ILOpCode.Endfinally:
  235. case ILOpCode.Throw:
  236. case ILOpCode.Rethrow:
  237. case ILOpCode.Leave:
  238. case ILOpCode.Leave_s:
  239. return true;
  240. default:
  241. return false;
  242. }
  243. }
  244. void SortChildren()
  245. {
  246. Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
  247. foreach (ILStructure child in Children)
  248. child.SortChildren();
  249. }
  250. /// <summary>
  251. /// Gets the innermost structure containing the specified offset.
  252. /// </summary>
  253. public ILStructure GetInnermost(int offset)
  254. {
  255. Debug.Assert(StartOffset <= offset && offset < EndOffset);
  256. foreach (ILStructure child in this.Children) {
  257. if (child.StartOffset <= offset && offset < child.EndOffset)
  258. return child.GetInnermost(offset);
  259. }
  260. return this;
  261. }
  262. }
  263. }