2004-01-15 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / class / Mono.CSharp.Debugger / MonoSymbolFile.cs
1 //
2 // System.Diagnostics.SymbolStore/MonoSymbolFile.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@gnome.org)
6 //
7 // (C) 2003 Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Collections;
14 using System.Text;
15 using System.IO;
16         
17 namespace Mono.CSharp.Debugger
18 {
19         public class MonoSymbolFileException : Exception
20         {
21                 public MonoSymbolFileException ()
22                         : base ()
23                 { }
24
25                 public MonoSymbolFileException (string message, params object[] args)
26                         : base (String.Format (message, args))
27                 { }
28         }
29
30         internal class MyMemoryStream : Stream
31         {
32                 int length;
33                 int real_length;
34                 int position;
35
36                 int chunk_size = 4096;
37                 ArrayList chunks = new ArrayList ();
38
39                 private struct Chunk {
40                         public readonly int Offset;
41                         public readonly int Length;
42                         public byte[] Buffer;
43
44                         public Chunk (int offset, int length)
45                         {
46                                 this.Offset = offset;
47                                 this.Length = length;
48                                 this.Buffer = new Byte [length];
49                         }
50                 }
51
52                 public override long Position {
53                         get { return position; }
54
55                         set {
56                                 if (value > length)
57                                         throw new ArgumentOutOfRangeException ();
58
59                                 position = (int) value;
60                         }
61                 }
62
63                 public override long Length {
64                         get { return length; }
65                 }
66
67                 public override bool CanRead {
68                         get { return true; }
69                 }
70
71                 public override bool CanWrite {
72                         get { return true; }
73                 }
74
75                 public override bool CanSeek {
76                         get { return true; }
77                 }
78
79                 public override void SetLength (long new_length)
80                 {
81                         if (new_length < length)
82                                 throw new ArgumentException ();
83
84                         while (new_length >= real_length) {
85                                 Chunk new_chunk = new Chunk (real_length, chunk_size);
86                                 chunks.Add (new_chunk);
87                                 real_length += chunk_size;
88                         }
89
90                         length = (int) new_length;
91                 }
92
93                 public override void Flush ()
94                 { }
95
96                 public override long Seek (long offset, SeekOrigin origin)
97                 {
98                         int ref_point;
99
100                         switch (origin) {
101                         case SeekOrigin.Begin:
102                                 ref_point = 0;
103                                 break;
104                         case SeekOrigin.Current:
105                                 ref_point = position;
106                                 break;
107                         case SeekOrigin.End:
108                                 ref_point = length;
109                                 break;
110                         default:
111                                 throw new ArgumentException ("Invalid SeekOrigin");
112                         }
113
114                         if ((ref_point + offset < 0) || (offset > real_length))
115                                 throw new ArgumentOutOfRangeException ();
116
117                         position = ref_point + (int) offset;
118
119                         return position;
120                 }
121
122                 Chunk FindChunk (int offset)
123                 {
124                         return (Chunk) chunks [offset / chunk_size];
125                 }
126
127                 public override int Read (byte[] buffer, int offset, int count)
128                 {
129                         int old_count = count;
130
131                         while (count > 0) {
132                                 Chunk chunk = FindChunk (position);
133                                 int coffset = position - chunk.Offset;
134                                 int rest = chunk.Length - coffset;
135                                 int size = System.Math.Min (count, rest);
136
137                                 Array.Copy (chunk.Buffer, coffset, buffer, offset, size);
138                                 position += size;
139                                 offset += size;
140                                 count -= size;
141                         }
142
143                         return old_count;
144                 }
145
146                 public override void Write (byte[] buffer, int offset, int count)
147                 {
148                         if (position + count > length)
149                                 SetLength (position + count);
150
151                         while (count > 0) {
152                                 Chunk chunk = FindChunk (position);
153                                 int coffset = position - chunk.Offset;
154                                 int rest = chunk.Length - coffset;
155                                 int size = System.Math.Min (count, rest);
156
157                                 Array.Copy (buffer, offset, chunk.Buffer, coffset, size);
158                                 position += size;
159                                 offset += size;
160                                 count -= size;
161                         }
162                 }
163
164                 public byte[] GetContents ()
165                 {
166                         byte[] retval = new byte [length];
167                         position = 0;
168                         Read (retval, 0, length);
169                         return retval;
170                 }
171         }
172
173         public class MonoDebuggerSupport
174         {
175                 static MethodInfo get_token;
176                 static MethodInfo get_method_token;
177                 static MethodInfo get_method;
178                 static MethodInfo local_type_from_sig;
179
180                 static MonoDebuggerSupport ()
181                 {
182                         Type type = typeof (Assembly);
183                         get_method_token = type.GetMethod ("MonoDebugger_GetMethodToken",
184                                                            BindingFlags.Instance |
185                                                            BindingFlags.NonPublic);
186                         if (get_method_token == null)
187                                 throw new Exception (
188                                         "Can't find Assembly.MonoDebugger_GetMethodToken");
189
190                         get_method = type.GetMethod ("MonoDebugger_GetMethod",
191                                                      BindingFlags.Instance |
192                                                      BindingFlags.NonPublic);
193                         if (get_method == null)
194                                 throw new Exception (
195                                         "Can't find Assembly.MonoDebugger_GetMethod");
196
197                         local_type_from_sig = type.GetMethod (
198                                 "MonoDebugger_GetLocalTypeFromSignature",
199                                 BindingFlags.Instance | BindingFlags.NonPublic);
200                         if (local_type_from_sig == null)
201                                 throw new Exception (
202                                         "Can't find Assembly.MonoDebugger_GetLocalTypeFromSignature");
203                 }
204
205                 public static int GetMethodToken (MethodBase method)
206                 {
207                         object[] args = new object[] { method };
208                         return (int) get_method_token.Invoke (method.ReflectedType.Assembly, args);
209                 }
210
211                 public static MethodBase GetMethod (Assembly assembly, int token)
212                 {
213                         object[] args = new object[] { token };
214                         return (MethodBase) get_method.Invoke (assembly, args);
215                 }
216
217                 public static Type GetLocalTypeFromSignature (Assembly assembly, byte[] sig)
218                 {
219                         object[] args = new object[] { sig };
220                         return (Type) local_type_from_sig.Invoke (assembly, args);
221                 }
222         }
223
224         public class MonoSymbolFile : IDisposable
225         {
226                 ArrayList methods = new ArrayList ();
227                 ArrayList sources = new ArrayList ();
228                 Hashtable method_source_hash = new Hashtable ();
229                 Hashtable type_hash = new Hashtable ();
230
231                 OffsetTable ot;
232                 int last_type_index;
233                 int last_method_index;
234                 int last_source_index;
235                 int last_namespace_index;
236
237                 public MonoSymbolFile ()
238                 { }
239
240                 internal int AddSource (SourceFileEntry source)
241                 {
242                         sources.Add (source);
243                         return ++last_source_index;
244                 }
245
246                 internal int DefineType (Type type)
247                 {
248                         if (type_hash.Contains (type))
249                                 return (int) type_hash [type];
250
251                         int index = ++last_type_index;
252                         type_hash.Add (type, index);
253                         return index;
254                 }
255
256                 internal void AddMethod (MethodEntry entry)
257                 {
258                         methods.Add (entry);
259                 }
260
261                 internal int GetNextTypeIndex ()
262                 {
263                         return ++last_type_index;
264                 }
265
266                 internal int GetNextMethodIndex ()
267                 {
268                         return ++last_method_index;
269                 }
270
271                 internal int GetNextNamespaceIndex ()
272                 {
273                         return ++last_namespace_index;
274                 }
275
276                 byte [] stringBuffer;
277                 int maxCharsPerRound;
278                 static Encoding enc = Encoding.UTF8;
279                 
280                 internal void WriteString (BinaryWriter bw, string s)
281                 {
282                         int len = enc.GetByteCount (s);
283                         bw.Write (len);
284                         StringSize += len;
285                         
286                         if (stringBuffer == null) {
287                                 stringBuffer = new byte [512];
288                                 maxCharsPerRound = 512 / enc.GetMaxByteCount (1);
289                         }
290                         
291                         int chpos = 0;
292                         int chrem = s.Length;
293                         while (chrem > 0) {
294                                 int cch = (chrem > maxCharsPerRound) ? maxCharsPerRound : chrem;
295                                 int blen = enc.GetBytes (s, chpos, cch, stringBuffer, 0);
296                                 bw.Write (stringBuffer, 0, blen);
297                                 
298                                 chpos += cch;
299                                 chrem -= cch;
300                         }
301                 }
302
303                 internal string ReadString (int offset)
304                 {
305                         int old_pos = (int) reader.BaseStream.Position;
306                         reader.BaseStream.Position = offset;
307                         int length = reader.ReadInt32 ();
308
309                         byte[] data = reader.ReadBytes (length);
310                         string text = Encoding.UTF8.GetString (data);
311                         reader.BaseStream.Position = old_pos;
312                         return text;
313                 }
314
315                 void Write (BinaryWriter bw)
316                 {
317                         // Magic number and file version.
318                         bw.Write (OffsetTable.Magic);
319                         bw.Write (OffsetTable.Version);
320
321                         //
322                         // Offsets of file sections; we must write this after we're done
323                         // writing the whole file, so we just reserve the space for it here.
324                         //
325                         long offset_table_offset = bw.BaseStream.Position;
326                         ot.Write (bw);
327
328                         //
329                         // Write data sections.
330                         //
331                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
332                         foreach (SourceFileEntry source in sources)
333                                 source.WriteData (bw);
334                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
335
336                         //
337                         // Write method table.
338                         //
339                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
340                         for (int i = 0; i < methods.Count; i++) {
341                                 MethodEntry entry = (MethodEntry) methods [i];
342                                 entry.WriteIndex (bw);
343                         }
344                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
345
346                         //
347                         // Write source table.
348                         //
349                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
350                         for (int i = 0; i < sources.Count; i++) {
351                                 SourceFileEntry source = (SourceFileEntry) sources [i];
352                                 source.Write (bw);
353                         }
354                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
355
356                         //
357                         // Fixup offset table.
358                         //
359                         ot.TypeCount = last_type_index;
360                         ot.MethodCount = methods.Count;
361                         ot.SourceCount = sources.Count;
362
363                         //
364                         // Write offset table.
365                         //
366                         ot.TotalFileSize = (int) bw.BaseStream.Position;
367                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
368                         ot.Write (bw);
369                         bw.Seek (0, SeekOrigin.End);
370                 }
371
372                 public byte[] CreateSymbolFile ()
373                 {
374                         if (reader != null)
375                                 throw new InvalidOperationException ();
376
377                         using (MyMemoryStream stream = new MyMemoryStream ()) {
378                                 Write (new BinaryWriter (stream));
379                                 return stream.GetContents ();
380                         }
381                 }
382
383                 Assembly assembly;
384                 BinaryReader reader;
385                 Hashtable method_hash;
386                 Hashtable source_file_hash;
387
388                 Hashtable method_token_hash;
389                 Hashtable method_name_hash;
390                 Hashtable method_full_name_hash;
391                 Hashtable source_name_hash;
392
393                 protected MonoSymbolFile (Assembly assembly, Stream stream)
394                 {
395                         this.assembly = assembly;
396
397                         reader = new BinaryReader (stream);
398
399                         try {
400                                 long magic = reader.ReadInt64 ();
401                                 long version = reader.ReadInt32 ();
402                                 if ((magic != OffsetTable.Magic) || (version != OffsetTable.Version))
403                                         throw new MonoSymbolFileException ();
404                                 ot = new OffsetTable (reader);
405                         } catch {
406                                 throw new MonoSymbolFileException ();
407                         }
408
409                         method_hash = new Hashtable ();
410                         source_file_hash = new Hashtable ();
411                 }
412
413                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
414                 {
415                         Stream stream = assembly.GetManifestResourceStream ("MonoSymbolFile");
416                         if (stream == null)
417                                 return null;
418
419                         return new MonoSymbolFile (assembly, stream);
420                 }
421
422                 public Assembly Assembly {
423                         get { return assembly; }
424                 }
425
426                 public int SourceCount {
427                         get { return ot.SourceCount; }
428                 }
429
430                 public int MethodCount {
431                         get { return ot.MethodCount; }
432                 }
433
434                 public int TypeCount {
435                         get { return ot.TypeCount; }
436                 }
437
438                 public int NamespaceCount {
439                         get { return last_namespace_index; }
440                 }
441
442                 internal int LineNumberCount = 0;
443                 internal int LocalCount = 0;
444                 internal int StringSize = 0;
445
446                 public SourceFileEntry GetSourceFile (int index)
447                 {
448                         if ((index < 1) || (index > ot.SourceCount))
449                                 throw new ArgumentException ();
450                         if (reader == null)
451                                 throw new InvalidOperationException ();
452
453                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
454                         if (source != null)
455                                 return source;
456
457                         reader.BaseStream.Position = ot.SourceTableOffset +
458                                 SourceFileEntry.Size * (index - 1);
459                         source = new SourceFileEntry (this, reader);
460                         source_file_hash.Add (index, source);
461                         return source;
462                 }
463
464                 public SourceFileEntry[] Sources {
465                         get {
466                                 if (reader == null)
467                                         throw new InvalidOperationException ();
468
469                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
470                                 for (int i = 0; i < SourceCount; i++)
471                                         retval [i] = GetSourceFile (i + 1);
472                                 return retval;
473                         }
474                 }
475
476                 public MethodIndexEntry GetMethodIndexEntry (int index)
477                 {
478                         int old_pos = (int) reader.BaseStream.Position;
479                         reader.BaseStream.Position = ot.MethodTableOffset +
480                                 MethodIndexEntry.Size * (index - 1);
481                         MethodIndexEntry ie = new MethodIndexEntry (reader);
482                         reader.BaseStream.Position = old_pos;
483                         return ie;
484                 }
485
486                 public MethodEntry GetMethodByToken (int token)
487                 {
488                         if (reader == null)
489                                 throw new InvalidOperationException ();
490
491                         if (method_token_hash == null) {
492                                 method_token_hash = new Hashtable ();
493
494                                 for (int i = 0; i < MethodCount; i++) {
495                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
496
497                                         method_token_hash.Add (ie.Token, i + 1);
498                                 }
499                         }
500
501                         object value = method_token_hash [token];
502                         if (value == null)
503                                 return null;
504
505                         return GetMethod ((int) value);
506                 }
507
508                 public MethodEntry GetMethod (MethodBase method)
509                 {
510                         if (reader == null)
511                                 throw new InvalidOperationException ();
512                         int token = MonoDebuggerSupport.GetMethodToken (method);
513                         return GetMethodByToken (token);
514                 }
515
516                 public MethodEntry GetMethod (int index)
517                 {
518                         if ((index < 1) || (index > ot.MethodCount))
519                                 throw new ArgumentException ();
520                         if (reader == null)
521                                 throw new InvalidOperationException ();
522
523                         MethodEntry entry = (MethodEntry) method_hash [index];
524                         if (entry != null)
525                                 return entry;
526
527                         MethodIndexEntry ie = GetMethodIndexEntry (index);
528                         reader.BaseStream.Position = ie.FileOffset;
529
530                         entry = new MethodEntry (this, reader, index);
531                         method_hash.Add (index, entry);
532                         return entry;
533                 }
534
535                 public MethodEntry[] Methods {
536                         get {
537                                 if (reader == null)
538                                         throw new InvalidOperationException ();
539
540                                 MethodEntry[] retval = new MethodEntry [MethodCount];
541                                 for (int i = 0; i < MethodCount; i++)
542                                         retval [i] = GetMethod (i + 1);
543                                 return retval;
544                         }
545                 }
546
547                 public MethodSourceEntry GetMethodSource (int index)
548                 {
549                         if ((index < 1) || (index > ot.MethodCount))
550                                 throw new ArgumentException ();
551                         if (reader == null)
552                                 throw new InvalidOperationException ();
553
554                         object entry = method_source_hash [index];
555                         if (entry != null)
556                                 return (MethodSourceEntry) entry;
557
558                         MethodEntry method = GetMethod (index);
559                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
560                                 if (source.Index == index) {
561                                         method_source_hash.Add (index, source);
562                                         return source;
563                                 }
564                         }
565
566                         throw new MonoSymbolFileException ("Internal error.");
567                 }
568
569                 public int FindMethod (string full_name)
570                 {
571                         if (reader == null)
572                                 throw new InvalidOperationException ();
573
574                         if (method_full_name_hash == null) {
575                                 method_full_name_hash = new Hashtable ();
576
577                                 for (int i = 0; i < ot.MethodCount; i++) {
578                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
579                                         string name = ReadString (ie.FullNameOffset);
580
581                                         method_full_name_hash.Add (name, i + 1);
582                                 }
583                         }
584
585                         object value = method_full_name_hash [full_name];
586                         if (value == null)
587                                 return -1;
588                         return (int) value;
589                 }
590
591                 public int[] MethodLookup (string query)
592                 {
593                         if (reader == null)
594                                 throw new InvalidOperationException ();
595
596                         ArrayList list;
597                         if (method_name_hash == null) {
598                                 method_name_hash = new Hashtable ();
599
600                                 for (int i = 0; i < ot.MethodCount; i++) {
601                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
602                                         string full_name = ReadString (ie.FullNameOffset);
603
604                                         int pos = full_name.IndexOf ('(');
605                                         string name = full_name.Substring (0, pos);
606
607                                         list = method_name_hash [name] as ArrayList;
608                                         if (list == null) {
609                                                 list = new ArrayList ();
610                                                 method_name_hash.Add (name, list);
611                                         }
612
613                                         list.Add (i + 1);
614                                 }
615                         }
616
617                         list = method_name_hash [query] as ArrayList;
618                         if (list == null)
619                                 return new int [0];
620
621                         int[] retval = new int [list.Count];
622                         list.CopyTo (retval, 0);
623                         return retval;
624                 }
625
626                 public int FindSource (string file_name)
627                 {
628                         if (reader == null)
629                                 throw new InvalidOperationException ();
630
631                         if (source_name_hash == null) {
632                                 source_name_hash = new Hashtable ();
633
634                                 for (int i = 0; i < ot.SourceCount; i++) {
635                                         SourceFileEntry source = GetSourceFile (i + 1);
636
637                                         source_name_hash.Add (source.FileName, i);
638                                 }
639                         }
640
641                         object value = source_name_hash [file_name];
642                         if (value == null)
643                                 return -1;
644                         return (int) value;
645                 }
646
647                 internal BinaryReader BinaryReader {
648                         get {
649                                 if (reader == null)
650                                         throw new InvalidOperationException ();
651
652                                 return reader;
653                         }
654                 }
655
656                 void IDisposable.Dispose ()
657                 {
658                         Dispose (true);
659                 }
660
661                 protected virtual void Dispose (bool disposing)
662                 {
663                         if (disposing) {
664                                 if (reader != null) {
665                                         reader.Close ();
666                                         reader = null;
667                                 }
668                         }
669                 }
670         }
671 }