minor refactoring in new symbol code

This commit is contained in:
Jesse Plamondon-Willard 2021-08-25 20:01:59 -04:00
parent e3b38a70f8
commit 0b29eb3bc3
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
4 changed files with 107 additions and 50 deletions

View File

@ -35,8 +35,11 @@ namespace StardewModdingAPI.Framework.ModLoading
/// <summary>A minimal assembly definition resolver which resolves references to known loaded assemblies.</summary>
private readonly AssemblyDefinitionResolver AssemblyDefinitionResolver;
private readonly SymbolReaderProvider SymbolReaderProvider;
private readonly SymbolWriterProvider SymbolWriterProvider;
/// <summary>Provides assembly symbol readers for Mono.Cecil.</summary>
private readonly SymbolReaderProvider SymbolReaderProvider = new SymbolReaderProvider();
/// <summary>Provides assembly symbol writers for Mono.Cecil.</summary>
private readonly SymbolWriterProvider SymbolWriterProvider = new SymbolWriterProvider();
/// <summary>The objects to dispose as part of this instance.</summary>
private readonly HashSet<IDisposable> Disposables = new HashSet<IDisposable>();
@ -65,9 +68,6 @@ namespace StardewModdingAPI.Framework.ModLoading
this.AssemblyDefinitionResolver = this.TrackForDisposal(new AssemblyDefinitionResolver());
Constants.ConfigureAssemblyResolver(this.AssemblyDefinitionResolver);
this.SymbolReaderProvider = new SymbolReaderProvider();
this.SymbolWriterProvider = new SymbolWriterProvider();
// generate type => assembly lookup for types which should be rewritten
this.TypeAssemblies = new Dictionary<string, Assembly>();
foreach (Assembly assembly in this.AssemblyMap.Targets)
@ -121,7 +121,7 @@ namespace StardewModdingAPI.Framework.ModLoading
// rewrite assembly
bool changed = this.RewriteAssembly(mod, assembly.Definition, loggedMessages, logPrefix: " ");
// detect broken assembly reference
foreach (AssemblyNameReference reference in assembly.Definition.MainModule.AssemblyReferences)
{
@ -142,10 +142,10 @@ namespace StardewModdingAPI.Framework.ModLoading
this.Monitor.Log($" Loading {assembly.File.Name} (rewritten)...", LogLevel.Trace);
// load assembly
using MemoryStream outStream = new MemoryStream();
using MemoryStream outAssemblyStream = new MemoryStream();
using MemoryStream outSymbolStream = new MemoryStream();
assembly.Definition.Write(outStream, new WriterParameters() { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider } );
byte[] bytes = outStream.ToArray();
assembly.Definition.Write(outAssemblyStream, new WriterParameters { WriteSymbols = true, SymbolStream = outSymbolStream, SymbolWriterProvider = this.SymbolWriterProvider });
byte[] bytes = outAssemblyStream.ToArray();
lastAssembly = Assembly.Load(bytes, outSymbolStream.ToArray());
}
else
@ -233,13 +233,13 @@ namespace StardewModdingAPI.Framework.ModLoading
if (!file.Exists)
yield break; // not a local assembly
// read assembly and PDB (if present)
// read assembly and symbols
byte[] assemblyBytes = File.ReadAllBytes(file.FullName);
Stream readStream = this.TrackForDisposal(new MemoryStream(assemblyBytes));
{
string symbolsPath = Path.Combine(Path.GetDirectoryName(file.FullName), Path.GetFileNameWithoutExtension(file.FullName)) + ".pdb";
if ( File.Exists( symbolsPath ) )
this.SymbolReaderProvider.AddSymbolMapping( Path.GetFileName( file.FullName ), this.TrackForDisposal( new MemoryStream( File.ReadAllBytes( symbolsPath ) ) ) );
FileInfo symbolsFile = new FileInfo(Path.Combine(Path.GetDirectoryName(file.FullName)!, Path.GetFileNameWithoutExtension(file.FullName)) + ".pdb");
if (symbolsFile.Exists)
this.SymbolReaderProvider.TryAddSymbolData(file.Name, this.TrackForDisposal(symbolsFile.OpenRead()));
}
AssemblyDefinition assembly = this.TrackForDisposal(AssemblyDefinition.ReadAssembly(readStream, new ReaderParameters(ReadingMode.Immediate) { AssemblyResolver = assemblyResolver, InMemory = true, ReadSymbols = true, SymbolReaderProvider = this.SymbolReaderProvider }));

View File

