| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 engine.incremental_resolver; | 5 library engine.incremental_resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 import 'dart:math' as math; | 8 import 'dart:math' as math; |
| 9 | 9 |
| 10 import 'ast.dart'; | 10 import 'ast.dart'; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 import 'utilities_dart.dart'; | 21 import 'utilities_dart.dart'; |
| 22 | 22 |
| 23 | 23 |
| 24 /** | 24 /** |
| 25 * If `true`, an attempt to resolve API-changing modifications is made. | 25 * If `true`, an attempt to resolve API-changing modifications is made. |
| 26 */ | 26 */ |
| 27 bool _resolveApiChanges = false; | 27 bool _resolveApiChanges = false; |
| 28 | 28 |
| 29 | 29 |
| 30 /** | 30 /** |
| 31 * This method is used to enable/disable API-changing modifications resolution. |
| 32 */ |
| 33 void set test_resolveApiChanges(bool value) { |
| 34 _resolveApiChanges = value; |
| 35 } |
| 36 |
| 37 |
| 38 /** |
| 31 * Instances of the class [DeclarationMatcher] determine whether the element | 39 * Instances of the class [DeclarationMatcher] determine whether the element |
| 32 * model defined by a given AST structure matches an existing element model. | 40 * model defined by a given AST structure matches an existing element model. |
| 33 */ | 41 */ |
| 34 class DeclarationMatcher extends RecursiveAstVisitor { | 42 class DeclarationMatcher extends RecursiveAstVisitor { |
| 35 /** | 43 /** |
| 36 * The libary containing the AST nodes being visited. | 44 * The libary containing the AST nodes being visited. |
| 37 */ | 45 */ |
| 38 LibraryElement _enclosingLibrary; | 46 LibraryElement _enclosingLibrary; |
| 39 | 47 |
| 40 /** | 48 /** |
| 41 * The compilation unit containing the AST nodes being visited. | 49 * The compilation unit containing the AST nodes being visited. |
| 42 */ | 50 */ |
| 43 CompilationUnitElement _enclosingUnit; | 51 CompilationUnitElement _enclosingUnit; |
| 44 | 52 |
| 45 /** | 53 /** |
| 46 * The function type alias containing the AST nodes being visited, or `null` i
f we are not | 54 * The function type alias containing the AST nodes being visited, or `null` i
f we are not |
| 47 * in the scope of a function type alias. | 55 * in the scope of a function type alias. |
| 48 */ | 56 */ |
| 49 FunctionTypeAliasElement _enclosingAlias; | 57 FunctionTypeAliasElement _enclosingAlias; |
| 50 | 58 |
| 51 /** | 59 /** |
| 52 * The class containing the AST nodes being visited, or `null` if we are not | 60 * The class containing the AST nodes being visited, or `null` if we are not |
| 53 * in the scope of a class. | 61 * in the scope of a class. |
| 54 */ | 62 */ |
| 55 ClassElement _enclosingClass; | 63 ClassElementImpl _enclosingClass; |
| 56 | 64 |
| 57 /** | 65 /** |
| 58 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the | 66 * The parameter containing the AST nodes being visited, or `null` if we are n
ot in the |
| 59 * scope of a parameter. | 67 * scope of a parameter. |
| 60 */ | 68 */ |
| 61 ParameterElement _enclosingParameter; | 69 ParameterElement _enclosingParameter; |
| 62 | 70 |
| 63 FieldDeclaration _enclosingFieldNode = null; | 71 FieldDeclaration _enclosingFieldNode = null; |
| 64 bool _inTopLevelVariableDeclaration = false; | 72 bool _inTopLevelVariableDeclaration = false; |
| 65 | 73 |
| 66 /** | 74 /** |
| 67 * Is `true` if the current class declaration has a constructor. | 75 * Is `true` if the current class declaration has a constructor. |
| 68 */ | 76 */ |
| 69 bool _hasConstructor = false; | 77 bool _hasConstructor = false; |
| 70 | 78 |
| 71 /** | 79 /** |
| 72 * A set containing all of the elements in the element model that were defined
by the old AST node | 80 * A set containing all of the elements in the element model that were defined
by the old AST node |
| 73 * corresponding to the AST node being visited. | 81 * corresponding to the AST node being visited. |
| 74 */ | 82 */ |
| 75 HashSet<Element> _allElements = new HashSet<Element>(); | 83 HashSet<Element> _allElements = new HashSet<Element>(); |
| 76 | 84 |
| 77 /** | 85 /** |
| 78 * A set containing all of the elements in the element model that were defined
by the old AST node | 86 * A set containing all of the elements were defined in the old element model, |
| 79 * corresponding to the AST node being visited that have not already been matc
hed to nodes in the | 87 * but are not defined in the new element model. |
| 80 * AST structure being visited. | |
| 81 */ | 88 */ |
| 82 HashSet<Element> _unmatchedElements = new HashSet<Element>(); | 89 HashSet<Element> _removedElements = new HashSet<Element>(); |
| 83 | 90 |
| 84 /** | 91 /** |
| 85 * Return `true` if the declarations within the given AST structure define an
element model | 92 * A set containing all of the elements are defined in the new element model, |
| 86 * that is equivalent to the corresponding elements rooted at the given elemen
t. | 93 * but were not defined in the old element model. |
| 87 * | |
| 88 * @param node the AST structure being compared to the element model | |
| 89 * @param element the root of the element model being compared to the AST stru
cture | |
| 90 * @return `true` if the AST structure defines the same elements as those in t
he given | |
| 91 * element model | |
| 92 */ | 94 */ |
| 93 bool matches(AstNode node, Element element) { | 95 HashSet<Element> _addedElements = new HashSet<Element>(); |
| 96 |
| 97 /** |
| 98 * Determines how elements model corresponding to the given [node] differs |
| 99 * from the [element]. |
| 100 */ |
| 101 DeclarationMatchKind matches(AstNode node, Element element) { |
| 94 logger.enter('match $element @ ${element.nameOffset}'); | 102 logger.enter('match $element @ ${element.nameOffset}'); |
| 95 try { | 103 try { |
| 96 _captureEnclosingElements(element); | 104 _captureEnclosingElements(element); |
| 97 _gatherElements(element); | 105 _gatherElements(element); |
| 98 node.accept(this); | 106 node.accept(this); |
| 99 } on _DeclarationMismatchException catch (exception) { | 107 } on _DeclarationMismatchException catch (exception) { |
| 100 return false; | 108 return DeclarationMatchKind.MISMATCH; |
| 101 } finally { | 109 } finally { |
| 102 logger.exit(); | 110 logger.exit(); |
| 103 } | 111 } |
| 104 return _unmatchedElements.isEmpty; | 112 // no API changes |
| 113 if (_removedElements.isEmpty && _addedElements.isEmpty) { |
| 114 return DeclarationMatchKind.MATCH; |
| 115 } |
| 116 // simple API change |
| 117 if (_removedElements.length <= 1 && _addedElements.length == 1) { |
| 118 return DeclarationMatchKind.MISMATCH_OK; |
| 119 } |
| 120 // something more complex |
| 121 return DeclarationMatchKind.MISMATCH; |
| 105 } | 122 } |
| 106 | 123 |
| 107 @override | 124 @override |
| 108 visitBlockFunctionBody(BlockFunctionBody node) { | 125 visitBlockFunctionBody(BlockFunctionBody node) { |
| 109 // ignore bodies | 126 // ignore bodies |
| 110 } | 127 } |
| 111 | 128 |
| 112 @override | 129 @override |
| 113 visitClassDeclaration(ClassDeclaration node) { | 130 visitClassDeclaration(ClassDeclaration node) { |
| 114 String name = node.name.name; | 131 String name = node.name.name; |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 } | 321 } |
| 305 // prepare element | 322 // prepare element |
| 306 Token property = node.propertyKeyword; | 323 Token property = node.propertyKeyword; |
| 307 ExecutableElementImpl element; | 324 ExecutableElementImpl element; |
| 308 if (property == null) { | 325 if (property == null) { |
| 309 element = _findElement(_enclosingClass.methods, name); | 326 element = _findElement(_enclosingClass.methods, name); |
| 310 } else { | 327 } else { |
| 311 element = _findElement(_enclosingClass.accessors, name); | 328 element = _findElement(_enclosingClass.accessors, name); |
| 312 } | 329 } |
| 313 // process element | 330 // process element |
| 314 _processElement(element); | |
| 315 _assertEquals(node.isStatic, element.isStatic); | |
| 316 _assertSameType(node.returnType, element.returnType); | |
| 317 _assertCompatibleParameters(node.parameters, element.parameters); | |
| 318 // matches, update the existing element | |
| 319 ExecutableElement newElement = node.element; | 331 ExecutableElement newElement = node.element; |
| 320 node.name.staticElement = element; | 332 try { |
| 321 _setLocalElements(element, newElement); | 333 _assertNotNull(element); |
| 322 _setParameterElements(node.parameters, element.parameters); | 334 _assertEquals(node.isStatic, element.isStatic); |
| 335 _assertSameType(node.returnType, element.returnType); |
| 336 _assertCompatibleParameters(node.parameters, element.parameters); |
| 337 _removedElements.remove(element); |
| 338 // matches, update the existing element |
| 339 node.name.staticElement = element; |
| 340 _setLocalElements(element, newElement); |
| 341 _setParameterElements(node.parameters, element.parameters); |
| 342 } on _DeclarationMismatchException catch (e) { |
| 343 _addedElements.add(newElement); |
| 344 // remove old element |
| 345 if (element is MethodElement) { |
| 346 _enclosingClass.methods.remove(element); |
| 347 } else if (element is PropertyAccessorElement) { |
| 348 _enclosingClass.accessors.remove(element); |
| 349 } |
| 350 // add new element |
| 351 if (newElement is MethodElement) { |
| 352 List<MethodElement> methods = _enclosingClass.methods; |
| 353 methods.add(newElement); |
| 354 _enclosingClass.methods = methods; |
| 355 } else { |
| 356 List<PropertyAccessorElement> accessors = _enclosingClass.accessors; |
| 357 accessors.add(newElement); |
| 358 _enclosingClass.accessors = accessors; |
| 359 } |
| 360 } |
| 323 } | 361 } |
| 324 | 362 |
| 325 @override | 363 @override |
| 326 visitPartDirective(PartDirective node) { | 364 visitPartDirective(PartDirective node) { |
| 327 String uri = _getStringValue(node.uri); | 365 String uri = _getStringValue(node.uri); |
| 328 if (uri != null) { | 366 if (uri != null) { |
| 329 CompilationUnitElement element = | 367 CompilationUnitElement element = |
| 330 _findUriReferencedElement(_enclosingLibrary.parts, uri); | 368 _findUriReferencedElement(_enclosingLibrary.parts, uri); |
| 331 _processElement(element); | 369 _processElement(element); |
| 332 } | 370 } |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 581 gatherer.addElements(_enclosingLibrary.exports); | 619 gatherer.addElements(_enclosingLibrary.exports); |
| 582 gatherer.addElements(_enclosingLibrary.parts); | 620 gatherer.addElements(_enclosingLibrary.parts); |
| 583 } | 621 } |
| 584 } | 622 } |
| 585 | 623 |
| 586 void _processElement(Element element) { | 624 void _processElement(Element element) { |
| 587 _assertNotNull(element); | 625 _assertNotNull(element); |
| 588 if (!_allElements.contains(element)) { | 626 if (!_allElements.contains(element)) { |
| 589 throw new _DeclarationMismatchException(); | 627 throw new _DeclarationMismatchException(); |
| 590 } | 628 } |
| 591 _unmatchedElements.remove(element); | 629 _removedElements.remove(element); |
| 592 } | 630 } |
| 593 | 631 |
| 594 /** | 632 /** |
| 595 * Return the [Element] in [elements] with the given [name]. | 633 * Return the [Element] in [elements] with the given [name]. |
| 596 */ | 634 */ |
| 597 static Element _findElement(List<Element> elements, String name) { | 635 static Element _findElement(List<Element> elements, String name) { |
| 598 for (Element element in elements) { | 636 for (Element element in elements) { |
| 599 if (element.name == name) { | 637 if (element.name == name) { |
| 600 return element; | 638 return element; |
| 601 } | 639 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 node.identifier.staticElement = element; | 683 node.identifier.staticElement = element; |
| 646 (element as ElementImpl).name = newElement.name; | 684 (element as ElementImpl).name = newElement.name; |
| 647 (element as ElementImpl).nameOffset = newElement.nameOffset; | 685 (element as ElementImpl).nameOffset = newElement.nameOffset; |
| 648 } | 686 } |
| 649 } | 687 } |
| 650 } | 688 } |
| 651 } | 689 } |
| 652 | 690 |
| 653 | 691 |
| 654 /** | 692 /** |
| 693 * Describes how declarations match an existing elements model. |
| 694 */ |
| 695 class DeclarationMatchKind { |
| 696 /** |
| 697 * Complete match, no API changes. |
| 698 */ |
| 699 static const MATCH = const DeclarationMatchKind('MATCH'); |
| 700 |
| 701 /** |
| 702 * Has API changes that we might be able to resolve incrementally. |
| 703 */ |
| 704 static const MISMATCH_OK = const DeclarationMatchKind('MISMATCH_OK'); |
| 705 |
| 706 /** |
| 707 * Has API changes that we cannot resolve incrementally. |
| 708 */ |
| 709 static const MISMATCH = const DeclarationMatchKind('MISMATCH'); |
| 710 |
| 711 final String name; |
| 712 |
| 713 const DeclarationMatchKind(this.name); |
| 714 |
| 715 @override |
| 716 String toString() => name; |
| 717 } |
| 718 |
| 719 |
| 720 /** |
| 655 * Instances of the class [IncrementalResolver] resolve the smallest portion of | 721 * Instances of the class [IncrementalResolver] resolve the smallest portion of |
| 656 * an AST structure that we currently know how to resolve. | 722 * an AST structure that we currently know how to resolve. |
| 657 */ | 723 */ |
| 658 class IncrementalResolver { | 724 class IncrementalResolver { |
| 659 /** | 725 /** |
| 660 * The object used to access the types from the core library. | 726 * The object used to access the types from the core library. |
| 661 */ | 727 */ |
| 662 final TypeProvider _typeProvider; | 728 final TypeProvider _typeProvider; |
| 663 | 729 |
| 664 /** | 730 /** |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 794 // check the whole enclosing Element. | 860 // check the whole enclosing Element. |
| 795 if (node is Declaration) { | 861 if (node is Declaration) { |
| 796 node = node.parent; | 862 node = node.parent; |
| 797 } | 863 } |
| 798 Element element = _getElement(node); | 864 Element element = _getElement(node); |
| 799 if (element == null) { | 865 if (element == null) { |
| 800 throw new AnalysisException( | 866 throw new AnalysisException( |
| 801 "Cannot resolve node: a ${node.runtimeType} does not define an element
"); | 867 "Cannot resolve node: a ${node.runtimeType} does not define an element
"); |
| 802 } | 868 } |
| 803 DeclarationMatcher matcher = new DeclarationMatcher(); | 869 DeclarationMatcher matcher = new DeclarationMatcher(); |
| 804 return !matcher.matches(node, element); | 870 return matcher.matches(node, element) != DeclarationMatchKind.MATCH; |
| 805 } | 871 } |
| 806 | 872 |
| 807 /** | 873 /** |
| 808 * Starting at [node], find the smallest AST node that can be resolved | 874 * Starting at [node], find the smallest AST node that can be resolved |
| 809 * independently of any other nodes. Return the node that was found. | 875 * independently of any other nodes. Return the node that was found. |
| 810 * | 876 * |
| 811 * [node] - the node at which the search is to begin | 877 * [node] - the node at which the search is to begin |
| 812 * | 878 * |
| 813 * Throws [AnalysisException] if there is no such node. | 879 * Throws [AnalysisException] if there is no such node. |
| 814 */ | 880 */ |
| (...skipping 771 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1586 // Don't visit children (such as property accessors). | 1652 // Don't visit children (such as property accessors). |
| 1587 } | 1653 } |
| 1588 | 1654 |
| 1589 @override | 1655 @override |
| 1590 visitTypeParameterElement(TypeParameterElement element) { | 1656 visitTypeParameterElement(TypeParameterElement element) { |
| 1591 } | 1657 } |
| 1592 | 1658 |
| 1593 void _addElement(Element element) { | 1659 void _addElement(Element element) { |
| 1594 if (element != null) { | 1660 if (element != null) { |
| 1595 matcher._allElements.add(element); | 1661 matcher._allElements.add(element); |
| 1596 matcher._unmatchedElements.add(element); | 1662 matcher._removedElements.add(element); |
| 1597 } | 1663 } |
| 1598 } | 1664 } |
| 1599 } | 1665 } |
| 1600 | 1666 |
| 1601 | 1667 |
| 1602 /** | 1668 /** |
| 1603 * Describes how two [Token]s are different. | 1669 * Describes how two [Token]s are different. |
| 1604 */ | 1670 */ |
| 1605 class _TokenDifferenceKind { | 1671 class _TokenDifferenceKind { |
| 1606 static const COMMENT = const _TokenDifferenceKind('COMMENT'); | 1672 static const COMMENT = const _TokenDifferenceKind('COMMENT'); |
| 1607 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC'); | 1673 static const COMMENT_DOC = const _TokenDifferenceKind('COMMENT_DOC'); |
| 1608 static const CONTENT = const _TokenDifferenceKind('CONTENT'); | 1674 static const CONTENT = const _TokenDifferenceKind('CONTENT'); |
| 1609 static const OFFSET = const _TokenDifferenceKind('OFFSET'); | 1675 static const OFFSET = const _TokenDifferenceKind('OFFSET'); |
| 1610 | 1676 |
| 1611 final String name; | 1677 final String name; |
| 1612 | 1678 |
| 1613 const _TokenDifferenceKind(this.name); | 1679 const _TokenDifferenceKind(this.name); |
| 1614 | 1680 |
| 1615 @override | 1681 @override |
| 1616 String toString() => name; | 1682 String toString() => name; |
| 1617 } | 1683 } |
| 1618 | 1684 |
| 1619 | 1685 |
| 1620 class _TokenPair { | 1686 class _TokenPair { |
| 1621 final _TokenDifferenceKind kind; | 1687 final _TokenDifferenceKind kind; |
| 1622 final Token oldToken; | 1688 final Token oldToken; |
| 1623 final Token newToken; | 1689 final Token newToken; |
| 1624 _TokenPair(this.kind, this.oldToken, this.newToken); | 1690 _TokenPair(this.kind, this.oldToken, this.newToken); |
| 1625 } | 1691 } |
| OLD | NEW |