2003-12-27 Ben Maurer <bmaurer@users.sourceforge.net>
[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 MonoSymbolFile : IDisposable
174         {
175                 ArrayList methods = new ArrayList ();
176                 ArrayList sources = new ArrayList ();
177                 Hashtable method_source_hash = new Hashtable ();
178                 Hashtable type_hash = new Hashtable ();
179
180                 OffsetTable ot;
181                 int last_type_index;
182                 int last_method_index;
183                 int last_source_index;
184                 int last_namespace_index;
185
186                 public MonoSymbolFile ()
187                 { }
188
189                 internal int AddSource (SourceFileEntry source)
190                 {
191                         sources.Add (source);
192                         return ++last_source_index;
193                 }
194
195                 internal int DefineType (Type type)
196                 {
197                         if (type_hash.Contains (type))
198                                 return (int) type_hash [type];
199
200                         int index = ++last_type_index;
201                         type_hash.Add (type, index);
202                         return index;
203                 }
204
205                 internal void AddMethod (MethodEntry entry)
206                 {
207                         methods.Add (entry);
208                 }
209
210                 internal int GetNextTypeIndex ()
211                 {
212                         return ++last_type_index;
213                 }
214
215                 internal int GetNextMethodIndex ()
216                 {
217                         return ++last_method_index;
218                 }
219
220                 internal int GetNextNamespaceIndex ()
221                 {
222                         return ++last_namespace_index;
223                 }
224
225                 byte [] stringBuffer;
226                 int maxCharsPerRound;
227                 static Encoding enc = Encoding.UTF8;
228                 
229                 internal void WriteString (BinaryWriter bw, string s)
230                 {
231                         int len = enc.GetByteCount (s);
232                         bw.Write (len);
233                         StringSize += len;
234                         
235                         if (stringBuffer == null) {
236                                 stringBuffer = new byte [512];
237                                 maxCharsPerRound = 512 / enc.GetMaxByteCount (1);
238                         }
239                         
240                         int chpos = 0;
241                         int chrem = s.Length;
242                         while (chrem > 0) {
243                                 int cch = (chrem > maxCharsPerRound) ? maxCharsPerRound : chrem;
244                                 int blen = enc.GetBytes (s, chpos, cch, stringBuffer, 0);
245                                 bw.Write (stringBuffer, 0, blen);
246                                 
247                                 chpos += cch;
248                                 chrem -= cch;
249                         }
250                 }
251
252                 internal string ReadString (int offset)
253                 {
254                         int old_pos = (int) reader.BaseStream.Position;
255                         reader.BaseStream.Position = offset;
256                         int length = reader.ReadInt32 ();
257
258                         byte[] data = reader.ReadBytes (length);
259                         string text = Encoding.UTF8.GetString (data);
260                         reader.BaseStream.Position = old_pos;
261                         return text;
262                 }
263
264                 void Write (BinaryWriter bw)
265                 {
266                         // Magic number and file version.
267                         bw.Write (OffsetTable.Magic);
268                         bw.Write (OffsetTable.Version);
269
270                         //
271                         // Offsets of file sections; we must write this after we're done
272                         // writing the whole file, so we just reserve the space for it here.
273                         //
274                         long offset_table_offset = bw.BaseStream.Position;
275                         ot.Write (bw);
276
277                         //
278                         // Write data sections.
279                         //
280                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
281                         foreach (SourceFileEntry source in sources)
282                                 source.WriteData (bw);
283                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
284
285                         //
286                         // Write method table.
287                         //
288                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
289                         for (int i = 0; i < methods.Count; i++) {
290                                 MethodEntry entry = (MethodEntry) methods [i];
291                                 entry.WriteIndex (bw);
292                         }
293                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
294
295                         //
296                         // Write source table.
297                         //
298                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
299                         for (int i = 0; i < sources.Count; i++) {
300                                 SourceFileEntry source = (SourceFileEntry) sources [i];
301                                 source.Write (bw);
302                         }
303                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
304
305                         //
306                         // Fixup offset table.
307                         //
308                         ot.TypeCount = last_type_index;
309                         ot.MethodCount = methods.Count;
310                         ot.SourceCount = sources.Count;
311
312                         //
313                         // Write offset table.
314                         //
315                         ot.TotalFileSize = (int) bw.BaseStream.Position;
316                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
317                         ot.Write (bw);
318                         bw.Seek (0, SeekOrigin.End);
319                 }
320
321                 public byte[] CreateSymbolFile ()
322                 {
323                         if (reader != null)
324                                 throw new InvalidOperationException ();
325
326                         using (MyMemoryStream stream = new MyMemoryStream ()) {
327                                 Write (new BinaryWriter (stream));
328                                 return stream.GetContents ();
329                         }
330                 }
331
332                 Assembly assembly;
333                 BinaryReader reader;
334                 Hashtable method_hash;
335                 Hashtable source_file_hash;
336
337                 Hashtable method_token_hash;
338                 Hashtable method_name_hash;
339                 Hashtable method_full_name_hash;
340                 Hashtable source_name_hash;
341
342                 protected MonoSymbolFile (Assembly assembly, Stream stream)
343                 {
344                         this.assembly = assembly;
345
346                         reader = new BinaryReader (stream);
347
348                         try {
349                                 long magic = reader.ReadInt64 ();
350                                 long version = reader.ReadInt32 ();
351                                 if ((magic != OffsetTable.Magic) || (version != OffsetTable.Version))
352                                         throw new MonoSymbolFileException ();
353                                 ot = new OffsetTable (reader);
354                         } catch {
355                                 throw new MonoSymbolFileException ();
356                         }
357
358                         method_hash = new Hashtable ();
359                         source_file_hash = new Hashtable ();
360                 }
361
362                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
363                 {
364                         Stream stream = assembly.GetManifestResourceStream ("MonoSymbolFile");
365                         if (stream == null)
366                                 return null;
367
368                         return new MonoSymbolFile (assembly, stream);
369                 }
370
371                 public Assembly Assembly {
372                         get { return assembly; }
373                 }
374
375                 public int SourceCount {
376                         get { return ot.SourceCount; }
377                 }
378
379                 public int MethodCount {
380                         get { return ot.MethodCount; }
381                 }
382
383                 public int TypeCount {
384                         get { return ot.TypeCount; }
385                 }
386
387                 public int NamespaceCount {
388                         get { return last_namespace_index; }
389                 }
390
391                 internal int LineNumberCount = 0;
392                 internal int LocalCount = 0;
393                 internal int StringSize = 0;
394
395                 public SourceFileEntry GetSourceFile (int index)
396                 {
397                         if ((index < 1) || (index > ot.SourceCount))
398                                 throw new ArgumentException ();
399                         if (reader == null)
400                                 throw new InvalidOperationException ();
401
402                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
403                         if (source != null)
404                                 return source;
405
406                         reader.BaseStream.Position = ot.SourceTableOffset +
407                                 SourceFileEntry.Size * (index - 1);
408                         source = new SourceFileEntry (this, reader);
409                         source_file_hash.Add (index, source);
410                         return source;
411                 }
412
413                 public SourceFileEntry[] Sources {
414                         get {
415                                 if (reader == null)
416                                         throw new InvalidOperationException ();
417
418                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
419                                 for (int i = 0; i < SourceCount; i++)
420                                         retval [i] = GetSourceFile (i + 1);
421                                 return retval;
422                         }
423                 }
424
425                 public MethodIndexEntry GetMethodIndexEntry (int index)
426                 {
427                         int old_pos = (int) reader.BaseStream.Position;
428                         reader.BaseStream.Position = ot.MethodTableOffset +
429                                 MethodIndexEntry.Size * (index - 1);
430                         MethodIndexEntry ie = new MethodIndexEntry (reader);
431                         reader.BaseStream.Position = old_pos;
432                         return ie;
433                 }
434
435                 public MethodEntry GetMethodByToken (int token)
436                 {
437                         if (reader == null)
438                                 throw new InvalidOperationException ();
439
440                         if (method_token_hash == null) {
441                                 method_token_hash = new Hashtable ();
442
443                                 for (int i = 0; i < MethodCount; i++) {
444                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
445
446                                         method_token_hash.Add (ie.Token, i + 1);
447                                 }
448                         }
449
450                         object value = method_token_hash [token];
451                         if (value == null)
452                                 return null;
453
454                         return GetMethod ((int) value);
455                 }
456
457                 public MethodEntry GetMethod (MethodBase method)
458                 {
459                         if (reader == null)
460                                 throw new InvalidOperationException ();
461                         int token = assembly.MonoDebugger_GetMethodToken (method);
462                         return GetMethodByToken (token);
463                 }
464
465                 public MethodEntry GetMethod (int index)
466                 {
467                         if ((index < 1) || (index > ot.MethodCount))
468                                 throw new ArgumentException ();
469                         if (reader == null)
470                                 throw new InvalidOperationException ();
471
472                         MethodEntry entry = (MethodEntry) method_hash [index];
473                         if (entry != null)
474                                 return entry;
475
476                         MethodIndexEntry ie = GetMethodIndexEntry (index);
477                         reader.BaseStream.Position = ie.FileOffset;
478
479                         entry = new MethodEntry (this, reader, index);
480                         method_hash.Add (index, entry);
481                         return entry;
482                 }
483
484                 public MethodEntry[] Methods {
485                         get {
486                                 if (reader == null)
487                                         throw new InvalidOperationException ();
488
489                                 MethodEntry[] retval = new MethodEntry [MethodCount];
490                                 for (int i = 0; i < MethodCount; i++)
491                                         retval [i] = GetMethod (i + 1);
492                                 return retval;
493                         }
494                 }
495
496                 public MethodSourceEntry GetMethodSource (int index)
497                 {
498                         if ((index < 1) || (index > ot.MethodCount))
499                                 throw new ArgumentException ();
500                         if (reader == null)
501                                 throw new InvalidOperationException ();
502
503                         object entry = method_source_hash [index];
504                         if (entry != null)
505                                 return (MethodSourceEntry) entry;
506
507                         MethodEntry method = GetMethod (index);
508                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
509                                 if (source.Index == index) {
510                                         method_source_hash.Add (index, source);
511                                         return source;
512                                 }
513                         }
514
515                         throw new MonoSymbolFileException ("Internal error.");
516                 }
517
518                 public int FindMethod (string full_name)
519                 {
520                         if (reader == null)
521                                 throw new InvalidOperationException ();
522
523                         if (method_full_name_hash == null) {
524                                 method_full_name_hash = new Hashtable ();
525
526                                 for (int i = 0; i < ot.MethodCount; i++) {
527                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
528                                         string name = ReadString (ie.FullNameOffset);
529
530                                         method_full_name_hash.Add (name, i + 1);
531                                 }
532                         }
533
534                         object value = method_full_name_hash [full_name];
535                         if (value == null)
536                                 return -1;
537                         return (int) value;
538                 }
539
540                 public int[] MethodLookup (string query)
541                 {
542                         if (reader == null)
543                                 throw new InvalidOperationException ();
544
545                         ArrayList list;
546                         if (method_name_hash == null) {
547                                 method_name_hash = new Hashtable ();
548
549                                 for (int i = 0; i < ot.MethodCount; i++) {
550                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
551                                         string full_name = ReadString (ie.FullNameOffset);
552
553                                         int pos = full_name.IndexOf ('(');
554                                         string name = full_name.Substring (0, pos);
555
556                                         list = method_name_hash [name] as ArrayList;
557                                         if (list == null) {
558                                                 list = new ArrayList ();
559                                                 method_name_hash.Add (name, list);
560                                         }
561
562                                         list.Add (i + 1);
563                                 }
564                         }
565
566                         list = method_name_hash [query] as ArrayList;
567                         if (list == null)
568                                 return new int [0];
569
570                         int[] retval = new int [list.Count];
571                         list.CopyTo (retval, 0);
572                         return retval;
573                 }
574
575                 public int FindSource (string file_name)
576                 {
577                         if (reader == null)
578                                 throw new InvalidOperationException ();
579
580                         if (source_name_hash == null) {
581                                 source_name_hash = new Hashtable ();
582
583                                 for (int i = 0; i < ot.SourceCount; i++) {
584                                         SourceFileEntry source = GetSourceFile (i + 1);
585
586                                         source_name_hash.Add (source.FileName, i);
587                                 }
588                         }
589
590                         object value = source_name_hash [file_name];
591                         if (value == null)
592                                 return -1;
593                         return (int) value;
594                 }
595
596                 internal BinaryReader BinaryReader {
597                         get {
598                                 if (reader == null)
599                                         throw new InvalidOperationException ();
600
601                                 return reader;
602                         }
603                 }
604
605                 void IDisposable.Dispose ()
606                 {
607                         Dispose (true);
608                 }
609
610                 protected virtual void Dispose (bool disposing)
611                 {
612                         if (disposing) {
613                                 if (reader != null) {
614                                         reader.Close ();
615                                         reader = null;
616                                 }
617                         }
618                 }
619         }
620 }