PageRenderTime 46ms CodeModel.GetById 18ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

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