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 'package:analyzer/src/generated/error_verifier.dart'; | |
| 11 | |
| 10 import 'ast.dart'; | 12 import 'ast.dart'; |
| 11 import 'element.dart'; | 13 import 'element.dart'; |
| 14 import 'engine.dart'; | |
| 12 import 'error.dart'; | 15 import 'error.dart'; |
| 13 import 'java_engine.dart'; | 16 import 'java_engine.dart'; |
| 14 import 'parser.dart'; | 17 import 'parser.dart'; |
| 15 import 'resolver.dart'; | 18 import 'resolver.dart'; |
| 16 import 'scanner.dart'; | 19 import 'scanner.dart'; |
| 17 import 'source.dart'; | 20 import 'source.dart'; |
| 18 | 21 |
| 19 | 22 |
| 20 /** | 23 /** |
| 21 * Attempts to update [oldUnit] to the state that would correspond to [newCode]. | |
| 22 * Returns `true` if success, or `false` otherwise. | |
| 23 * The [oldUnit] might be damaged. | |
| 24 */ | |
| 25 bool poorMansIncrementalResolution(TypeProvider typeProvider, | |
| 26 CompilationUnit oldUnit, String newCode) { | |
| 27 try { | |
| 28 CompilationUnit newUnit = _parseUnit(newCode); | |
| 29 _TokenPair firstPair = | |
| 30 _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); | |
| 31 _TokenPair lastPair = | |
| 32 _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); | |
| 33 if (firstPair != null && lastPair != null) { | |
| 34 // Prepare the "old" token range. | |
| 35 Token oldBeginToken; | |
| 36 Token oldEndToken; | |
| 37 if (firstPair.oldToken.offset < lastPair.oldToken.offset) { | |
| 38 oldBeginToken = firstPair.oldToken; | |
| 39 oldEndToken = lastPair.oldToken; | |
| 40 } else { | |
| 41 oldBeginToken = lastPair.oldToken; | |
| 42 oldEndToken = firstPair.oldToken; | |
| 43 } | |
| 44 // Prepare the "old" token tange. | |
| 45 Token newBeginToken; | |
| 46 Token newEndToken; | |
| 47 if (firstPair.newToken.offset < lastPair.newToken.offset) { | |
| 48 newBeginToken = firstPair.newToken; | |
| 49 newEndToken = lastPair.newToken; | |
| 50 } else { | |
| 51 newBeginToken = lastPair.newToken; | |
| 52 newEndToken = firstPair.newToken; | |
| 53 } | |
| 54 // Find nodes covering the "old" and "new" token ranges. | |
| 55 AstNode oldNode = | |
| 56 _findNodeWithTokens(oldUnit, oldBeginToken, oldEndToken); | |
| 57 AstNode newNode = | |
| 58 _findNodeWithTokens(newUnit, newBeginToken, newEndToken); | |
| 59 // Try to find the smallest common node, a FunctionBody currently. | |
| 60 { | |
| 61 List<AstNode> oldParents = _getParents(oldNode); | |
| 62 List<AstNode> newParents = _getParents(newNode); | |
| 63 int length = math.min(oldParents.length, newParents.length); | |
| 64 bool found = false; | |
| 65 for (int i = 0; i < length; i++) { | |
| 66 AstNode oldParent = oldParents[i]; | |
| 67 AstNode newParent = newParents[i]; | |
| 68 if (oldParent is FunctionBody && newParent is FunctionBody) { | |
| 69 oldNode = oldParent; | |
| 70 newNode = newParent; | |
| 71 found = true; | |
| 72 break; | |
| 73 } | |
| 74 } | |
| 75 if (!found) { | |
| 76 return false; | |
| 77 } | |
| 78 } | |
| 79 // replace node | |
| 80 NodeReplacer.replace(oldNode, newNode); | |
| 81 // update token references | |
| 82 oldNode.beginToken.previous.setNext(newNode.beginToken); | |
| 83 oldNode.endToken.setNext(oldNode.endToken.next); | |
| 84 // perform incremental resolution | |
| 85 // TODO(scheglov) update errors | |
| 86 AnalysisErrorListener errorListener = new BooleanErrorListener(); | |
| 87 CompilationUnitElement oldUnitElement = oldUnit.element; | |
| 88 IncrementalResolver incrementalResolver = new IncrementalResolver( | |
| 89 errorListener, | |
| 90 typeProvider, | |
| 91 oldUnitElement.library, | |
| 92 oldUnitElement, | |
| 93 oldUnitElement.source, | |
| 94 oldNode.offset, | |
| 95 oldNode.length, | |
| 96 newNode.length); | |
| 97 incrementalResolver.resolve(newNode); | |
| 98 return true; | |
| 99 } | |
| 100 } catch (e) { | |
| 101 // TODO(scheglov) find a way to log these exceptions | |
| 102 } | |
| 103 return false; | |
| 104 } | |
| 105 | |
| 106 | |
| 107 bool _equalToken(Token oldToken, Token newToken, int delta) { | |
| 108 if (oldToken.type != newToken.type) { | |
| 109 return false; | |
| 110 } | |
| 111 if (newToken.offset - oldToken.offset != delta) { | |
| 112 return false; | |
| 113 } | |
| 114 return oldToken.lexeme == newToken.lexeme; | |
| 115 } | |
| 116 | |
| 117 | |
| 118 _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) { | |
| 119 // print('first ------------'); | |
| 120 while (oldToken.type != TokenType.EOF && newToken.type != TokenType.EOF) { | |
| 121 // print('old: $oldToken @ ${oldToken.offset}'); | |
| 122 // print('new: $newToken @ ${newToken.offset}'); | |
| 123 if (!_equalToken(oldToken, newToken, 0)) { | |
| 124 return new _TokenPair(oldToken, newToken); | |
| 125 } | |
| 126 oldToken = oldToken.next; | |
| 127 newToken = newToken.next; | |
| 128 } | |
| 129 return null; | |
| 130 } | |
| 131 | |
| 132 | |
| 133 _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) { | |
| 134 // print('last ------------'); | |
| 135 int delta = newToken.offset - oldToken.offset; | |
| 136 while (oldToken.previous != oldToken && newToken.previous != newToken) { | |
| 137 // print('old: $oldToken @ ${oldToken.offset}'); | |
| 138 // print('new: $newToken @ ${newToken.offset}'); | |
| 139 if (!_equalToken(oldToken, newToken, delta)) { | |
| 140 return new _TokenPair(oldToken.next, newToken.next); | |
| 141 } | |
| 142 oldToken.offset += delta; | |
| 143 oldToken = oldToken.previous; | |
| 144 newToken = newToken.previous; | |
| 145 } | |
| 146 return null; | |
| 147 } | |
| 148 | |
| 149 | |
| 150 AstNode _findNodeWithTokens(AstNode root, Token first, Token last) { | |
| 151 int offset = first.offset; | |
| 152 int end = last.end; | |
| 153 NodeLocator nodeLocator = new NodeLocator.con2(offset, end); | |
| 154 return nodeLocator.searchWithin(root); | |
| 155 } | |
| 156 | |
| 157 | |
| 158 List<AstNode> _getParents(AstNode node) { | |
| 159 List<AstNode> parents = <AstNode>[]; | |
| 160 while (node != null) { | |
| 161 parents.insert(0, node); | |
| 162 node = node.parent; | |
| 163 } | |
| 164 return parents; | |
| 165 } | |
| 166 | |
| 167 | |
| 168 CompilationUnit _parseUnit(String code) { | |
| 169 // TODO(scheglov) remember and update errors | |
| 170 var errorListener = new BooleanErrorListener(); | |
| 171 var reader = new CharSequenceReader(code); | |
| 172 var scanner = new Scanner(null, reader, errorListener); | |
| 173 var token = scanner.tokenize(); | |
| 174 var parser = new Parser(null, errorListener); | |
| 175 return parser.parseCompilationUnit(token); | |
| 176 } | |
| 177 | |
| 178 | |
| 179 /** | |
| 180 * Instances of the class [DeclarationMatcher] determine whether the element | 24 * Instances of the class [DeclarationMatcher] determine whether the element |
| 181 * model defined by a given AST structure matches an existing element model. | 25 * model defined by a given AST structure matches an existing element model. |
| 182 */ | 26 */ |
| 183 class DeclarationMatcher extends RecursiveAstVisitor { | 27 class DeclarationMatcher extends RecursiveAstVisitor { |
| 184 /** | 28 /** |
| 185 * The libary containing the AST nodes being visited. | 29 * The libary containing the AST nodes being visited. |
| 186 */ | 30 */ |
| 187 LibraryElement _enclosingLibrary; | 31 LibraryElement _enclosingLibrary; |
| 188 | 32 |
| 189 /** | 33 /** |
| (...skipping 585 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 775 } | 619 } |
| 776 } | 620 } |
| 777 | 621 |
| 778 | 622 |
| 779 /** | 623 /** |
| 780 * Instances of the class [IncrementalResolver] resolve the smallest portion of | 624 * Instances of the class [IncrementalResolver] resolve the smallest portion of |
| 781 * an AST structure that we currently know how to resolve. | 625 * an AST structure that we currently know how to resolve. |
| 782 */ | 626 */ |
| 783 class IncrementalResolver { | 627 class IncrementalResolver { |
| 784 /** | 628 /** |
| 785 * The error listener that will be informed of any errors that are found | |
| 786 * during resolution. | |
| 787 */ | |
| 788 final AnalysisErrorListener _errorListener; | |
| 789 | |
| 790 /** | |
| 791 * The object used to access the types from the core library. | 629 * The object used to access the types from the core library. |
| 792 */ | 630 */ |
| 793 final TypeProvider _typeProvider; | 631 final TypeProvider _typeProvider; |
| 794 | 632 |
| 795 /** | 633 /** |
| 796 * The element for the library containing the compilation unit being resolved. | 634 * The element for the library containing the compilation unit being resolved. |
| 797 */ | 635 */ |
| 798 final LibraryElement _definingLibrary; | 636 final LibraryElement _definingLibrary; |
| 799 | 637 |
| 800 /** | 638 /** |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 815 /** | 653 /** |
| 816 * The number of characters in the original contents that were replaced. | 654 * The number of characters in the original contents that were replaced. |
| 817 */ | 655 */ |
| 818 final int _updateOldLength; | 656 final int _updateOldLength; |
| 819 | 657 |
| 820 /** | 658 /** |
| 821 * The number of characters in the replacement text. | 659 * The number of characters in the replacement text. |
| 822 */ | 660 */ |
| 823 final int _updateNewLength; | 661 final int _updateNewLength; |
| 824 | 662 |
| 663 ResolutionContext _resolutionContext; | |
| 664 | |
| 665 List<AnalysisError> _resolveErrors = AnalysisError.NO_ERRORS; | |
| 666 List<AnalysisError> _verifyErrors = AnalysisError.NO_ERRORS; | |
| 667 List<AnalysisError> _hints = AnalysisError.NO_ERRORS; | |
| 668 | |
| 825 /** | 669 /** |
| 826 * Initialize a newly created incremental resolver to resolve a node in the | 670 * Initialize a newly created incremental resolver to resolve a node in the |
| 827 * given source in the given library, reporting errors to the given error | 671 * given source in the given library. |
| 828 * listener. | |
| 829 */ | 672 */ |
| 830 IncrementalResolver(this._errorListener, this._typeProvider, | 673 IncrementalResolver(this._typeProvider, this._definingLibrary, |
| 831 this._definingLibrary, this._definingUnit, this._source, this._updateOffse t, | 674 this._definingUnit, this._source, this._updateOffset, this._updateOldLengt h, |
| 832 this._updateOldLength, this._updateNewLength); | 675 this._updateNewLength); |
| 833 | 676 |
| 834 /** | 677 /** |
| 835 * Resolve [node], reporting any errors or warnings to the given listener. | 678 * Resolve [node], reporting any errors or warnings to the given listener. |
| 836 * | 679 * |
| 837 * [node] - the root of the AST structure to be resolved. | 680 * [node] - the root of the AST structure to be resolved. |
| 838 */ | 681 */ |
| 839 void resolve(AstNode node) { | 682 void resolve(AstNode node) { |
| 840 AstNode rootNode = _findResolutionRoot(node); | 683 AstNode rootNode = _findResolutionRoot(node); |
| 841 // update elements | 684 // update elements |
| 842 _definingUnit.accept( | 685 _definingUnit.accept( |
| 843 new _ElementNameOffsetUpdater( | 686 new _ElementNameOffsetUpdater( |
| 844 _updateOffset, | 687 _updateOffset, |
| 845 _updateNewLength - _updateOldLength)); | 688 _updateNewLength - _updateOldLength)); |
| 846 if (_elementModelChanged(rootNode)) { | 689 if (_elementModelChanged(rootNode)) { |
| 847 throw new AnalysisException("Cannot resolve node: element model changed"); | 690 throw new AnalysisException("Cannot resolve node: element model changed"); |
| 848 } | 691 } |
| 849 _updateElements(rootNode); | 692 _updateElements(rootNode); |
| 850 // resolve root in scope | 693 // resolve |
| 851 ResolutionContext context = | 694 _resolveReferences(rootNode); |
| 852 ResolutionContextBuilder.contextFor(rootNode, _errorListener); | 695 // verify |
| 853 Scope scope = context.scope; | 696 _verify(rootNode); |
| 854 _resolveTypes(rootNode, scope); | 697 _generateHints(rootNode); |
| 855 _resolveVariables(rootNode, scope); | |
| 856 _resolveReferences(rootNode, context); | |
| 857 } | 698 } |
| 858 | 699 |
| 859 /** | 700 /** |
| 860 * Return `true` if the given node can be resolved independently of any other | 701 * Return `true` if the given node can be resolved independently of any other |
| 861 * nodes. | 702 * nodes. |
| 862 * | 703 * |
| 863 * *Note*: This method needs to be kept in sync with | 704 * *Note*: This method needs to be kept in sync with |
| 864 * [ScopeBuilder.ContextBuilder]. | 705 * [ScopeBuilder.ContextBuilder]. |
| 865 * | 706 * |
| 866 * [node] - the node being tested. | 707 * [node] - the node being tested. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 910 AstNode _findResolutionRoot(AstNode node) { | 751 AstNode _findResolutionRoot(AstNode node) { |
| 911 while (node != null) { | 752 while (node != null) { |
| 912 if (_canBeResolved(node)) { | 753 if (_canBeResolved(node)) { |
| 913 return node; | 754 return node; |
| 914 } | 755 } |
| 915 node = node.parent; | 756 node = node.parent; |
| 916 } | 757 } |
| 917 throw new AnalysisException("Cannot resolve node: no resolvable node"); | 758 throw new AnalysisException("Cannot resolve node: no resolvable node"); |
| 918 } | 759 } |
| 919 | 760 |
| 761 void _generateHints(AstNode node) { | |
| 762 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 763 CompilationUnit unit = node.getAncestor((n) => n is CompilationUnit); | |
| 764 AnalysisContext analysisContext = _definingLibrary.context; | |
| 765 HintGenerator hintGenerator = | |
| 766 new HintGenerator(<CompilationUnit>[unit], analysisContext, errorListene r); | |
| 767 hintGenerator.generateForLibrary(); | |
| 768 _hints = errorListener.getErrorsForSource(_source); | |
| 769 } | |
| 770 | |
| 920 /** | 771 /** |
| 921 * Return the element defined by [node], or `null` if the node does not | 772 * Return the element defined by [node], or `null` if the node does not |
| 922 * define an element. | 773 * define an element. |
| 923 */ | 774 */ |
| 924 Element _getElement(AstNode node) { | 775 Element _getElement(AstNode node) { |
| 925 if (node is Declaration) { | 776 if (node is Declaration) { |
| 926 return node.element; | 777 return node.element; |
| 927 } else if (node is CompilationUnit) { | 778 } else if (node is CompilationUnit) { |
| 928 return node.element; | 779 return node.element; |
| 929 } | 780 } |
| 930 return null; | 781 return null; |
| 931 } | 782 } |
| 932 | 783 |
| 933 void _resolveReferences(AstNode node, ResolutionContext context) { | 784 _resolveReferences(AstNode node) { |
| 934 ResolverVisitor visitor = new ResolverVisitor.con3( | 785 RecordingErrorListener errorListener = new RecordingErrorListener(); |
| 935 _definingLibrary, | 786 // prepare context |
| 936 _source, | 787 _resolutionContext = |
| 937 _typeProvider, | 788 ResolutionContextBuilder.contextFor(node, errorListener); |
| 938 context.scope, | 789 Scope scope = _resolutionContext.scope; |
| 939 _errorListener); | 790 // resolve types |
| 940 visitor.enclosingClass = context.enclosingClass; | 791 { |
| 941 node.accept(visitor); | 792 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( |
| 942 } | 793 _definingLibrary, |
| 943 | 794 _source, |
| 944 void _resolveTypes(AstNode node, Scope scope) { | 795 _typeProvider, |
| 945 TypeResolverVisitor visitor = new TypeResolverVisitor.con3( | 796 scope, |
| 946 _definingLibrary, | 797 errorListener); |
| 947 _source, | 798 node.accept(visitor); |
| 948 _typeProvider, | 799 } |
| 949 scope, | 800 // resolve variables |
| 950 _errorListener); | 801 { |
| 951 node.accept(visitor); | 802 VariableResolverVisitor visitor = new VariableResolverVisitor.con2( |
| 952 } | 803 _definingLibrary, |
| 953 | 804 _source, |
| 954 void _resolveVariables(AstNode node, Scope scope) { | 805 _typeProvider, |
| 955 VariableResolverVisitor visitor = new VariableResolverVisitor.con2( | 806 scope, |
| 956 _definingLibrary, | 807 errorListener); |
| 957 _source, | 808 node.accept(visitor); |
| 958 _typeProvider, | 809 } |
| 959 scope, | 810 // resolve references |
| 960 _errorListener); | 811 { |
| 961 node.accept(visitor); | 812 ResolverVisitor visitor = new ResolverVisitor.con3( |
| 813 _definingLibrary, | |
| 814 _source, | |
| 815 _typeProvider, | |
| 816 _resolutionContext.scope, | |
| 817 errorListener); | |
| 818 visitor.enclosingClass = _resolutionContext.enclosingClass; | |
| 819 node.accept(visitor); | |
| 820 } | |
| 821 // remember errors | |
| 822 _resolveErrors = errorListener.getErrorsForSource(_source); | |
| 962 } | 823 } |
| 963 | 824 |
| 964 void _updateElements(AstNode node) { | 825 void _updateElements(AstNode node) { |
| 965 // build elements in node | 826 // build elements in node |
| 966 ElementHolder holder; | 827 ElementHolder holder; |
| 967 _ElementsRestorer elementsRestorer = new _ElementsRestorer(node); | 828 _ElementsRestorer elementsRestorer = new _ElementsRestorer(node); |
| 968 try { | 829 try { |
| 969 holder = new ElementHolder(); | 830 holder = new ElementHolder(); |
| 970 ElementBuilder builder = new ElementBuilder(holder); | 831 ElementBuilder builder = new ElementBuilder(holder); |
| 971 node.accept(builder); | 832 node.accept(builder); |
| 972 } finally { | 833 } finally { |
| 973 elementsRestorer.restore(); | 834 elementsRestorer.restore(); |
| 974 } | 835 } |
| 975 // apply compatible changes to elements | 836 // apply compatible changes to elements |
| 976 if (node is FunctionDeclaration) { | 837 if (node is FunctionDeclaration) { |
| 977 FunctionElementImpl oldElement = node.element; | 838 FunctionElementImpl oldElement = node.element; |
| 978 FunctionElementImpl newElement = holder.functions[0]; | 839 FunctionElementImpl newElement = holder.functions[0]; |
| 979 oldElement.labels = newElement.labels; | 840 oldElement.labels = newElement.labels; |
| 980 oldElement.localVariables = newElement.localVariables; | 841 oldElement.localVariables = newElement.localVariables; |
| 981 } | 842 } |
| 982 if (node is MethodDeclaration) { | 843 if (node is MethodDeclaration) { |
| 983 MethodElementImpl oldElement = node.element; | 844 MethodElementImpl oldElement = node.element; |
| 984 MethodElementImpl newElement = holder.methods[0]; | 845 MethodElementImpl newElement = holder.methods[0]; |
| 985 oldElement.labels = newElement.labels; | 846 oldElement.labels = newElement.labels; |
| 986 oldElement.localVariables = newElement.localVariables; | 847 oldElement.localVariables = newElement.localVariables; |
| 987 } | 848 } |
| 988 } | 849 } |
| 850 | |
| 851 void _verify(AstNode node) { | |
| 852 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 853 ErrorReporter errorReporter = new ErrorReporter(errorListener, _source); | |
| 854 ErrorVerifier errorVerifier = new ErrorVerifier( | |
| 855 errorReporter, | |
| 856 _definingLibrary, | |
| 857 _typeProvider, | |
| 858 new InheritanceManager(_definingLibrary)); | |
| 859 if (_resolutionContext.enclosingClassDeclaration != null) { | |
| 860 errorVerifier.initClassDeclaration( | |
| 861 _resolutionContext.enclosingClassDeclaration); | |
| 862 } | |
| 863 node.accept(errorVerifier); | |
| 864 _verifyErrors = errorListener.getErrorsForSource(_source); | |
| 865 } | |
| 989 } | 866 } |
| 990 | 867 |
| 991 | 868 |
| 869 class PoorMansIncrementalResolver { | |
| 870 final TypeProvider _typeProvider; | |
| 871 final Source _unitSource; | |
| 872 final Source _librarySource; | |
| 873 final DartEntry _entry; | |
| 874 | |
| 875 int _updateOffset; | |
| 876 int _updateDelta; | |
| 877 int _updateEndOld; | |
| 878 int _updateEndNew; | |
| 879 | |
| 880 List<AnalysisError> _newScanErrors = <AnalysisError>[]; | |
| 881 List<AnalysisError> _newParseErrors = <AnalysisError>[]; | |
| 882 List<AnalysisError> _newResolveErrors = <AnalysisError>[]; | |
| 883 List<AnalysisError> _newVerifyErrors = <AnalysisError>[]; | |
| 884 List<AnalysisError> _newHints = <AnalysisError>[]; | |
| 885 | |
| 886 PoorMansIncrementalResolver(this._typeProvider, this._unitSource, | |
| 887 this._librarySource, this._entry); | |
| 888 | |
| 889 /** | |
| 890 * Attempts to update [oldUnit] to the state corresponding to [newCode]. | |
| 891 * Returns `true` if success, or `false` otherwise. | |
| 892 * The [oldUnit] might be damaged. | |
| 893 */ | |
| 894 bool resolve(CompilationUnit oldUnit, String newCode) { | |
| 895 try { | |
| 896 CompilationUnit newUnit = _parseUnit(newCode); | |
| 897 _TokenPair firstPair = | |
| 898 _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken); | |
| 899 _TokenPair lastPair = | |
| 900 _findLastDifferentToken(oldUnit.endToken, newUnit.endToken); | |
| 901 if (firstPair != null && lastPair != null) { | |
| 902 // Prepare the "old" token range. | |
| 903 Token oldBeginToken; | |
| 904 Token oldEndToken; | |
| 905 if (firstPair.oldToken.offset < lastPair.oldToken.offset) { | |
| 906 oldBeginToken = firstPair.oldToken; | |
| 907 oldEndToken = lastPair.oldToken; | |
| 908 } else { | |
| 909 oldBeginToken = lastPair.oldToken; | |
| 910 oldEndToken = firstPair.oldToken; | |
| 911 } | |
| 912 // Prepare the "old" token tange. | |
| 913 Token newBeginToken; | |
| 914 Token newEndToken; | |
| 915 if (firstPair.newToken.offset < lastPair.newToken.offset) { | |
| 916 newBeginToken = firstPair.newToken; | |
| 917 newEndToken = lastPair.newToken; | |
| 918 } else { | |
| 919 newBeginToken = lastPair.newToken; | |
| 920 newEndToken = firstPair.newToken; | |
| 921 } | |
| 922 // Find nodes covering the "old" and "new" token ranges. | |
| 923 AstNode oldNode = | |
| 924 _findNodeWithTokens(oldUnit, oldBeginToken, oldEndToken); | |
| 925 AstNode newNode = | |
| 926 _findNodeWithTokens(newUnit, newBeginToken, newEndToken); | |
| 927 // Try to find the smallest common node, a FunctionBody currently. | |
| 928 { | |
| 929 List<AstNode> oldParents = _getParents(oldNode); | |
| 930 List<AstNode> newParents = _getParents(newNode); | |
| 931 int length = math.min(oldParents.length, newParents.length); | |
| 932 bool found = false; | |
| 933 for (int i = 0; i < length; i++) { | |
| 934 AstNode oldParent = oldParents[i]; | |
| 935 AstNode newParent = newParents[i]; | |
| 936 if (oldParent is FunctionBody && newParent is FunctionBody) { | |
| 937 oldNode = oldParent; | |
| 938 newNode = newParent; | |
| 939 found = true; | |
| 940 break; | |
| 941 } | |
| 942 } | |
| 943 if (!found) { | |
| 944 return false; | |
| 945 } | |
| 946 } | |
| 947 // replace node | |
| 948 NodeReplacer.replace(oldNode, newNode); | |
| 949 // update token references | |
| 950 oldNode.beginToken.previous.setNext(newNode.beginToken); | |
| 951 oldNode.endToken.setNext(oldNode.endToken.next); | |
| 952 // prepare update range | |
| 953 _updateOffset = oldNode.offset; | |
| 954 _updateDelta = lastPair.delta; | |
| 955 _updateEndOld = oldNode.end; | |
| 956 _updateEndNew = newNode.end; | |
| 957 // perform incremental resolution | |
| 958 CompilationUnitElement oldUnitElement = oldUnit.element; | |
| 959 IncrementalResolver incrementalResolver = new IncrementalResolver( | |
| 960 _typeProvider, | |
| 961 oldUnitElement.library, | |
| 962 oldUnitElement, | |
| 963 oldUnitElement.source, | |
| 964 _updateOffset, | |
| 965 oldNode.length, | |
| 966 newNode.length + _updateDelta); | |
| 967 incrementalResolver.resolve(newNode); | |
| 968 _newResolveErrors = incrementalResolver._resolveErrors; | |
| 969 _newVerifyErrors = incrementalResolver._verifyErrors; | |
| 970 _newHints = incrementalResolver._hints; | |
| 971 _updateEntry(); | |
| 972 return true; | |
| 973 } | |
| 974 } catch (e, st) { | |
| 975 // TODO(scheglov) find a way to log these exceptions | |
| 976 print(e); | |
| 977 print(st); | |
| 978 } | |
| 979 return false; | |
| 980 } | |
| 981 | |
| 982 CompilationUnit _parseUnit(String code) { | |
| 983 Token token = _scan(code); | |
| 984 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 985 Parser parser = new Parser(_unitSource, errorListener); | |
| 986 CompilationUnit unit = parser.parseCompilationUnit(token); | |
| 987 _newParseErrors = errorListener.errors; | |
| 988 return unit; | |
| 989 } | |
| 990 | |
| 991 Token _scan(String code) { | |
| 992 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
| 993 CharSequenceReader reader = new CharSequenceReader(code); | |
| 994 Scanner scanner = new Scanner(_unitSource, reader, errorListener); | |
| 995 Token token = scanner.tokenize(); | |
| 996 _newScanErrors = errorListener.errors; | |
| 997 return token; | |
| 998 } | |
| 999 | |
| 1000 void _updateEntry() { | |
| 1001 { | |
| 1002 List<AnalysisError> oldErrors = _entry.getValue(DartEntry.SCAN_ERRORS); | |
| 1003 List<AnalysisError> errors = _updateErrors(oldErrors, _newScanErrors); | |
|
Brian Wilkerson
2014/11/25 14:53:58
Given that we're scanning and parsing the whole fi
scheglov
2014/11/25 18:54:11
Good idea!
Done.
| |
| 1004 _entry.setValue(DartEntry.SCAN_ERRORS, errors); | |
| 1005 } | |
| 1006 { | |
| 1007 List<AnalysisError> oldErrors = _entry.getValue(DartEntry.PARSE_ERRORS); | |
| 1008 List<AnalysisError> errors = _updateErrors(oldErrors, _newParseErrors); | |
| 1009 _entry.setValue(DartEntry.PARSE_ERRORS, errors); | |
| 1010 } | |
| 1011 { | |
| 1012 List<AnalysisError> oldErrors = | |
| 1013 _entry.getValueInLibrary(DartEntry.RESOLUTION_ERRORS, _librarySource); | |
| 1014 List<AnalysisError> errors = _updateErrors(oldErrors, _newResolveErrors); | |
| 1015 _entry.setValueInLibrary( | |
| 1016 DartEntry.RESOLUTION_ERRORS, | |
| 1017 _librarySource, | |
| 1018 errors); | |
| 1019 } | |
| 1020 { | |
| 1021 List<AnalysisError> oldErrors = | |
| 1022 _entry.getValueInLibrary(DartEntry.VERIFICATION_ERRORS, _librarySource ); | |
| 1023 List<AnalysisError> errors = _updateErrors(oldErrors, _newVerifyErrors); | |
| 1024 _entry.setValueInLibrary( | |
| 1025 DartEntry.VERIFICATION_ERRORS, | |
| 1026 _librarySource, | |
| 1027 errors); | |
| 1028 } | |
| 1029 { | |
| 1030 List<AnalysisError> oldErrors = | |
| 1031 _entry.getValueInLibrary(DartEntry.HINTS, _librarySource); | |
| 1032 List<AnalysisError> errors = _updateErrors(oldErrors, _newHints); | |
| 1033 _entry.setValueInLibrary(DartEntry.HINTS, _librarySource, errors); | |
| 1034 } | |
| 1035 } | |
| 1036 | |
| 1037 List<AnalysisError> _updateErrors(List<AnalysisError> oldErrors, | |
| 1038 List<AnalysisError> newErrors) { | |
| 1039 List<AnalysisError> errors = new List<AnalysisError>(); | |
| 1040 // add updated old errors | |
| 1041 for (AnalysisError error in oldErrors) { | |
| 1042 int errorOffset = error.offset; | |
| 1043 if (errorOffset < _updateOffset) { | |
| 1044 errors.add(error); | |
| 1045 } else if (errorOffset > _updateEndOld) { | |
| 1046 error.offset += _updateDelta; | |
| 1047 errors.add(error); | |
| 1048 } | |
| 1049 } | |
| 1050 // add new errors | |
| 1051 for (AnalysisError error in newErrors) { | |
| 1052 int errorOffset = error.offset; | |
| 1053 if (errorOffset > _updateOffset && errorOffset < _updateEndNew) { | |
| 1054 errors.add(error); | |
| 1055 } | |
| 1056 } | |
| 1057 // done | |
| 1058 return errors; | |
| 1059 } | |
| 1060 | |
| 1061 static bool _equalToken(Token oldToken, Token newToken, int delta) { | |
| 1062 if (oldToken.type != newToken.type) { | |
| 1063 return false; | |
| 1064 } | |
| 1065 if (newToken.offset - oldToken.offset != delta) { | |
| 1066 return false; | |
| 1067 } | |
| 1068 return oldToken.lexeme == newToken.lexeme; | |
| 1069 } | |
| 1070 | |
| 1071 static _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) { | |
| 1072 // print('first ------------'); | |
| 1073 while (oldToken.type != TokenType.EOF && newToken.type != TokenType.EOF) { | |
| 1074 // print('old: $oldToken @ ${oldToken.offset}'); | |
| 1075 // print('new: $newToken @ ${newToken.offset}'); | |
| 1076 if (!_equalToken(oldToken, newToken, 0)) { | |
| 1077 return new _TokenPair(oldToken, newToken, 0); | |
| 1078 } | |
| 1079 oldToken = oldToken.next; | |
| 1080 newToken = newToken.next; | |
| 1081 } | |
| 1082 return null; | |
| 1083 } | |
| 1084 | |
| 1085 static _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) { | |
| 1086 // print('last ------------'); | |
| 1087 int delta = newToken.offset - oldToken.offset; | |
| 1088 while (oldToken.previous != oldToken && newToken.previous != newToken) { | |
| 1089 // print('old: $oldToken @ ${oldToken.offset}'); | |
| 1090 // print('new: $newToken @ ${newToken.offset}'); | |
| 1091 if (!_equalToken(oldToken, newToken, delta)) { | |
| 1092 return new _TokenPair(oldToken.next, newToken.next, delta); | |
| 1093 } | |
| 1094 oldToken.offset += delta; | |
| 1095 oldToken = oldToken.previous; | |
| 1096 newToken = newToken.previous; | |
| 1097 } | |
| 1098 return null; | |
| 1099 } | |
| 1100 | |
| 1101 static AstNode _findNodeWithTokens(AstNode root, Token first, Token last) { | |
| 1102 int offset = first.offset; | |
| 1103 int end = last.end; | |
| 1104 NodeLocator nodeLocator = new NodeLocator.con2(offset, end); | |
| 1105 return nodeLocator.searchWithin(root); | |
| 1106 } | |
| 1107 | |
| 1108 static List<AstNode> _getParents(AstNode node) { | |
| 1109 List<AstNode> parents = <AstNode>[]; | |
| 1110 while (node != null) { | |
| 1111 parents.insert(0, node); | |
| 1112 node = node.parent; | |
| 1113 } | |
| 1114 return parents; | |
| 1115 } | |
| 1116 } | |
| 1117 | |
| 1118 | |
| 992 /** | 1119 /** |
| 993 * The context to resolve an [AstNode] in. | 1120 * The context to resolve an [AstNode] in. |
| 994 */ | 1121 */ |
| 995 class ResolutionContext { | 1122 class ResolutionContext { |
| 1123 ClassDeclaration enclosingClassDeclaration; | |
| 996 ClassElement enclosingClass; | 1124 ClassElement enclosingClass; |
| 997 Scope scope; | 1125 Scope scope; |
| 998 } | 1126 } |
| 999 | 1127 |
| 1000 | 1128 |
| 1001 /** | 1129 /** |
| 1002 * Instances of the class [ResolutionContextBuilder] build the context for a | 1130 * Instances of the class [ResolutionContextBuilder] build the context for a |
| 1003 * given node in an AST structure. At the moment, this class only handles | 1131 * given node in an AST structure. At the moment, this class only handles |
| 1004 * top-level and class-level declarations. | 1132 * top-level and class-level declarations. |
| 1005 */ | 1133 */ |
| 1006 class ResolutionContextBuilder { | 1134 class ResolutionContextBuilder { |
| 1007 /** | 1135 /** |
| 1008 * The listener to which analysis errors will be reported. | 1136 * The listener to which analysis errors will be reported. |
| 1009 */ | 1137 */ |
| 1010 final AnalysisErrorListener _errorListener; | 1138 final AnalysisErrorListener _errorListener; |
| 1011 | 1139 |
| 1012 /** | 1140 /** |
| 1013 * The class containing the AST nodes being visited, or `null` if we are not | 1141 * The class containing the enclosing [ClassDeclaration], or `null` if we are |
| 1142 * not in the scope of a class. | |
| 1143 */ | |
| 1144 ClassDeclaration _enclosingClassDeclaration; | |
| 1145 | |
| 1146 /** | |
| 1147 * The class containing the enclosing [ClassElement], or `null` if we are not | |
| 1014 * in the scope of a class. | 1148 * in the scope of a class. |
| 1015 */ | 1149 */ |
| 1016 ClassElement _enclosingClass; | 1150 ClassElement _enclosingClass; |
| 1017 | 1151 |
| 1018 /** | 1152 /** |
| 1019 * Initialize a newly created scope builder to generate a scope that will | 1153 * Initialize a newly created scope builder to generate a scope that will |
| 1020 * report errors to the given listener. | 1154 * report errors to the given listener. |
| 1021 */ | 1155 */ |
| 1022 ResolutionContextBuilder(this._errorListener); | 1156 ResolutionContextBuilder(this._errorListener); |
| 1023 | 1157 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 1048 if (node is CompilationUnit) { | 1182 if (node is CompilationUnit) { |
| 1049 return _scopeForCompilationUnit(node); | 1183 return _scopeForCompilationUnit(node); |
| 1050 } | 1184 } |
| 1051 AstNode parent = node.parent; | 1185 AstNode parent = node.parent; |
| 1052 if (parent == null) { | 1186 if (parent == null) { |
| 1053 throw new AnalysisException( | 1187 throw new AnalysisException( |
| 1054 "Cannot create scope: node is not part of a CompilationUnit"); | 1188 "Cannot create scope: node is not part of a CompilationUnit"); |
| 1055 } | 1189 } |
| 1056 Scope scope = _scopeForAstNode(parent); | 1190 Scope scope = _scopeForAstNode(parent); |
| 1057 if (node is ClassDeclaration) { | 1191 if (node is ClassDeclaration) { |
| 1192 _enclosingClassDeclaration = node; | |
| 1058 _enclosingClass = node.element; | 1193 _enclosingClass = node.element; |
| 1059 if (_enclosingClass == null) { | 1194 if (_enclosingClass == null) { |
| 1060 throw new AnalysisException( | 1195 throw new AnalysisException( |
| 1061 "Cannot build a scope for an unresolved class"); | 1196 "Cannot build a scope for an unresolved class"); |
| 1062 } | 1197 } |
| 1063 scope = new ClassScope( | 1198 scope = new ClassScope( |
| 1064 new TypeParameterScope(scope, _enclosingClass), | 1199 new TypeParameterScope(scope, _enclosingClass), |
| 1065 _enclosingClass); | 1200 _enclosingClass); |
| 1066 } else if (node is ClassTypeAlias) { | 1201 } else if (node is ClassTypeAlias) { |
| 1067 ClassElement element = node.element; | 1202 ClassElement element = node.element; |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1131 if (node == null) { | 1266 if (node == null) { |
| 1132 throw new AnalysisException("Cannot create context: node is null"); | 1267 throw new AnalysisException("Cannot create context: node is null"); |
| 1133 } | 1268 } |
| 1134 // build scope | 1269 // build scope |
| 1135 ResolutionContextBuilder builder = | 1270 ResolutionContextBuilder builder = |
| 1136 new ResolutionContextBuilder(errorListener); | 1271 new ResolutionContextBuilder(errorListener); |
| 1137 Scope scope = builder._scopeFor(node); | 1272 Scope scope = builder._scopeFor(node); |
| 1138 // prepare context | 1273 // prepare context |
| 1139 ResolutionContext context = new ResolutionContext(); | 1274 ResolutionContext context = new ResolutionContext(); |
| 1140 context.scope = scope; | 1275 context.scope = scope; |
| 1276 context.enclosingClassDeclaration = builder._enclosingClassDeclaration; | |
| 1141 context.enclosingClass = builder._enclosingClass; | 1277 context.enclosingClass = builder._enclosingClass; |
| 1142 return context; | 1278 return context; |
| 1143 } | 1279 } |
| 1144 } | 1280 } |
| 1145 | 1281 |
| 1146 | 1282 |
| 1147 /** | 1283 /** |
| 1148 * Instances of the class [_DeclarationMismatchException] represent an exception | 1284 * Instances of the class [_DeclarationMismatchException] represent an exception |
| 1149 * that is thrown when the element model defined by a given AST structure does | 1285 * that is thrown when the element model defined by a given AST structure does |
| 1150 * not match an existing element model. | 1286 * not match an existing element model. |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1266 @override | 1402 @override |
| 1267 visitSimpleIdentifier(SimpleIdentifier node) { | 1403 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1268 _elements[node] = node.staticElement; | 1404 _elements[node] = node.staticElement; |
| 1269 } | 1405 } |
| 1270 } | 1406 } |
| 1271 | 1407 |
| 1272 | 1408 |
| 1273 class _TokenPair { | 1409 class _TokenPair { |
| 1274 final Token oldToken; | 1410 final Token oldToken; |
| 1275 final Token newToken; | 1411 final Token newToken; |
| 1276 _TokenPair(this.oldToken, this.newToken); | 1412 final int delta; |
| 1413 _TokenPair(this.oldToken, this.newToken, this.delta); | |
| 1277 } | 1414 } |
| OLD | NEW |