From a302e55b8f6584857408f3a8afba1f0a77d8776f Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Tue, 28 Feb 2017 21:16:57 +0100 Subject: [PATCH 1/7] Saved the unresolved member declarations on the module states and refilled the unresolved and undeclared collections on the DeclarationFinder upon refresh. --- .../Symbols/DeclarationFinder.cs | 47 ++++++++++++------- Rubberduck.Parsing/VBA/ModuleState.cs | 7 ++- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 31 ++++++++---- .../VBA/RubberduckParserState.cs | 46 +++++++++++++++++- 4 files changed, 104 insertions(+), 27 deletions(-) diff --git a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs index 7da0ee93f9..3f63c08abd 100644 --- a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs +++ b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs @@ -30,7 +30,7 @@ public static IEnumerable AllValues( public static ConcurrentDictionary> ToConcurrentDictionary(this IEnumerable> source) { return new ConcurrentDictionary>(source.Select(x => new KeyValuePair>(x.Key, new ConcurrentBag(x)))); - } + } } public class DeclarationFinder @@ -42,8 +42,10 @@ public class DeclarationFinder private readonly AnnotationService _annotationService; private readonly ConcurrentDictionary> _declarationsByName; private readonly ConcurrentDictionary> _declarations; - private readonly ConcurrentDictionary> _undeclared; - private readonly ConcurrentBag _unresolved; + private readonly ConcurrentDictionary> _newUndeclared; + private readonly ConcurrentBag _newUnresolved; + private readonly Dictionary> _undeclared; + private readonly List _unresolved; private readonly ConcurrentDictionary> _annotations; private readonly ConcurrentDictionary> _parametersByParent; private readonly ConcurrentDictionary> _userDeclarationsByType; @@ -54,7 +56,7 @@ public class DeclarationFinder private static readonly object ThreadLock = new object(); - public DeclarationFinder(IReadOnlyList declarations, IEnumerable annotations, IHostApplication hostApp = null) + public DeclarationFinder(IReadOnlyList declarations, IEnumerable annotations, IReadOnlyList unresolvedMemberDeclarations, IHostApplication hostApp = null) { _hostApp = hostApp; _annotations = annotations.GroupBy(node => node.QualifiedSelection.QualifiedName).ToConcurrentDictionary(); @@ -90,8 +92,11 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable item.WithEventsField, item => item.Handlers.ToArray()) ), true); - _undeclared = new ConcurrentDictionary>(new Dictionary>()); - _unresolved = new ConcurrentBag(new List()); + _newUndeclared = new ConcurrentDictionary>(new Dictionary>()); + _undeclared = declarations.Where(declaration => declaration.IsUndeclared).GroupBy(item => item.QualifiedName).ToDictionary(group => group.Key, group => group.ToList()); + _newUnresolved = new ConcurrentBag(new List()); + _unresolved = unresolvedMemberDeclarations.ToList(); + _annotationService = new AnnotationService(this); var implementsInstructions = UserDeclarations(DeclarationType.ClassModule).SelectMany(cls => @@ -134,9 +139,14 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable item.Context, item => item.Members)), true); } + public IEnumerable FreshUndeclared + { + get { return _newUndeclared.AllValues(); } + } + public IEnumerable Undeclared { - get { return _undeclared.AllValues(); } + get { return _undeclared.SelectMany(item => item.Value).ToList(); } } public IEnumerable Members(Declaration module) @@ -209,14 +219,19 @@ public IEnumerable UserDeclarations(DeclarationType type) return result; } - public IEnumerable UnresolvedMemberDeclarations() + public IEnumerable FreshUnresolvedMemberDeclarations() { lock (ThreadLock) { - return _unresolved.ToArray(); + return _newUnresolved.ToArray(); } } + public IEnumerable UnresolvedMemberDeclarations() + { + return _unresolved.ToList(); + } + public IEnumerable FindHandlersForWithEventsField(Declaration field) { Declaration[] result; @@ -510,13 +525,13 @@ public Declaration OnUndeclaredVariable(Declaration enclosingProcedure, string i Accessibility.Implicit, DeclarationType.Variable, context, context.GetSelection(), false, null, false, annotations, null, true); - var hasUndeclared = _undeclared.ContainsKey(enclosingProcedure.QualifiedName); + var hasUndeclared = _newUndeclared.ContainsKey(enclosingProcedure.QualifiedName); if (hasUndeclared) { ConcurrentBag undeclared; - while (!_undeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared)) + while (!_newUndeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared)) { - _undeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared); + _newUndeclared.TryGetValue(enclosingProcedure.QualifiedName, out undeclared); } var inScopeUndeclared = undeclared.FirstOrDefault(d => d.IdentifierName == identifierName); if (inScopeUndeclared != null) @@ -527,7 +542,7 @@ public Declaration OnUndeclaredVariable(Declaration enclosingProcedure, string i } else { - _undeclared.TryAdd(enclosingProcedure.QualifiedName, new ConcurrentBag { undeclaredLocal }); + _newUndeclared.TryAdd(enclosingProcedure.QualifiedName, new ConcurrentBag { undeclaredLocal }); } return undeclaredLocal; } @@ -550,7 +565,7 @@ public void AddUnboundContext(Declaration parentDeclaration, VBAParser.LExprCont (access is VBAParser.MemberAccessExprContext) ? (ParserRuleContext)access.children[0] : withExpression.Context, annotations); - _unresolved.Add(declaration); + _newUnresolved.Add(declaration); } public Declaration OnBracketedExpression(string expression, ParserRuleContext context) @@ -561,13 +576,13 @@ public Declaration OnBracketedExpression(string expression, ParserRuleContext co var qualifiedName = hostApp.QualifiedName.QualifiedModuleName.QualifyMemberName(expression); ConcurrentBag undeclared; - if (_undeclared.TryGetValue(qualifiedName, out undeclared)) + if (_newUndeclared.TryGetValue(qualifiedName, out undeclared)) { return undeclared.SingleOrDefault(); } var item = new Declaration(qualifiedName, hostApp, hostApp, Tokens.Variant, string.Empty, false, false, Accessibility.Global, DeclarationType.BracketedExpression, context, context.GetSelection(), false, null); - _undeclared.TryAdd(qualifiedName, new ConcurrentBag { item }); + _newUndeclared.TryAdd(qualifiedName, new ConcurrentBag { item }); return item; } diff --git a/Rubberduck.Parsing/VBA/ModuleState.cs b/Rubberduck.Parsing/VBA/ModuleState.cs index 9773b718ab..0fcee29caa 100644 --- a/Rubberduck.Parsing/VBA/ModuleState.cs +++ b/Rubberduck.Parsing/VBA/ModuleState.cs @@ -13,6 +13,7 @@ namespace Rubberduck.Parsing.VBA public class ModuleState { public ConcurrentDictionary Declarations { get; private set; } + public ConcurrentDictionary UnresolvedMemberDeclarations { get; private set; } public ITokenStream TokenStream { get; private set; } public IParseTree ParseTree { get; private set; } public ParserState State { get; private set; } @@ -29,7 +30,8 @@ public class ModuleState public ModuleState(ConcurrentDictionary declarations) { Declarations = declarations; - TokenStream = null; + UnresolvedMemberDeclarations = new ConcurrentDictionary(); + TokenStream = null;UnboundMemberDeclaration ParseTree = null; if (declarations.Any() && declarations.ElementAt(0).Key.QualifiedName.QualifiedModuleName.Component != null) @@ -55,6 +57,7 @@ public ModuleState(ConcurrentDictionary declarations) public ModuleState(ParserState state) { Declarations = new ConcurrentDictionary(); + UnresolvedMemberDeclarations = new ConcurrentDictionary(); TokenStream = null; ParseTree = null; State = state; @@ -72,6 +75,7 @@ public ModuleState(ParserState state) public ModuleState(SyntaxErrorException moduleException) { Declarations = new ConcurrentDictionary(); + UnresolvedMemberDeclarations = new ConcurrentDictionary(); TokenStream = null; ParseTree = null; State = ParserState.Error; @@ -89,6 +93,7 @@ public ModuleState(SyntaxErrorException moduleException) public ModuleState(IDictionary, Attributes> moduleAttributes) { Declarations = new ConcurrentDictionary(); + UnresolvedMemberDeclarations = new ConcurrentDictionary(); TokenStream = null; ParseTree = null; State = ParserState.None; diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index 22109be3ac..cdf7b5607b 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -241,7 +241,8 @@ private void ExecuteCommonParseActivities(List toParse, Cancellati throw new OperationCanceledException(token); } - ResolveAllReferences(token); + var toResolveReferences = State.ParseTrees.Select(kvp => kvp.Key).ToHashSet(); + ResolveAllReferences(toResolveReferences, token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) { @@ -469,11 +470,11 @@ private Declaration CreateProjectDeclaration(QualifiedModuleName projectQualifie } - private void ResolveAllReferences(CancellationToken token) + private void ResolveAllReferences(ICollection toResolve, CancellationToken token) { token.ThrowIfCancellationRequested(); - var components = State.ParseTrees.Select(kvp => kvp.Key.Component).ToList(); + var components = toResolve.Select(qmn => qmn.Component).ToList(); token.ThrowIfCancellationRequested(); @@ -485,13 +486,15 @@ private void ResolveAllReferences(CancellationToken token) token.ThrowIfCancellationRequested(); + var parseTreesToResolve = State.ParseTrees.Where(kvp => toResolve.Contains(kvp.Key)).ToList(); + var options = new ParallelOptions(); options.CancellationToken = token; options.MaxDegreeOfParallelism = _maxDegreeOfReferenceResolverParallelism; try { - Parallel.For(0, State.ParseTrees.Count, options, - (index) => ResolveReferences(State.DeclarationFinder, State.ParseTrees[index].Key, State.ParseTrees[index].Value, token) + Parallel.For(0, parseTreesToResolve.Count, options, + (index) => ResolveReferences(State.DeclarationFinder, parseTreesToResolve[index].Key, parseTreesToResolve[index].Value, token) ); } catch (AggregateException exception) @@ -510,10 +513,11 @@ private void ResolveAllReferences(CancellationToken token) token.ThrowIfCancellationRequested(); - AddUndeclaredVariablesToDeclarations(); + AddNewUndeclaredVariablesToDeclarations(); + AddNewUnresolvedMemberDeclarations(); //This is here and not in the calling method because it has to happen before the ready state is reached. - //RefreshDeclarationFinder(); //Commented out because it breaks the unresolved and undeclared collections. + RefreshDeclarationFinder(); token.ThrowIfCancellationRequested(); @@ -604,15 +608,24 @@ private void AddModuleToModuleReferences(DeclarationFinder finder, QualifiedModu } } - private void AddUndeclaredVariablesToDeclarations() + private void AddNewUndeclaredVariablesToDeclarations() { - var undeclared = State.DeclarationFinder.Undeclared.ToList(); + var undeclared = State.DeclarationFinder.FreshUndeclared.ToList(); foreach (var declaration in undeclared) { State.AddDeclaration(declaration); } } + private void AddNewUnresolvedMemberDeclarations() + { + var unresolved = State.DeclarationFinder.FreshUnresolvedMemberDeclarations().ToList(); + foreach (var declaration in unresolved) + { + State.AddUnresolvedMemberDeclaration(declaration); + } + } + /// /// Starts parsing all components of all unprotected VBProjects associated with the VBE-Instance passed to the constructor of this parser instance. diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs index d5501d5ba4..6632057f2f 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs @@ -69,7 +69,7 @@ public sealed class RubberduckParserState : IDisposable internal void RefreshFinder(IHostApplication host) { - DeclarationFinder = new DeclarationFinder(AllDeclarations, AllAnnotations, host); + DeclarationFinder = new DeclarationFinder(AllDeclarations, AllAnnotations, AllUnresolvedMemberDeclarations, host); } private readonly IVBE _vbe; @@ -629,6 +629,28 @@ public IReadOnlyList AllDeclarations } } + /// + /// Gets a copy of the unresolved member declarations. + /// + public IReadOnlyList AllUnresolvedMemberDeclarations + { + get + { + var declarations = new List(); + foreach (var state in _moduleStates.Values) + { + if (state.UnresolvedMemberDeclarations == null) + { + continue; + } + + declarations.AddRange(state.UnresolvedMemberDeclarations.Keys); + } + + return declarations; + } + } + private readonly ConcurrentBag _builtInDeclarationTrees = new ConcurrentBag(); public IProducerConsumerCollection BuiltInDeclarationTrees { get { return _builtInDeclarationTrees; } } @@ -694,6 +716,28 @@ public void AddDeclaration(Declaration declaration) } } + /// + /// Adds the specified to the collection (replaces existing). + /// + public void AddUnresolvedMemberDeclaration(UnboundMemberDeclaration declaration) + { + var key = declaration.QualifiedName.QualifiedModuleName; + var declarations = _moduleStates.GetOrAdd(key, new ModuleState(new ConcurrentDictionary())).UnresolvedMemberDeclarations; + + if (declarations.ContainsKey(declaration)) + { + byte _; + while (!declarations.TryRemove(declaration, out _)) + { + Logger.Warn("Could not remove existing unresolved member declaration for '{0}' ({1}). Retrying.", declaration.IdentifierName, declaration.DeclarationType); + } + } + while (!declarations.TryAdd(declaration, 0) && !declarations.ContainsKey(declaration)) + { + Logger.Warn("Could not add unresolved member declaration '{0}' ({1}). Retrying.", declaration.IdentifierName, declaration.DeclarationType); + } + } + private void ClearStateCache(string projectId, bool notifyStateChanged = false) { try From 9be84819719251b7e3b088bc37b038421688668d Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Wed, 1 Mar 2017 00:00:28 +0100 Subject: [PATCH 2/7] Added infrastructure to remove references by specified modules only. --- Rubberduck.Parsing/Symbols/Declaration.cs | 7 +++++++ Rubberduck.Parsing/VBA/RubberduckParserState.cs | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Rubberduck.Parsing/Symbols/Declaration.cs b/Rubberduck.Parsing/Symbols/Declaration.cs index 6a149d9f62..bcd9bfa599 100644 --- a/Rubberduck.Parsing/Symbols/Declaration.cs +++ b/Rubberduck.Parsing/Symbols/Declaration.cs @@ -616,5 +616,12 @@ public void ClearReferences() { _references = new ConcurrentBag(); } + + public void RemoveReferencesFrom(ICollection modulesByWhichToRemoveReferences) + { + //This gets replaced with a new ConcurrentBag because one cannot remove specific items from a ConcurrentBag. + //Moreover, changing to a ConcurrentDictionary breaks all sorts of tests, for some obscure reason. + var newReferences = new ConcurrentBag(_references.Where(reference => !modulesByWhichToRemoveReferences.Contains(reference.QualifiedModuleName))); + } } } diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs index 6632057f2f..5bd67129ee 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs @@ -795,6 +795,14 @@ public void ClearAllReferences() } } + public void RemoveAllReferencesBy(ICollection referencesFromToRemove) + { + foreach (var declaration in AllDeclarations) + { + declaration.RemoveReferencesFrom(referencesFromToRemove); + } + } + public bool ClearStateCache(IVBComponent component, bool notifyStateChanged = false) { return component != null && ClearStateCache(new QualifiedModuleName(component), notifyStateChanged); From 96a014d00b89ff5052172fa5e2bd5a39cfa6d231 Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Wed, 1 Mar 2017 00:49:23 +0100 Subject: [PATCH 3/7] Set up reference removal and modules to reresolve in the ParseCoordinator. (Not yet activated.) --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 34 +++++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index cdf7b5607b..f99eaf966c 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -214,10 +214,8 @@ private void ExecuteCommonParseActivities(List toParse, Cancellati token.ThrowIfCancellationRequested(); - _projectDeclarations.Clear(); - State.ClearAllReferences(); - - ClearModuleToModuleReferences(toParse); + var toResolveReferences = ModulesForWhichToResolveReferences(toParse); + PerformPreParseCleanup(toParse, toResolveReferences); ParseComponents(toParse, token); @@ -241,7 +239,7 @@ private void ExecuteCommonParseActivities(List toParse, Cancellati throw new OperationCanceledException(token); } - var toResolveReferences = State.ParseTrees.Select(kvp => kvp.Key).ToHashSet(); + toResolveReferences = State.ParseTrees.Select(kvp => kvp.Key).ToHashSet(); ResolveAllReferences(toResolveReferences, token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) @@ -271,6 +269,24 @@ private void SetModuleStates(List components, ParserState parserSt } } + private ICollection ModulesForWhichToResolveReferences(List toParse) + { + var toResolveReferences = new HashSet(); + foreach (var qmn in toParse.Select(component => new QualifiedModuleName(component))) + { + toResolveReferences.UnionWith(State.ModulesReferencing(qmn)); + } + return toResolveReferences; + } + + private void PerformPreParseCleanup(List toParse, ICollection toResolveReferences) + { + ClearModuleToModuleReferences(toParse); + State.RemoveAllReferencesBy(toResolveReferences); + State.ClearAllReferences(); //This has to stay here until the selective resolving actually works. + _projectDeclarations.Clear(); + } + private void ClearModuleToModuleReferences(List toClear) { foreach (var component in toClear) @@ -711,10 +727,12 @@ private bool CleanUpRemovedComponents(List components) { var removedModuledecalrations = RemovedModuleDeclarations(components); var componentRemoved = removedModuledecalrations.Any(); - foreach (var declaration in removedModuledecalrations) + var removedModules = removedModuledecalrations.Select(declaration => declaration.QualifiedName.QualifiedModuleName).ToHashSet(); + State.RemoveAllReferencesBy(removedModules); + foreach (var qmn in removedModules) { - State.ClearModuleToModuleReferencesFromModule(declaration.QualifiedName.QualifiedModuleName); - State.ClearStateCache(declaration.QualifiedName.QualifiedModuleName); + State.ClearModuleToModuleReferencesFromModule(qmn); + State.ClearStateCache(qmn); } return componentRemoved; } From b8756b109923ec98409fda808d956916ad47306d Mon Sep 17 00:00:00 2001 From: Max Doerner Date: Wed, 1 Mar 2017 21:54:47 +0100 Subject: [PATCH 4/7] Fixed stupid bug, enabled selective resolving of references. --- Rubberduck.Parsing/Symbols/DeclarationFinder.cs | 7 ------- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 4 +--- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs index 3f63c08abd..39441a4d3a 100644 --- a/Rubberduck.Parsing/Symbols/DeclarationFinder.cs +++ b/Rubberduck.Parsing/Symbols/DeclarationFinder.cs @@ -44,7 +44,6 @@ public class DeclarationFinder private readonly ConcurrentDictionary> _declarations; private readonly ConcurrentDictionary> _newUndeclared; private readonly ConcurrentBag _newUnresolved; - private readonly Dictionary> _undeclared; private readonly List _unresolved; private readonly ConcurrentDictionary> _annotations; private readonly ConcurrentDictionary> _parametersByParent; @@ -93,7 +92,6 @@ public DeclarationFinder(IReadOnlyList declarations, IEnumerable>(new Dictionary>()); - _undeclared = declarations.Where(declaration => declaration.IsUndeclared).GroupBy(item => item.QualifiedName).ToDictionary(group => group.Key, group => group.ToList()); _newUnresolved = new ConcurrentBag(new List()); _unresolved = unresolvedMemberDeclarations.ToList(); @@ -144,11 +142,6 @@ public IEnumerable FreshUndeclared get { return _newUndeclared.AllValues(); } } - public IEnumerable Undeclared - { - get { return _undeclared.SelectMany(item => item.Value).ToList(); } - } - public IEnumerable Members(Declaration module) { return Members(module.QualifiedName.QualifiedModuleName); diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index f99eaf966c..7c3f5ea1b6 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -239,7 +239,6 @@ private void ExecuteCommonParseActivities(List toParse, Cancellati throw new OperationCanceledException(token); } - toResolveReferences = State.ParseTrees.Select(kvp => kvp.Key).ToHashSet(); ResolveAllReferences(toResolveReferences, token); if (token.IsCancellationRequested || State.Status >= ParserState.Error) @@ -271,7 +270,7 @@ private void SetModuleStates(List components, ParserState parserSt private ICollection ModulesForWhichToResolveReferences(List toParse) { - var toResolveReferences = new HashSet(); + var toResolveReferences = toParse.Select(component => new QualifiedModuleName(component)).ToHashSet(); foreach (var qmn in toParse.Select(component => new QualifiedModuleName(component))) { toResolveReferences.UnionWith(State.ModulesReferencing(qmn)); @@ -283,7 +282,6 @@ private void PerformPreParseCleanup(List toParse, ICollection Date: Wed, 1 Mar 2017 23:40:14 +0100 Subject: [PATCH 5/7] Some optimization --- Rubberduck.Parsing/VBA/ParseCoordinator.cs | 63 +++++++++++++------ .../VBA/RubberduckParserState.cs | 18 +++--- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/Rubberduck.Parsing/VBA/ParseCoordinator.cs b/Rubberduck.Parsing/VBA/ParseCoordinator.cs index 7c3f5ea1b6..32886bb60d 100644 --- a/Rubberduck.Parsing/VBA/ParseCoordinator.cs +++ b/Rubberduck.Parsing/VBA/ParseCoordinator.cs @@ -31,6 +31,7 @@ public class ParseCoordinator : IParseCoordinator private const int _maxDegreeOfDeclarationResolverParallelism = -1; private const int _maxDegreeOfReferenceResolverParallelism = -1; private const int _maxDegreeOfModuleStateChangeParallelism = -1; + private const int _maxDegreeOfReferenceRemovalParallelism = -1; private const int _maxReferenceLoadingConcurrency = -1; private readonly IDictionary, Attributes>> _componentAttributes @@ -185,7 +186,7 @@ private void ClearComponentStateCacheForTests() } } - private void CleanUpComponentAttributes(List components) + private void CleanUpComponentAttributes(ICollection components) { foreach (var key in _componentAttributes.Keys) { @@ -214,8 +215,9 @@ private void ExecuteCommonParseActivities(List toParse, Cancellati token.ThrowIfCancellationRequested(); - var toResolveReferences = ModulesForWhichToResolveReferences(toParse); - PerformPreParseCleanup(toParse, toResolveReferences); + var modulesToParse = toParse.Select(component => new QualifiedModuleName(component)).ToHashSet(); + var toResolveReferences = ModulesForWhichToResolveReferences(modulesToParse); + PerformPreParseCleanup(modulesToParse, toResolveReferences, token); ParseComponents(toParse, token); @@ -268,28 +270,48 @@ private void SetModuleStates(List components, ParserState parserSt } } - private ICollection ModulesForWhichToResolveReferences(List toParse) + private ICollection ModulesForWhichToResolveReferences(ICollection modulesToParse) { - var toResolveReferences = toParse.Select(component => new QualifiedModuleName(component)).ToHashSet(); - foreach (var qmn in toParse.Select(component => new QualifiedModuleName(component))) + var toResolveReferences = modulesToParse.ToHashSet(); + foreach (var qmn in modulesToParse) { toResolveReferences.UnionWith(State.ModulesReferencing(qmn)); } return toResolveReferences; } - private void PerformPreParseCleanup(List toParse, ICollection toResolveReferences) + private void PerformPreParseCleanup(ICollection modulesToParse, ICollection toResolveReferences, CancellationToken token) { - ClearModuleToModuleReferences(toParse); - State.RemoveAllReferencesBy(toResolveReferences); + ClearModuleToModuleReferences(modulesToParse); + RemoveAllReferencesBy(toResolveReferences, modulesToParse, State.DeclarationFinder, token); //All declarations on the modulesToParse get destroyed anyway. _projectDeclarations.Clear(); } - private void ClearModuleToModuleReferences(List toClear) + private void ClearModuleToModuleReferences(ICollection toClear) { - foreach (var component in toClear) + foreach (var qmn in toClear) { - State.ClearModuleToModuleReferencesFromModule(new QualifiedModuleName(component)); + State.ClearModuleToModuleReferencesFromModule(qmn); + } + } + + //This doesn not live on the RubberduckParserState to keep concurrency haanlding out of it. + public void RemoveAllReferencesBy(ICollection referencesFromToRemove, ICollection modulesNotNeedingReferenceRemoval, DeclarationFinder finder, CancellationToken token) + { + var referencedModulesNeedingReferenceRemoval = State.ModulesReferencedBy(referencesFromToRemove).Where(qmn => !modulesNotNeedingReferenceRemoval.Contains(qmn)); + + var options = new ParallelOptions(); + options.CancellationToken = token; + options.MaxDegreeOfParallelism = _maxDegreeOfReferenceRemovalParallelism; + + Parallel.ForEach(referencedModulesNeedingReferenceRemoval, options, qmn => RemoveReferences(finder.Members(qmn), referencesFromToRemove)); + } + + private void RemoveReferences(IEnumerable declarations, ICollection referencesFromToRemove) + { + foreach (var declaration in declarations) + { + declaration.RemoveReferencesFrom(referencesFromToRemove); } } @@ -688,7 +710,7 @@ private void ParseAllInternal(object requestor, CancellationToken token) token.ThrowIfCancellationRequested(); - var componentsRemoved = CleanUpRemovedComponents(components); + var componentsRemoved = CleanUpRemovedComponents(components, token); token.ThrowIfCancellationRequested(); @@ -721,21 +743,24 @@ private void ParseAllInternal(object requestor, CancellationToken token) /// Clears state cache of removed components. /// Returns whether components have been removed. /// - private bool CleanUpRemovedComponents(List components) + private bool CleanUpRemovedComponents(ICollection components, CancellationToken token) { var removedModuledecalrations = RemovedModuleDeclarations(components); var componentRemoved = removedModuledecalrations.Any(); var removedModules = removedModuledecalrations.Select(declaration => declaration.QualifiedName.QualifiedModuleName).ToHashSet(); - State.RemoveAllReferencesBy(removedModules); - foreach (var qmn in removedModules) + if (removedModules.Any()) { - State.ClearModuleToModuleReferencesFromModule(qmn); - State.ClearStateCache(qmn); + RemoveAllReferencesBy(removedModules, removedModules, State.DeclarationFinder, token); + foreach (var qmn in removedModules) + { + State.ClearModuleToModuleReferencesFromModule(qmn); + State.ClearStateCache(qmn); + } } return componentRemoved; } - private IEnumerable RemovedModuleDeclarations(List components) + private IEnumerable RemovedModuleDeclarations(ICollection components) { var moduleDeclarations = State.AllUserDeclarations.Where(declaration => declaration.DeclarationType.HasFlag(DeclarationType.Module)); var componentKeys = components.Select(component => new { name = component.Name, projectId = component.Collection.Parent.HelpFile }).ToHashSet(); diff --git a/Rubberduck.Parsing/VBA/RubberduckParserState.cs b/Rubberduck.Parsing/VBA/RubberduckParserState.cs index 5bd67129ee..092f197b40 100644 --- a/Rubberduck.Parsing/VBA/RubberduckParserState.cs +++ b/Rubberduck.Parsing/VBA/RubberduckParserState.cs @@ -795,14 +795,6 @@ public void ClearAllReferences() } } - public void RemoveAllReferencesBy(ICollection referencesFromToRemove) - { - foreach (var declaration in AllDeclarations) - { - declaration.RemoveReferencesFrom(referencesFromToRemove); - } - } - public bool ClearStateCache(IVBComponent component, bool notifyStateChanged = false) { return component != null && ClearStateCache(new QualifiedModuleName(component), notifyStateChanged); @@ -1189,6 +1181,16 @@ public HashSet ModulesReferencedBy(QualifiedModuleName refe return new HashSet(referencingModuleState.HasReferenceToModule.Keys); } + public HashSet ModulesReferencedBy(IEnumerable referencingModules) + { + var referencedModules = new HashSet(); + foreach (var referencingModule in referencedModules) + { + referencedModules.UnionWith(ModulesReferencedBy(referencingModule)); + } + return referencedModules; + } + public HashSet ModulesReferencing(QualifiedModuleName referencedModule) { ModuleState referencedModuleState; From ceefdb1e6260f2cf32c4ebb9ba35b9b4ed5e716d Mon Sep 17 00:00:00 2001 From: comintern Date: Wed, 1 Mar 2017 17:49:52 -0600 Subject: [PATCH 6/7] Stop throwing in WndProc of SubclassingWindow --- Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs b/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs index 6cec5a9b79..340e7d253b 100644 --- a/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs +++ b/Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs @@ -43,7 +43,7 @@ protected SubclassingWindow(IntPtr subclassId, IntPtr hWnd) public void Dispose() { ReleaseHandle(); - //GC.SuppressFinalize(this); + _thisHandle.Free(); } private void AssignHandle() @@ -86,14 +86,13 @@ public virtual int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr l { if (!_listening) { - throw new Exception("State corrupted. Received window message while not listening."); + Debug.WriteLine("State corrupted. Received window message while not listening."); + return DefSubclassProc(hWnd, msg, wParam, lParam); } - Debug.Assert(IsWindow(_hwnd)); if ((uint)msg == (uint)WM.RUBBERDUCK_SINKING || (uint)msg == (uint)WM.DESTROY) { - ReleaseHandle(); - _thisHandle.Free(); + ReleaseHandle(); } return DefSubclassProc(hWnd, msg, wParam, lParam); } From 6c62ae2ffc9851c1275a8afd6815eb0465cb6566 Mon Sep 17 00:00:00 2001 From: comintern Date: Wed, 1 Mar 2017 17:50:29 -0600 Subject: [PATCH 7/7] Stop SelectionChange events from blocking on the main UI thread. --- RetailCoder.VBE/UI/SelectionChangeService.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/RetailCoder.VBE/UI/SelectionChangeService.cs b/RetailCoder.VBE/UI/SelectionChangeService.cs index df4d85fd92..14f97377af 100644 --- a/RetailCoder.VBE/UI/SelectionChangeService.cs +++ b/RetailCoder.VBE/UI/SelectionChangeService.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using Rubberduck.Parsing; using Rubberduck.Parsing.Symbols; using Rubberduck.VBEditor.Events; @@ -39,8 +40,11 @@ private void OnVbeSelectionChanged(object sender, SelectionChangedEventArgs e) return; } - var eventArgs = new DeclarationChangedEventArgs(e.CodePane, _parser.State.FindSelectedDeclaration(e.CodePane)); - DispatchSelectedDeclaration(eventArgs); + new Task(() => + { + var eventArgs = new DeclarationChangedEventArgs(e.CodePane, _parser.State.FindSelectedDeclaration(e.CodePane)); + DispatchSelectedDeclaration(eventArgs); + }).Start(); } private void OnVbeFocusChanged(object sender, WindowChangedEventArgs e) @@ -55,13 +59,13 @@ private void OnVbeFocusChanged(object sender, WindowChangedEventArgs e) { return; } - DispatchSelectedDesignerDeclaration(_vbe.SelectedVBComponent); + new Task(() => DispatchSelectedDesignerDeclaration(_vbe.SelectedVBComponent)).Start(); break; case WindowKind.CodeWindow: //Caret changed in a code pane. if (e.CodePane != null && !e.CodePane.IsWrappingNullReference) { - DispatchSelectedDeclaration(new DeclarationChangedEventArgs(e.CodePane, _parser.State.FindSelectedDeclaration(e.CodePane))); + new Task(() => DispatchSelectedDeclaration(new DeclarationChangedEventArgs(e.CodePane, _parser.State.FindSelectedDeclaration(e.CodePane)))).Start(); } break; } @@ -69,7 +73,7 @@ private void OnVbeFocusChanged(object sender, WindowChangedEventArgs e) else if (e.EventType == FocusType.ChildFocus) { //Treeview selection changed in project window. - DispatchSelectedProjectNodeDeclaration(_vbe.SelectedVBComponent); + new Task(() => DispatchSelectedProjectNodeDeclaration(_vbe.SelectedVBComponent)).Start(); } }