Browse Source

Move assembly resolution logic to better place

WholeProjectDecompiler shall not care about checking whether an assembly
is in GAC.
pull/2031/head
dymanoid 5 years ago
parent
commit
b492a20442
  1. 4
      ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
  2. 4
      ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs
  3. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 28
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  5. 28
      ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs
  6. 70
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  7. 1
      ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
  8. 5
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  9. 4
      ILSpy/Languages/CSharpLanguage.cs
  10. 5
      ILSpy/LoadedAssembly.cs

4
ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs

@ -176,14 +176,12 @@ Remarks: @@ -176,14 +176,12 @@ Remarks:
int DecompileAsProject(string assemblyFileName, string outputDirectory)
{
var decompiler = new WholeProjectDecompiler() { Settings = GetSettings() };
var module = new PEFile(assemblyFileName);
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Reader.DetectTargetFrameworkId());
foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path);
}
decompiler.AssemblyResolver = resolver;
decompiler.DebugInfoProvider = TryLoadPDB(module);
var decompiler = new WholeProjectDecompiler(GetSettings(), resolver, TryLoadPDB(module));
decompiler.DecompileProject(module, outputDirectory);
return 0;
}

4
ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs

@ -78,9 +78,9 @@ namespace ICSharpCode.Decompiler.PowerShell @@ -78,9 +78,9 @@ namespace ICSharpCode.Decompiler.PowerShell
private void DoDecompile(string path)
{
WholeProjectDecompiler decompiler = new WholeProjectDecompiler();
PEFile module = Decompiler.TypeSystem.MainModule.PEFile;
decompiler.AssemblyResolver = new UniversalAssemblyResolver(module.FileName, false, module.Reader.DetectTargetFrameworkId());
var assemblyResolver = new UniversalAssemblyResolver(module.FileName, false, module.Reader.DetectTargetFrameworkId());
WholeProjectDecompiler decompiler = new WholeProjectDecompiler(assemblyResolver);
decompiler.ProgressIndicator = this;
fileName = module.FileName;
completed = 0;

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -88,6 +88,7 @@ @@ -88,6 +88,7 @@
<Compile Include="..\ILSpy\DebugInfo\DebugInfoUtils.cs" Link="DebugInfoUtils.cs" />
<Compile Include="..\ILSpy\DebugInfo\PortableDebugInfoProvider.cs" Link="PortableDebugInfoProvider.cs" />
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\ILPretty\Issue1681.cs" />
<None Include="TestCases\Pretty\IndexRangeTest.cs" />

28
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -148,16 +148,19 @@ namespace ICSharpCode.Decompiler.Tests @@ -148,16 +148,19 @@ namespace ICSharpCode.Decompiler.Tests
Stopwatch w = Stopwatch.StartNew();
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) {
PEFile module = new PEFile(file, fileStream, PEStreamOptions.PrefetchEntireImage);
var resolver = new UniversalAssemblyResolver(file, false, module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata);
var resolver = new TestAssemblyResolver(file, inputDir, module.Reader.DetectTargetFrameworkId());
resolver.AddSearchDirectory(inputDir);
resolver.RemoveSearchDirectory(".");
var decompiler = new TestProjectDecompiler(inputDir);
decompiler.AssemblyResolver = resolver;
// use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes
var projectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}");
// Let's limit the roundtrip tests to C# 7.3 for now; because 8.0 is still in preview
// and the generated project doesn't build as-is.
decompiler.Settings = new DecompilerSettings(LanguageVersion.CSharp7_3);
// use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes
decompiler.ProjectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}");
var settings = new DecompilerSettings(LanguageVersion.CSharp7_3);
var decompiler = new TestProjectDecompiler(projectGuid, resolver, settings);
if (snkFilePath != null) {
decompiler.StrongNameKeyFile = Path.Combine(inputDir, snkFilePath);
}
@ -260,18 +263,9 @@ namespace ICSharpCode.Decompiler.Tests @@ -260,18 +263,9 @@ namespace ICSharpCode.Decompiler.Tests
class TestProjectDecompiler : WholeProjectDecompiler
{
readonly string[] localAssemblies;
public TestProjectDecompiler(string baseDir)
{
localAssemblies = new DirectoryInfo(baseDir).EnumerateFiles("*.dll").Select(f => f.FullName).ToArray();
}
protected override bool IsGacAssembly(IAssemblyReference r, PEFile asm)
public TestProjectDecompiler(Guid projecGuid, IAssemblyResolver resolver, DecompilerSettings settings)
: base(settings, projecGuid, resolver, debugInfoProvider: null)
{
if (asm == null)
return false;
return !localAssemblies.Contains(asm.FileName);
}
}

