/ILSpy/LoadedAssembly.cs

http://github.com/icsharpcode/ILSpy · C# · 439 lines · 330 code · 59 blank · 50 comment · 63 complexity · ee2cd19268bfda54532c6b44c6f72bc5 MD5 · raw file

  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. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.IO;
  22. using System.Reflection.Metadata;
  23. using System.Reflection.PortableExecutable;
  24. using System.Runtime.CompilerServices;
  25. using System.Runtime.InteropServices;
  26. using System.Threading;
  27. using System.Threading.Tasks;
  28. using ICSharpCode.Decompiler.DebugInfo;
  29. using ICSharpCode.Decompiler.Metadata;
  30. using ICSharpCode.Decompiler.PdbProvider;
  31. using ICSharpCode.Decompiler.TypeSystem;
  32. using ICSharpCode.Decompiler.TypeSystem.Implementation;
  33. using ICSharpCode.ILSpy.Options;
  34. namespace ICSharpCode.ILSpy
  35. {
  36. /// <summary>
  37. /// Represents an assembly loaded into ILSpy.
  38. /// </summary>
  39. [DebuggerDisplay("[LoadedAssembly {shortName}]")]
  40. public sealed class LoadedAssembly
  41. {
  42. internal static readonly ConditionalWeakTable<PEFile, LoadedAssembly> loadedAssemblies = new ConditionalWeakTable<PEFile, LoadedAssembly>();
  43. readonly Task<PEFile> assemblyTask;
  44. readonly AssemblyList assemblyList;
  45. readonly string fileName;
  46. readonly string shortName;
  47. public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null)
  48. {
  49. this.assemblyList = assemblyList ?? throw new ArgumentNullException(nameof(assemblyList));
  50. this.fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
  51. this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set
  52. this.shortName = Path.GetFileNameWithoutExtension(fileName);
  53. this.resolver = new MyAssemblyResolver(this);
  54. }
  55. /// <summary>
  56. /// Returns a target framework identifier in the form '&lt;framework&gt;Version=v&lt;version&gt;'.
  57. /// Returns an empty string if no TargetFrameworkAttribute was found or the file doesn't contain an assembly header, i.e., is only a module.
  58. /// </summary>
  59. public async Task<string> GetTargetFrameworkIdAsync()
  60. {
  61. var assembly = await GetPEFileAsync().ConfigureAwait(false);
  62. return assembly.DetectTargetFrameworkId() ?? string.Empty;
  63. }
  64. public ReferenceLoadInfo LoadedAssemblyReferencesInfo { get; } = new ReferenceLoadInfo();
  65. IDebugInfoProvider debugInfoProvider;
  66. /// <summary>
  67. /// Gets the <see cref="PEFile"/>.
  68. /// </summary>
  69. public Task<PEFile> GetPEFileAsync()
  70. {
  71. return assemblyTask;
  72. }
  73. /// <summary>
  74. /// Gets the <see cref="PEFile"/>.
  75. /// Returns null in case of load errors.
  76. /// </summary>
  77. public PEFile GetPEFileOrNull()
  78. {
  79. try {
  80. return GetPEFileAsync().GetAwaiter().GetResult();
  81. } catch (Exception ex) {
  82. System.Diagnostics.Trace.TraceError(ex.ToString());
  83. return null;
  84. }
  85. }
  86. ICompilation typeSystem;
  87. /// <summary>
  88. /// Gets a type system containing all types from this assembly + primitve types from mscorlib.
  89. /// Returns null in case of load errors.
  90. /// </summary>
  91. /// <remarks>
  92. /// This is an uncached type system.
  93. /// </remarks>
  94. public ICompilation GetTypeSystemOrNull()
  95. {
  96. if (typeSystem != null)
  97. return typeSystem;
  98. var module = GetPEFileOrNull();
  99. if (module == null)
  100. return null;
  101. return typeSystem = new SimpleCompilation(
  102. module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
  103. MinimalCorlib.Instance);
  104. }
  105. ICompilation typeSystemWithOptions;
  106. TypeSystemOptions currentTypeSystemOptions;
  107. public ICompilation GetTypeSystemOrNull(TypeSystemOptions options)
  108. {
  109. if (typeSystemWithOptions != null && options == currentTypeSystemOptions)
  110. return typeSystemWithOptions;
  111. var module = GetPEFileOrNull();
  112. if (module == null)
  113. return null;
  114. currentTypeSystemOptions = options;
  115. return typeSystemWithOptions = new SimpleCompilation(
  116. module.WithOptions(options | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers),
  117. MinimalCorlib.Instance);
  118. }
  119. public AssemblyList AssemblyList => assemblyList;
  120. public string FileName => fileName;
  121. public string ShortName => shortName;
  122. public string Text {
  123. get {
  124. if (IsLoaded && !HasLoadError) {
  125. PEFile module = GetPEFileOrNull();
  126. var metadata = module?.Metadata;
  127. string versionOrInfo = null;
  128. if (metadata != null) {
  129. if (metadata.IsAssembly) {
  130. versionOrInfo = metadata.GetAssemblyDefinition().Version?.ToString();
  131. var tfId = GetTargetFrameworkIdAsync().Result;
  132. if (!string.IsNullOrEmpty(tfId))
  133. versionOrInfo += ", " + tfId.Replace("Version=", " ");
  134. } else {
  135. versionOrInfo = ".netmodule";
  136. }
  137. }
  138. if (versionOrInfo == null)
  139. return ShortName;
  140. return string.Format("{0} ({1})", ShortName, versionOrInfo);
  141. } else {
  142. return ShortName;
  143. }
  144. }
  145. }
  146. public bool IsLoaded => assemblyTask.IsCompleted;
  147. public bool HasLoadError => assemblyTask.IsFaulted;
  148. public bool IsAutoLoaded { get; set; }
  149. public string PdbFileOverride { get; set; }
  150. PEFile LoadAssembly(object state)
  151. {
  152. MetadataReaderOptions options;
  153. if (DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections) {
  154. options = MetadataReaderOptions.ApplyWindowsRuntimeProjections;
  155. } else {
  156. options = MetadataReaderOptions.None;
  157. }
  158. PEFile module;
  159. // runs on background thread
  160. if (state is Stream stream) {
  161. // Read the module from a precrafted stream
  162. var streamOptions = stream is MemoryStream ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default;
  163. module = new PEFile(fileName, stream, streamOptions, metadataOptions: options);
  164. } else {
  165. // Read the module from disk (by default)
  166. stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
  167. module = new PEFile(fileName, stream, PEStreamOptions.PrefetchEntireImage,
  168. metadataOptions: options);
  169. }
  170. if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
  171. try {
  172. debugInfoProvider = DebugInfoUtils.FromFile(module, PdbFileOverride)
  173. ?? DebugInfoUtils.LoadSymbols(module);
  174. } catch (IOException) {
  175. } catch (UnauthorizedAccessException) {
  176. } catch (InvalidOperationException) {
  177. // ignore any errors during symbol loading
  178. }
  179. }
  180. lock (loadedAssemblies) {
  181. loadedAssemblies.Add(module, this);
  182. }
  183. return module;
  184. }
  185. [ThreadStatic]
  186. static int assemblyLoadDisableCount;
  187. public static IDisposable DisableAssemblyLoad(AssemblyList assemblyList)
  188. {
  189. assemblyLoadDisableCount++;
  190. return new DecrementAssemblyLoadDisableCount(assemblyList);
  191. }
  192. public static IDisposable DisableAssemblyLoad()
  193. {
  194. assemblyLoadDisableCount++;
  195. return new DecrementAssemblyLoadDisableCount(MainWindow.Instance.CurrentAssemblyList);
  196. }
  197. sealed class DecrementAssemblyLoadDisableCount : IDisposable
  198. {
  199. bool disposed;
  200. AssemblyList assemblyList;
  201. public DecrementAssemblyLoadDisableCount(AssemblyList assemblyList)
  202. {
  203. this.assemblyList = assemblyList;
  204. }
  205. public void Dispose()
  206. {
  207. if (!disposed) {
  208. disposed = true;
  209. assemblyLoadDisableCount--;
  210. // clear the lookup cache since we might have stored the lookups failed due to DisableAssemblyLoad()
  211. assemblyList.ClearCache();
  212. }
  213. }
  214. }
  215. sealed class MyAssemblyResolver : IAssemblyResolver
  216. {
  217. readonly LoadedAssembly parent;
  218. public MyAssemblyResolver(LoadedAssembly parent)
  219. {
  220. this.parent = parent;
  221. }
  222. public PEFile Resolve(Decompiler.Metadata.IAssemblyReference reference)
  223. {
  224. return parent.LookupReferencedAssembly(reference)?.GetPEFileOrNull();
  225. }
  226. public PEFile ResolveModule(PEFile mainModule, string moduleName)
  227. {
  228. return parent.LookupReferencedModule(mainModule, moduleName)?.GetPEFileOrNull();
  229. }
  230. }
  231. readonly MyAssemblyResolver resolver;
  232. public IAssemblyResolver GetAssemblyResolver()
  233. {
  234. return resolver;
  235. }
  236. /// <summary>
  237. /// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available.
  238. /// </summary>
  239. public IDebugInfoProvider GetDebugInfoOrNull()
  240. {
  241. if (GetPEFileOrNull() == null)
  242. return null;
  243. return debugInfoProvider;
  244. }
  245. public LoadedAssembly LookupReferencedAssembly(IAssemblyReference reference)
  246. {
  247. if (reference == null)
  248. throw new ArgumentNullException(nameof(reference));
  249. var tfm = GetTargetFrameworkIdAsync().Result;
  250. if (reference.IsWindowsRuntime) {
  251. return assemblyList.assemblyLookupCache.GetOrAdd((reference.Name, true, tfm), key => LookupReferencedAssemblyInternal(reference, true, tfm));
  252. } else {
  253. return assemblyList.assemblyLookupCache.GetOrAdd((reference.FullName, false, tfm), key => LookupReferencedAssemblyInternal(reference, false, tfm));
  254. }
  255. }
  256. public LoadedAssembly LookupReferencedModule(PEFile mainModule, string moduleName)
  257. {
  258. if (mainModule == null)
  259. throw new ArgumentNullException(nameof(mainModule));
  260. if (moduleName == null)
  261. throw new ArgumentNullException(nameof(moduleName));
  262. return assemblyList.moduleLookupCache.GetOrAdd(mainModule.FileName + ";" + moduleName, _ => LookupReferencedModuleInternal(mainModule, moduleName));
  263. }
  264. class MyUniversalResolver : UniversalAssemblyResolver
  265. {
  266. public MyUniversalResolver(LoadedAssembly assembly)
  267. : base(assembly.FileName, false, assembly.GetTargetFrameworkIdAsync().Result, PEStreamOptions.PrefetchEntireImage, DecompilerSettingsPanel.CurrentDecompilerSettings.ApplyWindowsRuntimeProjections ? MetadataReaderOptions.ApplyWindowsRuntimeProjections : MetadataReaderOptions.None)
  268. {
  269. }
  270. }
  271. static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
  272. MyUniversalResolver universalResolver;
  273. LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm)
  274. {
  275. string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
  276. string file;
  277. LoadedAssembly asm;
  278. lock (loadingAssemblies) {
  279. foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
  280. var module = loaded.GetPEFileOrNull();
  281. var reader = module?.Metadata;
  282. if (reader == null || !reader.IsAssembly) continue;
  283. var asmDef = reader.GetAssemblyDefinition();
  284. var asmDefName = loaded.GetTargetFrameworkIdAsync().Result + ";" + (isWinRT ? reader.GetString(asmDef.Name) : reader.GetFullAssemblyName());
  285. if (key.Equals(asmDefName, StringComparison.OrdinalIgnoreCase)) {
  286. LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.FullName, MessageKind.Info, "Success - Found in Assembly List");
  287. return loaded;
  288. }
  289. }
  290. if (universalResolver == null) {
  291. universalResolver = new MyUniversalResolver(this);
  292. }
  293. file = universalResolver.FindAssemblyFile(fullName);
  294. foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
  295. if (loaded.FileName.Equals(file, StringComparison.OrdinalIgnoreCase)) {
  296. return loaded;
  297. }
  298. }
  299. if (file != null && loadingAssemblies.TryGetValue(file, out asm))
  300. return asm;
  301. if (assemblyLoadDisableCount > 0)
  302. return null;
  303. if (file != null) {
  304. LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
  305. asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
  306. } else {
  307. LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
  308. return null;
  309. }
  310. loadingAssemblies.Add(file, asm);
  311. }
  312. App.Current.Dispatcher.BeginInvoke((Action)delegate () {
  313. lock (assemblyList.assemblies) {
  314. assemblyList.assemblies.Add(asm);
  315. }
  316. lock (loadingAssemblies) {
  317. loadingAssemblies.Remove(file);
  318. }
  319. });
  320. return asm;
  321. }
  322. LoadedAssembly LookupReferencedModuleInternal(PEFile mainModule, string moduleName)
  323. {
  324. string file;
  325. LoadedAssembly asm;
  326. lock (loadingAssemblies) {
  327. foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
  328. var reader = loaded.GetPEFileOrNull()?.Metadata;
  329. if (reader == null || reader.IsAssembly) continue;
  330. var moduleDef = reader.GetModuleDefinition();
  331. if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase)) {
  332. LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List");
  333. return loaded;
  334. }
  335. }
  336. file = Path.Combine(Path.GetDirectoryName(mainModule.FileName), moduleName);
  337. if (!File.Exists(file))
  338. return null;
  339. foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
  340. if (loaded.FileName.Equals(file, StringComparison.OrdinalIgnoreCase)) {
  341. return loaded;
  342. }
  343. }
  344. if (file != null && loadingAssemblies.TryGetValue(file, out asm))
  345. return asm;
  346. if (assemblyLoadDisableCount > 0)
  347. return null;
  348. if (file != null) {
  349. LoadedAssemblyReferencesInfo.AddMessage(moduleName, MessageKind.Info, "Success - Loading from: " + file);
  350. asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
  351. } else {
  352. LoadedAssemblyReferencesInfo.AddMessageOnce(moduleName, MessageKind.Error, "Could not find reference: " + moduleName);
  353. return null;
  354. }
  355. loadingAssemblies.Add(file, asm);
  356. }
  357. App.Current.Dispatcher.BeginInvoke((Action)delegate () {
  358. lock (assemblyList.assemblies) {
  359. assemblyList.assemblies.Add(asm);
  360. }
  361. lock (loadingAssemblies) {
  362. loadingAssemblies.Remove(file);
  363. }
  364. });
  365. return asm;
  366. }
  367. public Task ContinueWhenLoaded(Action<Task<PEFile>> onAssemblyLoaded, TaskScheduler taskScheduler)
  368. {
  369. return this.assemblyTask.ContinueWith(onAssemblyLoaded, default(CancellationToken), TaskContinuationOptions.RunContinuationsAsynchronously, taskScheduler);
  370. }
  371. /// <summary>
  372. /// Wait until the assembly is loaded.
  373. /// Throws an AggregateException when loading the assembly fails.
  374. /// </summary>
  375. public void WaitUntilLoaded()
  376. {
  377. assemblyTask.Wait();
  378. }
  379. }
  380. }