diff options
Diffstat (limited to 'src/SMAPI/Framework/ModLoading/Symbols')
3 files changed, 166 insertions, 0 deletions
diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs new file mode 100644 index 00000000..2171895d --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReader.cs @@ -0,0 +1,72 @@ +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Mono.Cecil.Pdb; + +namespace StardewModdingAPI.Framework.ModLoading.Symbols +{ + /// <summary>Reads symbol data for an assembly.</summary> + internal class SymbolReader : ISymbolReader + { + /********* + ** Fields + *********/ + /// <summary>The module for which to read symbols.</summary> + private readonly ModuleDefinition Module; + + /// <summary>The symbol file stream.</summary> + private readonly Stream Stream; + + /// <summary>The underlying symbol reader.</summary> + private ISymbolReader Reader; + + + /********* + ** Public methods + *********/ + /// <summary>Construct an instance.</summary> + /// <param name="module">The module for which to read symbols.</param> + /// <param name="stream">The symbol file stream.</param> + public SymbolReader(ModuleDefinition module, Stream stream) + { + this.Module = module; + this.Stream = stream; + this.Reader = new NativePdbReaderProvider().GetSymbolReader(module, stream); + } + + /// <summary>Get the symbol writer provider for the assembly.</summary> + public ISymbolWriterProvider GetWriterProvider() + { + return new PortablePdbWriterProvider(); + } + + /// <summary>Process a debug header in the symbol file.</summary> + /// <param name="header">The debug header.</param> + public bool ProcessDebugHeader(ImageDebugHeader header) + { + try + { + return this.Reader.ProcessDebugHeader(header); + } + catch + { + this.Reader.Dispose(); + this.Reader = new PortablePdbReaderProvider().GetSymbolReader(this.Module, this.Stream); + return this.Reader.ProcessDebugHeader(header); + } + } + + /// <summary>Read the method debug information for a method in the assembly.</summary> + /// <param name="method">The method definition.</param> + public MethodDebugInformation Read(MethodDefinition method) + { + return this.Reader.Read(method); + } + + /// <inheritdoc /> + public void Dispose() + { + this.Reader.Dispose(); + } + } +} diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs new file mode 100644 index 00000000..44074337 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolReaderProvider.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.Framework.ModLoading.Symbols +{ + /// <summary>Provides assembly symbol readers for Mono.Cecil.</summary> + internal class SymbolReaderProvider : ISymbolReaderProvider + { + /********* + ** Fields + *********/ + /// <summary>The underlying symbol reader provider.</summary> + private readonly ISymbolReaderProvider BaseProvider = new DefaultSymbolReaderProvider(throwIfNoSymbol: false); + + /// <summary>The symbol data loaded by absolute assembly path.</summary> + private readonly Dictionary<string, Stream> SymbolsByAssemblyPath = new Dictionary<string, Stream>(StringComparer.OrdinalIgnoreCase); + + + /********* + ** Public methods + *********/ + /// <summary>Add the symbol file for a given assembly name, if it's not already registered.</summary> + /// <param name="fileName">The assembly file name.</param> + /// <param name="getSymbolStream">Get the raw file stream for the symbols.</param> + public void TryAddSymbolData(string fileName, Func<Stream> getSymbolStream) + { + if (!this.SymbolsByAssemblyPath.ContainsKey(fileName)) + this.SymbolsByAssemblyPath.Add(fileName, getSymbolStream()); + } + + /// <summary>Get a symbol reader for a given module and assembly name.</summary> + /// <param name="module">The loaded assembly module.</param> + /// <param name="fileName">The assembly file name.</param> + public ISymbolReader GetSymbolReader(ModuleDefinition module, string fileName) + { + return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData) + ? new SymbolReader(module, symbolData) + : this.BaseProvider.GetSymbolReader(module, fileName); + } + + /// <summary>Get a symbol reader for a given module and symbol stream.</summary> + /// <param name="module">The loaded assembly module.</param> + /// <param name="symbolStream">The loaded symbol file stream.</param> + public ISymbolReader GetSymbolReader(ModuleDefinition module, Stream symbolStream) + { + return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData) + ? new SymbolReader(module, symbolData) + : this.BaseProvider.GetSymbolReader(module, symbolStream); + } + } +} diff --git a/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs b/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs new file mode 100644 index 00000000..8f7e05d1 --- /dev/null +++ b/src/SMAPI/Framework/ModLoading/Symbols/SymbolWriterProvider.cs @@ -0,0 +1,40 @@ +using System.IO; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace StardewModdingAPI.Framework.ModLoading.Symbols +{ + /// <summary>Provides assembly symbol writers for Mono.Cecil.</summary> + internal class SymbolWriterProvider : ISymbolWriterProvider + { + /********* + ** Fields + *********/ + /// <summary>The default symbol writer provider.</summary> + private readonly ISymbolWriterProvider DefaultProvider = new DefaultSymbolWriterProvider(); + + /// <summary>The symbol writer provider for the portable PDB format.</summary> + private readonly ISymbolWriterProvider PortablePdbProvider = new PortablePdbWriterProvider(); + + + /********* + ** Public methods + *********/ + /// <summary>Get a symbol writer for a given module and assembly path.</summary> + /// <param name="module">The loaded assembly module.</param> + /// <param name="fileName">The assembly name.</param> + public ISymbolWriter GetSymbolWriter(ModuleDefinition module, string fileName) + { + return this.DefaultProvider.GetSymbolWriter(module, fileName); + } + + /// <summary>Get a symbol writer for a given module and symbol stream.</summary> + /// <param name="module">The loaded assembly module.</param> + /// <param name="symbolStream">The loaded symbol file stream.</param> + public ISymbolWriter GetSymbolWriter(ModuleDefinition module, Stream symbolStream) + { + // Not implemented in default native pdb writer, so fallback to portable + return this.PortablePdbProvider.GetSymbolWriter(module, symbolStream); + } + } +} |