Chromium Code Reviews| 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'; |
| 11 import 'element.dart'; | 11 import 'element.dart'; |
| 12 import 'engine.dart'; | 12 import 'engine.dart'; |
| 13 import 'error.dart'; | 13 import 'error.dart'; |
| 14 import 'error_verifier.dart'; | 14 import 'error_verifier.dart'; |
| 15 import 'incremental_logger.dart' show logger, LoggingTimer; | 15 import 'incremental_logger.dart' show logger, LoggingTimer; |
| 16 import 'java_engine.dart'; | 16 import 'java_engine.dart'; |
| 17 import 'parser.dart'; | 17 import 'parser.dart'; |
| 18 import 'resolver.dart'; | 18 import 'resolver.dart'; |
| 19 import 'scanner.dart'; | 19 import 'scanner.dart'; |
| 20 import 'source.dart'; | 20 import 'source.dart'; |
| 21 import 'utilities_collection.dart'; | |
| 21 import 'utilities_dart.dart'; | 22 import 'utilities_dart.dart'; |
| 22 | 23 |
| 23 | 24 |
| 24 /** | 25 /** |
| 25 * If `true`, an attempt to resolve API-changing modifications is made. | 26 * If `true`, an attempt to resolve API-changing modifications is made. |
| 26 */ | 27 */ |
| 27 bool _resolveApiChanges = false; | 28 bool _resolveApiChanges = false; |
| 28 | 29 |
| 29 | 30 |
| 30 /** | 31 /** |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 111 } | 112 } |
| 112 // no API changes | 113 // no API changes |
| 113 if (_removedElements.isEmpty && _addedElements.isEmpty) { | 114 if (_removedElements.isEmpty && _addedElements.isEmpty) { |
| 114 return DeclarationMatchKind.MATCH; | 115 return DeclarationMatchKind.MATCH; |
| 115 } | 116 } |
| 116 // simple API change | 117 // simple API change |
| 117 if (_removedElements.length <= 1 && _addedElements.length == 1) { | 118 if (_removedElements.length <= 1 && _addedElements.length == 1) { |
| 118 return DeclarationMatchKind.MISMATCH_OK; | 119 return DeclarationMatchKind.MISMATCH_OK; |
| 119 } | 120 } |
| 120 // something more complex | 121 // something more complex |
| 122 logger.log('_removedElements: $_removedElements'); | |
| 123 logger.log('_addedElements: $_addedElements'); | |
| 121 return DeclarationMatchKind.MISMATCH; | 124 return DeclarationMatchKind.MISMATCH; |
| 122 } | 125 } |
| 123 | 126 |
| 124 @override | 127 @override |
| 125 visitBlockFunctionBody(BlockFunctionBody node) { | 128 visitBlockFunctionBody(BlockFunctionBody node) { |
| 126 // ignore bodies | 129 // ignore bodies |
| 127 } | 130 } |
| 128 | 131 |
| 129 @override | 132 @override |
| 130 visitClassDeclaration(ClassDeclaration node) { | 133 visitClassDeclaration(ClassDeclaration node) { |
| (...skipping 383 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 514 if (type == null) { | 517 if (type == null) { |
| 515 return _assertTrue(false); | 518 return _assertTrue(false); |
| 516 } | 519 } |
| 517 // prepare name | 520 // prepare name |
| 518 Identifier nameIdentifier = node.name; | 521 Identifier nameIdentifier = node.name; |
| 519 if (nameIdentifier is PrefixedIdentifier) { | 522 if (nameIdentifier is PrefixedIdentifier) { |
| 520 nameIdentifier = (nameIdentifier as PrefixedIdentifier).identifier; | 523 nameIdentifier = (nameIdentifier as PrefixedIdentifier).identifier; |
| 521 } | 524 } |
| 522 String nodeName = nameIdentifier.name; | 525 String nodeName = nameIdentifier.name; |
| 523 // check specific type kinds | 526 // check specific type kinds |
| 524 if (type is InterfaceType) { | 527 if (type is ParameterizedType) { |
| 525 _assertEquals(nodeName, type.name); | 528 _assertEquals(nodeName, type.name); |
| 526 // check arguments | 529 // check arguments |
| 527 TypeArgumentList nodeArgumentList = node.typeArguments; | 530 TypeArgumentList nodeArgumentList = node.typeArguments; |
| 528 List<DartType> typeArguments = type.typeArguments; | 531 List<DartType> typeArguments = type.typeArguments; |
| 529 if (nodeArgumentList == null) { | 532 if (nodeArgumentList == null) { |
| 530 // Node doesn't have type arguments, so all type arguments of the | 533 // Node doesn't have type arguments, so all type arguments of the |
| 531 // element must be "dynamic". | 534 // element must be "dynamic". |
| 532 for (DartType typeArgument in typeArguments) { | 535 for (DartType typeArgument in typeArguments) { |
| 533 _assertTrue(typeArgument.isDynamic); | 536 _assertTrue(typeArgument.isDynamic); |
| 534 } | 537 } |
| 535 } else { | 538 } else { |
| 536 List<TypeName> nodeArguments = nodeArgumentList.arguments; | 539 List<TypeName> nodeArguments = nodeArgumentList.arguments; |
| 537 _assertSameTypes(nodeArguments, typeArguments); | 540 _assertSameTypes(nodeArguments, typeArguments); |
| 538 } | 541 } |
| 539 } else if (type is TypeParameterType) { | 542 } else if (type is TypeParameterType) { |
| 540 _assertEquals(nodeName, type.name); | 543 _assertEquals(nodeName, type.name); |
| 541 // TODO(scheglov) it should be possible to rename type parameters | 544 // TODO(scheglov) it should be possible to rename type parameters |
| 542 } else if (type.isVoid) { | 545 } else if (type.isVoid) { |
| 543 _assertEquals(nodeName, 'void'); | 546 _assertEquals(nodeName, 'void'); |
| 544 } else if (type.isDynamic) { | 547 } else if (type.isDynamic) { |
| 545 _assertEquals(nodeName, 'dynamic'); | 548 _assertEquals(nodeName, 'dynamic'); |
| 546 } else { | 549 } else { |
| 547 // TODO(scheglov) support other types | 550 // TODO(scheglov) support other types |
| 548 // print('node: $node type: $type type.type: ${type.runtimeType}'); | 551 logger.log('node: $node type: $type type.type: ${type.runtimeType}'); |
| 549 _assertTrue(false); | 552 _assertTrue(false); |
| 550 } | 553 } |
| 551 } | 554 } |
| 552 | 555 |
| 553 void _assertSameTypeParameter(TypeParameter node, | 556 void _assertSameTypeParameter(TypeParameter node, |
| 554 TypeParameterElement element) { | 557 TypeParameterElement element) { |
| 555 _assertSameType(node.bound, element.bound); | 558 _assertSameType(node.bound, element.bound); |
| 556 } | 559 } |
| 557 | 560 |
| 558 void _assertSameTypeParameters(TypeParameterList nodesList, | 561 void _assertSameTypeParameters(TypeParameterList nodesList, |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 758 final int _updateNewLength; | 761 final int _updateNewLength; |
| 759 | 762 |
| 760 RecordingErrorListener errorListener = new RecordingErrorListener(); | 763 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 761 ResolutionContext _resolutionContext; | 764 ResolutionContext _resolutionContext; |
| 762 | 765 |
| 763 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; | 766 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; |
| 764 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; | 767 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; |
| 765 List<AnalysisError> _hints = AnalysisError.NO_ERRORS; | 768 List<AnalysisError> _hints = AnalysisError.NO_ERRORS; |
| 766 | 769 |
| 767 /** | 770 /** |
| 771 * The elements that should be resolved because of API changes. | |
| 772 */ | |
| 773 HashSet<Element> _resolutionQueue = new HashSet<Element>(); | |
|
Brian Wilkerson
2014/12/05 23:05:57
I have some concerns about how long the incrementa
scheglov
2014/12/05 23:49:05
Done.
| |
| 774 | |
| 775 /** | |
| 768 * Initialize a newly created incremental resolver to resolve a node in the | 776 * Initialize a newly created incremental resolver to resolve a node in the |
| 769 * given source in the given library. | 777 * given source in the given library. |
| 770 */ | 778 */ |
| 771 IncrementalResolver(this._typeProvider, this._definingUnit, | 779 IncrementalResolver(this._typeProvider, this._definingUnit, |
| 772 this._updateOffset, this._updateOldLength, this._updateNewLength) { | 780 this._updateOffset, this._updateOldLength, this._updateNewLength) { |
| 773 _definingLibrary = _definingUnit.library; | 781 _definingLibrary = _definingUnit.library; |
| 774 _source = _definingUnit.source; | 782 _source = _definingUnit.source; |
| 775 } | 783 } |
| 776 | 784 |
| 777 /** | 785 /** |
| 778 * Resolve [node], reporting any errors or warnings to the given listener. | 786 * Resolve [node], reporting any errors or warnings to the given listener. |
| 779 * | 787 * |
| 780 * [node] - the root of the AST structure to be resolved. | 788 * [node] - the root of the AST structure to be resolved. |
| 781 * | 789 * |
| 782 * Returns `true` if resolution was successful. | 790 * Returns `true` if resolution was successful. |
| 783 */ | 791 */ |
| 784 bool resolve(AstNode node) { | 792 bool resolve(AstNode node) { |
| 785 logger.enter('resolve: $_definingUnit'); | 793 logger.enter('resolve: $_definingUnit'); |
| 786 try { | 794 try { |
| 787 logger.log(() => 'node: $node'); | 795 logger.log(() => 'node: $node'); |
| 788 AstNode rootNode = _findResolutionRoot(node); | 796 AstNode rootNode = _findResolutionRoot(node); |
| 789 logger.log(() => 'rootNode: $rootNode'); | 797 logger.log(() => 'rootNode: $rootNode'); |
| 790 _prepareResolutionContext(rootNode); | 798 _prepareResolutionContext(rootNode); |
| 791 // update elements | 799 // update elements |
| 792 _updateElementNameOffsets( | 800 _updateElementNameOffsets( |
| 793 _definingUnit, | 801 _definingUnit, |
| 794 _updateOffset, | 802 _updateOffset, |
| 795 _updateNewLength - _updateOldLength); | 803 _updateNewLength - _updateOldLength); |
| 796 _buildElements(rootNode); | 804 _buildElements(rootNode); |
| 797 if (_elementModelChanged(rootNode)) { | 805 if (!_canBeIncrementallyResolved(rootNode)) { |
| 798 return false; | 806 return false; |
| 799 } | 807 } |
| 800 // resolve | 808 // resolve |
| 801 _resolveReferences(rootNode); | 809 _resolveReferences(rootNode); |
| 802 // verify | 810 // verify |
| 803 _verify(rootNode); | 811 _verify(rootNode); |
| 804 _generateHints(rootNode); | 812 _generateHints(rootNode); |
| 813 // resolve queue in response of API changes | |
| 814 _resolveQueue(); | |
| 805 // OK | 815 // OK |
| 806 return true; | 816 return true; |
| 807 } finally { | 817 } finally { |
| 808 logger.exit(); | 818 logger.exit(); |
| 809 } | 819 } |
| 810 } | 820 } |
| 811 | 821 |
| 812 void _buildElements(AstNode node) { | 822 void _buildElements(AstNode node) { |
| 813 LoggingTimer timer = logger.startTimer(); | 823 LoggingTimer timer = logger.startTimer(); |
| 814 try { | 824 try { |
| 815 ElementHolder holder = new ElementHolder(); | 825 ElementHolder holder = new ElementHolder(); |
| 816 ElementBuilder builder = new ElementBuilder(holder); | 826 ElementBuilder builder = new ElementBuilder(holder); |
| 817 node.accept(builder); | 827 node.accept(builder); |
| 818 } finally { | 828 } finally { |
| 819 timer.stop('build elements'); | 829 timer.stop('build elements'); |
| 820 } | 830 } |
| 821 } | 831 } |
| 822 | 832 |
| 823 /** | 833 /** |
| 834 * Return `true` if [node] does not have element model changes, or these | |
| 835 * changes can be incrementally propagated. | |
| 836 */ | |
| 837 bool _canBeIncrementallyResolved(AstNode node) { | |
| 838 // If we are replacing the whole declaration, this means that its signature | |
| 839 // is changed. It might be an API change, or not. | |
| 840 // | |
| 841 // If, for example, a required parameter is changed, it is not an API | |
| 842 // change, but we want to find the existing corresponding Element in the | |
| 843 // enclosing one, set it for the node and update as needed. | |
| 844 // | |
| 845 // If, for example, the name of a method is changed, it is an API change, | |
| 846 // we need to know the old Element and the new Element. Again, we need to | |
| 847 // check the whole enclosing Element. | |
| 848 if (node is Declaration) { | |
| 849 node = node.parent; | |
| 850 } | |
| 851 Element element = _getElement(node); | |
| 852 DeclarationMatcher matcher = new DeclarationMatcher(); | |
| 853 DeclarationMatchKind matchKind = matcher.matches(node, element); | |
| 854 if (matchKind == DeclarationMatchKind.MATCH) { | |
| 855 return true; | |
| 856 } | |
| 857 // try to resolve a simple API change | |
| 858 if (_resolveApiChanges && matchKind == DeclarationMatchKind.MISMATCH_OK) { | |
| 859 _fillResolutionQueue(matcher); | |
| 860 return true; | |
| 861 } | |
| 862 // mismatch that cannot be incrementally fixed | |
| 863 return false; | |
| 864 } | |
| 865 | |
| 866 /** | |
| 824 * Return `true` if the given node can be resolved independently of any other | 867 * Return `true` if the given node can be resolved independently of any other |
| 825 * nodes. | 868 * nodes. |
| 826 * | 869 * |
| 827 * *Note*: This method needs to be kept in sync with | 870 * *Note*: This method needs to be kept in sync with |
| 828 * [ScopeBuilder.ContextBuilder]. | 871 * [ScopeBuilder.ContextBuilder]. |
| 829 * | 872 * |
| 830 * [node] - the node being tested. | 873 * [node] - the node being tested. |
| 831 */ | 874 */ |
| 832 bool _canBeResolved(AstNode node) => | 875 bool _canBeResolved(AstNode node) => |
| 833 node is ClassDeclaration || | 876 node is ClassDeclaration || |
| 834 node is ClassTypeAlias || | 877 node is ClassTypeAlias || |
| 835 node is CompilationUnit || | 878 node is CompilationUnit || |
| 836 node is ConstructorDeclaration || | 879 node is ConstructorDeclaration || |
| 837 node is FunctionDeclaration || | 880 node is FunctionDeclaration || |
| 838 node is FunctionTypeAlias || | 881 node is FunctionTypeAlias || |
| 839 node is MethodDeclaration; | 882 node is MethodDeclaration; |
| 840 | 883 |
| 841 /** | 884 void _fillResolutionQueue(DeclarationMatcher matcher) { |
| 842 * Return `true` if the portion of the element model defined by the given node | 885 for (Element removedElement in matcher._removedElements) { |
| 843 * has changed. | 886 AnalysisContextImpl context = removedElement.context; |
| 844 * | 887 IntSet users = removedElement.users; |
| 845 * [node] - the node defining the portion of the element model being tested. | 888 while (!users.isEmpty) { |
| 846 * | 889 int id = users.remove(); |
| 847 * Throws [AnalysisException] if the correctness of the element model cannot | 890 Element removedElementUser = context.findElementById(id); |
| 848 * be determined. | 891 _resolutionQueue.add(removedElementUser); |
| 849 */ | 892 } |
| 850 bool _elementModelChanged(AstNode node) { | |
| 851 // If we are replacing the whole declaration, this means that its signature | |
| 852 // is changed. It might be an API change, or not. | |
| 853 // | |
| 854 // If, for example, a required parameter is changed, it is not an API | |
| 855 // change, but we want to find the existing corresponding Element in the | |
| 856 // enclosing one, set it for the node and update as needed. | |
| 857 // | |
| 858 // If, for example, the name of a method is changed, it is an API change, | |
| 859 // we need to know the old Element and the new Element. Again, we need to | |
| 860 // check the whole enclosing Element. | |
| 861 if (node is Declaration) { | |
| 862 node = node.parent; | |
| 863 } | 893 } |
| 864 Element element = _getElement(node); | 894 // TODO(scheglov) a method change might also require its class, and |
| 865 if (element == null) { | 895 // subclasses resolution |
| 866 throw new AnalysisException( | |
| 867 "Cannot resolve node: a ${node.runtimeType} does not define an element "); | |
| 868 } | |
| 869 DeclarationMatcher matcher = new DeclarationMatcher(); | |
| 870 return matcher.matches(node, element) != DeclarationMatchKind.MATCH; | |
| 871 } | 896 } |
| 872 | 897 |
| 873 /** | 898 /** |
| 874 * Starting at [node], find the smallest AST node that can be resolved | 899 * Starting at [node], find the smallest AST node that can be resolved |
| 875 * independently of any other nodes. Return the node that was found. | 900 * independently of any other nodes. Return the node that was found. |
| 876 * | 901 * |
| 877 * [node] - the node at which the search is to begin | 902 * [node] - the node at which the search is to begin |
| 878 * | 903 * |
| 879 * Throws [AnalysisException] if there is no such node. | 904 * Throws [AnalysisException] if there is no such node. |
| 880 */ | 905 */ |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 916 return null; | 941 return null; |
| 917 } | 942 } |
| 918 | 943 |
| 919 void _prepareResolutionContext(AstNode node) { | 944 void _prepareResolutionContext(AstNode node) { |
| 920 if (_resolutionContext == null) { | 945 if (_resolutionContext == null) { |
| 921 _resolutionContext = | 946 _resolutionContext = |
| 922 ResolutionContextBuilder.contextFor(node, errorListener); | 947 ResolutionContextBuilder.contextFor(node, errorListener); |
| 923 } | 948 } |
| 924 } | 949 } |
| 925 | 950 |
| 951 /** | |
| 952 * Resolves elements [_resolutionQueue]. | |
| 953 * | |
| 954 * TODO(scheglov) work in progress | |
| 955 */ | |
| 956 void _resolveQueue() { | |
| 957 for (Element element in _resolutionQueue) { | |
| 958 // TODO(scheglov) in general, we should not call Element.node, it | |
| 959 // might perform complete unit resolution. | |
| 960 AstNode node = element.node; | |
| 961 CompilationUnitElement unit = | |
| 962 element.getAncestor((e) => e is CompilationUnitElement); | |
| 963 IncrementalResolver resolver = | |
| 964 new IncrementalResolver(_typeProvider, unit, 0, 0, 0); | |
| 965 resolver._resolveReferences(node); | |
| 966 } | |
| 967 } | |
| 968 | |
| 926 _resolveReferences(AstNode node) { | 969 _resolveReferences(AstNode node) { |
| 927 LoggingTimer timer = logger.startTimer(); | 970 LoggingTimer timer = logger.startTimer(); |
| 928 try { | 971 try { |
| 929 _prepareResolutionContext(node); | 972 _prepareResolutionContext(node); |
| 930 Scope scope = _resolutionContext.scope; | 973 Scope scope = _resolutionContext.scope; |
| 931 // resolve types | 974 // resolve types |
| 932 { | 975 { |
| 933 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( | 976 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( |
| 934 _definingLibrary, | 977 _definingLibrary, |
| 935 _source, | 978 _source, |
| (...skipping 746 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1682 String toString() => name; | 1725 String toString() => name; |
| 1683 } | 1726 } |
| 1684 | 1727 |
| 1685 | 1728 |
| 1686 class _TokenPair { | 1729 class _TokenPair { |
| 1687 final _TokenDifferenceKind kind; | 1730 final _TokenDifferenceKind kind; |
| 1688 final Token oldToken; | 1731 final Token oldToken; |
| 1689 final Token newToken; | 1732 final Token newToken; |
| 1690 _TokenPair(this.kind, this.oldToken, this.newToken); | 1733 _TokenPair(this.kind, this.oldToken, this.newToken); |
| 1691 } | 1734 } |
| OLD | NEW |