2 // System.Diagnostics.SymbolStore/MonoSymbolTable.cs
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2002 Ximian, Inc. http://www.ximian.com
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Collections;
18 // Parts which are actually written into the symbol file are marked with
20 // #region This is actually written to the symbol file
23 // Please do not modify these regions without previously talking to me.
25 // All changes to the file format must be synchronized in several places:
27 // a) The fields in these regions (and their order) must match the actual
28 // contents of the symbol file.
30 // This helps people to understand the symbol file format without reading
31 // too much source code, ie. you look at the appropriate region and then
32 // you know what's actually in the file.
34 // It is also required to help me enforce b).
36 // b) The regions must be kept in sync with the unmanaged code in
37 // mono/metadata/debug-mono-symfile.h
39 // When making changes to the file format, you must also increase two version
42 // i) OffsetTable.Version in this file.
43 // ii) MONO_SYMBOL_FILE_VERSION in mono/metadata/debug-mono-symfile.h
45 // After doing so, recompile everything, including the debugger. Symbol files
46 // with different versions are incompatible to each other and the debugger and
47 // the runtime enfore this, so you need to recompile all your assemblies after
48 // changing the file format.
51 namespace Mono.CSharp.Debugger
53 public struct OffsetTable
55 public const int Version = 36;
56 public const long Magic = 0x45e82623fd7fa614;
58 #region This is actually written to the symbol file
59 public int TotalFileSize;
60 public int DataSectionOffset;
61 public int DataSectionSize;
62 public int SourceCount;
63 public int SourceTableOffset;
64 public int SourceTableSize;
65 public int MethodCount;
66 public int MethodTableOffset;
67 public int MethodTableSize;
71 internal OffsetTable (BinaryReader reader)
73 TotalFileSize = reader.ReadInt32 ();
74 DataSectionOffset = reader.ReadInt32 ();
75 DataSectionSize = reader.ReadInt32 ();
76 SourceCount = reader.ReadInt32 ();
77 SourceTableOffset = reader.ReadInt32 ();
78 SourceTableSize = reader.ReadInt32 ();
79 MethodCount = reader.ReadInt32 ();
80 MethodTableOffset = reader.ReadInt32 ();
81 MethodTableSize = reader.ReadInt32 ();
82 TypeCount = reader.ReadInt32 ();
85 internal void Write (BinaryWriter bw)
87 bw.Write (TotalFileSize);
88 bw.Write (DataSectionOffset);
89 bw.Write (DataSectionSize);
90 bw.Write (SourceCount);
91 bw.Write (SourceTableOffset);
92 bw.Write (SourceTableSize);
93 bw.Write (MethodCount);
94 bw.Write (MethodTableOffset);
95 bw.Write (MethodTableSize);
99 public override string ToString ()
101 return String.Format (
102 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
103 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
104 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
105 MethodTableSize, TypeCount);
109 public struct LineNumberEntry
111 #region This is actually written to the symbol file
112 public readonly int Row;
113 public readonly int Offset;
116 public LineNumberEntry (int row, int offset)
119 this.Offset = offset;
122 public static LineNumberEntry Null = new LineNumberEntry (0, 0);
124 internal LineNumberEntry (BinaryReader reader)
126 Row = reader.ReadInt32 ();
127 Offset = reader.ReadInt32 ();
130 internal void Write (BinaryWriter bw)
136 private class OffsetComparerClass : IComparer
138 public int Compare (object a, object b)
140 LineNumberEntry l1 = (LineNumberEntry) a;
141 LineNumberEntry l2 = (LineNumberEntry) b;
143 if (l1.Offset < l2.Offset)
145 else if (l1.Offset > l2.Offset)
152 private class RowComparerClass : IComparer
154 public int Compare (object a, object b)
156 LineNumberEntry l1 = (LineNumberEntry) a;
157 LineNumberEntry l2 = (LineNumberEntry) b;
161 else if (l1.Row > l2.Row)
168 public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
169 public static readonly IComparer RowComparer = new RowComparerClass ();
171 public override string ToString ()
173 return String.Format ("[Line {0}:{1}]", Row, Offset);
177 public class LexicalBlockEntry
180 #region This is actually written to the symbol file
181 public int StartOffset;
182 public int EndOffset;
185 public LexicalBlockEntry (int index, int start_offset)
188 this.StartOffset = start_offset;
191 internal LexicalBlockEntry (int index, BinaryReader reader)
194 this.StartOffset = reader.ReadInt32 ();
195 this.EndOffset = reader.ReadInt32 ();
198 public void Close (int end_offset)
200 this.EndOffset = end_offset;
203 internal void Write (BinaryWriter bw)
205 bw.Write (StartOffset);
206 bw.Write (EndOffset);
209 public override string ToString ()
211 return String.Format ("[LexicalBlock {0}:{1}]", StartOffset, EndOffset);
215 public struct LocalVariableEntry
217 #region This is actually written to the symbol file
218 public readonly string Name;
219 public readonly FieldAttributes Attributes;
220 public readonly byte[] Signature;
221 public readonly int BlockIndex;
224 public LocalVariableEntry (string Name, FieldAttributes Attributes, byte[] Signature,
228 this.Attributes = Attributes;
229 this.Signature = Signature;
230 this.BlockIndex = BlockIndex;
233 internal LocalVariableEntry (BinaryReader reader)
235 int name_length = reader.ReadInt32 ();
236 byte[] name = reader.ReadBytes (name_length);
237 Name = Encoding.UTF8.GetString (name);
238 Attributes = (FieldAttributes) reader.ReadInt32 ();
239 int sig_length = reader.ReadInt32 ();
240 Signature = reader.ReadBytes (sig_length);
241 BlockIndex = reader.ReadInt32 ();
244 internal void Write (MonoSymbolFile file, BinaryWriter bw)
246 file.WriteString (bw, Name);
247 bw.Write ((int) Attributes);
248 bw.Write ((int) Signature.Length);
249 bw.Write (Signature);
250 bw.Write (BlockIndex);
253 public override string ToString ()
255 return String.Format ("[LocalVariable {0}:{1}]", Name, Attributes);
259 public class SourceFileEntry
261 #region This is actually written to the symbol file
262 public readonly int Index;
267 int NamespaceTableOffset;
273 ArrayList namespaces;
276 public static int Size {
280 internal SourceFileEntry (MonoSymbolFile file, string file_name)
283 this.file_name = file_name;
284 this.Index = file.AddSource (this);
287 methods = new ArrayList ();
288 namespaces = new ArrayList ();
291 public void DefineMethod (MethodBase method, int token, LocalVariableEntry[] locals,
292 LineNumberEntry[] lines, LexicalBlockEntry[] blocks,
293 int start, int end, int namespace_id)
296 throw new InvalidOperationException ();
298 MethodEntry entry = new MethodEntry (
299 file, this, method, token, locals, lines, blocks, start, end, namespace_id);
302 file.AddMethod (entry);
305 public int DefineNamespace (string name, string[] using_clauses, int parent)
308 throw new InvalidOperationException ();
310 int index = file.GetNextNamespaceIndex ();
311 NamespaceEntry ns = new NamespaceEntry (name, index, using_clauses, parent);
316 internal void WriteData (BinaryWriter bw)
318 NameOffset = (int) bw.BaseStream.Position;
319 file.WriteString (bw, file_name);
321 ArrayList list = new ArrayList ();
322 foreach (MethodEntry entry in methods)
323 list.Add (entry.Write (file, bw));
327 MethodOffset = (int) bw.BaseStream.Position;
328 foreach (MethodSourceEntry method in list)
331 NamespaceCount = namespaces.Count;
332 NamespaceTableOffset = (int) bw.BaseStream.Position;
333 foreach (NamespaceEntry ns in namespaces)
337 internal void Write (BinaryWriter bw)
341 bw.Write (NamespaceCount);
342 bw.Write (NameOffset);
343 bw.Write (MethodOffset);
344 bw.Write (NamespaceTableOffset);
347 internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
351 Index = reader.ReadInt32 ();
352 Count = reader.ReadInt32 ();
353 NamespaceCount = reader.ReadInt32 ();
354 NameOffset = reader.ReadInt32 ();
355 MethodOffset = reader.ReadInt32 ();
356 NamespaceTableOffset = reader.ReadInt32 ();
358 file_name = file.ReadString (NameOffset);
361 public string FileName {
362 get { return file_name; }
365 public MethodSourceEntry[] Methods {
368 throw new InvalidOperationException ();
370 BinaryReader reader = file.BinaryReader;
371 int old_pos = (int) reader.BaseStream.Position;
373 reader.BaseStream.Position = MethodOffset;
374 ArrayList list = new ArrayList ();
375 for (int i = 0; i < Count; i ++)
376 list.Add (new MethodSourceEntry (reader));
377 reader.BaseStream.Position = old_pos;
379 MethodSourceEntry[] retval = new MethodSourceEntry [Count];
380 list.CopyTo (retval, 0);
385 public override string ToString ()
387 return String.Format ("SourceFileEntry ({0}:{1}:{2})",
388 Index, file_name, Count);
392 public struct MethodSourceEntry : IComparable
394 #region This is actually written to the symbol file
395 public readonly int Index;
396 public readonly int FileOffset;
397 public readonly int StartRow;
398 public readonly int EndRow;
401 public MethodSourceEntry (int index, int file_offset, int start, int end)
404 this.FileOffset = file_offset;
405 this.StartRow = start;
409 internal MethodSourceEntry (BinaryReader reader)
411 Index = reader.ReadInt32 ();
412 FileOffset = reader.ReadInt32 ();
413 StartRow = reader.ReadInt32 ();
414 EndRow = reader.ReadInt32 ();
417 public static int Size {
421 internal void Write (BinaryWriter bw)
424 bw.Write (FileOffset);
429 public int CompareTo (object obj)
431 MethodSourceEntry method = (MethodSourceEntry) obj;
433 if (method.StartRow < StartRow)
435 else if (method.StartRow > StartRow)
441 public override string ToString ()
443 return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
444 Index, FileOffset, StartRow, EndRow);
448 public struct MethodIndexEntry
450 #region This is actually written to the symbol file
451 public readonly int FileOffset;
452 public readonly int Token;
455 public static int Size {
459 public MethodIndexEntry (int offset, int token)
461 this.FileOffset = offset;
465 internal MethodIndexEntry (BinaryReader reader)
467 FileOffset = reader.ReadInt32 ();
468 Token = reader.ReadInt32 ();
471 internal void Write (BinaryWriter bw)
473 bw.Write (FileOffset);
477 public override string ToString ()
479 return String.Format ("MethodIndexEntry ({0}:{1:x})",
484 public class MethodEntry
486 #region This is actually written to the symbol file
487 public readonly int SourceFileIndex;
488 public readonly int Token;
489 public readonly int StartRow;
490 public readonly int EndRow;
491 public readonly int ClassTypeIndex;
492 public readonly int NumParameters;
493 public readonly int NumLocals;
494 public readonly int NumLineNumbers;
495 public readonly int NamespaceID;
496 public readonly bool LocalNamesAmbiguous;
499 int TypeIndexTableOffset;
500 int LocalVariableTableOffset;
501 int LineNumberTableOffset;
502 int NumLexicalBlocks;
503 int LexicalBlockTableOffset;
509 public readonly int Index;
510 public readonly SourceFileEntry SourceFile;
511 public readonly LineNumberEntry[] LineNumbers;
512 public readonly int[] ParamTypeIndices;
513 public readonly int[] LocalTypeIndices;
514 public readonly LocalVariableEntry[] Locals;
515 public readonly Type[] LocalTypes;
516 public readonly LexicalBlockEntry[] LexicalBlocks;
518 public readonly MonoSymbolFile SymbolFile;
520 public static int Size {
528 public MethodBase MethodBase {
529 get { return MonoDebuggerSupport.GetMethod (SymbolFile.Assembly, Token); }
532 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
534 this.SymbolFile = file;
536 SourceFileIndex = reader.ReadInt32 ();
537 Token = reader.ReadInt32 ();
538 StartRow = reader.ReadInt32 ();
539 EndRow = reader.ReadInt32 ();
540 ClassTypeIndex = reader.ReadInt32 ();
541 NumParameters = reader.ReadInt32 ();
542 NumLocals = reader.ReadInt32 ();
543 NumLineNumbers = reader.ReadInt32 ();
544 NameOffset = reader.ReadInt32 ();
545 TypeIndexTableOffset = reader.ReadInt32 ();
546 LocalVariableTableOffset = reader.ReadInt32 ();
547 LineNumberTableOffset = reader.ReadInt32 ();
548 NumLexicalBlocks = reader.ReadInt32 ();
549 LexicalBlockTableOffset = reader.ReadInt32 ();
550 NamespaceID = reader.ReadInt32 ();
551 LocalNamesAmbiguous = reader.ReadInt32 () != 0;
553 name = file.ReadString (NameOffset);
555 SourceFile = file.GetSourceFile (SourceFileIndex);
557 if (LineNumberTableOffset != 0) {
558 long old_pos = reader.BaseStream.Position;
559 reader.BaseStream.Position = LineNumberTableOffset;
561 LineNumbers = new LineNumberEntry [NumLineNumbers];
563 for (int i = 0; i < NumLineNumbers; i++)
564 LineNumbers [i] = new LineNumberEntry (reader);
566 reader.BaseStream.Position = old_pos;
569 if (LocalVariableTableOffset != 0) {
570 long old_pos = reader.BaseStream.Position;
571 reader.BaseStream.Position = LocalVariableTableOffset;
573 Locals = new LocalVariableEntry [NumLocals];
574 LocalTypes = new Type [NumLocals];
576 Assembly ass = file.Assembly;
578 for (int i = 0; i < NumLocals; i++) {
579 Locals [i] = new LocalVariableEntry (reader);
580 LocalTypes [i] = MonoDebuggerSupport.GetLocalTypeFromSignature (
581 ass, Locals [i].Signature);
584 reader.BaseStream.Position = old_pos;
587 if (TypeIndexTableOffset != 0) {
588 long old_pos = reader.BaseStream.Position;
589 reader.BaseStream.Position = TypeIndexTableOffset;
591 ParamTypeIndices = new int [NumParameters];
592 LocalTypeIndices = new int [NumLocals];
594 for (int i = 0; i < NumParameters; i++)
595 ParamTypeIndices [i] = reader.ReadInt32 ();
596 for (int i = 0; i < NumLocals; i++)
597 LocalTypeIndices [i] = reader.ReadInt32 ();
599 reader.BaseStream.Position = old_pos;
602 if (LexicalBlockTableOffset != 0) {
603 long old_pos = reader.BaseStream.Position;
604 reader.BaseStream.Position = LexicalBlockTableOffset;
606 LexicalBlocks = new LexicalBlockEntry [NumLexicalBlocks];
607 for (int i = 0; i < NumLexicalBlocks; i++)
608 LexicalBlocks [i] = new LexicalBlockEntry (i, reader);
610 reader.BaseStream.Position = old_pos;
614 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
615 int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
616 LexicalBlockEntry[] blocks, int start_row, int end_row,
619 this.SymbolFile = file;
620 Index = file.GetNextMethodIndex ();
623 SourceFileIndex = source.Index;
625 StartRow = start_row;
627 NamespaceID = namespace_id;
628 LexicalBlocks = blocks;
629 NumLexicalBlocks = LexicalBlocks.Length;
631 LineNumbers = BuildLineNumberTable (lines);
632 NumLineNumbers = LineNumbers.Length;
634 ParameterInfo[] parameters = method.GetParameters ();
635 if (parameters == null)
636 parameters = new ParameterInfo [0];
640 NumParameters = parameters.Length;
641 ParamTypeIndices = new int [NumParameters];
642 for (int i = 0; i < NumParameters; i++)
643 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
645 NumLocals = locals.Length;
648 if (NumLocals <= 32) {
649 // Most of the time, the O(n^2) factor is actually
650 // less than the cost of allocating the hash table,
651 // 32 is a rough number obtained through some testing.
653 for (int i = 0; i < NumLocals; i ++) {
654 string nm = locals [i].Name;
656 for (int j = i + 1; j < NumLocals; j ++) {
657 if (locals [j].Name == nm) {
658 LocalNamesAmbiguous = true;
659 goto locals_check_done;
666 Hashtable local_names = new Hashtable ();
667 foreach (LocalVariableEntry local in locals) {
668 if (local_names.Contains (local.Name)) {
669 LocalNamesAmbiguous = true;
672 local_names.Add (local.Name, local);
676 LocalTypeIndices = new int [NumLocals];
677 for (int i = 0; i < NumLocals; i++)
678 LocalTypeIndices [i] = file.GetNextTypeIndex ();
680 ClassTypeIndex = file.DefineType (method.ReflectedType);
683 // BuildLineNumberTable() eliminates duplicate line numbers and ensures
684 // we aren't going "backwards" since this would counfuse the runtime's
685 // debugging code (and the debugger).
687 // In the line number table, the "offset" field most be strictly
688 // monotonic increasing; that is, the next entry must not have an offset
689 // which is equal to or less than the current one.
691 // The most common case is that our input (ie. the line number table as
692 // we get it from mcs) contains several entries with the same offset
693 // (and different line numbers) - but it may also happen that the offset
694 // is decreasing (this can be considered as an exception, such lines will
695 // simply be discarded).
696 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
698 ArrayList list = new ArrayList ();
699 int last_offset = -1;
702 for (int i = 0; i < line_numbers.Length; i++) {
703 LineNumberEntry line = (LineNumberEntry) line_numbers [i];
705 if (line.Offset > last_offset) {
707 list.Add (new LineNumberEntry (last_row, last_offset));
709 last_offset = line.Offset;
710 } else if (line.Row > last_row) {
716 list.Add (new LineNumberEntry (last_row, last_offset));
718 LineNumberEntry[] retval = new LineNumberEntry [list.Count];
719 list.CopyTo (retval, 0);
723 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
725 NameOffset = (int) bw.BaseStream.Position;
726 file.WriteString (bw, name);
728 TypeIndexTableOffset = (int) bw.BaseStream.Position;
730 for (int i = 0; i < NumParameters; i++)
731 bw.Write (ParamTypeIndices [i]);
732 for (int i = 0; i < NumLocals; i++)
733 bw.Write (LocalTypeIndices [i]);
735 LocalVariableTableOffset = (int) bw.BaseStream.Position;
736 for (int i = 0; i < NumLocals; i++)
737 Locals [i].Write (file, bw);
738 file.LocalCount += NumLocals;
740 LineNumberTableOffset = (int) bw.BaseStream.Position;
741 for (int i = 0; i < NumLineNumbers; i++)
742 LineNumbers [i].Write (bw);
743 file.LineNumberCount += NumLineNumbers;
745 LexicalBlockTableOffset = (int) bw.BaseStream.Position;
746 for (int i = 0; i < NumLexicalBlocks; i++)
747 LexicalBlocks [i].Write (bw);
748 file_offset = (int) bw.BaseStream.Position;
750 bw.Write (SourceFileIndex);
754 bw.Write (ClassTypeIndex);
755 bw.Write (NumParameters);
756 bw.Write (NumLocals);
757 bw.Write (NumLineNumbers);
758 bw.Write (NameOffset);
759 bw.Write (TypeIndexTableOffset);
760 bw.Write (LocalVariableTableOffset);
761 bw.Write (LineNumberTableOffset);
762 bw.Write (NumLexicalBlocks);
763 bw.Write (LexicalBlockTableOffset);
764 bw.Write (NamespaceID);
765 bw.Write (LocalNamesAmbiguous ? 1 : 0);
767 return new MethodSourceEntry (Index, file_offset, StartRow, EndRow);
770 internal void WriteIndex (BinaryWriter bw)
772 new MethodIndexEntry (file_offset, Token).Write (bw);
775 public override string ToString ()
777 return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {6}:{7}:{8}:{9} - {5}]",
778 Index, Token, SourceFileIndex, StartRow, EndRow,
779 SourceFile, ClassTypeIndex, NumParameters,
780 NumLocals, NumLineNumbers);
784 public struct NamespaceEntry
786 #region This is actually written to the symbol file
787 public readonly string Name;
788 public readonly int Index;
789 public readonly int Parent;
790 public readonly string[] UsingClauses;
793 public NamespaceEntry (string name, int index, string[] using_clauses, int parent)
797 this.Parent = parent;
798 this.UsingClauses = using_clauses != null ? using_clauses : new string [0];
801 internal void Write (MonoSymbolFile file, BinaryWriter bw)
803 file.WriteString (bw, Name);
806 bw.Write (UsingClauses.Length);
807 foreach (string uc in UsingClauses)
808 file.WriteString (bw, uc);
811 public override string ToString ()
813 return String.Format ("[Namespace {0}:{1}:{2}]", Name, Index, Parent);