.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

201 lines
6.3 KiB

// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpyX.Extensions;
using ICSharpCode.ILSpyX.FileLoaders;
namespace ICSharpCode.ILSpyX
{
class AssemblyListSnapshot
{
readonly ImmutableArray<LoadedAssembly> assemblies;
Dictionary<string, MetadataFile>? asmLookupByFullName;
Dictionary<string, MetadataFile>? asmLookupByShortName;
Dictionary<string, List<(MetadataFile module, Version version)>>? asmLookupByShortNameGrouped;
public ImmutableArray<LoadedAssembly> Assemblies => assemblies;
public AssemblyListSnapshot(ImmutableArray<LoadedAssembly> assemblies)
{
this.assemblies = assemblies;
}
public async Task<MetadataFile?> TryGetModuleAsync(IAssemblyReference reference, string tfm)
{
bool isWinRT = reference.IsWindowsRuntime;
if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
{
tfm = ".NETFramework,Version=v4";
}
string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName);
var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName);
if (lookup == null)
{
lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup);
}
if (lookup.TryGetValue(key, out MetadataFile? module))
return module;
return null;
}
public async Task<MetadataFile?> TryGetSimilarModuleAsync(IAssemblyReference reference)
{
var lookup = LazyInit.VolatileRead(ref asmLookupByShortNameGrouped);
if (lookup == null)
{
lookup = await CreateLoadedAssemblyShortNameGroupLookupAsync().ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref asmLookupByShortNameGrouped, lookup);
}
if (!lookup.TryGetValue(reference.Name, out var candidates))
return null;
return candidates.FirstOrDefault(c => c.version >= reference.Version).module ?? candidates.Last().module;
}
private async Task<Dictionary<string, MetadataFile>> CreateLoadedAssemblyLookupAsync(bool shortNames)
{
var result = new Dictionary<string, MetadataFile>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in assemblies)
{
try
{
var module = await loaded.GetMetadataFileOrNullAsync().ConfigureAwait(false);
if (module == null)
continue;
var reader = module.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
string tfm = await loaded.GetTargetFrameworkIdAsync().ConfigureAwait(false);
if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
{
tfm = ".NETFramework,Version=v4";
}
string key = tfm + ";"
+ (shortNames ? module.Name : module.FullName);
if (!result.ContainsKey(key))
{
result.Add(key, module);
}
}
catch (BadImageFormatException)
{
continue;
}
}
return result;
}
private async Task<Dictionary<string, List<(MetadataFile module, Version version)>>> CreateLoadedAssemblyShortNameGroupLookupAsync()
{
var result = new Dictionary<string, List<(MetadataFile module, Version version)>>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in assemblies)
{
try
{
var module = await loaded.GetMetadataFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
var line = (module!, version: asmDef.Version);
if (!result.TryGetValue(asmDefName, out var existing))
{
existing = new List<(MetadataFile module, Version version)>();
result.Add(asmDefName, existing);
existing.Add(line);
continue;
}
int index = existing.BinarySearch(line.version, l => l.version);
index = index < 0 ? ~index : index + 1;
existing.Insert(index, line);
}
catch (BadImageFormatException)
{
continue;
}
}
return result;
}
/// <summary>
/// Gets all loaded assemblies recursively, including assemblies found in bundles or packages.
/// </summary>
public async Task<IList<LoadedAssembly>> GetAllAssembliesAsync()
{
var results = new List<LoadedAssembly>(assemblies.Length);
foreach (var asm in assemblies)
{
LoadResult result;
try
{
result = await asm.GetLoadResultAsync().ConfigureAwait(false);
}
catch
{
results.Add(asm);
continue;
}
if (result.Package != null)
{
AddDescendants(result.Package.RootFolder);
}
else if (result.MetadataFile != null)
{
results.Add(asm);
}
}
void AddDescendants(PackageFolder folder)
{
foreach (var subFolder in folder.Folders)
{
AddDescendants(subFolder);
}
foreach (var entry in folder.Entries)
{
if (!entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
continue;
var asm = folder.ResolveFileName(entry.Name);
if (asm == null)
continue;
results.Add(asm);
}
}
return results;
}
}
}