diff --git a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj index 78ac881be1..cdb63cb3f9 100644 --- a/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj +++ b/src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj @@ -160,6 +160,7 @@ ToolTipService.cs + @@ -878,6 +879,10 @@ ICSharpCode.AvalonEdit False + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} ICSharpCode.NRefactory diff --git a/src/Main/Base/Project/Parser/DefaultAssemblySearcher.cs b/src/Main/Base/Project/Parser/DefaultAssemblySearcher.cs new file mode 100644 index 0000000000..e0b17ffae8 --- /dev/null +++ b/src/Main/Base/Project/Parser/DefaultAssemblySearcher.cs @@ -0,0 +1,75 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using ICSharpCode.Core; +using Mono.Cecil; +using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.ClassBrowser; + +namespace ICSharpCode.SharpDevelop.Parser +{ + public class DefaultAssemblySearcher : IAssemblySearcher + { + FileName mainAssemblyFileName; + DirectoryName baseDirectory; + Lazy> localAssemblies; + + public DefaultAssemblySearcher(FileName mainAssemblyFileName) + { + if (mainAssemblyFileName == null) + throw new ArgumentNullException("mainAssemblyFileName"); + this.mainAssemblyFileName = mainAssemblyFileName; + this.baseDirectory = mainAssemblyFileName.GetParentDirectory(); + this.localAssemblies = new Lazy>( + delegate { + var list = new Dictionary(); + var dirInfo = new DirectoryInfo(baseDirectory); + + foreach (var file in dirInfo.GetFiles("*.dll").Concat(dirInfo.GetFiles("*.exe"))) { + try { + var definition = AssemblyDefinition.ReadAssembly(file.FullName); + var name = new DomAssemblyName(definition.FullName); + if (!list.ContainsKey(name)) + list.Add(name, new FileName(file.FullName)); + } catch (IOException ex) { + LoggingService.Warn("Ignoring error while scanning local assemblies from " + baseDirectory, ex); + } catch (UnauthorizedAccessException ex) { + LoggingService.Warn("Ignoring error while scanning local assemblies from " + baseDirectory, ex); + } + } + + return list; + } + ); + } + + public FileName FindAssembly(DomAssemblyName fullName) + { + // look for the assembly in the current loaded assembly lists + var classBrowser = SD.GetRequiredService(); + var relevantClassBrowserAssemblies = classBrowser.AssemblyLists + .Where(l => l.Assemblies.Any(a => a.Location == mainAssemblyFileName)) + .SelectMany(l => l.Assemblies); + foreach (var asm in relevantClassBrowserAssemblies) { + // TODO I am pretty sure we need the full name here as well... + if (asm.AssemblyName == fullName.ShortName) + return asm.Location; + } + + // scan current directory + var files = localAssemblies.Value; + FileName file; + if (files.TryGetValue(fullName, out file)) + return file; + + // look in GAC + return SD.GlobalAssemblyCache.FindAssemblyInNetGac(fullName); + } + } +} + + diff --git a/src/Main/Base/Project/Parser/IAssemblyParserService.cs b/src/Main/Base/Project/Parser/IAssemblyParserService.cs index 8982d3ecc5..9562056fed 100644 --- a/src/Main/Base/Project/Parser/IAssemblyParserService.cs +++ b/src/Main/Base/Project/Parser/IAssemblyParserService.cs @@ -2,11 +2,15 @@ // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Core; using ICSharpCode.NRefactory.TypeSystem; +using Mono.Cecil; using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom.ClassBrowser; @@ -21,12 +25,7 @@ namespace ICSharpCode.SharpDevelop.Parser /// /// Loads the specified assembly file from disk. /// - IUnresolvedAssembly GetAssembly(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)); - - /// - /// Loads the specified assembly file from disk. - /// - Task GetAssemblyAsync(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)); + IUnresolvedAssembly GetAssembly(FileName fileName, bool includeInternalMembers = false, CancellationToken cancellationToken = default(CancellationToken)); /// /// using (AssemblyParserService.AvoidRedundantChecks()) @@ -47,30 +46,21 @@ namespace ICSharpCode.SharpDevelop.Parser /// /// Creates a compilation for the specified assembly. /// - ICompilation CreateCompilationForAssembly(IAssemblyModel 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. + /// + IAssemblyModel GetAssemblyModel(FileName fileName, bool includeInternalMembers = false); } public interface IAssemblySearcher { FileName FindAssembly(DomAssemblyName fullName); } - - public class DefaultAssemblySearcher : IAssemblySearcher - { - FileName mainAssemblyFileName; - DirectoryName baseDirectory; - - public DefaultAssemblySearcher(FileName mainAssemblyFileName) - { - if (mainAssemblyFileName == null) - throw new ArgumentNullException("mainAssemblyFileName"); - this.mainAssemblyFileName = mainAssemblyFileName; - this.baseDirectory = mainAssemblyFileName.GetParentDirectory(); - } - - public FileName FindAssembly(DomAssemblyName fullName) - { - throw new NotImplementedException(); - } - } } diff --git a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs index 1797c43253..4690909f61 100644 --- a/src/Main/SharpDevelop/Parser/AssemblyParserService.cs +++ b/src/Main/SharpDevelop/Parser/AssemblyParserService.cs @@ -11,11 +11,13 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; using ICSharpCode.Core; +using ICSharpCode.NRefactory; using ICSharpCode.NRefactory.Documentation; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.Utils; using ICSharpCode.SharpDevelop.Dom; +using ICSharpCode.SharpDevelop.Dom.ClassBrowser; using Mono.Cecil; namespace ICSharpCode.SharpDevelop.Parser @@ -26,15 +28,21 @@ namespace ICSharpCode.SharpDevelop.Parser sealed class AssemblyParserService : IAssemblyParserService { #region Get Assembly By File Name + [Serializable] + [FastSerializerVersion(1)] sealed class LoadedAssembly { - public readonly Task ProjectContent; + public readonly IUnresolvedAssembly ProjectContent; public readonly DateTime AssemblyFileLastWriteTime; + public readonly bool HasInternalMembers; + public readonly IReadOnlyList References; - public LoadedAssembly(Task projectContent, DateTime assemblyFileLastWriteTime) + public LoadedAssembly(IUnresolvedAssembly projectContent, DateTime assemblyFileLastWriteTime, bool hasInternalMembers, IEnumerable references) { this.ProjectContent = projectContent; this.AssemblyFileLastWriteTime = assemblyFileLastWriteTime; + this.HasInternalMembers = hasInternalMembers; + this.References = references.ToArray(); } } @@ -43,26 +51,11 @@ namespace ICSharpCode.SharpDevelop.Parser [ThreadStatic] static Dictionary up2dateProjectContents; - public IUnresolvedAssembly GetAssembly(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)) + public IUnresolvedAssembly GetAssembly(FileName fileName, bool includeInternalMembers = false, CancellationToken cancellationToken = default(CancellationToken)) { // We currently do not support cancelling the load operation itself, because another GetAssembly() call // with a different cancellation token might request the same assembly. - bool isNewTask; - LoadedAssembly asm = GetLoadedAssembly(fileName, out isNewTask); - if (isNewTask) - asm.ProjectContent.RunSynchronously(); - else - asm.ProjectContent.Wait(cancellationToken); - return asm.ProjectContent.Result; - } - - public Task GetAssemblyAsync(FileName fileName, CancellationToken cancellationToken = default(CancellationToken)) - { - bool isNewTask; - LoadedAssembly asm = GetLoadedAssembly(fileName, out isNewTask); - if (isNewTask) - asm.ProjectContent.Start(); - return asm.ProjectContent; + return GetLoadedAssembly(fileName, includeInternalMembers).ProjectContent; } /// @@ -99,9 +92,8 @@ namespace ICSharpCode.SharpDevelop.Parser projectContentDictionary.Remove(key); } - LoadedAssembly GetLoadedAssembly(FileName fileName, out bool isNewTask) + LoadedAssembly GetLoadedAssembly(FileName fileName, bool includeInternalMembers) { - isNewTask = false; LoadedAssembly asm; var up2dateProjectContents = AssemblyParserService.up2dateProjectContents; if (up2dateProjectContents != null) { @@ -112,15 +104,13 @@ namespace ICSharpCode.SharpDevelop.Parser lock (projectContentDictionary) { if (projectContentDictionary.TryGetValue(fileName, out asm)) { if (asm.AssemblyFileLastWriteTime == lastWriteTime) { - return asm; + if (!includeInternalMembers || includeInternalMembers == asm.HasInternalMembers) + return asm; } } else { asm = null; } - var task = new Task(() => LoadAssembly(fileName, CancellationToken.None)); - isNewTask = true; - asm = new LoadedAssembly(task, lastWriteTime); - + asm = LoadAssembly(fileName, CancellationToken.None, includeInternalMembers); if (up2dateProjectContents == null) CleanWeakDictionary(); // The assembly might already be in the dictionary if we had loaded it before, @@ -133,11 +123,11 @@ namespace ICSharpCode.SharpDevelop.Parser #endregion #region Load Assembly - IUnresolvedAssembly LoadAssembly(FileName fileName, CancellationToken cancellationToken) + LoadedAssembly LoadAssembly(FileName fileName, CancellationToken cancellationToken, bool includeInternalMembers) { DateTime lastWriteTime = File.GetLastWriteTimeUtc(fileName); string cacheFileName = GetCacheFileName(fileName); - IUnresolvedAssembly pc = TryReadFromCache(cacheFileName, lastWriteTime); + LoadedAssembly pc = TryReadFromCache(cacheFileName, lastWriteTime); if (pc != null) return pc; @@ -148,6 +138,7 @@ namespace ICSharpCode.SharpDevelop.Parser AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param); CecilLoader l = new CecilLoader(); + l.IncludeInternalMembers = includeInternalMembers; string xmlDocFile = FindXmlDocumentation(fileName, asm.MainModule.Runtime); if (xmlDocFile != null) { try { @@ -161,7 +152,9 @@ namespace ICSharpCode.SharpDevelop.Parser } } l.CancellationToken = cancellationToken; - pc = l.LoadAssembly(asm); + var references = asm.MainModule.AssemblyReferences + .Select(anr => new DomAssemblyName(anr.FullName)); + pc = new LoadedAssembly(l.LoadAssembly(asm), lastWriteTime, includeInternalMembers, references); SaveToCacheAsync(cacheFileName, lastWriteTime, pc).FireAndForget(); //SaveToCache(cacheFileName, lastWriteTime, pc); return pc; @@ -244,7 +237,7 @@ namespace ICSharpCode.SharpDevelop.Parser return cacheFileName; } - static IUnresolvedAssembly TryReadFromCache(string cacheFileName, DateTime lastWriteTime) + static LoadedAssembly TryReadFromCache(string cacheFileName, DateTime lastWriteTime) { if (cacheFileName == null || !File.Exists(cacheFileName)) return null; @@ -257,7 +250,7 @@ namespace ICSharpCode.SharpDevelop.Parser return null; } FastSerializer s = new FastSerializer(); - return (IUnresolvedAssembly)s.Deserialize(reader); + return s.Deserialize(reader) as LoadedAssembly; } } } catch (IOException ex) { @@ -272,19 +265,19 @@ namespace ICSharpCode.SharpDevelop.Parser } } - Task SaveToCacheAsync(string cacheFileName, DateTime lastWriteTime, IUnresolvedAssembly pc) + Task SaveToCacheAsync(string cacheFileName, DateTime lastWriteTime, LoadedAssembly asm) { if (cacheFileName == null) return Task.FromResult(null); // Call SaveToCache on a background task: var shutdownService = SD.ShutdownService; - var task = IOTaskScheduler.Factory.StartNew(delegate { SaveToCache(cacheFileName, lastWriteTime, pc); }, shutdownService.ShutdownToken); + var task = IOTaskScheduler.Factory.StartNew(delegate { SaveToCache(cacheFileName, lastWriteTime, asm); }, shutdownService.ShutdownToken); shutdownService.AddBackgroundTask(task); return task; } - void SaveToCache(string cacheFileName, DateTime lastWriteTime, IUnresolvedAssembly pc) + void SaveToCache(string cacheFileName, DateTime lastWriteTime, LoadedAssembly asm) { if (cacheFileName == null) return; @@ -295,7 +288,7 @@ namespace ICSharpCode.SharpDevelop.Parser using (BinaryWriter writer = new BinaryWriterWith7BitEncodedInts(fs)) { writer.Write(lastWriteTime.Ticks); FastSerializer s = new FastSerializer(); - s.Serialize(writer, pc); + s.Serialize(writer, asm); } } } catch (IOException ex) { @@ -309,18 +302,33 @@ namespace ICSharpCode.SharpDevelop.Parser } #endregion - public ICompilation CreateCompilationForAssembly(IAssemblyModel assembly) + public ICompilation CreateCompilationForAssembly(IAssemblyModel assembly, bool includeInternalMembers = false) { - var mainAssembly = GetAssembly(new FileName(assembly.Context.Location)); + var fileName = new FileName(assembly.Location); + var mainAssembly = GetAssembly(fileName, includeInternalMembers); + var searcher = new DefaultAssemblySearcher(fileName); var references = assembly.References - .Select(r => FindAssembly(r, assembly)) + .Select(searcher.FindAssembly) .Where(f => f != null); return new SimpleCompilation(mainAssembly, references.Select(fn => GetAssembly(fn))); } - FileName FindAssembly(DomAssemblyName name, IAssemblyModel mainAssembly) + public ICompilation CreateCompilationForAssembly(FileName assembly, bool includeInternalMembers = false) { - throw new NotImplementedException(); + return CreateCompilationForAssembly(GetAssemblyModel(assembly, includeInternalMembers), includeInternalMembers); + } + + public IAssemblyModel GetAssemblyModel(FileName fileName, bool includeInternalMembers = false) + { + LoadedAssembly assembly = GetLoadedAssembly(fileName, includeInternalMembers); + IEntityModelContext context = new AssemblyEntityModelContext(assembly.ProjectContent); + IUpdateableAssemblyModel model = SD.GetService().CreateAssemblyModel(context); + + model.Update(EmptyList.Instance, assembly.ProjectContent.TopLevelTypeDefinitions.ToList()); + model.AssemblyName = assembly.ProjectContent.AssemblyName; + model.References = assembly.References.ToList(); + + return model; } } }