PageRenderTime 63ms CodeModel.GetById 17ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/symbols/mdb/Mono.CompilerServices.SymbolWriter/MonoSymbolFile.cs

http://github.com/jbevain/cecil
C# | 643 lines | 472 code | 112 blank | 59 comment | 70 complexity | 33ee9607a245bdae58b527f3881f4bb7 MD5 | raw file
  1//
  2// MonoSymbolFile.cs
  3//
  4// Authors:
  5//   Martin Baulig (martin@ximian.com)
  6//   Marek Safar (marek.safar@gmail.com)
  7//
  8// (C) 2003 Ximian, Inc.  http://www.ximian.com
  9// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
 10//
 11//
 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:
 19//
 20// The above copyright notice and this permission notice shall be
 21// included in all copies or substantial portions of the Software.
 22//
 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.
 30//
 31
 32using System;
 33using System.Reflection;
 34using System.Collections.Generic;
 35using System.IO;
 36
 37namespace Mono.CompilerServices.SymbolWriter
 38{
 39	public class MonoSymbolFileException : Exception
 40	{
 41		public MonoSymbolFileException ()
 42			: base ()
 43		{ }
 44
 45		public MonoSymbolFileException (string message, params object[] args)
 46			: base (String.Format (message, args))
 47		{
 48		}
 49
 50		public MonoSymbolFileException (string message, Exception innerException)
 51			: base (message, innerException)
 52		{
 53		}
 54	}
 55
 56	sealed class MyBinaryWriter : BinaryWriter
 57	{
 58		public MyBinaryWriter (Stream stream)
 59			: base (stream)
 60		{ }
 61
 62		public void WriteLeb128 (int value)
 63		{
 64			base.Write7BitEncodedInt (value);
 65		}
 66	}
 67
 68	internal class MyBinaryReader : BinaryReader
 69	{
 70		public MyBinaryReader (Stream stream)
 71			: base (stream)
 72		{ }
 73
 74		public int ReadLeb128 ()
 75		{
 76			return base.Read7BitEncodedInt ();
 77		}
 78
 79		public string ReadString (int offset)
 80		{
 81			long old_pos = BaseStream.Position;
 82			BaseStream.Position = offset;
 83
 84			string text = ReadString ();
 85
 86			BaseStream.Position = old_pos;
 87			return text;
 88		}
 89	}
 90
 91	public interface ISourceFile
 92	{
 93		SourceFileEntry Entry {
 94			get;
 95		}
 96	}
 97
 98	public interface ICompileUnit
 99	{
100		CompileUnitEntry Entry {
101			get;
102		}
103	}
104
105	public interface IMethodDef
106	{
107		string Name {
108			get;
109		}
110
111		int Token {
112			get;
113		}
114	}
115
116	public class MonoSymbolFile : IDisposable
117	{
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;
122
123		OffsetTable ot;
124		int last_type_index;
125		int last_method_index;
126		int last_namespace_index;
127
128		public readonly int MajorVersion = OffsetTable.MajorVersion;
129		public readonly int MinorVersion = OffsetTable.MinorVersion;
130
131		public int NumLineNumbers;
132
133		public MonoSymbolFile ()
134		{
135			ot = new OffsetTable ();
136		}
137
138		public int AddSource (SourceFileEntry source)
139		{
140			sources.Add (source);
141			return sources.Count;
142		}
143
144		public int AddCompileUnit (CompileUnitEntry entry)
145		{
146			comp_units.Add (entry);
147			return comp_units.Count;
148		}
149
150		public void AddMethod (MethodEntry entry)
151		{
152			methods.Add (entry);
153		}
154
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,
159						 int namespace_id)
160		{
161			if (reader != null)
162				throw new InvalidOperationException ();
163
164			MethodEntry method = new MethodEntry (
165				this, comp_unit, token, scope_vars, locals, lines, code_blocks,
166				real_name, flags, namespace_id);
167			AddMethod (method);
168			return method;
169		}
170
171		internal void DefineAnonymousScope (int id)
172		{
173			if (reader != null)
174				throw new InvalidOperationException ();
175
176			if (anonymous_scopes == null)
177				anonymous_scopes = new Dictionary<int, AnonymousScopeEntry>  ();
178
179			anonymous_scopes.Add (id, new AnonymousScopeEntry (id));
180		}
181
182		internal void DefineCapturedVariable (int scope_id, string name, string captured_name,
183						      CapturedVariable.CapturedKind kind)
184		{
185			if (reader != null)
186				throw new InvalidOperationException ();
187
188			AnonymousScopeEntry scope = anonymous_scopes [scope_id];
189			scope.AddCapturedVariable (name, captured_name, kind);
190		}
191
192		internal void DefineCapturedScope (int scope_id, int id, string captured_name)
193		{
194			if (reader != null)
195				throw new InvalidOperationException ();
196
197			AnonymousScopeEntry scope = anonymous_scopes [scope_id];
198			scope.AddCapturedScope (id, captured_name);
199		}
200
201		internal int GetNextTypeIndex ()
202		{
203			return ++last_type_index;
204		}
205
206		internal int GetNextMethodIndex ()
207		{
208			return ++last_method_index;
209		}
210
211		internal int GetNextNamespaceIndex ()
212		{
213			return ++last_namespace_index;
214		}
215
216		void Write (MyBinaryWriter bw, Guid guid)
217		{
218			// Magic number and file version.
219			bw.Write (OffsetTable.Magic);
220			bw.Write (MajorVersion);
221			bw.Write (MinorVersion);
222
223			bw.Write (guid.ToByteArray ());
224
225			//
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.
228			//
229			long offset_table_offset = bw.BaseStream.Position;
230			ot.Write (bw, MajorVersion, MinorVersion);
231
232			//
233			// Sort the methods according to their tokens and update their index.
234			//
235			methods.Sort ();
236			for (int i = 0; i < methods.Count; i++)
237				methods [i].Index = i + 1;
238
239			//
240			// Write data sections.
241			//
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;
250
251			//
252			// Write the method index table.
253			//
254			ot.MethodTableOffset = (int) bw.BaseStream.Position;
255			for (int i = 0; i < methods.Count; i++) {
256				MethodEntry entry = methods [i];
257				entry.Write (bw);
258			}
259			ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
260
261			//
262			// Write source table.
263			//
264			ot.SourceTableOffset = (int) bw.BaseStream.Position;
265			for (int i = 0; i < sources.Count; i++) {
266				SourceFileEntry source = sources [i];
267				source.Write (bw);
268			}
269			ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
270
271			//
272			// Write compilation unit table.
273			//
274			ot.CompileUnitTableOffset = (int) bw.BaseStream.Position;
275			for (int i = 0; i < comp_units.Count; i++) {
276				CompileUnitEntry unit = comp_units [i];
277				unit.Write (bw);
278			}
279			ot.CompileUnitTableSize = (int) bw.BaseStream.Position - ot.CompileUnitTableOffset;
280
281			//
282			// Write anonymous scope table.
283			//
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)
288					scope.Write (bw);
289			}
290			ot.AnonymousScopeTableSize = (int) bw.BaseStream.Position - ot.AnonymousScopeTableOffset;
291
292			//
293			// Fixup offset table.
294			//
295			ot.TypeCount = last_type_index;
296			ot.MethodCount = methods.Count;
297			ot.SourceCount = sources.Count;
298			ot.CompileUnitCount = comp_units.Count;
299
300			//
301			// Write offset table.
302			//
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);
307
308#if false
309			Console.WriteLine ("TOTAL: {0} line numbes, {1} bytes, extended {2} bytes, " +
310					   "{3} methods.", NumLineNumbers, LineNumberSize,
311					   ExtendedLineNumberSize, methods.Count);
312#endif
313		}
314
315		public void CreateSymbolFile (Guid guid, FileStream fs)
316		{
317			if (reader != null)
318				throw new InvalidOperationException ();
319
320			Write (new MyBinaryWriter (fs), guid);
321		}
322
323		MyBinaryReader reader;
324		Dictionary<int, SourceFileEntry> source_file_hash;
325		Dictionary<int, CompileUnitEntry> compile_unit_hash;
326
327		List<MethodEntry> method_list;
328		Dictionary<int, MethodEntry> method_token_hash;
329		Dictionary<string, int> source_name_hash;
330
331		Guid guid;
332
333		MonoSymbolFile (Stream stream)
334		{
335			reader = new MyBinaryReader (stream);
336
337			try {
338				long magic = reader.ReadInt64 ();
339				int major_version = reader.ReadInt32 ();
340				int minor_version = reader.ReadInt32 ();
341
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);
351
352				MajorVersion = major_version;
353				MinorVersion = minor_version;
354				guid = new Guid (reader.ReadBytes (16));
355
356				ot = new OffsetTable (reader, major_version, minor_version);
357			} catch (Exception e) {
358				throw new MonoSymbolFileException ("Cannot read symbol file", e);
359			}
360
361			source_file_hash = new Dictionary<int, SourceFileEntry> ();
362			compile_unit_hash = new Dictionary<int, CompileUnitEntry> ();
363		}
364
365#if !NET_CORE
366		public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
367		{
368			string filename = assembly.Location;
369			string name = filename + ".mdb";
370
371			Module[] modules = assembly.GetModules ();
372			Guid assembly_guid = modules[0].ModuleVersionId;
373
374			return ReadSymbolFile (name, assembly_guid);
375		}
376#endif
377
378		public static MonoSymbolFile ReadSymbolFile (string mdbFilename)
379		{
380			return ReadSymbolFile (new FileStream (mdbFilename, FileMode.Open, FileAccess.Read));
381		}
382
383		public static MonoSymbolFile ReadSymbolFile (string mdbFilename, Guid assemblyGuid)
384		{
385			var sf = ReadSymbolFile (mdbFilename);
386			if (assemblyGuid != sf.guid)
387				throw new MonoSymbolFileException ("Symbol file `{0}' does not match assembly", mdbFilename);
388
389			return sf;
390		}
391
392		public static MonoSymbolFile ReadSymbolFile (Stream stream)
393		{
394			return new MonoSymbolFile (stream);
395		}
396
397		public int CompileUnitCount {
398			get { return ot.CompileUnitCount; }
399		}
400
401		public int SourceCount {
402			get { return ot.SourceCount; }
403		}
404
405		public int MethodCount {
406			get { return ot.MethodCount; }
407		}
408
409		public int TypeCount {
410			get { return ot.TypeCount; }
411		}
412
413		public int AnonymousScopeCount {
414			get { return ot.AnonymousScopeCount; }
415		}
416
417		public int NamespaceCount {
418			get { return last_namespace_index; }
419		}
420
421		public Guid Guid {
422			get { return guid; }
423		}
424
425		public OffsetTable OffsetTable {
426			get { return ot; }
427		}
428
429		internal int LineNumberCount = 0;
430		internal int LocalCount = 0;
431		internal int StringSize = 0;
432
433		internal int LineNumberSize = 0;
434		internal int ExtendedLineNumberSize = 0;
435
436		public SourceFileEntry GetSourceFile (int index)
437		{
438			if ((index < 1) || (index > ot.SourceCount))
439				throw new ArgumentException ();
440			if (reader == null)
441				throw new InvalidOperationException ();
442
443			lock (this) {
444				SourceFileEntry source;
445				if (source_file_hash.TryGetValue (index, out source))
446					return source;
447
448				long old_pos = reader.BaseStream.Position;
449
450				reader.BaseStream.Position = ot.SourceTableOffset +
451					SourceFileEntry.Size * (index - 1);
452				source = new SourceFileEntry (this, reader);
453				source_file_hash.Add (index, source);
454
455				reader.BaseStream.Position = old_pos;
456				return source;
457			}
458		}
459
460		public SourceFileEntry[] Sources {
461			get {
462				if (reader == null)
463					throw new InvalidOperationException ();
464
465				SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
466				for (int i = 0; i < SourceCount; i++)
467					retval [i] = GetSourceFile (i + 1);
468				return retval;
469			}
470		}
471
472		public CompileUnitEntry GetCompileUnit (int index)
473		{
474			if ((index < 1) || (index > ot.CompileUnitCount))
475				throw new ArgumentException ();
476			if (reader == null)
477				throw new InvalidOperationException ();
478
479			lock (this) {
480				CompileUnitEntry unit;
481				if (compile_unit_hash.TryGetValue (index, out unit))
482					return unit;
483
484				long old_pos = reader.BaseStream.Position;
485
486				reader.BaseStream.Position = ot.CompileUnitTableOffset +
487					CompileUnitEntry.Size * (index - 1);
488				unit = new CompileUnitEntry (this, reader);
489				compile_unit_hash.Add (index, unit);
490
491				reader.BaseStream.Position = old_pos;
492				return unit;
493			}
494		}
495
496		public CompileUnitEntry[] CompileUnits {
497			get {
498				if (reader == null)
499					throw new InvalidOperationException ();
500
501				CompileUnitEntry[] retval = new CompileUnitEntry [CompileUnitCount];
502				for (int i = 0; i < CompileUnitCount; i++)
503					retval [i] = GetCompileUnit (i + 1);
504				return retval;
505			}
506		}
507
508		void read_methods ()
509		{
510			lock (this) {
511				if (method_token_hash != null)
512					return;
513
514				method_token_hash = new Dictionary<int, MethodEntry> ();
515				method_list = new List<MethodEntry> ();
516
517				long old_pos = reader.BaseStream.Position;
518				reader.BaseStream.Position = ot.MethodTableOffset;
519
520				for (int i = 0; i < MethodCount; i++) {
521					MethodEntry entry = new MethodEntry (this, reader, i + 1);
522					method_token_hash.Add (entry.Token, entry);
523					method_list.Add (entry);
524				}
525
526				reader.BaseStream.Position = old_pos;
527			}
528		}
529
530		public MethodEntry GetMethodByToken (int token)
531		{
532			if (reader == null)
533				throw new InvalidOperationException ();
534
535			lock (this) {
536				read_methods ();
537				MethodEntry me;
538				method_token_hash.TryGetValue (token, out me);
539				return me;
540			}
541		}
542
543		public MethodEntry GetMethod (int index)
544		{
545			if ((index < 1) || (index > ot.MethodCount))
546				throw new ArgumentException ();
547			if (reader == null)
548				throw new InvalidOperationException ();
549
550			lock (this) {
551				read_methods ();
552				return method_list [index - 1];
553			}
554		}
555
556		public MethodEntry[] Methods {
557			get {
558				if (reader == null)
559					throw new InvalidOperationException ();
560
561				lock (this) {
562					read_methods ();
563					MethodEntry[] retval = new MethodEntry [MethodCount];
564					method_list.CopyTo (retval, 0);
565					return retval;
566				}
567			}
568		}
569
570		public int FindSource (string file_name)
571		{
572			if (reader == null)
573				throw new InvalidOperationException ();
574
575			lock (this) {
576				if (source_name_hash == null) {
577					source_name_hash = new Dictionary<string, int> ();
578
579					for (int i = 0; i < ot.SourceCount; i++) {
580						SourceFileEntry source = GetSourceFile (i + 1);
581						source_name_hash.Add (source.FileName, i);
582					}
583				}
584
585				int value;
586				if (!source_name_hash.TryGetValue (file_name, out value))
587					return -1;
588				return value;
589			}
590		}
591
592		public AnonymousScopeEntry GetAnonymousScope (int id)
593		{
594			if (reader == null)
595				throw new InvalidOperationException ();
596
597			AnonymousScopeEntry scope;
598			lock (this) {
599				if (anonymous_scopes != null) {
600					anonymous_scopes.TryGetValue (id, out scope);
601					return scope;
602				}
603
604				anonymous_scopes = new Dictionary<int, AnonymousScopeEntry> ();
605				reader.BaseStream.Position = ot.AnonymousScopeTableOffset;
606				for (int i = 0; i < ot.AnonymousScopeCount; i++) {
607					scope = new AnonymousScopeEntry (reader);
608					anonymous_scopes.Add (scope.ID, scope);
609				}
610
611				return anonymous_scopes [id];
612			}
613		}
614
615		internal MyBinaryReader BinaryReader {
616			get {
617				if (reader == null)
618					throw new InvalidOperationException ();
619
620				return reader;
621			}
622		}
623
624		public void Dispose ()
625		{
626			Dispose (true);
627		}
628
629		protected virtual void Dispose (bool disposing)
630		{
631			if (disposing) {
632				if (reader != null) {
633#if NET_CORE
634					reader.Dispose ();
635#else
636					reader.Close ();
637#endif
638					reader = null;
639				}
640			}
641		}
642	}
643}