Fixed Mono.Debugger.Soft unit tests and added new `new String('a', 3);` unit test...
[mono.git] / mcs / class / Mono.Debugger.Soft / Mono.Debugger.Soft / VirtualMachine.cs
1 using System;
2 using System.IO;
3 using System.Threading;
4 using System.Net;
5 using System.Diagnostics;
6 using System.Collections;
7 using System.Collections.Generic;
8 using Mono.Cecil.Metadata;
9
10 namespace Mono.Debugger.Soft
11 {
12         public class VirtualMachine : Mirror
13         {
14                 Queue queue;
15                 object queue_monitor;
16                 object startup_monitor;
17                 AppDomainMirror root_domain;
18                 Dictionary<int, EventRequest> requests;
19                 ITargetProcess process;
20
21                 internal Connection conn;
22
23                 VersionInfo version;
24
25                 internal VirtualMachine (ITargetProcess process, Connection conn) : base () {
26                         SetVirtualMachine (this);
27                         queue = new Queue ();
28                         queue_monitor = new Object ();
29                         startup_monitor = new Object ();
30                         requests = new Dictionary <int, EventRequest> ();
31                         this.conn = conn;
32                         this.process = process;
33                         conn.ErrorHandler += ErrorHandler;
34                 }
35
36                 // The standard output of the process is available normally through Process
37                 public StreamReader StandardOutput { get; set; }
38                 public StreamReader StandardError { get; set; }
39
40                 
41                 public Process Process {
42                         get {
43                                 ProcessWrapper pw = process as ProcessWrapper;
44                                 if (pw == null)
45                                     throw new InvalidOperationException ("Process instance not available");
46                                 return pw.Process;
47                         }
48                 }
49
50                 public ITargetProcess TargetProcess {
51                         get {
52                                 return process;
53                         }
54                 }
55
56                 public AppDomainMirror RootDomain {
57                         get {
58                                 return root_domain;
59                         }
60             }
61
62                 public EndPoint EndPoint {
63                         get {
64                                 var tcpConn = conn as TcpConnection;
65                                 if (tcpConn != null)
66                                         return tcpConn.EndPoint;
67                                 return null;
68                         }
69                 }
70
71                 public VersionInfo Version {
72                         get {
73                                 return version;
74                         }
75                 }
76
77                 EventSet current_es;
78                 int current_es_index;
79
80                 /*
81                  * It is impossible to determine when to resume when using this method, since
82                  * the debuggee is suspended only once per event-set, not event.
83                  */
84                 [Obsolete ("Use GetNextEventSet () instead")]
85                 public Event GetNextEvent () {
86                         lock (queue_monitor) {
87                                 if (current_es == null || current_es_index == current_es.Events.Length) {
88                                         if (queue.Count == 0)
89                                                 Monitor.Wait (queue_monitor);
90                                         current_es = (EventSet)queue.Dequeue ();
91                                         current_es_index = 0;
92                                 }
93                                 return current_es.Events [current_es_index ++];
94                         }
95                 }
96
97                 public Event GetNextEvent (int timeout) {
98                         throw new NotImplementedException ();
99                 }
100
101                 public EventSet GetNextEventSet () {
102                         lock (queue_monitor) {
103                                 if (queue.Count == 0)
104                                         Monitor.Wait (queue_monitor);
105
106                                 current_es = null;
107                                 current_es_index = 0;
108
109                                 return (EventSet)queue.Dequeue ();
110                         }
111                 }
112
113                 [Obsolete ("Use GetNextEventSet () instead")]
114                 public T GetNextEvent<T> () where T : Event {
115                         return GetNextEvent () as T;
116                 }
117
118                 public void Suspend () {
119                         conn.VM_Suspend ();
120             }
121
122                 public void Resume () {
123                         try {
124                                 InvalidateThreadAndFrameCaches ();
125                                 conn.VM_Resume ();
126                         } catch (CommandException ex) {
127                                 if (ex.ErrorCode == ErrorCode.NOT_SUSPENDED)
128                                         throw new VMNotSuspendedException ();
129
130                                 throw;
131                         }
132             }
133
134                 public void Exit (int exitCode) {
135                         conn.VM_Exit (exitCode);
136                 }
137
138                 public void Detach () {
139                         conn.VM_Dispose ();
140                         conn.Close ();
141                         notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, 0, 0, null, 0);
142                 }
143
144                 [Obsolete ("This method was poorly named; use the Detach() method instead")]
145                 public void Dispose ()
146                 {
147                         Detach ();
148                 }
149
150                 public void ForceDisconnect ()
151                 {
152                         conn.ForceDisconnect ();
153                 }
154
155                 HashSet<ThreadMirror> threadsToInvalidate = new HashSet<ThreadMirror> ();
156                 ThreadMirror[] threadCache;
157                 object threadCacheLocker = new object ();
158
159                 void InvalidateThreadAndFrameCaches () {
160                         lock (threadsToInvalidate) {
161                                 foreach (var thread in threadsToInvalidate)
162                                         thread.InvalidateFrames ();
163                                 threadsToInvalidate.Clear ();
164                         }
165                         threadCache = null;
166                 }
167
168                 internal void InvalidateThreadCache () {
169                         threadCache = null;
170                 }
171
172                 internal void AddThreadToInvalidateList (ThreadMirror threadMirror)
173                 {
174                         lock (threadsToInvalidate) {
175                                 threadsToInvalidate.Add (threadMirror);
176                         }
177                 }
178
179                 public IList<ThreadMirror> GetThreads () {
180                         var threads = threadCache;
181                         if (threads == null) {
182                                 long[] ids = null;
183                                 var fetchingEvent = new ManualResetEvent (false);
184                                 vm.conn.VM_GetThreads ((threadsIds) => {
185                                         ids = threadsIds;
186                                         threadCache = threads = new ThreadMirror [threadsIds.Length];
187                                         fetchingEvent.Set ();
188                                 });
189                                 if (WaitHandle.WaitAny (new []{ vm.conn.DisconnectedEvent, fetchingEvent }) == 0) {
190                                         throw new VMDisconnectedException ();
191                                 }
192                                 for (int i = 0; i < ids.Length; ++i)
193                                         threads [i] = GetThread (ids [i]);
194                                 //Uncomment lines below if you want to re-fetch threads if new threads were started/stopped while
195                                 //featching threads... This is probably more correct but might cause deadlock of this method if runtime
196                                 //is starting/stopping threads nonstop, need way to prevent this(counting number of recursions?)
197                                 //possiblity before uncommenting
198                                 //if (threadCache != threads) {//While fetching threads threadCache was invalidated(thread was created/destoyed)
199                                 //      return GetThreads ();
200                                 //}
201                                 return threads;
202                         } else {
203                                 return threads;
204                         }
205                 }
206
207                 // Same as the mirrorOf methods in JDI
208                 public PrimitiveValue CreateValue (object value) {
209                         if (value == null)
210                                 return new PrimitiveValue (vm, null);
211
212                         if (!value.GetType ().IsPrimitive)
213                                 throw new ArgumentException ("value must be of a primitive type instead of '" + value.GetType () + "'", "value");
214
215                         return new PrimitiveValue (vm, value);
216                 }
217
218                 public EnumMirror CreateEnumMirror (TypeMirror type, PrimitiveValue value) {
219                         return new EnumMirror (this, type, value);
220                 }
221
222                 //
223                 // Enable send and receive timeouts on the connection and send a keepalive event
224                 // every 'keepalive_interval' milliseconds.
225                 //
226
227                 public void SetSocketTimeouts (int send_timeout, int receive_timeout, int keepalive_interval)
228                 {
229                         conn.SetSocketTimeouts (send_timeout, receive_timeout, keepalive_interval);
230                 }
231
232                 //
233                 // Methods to create event request objects
234                 //
235                 public BreakpointEventRequest CreateBreakpointRequest (MethodMirror method, long il_offset) {
236                         return new BreakpointEventRequest (this, method, il_offset);
237                 }
238
239                 public BreakpointEventRequest CreateBreakpointRequest (Location loc) {
240                         if (loc == null)
241                                 throw new ArgumentNullException ("loc");
242                         CheckMirror (loc);
243                         return new BreakpointEventRequest (this, loc.Method, loc.ILOffset);
244                 }
245
246                 public StepEventRequest CreateStepRequest (ThreadMirror thread) {
247                         return new StepEventRequest (this, thread);
248                 }
249
250                 public MethodEntryEventRequest CreateMethodEntryRequest () {
251                         return new MethodEntryEventRequest (this);
252                 }
253
254                 public MethodExitEventRequest CreateMethodExitRequest () {
255                         return new MethodExitEventRequest (this);
256                 }
257
258                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type) {
259                         return new ExceptionEventRequest (this, exc_type, true, true);
260                 }
261
262                 public ExceptionEventRequest CreateExceptionRequest (TypeMirror exc_type, bool caught, bool uncaught) {
263                         return new ExceptionEventRequest (this, exc_type, caught, uncaught);
264                 }
265
266                 public AssemblyLoadEventRequest CreateAssemblyLoadRequest () {
267                         return new AssemblyLoadEventRequest (this);
268                 }
269
270                 public TypeLoadEventRequest CreateTypeLoadRequest () {
271                         return new TypeLoadEventRequest (this);
272                 }
273
274                 public void EnableEvents (params EventType[] events) {
275                         EnableEvents (events, SuspendPolicy.All);
276                 }
277
278                 public void EnableEvents (EventType[] events, SuspendPolicy suspendPolicy) {
279                         foreach (EventType etype in events) {
280                                 if (etype == EventType.Breakpoint)
281                                         throw new ArgumentException ("Breakpoint events cannot be requested using EnableEvents", "events");
282                                 conn.EnableEvent (etype, suspendPolicy, null);
283                         }
284                 }
285
286                 public BreakpointEventRequest SetBreakpoint (MethodMirror method, long il_offset) {
287                         BreakpointEventRequest req = CreateBreakpointRequest (method, il_offset);
288
289                         req.Enable ();
290
291                         return req;
292                 }
293
294                 public void ClearAllBreakpoints () {
295                         conn.ClearAllBreakpoints ();
296                 }
297                 
298                 public void Disconnect () {
299                         conn.Close ();
300                 }
301
302                 //
303                 // Return a list of TypeMirror objects for all loaded types which reference the
304                 // source file FNAME. Might return false positives.
305                 // Since protocol version 2.7.
306                 //
307                 public IList<TypeMirror> GetTypesForSourceFile (string fname, bool ignoreCase) {
308                         long[] ids = conn.VM_GetTypesForSourceFile (fname, ignoreCase);
309                         var res = new TypeMirror [ids.Length];
310                         for (int i = 0; i < ids.Length; ++i)
311                                 res [i] = GetType (ids [i]);
312                         return res;
313                 }
314
315                 //
316                 // Return a list of TypeMirror objects for all loaded types named 'NAME'.
317                 // NAME should be in the the same for as with Assembly.GetType ().
318                 // Since protocol version 2.9.
319                 //
320                 public IList<TypeMirror> GetTypes (string name, bool ignoreCase) {
321                         long[] ids = conn.VM_GetTypes (name, ignoreCase);
322                         var res = new TypeMirror [ids.Length];
323                         for (int i = 0; i < ids.Length; ++i)
324                                 res [i] = GetType (ids [i]);
325                         return res;
326                 }
327                 
328                 internal void queue_event_set (EventSet es) {
329                         lock (queue_monitor) {
330                                 queue.Enqueue (es);
331                                 Monitor.Pulse (queue_monitor);
332                         }
333                 }
334
335                 internal void ErrorHandler (object sender, ErrorHandlerEventArgs args) {
336                         switch (args.ErrorCode) {
337                         case ErrorCode.INVALID_OBJECT:
338                                 throw new ObjectCollectedException ();
339                         case ErrorCode.INVALID_FRAMEID:
340                                 throw new InvalidStackFrameException ();
341                         case ErrorCode.NOT_SUSPENDED:
342                                 throw new VMNotSuspendedException ();
343                         case ErrorCode.NOT_IMPLEMENTED:
344                                 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
345                         case ErrorCode.ABSENT_INFORMATION:
346                                 throw new AbsentInformationException ();
347                         case ErrorCode.NO_SEQ_POINT_AT_IL_OFFSET:
348                                 throw new ArgumentException ("Cannot set breakpoint on the specified IL offset.");
349                         default:
350                                 throw new CommandException (args.ErrorCode);
351                         }
352                 }
353
354                 /* Wait for the debuggee to start up and connect to it */
355                 internal void connect () {
356                         conn.Connect ();
357
358                         // Test the connection
359                         version = conn.Version;
360                         if (version.MajorVersion != Connection.MAJOR_VERSION)
361                                 throw new NotSupportedException (String.Format ("The debuggee implements protocol version {0}.{1}, while {2}.{3} is required.", version.MajorVersion, version.MinorVersion, Connection.MAJOR_VERSION, Connection.MINOR_VERSION));
362
363                         long root_domain_id = conn.RootDomain;
364                         root_domain = GetDomain (root_domain_id);
365                 }
366
367                 internal void notify_vm_event (EventType evtype, SuspendPolicy spolicy, int req_id, long thread_id, string vm_uri, int exit_code) {
368                         //Console.WriteLine ("Event: " + evtype + "(" + vm_uri + ")");
369
370                         switch (evtype) {
371                         case EventType.VMStart:
372                                 /* Notify the main thread that the debuggee started up */
373                                 lock (startup_monitor) {
374                                         Monitor.Pulse (startup_monitor);
375                                 }
376                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMStartEvent (vm, req_id, thread_id) }));
377                                 break;
378                         case EventType.VMDeath:
379                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDeathEvent (vm, req_id, exit_code) }));
380                                 break;
381                         case EventType.VMDisconnect:
382                                 queue_event_set (new EventSet (this, spolicy, new Event[] { new VMDisconnectEvent (vm, req_id) }));
383                                 break;
384                         default:
385                                 throw new Exception ();
386                         }
387                 }
388
389                 //
390                 // Methods to create instances of mirror objects
391                 //
392
393                 /*
394                 class MirrorCache<T> {
395                         static Dictionary <long, T> mirrors;
396                         static object mirror_lock = new object ();
397
398                         internal static T GetMirror (VirtualMachine vm, long id) {
399                                 lock (mirror_lock) {
400                                 if (mirrors == null)
401                                         mirrors = new Dictionary <long, T> ();
402                                 T obj;
403                                 if (!mirrors.TryGetValue (id, out obj)) {
404                                         obj = CreateMirror (vm, id);
405                                         mirrors [id] = obj;
406                                 }
407                                 return obj;
408                                 }
409                         }
410
411                         internal static T CreateMirror (VirtualMachine vm, long id) {
412                         }
413                 }
414                 */
415
416                 // FIXME: When to remove items from the cache ?
417
418                 Dictionary <long, MethodMirror> methods;
419                 object methods_lock = new object ();
420
421                 internal MethodMirror GetMethod (long id) {
422                         lock (methods_lock) {
423                                 if (methods == null)
424                                         methods = new Dictionary <long, MethodMirror> ();
425                                 MethodMirror obj;
426                                 if (id == 0)
427                                         return null;
428                                 if (!methods.TryGetValue (id, out obj)) {
429                                         obj = new MethodMirror (this, id);
430                                         methods [id] = obj;
431                                 }
432                                 return obj;
433                         }
434             }
435
436                 Dictionary <long, AssemblyMirror> assemblies;
437                 object assemblies_lock = new object ();
438
439                 internal AssemblyMirror GetAssembly (long id) {
440                         lock (assemblies_lock) {
441                                 if (assemblies == null)
442                                         assemblies = new Dictionary <long, AssemblyMirror> ();
443                                 AssemblyMirror obj;
444                                 if (id == 0)
445                                         return null;
446                                 if (!assemblies.TryGetValue (id, out obj)) {
447                                         obj = new AssemblyMirror (this, id);
448                                         assemblies [id] = obj;
449                                 }
450                                 return obj;
451                         }
452             }
453
454                 Dictionary <long, ModuleMirror> modules;
455                 object modules_lock = new object ();
456
457                 internal ModuleMirror GetModule (long id) {
458                         lock (modules_lock) {
459                                 if (modules == null)
460                                         modules = new Dictionary <long, ModuleMirror> ();
461                                 ModuleMirror obj;
462                                 if (id == 0)
463                                         return null;
464                                 if (!modules.TryGetValue (id, out obj)) {
465                                         obj = new ModuleMirror (this, id);
466                                         modules [id] = obj;
467                                 }
468                                 return obj;
469                         }
470             }
471
472                 Dictionary <long, AppDomainMirror> domains;
473                 object domains_lock = new object ();
474
475                 internal AppDomainMirror GetDomain (long id) {
476                         lock (domains_lock) {
477                                 if (domains == null)
478                                         domains = new Dictionary <long, AppDomainMirror> ();
479                                 AppDomainMirror obj;
480                                 if (id == 0)
481                                         return null;
482                                 if (!domains.TryGetValue (id, out obj)) {
483                                         obj = new AppDomainMirror (this, id);
484                                         domains [id] = obj;
485                                 }
486                                 return obj;
487                         }
488             }
489
490                 internal void InvalidateAssemblyCaches () {
491                         lock (domains_lock) {
492                                 foreach (var d in domains.Values)
493                                         d.InvalidateAssembliesCache ();
494                         }
495                 }
496
497                 Dictionary <long, TypeMirror> types;
498                 object types_lock = new object ();
499
500                 internal TypeMirror GetType (long id) {
501                         lock (types_lock) {
502                                 if (types == null)
503                                         types = new Dictionary <long, TypeMirror> ();
504                                 TypeMirror obj;
505                                 if (id == 0)
506                                         return null;
507                                 if (!types.TryGetValue (id, out obj)) {
508                                         obj = new TypeMirror (this, id);
509                                         types [id] = obj;
510                                 }
511                                 return obj;
512                         }
513             }
514
515                 internal TypeMirror[] GetTypes (long[] ids) {
516                         var res = new TypeMirror [ids.Length];
517                         for (int i = 0; i < ids.Length; ++i)
518                                 res [i] = GetType (ids [i]);
519                         return res;
520                 }
521
522                 Dictionary <long, ObjectMirror> objects;
523                 object objects_lock = new object ();
524
525                 internal T GetObject<T> (long id, long domain_id, long type_id) where T : ObjectMirror {
526                         lock (objects_lock) {
527                                 if (objects == null)
528                                         objects = new Dictionary <long, ObjectMirror> ();
529                                 ObjectMirror obj;
530                                 if (!objects.TryGetValue (id, out obj)) {
531                                         /*
532                                          * Obtain the domain/type of the object to determine the type of
533                                          * object we need to create.
534                                          */
535                                         if (domain_id == 0 || type_id == 0) {
536                                                 if (conn.Version.AtLeast (2, 5)) {
537                                                         var info = conn.Object_GetInfo (id);
538                                                         domain_id = info.domain_id;
539                                                         type_id = info.type_id;
540                                                 } else {
541                                                         if (domain_id == 0)
542                                                                 domain_id = conn.Object_GetDomain (id);
543                                                         if (type_id == 0)
544                                                                 type_id = conn.Object_GetType (id);
545                                                 }
546                                         }
547                                         AppDomainMirror d = GetDomain (domain_id);
548                                         TypeMirror t = GetType (type_id);
549
550                                         if (t.Assembly == d.Corlib && t.Namespace == "System.Threading" && t.Name == "Thread")
551                                                 obj = new ThreadMirror (this, id, t, d);
552                                         else if (t.Assembly == d.Corlib && t.Namespace == "System" && t.Name == "String")
553                                                 obj = new StringMirror (this, id, t, d);
554                                         else if (typeof (T) == typeof (ArrayMirror))
555                                                 obj = new ArrayMirror (this, id, t, d);
556                                         else
557                                                 obj = new ObjectMirror (this, id, t, d);
558                                         objects [id] = obj;
559                                 }
560                                 return (T)obj;
561                         }
562             }
563
564                 internal T GetObject<T> (long id) where T : ObjectMirror {
565                         return GetObject<T> (id, 0, 0);
566                 }
567
568                 internal ObjectMirror GetObject (long objid) {
569                         return GetObject<ObjectMirror> (objid);
570                 }
571
572                 internal ThreadMirror GetThread (long id) {
573                         return GetObject <ThreadMirror> (id);
574                 }
575
576                 Dictionary <long, FieldInfoMirror> fields;
577                 object fields_lock = new object ();
578
579                 internal FieldInfoMirror GetField (long id) {
580                         lock (fields_lock) {
581                                 if (fields == null)
582                                         fields = new Dictionary <long, FieldInfoMirror> ();
583                                 FieldInfoMirror obj;
584                                 if (id == 0)
585                                         return null;
586                                 if (!fields.TryGetValue (id, out obj)) {
587                                         obj = new FieldInfoMirror (this, id);
588                                         fields [id] = obj;
589                                 }
590                                 return obj;
591                         }
592             }
593
594                 object requests_lock = new object ();
595
596                 internal void AddRequest (EventRequest req, int id) {
597                         lock (requests_lock) {
598                                 requests [id] = req;
599                         }
600                 }
601
602                 internal void RemoveRequest (EventRequest req, int id) {
603                         lock (requests_lock) {
604                                 requests.Remove (id);
605                         }
606                 }
607
608                 internal EventRequest GetRequest (int id) {
609                         lock (requests_lock) {
610                                 return requests [id];
611                         }
612                 }
613
614                 internal Value DecodeValue (ValueImpl v) {
615                         return DecodeValue (v, null);
616                 }
617
618                 internal Value DecodeValue (ValueImpl v, Dictionary<int, Value> parent_vtypes) {
619                         if (v.Value != null)
620                                 return new PrimitiveValue (this, v.Value);
621
622                         switch (v.Type) {
623                         case ElementType.Void:
624                                 return null;
625                         case ElementType.SzArray:
626                         case ElementType.Array:
627                                 return GetObject<ArrayMirror> (v.Objid);
628                         case ElementType.String:
629                                 return GetObject<StringMirror> (v.Objid);
630                         case ElementType.Class:
631                         case ElementType.Object:
632                                 return GetObject (v.Objid);
633                         case ElementType.ValueType:
634                                 if (parent_vtypes == null)
635                                         parent_vtypes = new Dictionary<int, Value> ();
636                                 StructMirror vtype;
637                                 if (v.IsEnum)
638                                         vtype = new EnumMirror (this, GetType (v.Klass), (Value[])null);
639                                 else
640                                         vtype = new StructMirror (this, GetType (v.Klass), (Value[])null);
641                                 parent_vtypes [parent_vtypes.Count] = vtype;
642                                 vtype.SetFields (DecodeValues (v.Fields, parent_vtypes));
643                                 parent_vtypes.Remove (parent_vtypes.Count - 1);
644                                 return vtype;
645                         case (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL:
646                                 return new PrimitiveValue (this, null);
647                         case (ElementType)ValueTypeId.VALUE_TYPE_ID_PARENT_VTYPE:
648                                 return parent_vtypes [v.Index];
649                         default:
650                                 throw new NotImplementedException ("" + v.Type);
651                         }
652                 }
653
654                 internal Value[] DecodeValues (ValueImpl[] values) {
655                         Value[] res = new Value [values.Length];
656                         for (int i = 0; i < values.Length; ++i)
657                                 res [i] = DecodeValue (values [i]);
658                         return res;
659                 }
660
661                 internal Value[] DecodeValues (ValueImpl[] values, Dictionary<int, Value> parent_vtypes) {
662                         Value[] res = new Value [values.Length];
663                         for (int i = 0; i < values.Length; ++i)
664                                 res [i] = DecodeValue (values [i], parent_vtypes);
665                         return res;
666                 }
667
668                 internal ValueImpl EncodeValue (Value v, List<Value> duplicates = null) {
669                         if (v is PrimitiveValue) {
670                                 object val = (v as PrimitiveValue).Value;
671                                 if (val == null)
672                                         return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
673                                 else
674                                         return new ValueImpl { Value = val };
675                         } else if (v is ObjectMirror) {
676                                 return new ValueImpl { Type = ElementType.Object, Objid = (v as ObjectMirror).Id };
677                         } else if (v is StructMirror) {
678                                 if (duplicates == null)
679                                         duplicates = new List<Value> ();
680                                 if (duplicates.Contains (v))
681                                         return new ValueImpl { Type = (ElementType)ValueTypeId.VALUE_TYPE_ID_NULL, Objid = 0 };
682                                 duplicates.Add (v);
683
684                                 return new ValueImpl { Type = ElementType.ValueType, Klass = (v as StructMirror).Type.Id, Fields = EncodeValues ((v as StructMirror).Fields, duplicates) };
685                         } else {
686                                 throw new NotSupportedException ();
687                         }
688                 }
689
690                 internal ValueImpl[] EncodeValues (IList<Value> values, List<Value> duplicates = null) {
691                         ValueImpl[] res = new ValueImpl [values.Count];
692                         for (int i = 0; i < values.Count; ++i)
693                                 res [i] = EncodeValue (values [i], duplicates);
694                         return res;
695                 }
696
697                 internal void CheckProtocolVersion (int major, int minor) {
698                         if (!conn.Version.AtLeast (major, minor))
699                                 throw new NotSupportedException ("This request is not supported by the protocol version implemented by the debuggee.");
700                 }
701     }
702
703         class EventHandler : MarshalByRefObject, IEventHandler
704         {               
705                 VirtualMachine vm;
706
707                 public EventHandler (VirtualMachine vm) {
708                         this.vm = vm;
709                 }
710
711                 public void Events (SuspendPolicy suspend_policy, EventInfo[] events) {
712                         var l = new List<Event> ();
713
714                         for (int i = 0; i < events.Length; ++i) {
715                                 EventInfo ei = events [i];
716                                 int req_id = ei.ReqId;
717                                 long thread_id = ei.ThreadId;
718                                 long id = ei.Id;
719                                 long loc = ei.Location;
720
721                                 switch (ei.EventType) {
722                                 case EventType.VMStart:
723                                         vm.notify_vm_event (EventType.VMStart, suspend_policy, req_id, thread_id, null, 0);
724                                         break;
725                                 case EventType.VMDeath:
726                                         vm.notify_vm_event (EventType.VMDeath, suspend_policy, req_id, thread_id, null, ei.ExitCode);
727                                         break;
728                                 case EventType.ThreadStart:
729                                         vm.InvalidateThreadCache ();
730                                         l.Add (new ThreadStartEvent (vm, req_id, id));
731                                         break;
732                                 case EventType.ThreadDeath:
733                                         vm.GetThread (id).InvalidateFrames ();
734                                         vm.InvalidateThreadCache ();
735                                         l.Add (new ThreadDeathEvent (vm, req_id, id));
736                                         break;
737                                 case EventType.AssemblyLoad:
738                                         vm.InvalidateAssemblyCaches ();
739                                         l.Add (new AssemblyLoadEvent (vm, req_id, thread_id, id));
740                                         break;
741                                 case EventType.AssemblyUnload:
742                                         vm.InvalidateAssemblyCaches ();
743                                         l.Add (new AssemblyUnloadEvent (vm, req_id, thread_id, id));
744                                         break;
745                                 case EventType.TypeLoad:
746                                         l.Add (new TypeLoadEvent (vm, req_id, thread_id, id));
747                                         break;
748                                 case EventType.MethodEntry:
749                                         l.Add (new MethodEntryEvent (vm, req_id, thread_id, id));
750                                         break;
751                                 case EventType.MethodExit:
752                                         l.Add (new MethodExitEvent (vm, req_id, thread_id, id));
753                                         break;
754                                 case EventType.Breakpoint:
755                                         l.Add (new BreakpointEvent (vm, req_id, thread_id, id, loc));
756                                         break;
757                                 case EventType.Step:
758                                         l.Add (new StepEvent (vm, req_id, thread_id, id, loc));
759                                         break;
760                                 case EventType.Exception:
761                                         l.Add (new ExceptionEvent (vm, req_id, thread_id, id, loc));
762                                         break;
763                                 case EventType.AppDomainCreate:
764                                         l.Add (new AppDomainCreateEvent (vm, req_id, thread_id, id));
765                                         break;
766                                 case EventType.AppDomainUnload:
767                                         l.Add (new AppDomainUnloadEvent (vm, req_id, thread_id, id));
768                                         break;
769                                 case EventType.UserBreak:
770                                         l.Add (new UserBreakEvent (vm, req_id, thread_id));
771                                         break;
772                                 case EventType.UserLog:
773                                         l.Add (new UserLogEvent (vm, req_id, thread_id, ei.Level, ei.Category, ei.Message));
774                                         break;
775                                 }
776                         }
777                         
778                         if (l.Count > 0)
779                                 vm.queue_event_set (new EventSet (vm, suspend_policy, l.ToArray ()));
780                 }
781
782                 public void VMDisconnect (int req_id, long thread_id, string vm_uri) {
783                         vm.notify_vm_event (EventType.VMDisconnect, SuspendPolicy.None, req_id, thread_id, vm_uri, 0);
784         }
785     }
786
787         public class CommandException : Exception {
788
789                 internal CommandException (ErrorCode error_code) : base ("Debuggee returned error code " + error_code + ".") {
790                         ErrorCode = error_code;
791                 }
792
793                 public ErrorCode ErrorCode {
794                         get; set;
795                 }
796         }
797
798         public class VMNotSuspendedException : InvalidOperationException
799         {
800                 public VMNotSuspendedException () : base ("The vm is not suspended.")
801                 {
802                 }
803         }
804 }