5 // Martin Baulig (martin@ximian.com)
6 // Marek Safar (marek.safar@gmail.com)
8 // (C) 2003 Ximian, Inc. http://www.ximian.com
9 // Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Reflection;
34 using System.Collections.Generic;
37 namespace Mono.CompilerServices.SymbolWriter
39 public class MonoSymbolFileException : Exception
41 public MonoSymbolFileException ()
45 public MonoSymbolFileException (string message, params object[] args)
46 : base (String.Format (message, args))
50 public MonoSymbolFileException (string message, Exception innerException)
51 : base (message, innerException)
56 internal class MyBinaryWriter : BinaryWriter
58 public MyBinaryWriter (Stream stream)
62 public void WriteLeb128 (int value)
64 base.Write7BitEncodedInt (value);
68 internal class MyBinaryReader : BinaryReader
70 public MyBinaryReader (Stream stream)
74 public int ReadLeb128 ()
76 return base.Read7BitEncodedInt ();
79 public string ReadString (int offset)
81 long old_pos = BaseStream.Position;
82 BaseStream.Position = offset;
84 string text = ReadString ();
86 BaseStream.Position = old_pos;
91 public interface ISourceFile
93 SourceFileEntry Entry {
98 public interface ICompileUnit
100 CompileUnitEntry Entry {
105 public interface IMethodDef
116 public class MonoSymbolFile : IDisposable
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;
125 int last_method_index;
126 int last_namespace_index;
128 public readonly int MajorVersion = OffsetTable.MajorVersion;
129 public readonly int MinorVersion = OffsetTable.MinorVersion;
131 public int NumLineNumbers;
133 internal MonoSymbolFile ()
135 ot = new OffsetTable ();
138 internal int AddSource (SourceFileEntry source)
140 sources.Add (source);
141 return sources.Count;
144 internal int AddCompileUnit (CompileUnitEntry entry)
146 comp_units.Add (entry);
147 return comp_units.Count;
150 internal void AddMethod (MethodEntry entry)
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,
162 throw new InvalidOperationException ();
164 MethodEntry method = new MethodEntry (
165 this, comp_unit, token, scope_vars, locals, lines, code_blocks,
166 real_name, flags, namespace_id);
171 internal void DefineAnonymousScope (int id)
174 throw new InvalidOperationException ();
176 if (anonymous_scopes == null)
177 anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
179 anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
182 internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
183 CapturedVariable.CapturedKind kind)
186 throw new InvalidOperationException ();
188 AnonymousScopeEntry scope = anonymous_scopes [scope_id];
189 scope.AddCapturedVariable (name, captured_name, kind);
192 internal void DefineCapturedScope (int scope_id, int id, string captured_name)
195 throw new InvalidOperationException ();
197 AnonymousScopeEntry scope = anonymous_scopes [scope_id];
198 scope.AddCapturedScope (id, captured_name);
201 internal int GetNextTypeIndex ()
203 return ++last_type_index;
206 internal int GetNextMethodIndex ()
208 return ++last_method_index;
211 internal int GetNextNamespaceIndex ()
213 return ++last_namespace_index;
216 void Write (MyBinaryWriter bw, Guid guid)
218 // Magic number and file version.
219 bw.Write (OffsetTable.Magic);
220 bw.Write (MajorVersion);
221 bw.Write (MinorVersion);
223 bw.Write (guid.ToByteArray ());
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.
229 long offset_table_offset = bw.BaseStream.Position;
230 ot.Write (bw, MajorVersion, MinorVersion);
233 // Sort the methods according to their tokens and update their index.
236 for (int i = 0; i < methods.Count; i++)
237 methods [i].Index = i + 1;
240 // Write data sections.
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;
252 // Write the method index table.
254 ot.MethodTableOffset = (int) bw.BaseStream.Position;
255 for (int i = 0; i < methods.Count; i++) {
256 MethodEntry entry = methods [i];
259 ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
262 // Write source table.
264 ot.SourceTableOffset = (int) bw.BaseStream.Position;
265 for (int i = 0; i < sources.Count; i++) {
266 SourceFileEntry source = sources [i];
269 ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
272 // Write compilation unit table.
274 ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
275 for (int i = 0; i < comp_units.Count; i++) {
276 CompileUnitEntry unit = comp_units [i];
279 ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
282 // Write anonymous scope table.
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)
290 ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
293 // Fixup offset table.
295 ot.TypeCount = last_type_index;
296 ot.MethodCount = methods.Count;
297 ot.SourceCount = sources.Count;
298 ot.CompileUnitCount = comp_units.Count;
301 // Write offset table.
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);
309 Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
310 "{3} methods.", NumLineNumbers, LineNumberSize,
311 ExtendedLineNumberSize, methods.Count);
315 public void CreateSymbolFile (Guid guid, FileStream fs)
318 throw new InvalidOperationException ();
320 Write (new MyBinaryWriter (fs), guid);
323 MyBinaryReader reader;
324 Dictionary<int, SourceFileEntry> source_file_hash;
325 Dictionary<int, CompileUnitEntry> compile_unit_hash;
327 List<MethodEntry> method_list;
328 Dictionary<int, MethodEntry> method_token_hash;
329 Dictionary<string, int> source_name_hash;
333 MonoSymbolFile (Stream stream)
335 reader = new MyBinaryReader (stream);
338 long magic = reader.ReadInt64 ();
339 int major_version = reader.ReadInt32 ();
340 int minor_version = reader.ReadInt32 ();
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);
352 MajorVersion = major_version;
353 MinorVersion = minor_version;
354 guid = new Guid (reader.ReadBytes (16));
356 ot = new OffsetTable (reader, major_version, minor_version);
357 } catch (Exception e) {
358 throw new MonoSymbolFileException ("Cannot read symbol file", e);
361 source_file_hash = new Dictionary<int, SourceFileEntry> ();
362 compile_unit_hash = new Dictionary<int, CompileUnitEntry> ();
365 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
367 string filename = assembly.Location;
368 string name = filename + ".mdb";
370 Module[] modules = assembly.GetModules ();
371 Guid assembly_guid = modules[0].ModuleVersionId;
373 return ReadSymbolFile (name, assembly_guid);
376 public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
378 return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read));
381 public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid)
383 var sf = ReadSymbolFile (mdbFilename);
384 if (assemblyGuid != sf.guid)
385 throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename);
390 public static MonoSymbolFile ReadSymbolFile (Stream stream)
392 return new MonoSymbolFile (stream);
395 public int CompileUnitCount {
396 get { return ot.CompileUnitCount; }
399 public int SourceCount {
400 get { return ot.SourceCount; }
403 public int MethodCount {
404 get { return ot.MethodCount; }
407 public int TypeCount {
408 get { return ot.TypeCount; }
411 public int AnonymousScopeCount {
412 get { return ot.AnonymousScopeCount; }
415 public int NamespaceCount {
416 get { return last_namespace_index; }
423 public OffsetTable OffsetTable {
427 internal int LineNumberCount = 0;
428 internal int LocalCount = 0;
429 internal int StringSize = 0;
431 internal int LineNumberSize = 0;
432 internal int ExtendedLineNumberSize = 0;
434 public SourceFileEntry GetSourceFile (int index)
436 if ((index < 1) || (index > ot.SourceCount))
437 throw new ArgumentException ();
439 throw new InvalidOperationException ();
442 SourceFileEntry source;
443 if (source_file_hash.TryGetValue (index, out source))
446 long old_pos = reader.BaseStream.Position;
448 reader.BaseStream.Position = ot.SourceTableOffset +
449 SourceFileEntry.Size * (index - 1);
450 source = new SourceFileEntry (this, reader);
451 source_file_hash.Add (index, source);
453 reader.BaseStream.Position = old_pos;
458 public SourceFileEntry[] Sources {
461 throw new InvalidOperationException ();
463 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
464 for (int i = 0; i < SourceCount; i++)
465 retval [i] = GetSourceFile (i + 1);
470 public CompileUnitEntry GetCompileUnit (int index)
472 if ((index < 1) || (index > ot.CompileUnitCount))
473 throw new ArgumentException ();
475 throw new InvalidOperationException ();
478 CompileUnitEntry unit;
479 if (compile_unit_hash.TryGetValue (index, out unit))
482 long old_pos = reader.BaseStream.Position;
484 reader.BaseStream.Position = ot.CompileUnitTableOffset +
485 CompileUnitEntry.Size * (index - 1);
486 unit = new CompileUnitEntry (this, reader);
487 compile_unit_hash.Add (index, unit);
489 reader.BaseStream.Position = old_pos;
494 public CompileUnitEntry[] CompileUnits {
497 throw new InvalidOperationException ();
499 CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
500 for (int i = 0; i < CompileUnitCount; i++)
501 retval [i] = GetCompileUnit (i + 1);
509 if (method_token_hash != null)
512 method_token_hash = new Dictionary<int, MethodEntry> ();
513 method_list = new List<MethodEntry> ();
515 long old_pos = reader.BaseStream.Position;
516 reader.BaseStream.Position = ot.MethodTableOffset;
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);
524 reader.BaseStream.Position = old_pos;
528 public MethodEntry GetMethodByToken (int token)
531 throw new InvalidOperationException ();
536 method_token_hash.TryGetValue (token, out me);
541 public MethodEntry GetMethod (int index)
543 if ((index < 1) || (index > ot.MethodCount))
544 throw new ArgumentException ();
546 throw new InvalidOperationException ();
550 return method_list [index - 1];
554 public MethodEntry[] Methods {
557 throw new InvalidOperationException ();
561 MethodEntry[] retval = new MethodEntry [MethodCount];
562 method_list.CopyTo (retval, 0);
568 public int FindSource (string file_name)
571 throw new InvalidOperationException ();
574 if (source_name_hash == null) {
575 source_name_hash = new Dictionary<string, int> ();
577 for (int i = 0; i < ot.SourceCount; i++) {
578 SourceFileEntry source = GetSourceFile (i + 1);
579 source_name_hash.Add (source.FileName, i);
584 if (!source_name_hash.TryGetValue (file_name, out value))
590 public AnonymousScopeEntry GetAnonymousScope (int id)
593 throw new InvalidOperationException ();
595 AnonymousScopeEntry scope;
597 if (anonymous_scopes != null) {
598 anonymous_scopes.TryGetValue (id, out scope);
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);
609 return anonymous_scopes [id];
613 internal MyBinaryReader BinaryReader {
616 throw new InvalidOperationException ();
622 public void Dispose ()
627 protected virtual void Dispose (bool disposing)
630 if (reader != null) {