2 // Mono.CSharp.Debugger/MonoSymbolFile.cs
5 // Martin Baulig (martin@ximian.com)
7 // (C) 2003 Ximian, Inc. http://www.ximian.com
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
32 using System
.Reflection
;
33 using SRE
= System
.Reflection
.Emit
;
34 using System
.Collections
;
36 using System
.Threading
;
39 namespace Mono
.CompilerServices
.SymbolWriter
41 public class MonoSymbolFileException
: Exception
43 public MonoSymbolFileException ()
47 public MonoSymbolFileException (string message
, params object[] args
)
48 : base (String
.Format (message
, args
))
52 internal class MyBinaryWriter
: BinaryWriter
54 public MyBinaryWriter (Stream stream
)
58 public void WriteLeb128 (int value)
60 base.Write7BitEncodedInt (value);
64 internal class MyBinaryReader
: BinaryReader
66 public MyBinaryReader (Stream stream
)
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
;
87 public interface ISourceFile
89 SourceFileEntry Entry
{
94 public interface ICompileUnit
96 CompileUnitEntry Entry
{
101 public interface IMethodDef
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
);
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
);
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
;
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
);
210 internal void AddMethod (MethodEntry 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
,
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
);
231 internal void DefineAnonymousScope (int id
)
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
)
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
)
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.
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
];
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
];
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
];
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
)
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
);
369 Console
.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
370 "{3} methods.", NumLineNumbers
, LineNumberSize
,
371 ExtendedLineNumberSize
, methods
.Count
);
375 public void CreateSymbolFile (Guid guid
, FileStream fs
)
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
;
393 MonoSymbolFile (string filename
)
395 this.FileName
= filename
;
396 FileStream stream
= new FileStream (filename
, FileMode
.Open
, FileAccess
.Read
);
397 reader
= new MyBinaryReader (stream
);
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
);
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
)
439 throw new MonoSymbolFileException (
440 "Symbol file `{0}' does not match assembly `{1}'",
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
);
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)
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
);
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; }
514 public OffsetTable OffsetTable
{
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 ();
530 throw new InvalidOperationException ();
533 SourceFileEntry source
= (SourceFileEntry
) source_file_hash
[index
];
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
;
549 public SourceFileEntry
[] Sources
{
552 throw new InvalidOperationException ();
554 SourceFileEntry
[] retval
= new SourceFileEntry
[SourceCount
];
555 for (int i
= 0; i
< SourceCount
; i
++)
556 retval
[i
] = GetSourceFile (i
+ 1);
561 public CompileUnitEntry
GetCompileUnit (int index
)
563 if ((index
< 1) || (index
> ot
.CompileUnitCount
))
564 throw new ArgumentException ();
566 throw new InvalidOperationException ();
569 CompileUnitEntry unit
= (CompileUnitEntry
) compile_unit_hash
[index
];
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
;
585 public CompileUnitEntry
[] CompileUnits
{
588 throw new InvalidOperationException ();
590 CompileUnitEntry
[] retval
= new CompileUnitEntry
[CompileUnitCount
];
591 for (int i
= 0; i
< CompileUnitCount
; i
++)
592 retval
[i
] = GetCompileUnit (i
+ 1);
600 if (method_token_hash
!= null)
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
)
622 throw new InvalidOperationException ();
626 return (MethodEntry
) method_token_hash
[token
];
630 public MethodEntry
GetMethod (int index
)
632 if ((index
< 1) || (index
> ot
.MethodCount
))
633 throw new ArgumentException ();
635 throw new InvalidOperationException ();
639 return (MethodEntry
) method_list
[index
- 1];
643 public MethodEntry
[] Methods
{
646 throw new InvalidOperationException ();
650 MethodEntry
[] retval
= new MethodEntry
[MethodCount
];
651 method_list
.CopyTo (retval
, 0);
657 public int FindSource (string file_name
)
660 throw new InvalidOperationException ();
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
];
679 public AnonymousScopeEntry
GetAnonymousScope (int id
)
682 throw new InvalidOperationException ();
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
{
702 throw new InvalidOperationException ();
708 public void Dispose ()
713 protected virtual void Dispose (bool disposing
)
716 if (reader
!= null) {