// 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. using System; using System.IO; using System.Threading.Tasks; //using System.Windows.Threading; //using ICSharpCode.ILSpy.Options; using Mono.Cecil; namespace ICSharpCode.ILSpy { /// /// Represents an assembly loaded into ILSpy. /// public sealed class LoadedAssembly { readonly Task assemblyTask; readonly AssemblyList assemblyList; readonly string fileName; readonly string shortName; public Guid ProjectGuid{ get; set;} public string ProjectFileName{ get; set; } public LoadedAssembly(AssemblyList assemblyList, string fileName, Stream stream = null) { if (assemblyList == null) throw new ArgumentNullException("assemblyList"); if (fileName == null) throw new ArgumentNullException("fileName"); this.assemblyList = assemblyList; this.fileName = fileName; this.assemblyTask = Task.Factory.StartNew(LoadAssembly, stream); // requires that this.fileName is set this.shortName = Path.GetFileNameWithoutExtension(fileName); } /// /// Gets the Cecil ModuleDefinition. /// Can be null when there was a load error. /// public ModuleDefinition ModuleDefinition { get { try { return assemblyTask.Result; } catch (AggregateException) { return null; } } } /// /// Gets the Cecil AssemblyDefinition. /// Is null when there was a load error; or when opening a netmodule. /// public AssemblyDefinition AssemblyDefinition { get { var module = this.ModuleDefinition; return module != null ? module.Assembly : null; } } public AssemblyList AssemblyList { get { return assemblyList; } } public string FileName { get { return fileName; } } public string ShortName { get { return shortName; } } public string Text { get { if (AssemblyDefinition != null) { return String.Format("{0} ({1})", ShortName, AssemblyDefinition.Name.Version); } else { return ShortName; } } } public bool IsLoaded { get { return assemblyTask.IsCompleted; } } public bool HasLoadError { get { return assemblyTask.IsFaulted; } } public bool IsAutoLoaded { get; set; } ModuleDefinition LoadAssembly(object state) { var stream = state as Stream; ModuleDefinition module; // runs on background thread ReaderParameters p = new ReaderParameters(); p.AssemblyResolver = new MyAssemblyResolver(this); if (stream != null) { // Read the module from a precrafted stream module = ModuleDefinition.ReadModule(stream, p); } else { // Read the module from disk (by default) module = ModuleDefinition.ReadModule(fileName, p); } //if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) { // try { // LoadSymbols(module); // } catch (IOException) { // } catch (UnauthorizedAccessException) { // } catch (InvalidOperationException) { // // ignore any errors during symbol loading // } //} return module; } // private void LoadSymbols(ModuleDefinition module) // { // // search for pdb in same directory as dll // string pdbName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".pdb"); // if (File.Exists(pdbName)) { // using (Stream s = File.OpenRead(pdbName)) { // module.ReadSymbols(new Mono.Cecil.Pdb.PdbReaderProvider().GetSymbolReader(module, s)); // } // return; // } // // // TODO: use symbol cache, get symbols from microsoft // } [ThreadStatic] static int assemblyLoadDisableCount; public static IDisposable DisableAssemblyLoad() { assemblyLoadDisableCount++; return new DecrementAssemblyLoadDisableCount(); } sealed class DecrementAssemblyLoadDisableCount : IDisposable { bool disposed; public void Dispose() { if (!disposed) { disposed = true; assemblyLoadDisableCount--; // clear the lookup cache since we might have stored the lookups failed due to DisableAssemblyLoad() //MainWindow.Instance.CurrentAssemblyList.ClearCache(); } } } sealed class MyAssemblyResolver : IAssemblyResolver { readonly LoadedAssembly parent; public MyAssemblyResolver(LoadedAssembly parent) { this.parent = parent; } public AssemblyDefinition Resolve(AssemblyNameReference name) { var node = parent.LookupReferencedAssembly(name); return node != null ? node.AssemblyDefinition : null; } public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) { var node = parent.LookupReferencedAssembly(name); return node != null ? node.AssemblyDefinition : null; } public AssemblyDefinition Resolve(string fullName) { var node = parent.LookupReferencedAssembly(fullName); return node != null ? node.AssemblyDefinition : null; } public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters) { var node = parent.LookupReferencedAssembly(fullName); return node != null ? node.AssemblyDefinition : null; } } public IAssemblyResolver GetAssemblyResolver() { return new MyAssemblyResolver(this); } public LoadedAssembly LookupReferencedAssembly(AssemblyNameReference name) { if (name == null) throw new ArgumentNullException("name"); if (name.IsWindowsRuntime) { return assemblyList.winRTMetadataLookupCache.GetOrAdd(name.Name, LookupWinRTMetadata); } else { return assemblyList.assemblyLookupCache.GetOrAdd(name.FullName, LookupReferencedAssemblyInternal); } } public LoadedAssembly LookupReferencedAssembly(string fullName) { return assemblyList.assemblyLookupCache.GetOrAdd(fullName, LookupReferencedAssemblyInternal); } LoadedAssembly LookupReferencedAssemblyInternal(string fullName) { foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) { if (asm.AssemblyDefinition != null && fullName.Equals(asm.AssemblyDefinition.FullName, StringComparison.OrdinalIgnoreCase)) return asm; } if (assemblyLoadDisableCount > 0) return null; // if (!App.Current.Dispatcher.CheckAccess()) { // // Call this method on the GUI thread. // return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupReferencedAssembly), fullName); // } var name = AssemblyNameReference.Parse(fullName); string file = null; // GacInterop.FindAssemblyInNetGac(name); if (file == null) { string dir = Path.GetDirectoryName(this.fileName); if (File.Exists(Path.Combine(dir, name.Name + ".dll"))) file = Path.Combine(dir, name.Name + ".dll"); else if (File.Exists(Path.Combine(dir, name.Name + ".exe"))) file = Path.Combine(dir, name.Name + ".exe"); } if (file != null) { var loaded = assemblyList.OpenAssembly(file, true); return loaded; } else { return null; } } LoadedAssembly LookupWinRTMetadata(string name) { foreach (LoadedAssembly asm in assemblyList.GetAssemblies()) { if (asm.AssemblyDefinition != null && name.Equals(asm.AssemblyDefinition.Name.Name, StringComparison.OrdinalIgnoreCase)) return asm; } if (assemblyLoadDisableCount > 0) return null; // if (!App.Current.Dispatcher.CheckAccess()) { // // Call this method on the GUI thread. // return (LoadedAssembly)App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func(LookupWinRTMetadata), name); // } string file = Path.Combine(Environment.SystemDirectory, "WinMetadata", name + ".winmd"); if (File.Exists(file)) { return assemblyList.OpenAssembly(file, true); } else { return null; } } public Task ContinueWhenLoaded(Action> onAssemblyLoaded, TaskScheduler taskScheduler) { return this.assemblyTask.ContinueWith(onAssemblyLoaded, taskScheduler); } /// /// Wait until the assembly is loaded. /// Throws an AggregateException when loading the assembly fails. /// public void WaitUntilLoaded() { assemblyTask.Wait(); } } }