PageRenderTime 47ms CodeModel.GetById 7ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 1ms

/Mono.Cecil.Cil/Symbols.cs

http://github.com/jbevain/cecil
C# | 1164 lines | 895 code | 256 blank | 13 comment | 124 complexity | d38b7c5e9999be051e679e5770a133e1 MD5 | raw file
   1//
   2// Author:
   3//   Jb Evain (jbevain@gmail.com)
   4//
   5// Copyright (c) 2008 - 2015 Jb Evain
   6// Copyright (c) 2008 - 2011 Novell, Inc.
   7//
   8// Licensed under the MIT/X11 license.
   9//
  10
  11using System;
  12using System.Collections.Generic;
  13using System.IO;
  14using System.Runtime.InteropServices;
  15using System.Threading;
  16using SR = System.Reflection;
  17
  18using Mono.Collections.Generic;
  19using Mono.Cecil.Cil;
  20using Mono.Cecil.PE;
  21
  22namespace Mono.Cecil.Cil {
  23
  24	[StructLayout (LayoutKind.Sequential)]
  25	public struct ImageDebugDirectory {
  26		public const int Size = 28;
  27
  28		public int Characteristics;
  29		public int TimeDateStamp;
  30		public short MajorVersion;
  31		public short MinorVersion;
  32		public ImageDebugType Type;
  33		public int SizeOfData;
  34		public int AddressOfRawData;
  35		public int PointerToRawData;
  36	}
  37
  38	public enum ImageDebugType {
  39		CodeView = 2,
  40		Deterministic = 16,
  41		EmbeddedPortablePdb = 17,
  42	}
  43
  44	public sealed class ImageDebugHeader {
  45
  46		readonly ImageDebugHeaderEntry [] entries;
  47
  48		public bool HasEntries {
  49			get { return !entries.IsNullOrEmpty (); }
  50		}
  51
  52		public ImageDebugHeaderEntry [] Entries {
  53			get { return entries; }
  54		}
  55
  56		public ImageDebugHeader (ImageDebugHeaderEntry [] entries)
  57		{
  58			this.entries = entries ?? Empty<ImageDebugHeaderEntry>.Array;
  59		}
  60
  61		public ImageDebugHeader ()
  62			: this (Empty<ImageDebugHeaderEntry>.Array)
  63		{
  64		}
  65
  66		public ImageDebugHeader (ImageDebugHeaderEntry entry)
  67			: this (new [] { entry })
  68		{
  69		}
  70	}
  71
  72	public sealed class ImageDebugHeaderEntry {
  73
  74		ImageDebugDirectory directory;
  75		readonly byte [] data;
  76
  77		public ImageDebugDirectory Directory {
  78			get { return directory; }
  79			internal set { directory = value; }
  80		}
  81
  82		public byte [] Data {
  83			get { return data; }
  84		}
  85
  86		public ImageDebugHeaderEntry (ImageDebugDirectory directory, byte [] data)
  87		{
  88			this.directory = directory;
  89			this.data = data ?? Empty<byte>.Array;
  90		}
  91	}
  92
  93	public sealed class ScopeDebugInformation : DebugInformation {
  94
  95		internal InstructionOffset start;
  96		internal InstructionOffset end;
  97		internal ImportDebugInformation import;
  98		internal Collection<ScopeDebugInformation> scopes;
  99		internal Collection<VariableDebugInformation> variables;
 100		internal Collection<ConstantDebugInformation> constants;
 101
 102		public InstructionOffset Start {
 103			get { return start; }
 104			set { start = value; }
 105		}
 106
 107		public InstructionOffset End {
 108			get { return end; }
 109			set { end = value; }
 110		}
 111
 112		public ImportDebugInformation Import {
 113			get { return import; }
 114			set { import = value; }
 115		}
 116
 117		public bool HasScopes {
 118			get { return !scopes.IsNullOrEmpty (); }
 119		}
 120
 121		public Collection<ScopeDebugInformation> Scopes {
 122			get {
 123				if (scopes == null)
 124					Interlocked.CompareExchange (ref scopes, new Collection<ScopeDebugInformation> (), null);
 125
 126				return scopes;
 127			}
 128		}
 129
 130		public bool HasVariables {
 131			get { return !variables.IsNullOrEmpty (); }
 132		}
 133
 134		public Collection<VariableDebugInformation> Variables {
 135			get {
 136				if (variables == null)
 137					Interlocked.CompareExchange (ref variables, new Collection<VariableDebugInformation> (), null);
 138
 139				return variables;
 140			}
 141		}
 142
 143		public bool HasConstants {
 144			get { return !constants.IsNullOrEmpty (); }
 145		}
 146
 147		public Collection<ConstantDebugInformation> Constants {
 148			get {
 149				if (constants == null)
 150					Interlocked.CompareExchange (ref constants, new Collection<ConstantDebugInformation> (), null);
 151
 152				return constants;
 153			}
 154		}
 155
 156		internal ScopeDebugInformation ()
 157		{
 158			this.token = new MetadataToken (TokenType.LocalScope);
 159		}
 160
 161		public ScopeDebugInformation (Instruction start, Instruction end)
 162			: this ()
 163		{
 164			if (start == null)
 165				throw new ArgumentNullException ("start");
 166
 167			this.start = new InstructionOffset (start);
 168
 169			if (end != null)
 170				this.end = new InstructionOffset (end);
 171		}
 172
 173		public bool TryGetName (VariableDefinition variable, out string name)
 174		{
 175			name = null;
 176			if (variables == null || variables.Count == 0)
 177				return false;
 178
 179			for (int i = 0; i < variables.Count; i++) {
 180				if (variables [i].Index == variable.Index) {
 181					name = variables [i].Name;
 182					return true;
 183				}
 184			}
 185
 186			return false;
 187		}
 188	}
 189
 190	public struct InstructionOffset {
 191
 192		readonly Instruction instruction;
 193		readonly int? offset;
 194
 195		public int Offset {
 196			get {
 197				if (instruction != null)
 198					return instruction.Offset;
 199				if (offset.HasValue)
 200					return offset.Value;
 201
 202				throw new NotSupportedException ();
 203			}
 204		}
 205
 206		public bool IsEndOfMethod {
 207			get { return instruction == null && !offset.HasValue; }
 208		}
 209
 210		public InstructionOffset (Instruction instruction)
 211		{
 212			if (instruction == null)
 213				throw new ArgumentNullException ("instruction");
 214
 215			this.instruction = instruction;
 216			this.offset = null;
 217		}
 218
 219		public InstructionOffset (int offset)
 220		{
 221			this.instruction = null;
 222			this.offset = offset;
 223		}
 224	}
 225
 226	[Flags]
 227	public enum VariableAttributes : ushort {
 228		None = 0,
 229		DebuggerHidden = 1,
 230	}
 231
 232	public struct VariableIndex {
 233		readonly VariableDefinition variable;
 234		readonly int? index;
 235
 236		public int Index {
 237			get {
 238				if (variable != null)
 239					return variable.Index;
 240				if (index.HasValue)
 241					return index.Value;
 242
 243				throw new NotSupportedException ();
 244			}
 245		}
 246
 247		public VariableIndex (VariableDefinition variable)
 248		{
 249			if (variable == null)
 250				throw new ArgumentNullException ("variable");
 251
 252			this.variable = variable;
 253			this.index = null;
 254		}
 255
 256		public VariableIndex (int index)
 257		{
 258			this.variable = null;
 259			this.index = index;
 260		}
 261	}
 262
 263	public abstract class DebugInformation : ICustomDebugInformationProvider {
 264
 265		internal MetadataToken token;
 266		internal Collection<CustomDebugInformation> custom_infos;
 267
 268		public MetadataToken MetadataToken {
 269			get { return token; }
 270			set { token = value; }
 271		}
 272
 273		public bool HasCustomDebugInformations {
 274			get { return !custom_infos.IsNullOrEmpty (); }
 275		}
 276
 277		public Collection<CustomDebugInformation> CustomDebugInformations {
 278			get {
 279				if (custom_infos == null)
 280					Interlocked.CompareExchange (ref custom_infos, new Collection<CustomDebugInformation> (), null);
 281
 282				return custom_infos;
 283			}
 284		}
 285
 286		internal DebugInformation ()
 287		{
 288		}
 289	}
 290
 291	public sealed class VariableDebugInformation : DebugInformation {
 292
 293		string name;
 294		ushort attributes;
 295		internal VariableIndex index;
 296
 297		public int Index {
 298			get { return index.Index; }
 299		}
 300
 301		public string Name {
 302			get { return name; }
 303			set { name = value; }
 304		}
 305
 306		public VariableAttributes Attributes {
 307			get { return (VariableAttributes) attributes; }
 308			set { attributes = (ushort) value; }
 309		}
 310
 311		public bool IsDebuggerHidden {
 312			get { return attributes.GetAttributes ((ushort) VariableAttributes.DebuggerHidden); }
 313			set { attributes = attributes.SetAttributes ((ushort) VariableAttributes.DebuggerHidden, value); }
 314		}
 315
 316		internal VariableDebugInformation (int index, string name)
 317		{
 318			if (name == null)
 319				throw new ArgumentNullException ("name");
 320
 321			this.index = new VariableIndex (index);
 322			this.name = name;
 323		}
 324
 325		public VariableDebugInformation (VariableDefinition variable, string name)
 326		{
 327			if (variable == null)
 328				throw new ArgumentNullException ("variable");
 329			if (name == null)
 330				throw new ArgumentNullException ("name");
 331
 332			this.index = new VariableIndex (variable);
 333			this.name = name;
 334			this.token = new MetadataToken (TokenType.LocalVariable);
 335		}
 336	}
 337
 338	public sealed class ConstantDebugInformation : DebugInformation {
 339
 340		string name;
 341		TypeReference constant_type;
 342		object value;
 343
 344		public string Name {
 345			get { return name; }
 346			set { name = value; }
 347		}
 348
 349		public TypeReference ConstantType {
 350			get { return constant_type; }
 351			set { constant_type = value; }
 352		}
 353
 354		public object Value {
 355			get { return value; }
 356			set { this.value = value; }
 357		}
 358
 359		public ConstantDebugInformation (string name, TypeReference constant_type, object value)
 360		{
 361			if (name == null)
 362				throw new ArgumentNullException ("name");
 363
 364			this.name = name;
 365			this.constant_type = constant_type;
 366			this.value = value;
 367			this.token = new MetadataToken (TokenType.LocalConstant);
 368		}
 369	}
 370
 371	public enum ImportTargetKind : byte {
 372		ImportNamespace = 1,
 373		ImportNamespaceInAssembly = 2,
 374		ImportType = 3,
 375		ImportXmlNamespaceWithAlias = 4,
 376		ImportAlias = 5,
 377		DefineAssemblyAlias = 6,
 378		DefineNamespaceAlias = 7,
 379		DefineNamespaceInAssemblyAlias = 8,
 380		DefineTypeAlias = 9,
 381	}
 382
 383	public sealed class ImportTarget {
 384
 385		internal ImportTargetKind kind;
 386
 387		internal string @namespace;
 388		internal TypeReference type;
 389		internal AssemblyNameReference reference;
 390		internal string alias;
 391
 392		public string Namespace {
 393			get { return @namespace; }
 394			set { @namespace = value; }
 395		}
 396
 397		public TypeReference Type {
 398			get { return type; }
 399			set { type = value; }
 400		}
 401
 402		public AssemblyNameReference AssemblyReference {
 403			get { return reference; }
 404			set { reference = value; }
 405		}
 406
 407		public string Alias {
 408			get { return alias; }
 409			set { alias = value; }
 410		}
 411
 412		public ImportTargetKind Kind {
 413			get { return kind; }
 414			set { kind = value; }
 415		}
 416
 417		public ImportTarget (ImportTargetKind kind)
 418		{
 419			this.kind = kind;
 420		}
 421	}
 422
 423	public sealed class ImportDebugInformation : DebugInformation {
 424
 425		internal ImportDebugInformation parent;
 426		internal Collection<ImportTarget> targets;
 427
 428		public bool HasTargets {
 429			get { return !targets.IsNullOrEmpty (); }
 430		}
 431
 432		public Collection<ImportTarget> Targets {
 433			get
 434			{
 435				if (targets == null)
 436					Interlocked.CompareExchange (ref targets, new Collection<ImportTarget> (), null);
 437
 438				return targets;
 439			}
 440		}
 441
 442		public ImportDebugInformation Parent {
 443			get { return parent; }
 444			set { parent = value; }
 445		}
 446
 447		public ImportDebugInformation ()
 448		{
 449			this.token = new MetadataToken (TokenType.ImportScope);
 450		}
 451	}
 452
 453	public interface ICustomDebugInformationProvider : IMetadataTokenProvider {
 454		bool HasCustomDebugInformations { get; }
 455		Collection<CustomDebugInformation> CustomDebugInformations { get; }
 456	}
 457
 458	public enum CustomDebugInformationKind {
 459		Binary,
 460		StateMachineScope,
 461		DynamicVariable,
 462		DefaultNamespace,
 463		AsyncMethodBody,
 464		EmbeddedSource,
 465		SourceLink,
 466	}
 467
 468	public abstract class CustomDebugInformation : DebugInformation {
 469
 470		Guid identifier;
 471
 472		public Guid Identifier {
 473			get { return identifier; }
 474		}
 475
 476		public abstract CustomDebugInformationKind Kind { get; }
 477
 478		internal CustomDebugInformation (Guid identifier)
 479		{
 480			this.identifier = identifier;
 481			this.token = new MetadataToken (TokenType.CustomDebugInformation);
 482		}
 483	}
 484
 485	public sealed class BinaryCustomDebugInformation : CustomDebugInformation {
 486
 487		byte [] data;
 488
 489		public byte [] Data {
 490			get { return data; }
 491			set { data = value; }
 492		}
 493
 494		public override CustomDebugInformationKind Kind {
 495			get { return CustomDebugInformationKind.Binary; }
 496		}
 497
 498		public BinaryCustomDebugInformation (Guid identifier, byte [] data)
 499			: base (identifier)
 500		{
 501			this.data = data;
 502		}
 503	}
 504
 505	public sealed class AsyncMethodBodyDebugInformation : CustomDebugInformation {
 506
 507		internal InstructionOffset catch_handler;
 508		internal Collection<InstructionOffset> yields;
 509		internal Collection<InstructionOffset> resumes;
 510		internal Collection<MethodDefinition> resume_methods;
 511
 512		public InstructionOffset CatchHandler {
 513			get { return catch_handler; }
 514			set { catch_handler = value; }
 515		}
 516
 517		public Collection<InstructionOffset> Yields {
 518			get {
 519				if (yields == null)
 520					Interlocked.CompareExchange (ref yields, new Collection<InstructionOffset> (), null);
 521
 522				return yields;
 523			}
 524		}
 525
 526		public Collection<InstructionOffset> Resumes {
 527			get {
 528				if (resumes == null)
 529					Interlocked.CompareExchange (ref resumes, new Collection<InstructionOffset> (), null);
 530
 531				return resumes;
 532			}
 533		}
 534
 535		public Collection<MethodDefinition> ResumeMethods {
 536			get { return resume_methods ?? (resume_methods = new Collection<MethodDefinition> ()); }
 537		}
 538
 539		public override CustomDebugInformationKind Kind {
 540			get { return CustomDebugInformationKind.AsyncMethodBody; }
 541		}
 542
 543		public static Guid KindIdentifier = new Guid ("{54FD2AC5-E925-401A-9C2A-F94F171072F8}");
 544
 545		internal AsyncMethodBodyDebugInformation (int catchHandler)
 546			: base (KindIdentifier)
 547		{
 548			this.catch_handler = new InstructionOffset (catchHandler);
 549		}
 550
 551		public AsyncMethodBodyDebugInformation (Instruction catchHandler)
 552			: base (KindIdentifier)
 553		{
 554			this.catch_handler = new InstructionOffset (catchHandler);
 555		}
 556
 557		public AsyncMethodBodyDebugInformation ()
 558			: base (KindIdentifier)
 559		{
 560			this.catch_handler = new InstructionOffset (-1);
 561		}
 562	}
 563
 564	public sealed class StateMachineScope {
 565
 566		internal InstructionOffset start;
 567		internal InstructionOffset end;
 568
 569		public InstructionOffset Start {
 570			get { return start; }
 571			set { start = value; }
 572		}
 573
 574		public InstructionOffset End {
 575			get { return end; }
 576			set { end = value; }
 577		}
 578
 579		internal StateMachineScope (int start, int end)
 580		{
 581			this.start = new InstructionOffset (start);
 582			this.end = new InstructionOffset (end);
 583		}
 584
 585		public StateMachineScope (Instruction start, Instruction end)
 586		{
 587			this.start = new InstructionOffset (start);
 588			this.end = end != null ? new InstructionOffset (end) : new InstructionOffset ();
 589		}
 590	}
 591
 592	public sealed class StateMachineScopeDebugInformation : CustomDebugInformation {
 593
 594		internal Collection<StateMachineScope> scopes;
 595
 596		public Collection<StateMachineScope> Scopes {
 597			get { return scopes ?? (scopes = new Collection<StateMachineScope> ()); }
 598		}
 599
 600		public override CustomDebugInformationKind Kind {
 601			get { return CustomDebugInformationKind.StateMachineScope; }
 602		}
 603
 604		public static Guid KindIdentifier = new Guid ("{6DA9A61E-F8C7-4874-BE62-68BC5630DF71}");
 605
 606		public StateMachineScopeDebugInformation ()
 607			: base (KindIdentifier)
 608		{
 609		}
 610	}
 611
 612	public sealed class EmbeddedSourceDebugInformation : CustomDebugInformation {
 613
 614		internal byte [] content;
 615		internal bool compress;
 616
 617		public byte [] Content {
 618			get { return content; }
 619			set { content = value; }
 620		}
 621
 622		public bool Compress {
 623			get { return compress; }
 624			set { compress = value; }
 625		}
 626
 627		public override CustomDebugInformationKind Kind {
 628			get { return CustomDebugInformationKind.EmbeddedSource; }
 629		}
 630
 631		public static Guid KindIdentifier = new Guid ("{0E8A571B-6926-466E-B4AD-8AB04611F5FE}");
 632
 633		public EmbeddedSourceDebugInformation (byte [] content, bool compress)
 634			: base (KindIdentifier)
 635		{
 636			this.content = content;
 637			this.compress = compress;
 638		}
 639	}
 640
 641	public sealed class SourceLinkDebugInformation : CustomDebugInformation {
 642
 643		internal string content;
 644
 645		public string Content {
 646			get { return content; }
 647			set { content = value; }
 648		}
 649
 650		public override CustomDebugInformationKind Kind {
 651			get { return CustomDebugInformationKind.SourceLink; }
 652		}
 653
 654		public static Guid KindIdentifier = new Guid ("{CC110556-A091-4D38-9FEC-25AB9A351A6A}");
 655
 656		public SourceLinkDebugInformation (string content)
 657			: base (KindIdentifier)
 658		{
 659			this.content = content;
 660		}
 661	}
 662
 663	public sealed class MethodDebugInformation : DebugInformation {
 664
 665		internal MethodDefinition method;
 666		internal Collection<SequencePoint> sequence_points;
 667		internal ScopeDebugInformation scope;
 668		internal MethodDefinition kickoff_method;
 669		internal int code_size;
 670		internal MetadataToken local_var_token;
 671
 672		public MethodDefinition Method {
 673			get { return method; }
 674		}
 675
 676		public bool HasSequencePoints {
 677			get { return !sequence_points.IsNullOrEmpty (); }
 678		}
 679
 680		public Collection<SequencePoint> SequencePoints {
 681			get {
 682				if (sequence_points == null)
 683					Interlocked.CompareExchange (ref sequence_points, new Collection<SequencePoint> (), null);
 684
 685				return sequence_points;
 686			}
 687		}
 688
 689		public ScopeDebugInformation Scope {
 690			get { return scope; }
 691			set { scope = value; }
 692		}
 693
 694		public MethodDefinition StateMachineKickOffMethod {
 695			get { return kickoff_method; }
 696			set { kickoff_method = value; }
 697		}
 698
 699		internal MethodDebugInformation (MethodDefinition method)
 700		{
 701			if (method == null)
 702				throw new ArgumentNullException ("method");
 703
 704			this.method = method;
 705			this.token = new MetadataToken (TokenType.MethodDebugInformation, method.MetadataToken.RID);
 706		}
 707
 708		public SequencePoint GetSequencePoint (Instruction instruction)
 709		{
 710			if (!HasSequencePoints)
 711				return null;
 712
 713			for (int i = 0; i < sequence_points.Count; i++)
 714				if (sequence_points [i].Offset == instruction.Offset)
 715					return sequence_points [i];
 716
 717			return null;
 718		}
 719
 720		public IDictionary<Instruction, SequencePoint> GetSequencePointMapping ()
 721		{
 722			var instruction_mapping = new Dictionary<Instruction, SequencePoint> ();
 723			if (!HasSequencePoints || !method.HasBody)
 724				return instruction_mapping;
 725
 726			var offset_mapping = new Dictionary<int, SequencePoint> (sequence_points.Count);
 727
 728			for (int i = 0; i < sequence_points.Count; i++) {
 729				if (!offset_mapping.ContainsKey (sequence_points [i].Offset))
 730					offset_mapping.Add (sequence_points [i].Offset, sequence_points [i]);
 731			}
 732
 733			var instructions = method.Body.Instructions;
 734
 735			for (int i = 0; i < instructions.Count; i++) {
 736				SequencePoint sequence_point;
 737				if (offset_mapping.TryGetValue (instructions [i].Offset, out sequence_point))
 738					instruction_mapping.Add (instructions [i], sequence_point);
 739			}
 740
 741			return instruction_mapping;
 742		}
 743
 744		public IEnumerable<ScopeDebugInformation> GetScopes ()
 745		{
 746			if (scope == null)
 747				return Empty<ScopeDebugInformation>.Array;
 748
 749			return GetScopes (new[] { scope });
 750		}
 751
 752		static IEnumerable<ScopeDebugInformation> GetScopes (IList<ScopeDebugInformation> scopes)
 753		{
 754			for (int i = 0; i < scopes.Count; i++) {
 755				var scope = scopes [i];
 756
 757				yield return scope;
 758
 759				if (!scope.HasScopes)
 760					continue;
 761
 762				foreach (var sub_scope in GetScopes (scope.Scopes))
 763					yield return sub_scope;
 764			}
 765		}
 766
 767		public bool TryGetName (VariableDefinition variable, out string name)
 768		{
 769			name = null;
 770
 771			var has_name = false;
 772			var unique_name = "";
 773
 774			foreach (var scope in GetScopes ()) {
 775				string slot_name;
 776				if (!scope.TryGetName (variable, out slot_name))
 777					continue;
 778
 779				if (!has_name) {
 780					has_name = true;
 781					unique_name = slot_name;
 782					continue;
 783				}
 784
 785				if (unique_name != slot_name)
 786					return false;
 787			}
 788
 789			name = unique_name;
 790			return has_name;
 791		}
 792	}
 793
 794	public interface ISymbolReader : IDisposable {
 795
 796		ISymbolWriterProvider GetWriterProvider ();
 797		bool ProcessDebugHeader (ImageDebugHeader header);
 798		MethodDebugInformation Read (MethodDefinition method);
 799	}
 800
 801	public interface ISymbolReaderProvider {
 802		ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName);
 803		ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream);
 804	}
 805
 806#if !NET_CORE
 807	[Serializable]
 808#endif
 809	public sealed class SymbolsNotFoundException : FileNotFoundException {
 810
 811		public SymbolsNotFoundException (string message) : base (message)
 812		{
 813		}
 814
 815#if !NET_CORE
 816		SymbolsNotFoundException (
 817			System.Runtime.Serialization.SerializationInfo info,
 818			System.Runtime.Serialization.StreamingContext context)
 819			: base (info, context)
 820		{
 821		}
 822#endif
 823	}
 824
 825#if !NET_CORE
 826	[Serializable]
 827#endif
 828	public sealed class SymbolsNotMatchingException : InvalidOperationException {
 829
 830		public SymbolsNotMatchingException (string message) : base (message)
 831		{
 832		}
 833
 834#if !NET_CORE
 835		SymbolsNotMatchingException (
 836			System.Runtime.Serialization.SerializationInfo info,
 837			System.Runtime.Serialization.StreamingContext context)
 838			: base (info, context)
 839		{
 840		}
 841#endif
 842	}
 843
 844	public class DefaultSymbolReaderProvider : ISymbolReaderProvider {
 845
 846		readonly bool throw_if_no_symbol;
 847
 848		public DefaultSymbolReaderProvider ()
 849			: this (throwIfNoSymbol: true)
 850		{
 851		}
 852
 853		public DefaultSymbolReaderProvider (bool throwIfNoSymbol)
 854		{
 855			throw_if_no_symbol = throwIfNoSymbol;
 856		}
 857
 858		public ISymbolReader GetSymbolReader (ModuleDefinition module, string fileName)
 859		{
 860			if (module.Image.HasDebugTables ())
 861				return null;
 862
 863			if (module.HasDebugHeader) {
 864				var header = module.GetDebugHeader ();
 865				var entry = header.GetEmbeddedPortablePdbEntry ();
 866				if (entry != null)
 867					return new EmbeddedPortablePdbReaderProvider ().GetSymbolReader (module, fileName);
 868			}
 869
 870			var pdb_file_name = Mixin.GetPdbFileName (fileName);
 871
 872			if (File.Exists (pdb_file_name)) {
 873				if (Mixin.IsPortablePdb (Mixin.GetPdbFileName (fileName)))
 874					return new PortablePdbReaderProvider ().GetSymbolReader (module, fileName);
 875
 876				try {
 877					return SymbolProvider.GetReaderProvider (SymbolKind.NativePdb).GetSymbolReader (module, fileName);
 878				} catch (Exception) {
 879					// We might not include support for native pdbs.
 880				}
 881			}
 882
 883			var mdb_file_name = Mixin.GetMdbFileName (fileName);
 884			if (File.Exists (mdb_file_name)) {
 885				try {
 886					return SymbolProvider.GetReaderProvider (SymbolKind.Mdb).GetSymbolReader (module, fileName);
 887				} catch (Exception) {
 888					// We might not include support for mdbs.
 889				}
 890			}
 891
 892			if (throw_if_no_symbol)
 893				throw new SymbolsNotFoundException (string.Format ("No symbol found for file: {0}", fileName));
 894
 895			return null;
 896		}
 897
 898		public ISymbolReader GetSymbolReader (ModuleDefinition module, Stream symbolStream)
 899		{
 900			if (module.Image.HasDebugTables ())
 901				return null;
 902
 903			if (module.HasDebugHeader) {
 904				var header = module.GetDebugHeader ();
 905				var entry = header.GetEmbeddedPortablePdbEntry ();
 906				if (entry != null)
 907					return new EmbeddedPortablePdbReaderProvider ().GetSymbolReader (module, "");
 908			}
 909
 910			Mixin.CheckStream (symbolStream);
 911			Mixin.CheckReadSeek (symbolStream);
 912
 913			var position = symbolStream.Position;
 914
 915			const int portablePdbHeader = 0x424a5342;
 916
 917			var reader = new BinaryStreamReader (symbolStream);
 918			var intHeader = reader.ReadInt32 ();
 919			symbolStream.Position = position;
 920
 921			if (intHeader == portablePdbHeader) {
 922				return new PortablePdbReaderProvider ().GetSymbolReader (module, symbolStream);
 923			}
 924
 925			const string nativePdbHeader = "Microsoft C/C++ MSF 7.00";
 926
 927			var bytesHeader = reader.ReadBytes (nativePdbHeader.Length);
 928			symbolStream.Position = position;
 929			var isNativePdb = true;
 930
 931			for (var i = 0; i < bytesHeader.Length; i++) {
 932				if (bytesHeader [i] != (byte) nativePdbHeader [i]) {
 933					isNativePdb = false;
 934					break;
 935				}
 936			}
 937
 938			if (isNativePdb) {
 939				try {
 940					return SymbolProvider.GetReaderProvider (SymbolKind.NativePdb).GetSymbolReader (module, symbolStream);
 941				} catch (Exception) {
 942					// We might not include support for native pdbs.
 943				}
 944			}
 945
 946			const long mdbHeader = 0x45e82623fd7fa614;
 947
 948			var longHeader = reader.ReadInt64 ();
 949			symbolStream.Position = position;
 950
 951			if (longHeader == mdbHeader) {
 952				try {
 953					return SymbolProvider.GetReaderProvider (SymbolKind.Mdb).GetSymbolReader (module, symbolStream);
 954				} catch (Exception) {
 955					// We might not include support for mdbs.
 956				}
 957			}
 958
 959			if (throw_if_no_symbol)
 960				throw new SymbolsNotFoundException (string.Format ("No symbols found in stream"));
 961
 962			return null;
 963		}
 964	}
 965
 966	enum SymbolKind {
 967		NativePdb,
 968		PortablePdb,
 969		EmbeddedPortablePdb,
 970		Mdb,
 971	}
 972
 973	static class SymbolProvider {
 974
 975		static SR.AssemblyName GetSymbolAssemblyName (SymbolKind kind)
 976		{
 977			if (kind == SymbolKind.PortablePdb)
 978				throw new ArgumentException ();
 979
 980			var suffix = GetSymbolNamespace (kind);
 981
 982			var cecil_name = typeof (SymbolProvider).Assembly.GetName ();
 983
 984			var name = new SR.AssemblyName {
 985				Name = cecil_name.Name + "." + suffix,
 986				Version = cecil_name.Version,
 987#if NET_CORE
 988				CultureName = cecil_name.CultureName,
 989#else
 990				CultureInfo = cecil_name.CultureInfo,
 991#endif
 992			};
 993
 994			name.SetPublicKeyToken (cecil_name.GetPublicKeyToken ());
 995
 996			return name;
 997		}
 998
 999		static Type GetSymbolType (SymbolKind kind, string fullname)
1000		{
1001			var type = Type.GetType (fullname);
1002			if (type != null)
1003				return type;
1004
1005			var assembly_name = GetSymbolAssemblyName (kind);
1006
1007			type = Type.GetType (fullname + ", " + assembly_name.FullName);
1008			if (type != null)
1009				return type;
1010
1011			try {
1012				var assembly = SR.Assembly.Load (assembly_name);
1013				if (assembly != null)
1014					return assembly.GetType (fullname);
1015			} catch (FileNotFoundException) {
1016			} catch (FileLoadException) {
1017			}
1018
1019			return null;
1020		}
1021
1022		public static ISymbolReaderProvider GetReaderProvider (SymbolKind kind)
1023		{
1024			if (kind == SymbolKind.PortablePdb)
1025				return new PortablePdbReaderProvider ();
1026			if (kind == SymbolKind.EmbeddedPortablePdb)
1027				return new EmbeddedPortablePdbReaderProvider ();
1028
1029			var provider_name = GetSymbolTypeName (kind, "ReaderProvider");
1030			var type = GetSymbolType (kind, provider_name);
1031			if (type == null)
1032				throw new TypeLoadException ("Could not find symbol provider type " + provider_name);
1033
1034			return (ISymbolReaderProvider) Activator.CreateInstance (type);
1035		}
1036
1037		static string GetSymbolTypeName (SymbolKind kind, string name)
1038		{
1039			return "Mono.Cecil" + "." + GetSymbolNamespace (kind) + "." + kind + name;
1040		}
1041
1042		static string GetSymbolNamespace (SymbolKind kind)
1043		{
1044			if (kind == SymbolKind.PortablePdb || kind == SymbolKind.EmbeddedPortablePdb)
1045				return "Cil";
1046			if (kind == SymbolKind.NativePdb)
1047				return "Pdb";
1048			if (kind == SymbolKind.Mdb)
1049				return "Mdb";
1050
1051			throw new ArgumentException ();
1052		}
1053	}
1054
1055	public interface ISymbolWriter : IDisposable {
1056
1057		ISymbolReaderProvider GetReaderProvider ();
1058		ImageDebugHeader GetDebugHeader ();
1059		void Write (MethodDebugInformation info);
1060	}
1061
1062	public interface ISymbolWriterProvider {
1063
1064		ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName);
1065		ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream);
1066	}
1067
1068	public class DefaultSymbolWriterProvider : ISymbolWriterProvider {
1069
1070		public ISymbolWriter GetSymbolWriter (ModuleDefinition module, string fileName)
1071		{
1072			var reader = module.SymbolReader;
1073			if (reader == null)
1074				throw new InvalidOperationException ();
1075
1076			if (module.Image != null && module.Image.HasDebugTables ())
1077				return null;
1078
1079			return reader.GetWriterProvider ().GetSymbolWriter (module, fileName);
1080		}
1081
1082		public ISymbolWriter GetSymbolWriter (ModuleDefinition module, Stream symbolStream)
1083		{
1084			throw new NotSupportedException ();
1085		}
1086	}
1087}
1088
1089namespace Mono.Cecil {
1090
1091	static partial class Mixin {
1092
1093		public static ImageDebugHeaderEntry GetCodeViewEntry (this ImageDebugHeader header)
1094		{
1095			return GetEntry (header, ImageDebugType.CodeView);
1096		}
1097
1098		public static ImageDebugHeaderEntry GetDeterministicEntry (this ImageDebugHeader header)
1099		{
1100			return GetEntry (header, ImageDebugType.Deterministic);
1101		}
1102
1103		public static ImageDebugHeader AddDeterministicEntry (this ImageDebugHeader header)
1104		{
1105			var entry = new ImageDebugHeaderEntry (new ImageDebugDirectory { Type = ImageDebugType.Deterministic }, Empty<byte>.Array);
1106			if (header == null)
1107				return new ImageDebugHeader (entry);
1108
1109			var entries = new ImageDebugHeaderEntry [header.Entries.Length + 1];
1110			Array.Copy (header.Entries, entries, header.Entries.Length);
1111			entries [entries.Length - 1] = entry;
1112			return new ImageDebugHeader (entries);
1113		}
1114
1115		public static ImageDebugHeaderEntry GetEmbeddedPortablePdbEntry (this ImageDebugHeader header)
1116		{
1117			return GetEntry (header, ImageDebugType.EmbeddedPortablePdb);
1118		}
1119
1120		private static ImageDebugHeaderEntry GetEntry (this ImageDebugHeader header, ImageDebugType type)
1121		{
1122			if (!header.HasEntries)
1123				return null;
1124
1125			for (var i = 0; i < header.Entries.Length; i++) {
1126				var entry = header.Entries [i];
1127				if (entry.Directory.Type == type)
1128					return entry;
1129			}
1130
1131			return null;
1132		}
1133
1134		public static string GetPdbFileName (string assemblyFileName)
1135		{
1136			return Path.ChangeExtension (assemblyFileName, ".pdb");
1137		}
1138
1139		public static string GetMdbFileName (string assemblyFileName)
1140		{
1141			return assemblyFileName + ".mdb";
1142		}
1143
1144		public static bool IsPortablePdb (string fileName)
1145		{
1146			using (var file = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
1147				return IsPortablePdb (file);
1148		}
1149
1150		public static bool IsPortablePdb (Stream stream)
1151		{
1152			const uint ppdb_signature = 0x424a5342;
1153
1154			if (stream.Length < 4) return false;
1155			var position = stream.Position;
1156			try {
1157				var reader = new BinaryReader (stream);
1158				return reader.ReadUInt32 () == ppdb_signature;
1159			} finally {
1160				stream.Position = position;
1161			}
1162		}
1163	}
1164}