Browse Source

Merge pull request #2301 from icsharpcode/async-resolve

async IAssemblyResolver
pull/2308/head
Daniel Grunwald 4 years ago committed by GitHub
parent
commit
0c23ab1998
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
  2. 36
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  3. 50
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  4. 8
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs
  5. 11
      ILSpy/Analyzers/AnalyzerScope.cs
  6. 178
      ILSpy/AssemblyList.cs
  7. 7
      ILSpy/AssemblyListManager.cs
  8. 141
      ILSpy/Languages/CSharpLanguage.cs
  9. 25
      ILSpy/Languages/ILLanguage.cs
  10. 423
      ILSpy/LoadedAssembly.cs
  11. 9
      ILSpy/LoadedAssemblyExtensions.cs
  12. 28
      ILSpy/LoadedPackage.cs
  13. 2
      ILSpy/MainWindow.xaml.cs
  14. 41
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  15. 7
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  16. 9
      ILSpy/TreeNodes/ModuleReferenceTreeNode.cs
  17. 2
      ILSpy/ViewModels/ManageAssemblyListsViewModel.cs

3
ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs

@ -23,6 +23,7 @@ using System.Reflection; @@ -23,6 +23,7 @@ using System.Reflection;
using System.Reflection.Metadata;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Metadata
{
@ -47,6 +48,8 @@ namespace ICSharpCode.Decompiler.Metadata @@ -47,6 +48,8 @@ namespace ICSharpCode.Decompiler.Metadata
#if !VSADDIN
PEFile Resolve(IAssemblyReference reference);
PEFile ResolveModule(PEFile mainModule, string moduleName);
Task<PEFile> ResolveAsync(IAssemblyReference reference);
Task<PEFile> ResolveModuleAsync(PEFile mainModule, string moduleName);
#endif
}

