Merge pull request #4185 from BrzVlad/fix-arm64-finally-abort
[mono.git] / mcs / tools / mono-symbolicate / LocationProvider.cs
1 using System;
2 using System.IO;
3 using System.Linq;
4 using System.Text;
5 using System.Collections.Generic;
6 using Mono.Cecil;
7 using Mono.Cecil.Cil;
8 using Mono.Collections.Generic;
9
10 namespace Mono
11 {
12         class AssemblyLocationProvider
13         {
14                 AssemblyDefinition assembly;
15                 Logger logger;
16
17                 public AssemblyLocationProvider (string assemblyPath, Logger logger)
18                 {
19                         assemblyPath = Path.GetFullPath (assemblyPath);
20                         this.logger = logger;
21
22                         if (!File.Exists (assemblyPath))
23                                 throw new ArgumentException ("assemblyPath does not exist: "+ assemblyPath);
24
25                         var readerParameters = new ReaderParameters { ReadSymbols = true };
26                         assembly = AssemblyDefinition.ReadAssembly (assemblyPath, readerParameters);
27                 }
28
29                 public bool TryResolveLocation (StackFrameData sfData, SeqPointInfo seqPointInfo)
30                 {
31                         if (!assembly.MainModule.HasSymbols)
32                                 return false;
33
34                         TypeDefinition type = null;
35                         string[] nested;
36                         if (sfData.TypeFullName.IndexOf ('/') >= 0)
37                                  nested = sfData.TypeFullName.Split ('/');
38                         else
39                                 nested = sfData.TypeFullName.Split ('+');
40
41                         var types = assembly.MainModule.Types;
42                         foreach (var ntype in nested) {
43                                 if (type == null) {
44                                         // Use namespace first time.
45                                         type = types.FirstOrDefault (t => t.FullName == ntype);
46                                 } else {
47                                         type = types.FirstOrDefault (t => t.Name == ntype);
48                                 }
49
50                                 if (type == null) {
51                                         logger.LogWarning ("Could not find type: {0}", ntype);
52                                         return false;
53                                 }
54
55                                 types = type.NestedTypes;
56                         }
57
58                         var parensStart = sfData.MethodSignature.IndexOf ('(');
59                         var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
60                         var methodParameters = sfData.MethodSignature.Substring (parensStart);
61                         var methods = type.Methods.Where (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters)).ToArray ();
62                         if (methods.Length == 0) {
63                                 logger.LogWarning ("Could not find method: {0}", methodName);
64                                 return false;
65                         }
66                         if (methods.Length > 1) {
67                                 logger.LogWarning ("Ambiguous match for method: {0}", sfData.MethodSignature);
68                                 return false;
69                         }
70                         var method = methods [0];
71
72                         int ilOffset;
73                         if (sfData.IsILOffset) {
74                                 ilOffset = sfData.Offset;
75                         } else {
76                                 if (seqPointInfo == null)
77                                         return false;
78
79                                 ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
80                         }
81
82                         if (ilOffset < 0)
83                                 return false;
84
85                         if (!method.DebugInformation.HasSequencePoints)
86                                 return false;
87
88                         SequencePoint prev = null;
89                         foreach (var sp in method.DebugInformation.SequencePoints.OrderBy (l => l.Offset)) {
90                                 if (sp.Offset >= ilOffset) {
91                                         sfData.SetLocation (sp.Document.Url, sp.StartLine);
92                                         return true;
93                                 }
94
95                                 prev = sp;
96                         }
97
98                         if (prev != null) {
99                                 sfData.SetLocation (prev.Document.Url, prev.StartLine);
100                                 return true;
101                         }
102
103                         return false;
104                 }
105
106                 static bool CompareName (MethodDefinition candidate, string expected)
107                 {
108                         if (candidate.Name == expected)
109                                 return true;
110
111                         if (!candidate.HasGenericParameters)
112                                 return false;
113
114                         var genStart = expected.IndexOf ('[');
115                         if (genStart < 0)
116                                 genStart = expected.IndexOf ('<');
117
118                         if (genStart < 0)
119                                 return false;
120
121                         if (candidate.Name != expected.Substring (0, genStart))
122                                 return false;
123
124                         int arity = 1;
125                         for (int pos = genStart; pos < expected.Length; ++pos) {
126                                 if (expected [pos] == ',')
127                                         ++arity;
128                         }
129
130                         return candidate.GenericParameters.Count == arity;
131                 }
132
133                 static string RemoveGenerics (string expected, char open, char close)
134                 {
135                         if (expected.IndexOf (open) < 0)
136                                 return expected;
137
138                         var sb = new StringBuilder ();
139                         for (int i = 0; i < expected.Length;) {
140                                 int start = expected.IndexOf (open, i);
141                                 int end = expected.IndexOf (close, i);
142                                 if (start < 0 || end < 0) {
143                                         sb.Append (expected, i, expected.Length - i);
144                                         break;
145                                 }
146
147                                 bool is_ginst = false;
148                                 for (int j = start + 1; j < end; ++j) {
149                                         if (expected [j] != ',')
150                                                 is_ginst = true;
151                                 }
152
153                                 if (is_ginst) //discard the the generic args
154                                         sb.Append (expected, i, start - i);
155                                 else //include array arity
156                                         sb.Append (expected, i, end + 1 - i);
157                                 i = end + 1;
158
159                         }
160                         return sb.ToString ();
161                 }
162
163                 static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
164                 {
165                         var builder = new StringBuilder ();
166                         builder.Append ("(");
167
168                         for (int i = 0; i < candidate.Count; i++) {
169                                 var parameter = candidate [i];
170                                 if (i > 0)
171                                         builder.Append (", ");
172
173                                 if (parameter.ParameterType.IsSentinel)
174                                         builder.Append ("...,");
175
176                                 var pt = parameter.ParameterType;
177                                 FormatElementType (pt, builder);
178
179                                 builder.Append (" ");
180                                 builder.Append (parameter.Name);
181                         }
182
183                         builder.Append (")");
184
185                         if (builder.ToString () == RemoveGenerics (expected, '[', ']'))
186                                 return true;
187
188                         //now try the compact runtime format.
189
190                         builder.Clear ();
191
192                         builder.Append ("(");
193
194                         for (int i = 0; i < candidate.Count; i++) {
195                                 var parameter = candidate [i];
196                                 if (i > 0)
197                                         builder.Append (",");
198
199                                 if (parameter.ParameterType.IsSentinel)
200                                         builder.Append ("...,");
201
202                                 var pt = parameter.ParameterType;
203
204                                 RuntimeFormatElementType (pt, builder);
205                         }
206
207                         builder.Append (")");
208
209                         if (builder.ToString () == RemoveGenerics (expected, '<', '>'))
210                                 return true;
211                         return false;
212
213                 }
214
215                 static void RuntimeFormatElementType (TypeReference tr, StringBuilder builder)
216                 {
217                         var ts = tr as TypeSpecification;
218                         if (ts != null) {
219                                 if (ts.IsByReference) {
220                                         RuntimeFormatElementType (ts.ElementType, builder);
221                                         builder.Append ("&");
222                                         return;
223                                 }
224                         }
225
226                         switch (tr.MetadataType) {
227                         case MetadataType.Void:
228                                 builder.Append ("void");
229                                 break;
230                         case MetadataType.Boolean:
231                                 builder.Append ("bool");
232                                 break;
233                         case MetadataType.Char:
234                                 builder.Append ("char");
235                                 break;
236                         case MetadataType.SByte:
237                                 builder.Append ("sbyte");
238                                 break;
239                         case MetadataType.Byte:
240                                 builder.Append ("byte");
241                                 break;
242                         case MetadataType.Int16:
243                                 builder.Append ("int16");
244                                 break;
245                         case MetadataType.UInt16:
246                                 builder.Append ("uint16");
247                                 break;
248                         case MetadataType.Int32:
249                                 builder.Append ("int");
250                                 break;
251                         case MetadataType.UInt32:
252                                 builder.Append ("uint");
253                                 break;
254                         case MetadataType.Int64:
255                                 builder.Append ("long");
256                                 break;
257                         case MetadataType.UInt64:
258                                 builder.Append ("ulong");
259                                 break;
260                         case MetadataType.Single:
261                                 builder.Append ("single");
262                                 break;
263                         case MetadataType.Double:
264                                 builder.Append ("double");
265                                 break;
266                         case MetadataType.String:
267                                 builder.Append ("string");
268                                 break;
269                         case MetadataType.Pointer:
270                                 builder.Append (((TypeSpecification)tr).ElementType);
271                                 builder.Append ("*");
272                                 break;
273                         case MetadataType.ValueType:
274                         case MetadataType.Class:
275                         case MetadataType.GenericInstance: {
276                                 FormatName (tr, builder, '/');
277                                 break;
278                         }
279                         case MetadataType.Var:
280                         case MetadataType.MVar:
281                                 builder.Append (tr.Name);
282                                 builder.Append ("_REF");
283                                 break;
284                         case MetadataType.Array: {
285                                 var array = (ArrayType)tr;
286                                 RuntimeFormatElementType (array.ElementType, builder);
287                                 builder.Append ("[");
288
289                                 for (int i = 0; i < array.Rank - 1; ++i)
290                                         builder.Append (",");
291
292                                 builder.Append ("]");
293                                 break;
294                         }
295
296                         case MetadataType.TypedByReference:
297                                 builder.Append ("typedbyref");
298                                 break;
299                         case MetadataType.IntPtr:
300                                 builder.Append ("intptr");
301                                 break;
302                         case MetadataType.UIntPtr:
303                                 builder.Append ("uintptr");
304                                 break;
305                         case MetadataType.FunctionPointer:
306                                 builder.Append ("*()");
307                                 break;
308                         case MetadataType.Object:
309                                 builder.Append ("object");
310                                 break;
311                         default:
312                                 builder.Append ("-unknown-");
313                                 break;
314                         }
315                 }
316
317                 static void FormatName (TypeReference tr, StringBuilder builder, char sep)
318                 {
319                         if (tr.IsNested && !(tr.MetadataType == MetadataType.Var || tr.MetadataType == MetadataType.MVar)) {
320                                 FormatName (tr.DeclaringType, builder, sep);
321                                 builder.Append (sep);
322                         }
323                         if (!string.IsNullOrEmpty (tr.Namespace)) {
324                                 builder.Append (tr.Namespace);
325                                 builder.Append (".");
326                         }
327
328                         builder.Append (tr.Name);
329                 }
330
331                 static void FormatElementType (TypeReference tr, StringBuilder builder)
332                 {
333                         var ts = tr as TypeSpecification;
334                         if (ts != null) {
335                                 if (ts.IsByReference) {
336                                         FormatElementType (ts.ElementType, builder);
337                                         builder.Append ("&");
338                                         return;
339                                 }
340
341                                 var array = ts as ArrayType;
342                                 if (array != null) {
343                                         FormatElementType (ts.ElementType, builder);
344                                         builder.Append ("[");
345
346                                         for (int ii = 0; ii < array.Rank - 1; ++ii) {
347                                                 builder.Append (",");
348                                         }
349
350                                         builder.Append ("]");
351                                         return;
352                                 }
353                         }
354                         FormatName (tr, builder, '+');
355                 }
356         }
357 }
358