* Makefile: Add reference to ICSharpCode.SharpZipLib.dll.
[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                 public string ReadString (int offset)
76                 {
77                         long old_pos = BaseStream.Position;
78                         BaseStream.Position = offset;
79
80                         string text = ReadString ();
81
82                         BaseStream.Position = old_pos;
83                         return text;
84                 }
85         }
86
87         public interface ISourceFile
88         {
89                 SourceFileEntry Entry {
90                         get;
91                 }
92         }
93
94         public interface ICompileUnit
95         {
96                 CompileUnitEntry Entry {
97                         get;
98                 }
99         }
100
101         public interface IMethodDef
102         {
103                 string Name {
104                         get;
105                 }
106
107                 int Token {
108                         get;
109                 }
110         }
111
112 #if !CECIL
113         internal class MonoDebuggerSupport
114         {
115                 static GetMethodTokenFunc get_method_token;
116                 static GetGuidFunc get_guid;
117                 static GetLocalIndexFunc get_local_index;
118
119                 delegate int GetMethodTokenFunc (MethodBase method);
120                 delegate Guid GetGuidFunc (Module module);
121                 delegate int GetLocalIndexFunc (SRE.LocalBuilder local);
122
123                 static Delegate create_delegate (Type type, Type delegate_type, string name)
124                 {
125                         MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
126                                                         BindingFlags.NonPublic);
127                         if (mi == null)
128                                 throw new Exception ("Can't find " + name);
129
130                         return Delegate.CreateDelegate (delegate_type, mi);
131                 }
132
133                 static MonoDebuggerSupport ()
134                 {
135                         get_method_token = (GetMethodTokenFunc) create_delegate (
136                                 typeof (Assembly), typeof (GetMethodTokenFunc),
137                                 "MonoDebugger_GetMethodToken");
138
139                         get_guid = (GetGuidFunc) create_delegate (
140                                 typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid");
141
142                         get_local_index = (GetLocalIndexFunc) create_delegate (
143                                 typeof (SRE.LocalBuilder), typeof (GetLocalIndexFunc),
144                                 "Mono_GetLocalIndex");
145                 }
146
147                 public static int GetMethodToken (MethodBase method)
148                 {
149                         return get_method_token (method);
150                 }
151
152                 public static Guid GetGuid (Module module)
153                 {
154                         return get_guid (module);
155                 }
156
157                 public static int GetLocalIndex (SRE.LocalBuilder local)
158                 {
159                         return get_local_index (local);
160                 }
161         }
162 #endif
163
164         public class MonoSymbolFile : IDisposable
165         {
166                 ArrayList methods = new ArrayList ();
167                 ArrayList sources = new ArrayList ();
168                 ArrayList comp_units = new ArrayList ();
169                 Hashtable type_hash = new Hashtable ();
170                 Hashtable anonymous_scopes;
171
172                 OffsetTable ot;
173                 int last_type_index;
174                 int last_method_index;
175                 int last_namespace_index;
176
177                 public readonly string FileName = "<dynamic>";
178                 public readonly int MajorVersion = OffsetTable.MajorVersion;
179                 public readonly int MinorVersion = OffsetTable.MinorVersion;
180
181                 public int NumLineNumbers;
182
183                 internal MonoSymbolFile ()
184                 {
185                         ot = new OffsetTable ();
186                 }
187
188                 internal int AddSource (SourceFileEntry source)
189                 {
190                         sources.Add (source);
191                         return sources.Count;
192                 }
193
194                 internal int AddCompileUnit (CompileUnitEntry entry)
195                 {
196                         comp_units.Add (entry);
197                         return comp_units.Count;
198                 }
199
200                 internal int DefineType (Type type)
201                 {
202                         if (type_hash.Contains (type))
203                                 return (int) type_hash [type];
204
205                         int index = ++last_type_index;
206                         type_hash.Add (type, index);
207                         return index;
208                 }
209
210                 internal void AddMethod (MethodEntry entry)
211                 {
212                         methods.Add (entry);
213                 }
214
215                 public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token,
216                                                  ScopeVariable[] scope_vars, LocalVariableEntry[] locals,
217                                                  LineNumberEntry[] lines, CodeBlockEntry[] code_blocks,
218                                                  string real_name, MethodEntry.Flags flags,
219                                                  int namespace_id)
220                 {
221                         if (reader != null)
222                                 throw new InvalidOperationException ();
223
224                         MethodEntry method = new MethodEntry (
225                                 this, comp_unit, token, scope_vars, locals, lines, code_blocks, 
226                                 real_name, flags, namespace_id);
227                         AddMethod (method);
228                         return method;
229                 }
230
231                 internal void DefineAnonymousScope (int id)
232                 {
233                         if (reader != null)
234                                 throw new InvalidOperationException ();
235
236                         if (anonymous_scopes == null)
237                                 anonymous_scopes = new Hashtable ();
238
239                         anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
240                 }
241
242                 internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
243                                                       CapturedVariable.CapturedKind kind)
244                 {
245                         if (reader != null)
246                                 throw new InvalidOperationException ();
247
248                         AnonymousScopeEntry scope = (AnonymousScopeEntry) anonymous_scopes [scope_id];
249                         scope.AddCapturedVariable (name, captured_name, kind);
250                 }
251
252                 internal void DefineCapturedScope (int scope_id, int id, string captured_name)
253                 {
254                         if (reader != null)
255                                 throw new InvalidOperationException ();
256
257                         AnonymousScopeEntry scope = (AnonymousScopeEntry) anonymous_scopes [scope_id];
258                         scope.AddCapturedScope (id, captured_name);
259                 }
260
261                 internal int GetNextTypeIndex ()
262                 {
263                         return ++last_type_index;
264                 }
265
266                 internal int GetNextMethodIndex ()
267                 {
268                         return ++last_method_index;
269                 }
270
271                 internal int GetNextNamespaceIndex ()
272                 {
273                         return ++last_namespace_index;
274                 }
275                 
276                 void Write (MyBinaryWriter bw, Guid guid)
277                 {
278                         // Magic number and file version.
279                         bw.Write (OffsetTable.Magic);
280                         bw.Write (MajorVersion);
281                         bw.Write (MinorVersion);
282
283                         bw.Write (guid.ToByteArray ());
284
285                         //
286                         // Offsets of file sections; we must write this after we're done
287                         // writing the whole file, so we just reserve the space for it here.
288                         //
289                         long offset_table_offset = bw.BaseStream.Position;
290                         ot.Write (bw, MajorVersion, MinorVersion);
291
292                         //
293                         // Sort the methods according to their tokens and update their index.
294                         //
295                         methods.Sort ();
296                         for (int i = 0; i < methods.Count; i++)
297                                 ((MethodEntry) methods [i]).Index = i + 1;
298
299                         //
300                         // Write data sections.
301                         //
302                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
303                         foreach (SourceFileEntry source in sources)
304                                 source.WriteData (bw);
305                         foreach (CompileUnitEntry comp_unit in comp_units)
306                                 comp_unit.WriteData (bw);
307                         foreach (MethodEntry method in methods)
308                                 method.WriteData (this, bw);
309                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
310
311                         //
312                         // Write the method index table.
313                         //
314                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
315                         for (int i = 0; i < methods.Count; i++) {
316                                 MethodEntry entry = (MethodEntry) methods [i];
317                                 entry.Write (bw);
318                         }
319                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
320
321                         //
322                         // Write source table.
323                         //
324                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
325                         for (int i = 0; i < sources.Count; i++) {
326                                 SourceFileEntry source = (SourceFileEntry) sources [i];
327                                 source.Write (bw);
328                         }
329                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
330
331                         //
332                         // Write compilation unit table.
333                         //
334                         ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
335                         for (int i = 0; i < comp_units.Count; i++) {
336                                 CompileUnitEntry unit = (CompileUnitEntry) comp_units [i];
337                                 unit.Write (bw);
338                         }
339                         ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
340
341                         //
342                         // Write anonymous scope table.
343                         //
344                         ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0;
345                         ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position;
346                         if (anonymous_scopes != null) {
347                                 foreach (AnonymousScopeEntry scope in anonymous_scopes.Values)
348                                         scope.Write (bw);
349                         }
350                         ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
351
352                         //
353                         // Fixup offset table.
354                         //
355                         ot.TypeCount = last_type_index;
356                         ot.MethodCount = methods.Count;
357                         ot.SourceCount = sources.Count;
358                         ot.CompileUnitCount = comp_units.Count;
359
360                         //
361                         // Write offset table.
362                         //
363                         ot.TotalFileSize = (int) bw.BaseStream.Position;
364                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
365                         ot.Write (bw, MajorVersion, MinorVersion);
366                         bw.Seek (0, SeekOrigin.End);
367
368 #if false
369                         Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
370                                            "{3} methods.", NumLineNumbers, LineNumberSize,
371                                            ExtendedLineNumberSize, methods.Count);
372 #endif
373                 }
374
375                 public void CreateSymbolFile (Guid guid, FileStream fs)
376                 {
377                         if (reader != null)
378                                 throw new InvalidOperationException ();
379
380                         Write (new MyBinaryWriter (fs), guid);
381                 }
382
383                 MyBinaryReader reader;
384                 Hashtable source_file_hash;
385                 Hashtable compile_unit_hash;
386
387                 ArrayList method_list;
388                 Hashtable method_token_hash;
389                 Hashtable source_name_hash;
390
391                 Guid guid;
392
393                 MonoSymbolFile (string filename)
394                 {
395                         this.FileName = filename;
396                         FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
397                         reader = new MyBinaryReader (stream);
398
399                         try {
400                                 long magic = reader.ReadInt64 ();
401                                 int major_version = reader.ReadInt32 ();
402                                 int minor_version = reader.ReadInt32 ();
403
404                                 if (magic != OffsetTable.Magic)
405                                         throw new MonoSymbolFileException (
406                                                 "Symbol file `{0}' is not a valid " +
407                                                 "Mono symbol file", filename);
408                                 if (major_version != OffsetTable.MajorVersion)
409                                         throw new MonoSymbolFileException (
410                                                 "Symbol file `{0}' has version {1}, " +
411                                                 "but expected {2}", filename, major_version,
412                                                 OffsetTable.MajorVersion);
413                                 if (minor_version != OffsetTable.MinorVersion)
414                                         throw new MonoSymbolFileException (
415                                                 "Symbol file `{0}' has version {1}.{2}, " +
416                                                 "but expected {3}.{4}", filename, major_version,
417                                                 minor_version, OffsetTable.MajorVersion,
418                                                 OffsetTable.MinorVersion);
419
420                                 MajorVersion = major_version;
421                                 MinorVersion = minor_version;
422                                 guid = new Guid (reader.ReadBytes (16));
423
424                                 ot = new OffsetTable (reader, major_version, minor_version);
425                         } catch {
426                                 throw new MonoSymbolFileException (
427                                         "Cannot read symbol file `{0}'", filename);
428                         }
429
430                         source_file_hash = new Hashtable ();
431                         compile_unit_hash = new Hashtable ();
432                 }
433
434                 void CheckGuidMatch (Guid other, string filename, string assembly)
435                 {
436                         if (other == guid)
437                                 return;
438
439                         throw new MonoSymbolFileException (
440                                 "Symbol file `{0}' does not match assembly `{1}'",
441                                 filename, assembly);
442                 }
443
444 #if CECIL
445                 protected MonoSymbolFile (string filename, Mono.Cecil.AssemblyDefinition assembly) : this (filename)
446                 {
447                         Guid mvid = assembly.MainModule.Mvid;
448
449                         CheckGuidMatch (mvid, filename, assembly.MainModule.Image.FileInformation.FullName);
450                 }
451
452                 public static MonoSymbolFile ReadSymbolFile (Mono.Cecil.AssemblyDefinition assembly, string filename)
453                 {
454                         string name = filename + ".mdb";
455
456                         return new MonoSymbolFile (name, assembly);
457                 }
458 #else
459                 protected MonoSymbolFile (string filename, Assembly assembly) : this (filename)
460                 {
461                         // Check that the MDB file matches the assembly, if we have been
462                         // passed an assembly.
463                         if (assembly == null)
464                                 return;
465                         
466                         Module[] modules = assembly.GetModules ();
467                         Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]);
468
469                         CheckGuidMatch (assembly_guid, filename, assembly.Location);
470                 }
471
472                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
473                 {
474                         string filename = assembly.Location;
475                         string name = filename + ".mdb";
476
477                         return new MonoSymbolFile (name, assembly);
478                 }
479 #endif
480
481                 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
482                 {
483                         return new MonoSymbolFile (mdbFilename, null);
484                 }
485
486                 public int CompileUnitCount {
487                         get { return ot.CompileUnitCount; }
488                 }
489
490                 public int SourceCount {
491                         get { return ot.SourceCount; }
492                 }
493
494                 public int MethodCount {
495                         get { return ot.MethodCount; }
496                 }
497
498                 public int TypeCount {
499                         get { return ot.TypeCount; }
500                 }
501
502                 public int AnonymousScopeCount {
503                         get { return ot.AnonymousScopeCount; }
504                 }
505
506                 public int NamespaceCount {
507                         get { return last_namespace_index; }
508                 }
509
510                 public Guid Guid {
511                         get { return guid; }
512                 }
513
514                 public OffsetTable OffsetTable {
515                         get { return ot; }
516                 }
517
518                 internal int LineNumberCount = 0;
519                 internal int LocalCount = 0;
520                 internal int StringSize = 0;
521
522                 internal int LineNumberSize = 0;
523                 internal int ExtendedLineNumberSize = 0;
524
525                 public SourceFileEntry GetSourceFile (int index)
526                 {
527                         if ((index < 1) || (index > ot.SourceCount))
528                                 throw new ArgumentException ();
529                         if (reader == null)
530                                 throw new InvalidOperationException ();
531
532                         lock (this) {
533                                 SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
534                                 if (source != null)
535                                         return source;
536
537                                 long old_pos = reader.BaseStream.Position;
538
539                                 reader.BaseStream.Position = ot.SourceTableOffset +
540                                         SourceFileEntry.Size * (index - 1);
541                                 source = new SourceFileEntry (this, reader);
542                                 source_file_hash.Add (index, source);
543
544                                 reader.BaseStream.Position = old_pos;
545                                 return source;
546                         }
547                 }
548
549                 public SourceFileEntry[] Sources {
550                         get {
551                                 if (reader == null)
552                                         throw new InvalidOperationException ();
553
554                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
555                                 for (int i = 0; i < SourceCount; i++)
556                                         retval [i] = GetSourceFile (i + 1);
557                                 return retval;
558                         }
559                 }
560
561                 public CompileUnitEntry GetCompileUnit (int index)
562                 {
563                         if ((index < 1) || (index > ot.CompileUnitCount))
564                                 throw new ArgumentException ();
565                         if (reader == null)
566                                 throw new InvalidOperationException ();
567
568                         lock (this) {
569                                 CompileUnitEntry unit = (CompileUnitEntry) compile_unit_hash [index];
570                                 if (unit != null)
571                                         return unit;
572
573                                 long old_pos = reader.BaseStream.Position;
574
575                                 reader.BaseStream.Position = ot.CompileUnitTableOffset +
576                                         CompileUnitEntry.Size * (index - 1);
577                                 unit = new CompileUnitEntry (this, reader);
578                                 compile_unit_hash.Add (index, unit);
579
580                                 reader.BaseStream.Position = old_pos;
581                                 return unit;
582                         }
583                 }
584
585                 public CompileUnitEntry[] CompileUnits {
586                         get {
587                                 if (reader == null)
588                                         throw new InvalidOperationException ();
589
590                                 CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
591                                 for (int i = 0; i < CompileUnitCount; i++)
592                                         retval [i] = GetCompileUnit (i + 1);
593                                 return retval;
594                         }
595                 }
596
597                 void read_methods ()
598                 {
599                         lock (this) {
600                                 if (method_token_hash != null)
601                                         return;
602
603                                 method_token_hash = new Hashtable ();
604                                 method_list = new ArrayList ();
605
606                                 long old_pos = reader.BaseStream.Position;
607                                 reader.BaseStream.Position = ot.MethodTableOffset;
608
609                                 for (int i = 0; i < MethodCount; i++) {
610                                         MethodEntry entry = new MethodEntry (this, reader, i + 1);
611                                         method_token_hash.Add (entry.Token, entry);
612                                         method_list.Add (entry);
613                                 }
614
615                                 reader.BaseStream.Position = old_pos;
616                         }
617                 }
618
619                 public MethodEntry GetMethodByToken (int token)
620                 {
621                         if (reader == null)
622                                 throw new InvalidOperationException ();
623
624                         lock (this) {
625                                 read_methods ();
626                                 return (MethodEntry) method_token_hash [token];
627                         }
628                 }
629
630                 public MethodEntry GetMethod (int index)
631                 {
632                         if ((index < 1) || (index > ot.MethodCount))
633                                 throw new ArgumentException ();
634                         if (reader == null)
635                                 throw new InvalidOperationException ();
636
637                         lock (this) {
638                                 read_methods ();
639                                 return (MethodEntry) method_list [index - 1];
640                         }
641                 }
642
643                 public MethodEntry[] Methods {
644                         get {
645                                 if (reader == null)
646                                         throw new InvalidOperationException ();
647
648                                 lock (this) {
649                                         read_methods ();
650                                         MethodEntry[] retval = new MethodEntry [MethodCount];
651                                         method_list.CopyTo (retval, 0);
652                                         return retval;
653                                 }
654                         }
655                 }
656
657                 public int FindSource (string file_name)
658                 {
659                         if (reader == null)
660                                 throw new InvalidOperationException ();
661
662                         lock (this) {
663                                 if (source_name_hash == null) {
664                                         source_name_hash = new Hashtable ();
665
666                                         for (int i = 0; i < ot.SourceCount; i++) {
667                                                 SourceFileEntry source = GetSourceFile (i + 1);
668                                                 source_name_hash.Add (source.FileName, i);
669                                         }
670                                 }
671
672                                 object value = source_name_hash [file_name];
673                                 if (value == null)
674                                         return -1;
675                                 return (int) value;
676                         }
677                 }
678
679                 public AnonymousScopeEntry GetAnonymousScope (int id)
680                 {
681                         if (reader == null)
682                                 throw new InvalidOperationException ();
683
684                         lock (this) {
685                                 if (anonymous_scopes != null)
686                                         return (AnonymousScopeEntry) anonymous_scopes [id];
687
688                                 anonymous_scopes = new Hashtable ();
689                                 reader.BaseStream.Position = ot.AnonymousScopeTableOffset;
690                                 for (int i = 0; i < ot.AnonymousScopeCount; i++) {
691                                         AnonymousScopeEntry scope = new AnonymousScopeEntry (reader);
692                                         anonymous_scopes.Add (scope.ID, scope);
693                                 }
694
695                                 return (AnonymousScopeEntry) anonymous_scopes [id];
696                         }
697                 }
698
699                 internal MyBinaryReader BinaryReader {
700                         get {
701                                 if (reader == null)
702                                         throw new InvalidOperationException ();
703
704                                 return reader;
705                         }
706                 }
707
708                 public void Dispose ()
709                 {
710                         Dispose (true);
711                 }
712
713                 protected virtual void Dispose (bool disposing)
714                 {
715                         if (disposing) {
716                                 if (reader != null) {
717                                         reader.Close ();
718                                         reader = null;
719                                 }
720                         }
721                 }
722         }
723 }