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 | 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 AstNode oldNode = _findNodeWithTokens(oldUnit, firstPair.a, lastPair.a); | |
| 35 AstNode newNode = _findNodeWithTokens(newUnit, firstPair.b, lastPair.b); | |
| 36 // Try to find the smallest common node, a FunctionBody currently. | |
| 37 { | |
| 38 List<AstNode> oldParents = _getParents(oldNode); | |
|
Brian Wilkerson
2014/11/24 15:09:14
Why compute a list? Why not just walk up both pare
| |
| 39 List<AstNode> newParents = _getParents(newNode); | |
| 40 int length = math.min(oldParents.length, newParents.length); | |
| 41 bool found = false; | |
| 42 for (int i = 0; i < length; i++) { | |
| 43 AstNode oldParent = oldParents[i]; | |
| 44 AstNode newParent = newParents[i]; | |
| 45 if (oldParent is FunctionBody && | |
| 46 newParent is FunctionBody) { | |
| 47 oldNode = oldParent; | |
| 48 newNode = newParent; | |
| 49 found = true; | |
| 50 break; | |
| 51 } | |
| 52 } | |
| 53 if (!found) { | |
| 54 return false; | |
| 55 } | |
| 56 } | |
| 57 // replace node | |
| 58 NodeReplacer.replace(oldNode, newNode); | |
| 59 // update token references | |
| 60 firstPair.a.previous.setNext(firstPair.b); | |
| 61 lastPair.b.setNext(firstPair.a.next); | |
|
Paul Berry
2014/11/24 15:02:25
I think this should be:
lastPair.b.setNext(last
scheglov
2014/11/24 18:59:06
Done.
| |
| 62 // perform incremental resolution | |
| 63 // TODO(scheglov) update errors | |
| 64 AnalysisErrorListener errorListener = new BooleanErrorListener(); | |
| 65 CompilationUnitElement oldUnitElement = oldUnit.element; | |
| 66 IncrementalResolver incrementalResolver = new IncrementalResolver( | |
| 67 errorListener, | |
| 68 typeProvider, | |
| 69 oldUnitElement.library, | |
| 70 oldUnitElement, | |
| 71 oldUnitElement.source, | |
| 72 oldNode.offset, | |
| 73 oldNode.length, | |
| 74 newNode.length); | |
| 75 incrementalResolver.resolve(newNode); | |
| 76 return true; | |
| 77 } | |
| 78 } catch (e) { | |
| 79 } | |
|
Paul Berry
2014/11/24 15:02:25
Can we report the exception using the "server.erro
scheglov
2014/11/24 18:59:05
We're in a wrong project to do this.
I'll add TODO
| |
| 80 return false; | |
| 81 } | |
| 82 | |
| 83 | |
| 84 List<AstNode> _getParents(AstNode node) { | |
| 85 List<AstNode> parents = <AstNode>[]; | |
| 86 while (node != null) { | |
| 87 parents.insert(0, node); | |
| 88 node = node.parent; | |
| 89 } | |
| 90 return parents; | |
| 91 } | |
| 92 | |
| 93 AstNode _findNodeWithTokens(AstNode root, Token first, Token last) { | |
| 94 int offset = first.offset; | |
| 95 int end = last.end; | |
| 96 NodeLocator nodeLocator = new NodeLocator.con2(offset, end); | |
| 97 return nodeLocator.searchWithin(root); | |
| 98 } | |
| 99 | |
| 100 | |
| 101 class _TokenPair { | |
| 102 final Token a; | |
|
Paul Berry
2014/11/24 15:02:25
It looks like all uses of _TokenPair use "a" to re
scheglov
2014/11/24 18:59:05
Done.
| |
| 103 final Token b; | |
| 104 _TokenPair(this.a, this.b); | |
| 105 } | |
| 106 | |
| 107 | |
| 108 _TokenPair _findFirstDifferentToken(Token a, Token b) { | |
|
Paul Berry
2014/11/24 15:02:25
The same rename would be nice here too (and also i
scheglov
2014/11/24 18:59:05
Done.
| |
| 109 // print('first ------------'); | |
|
Paul Berry
2014/11/24 15:02:25
Commented out debug code should be removed (and al
scheglov
2014/11/24 18:59:05
I'd prefer to keep it in for some time and remove
| |
| 110 while (true) { | |
| 111 // print('a: $a @ ${a.offset}'); | |
| 112 // print('b: $b @ ${b.offset}'); | |
| 113 if (!_equalToken(a, b, 0)) { | |
| 114 return new _TokenPair(a, b); | |
| 115 } | |
| 116 if (a.type == TokenType.EOF) { | |
|
Brian Wilkerson
2014/11/24 15:09:13
Why not make this part of the while loop's conditi
scheglov
2014/11/24 18:59:06
Done.
| |
| 117 return null; | |
| 118 } | |
| 119 a = a.next; | |
| 120 b = b.next; | |
| 121 } | |
| 122 return null; | |
| 123 } | |
| 124 | |
| 125 | |
| 126 _TokenPair _findLastDifferentToken(Token a, Token b) { | |
| 127 // print('last ------------'); | |
| 128 int delta = b.offset - a.offset; | |
| 129 while (a.previous != a && b.previous != b) { | |
| 130 // print('a: $a @ ${a.offset}'); | |
| 131 // print('b: $b @ ${b.offset}'); | |
| 132 if (!_equalToken(a, b, delta)) { | |
| 133 return new _TokenPair(a.next, b.next); | |
| 134 } | |
| 135 a.offset += delta; | |
| 136 a = a.previous; | |
| 137 b = b.previous; | |
| 138 } | |
|
Brian Wilkerson
2014/11/24 15:09:14
For what it's worth, I would find the code easier
scheglov
2014/11/24 18:59:06
Done.
| |
| 139 } | |
| 140 | |
| 141 | |
| 142 bool _equalToken(Token a, Token b, int delta) { | |
| 143 if (a.type != b.type) { | |
| 144 return false; | |
| 145 } | |
| 146 if (b.offset - a.offset != delta) { | |
| 147 return false; | |
| 148 } | |
| 149 return a.lexeme == b.lexeme; | |
| 150 } | |
| 151 | |
| 152 | |
| 153 CompilationUnit _parseUnit(String code) { | |
| 154 // TODO(scheglov) remember and update errors | |
| 155 var errorListener = new BooleanErrorListener(); | |
| 156 var reader = new CharSequenceReader(code); | |
| 157 var scanner = new Scanner(null, reader, errorListener); | |
| 158 var token = scanner.tokenize(); | |
| 159 var parser = new Parser(null, errorListener); | |
| 160 return parser.parseCompilationUnit(token); | |
| 161 } | |
| 162 | |
| 163 | |
| 164 /** | |
| 19 * Instances of the class [DeclarationMatcher] determine whether the element | 165 * Instances of the class [DeclarationMatcher] determine whether the element |
| 20 * model defined by a given AST structure matches an existing element model. | 166 * model defined by a given AST structure matches an existing element model. |
| 21 */ | 167 */ |
| 22 class DeclarationMatcher extends RecursiveAstVisitor { | 168 class DeclarationMatcher extends RecursiveAstVisitor { |
| 23 /** | 169 /** |
| 24 * The libary containing the AST nodes being visited. | 170 * The libary containing the AST nodes being visited. |
| 25 */ | 171 */ |
| 26 LibraryElement _enclosingLibrary; | 172 LibraryElement _enclosingLibrary; |
| 27 | 173 |
| 28 /** | 174 /** |
| (...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 447 List<DartType> typeArguments = type.typeArguments; | 593 List<DartType> typeArguments = type.typeArguments; |
| 448 if (nodeArgumentList == null) { | 594 if (nodeArgumentList == null) { |
| 449 _assertTrue(typeArguments.isEmpty); | 595 _assertTrue(typeArguments.isEmpty); |
| 450 } else { | 596 } else { |
| 451 List<TypeName> nodeArguments = nodeArgumentList.arguments; | 597 List<TypeName> nodeArguments = nodeArgumentList.arguments; |
| 452 _assertSameTypes(nodeArguments, typeArguments); | 598 _assertSameTypes(nodeArguments, typeArguments); |
| 453 } | 599 } |
| 454 } else if (type is TypeParameterType) { | 600 } else if (type is TypeParameterType) { |
| 455 _assertEquals(nodeName, type.name); | 601 _assertEquals(nodeName, type.name); |
| 456 // TODO(scheglov) it should be possible to rename type parameters | 602 // TODO(scheglov) it should be possible to rename type parameters |
| 603 } else if (type is VoidType) { | |
| 604 _assertEquals(nodeName, 'void'); | |
| 605 // TODO(scheglov) add test for "void" | |
| 457 } else { | 606 } else { |
| 458 // TODO(scheglov) support other types | 607 // TODO(scheglov) support other types |
| 608 // print('node: $node type: $type type.type: ${type.runtimeType}'); | |
| 459 _assertTrue(false); | 609 _assertTrue(false); |
| 460 } | 610 } |
| 461 } | 611 } |
| 462 | 612 |
| 463 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) { | 613 void _assertSameTypes(List<TypeName> nodes, List<DartType> types) { |
| 464 int length = nodes.length; | 614 int length = nodes.length; |
| 465 _assertEquals(length, types.length); | 615 _assertEquals(length, types.length); |
| 466 for (int i = 0; i < length; i++) { | 616 for (int i = 0; i < length; i++) { |
| 467 _assertSameType(nodes[i], types[i]); | 617 _assertSameType(nodes[i], types[i]); |
| 468 } | 618 } |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 541 if (element.displayName == name && element.nameOffset == offset) { | 691 if (element.displayName == name && element.nameOffset == offset) { |
| 542 return element; | 692 return element; |
| 543 } | 693 } |
| 544 } | 694 } |
| 545 return null; | 695 return null; |
| 546 } | 696 } |
| 547 | 697 |
| 548 void _gatherElements(Element element) { | 698 void _gatherElements(Element element) { |
| 549 _ElementsGatherer gatherer = new _ElementsGatherer(this); | 699 _ElementsGatherer gatherer = new _ElementsGatherer(this); |
| 550 element.accept(gatherer); | 700 element.accept(gatherer); |
| 551 // TODO(scheglov) push into CompilationUnitElement | 701 // TODO(scheglov) what if a change in a directive? |
| 552 if (identical(_enclosingUnit, _enclosingLibrary.definingCompilationUnit)) { | 702 if (identical(element, _enclosingLibrary.definingCompilationUnit)) { |
| 553 gatherer.addElements(_enclosingLibrary.imports); | 703 gatherer.addElements(_enclosingLibrary.imports); |
| 554 gatherer.addElements(_enclosingLibrary.exports); | 704 gatherer.addElements(_enclosingLibrary.exports); |
| 555 gatherer.addElements(_enclosingLibrary.parts); | 705 gatherer.addElements(_enclosingLibrary.parts); |
| 556 } | 706 } |
| 557 } | 707 } |
| 558 | 708 |
| 559 /** | 709 /** |
| 560 * Return the value of the given string literal, or `null` if the string is no t a constant | 710 * Return the value of the given string literal, or `null` if the string is no t a constant |
| 561 * string without any string interpolation. | 711 * string without any string interpolation. |
| 562 * | 712 * |
| (...skipping 480 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1043 visitFunctionExpression(FunctionExpression node) { | 1193 visitFunctionExpression(FunctionExpression node) { |
| 1044 _elements[node] = node.element; | 1194 _elements[node] = node.element; |
| 1045 super.visitFunctionExpression(node); | 1195 super.visitFunctionExpression(node); |
| 1046 } | 1196 } |
| 1047 | 1197 |
| 1048 @override | 1198 @override |
| 1049 visitSimpleIdentifier(SimpleIdentifier node) { | 1199 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1050 _elements[node] = node.staticElement; | 1200 _elements[node] = node.staticElement; |
| 1051 } | 1201 } |
| 1052 } | 1202 } |
| OLD | NEW |