36
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -24,6 +24,7 @@ using System.Reflection.Metadata; @@ -24,6 +24,7 @@ using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ICSharpCode.Decompiler.Metadata
{
@ -67,7 +68,6 @@ namespace ICSharpCode.Decompiler.Metadata @@ -67,7 +68,6 @@ namespace ICSharpCode.Decompiler.Metadata
readonly string baseDirectory;
readonly List<string> directories = new List<string>();
static readonly List<string> gac_paths = GetGacPaths();
HashSet<string> targetFrameworkSearchPaths;
static readonly DecompilerRuntime decompilerRuntime;
public void AddSearchDirectory(string directory)
@ -87,9 +87,9 @@ namespace ICSharpCode.Decompiler.Metadata @@ -87,9 +87,9 @@ namespace ICSharpCode.Decompiler.Metadata
return directories.ToArray();
}
string targetFramework;
TargetFrameworkIdentifier targetFrameworkIdentifier;
Version targetFrameworkVersion;
readonly string targetFramework;
readonly TargetFrameworkIdentifier targetFrameworkIdentifier;
readonly Version targetFrameworkVersion;
/// <summary>
/// Creates a new instance of the <see cref="UniversalAssemblyResolver"/>.
@ -204,6 +204,16 @@ namespace ICSharpCode.Decompiler.Metadata @@ -204,6 +204,16 @@ namespace ICSharpCode.Decompiler.Metadata
}
return new PEFile(moduleFileName, new FileStream(moduleFileName, FileMode.Open, FileAccess.Read), streamOptions, metadataOptions);
}
public Task<PEFile> ResolveAsync(IAssemblyReference name)
{
return Task.Run(() => Resolve(name));
}
public Task<PEFile> ResolveModuleAsync(PEFile mainModule, string moduleName)
{
return Task.Run(() => ResolveModule(mainModule, moduleName));
}
#endif
public override bool IsSharedAssembly(IAssemblyReference reference, out string runtimePack)
@ -301,26 +311,20 @@ namespace ICSharpCode.Decompiler.Metadata @@ -301,26 +311,20 @@ namespace ICSharpCode.Decompiler.Metadata
return null;
}
void AddTargetFrameworkSearchPathIfExists(string path)
{
if (targetFrameworkSearchPaths == null)
{
targetFrameworkSearchPaths = new HashSet<string>();
}
if (Directory.Exists(path))
targetFrameworkSearchPaths.Add(path);
}
/// <summary>
/// This only works on Windows
/// </summary>
string ResolveSilverlight(IAssemblyReference name, Version version)
{
AddTargetFrameworkSearchPathIfExists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Silverlight"));
AddTargetFrameworkSearchPathIfExists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Silverlight"));
string[] targetFrameworkSearchPaths = {
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Microsoft Silverlight"),
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Microsoft Silverlight")
};
foreach (var baseDirectory in targetFrameworkSearchPaths)
{
if (!Directory.Exists(baseDirectory))
continue;
var versionDirectory = Path.Combine(baseDirectory, FindClosestVersionDirectory(baseDirectory, version));
var file = SearchDirectory(name, versionDirectory);
if (file != null)

50
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
@ -181,7 +182,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -181,7 +182,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
// Load referenced assemblies and type-forwarder references.
// This is necessary to make .NET Core/PCL binaries work better.
var referencedAssemblies = new List<PEFile>();
var assemblyReferenceQueue = new Queue<(bool IsAssembly, PEFile MainModule, object Reference)>();
var assemblyReferenceQueue = new Queue<(bool IsAssembly, PEFile MainModule, object Reference, Task<PEFile> ResolveTask)>();
var comparer = KeyComparer.Create(((bool IsAssembly, PEFile MainModule, object Reference) reference) =>
reference.IsAssembly ? "A:" + ((AssemblyReference)reference.Reference).FullName :
"M:" + reference.Reference);
var assemblyReferencesInQueue = new HashSet<(bool IsAssembly, PEFile Parent, object Reference)>(comparer);
var mainMetadata = mainModule.Metadata;
foreach (var h in mainMetadata.GetModuleReferences())
{
@ -194,7 +199,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -194,7 +199,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
var file = mainMetadata.GetAssemblyFile(fileHandle);
if (mainMetadata.StringComparer.Equals(file.Name, moduleName) && file.ContainsMetadata)
{
assemblyReferenceQueue.Enqueue((false, mainModule, moduleName));
AddToQueue(false, mainModule, moduleName);
break;
}
}
@ -205,26 +210,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -205,26 +210,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
foreach (var refs in mainModule.AssemblyReferences)
{
assemblyReferenceQueue.Enqueue((true, mainModule, refs));
AddToQueue(true, mainModule, refs);
}
var comparer = KeyComparer.Create(((bool IsAssembly, PEFile MainModule, object Reference) reference) =>
reference.IsAssembly ? "A:" + ((AssemblyReference)reference.Reference).FullName :
"M:" + reference.Reference);
var processedAssemblyReferences = new HashSet<(bool IsAssembly, PEFile Parent, object Reference)>(comparer);
while (assemblyReferenceQueue.Count > 0)
{
var asmRef = assemblyReferenceQueue.Dequeue();
if (!processedAssemblyReferences.Add(asmRef))
continue;
PEFile asm;
if (asmRef.IsAssembly)
{
asm = assemblyResolver.Resolve((AssemblyReference)asmRef.Reference);
}
else
{
asm = assemblyResolver.ResolveModule(asmRef.MainModule, (string)asmRef.Reference);
}
var asm = asmRef.ResolveTask.GetAwaiter().GetResult();
if (asm != null)
{
referencedAssemblies.Add(asm);
@ -235,11 +226,11 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -235,11 +226,11 @@ namespace ICSharpCode.Decompiler.TypeSystem
switch (exportedType.Implementation.Kind)
{
case SRM.HandleKind.AssemblyReference:
assemblyReferenceQueue.Enqueue((true, asm, new AssemblyReference(asm, (SRM.AssemblyReferenceHandle)exportedType.Implementation)));
AddToQueue(true, asm, new AssemblyReference(asm, (SRM.AssemblyReferenceHandle)exportedType.Implementation));
break;
case SRM.HandleKind.AssemblyFile:
var file = metadata.GetAssemblyFile((SRM.AssemblyFileHandle)exportedType.Implementation);
assemblyReferenceQueue.Enqueue((false, asm, metadata.GetString(file.Name)));
AddToQueue(false, asm, metadata.GetString(file.Name));
break;
}
}
@ -261,6 +252,25 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -261,6 +252,25 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
this.MainModule = (MetadataModule)base.MainModule;
void AddToQueue(bool isAssembly, PEFile mainModule, object reference)
{
if (assemblyReferencesInQueue.Add((isAssembly, mainModule, reference)))
{
// Immediately start loading the referenced module as we add the entry to the queue.
// This allows loading multiple modules in parallel.
Task<PEFile> asm;
if (isAssembly)
{
asm = assemblyResolver.ResolveAsync((AssemblyReference)reference);
}
else
{
asm = assemblyResolver.ResolveModuleAsync(mainModule, (string)reference);
}
assemblyReferenceQueue.Enqueue((isAssembly, mainModule, reference, asm));
}
}
bool IsMissing(KnownTypeReference knownType)
{
var name = knownType.TypeName;

8
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -212,17 +212,17 @@ namespace ICSharpCode.ILSpy.ReadyToRun @@ -212,17 +212,17 @@ namespace ICSharpCode.ILSpy.ReadyToRun
private class ReadyToRunAssemblyResolver : ILCompiler.Reflection.ReadyToRun.IAssemblyResolver
{
private LoadedAssembly loadedAssembly;
private Decompiler.Metadata.IAssemblyResolver assemblyResolver;
public ReadyToRunAssemblyResolver(LoadedAssembly loadedAssembly)
{
this.loadedAssembly = loadedAssembly;
assemblyResolver = loadedAssembly.GetAssemblyResolver();
}
public IAssemblyMetadata FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile)
{
LoadedAssembly loadedAssembly = this.loadedAssembly.LookupReferencedAssembly(new Decompiler.Metadata.AssemblyReference(metadataReader, assemblyReferenceHandle));
PEReader reader = loadedAssembly?.GetPEFileOrNull()?.Reader;
PEFile module = assemblyResolver.Resolve(new Decompiler.Metadata.AssemblyReference(metadataReader, assemblyReferenceHandle));
PEReader reader = module?.Reader;
return reader == null ? null : new StandaloneAssemblyMetadata(reader);
}

11
ILSpy/Analyzers/AnalyzerScope.cs

@ -147,16 +147,13 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -147,16 +147,13 @@ namespace ICSharpCode.ILSpy.Analyzers
continue;
if (checkedFiles.Contains(module))
continue;
var resolver = assembly.GetAssemblyResolver();
var resolver = assembly.GetAssemblyResolver(loadOnDemand: false);
foreach (var reference in module.AssemblyReferences)
{
using (LoadedAssembly.DisableAssemblyLoad(AssemblyList))
if (resolver.Resolve(reference) == curFile)
{
if (resolver.Resolve(reference) == curFile)
{
found = true;
break;
}
found = true;
break;
}
}
if (found && checkedFiles.Add(module))

178
ILSpy/AssemblyList.cs

@ -17,12 +17,13 @@ @@ -17,12 +17,13 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Xml.Linq;
@ -39,8 +40,7 @@ namespace ICSharpCode.ILSpy @@ -39,8 +40,7 @@ namespace ICSharpCode.ILSpy
/// <summary>Dirty flag, used to mark modifications so that the list is saved later</summary>
bool dirty;
internal readonly ConcurrentDictionary<(string assemblyName, bool isWinRT, string targetFrameworkIdentifier), LoadedAssembly> assemblyLookupCache = new ConcurrentDictionary<(string assemblyName, bool isWinRT, string targetFrameworkIdentifier), LoadedAssembly>();
internal readonly ConcurrentDictionary<string, LoadedAssembly> moduleLookupCache = new ConcurrentDictionary<string, LoadedAssembly>();
readonly object lockObj = new object();
/// <summary>
/// The assemblies in this list.
@ -51,7 +51,14 @@ namespace ICSharpCode.ILSpy @@ -51,7 +51,14 @@ namespace ICSharpCode.ILSpy
/// Technically read accesses need locking when done on non-GUI threads... but whenever possible, use the
/// thread-safe <see cref="GetAssemblies()"/> method.
/// </remarks>
internal readonly ObservableCollection<LoadedAssembly> assemblies = new ObservableCollection<LoadedAssembly>();
readonly ObservableCollection<LoadedAssembly> assemblies = new ObservableCollection<LoadedAssembly>();
/// <summary>
/// Assembly lookup by filename.
/// Usually byFilename.Values == assemblies; but when an assembly is loaded by a background thread,
/// that assembly is added to byFilename immediately, and to assemblies only later on the main thread.
/// </summary>
readonly Dictionary<string, LoadedAssembly> byFilename = new Dictionary<string, LoadedAssembly>(StringComparer.OrdinalIgnoreCase);
public AssemblyList(string listName)
{
@ -81,17 +88,37 @@ namespace ICSharpCode.ILSpy @@ -81,17 +88,37 @@ namespace ICSharpCode.ILSpy
this.assemblies.AddRange(list.assemblies);
}
public event NotifyCollectionChangedEventHandler CollectionChanged {
add {
App.Current.Dispatcher.VerifyAccess();
this.assemblies.CollectionChanged += value;
}
remove {
App.Current.Dispatcher.VerifyAccess();
this.assemblies.CollectionChanged -= value;
}
}
/// <summary>
/// Gets the loaded assemblies. This method is thread-safe.
/// </summary>
public LoadedAssembly[] GetAssemblies()
{
lock (assemblies)
lock (lockObj)
{
return assemblies.ToArray();
}
}
public int Count {
get {
lock (lockObj)
{
return assemblies.Count;
}
}
}
/// <summary>
/// Saves this assembly list to XML.
/// </summary>
@ -111,9 +138,30 @@ namespace ICSharpCode.ILSpy @@ -111,9 +138,30 @@ namespace ICSharpCode.ILSpy
get { return listName; }
}
internal void Move(LoadedAssembly[] assembliesToMove, int index)
{
App.Current.Dispatcher.VerifyAccess();
lock (lockObj)
{
foreach (LoadedAssembly asm in assembliesToMove)
{
int nodeIndex = assemblies.IndexOf(asm);
Debug.Assert(nodeIndex >= 0);
if (nodeIndex < index)
index--;
assemblies.RemoveAt(nodeIndex);
}
Array.Reverse(assembliesToMove);
foreach (LoadedAssembly asm in assembliesToMove)
{
assemblies.Insert(index, asm);
}
}
}
void Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ClearCache();
Debug.Assert(Monitor.IsEntered(lockObj));
if (CollectionChangeHasEffectOnSave(e))
{
RefreshSave();
@ -155,10 +203,18 @@ namespace ICSharpCode.ILSpy @@ -155,10 +203,18 @@ namespace ICSharpCode.ILSpy
}
}
internal void ClearCache()
/// <summary>
/// Find an assembly that was previously opened.
/// </summary>
public LoadedAssembly FindAssembly(string file)
{
assemblyLookupCache.Clear();
moduleLookupCache.Clear();
file = Path.GetFullPath(file);
lock (lockObj)
{
if (byFilename.TryGetValue(file, out var asm))
return asm;
}
return null;
}
public LoadedAssembly Open(string assemblyUri, bool isAutoLoaded = false)
@ -170,25 +226,18 @@ namespace ICSharpCode.ILSpy @@ -170,25 +226,18 @@ namespace ICSharpCode.ILSpy
/// Opens an assembly from disk.
/// Returns the existing assembly node if it is already loaded.
/// </summary>
/// <remarks>
/// If called on the UI thread, the newly opened assembly is added to the list synchronously.
/// If called on another thread, the newly opened assembly won't be returned by GetAssemblies()
/// until the UI thread gets around to adding the assembly.
/// </remarks>
public LoadedAssembly OpenAssembly(string file, bool isAutoLoaded = false)
{
App.Current.Dispatcher.VerifyAccess();
file = Path.GetFullPath(file);
foreach (LoadedAssembly asm in this.assemblies)
{
if (file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase))
return asm;
}
var newAsm = new LoadedAssembly(this, file);
newAsm.IsAutoLoaded = isAutoLoaded;
lock (assemblies)
{
this.assemblies.Add(newAsm);
}
return newAsm;
return OpenAssembly(file, () => {
var newAsm = new LoadedAssembly(this, file);
newAsm.IsAutoLoaded = isAutoLoaded;
return newAsm;
});
}
/// <summary>
@ -196,21 +245,42 @@ namespace ICSharpCode.ILSpy @@ -196,21 +245,42 @@ namespace ICSharpCode.ILSpy
/// </summary>
public LoadedAssembly OpenAssembly(string file, Stream stream, bool isAutoLoaded = false)
{
App.Current.Dispatcher.VerifyAccess();
return OpenAssembly(file, () => {
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = isAutoLoaded;
return newAsm;
});
}
foreach (LoadedAssembly asm in this.assemblies)
LoadedAssembly OpenAssembly(string file, Func<LoadedAssembly> load)
{
file = Path.GetFullPath(file);
bool isUIThread = App.Current.Dispatcher.Thread == Thread.CurrentThread;
LoadedAssembly asm;
lock (lockObj)
{
if (file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase))
if (byFilename.TryGetValue(file, out asm))
return asm;
asm = load();
Debug.Assert(asm.FileName == file);
byFilename.Add(asm.FileName, asm);
if (isUIThread)
{
assemblies.Add(asm);
}
}
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = isAutoLoaded;
lock (assemblies)
if (!isUIThread)
{
this.assemblies.Add(newAsm);
App.Current.Dispatcher.BeginInvoke((Action)delegate () {
lock (lockObj)
{
assemblies.Add(asm);
}
}, DispatcherPriority.Normal);
}
return newAsm;
return asm;
}
/// <summary>
@ -221,20 +291,22 @@ namespace ICSharpCode.ILSpy @@ -221,20 +291,22 @@ namespace ICSharpCode.ILSpy
{
App.Current.Dispatcher.VerifyAccess();
file = Path.GetFullPath(file);
var target = this.assemblies.FirstOrDefault(asm => file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase));
if (target == null)
return null;
var index = this.assemblies.IndexOf(target);
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = target.IsAutoLoaded;
lock (assemblies)
lock (lockObj)
{
this.assemblies.Remove(target);
this.assemblies.Insert(index, newAsm);
if (!byFilename.TryGetValue(file, out LoadedAssembly target))
return null;
int index = this.assemblies.IndexOf(target);
if (index < 0)
return null;
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult(stream));
newAsm.IsAutoLoaded = target.IsAutoLoaded;
Debug.Assert(newAsm.FileName == file);
byFilename[file] = newAsm;
this.assemblies[index] = newAsm;
return newAsm;
}
return newAsm;
}
public LoadedAssembly ReloadAssembly(string file)
@ -251,10 +323,13 @@ namespace ICSharpCode.ILSpy @@ -251,10 +323,13 @@ namespace ICSharpCode.ILSpy
public LoadedAssembly ReloadAssembly(LoadedAssembly target)
{
App.Current.Dispatcher.VerifyAccess();
var index = this.assemblies.IndexOf(target);
if (index < 0)
return null;
var newAsm = new LoadedAssembly(this, target.FileName, pdbFileName: target.PdbFileName);
newAsm.IsAutoLoaded = target.IsAutoLoaded;
lock (assemblies)
lock (lockObj)
{
this.assemblies.Remove(target);
this.assemblies.Insert(index, newAsm);
@ -265,16 +340,17 @@ namespace ICSharpCode.ILSpy @@ -265,16 +340,17 @@ namespace ICSharpCode.ILSpy
public void Unload(LoadedAssembly assembly)
{
App.Current.Dispatcher.VerifyAccess();
lock (assemblies)
lock (lockObj)
{
assemblies.Remove(assembly);
byFilename.Remove(assembly.FileName);
}
RequestGC();
}
static bool gcRequested;
void RequestGC()
static void RequestGC()
{
if (gcRequested)
return;
@ -294,7 +370,7 @@ namespace ICSharpCode.ILSpy @@ -294,7 +370,7 @@ namespace ICSharpCode.ILSpy
public void Sort(int index, int count, IComparer<LoadedAssembly> comparer)
{
App.Current.Dispatcher.VerifyAccess();
lock (assemblies)
lock (lockObj)
{
List<LoadedAssembly> list = new List<LoadedAssembly>(assemblies);
list.Sort(index, Math.Min(count, list.Count - index), comparer);

7
ILSpy/AssemblyListManager.cs

@ -16,7 +16,6 @@ @@ -16,7 +16,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;
@ -165,7 +164,7 @@ namespace ICSharpCode.ILSpy @@ -165,7 +164,7 @@ namespace ICSharpCode.ILSpy
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.DotNet4List))
{
AssemblyList dotnet4 = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.DotNet4List);
if (dotnet4.assemblies.Count > 0)
if (dotnet4.Count > 0)
{
CreateList(dotnet4);
}
@ -174,7 +173,7 @@ namespace ICSharpCode.ILSpy @@ -174,7 +173,7 @@ namespace ICSharpCode.ILSpy
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.DotNet35List))
{
AssemblyList dotnet35 = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.DotNet35List);
if (dotnet35.assemblies.Count > 0)
if (dotnet35.Count > 0)
{
CreateList(dotnet35);
}
@ -183,7 +182,7 @@ namespace ICSharpCode.ILSpy @@ -183,7 +182,7 @@ namespace ICSharpCode.ILSpy
if (!AssemblyLists.Contains(ManageAssemblyListsViewModel.ASPDotNetMVC3List))
{
AssemblyList mvc = ManageAssemblyListsViewModel.CreateDefaultList(ManageAssemblyListsViewModel.ASPDotNetMVC3List);
if (mvc.assemblies.Count > 0)
if (mvc.Count > 0)
{
CreateList(mvc);
}

141
ILSpy/Languages/CSharpLanguage.cs

@ -404,86 +404,83 @@ namespace ICSharpCode.ILSpy @@ -404,86 +404,83 @@ namespace ICSharpCode.ILSpy
base.DecompileAssembly(assembly, output, options);
// don't automatically load additional assemblies when an assembly node is selected in the tree view
using (options.FullDecompilation ? null : LoadedAssembly.DisableAssemblyLoad(assembly.AssemblyList))
IAssemblyResolver assemblyResolver = assembly.GetAssemblyResolver(loadOnDemand: options.FullDecompilation);
var typeSystem = new DecompilerTypeSystem(module, assemblyResolver, options.DecompilerSettings);
var globalType = typeSystem.MainModule.TypeDefinitions.FirstOrDefault();
if (globalType != null)
{
IAssemblyResolver assemblyResolver = assembly.GetAssemblyResolver();
var typeSystem = new DecompilerTypeSystem(module, assemblyResolver, options.DecompilerSettings);
var globalType = typeSystem.MainModule.TypeDefinitions.FirstOrDefault();
if (globalType != null)
output.Write("// Global type: ");
output.WriteReference(globalType, globalType.FullName);
output.WriteLine();
}
var metadata = module.Metadata;
var corHeader = module.Reader.PEHeaders.CorHeader;
var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress);
if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition)
{
var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new Decompiler.TypeSystem.GenericContext());
if (entrypoint != null)
{
output.Write("// Global type: ");
output.WriteReference(globalType, globalType.FullName);
output.Write("// Entry point: ");
output.WriteReference(entrypoint, entrypoint.DeclaringType.FullName + "." + entrypoint.Name);
output.WriteLine();
}
var metadata = module.Metadata;
var corHeader = module.Reader.PEHeaders.CorHeader;
var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress);
if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition)
{
var entrypoint = typeSystem.MainModule.ResolveMethod(entrypointHandle, new Decompiler.TypeSystem.GenericContext());
if (entrypoint != null)
{
output.Write("// Entry point: ");
output.WriteReference(entrypoint, entrypoint.DeclaringType.FullName + "." + entrypoint.Name);
output.WriteLine();
}
}
output.WriteLine("// Architecture: " + GetPlatformDisplayName(module));
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0)
{
output.WriteLine("// This assembly contains unmanaged code.");
}
string runtimeName = GetRuntimeDisplayName(module);
if (runtimeName != null)
{
output.WriteLine("// Runtime: " + runtimeName);
}
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.StrongNameSigned) != 0)
{
output.WriteLine("// This assembly is signed with a strong name key.");
}
if (module.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible))
{
output.WriteLine("// This assembly was compiled using the /deterministic option.");
}
if (metadata.IsAssembly)
{
var asm = metadata.GetAssemblyDefinition();
if (asm.HashAlgorithm != AssemblyHashAlgorithm.None)
output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper());
if (!asm.PublicKey.IsNil)
{
output.Write("// Public key: ");
var reader = metadata.GetBlobReader(asm.PublicKey);
while (reader.RemainingBytes > 0)
output.Write(reader.ReadByte().ToString("x2"));
output.WriteLine();
}
}
var debugInfo = assembly.GetDebugInfoOrNull();
if (debugInfo != null)
}
output.WriteLine("// Architecture: " + GetPlatformDisplayName(module));
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0)
{
output.WriteLine("// This assembly contains unmanaged code.");
}
string runtimeName = GetRuntimeDisplayName(module);
if (runtimeName != null)
{
output.WriteLine("// Runtime: " + runtimeName);
}
if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.StrongNameSigned) != 0)
{
output.WriteLine("// This assembly is signed with a strong name key.");
}
if (module.Reader.ReadDebugDirectory().Any(d => d.Type == DebugDirectoryEntryType.Reproducible))
{
output.WriteLine("// This assembly was compiled using the /deterministic option.");
}
if (metadata.IsAssembly)
{
var asm = metadata.GetAssemblyDefinition();
if (asm.HashAlgorithm != AssemblyHashAlgorithm.None)
output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper());
if (!asm.PublicKey.IsNil)
{
output.WriteLine("// Debug info: " + debugInfo.Description);
output.Write("// Public key: ");
var reader = metadata.GetBlobReader(asm.PublicKey);
while (reader.RemainingBytes > 0)
output.Write(reader.ReadByte().ToString("x2"));
output.WriteLine();
}
output.WriteLine();
}
var debugInfo = assembly.GetDebugInfoOrNull();
if (debugInfo != null)
{
output.WriteLine("// Debug info: " + debugInfo.Description);
}
output.WriteLine();
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings);
decompiler.CancellationToken = options.CancellationToken;
if (options.EscapeInvalidIdentifiers)
{
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
}
SyntaxTree st;
if (options.FullDecompilation)
{
st = decompiler.DecompileWholeModuleAsSingleFile();
}
else
{
st = decompiler.DecompileModuleAndAssemblyAttributes();
}
WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem);
CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings);
decompiler.CancellationToken = options.CancellationToken;
if (options.EscapeInvalidIdentifiers)
{
decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers());
}
SyntaxTree st;
if (options.FullDecompilation)
{
st = decompiler.DecompileWholeModuleAsSingleFile();
}
else
{
st = decompiler.DecompileModuleAndAssemblyAttributes();
}
WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem);
return null;
}
}

