Merge pull request #3321 from BrzVlad/fix-block-state-membar
[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                         var nested = sfData.TypeFullName.Split ('+');
36                         var types = assembly.MainModule.Types;
37                         foreach (var ntype in nested) {
38                                 if (type == null) {
39                                         // Use namespace first time.
40                                         type = types.FirstOrDefault (t => t.FullName == ntype);
41                                 } else {
42                                         type = types.FirstOrDefault (t => t.Name == ntype);
43                                 }
44
45                                 if (type == null) {
46                                         logger.LogWarning ("Could not find type: {0}", ntype);
47                                         return false;
48                                 }
49
50                                 types = type.NestedTypes;
51                         }
52
53                         var parensStart = sfData.MethodSignature.IndexOf ('(');
54                         var methodName = sfData.MethodSignature.Substring (0, parensStart).TrimEnd ();
55                         var methodParameters = sfData.MethodSignature.Substring (parensStart);
56                         var method = type.Methods.FirstOrDefault (m => CompareName (m, methodName) && CompareParameters (m.Parameters, methodParameters));
57                         if (method == null) {
58                                 logger.LogWarning ("Could not find method: {0}", methodName);
59                                 return false;
60                         }
61
62                         int ilOffset;
63                         if (sfData.IsILOffset) {
64                                 ilOffset = sfData.Offset;
65                         } else {
66                                 if (seqPointInfo == null)
67                                         return false;
68
69                                 ilOffset = seqPointInfo.GetILOffset (method.MetadataToken.ToInt32 (), sfData.MethodIndex, sfData.Offset);
70                         }
71
72                         if (ilOffset < 0)
73                                 return false;
74
75                         SequencePoint sp = null;
76                         foreach (var instr in method.Body.Instructions) {
77                                 if (instr.SequencePoint != null)
78                                         sp = instr.SequencePoint;
79                                 
80                                 if (instr.Offset >= ilOffset) {
81                                         sfData.SetLocation (sp.Document.Url, sp.StartLine);
82                                         return true;
83                                 }
84                         }
85
86                         return false;
87                 }
88
89                 static bool CompareName (MethodDefinition candidate, string expected)
90                 {
91                         if (candidate.Name == expected)
92                                 return true;
93
94                         if (!candidate.HasGenericParameters)
95                                 return false;
96                         
97                         var genStart = expected.IndexOf ('[');
98                         if (genStart < 0)
99                                 return false;
100
101                         if (candidate.Name != expected.Substring (0, genStart))
102                                 return false;
103
104                         int arity = 1;
105                         for (int pos = genStart; pos < expected.Length; ++pos) {
106                                 if (expected [pos] == ',')
107                                         ++arity;
108                         }
109
110                         return candidate.GenericParameters.Count == arity;
111                 }
112
113                 static bool CompareParameters (Collection<ParameterDefinition> candidate, string expected)
114                 {
115                         var builder = new StringBuilder ();
116                         builder.Append ("(");
117
118                         for (int i = 0; i < candidate.Count; i++) {
119                                 var parameter = candidate [i];
120                                 if (i > 0)
121                                         builder.Append (", ");
122
123                                 if (parameter.ParameterType.IsSentinel)
124                                         builder.Append ("...,");
125
126                                 var pt = parameter.ParameterType;
127                                 if (!string.IsNullOrEmpty (pt.Namespace)) {
128                                         builder.Append (pt.Namespace);
129                                         builder.Append (".");
130                                 }
131
132                                 FormatElementType (pt, builder);
133
134                                 builder.Append (" ");
135                                 builder.Append (parameter.Name);
136                         }
137
138                         builder.Append (")");
139
140                         return builder.ToString () == expected;
141                 }
142
143                 static void FormatElementType (TypeReference tr, StringBuilder builder)
144                 {
145                         var ts = tr as TypeSpecification;
146                         if (ts != null) {
147                                 if (ts.IsByReference) {
148                                         FormatElementType (ts.ElementType, builder);
149                                         builder.Append ("&");
150                                         return;
151                                 }
152
153                                 var array = ts as ArrayType;
154                                 if (array != null) {
155                                         FormatElementType (ts.ElementType, builder);
156                                         builder.Append ("[");
157
158                                         for (int ii = 0; ii < array.Rank - 1; ++ii) {
159                                                 builder.Append (",");
160                                         }
161
162                                         builder.Append ("]");
163                                         return;
164                                 }
165                         }
166
167                         builder.Append (tr.Name);
168                 }
169         }
170 }
171