public NodeStateTable <T> GetLatestStateTableForNode <T>(IIncrementalGeneratorNode <T> source) { // if we've already evaluated a node during this build, we can just return the existing result if (_stateTableBuilder.TryGetTable(source, out var table)) { return((NodeStateTable <T>)table); } // get the previous table, if there was one for this node NodeStateTable <T> previousTable = _previousTable._tables.GetStateTableOrEmpty <T>(source); // request the node update its state based on the current driver table and store the new result var newTable = source.UpdateStateTable(this, previousTable, _cancellationToken); _stateTableBuilder.SetTable(source, newTable); return(newTable); }
public IStateTable GetSyntaxInputTable(SyntaxInputNode syntaxInputNode, NodeStateTable <SyntaxTree> syntaxTreeTable) { Debug.Assert(_syntaxInputNodes.Contains(syntaxInputNode)); // when we don't have a value for this node, we update all the syntax inputs at once if (!_tableBuilder.Contains(syntaxInputNode)) { // CONSIDER: when the compilation is the same as previous, the syntax trees must also be the same. // if we have a previous state table for a node, we can just short circuit knowing that it is up to date // This step isn't part of the tree, so we can skip recording. var compilationIsCached = _compilation == _previous._compilation; // get a builder for each input node var syntaxInputBuilders = ArrayBuilder <(SyntaxInputNode node, ISyntaxInputBuilder builder)> .GetInstance(_syntaxInputNodes.Length); foreach (var node in _syntaxInputNodes) { // We don't cache the tracked incremental steps in a manner that we can easily rehydrate between runs, // so we disable the cached compilation perf optimization when incremental step tracking is enabled. if (compilationIsCached && !_enableTracking && _previous._tables.TryGetValue(node, out var previousStateTable)) { _tableBuilder.SetTable(node, previousStateTable); } else { syntaxInputBuilders.Add((node, node.GetBuilder(_previous._tables, _enableTracking))); _syntaxTimes[node] = TimeSpan.Zero; } } if (syntaxInputBuilders.Count > 0) { // at this point we need to grab the syntax trees from the new compilation, and optionally diff them against the old ones NodeStateTable <SyntaxTree> syntaxTreeState = syntaxTreeTable; // update each tree for the builders, sharing the semantic model foreach (var(tree, state, syntaxTreeIndex, stepInfo) in syntaxTreeState) { var root = new Lazy <SyntaxNode>(() => tree.GetRoot(_cancellationToken)); var model = state != EntryState.Removed ? new Lazy <SemanticModel>(() => _compilation.GetSemanticModel(tree)) : null; for (int i = 0; i < syntaxInputBuilders.Count; i++) { var currentNode = syntaxInputBuilders[i].node; try { Stopwatch sw = Stopwatch.StartNew(); try { _cancellationToken.ThrowIfCancellationRequested(); syntaxInputBuilders[i].builder.VisitTree(root, state, model, _cancellationToken); } finally { var elapsed = sw.Elapsed; // if this node isn't the one that caused the update, ensure we remember it and remove the time it took from the requester if (currentNode != syntaxInputNode) { _syntaxTimes[syntaxInputNode] = _syntaxTimes[syntaxInputNode].Subtract(elapsed); _syntaxTimes[currentNode] = _syntaxTimes[currentNode].Add(elapsed); } } } catch (UserFunctionException ufe) { // we're evaluating this node ahead of time, so we can't just throw the exception // instead we'll hold onto it, and throw the exception when a downstream node actually // attempts to read the value _syntaxExceptions[currentNode] = ufe; syntaxInputBuilders.RemoveAt(i); i--; } } } // save the updated inputs foreach ((var node, ISyntaxInputBuilder builder) in syntaxInputBuilders) { builder.SaveStateAndFree(_tableBuilder); Debug.Assert(_tableBuilder.Contains(node)); } } syntaxInputBuilders.Free(); } // if we don't have an entry for this node, it must have thrown an exception if (!_tableBuilder.TryGetTable(syntaxInputNode, out var result)) { throw _syntaxExceptions[syntaxInputNode]; } return(result); }