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