// 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 assemblies; Dictionary? asmLookupByFullName; Dictionary? asmLookupByShortName; Dictionary>? asmLookupByShortNameGrouped; public ImmutableArray Assemblies => assemblies; public AssemblyListSnapshot(ImmutableArray assemblies) { this.assemblies = assemblies; } public async Task 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 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> CreateLoadedAssemblyLookupAsync(bool shortNames) { var result = new Dictionary(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>> CreateLoadedAssemblyShortNameGroupLookupAsync() { var result = new Dictionary>(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; } /// /// Gets all loaded assemblies recursively, including assemblies found in bundles or packages. /// public async Task> GetAllAssembliesAsync() { var results = new List(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; } } }