| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library analyzer.src.task.incremental_element_builder; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:analyzer/src/generated/ast.dart'; | |
| 10 import 'package:analyzer/src/generated/element.dart'; | |
| 11 import 'package:analyzer/src/generated/resolver.dart'; | |
| 12 import 'package:analyzer/src/generated/scanner.dart'; | |
| 13 import 'package:analyzer/src/generated/source.dart'; | |
| 14 | |
| 15 /** | |
| 16 * The change of a single [CompilationUnitElement]. | |
| 17 */ | |
| 18 class CompilationUnitElementDelta { | |
| 19 /** | |
| 20 * One or more directives were added/removed. | |
| 21 */ | |
| 22 bool hasDirectiveChange = false; | |
| 23 | |
| 24 /** | |
| 25 * The list of added top-level element. | |
| 26 */ | |
| 27 final List<Element> addedDeclarations = <Element>[]; | |
| 28 | |
| 29 /** | |
| 30 * The list of removed top-level elements. | |
| 31 */ | |
| 32 final List<Element> removedDeclarations = <Element>[]; | |
| 33 } | |
| 34 | |
| 35 /** | |
| 36 * Incrementally updates the existing [unitElement] and builds elements for | |
| 37 * the [newUnit]. | |
| 38 */ | |
| 39 class IncrementalCompilationUnitElementBuilder { | |
| 40 final Source unitSource; | |
| 41 final Source librarySource; | |
| 42 final CompilationUnit oldUnit; | |
| 43 final CompilationUnitElementImpl unitElement; | |
| 44 final CompilationUnit newUnit; | |
| 45 final ElementHolder holder = new ElementHolder(); | |
| 46 | |
| 47 /** | |
| 48 * The change between element models of [oldUnit] and [newUnit]. | |
| 49 */ | |
| 50 final CompilationUnitElementDelta unitDelta = | |
| 51 new CompilationUnitElementDelta(); | |
| 52 | |
| 53 factory IncrementalCompilationUnitElementBuilder( | |
| 54 CompilationUnit oldUnit, CompilationUnit newUnit) { | |
| 55 CompilationUnitElementImpl unitElement = oldUnit.element; | |
| 56 return new IncrementalCompilationUnitElementBuilder._(unitElement.source, | |
| 57 unitElement.librarySource, oldUnit, newUnit, unitElement); | |
| 58 } | |
| 59 | |
| 60 IncrementalCompilationUnitElementBuilder._(this.unitSource, | |
| 61 this.librarySource, this.oldUnit, this.newUnit, this.unitElement); | |
| 62 | |
| 63 /** | |
| 64 * Updates [oldUnit] to have the same directives and declarations, in the | |
| 65 * same order as in [newUnit]. Existing resolution is kept where possible. | |
| 66 * | |
| 67 * Updates [unitElement] by adding/removing elements as needed. | |
| 68 * | |
| 69 * Fills [unitDelta] with added/remove elements. | |
| 70 */ | |
| 71 void build() { | |
| 72 new CompilationUnitBuilder().buildCompilationUnit( | |
| 73 unitSource, newUnit, librarySource); | |
| 74 _processDirectives(); | |
| 75 _processUnitMembers(); | |
| 76 newUnit.element = unitElement; | |
| 77 _replaceUnitContents(oldUnit, newUnit); | |
| 78 } | |
| 79 | |
| 80 void _addElementToHolder(Element element) { | |
| 81 if (element is PropertyAccessorElement) { | |
| 82 holder.addAccessor(element); | |
| 83 } else if (element is ClassElement) { | |
| 84 if (element.isEnum) { | |
| 85 holder.addEnum(element); | |
| 86 } else { | |
| 87 holder.addType(element); | |
| 88 } | |
| 89 } else if (element is FunctionElement) { | |
| 90 holder.addFunction(element); | |
| 91 } else if (element is FunctionTypeAliasElement) { | |
| 92 holder.addTypeAlias(element); | |
| 93 } else if (element is TopLevelVariableElement) { | |
| 94 holder.addTopLevelVariable(element); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 void _processDirectives() { | |
| 99 Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>(); | |
| 100 for (Directive oldDirective in oldUnit.directives) { | |
| 101 String code = TokenUtils.getFullCode(oldDirective); | |
| 102 oldDirectiveMap[code] = oldDirective; | |
| 103 } | |
| 104 // Replace new nodes with the identical old nodes. | |
| 105 Set<Directive> removedDirectives = oldUnit.directives.toSet(); | |
| 106 for (Directive newDirective in newUnit.directives) { | |
| 107 String code = TokenUtils.getFullCode(newDirective); | |
| 108 // Prepare an old directive. | |
| 109 Directive oldDirective = oldDirectiveMap[code]; | |
| 110 if (oldDirective == null) { | |
| 111 unitDelta.hasDirectiveChange = true; | |
| 112 continue; | |
| 113 } | |
| 114 // URI's must be resolved to the same sources. | |
| 115 if (newDirective is UriBasedDirective && | |
| 116 oldDirective is UriBasedDirective) { | |
| 117 if (oldDirective.source != newDirective.source) { | |
| 118 continue; | |
| 119 } | |
| 120 } | |
| 121 // Do replacement. | |
| 122 _replaceNode(newDirective, oldDirective); | |
| 123 removedDirectives.remove(oldDirective); | |
| 124 } | |
| 125 // If there are any directives left, then these directives were removed. | |
| 126 if (removedDirectives.isNotEmpty) { | |
| 127 unitDelta.hasDirectiveChange = true; | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 void _processUnitMembers() { | |
| 132 Map<String, CompilationUnitMember> oldNodeMap = | |
| 133 new HashMap<String, CompilationUnitMember>(); | |
| 134 for (CompilationUnitMember oldNode in oldUnit.declarations) { | |
| 135 String code = TokenUtils.getFullCode(oldNode); | |
| 136 oldNodeMap[code] = oldNode; | |
| 137 } | |
| 138 // Prepare all old top-level elements. | |
| 139 Set<Element> removedElements = new Set<Element>(); | |
| 140 removedElements.addAll(unitElement.accessors); | |
| 141 removedElements.addAll(unitElement.enums); | |
| 142 removedElements.addAll(unitElement.functions); | |
| 143 removedElements.addAll(unitElement.functionTypeAliases); | |
| 144 removedElements.addAll(unitElement.types); | |
| 145 removedElements.addAll(unitElement.topLevelVariables); | |
| 146 // Replace new nodes with the identical old nodes. | |
| 147 for (CompilationUnitMember newNode in newUnit.declarations) { | |
| 148 String code = TokenUtils.getFullCode(newNode); | |
| 149 // Prepare an old node. | |
| 150 CompilationUnitMember oldNode = oldNodeMap[code]; | |
| 151 if (oldNode == null) { | |
| 152 List<Element> elements = _getElements(newNode); | |
| 153 elements.forEach(_addElementToHolder); | |
| 154 elements.forEach(unitDelta.addedDeclarations.add); | |
| 155 continue; | |
| 156 } | |
| 157 // Do replacement. | |
| 158 _replaceNode(newNode, oldNode); | |
| 159 List<Element> elements = _getElements(oldNode); | |
| 160 elements.forEach(_addElementToHolder); | |
| 161 elements.forEach(removedElements.remove); | |
| 162 } | |
| 163 unitDelta.removedDeclarations.addAll(removedElements); | |
| 164 // Update CompilationUnitElement. | |
| 165 unitElement.accessors = holder.accessors; | |
| 166 unitElement.enums = holder.enums; | |
| 167 unitElement.functions = holder.functions; | |
| 168 unitElement.typeAliases = holder.typeAliases; | |
| 169 unitElement.types = holder.types; | |
| 170 unitElement.topLevelVariables = holder.topLevelVariables; | |
| 171 holder.validate(); | |
| 172 } | |
| 173 | |
| 174 /** | |
| 175 * Replaces [newNode] with [oldNode], updates tokens and elements. | |
| 176 * The nodes must have the same tokens, but offsets may be different. | |
| 177 */ | |
| 178 void _replaceNode(AstNode newNode, AstNode oldNode) { | |
| 179 // Replace node. | |
| 180 NodeReplacer.replace(newNode, oldNode); | |
| 181 // Replace tokens. | |
| 182 { | |
| 183 Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode); | |
| 184 Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode); | |
| 185 oldBeginToken.previous.setNext(newBeginToken); | |
| 186 oldNode.endToken.setNext(newNode.endToken.next); | |
| 187 } | |
| 188 // Change tokens offsets. | |
| 189 Map<int, int> offsetMap = new HashMap<int, int>(); | |
| 190 TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken, | |
| 191 newNode.beginToken, oldNode.endToken, newNode.endToken, true); | |
| 192 // Change elements offsets. | |
| 193 { | |
| 194 var visitor = new _UpdateElementOffsetsVisitor(offsetMap); | |
| 195 List<Element> elements = _getElements(oldNode); | |
| 196 for (Element element in elements) { | |
| 197 element.accept(visitor); | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 /** | |
| 203 * Returns [Element]s that are declared directly by the given [node]. | |
| 204 * This does not include any child elements - parameters, local variables. | |
| 205 * | |
| 206 * Usually just one [Element] is returned, but [VariableDeclarationList] | |
| 207 * nodes may declare more than one. | |
| 208 */ | |
| 209 static List<Element> _getElements(AstNode node) { | |
| 210 List<Element> elements = <Element>[]; | |
| 211 if (node is TopLevelVariableDeclaration) { | |
| 212 VariableDeclarationList variableList = node.variables; | |
| 213 if (variableList != null) { | |
| 214 for (VariableDeclaration variable in variableList.variables) { | |
| 215 TopLevelVariableElement element = variable.element; | |
| 216 elements.add(element); | |
| 217 if (element.getter != null) { | |
| 218 elements.add(element.getter); | |
| 219 } | |
| 220 if (element.setter != null) { | |
| 221 elements.add(element.setter); | |
| 222 } | |
| 223 } | |
| 224 } | |
| 225 } else if (node is PartDirective || node is PartOfDirective) { | |
| 226 } else if (node is Directive && node.element != null) { | |
| 227 elements.add(node.element); | |
| 228 } else if (node is Declaration && node.element != null) { | |
| 229 Element element = node.element; | |
| 230 elements.add(element); | |
| 231 if (element is PropertyAccessorElement) { | |
| 232 elements.add(element.variable); | |
| 233 } | |
| 234 } | |
| 235 return elements; | |
| 236 } | |
| 237 | |
| 238 /** | |
| 239 * Replaces contents of the [to] unit with the contenxts of the [from] unit. | |
| 240 */ | |
| 241 static void _replaceUnitContents(CompilationUnit to, CompilationUnit from) { | |
| 242 to.directives.clear(); | |
| 243 to.declarations.clear(); | |
| 244 to.beginToken = from.beginToken; | |
| 245 to.scriptTag = from.scriptTag; | |
| 246 to.directives.addAll(from.directives); | |
| 247 to.declarations.addAll(from.declarations); | |
| 248 to.element = to.element; | |
| 249 to.lineInfo = from.lineInfo; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 /** | |
| 254 * Utilities for [Token] manipulations. | |
| 255 */ | |
| 256 class TokenUtils { | |
| 257 static const String _SEPARATOR = "\uFFFF"; | |
| 258 | |
| 259 /** | |
| 260 * Copy offsets from [newToken]s to [oldToken]s. | |
| 261 */ | |
| 262 static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken, | |
| 263 Token newToken, Token oldEndToken, Token newEndToken, | |
| 264 [bool goUpComment = false]) { | |
| 265 if (oldToken is CommentToken && newToken is CommentToken) { | |
| 266 if (goUpComment) { | |
| 267 copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent, | |
| 268 (newToken as CommentToken).parent, oldEndToken, newEndToken); | |
| 269 } | |
| 270 while (oldToken != null) { | |
| 271 offsetMap[oldToken.offset] = newToken.offset; | |
| 272 oldToken.offset = newToken.offset; | |
| 273 oldToken = oldToken.next; | |
| 274 newToken = newToken.next; | |
| 275 } | |
| 276 assert(oldToken == null); | |
| 277 assert(newToken == null); | |
| 278 return; | |
| 279 } | |
| 280 while (true) { | |
| 281 if (oldToken.precedingComments != null) { | |
| 282 assert(newToken.precedingComments != null); | |
| 283 copyTokenOffsets(offsetMap, oldToken.precedingComments, | |
| 284 newToken.precedingComments, oldEndToken, newEndToken); | |
| 285 } | |
| 286 offsetMap[oldToken.offset] = newToken.offset; | |
| 287 oldToken.offset = newToken.offset; | |
| 288 if (oldToken == oldEndToken) { | |
| 289 assert(newToken == newEndToken); | |
| 290 break; | |
| 291 } | |
| 292 oldToken = oldToken.next; | |
| 293 newToken = newToken.next; | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 static Token getBeginTokenNotComment(AstNode node) { | |
| 298 Token oldBeginToken = node.beginToken; | |
| 299 if (oldBeginToken is CommentToken) { | |
| 300 oldBeginToken = (oldBeginToken as CommentToken).parent; | |
| 301 } | |
| 302 return oldBeginToken; | |
| 303 } | |
| 304 | |
| 305 /** | |
| 306 * Return the token string of all the [node] tokens. | |
| 307 */ | |
| 308 static String getFullCode(AstNode node) { | |
| 309 List<Token> tokens = getTokens(node); | |
| 310 return joinTokens(tokens); | |
| 311 } | |
| 312 | |
| 313 /** | |
| 314 * Returns all tokends (including comments) of the given [node]. | |
| 315 */ | |
| 316 static List<Token> getTokens(AstNode node) { | |
| 317 List<Token> tokens = <Token>[]; | |
| 318 Token token = getBeginTokenNotComment(node); | |
| 319 Token endToken = node.endToken; | |
| 320 while (true) { | |
| 321 // append comment tokens | |
| 322 for (Token commentToken = token.precedingComments; | |
| 323 commentToken != null; | |
| 324 commentToken = commentToken.next) { | |
| 325 tokens.add(commentToken); | |
| 326 } | |
| 327 // append token | |
| 328 tokens.add(token); | |
| 329 // next token | |
| 330 if (token == endToken) { | |
| 331 break; | |
| 332 } | |
| 333 token = token.next; | |
| 334 } | |
| 335 return tokens; | |
| 336 } | |
| 337 | |
| 338 static String joinTokens(List<Token> tokens) { | |
| 339 return tokens.map((token) => token.lexeme).join(_SEPARATOR); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 /** | |
| 344 * Updates name offsets of [Element]s according to the [map]. | |
| 345 */ | |
| 346 class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor { | |
| 347 final Map<int, int> map; | |
| 348 | |
| 349 _UpdateElementOffsetsVisitor(this.map); | |
| 350 | |
| 351 void visitElement(Element element) { | |
| 352 if (element is CompilationUnitElement) { | |
| 353 return; | |
| 354 } | |
| 355 if (element.isSynthetic) { | |
| 356 return; | |
| 357 } | |
| 358 int oldOffset = element.nameOffset; | |
| 359 int newOffset = map[oldOffset]; | |
| 360 assert(newOffset != null); | |
| 361 (element as ElementImpl).nameOffset = newOffset; | |
| 362 if (element is! LibraryElement) { | |
| 363 super.visitElement(element); | |
| 364 } | |
| 365 } | |
| 366 } | |
| OLD | NEW |