using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using dnlib.DotNet; namespace Confuser.Core { /// /// Sort modules according dependencies. /// internal class ModuleSorter { readonly List modules; public ModuleSorter(IEnumerable modules) { this.modules = modules.ToList(); } public IList Sort() { var edges = new List(); var roots = new HashSet(modules); var asmMap = modules.GroupBy(module => module.Assembly.ToAssemblyRef(), AssemblyNameComparer.CompareAll) .ToDictionary(gp => gp.Key, gp => gp.ToList(), AssemblyNameComparer.CompareAll); foreach (ModuleDefMD m in modules) foreach (AssemblyRef nameRef in m.GetAssemblyRefs()) { if (!asmMap.ContainsKey(nameRef)) continue; foreach (var asmModule in asmMap[nameRef]) edges.Add(new DependencyGraphEdge(asmModule, m)); roots.Remove(m); } var sorted = SortGraph(roots, edges).ToList(); Debug.Assert(sorted.Count == modules.Count); return sorted; } IEnumerable SortGraph(IEnumerable roots, IList edges) { var visited = new HashSet(); var queue = new Queue(roots); do { while (queue.Count > 0) { var node = queue.Dequeue(); visited.Add(node); Debug.Assert(!edges.Where(edge => edge.To == node).Any()); yield return node; foreach (DependencyGraphEdge edge in edges.Where(edge => edge.From == node).ToList()) { edges.Remove(edge); if (!edges.Any(e => e.To == edge.To)) queue.Enqueue(edge.To); } } if (edges.Count > 0) { foreach (var edge in edges) { if (!visited.Contains(edge.From)) { queue.Enqueue(edge.From); break; } } } } while (edges.Count > 0); } class DependencyGraphEdge { public DependencyGraphEdge(ModuleDefMD from, ModuleDefMD to) { From = from; To = to; } public ModuleDefMD From { get; private set; } public ModuleDefMD To { get; private set; } } } }