25
ILSpy/Languages/ILLanguage.cs

@ -172,22 +172,19 @@ namespace ICSharpCode.ILSpy @@ -172,22 +172,19 @@ namespace ICSharpCode.ILSpy
}
// don't automatically load additional assemblies when an assembly node is selected in the tree view
using (options.FullDecompilation ? null : LoadedAssembly.DisableAssemblyLoad(assembly.AssemblyList))
dis.AssemblyResolver = module.GetAssemblyResolver(loadOnDemand: options.FullDecompilation);
dis.DebugInfo = module.GetDebugInfoOrNull();
if (options.FullDecompilation)
dis.WriteAssemblyReferences(metadata);
if (metadata.IsAssembly)
dis.WriteAssemblyHeader(module);
output.WriteLine();
dis.WriteModuleHeader(module);
if (options.FullDecompilation)
{
dis.AssemblyResolver = module.GetAssemblyResolver();
dis.DebugInfo = module.GetDebugInfoOrNull();
if (options.FullDecompilation)
dis.WriteAssemblyReferences(metadata);
if (metadata.IsAssembly)
dis.WriteAssemblyHeader(module);
output.WriteLine();
dis.WriteModuleHeader(module);
if (options.FullDecompilation)
{
output.WriteLine();
output.WriteLine();
dis.WriteModuleContents(module);
}
output.WriteLine();
dis.WriteModuleContents(module);
}
return null;
}

