Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(466)

Side by Side Diff: pkg/analyzer/lib/src/task/incremental_element_builder.dart

Issue 2834773003: Remove the limited invalidation feature. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/test/generated/engine_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/dart/ast/ast.dart';
10 import 'package:analyzer/dart/ast/token.dart';
11 import 'package:analyzer/dart/element/element.dart';
12 import 'package:analyzer/dart/element/visitor.dart';
13 import 'package:analyzer/src/dart/ast/token.dart';
14 import 'package:analyzer/src/dart/ast/utilities.dart';
15 import 'package:analyzer/src/dart/constant/utilities.dart';
16 import 'package:analyzer/src/dart/element/builder.dart';
17 import 'package:analyzer/src/dart/element/element.dart';
18 import 'package:analyzer/src/generated/resolver.dart';
19 import 'package:analyzer/src/generated/source.dart';
20 import 'package:analyzer/src/task/dart.dart';
21
22 /**
23 * The change of a single [ClassElement].
24 */
25 class ClassElementDelta {
26 final ClassElement _element;
27 final Source librarySource;
28 final String name;
29
30 final Set<ClassElementDelta> superDeltas = new Set<ClassElementDelta>();
31
32 bool hasAnnotationChanges = false;
33
34 final List<PropertyAccessorElement> addedAccessors =
35 <PropertyAccessorElement>[];
36 final List<PropertyAccessorElement> removedAccessors =
37 <PropertyAccessorElement>[];
38
39 final List<ConstructorElement> addedConstructors = <ConstructorElement>[];
40 final List<ConstructorElement> removedConstructors = <ConstructorElement>[];
41 bool hasUnnamedConstructorChange = false;
42
43 final List<MethodElement> addedMethods = <MethodElement>[];
44 final List<MethodElement> removedMethods = <MethodElement>[];
45
46 ClassElementDelta(this._element, this.librarySource, this.name);
47
48 /**
49 * Return `true` if this delta has changes to the [name] visible in the
50 * given [librarySource].
51 */
52 bool hasChanges(Source librarySource, String name) {
53 if (Identifier.isPrivateName(name) && librarySource != this.librarySource) {
54 return false;
55 }
56 return _hasElementWithName(addedAccessors, name) ||
57 _hasElementWithName(removedAccessors, name) ||
58 _hasElementWithName(addedConstructors, name) ||
59 _hasElementWithName(removedConstructors, name) ||
60 _hasElementWithName(addedMethods, name) ||
61 _hasElementWithName(removedMethods, name);
62 }
63
64 static bool _hasElementWithName(List<Element> elements, String name) {
65 return elements.any((e) => e.displayName == name);
66 }
67 }
68
69 /**
70 * The change of a single [CompilationUnitElement].
71 */
72 class CompilationUnitElementDelta {
73 /**
74 * One or more directives were added/removed.
75 */
76 bool hasDirectiveChange = false;
77
78 /**
79 * The list of added top-level element.
80 */
81 final List<Element> addedDeclarations = <Element>[];
82
83 /**
84 * The list of removed top-level elements.
85 */
86 final List<Element> removedDeclarations = <Element>[];
87
88 /**
89 * The map from names of changed classes to the change deltas.
90 */
91 final Map<String, ClassElementDelta> classDeltas =
92 <String, ClassElementDelta>{};
93 }
94
95 /**
96 * Incrementally updates the existing [unitElement] and builds elements for
97 * the [newUnit].
98 */
99 class IncrementalCompilationUnitElementBuilder {
100 final Source unitSource;
101 final Source librarySource;
102 final CompilationUnit oldUnit;
103 final CompilationUnitElementImpl unitElement;
104 final CompilationUnit newUnit;
105 final ElementHolder unitElementHolder = new ElementHolder();
106
107 final List<ConstantEvaluationTarget> unitConstants =
108 <ConstantEvaluationTarget>[];
109
110 /**
111 * The change between element models of [oldUnit] and [newUnit].
112 */
113 final CompilationUnitElementDelta unitDelta =
114 new CompilationUnitElementDelta();
115
116 factory IncrementalCompilationUnitElementBuilder(
117 CompilationUnit oldUnit, CompilationUnit newUnit) {
118 CompilationUnitElementImpl unitElement = oldUnit.element;
119 return new IncrementalCompilationUnitElementBuilder._(unitElement.source,
120 unitElement.librarySource, oldUnit, newUnit, unitElement);
121 }
122
123 IncrementalCompilationUnitElementBuilder._(this.unitSource,
124 this.librarySource, this.oldUnit, this.newUnit, this.unitElement);
125
126 /**
127 * Updates [oldUnit] to have the same directives and declarations, in the
128 * same order as in [newUnit]. Existing resolution is kept where possible.
129 *
130 * Updates [unitElement] by adding/removing elements as needed.
131 *
132 * Fills [unitDelta] with added/remove elements.
133 */
134 void build() {
135 _materializeLazyElements();
136 new CompilationUnitBuilder()
137 .buildCompilationUnit(unitSource, newUnit, librarySource);
138 newUnit.accept(new EnumMemberBuilder(unitElement.context.typeProvider));
139 _processDirectives();
140 _processUnitMembers();
141 _replaceUnitContents(oldUnit, newUnit);
142 _findConstants();
143 newUnit.element = unitElement;
144 unitElement.setCodeRange(0, newUnit.endToken.end);
145 }
146
147 void _addElementToUnitHolder(Element element) {
148 if (element is ClassElement) {
149 if (element.isEnum) {
150 unitElementHolder.addEnum(element);
151 } else {
152 unitElementHolder.addType(element);
153 }
154 } else if (element is FunctionElement) {
155 unitElementHolder.addFunction(element);
156 } else if (element is FunctionTypeAliasElement) {
157 unitElementHolder.addTypeAlias(element);
158 } else if (element is PropertyAccessorElement) {
159 unitElementHolder.addAccessor(element);
160 } else if (element is TopLevelVariableElement) {
161 unitElementHolder.addTopLevelVariable(element);
162 }
163 }
164
165 void _findConstants() {
166 ConstantFinder finder = new ConstantFinder();
167 oldUnit.accept(finder);
168 unitConstants.addAll(finder.constantsToCompute);
169 // Update annotation constants to using the old unit element.
170 for (ConstantEvaluationTarget constant in unitConstants) {
171 if (constant is ElementAnnotationImpl) {
172 constant.compilationUnit = unitElement;
173 }
174 }
175 }
176
177 void _materializeLazyElements() {
178 unitElement.accept(new RecursiveElementVisitor());
179 }
180
181 ClassElementDelta _processClassMembers(
182 ClassDeclaration oldClass, ClassDeclaration newClass) {
183 // If the class hierarchy or type parameters are changed,
184 // then the class changed too much - don't compute the delta.
185 if (newClass.abstractKeyword != null && oldClass.abstractKeyword == null ||
186 newClass.abstractKeyword == null && oldClass.abstractKeyword != null ||
187 TokenUtils.getFullCode(newClass.typeParameters) !=
188 TokenUtils.getFullCode(oldClass.typeParameters) ||
189 TokenUtils.getFullCode(newClass.extendsClause) !=
190 TokenUtils.getFullCode(oldClass.extendsClause) ||
191 TokenUtils.getFullCode(newClass.withClause) !=
192 TokenUtils.getFullCode(oldClass.withClause) ||
193 TokenUtils.getFullCode(newClass.implementsClause) !=
194 TokenUtils.getFullCode(oldClass.implementsClause)) {
195 return null;
196 }
197 // Build the old class members map.
198 Map<String, ClassMember> oldNodeMap = new HashMap<String, ClassMember>();
199 for (ClassMember oldNode in oldClass.members) {
200 String code = TokenUtils.getFullCode(oldNode);
201 oldNodeMap[code] = oldNode;
202 }
203 // Prepare elements.
204 ClassElement newElement = newClass.element;
205 ClassElement oldElement = oldClass.element;
206 // Use the old element for the new node.
207 newClass.name.staticElement = oldElement;
208 if (newElement is ClassElementImpl && oldElement is ClassElementImpl) {
209 oldElement.nameOffset = newElement.nameOffset;
210 oldElement.setCodeRange(newElement.codeOffset, newElement.codeLength);
211 oldElement.typeParameters = newElement.typeParameters;
212 }
213 // Prepare delta.
214 ClassElementImpl classElement = oldClass.element;
215 ElementHolder classElementHolder = new ElementHolder();
216 ClassElementDelta classDelta =
217 new ClassElementDelta(classElement, librarySource, classElement.name);
218 // Check for annotation changes.
219 {
220 String oldAnnotationsCode =
221 TokenUtils.getFullCodeOfList(oldClass.metadata);
222 String newAnnotationsCode =
223 TokenUtils.getFullCodeOfList(newClass.metadata);
224 classDelta.hasAnnotationChanges =
225 oldAnnotationsCode != newAnnotationsCode;
226 }
227 // Prepare all old member elements.
228 var removedAccessors = new Set<PropertyAccessorElement>.identity();
229 var removedConstructors = new Set<ConstructorElement>.identity();
230 var removedMethods = new Set<MethodElement>.identity();
231 removedAccessors.addAll(classElement.accessors);
232 removedConstructors.addAll(classElement.constructors);
233 removedMethods.addAll(classElement.methods);
234 // Utilities.
235 void processConstructorDeclaration(
236 ConstructorDeclaration node, bool isNew) {
237 ConstructorElement element = node.element;
238 if (element != null) {
239 classElementHolder.addConstructor(element);
240 if (isNew) {
241 classDelta.addedConstructors.add(element);
242 } else {
243 removedConstructors.remove(element);
244 }
245 }
246 }
247
248 void processFieldDeclaration(FieldDeclaration node, bool isNew) {
249 for (VariableDeclaration field in node.fields.variables) {
250 PropertyInducingElement element = field.element;
251 if (element != null) {
252 PropertyAccessorElement getter = element.getter;
253 PropertyAccessorElement setter = element.setter;
254 if (getter != null) {
255 classElementHolder.addAccessor(getter);
256 if (isNew) {
257 classDelta.addedAccessors.add(getter);
258 } else {
259 removedAccessors.remove(getter);
260 }
261 }
262 if (setter != null) {
263 classElementHolder.addAccessor(setter);
264 if (isNew) {
265 classDelta.addedAccessors.add(setter);
266 } else {
267 removedAccessors.remove(setter);
268 }
269 }
270 }
271 }
272 }
273
274 void processMethodDeclaration(MethodDeclaration node, bool isNew) {
275 Element element = node.element;
276 if (element is MethodElement) {
277 classElementHolder.addMethod(element);
278 if (isNew) {
279 classDelta.addedMethods.add(element);
280 } else {
281 removedMethods.remove(element);
282 }
283 } else if (element is PropertyAccessorElement) {
284 classElementHolder.addAccessor(element);
285 if (isNew) {
286 classDelta.addedAccessors.add(element);
287 } else {
288 removedAccessors.remove(element);
289 }
290 }
291 }
292
293 // Replace new nodes with the identical old nodes.
294 bool newHasConstructor = false;
295 for (ClassMember newNode in newClass.members) {
296 String code = TokenUtils.getFullCode(newNode);
297 ClassMember oldNode = oldNodeMap.remove(code);
298 // When we type a name before a constructor with a documentation
299 // comment, this makes the comment disappear from AST. So, even though
300 // tokens are the same, the nodes are not the same.
301 if (oldNode != null) {
302 if (oldNode.documentationComment == null &&
303 newNode.documentationComment != null ||
304 oldNode.documentationComment != null &&
305 newNode.documentationComment == null) {
306 oldNode = null;
307 }
308 }
309 // Add the new element.
310 if (oldNode == null) {
311 if (newNode is ConstructorDeclaration) {
312 newHasConstructor = true;
313 processConstructorDeclaration(newNode, true);
314 }
315 if (newNode is FieldDeclaration) {
316 processFieldDeclaration(newNode, true);
317 }
318 if (newNode is MethodDeclaration) {
319 processMethodDeclaration(newNode, true);
320 }
321 continue;
322 }
323 // Do replacement.
324 _replaceNode(newNode, oldNode);
325 if (oldNode is ConstructorDeclaration) {
326 processConstructorDeclaration(oldNode, false);
327 }
328 if (oldNode is FieldDeclaration) {
329 processFieldDeclaration(oldNode, false);
330 }
331 if (oldNode is MethodDeclaration) {
332 processMethodDeclaration(oldNode, false);
333 }
334 }
335 // If the class had only a default synthetic constructor, and there are
336 // no explicit constructors in the new AST, keep the constructor.
337 if (!newHasConstructor) {
338 List<ConstructorElement> constructors = classElement.constructors;
339 if (constructors.length == 1) {
340 ConstructorElement constructor = constructors[0];
341 if (constructor.isSynthetic && constructor.isDefaultConstructor) {
342 classElementHolder.addConstructor(constructor);
343 removedConstructors.remove(constructor);
344 }
345 }
346 }
347 // Update the delta.
348 classDelta.removedAccessors.addAll(removedAccessors);
349 classDelta.removedConstructors.addAll(removedConstructors);
350 classDelta.removedMethods.addAll(removedMethods);
351 // Prepare fields.
352 List<PropertyAccessorElement> newAccessors = classElementHolder.accessors;
353 Map<String, FieldElement> newFields = <String, FieldElement>{};
354 for (PropertyAccessorElement accessor in newAccessors) {
355 newFields[accessor.displayName] = accessor.variable;
356 }
357 // Update references to fields from constructors.
358 for (ClassMember member in newClass.members) {
359 if (member is ConstructorDeclaration) {
360 for (FormalParameter parameter in member.parameters.parameters) {
361 FormalParameter normalParameter = parameter;
362 if (parameter is DefaultFormalParameter) {
363 normalParameter = parameter.parameter;
364 }
365 if (normalParameter is FieldFormalParameter) {
366 FieldFormalParameterElementImpl parameterElement =
367 normalParameter.element as FieldFormalParameterElementImpl;
368 parameterElement.field = newFields[parameterElement.name];
369 }
370 }
371 }
372 }
373 // Update ClassElement.
374 classElement.metadata = newElement.metadata;
375 classElement.accessors = newAccessors;
376 classElement.constructors = classElementHolder.constructors;
377 classElement.fields = newFields.values.toList();
378 classElement.methods = classElementHolder.methods;
379 classElement.version++;
380 classElementHolder.validate();
381 // Ensure at least a default synthetic constructor.
382 if (classElement.constructors.isEmpty) {
383 ConstructorElementImpl constructor =
384 new ConstructorElementImpl.forNode(null);
385 constructor.isSynthetic = true;
386 classElement.constructors = <ConstructorElement>[constructor];
387 classDelta.addedConstructors.add(constructor);
388 }
389 classDelta.hasUnnamedConstructorChange =
390 classDelta.addedConstructors.any((c) => c.name == '') ||
391 classDelta.removedConstructors.any((c) => c.name == '');
392 // OK
393 return classDelta;
394 }
395
396 void _processDirectives() {
397 Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>();
398 for (Directive oldDirective in oldUnit.directives) {
399 String code = TokenUtils.getFullCode(oldDirective);
400 oldDirectiveMap[code] = oldDirective;
401 }
402 // Replace new nodes with the identical old nodes.
403 Set<Directive> removedDirectives = oldUnit.directives.toSet();
404 for (Directive newDirective in newUnit.directives) {
405 String code = TokenUtils.getFullCode(newDirective);
406 // Prepare an old directive.
407 Directive oldDirective = oldDirectiveMap[code];
408 if (oldDirective == null) {
409 unitDelta.hasDirectiveChange = true;
410 continue;
411 }
412 // URI's must be resolved to the same sources.
413 if (newDirective is UriBasedDirective &&
414 oldDirective is UriBasedDirective) {
415 Source source(UriBasedDirective directive) =>
416 directive is NamespaceDirective
417 ? directive.selectedSource
418 : directive.uriSource;
419 if (source(oldDirective) != source(newDirective)) {
420 continue;
421 }
422 }
423 // Do replacement.
424 _replaceNode(newDirective, oldDirective);
425 removedDirectives.remove(oldDirective);
426 }
427 // If there are any directives left, then these directives were removed.
428 if (removedDirectives.isNotEmpty) {
429 unitDelta.hasDirectiveChange = true;
430 }
431 }
432
433 void _processUnitMembers() {
434 Map<String, CompilationUnitMember> oldNodeMap =
435 new HashMap<String, CompilationUnitMember>();
436 Map<String, ClassDeclaration> nameToOldClassMap =
437 new HashMap<String, ClassDeclaration>();
438 for (CompilationUnitMember oldNode in oldUnit.declarations) {
439 String code = TokenUtils.getFullCode(oldNode);
440 oldNodeMap[code] = oldNode;
441 if (oldNode is ClassDeclaration) {
442 nameToOldClassMap[oldNode.name.name] = oldNode;
443 }
444 }
445 // Prepare all old top-level elements.
446 Set<Element> removedElements = new Set<Element>();
447 removedElements.addAll(unitElement.accessors);
448 removedElements.addAll(unitElement.enums);
449 removedElements.addAll(unitElement.functions);
450 removedElements.addAll(unitElement.functionTypeAliases);
451 removedElements.addAll(unitElement.types);
452 removedElements.addAll(unitElement.topLevelVariables);
453 // Replace new nodes with the identical old nodes.
454 for (CompilationUnitMember newNode in newUnit.declarations) {
455 String code = TokenUtils.getFullCode(newNode);
456 CompilationUnitMember oldNode = oldNodeMap[code];
457 // Add the new element.
458 if (oldNode == null) {
459 // Compute a delta for the class.
460 if (newNode is ClassDeclaration) {
461 ClassDeclaration oldClass = nameToOldClassMap[newNode.name.name];
462 if (oldClass != null) {
463 ClassElementDelta delta = _processClassMembers(oldClass, newNode);
464 if (delta != null) {
465 unitDelta.classDeltas[delta._element.name] = delta;
466 _addElementToUnitHolder(delta._element);
467 removedElements.remove(delta._element);
468 continue;
469 }
470 }
471 }
472 // Add the new node elements.
473 List<Element> elements = _getElements(newNode);
474 elements.forEach(_addElementToUnitHolder);
475 elements.forEach(unitDelta.addedDeclarations.add);
476 continue;
477 }
478 // Do replacement.
479 _replaceNode(newNode, oldNode);
480 List<Element> elements = _getElements(oldNode);
481 elements.forEach(_addElementToUnitHolder);
482 elements.forEach(removedElements.remove);
483 }
484 unitDelta.removedDeclarations.addAll(removedElements);
485 // Update CompilationUnitElement.
486 unitElement.accessors = unitElementHolder.accessors;
487 unitElement.enums = unitElementHolder.enums;
488 unitElement.functions = unitElementHolder.functions;
489 unitElement.typeAliases = unitElementHolder.typeAliases;
490 unitElement.types = unitElementHolder.types;
491 unitElement.topLevelVariables = unitElementHolder.topLevelVariables;
492 unitElementHolder.validate();
493 }
494
495 /**
496 * Replaces [newNode] with [oldNode], updates tokens and elements.
497 * The nodes must have the same tokens, but offsets may be different.
498 */
499 void _replaceNode(AstNode newNode, AstNode oldNode) {
500 // Replace node.
501 NodeReplacer.replace(newNode, oldNode);
502 // Replace tokens.
503 Token oldBeginToken = TokenUtils.getBeginTokenNotComment(oldNode);
504 Token newBeginToken = TokenUtils.getBeginTokenNotComment(newNode);
505 newBeginToken.previous.setNext(oldBeginToken);
506 oldNode.endToken.setNext(newNode.endToken.next);
507 // Change tokens offsets.
508 Map<int, int> offsetMap = new HashMap<int, int>();
509 TokenUtils.copyTokenOffsets(offsetMap, oldBeginToken, newBeginToken,
510 oldNode.endToken, newNode.endToken);
511 // Change elements offsets.
512 {
513 var visitor = new _UpdateElementOffsetsVisitor(offsetMap);
514 List<Element> elements = _getElements(oldNode);
515 for (Element element in elements) {
516 element.accept(visitor);
517 }
518 }
519 }
520
521 /**
522 * Returns [Element]s that are declared directly by the given [node].
523 * This does not include any child elements - parameters, local variables.
524 *
525 * Usually just one [Element] is returned, but [VariableDeclarationList]
526 * nodes may declare more than one.
527 */
528 static List<Element> _getElements(AstNode node) {
529 List<Element> elements = <Element>[];
530 void addPropertyAccessors(VariableDeclarationList variableList) {
531 if (variableList != null) {
532 for (VariableDeclaration variable in variableList.variables) {
533 PropertyInducingElement element = variable.element;
534 if (element != null) {
535 elements.add(element);
536 if (element.getter != null) {
537 elements.add(element.getter);
538 }
539 if (element.setter != null) {
540 elements.add(element.setter);
541 }
542 }
543 }
544 }
545 }
546
547 if (node is FieldDeclaration) {
548 addPropertyAccessors(node.fields);
549 } else if (node is TopLevelVariableDeclaration) {
550 addPropertyAccessors(node.variables);
551 } else if (node is PartDirective || node is PartOfDirective) {
552 // Ignore.
553 } else if (node is Directive && node.element != null) {
554 elements.add(node.element);
555 } else if (node is Declaration && node.element != null) {
556 Element element = node.element;
557 elements.add(element);
558 if (element is PropertyAccessorElement) {
559 elements.add(element.variable);
560 }
561 }
562 return elements;
563 }
564
565 /**
566 * Replaces contents of the [to] unit with the contexts of the [from] unit.
567 */
568 static void _replaceUnitContents(CompilationUnit to, CompilationUnit from) {
569 to.directives.clear();
570 to.declarations.clear();
571 to.beginToken = from.beginToken;
572 to.scriptTag = from.scriptTag;
573 to.directives.addAll(from.directives);
574 to.declarations.addAll(from.declarations);
575 to.element = to.element;
576 to.lineInfo = from.lineInfo;
577 to.endToken = from.endToken;
578 }
579 }
580
581 /**
582 * Utilities for [Token] manipulations.
583 */
584 class TokenUtils {
585 static const String _SEPARATOR = "\uFFFF";
586
587 /**
588 * Copy offsets from [newToken]s to [oldToken]s.
589 */
590 static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken,
591 Token newToken, Token oldEndToken, Token newEndToken) {
592 if (oldToken is CommentToken && newToken is CommentToken) {
593 // Update documentation tokens.
594 while (oldToken != null) {
595 offsetMap[oldToken.offset] = newToken.offset;
596 offsetMap[oldToken.end] = newToken.end;
597 oldToken.offset = newToken.offset;
598 // Update (otherwise unlinked) reference tokens in documentation.
599 if (oldToken is DocumentationCommentToken &&
600 newToken is DocumentationCommentToken) {
601 List<Token> oldReferences = oldToken.references;
602 List<Token> newReferences = newToken.references;
603 assert(oldReferences.length == newReferences.length);
604 for (int i = 0; i < oldReferences.length; i++) {
605 Token oldToken = oldReferences[i];
606 Token newToken = newReferences[i];
607 // For [new Name] the 'Name' token is the reference.
608 // But we need to process all tokens, including 'new'.
609 while (oldToken.previous != null &&
610 oldToken.previous.type != TokenType.EOF) {
611 oldToken = oldToken.previous;
612 }
613 while (newToken.previous != null &&
614 newToken.previous.type != TokenType.EOF) {
615 newToken = newToken.previous;
616 }
617 copyTokenOffsets(
618 offsetMap, oldToken, newToken, oldEndToken, newEndToken);
619 }
620 }
621 // Next tokens.
622 oldToken = oldToken.next;
623 newToken = newToken.next;
624 }
625 assert(oldToken == null);
626 assert(newToken == null);
627 return;
628 }
629 while (true) {
630 if (oldToken.precedingComments != null) {
631 assert(newToken.precedingComments != null);
632 copyTokenOffsets(offsetMap, oldToken.precedingComments,
633 newToken.precedingComments, oldEndToken, newEndToken);
634 }
635 offsetMap[oldToken.offset] = newToken.offset;
636 offsetMap[oldToken.end] = newToken.end;
637 oldToken.offset = newToken.offset;
638 if (oldToken.type == TokenType.EOF) {
639 assert(newToken.type == TokenType.EOF);
640 break;
641 }
642 if (oldToken == oldEndToken) {
643 assert(newToken == newEndToken);
644 break;
645 }
646 oldToken = oldToken.next;
647 newToken = newToken.next;
648 }
649 }
650
651 static Token getBeginTokenNotComment(AstNode node) {
652 Token oldBeginToken = node.beginToken;
653 if (oldBeginToken is CommentToken) {
654 return oldBeginToken.parent;
655 }
656 return oldBeginToken;
657 }
658
659 /**
660 * Return the token string of all the [node].
661 */
662 static String getFullCode(AstNode node) {
663 if (node == null) {
664 return '';
665 }
666 List<Token> tokens = getTokens(node);
667 return joinTokens(tokens);
668 }
669
670 /**
671 * Return the token string of all the [nodes].
672 */
673 static String getFullCodeOfList(List<AstNode> nodes) {
674 if (nodes == null) {
675 return '';
676 }
677 return nodes.map(getFullCode).join(_SEPARATOR);
678 }
679
680 /**
681 * Returns all tokens (including comments) of the given [node].
682 */
683 static List<Token> getTokens(AstNode node) {
684 List<Token> tokens = <Token>[];
685 Token token = getBeginTokenNotComment(node);
686 Token endToken = node.endToken;
687 while (true) {
688 // stop if past the end token
689 if (token.offset > endToken.end) {
690 break;
691 }
692 // append comment tokens
693 for (Token commentToken = token.precedingComments;
694 commentToken != null;
695 commentToken = commentToken.next) {
696 tokens.add(commentToken);
697 }
698 // append token
699 tokens.add(token);
700 // next token
701 if (token == endToken) {
702 break;
703 }
704 token = token.next;
705 }
706 return tokens;
707 }
708
709 static String joinTokens(List<Token> tokens) {
710 return tokens.map((token) => token.lexeme).join(_SEPARATOR);
711 }
712 }
713
714 /**
715 * Updates name offsets of [Element]s according to the [map].
716 */
717 class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor {
718 final Map<int, int> map;
719
720 _UpdateElementOffsetsVisitor(this.map);
721
722 void visitElement(Element element) {
723 if (element is ElementImpl) {
724 // name offset
725 {
726 int oldOffset = element.nameOffset;
727 int newOffset = map[oldOffset];
728 // Some synthetic elements have new offsets, e.g. synthetic accessors
729 // of property inducing elements. But some are purely synthetic, e.g.
730 // synthetic enum fields and their accessors.
731 // PrefixElement(s) can be shared between import directives, so
732 // their name offsets are outside of the second and subsequent import
733 // directives. But we update the name offsets while visiting the first
734 // import directive.
735 if (newOffset == null) {
736 assert(element.isSynthetic || element is PrefixElement);
737 return;
738 }
739 element.nameOffset = newOffset;
740 }
741 // stop here for LibraryElement
742 if (element is LibraryElementImpl) {
743 return;
744 }
745 // code range
746 {
747 int oldOffset = element.codeOffset;
748 if (oldOffset != null) {
749 int oldEnd = oldOffset + element.codeLength;
750 int newOffset = map[oldOffset];
751 int newEnd = map[oldEnd];
752 assert(newOffset != null);
753 assert(newEnd != null);
754 int newLength = newEnd - newOffset;
755 element.setCodeRange(newOffset, newLength);
756 }
757 }
758 // visible range
759 if (element is LocalElement) {
760 SourceRange oldVisibleRange = (element as LocalElement).visibleRange;
761 if (oldVisibleRange != null) {
762 int oldOffset = oldVisibleRange.offset;
763 int oldLength = oldVisibleRange.length;
764 int oldEnd = oldOffset + oldLength;
765 int newOffset = map[oldOffset];
766 int newEnd = map[oldEnd];
767 assert(newOffset != null);
768 assert(newEnd != null);
769 int newLength = newEnd - newOffset;
770 if (newOffset != oldOffset || newLength != oldLength) {
771 if (element is FunctionElementImpl) {
772 element.setVisibleRange(newOffset, newLength);
773 } else if (element is LocalVariableElementImpl) {
774 element.setVisibleRange(newOffset, newLength);
775 } else if (element is ParameterElementImpl) {
776 element.setVisibleRange(newOffset, newLength);
777 }
778 }
779 }
780 }
781 }
782 super.visitElement(element);
783 }
784 }
OLDNEW
« no previous file with comments | « pkg/analyzer/lib/src/task/dart.dart ('k') | pkg/analyzer/test/generated/engine_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698