diff --git a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs index 72cde6d511..2e0aa325ce 100644 --- a/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs +++ b/src/AddIns/BackendBindings/Python/PythonBinding/Test/Utils/MockProjectContent.cs @@ -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); } diff --git a/src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs b/src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs index 65b24aafc5..b1a6e3b442 100644 --- a/src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs +++ b/src/AddIns/BackendBindings/XamlBinding/Project/Src/XamlCompilationUnit.cs @@ -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; } diff --git a/src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs b/src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs index d6bf8aafbc..1869056e35 100644 --- a/src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs +++ b/src/AddIns/Misc/UnitTesting/Test/Utils/MockProjectContent.cs @@ -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(); } diff --git a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs index aff87cf737..6f4caf2db1 100644 --- a/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs +++ b/src/Main/Base/Project/Src/Services/RefactoringService/RefactoringMenuBuilder.cs @@ -197,7 +197,7 @@ namespace ICSharpCode.SharpDevelop.Refactoring void SearchAllClassesWithName(List 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); } diff --git a/src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs b/src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs index 2b3fe72b0f..139f6226fa 100644 --- a/src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs +++ b/src/Main/Base/Project/Src/TextEditor/Commands/ClassBookmarkMenuBuilder.cs @@ -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]; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj index 5d955e3a10..bba40cc76a 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/ICSharpCode.SharpDevelop.Dom.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs index 889f1b6a45..461566feb4 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/CecilReader.cs @@ -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); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs new file mode 100644 index 0000000000..36c0dbab22 --- /dev/null +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/DomCache.cs @@ -0,0 +1,45 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.SharpDevelop.Dom +{ + static class DomCache + { + /// + /// Clear the static searchclass cache. You should call this method + /// whenever the DOM changes. + /// + /// + /// automatically called by DefaultProjectContent.UpdateCompilationUnit + /// and DefaultProjectContent.OnReferencedContentsChanged. + /// + public static void Clear() + { + List oldActions; + lock (lockObject) { + oldActions = actions; + actions = new List(); + } + foreach (Action a in oldActions) { + a(); + } + } + + static readonly object lockObject = new Object(); + static List actions = new List(); + + public static void RegisterForClear(Action action) + { + lock (lockObject) { + actions.Add(action); + } + } + } +} diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs index 0540404528..ba22beb063 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/DefaultClass.cs @@ -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 return CompareTo((IClass)o); } - List inheritanceTreeCache; + volatile List inheritanceTreeCache; public IEnumerable ClassInheritanceTree { get { - if (inheritanceTreeCache != null) - return inheritanceTreeCache; - List visitedList = new List(); + List visitedList = inheritanceTreeCache; + if (visitedList != null) + return visitedList; + visitedList = new List(); Queue typesToVisit = new Queue(); bool enqueuedLastBaseType = false; IClass currentClass = this; @@ -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; + /// + /// Specifies whether to keep the inheritance tree when the DomCache is cleared. + /// + protected bool KeepInheritanceTree = false; public IReturnType GetBaseType(int index) { diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs index 8c7d49d4a3..99e590b037 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SearchClassReturnType.cs @@ -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 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 cache = new Dictionary(new ReferenceComparer()); + volatile IReturnType cachedBaseType; + int isSearching; // 0=false, 1=true - class ReferenceComparer : IEqualityComparer + 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(); - } + cachedBaseType = null; } - /// - /// Clear the static searchclass cache. You should call this method - /// whenever the DOM changes. - /// - /// - /// automatically called by DefaultProjectContent.UpdateCompilationUnit - /// and DefaultProjectContent.OnReferencedContentsChanged. - /// - internal static void ClearCache() - { - lock (cache) { - cache.Clear(); - } - } - - 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; } } } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs index d74838e33b..f972323738 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/Implementations/SystemTypes.cs @@ -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 GetMethods() diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs index f4bdad4fb1..e5bcfc5113 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/MemberLookupHelper.cs @@ -699,6 +699,7 @@ namespace ICSharpCode.SharpDevelop.Dom return resultList; } + HashSet visitedSet = new HashSet(); List visitedList = new List(); Queue typesToVisit = new Queue(); bool enqueuedLastBaseType = false; @@ -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)); diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs index 430f025868..a9d66b7090 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/DefaultProjectContent.cs @@ -229,7 +229,7 @@ namespace ICSharpCode.SharpDevelop.Dom lock (namespaces) { AddClassToNamespaceListInternal(addClass); } - SearchClassReturnType.ClearCache(); + DomCache.Clear(); } /// @@ -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 } assemblyAttributes.AddRange(parserOutput.Attributes); } - SearchClassReturnType.ClearCache(); + DomCache.Clear(); } protected void RemoveClass(IClass @class) @@ -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 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 } // 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 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 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 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 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 protected virtual void OnReferencedContentsChanged(EventArgs e) { systemTypes = null; // re-create system types - SearchClassReturnType.ClearCache(); + DomCache.Clear(); if (ReferencedContentsChanged != null) { ReferencedContentsChanged(this, e); } diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs index fb8cc6c4c3..e7ad8cee88 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ProjectContent/IProjectContent.cs @@ -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); /// /// Adds the contents of the specified to the . @@ -113,6 +113,24 @@ namespace ICSharpCode.SharpDevelop.Dom FilePosition GetPosition(IEntity entity); } + [Flags] + public enum GetClassOptions + { + None = 0, + /// + /// Also look in referenced project contents. + /// + LookInReferences = 1, + /// + /// Try if the class is an inner class. + /// + LookForInnerClass = 2, + /// + /// Default = LookInReferences + LookForInnerClass + /// + Default = LookInReferences | LookForInnerClass + } + public struct SearchTypeRequest { public readonly string Name; diff --git a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs index 06b3eff4b2..6d24ab937c 100644 --- a/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs +++ b/src/Main/ICSharpCode.SharpDevelop.Dom/Project/Src/ReflectionLayer/ReflectionClass.cs @@ -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));