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