BindingFlags.Public needed here as Exception.HResult is now public in .NET 4.5. This...
[mono.git] / mcs / class / Mono.CompilerServices.SymbolWriter / MonoSymbolTable.cs
1 //
2 // Mono.CSharp.Debugger/MonoSymbolTable.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@ximian.com)
6 //
7 // (C) 2002 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.Security.Cryptography;
33 using System.Collections.Generic;
34 using System.Text;
35 using System.IO;
36
37 //
38 // Parts which are actually written into the symbol file are marked with
39 //
40 //         #region This is actually written to the symbol file
41 //         #endregion
42 //
43 // Please do not modify these regions without previously talking to me.
44 //
45 // All changes to the file format must be synchronized in several places:
46 //
47 // a) The fields in these regions (and their order) must match the actual
48 //    contents of the symbol file.
49 //
50 //    This helps people to understand the symbol file format without reading
51 //    too much source code, ie. you look at the appropriate region and then
52 //    you know what's actually in the file.
53 //
54 //    It is also required to help me enforce b).
55 //
56 // b) The regions must be kept in sync with the unmanaged code in
57 //    mono/metadata/debug-mono-symfile.h
58 //
59 // When making changes to the file format, you must also increase two version
60 // numbers:
61 //
62 // i)  OffsetTable.Version in this file.
63 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
64 //
65 // After doing so, recompile everything, including the debugger.  Symbol files
66 // with different versions are incompatible to each other and the debugger and
67 // the runtime enfore this, so you need to recompile all your assemblies after
68 // changing the file format.
69 //
70
71 namespace Mono.CompilerServices.SymbolWriter
72 {
73         public class OffsetTable
74         {
75                 public const int  MajorVersion = 50;
76                 public const int  MinorVersion = 0;
77                 public const long Magic        = 0x45e82623fd7fa614;
78
79                 #region This is actually written to the symbol file
80                 public int TotalFileSize;
81                 public int DataSectionOffset;
82                 public int DataSectionSize;
83                 public int CompileUnitCount;
84                 public int CompileUnitTableOffset;
85                 public int CompileUnitTableSize;
86                 public int SourceCount;
87                 public int SourceTableOffset;
88                 public int SourceTableSize;
89                 public int MethodCount;
90                 public int MethodTableOffset;
91                 public int MethodTableSize;
92                 public int TypeCount;
93                 public int AnonymousScopeCount;
94                 public int AnonymousScopeTableOffset;
95                 public int AnonymousScopeTableSize;
96
97                 [Flags]
98                 public enum Flags
99                 {
100                         IsAspxSource            = 1,
101                         WindowsFileNames        = 2
102                 }
103
104                 public Flags FileFlags;
105
106                 public int LineNumberTable_LineBase = LineNumberTable.Default_LineBase;
107                 public int LineNumberTable_LineRange = LineNumberTable.Default_LineRange;
108                 public int LineNumberTable_OpcodeBase = LineNumberTable.Default_OpcodeBase;
109                 #endregion
110
111                 internal OffsetTable ()
112                 {
113                         int platform = (int) Environment.OSVersion.Platform;
114                         if ((platform != 4) && (platform != 128))
115                                 FileFlags |= Flags.WindowsFileNames;
116                 }
117
118                 internal OffsetTable (BinaryReader reader, int major_version, int minor_version)
119                 {
120                         TotalFileSize = reader.ReadInt32 ();
121                         DataSectionOffset = reader.ReadInt32 ();
122                         DataSectionSize = reader.ReadInt32 ();
123                         CompileUnitCount = reader.ReadInt32 ();
124                         CompileUnitTableOffset = reader.ReadInt32 ();
125                         CompileUnitTableSize = reader.ReadInt32 ();
126                         SourceCount = reader.ReadInt32 ();
127                         SourceTableOffset = reader.ReadInt32 ();
128                         SourceTableSize = reader.ReadInt32 ();
129                         MethodCount = reader.ReadInt32 ();
130                         MethodTableOffset = reader.ReadInt32 ();
131                         MethodTableSize = reader.ReadInt32 ();
132                         TypeCount = reader.ReadInt32 ();
133
134                         AnonymousScopeCount = reader.ReadInt32 ();
135                         AnonymousScopeTableOffset = reader.ReadInt32 ();
136                         AnonymousScopeTableSize = reader.ReadInt32 ();
137
138                         LineNumberTable_LineBase = reader.ReadInt32 ();
139                         LineNumberTable_LineRange = reader.ReadInt32 ();
140                         LineNumberTable_OpcodeBase = reader.ReadInt32 ();
141
142                         FileFlags = (Flags) reader.ReadInt32 ();
143                 }
144
145                 internal void Write (BinaryWriter bw, int major_version, int minor_version)
146                 {
147                         bw.Write (TotalFileSize);
148                         bw.Write (DataSectionOffset);
149                         bw.Write (DataSectionSize);
150                         bw.Write (CompileUnitCount);
151                         bw.Write (CompileUnitTableOffset);
152                         bw.Write (CompileUnitTableSize);
153                         bw.Write (SourceCount);
154                         bw.Write (SourceTableOffset);
155                         bw.Write (SourceTableSize);
156                         bw.Write (MethodCount);
157                         bw.Write (MethodTableOffset);
158                         bw.Write (MethodTableSize);
159                         bw.Write (TypeCount);
160
161                         bw.Write (AnonymousScopeCount);
162                         bw.Write (AnonymousScopeTableOffset);
163                         bw.Write (AnonymousScopeTableSize);
164
165                         bw.Write (LineNumberTable_LineBase);
166                         bw.Write (LineNumberTable_LineRange);
167                         bw.Write (LineNumberTable_OpcodeBase);
168
169                         bw.Write ((int) FileFlags);
170                 }
171
172                 public override string ToString ()
173                 {
174                         return String.Format (
175                                 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
176                                 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
177                                 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
178                                 MethodTableSize, TypeCount);
179                 }
180         }
181
182         public class LineNumberEntry
183         {
184                 #region This is actually written to the symbol file
185                 public readonly int Row;
186                 public readonly int File;
187                 public readonly int Offset;
188                 public readonly bool IsHidden;  // Obsolete is never used
189                 #endregion
190
191                 public sealed class LocationComparer : IComparer<LineNumberEntry>
192                 {
193                         public static readonly LocationComparer Default = new LocationComparer ();
194
195                         public int Compare (LineNumberEntry l1, LineNumberEntry l2)
196                         {
197                                 return l1.Row == l2.Row ?
198                                         l1.Offset.CompareTo (l2.Offset) :
199                                         l1.Row.CompareTo (l2.Row);
200                         }
201                 }
202
203                 public static readonly LineNumberEntry Null = new LineNumberEntry (0, 0, 0);
204
205                 public LineNumberEntry (int file, int row, int offset)
206                         : this (file, row, offset, false)
207                 { }
208
209                 public LineNumberEntry (int file, int row, int offset, bool is_hidden)
210                 {
211                         this.File = file;
212                         this.Row = row;
213                         this.Offset = offset;
214                         this.IsHidden = is_hidden;
215                 }
216
217                 public override string ToString ()
218                 {
219                         return String.Format ("[Line {0}:{1}:{2}]", File, Row, Offset);
220                 }
221         }
222
223         public class CodeBlockEntry
224         {
225                 public int Index;
226                 #region This is actually written to the symbol file
227                 public int Parent;
228                 public Type BlockType;
229                 public int StartOffset;
230                 public int EndOffset;
231                 #endregion
232
233                 public enum Type {
234                         Lexical                 = 1,
235                         CompilerGenerated       = 2,
236                         IteratorBody            = 3,
237                         IteratorDispatcher      = 4
238                 }
239
240                 public CodeBlockEntry (int index, int parent, Type type, int start_offset)
241                 {
242                         this.Index = index;
243                         this.Parent = parent;
244                         this.BlockType = type;
245                         this.StartOffset = start_offset;
246                 }
247
248                 internal CodeBlockEntry (int index, MyBinaryReader reader)
249                 {
250                         this.Index = index;
251                         int type_flag = reader.ReadLeb128 ();
252                         BlockType = (Type) (type_flag & 0x3f);
253                         this.Parent = reader.ReadLeb128 ();
254                         this.StartOffset = reader.ReadLeb128 ();
255                         this.EndOffset = reader.ReadLeb128 ();
256
257                         /* Reserved for future extensions. */
258                         if ((type_flag & 0x40) != 0) {
259                                 int data_size = reader.ReadInt16 ();
260                                 reader.BaseStream.Position += data_size;
261                         }                               
262                 }
263
264                 public void Close (int end_offset)
265                 {
266                         this.EndOffset = end_offset;
267                 }
268
269                 internal void Write (MyBinaryWriter bw)
270                 {
271                         bw.WriteLeb128 ((int) BlockType);
272                         bw.WriteLeb128 (Parent);
273                         bw.WriteLeb128 (StartOffset);
274                         bw.WriteLeb128 (EndOffset);
275                 }
276
277                 public override string ToString ()
278                 {
279                         return String.Format ("[CodeBlock {0}:{1}:{2}:{3}:{4}]",
280                                               Index, Parent, BlockType, StartOffset, EndOffset);
281                 }
282         }
283
284         public struct LocalVariableEntry
285         {
286                 #region This is actually written to the symbol file
287                 public readonly int Index;
288                 public readonly string Name;
289                 public readonly int BlockIndex;
290                 #endregion
291
292                 public LocalVariableEntry (int index, string name, int block)
293                 {
294                         this.Index = index;
295                         this.Name = name;
296                         this.BlockIndex = block;
297                 }
298
299                 internal LocalVariableEntry (MonoSymbolFile file, MyBinaryReader reader)
300                 {
301                         Index = reader.ReadLeb128 ();
302                         Name = reader.ReadString ();
303                         BlockIndex = reader.ReadLeb128 ();
304                 }
305
306                 internal void Write (MonoSymbolFile file, MyBinaryWriter bw)
307                 {
308                         bw.WriteLeb128 (Index);
309                         bw.Write (Name);
310                         bw.WriteLeb128 (BlockIndex);
311                 }
312
313                 public override string ToString ()
314                 {
315                         return String.Format ("[LocalVariable {0}:{1}:{2}]",
316                                               Name, Index, BlockIndex - 1);
317                 }
318         }
319
320         public struct CapturedVariable
321         {
322                 #region This is actually written to the symbol file
323                 public readonly string Name;
324                 public readonly string CapturedName;
325                 public readonly CapturedKind Kind;
326                 #endregion
327
328                 public enum CapturedKind : byte
329                 {
330                         Local,
331                         Parameter,
332                         This
333                 }
334
335                 public CapturedVariable (string name, string captured_name,
336                                          CapturedKind kind)
337                 {
338                         this.Name = name;
339                         this.CapturedName = captured_name;
340                         this.Kind = kind;
341                 }
342
343                 internal CapturedVariable (MyBinaryReader reader)
344                 {
345                         Name = reader.ReadString ();
346                         CapturedName = reader.ReadString ();
347                         Kind = (CapturedKind) reader.ReadByte ();
348                 }
349
350                 internal void Write (MyBinaryWriter bw)
351                 {
352                         bw.Write (Name);
353                         bw.Write (CapturedName);
354                         bw.Write ((byte) Kind);
355                 }
356
357                 public override string ToString ()
358                 {
359                         return String.Format ("[CapturedVariable {0}:{1}:{2}]",
360                                               Name, CapturedName, Kind);
361                 }
362         }
363
364         public struct CapturedScope
365         {
366                 #region This is actually written to the symbol file
367                 public readonly int Scope;
368                 public readonly string CapturedName;
369                 #endregion
370
371                 public CapturedScope (int scope, string captured_name)
372                 {
373                         this.Scope = scope;
374                         this.CapturedName = captured_name;
375                 }
376
377                 internal CapturedScope (MyBinaryReader reader)
378                 {
379                         Scope = reader.ReadLeb128 ();
380                         CapturedName = reader.ReadString ();
381                 }
382
383                 internal void Write (MyBinaryWriter bw)
384                 {
385                         bw.WriteLeb128 (Scope);
386                         bw.Write (CapturedName);
387                 }
388
389                 public override string ToString ()
390                 {
391                         return String.Format ("[CapturedScope {0}:{1}]",
392                                               Scope, CapturedName);
393                 }
394         }
395
396         public struct ScopeVariable
397         {
398                 #region This is actually written to the symbol file
399                 public readonly int Scope;
400                 public readonly int Index;
401                 #endregion
402
403                 public ScopeVariable (int scope, int index)
404                 {
405                         this.Scope = scope;
406                         this.Index = index;
407                 }
408
409                 internal ScopeVariable (MyBinaryReader reader)
410                 {
411                         Scope = reader.ReadLeb128 ();
412                         Index = reader.ReadLeb128 ();
413                 }
414
415                 internal void Write (MyBinaryWriter bw)
416                 {
417                         bw.WriteLeb128 (Scope);
418                         bw.WriteLeb128 (Index);
419                 }
420
421                 public override string ToString ()
422                 {
423                         return String.Format ("[ScopeVariable {0}:{1}]", Scope, Index);
424                 }
425         }
426
427         public class AnonymousScopeEntry
428         {
429                 #region This is actually written to the symbol file
430                 public readonly int ID;
431                 #endregion
432
433                 List<CapturedVariable> captured_vars = new List<CapturedVariable> ();
434                 List<CapturedScope> captured_scopes = new List<CapturedScope> ();
435
436                 public AnonymousScopeEntry (int id)
437                 {
438                         this.ID = id;
439                 }
440
441                 internal AnonymousScopeEntry (MyBinaryReader reader)
442                 {
443                         ID = reader.ReadLeb128 ();
444
445                         int num_captured_vars = reader.ReadLeb128 ();
446                         for (int i = 0; i < num_captured_vars; i++)
447                                 captured_vars.Add (new CapturedVariable (reader));
448
449                         int num_captured_scopes = reader.ReadLeb128 ();
450                         for (int i = 0; i < num_captured_scopes; i++)
451                                 captured_scopes.Add (new CapturedScope (reader));
452                 }
453
454                 internal void AddCapturedVariable (string name, string captured_name,
455                                                    CapturedVariable.CapturedKind kind)
456                 {
457                         captured_vars.Add (new CapturedVariable (name, captured_name, kind));
458                 }
459
460                 public CapturedVariable[] CapturedVariables {
461                         get {
462                                 CapturedVariable[] retval = new CapturedVariable [captured_vars.Count];
463                                 captured_vars.CopyTo (retval, 0);
464                                 return retval;
465                         }
466                 }
467
468                 internal void AddCapturedScope (int scope, string captured_name)
469                 {
470                         captured_scopes.Add (new CapturedScope (scope, captured_name));
471                 }
472
473                 public CapturedScope[] CapturedScopes {
474                         get {
475                                 CapturedScope[] retval = new CapturedScope [captured_scopes.Count];
476                                 captured_scopes.CopyTo (retval, 0);
477                                 return retval;
478                         }
479                 }
480
481                 internal void Write (MyBinaryWriter bw)
482                 {
483                         bw.WriteLeb128 (ID);
484
485                         bw.WriteLeb128 (captured_vars.Count);
486                         foreach (CapturedVariable cv in captured_vars)
487                                 cv.Write (bw);
488
489                         bw.WriteLeb128 (captured_scopes.Count);
490                         foreach (CapturedScope cs in captured_scopes)
491                                 cs.Write (bw);
492                 }
493
494                 public override string ToString ()
495                 {
496                         return String.Format ("[AnonymousScope {0}]", ID);
497                 }
498         }
499
500         public class CompileUnitEntry : ICompileUnit
501         {
502                 #region This is actually written to the symbol file
503                 public readonly int Index;
504                 int DataOffset;
505                 #endregion
506
507                 MonoSymbolFile file;
508                 SourceFileEntry source;
509                 List<SourceFileEntry> include_files;
510                 List<NamespaceEntry> namespaces;
511
512                 bool creating;
513
514                 public static int Size {
515                         get { return 8; }
516                 }
517
518                 CompileUnitEntry ICompileUnit.Entry {
519                         get { return this; }
520                 }
521
522                 public CompileUnitEntry (MonoSymbolFile file, SourceFileEntry source)
523                 {
524                         this.file = file;
525                         this.source = source;
526
527                         this.Index = file.AddCompileUnit (this);
528
529                         creating = true;
530                         namespaces = new List<NamespaceEntry> ();
531                 }
532
533                 public void AddFile (SourceFileEntry file)
534                 {
535                         if (!creating)
536                                 throw new InvalidOperationException ();
537
538                         if (include_files == null)
539                                 include_files = new List<SourceFileEntry> ();
540
541                         include_files.Add (file);
542                 }
543
544                 public SourceFileEntry SourceFile {
545                         get {
546                                 if (creating)
547                                         return source;
548
549                                 ReadData ();
550                                 return source;
551                         }
552                 }
553
554                 public int DefineNamespace (string name, string[] using_clauses, int parent)
555                 {
556                         if (!creating)
557                                 throw new InvalidOperationException ();
558
559                         int index = file.GetNextNamespaceIndex ();
560                         NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
561                         namespaces.Add (ns);
562                         return index;
563                 }
564
565                 internal void WriteData (MyBinaryWriter bw)
566                 {
567                         DataOffset = (int) bw.BaseStream.Position;
568                         bw.WriteLeb128 (source.Index);
569
570                         int count_includes = include_files != null ? include_files.Count : 0;
571                         bw.WriteLeb128 (count_includes);
572                         if (include_files != null) {
573                                 foreach (SourceFileEntry entry in include_files)
574                                         bw.WriteLeb128 (entry.Index);
575                         }
576
577                         bw.WriteLeb128 (namespaces.Count);
578                         foreach (NamespaceEntry ns in namespaces)
579                                 ns.Write (file, bw);
580                 }
581
582                 internal void Write (BinaryWriter bw)
583                 {
584                         bw.Write (Index);
585                         bw.Write (DataOffset);
586                 }
587
588                 internal CompileUnitEntry (MonoSymbolFile file, MyBinaryReader reader)
589                 {
590                         this.file = file;
591
592                         Index = reader.ReadInt32 ();
593                         DataOffset = reader.ReadInt32 ();
594                 }
595
596                 void ReadData ()
597                 {
598                         if (creating)
599                                 throw new InvalidOperationException ();
600
601                         lock (file) {
602                                 if (namespaces != null)
603                                         return;
604
605                                 MyBinaryReader reader = file.BinaryReader;
606                                 int old_pos = (int) reader.BaseStream.Position;
607
608                                 reader.BaseStream.Position = DataOffset;
609
610                                 int source_idx = reader.ReadLeb128 ();
611                                 source = file.GetSourceFile (source_idx);
612
613                                 int count_includes = reader.ReadLeb128 ();
614                                 if (count_includes > 0) {
615                                         include_files = new List<SourceFileEntry> ();
616                                         for (int i = 0; i < count_includes; i++)
617                                                 include_files.Add (file.GetSourceFile (reader.ReadLeb128 ()));
618                                 }
619
620                                 int count_ns = reader.ReadLeb128 ();
621                                 namespaces = new List<NamespaceEntry> ();
622                                 for (int i = 0; i < count_ns; i ++)
623                                         namespaces.Add (new NamespaceEntry (file, reader));
624
625                                 reader.BaseStream.Position = old_pos;
626                         }
627                 }
628
629                 public NamespaceEntry[] Namespaces {
630                         get {
631                                 ReadData ();
632                                 NamespaceEntry[] retval = new NamespaceEntry [namespaces.Count];
633                                 namespaces.CopyTo (retval, 0);
634                                 return retval;
635                         }
636                 }
637
638                 public SourceFileEntry[] IncludeFiles {
639                         get {
640                                 ReadData ();
641                                 if (include_files == null)
642                                         return new SourceFileEntry [0];
643
644                                 SourceFileEntry[] retval = new SourceFileEntry [include_files.Count];
645                                 include_files.CopyTo (retval, 0);
646                                 return retval;
647                         }
648                 }
649         }
650
651         public class SourceFileEntry
652         {
653                 #region This is actually written to the symbol file
654                 public readonly int Index;
655                 int DataOffset;
656                 #endregion
657
658                 MonoSymbolFile file;
659                 string file_name;
660                 byte[] guid;
661                 byte[] hash;
662                 bool creating;
663                 bool auto_generated;
664
665                 public static int Size {
666                         get { return 8; }
667                 }
668
669                 public SourceFileEntry (MonoSymbolFile file, string file_name)
670                 {
671                         this.file = file;
672                         this.file_name = file_name;
673                         this.Index = file.AddSource (this);
674
675                         creating = true;
676                 }
677
678                 public SourceFileEntry (MonoSymbolFile file, string file_name,
679                                         byte[] guid, byte[] checksum)
680                         : this (file, file_name)
681                 {
682                         this.guid = guid;
683                         this.hash = checksum;
684                 }
685
686                 public byte[] Checksum {
687                         get {
688                                 return hash;
689                         }
690                 }
691
692                 internal void WriteData (MyBinaryWriter bw)
693                 {
694                         DataOffset = (int) bw.BaseStream.Position;
695                         bw.Write (file_name);
696
697                         if (guid == null) {
698                                 guid = Guid.NewGuid ().ToByteArray ();
699                                 try {
700                                         using (FileStream fs = new FileStream (file_name, FileMode.Open, FileAccess.Read)) {
701                                                 MD5 md5 = MD5.Create ();
702                                                 hash = md5.ComputeHash (fs);
703                                         }
704                                 } catch {
705                                         hash = new byte [16];
706                                 }
707                         }
708
709                         bw.Write (guid);
710                         bw.Write (hash);
711                         bw.Write ((byte) (auto_generated ? 1 : 0));
712                 }
713
714                 internal void Write (BinaryWriter bw)
715                 {
716                         bw.Write (Index);
717                         bw.Write (DataOffset);
718                 }
719
720                 internal SourceFileEntry (MonoSymbolFile file, MyBinaryReader reader)
721                 {
722                         this.file = file;
723
724                         Index = reader.ReadInt32 ();
725                         DataOffset = reader.ReadInt32 ();
726
727                         int old_pos = (int) reader.BaseStream.Position;
728                         reader.BaseStream.Position = DataOffset;
729
730                         file_name = reader.ReadString ();
731                         guid = reader.ReadBytes (16);
732                         hash = reader.ReadBytes (16);
733                         auto_generated = reader.ReadByte () == 1;
734
735                         reader.BaseStream.Position = old_pos;
736                 }
737
738                 public string FileName {
739                         get { return file_name; }
740                 }
741
742                 public bool AutoGenerated {
743                         get { return auto_generated; }
744                 }
745
746                 public void SetAutoGenerated ()
747                 {
748                         if (!creating)
749                                 throw new InvalidOperationException ();
750
751                         auto_generated = true;
752                         file.OffsetTable.FileFlags |= OffsetTable.Flags.IsAspxSource;
753                 }
754
755                 public bool CheckChecksum ()
756                 {
757                         try {
758                                 using (FileStream fs = new FileStream (file_name, FileMode.Open)) {
759                                         MD5 md5 = MD5.Create ();
760                                         byte[] data = md5.ComputeHash (fs);
761                                         for (int i = 0; i < 16; i++)
762                                                 if (data [i] != hash [i])
763                                                         return false;
764                                         return true;
765                                 }
766                         } catch {
767                                 return false;
768                         }
769                 }
770
771                 public override string ToString ()
772                 {
773                         return String.Format ("SourceFileEntry ({0}:{1})", Index, DataOffset);
774                 }
775         }
776
777         public class LineNumberTable
778         {
779                 protected LineNumberEntry[] _line_numbers;
780                 public LineNumberEntry[] LineNumbers {
781                         get { return _line_numbers; }
782                 }
783
784                 public readonly int LineBase;
785                 public readonly int LineRange;
786                 public readonly byte OpcodeBase;
787                 public readonly int MaxAddressIncrement;
788
789 #region Configurable constants
790                 public const int Default_LineBase = -1;
791                 public const int Default_LineRange = 8;
792                 public const byte Default_OpcodeBase = 9;
793
794                 public const bool SuppressDuplicates = true;
795 #endregion
796
797                 public const byte DW_LNS_copy = 1;
798                 public const byte DW_LNS_advance_pc = 2;
799                 public const byte DW_LNS_advance_line = 3;
800                 public const byte DW_LNS_set_file = 4;
801                 public const byte DW_LNS_const_add_pc = 8;
802
803                 public const byte DW_LNE_end_sequence = 1;
804
805                 // MONO extensions.
806                 public const byte DW_LNE_MONO_negate_is_hidden = 0x40;
807
808                 internal const byte DW_LNE_MONO__extensions_start = 0x40;
809                 internal const byte DW_LNE_MONO__extensions_end   = 0x7f;
810
811                 protected LineNumberTable (MonoSymbolFile file)
812                 {
813                         this.LineBase = file.OffsetTable.LineNumberTable_LineBase;
814                         this.LineRange = file.OffsetTable.LineNumberTable_LineRange;
815                         this.OpcodeBase = (byte) file.OffsetTable.LineNumberTable_OpcodeBase;
816                         this.MaxAddressIncrement = (255 - OpcodeBase) / LineRange;
817                 }
818
819                 internal LineNumberTable (MonoSymbolFile file, LineNumberEntry[] lines)
820                         : this (file)
821                 {
822                         this._line_numbers = lines;
823                 }
824
825                 internal void Write (MonoSymbolFile file, MyBinaryWriter bw)
826                 {
827                         int start = (int) bw.BaseStream.Position;
828
829                         bool last_is_hidden = false;
830                         int last_line = 1, last_offset = 0, last_file = 1;
831                         for (int i = 0; i < LineNumbers.Length; i++) {
832                                 int line_inc = LineNumbers [i].Row - last_line;
833                                 int offset_inc = LineNumbers [i].Offset - last_offset;
834
835                                 if (SuppressDuplicates && (i+1 < LineNumbers.Length)) {
836                                         if (LineNumbers [i+1].Equals (LineNumbers [i]))
837                                                 continue;
838                                 }
839
840                                 if (LineNumbers [i].File != last_file) {
841                                         bw.Write (DW_LNS_set_file);
842                                         bw.WriteLeb128 (LineNumbers [i].File);
843                                         last_file = LineNumbers [i].File;
844                                 }
845
846                                 if (LineNumbers [i].IsHidden != last_is_hidden) {
847                                         bw.Write ((byte) 0);
848                                         bw.Write ((byte) 1);
849                                         bw.Write (DW_LNE_MONO_negate_is_hidden);
850                                         last_is_hidden = LineNumbers [i].IsHidden;
851                                 }
852
853                                 if (offset_inc >= MaxAddressIncrement) {
854                                         if (offset_inc < 2 * MaxAddressIncrement) {
855                                                 bw.Write (DW_LNS_const_add_pc);
856                                                 offset_inc -= MaxAddressIncrement;
857                                         } else {
858                                                 bw.Write (DW_LNS_advance_pc);
859                                                 bw.WriteLeb128 (offset_inc);
860                                                 offset_inc = 0;
861                                         }
862                                 }
863
864                                 if ((line_inc < LineBase) || (line_inc >= LineBase + LineRange)) {
865                                         bw.Write (DW_LNS_advance_line);
866                                         bw.WriteLeb128 (line_inc);
867                                         if (offset_inc != 0) {
868                                                 bw.Write (DW_LNS_advance_pc);
869                                                 bw.WriteLeb128 (offset_inc);
870                                         }
871                                         bw.Write (DW_LNS_copy);
872                                 } else {
873                                         byte opcode;
874                                         opcode = (byte) (line_inc - LineBase + (LineRange * offset_inc) +
875                                                          OpcodeBase);
876                                         bw.Write (opcode);
877                                 }
878
879                                 last_line = LineNumbers [i].Row;
880                                 last_offset = LineNumbers [i].Offset;
881                         }
882
883                         bw.Write ((byte) 0);
884                         bw.Write ((byte) 1);
885                         bw.Write (DW_LNE_end_sequence);
886
887                         file.ExtendedLineNumberSize += (int) bw.BaseStream.Position - start;
888                 }
889
890                 internal static LineNumberTable Read (MonoSymbolFile file, MyBinaryReader br)
891                 {
892                         LineNumberTable lnt = new LineNumberTable (file);
893                         lnt.DoRead (file, br);
894                         return lnt;
895                 }
896
897                 void DoRead (MonoSymbolFile file, MyBinaryReader br)
898                 {
899                         var lines = new List<LineNumberEntry> ();
900
901                         bool is_hidden = false, modified = false;
902                         int stm_line = 1, stm_offset = 0, stm_file = 1;
903                         while (true) {
904                                 byte opcode = br.ReadByte ();
905
906                                 if (opcode == 0) {
907                                         byte size = br.ReadByte ();
908                                         long end_pos = br.BaseStream.Position + size;
909                                         opcode = br.ReadByte ();
910
911                                         if (opcode == DW_LNE_end_sequence) {
912                                                 if (modified)
913                                                         lines.Add (new LineNumberEntry (
914                                                                 stm_file, stm_line, stm_offset, is_hidden));
915                                                 break;
916                                         } else if (opcode == DW_LNE_MONO_negate_is_hidden) {
917                                                 is_hidden = !is_hidden;
918                                                 modified = true;
919                                         } else if ((opcode >= DW_LNE_MONO__extensions_start) &&
920                                                    (opcode <= DW_LNE_MONO__extensions_end)) {
921                                                 ; // reserved for future extensions
922                                         } else {
923                                                 throw new MonoSymbolFileException ("Unknown extended opcode {0:x}", opcode);
924                                         }
925
926                                         br.BaseStream.Position = end_pos;
927                                         continue;
928                                 } else if (opcode < OpcodeBase) {
929                                         switch (opcode) {
930                                         case DW_LNS_copy:
931                                                 lines.Add (new LineNumberEntry (
932                                                         stm_file, stm_line, stm_offset, is_hidden));
933                                                 modified = false;
934                                                 break;
935                                         case DW_LNS_advance_pc:
936                                                 stm_offset += br.ReadLeb128 ();
937                                                 modified = true;
938                                                 break;
939                                         case DW_LNS_advance_line:
940                                                 stm_line += br.ReadLeb128 ();
941                                                 modified = true;
942                                                 break;
943                                         case DW_LNS_set_file:
944                                                 stm_file = br.ReadLeb128 ();
945                                                 modified = true;
946                                                 break;
947                                         case DW_LNS_const_add_pc:
948                                                 stm_offset += MaxAddressIncrement;
949                                                 modified = true;
950                                                 break;
951                                         default:
952                                                 throw new MonoSymbolFileException (
953                                                         "Unknown standard opcode {0:x} in LNT",
954                                                         opcode);
955                                         }
956                                 } else {
957                                         opcode -= OpcodeBase;
958
959                                         stm_offset += opcode / LineRange;
960                                         stm_line += LineBase + (opcode % LineRange);
961                                         lines.Add (new LineNumberEntry (
962                                                 stm_file, stm_line, stm_offset, is_hidden));
963                                         modified = false;
964                                 }
965                         }
966
967                         _line_numbers = new LineNumberEntry [lines.Count];
968                         lines.CopyTo (_line_numbers, 0);
969                 }
970
971                 public bool GetMethodBounds (out LineNumberEntry start, out LineNumberEntry end)
972                 {
973                         if (_line_numbers.Length > 1) {
974                                 start = _line_numbers [0];
975                                 end = _line_numbers [_line_numbers.Length - 1];
976                                 return true;
977                         }
978
979                         start = LineNumberEntry.Null;
980                         end = LineNumberEntry.Null;
981                         return false;
982                 }
983         }
984
985         public class MethodEntry : IComparable
986         {
987                 #region This is actually written to the symbol file
988                 public readonly int CompileUnitIndex;
989                 public readonly int Token;
990                 public readonly int NamespaceID;
991
992                 int DataOffset;
993                 int LocalVariableTableOffset;
994                 int LineNumberTableOffset;
995                 int CodeBlockTableOffset;
996                 int ScopeVariableTableOffset;
997                 int RealNameOffset;
998                 Flags flags;
999                 #endregion
1000
1001                 int index;
1002
1003                 public Flags MethodFlags {
1004                         get { return flags; }
1005                 }
1006
1007                 public readonly CompileUnitEntry CompileUnit;
1008
1009                 LocalVariableEntry[] locals;
1010                 CodeBlockEntry[] code_blocks;
1011                 ScopeVariable[] scope_vars;
1012                 LineNumberTable lnt;
1013                 string real_name;
1014
1015                 public readonly MonoSymbolFile SymbolFile;
1016
1017                 public int Index {
1018                         get { return index; }
1019                         set { index = value; }
1020                 }
1021
1022                 [Flags]
1023                 public enum Flags
1024                 {
1025                         LocalNamesAmbiguous     = 1
1026                 }
1027
1028                 public const int Size = 12;
1029
1030                 internal MethodEntry (MonoSymbolFile file, MyBinaryReader reader, int index)
1031                 {
1032                         this.SymbolFile = file;
1033                         this.index = index;
1034
1035                         Token = reader.ReadInt32 ();
1036                         DataOffset = reader.ReadInt32 ();
1037                         LineNumberTableOffset = reader.ReadInt32 ();
1038
1039                         long old_pos = reader.BaseStream.Position;
1040                         reader.BaseStream.Position = DataOffset;
1041
1042                         CompileUnitIndex = reader.ReadLeb128 ();
1043                         LocalVariableTableOffset = reader.ReadLeb128 ();
1044                         NamespaceID = reader.ReadLeb128 ();
1045
1046                         CodeBlockTableOffset = reader.ReadLeb128 ();
1047                         ScopeVariableTableOffset = reader.ReadLeb128 ();
1048
1049                         RealNameOffset = reader.ReadLeb128 ();
1050
1051                         flags = (Flags) reader.ReadLeb128 ();
1052
1053                         reader.BaseStream.Position = old_pos;
1054
1055                         CompileUnit = file.GetCompileUnit (CompileUnitIndex);
1056                 }
1057
1058                 internal MethodEntry (MonoSymbolFile file, CompileUnitEntry comp_unit,
1059                                       int token, ScopeVariable[] scope_vars,
1060                                       LocalVariableEntry[] locals, LineNumberEntry[] lines,
1061                                       CodeBlockEntry[] code_blocks, string real_name,
1062                                       Flags flags, int namespace_id)
1063                 {
1064                         this.SymbolFile = file;
1065                         this.real_name = real_name;
1066                         this.locals = locals;
1067                         this.code_blocks = code_blocks;
1068                         this.scope_vars = scope_vars;
1069                         this.flags = flags;
1070
1071                         index = -1;
1072
1073                         Token = token;
1074                         CompileUnitIndex = comp_unit.Index;
1075                         CompileUnit = comp_unit;
1076                         NamespaceID = namespace_id;
1077
1078                         CheckLineNumberTable (lines);
1079                         lnt = new LineNumberTable (file, lines);
1080                         file.NumLineNumbers += lines.Length;
1081
1082                         int num_locals = locals != null ? locals.Length : 0;
1083
1084                         if (num_locals <= 32) {
1085                                 // Most of the time, the O(n^2) factor is actually
1086                                 // less than the cost of allocating the hash table,
1087                                 // 32 is a rough number obtained through some testing.
1088                                 
1089                                 for (int i = 0; i < num_locals; i ++) {
1090                                         string nm = locals [i].Name;
1091                                         
1092                                         for (int j = i + 1; j < num_locals; j ++) {
1093                                                 if (locals [j].Name == nm) {
1094                                                         flags |= Flags.LocalNamesAmbiguous;
1095                                                         goto locals_check_done;
1096                                                 }
1097                                         }
1098                                 }
1099                         locals_check_done :
1100                                 ;
1101                         } else {
1102                                 var local_names = new Dictionary<string, LocalVariableEntry> ();
1103                                 foreach (LocalVariableEntry local in locals) {
1104                                         if (local_names.ContainsKey (local.Name)) {
1105                                                 flags |= Flags.LocalNamesAmbiguous;
1106                                                 break;
1107                                         }
1108                                         local_names.Add (local.Name, local);
1109                                 }
1110                         }
1111                 }
1112                 
1113                 static void CheckLineNumberTable (LineNumberEntry[] line_numbers)
1114                 {
1115                         int last_offset = -1;
1116                         int last_row = -1;
1117
1118                         if (line_numbers == null)
1119                                 return;
1120                         
1121                         for (int i = 0; i < line_numbers.Length; i++) {
1122                                 LineNumberEntry line = line_numbers [i];
1123
1124                                 if (line.Equals (LineNumberEntry.Null))
1125                                         throw new MonoSymbolFileException ();
1126
1127                                 if (line.Offset < last_offset)
1128                                         throw new MonoSymbolFileException ();
1129
1130                                 if (line.Offset > last_offset) {
1131                                         last_row = line.Row;
1132                                         last_offset = line.Offset;
1133                                 } else if (line.Row > last_row) {
1134                                         last_row = line.Row;
1135                                 }
1136                         }
1137                 }
1138
1139                 internal void Write (MyBinaryWriter bw)
1140                 {
1141                         if ((index <= 0) || (DataOffset == 0))
1142                                 throw new InvalidOperationException ();
1143
1144                         bw.Write (Token);
1145                         bw.Write (DataOffset);
1146                         bw.Write (LineNumberTableOffset);
1147                 }
1148
1149                 internal void WriteData (MonoSymbolFile file, MyBinaryWriter bw)
1150                 {
1151                         if (index <= 0)
1152                                 throw new InvalidOperationException ();
1153
1154                         LocalVariableTableOffset = (int) bw.BaseStream.Position;
1155                         int num_locals = locals != null ? locals.Length : 0;
1156                         bw.WriteLeb128 (num_locals);
1157                         for (int i = 0; i < num_locals; i++)
1158                                 locals [i].Write (file, bw);
1159                         file.LocalCount += num_locals;
1160
1161                         CodeBlockTableOffset = (int) bw.BaseStream.Position;
1162                         int num_code_blocks = code_blocks != null ? code_blocks.Length : 0;
1163                         bw.WriteLeb128 (num_code_blocks);
1164                         for (int i = 0; i < num_code_blocks; i++)
1165                                 code_blocks [i].Write (bw);
1166
1167                         ScopeVariableTableOffset = (int) bw.BaseStream.Position;
1168                         int num_scope_vars = scope_vars != null ? scope_vars.Length : 0;
1169                         bw.WriteLeb128 (num_scope_vars);
1170                         for (int i = 0; i < num_scope_vars; i++)
1171                                 scope_vars [i].Write (bw);
1172
1173                         if (real_name != null) {
1174                                 RealNameOffset = (int) bw.BaseStream.Position;
1175                                 bw.Write (real_name);
1176                         }
1177
1178                         LineNumberTableOffset = (int) bw.BaseStream.Position;
1179                         lnt.Write (file, bw);
1180
1181                         DataOffset = (int) bw.BaseStream.Position;
1182
1183                         bw.WriteLeb128 (CompileUnitIndex);
1184                         bw.WriteLeb128 (LocalVariableTableOffset);
1185                         bw.WriteLeb128 (NamespaceID);
1186
1187                         bw.WriteLeb128 (CodeBlockTableOffset);
1188                         bw.WriteLeb128 (ScopeVariableTableOffset);
1189
1190                         bw.WriteLeb128 (RealNameOffset);
1191                         bw.WriteLeb128 ((int) flags);
1192                 }
1193
1194                 public LineNumberTable GetLineNumberTable ()
1195                 {
1196                         lock (SymbolFile) {
1197                                 if (lnt != null)
1198                                         return lnt;
1199
1200                                 if (LineNumberTableOffset == 0)
1201                                         return null;
1202
1203                                 MyBinaryReader reader = SymbolFile.BinaryReader;
1204                                 long old_pos = reader.BaseStream.Position;
1205                                 reader.BaseStream.Position = LineNumberTableOffset;
1206
1207                                 lnt = LineNumberTable.Read (SymbolFile, reader);
1208
1209                                 reader.BaseStream.Position = old_pos;
1210                                 return lnt;
1211                         }
1212                 }
1213
1214                 public LocalVariableEntry[] GetLocals ()
1215                 {
1216                         lock (SymbolFile) {
1217                                 if (locals != null)
1218                                         return locals;
1219
1220                                 if (LocalVariableTableOffset == 0)
1221                                         return null;
1222
1223                                 MyBinaryReader reader = SymbolFile.BinaryReader;
1224                                 long old_pos = reader.BaseStream.Position;
1225                                 reader.BaseStream.Position = LocalVariableTableOffset;
1226
1227                                 int num_locals = reader.ReadLeb128 ();
1228                                 locals = new LocalVariableEntry [num_locals];
1229
1230                                 for (int i = 0; i < num_locals; i++)
1231                                         locals [i] = new LocalVariableEntry (SymbolFile, reader);
1232
1233                                 reader.BaseStream.Position = old_pos;
1234                                 return locals;
1235                         }
1236                 }
1237
1238                 public CodeBlockEntry[] GetCodeBlocks ()
1239                 {
1240                         lock (SymbolFile) {
1241                                 if (code_blocks != null)
1242                                         return code_blocks;
1243
1244                                 if (CodeBlockTableOffset == 0)
1245                                         return null;
1246
1247                                 MyBinaryReader reader = SymbolFile.BinaryReader;
1248                                 long old_pos = reader.BaseStream.Position;
1249                                 reader.BaseStream.Position = CodeBlockTableOffset;
1250
1251                                 int num_code_blocks = reader.ReadLeb128 ();
1252                                 code_blocks = new CodeBlockEntry [num_code_blocks];
1253
1254                                 for (int i = 0; i < num_code_blocks; i++)
1255                                         code_blocks [i] = new CodeBlockEntry (i, reader);
1256
1257                                 reader.BaseStream.Position = old_pos;
1258                                 return code_blocks;
1259                         }
1260                 }
1261
1262                 public ScopeVariable[] GetScopeVariables ()
1263                 {
1264                         lock (SymbolFile) {
1265                                 if (scope_vars != null)
1266                                         return scope_vars;
1267
1268                                 if (ScopeVariableTableOffset == 0)
1269                                         return null;
1270
1271                                 MyBinaryReader reader = SymbolFile.BinaryReader;
1272                                 long old_pos = reader.BaseStream.Position;
1273                                 reader.BaseStream.Position = ScopeVariableTableOffset;
1274
1275                                 int num_scope_vars = reader.ReadLeb128 ();
1276                                 scope_vars = new ScopeVariable [num_scope_vars];
1277
1278                                 for (int i = 0; i < num_scope_vars; i++)
1279                                         scope_vars [i] = new ScopeVariable (reader);
1280
1281                                 reader.BaseStream.Position = old_pos;
1282                                 return scope_vars;
1283                         }
1284                 }
1285
1286                 public string GetRealName ()
1287                 {
1288                         lock (SymbolFile) {
1289                                 if (real_name != null)
1290                                         return real_name;
1291
1292                                 if (RealNameOffset == 0)
1293                                         return null;
1294
1295                                 real_name = SymbolFile.BinaryReader.ReadString (RealNameOffset);
1296                                 return real_name;
1297                         }
1298                 }
1299
1300                 public int CompareTo (object obj)
1301                 {
1302                         MethodEntry method = (MethodEntry) obj;
1303
1304                         if (method.Token < Token)
1305                                 return 1;
1306                         else if (method.Token > Token)
1307                                 return -1;
1308                         else
1309                                 return 0;
1310                 }
1311
1312                 public override string ToString ()
1313                 {
1314                         return String.Format ("[Method {0}:{1:x}:{2}:{3}]",
1315                                               index, Token, CompileUnitIndex, CompileUnit);
1316                 }
1317         }
1318
1319         public struct NamespaceEntry
1320         {
1321                 #region This is actually written to the symbol file
1322                 public readonly string Name;
1323                 public readonly int Index;
1324                 public readonly int Parent;
1325                 public readonly string[] UsingClauses;
1326                 #endregion
1327
1328                 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
1329                 {
1330                         this.Name = name;
1331                         this.Index = index;
1332                         this.Parent = parent;
1333                         this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
1334                 }
1335
1336                 internal NamespaceEntry (MonoSymbolFile file, MyBinaryReader reader)
1337                 {
1338                         Name = reader.ReadString ();
1339                         Index = reader.ReadLeb128 ();
1340                         Parent = reader.ReadLeb128 ();
1341
1342                         int count = reader.ReadLeb128 ();
1343                         UsingClauses = new string [count];
1344                         for (int i = 0; i < count; i++)
1345                                 UsingClauses [i] = reader.ReadString ();
1346                 }
1347
1348                 internal void Write (MonoSymbolFile file, MyBinaryWriter bw)
1349                 {
1350                         bw.Write (Name);
1351                         bw.WriteLeb128 (Index);
1352                         bw.WriteLeb128 (Parent);
1353                         bw.WriteLeb128 (UsingClauses.Length);
1354                         foreach (string uc in UsingClauses)
1355                                 bw.Write (uc);
1356                 }
1357
1358                 public override string ToString ()
1359                 {
1360                         return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);
1361                 }
1362         }
1363 }