2004-05-28 Gonzalo Paniagua Javier <gonzalo@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 source_name_hash;
400
401                 protected MonoSymbolFile (Assembly assembly, Stream stream)
402                 {
403                         this.assembly = assembly;
404
405                         reader = new BinaryReader (stream);
406
407                         try {
408                                 long magic = reader.ReadInt64 ();
409                                 long version = reader.ReadInt32 ();
410                                 if ((magic != OffsetTable.Magic) || (version != OffsetTable.Version))
411                                         throw new MonoSymbolFileException ();
412                                 ot = new OffsetTable (reader);
413                         } catch {
414                                 throw new MonoSymbolFileException ();
415                         }
416
417                         method_hash = new Hashtable ();
418                         source_file_hash = new Hashtable ();
419                 }
420
421                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
422                 {
423                         Stream stream = assembly.GetManifestResourceStream ("MonoSymbolFile");
424                         if (stream == null)
425                                 return null;
426
427                         return new MonoSymbolFile (assembly, stream);
428                 }
429
430                 public Assembly Assembly {
431                         get { return assembly; }
432                 }
433
434                 public int SourceCount {
435                         get { return ot.SourceCount; }
436                 }
437
438                 public int MethodCount {
439                         get { return ot.MethodCount; }
440                 }
441
442                 public int TypeCount {
443                         get { return ot.TypeCount; }
444                 }
445
446                 public int NamespaceCount {
447                         get { return last_namespace_index; }
448                 }
449
450                 internal int LineNumberCount = 0;
451                 internal int LocalCount = 0;
452                 internal int StringSize = 0;
453
454                 public SourceFileEntry GetSourceFile (int index)
455                 {
456                         if ((index < 1) || (index > ot.SourceCount))
457                                 throw new ArgumentException ();
458                         if (reader == null)
459                                 throw new InvalidOperationException ();
460
461                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
462                         if (source != null)
463                                 return source;
464
465                         reader.BaseStream.Position = ot.SourceTableOffset +
466                                 SourceFileEntry.Size * (index - 1);
467                         source = new SourceFileEntry (this, reader);
468                         source_file_hash.Add (index, source);
469                         return source;
470                 }
471
472                 public SourceFileEntry[] Sources {
473                         get {
474                                 if (reader == null)
475                                         throw new InvalidOperationException ();
476
477                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
478                                 for (int i = 0; i < SourceCount; i++)
479                                         retval [i] = GetSourceFile (i + 1);
480                                 return retval;
481                         }
482                 }
483
484                 public MethodIndexEntry GetMethodIndexEntry (int index)
485                 {
486                         int old_pos = (int) reader.BaseStream.Position;
487                         reader.BaseStream.Position = ot.MethodTableOffset +
488                                 MethodIndexEntry.Size * (index - 1);
489                         MethodIndexEntry ie = new MethodIndexEntry (reader);
490                         reader.BaseStream.Position = old_pos;
491                         return ie;
492                 }
493
494                 public MethodEntry GetMethodByToken (int token)
495                 {
496                         if (reader == null)
497                                 throw new InvalidOperationException ();
498
499                         if (method_token_hash == null) {
500                                 method_token_hash = new Hashtable ();
501
502                                 for (int i = 0; i < MethodCount; i++) {
503                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
504
505                                         method_token_hash.Add (ie.Token, i + 1);
506                                 }
507                         }
508
509                         object value = method_token_hash [token];
510                         if (value == null)
511                                 return null;
512
513                         return GetMethod ((int) value);
514                 }
515
516                 public MethodEntry GetMethod (MethodBase method)
517                 {
518                         if (reader == null)
519                                 throw new InvalidOperationException ();
520                         int token = MonoDebuggerSupport.GetMethodToken (method);
521                         return GetMethodByToken (token);
522                 }
523
524                 public MethodEntry GetMethod (int index)
525                 {
526                         if ((index < 1) || (index > ot.MethodCount))
527                                 throw new ArgumentException ();
528                         if (reader == null)
529                                 throw new InvalidOperationException ();
530
531                         MethodEntry entry = (MethodEntry) method_hash [index];
532                         if (entry != null)
533                                 return entry;
534
535                         MethodIndexEntry ie = GetMethodIndexEntry (index);
536                         reader.BaseStream.Position = ie.FileOffset;
537
538                         entry = new MethodEntry (this, reader, index);
539                         method_hash.Add (index, entry);
540                         return entry;
541                 }
542
543                 public MethodEntry[] Methods {
544                         get {
545                                 if (reader == null)
546                                         throw new InvalidOperationException ();
547
548                                 MethodEntry[] retval = new MethodEntry [MethodCount];
549                                 for (int i = 0; i < MethodCount; i++)
550                                         retval [i] = GetMethod (i + 1);
551                                 return retval;
552                         }
553                 }
554
555                 public MethodSourceEntry GetMethodSource (int index)
556                 {
557                         if ((index < 1) || (index > ot.MethodCount))
558                                 throw new ArgumentException ();
559                         if (reader == null)
560                                 throw new InvalidOperationException ();
561
562                         object entry = method_source_hash [index];
563                         if (entry != null)
564                                 return (MethodSourceEntry) entry;
565
566                         MethodEntry method = GetMethod (index);
567                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
568                                 if (source.Index == index) {
569                                         method_source_hash.Add (index, source);
570                                         return source;
571                                 }
572                         }
573
574                         throw new MonoSymbolFileException ("Internal error.");
575                 }
576
577                 public int FindSource (string file_name)
578                 {
579                         if (reader == null)
580                                 throw new InvalidOperationException ();
581
582                         if (source_name_hash == null) {
583                                 source_name_hash = new Hashtable ();
584
585                                 for (int i = 0; i < ot.SourceCount; i++) {
586                                         SourceFileEntry source = GetSourceFile (i + 1);
587
588                                         source_name_hash.Add (source.FileName, i);
589                                 }
590                         }
591
592                         object value = source_name_hash [file_name];
593                         if (value == null)
594                                 return -1;
595                         return (int) value;
596                 }
597
598                 internal BinaryReader BinaryReader {
599                         get {
600                                 if (reader == null)
601                                         throw new InvalidOperationException ();
602
603                                 return reader;
604                         }
605                 }
606
607                 void IDisposable.Dispose ()
608                 {
609                         Dispose (true);
610                 }
611
612                 protected virtual void Dispose (bool disposing)
613                 {
614                         if (disposing) {
615                                 if (reader != null) {
616                                         reader.Close ();
617                                         reader = null;
618                                 }
619                         }
620                 }
621         }
622 }