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