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