// 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.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; using System.Linq; //using System.Windows.Threading; using System.Xml.Linq; namespace ICSharpCode.ILSpy { /// /// A list of assemblies. /// public class AssemblyList { readonly string listName; /// Dirty flag, used to mark modifications so that the list is saved later bool dirty; internal readonly ConcurrentDictionary assemblyLookupCache = new ConcurrentDictionary(); internal readonly ConcurrentDictionary winRTMetadataLookupCache = new ConcurrentDictionary(); /// /// The assemblies in this list. /// Needs locking for multi-threaded access! /// Write accesses are allowed on the GUI thread only (but still need locking!) /// /// /// Technically read accesses need locking when done on non-GUI threads... but whenever possible, use the /// thread-safe method. /// internal readonly List assemblies = new List(); public AssemblyList(string listName) { this.listName = listName; // assemblies.CollectionChanged += Assemblies_CollectionChanged; } /// /// Loads an assembly list from XML. /// public AssemblyList(XElement listElement) : this((string)listElement.Attribute("name")) { foreach (var asm in listElement.Elements("Assembly")) { OpenAssembly((string)asm); } this.dirty = false; // OpenAssembly() sets dirty, so reset it afterwards } /// /// Gets the loaded assemblies. This method is thread-safe. /// public LoadedAssembly[] GetAssemblies() { lock (assemblies) { return assemblies.ToArray(); } } /// /// Saves this assembly list to XML. /// internal XElement SaveAsXml() { return new XElement( "List", new XAttribute("name", this.ListName), assemblies.Where(asm => !asm.IsAutoLoaded).Select(asm => new XElement("Assembly", asm.FileName)) ); } public LoadedAssembly findAssemblyByShortName (string shortName) { foreach (LoadedAssembly asm in assemblies) { if (asm.ShortName == shortName) { return asm; } } return null; } /// /// Gets the name of this list. /// public string ListName { get { return listName; } } void Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { ClearCache(); // Whenever the assembly list is modified, mark it as dirty // and enqueue a task that saves it once the UI has finished modifying the assembly list. if (!dirty) { dirty = true; // App.Current.Dispatcher.BeginInvoke( // DispatcherPriority.Background, // new Action( // delegate { // dirty = false; // AssemblyListManager.SaveList(this); // ClearCache(); // }) // ); } } internal void RefreshSave() { if (!dirty) { dirty = true; // App.Current.Dispatcher.BeginInvoke( // DispatcherPriority.Background, // new Action( // delegate { // dirty = false; // AssemblyListManager.SaveList(this); // }) // ); } } internal void ClearCache() { assemblyLookupCache.Clear(); winRTMetadataLookupCache.Clear(); } /// /// Opens an assembly from disk. /// Returns the existing assembly node if it is already loaded. /// public LoadedAssembly OpenAssembly(string file, bool isAutoLoaded=false) { //App.Current.Dispatcher.VerifyAccess(); file = Path.GetFullPath(file); foreach (LoadedAssembly asm in this.assemblies) { if (file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase)) return asm; } var newAsm = new LoadedAssembly(this, file); newAsm.IsAutoLoaded = isAutoLoaded; lock (assemblies) { this.assemblies.Add(newAsm); } return newAsm; } /// /// Replace the assembly object model from a crafted stream, without disk I/O /// Returns null if it is not already loaded. /// // public LoadedAssembly HotReplaceAssembly(string file, Stream stream) // { // App.Current.Dispatcher.VerifyAccess(); // file = Path.GetFullPath(file); // // var target = this.assemblies.FirstOrDefault(asm => file.Equals(asm.FileName, StringComparison.OrdinalIgnoreCase)); // if (target == null) // return null; // // var index = this.assemblies.IndexOf(target); // var newAsm = new LoadedAssembly(this, file, stream); // newAsm.IsAutoLoaded = target.IsAutoLoaded; // lock (assemblies) // { // this.assemblies.Remove(target); // this.assemblies.Insert(index, newAsm); // } // return newAsm; // } public void Unload(LoadedAssembly assembly) { //App.Current.Dispatcher.VerifyAccess(); lock (assemblies) { assemblies.Remove(assembly); } RequestGC(); } static bool gcRequested; void RequestGC() { if (gcRequested) return; gcRequested = true; // App.Current.Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action( // delegate { // gcRequested = false; // GC.Collect(); // })); } public void Sort(IComparer comparer) { Sort(0, int.MaxValue, comparer); } public void Sort(int index, int count, IComparer comparer) { //App.Current.Dispatcher.VerifyAccess(); lock (assemblies) { List list = new List(assemblies); list.Sort(index, Math.Min(count, list.Count - index), comparer); assemblies.Clear(); assemblies.AddRange(list); } } } }