New test.
[mono.git] / mcs / class / Mono.CompilerServices.SymbolWriter / MonoSymbolFile.cs
1 //
2 // Mono.CSharp.Debugger/MonoSymbolFile.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@ximian.com)
6 //
7 // (C) 2003 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.Reflection;
33 using SRE = System.Reflection.Emit;
34 using System.Collections;
35 using System.Text;
36 using System.Threading;
37 using System.IO;
38         
39 namespace Mono.CompilerServices.SymbolWriter
40 {
41         public class MonoSymbolFileException : Exception
42         {
43                 public MonoSymbolFileException ()
44                         : base ()
45                 { }
46
47                 public MonoSymbolFileException (string message, params object[] args)
48                         : base (String.Format (message, args))
49                 { }
50         }
51
52         internal class MyBinaryWriter : BinaryWriter
53         {
54                 public MyBinaryWriter (Stream stream)
55                         : base (stream)
56                 { }
57
58                 public void WriteLeb128 (int value)
59                 {
60                         base.Write7BitEncodedInt (value);
61                 }
62         }
63
64         internal class MyBinaryReader : BinaryReader
65         {
66                 public MyBinaryReader (Stream stream)
67                         : base (stream)
68                 { }
69
70                 public int ReadLeb128 ()
71                 {
72                         return base.Read7BitEncodedInt ();
73                 }
74         }
75         
76         internal class MonoDebuggerSupport
77         {
78                 static GetMethodTokenFunc get_method_token;
79                 static GetGuidFunc get_guid;
80                 static GetLocalIndexFunc get_local_index;
81
82                 delegate int GetMethodTokenFunc (MethodBase method);
83                 delegate Guid GetGuidFunc (Module module);
84                 delegate int GetLocalIndexFunc (SRE.LocalBuilder local);
85
86                 static Delegate create_delegate (Type type, Type delegate_type, string name)
87                 {
88                         MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
89                                                         BindingFlags.NonPublic);
90                         if (mi == null)
91                                 throw new Exception ("Can't find " + name);
92
93                         return Delegate.CreateDelegate (delegate_type, mi);
94                 }
95
96                 static MonoDebuggerSupport ()
97                 {
98                         get_method_token = (GetMethodTokenFunc) create_delegate (
99                                 typeof (Assembly), typeof (GetMethodTokenFunc),
100                                 "MonoDebugger_GetMethodToken");
101
102                         get_guid = (GetGuidFunc) create_delegate (
103                                 typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid");
104
105                         get_local_index = (GetLocalIndexFunc) create_delegate (
106                                 typeof (SRE.LocalBuilder), typeof (GetLocalIndexFunc),
107                                 "Mono_GetLocalIndex");
108                 }
109
110                 public static int GetMethodToken (MethodBase method)
111                 {
112                         return get_method_token (method);
113                 }
114
115                 public static Guid GetGuid (Module module)
116                 {
117                         return get_guid (module);
118                 }
119
120                 public static int GetLocalIndex (SRE.LocalBuilder local)
121                 {
122                         return get_local_index (local);
123                 }
124         }
125
126         public class MonoSymbolFile : IDisposable
127         {
128                 ArrayList methods = new ArrayList ();
129                 ArrayList sources = new ArrayList ();
130                 Hashtable method_source_hash = new Hashtable ();
131                 Hashtable type_hash = new Hashtable ();
132
133                 OffsetTable ot;
134                 int last_type_index;
135                 int last_method_index;
136                 int last_source_index;
137                 int last_namespace_index;
138
139                 public int NumLineNumbers;
140
141                 public MonoSymbolFile ()
142                 { }
143
144                 internal int AddSource (SourceFileEntry source)
145                 {
146                         sources.Add (source);
147                         return ++last_source_index;
148                 }
149
150                 internal int DefineType (Type type)
151                 {
152                         if (type_hash.Contains (type))
153                                 return (int) type_hash [type];
154
155                         int index = ++last_type_index;
156                         type_hash.Add (type, index);
157                         return index;
158                 }
159
160                 internal void AddMethod (MethodEntry entry)
161                 {
162                         methods.Add (entry);
163                 }
164
165                 internal int GetNextTypeIndex ()
166                 {
167                         return ++last_type_index;
168                 }
169
170                 internal int GetNextMethodIndex ()
171                 {
172                         return ++last_method_index;
173                 }
174
175                 internal int GetNextNamespaceIndex ()
176                 {
177                         return ++last_namespace_index;
178                 }
179                 
180                 internal string ReadString (int offset)
181                 {
182                         int old_pos = (int) reader.BaseStream.Position;
183                         reader.BaseStream.Position = offset;
184
185                         string text = reader.ReadString ();
186
187                         reader.BaseStream.Position = old_pos;
188                         return text;
189                 }
190
191                 void Write (MyBinaryWriter bw, Guid guid)
192                 {
193                         // Magic number and file version.
194                         bw.Write (OffsetTable.Magic);
195                         bw.Write (OffsetTable.Version);
196
197                         bw.Write (guid.ToByteArray ());
198
199                         //
200                         // Offsets of file sections; we must write this after we're done
201                         // writing the whole file, so we just reserve the space for it here.
202                         //
203                         long offset_table_offset = bw.BaseStream.Position;
204                         ot.Write (bw);
205
206                         //
207                         // Sort the methods according to their tokens and update their index.
208                         //
209                         methods.Sort ();
210                         for (int i = 0; i < methods.Count; i++)
211                                 ((MethodEntry) methods [i]).Index = i + 1;
212
213                         //
214                         // Write data sections.
215                         //
216                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
217                         foreach (SourceFileEntry source in sources)
218                                 source.WriteData (bw);
219                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
220
221                         //
222                         // Write the method index table.
223                         //
224                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
225                         for (int i = 0; i < methods.Count; i++) {
226                                 MethodEntry entry = (MethodEntry) methods [i];
227                                 entry.WriteIndex (bw);
228                         }
229                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
230
231                         //
232                         // Write source table.
233                         //
234                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
235                         for (int i = 0; i < sources.Count; i++) {
236                                 SourceFileEntry source = (SourceFileEntry) sources [i];
237                                 source.Write (bw);
238                         }
239                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
240
241                         //
242                         // Fixup offset table.
243                         //
244                         ot.TypeCount = last_type_index;
245                         ot.MethodCount = methods.Count;
246                         ot.SourceCount = sources.Count;
247
248                         //
249                         // Write offset table.
250                         //
251                         ot.TotalFileSize = (int) bw.BaseStream.Position;
252                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
253                         ot.Write (bw);
254                         bw.Seek (0, SeekOrigin.End);
255                 }
256
257                 public void CreateSymbolFile (Guid guid, FileStream fs)
258                 {
259                         if (reader != null)
260                                 throw new InvalidOperationException ();
261                         
262                         Write (new MyBinaryWriter (fs), guid);
263                 }
264
265                 Assembly assembly;
266                 MyBinaryReader reader;
267                 Hashtable method_hash;
268                 Hashtable source_file_hash;
269
270                 Hashtable method_token_hash;
271                 Hashtable source_name_hash;
272
273                 protected MonoSymbolFile (string filename, Assembly assembly)
274                 {
275                         this.assembly = assembly;
276
277                         FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
278                         reader = new MyBinaryReader (stream);
279
280                         Guid guid;
281
282                         try {
283                                 long magic = reader.ReadInt64 ();
284                                 long version = reader.ReadInt32 ();
285                                 if (magic != OffsetTable.Magic)
286                                         throw new MonoSymbolFileException (
287                                                 "Symbol file `{0}' is not a valid " +
288                                                 "Mono symbol file", filename);
289                                 if (version != OffsetTable.Version)
290                                         throw new MonoSymbolFileException (
291                                                 "Symbol file `{0}' has version {1}, " +
292                                                 "but expected {2}", filename, version,
293                                                 OffsetTable.Version);
294
295                                 guid = new Guid (reader.ReadBytes (16));
296
297                                 ot = new OffsetTable (reader);
298                         } catch {
299                                 throw new MonoSymbolFileException (
300                                         "Cannot read symbol file `{0}'", filename);
301                         }
302
303                         if (assembly != null) {
304                                 // Check that the MDB file matches the assembly, if we have been
305                                 // passed an assembly.
306                                 
307                                 Module[] modules = assembly.GetModules ();
308                                 Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]);
309         
310                                 if (guid != assembly_guid)
311                                         throw new MonoSymbolFileException (
312                                                 "Symbol file `{0}' does not match assembly `{1}'",
313                                                 filename, assembly.Location);
314                         }
315                         
316                         method_hash = new Hashtable ();
317                         source_file_hash = new Hashtable ();
318                 }
319
320                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
321                 {
322                         string filename = assembly.Location;
323                         string name = filename + ".mdb";
324
325                         return new MonoSymbolFile (name, assembly);
326                 }
327
328                 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
329                 {
330                         return new MonoSymbolFile (mdbFilename, null);
331                 }
332
333                 public Assembly Assembly {
334                         get { return assembly; }
335                 }
336
337                 public int SourceCount {
338                         get { return ot.SourceCount; }
339                 }
340
341                 public int MethodCount {
342                         get { return ot.MethodCount; }
343                 }
344
345                 public int TypeCount {
346                         get { return ot.TypeCount; }
347                 }
348
349                 public int NamespaceCount {
350                         get { return last_namespace_index; }
351                 }
352
353                 internal int LineNumberCount = 0;
354                 internal int LocalCount = 0;
355                 internal int StringSize = 0;
356
357                 public SourceFileEntry GetSourceFile (int index)
358                 {
359                         if ((index < 1) || (index > ot.SourceCount))
360                                 throw new ArgumentException ();
361                         if (reader == null)
362                                 throw new InvalidOperationException ();
363
364                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
365                         if (source != null)
366                                 return source;
367
368                         reader.BaseStream.Position = ot.SourceTableOffset +
369                                 SourceFileEntry.Size * (index - 1);
370                         source = new SourceFileEntry (this, reader);
371                         source_file_hash.Add (index, source);
372                         return source;
373                 }
374
375                 public SourceFileEntry[] Sources {
376                         get {
377                                 if (reader == null)
378                                         throw new InvalidOperationException ();
379
380                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
381                                 for (int i = 0; i < SourceCount; i++)
382                                         retval [i] = GetSourceFile (i + 1);
383                                 return retval;
384                         }
385                 }
386
387                 public MethodIndexEntry GetMethodIndexEntry (int index)
388                 {
389                         int old_pos = (int) reader.BaseStream.Position;
390                         reader.BaseStream.Position = ot.MethodTableOffset +
391                                 MethodIndexEntry.Size * (index - 1);
392                         MethodIndexEntry ie = new MethodIndexEntry (reader);
393                         reader.BaseStream.Position = old_pos;
394                         return ie;
395                 }
396
397                 public MethodEntry GetMethodByToken (int token)
398                 {
399                         if (reader == null)
400                                 throw new InvalidOperationException ();
401
402                         if (method_token_hash == null) {
403                                 method_token_hash = new Hashtable ();
404
405                                 for (int i = 0; i < MethodCount; i++) {
406                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
407
408                                         method_token_hash.Add (ie.Token, i + 1);
409                                 }
410                         }
411
412                         object value = method_token_hash [token];
413                         if (value == null)
414                                 return null;
415
416                         return GetMethod ((int) value);
417                 }
418
419                 public MethodEntry GetMethod (MethodBase method)
420                 {
421                         if (reader == null)
422                                 throw new InvalidOperationException ();
423                         int token = MonoDebuggerSupport.GetMethodToken (method);
424                         return GetMethodByToken (token);
425                 }
426
427                 public MethodEntry GetMethod (int index)
428                 {
429                         if ((index < 1) || (index > ot.MethodCount))
430                                 throw new ArgumentException ();
431                         if (reader == null)
432                                 throw new InvalidOperationException ();
433
434                         MethodEntry entry = (MethodEntry) method_hash [index];
435                         if (entry != null)
436                                 return entry;
437
438                         MethodIndexEntry ie = GetMethodIndexEntry (index);
439                         reader.BaseStream.Position = ie.FileOffset;
440
441                         entry = new MethodEntry (this, reader, index);
442                         method_hash.Add (index, entry);
443                         return entry;
444                 }
445
446                 public MethodEntry[] Methods {
447                         get {
448                                 if (reader == null)
449                                         throw new InvalidOperationException ();
450
451                                 MethodEntry[] retval = new MethodEntry [MethodCount];
452                                 for (int i = 0; i < MethodCount; i++)
453                                         retval [i] = GetMethod (i + 1);
454                                 return retval;
455                         }
456                 }
457
458                 public MethodSourceEntry GetMethodSource (int index)
459                 {
460                         if ((index < 1) || (index > ot.MethodCount))
461                                 throw new ArgumentException ();
462                         if (reader == null)
463                                 throw new InvalidOperationException ();
464
465                         object entry = method_source_hash [index];
466                         if (entry != null)
467                                 return (MethodSourceEntry) entry;
468
469                         MethodEntry method = GetMethod (index);
470                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
471                                 if (source.Index == index) {
472                                         method_source_hash.Add (index, source);
473                                         return source;
474                                 }
475                         }
476
477                         throw new MonoSymbolFileException ("Internal error.");
478                 }
479
480                 public int FindSource (string file_name)
481                 {
482                         if (reader == null)
483                                 throw new InvalidOperationException ();
484
485                         if (source_name_hash == null) {
486                                 source_name_hash = new Hashtable ();
487
488                                 for (int i = 0; i < ot.SourceCount; i++) {
489                                         SourceFileEntry source = GetSourceFile (i + 1);
490
491                                         source_name_hash.Add (source.FileName, i);
492                                 }
493                         }
494
495                         object value = source_name_hash [file_name];
496                         if (value == null)
497                                 return -1;
498                         return (int) value;
499                 }
500
501                 internal MyBinaryReader BinaryReader {
502                         get {
503                                 if (reader == null)
504                                         throw new InvalidOperationException ();
505
506                                 return reader;
507                         }
508                 }
509
510                 public void Dispose ()
511                 {
512                         Dispose (true);
513                 }
514
515                 protected virtual void Dispose (bool disposing)
516                 {
517                         if (disposing) {
518                                 if (reader != null) {
519                                         reader.Close ();
520                                         reader = null;
521                                 }
522                         }
523                 }
524         }
525 }