423
ILSpy/LoadedAssembly.cs

@ -26,7 +26,6 @@ using System.Reflection.PortableExecutable; @@ -26,7 +26,6 @@ using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
@ -95,7 +94,6 @@ namespace ICSharpCode.ILSpy @@ -95,7 +94,6 @@ namespace ICSharpCode.ILSpy
this.loadingTask = Task.Run(() => LoadAsync(stream)); // requires that this.fileName is set
this.shortName = Path.GetFileNameWithoutExtension(fileName);
this.resolver = new MyAssemblyResolver(this);
}
public LoadedAssembly(LoadedAssembly bundle, string fileName, Task<Stream> stream, IAssemblyResolver assemblyResolver = null)
@ -158,6 +156,24 @@ namespace ICSharpCode.ILSpy @@ -158,6 +156,24 @@ namespace ICSharpCode.ILSpy
}
}
/// <summary>
/// Gets the <see cref="PEFile"/>.
/// Returns null in case of load errors.
/// </summary>
public async Task<PEFile> GetPEFileOrNullAsync()
{
try
{
var loadResult = await loadingTask.ConfigureAwait(false);
return loadResult.PEFile;
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;
}
}
ICompilation typeSystem;
/// <summary>
@ -368,209 +384,118 @@ namespace ICSharpCode.ILSpy @@ -368,209 +384,118 @@ namespace ICSharpCode.ILSpy
return debugInfoProvider;
}
[ThreadStatic]
static int assemblyLoadDisableCount;
public static IDisposable DisableAssemblyLoad(AssemblyList assemblyList)
{
assemblyLoadDisableCount++;
return new DecrementAssemblyLoadDisableCount(assemblyList);
}
public static IDisposable DisableAssemblyLoad()
{
assemblyLoadDisableCount++;
return new DecrementAssemblyLoadDisableCount(MainWindow.Instance.CurrentAssemblyList);
}
sealed class DecrementAssemblyLoadDisableCount : IDisposable
{
bool disposed;
AssemblyList assemblyList;
public DecrementAssemblyLoadDisableCount(AssemblyList assemblyList)
{
this.assemblyList = assemblyList;
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
assemblyLoadDisableCount--;
// clear the lookup cache since we might have stored the lookups failed due to DisableAssemblyLoad()
assemblyList.ClearCache();
}
}
}
sealed class MyAssemblyResolver : IAssemblyResolver
{
readonly LoadedAssembly parent;
readonly bool loadOnDemand;
readonly IAssemblyResolver providedAssemblyResolver;
readonly AssemblyList assemblyList;
readonly LoadedAssembly[] alreadyLoadedAssemblies;
readonly Task<string> tfmTask;
readonly ReferenceLoadInfo referenceLoadInfo;
public MyAssemblyResolver(LoadedAssembly parent)
public MyAssemblyResolver(LoadedAssembly parent, bool loadOnDemand)
{
this.parent = parent;
this.loadOnDemand = loadOnDemand;
this.providedAssemblyResolver = parent.providedAssemblyResolver;
this.assemblyList = parent.assemblyList;
// Note: we cache a copy of the assembly list in the constructor, so that the
// resolve calls only search-by-asm-name in the assemblies that were already loaded
// at the time of the GetResolver() call.
this.alreadyLoadedAssemblies = assemblyList.GetAssemblies();
// If we didn't do this, we'd also search in the assemblies that we just started to load
// in previous Resolve() calls; but we don't want to wait for those to be loaded.
this.tfmTask = parent.GetTargetFrameworkIdAsync();
this.referenceLoadInfo = parent.LoadedAssemblyReferencesInfo;
}
public PEFile Resolve(IAssemblyReference reference)
{
var module = parent.providedAssemblyResolver?.Resolve(reference);
if (module != null)
return module;
return parent.LookupReferencedAssembly(reference)?.GetPEFileOrNull();
return ResolveAsync(reference).GetAwaiter().GetResult();
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
Dictionary<string, PEFile> asmLookupByFullName;
Dictionary<string, PEFile> asmLookupByShortName;
/// <summary>
/// 0) if we're inside a package, look for filename.dll in parent directories
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
/// 4) look in /dotnet/shared/{runtime-pack}/{closest-version}
/// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs
/// 7) Search the GAC
/// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 9) try to find match by asm name (no tfm/version) in loaded assemblies
/// </summary>
public async Task<PEFile> ResolveAsync(IAssemblyReference reference)
{
var module = parent.providedAssemblyResolver?.ResolveModule(mainModule, moduleName);
if (module != null)
return module;
return parent.LookupReferencedModule(mainModule, moduleName)?.GetPEFileOrNull();
}
}
readonly MyAssemblyResolver resolver;
public IAssemblyResolver GetAssemblyResolver()
{
return resolver;
}
public AssemblyReferenceClassifier GetAssemblyReferenceClassifier()
{
return universalResolver;
}
/// <summary>
/// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available.
/// </summary>
public IDebugInfoProvider GetDebugInfoOrNull()
{
if (GetPEFileOrNull() == null)
return null;
return debugInfoProvider;
}
public LoadedAssembly LookupReferencedAssembly(IAssemblyReference reference)
{
if (reference == null)
throw new ArgumentNullException(nameof(reference));
var tfm = GetTargetFrameworkIdAsync().Result;
if (reference.IsWindowsRuntime)
{
return assemblyList.assemblyLookupCache.GetOrAdd((reference.Name, true, tfm), key => LookupReferencedAssemblyInternal(reference, true, tfm));
}
else
{
return assemblyList.assemblyLookupCache.GetOrAdd((reference.FullName, false, tfm), key => LookupReferencedAssemblyInternal(reference, false, tfm));
}
}
public LoadedAssembly LookupReferencedModule(PEFile mainModule, string moduleName)
{
if (mainModule == null)
throw new ArgumentNullException(nameof(mainModule));
if (moduleName == null)
throw new ArgumentNullException(nameof(moduleName));
return assemblyList.moduleLookupCache.GetOrAdd(mainModule.FileName + ";" + moduleName, _ => LookupReferencedModuleInternal(mainModule, moduleName));
}
class MyUniversalResolver : UniversalAssemblyResolver
{
public MyUniversalResolver(LoadedAssembly assembly)
: base(assembly.FileName, false, assembly.GetTargetFrameworkIdAsync().Result, PEStreamOptions.PrefetchEntireImage, DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None)
{
}
}
PEFile module;
// 0) if we're inside a package, look for filename.dll in parent directories
if (providedAssemblyResolver != null)
{
module = await providedAssemblyResolver.ResolveAsync(reference).ConfigureAwait(false);
if (module != null)
return module;
}
static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
MyUniversalResolver universalResolver;
string tfm = await tfmTask.ConfigureAwait(false);
/// <summary>
/// 0) if we're inside a package, look for filename.dll in parent directories
/// (this step already happens in MyAssemblyResolver; not in LookupReferencedAssembly)
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
/// 4) look in /dotnet/shared/{runtime-pack}/{closest-version}
/// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs
/// 7) Search the GAC
/// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 9) try to find match by asm name (no tfm/version) in loaded assemblies
/// </summary>
LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm)
{
string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
bool isWinRT = reference.IsWindowsRuntime;
string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName);
string file;
LoadedAssembly asm;
lock (loadingAssemblies)
{
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies())
// 1) try to find exact match by tfm + full asm name in loaded assemblies
var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName);
if (lookup == null)
{
try
{
var module = loaded.GetPEFileOrNull();
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = loaded.GetTargetFrameworkIdAsync().Result + ";"
+ (isWinRT ? reader.GetString(asmDef.Name) : reader.GetFullAssemblyName());
if (key.Equals(asmDefName, StringComparison.OrdinalIgnoreCase))
{
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.FullName, MessageKind.Info, "Success - Found in Assembly List");
return loaded;
}
}
catch (BadImageFormatException)
{
continue;
}
lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup);
}
if (universalResolver == null)
if (lookup.TryGetValue(key, out module))
{
universalResolver = new MyUniversalResolver(this);
referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Info, "Success - Found in Assembly List");
return module;
}
file = universalResolver.FindAssemblyFile(fullName);
string file = parent.GetUniversalResolver().FindAssemblyFile(reference);
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies())
if (file != null)
{
if (loaded.FileName.Equals(file, StringComparison.OrdinalIgnoreCase))
// Load assembly from disk
LoadedAssembly asm;
if (loadOnDemand)
{
return loaded;
asm = assemblyList.OpenAssembly(file, isAutoLoaded: true);
}
else
{
asm = assemblyList.FindAssembly(file);
}
if (asm != null)
{
referenceLoadInfo.AddMessage(reference.ToString(), MessageKind.Info, "Success - Loading from: " + file);
return await asm.GetPEFileOrNullAsync().ConfigureAwait(false);
}
}
if (file != null && loadingAssemblies.TryGetValue(file, out asm))
return asm;
if (assemblyLoadDisableCount > 0)
return null;
if (file != null)
{
LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
}
else
{
// Assembly not found; try to find a similar-enough already-loaded assembly
var candidates = new List<(LoadedAssembly assembly, Version version)>();
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies())
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
{
var module = loaded.GetPEFileOrNull();
module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
if (fullName.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase))
if (reference.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase))
{
candidates.Add((loaded, asmDef.Version));
}
@ -578,99 +503,135 @@ namespace ICSharpCode.ILSpy @@ -578,99 +503,135 @@ namespace ICSharpCode.ILSpy
if (candidates.Count == 0)
{
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
referenceLoadInfo.AddMessageOnce(reference.ToString(), MessageKind.Error, "Could not find reference: " + reference);
return null;
}
candidates.SortBy(c => c.version);
var bestCandidate = candidates.FirstOrDefault(c => c.version >= fullName.Version).assembly ?? candidates.Last().assembly;
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return bestCandidate;
var bestCandidate = candidates.FirstOrDefault(c => c.version >= reference.Version).assembly ?? candidates.Last().assembly;
referenceLoadInfo.AddMessageOnce(reference.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return await bestCandidate.GetPEFileOrNullAsync().ConfigureAwait(false);
}
loadingAssemblies.Add(file, asm);
}
App.Current.Dispatcher.BeginInvoke((Action)delegate () {
lock (assemblyList.assemblies)
{
assemblyList.assemblies.Add(asm);
}
lock (loadingAssemblies)
{
loadingAssemblies.Remove(file);
}
}, DispatcherPriority.Normal);
return asm;
}
LoadedAssembly LookupReferencedModuleInternal(PEFile mainModule, string moduleName)
{
string file;
LoadedAssembly asm;
lock (loadingAssemblies)
private async Task<Dictionary<string, PEFile>> CreateLoadedAssemblyLookupAsync(bool shortNames)
{
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies())
var result = new Dictionary<string, PEFile>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
{
var reader = loaded.GetPEFileOrNull()?.Metadata;
if (reader == null || reader.IsAssembly)
continue;
var moduleDef = reader.GetModuleDefinition();
if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase))
try
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
string tfm = await loaded.GetTargetFrameworkIdAsync();
string key = tfm + ";"
+ (shortNames ? reader.GetString(asmDef.Name) : reader.GetFullAssemblyName());
if (!result.ContainsKey(key))
{
result.Add(key, module);
}
}
catch (BadImageFormatException)
{
LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List");
return loaded;
continue;
}
}
return result;
}
file = Path.Combine(Path.GetDirectoryName(mainModule.FileName), moduleName);
if (!File.Exists(file))
return null;
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
return ResolveModuleAsync(mainModule, moduleName).GetAwaiter().GetResult();
}
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies())
public async Task<PEFile> ResolveModuleAsync(PEFile mainModule, string moduleName)
{
if (providedAssemblyResolver != null)
{
if (loaded.FileName.Equals(file, StringComparison.OrdinalIgnoreCase))
{
return loaded;
}
var module = await providedAssemblyResolver.ResolveModuleAsync(mainModule, moduleName).ConfigureAwait(false);
if (module != null)
return module;
}
if (file != null && loadingAssemblies.TryGetValue(file, out asm))
return asm;
if (assemblyLoadDisableCount > 0)
return null;
if (file != null)
string file = Path.Combine(Path.GetDirectoryName(mainModule.FileName), moduleName);
if (File.Exists(file))
{
LoadedAssemblyReferencesInfo.AddMessage(moduleName, MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
// Load module from disk
LoadedAssembly asm;
if (loadOnDemand)
{
asm = assemblyList.OpenAssembly(file, isAutoLoaded: true);
}
else
{
asm = assemblyList.FindAssembly(file);
}
if (asm != null)
{
return await asm.GetPEFileOrNullAsync().ConfigureAwait(false);
}
}
else
{
LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Error, "Could not find reference: " + moduleName);
return null;
// Module does not exist on disk, look for one with a matching name in the assemblylist:
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || reader.IsAssembly)
continue;
var moduleDef = reader.GetModuleDefinition();
if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase))
{
referenceLoadInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List");
return module;
}
}
}
loadingAssemblies.Add(file, asm);
return null;
}
App.Current.Dispatcher.BeginInvoke((Action)delegate () {
lock (assemblyList.assemblies)
{
assemblyList.assemblies.Add(asm);
}
lock (loadingAssemblies)
{
loadingAssemblies.Remove(file);
}
});
return asm;
}
[Obsolete("Use GetPEFileAsync() or GetLoadResultAsync() instead")]
public Task ContinueWhenLoaded(Action<Task<PEFile>> onAssemblyLoaded, TaskScheduler taskScheduler)
public IAssemblyResolver GetAssemblyResolver(bool loadOnDemand = true)
{
return new MyAssemblyResolver(this, loadOnDemand);
}
private MyUniversalResolver GetUniversalResolver()
{
return LazyInitializer.EnsureInitialized(ref this.universalResolver, () => new MyUniversalResolver(this));
}
public AssemblyReferenceClassifier GetAssemblyReferenceClassifier()
{
return this.GetPEFileAsync().ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
return GetUniversalResolver();
}
/// <summary>
/// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available.
/// </summary>
public IDebugInfoProvider GetDebugInfoOrNull()
{
if (GetPEFileOrNull() == null)
return null;
return debugInfoProvider;
}
class MyUniversalResolver : UniversalAssemblyResolver
{
public MyUniversalResolver(LoadedAssembly assembly)
: base(assembly.FileName, false, assembly.GetTargetFrameworkIdAsync().Result, PEStreamOptions.PrefetchEntireImage, DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None)
{
}
}
MyUniversalResolver universalResolver;
/// <summary>
/// Wait until the assembly is loaded.
/// Throws an AggregateException when loading the assembly fails.

