Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1024)

Unified Diff: pkg/analyzer/lib/src/generated/incremental_resolver.dart

Issue 753803002: Use a poor man's incremental parser to perform incremental resolution. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fixes for review comments. Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer/lib/src/generated/incremental_resolver.dart
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index 2658a4578f36391bcc14ef71be72bd9063d29214..ef50f1546cd387bfb44e7d4b111f14bd72366f39 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -5,6 +5,7 @@
library engine.incremental_resolver;
import 'dart:collection';
+import 'dart:math' as math;
import 'ast.dart';
import 'element.dart';
@@ -13,6 +14,172 @@ import 'java_engine.dart';
import 'resolver.dart';
import 'scanner.dart';
import 'source.dart';
+import 'parser.dart';
+
+
+/**
+ * Attempts to update [oldUnit] to the state that would correspond to [newCode].
+ * Returns `true` if success, or `false` otherwise.
+ * The [oldUnit] might be damaged.
+ */
+bool poorMansIncrementalResolution(TypeProvider typeProvider,
+ CompilationUnit oldUnit, String newCode) {
+ try {
+ CompilationUnit newUnit = _parseUnit(newCode);
+ _TokenPair firstPair =
+ _findFirstDifferentToken(oldUnit.beginToken, newUnit.beginToken);
+ _TokenPair lastPair =
+ _findLastDifferentToken(oldUnit.endToken, newUnit.endToken);
+ if (firstPair != null && lastPair != null) {
+ // Prepare the "old" token range.
+ Token oldBeginToken;
+ Token oldEndToken;
+ if (firstPair.oldToken.offset < lastPair.oldToken.offset) {
+ oldBeginToken = firstPair.oldToken;
+ oldEndToken = lastPair.oldToken;
+ } else {
+ oldBeginToken = lastPair.oldToken;
+ oldEndToken = firstPair.oldToken;
+ }
+ // Prepare the "old" token tange.
+ Token newBeginToken;
+ Token newEndToken;
+ if (firstPair.newToken.offset < lastPair.newToken.offset) {
+ newBeginToken = firstPair.newToken;
+ newEndToken = lastPair.newToken;
+ } else {
+ newBeginToken = lastPair.newToken;
+ newEndToken = firstPair.newToken;
+ }
+ // Find nodes covering the "old" and "new" token ranges.
+ AstNode oldNode =
+ _findNodeWithTokens(oldUnit, oldBeginToken, oldEndToken);
+ AstNode newNode =
+ _findNodeWithTokens(newUnit, newBeginToken, newEndToken);
+ // Try to find the smallest common node, a FunctionBody currently.
+ {
+ List<AstNode> oldParents = _getParents(oldNode);
+ List<AstNode> newParents = _getParents(newNode);
+ int length = math.min(oldParents.length, newParents.length);
+ bool found = false;
+ for (int i = 0; i < length; i++) {
+ AstNode oldParent = oldParents[i];
+ AstNode newParent = newParents[i];
+ if (oldParent is FunctionBody && newParent is FunctionBody) {
+ oldNode = oldParent;
+ newNode = newParent;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+ // replace node
+ NodeReplacer.replace(oldNode, newNode);
+ // update token references
+ oldNode.beginToken.previous.setNext(newNode.beginToken);
+ oldNode.endToken.setNext(oldNode.endToken.next);
+ // perform incremental resolution
+ // TODO(scheglov) update errors
+ AnalysisErrorListener errorListener = new BooleanErrorListener();
+ CompilationUnitElement oldUnitElement = oldUnit.element;
+ IncrementalResolver incrementalResolver = new IncrementalResolver(
+ errorListener,
+ typeProvider,
+ oldUnitElement.library,
+ oldUnitElement,
+ oldUnitElement.source,
+ oldNode.offset,
+ oldNode.length,
+ newNode.length);
+ incrementalResolver.resolve(newNode);
+ return true;
+ }
+ } catch (e) {
+ // TODO(scheglov) find a way to log these exceptions
+ }
+ return false;
+}
+
+
+List<AstNode> _getParents(AstNode node) {
+ List<AstNode> parents = <AstNode>[];
+ while (node != null) {
+ parents.insert(0, node);
+ node = node.parent;
+ }
+ return parents;
+}
+
+AstNode _findNodeWithTokens(AstNode root, Token first, Token last) {
+ int offset = first.offset;
+ int end = last.end;
+ NodeLocator nodeLocator = new NodeLocator.con2(offset, end);
+ return nodeLocator.searchWithin(root);
+}
+
+
+class _TokenPair {
+ final Token oldToken;
+ final Token newToken;
+ _TokenPair(this.oldToken, this.newToken);
+}
+
+
+_TokenPair _findFirstDifferentToken(Token oldToken, Token newToken) {
+// print('first ------------');
+ while (oldToken.type != TokenType.EOF && newToken.type != TokenType.EOF) {
+// print('old: $oldToken @ ${oldToken.offset}');
+// print('new: $newToken @ ${newToken.offset}');
+ if (!_equalToken(oldToken, newToken, 0)) {
+ return new _TokenPair(oldToken, newToken);
+ }
+ oldToken = oldToken.next;
+ newToken = newToken.next;
+ }
+ return null;
+}
+
+
+_TokenPair _findLastDifferentToken(Token oldToken, Token newToken) {
+// print('last ------------');
+ int delta = newToken.offset - oldToken.offset;
+ while (oldToken.previous != oldToken && newToken.previous != newToken) {
+// print('old: $oldToken @ ${oldToken.offset}');
+// print('new: $newToken @ ${newToken.offset}');
+ if (!_equalToken(oldToken, newToken, delta)) {
+ return new _TokenPair(oldToken.next, newToken.next);
+ }
+ oldToken.offset += delta;
+ oldToken = oldToken.previous;
+ newToken = newToken.previous;
+ }
+ return null;
+}
+
+
+bool _equalToken(Token a, Token b, int delta) {
+ if (a.type != b.type) {
+ return false;
+ }
+ if (b.offset - a.offset != delta) {
+ return false;
+ }
+ return a.lexeme == b.lexeme;
+}
+
+
+CompilationUnit _parseUnit(String code) {
+ // TODO(scheglov) remember and update errors
+ var errorListener = new BooleanErrorListener();
+ var reader = new CharSequenceReader(code);
+ var scanner = new Scanner(null, reader, errorListener);
+ var token = scanner.tokenize();
+ var parser = new Parser(null, errorListener);
+ return parser.parseCompilationUnit(token);
+}
/**
@@ -390,7 +557,7 @@ class DeclarationMatcher extends RecursiveAstVisitor {
if (node is SimpleFormalParameter) {
_assertSameType(node.type, element.type);
node.identifier.staticElement = element;
- element.nameOffset = node.identifier.offset;
+ (element as ElementImpl).nameOffset = node.identifier.offset;
(element as ElementImpl).name = node.identifier.name;
} else {
// TODO(scheglov) support other parameter types
@@ -457,8 +624,12 @@ class DeclarationMatcher extends RecursiveAstVisitor {
} else if (type is TypeParameterType) {
_assertEquals(nodeName, type.name);
// TODO(scheglov) it should be possible to rename type parameters
+ } else if (type is VoidType) {
+ _assertEquals(nodeName, 'void');
+ // TODO(scheglov) add test for "void"
} else {
// TODO(scheglov) support other types
+// print('node: $node type: $type type.type: ${type.runtimeType}');
_assertTrue(false);
}
}
@@ -551,8 +722,8 @@ class DeclarationMatcher extends RecursiveAstVisitor {
void _gatherElements(Element element) {
_ElementsGatherer gatherer = new _ElementsGatherer(this);
element.accept(gatherer);
- // TODO(scheglov) push into CompilationUnitElement
- if (identical(_enclosingUnit, _enclosingLibrary.definingCompilationUnit)) {
+ // TODO(scheglov) what if a change in a directive?
+ if (identical(element, _enclosingLibrary.definingCompilationUnit)) {
gatherer.addElements(_enclosingLibrary.imports);
gatherer.addElements(_enclosingLibrary.exports);
gatherer.addElements(_enclosingLibrary.parts);
« no previous file with comments | « pkg/analyzer/lib/src/generated/engine.dart ('k') | pkg/analyzer/test/generated/incremental_resolver_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698