OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library analyzer.src.task.incremental_element_builder; | 5 library analyzer.src.task.incremental_element_builder; |
6 | 6 |
7 import 'dart:collection'; | 7 import 'dart:collection'; |
8 | 8 |
9 import 'package:analyzer/src/generated/ast.dart'; | 9 import 'package:analyzer/src/generated/ast.dart'; |
10 import 'package:analyzer/src/generated/element.dart'; | 10 import 'package:analyzer/src/generated/element.dart'; |
11 import 'package:analyzer/src/generated/resolver.dart'; | 11 import 'package:analyzer/src/generated/resolver.dart'; |
12 import 'package:analyzer/src/generated/scanner.dart'; | 12 import 'package:analyzer/src/generated/scanner.dart'; |
13 import 'package:analyzer/src/generated/source.dart'; | 13 import 'package:analyzer/src/generated/source.dart'; |
14 | 14 |
15 /** | 15 /** |
16 * Incrementally updates the existing [oldUnitElement] and builds elements for | 16 * Incrementally updates the existing [unitElement] and builds elements for |
17 * the [newUnit]. | 17 * the [newUnit]. |
18 */ | 18 */ |
19 class IncrementalCompilationUnitElementBuilder { | 19 class IncrementalCompilationUnitElementBuilder { |
20 final Source source; | 20 final Source source; |
21 final CompilationUnit oldUnit; | 21 final CompilationUnit oldUnit; |
22 final CompilationUnitElement oldUnitElement; | 22 final CompilationUnitElementImpl unitElement; |
23 final CompilationUnit newUnit; | 23 final CompilationUnit newUnit; |
| 24 final ElementHolder holder = new ElementHolder(); |
24 | 25 |
25 IncrementalCompilationUnitElementBuilder( | 26 IncrementalCompilationUnitElementBuilder( |
26 CompilationUnit oldUnit, this.newUnit) | 27 CompilationUnit oldUnit, this.newUnit) |
27 : oldUnit = oldUnit, | 28 : oldUnit = oldUnit, |
28 oldUnitElement = oldUnit.element, | 29 unitElement = oldUnit.element, |
29 source = oldUnit.element.source; | 30 source = oldUnit.element.source; |
30 | 31 |
31 void build() { | 32 void build() { |
32 new CompilationUnitBuilder().buildCompilationUnit(source, newUnit); | 33 new CompilationUnitBuilder().buildCompilationUnit(source, newUnit); |
33 _processDirectives(); | 34 _processDirectives(); |
| 35 _processUnitMembers(); |
| 36 newUnit.element = unitElement; |
| 37 } |
| 38 |
| 39 void _addElementsToHolder(CompilationUnitMember node) { |
| 40 List<Element> elements = _getElements(node); |
| 41 elements.forEach(_addElementToHolder); |
| 42 } |
| 43 |
| 44 void _addElementToHolder(Element element) { |
| 45 if (element is PropertyAccessorElement) { |
| 46 holder.addAccessor(element); |
| 47 } else if (element is ClassElement) { |
| 48 if (element.isEnum) { |
| 49 holder.addEnum(element); |
| 50 } else { |
| 51 holder.addType(element); |
| 52 } |
| 53 } else if (element is FunctionElement) { |
| 54 holder.addFunction(element); |
| 55 } else if (element is FunctionTypeAliasElement) { |
| 56 holder.addTypeAlias(element); |
| 57 } else if (element is TopLevelVariableElement) { |
| 58 holder.addTopLevelVariable(element); |
| 59 } |
34 } | 60 } |
35 | 61 |
36 void _processDirectives() { | 62 void _processDirectives() { |
37 Map<String, Directive> oldDirectiveMap = <String, Directive>{}; | 63 Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>(); |
38 // Fill the old directives map. | |
39 for (Directive oldDirective in oldUnit.directives) { | 64 for (Directive oldDirective in oldUnit.directives) { |
40 String code = TokenUtils.getFullCode(oldDirective); | 65 String code = TokenUtils.getFullCode(oldDirective); |
41 oldDirectiveMap[code] = oldDirective; | 66 oldDirectiveMap[code] = oldDirective; |
42 } | 67 } |
43 // Replace new nodes with the identical old nodes. | 68 // Replace new nodes with the identical old nodes. |
44 for (Directive newDirective in newUnit.directives) { | 69 for (Directive newDirective in newUnit.directives) { |
45 String code = TokenUtils.getFullCode(newDirective); | 70 String code = TokenUtils.getFullCode(newDirective); |
46 // Prepare an old directive. | 71 // Prepare an old directive. |
47 Directive oldDirective = oldDirectiveMap[code]; | 72 Directive oldDirective = oldDirectiveMap[code]; |
48 if (oldDirective == null) { | 73 if (oldDirective == null) { |
49 continue; | 74 continue; |
50 } | 75 } |
51 // URI's must be resolved to the same sources. | 76 // URI's must be resolved to the same sources. |
52 if (newDirective is UriBasedDirective && | 77 if (newDirective is UriBasedDirective && |
53 oldDirective is UriBasedDirective) { | 78 oldDirective is UriBasedDirective) { |
54 if (oldDirective.source != newDirective.source) { | 79 if (oldDirective.source != newDirective.source) { |
55 continue; | 80 continue; |
56 } | 81 } |
57 } | 82 } |
58 // Do replacement. | 83 // Do replacement. |
59 _replaceNode(newDirective, oldDirective, oldDirective.element); | 84 _replaceNode(newDirective, oldDirective); |
60 } | 85 } |
61 } | 86 } |
62 | 87 |
| 88 void _processUnitMembers() { |
| 89 Map<String, CompilationUnitMember> oldNodeMap = |
| 90 new HashMap<String, CompilationUnitMember>(); |
| 91 for (CompilationUnitMember oldNode in oldUnit.declarations) { |
| 92 String code = TokenUtils.getFullCode(oldNode); |
| 93 oldNodeMap[code] = oldNode; |
| 94 } |
| 95 // Replace new nodes with the identical old nodes. |
| 96 for (CompilationUnitMember newNode in newUnit.declarations) { |
| 97 String code = TokenUtils.getFullCode(newNode); |
| 98 // Prepare an old node. |
| 99 CompilationUnitMember oldNode = oldNodeMap[code]; |
| 100 if (oldNode == null) { |
| 101 _addElementsToHolder(newNode); |
| 102 continue; |
| 103 } |
| 104 // Do replacement. |
| 105 _replaceNode(newNode, oldNode); |
| 106 _addElementsToHolder(oldNode); |
| 107 } |
| 108 // Update CompilationUnitElement. |
| 109 unitElement.accessors = holder.accessors; |
| 110 unitElement.enums = holder.enums; |
| 111 unitElement.functions = holder.functions; |
| 112 unitElement.typeAliases = holder.typeAliases; |
| 113 unitElement.types = holder.types; |
| 114 unitElement.topLevelVariables = holder.topLevelVariables; |
| 115 holder.validate(); |
| 116 } |
| 117 |
63 /** | 118 /** |
64 * Replaces [newNode] with [oldNode], updates tokens and elements. | 119 * Replaces [newNode] with [oldNode], updates tokens and elements. |
65 * The nodes must have the same tokens, but offsets may be different. | 120 * The nodes must have the same tokens, but offsets may be different. |
66 */ | 121 */ |
67 void _replaceNode(AstNode newNode, AstNode oldNode, Element oldElement) { | 122 void _replaceNode(AstNode newNode, AstNode oldNode) { |
68 // Replace node. | 123 // Replace node. |
69 NodeReplacer.replace(newNode, oldNode); | 124 NodeReplacer.replace(newNode, oldNode); |
70 // Replace tokens. | 125 // Replace tokens. |
71 { | 126 { |
72 Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode); | 127 Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode); |
73 Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode); | 128 Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode); |
74 oldBeginToken.previous.setNext(newBeginToken); | 129 oldBeginToken.previous.setNext(newBeginToken); |
75 oldNode.endToken.setNext(newNode.endToken.next); | 130 oldNode.endToken.setNext(newNode.endToken.next); |
76 } | 131 } |
77 // Change tokens offsets. | 132 // Change tokens offsets. |
78 Map<int, int> offsetMap = new HashMap<int, int>(); | 133 Map<int, int> offsetMap = new HashMap<int, int>(); |
79 TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken, | 134 TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken, |
80 newNode.beginToken, oldNode.endToken, newNode.endToken, true); | 135 newNode.beginToken, oldNode.endToken, newNode.endToken, true); |
81 // Change elements offsets. | 136 // Change elements offsets. |
82 oldElement.accept(new _UpdateElementOffsetsVisitor(offsetMap)); | 137 { |
| 138 var visitor = new _UpdateElementOffsetsVisitor(offsetMap); |
| 139 List<Element> elements = _getElements(oldNode); |
| 140 for (Element element in elements) { |
| 141 element.accept(visitor); |
| 142 } |
| 143 } |
| 144 } |
| 145 |
| 146 /** |
| 147 * Returns [Element]s that are declared directly by the given [node]. |
| 148 * This does not include any child elements - parameters, local variables. |
| 149 * |
| 150 * Usually just one [Element] is returned, but [VariableDeclarationList] |
| 151 * nodes may declare more than one. |
| 152 */ |
| 153 static List<Element> _getElements(AstNode node) { |
| 154 List<Element> elements = <Element>[]; |
| 155 if (node is TopLevelVariableDeclaration) { |
| 156 VariableDeclarationList variableList = node.variables; |
| 157 if (variableList != null) { |
| 158 for (VariableDeclaration variable in variableList.variables) { |
| 159 TopLevelVariableElement element = variable.element; |
| 160 elements.add(element); |
| 161 elements.add(element.getter); |
| 162 elements.add(element.setter); |
| 163 } |
| 164 } |
| 165 } else if (node is Directive && node.element != null) { |
| 166 elements.add(node.element); |
| 167 } else if (node is Declaration && node.element != null) { |
| 168 Element element = node.element; |
| 169 elements.add(element); |
| 170 if (element is PropertyAccessorElement) { |
| 171 elements.add(element.variable); |
| 172 } |
| 173 } |
| 174 return elements; |
83 } | 175 } |
84 } | 176 } |
85 | 177 |
86 /** | 178 /** |
87 * Utilities for [Token] manipulations. | 179 * Utilities for [Token] manipulations. |
88 */ | 180 */ |
89 class TokenUtils { | 181 class TokenUtils { |
90 static const String _SEPARATOR = "\uFFFF"; | 182 static const String _SEPARATOR = "\uFFFF"; |
91 | 183 |
92 /** | 184 /** |
93 * Copy offsets from [newToken]s to [oldToken]s. | 185 * Copy offsets from [newToken]s to [oldToken]s. |
94 */ | 186 */ |
95 static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken, | 187 static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken, |
96 Token newToken, Token oldEndToken, Token newEndToken, | 188 Token newToken, Token oldEndToken, Token newEndToken, |
97 [bool goUpComment = false]) { | 189 [bool goUpComment = false]) { |
98 if (oldToken is CommentToken && newToken is CommentToken) { | 190 if (oldToken is CommentToken && newToken is CommentToken) { |
99 if (goUpComment) { | 191 if (goUpComment) { |
100 copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent, | 192 copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent, |
101 (newToken as CommentToken).parent, oldEndToken, newEndToken); | 193 (newToken as CommentToken).parent, oldEndToken, newEndToken); |
102 } | 194 } |
103 while (oldToken.type != TokenType.EOF) { | 195 while (oldToken != null) { |
104 offsetMap[oldToken.offset] = newToken.offset; | 196 offsetMap[oldToken.offset] = newToken.offset; |
105 oldToken.offset = newToken.offset; | 197 oldToken.offset = newToken.offset; |
106 oldToken = oldToken.next; | 198 oldToken = oldToken.next; |
107 newToken = newToken.next; | 199 newToken = newToken.next; |
108 } | 200 } |
| 201 assert(oldToken == null); |
| 202 assert(newToken == null); |
| 203 return; |
109 } | 204 } |
110 while (true) { | 205 while (true) { |
111 if (oldToken.precedingComments != null) { | 206 if (oldToken.precedingComments != null) { |
112 assert(newToken.precedingComments == null); | 207 assert(newToken.precedingComments != null); |
113 copyTokenOffsets(offsetMap, oldToken.precedingComments, | 208 copyTokenOffsets(offsetMap, oldToken.precedingComments, |
114 newToken.precedingComments, oldEndToken, newEndToken); | 209 newToken.precedingComments, oldEndToken, newEndToken); |
115 } | 210 } |
116 offsetMap[oldToken.offset] = newToken.offset; | 211 offsetMap[oldToken.offset] = newToken.offset; |
117 oldToken.offset = newToken.offset; | 212 oldToken.offset = newToken.offset; |
118 if (oldToken == oldEndToken) { | 213 if (oldToken == oldEndToken) { |
119 assert(newToken == newEndToken); | 214 assert(newToken == newEndToken); |
120 break; | 215 break; |
121 } | 216 } |
122 oldToken = oldToken.next; | 217 oldToken = oldToken.next; |
(...skipping 10 matching lines...) Expand all Loading... |
133 } | 228 } |
134 | 229 |
135 /** | 230 /** |
136 * Return the token string of all the [node] tokens. | 231 * Return the token string of all the [node] tokens. |
137 */ | 232 */ |
138 static String getFullCode(AstNode node) { | 233 static String getFullCode(AstNode node) { |
139 List<Token> tokens = getTokens(node); | 234 List<Token> tokens = getTokens(node); |
140 return joinTokens(tokens); | 235 return joinTokens(tokens); |
141 } | 236 } |
142 | 237 |
| 238 /** |
| 239 * Returns all tokends (including comments) of the given [node]. |
| 240 */ |
143 static List<Token> getTokens(AstNode node) { | 241 static List<Token> getTokens(AstNode node) { |
144 List<Token> tokens = <Token>[]; | 242 List<Token> tokens = <Token>[]; |
145 Token token = node.beginToken; | 243 Token token = getBeginTokenNotComment(node); |
146 Token endToken = node.endToken; | 244 Token endToken = node.endToken; |
147 while (true) { | 245 while (true) { |
148 // append comment tokens | 246 // append comment tokens |
149 for (Token commentToken = token.precedingComments; | 247 for (Token commentToken = token.precedingComments; |
150 commentToken != null; | 248 commentToken != null; |
151 commentToken = commentToken.next) { | 249 commentToken = commentToken.next) { |
152 tokens.add(commentToken); | 250 tokens.add(commentToken); |
153 } | 251 } |
154 // append token | 252 // append token |
155 tokens.add(token); | 253 tokens.add(token); |
(...skipping 14 matching lines...) Expand all Loading... |
170 /** | 268 /** |
171 * Updates name offsets of [Element]s according to the [map]. | 269 * Updates name offsets of [Element]s according to the [map]. |
172 */ | 270 */ |
173 class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor { | 271 class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor { |
174 final Map<int, int> map; | 272 final Map<int, int> map; |
175 | 273 |
176 _UpdateElementOffsetsVisitor(this.map); | 274 _UpdateElementOffsetsVisitor(this.map); |
177 | 275 |
178 visitElement(Element element) { | 276 visitElement(Element element) { |
179 int oldOffset = element.nameOffset; | 277 int oldOffset = element.nameOffset; |
180 int newOffset = map[oldOffset]; | 278 if (oldOffset != -1) { |
181 assert(newOffset != null); | 279 int newOffset = map[oldOffset]; |
182 (element as ElementImpl).nameOffset = newOffset; | 280 assert(newOffset != null); |
| 281 (element as ElementImpl).nameOffset = newOffset; |
| 282 } |
183 if (element is! LibraryElement) { | 283 if (element is! LibraryElement) { |
184 super.visitElement(element); | 284 super.visitElement(element); |
185 } | 285 } |
186 } | 286 } |
187 } | 287 } |
OLD | NEW |