Browse Source

Performance tweaks to code completion:

- don't duplicate search for nested classes in referenced project contents
 - MemberLookupHelper.GetTypeInheritanceTree: use HashSet instead of List to find duplicate types
 - DefaultClass.ClassInheritanceTree: cache the inheritance tree
Fixes SD2-1460 - Extreme delay requesting code completion for PropertyTabsCollection when Linq is imported

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@3573 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 18 years ago
parent
commit
f54b4e40ab
  1. 2
      src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs
  2. 2
      src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs
  3. 2
      src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs
  4. 2
      src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs
  5. 2
      src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs
  6. 1
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj
  7. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs
  8. 45
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs
  9. 21
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs
  10. 70
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs
  11. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs
  12. 3
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs
  13. 53
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs
  14. 20
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs
  15. 2
      src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs

2
src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs

@ -272,7 +272,7 @@ namespace PythonBinding.Tests.Utils @@ -272,7 +272,7 @@ namespace PythonBinding.Tests.Utils
return namespaceContents;
}
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, bool lookInReferences)
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options)
{
return GetClass(typeName, typeParameterCount);
}

2
src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs

@ -82,7 +82,7 @@ namespace XamlBinding @@ -82,7 +82,7 @@ namespace XamlBinding
{
string namespaceName = att.PositionalArguments[1] as string;
if (xmlNamespace.Equals(att.PositionalArguments[0]) && namespaceName != null) {
IClass c = projectContent.GetClass(namespaceName + "." + className, 0, LanguageProperties.CSharp, false);
IClass c = projectContent.GetClass(namespaceName + "." + className, 0, LanguageProperties.CSharp, GetClassOptions.None);
if (c != null)
return c.DefaultReturnType;
}

2
src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs

@ -118,7 +118,7 @@ namespace UnitTesting.Tests.Utils @@ -118,7 +118,7 @@ namespace UnitTesting.Tests.Utils
throw new NotImplementedException();
}
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, bool lookInReferences)
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options)
{
throw new NotImplementedException();
}

2
src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs

