PageRenderTime 52ms CodeModel.GetById 15ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 1ms

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