diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs index 7960dd3732..03a29c859d 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CSharpResolver.cs @@ -79,11 +79,64 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver /// setting one of the properties does not automatically set the other. public IMember CurrentMember { get; set; } + /// + /// Gets the current project content. + /// Returns CurrentUsingScope.ProjectContent. + /// + public IProjectContent ProjectContent { + get { + if (currentUsingScope != null) + return currentUsingScope.UsingScope.ProjectContent; + else + return null; + } + } + #endregion + + #region Per-CurrentTypeDefinition Cache + TypeDefinitionCache currentTypeDefinition; + /// /// Gets/Sets the current type definition that is used to look up identifiers as simple members. /// - public ITypeDefinition CurrentTypeDefinition { get; set; } + public ITypeDefinition CurrentTypeDefinition { + get { return currentTypeDefinition != null ? currentTypeDefinition.TypeDefinition : null; } + set { + if (value == null) { + currentTypeDefinition = null; + } else { + if (currentTypeDefinition != null && currentTypeDefinition.TypeDefinition == value) + return; + + CacheManager cache = context.CacheManager; + if (cache != null) { + currentTypeDefinition = cache.GetThreadLocal(value) as TypeDefinitionCache; + if (currentTypeDefinition == null) { + currentTypeDefinition = new TypeDefinitionCache(value); + cache.SetThreadLocal(value, currentTypeDefinition); + } + } else { + currentTypeDefinition = new TypeDefinitionCache(value); + } + } + } + } + + sealed class TypeDefinitionCache + { + public readonly ITypeDefinition TypeDefinition; + public readonly Dictionary SimpleNameLookupCacheExpression = new Dictionary(); + public readonly Dictionary SimpleNameLookupCacheInvocationTarget = new Dictionary(); + public readonly Dictionary SimpleTypeLookupCache = new Dictionary(); + + public TypeDefinitionCache(ITypeDefinition typeDefinition) + { + this.TypeDefinition = typeDefinition; + } + } + #endregion + #region CurrentUsingScope UsingScopeCache currentUsingScope; /// @@ -100,10 +153,10 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver CacheManager cache = context.CacheManager; if (cache != null) { - currentUsingScope = cache.GetShared(value) as UsingScopeCache; + currentUsingScope = cache.GetThreadLocal(value) as UsingScopeCache; if (currentUsingScope == null) { currentUsingScope = new UsingScopeCache(value); - cache.SetShared(value, currentUsingScope); + cache.SetThreadLocal(value, currentUsingScope); } } else { currentUsingScope = new UsingScopeCache(value); @@ -112,21 +165,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } - /// - /// Gets the current project content. - /// Returns CurrentUsingScope.ProjectContent. - /// - public IProjectContent ProjectContent { - get { - if (currentUsingScope != null) - return currentUsingScope.UsingScope.ProjectContent; - else - return null; - } - } - #endregion - - #region Per-UsingScope Cache /// /// There is one cache instance per using scope; and it might be shared between multiple resolvers /// that are on different threads, so it must be thread-safe. @@ -134,9 +172,9 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver sealed class UsingScopeCache { public readonly UsingScope UsingScope; + public readonly Dictionary ResolveCache = new Dictionary(); - public volatile List> AllExtensionMethods; - + public List> AllExtensionMethods; public UsingScopeCache(UsingScope usingScope) { @@ -1967,7 +2005,63 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (parameterizeResultType && typeArguments.All(t => t.Kind == TypeKind.UnboundTypeArgument)) parameterizeResultType = false; - ResolveResult r; + ResolveResult r = null; + if (currentTypeDefinition != null) { + Dictionary cache = null; + bool foundInCache = false; + if (k == 0) { + switch (lookupMode) { + case SimpleNameLookupMode.Expression: + cache = currentTypeDefinition.SimpleNameLookupCacheExpression; + break; + case SimpleNameLookupMode.InvocationTarget: + cache = currentTypeDefinition.SimpleNameLookupCacheInvocationTarget; + break; + case SimpleNameLookupMode.Type: + cache = currentTypeDefinition.SimpleTypeLookupCache; + break; + } + if (cache != null) { + foundInCache = cache.TryGetValue(identifier, out r); + } + } + if (!foundInCache) { + r = LookInCurrentType(identifier, typeArguments, lookupMode, parameterizeResultType); + if (cache != null) { + // also cache missing members (r==null) + cache[identifier] = r; + } + } + if (r != null) + return r; + } + + if (currentUsingScope != null) { + if (k == 0 && lookupMode != SimpleNameLookupMode.TypeInUsingDeclaration) { + if (!currentUsingScope.ResolveCache.TryGetValue(identifier, out r)) { + r = LookInCurrentUsingScope(identifier, typeArguments, false, false); + currentUsingScope.ResolveCache[identifier] = r; + } + } else { + r = LookInCurrentUsingScope(identifier, typeArguments, lookupMode == SimpleNameLookupMode.TypeInUsingDeclaration, parameterizeResultType); + } + if (r != null) + return r; + } + + if (typeArguments.Count == 0) { + if (identifier == "dynamic") + return new TypeResolveResult(SharedTypes.Dynamic); + else + return new UnknownIdentifierResolveResult(identifier); + } else { + return ErrorResult; + } + } + + ResolveResult LookInCurrentType(string identifier, IList typeArguments, SimpleNameLookupMode lookupMode, bool parameterizeResultType) + { + int k = typeArguments.Count; // look in current type definitions for (ITypeDefinition t = this.CurrentTypeDefinition; t != null; t = t.DeclaringTypeDefinition) { if (k == 0) { @@ -1986,6 +2080,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } MemberLookup lookup = new MemberLookup(context, t, t.ProjectContent); + ResolveResult r; if (lookupMode == SimpleNameLookupMode.Expression || lookupMode == SimpleNameLookupMode.InvocationTarget) { r = lookup.Lookup(new TypeResolveResult(t), identifier, typeArguments, lookupMode == SimpleNameLookupMode.InvocationTarget); } else { @@ -1994,9 +2089,15 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (!(r is UnknownMemberResolveResult)) // but do return AmbiguousMemberResolveResult return r; } - + return null; + } + + ResolveResult LookInCurrentUsingScope(string identifier, IList typeArguments, bool isInUsingDeclaration, bool parameterizeResultType) + { + int k = typeArguments.Count; // look in current namespace definitions - for (UsingScope n = this.CurrentUsingScope; n != null; n = n.Parent) { + UsingScope currentUsingScope = this.CurrentUsingScope; + for (UsingScope n = currentUsingScope; n != null; n = n.Parent) { // first look for a namespace if (k == 0) { string fullName = NamespaceDeclaration.BuildQualifiedName(n.NamespaceName, identifier); @@ -2023,7 +2124,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver if (n.ExternAliases.Contains(identifier)) { return ResolveExternAlias(identifier); } - if (lookupMode != SimpleNameLookupMode.TypeInUsingDeclaration || n != this.CurrentUsingScope) { + if (!(isInUsingDeclaration && n == currentUsingScope)) { foreach (var pair in n.UsingAliases) { if (pair.Key == identifier) { NamespaceResolveResult ns = pair.Value.ResolveNamespace(context); @@ -2036,7 +2137,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } } // finally, look in the imported namespaces: - if (lookupMode != SimpleNameLookupMode.TypeInUsingDeclaration || n != this.CurrentUsingScope) { + if (!(isInUsingDeclaration && n == currentUsingScope)) { IType firstResult = null; foreach (var u in n.Usings) { NamespaceResolveResult ns = u.ResolveNamespace(context); @@ -2059,14 +2160,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } // if we didn't find anything: repeat lookup with parent namespace } - if (typeArguments.Count == 0) { - if (identifier == "dynamic") - return new TypeResolveResult(SharedTypes.Dynamic); - else - return new UnknownIdentifierResolveResult(identifier); - } else { - return ErrorResult; - } + return null; } ///