Hmm, sort the table for the moment, this is just a bit slower, but more stable.
[mono.git] / mcs / class / Mono.CSharp.Debugger / MonoSymbolTable.cs
1 //
2 // System.Diagnostics.SymbolStore/MonoSymbolTable.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@gnome.org)
6 //
7 // (C) 2002 Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Collections;
14 using System.Text;
15 using System.IO;
16         
17 namespace Mono.CSharp.Debugger
18 {
19         public struct OffsetTable
20         {
21                 public const int  Version = 29;
22                 public const long Magic   = 0x45e82623fd7fa614;
23
24                 public int TotalFileSize;
25                 public int DataSectionOffset;
26                 public int DataSectionSize;
27                 public int SourceCount;
28                 public int SourceTableOffset;
29                 public int SourceTableSize;
30                 public int MethodCount;
31                 public int MethodTableOffset;
32                 public int MethodTableSize;
33                 public int TypeCount;
34
35                 internal OffsetTable (BinaryReader reader)
36                 {
37                         TotalFileSize = reader.ReadInt32 ();
38                         DataSectionOffset = reader.ReadInt32 ();
39                         DataSectionSize = reader.ReadInt32 ();
40                         SourceCount = reader.ReadInt32 ();
41                         SourceTableOffset = reader.ReadInt32 ();
42                         SourceTableSize = reader.ReadInt32 ();
43                         MethodCount = reader.ReadInt32 ();
44                         MethodTableOffset = reader.ReadInt32 ();
45                         MethodTableSize = reader.ReadInt32 ();
46                         TypeCount = reader.ReadInt32 ();
47                 }
48
49                 internal void Write (BinaryWriter bw)
50                 {
51                         bw.Write (TotalFileSize);
52                         bw.Write (DataSectionOffset);
53                         bw.Write (DataSectionSize);
54                         bw.Write (SourceCount);
55                         bw.Write (SourceTableOffset);
56                         bw.Write (SourceTableSize);
57                         bw.Write (MethodCount);
58                         bw.Write (MethodTableOffset);
59                         bw.Write (MethodTableSize);
60                         bw.Write (TypeCount);
61                 }
62
63                 public override string ToString ()
64                 {
65                         return String.Format (
66                                 "OffsetTable [{0} - {1}:{2} - {3}:{4}:{5} - {6}:{7}:{8} - {9}]",
67                                 TotalFileSize, DataSectionOffset, DataSectionSize, SourceCount,
68                                 SourceTableOffset, SourceTableSize, MethodCount, MethodTableOffset,
69                                 MethodTableSize, TypeCount);
70                 }
71         }
72
73         public struct LineNumberEntry
74         {
75                 public readonly int Row;
76                 public readonly int Offset;
77
78                 public LineNumberEntry (int row, int offset)
79                 {
80                         this.Row = row;
81                         this.Offset = offset;
82                 }
83
84                 public static LineNumberEntry Null = new LineNumberEntry (0, 0);
85
86                 internal LineNumberEntry (BinaryReader reader)
87                 {
88                         Row = reader.ReadInt32 ();
89                         Offset = reader.ReadInt32 ();
90                 }
91
92                 internal void Write (BinaryWriter bw)
93                 {
94                         bw.Write (Row);
95                         bw.Write (Offset);
96                 }
97
98                 private class OffsetComparerClass : IComparer
99                 {
100                         public int Compare (object a, object b)
101                         {
102                                 LineNumberEntry l1 = (LineNumberEntry) a;
103                                 LineNumberEntry l2 = (LineNumberEntry) b;
104
105                                 if (l1.Offset < l2.Offset)
106                                         return -1;
107                                 else if (l1.Offset > l2.Offset)
108                                         return 1;
109                                 else
110                                         return 0;
111                         }
112                 }
113
114                 private class RowComparerClass : IComparer
115                 {
116                         public int Compare (object a, object b)
117                         {
118                                 LineNumberEntry l1 = (LineNumberEntry) a;
119                                 LineNumberEntry l2 = (LineNumberEntry) b;
120
121                                 if (l1.Row < l2.Row)
122                                         return -1;
123                                 else if (l1.Row > l2.Row)
124                                         return 1;
125                                 else
126                                         return 0;
127                         }
128                 }
129
130                 public static readonly IComparer OffsetComparer = new OffsetComparerClass ();
131                 public static readonly IComparer RowComparer = new RowComparerClass ();
132
133                 public override string ToString ()
134                 {
135                         return String.Format ("[Line {0}:{1}]", Row, Offset);
136                 }
137         }
138
139         public struct LocalVariableEntry
140         {
141                 public readonly string Name;
142                 public readonly FieldAttributes Attributes;
143                 public readonly byte[] Signature;
144
145                 public LocalVariableEntry (string Name, FieldAttributes Attributes, byte[] Signature)
146                 {
147                         this.Name = Name;
148                         this.Attributes = Attributes;
149                         this.Signature = Signature;
150                 }
151
152                 internal LocalVariableEntry (BinaryReader reader)
153                 {
154                         int name_length = reader.ReadInt32 ();
155                         byte[] name = reader.ReadBytes (name_length);
156                         Name = Encoding.UTF8.GetString (name);
157                         Attributes = (FieldAttributes) reader.ReadInt32 ();
158                         int sig_length = reader.ReadInt32 ();
159                         Signature = reader.ReadBytes (sig_length);
160                 }
161
162                 internal void Write (MonoSymbolFile file, BinaryWriter bw)
163                 {
164                         file.WriteString (bw, Name);
165                         bw.Write ((int) Attributes);
166                         bw.Write ((int) Signature.Length);
167                         bw.Write (Signature);
168                 }
169
170                 public override string ToString ()
171                 {
172                         return String.Format ("[LocalVariable {0}:{1}]", Name, Attributes);
173                 }
174         }
175
176         public class SourceFileEntry
177         {
178                 MonoSymbolFile file;
179                 string file_name;
180                 ArrayList methods;
181                 int index, count, name_offset, method_offset;
182                 bool creating;
183
184                 internal static int Size {
185                         get { return 16; }
186                 }
187
188                 internal SourceFileEntry (MonoSymbolFile file, string file_name, int index)
189                 {
190                         this.file = file;
191                         this.file_name = file_name;
192                         this.index = index;
193
194                         creating = true;
195                         methods = new ArrayList ();
196                 }
197
198                 public void DefineMethod (MethodBase method, int token, LocalVariableEntry[] locals,
199                                           LineNumberEntry[] lines, int start, int end)
200                 {
201                         if (!creating)
202                                 throw new InvalidOperationException ();
203
204                         MethodEntry entry = new MethodEntry (
205                                 file, this, method, token, locals, lines, start, end);
206
207                         methods.Add (entry);
208                         file.AddMethod (entry);
209                 }
210
211                 internal void WriteData (BinaryWriter bw)
212                 {
213                         name_offset = (int) bw.BaseStream.Position;
214                         file.WriteString (bw, file_name);
215
216                         ArrayList list = new ArrayList ();
217                         foreach (MethodEntry entry in methods)
218                                 list.Add (entry.Write (file, bw));
219                         list.Sort ();
220                         count = list.Count;
221
222                         method_offset = (int) bw.BaseStream.Position;
223                         foreach (MethodSourceEntry method in list)
224                                 method.Write (bw);
225                 }
226
227                 internal void Write (BinaryWriter bw)
228                 {
229                         bw.Write (index);
230                         bw.Write (count);
231                         bw.Write (name_offset);
232                         bw.Write (method_offset);
233                 }
234
235                 internal SourceFileEntry (MonoSymbolFile file, BinaryReader reader)
236                 {
237                         this.file = file;
238
239                         index = reader.ReadInt32 ();
240                         count = reader.ReadInt32 ();
241                         name_offset = reader.ReadInt32 ();
242                         method_offset = reader.ReadInt32 ();
243
244                         file_name = file.ReadString (name_offset);
245                 }
246
247                 public int Index {
248                         get { return index; }
249                 }
250
251                 public string FileName {
252                         get { return file_name; }
253                 }
254
255                 public MethodSourceEntry[] Methods {
256                         get {
257                                 if (creating)
258                                         throw new InvalidOperationException ();
259
260                                 BinaryReader reader = file.BinaryReader;
261                                 int old_pos = (int) reader.BaseStream.Position;
262
263                                 reader.BaseStream.Position = method_offset;
264                                 ArrayList list = new ArrayList ();
265                                 for (int i = 0; i < count; i ++)
266                                         list.Add (new MethodSourceEntry (reader));
267                                 reader.BaseStream.Position = old_pos;
268
269                                 MethodSourceEntry[] retval = new MethodSourceEntry [count];
270                                 list.CopyTo (retval, 0);
271                                 return retval;
272                         }
273                 }
274
275                 public override string ToString ()
276                 {
277                         return String.Format ("SourceFileEntry ({0}:{1}:{2})", index, file_name, count);
278                 }
279         }
280
281         public struct MethodSourceEntry : IComparable
282         {
283                 public readonly int Index;
284                 public readonly int FileOffset;
285                 public readonly int StartRow;
286                 public readonly int EndRow;
287
288                 public MethodSourceEntry (int index, int file_offset, int start, int end)
289                 {
290                         this.Index = index;
291                         this.FileOffset = file_offset;
292                         this.StartRow = start;
293                         this.EndRow = end;
294                 }
295
296                 internal MethodSourceEntry (BinaryReader reader)
297                 {
298                         Index = reader.ReadInt32 ();
299                         FileOffset = reader.ReadInt32 ();
300                         StartRow = reader.ReadInt32 ();
301                         EndRow = reader.ReadInt32 ();
302                 }
303
304                 public static int Size
305                 {
306                         get {
307                                 return 16;
308                         }
309                 }
310
311                 internal void Write (BinaryWriter bw)
312                 {
313                         bw.Write (Index);
314                         bw.Write (FileOffset);
315                         bw.Write (StartRow);
316                         bw.Write (EndRow);
317                 }
318
319                 public int CompareTo (object obj)
320                 {
321                         MethodSourceEntry method = (MethodSourceEntry) obj;
322
323                         if (method.StartRow < StartRow)
324                                 return -1;
325                         else if (method.StartRow > StartRow)
326                                 return 1;
327                         else
328                                 return 0;
329                 }
330
331                 public override string ToString ()
332                 {
333                         return String.Format ("MethodSourceEntry ({0}:{1}:{2}:{3})",
334                                               Index, FileOffset, StartRow, EndRow);
335                 }
336         }
337
338         public class MethodEntry
339         {
340                 #region This is actually written to the symbol file
341                 public readonly int SourceFileIndex;
342                 public readonly int Token;
343                 public readonly int StartRow;
344                 public readonly int EndRow;
345                 public readonly int ThisTypeIndex;
346                 public readonly int NumParameters;
347                 public readonly int NumLocals;
348                 public readonly int NumLineNumbers;
349
350                 int NameOffset;
351                 int FullNameOffset;
352                 int TypeIndexTableOffset;
353                 int LocalVariableTableOffset;
354                 int LineNumberTableOffset;
355                 #endregion
356
357                 int index;
358                 int file_offset;
359                 string name;
360                 string full_name;
361
362                 public readonly SourceFileEntry SourceFile;
363                 public readonly LineNumberEntry[] LineNumbers;
364                 public readonly int[] ParamTypeIndices;
365                 public readonly int[] LocalTypeIndices;
366                 public readonly LocalVariableEntry[] Locals;
367
368                 public static int Size
369                 {
370                         get {
371                                 return 48;
372                         }
373                 }
374
375                 public string Name {
376                         get { return name; }
377                 }
378
379                 public string FullName {
380                         get { return full_name; }
381                 }
382
383                 internal MethodEntry (MonoSymbolFile file, BinaryReader reader, int index)
384                 {
385                         this.index = index;
386                         SourceFileIndex = reader.ReadInt32 ();
387                         Token = reader.ReadInt32 ();
388                         StartRow = reader.ReadInt32 ();
389                         EndRow = reader.ReadInt32 ();
390                         ThisTypeIndex = reader.ReadInt32 ();
391                         NumParameters = reader.ReadInt32 ();
392                         NumLocals = reader.ReadInt32 ();
393                         NumLineNumbers = reader.ReadInt32 ();
394                         NameOffset = reader.ReadInt32 ();
395                         FullNameOffset = reader.ReadInt32 ();
396                         TypeIndexTableOffset = reader.ReadInt32 ();
397                         LocalVariableTableOffset = reader.ReadInt32 ();
398                         LineNumberTableOffset = reader.ReadInt32 ();
399
400                         name = file.ReadString (NameOffset);
401                         full_name = file.ReadString (FullNameOffset);
402
403                         SourceFile = file.GetSourceFile (SourceFileIndex);
404
405                         if (LineNumberTableOffset != 0) {
406                                 long old_pos = reader.BaseStream.Position;
407                                 reader.BaseStream.Position = LineNumberTableOffset;
408
409                                 LineNumbers = new LineNumberEntry [NumLineNumbers];
410
411                                 for (int i = 0; i < NumLineNumbers; i++)
412                                         LineNumbers [i] = new LineNumberEntry (reader);
413
414                                 reader.BaseStream.Position = old_pos;
415                         }
416
417                         if (LocalVariableTableOffset != 0) {
418                                 long old_pos = reader.BaseStream.Position;
419                                 reader.BaseStream.Position = LocalVariableTableOffset;
420
421                                 Locals = new LocalVariableEntry [NumLocals];
422
423                                 for (int i = 0; i < NumLocals; i++)
424                                         Locals [i] = new LocalVariableEntry (reader);
425
426                                 reader.BaseStream.Position = old_pos;
427                         }
428
429                         if (TypeIndexTableOffset != 0) {
430                                 long old_pos = reader.BaseStream.Position;
431                                 reader.BaseStream.Position = TypeIndexTableOffset;
432
433                                 ParamTypeIndices = new int [NumParameters];
434                                 LocalTypeIndices = new int [NumLocals];
435
436                                 for (int i = 0; i < NumParameters; i++)
437                                         ParamTypeIndices [i] = reader.ReadInt32 ();
438                                 for (int i = 0; i < NumLocals; i++)
439                                         LocalTypeIndices [i] = reader.ReadInt32 ();
440
441                                 reader.BaseStream.Position = old_pos;
442                         }
443                 }
444
445                 internal MethodEntry (MonoSymbolFile file, SourceFileEntry source, MethodBase method,
446                                       int token, LocalVariableEntry[] locals, LineNumberEntry[] lines,
447                                       int start_row, int end_row)
448                 {
449                         index = file.GetNextMethodIndex ();
450
451                         Token = token;
452                         SourceFileIndex = source.Index;
453                         SourceFile = source;
454                         StartRow = start_row;
455                         EndRow = end_row;
456
457                         LineNumbers = BuildLineNumberTable (lines);
458                         NumLineNumbers = LineNumbers.Length;
459
460                         ParameterInfo[] parameters = method.GetParameters ();
461                         if (parameters == null)
462                                 parameters = new ParameterInfo [0];
463
464                         StringBuilder sb = new StringBuilder ();
465                         sb.Append (method.DeclaringType.FullName);
466                         sb.Append (".");
467                         sb.Append (method.Name);
468                         sb.Append ("(");
469                         for (int i = 0; i < parameters.Length; i++) {
470                                 if (i > 0)
471                                         sb.Append (",");
472                                 sb.Append (parameters [i].ParameterType.FullName);
473                         }
474                         sb.Append (")");
475
476                         name = method.Name;
477                         full_name = sb.ToString ();
478
479                         NumParameters = parameters.Length;
480                         ParamTypeIndices = new int [NumParameters];
481                         for (int i = 0; i < NumParameters; i++)
482                                 ParamTypeIndices [i] = file.DefineType (parameters [i].ParameterType);
483
484                         NumLocals = locals.Length;
485                         Locals = locals;
486
487                         LocalTypeIndices = new int [NumLocals];
488                         for (int i = 0; i < NumLocals; i++)
489                                 LocalTypeIndices [i] = file.GetNextTypeIndex ();
490
491                         if (method.IsStatic)
492                                 ThisTypeIndex = 0;
493                         else
494                                 ThisTypeIndex = file.DefineType (method.ReflectedType);
495                 }
496
497                 LineNumberEntry[] BuildLineNumberTable (LineNumberEntry[] line_numbers)
498                 {
499                         Array.Sort (line_numbers, LineNumberEntry.OffsetComparer);
500
501                         ArrayList list = new ArrayList ();
502                         int last_offset = -1;
503                         int last_row = -1;
504
505                         for (int i = 0; i < line_numbers.Length; i++) {
506                                 LineNumberEntry line = (LineNumberEntry) line_numbers [i];
507
508                                 if (line.Offset > last_offset) {
509                                         if (last_row >= 0)
510                                                 list.Add (new LineNumberEntry (last_row, last_offset));
511                                         last_row = line.Row;
512                                         last_offset = line.Offset;
513                                 } else if (line.Row > last_row) {
514                                         last_row = line.Row;
515                                 }
516                         }
517
518                         if (last_row >= 0)
519                                 list.Add (new LineNumberEntry (last_row, last_offset));
520
521                         LineNumberEntry[] retval = new LineNumberEntry [list.Count];
522                         list.CopyTo (retval, 0);
523                         return retval;
524                 }
525
526                 internal MethodSourceEntry Write (MonoSymbolFile file, BinaryWriter bw)
527                 {
528                         NameOffset = (int) bw.BaseStream.Position;
529                         file.WriteString (bw, name);
530
531                         FullNameOffset = (int) bw.BaseStream.Position;
532                         file.WriteString (bw, full_name);
533
534                         TypeIndexTableOffset = (int) bw.BaseStream.Position;
535
536                         for (int i = 0; i < NumParameters; i++)
537                                 bw.Write (ParamTypeIndices [i]);
538                         for (int i = 0; i < NumLocals; i++)
539                                 bw.Write (LocalTypeIndices [i]);
540
541                         LocalVariableTableOffset = (int) bw.BaseStream.Position;
542
543                         for (int i = 0; i < NumLocals; i++)
544                                 Locals [i].Write (file, bw);
545
546                         LineNumberTableOffset = (int) bw.BaseStream.Position;
547
548                         for (int i = 0; i < NumLineNumbers; i++)
549                                 LineNumbers [i].Write (bw);
550
551                         file.LineNumberCount += NumLineNumbers;
552                         file.LocalCount += NumLocals;
553
554                         file_offset = (int) bw.BaseStream.Position;
555
556                         bw.Write (SourceFileIndex);
557                         bw.Write (Token);
558                         bw.Write (StartRow);
559                         bw.Write (EndRow);
560                         bw.Write (ThisTypeIndex);
561                         bw.Write (NumParameters);
562                         bw.Write (NumLocals);
563                         bw.Write (NumLineNumbers);
564                         bw.Write (NameOffset);
565                         bw.Write (FullNameOffset);
566                         bw.Write (TypeIndexTableOffset);
567                         bw.Write (LocalVariableTableOffset);
568                         bw.Write (LineNumberTableOffset);
569
570                         return new MethodSourceEntry (index, file_offset, StartRow, EndRow);
571                 }
572
573                 internal void WriteIndex (BinaryWriter bw)
574                 {
575                         bw.Write (file_offset);
576                         bw.Write (FullNameOffset);
577                 }
578
579                 public override string ToString ()
580                 {
581                         return String.Format ("[Method {0}:{1}:{2}:{3}:{4} - {7}:{8}:{9}:{10} - {5} - {6}]",
582                                               index, Token, SourceFileIndex, StartRow, EndRow,
583                                               SourceFile, FullName, ThisTypeIndex, NumParameters,
584                                               NumLocals, NumLineNumbers);
585                 }
586         }
587 }