| 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 | 9 |
| 9 import 'ast.dart'; | 10 import 'ast.dart'; |
| 10 import 'element.dart'; | 11 import 'element.dart'; |
| 11 import 'error.dart'; | 12 import 'error.dart'; |
| 12 import 'java_engine.dart'; | 13 import 'java_engine.dart'; |
| 13 import 'resolver.dart'; | 14 import 'resolver.dart'; |
| 14 import 'scanner.dart'; | 15 import 'scanner.dart'; |
| 15 import 'source.dart'; | 16 import 'source.dart'; |
| 17 import 'parser.dart'; |
| 16 | 18 |
| 17 | 19 |
| 18 /** | 20 /** |
| 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 List<AstNode> _getParents(AstNode node) { |
| 108 List<AstNode> parents = <AstNode>[]; |
| 109 while (node != null) { |
| 110 parents.insert(0, node); |
| 111 node = node.parent; |
| 112 } |
| 113 return parents; |
| 114 } |
| 115 |
| 116 AstNode _findNodeWithTokens(AstNode root, Token first, Token last) { |
| 117 int offset = first.offset; |
| 118 int end = last.end; |
| 119 NodeLocator nodeLocator = new NodeLocator.con2(offset, end); |
| 120 return nodeLocator.searchWithin(root); |
| 121 } |
| 122 |
| 123 |
| 124 class _TokenPair { |
| 125 final Token oldToken; |
| 126 final Token newToken; |
| 127 _TokenPair(this.oldToken, this.newToken); |
| 128 } |
| 129 |
| 130 |
| 131 _TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) { |
| 132 // print('first ------------'); |
| 133 while (oldToken.type != TokenType.EOF && newToken.type != TokenType.EOF) { |
| 134 // print('old: $oldToken @ ${oldToken.offset}'); |
| 135 // print('new: $newToken @ ${newToken.offset}'); |
| 136 if (!_equalToken(oldToken, newToken, 0)) { |
| 137 return new _TokenPair(oldToken, newToken); |
| 138 } |
| 139 oldToken = oldToken.next; |
| 140 newToken = newToken.next; |
| 141 } |
| 142 return null; |
| 143 } |
| 144 |
| 145 |
| 146 _TokenPair _findLastDifferentToken(Token oldToken, Token newToken) { |
| 147 // print('last ------------'); |
| 148 int delta = newToken.offset - oldToken.offset; |
| 149 while (oldToken.previous != oldToken && newToken.previous != newToken) { |
| 150 // print('old: $oldToken @ ${oldToken.offset}'); |
| 151 // print('new: $newToken @ ${newToken.offset}'); |
| 152 if (!_equalToken(oldToken, newToken, delta)) { |
| 153 return new _TokenPair(oldToken.next, newToken.next); |
| 154 } |
| 155 oldToken.offset += delta; |
| 156 oldToken = oldToken.previous; |
| 157 newToken = newToken.previous; |
| 158 } |
| 159 return null; |
| 160 } |
| 161 |
| 162 |
| 163 bool _equalToken(Token a, Token b, int delta) { |
| 164 if (a.type != b.type) { |
| 165 return false; |
| 166 } |
| 167 if (b.offset - a.offset != delta) { |
| 168 return false; |
| 169 } |
| 170 return a.lexeme == b.lexeme; |
| 171 } |
| 172 |
| 173 |
| 174 CompilationUnit _parseUnit(String code) { |
| 175 // TODO(scheglov) remember and update errors |
| 176 var errorListener = new BooleanErrorListener(); |
| 177 var reader = new CharSequenceReader(code); |
| 178 var scanner = new Scanner(null, reader, errorListener); |
| 179 var token = scanner.tokenize(); |
| 180 var parser = new Parser(null, errorListener); |
| 181 return parser.parseCompilationUnit(token); |
| 182 } |
| 183 |
| 184 |
| 185 /** |
| 19 * Instances of the class [DeclarationMatcher] determine whether the element | 186 * Instances of the class [DeclarationMatcher] determine whether the element |
| 20 * model defined by a given AST structure matches an existing element model. | 187 * model defined by a given AST structure matches an existing element model. |
| 21 */ | 188 */ |
| 22 class DeclarationMatcher extends RecursiveAstVisitor { | 189 class DeclarationMatcher extends RecursiveAstVisitor { |
| 23 /** | 190 /** |
| 24 * The libary containing the AST nodes being visited. | 191 * The libary containing the AST nodes being visited. |
| 25 */ | 192 */ |
| 26 LibraryElement _enclosingLibrary; | 193 LibraryElement _enclosingLibrary; |
| 27 | 194 |
| 28 /** | 195 /** |
| (...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 383 } | 550 } |
| 384 _assertTrue(showNames.isEmpty); | 551 _assertTrue(showNames.isEmpty); |
| 385 _assertTrue(hideNames.isEmpty); | 552 _assertTrue(hideNames.isEmpty); |
| 386 } | 553 } |
| 387 | 554 |
| 388 void _assertCompatibleParameter(FormalParameter node, | 555 void _assertCompatibleParameter(FormalParameter node, |
| 389 ParameterElement element) { | 556 ParameterElement element) { |
| 390 if (node is SimpleFormalParameter) { | 557 if (node is SimpleFormalParameter) { |
| 391 _assertSameType(node.type, element.type); | 558 _assertSameType(node.type, element.type); |
| 392 node.identifier.staticElement = element; | 559 node.identifier.staticElement = element; |
| 393 element.nameOffset = node.identifier.offset; | 560 (element as ElementImpl).nameOffset = node.identifier.offset; |
| 394 (element as ElementImpl).name = node.identifier.name; | 561 (element as ElementImpl).name = node.identifier.name; |
| 395 } else { | 562 } else { |
| 396 // TODO(scheglov) support other parameter types | 563 // TODO(scheglov) support other parameter types |
| 397 _assertTrue(false); | 564 _assertTrue(false); |
| 398 } | 565 } |
| 399 // TODO(scheglov) check names of named parameters | 566 // TODO(scheglov) check names of named parameters |
| 400 } | 567 } |
| 401 | 568 |
| 402 void _assertCompatibleParameters(FormalParameterList nodes, | 569 void _assertCompatibleParameters(FormalParameterList nodes, |
| 403 List<ParameterElement> elements) { | 570 List<ParameterElement> elements) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 450 List<DartType> typeArguments = type.typeArguments; | 617 List<DartType> typeArguments = type.typeArguments; |
| 451 if (nodeArgumentList == null) { | 618 if (nodeArgumentList == null) { |
| 452 _assertTrue(typeArguments.isEmpty); | 619 _assertTrue(typeArguments.isEmpty); |
| 453 } else { | 620 } else { |
| 454 List<TypeName> nodeArguments = nodeArgumentList.arguments; | 621 List<TypeName> nodeArguments = nodeArgumentList.arguments; |
| 455 _assertSameTypes(nodeArguments, typeArguments); | 622 _assertSameTypes(nodeArguments, typeArguments); |
| 456 } | 623 } |
| 457 } else if (type is TypeParameterType) { | 624 } else if (type is TypeParameterType) { |
| 458 _assertEquals(nodeName, type.name); | 625 _assertEquals(nodeName, type.name); |
| 459 // TODO(scheglov) it should be possible to rename type parameters | 626 // TODO(scheglov) it should be possible to rename type parameters |
| 627 } else if (type is VoidType) { |
| 628 _assertEquals(nodeName, 'void'); |
| 629 // TODO(scheglov) add test for "void" |
| 460 } else { | 630 } else { |
| 461 // TODO(scheglov) support other types | 631 // TODO(scheglov) support other types |
| 632 // print('node: $node type: $type type.type: ${type.runtimeType}'); |
| 462 _assertTrue(false); | 633 _assertTrue(false); |
| 463 } | 634 } |
| 464 } | 635 } |
| 465 | 636 |
| 466 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) { | 637 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) { |
| 467 int length = nodes.length; | 638 int length = nodes.length; |
| 468 _assertEquals(length, types.length); | 639 _assertEquals(length, types.length); |
| 469 for (int i = 0; i < length; i++) { | 640 for (int i = 0; i < length; i++) { |
| 470 _assertSameType(nodes[i], types[i]); | 641 _assertSameType(nodes[i], types[i]); |
| 471 } | 642 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 544 if (element.displayName == name && element.nameOffset == offset) { | 715 if (element.displayName == name && element.nameOffset == offset) { |
| 545 return element; | 716 return element; |
| 546 } | 717 } |
| 547 } | 718 } |
| 548 return null; | 719 return null; |
| 549 } | 720 } |
| 550 | 721 |
| 551 void _gatherElements(Element element) { | 722 void _gatherElements(Element element) { |
| 552 _ElementsGatherer gatherer = new _ElementsGatherer(this); | 723 _ElementsGatherer gatherer = new _ElementsGatherer(this); |
| 553 element.accept(gatherer); | 724 element.accept(gatherer); |
| 554 // TODO(scheglov) push into CompilationUnitElement | 725 // TODO(scheglov) what if a change in a directive? |
| 555 if (identical(_enclosingUnit, _enclosingLibrary.definingCompilationUnit)) { | 726 if (identical(element, _enclosingLibrary.definingCompilationUnit)) { |
| 556 gatherer.addElements(_enclosingLibrary.imports); | 727 gatherer.addElements(_enclosingLibrary.imports); |
| 557 gatherer.addElements(_enclosingLibrary.exports); | 728 gatherer.addElements(_enclosingLibrary.exports); |
| 558 gatherer.addElements(_enclosingLibrary.parts); | 729 gatherer.addElements(_enclosingLibrary.parts); |
| 559 } | 730 } |
| 560 } | 731 } |
| 561 | 732 |
| 562 /** | 733 /** |
| 563 * Return the value of the given string literal, or `null` if the string is no
t a constant | 734 * Return the value of the given string literal, or `null` if the string is no
t a constant |
| 564 * string without any string interpolation. | 735 * string without any string interpolation. |
| 565 * | 736 * |
| (...skipping 516 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1082 visitFunctionExpression(FunctionExpression node) { | 1253 visitFunctionExpression(FunctionExpression node) { |
| 1083 _elements[node] = node.element; | 1254 _elements[node] = node.element; |
| 1084 super.visitFunctionExpression(node); | 1255 super.visitFunctionExpression(node); |
| 1085 } | 1256 } |
| 1086 | 1257 |
| 1087 @override | 1258 @override |
| 1088 visitSimpleIdentifier(SimpleIdentifier node) { | 1259 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1089 _elements[node] = node.staticElement; | 1260 _elements[node] = node.staticElement; |
| 1090 } | 1261 } |
| 1091 } | 1262 } |
| OLD | NEW |