28
ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.Decompiler.Tests
{
sealed class TestAssemblyResolver : UniversalAssemblyResolver
{
readonly HashSet<string> localAssemblies = new HashSet<string>();
public TestAssemblyResolver(string mainAssemblyFileName, string baseDir, string targetFramework)
: base(mainAssemblyFileName, false, targetFramework, PEStreamOptions.PrefetchMetadata, MetadataReaderOptions.ApplyWindowsRuntimeProjections)
{
var assemblyNames = new DirectoryInfo(baseDir).EnumerateFiles("*.dll").Select(f => Path.GetFileNameWithoutExtension(f.Name));
foreach (var name in assemblyNames) {
localAssemblies.Add(name);
}
}
public override bool IsGacAssembly(IAssemblyReference reference)
{
return reference != null && !localAssemblies.Contains(reference.Name);
}
}
}

70
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -45,18 +45,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -45,18 +45,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public class WholeProjectDecompiler
{
#region Settings
DecompilerSettings settings = new DecompilerSettings();
public DecompilerSettings Settings {
get {
return settings;
}
set {
if (value == null)
throw new ArgumentNullException();
settings = value;
}
}
/// <summary>
/// Gets the setting this instance uses for decompiling.
/// </summary>
public DecompilerSettings Settings { get; }
LanguageVersion? languageVersion;
@ -71,15 +63,14 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -71,15 +63,14 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
}
public IAssemblyResolver AssemblyResolver { get; set; }
public IAssemblyResolver AssemblyResolver { get; }
public IDebugInfoProvider DebugInfoProvider { get; set; }
public IDebugInfoProvider DebugInfoProvider { get; }
/// <summary>
/// The MSBuild ProjectGuid to use for the new project.
/// <c>null</c> to automatically generate a new GUID.
/// </summary>
public Guid? ProjectGuid { get; set; }
public Guid ProjectGuid { get; }
/// <summary>
/// Path to the snk file to use for signing.
@ -92,6 +83,31 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -92,6 +83,31 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public IProgress<DecompilationProgress> ProgressIndicator { get; set; }
#endregion
public WholeProjectDecompiler(IAssemblyResolver assemblyResolver)
: this(new DecompilerSettings(), assemblyResolver, debugInfoProvider: null)
{
}
public WholeProjectDecompiler(
DecompilerSettings settings,
IAssemblyResolver assemblyResolver,
IDebugInfoProvider debugInfoProvider)
: this(settings, Guid.NewGuid(), assemblyResolver, debugInfoProvider)
{
}
protected WholeProjectDecompiler(
DecompilerSettings settings,
Guid projectGuid,
IAssemblyResolver assemblyResolver,
IDebugInfoProvider debugInfoProvider)
{
Settings = settings ?? throw new ArgumentNullException(nameof(settings));
ProjectGuid = projectGuid;
AssemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver));
DebugInfoProvider = debugInfoProvider;
}
// per-run members
HashSet<string> directories = new HashSet<string>(Platform.FileNameComparer);
@ -132,7 +148,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -132,7 +148,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
string platformName = GetPlatformName(module);
Guid guid = this.ProjectGuid ?? Guid.NewGuid();
Guid guid = this.ProjectGuid;
var targetFramework = DetectTargetFramework(module);
List<Guid> typeGuids = new List<Guid>();
@ -228,7 +244,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -228,7 +244,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
w.WriteStartElement("Reference");
w.WriteAttributeString("Include", r.Name);
var asm = AssemblyResolver.Resolve(r);
if (!IsGacAssembly(r, asm)) {
if (!AssemblyResolver.IsGacAssembly(r)) {
if (asm != null) {
w.WriteElementString("HintPath", asm.FileName);
}
@ -313,18 +329,14 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -313,18 +329,14 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return result;
}
protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm)
{
return false;
}
#endregion
#region WriteCodeFilesInProject
protected virtual bool IncludeTypeWhenDecompilingProject(Metadata.PEFile module, TypeDefinitionHandle type)
protected virtual bool IncludeTypeWhenDecompilingProject(PEFile module, TypeDefinitionHandle type)
{
var metadata = module.Metadata;
var typeDef = metadata.GetTypeDefinition(type);
if (metadata.GetString(typeDef.Name) == "<Module>" || CSharpDecompiler.MemberIsHidden(module, type, settings))
if (metadata.GetString(typeDef.Name) == "<Module>" || CSharpDecompiler.MemberIsHidden(module, type, Settings))
return false;
if (metadata.GetString(typeDef.Namespace) == "XamlGeneratedNamespace" && metadata.GetString(typeDef.Name) == "GeneratedInternalTypeHelper")
return false;
@ -333,7 +345,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -333,7 +345,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts)
{
var decompiler = new CSharpDecompiler(ts, settings);
var decompiler = new CSharpDecompiler(ts, Settings);
decompiler.DebugInfoProvider = DebugInfoProvider;
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
decompiler.AstTransforms.Add(new RemoveCLSCompliantAttribute());
@ -352,7 +364,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -352,7 +364,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
Directory.CreateDirectory(Path.Combine(targetDirectory, prop));
string assemblyInfo = Path.Combine(prop, "AssemblyInfo.cs");
using (StreamWriter w = new StreamWriter(Path.Combine(targetDirectory, assemblyInfo))) {
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions));
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
return new[] { ("Compile", assemblyInfo) };
}
@ -374,8 +386,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -374,8 +386,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
}
}, StringComparer.OrdinalIgnoreCase).ToList();
int total = files.Count;
var progress = this.ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, settings);
var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach(
files,
new ParallelOptions {
@ -388,7 +400,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -388,7 +400,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
CSharpDecompiler decompiler = CreateDecompiler(ts);
decompiler.CancellationToken = cancellationToken;
var syntaxTree = decompiler.DecompileTypes(file.ToArray());
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, settings.CSharpFormattingOptions));
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
}

