summaryrefslogtreecommitdiff
path: root/mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/ObjectMirror.cs
blob: 8a060a2df938a5275576ec71d11e5aa56869cfa5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Messaging;
using System.Threading;
#if NET_4_5
using System.Threading.Tasks;
#endif

namespace Mono.Debugger.Soft
{
	public class ObjectMirror : Value {
		TypeMirror type;
		AppDomainMirror domain;
	
		internal ObjectMirror (VirtualMachine vm, long id) : base (vm, id) {
		}
	
		internal ObjectMirror (VirtualMachine vm, long id, TypeMirror type, AppDomainMirror domain) : base (vm, id) {
			this.type = type;
			this.domain = domain;
		}

		void GetInfo () {
			var info = vm.conn.Object_GetInfo (id);
			type = vm.GetType (info.type_id);
			domain = vm.GetDomain (info.domain_id);
		}

		public TypeMirror Type {
			get {
				if (type == null) {
					if (vm.conn.Version.AtLeast (2, 5))
						GetInfo ();
					else
				 		type = vm.GetType (vm.conn.Object_GetType (id));
				}
				return type;
			}
		}

		public AppDomainMirror Domain {
			get {
				if (domain == null) {
					if (vm.conn.Version.AtLeast (2, 5))
						GetInfo ();
					else
						domain = vm.GetDomain (vm.conn.Object_GetDomain (id));
				}
				return domain;
			}
		}

		public bool IsCollected {
			get {
				return vm.conn.Object_IsCollected (id);
			}
		}

		public Value GetValue (FieldInfoMirror field) {
			return GetValues (new FieldInfoMirror [] { field }) [0];
		}

		public Value[] GetValues (IList<FieldInfoMirror> fields) {
			if (fields == null)
				throw new ArgumentNullException ("fields");
			foreach (FieldInfoMirror f in fields) {
				if (f == null)
					throw new ArgumentNullException ("field");
				CheckMirror (f);
			}
			long[] ids = new long [fields.Count];
			for (int i = 0; i < fields.Count; ++i)
				ids [i] = fields [i].Id;
			try {
				return vm.DecodeValues (vm.conn.Object_GetValues (id, ids));
			} catch (CommandException ex) {
				if (ex.ErrorCode == ErrorCode.INVALID_FIELDID)
					throw new ArgumentException ("One of the fields is not valid for this type.", "fields");
				else
					throw;
			}
		}

		public void SetValues (IList<FieldInfoMirror> fields, Value[] values) {
			if (fields == null)
				throw new ArgumentNullException ("fields");
			if (values == null)
				throw new ArgumentNullException ("values");
			foreach (FieldInfoMirror f in fields) {
				if (f == null)
					throw new ArgumentNullException ("field");
				CheckMirror (f);
			}
			foreach (Value v in values) {
				if (v == null)
					throw new ArgumentNullException ("values");
				CheckMirror (v);
			}
			long[] ids = new long [fields.Count];
			for (int i = 0; i < fields.Count; ++i)
				ids [i] = fields [i].Id;
			try {
				vm.conn.Object_SetValues (id, ids, vm.EncodeValues (values));
			} catch (CommandException ex) {
				if (ex.ErrorCode == ErrorCode.INVALID_FIELDID)
					throw new ArgumentException ("One of the fields is not valid for this type.", "fields");
				else if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
					throw new ArgumentException ("One of the values is not valid for its field.", "values");
				else
					throw;
			}
		}

		public void SetValue (FieldInfoMirror field, Value value) {
			SetValues (new FieldInfoMirror [] { field }, new Value [] { value });
		}

		/*
		 * The current address of the object. It can change during garbage 
		 * collections. Use a long since the debuggee might have a different 
		 * pointer size. 
		 */
		public long Address {
			get {
				return vm.conn.Object_GetAddress (id);
			}
		}

		public Value InvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments) {
			return InvokeMethod (vm, thread, method, this, arguments, InvokeOptions.None);
		}

