File: CodeMappings.cs

package info (click to toggle)
monodevelop 3.0.3.2%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 153,256 kB
  • sloc: cs: 1,020,242; xml: 751,053; makefile: 9,596; sh: 1,529; objc: 302; sql: 129; ansic: 96
file content (256 lines) | stat: -rwxr-xr-x 8,560 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;

namespace ICSharpCode.Decompiler
{
	/// <summary>
	/// Maps the source code to IL.
	/// </summary>
	public sealed class SourceCodeMapping
	{
		/// <summary>
		/// Gets or sets the start location of the instruction.
		/// </summary>
		public TextLocation StartLocation { get; set; }
		
		/// <summary>
		/// Gets or sets the end location of the instruction.
		/// </summary>
		public TextLocation EndLocation { get; set; }
		
		/// <summary>
		/// Gets or sets IL Range offset for the source code line. E.g.: 13-19 &lt;-&gt; 135.
		/// </summary>
		public ILRange ILInstructionOffset { get; set; }
		
		/// <summary>
		/// Gets or sets the member mapping this source code mapping belongs to.
		/// </summary>
		public MemberMapping MemberMapping { get; set; }
		
		/// <summary>
		/// Retrieves the array that contains the IL range and the missing gaps between ranges.
		/// </summary>
		/// <returns>The array representation of the step aranges.</returns>
		public int[] ToArray(bool isMatch)
		{
			var currentList = new List<ILRange>();
			
			// add list for the current source code line
			currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings
			                                           .FindAll(m => m.StartLocation.Line == this.StartLocation.Line)
			                                           .ConvertAll<ILRange>(m => m.ILInstructionOffset)));
			
			if (!isMatch) {
				// add inverted
				currentList.AddRange(MemberMapping.InvertedList);
			} else {
				// if the current list contains the last mapping, add also the last gap
				var lastInverted = MemberMapping.InvertedList.LastOrDefault();
				if (lastInverted != null && lastInverted.From == currentList[currentList.Count - 1].To)
					currentList.Add(lastInverted);
			}
			
			// set the output
			var resultList = new List<int>();
			foreach (var element in ILRange.OrderAndJoint(currentList)) {
				resultList.Add(element.From);
				resultList.Add(element.To);
			}
			
			return resultList.ToArray();
		}
	}
	
	/// <summary>
	/// Stores the member information and its source code mappings.
	/// </summary>
	public sealed class MemberMapping
	{
		IEnumerable<ILRange> invertedList;
		
		internal MemberMapping()
		{
		}
		
		public MemberMapping(MethodDefinition method)
		{
			this.MetadataToken = method.MetadataToken.ToInt32();
			this.MemberCodeMappings = new List<SourceCodeMapping>();
			this.MemberReference = method;
			this.CodeSize = method.Body.CodeSize;
		}
		
		/// <summary>
		/// Gets or sets the type of the mapping.
		/// </summary>
		public MemberReference MemberReference { get; internal set; }
		
		/// <summary>
		/// Metadata token of the member.
		/// </summary>
		public int MetadataToken { get; internal set; }
		
		/// <summary>
		/// Gets or sets the code size for the member mapping.
		/// </summary>
		public int CodeSize { get; internal set; }
		
		/// <summary>
		/// Gets or sets the source code mappings.
		/// </summary>
		public List<SourceCodeMapping> MemberCodeMappings { get; internal set; }
		
		/// <summary>
		/// Gets or sets the local variables.
		/// </summary>
		public IEnumerable<ILVariable> LocalVariables { get; internal set; }
		
		/// <summary>
		/// Gets the inverted IL Ranges.<br/>
		/// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21).
		/// </summary>
		/// <returns>IL Range inverted list.</returns>
		public IEnumerable<ILRange> InvertedList
		{
			get {
				if (invertedList == null) {
					var list = MemberCodeMappings.ConvertAll<ILRange>(
						s => new ILRange { From = s.ILInstructionOffset.From, To = s.ILInstructionOffset.To });
					invertedList = ILRange.OrderAndJoint(ILRange.Invert(list, CodeSize));
				}
				return invertedList;
			}
		}
	}
	
	/// <summary>
	/// Code mappings helper class.
	/// </summary>
	public static class CodeMappings
	{
		/// <summary>
		/// Gets source code mapping and metadata token based on type name and line number.
		/// </summary>
		/// <param name="codeMappings">Code mappings storage.</param>
		/// <param name="typeName">Member reference name.</param>
		/// <param name="lineNumber">Line number.</param>
		/// <param name="metadataToken">Metadata token.</param>
		/// <returns></returns>
		public static SourceCodeMapping GetInstructionByLineNumber(
			this MemberMapping codeMapping,
			int lineNumber,
			out int metadataToken)
		{
			if (codeMapping == null)
				throw new ArgumentException("CodeMappings storage must be valid!");
			
			var map = codeMapping.MemberCodeMappings.Find(m => m.StartLocation.Line == lineNumber);
			if (map != null) {
				metadataToken = codeMapping.MetadataToken;
				return map;
			}
			
			metadataToken = 0;
			return null;
		}
		
		/// <summary>
		/// Gets a mapping given a type, a token and an IL offset.
		/// </summary>
		/// <param name="codeMappings">Code mappings storage.</param>
		/// <param name="token">Token.</param>
		/// <param name="ilOffset">IL offset.</param>
		/// <param name="isMatch">True, if perfect match.</param>
		/// <returns>A code mapping.</returns>
		public static SourceCodeMapping GetInstructionByTokenAndOffset(
			this MemberMapping codeMapping,
			int ilOffset,
			out bool isMatch)
		{
			isMatch = false;
			
			if (codeMapping == null)
				throw new ArgumentNullException("CodeMappings storage must be valid!");
			
			// try find an exact match
			var map = codeMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To);
			
			if (map == null) {
				// get the immediate next one
				map = codeMapping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset);
				isMatch = false;
				if (map == null)
					map = codeMapping.MemberCodeMappings.LastOrDefault(); // get the last
				
				return map;
			}
			
			isMatch = true;
			return map;
		}
		
		/// <summary>
		/// Gets the source code and type name from metadata token and offset.
		/// </summary>
		/// <param name="codeMappings">Code mapping storage.</param>
		/// <param name="token">Metadata token.</param>
		/// <param name="ilOffset">IL offset.</param>
		/// <param name="typeName">Type definition.</param>
		/// <param name="line">Line number.</param>
		/// <remarks>It is possible to exist to different types from different assemblies with the same metadata token.</remarks>
		public static bool GetInstructionByTokenAndOffset(
			this MemberMapping mapping,
			int ilOffset,
			out MemberReference member,
			out int line)
		{
			member = null;
			line = 0;
			
			if (mapping == null)
				throw new ArgumentException("CodeMappings storage must be valid!");

			var codeMapping = mapping.MemberCodeMappings.Find(
				cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1);
			if (codeMapping == null) {
				codeMapping = mapping.MemberCodeMappings.Find(cm => cm.ILInstructionOffset.From > ilOffset);
				if (codeMapping == null) {
					codeMapping = mapping.MemberCodeMappings.LastOrDefault();
					if (codeMapping == null)
						return false;
				}
			}
			
			member = mapping.MemberReference;
			line = codeMapping.StartLocation.Line;
			return true;
		}
	}
}