diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyDecompilerService.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyDecompilerService.cs index 8e48231e05..0a09560b11 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyDecompilerService.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyDecompilerService.cs @@ -23,45 +23,46 @@ namespace ICSharpCode.ILSpyAddIn /// public static class ILSpyDecompilerService { - class AssemblyCacheInfo + class ModuleCacheInfo { public readonly DateTime LastUpdateTime; - public readonly WeakReference Assembly; + public readonly WeakReference Module; - public AssemblyCacheInfo(DateTime lastUpdateTime, AssemblyDefinition assembly) + public ModuleCacheInfo(DateTime lastUpdateTime, ModuleDefinition assembly) { if (assembly == null) throw new ArgumentNullException("assembly"); this.LastUpdateTime = lastUpdateTime; - this.Assembly = new WeakReference(assembly); + this.Module = new WeakReference(assembly); } } - static readonly Dictionary assemblyCache = new Dictionary(); + static readonly Dictionary moduleCache = new Dictionary(); - static AssemblyDefinition GetAssemblyDefinitionFromCache(FileName file, ReaderParameters parameters = null) + static ModuleDefinition GetModuleDefinitionFromCache(FileName file) { if (file == null) return null; - if (parameters == null) parameters = new ReaderParameters(); + ReaderParameters parameters = new ReaderParameters(); + var resolver = new ILSpyAssemblyResolver(file); var lastUpdateTime = File.GetLastWriteTimeUtc(file); - lock (assemblyCache) { - AssemblyCacheInfo info; - AssemblyDefinition asm; - if (!assemblyCache.TryGetValue(file, out info)) { - asm = AssemblyDefinition.ReadAssembly(file, parameters); - assemblyCache.Add(file, new AssemblyCacheInfo(lastUpdateTime, asm)); - return asm; + lock (moduleCache) { + ModuleCacheInfo info; + ModuleDefinition module; + if (!moduleCache.TryGetValue(file, out info)) { + module = ModuleDefinition.ReadModule(file, parameters); + moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); + return module; } else if (info.LastUpdateTime < lastUpdateTime) { - assemblyCache.Remove(file); - asm = AssemblyDefinition.ReadAssembly(file, parameters); - assemblyCache.Add(file, new AssemblyCacheInfo(lastUpdateTime, asm)); - return asm; + moduleCache.Remove(file); + module = ModuleDefinition.ReadModule(file, parameters); + moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); + return module; } else { - if (info.Assembly.TryGetTarget(out asm)) - return asm; - asm = AssemblyDefinition.ReadAssembly(file, parameters); - info.Assembly.SetTarget(asm); - return asm; + if (info.Module.TryGetTarget(out module)) + return module; + module = ModuleDefinition.ReadModule(file, parameters); + info.Module.SetTarget(module); + return module; } } } @@ -77,14 +78,19 @@ namespace ICSharpCode.ILSpyAddIn { } + /// + /// Used to remember the referenced assemblies for the WeakReference cache policy. + /// readonly ISet resolvedAssemblies = new HashSet(); AssemblyDefinition Resolve(DomAssemblyName name, ReaderParameters parameters) { - var assemblyDefinition = GetAssemblyDefinitionFromCache(FindAssembly(name), parameters); - if (assemblyDefinition != null) - resolvedAssemblies.Add(assemblyDefinition); - return assemblyDefinition; + var moduleDefinition = GetModuleDefinitionFromCache(FindAssembly(name)); + if (moduleDefinition != null) { + resolvedAssemblies.Add(moduleDefinition.Assembly); + return moduleDefinition.Assembly; + } + return null; } public AssemblyDefinition Resolve(AssemblyNameReference name) @@ -111,7 +117,7 @@ namespace ICSharpCode.ILSpyAddIn public static ILSpyUnresolvedFile DecompileType(DecompiledTypeReference name) { if (name == null) - throw new ArgumentNullException("entity"); + throw new ArgumentNullException("name"); return DoDecompile(name); } @@ -122,17 +128,16 @@ namespace ICSharpCode.ILSpyAddIn cancellationToken); } - static AstBuilder CreateAstBuilder(DecompiledTypeReference name, out AssemblyDefinition[] neededAssemblies, CancellationToken cancellationToken = default(CancellationToken)) + static AstBuilder CreateAstBuilder(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) { ReaderParameters readerParameters = new ReaderParameters(); // Use new assembly resolver instance so that the AssemblyDefinitions // can be garbage-collected once the code is decompiled. var resolver = new ILSpyAssemblyResolver(name.AssemblyFile); readerParameters.AssemblyResolver = resolver; - AssemblyDefinition asm = GetAssemblyDefinitionFromCache(name.AssemblyFile, readerParameters); - if (asm == null) + ModuleDefinition module = GetModuleDefinitionFromCache(name.AssemblyFile); + if (module == null) throw new InvalidOperationException("Could not find assembly file"); - ModuleDefinition module = asm.MainModule; TypeDefinition typeDefinition = module.GetType(name.Type.ReflectionName); if (typeDefinition == null) throw new InvalidOperationException("Could not find type"); @@ -140,21 +145,18 @@ namespace ICSharpCode.ILSpyAddIn context.CancellationToken = cancellationToken; AstBuilder astBuilder = new AstBuilder(context); astBuilder.AddType(typeDefinition); - neededAssemblies = resolver.ResolvedAssemblies.ToArray(); return astBuilder; } static ILSpyUnresolvedFile DoDecompile(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) { - AssemblyDefinition[] assemblies; - return ILSpyUnresolvedFile.Create(name, CreateAstBuilder(name, out assemblies, cancellationToken)); + return ILSpyUnresolvedFile.Create(name, CreateAstBuilder(name, cancellationToken)); } public static ILSpyFullParseInformation ParseDecompiledType(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) { - AssemblyDefinition[] assemblies; - var astBuilder = CreateAstBuilder(name, out assemblies, cancellationToken); - return new ILSpyFullParseInformation(ILSpyUnresolvedFile.Create(name, astBuilder), null, astBuilder.SyntaxTree, assemblies); + var astBuilder = CreateAstBuilder(name, cancellationToken); + return new ILSpyFullParseInformation(ILSpyUnresolvedFile.Create(name, astBuilder), null, astBuilder.SyntaxTree); } } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyFullParseInformation.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyFullParseInformation.cs index c9d2d58056..d4ccd55cc1 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyFullParseInformation.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyFullParseInformation.cs @@ -17,14 +17,11 @@ namespace ICSharpCode.ILSpyAddIn public class ILSpyFullParseInformation : ParseInformation { SyntaxTree syntaxTree; - // Required to make WeakReferences work properly as cache policy. - AssemblyDefinition[] neededAssemblies; - public ILSpyFullParseInformation(ILSpyUnresolvedFile unresolvedFile, ITextSourceVersion parsedVersion, SyntaxTree syntaxTree, IEnumerable neededAssemblies) + public ILSpyFullParseInformation(ILSpyUnresolvedFile unresolvedFile, ITextSourceVersion parsedVersion, SyntaxTree syntaxTree) : base(unresolvedFile, parsedVersion, true) { this.syntaxTree = syntaxTree; - this.neededAssemblies = neededAssemblies.ToArray(); } public SyntaxTree SyntaxTree { get { return syntaxTree; } } diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyParser.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyParser.cs index e69cc37b58..aadd3da5ac 100644 --- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyParser.cs +++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyParser.cs @@ -55,16 +55,6 @@ namespace ICSharpCode.ILSpyAddIn throw new NotImplementedException(); } - static readonly Lazy defaultReferences = new Lazy( - delegate { - Assembly[] assemblies = { - typeof(object).Assembly, - typeof(Uri).Assembly, - typeof(Enumerable).Assembly - }; - return assemblies.Select(asm => new CecilLoader().LoadAssemblyFile(asm.Location)).ToArray(); - }); - public ICompilation CreateCompilationForSingleFile(FileName fileName, IUnresolvedFile unresolvedFile) { DecompiledTypeReference reference = DecompiledTypeReference.FromFileName(fileName); @@ -73,10 +63,9 @@ namespace ICSharpCode.ILSpyAddIn if (model == null) model = SD.AssemblyParserService.GetAssemblyModelSafe(reference.AssemblyFile, true); if (model != null) - return SD.AssemblyParserService.CreateCompilationForAssembly(model, true); + return model.Context.GetCompilation(); } return new CSharpProjectContent() - .AddAssemblyReferences(defaultReferences.Value) .AddOrUpdateFiles(unresolvedFile) .CreateCompilation(); } diff --git a/src/Main/Base/Project/Dom/IEntityModelContext.cs b/src/Main/Base/Project/Dom/IEntityModelContext.cs index 542c87d107..e48f8f6489 100644 --- a/src/Main/Base/Project/Dom/IEntityModelContext.cs +++ b/src/Main/Base/Project/Dom/IEntityModelContext.cs @@ -91,20 +91,40 @@ namespace ICSharpCode.SharpDevelop.Dom } } + public class DomAssemblyNameReference : IAssemblyReference + { + DomAssemblyName name; + IAssemblySearcher searcher; + + public DomAssemblyNameReference(DomAssemblyName name, IAssemblySearcher searcher) + { + if (name == null) + throw new ArgumentNullException("name"); + if (searcher == null) + throw new ArgumentNullException("searcher"); + this.name = name; + this.searcher = searcher; + } + + public IAssembly Resolve(ITypeResolveContext context) + { + return SD.AssemblyParserService.GetAssembly(searcher.FindAssembly(name), true).Resolve(context); + } + } + public class AssemblyEntityModelContext : IEntityModelContext { - ICompilation compilation; + Lazy compilation; IUnresolvedAssembly mainAssembly; IAssemblyReference[] references; - public AssemblyEntityModelContext(IUnresolvedAssembly mainAssembly, params IAssemblyReference[] references) + public AssemblyEntityModelContext(IUnresolvedAssembly mainAssembly, IAssemblyReference[] references) { if (mainAssembly == null) throw new ArgumentNullException("mainAssembly"); this.mainAssembly = mainAssembly; this.references = references; - // implement lazy init + weak caching - this.compilation = new SimpleCompilation(mainAssembly, references); + this.compilation = new Lazy(() => new SimpleCompilation(mainAssembly, references)); } public string AssemblyName { @@ -117,7 +137,7 @@ namespace ICSharpCode.SharpDevelop.Dom public ICompilation GetCompilation() { - return compilation; + return compilation.Value; } public bool IsBetterPart(IUnresolvedTypeDefinition part1, IUnresolvedTypeDefinition part2) diff --git a/src/Main/Base/Project/Parser/IAssemblyParserService.cs b/src/Main/Base/Project/Parser/IAssemblyParserService.cs index da5bc8379c..90a4e2fb31 100644 --- a/src/Main/Base/Project/Parser/IAssemblyParserService.cs +++ b/src/Main/Base/Project/Parser/IAssemblyParserService.cs @@ -51,16 +51,6 @@ namespace ICSharpCode.SharpDevelop.Parser event EventHandler AssemblyRefreshed; - /// - /// Creates a compilation for the specified assembly. - /// - ICompilation CreateCompilationForAssembly(IAssemblyModel assembly, bool includeInternalMembers = false); - - /// - /// Creates a compilation for the specified assembly. - /// - ICompilation CreateCompilationForAssembly(FileName assembly, bool includeInternalMembers = false); - /// /// Creates an IAssemblyModel for the given assembly file. /// diff --git a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs index bf1a42be76..d8db427c0d 100644 --- a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs +++ b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs @@ -82,14 +82,14 @@ namespace ICSharpCode.SharpDevelop.Parser void CleanWeakDictionary() { - Debug.Assert(Monitor.IsEntered(projectContentDictionary)); - List removed = new List(); - foreach (var pair in projectContentDictionary) { - //if (!pair.Value.IsAlive) - // removed.Add(pair.Key); - } - foreach (var key in removed) - projectContentDictionary.Remove(key); +// Debug.Assert(Monitor.IsEntered(projectContentDictionary)); +// List removed = new List(); +// foreach (var pair in projectContentDictionary) { +// if (!pair.Value.IsAlive) +// removed.Add(pair.Key); +// } +// foreach (var key in removed) +// projectContentDictionary.Remove(key); } LoadedAssembly GetLoadedAssembly(FileName fileName, bool includeInternalMembers) @@ -326,26 +326,12 @@ namespace ICSharpCode.SharpDevelop.Parser } #endregion - public ICompilation CreateCompilationForAssembly(IAssemblyModel assembly, bool includeInternalMembers = false) - { - var mainAssembly = GetAssembly(assembly.Location, includeInternalMembers); - var searcher = new DefaultAssemblySearcher(assembly.Location); - var references = assembly.References - .Select(searcher.FindAssembly) - .Where(f => f != null); - return new SimpleCompilation(mainAssembly, references.Select(fn => GetAssembly(fn, includeInternalMembers))); - } - - public ICompilation CreateCompilationForAssembly(FileName assembly, bool includeInternalMembers = false) - { - return CreateCompilationForAssembly(GetAssemblyModel(assembly, includeInternalMembers), includeInternalMembers); - } - public IAssemblyModel GetAssemblyModel(FileName fileName, bool includeInternalMembers = false) { LoadedAssembly assembly = GetLoadedAssembly(fileName, includeInternalMembers); - // TODO context might need references as well - IEntityModelContext context = new AssemblyEntityModelContext(assembly.ProjectContent); + IEntityModelContext context = new AssemblyEntityModelContext( + assembly.ProjectContent, + assembly.References.Select(name => new DomAssemblyNameReference(name, new DefaultAssemblySearcher(fileName))).ToArray()); IUpdateableAssemblyModel model = SD.GetService().CreateAssemblyModel(context); model.Update(EmptyList.Instance, assembly.ProjectContent.TopLevelTypeDefinitions.ToList());