// Adds every syntax/bound pair in a tree rooted at the given bound node to the map, and the // performs a lookup of the given syntax node in the map. private BoundNode AddBoundTreeAndGetBoundNodeFromMap(SyntaxNode syntax, BoundNode bound) { using (nodeMapLock.DisposableWrite()) { NodeMapBuilder.AddToMap(bound, this.guardedNodeMap); BoundNode result; this.guardedNodeMap.TryGetValue(syntax, out result); return(result); } }
/// <summary> /// Walks the bound tree and adds all non compiler generated bound nodes whose syntax matches the given one /// to the cache. /// </summary> /// <param name="root">The root of the bound tree.</param> /// <param name="map">The cache.</param> /// <param name="node">The syntax node where to add bound nodes for.</param> public static void AddToMap(BoundNode root, Dictionary<CSharpSyntaxNode, ImmutableArray<BoundNode>> map, CSharpSyntaxNode node = null) { Debug.Assert(node == null || root == null || !(root.Syntax is StatementSyntax), "individually added nodes are not supposed to be statements."); if (root == null || map.ContainsKey(root.Syntax)) { // root node is already in the map, children must be in the map too. return; } var additionMap = OrderPreservingMultiDictionary<CSharpSyntaxNode, BoundNode>.GetInstance(); var builder = new NodeMapBuilder(additionMap, node); builder.Visit(root); foreach (CSharpSyntaxNode key in additionMap.Keys) { if (map.ContainsKey(key)) { #if DEBUG // It's possible that AddToMap was previously called with a subtree of root. If this is the case, // then we'll see an entry in the map. Since the incremental binder should also have seen the // pre-existing map entry, the entry in addition map should be identical. // Another, more unfortunate, possibility is that we've had to re-bind the syntax and the new bound // nodes are equivalent, but not identical, to the existing ones. In such cases, we prefer the // existing nodes so that the cache will always return the same bound node for a given syntax node. // EXAMPLE: Suppose we have the statement P.M(1); // First, we ask for semantic info about "P". We'll walk up to the statement level and bind that. // We'll end up with map entries for "1", "P", "P.M(1)", and "P.M(1);". // Next, we ask for semantic info about "P.M". That isn't in our map, so we walk up to the statement // level - again - and bind that - again. // Once again, we'll end up with map entries for "1", "P", "P.M(1)", and "P.M(1);". They will // have the same structure as the original map entries, but will not be ReferenceEquals. var existing = map[key]; var added = additionMap[key]; Debug.Assert(existing.Length == added.Length, "existing.Length == added.Length"); for (int i = 0; i < existing.Length; i++) { // TODO: it would be great if we could check !ReferenceEquals(existing[i], added[i]) (DevDiv #11584). // Known impediments include: // 1) Field initializers aren't cached because they're not in statements. // 2) Single local declarations (e.g. "int x = 1;" vs "int x = 1, y = 2;") aren't found in the cache // since nothing is cached for the statement syntax. if (existing[i].Kind != added[i].Kind) { Debug.Assert(!(key is StatementSyntax), "!(key is StatementSyntax)"); // This also seems to be happening when we get equivalent BoundTypeExpression and BoundTypeOrValueExpression nodes. if (existing[i].Kind == BoundKind.TypeExpression && added[i].Kind == BoundKind.TypeOrValueExpression) { Debug.Assert( ((BoundTypeExpression)existing[i]).Type == ((BoundTypeOrValueExpression)added[i]).Type, string.Format( CultureInfo.InvariantCulture, "((BoundTypeExpression)existing[{0}]).Type == ((BoundTypeOrValueExpression)added[{0}]).Type", i)); } else if (existing[i].Kind == BoundKind.TypeOrValueExpression && added[i].Kind == BoundKind.TypeExpression) { Debug.Assert( ((BoundTypeOrValueExpression)existing[i]).Type == ((BoundTypeExpression)added[i]).Type, string.Format( CultureInfo.InvariantCulture, "((BoundTypeOrValueExpression)existing[{0}]).Type == ((BoundTypeExpression)added[{0}]).Type", i)); } else { Debug.Assert(false, "New bound node does not match existing bound node"); } } else { Debug.Assert( (object)existing[i] == added[i] || !(key is StatementSyntax), string.Format( CultureInfo.InvariantCulture, "(object)existing[{0}] == added[{0}] || !(key is StatementSyntax)", i)); } } #endif } else { map[key] = additionMap[key]; } } additionMap.Free(); }
public static void AddToMap(BoundNode root, Dictionary <SyntaxNode, ImmutableArray <BoundNode> > map, SyntaxTree tree, SyntaxNode node = null) { Debug.Assert(node == null || root == null || !(root.Syntax is StatementSyntax), "individually added nodes are not supposed to be statements."); if (root == null || map.ContainsKey(root.Syntax)) { // root node is already in the map, children must be in the map too. return; } var additionMap = OrderPreservingMultiDictionary <SyntaxNode, BoundNode> .GetInstance(); var builder = new NodeMapBuilder(additionMap, tree, node); builder.Visit(root); foreach (CSharpSyntaxNode key in additionMap.Keys) { if (map.ContainsKey(key)) { #if DEBUG // It's possible that AddToMap was previously called with a subtree of root. If this is the case, // then we'll see an entry in the map. Since the incremental binder should also have seen the // pre-existing map entry, the entry in addition map should be identical. // Another, more unfortunate, possibility is that we've had to re-bind the syntax and the new bound // nodes are equivalent, but not identical, to the existing ones. In such cases, we prefer the // existing nodes so that the cache will always return the same bound node for a given syntax node. // EXAMPLE: Suppose we have the statement P.M(1); // First, we ask for semantic info about "P". We'll walk up to the statement level and bind that. // We'll end up with map entries for "1", "P", "P.M(1)", and "P.M(1);". // Next, we ask for semantic info about "P.M". That isn't in our map, so we walk up to the statement // level - again - and bind that - again. // Once again, we'll end up with map entries for "1", "P", "P.M(1)", and "P.M(1);". They will // have the same structure as the original map entries, but will not be ReferenceEquals. var existing = map[key]; var added = additionMap[key]; Debug.Assert(existing.Length == added.Length, "existing.Length == added.Length"); for (int i = 0; i < existing.Length; i++) { // TODO: it would be great if we could check !ReferenceEquals(existing[i], added[i]) (DevDiv #11584). // Known impediments include: // 1) Field initializers aren't cached because they're not in statements. // 2) Single local declarations (e.g. "int x = 1;" vs "int x = 1, y = 2;") aren't found in the cache // since nothing is cached for the statement syntax. if (existing[i].Kind != added[i].Kind) { Debug.Assert(!(key is StatementSyntax), "!(key is StatementSyntax)"); // This also seems to be happening when we get equivalent BoundTypeExpression and BoundTypeOrValueExpression nodes. if (existing[i].Kind == BoundKind.TypeExpression && added[i].Kind == BoundKind.TypeOrValueExpression) { Debug.Assert( TypeSymbol.Equals(((BoundTypeExpression)existing[i]).Type, ((BoundTypeOrValueExpression)added[i]).Type, TypeCompareKind.ConsiderEverything2), string.Format( System.Globalization.CultureInfo.InvariantCulture, "((BoundTypeExpression)existing[{0}]).Type == ((BoundTypeOrValueExpression)added[{0}]).Type", i)); } else if (existing[i].Kind == BoundKind.TypeOrValueExpression && added[i].Kind == BoundKind.TypeExpression) { Debug.Assert( TypeSymbol.Equals(((BoundTypeOrValueExpression)existing[i]).Type, ((BoundTypeExpression)added[i]).Type, TypeCompareKind.ConsiderEverything2), string.Format( System.Globalization.CultureInfo.InvariantCulture, "((BoundTypeOrValueExpression)existing[{0}]).Type == ((BoundTypeExpression)added[{0}]).Type", i)); } else { Debug.Assert(false, "New bound node does not match existing bound node"); } } else { Debug.Assert( (object)existing[i] == added[i] || !(key is StatementSyntax), string.Format( System.Globalization.CultureInfo.InvariantCulture, "(object)existing[{0}] == added[{0}] || !(key is StatementSyntax)", i)); } } #endif } else { map[key] = additionMap[key]; } } additionMap.Free(); }