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