2010-03-02 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Mono.CompilerServices.SymbolWriter / MonoSymbolFile.cs
blob3f11a0f56c7a77a68fb31c7bc77615e83c6b0466
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 //
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.
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;
39 namespace Mono.CompilerServices.SymbolWriter
41 public class MonoSymbolFileException : Exception
43 public MonoSymbolFileException ()
44 : base ()
45 { }
47 public MonoSymbolFileException (string message, params object[] args)
48 : base (String.Format (message, args))
49 { }
52 internal class MyBinaryWriter : BinaryWriter
54 public MyBinaryWriter (Stream stream)
55 : base (stream)
56 { }
58 public void WriteLeb128 (int value)
60 base.Write7BitEncodedInt (value);
64 internal class MyBinaryReader : BinaryReader
66 public MyBinaryReader (Stream stream)
67 : base (stream)
68 { }
70 public int ReadLeb128 ()
72 return base.Read7BitEncodedInt ();
75 public string ReadString (int offset)
77 long old_pos = BaseStream.Position;
78 BaseStream.Position = offset;
80 string text = ReadString ();
82 BaseStream.Position = old_pos;
83 return text;
87 public interface ISourceFile
89 SourceFileEntry Entry {
90 get;
94 public interface ICompileUnit
96 CompileUnitEntry Entry {
97 get;
101 public interface IMethodDef
103 string Name {
104 get;
107 int Token {
108 get;
112 #if !CECIL
113 internal class MonoDebuggerSupport
115 static GetMethodTokenFunc get_method_token;
116 static GetGuidFunc get_guid;
117 static GetLocalIndexFunc get_local_index;
119 delegate int GetMethodTokenFunc (MethodBase method);
120 delegate Guid GetGuidFunc (Module module);
121 delegate int GetLocalIndexFunc (SRE.LocalBuilder local);
123 static Delegate create_delegate (Type type, Type delegate_type, string name)
125 MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
126 BindingFlags.NonPublic);
127 if (mi == null)
128 throw new Exception ("Can't find " + name);
130 return Delegate.CreateDelegate (delegate_type, mi);
133 static MonoDebuggerSupport ()
135 get_method_token = (GetMethodTokenFunc) create_delegate (
136 typeof (Assembly), typeof (GetMethodTokenFunc),
137 "MonoDebugger_GetMethodToken");
139 get_guid = (GetGuidFunc) create_delegate (
140 typeof (Module), typeof (GetGuidFunc), "Mono_GetGuid");
142 get_local_index = (GetLocalIndexFunc) create_delegate (
143 typeof (SRE.LocalBuilder), typeof (GetLocalIndexFunc),
144 "Mono_GetLocalIndex");
147 public static int GetMethodToken (MethodBase method)
149 return get_method_token (method);
152 public static Guid GetGuid (Module module)
154 return get_guid (module);
157 public static int GetLocalIndex (SRE.LocalBuilder local)
159 return get_local_index (local);
162 #endif
164 public class MonoSymbolFile : IDisposable
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;
172 OffsetTable ot;
173 int last_type_index;
174 int last_method_index;
175 int last_namespace_index;
177 public readonly string FileName = "<dynamic>";
178 public readonly int MajorVersion = OffsetTable.MajorVersion;
179 public readonly int MinorVersion = OffsetTable.MinorVersion;
181 public int NumLineNumbers;
183 internal MonoSymbolFile ()
185 ot = new OffsetTable ();
188 internal int AddSource (SourceFileEntry source)
190 sources.Add (source);
191 return sources.Count;
194 internal int AddCompileUnit (CompileUnitEntry entry)
196 comp_units.Add (entry);
197 return comp_units.Count;
200 internal int DefineType (Type type)
202 if (type_hash.Contains (type))
203 return (int) type_hash [type];
205 int index = ++last_type_index;
206 type_hash.Add (type, index);
207 return index;
210 internal void AddMethod (MethodEntry entry)
212 methods.Add (entry);
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)
221 if (reader != null)
222 throw new InvalidOperationException ();
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;
231 internal void DefineAnonymousScope (int id)
233 if (reader != null)
234 throw new InvalidOperationException ();
236 if (anonymous_scopes == null)
237 anonymous_scopes = new Hashtable ();
239 anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
242 internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
243 CapturedVariable.CapturedKind kind)
245 if (reader != null)
246 throw new InvalidOperationException ();
248 AnonymousScopeEntry scope = (AnonymousScopeEntry) anonymous_scopes [scope_id];
249 scope.AddCapturedVariable (name, captured_name, kind);
252 internal void DefineCapturedScope (int scope_id, int id, string captured_name)
254 if (reader != null)
255 throw new InvalidOperationException ();
257 AnonymousScopeEntry scope = (AnonymousScopeEntry) anonymous_scopes [scope_id];
258 scope.AddCapturedScope (id, captured_name);
261 internal int GetNextTypeIndex ()
263 return ++last_type_index;
266 internal int GetNextMethodIndex ()
268 return ++last_method_index;
271 internal int GetNextNamespaceIndex ()
273 return ++last_namespace_index;
276 void Write (MyBinaryWriter bw, Guid guid)
278 // Magic number and file version.
279 bw.Write (OffsetTable.Magic);
280 bw.Write (MajorVersion);
281 bw.Write (MinorVersion);
283 bw.Write (guid.ToByteArray ());
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.
289 long offset_table_offset = bw.BaseStream.Position;
290 ot.Write (bw, MajorVersion, MinorVersion);
293 // Sort the methods according to their tokens and update their index.
295 methods.Sort ();
296 for (int i = 0; i < methods.Count; i++)
297 ((MethodEntry) methods [i]).Index = i + 1;
300 // Write data sections.
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;
312 // Write the method index table.
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);
319 ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
322 // Write source table.
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);
329 ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
332 // Write compilation unit table.
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);
339 ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
342 // Write anonymous scope table.
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);
350 ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
353 // Fixup offset table.
355 ot.TypeCount = last_type_index;
356 ot.MethodCount = methods.Count;
357 ot.SourceCount = sources.Count;
358 ot.CompileUnitCount = comp_units.Count;
361 // Write offset table.
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);
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
375 public void CreateSymbolFile (Guid guid, FileStream fs)
377 if (reader != null)
378 throw new InvalidOperationException ();
380 Write (new MyBinaryWriter (fs), guid);
383 MyBinaryReader reader;
384 Hashtable source_file_hash;
385 Hashtable compile_unit_hash;
387 ArrayList method_list;
388 Hashtable method_token_hash;
389 Hashtable source_name_hash;
391 Guid guid;
393 MonoSymbolFile (string filename)
395 this.FileName = filename;
396 FileStream stream = new FileStream (filename, FileMode.Open, FileAccess.Read);
397 reader = new MyBinaryReader (stream);
399 try {
400 long magic = reader.ReadInt64 ();
401 int major_version = reader.ReadInt32 ();
402 int minor_version = reader.ReadInt32 ();
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);
420 MajorVersion = major_version;
421 MinorVersion = minor_version;
422 guid = new Guid (reader.ReadBytes (16));
424 ot = new OffsetTable (reader, major_version, minor_version);
425 } catch {
426 throw new MonoSymbolFileException (
427 "Cannot read symbol file `{0}'", filename);
430 source_file_hash = new Hashtable ();
431 compile_unit_hash = new Hashtable ();
434 void CheckGuidMatch (Guid other, string filename, string assembly)
436 if (other == guid)
437 return;
439 throw new MonoSymbolFileException (
440 "Symbol file `{0}' does not match assembly `{1}'",
441 filename, assembly);
444 #if CECIL
445 protected MonoSymbolFile (string filename, Mono.Cecil.AssemblyDefinition assembly) : this (filename)
447 Guid mvid = assembly.MainModule.Mvid;
449 CheckGuidMatch (mvid, filename, assembly.MainModule.Image.FileInformation.FullName);
452 public static MonoSymbolFile ReadSymbolFile (Mono.Cecil.AssemblyDefinition assembly, string filename)
454 string name = filename + ".mdb";
456 return new MonoSymbolFile (name, assembly);
458 #else
459 protected MonoSymbolFile (string filename, Assembly assembly) : this (filename)
461 // Check that the MDB file matches the assembly, if we have been
462 // passed an assembly.
463 if (assembly == null)
464 return;
466 Module[] modules = assembly.GetModules ();
467 Guid assembly_guid = MonoDebuggerSupport.GetGuid (modules [0]);
469 CheckGuidMatch (assembly_guid, filename, assembly.Location);
472 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
474 string filename = assembly.Location;
475 string name = filename + ".mdb";
477 return new MonoSymbolFile (name, assembly);
479 #endif
481 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
483 return new MonoSymbolFile (mdbFilename, null);
486 public int CompileUnitCount {
487 get { return ot.CompileUnitCount; }
490 public int SourceCount {
491 get { return ot.SourceCount; }
494 public int MethodCount {
495 get { return ot.MethodCount; }
498 public int TypeCount {
499 get { return ot.TypeCount; }
502 public int AnonymousScopeCount {
503 get { return ot.AnonymousScopeCount; }
506 public int NamespaceCount {
507 get { return last_namespace_index; }
510 public Guid Guid {
511 get { return guid; }
514 public OffsetTable OffsetTable {
515 get { return ot; }
518 internal int LineNumberCount = 0;
519 internal int LocalCount = 0;
520 internal int StringSize = 0;
522 internal int LineNumberSize = 0;
523 internal int ExtendedLineNumberSize = 0;
525 public SourceFileEntry GetSourceFile (int index)
527 if ((index < 1) || (index > ot.SourceCount))
528 throw new ArgumentException ();
529 if (reader == null)
530 throw new InvalidOperationException ();
532 lock (this) {
533 SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
534 if (source != null)
535 return source;
537 long old_pos = reader.BaseStream.Position;
539 reader.BaseStream.Position = ot.SourceTableOffset +
540 SourceFileEntry.Size * (index - 1);
541 source = new SourceFileEntry (this, reader);
542 source_file_hash.Add (index, source);
544 reader.BaseStream.Position = old_pos;
545 return source;
549 public SourceFileEntry[] Sources {
550 get {
551 if (reader == null)
552 throw new InvalidOperationException ();
554 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
555 for (int i = 0; i < SourceCount; i++)
556 retval [i] = GetSourceFile (i + 1);
557 return retval;
561 public CompileUnitEntry GetCompileUnit (int index)
563 if ((index < 1) || (index > ot.CompileUnitCount))
564 throw new ArgumentException ();
565 if (reader == null)
566 throw new InvalidOperationException ();
568 lock (this) {
569 CompileUnitEntry unit = (CompileUnitEntry) compile_unit_hash [index];
570 if (unit != null)
571 return unit;
573 long old_pos = reader.BaseStream.Position;
575 reader.BaseStream.Position = ot.CompileUnitTableOffset +
576 CompileUnitEntry.Size * (index - 1);
577 unit = new CompileUnitEntry (this, reader);
578 compile_unit_hash.Add (index, unit);
580 reader.BaseStream.Position = old_pos;
581 return unit;
585 public CompileUnitEntry[] CompileUnits {
586 get {
587 if (reader == null)
588 throw new InvalidOperationException ();
590 CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
591 for (int i = 0; i < CompileUnitCount; i++)
592 retval [i] = GetCompileUnit (i + 1);
593 return retval;
597 void read_methods ()
599 lock (this) {
600 if (method_token_hash != null)
601 return;
603 method_token_hash = new Hashtable ();
604 method_list = new ArrayList ();
606 long old_pos = reader.BaseStream.Position;
607 reader.BaseStream.Position = ot.MethodTableOffset;
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);
615 reader.BaseStream.Position = old_pos;
619 public MethodEntry GetMethodByToken (int token)
621 if (reader == null)
622 throw new InvalidOperationException ();
624 lock (this) {
625 read_methods ();
626 return (MethodEntry) method_token_hash [token];
630 public MethodEntry GetMethod (int index)
632 if ((index < 1) || (index > ot.MethodCount))
633 throw new ArgumentException ();
634 if (reader == null)
635 throw new InvalidOperationException ();
637 lock (this) {
638 read_methods ();
639 return (MethodEntry) method_list [index - 1];
643 public MethodEntry[] Methods {
644 get {
645 if (reader == null)
646 throw new InvalidOperationException ();
648 lock (this) {
649 read_methods ();
650 MethodEntry[] retval = new MethodEntry [MethodCount];
651 method_list.CopyTo (retval, 0);
652 return retval;
657 public int FindSource (string file_name)
659 if (reader == null)
660 throw new InvalidOperationException ();
662 lock (this) {
663 if (source_name_hash == null) {
664 source_name_hash = new Hashtable ();
666 for (int i = 0; i < ot.SourceCount; i++) {
667 SourceFileEntry source = GetSourceFile (i + 1);
668 source_name_hash.Add (source.FileName, i);
672 object value = source_name_hash [file_name];
673 if (value == null)
674 return -1;
675 return (int) value;
679 public AnonymousScopeEntry GetAnonymousScope (int id)
681 if (reader == null)
682 throw new InvalidOperationException ();
684 lock (this) {
685 if (anonymous_scopes != null)
686 return (AnonymousScopeEntry) anonymous_scopes [id];
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);
695 return (AnonymousScopeEntry) anonymous_scopes [id];
699 internal MyBinaryReader BinaryReader {
700 get {
701 if (reader == null)
702 throw new InvalidOperationException ();
704 return reader;
708 public void Dispose ()
710 Dispose (true);
713 protected virtual void Dispose (bool disposing)
715 if (disposing) {
716 if (reader != null) {
717 reader.Close ();
718 reader = null;