Merge pull request #649 from DavidS/feature/implement-additional-reference-path
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / MethodMirror.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Reflection;
6 using C = Mono.Cecil;
7 using Mono.Cecil.Metadata;
8
9 namespace Mono.Debugger.Soft
10 {
11         public class MethodMirror : Mirror
12         {
13                 string name;
14                 MethodInfo info;
15                 TypeMirror declaring_type;
16                 DebugInfo debug_info;
17                 C.MethodDefinition meta;
18                 CustomAttributeDataMirror[] cattrs;
19                 ParameterInfoMirror[] param_info;
20                 ParameterInfoMirror ret_param;
21                 LocalVariable[] locals;
22                 IList<Location> locations;
23                 MethodBodyMirror body;
24                 MethodMirror gmd;
25                 TypeMirror[] type_args;
26
27                 internal MethodMirror (VirtualMachine vm, long id) : base (vm, id) {
28                 }
29
30                 public string Name {
31                         get {
32                                 if (name == null)
33                                         name = vm.conn.Method_GetName (id);
34                                 return name;
35                         }
36                 }
37
38                 public TypeMirror DeclaringType {
39                         get {
40                                 if (declaring_type == null)
41                                         declaring_type = vm.GetType (vm.conn.Method_GetDeclaringType (id));
42                                 return declaring_type;
43                         }
44                 }
45
46                 public TypeMirror ReturnType {
47                         get {
48                                 return ReturnParameter.ParameterType;
49                         }
50                 }
51
52                 // FIXME:
53                 public string FullName {
54                         get {
55                                 string type_namespace = DeclaringType.Namespace;
56                                 string type_name = DeclaringType.Name;
57                                 StringBuilder sb = new StringBuilder ();
58                                 sb.Append (ReturnType.Name);
59                                 sb.Append (' ');
60                                 if (type_namespace != String.Empty)
61                                         sb.Append (type_namespace + ".");
62                                 sb.Append(type_name);
63                                 sb.Append(":");
64                                 sb.Append(Name);
65                                 sb.Append(" ");
66                                 sb.Append("(");
67                                 for (var i = 0; i < param_info.Length; i++) {
68                                         sb.Append(param_info[i].ParameterType.Name);
69                                         if (i != param_info.Length - 1)
70                                                 sb.Append(", ");
71                                 }
72                                 sb.Append(")");
73                                 return sb.ToString ();
74                         }
75                 }
76
77                 /*
78                  * Creating the custom attributes themselves could modify the behavior of the
79                  * debuggee, so we return objects similar to the CustomAttributeData objects
80                  * used by the reflection-only functionality on .net.
81                  * Since protocol version 2.21
82                  */
83                 public CustomAttributeDataMirror[] GetCustomAttributes (bool inherit) {
84                         return GetCAttrs (null, inherit);
85                 }
86
87                 /* Since protocol version 2.21 */
88                 public CustomAttributeDataMirror[] GetCustomAttributes (TypeMirror attributeType, bool inherit) {
89                         if (attributeType == null)
90                                 throw new ArgumentNullException ("attributeType");
91                         return GetCAttrs (attributeType, inherit);
92                 }
93
94                 CustomAttributeDataMirror[] GetCAttrs (TypeMirror type, bool inherit) {
95                         if (cattrs == null && meta != null && !Metadata.HasCustomAttributes)
96                                 cattrs = new CustomAttributeDataMirror [0];
97
98                         // FIXME: Handle inherit
99                         if (cattrs == null) {
100                                 CattrInfo[] info = vm.conn.Method_GetCustomAttributes (id, 0, false);
101                                 cattrs = CustomAttributeDataMirror.Create (vm, info);
102                         }
103                         var res = new List<CustomAttributeDataMirror> ();
104                         foreach (var attr in cattrs)
105                                 if (type == null || attr.Constructor.DeclaringType == type)
106                                         res.Add (attr);
107                         return res.ToArray ();
108                 }
109
110                 MethodInfo GetInfo () {
111                         if (info == null)
112                                 info = vm.conn.Method_GetInfo (id);
113                         
114                         return info;
115                 }
116
117                 public int MetadataToken {
118                         get {
119                                 return GetInfo ().token;
120                         }
121                 }
122
123                 public MethodAttributes Attributes {
124                         get {
125                                 return (MethodAttributes) GetInfo ().attributes;
126                         }
127                 }
128
129                 public bool IsPublic { 
130                         get {
131                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public;
132                         }
133                 }
134                 public bool IsPrivate {
135                         get {
136                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Private;
137                         }
138                 }
139                 public bool IsFamily {
140                         get {
141                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family;
142                         }
143                 }
144                 public bool IsAssembly {
145                         get {
146                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Assembly;
147                         }
148                 }
149                 public bool IsFamilyAndAssembly {
150                         get {
151                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamANDAssem;
152                         }
153                 }
154                 public bool IsFamilyOrAssembly {
155                         get {
156                                 return (Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem;
157                         }
158                 }
159                 public bool IsStatic {
160                         get {
161                                 return (Attributes & MethodAttributes.Static) != 0;
162                         }
163                 }
164                 public bool IsFinal {
165                         get {
166                                 return (Attributes & MethodAttributes.Final) != 0;
167                         }
168                 }
169                 public bool IsVirtual {
170                         get {
171                                 return (Attributes & MethodAttributes.Virtual) != 0;
172                         }
173                 }
174                 public bool IsHideBySig {
175                         get {
176                                 return (Attributes & MethodAttributes.HideBySig) != 0;
177                         }
178                 }
179                 public bool IsAbstract {
180                         get {
181                                 return (Attributes & MethodAttributes.Abstract) != 0;
182                         }
183                 }
184                 public bool IsSpecialName {
185                         get {
186                                 return (Attributes & MethodAttributes.SpecialName) != 0;
187                         }
188                 }
189
190                 public bool IsConstructor {
191                         get {
192                                 int attr = (int)Attributes;
193                                 return ((attr & (int)MethodAttributes.RTSpecialName) != 0
194                                         && (Name == ".ctor"));
195                         }
196                 }
197
198                 // Since protocol version 2.12
199                 public bool IsGenericMethodDefinition {
200                         get {
201                                 vm.CheckProtocolVersion (2, 12);
202                                 return GetInfo ().is_gmd;
203                         }
204                 }
205
206                 // Since protocol version 2.12
207                 public bool IsGenericMethod {
208                         get {
209                                 vm.CheckProtocolVersion (2, 12);
210                                 return GetInfo ().is_generic_method;
211                         }
212                 }
213
214                 public MethodImplAttributes GetMethodImplementationFlags() {
215                         return (MethodImplAttributes)GetInfo ().iattributes;
216                 }
217
218                 public ParameterInfoMirror[] GetParameters () {
219                         if (param_info == null) {
220                                 var pi = vm.conn.Method_GetParamInfo (id);
221                                 param_info = new ParameterInfoMirror [pi.param_count];
222                                 // Return
223                                 ret_param = new ParameterInfoMirror (this, -1, vm.GetType (pi.ret_type), null, ParameterAttributes.Retval);
224                                 // FIXME: this
225                                 // FIXME: Attributes
226                                 for (int i = 0; i < pi.param_count; ++i) {
227                                         param_info [i] = new ParameterInfoMirror (this, i, vm.GetType (pi.param_types [i]), pi.param_names [i], 0);
228                                 }
229                         }
230
231                         return param_info;
232                 }
233
234                 public ParameterInfoMirror ReturnParameter {
235                         get {
236                                 if (ret_param == null)
237                                         GetParameters ();
238                                 return ret_param;
239                         }
240                 }
241
242                 public LocalVariable[] GetLocals () {
243                         if (locals == null) {
244                                 LocalsInfo li = new LocalsInfo ();
245                                 try {
246                                         li = vm.conn.Method_GetLocalsInfo (id);
247                                 } catch (CommandException) {
248                                         throw new AbsentInformationException ();
249                                 }
250
251                                 // Add the arguments as well
252                                 var pi = vm.conn.Method_GetParamInfo (id);
253
254                                 locals = new LocalVariable [pi.param_count + li.names.Length];
255
256                                 for (int i = 0; i < pi.param_count; ++i)
257                                         locals [i] = new LocalVariable (vm, this, i, pi.param_types [i], pi.param_names [i], -1, -1, true);
258
259                                 for (int i = 0; i < li.names.Length; ++i)
260                                         locals [i + pi.param_count] = new LocalVariable (vm, this, i, li.types [i], li.names [i], li.live_range_start [i], li.live_range_end [i], false);
261                         }
262                         return locals;
263                 }
264
265                 public LocalVariable GetLocal (string name) {
266                         if (name == null)
267                                 throw new ArgumentNullException ("name");
268
269                         GetLocals ();
270
271                         LocalVariable res = null;
272                         for (int i = 0; i < locals.Length; ++i) {
273                                 if (locals [i].Name == name) {
274                                         if (res != null)
275                                                 throw new AmbiguousMatchException ("More that one local has the name '" + name + "'.");
276                                         res = locals [i];
277                                 }
278                         }
279
280                         return res;
281                 }
282
283                 public MethodBodyMirror GetMethodBody () {
284                         if (body == null) {
285                                 MethodBodyInfo info = vm.conn.Method_GetBody (id);
286
287                                 body = new MethodBodyMirror (vm, this, info);
288                         }
289                         return body;
290                 }
291
292                 public MethodMirror GetGenericMethodDefinition () {
293                         vm.CheckProtocolVersion (2, 12);
294                         if (gmd == null) {
295                                 if (info.gmd == 0)
296                                         throw new InvalidOperationException ();
297                                 gmd = vm.GetMethod (info.gmd);
298                         }
299                         return gmd;
300                 }
301
302                 // Since protocol version 2.15
303                 public TypeMirror[] GetGenericArguments () {
304                         vm.CheckProtocolVersion (2, 15);
305                         if (type_args == null)
306                                 type_args = vm.GetTypes (GetInfo ().type_args);
307                         return type_args;
308                 }
309
310                 // Since protocol version 2.24
311                 public MethodMirror MakeGenericMethod (TypeMirror[] args) {
312                         if (args == null)
313                                 throw new ArgumentNullException ("args");
314                         foreach (var a in args)
315                                 if (a == null)
316                                         throw new ArgumentNullException ("args");
317
318                         if (!IsGenericMethodDefinition)
319                                 throw new InvalidOperationException ("not a generic method definition");
320
321                         if (GetGenericArguments ().Length != args.Length)
322                                 throw new ArgumentException ("Incorrect length");
323
324                         vm.CheckProtocolVersion (2, 24);
325                         long id = -1;
326                         try {
327                                 id = vm.conn.Method_MakeGenericMethod (Id, args.Select (t => t.Id).ToArray ());
328                         } catch (CommandException) {
329                                 throw new InvalidOperationException ();
330                         }
331                         return vm.GetMethod (id);
332                 }
333
334                 public IList<int> ILOffsets {
335                         get {
336                                 if (debug_info == null)
337                                         debug_info = vm.conn.Method_GetDebugInfo (id);
338                                 return Array.AsReadOnly (debug_info.il_offsets);
339                         }
340                 }
341
342                 public IList<int> LineNumbers {
343                         get {
344                                 if (debug_info == null)
345                                         debug_info = vm.conn.Method_GetDebugInfo (id);
346                                 return Array.AsReadOnly (debug_info.line_numbers);
347                         }
348                 }
349
350                 public string SourceFile {
351                         get {
352                                 if (debug_info == null)
353                                         debug_info = vm.conn.Method_GetDebugInfo (id);
354                                 return debug_info.source_files.Length > 0 ? debug_info.source_files [0].source_file : null;
355                         }
356                 }
357
358                 public IList<Location> Locations {
359                         get {
360                                 if (locations == null) {
361                                         var il_offsets = ILOffsets;
362                                         var line_numbers = LineNumbers;
363                                         IList<Location> res = new Location [ILOffsets.Count];
364                                         for (int i = 0; i < il_offsets.Count; ++i)
365                                                 res [i] = new Location (vm, this, -1, il_offsets [i], debug_info.source_files [i].source_file, line_numbers [i], debug_info.column_numbers [i], debug_info.source_files [i].hash);
366                                         locations = res;
367                                 }
368                                 return locations;
369                         }
370                 }                               
371
372                 internal int il_offset_to_line_number (int il_offset, out string src_file, out byte[] src_hash, out int column_number) {
373                         if (debug_info == null)
374                                 debug_info = vm.conn.Method_GetDebugInfo (id);
375
376                         // FIXME: Optimize this
377                         src_file = null;
378                         src_hash = null;
379                         column_number = 0;
380                         for (int i = debug_info.il_offsets.Length - 1; i >= 0; --i) {
381                                 if (debug_info.il_offsets [i] <= il_offset) {
382                                         src_file = debug_info.source_files [i].source_file;
383                                         src_hash = debug_info.source_files [i].hash;
384                                         column_number = debug_info.column_numbers [i];
385                                         return debug_info.line_numbers [i];
386                                 }
387                         }
388                         return -1;
389                 }
390
391                 public Location LocationAtILOffset (int il_offset) {
392                         IList<Location> locs = Locations;
393
394                         // FIXME: Optimize this
395                         for (int i = locs.Count - 1; i >= 0; --i) {
396                                 if (locs [i].ILOffset <= il_offset)
397                                         return locs [i];
398                         }
399
400                         return null;
401                 }
402
403                 public C.MethodDefinition Metadata {
404                         get {
405                                 if (meta == null)
406                                         meta = (C.MethodDefinition)DeclaringType.Assembly.Metadata.MainModule.LookupToken (MetadataToken);
407                                 return meta;
408                         }
409                 }
410         }
411 }