Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / ObjectMirror.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Runtime.Remoting.Messaging;
4 using System.Threading;
5 #if NET_4_5
6 using System.Threading.Tasks;
7 #endif
8
9 namespace Mono.Debugger.Soft
10 {
11         public class ObjectMirror : Value {
12                 TypeMirror type;
13                 AppDomainMirror domain;
14         
15                 internal ObjectMirror (VirtualMachine vm, long id) : base (vm, id) {
16                 }
17         
18                 internal ObjectMirror (VirtualMachine vm, long id, TypeMirror type, AppDomainMirror domain) : base (vm, id) {
19                         this.type = type;
20                         this.domain = domain;
21                 }
22
23                 void GetInfo () {
24                         var info = vm.conn.Object_GetInfo (id);
25                         type = vm.GetType (info.type_id);
26                         domain = vm.GetDomain (info.domain_id);
27                 }
28
29                 public TypeMirror Type {
30                         get {
31                                 if (type == null) {
32                                         if (vm.conn.Version.AtLeast (2, 5))
33                                                 GetInfo ();
34                                         else
35                                                 type = vm.GetType (vm.conn.Object_GetType (id));
36                                 }
37                                 return type;
38                         }
39                 }
40
41                 public AppDomainMirror Domain {
42                         get {
43                                 if (domain == null) {
44                                         if (vm.conn.Version.AtLeast (2, 5))
45                                                 GetInfo ();
46                                         else
47                                                 domain = vm.GetDomain (vm.conn.Object_GetDomain (id));
48                                 }
49                                 return domain;
50                         }
51                 }
52
53                 public bool IsCollected {
54                         get {
55                                 return vm.conn.Object_IsCollected (id);
56                         }
57                 }
58
59                 public Value GetValue (FieldInfoMirror field) {
60                         return GetValues (new FieldInfoMirror [] { field }) [0];
61                 }
62
63                 public Value[] GetValues (IList<FieldInfoMirror> fields) {
64                         if (fields == null)
65                                 throw new ArgumentNullException ("fields");
66                         foreach (FieldInfoMirror f in fields) {
67                                 if (f == null)
68                                         throw new ArgumentNullException ("field");
69                                 CheckMirror (f);
70                         }
71                         long[] ids = new long [fields.Count];
72                         for (int i = 0; i < fields.Count; ++i)
73                                 ids [i] = fields [i].Id;
74                         try {
75                                 return vm.DecodeValues (vm.conn.Object_GetValues (id, ids));
76                         } catch (CommandException ex) {
77                                 if (ex.ErrorCode == ErrorCode.INVALID_FIELDID)
78                                         throw new ArgumentException ("One of the fields is not valid for this type.", "fields");
79                                 else
80                                         throw;
81                         }
82                 }
83
84                 public void SetValues (IList<FieldInfoMirror> fields, Value[] values) {
85                         if (fields == null)
86                                 throw new ArgumentNullException ("fields");
87                         if (values == null)
88                                 throw new ArgumentNullException ("values");
89                         foreach (FieldInfoMirror f in fields) {
90                                 if (f == null)
91                                         throw new ArgumentNullException ("field");
92                                 CheckMirror (f);
93                         }
94                         foreach (Value v in values) {
95                                 if (v == null)
96                                         throw new ArgumentNullException ("values");
97                                 CheckMirror (v);
98                         }
99                         long[] ids = new long [fields.Count];
100                         for (int i = 0; i < fields.Count; ++i)
101                                 ids [i] = fields [i].Id;
102                         try {
103                                 vm.conn.Object_SetValues (id, ids, vm.EncodeValues (values));
104                         } catch (CommandException ex) {
105                                 if (ex.ErrorCode == ErrorCode.INVALID_FIELDID)
106                                         throw new ArgumentException ("One of the fields is not valid for this type.", "fields");
107                                 else if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
108                                         throw new ArgumentException ("One of the values is not valid for its field.", "values");
109                                 else
110                                         throw;
111                         }
112                 }
113
114                 public void SetValue (FieldInfoMirror field, Value value) {
115                         SetValues (new FieldInfoMirror [] { field }, new Value [] { value });
116                 }
117
118                 /*
119                  * The current address of the object. It can change during garbage 
120                  * collections. Use a long since the debuggee might have a different 
121                  * pointer size. 
122                  */
123                 public long Address {
124                         get {
125                                 return vm.conn.Object_GetAddress (id);
126                         }
127                 }
128
129                 public Value InvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments) {
130                         return InvokeMethod (vm, thread, method, this, arguments, InvokeOptions.None);
131                 }
132
133                 public Value InvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options) {
134                         return InvokeMethod (vm, thread, method, this, arguments, options);
135                 }
136
137                 [Obsolete ("Use the overload without the 'vm' argument")]
138                 public IAsyncResult BeginInvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
139                         return BeginInvokeMethod (vm, thread, method, this, arguments, options, callback, state);
140                 }
141
142                 public IAsyncResult BeginInvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
143                         return BeginInvokeMethod (vm, thread, method, this, arguments, options, callback, state);
144                 }
145
146                 public Value EndInvokeMethod (IAsyncResult asyncResult) {
147                         return EndInvokeMethodInternal (asyncResult);
148                 }
149
150 #if NET_4_5
151                 public Task<Value> InvokeMethodAsync (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options = InvokeOptions.None) {
152                         var tcs = new TaskCompletionSource<Value> ();
153                         BeginInvokeMethod (thread, method, arguments, options, iar =>
154                                         {
155                                                 try {
156                                                         tcs.SetResult (EndInvokeMethod (iar));
157                                                 } catch (OperationCanceledException) {
158                                                         tcs.TrySetCanceled ();
159                                                 } catch (Exception ex) {
160                                                         tcs.TrySetException (ex);
161                                                 }
162                                         }, null);
163                         return tcs.Task;
164                 }
165 #endif
166
167                 //
168                 // Invoke the members of METHODS one-by-one, calling CALLBACK after each invoke was finished. The IAsyncResult will be marked as completed after all invokes have
169                 // finished. The callback will be called with a different IAsyncResult that represents one method invocation.
170                 // From protocol version 2.22.
171                 //
172                 public IAsyncResult BeginInvokeMultiple (ThreadMirror thread, MethodMirror[] methods, IList<IList<Value>> arguments, InvokeOptions options, AsyncCallback callback, object state) {
173                         return BeginInvokeMultiple (vm, thread, methods, this, arguments, options, callback, state);
174                 }
175
176                 public void EndInvokeMultiple (IAsyncResult asyncResult) {
177                         EndInvokeMultipleInternal (asyncResult);
178                 }
179
180                 /*
181                  * Common implementation for invokes
182                  */
183
184                 class InvokeAsyncResult : IInvokeAsyncResult {
185
186                         public object AsyncState {
187                                 get; set;
188                         }
189
190                         public WaitHandle AsyncWaitHandle {
191                                 get; set;
192                         }
193
194                         public bool CompletedSynchronously {
195                                 get {
196                                         return false;
197                                 }
198                         }
199
200                         public bool IsCompleted {
201                                 get; set;
202                         }
203
204                         public AsyncCallback Callback {
205                                 get; set;
206                         }
207
208                         public ErrorCode ErrorCode {
209                                 get; set;
210                         }
211
212                         public VirtualMachine VM {
213                                 get; set;
214                         }
215
216                         public ThreadMirror Thread {
217                                 get; set;
218                         }
219
220                         public ValueImpl Value {
221                                 get; set;
222                         }
223
224                         public ValueImpl Exception {
225                                 get; set;
226                         }
227
228                         public int ID {
229                                 get; set;
230                         }
231
232                         public bool IsMultiple {
233                                 get; set;
234                         }
235                            
236                         public int NumPending;
237
238                         public void Abort ()
239                         {
240                                 if (ID == 0) // Ooops
241                                         return;
242
243                                 ObjectMirror.AbortInvoke (VM, Thread, ID);
244                         }
245                 }
246
247                 internal static IInvokeAsyncResult BeginInvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, Value this_obj, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
248                         if (thread == null)
249                                 throw new ArgumentNullException ("thread");
250                         if (method == null)
251                                 throw new ArgumentNullException ("method");
252                         if (arguments == null)
253                                 arguments = new Value [0];
254
255                         InvokeFlags f = InvokeFlags.NONE;
256
257                         if ((options & InvokeOptions.DisableBreakpoints) != 0)
258                                 f |= InvokeFlags.DISABLE_BREAKPOINTS;
259                         if ((options & InvokeOptions.SingleThreaded) != 0)
260                                 f |= InvokeFlags.SINGLE_THREADED;
261
262                         InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };
263
264                         r.ID = vm.conn.VM_BeginInvokeMethod (thread.Id, method.Id, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), vm.EncodeValues (arguments), f, InvokeCB, r);
265
266                         return r;
267                 }
268
269                 // This is called when the result of an invoke is received
270                 static void InvokeCB (ValueImpl v, ValueImpl exc, ErrorCode error, object state) {
271                         InvokeAsyncResult r = (InvokeAsyncResult)state;
272
273                         if (error != 0) {
274                                 r.ErrorCode = error;
275                         } else {
276                                 r.Value = v;
277                                 r.Exception = exc;
278                         }
279
280                         r.IsCompleted = true;
281                         ((ManualResetEvent)r.AsyncWaitHandle).Set ();
282
283                         if (r.Callback != null)
284                                 r.Callback.BeginInvoke (r, null, null);
285                 }
286
287             internal static Value EndInvokeMethodInternal (IAsyncResult asyncResult) {
288                         if (asyncResult == null)
289                                 throw new ArgumentNullException ("asyncResult");
290
291                         InvokeAsyncResult r = (InvokeAsyncResult)asyncResult;
292
293                         if (!r.IsCompleted)
294                                 r.AsyncWaitHandle.WaitOne ();
295
296                         if (r.ErrorCode != 0) {
297                                 try {
298                                         r.VM.ErrorHandler (null, new ErrorHandlerEventArgs () { ErrorCode = r.ErrorCode });
299                                 } catch (CommandException ex) {
300                                         if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
301                                                 throw new ArgumentException ("Incorrect number or types of arguments", "arguments");
302                                         else
303                                                 throw;
304                                 }
305                                 throw new NotImplementedException ();
306                         } else {
307                                 if (r.Exception != null)
308                                         throw new InvocationException ((ObjectMirror)r.VM.DecodeValue (r.Exception));
309                                 else
310                                         return r.VM.DecodeValue (r.Value);
311                         }
312                 }
313
314             internal static void EndInvokeMultipleInternal (IAsyncResult asyncResult) {
315                         if (asyncResult == null)
316                                 throw new ArgumentNullException ("asyncResult");
317
318                         InvokeAsyncResult r = (InvokeAsyncResult)asyncResult;
319
320                         if (!r.IsCompleted)
321                                 r.AsyncWaitHandle.WaitOne ();
322                 }
323
324                 internal static Value InvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, Value this_obj, IList<Value> arguments, InvokeOptions options) {
325                         return EndInvokeMethodInternal (BeginInvokeMethod (vm, thread, method, this_obj, arguments, options, null, null));
326                 }
327
328                 internal static void AbortInvoke (VirtualMachine vm, ThreadMirror thread, int id)
329                 {
330                         vm.conn.VM_AbortInvoke (thread.Id, id);
331                 }
332
333                 //
334                 // Implementation of InvokeMultiple
335                 //
336
337                 internal static IInvokeAsyncResult BeginInvokeMultiple (VirtualMachine vm, ThreadMirror thread, MethodMirror[] methods, Value this_obj, IList<IList<Value>> arguments, InvokeOptions options, AsyncCallback callback, object state) {
338                         if (thread == null)
339                                 throw new ArgumentNullException ("thread");
340                         if (methods == null)
341                                 throw new ArgumentNullException ("methods");
342                         foreach (var m in methods)
343                                 if (m == null)
344                                         throw new ArgumentNullException ("method");
345                         if (arguments == null) {
346                                 arguments = new List<IList<Value>> ();
347                                 for (int i = 0; i < methods.Length; ++i)
348                                         arguments.Add (new Value [0]);
349                         } else {
350                                 // FIXME: Not needed for property evaluation
351                                 throw new NotImplementedException ();
352                         }
353                         if (callback == null)
354                                 throw new ArgumentException ("A callback argument is required for this method.", "callback");
355
356                         InvokeFlags f = InvokeFlags.NONE;
357
358                         if ((options & InvokeOptions.DisableBreakpoints) != 0)
359                                 f |= InvokeFlags.DISABLE_BREAKPOINTS;
360                         if ((options & InvokeOptions.SingleThreaded) != 0)
361                                 f |= InvokeFlags.SINGLE_THREADED;
362
363                         InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback, NumPending = methods.Length, IsMultiple = true };
364
365                         var mids = new long [methods.Length];
366                         for (int i = 0; i < methods.Length; ++i)
367                                 mids [i] = methods [i].Id;
368                         var args = new List<ValueImpl[]> ();
369                         for (int i = 0; i < methods.Length; ++i)
370                                 args.Add (vm.EncodeValues (arguments [i]));
371                         r.ID = vm.conn.VM_BeginInvokeMethods (thread.Id, mids, this_obj != null ? vm.EncodeValue (this_obj) : vm.EncodeValue (vm.CreateValue (null)), args, f, InvokeMultipleCB, r);
372
373                         return r;
374                 }
375
376                 // This is called when the result of an invoke is received
377                 static void InvokeMultipleCB (ValueImpl v, ValueImpl exc, ErrorCode error, object state) {
378                         var r = (InvokeAsyncResult)state;
379
380                         Interlocked.Decrement (ref r.NumPending);
381
382                         if (r.NumPending == 0) {
383                                 r.IsCompleted = true;
384                                 ((ManualResetEvent)r.AsyncWaitHandle).Set ();
385                         }
386
387                         // Have to pass another asyncresult to the callback since multiple threads can execute it concurrently with results of multiple invocations
388                         var r2 = new InvokeAsyncResult { AsyncState = r.AsyncState, AsyncWaitHandle = null, VM = r.VM, Thread = r.Thread, Callback = r.Callback, IsCompleted = true };
389
390                         if (error != 0) {
391                                 r2.ErrorCode = error;
392                         } else {
393                                 r2.Value = v;
394                                 r2.Exception = exc;
395                         }
396
397                         r.Callback.BeginInvoke (r2, null, null);
398                 }
399         }
400 }