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.

200 lines
6.2 KiB

  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. #nullable enable
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Collections.Immutable;
  22. using System.Linq;
  23. using System.Threading.Tasks;
  24. using ICSharpCode.Decompiler.Metadata;
  25. using ICSharpCode.Decompiler.Util;
  26. using ICSharpCode.ILSpyX.Extensions;
  27. namespace ICSharpCode.ILSpyX
  28. {
  29. class AssemblyListSnapshot
  30. {
  31. readonly ImmutableArray<LoadedAssembly> assemblies;
  32. Dictionary<string, PEFile>? asmLookupByFullName;
  33. Dictionary<string, PEFile>? asmLookupByShortName;
  34. Dictionary<string, List<(PEFile module, Version version)>>? asmLookupByShortNameGrouped;
  35. public ImmutableArray<LoadedAssembly> Assemblies => assemblies;
  36. public AssemblyListSnapshot(ImmutableArray<LoadedAssembly> assemblies)
  37. {
  38. this.assemblies = assemblies;
  39. }
  40. public async Task<PEFile?> TryGetModuleAsync(IAssemblyReference reference, string tfm)
  41. {
  42. bool isWinRT = reference.IsWindowsRuntime;
  43. if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
  44. {
  45. tfm = ".NETFramework,Version=v4";
  46. }
  47. string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName);
  48. var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName);
  49. if (lookup == null)
  50. {
  51. lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false);
  52. lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup);
  53. }
  54. if (lookup.TryGetValue(key, out PEFile? module))
  55. return module;
  56. return null;
  57. }
  58. public async Task<PEFile?> TryGetSimilarModuleAsync(IAssemblyReference reference)
  59. {
  60. var lookup = LazyInit.VolatileRead(ref asmLookupByShortNameGrouped);
  61. if (lookup == null)
  62. {
  63. lookup = await CreateLoadedAssemblyShortNameGroupLookupAsync().ConfigureAwait(false);
  64. lookup = LazyInit.GetOrSet(ref asmLookupByShortNameGrouped, lookup);
  65. }
  66. if (!lookup.TryGetValue(reference.Name, out var candidates))
  67. return null;
  68. return candidates.FirstOrDefault(c => c.version >= reference.Version).module ?? candidates.Last().module;
  69. }
  70. private async Task<Dictionary<string, PEFile>> CreateLoadedAssemblyLookupAsync(bool shortNames)
  71. {
  72. var result = new Dictionary<string, PEFile>(StringComparer.OrdinalIgnoreCase);
  73. foreach (LoadedAssembly loaded in assemblies)
  74. {
  75. try
  76. {
  77. var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
  78. if (module == null)
  79. continue;
  80. var reader = module.Metadata;
  81. if (reader == null || !reader.IsAssembly)
  82. continue;
  83. string tfm = await loaded.GetTargetFrameworkIdAsync().ConfigureAwait(false);
  84. if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
  85. {
  86. tfm = ".NETFramework,Version=v4";
  87. }
  88. string key = tfm + ";"
  89. + (shortNames ? module.Name : module.FullName);
  90. if (!result.ContainsKey(key))
  91. {
  92. result.Add(key, module);
  93. }
  94. }
  95. catch (BadImageFormatException)
  96. {
  97. continue;
  98. }
  99. }
  100. return result;
  101. }
  102. private async Task<Dictionary<string, List<(PEFile module, Version version)>>> CreateLoadedAssemblyShortNameGroupLookupAsync()
  103. {
  104. var result = new Dictionary<string, List<(PEFile module, Version version)>>(StringComparer.OrdinalIgnoreCase);
  105. foreach (LoadedAssembly loaded in assemblies)
  106. {
  107. try
  108. {
  109. var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
  110. var reader = module?.Metadata;
  111. if (reader == null || !reader.IsAssembly)
  112. continue;
  113. var asmDef = reader.GetAssemblyDefinition();
  114. var asmDefName = reader.GetString(asmDef.Name);
  115. var line = (module!, version: asmDef.Version);
  116. if (!result.TryGetValue(asmDefName, out var existing))
  117. {
  118. existing = new List<(PEFile module, Version version)>();
  119. result.Add(asmDefName, existing);
  120. existing.Add(line);
  121. continue;
  122. }
  123. int index = existing.BinarySearch(line.version, l => l.version);
  124. index = index < 0 ? ~index : index + 1;
  125. existing.Insert(index, line);
  126. }
  127. catch (BadImageFormatException)
  128. {
  129. continue;
  130. }
  131. }
  132. return result;
  133. }
  134. /// <summary>
  135. /// Gets all loaded assemblies recursively, including assemblies found in bundles or packages.
  136. /// </summary>
  137. public async Task<IList<LoadedAssembly>> GetAllAssembliesAsync()
  138. {
  139. var results = new List<LoadedAssembly>(assemblies.Length);
  140. foreach (var asm in assemblies)
  141. {
  142. LoadedAssembly.LoadResult result;
  143. try
  144. {
  145. result = await asm.GetLoadResultAsync().ConfigureAwait(false);
  146. }
  147. catch
  148. {
  149. results.Add(asm);
  150. continue;
  151. }
  152. if (result.Package != null)
  153. {
  154. AddDescendants(result.Package.RootFolder);
  155. }
  156. else if (result.PEFile != null)
  157. {
  158. results.Add(asm);
  159. }
  160. }
  161. void AddDescendants(PackageFolder folder)
  162. {
  163. foreach (var subFolder in folder.Folders)
  164. {
  165. AddDescendants(subFolder);
  166. }
  167. foreach (var entry in folder.Entries)
  168. {
  169. if (!entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
  170. continue;
  171. var asm = folder.ResolveFileName(entry.Name);
  172. if (asm == null)
  173. continue;
  174. results.Add(asm);
  175. }
  176. }
  177. return results;
  178. }
  179. }
  180. }