9
ILSpy/LoadedAssemblyExtensions.cs

@ -1,10 +1,5 @@ @@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
@ -31,9 +26,9 @@ namespace ICSharpCode.ILSpy @@ -31,9 +26,9 @@ namespace ICSharpCode.ILSpy
return Mono.Cecil.ModuleDefinition.ReadModule(new UnmanagedMemoryStream(image.Pointer, image.Length));
}
public static IAssemblyResolver GetAssemblyResolver(this PEFile file)
public static IAssemblyResolver GetAssemblyResolver(this PEFile file, bool loadOnDemand = true)
{
return GetLoadedAssembly(file).GetAssemblyResolver();
return GetLoadedAssembly(file).GetAssemblyResolver(loadOnDemand);
}
public static IDebugInfoProvider GetDebugInfoOrNull(this PEFile file)

28
ILSpy/LoadedPackage.cs

@ -240,6 +240,20 @@ namespace ICSharpCode.ILSpy @@ -240,6 +240,20 @@ namespace ICSharpCode.ILSpy
return parent?.Resolve(reference);
}
public Task<PEFile> ResolveAsync(IAssemblyReference reference)
{
var asm = ResolveFileName(reference.Name + ".dll");
if (asm != null)
{
return asm.GetPEFileOrNullAsync();
}
if (parent != null)
{
return parent.ResolveAsync(reference);
}
return null;
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
var asm = ResolveFileName(moduleName + ".dll");
@ -250,6 +264,20 @@ namespace ICSharpCode.ILSpy @@ -250,6 +264,20 @@ namespace ICSharpCode.ILSpy
return parent?.ResolveModule(mainModule, moduleName);
}
public Task<PEFile> ResolveModuleAsync(PEFile mainModule, string moduleName)
{
var asm = ResolveFileName(moduleName + ".dll");
if (asm != null)
{
return asm.GetPEFileOrNullAsync();
}
if (parent != null)
{
return parent.ResolveModuleAsync(mainModule, moduleName);
}
return null;
}
readonly Dictionary<string, LoadedAssembly> assemblies = new Dictionary<string, LoadedAssembly>(StringComparer.OrdinalIgnoreCase);
internal LoadedAssembly ResolveFileName(string name)