		public Value InvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options) {
			return InvokeMethod (vm, thread, method, this, arguments, options);
		}

		[Obsolete ("Use the overload without the 'vm' argument")]
		public IAsyncResult BeginInvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
			return BeginInvokeMethod (vm, thread, method, this, arguments, options, callback, state);
		}

		public IAsyncResult BeginInvokeMethod (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
			return BeginInvokeMethod (vm, thread, method, this, arguments, options, callback, state);
		}

		public Value EndInvokeMethod (IAsyncResult asyncResult) {
			return EndInvokeMethodInternal (asyncResult);
		}

#if NET_4_5
		public Task<Value> InvokeMethodAsync (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options = InvokeOptions.None) {
			var tcs = new TaskCompletionSource<Value> ();
			BeginInvokeMethod (thread, method, arguments, options, iar =>
					{
						try {
							tcs.SetResult (EndInvokeMethod (iar));
						} catch (OperationCanceledException) {
							tcs.TrySetCanceled ();
						} catch (Exception ex) {
							tcs.TrySetException (ex);
						}
					}, null);
			return tcs.Task;
		}
#endif

		//
		// 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
		// finished. The callback will be called with a different IAsyncResult that represents one method invocation.
		// From protocol version 2.22.
		//
		public IAsyncResult BeginInvokeMultiple (ThreadMirror thread, MethodMirror[] methods, IList<IList<Value>> arguments, InvokeOptions options, AsyncCallback callback, object state) {
			return BeginInvokeMultiple (vm, thread, methods, this, arguments, options, callback, state);
		}

		public void EndInvokeMultiple (IAsyncResult asyncResult) {
			EndInvokeMultipleInternal (asyncResult);
		}

		/*
		 * Common implementation for invokes
		 */

		class InvokeAsyncResult : IInvokeAsyncResult {

			public object AsyncState {
				get; set;
			}

			public WaitHandle AsyncWaitHandle {
				get; set;
			}

			public bool CompletedSynchronously {
				get {
					return false;
				}
			}

			public bool IsCompleted {
				get; set;
			}

			public AsyncCallback Callback {
				get; set;
			}

			public ErrorCode ErrorCode {
				get; set;
			}

			public VirtualMachine VM {
				get; set;
			}

			public ThreadMirror Thread {
				get; set;
			}

			public ValueImpl Value {
				get; set;
			}

			public ValueImpl Exception {
				get; set;
			}

			public int ID {
				get; set;
			}

			public bool IsMultiple {
				get; set;
			}
			   
			public int NumPending;

			public void Abort ()
			{
				if (ID == 0) // Ooops
					return;

				ObjectMirror.AbortInvoke (VM, Thread, ID);
			}
		}

		internal static IInvokeAsyncResult BeginInvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, Value this_obj, IList<Value> arguments, InvokeOptions options, AsyncCallback callback, object state) {
			if (thread == null)
				throw new ArgumentNullException ("thread");
			if (method == null)
				throw new ArgumentNullException ("method");
			if (arguments == null)
				arguments = new Value [0];

			InvokeFlags f = InvokeFlags.NONE;

			if ((options & InvokeOptions.DisableBreakpoints) != 0)
				f |= InvokeFlags.DISABLE_BREAKPOINTS;
			if ((options & InvokeOptions.SingleThreaded) != 0)
				f |= InvokeFlags.SINGLE_THREADED;

			InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };

			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);

			return r;
		}

		// This is called when the result of an invoke is received
		static void InvokeCB (ValueImpl v, ValueImpl exc, ErrorCode error, object state) {
			InvokeAsyncResult r = (InvokeAsyncResult)state;

			if (error != 0) {
				r.ErrorCode = error;
			} else {
				r.Value = v;
				r.Exception = exc;
			}

			r.IsCompleted = true;
			((ManualResetEvent)r.AsyncWaitHandle).Set ();

			if (r.Callback != null)
				r.Callback.BeginInvoke (r, null, null);
		}

	    internal static Value EndInvokeMethodInternal (IAsyncResult asyncResult) {
			if (asyncResult == null)
				throw new ArgumentNullException ("asyncResult");

			InvokeAsyncResult r = (InvokeAsyncResult)asyncResult;

			if (!r.IsCompleted)
				r.AsyncWaitHandle.WaitOne ();

			if (r.ErrorCode != 0) {
				try {
					r.VM.ErrorHandler (null, new ErrorHandlerEventArgs () { ErrorCode = r.ErrorCode });
				} catch (CommandException ex) {
					if (ex.ErrorCode == ErrorCode.INVALID_ARGUMENT)
						throw new ArgumentException ("Incorrect number or types of arguments", "arguments");
					else
						throw;
				}
				throw new NotImplementedException ();
			} else {
				if (r.Exception != null)
					throw new InvocationException ((ObjectMirror)r.VM.DecodeValue (r.Exception));
				else
					return r.VM.DecodeValue (r.Value);
			}
		}

	    internal static void EndInvokeMultipleInternal (IAsyncResult asyncResult) {
			if (asyncResult == null)
				throw new ArgumentNullException ("asyncResult");

			InvokeAsyncResult r = (InvokeAsyncResult)asyncResult;

			if (!r.IsCompleted)
				r.AsyncWaitHandle.WaitOne ();
		}

		internal static Value InvokeMethod (VirtualMachine vm, ThreadMirror thread, MethodMirror method, Value this_obj, IList<Value> arguments, InvokeOptions options) {
			return EndInvokeMethodInternal (BeginInvokeMethod (vm, thread, method, this_obj, arguments, options, null, null));
		}

		internal static void AbortInvoke (VirtualMachine vm, ThreadMirror thread, int id)
		{
			vm.conn.VM_AbortInvoke (thread.Id, id);
		}

		//
		// Implementation of InvokeMultiple
		//

		internal static IInvokeAsyncResult BeginInvokeMultiple (VirtualMachine vm, ThreadMirror thread, MethodMirror[] methods, Value this_obj, IList<IList<Value>> arguments, InvokeOptions options, AsyncCallback callback, object state) {
			if (thread == null)
				throw new ArgumentNullException ("thread");
			if (methods == null)
				throw new ArgumentNullException ("methods");
			foreach (var m in methods)
				if (m == null)
					throw new ArgumentNullException ("method");
			if (arguments == null) {
				arguments = new List<IList<Value>> ();
				for (int i = 0; i < methods.Length; ++i)
					arguments.Add (new Value [0]);
			} else {
				// FIXME: Not needed for property evaluation
				throw new NotImplementedException ();
			}
			if (callback == null)
				throw new ArgumentException ("A callback argument is required for this method.", "callback");

			InvokeFlags f = InvokeFlags.NONE;

			if ((options & InvokeOptions.DisableBreakpoints) != 0)
				f |= InvokeFlags.DISABLE_BREAKPOINTS;
			if ((options & InvokeOptions.SingleThreaded) != 0)
				f |= InvokeFlags.SINGLE_THREADED;

			InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback, NumPending = methods.Length, IsMultiple = true };

			var mids = new long [methods.Length];
			for (int i = 0; i < methods.Length; ++i)
				mids [i] = methods [i].Id;
			var args = new List<ValueImpl[]> ();
			for (int i = 0; i < methods.Length; ++i)
				args.Add (vm.EncodeValues (arguments [i]));
			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);

			return r;
		}

		// This is called when the result of an invoke is received
		static void InvokeMultipleCB (ValueImpl v, ValueImpl exc, ErrorCode error, object state) {
			var r = (InvokeAsyncResult)state;

			Interlocked.Decrement (ref r.NumPending);

			if (r.NumPending == 0) {
				r.IsCompleted = true;
				((ManualResetEvent)r.AsyncWaitHandle).Set ();
			}

			// Have to pass another asyncresult to the callback since multiple threads can execute it concurrently with results of multiple invocations
			var r2 = new InvokeAsyncResult { AsyncState = r.AsyncState, AsyncWaitHandle = null, VM = r.VM, Thread = r.Thread, Callback = r.Callback, IsCompleted = true };

			if (error != 0) {
				r2.ErrorCode = error;
			} else {
				r2.Value = v;
				r2.Exception = exc;
			}

			r.Callback.BeginInvoke (r2, null, null);
		}
	}
}