diff --git a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
index 8f409a956..684933a25 100644
--- a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
+++ b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
@@ -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;
}
diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs
index 70ecdc439..736f59933 100644
--- a/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs
+++ b/ICSharpCode.Decompiler.PowerShell/GetDecompiledProjectCmdlet.cs
@@ -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;
diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index b409376e0..4aaec9f79 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -88,6 +88,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
index 92e7a16c9..2139269c9 100644
--- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
+++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
@@ -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
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);
}
}
diff --git a/ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs b/ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs
new file mode 100644
index 000000000..d97c6d526
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestAssemblyResolver.cs
@@ -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 localAssemblies = new HashSet();
+
+ 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);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
index 1dfa9020b..5e024b790 100644
--- a/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
@@ -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;
- }
- }
+ ///
+ /// Gets the setting this instance uses for decompiling.
+ ///
+ public DecompilerSettings Settings { get; }
LanguageVersion? languageVersion;
@@ -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; }
///
/// The MSBuild ProjectGuid to use for the new project.
- /// null to automatically generate a new GUID.
///
- public Guid? ProjectGuid { get; set; }
+ public Guid ProjectGuid { get; }
///
/// Path to the snk file to use for signing.
@@ -92,6 +83,31 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public IProgress 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 directories = new HashSet(Platform.FileNameComparer);
@@ -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 typeGuids = new List();
@@ -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
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) == "" || CSharpDecompiler.MemberIsHidden(module, type, settings))
+ if (metadata.GetString(typeDef.Name) == "" || 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
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
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
}
}, 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
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);
}
diff --git a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
index 22d154960..d066778af 100644
--- a/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
+++ b/ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
@@ -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
diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
index 22fc24105..3186fa3d3 100644
--- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
+++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
@@ -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) {
diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs
index 7fd6fe6cb..52c7e4644 100644
--- a/ILSpy/Languages/CSharpLanguage.cs
+++ b/ILSpy/Languages/CSharpLanguage.cs
@@ -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)
diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs
index 4ce66d2b5..fc0e1bcdc 100644
--- a/ILSpy/LoadedAssembly.cs
+++ b/ILSpy/LoadedAssembly.cs
@@ -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();