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