2
ILSpy/MainWindow.xaml.cs

@ -749,7 +749,7 @@ namespace ICSharpCode.ILSpy @@ -749,7 +749,7 @@ namespace ICSharpCode.ILSpy
history.Clear();
this.assemblyList = assemblyList;
assemblyList.assemblies.CollectionChanged += assemblyList_Assemblies_CollectionChanged;
assemblyList.CollectionChanged += assemblyList_Assemblies_CollectionChanged;
assemblyListTreeNode = new AssemblyListTreeNode(assemblyList);
assemblyListTreeNode.FilterSettings = sessionSettings.FilterSettings.Clone();

41
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
@ -45,17 +44,17 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -45,17 +44,17 @@ namespace ICSharpCode.ILSpy.TreeNodes
public AssemblyListTreeNode(AssemblyList assemblyList)
{
this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList));
BindToObservableCollection(assemblyList.assemblies);
BindToObservableCollection(assemblyList);
}
public override object Text {
get { return assemblyList.ListName; }
}
void BindToObservableCollection(ObservableCollection<LoadedAssembly> collection)
void BindToObservableCollection(AssemblyList collection)
{
this.Children.Clear();
this.Children.AddRange(collection.Select(a => new AssemblyTreeNode(a)));
this.Children.AddRange(collection.GetAssemblies().Select(a => new AssemblyTreeNode(a)));
collection.CollectionChanged += delegate (object sender, NotifyCollectionChangedEventArgs e) {
switch (e.Action)
{
@ -70,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -70,7 +69,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
throw new NotImplementedException();
case NotifyCollectionChangedAction.Reset:
this.Children.Clear();
this.Children.AddRange(collection.Select(a => new AssemblyTreeNode(a)));
this.Children.AddRange(collection.GetAssemblies().Select(a => new AssemblyTreeNode(a)));
break;
default:
throw new NotSupportedException("Invalid value for NotifyCollectionChangedAction");
@ -99,29 +98,15 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -99,29 +98,15 @@ namespace ICSharpCode.ILSpy.TreeNodes
files = e.Data.GetData(DataFormats.FileDrop) as string[];
if (files != null)
{
lock (assemblyList.assemblies)
{
var assemblies = files
.Where(file => file != null)
.Select(file => assemblyList.OpenAssembly(file))
.Where(asm => asm != null)
.Distinct()
.ToArray();
foreach (LoadedAssembly asm in assemblies)
{
int nodeIndex = assemblyList.assemblies.IndexOf(asm);
if (nodeIndex < index)
index--;
assemblyList.assemblies.RemoveAt(nodeIndex);
}
Array.Reverse(assemblies);
foreach (LoadedAssembly asm in assemblies)
{
assemblyList.assemblies.Insert(index, asm);
}
var nodes = assemblies.SelectArray(MainWindow.Instance.FindTreeNode);
MainWindow.Instance.SelectNodes(nodes);
}
var assemblies = files
.Where(file => file != null)
.Select(file => assemblyList.OpenAssembly(file))
.Where(asm => asm != null)
.Distinct()
.ToArray();
assemblyList.Move(assemblies, index);
var nodes = assemblies.SelectArray(MainWindow.Instance.FindTreeNode);
MainWindow.Instance.SelectNodes(nodes);
}
}

7
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
@ -460,10 +461,11 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -460,10 +461,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
return true;
}
public void Execute(TextViewContext context)
public async void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return;
var tasks = new List<Task>();
foreach (var node in context.SelectedTreeNodes)
{
var la = ((AssemblyTreeNode)node).LoadedAssembly;
@ -474,10 +476,11 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -474,10 +476,11 @@ namespace ICSharpCode.ILSpy.TreeNodes
var metadata = module.Metadata;
foreach (var assyRef in metadata.AssemblyReferences)
{
resolver.Resolve(new AssemblyReference(module, assyRef));
tasks.Add(resolver.ResolveAsync(new AssemblyReference(module, assyRef)));
}
}
}
await Task.WhenAll(tasks);
MainWindow.Instance.RefreshDecompiledView();
}
}