@ -197,7 +197,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring @@ -197,7 +197,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring
void SearchAllClassesWithName(List<IClass> searchResults, IProjectContent pc, string name, LanguageProperties language)
{
foreach (string ns in pc.NamespaceNames) {
IClass c = pc.GetClass(ns + "." + name, 0, language, false);
IClass c = pc.GetClass(ns + "." + name, 0, language, GetClassOptions.None);
if (c != null)
searchResults.Add(c);
}

2
src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs

@ -89,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands @@ -89,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.DefaultEditor.Commands
}
ParserService.ParseCurrentViewContent();
c = c.ProjectContent.GetClass(c.FullyQualifiedName, c.TypeParameters.Count, c.ProjectContent.Language, false);
c = c.ProjectContent.GetClass(c.FullyQualifiedName, c.TypeParameters.Count, c.ProjectContent.Language, GetClassOptions.LookForInnerClass);
c = GetCurrentPart(c);
if (c == null) {
return new ToolStripMenuItem[0];

1
src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj

@ -64,6 +64,7 @@ @@ -64,6 +64,7 @@
</Compile>
<Compile Include="Src\CSharp\OverloadResolution.cs" />
<Compile Include="Src\CSharp\TypeInference.cs" />
<Compile Include="Src\DomCache.cs" />
<Compile Include="Src\ExtensionMethods.cs" />
<Compile Include="Src\Implementations\AbstractEntity.cs" />
<Compile Include="Src\Implementations\AbstractMember.cs" />

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs

@ -179,7 +179,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -179,7 +179,7 @@ namespace ICSharpCode.SharpDevelop.Dom
: base(compilationUnit, declaringType)
{
this.FullyQualifiedName = fullName;
this.UseInheritanceCache = true;
this.KeepInheritanceTree = true;
AddAttributes(compilationUnit.ProjectContent, this.Attributes, td.CustomAttributes);

45
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <author name="Daniel Grunwald"/>
// <version>$Revision$</version>
// </file>
using System;
using System.Collections.Generic;
namespace ICSharpCode.SharpDevelop.Dom
{
static class DomCache
{
/// <summary>
/// Clear the static searchclass cache. You should call this method
/// whenever the DOM changes.
/// </summary>
/// <remarks>
/// automatically called by DefaultProjectContent.UpdateCompilationUnit
/// and DefaultProjectContent.OnReferencedContentsChanged.
/// </remarks>
public static void Clear()
{
List<Action> oldActions;
lock (lockObject) {
oldActions = actions;
actions = new List<Action>();
}
foreach (Action a in oldActions) {
a();
}
}
static readonly object lockObject = new Object();
static List<Action> actions = new List<Action>();
public static void RegisterForClear(Action action)
{
lock (lockObject) {
actions.Add(action);
}
}
}
}

21
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -60,7 +60,7 @@ namespace ICSharpCode.SharpDevelop.Dom
copy.UserData = this.UserData;
return copy;
}
*/
*/
byte flags;
const byte hasPublicOrInternalStaticMembersFlag = 0x02;
@ -348,13 +348,14 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -348,13 +348,14 @@ namespace ICSharpCode.SharpDevelop.Dom
return CompareTo((IClass)o);
}
List<IClass> inheritanceTreeCache;
volatile List<IClass> inheritanceTreeCache;
public IEnumerable<IClass> ClassInheritanceTree {
get {
if (inheritanceTreeCache != null)
return inheritanceTreeCache;
List<IClass> visitedList = new List<IClass>();
List<IClass> visitedList = inheritanceTreeCache;
if (visitedList != null)
return visitedList;
visitedList = new List<IClass>();
Queue<IReturnType> typesToVisit = new Queue<IReturnType>();
bool enqueuedLastBaseType = false;
IClass currentClass = this;
@ -378,13 +379,17 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -378,13 +379,17 @@ namespace ICSharpCode.SharpDevelop.Dom
currentClass = nextType.GetUnderlyingClass();
}
} while (nextType != null);
if (UseInheritanceCache)
inheritanceTreeCache = visitedList;
inheritanceTreeCache = visitedList;
if (!KeepInheritanceTree)
DomCache.RegisterForClear(delegate { inheritanceTreeCache = null; });
return visitedList;
}
}
protected bool UseInheritanceCache = false;
/// <summary>
/// Specifies whether to keep the inheritance tree when the DomCache is cleared.
/// </summary>
protected bool KeepInheritanceTree = false;
public IReturnType GetBaseType(int index)
{

70
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace ICSharpCode.SharpDevelop.Dom
{
@ -41,70 +42,29 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -41,70 +42,29 @@ namespace ICSharpCode.SharpDevelop.Dom
shortName = name.Substring(pos + 1);
}
// we need to use a static Dictionary as cache to provide a easy was to clear all cached
// BaseTypes.
// When the cached BaseTypes could not be cleared as soon as the parse information is updated
// (in contrast to a check if the parse information was updated when the base type is needed
// the next time), we can get a memory leak:
// The cached type of a property in Class1 is Class2. Then Class2 is updated, but the property
// in Class1 is not needed again -> the reference causes the GC to keep the old version
// of Class2 in memory.
// The solution is this static cache which is cleared when some parse information updates.
// That way, there can never be any reference to an out-of-date class.
static Dictionary<SearchClassReturnType, IReturnType> cache = new Dictionary<SearchClassReturnType, IReturnType>(new ReferenceComparer());
volatile IReturnType cachedBaseType;
int isSearching; // 0=false, 1=true
class ReferenceComparer : IEqualityComparer<SearchClassReturnType>
void ClearCachedBaseType()
{
public bool Equals(SearchClassReturnType x, SearchClassReturnType y)
{
return x == y; // don't use x.Equals(y) - Equals might cause a FullyQualifiedName lookup on its own
}
public int GetHashCode(SearchClassReturnType obj)
{
return obj.GetObjectHashCode();
}
}
/// <summary>
/// Clear the static searchclass cache. You should call this method
/// whenever the DOM changes.
/// </summary>
/// <remarks>
/// automatically called by DefaultProjectContent.UpdateCompilationUnit
/// and DefaultProjectContent.OnReferencedContentsChanged.
/// </remarks>
internal static void ClearCache()
{
lock (cache) {
cache.Clear();
}
cachedBaseType = null;
}
bool isSearching;
public override IReturnType BaseType {
get {
IReturnType type;
lock (cache) {
if (isSearching)
return null;
if (cache.TryGetValue(this, out type))
return type;
isSearching = true;
}
IReturnType type = cachedBaseType;
if (type != null)
return type;
if (Interlocked.CompareExchange(ref isSearching, 1, 0) != 0)
return null;
try {
type = pc.SearchType(new SearchTypeRequest(name, typeParameterCount, declaringClass, caretLine, caretColumn)).Result;
lock (cache) {
isSearching = false;
cache[this] = type;
}
cachedBaseType = type;
if (type != null)
DomCache.RegisterForClear(ClearCachedBaseType);
return type;
} catch {
isSearching = false;
throw;
} finally {
isSearching = 0;
}
}
}

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs

@ -111,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -111,7 +111,7 @@ namespace ICSharpCode.SharpDevelop.Dom
public override IClass GetUnderlyingClass()
{
return pc.GetClass("System.Void", 0, LanguageProperties.CSharp, true);
return pc.GetClass("System.Void", 0, LanguageProperties.CSharp, GetClassOptions.LookInReferences);
}
public override List<IMethod> GetMethods()

3
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs

@ -699,6 +699,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -699,6 +699,7 @@ namespace ICSharpCode.SharpDevelop.Dom
return resultList;
}
HashSet<IReturnType> visitedSet = new HashSet<IReturnType>();
List<IReturnType> visitedList = new List<IReturnType>();
Queue<IReturnType> typesToVisit = new Queue<IReturnType>();
bool enqueuedLastBaseType = false;
@ -708,7 +709,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -708,7 +709,7 @@ namespace ICSharpCode.SharpDevelop.Dom
IReturnType nextType;
do {
if (currentClass != null) {
if (!visitedList.Contains(currentType)) {
if (visitedSet.Add(currentType)) {
visitedList.Add(currentType);
foreach (IReturnType type in currentClass.BaseTypes) {
typesToVisit.Enqueue(TranslateIfRequired(currentType, type));

53
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs

@ -229,7 +229,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -229,7 +229,7 @@ namespace ICSharpCode.SharpDevelop.Dom
lock (namespaces) {
AddClassToNamespaceListInternal(addClass);
}
SearchClassReturnType.ClearCache();
DomCache.Clear();
}
/// <summary>
@ -462,7 +462,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -462,7 +462,7 @@ namespace ICSharpCode.SharpDevelop.Dom
foreach (IAttribute attr in unit.Attributes)
assemblyAttributes.Remove(attr);
}
SearchClassReturnType.ClearCache();
DomCache.Clear();
}
public void UpdateCompilationUnit(ICompilationUnit oldUnit, ICompilationUnit parserOutput, string fileName)
@ -481,7 +481,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -481,7 +481,7 @@ namespace ICSharpCode.SharpDevelop.Dom
}
assemblyAttributes.AddRange(parserOutput.Attributes);
}
SearchClassReturnType.ClearCache();
DomCache.Clear();
}
protected void RemoveClass(IClass @class)
@ -554,14 +554,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -554,14 +554,9 @@ namespace ICSharpCode.SharpDevelop.Dom
}
#region Default Parser Layer dependent functions
public IClass GetClass(string typeName)
{
return GetClass(typeName, 0);
}
public IClass GetClass(string typeName, int typeParameterCount)
{
return GetClass(typeName, typeParameterCount, language, true);
return GetClass(typeName, typeParameterCount, language, GetClassOptions.Default);
}
protected IClass GetClassInternal(string typeName, int typeParameterCount, LanguageProperties language)
@ -587,7 +582,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -587,7 +582,7 @@ namespace ICSharpCode.SharpDevelop.Dom
return c.IsPublic || c.ProjectContent == this;
}
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, bool lookInReferences)
public IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options)
{
IClass c = GetClassInternal(typeName, typeParameterCount, language);
if (c != null && c.TypeParameters.Count == typeParameterCount) {
@ -595,10 +590,10 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -595,10 +590,10 @@ namespace ICSharpCode.SharpDevelop.Dom
}
// Search in references:
if (lookInReferences) {
if ((options & GetClassOptions.LookInReferences) != 0) {
lock (referencedContents) {
foreach (IProjectContent content in referencedContents) {
IClass contentClass = content.GetClass(typeName, typeParameterCount, language, false);
IClass contentClass = content.GetClass(typeName, typeParameterCount, language, GetClassOptions.None);
if (contentClass != null) {
if (contentClass.TypeParameters.Count == typeParameterCount
&& IsAccessibleClass(contentClass))
@ -616,19 +611,21 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -616,19 +611,21 @@ namespace ICSharpCode.SharpDevelop.Dom
return c;
}
// not found -> maybe nested type -> trying to find class that contains this one.
int lastIndex = typeName.LastIndexOf('.');
if (lastIndex > 0) {
string outerName = typeName.Substring(0, lastIndex);
IClass upperClass = GetClass(outerName, typeParameterCount, language, lookInReferences);
if (upperClass != null) {
foreach (IClass upperBaseClass in upperClass.ClassInheritanceTree) {
IList<IClass> innerClasses = upperBaseClass.InnerClasses;
if (innerClasses != null) {
string innerName = typeName.Substring(lastIndex + 1);
foreach (IClass innerClass in innerClasses) {
if (language.NameComparer.Equals(innerClass.Name, innerName)) {
return innerClass;
if ((options & GetClassOptions.LookForInnerClass) != 0) {
// not found -> maybe nested type -> trying to find class that contains this one.
int lastIndex = typeName.LastIndexOf('.');
if (lastIndex > 0) {
string outerName = typeName.Substring(0, lastIndex);
IClass upperClass = GetClass(outerName, typeParameterCount, language, options);
if (upperClass != null) {
foreach (IClass upperBaseClass in upperClass.ClassInheritanceTree) {
IList<IClass> innerClasses = upperBaseClass.InnerClasses;
if (innerClasses != null) {
string innerName = typeName.Substring(lastIndex + 1);
foreach (IClass innerClass in innerClasses) {
if (language.NameComparer.Equals(innerClass.Name, innerName)) {
return innerClass;
}
}
}
}
@ -895,9 +892,9 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -895,9 +892,9 @@ namespace ICSharpCode.SharpDevelop.Dom
int typeParameterCount = className[className.Length - 1] - '0';
if (typeParameterCount < 0) typeParameterCount = 0;
className = className.Substring(0, className.Length - 2);
return GetClass(className, typeParameterCount, LanguageProperties.CSharp, lookInReferences);
return GetClass(className, typeParameterCount, LanguageProperties.CSharp, GetClassOptions.Default);
} else {
return GetClass(className, 0, LanguageProperties.CSharp, lookInReferences);
return GetClass(className, 0, LanguageProperties.CSharp, GetClassOptions.Default);
}
}
@ -999,7 +996,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -999,7 +996,7 @@ namespace ICSharpCode.SharpDevelop.Dom
protected virtual void OnReferencedContentsChanged(EventArgs e)
{
systemTypes = null; // re-create system types
SearchClassReturnType.ClearCache();
DomCache.Clear();
if (ReferencedContentsChanged != null) {
ReferencedContentsChanged(this, e);
}

20
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs

@ -89,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -89,7 +89,7 @@ namespace ICSharpCode.SharpDevelop.Dom
bool NamespaceExists(string name);
ArrayList GetNamespaceContents(string nameSpace);
IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, bool lookInReferences);
IClass GetClass(string typeName, int typeParameterCount, LanguageProperties language, GetClassOptions options);
bool NamespaceExists(string name, LanguageProperties language, bool lookInReferences);
/// <summary>
/// Adds the contents of the specified <paramref name="subNameSpace"/> to the <paramref name="list"/>.
@ -113,6 +113,24 @@ namespace ICSharpCode.SharpDevelop.Dom @@ -113,6 +113,24 @@ namespace ICSharpCode.SharpDevelop.Dom
FilePosition GetPosition(IEntity entity);
}
[Flags]
public enum GetClassOptions
{
None = 0,
/// <summary>
/// Also look in referenced project contents.
/// </summary>
LookInReferences = 1,
/// <summary>
/// Try if the class is an inner class.
/// </summary>
LookForInnerClass = 2,
/// <summary>
/// Default = LookInReferences + LookForInnerClass
/// </summary>
Default = LookInReferences | LookForInnerClass
}
public struct SearchTypeRequest
{
public readonly string Name;

2
src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs

@ -105,7 +105,7 @@ namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer @@ -105,7 +105,7 @@ namespace ICSharpCode.SharpDevelop.Dom.ReflectionLayer
FullyQualifiedName = fullName;
}
this.UseInheritanceCache = true;
this.KeepInheritanceTree = true;
try {
AddAttributes(compilationUnit.ProjectContent, this.Attributes, CustomAttributeData.GetCustomAttributes(type));

Loading…
Cancel
Save