@ -1,4 +1,3 @@
using System;
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -6,46 +5,68 @@ using Mono.Cecil.Pdb;
namespace StardewModdingAPI.Framework.ModLoading.Symbols
{
/// <summary>Reads symbol data for an assembly.</summary>
internal class SymbolReader : ISymbolReader
{
private ModuleDefinition Module;
private Stream Stream;
private ISymbolReader Using;
/*********
** Fields
*********/
/// <summary>The module for which to read symbols.</summary>
private readonly ModuleDefinition Module;
public SymbolReader( ModuleDefinition module, Stream stream )
/// <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.Using = new NativePdbReaderProvider().GetSymbolReader( module, stream );
}
public void Dispose()
{
this.Using.Dispose();
this.Reader = new NativePdbReaderProvider().GetSymbolReader(module, stream);
}
/// <summary>Get the symbol writer provider for the assembly.</summary>
public ISymbolWriterProvider GetWriterProvider()
{
return new PortablePdbWriterProvider();
}
public bool ProcessDebugHeader( ImageDebugHeader header )
/// <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.Using.ProcessDebugHeader( header );
return this.Reader.ProcessDebugHeader(header);
}
catch (Exception e)
catch
{
this.Using.Dispose();
this.Using = new PortablePdbReaderProvider().GetSymbolReader( this.Module, this.Stream );
return this.Using.ProcessDebugHeader( header );
this.Reader.Dispose();
this.Reader = new PortablePdbReaderProvider().GetSymbolReader(this.Module, this.Stream);
return this.Reader.ProcessDebugHeader(header);
}
}
public MethodDebugInformation Read( MethodDefinition method )
/// <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.Using.Read( method );
return this.Reader.Read(method);
}
/// <inheritdoc />
public void Dispose()
{
this.Reader.Dispose();
}
}
}

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using Mono.Cecil;
@ -5,31 +6,48 @@ 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();
private readonly Dictionary<string, Stream> SymbolMapping = new Dictionary<string, Stream>();
/// <summary>The symbol data loaded by absolute assembly path.</summary>
private readonly Dictionary<string, Stream> SymbolsByAssemblyPath = new Dictionary<string, Stream>(StringComparer.OrdinalIgnoreCase);
public void AddSymbolMapping( string dllName, Stream symbolStream )
/*********
** 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="symbolStream">The raw file stream for the symbols.</param>
public void AddSymbolData(string fileName, Stream symbolStream)
{
this.SymbolMapping.Add( dllName, symbolStream );
this.SymbolsByAssemblyPath.Add(fileName, symbolStream);
}
public ISymbolReader GetSymbolReader( ModuleDefinition module, string fileName )
/// <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)
{
if ( this.SymbolMapping.ContainsKey( module.Name ) )
return new SymbolReader( module, this.SymbolMapping[ module.Name ] );
return this.BaseProvider.GetSymbolReader( module, fileName );
return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData)
? new SymbolReader(module, symbolData)
: this.BaseProvider.GetSymbolReader(module, fileName);
}
public ISymbolReader GetSymbolReader( ModuleDefinition module, Stream symbolStream )
/// <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)
{
if ( this.SymbolMapping.ContainsKey( module.Name ) )
return new SymbolReader( module, this.SymbolMapping[ module.Name ] );
return this.BaseProvider.GetSymbolReader( module, symbolStream );
return this.SymbolsByAssemblyPath.TryGetValue(module.Name, out Stream symbolData)
? new SymbolReader(module, symbolData)
: this.BaseProvider.GetSymbolReader(module, symbolStream);
}
}
}

View File

@ -4,19 +4,37 @@ using Mono.Cecil.Cil;
namespace StardewModdingAPI.Framework.ModLoading.Symbols
{
/// <summary>Provides assembly symbol writers for Mono.Cecil.</summary>
internal class SymbolWriterProvider : ISymbolWriterProvider
{
private readonly ISymbolWriterProvider BaseProvider = new DefaultSymbolWriterProvider();
/*********
** Fields
*********/
/// <summary>The default symbol writer provider.</summary>
private readonly ISymbolWriterProvider DefaultProvider = new DefaultSymbolWriterProvider();
public ISymbolWriter GetSymbolWriter( ModuleDefinition module, string fileName )
/// <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.BaseProvider.GetSymbolWriter( module, fileName );
return this.DefaultProvider.GetSymbolWriter(module, fileName);
}
public ISymbolWriter GetSymbolWriter( ModuleDefinition module, Stream symbolStream )
/// <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 new PortablePdbWriterProvider().GetSymbolWriter( module, symbolStream );
return this.PortablePdbProvider.GetSymbolWriter(module, symbolStream);
}
}
}