9
ILSpy/TreeNodes/ModuleReferenceTreeNode.cs

@ -71,8 +71,13 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -71,8 +71,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
var assemblyListNode = parentAssembly.Parent as AssemblyListTreeNode;
if (assemblyListNode != null && containsMetadata)
{
assemblyListNode.Select(assemblyListNode.FindAssemblyNode(parentAssembly.LoadedAssembly.LookupReferencedModule(parentAssembly.LoadedAssembly.GetPEFileOrNull(), metadata.GetString(reference.Name))));
e.Handled = true;
var resolver = parentAssembly.LoadedAssembly.GetAssemblyResolver();
var mainModule = parentAssembly.LoadedAssembly.GetPEFileOrNull();
if (mainModule != null)
{
assemblyListNode.Select(assemblyListNode.FindAssemblyNode(resolver.ResolveModule(mainModule, metadata.GetString(reference.Name))));
e.Handled = true;
}
}
}

2
ILSpy/ViewModels/ManageAssemblyListsViewModel.cs

@ -358,7 +358,7 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -358,7 +358,7 @@ namespace ICSharpCode.ILSpy.ViewModels
if (dlg.ShowDialog() == true)
{
var list = CreateDefaultList(config.Name, config.Path, dlg.ListName);
if (list.assemblies.Count > 0)
if (list.Count > 0)
{
manager.CreateList(list);
}

Loading…
Cancel
Save