Merge pull request #952 from ermshiperete/bug-xamarin-2912
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / StackFrame.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4
5 namespace Mono.Debugger.Soft
6 {
7         public class StackFrame : Mirror
8         {
9                 ThreadMirror thread;
10                 MethodMirror method;
11                 int il_offset;
12                 Location location;
13                 StackFrameFlags flags;
14
15                 /*
16                  * FIXME: Decide on the way to request/handle debugging information:
17                  * - request the info in bulk for all frames/on demand for individual frames
18                  * - request the info from the runtime/request only the il offset, and compute
19                  *   everything else based on this info using the method debug info.
20                  */
21
22                 internal StackFrame (VirtualMachine vm, long id, ThreadMirror thread, MethodMirror method, int il_offset, StackFrameFlags flags) : base (vm, id) {
23                         this.thread = thread;
24                         this.method = method;
25                         this.il_offset = il_offset;
26                         this.flags = flags;
27                 }
28
29                 public ThreadMirror Thread {
30                         get {
31                                 return thread;
32                         }
33                 }
34
35                 public MethodMirror Method {
36                         get {
37                                 return method;
38                         }
39                 }
40
41                 public Location Location {
42                         get {
43                                 if (location == null) {
44                                         int line_number;
45                                         string src_file = null;
46                                         byte[] hash = null;
47                                         int column_number = 0;
48                                         int end_line_number = -1;
49                                         int end_column_number = -1;
50
51                                         if (il_offset == -1)
52                                                 line_number = -1;
53                                         else
54                                                 line_number = method.il_offset_to_line_number (il_offset, out src_file, out hash, out column_number, out end_line_number, out end_column_number);
55
56                                         location = new Location (vm, Method, 0, il_offset, src_file != null ? src_file : method.SourceFile, line_number, column_number, end_line_number, end_column_number, hash);
57                                 }
58                                 return location;
59                         }
60                 }
61
62                 public string FileName {
63                         get {
64                                 return Location.SourceFile;
65                         }
66                 }
67
68                 public int ILOffset {
69                         get {
70                                 return Location.ILOffset;
71                         }
72                 }
73
74                 public int LineNumber {
75                         get {
76                                 return Location.LineNumber;
77                         }
78                 }
79
80                 public int ColumnNumber {
81                         get {
82                                 return Location.ColumnNumber;
83                         }
84                 }
85
86                 public bool IsDebuggerInvoke {
87                         get {
88                                 return (flags & StackFrameFlags.DEBUGGER_INVOKE) != 0;
89                         }
90                 }
91
92                 /*
93                  * Whenever this frame transitions to native code. The method associated
94                  * with the frame is either an InternalCall or a pinvoke method.
95                  */
96                 public bool IsNativeTransition {
97                         get {
98                                 return (flags & StackFrameFlags.NATIVE_TRANSITION) != 0;
99                         }
100                 }
101
102                 public Value GetValue (ParameterInfoMirror param) {
103                         if (param == null)
104                                 throw new ArgumentNullException ("param");
105                         if (param.Method != Method)
106                                 throw new ArgumentException ("Parameter doesn't belong to this frame's method.");
107                         if (param.IsRetval)
108                                 throw new ArgumentException ("Parameter represents the method return value.");
109
110                         // FIXME: Liveness
111                         // FIXME: Allow returning the frame return value if possible
112                         return vm.DecodeValue (vm.conn.StackFrame_GetValues (thread.Id, Id, new int [] { (- param.Position) - 1 })[0]);
113                 }
114
115                 public Value GetValue (LocalVariable var) {
116                         if (var == null)
117                                 throw new ArgumentNullException ("var");
118                         if (var.Method != Method)
119                                 throw new ArgumentException ("Local variable doesn't belong to this frame's method.");
120
121                         // FIXME: Liveness
122                         // FIXME: Check for return value
123                         // FIXME: Allow returning the frame return value if possible
124                         return vm.DecodeValue (vm.conn.StackFrame_GetValues (thread.Id, Id, new int [] { var.GetValueIndex } )[0]);
125                 }
126
127                 public Value[] GetValues (LocalVariable[] vars) {
128                         if (vars == null)
129                                 throw new ArgumentNullException ("vars");
130                         for (int i = 0; i < vars.Length; ++i) {
131                                 if (vars [i] == null)
132                                         throw new ArgumentNullException ("vars");
133                                 if (vars [i].Method != Method)
134                                         throw new ArgumentException ("Local variable doesn't belong to this frame's method.");
135                         }
136                         int[] pos = new int [vars.Length];
137                         for (int i = 0; i < vars.Length; ++i)
138                                 pos [i] = vars [i].GetValueIndex;
139                         return vm.DecodeValues (vm.conn.StackFrame_GetValues (thread.Id, Id, pos));
140                 }
141
142                 public Value GetArgument (int pos) {
143                         return GetValue (Method.GetParameters () [pos]);
144                 }
145
146                 public Value GetThis () {
147                         return vm.DecodeValue (vm.conn.StackFrame_GetThis (thread.Id, Id));
148                 }
149
150                 public void SetValue (LocalVariable var, Value value) {
151                         if (var == null)
152                                 throw new ArgumentNullException ("var");
153                         if (var.Method != Method)
154                                 throw new ArgumentException ("Local variable doesn't belong to this frame's method.");
155                         if (value == null)
156                                 throw new ArgumentNullException ("value");
157                         CheckMirror (value);
158                         // FIXME: Liveness
159                         // FIXME: Check for return value
160                         try {
161                                 vm.conn.StackFrame_SetValues (thread.Id, Id, new int [] { var.GetValueIndex }, new ValueImpl [] { vm.EncodeValue (value) });
162                         } catch (CommandException ex) {
163                                 if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
164                                         throw new ArgumentException ("Value does not match the type of the local variable.");
165                                 else
166                                         throw;
167                         }
168                 }
169
170                 public void SetValue (ParameterInfoMirror param, Value value) {
171                         if (param == null)
172                                 throw new ArgumentNullException ("param");
173                         if (param.Method != Method)
174                                 throw new ArgumentException ("Parameter doesn't belong to this frame's method.");
175                         if (param.IsRetval)
176                                 throw new ArgumentException ("Parameter represents the method return value.");
177                         if (value == null)
178                                 throw new ArgumentNullException ("value");
179                         CheckMirror (value);
180
181                         // FIXME: Liveness
182                         // FIXME: Allow setting the frame return value if possible
183                         try {
184                                 vm.conn.StackFrame_SetValues (thread.Id, Id, new int [] { (- param.Position) - 1 }, new ValueImpl [] { vm.EncodeValue (value) });
185                         } catch (CommandException ex) {
186                                 if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
187                                         throw new ArgumentException ("Value does not match the type of the variable.");
188                                 else
189                                         throw;
190                         }
191                 }
192
193                 public IList<LocalVariable> GetVisibleVariables () {
194                         if (Location.ILOffset == -1)
195                                 throw new AbsentInformationException ();
196
197                         return Method.GetLocals ().Where (l => l.LiveRangeStart <= location.ILOffset && l.LiveRangeEnd >= location.ILOffset).ToList ();
198                 }
199
200                 public LocalVariable GetVisibleVariableByName (string name) {
201                         if (name == null)
202                                 throw new ArgumentNullException ("name");
203
204                         if (Location.ILOffset == -1)
205                                 throw new AbsentInformationException ();
206
207                         return Method.GetLocals ().Where (l => l.LiveRangeStart <= location.ILOffset && l.LiveRangeEnd >= location.ILOffset && l.Name == name).FirstOrDefault ();
208                 }
209     }
210 }