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) {