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