1
ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs

@ -46,6 +46,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -46,6 +46,7 @@ namespace ICSharpCode.Decompiler.Metadata
{
PEFile Resolve(IAssemblyReference reference);
PEFile ResolveModule(PEFile mainModule, string moduleName);
bool IsGacAssembly(IAssemblyReference reference);
}
public interface IAssemblyReference

5
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -173,6 +173,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -173,6 +173,11 @@ namespace ICSharpCode.Decompiler.Metadata
return new PEFile(moduleFileName, new FileStream(moduleFileName, FileMode.Open, FileAccess.Read), streamOptions, metadataOptions);
}
public virtual bool IsGacAssembly(IAssemblyReference reference)
{
return GetAssemblyInGac(reference) != null;
}
public string FindAssemblyFile(IAssemblyReference name)
{
if (name.IsWindowsRuntime) {

4
ILSpy/Languages/CSharpLanguage.cs

@ -438,12 +438,10 @@ namespace ICSharpCode.ILSpy @@ -438,12 +438,10 @@ namespace ICSharpCode.ILSpy
readonly DecompilationOptions options;
public ILSpyWholeProjectDecompiler(LoadedAssembly assembly, DecompilationOptions options)
: base(options.DecompilerSettings, assembly.GetAssemblyResolver(), assembly.GetDebugInfoOrNull())
{
this.assembly = assembly;
this.options = options;
base.Settings = options.DecompilerSettings;
base.AssemblyResolver = assembly.GetAssemblyResolver();
base.DebugInfoProvider = assembly.GetDebugInfoOrNull();
}
protected override IEnumerable<(string itemType, string fileName)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)

5
ILSpy/LoadedAssembly.cs

@ -253,6 +253,11 @@ namespace ICSharpCode.ILSpy @@ -253,6 +253,11 @@ namespace ICSharpCode.ILSpy
this.parent = parent;
}
public bool IsGacAssembly(IAssemblyReference reference)
{
return parent.universalResolver?.IsGacAssembly(reference) == true;
}
public PEFile Resolve(Decompiler.Metadata.IAssemblyReference reference)
{
return parent.LookupReferencedAssembly(reference)?.GetPEFileOrNull();

Loading…
Cancel
Save