Merge remote-tracking branch 'local/msvc-updates' into msvc-updates
[mono.git] / mcs / class / Mono.CompilerServices.SymbolWriter / MonoSymbolFile.cs
1 //
2 // MonoSymbolFile.cs
3 //
4 // Authors:
5 //   Martin Baulig (martin@ximian.com)
6 //   Marek Safar (marek.safar@gmail.com)
7 //
8 // (C) 2003 Ximian, Inc.  http://www.ximian.com
9 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
10 //
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Reflection;
34 using System.Collections.Generic;
35 using System.IO;
36         
37 namespace Mono.CompilerServices.SymbolWriter
38 {
39         public class MonoSymbolFileException : Exception
40         {
41                 public MonoSymbolFileException ()
42                         : base ()
43                 { }
44
45                 public MonoSymbolFileException (string message, params object[] args)
46                         : base (String.Format (message, args))
47                 {
48                 }
49
50                 public MonoSymbolFileException (string message, Exception innerException)
51                         : base (message, innerException)
52                 {
53                 }
54         }
55
56         internal class MyBinaryWriter : BinaryWriter
57         {
58                 public MyBinaryWriter (Stream stream)
59                         : base (stream)
60                 { }
61
62                 public void WriteLeb128 (int value)
63                 {
64                         base.Write7BitEncodedInt (value);
65                 }
66         }
67
68         internal class MyBinaryReader : BinaryReader
69         {
70                 public MyBinaryReader (Stream stream)
71                         : base (stream)
72                 { }
73
74                 public int ReadLeb128 ()
75                 {
76                         return base.Read7BitEncodedInt ();
77                 }
78
79                 public string ReadString (int offset)
80                 {
81                         long old_pos = BaseStream.Position;
82                         BaseStream.Position = offset;
83
84                         string text = ReadString ();
85
86                         BaseStream.Position = old_pos;
87                         return text;
88                 }
89         }
90
91         public interface ISourceFile
92         {
93                 SourceFileEntry Entry {
94                         get;
95                 }
96         }
97
98         public interface ICompileUnit
99         {
100                 CompileUnitEntry Entry {
101                         get;
102                 }
103         }
104
105         public interface IMethodDef
106         {
107                 string Name {
108                         get;
109                 }
110
111                 int Token {
112                         get;
113                 }
114         }
115
116         public class MonoSymbolFile : IDisposable
117         {
118                 List<MethodEntry> methods = new List<MethodEntry> ();
119                 List<SourceFileEntry> sources = new List<SourceFileEntry> ();
120                 List<CompileUnitEntry> comp_units = new List<CompileUnitEntry> ();
121                 Dictionary<int, AnonymousScopeEntry> anonymous_scopes;
122
123                 OffsetTable ot;
124                 int last_type_index;
125                 int last_method_index;
126                 int last_namespace_index;
127
128                 public readonly int MajorVersion = OffsetTable.MajorVersion;
129                 public readonly int MinorVersion = OffsetTable.MinorVersion;
130
131                 public int NumLineNumbers;
132
133                 internal MonoSymbolFile ()
134                 {
135                         ot = new OffsetTable ();
136                 }
137
138                 internal int AddSource (SourceFileEntry source)
139                 {
140                         sources.Add (source);
141                         return sources.Count;
142                 }
143
144                 internal int AddCompileUnit (CompileUnitEntry entry)
145                 {
146                         comp_units.Add (entry);
147                         return comp_units.Count;
148                 }
149
150                 internal void AddMethod (MethodEntry entry)
151                 {
152                         methods.Add (entry);
153                 }
154
155                 public MethodEntry DefineMethod (CompileUnitEntry comp_unit, int token,
156                                                  ScopeVariable[] scope_vars, LocalVariableEntry[] locals,
157                                                  LineNumberEntry[] lines, CodeBlockEntry[] code_blocks,
158                                                  string real_name, MethodEntry.Flags flags,
159                                                  int namespace_id)
160                 {
161                         if (reader != null)
162                                 throw new InvalidOperationException ();
163
164                         MethodEntry method = new MethodEntry (
165                                 this, comp_unit, token, scope_vars, locals, lines, code_blocks, 
166                                 real_name, flags, namespace_id);
167                         AddMethod (method);
168                         return method;
169                 }
170
171                 internal void DefineAnonymousScope (int id)
172                 {
173                         if (reader != null)
174                                 throw new InvalidOperationException ();
175
176                         if (anonymous_scopes == null)
177                                 anonymous_scopes = new Dictionary<int, AnonymousScopeEntry>  ();
178
179                         anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
180                 }
181
182                 internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
183                                                       CapturedVariable.CapturedKind kind)
184                 {
185                         if (reader != null)
186                                 throw new InvalidOperationException ();
187
188                         AnonymousScopeEntry scope = anonymous_scopes [scope_id];
189                         scope.AddCapturedVariable (name, captured_name, kind);
190                 }
191
192                 internal void DefineCapturedScope (int scope_id, int id, string captured_name)
193                 {
194                         if (reader != null)
195                                 throw new InvalidOperationException ();
196
197                         AnonymousScopeEntry scope = anonymous_scopes [scope_id];
198                         scope.AddCapturedScope (id, captured_name);
199                 }
200
201                 internal int GetNextTypeIndex ()
202                 {
203                         return ++last_type_index;
204                 }
205
206                 internal int GetNextMethodIndex ()
207                 {
208                         return ++last_method_index;
209                 }
210
211                 internal int GetNextNamespaceIndex ()
212                 {
213                         return ++last_namespace_index;
214                 }
215                 
216                 void Write (MyBinaryWriter bw, Guid guid)
217                 {
218                         // Magic number and file version.
219                         bw.Write (OffsetTable.Magic);
220                         bw.Write (MajorVersion);
221                         bw.Write (MinorVersion);
222
223                         bw.Write (guid.ToByteArray ());
224
225                         //
226                         // Offsets of file sections; we must write this after we're done
227                         // writing the whole file, so we just reserve the space for it here.
228                         //
229                         long offset_table_offset = bw.BaseStream.Position;
230                         ot.Write (bw, MajorVersion, MinorVersion);
231
232                         //
233                         // Sort the methods according to their tokens and update their index.
234                         //
235                         methods.Sort ();
236                         for (int i = 0; i < methods.Count; i++)
237                                 methods [i].Index = i + 1;
238
239                         //
240                         // Write data sections.
241                         //
242                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
243                         foreach (SourceFileEntry source in sources)
244                                 source.WriteData (bw);
245                         foreach (CompileUnitEntry comp_unit in comp_units)
246                                 comp_unit.WriteData (bw);
247                         foreach (MethodEntry method in methods)
248                                 method.WriteData (this, bw);
249                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
250
251                         //
252                         // Write the method index table.
253                         //
254                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
255                         for (int i = 0; i < methods.Count; i++) {
256                                 MethodEntry entry = methods [i];
257                                 entry.Write (bw);
258                         }
259                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
260
261                         //
262                         // Write source table.
263                         //
264                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
265                         for (int i = 0; i < sources.Count; i++) {
266                                 SourceFileEntry source = sources [i];
267                                 source.Write (bw);
268                         }
269                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
270
271                         //
272                         // Write compilation unit table.
273                         //
274                         ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
275                         for (int i = 0; i < comp_units.Count; i++) {
276                                 CompileUnitEntry unit = comp_units [i];
277                                 unit.Write (bw);
278                         }
279                         ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
280
281                         //
282                         // Write anonymous scope table.
283                         //
284                         ot.AnonymousScopeCount = anonymous_scopes != null ? anonymous_scopes.Count : 0;
285                         ot.AnonymousScopeTableOffset = (int) bw.BaseStream.Position;
286                         if (anonymous_scopes != null) {
287                                 foreach (AnonymousScopeEntry scope in anonymous_scopes.Values)
288                                         scope.Write (bw);
289                         }
290                         ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
291
292                         //
293                         // Fixup offset table.
294                         //
295                         ot.TypeCount = last_type_index;
296                         ot.MethodCount = methods.Count;
297                         ot.SourceCount = sources.Count;
298                         ot.CompileUnitCount = comp_units.Count;
299
300                         //
301                         // Write offset table.
302                         //
303                         ot.TotalFileSize = (int) bw.BaseStream.Position;
304                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
305                         ot.Write (bw, MajorVersion, MinorVersion);
306                         bw.Seek (0, SeekOrigin.End);
307
308 #if false
309                         Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
310                                            "{3} methods.", NumLineNumbers, LineNumberSize,
311                                            ExtendedLineNumberSize, methods.Count);
312 #endif
313                 }
314
315                 public void CreateSymbolFile (Guid guid, FileStream fs)
316                 {
317                         if (reader != null)
318                                 throw new InvalidOperationException ();
319
320                         Write (new MyBinaryWriter (fs), guid);
321                 }
322
323                 MyBinaryReader reader;
324                 Dictionary<int, SourceFileEntry> source_file_hash;
325                 Dictionary<int, CompileUnitEntry> compile_unit_hash;
326
327                 List<MethodEntry> method_list;
328                 Dictionary<int, MethodEntry> method_token_hash;
329                 Dictionary<string, int> source_name_hash;
330
331                 Guid guid;
332
333                 MonoSymbolFile (Stream stream)
334                 {
335                         reader = new MyBinaryReader (stream);
336
337                         try {
338                                 long magic = reader.ReadInt64 ();
339                                 int major_version = reader.ReadInt32 ();
340                                 int minor_version = reader.ReadInt32 ();
341
342                                 if (magic != OffsetTable.Magic)
343                                         throw new MonoSymbolFileException ("Symbol file is not a valid");
344                                 if (major_version != OffsetTable.MajorVersion)
345                                         throw new MonoSymbolFileException (
346                                                 "Symbol file has version {0} but expected {1}", major_version, OffsetTable.MajorVersion);
347                                 if (minor_version != OffsetTable.MinorVersion)
348                                         throw new MonoSymbolFileException ("Symbol file has version {0}.{1} but expected {2}.{3}",
349                                                 major_version, minor_version,
350                                                 OffsetTable.MajorVersion, OffsetTable.MinorVersion);
351
352                                 MajorVersion = major_version;
353                                 MinorVersion = minor_version;
354                                 guid = new Guid (reader.ReadBytes (16));
355
356                                 ot = new OffsetTable (reader, major_version, minor_version);
357                         } catch (Exception e) {
358                                 throw new MonoSymbolFileException ("Cannot read symbol file", e);
359                         }
360
361                         source_file_hash = new Dictionary<int, SourceFileEntry> ();
362                         compile_unit_hash = new Dictionary<int, CompileUnitEntry> ();
363                 }
364
365                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
366                 {
367                         string filename = assembly.Location;
368                         string name = filename + ".mdb";
369
370                         Module[] modules = assembly.GetModules ();
371                         Guid assembly_guid = modules[0].ModuleVersionId;
372
373                         return ReadSymbolFile (name, assembly_guid);
374                 }
375
376                 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
377                 {
378                         return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read));
379                 }
380
381                 public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid)
382                 {
383                         var sf = ReadSymbolFile (mdbFilename);
384                         if (assemblyGuid != sf.guid)
385                                 throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename);
386
387                         return sf;
388                 }
389
390                 public static MonoSymbolFile ReadSymbolFile (Stream stream)
391                 {
392                         return new MonoSymbolFile (stream);
393                 }
394
395                 public int CompileUnitCount {
396                         get { return ot.CompileUnitCount; }
397                 }
398
399                 public int SourceCount {
400                         get { return ot.SourceCount; }
401                 }
402
403                 public int MethodCount {
404                         get { return ot.MethodCount; }
405                 }
406
407                 public int TypeCount {
408                         get { return ot.TypeCount; }
409                 }
410
411                 public int AnonymousScopeCount {
412                         get { return ot.AnonymousScopeCount; }
413                 }
414
415                 public int NamespaceCount {
416                         get { return last_namespace_index; }
417                 }
418
419                 public Guid Guid {
420                         get { return guid; }
421                 }
422
423                 public OffsetTable OffsetTable {
424                         get { return ot; }
425                 }
426
427                 internal int LineNumberCount = 0;
428                 internal int LocalCount = 0;
429                 internal int StringSize = 0;
430
431                 internal int LineNumberSize = 0;
432                 internal int ExtendedLineNumberSize = 0;
433
434                 public SourceFileEntry GetSourceFile (int index)
435                 {
436                         if ((index < 1) || (index > ot.SourceCount))
437                                 throw new ArgumentException ();
438                         if (reader == null)
439                                 throw new InvalidOperationException ();
440
441                         lock (this) {
442                                 SourceFileEntry source;
443                                 if (source_file_hash.TryGetValue (index, out source))
444                                         return source;
445
446                                 long old_pos = reader.BaseStream.Position;
447
448                                 reader.BaseStream.Position = ot.SourceTableOffset +
449                                         SourceFileEntry.Size * (index - 1);
450                                 source = new SourceFileEntry (this, reader);
451                                 source_file_hash.Add (index, source);
452
453                                 reader.BaseStream.Position = old_pos;
454                                 return source;
455                         }
456                 }
457
458                 public SourceFileEntry[] Sources {
459                         get {
460                                 if (reader == null)
461                                         throw new InvalidOperationException ();
462
463                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
464                                 for (int i = 0; i < SourceCount; i++)
465                                         retval [i] = GetSourceFile (i + 1);
466                                 return retval;
467                         }
468                 }
469
470                 public CompileUnitEntry GetCompileUnit (int index)
471                 {
472                         if ((index < 1) || (index > ot.CompileUnitCount))
473                                 throw new ArgumentException ();
474                         if (reader == null)
475                                 throw new InvalidOperationException ();
476
477                         lock (this) {
478                                 CompileUnitEntry unit;
479                                 if (compile_unit_hash.TryGetValue (index, out unit))
480                                         return unit;
481
482                                 long old_pos = reader.BaseStream.Position;
483
484                                 reader.BaseStream.Position = ot.CompileUnitTableOffset +
485                                         CompileUnitEntry.Size * (index - 1);
486                                 unit = new CompileUnitEntry (this, reader);
487                                 compile_unit_hash.Add (index, unit);
488
489                                 reader.BaseStream.Position = old_pos;
490                                 return unit;
491                         }
492                 }
493
494                 public CompileUnitEntry[] CompileUnits {
495                         get {
496                                 if (reader == null)
497                                         throw new InvalidOperationException ();
498
499                                 CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
500                                 for (int i = 0; i < CompileUnitCount; i++)
501                                         retval [i] = GetCompileUnit (i + 1);
502                                 return retval;
503                         }
504                 }
505
506                 void read_methods ()
507                 {
508                         lock (this) {
509                                 if (method_token_hash != null)
510                                         return;
511
512                                 method_token_hash = new Dictionary<int, MethodEntry> ();
513                                 method_list = new List<MethodEntry> ();
514
515                                 long old_pos = reader.BaseStream.Position;
516                                 reader.BaseStream.Position = ot.MethodTableOffset;
517
518                                 for (int i = 0; i < MethodCount; i++) {
519                                         MethodEntry entry = new MethodEntry (this, reader, i + 1);
520                                         method_token_hash.Add (entry.Token, entry);
521                                         method_list.Add (entry);
522                                 }
523
524                                 reader.BaseStream.Position = old_pos;
525                         }
526                 }
527
528                 public MethodEntry GetMethodByToken (int token)
529                 {
530                         if (reader == null)
531                                 throw new InvalidOperationException ();
532
533                         lock (this) {
534                                 read_methods ();
535                                 MethodEntry me;
536                                 method_token_hash.TryGetValue (token, out me);
537                                 return me;
538                         }
539                 }
540
541                 public MethodEntry GetMethod (int index)
542                 {
543                         if ((index < 1) || (index > ot.MethodCount))
544                                 throw new ArgumentException ();
545                         if (reader == null)
546                                 throw new InvalidOperationException ();
547
548                         lock (this) {
549                                 read_methods ();
550                                 return method_list [index - 1];
551                         }
552                 }
553
554                 public MethodEntry[] Methods {
555                         get {
556                                 if (reader == null)
557                                         throw new InvalidOperationException ();
558
559                                 lock (this) {
560                                         read_methods ();
561                                         MethodEntry[] retval = new MethodEntry [MethodCount];
562                                         method_list.CopyTo (retval, 0);
563                                         return retval;
564                                 }
565                         }
566                 }
567
568                 public int FindSource (string file_name)
569                 {
570                         if (reader == null)
571                                 throw new InvalidOperationException ();
572
573                         lock (this) {
574                                 if (source_name_hash == null) {
575                                         source_name_hash = new Dictionary<string, int> ();
576
577                                         for (int i = 0; i < ot.SourceCount; i++) {
578                                                 SourceFileEntry source = GetSourceFile (i + 1);
579                                                 source_name_hash.Add (source.FileName, i);
580                                         }
581                                 }
582
583                                 int value;
584                                 if (!source_name_hash.TryGetValue (file_name, out value))
585                                         return -1;
586                                 return value;
587                         }
588                 }
589
590                 public AnonymousScopeEntry GetAnonymousScope (int id)
591                 {
592                         if (reader == null)
593                                 throw new InvalidOperationException ();
594
595                         AnonymousScopeEntry scope;
596                         lock (this) {
597                                 if (anonymous_scopes != null) {
598                                         anonymous_scopes.TryGetValue (id, out scope);
599                                         return scope;
600                                 }
601
602                                 anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
603                                 reader.BaseStream.Position = ot.AnonymousScopeTableOffset;
604                                 for (int i = 0; i < ot.AnonymousScopeCount; i++) {
605                                         scope = new AnonymousScopeEntry (reader);
606                                         anonymous_scopes.Add (scope.ID, scope);
607                                 }
608
609                                 return anonymous_scopes [id];
610                         }
611                 }
612
613                 internal MyBinaryReader BinaryReader {
614                         get {
615                                 if (reader == null)
616                                         throw new InvalidOperationException ();
617
618                                 return reader;
619                         }
620                 }
621
622                 public void Dispose ()
623                 {
624                         Dispose (true);
625                 }
626
627                 protected virtual void Dispose (bool disposing)
628                 {
629                         if (disposing) {
630                                 if (reader != null) {
631                                         reader.Close ();
632                                         reader = null;
633                                 }
634                         }
635                 }
636         }
637 }