2003-02-08 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 = 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 = 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 source_hash = new Hashtable ();
178                 Hashtable method_source_hash = new Hashtable ();
179                 Hashtable type_hash = new Hashtable ();
180
181                 OffsetTable ot;
182                 int last_type_index;
183                 int last_method_index;
184
185                 public MonoSymbolFile ()
186                 { }
187
188                 public SourceFileEntry DefineSource (string source_file)
189                 {
190                         if (reader != null)
191                                 throw new InvalidOperationException ();
192
193                         SourceFileEntry source = (SourceFileEntry) source_hash [source_file];
194                         if (source == null) {
195                                 source = new SourceFileEntry (this, source_file, sources.Count + 1);
196                                 source_hash.Add (source_file, source);
197                                 sources.Add (source);
198                         }
199                         return source;
200                 }
201
202                 internal int DefineType (Type type)
203                 {
204                         if (type_hash.Contains (type))
205                                 return (int) type_hash [type];
206
207                         int index = ++last_type_index;
208                         type_hash.Add (type, index);
209                         return index;
210                 }
211
212                 internal void AddMethod (MethodEntry entry)
213                 {
214                         methods.Add (entry);
215                 }
216
217                 internal int GetNextTypeIndex ()
218                 {
219                         return ++last_type_index;
220                 }
221
222                 internal int GetNextMethodIndex ()
223                 {
224                         return ++last_method_index;
225                 }
226
227                 internal void WriteString (BinaryWriter bw, string text)
228                 {
229                         byte[] data = Encoding.UTF8.GetBytes (text);
230                         bw.Write ((int) data.Length);
231                         bw.Write (data);
232                         StringSize += data.Length;
233                 }
234
235                 internal string ReadString (int offset)
236                 {
237                         int old_pos = (int) reader.BaseStream.Position;
238                         reader.BaseStream.Position = offset;
239                         int length = reader.ReadInt32 ();
240
241                         byte[] data = reader.ReadBytes (length);
242                         string text = Encoding.UTF8.GetString (data);
243                         reader.BaseStream.Position = old_pos;
244                         return text;
245                 }
246
247                 void Write (BinaryWriter bw)
248                 {
249                         // Magic number and file version.
250                         bw.Write (OffsetTable.Magic);
251                         bw.Write (OffsetTable.Version);
252
253                         //
254                         // Offsets of file sections; we must write this after we're done
255                         // writing the whole file, so we just reserve the space for it here.
256                         //
257                         long offset_table_offset = bw.BaseStream.Position;
258                         ot.Write (bw);
259
260                         //
261                         // Write data sections.
262                         //
263                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
264                         foreach (SourceFileEntry source in sources)
265                                 source.WriteData (bw);
266                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
267
268                         //
269                         // Write method table.
270                         //
271                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
272                         for (int i = 0; i < methods.Count; i++) {
273                                 MethodEntry entry = (MethodEntry) methods [i];
274                                 entry.WriteIndex (bw);
275                         }
276                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
277
278                         //
279                         // Write source table.
280                         //
281                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
282                         for (int i = 0; i < sources.Count; i++) {
283                                 SourceFileEntry source = (SourceFileEntry) sources [i];
284                                 source.Write (bw);
285                         }
286                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
287
288                         //
289                         // Fixup offset table.
290                         //
291                         ot.TypeCount = last_type_index;
292                         ot.MethodCount = methods.Count;
293                         ot.SourceCount = sources.Count;
294
295                         //
296                         // Write offset table.
297                         //
298                         ot.TotalFileSize = (int) bw.BaseStream.Position;
299                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
300                         ot.Write (bw);
301                         bw.Seek (0, SeekOrigin.End);
302                 }
303
304                 public byte[] CreateSymbolFile ()
305                 {
306                         if (reader != null)
307                                 throw new InvalidOperationException ();
308
309                         using (MyMemoryStream stream = new MyMemoryStream ()) {
310                                 Write (new BinaryWriter (stream));
311                                 Console.WriteLine ("WROTE SYMFILE: {0} sources, {1} methods, {2} types, " +
312                                                    "{3} line numbers, {4} locals, {5} bytes of string data",
313                                                    SourceCount, MethodCount, TypeCount, LineNumberCount,
314                                                    LocalCount, StringSize);
315                                 Console.WriteLine (ot);
316                                 return stream.GetContents ();
317                         }
318                 }
319
320                 // Stream stream;
321                 BinaryReader reader;
322                 Hashtable method_hash;
323                 Hashtable source_file_hash;
324
325                 Hashtable method_name_hash;
326                 Hashtable source_name_hash;
327
328                 protected MonoSymbolFile (Stream stream)
329                 {
330                         reader = new BinaryReader (stream);
331
332                         try {
333                                 long magic = reader.ReadInt64 ();
334                                 long version = reader.ReadInt32 ();
335                                 if ((magic != OffsetTable.Magic) || (version != OffsetTable.Version))
336                                         throw new MonoSymbolFileException ();
337                                 ot = new OffsetTable (reader);
338                         } catch {
339                                 throw new MonoSymbolFileException ();
340                         }
341
342                         method_hash = new Hashtable ();
343                         source_file_hash = new Hashtable ();
344                 }
345
346                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
347                 {
348                         Stream stream = assembly.GetManifestResourceStream ("MonoSymbolFile");
349                         if (stream == null)
350                                 return null;
351
352                         return new MonoSymbolFile (stream);
353                 }
354
355                 public int SourceCount {
356                         get { return ot.SourceCount; }
357                 }
358
359                 public int MethodCount {
360                         get { return ot.MethodCount; }
361                 }
362
363                 public int TypeCount {
364                         get { return ot.TypeCount; }
365                 }
366
367                 internal int LineNumberCount = 0;
368                 internal int LocalCount = 0;
369                 internal int StringSize = 0;
370
371                 public SourceFileEntry GetSourceFile (int index)
372                 {
373                         if ((index < 1) || (index > ot.SourceCount))
374                                 throw new ArgumentException ();
375                         if (reader == null)
376                                 throw new InvalidOperationException ();
377
378                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
379                         if (source != null)
380                                 return source;
381
382                         reader.BaseStream.Position = ot.SourceTableOffset +
383                                 SourceFileEntry.Size * (index - 1);
384                         source = new SourceFileEntry (this, reader);
385                         source_file_hash.Add (index, source);
386                         return source;
387                 }
388
389                 public SourceFileEntry[] Sources {
390                         get {
391                                 if (reader == null)
392                                         throw new InvalidOperationException ();
393
394                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
395                                 for (int i = 0; i < SourceCount; i++)
396                                         retval [i] = GetSourceFile (i + 1);
397                                 return retval;
398                         }
399                 }
400
401                 public MethodEntry GetMethod (int index)
402                 {
403                         if ((index < 1) || (index > ot.MethodCount))
404                                 throw new ArgumentException ();
405                         if (reader == null)
406                                 throw new InvalidOperationException ();
407
408                         MethodEntry entry = (MethodEntry) method_hash [index];
409                         if (entry != null)
410                                 return entry;
411
412                         reader.BaseStream.Position = ot.MethodTableOffset + 8 * (index - 1);
413                         reader.BaseStream.Position = reader.ReadInt32 ();
414
415                         entry = new MethodEntry (this, reader, index);
416                         method_hash.Add (index, entry);
417                         return entry;
418                 }
419
420                 public MethodEntry[] Methods {
421                         get {
422                                 if (reader == null)
423                                         throw new InvalidOperationException ();
424
425                                 MethodEntry[] retval = new MethodEntry [MethodCount];
426                                 for (int i = 0; i < MethodCount; i++)
427                                         retval [i] = GetMethod (i + 1);
428                                 return retval;
429                         }
430                 }
431
432                 public MethodSourceEntry GetMethodSource (int index)
433                 {
434                         if ((index < 1) || (index > ot.MethodCount))
435                                 throw new ArgumentException ();
436                         if (reader == null)
437                                 throw new InvalidOperationException ();
438
439                         object entry = method_source_hash [index];
440                         if (entry != null)
441                                 return (MethodSourceEntry) entry;
442
443                         MethodEntry method = GetMethod (index);
444                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
445                                 if (source.Index == index) {
446                                         method_source_hash.Add (index, source);
447                                         return source;
448                                 }
449                         }
450
451                         throw new MonoSymbolFileException ("Internal error.");
452                 }
453
454                 public int FindMethod (string full_name)
455                 {
456                         if (reader == null)
457                                 throw new InvalidOperationException ();
458
459                         if (method_name_hash == null) {
460                                 method_name_hash = new Hashtable ();
461
462                                 for (int i = 0; i < ot.MethodCount; i++) {
463                                         reader.BaseStream.Position = ot.MethodTableOffset + 8 * i;
464
465                                         int offset = reader.ReadInt32 ();
466                                         int name_offset = reader.ReadInt32 ();
467                                         string name = ReadString (name_offset);
468
469                                         method_name_hash.Add (name, i + 1);
470                                 }
471                         }
472
473                         object value = method_name_hash [full_name];
474                         if (value == null)
475                                 return -1;
476                         return (int) value;
477                 }
478
479                 public int FindSource (string file_name)
480                 {
481                         if (reader == null)
482                                 throw new InvalidOperationException ();
483
484                         if (source_name_hash == null) {
485                                 source_name_hash = new Hashtable ();
486
487                                 for (int i = 0; i < ot.SourceCount; i++) {
488                                         SourceFileEntry source = GetSourceFile (i + 1);
489
490                                         source_name_hash.Add (source.FileName, i);
491                                 }
492                         }
493
494                         object value = source_name_hash [file_name];
495                         if (value == null)
496                                 return -1;
497                         return (int) value;
498                 }
499
500                 internal BinaryReader BinaryReader {
501                         get {
502                                 if (reader == null)
503                                         throw new InvalidOperationException ();
504
505                                 return reader;
506                         }
507                 }
508
509                 void IDisposable.Dispose ()
510                 {
511                         Dispose (true);
512                 }
513
514                 protected virtual void Dispose (bool disposing)
515                 {
516                         if (disposing) {
517                                 if (reader != null) {
518                                         reader.Close ();
519                                         reader = null;
520                                 }
521                         }
522                 }
523         }
524 }