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

Unified Diff: mojo/public/dart/third_party/analyzer/lib/src/task/incremental_element_builder.dart

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 months 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: mojo/public/dart/third_party/analyzer/lib/src/task/incremental_element_builder.dart
diff --git a/mojo/public/dart/third_party/analyzer/lib/src/task/incremental_element_builder.dart b/mojo/public/dart/third_party/analyzer/lib/src/task/incremental_element_builder.dart
new file mode 100644
index 0000000000000000000000000000000000000000..bc562a73e6ce82fc813e4d09a504d3f920ced6ed
--- /dev/null
+++ b/mojo/public/dart/third_party/analyzer/lib/src/task/incremental_element_builder.dart
@@ -0,0 +1,366 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library analyzer.src.task.incremental_element_builder;
+
+import 'dart:collection';
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/**
+ * The change of a single [CompilationUnitElement].
+ */
+class CompilationUnitElementDelta {
+ /**
+ * One or more directives were added/removed.
+ */
+ bool hasDirectiveChange = false;
+
+ /**
+ * The list of added top-level element.
+ */
+ final List<Element> addedDeclarations = <Element>[];
+
+ /**
+ * The list of removed top-level elements.
+ */
+ final List<Element> removedDeclarations = <Element>[];
+}
+
+/**
+ * Incrementally updates the existing [unitElement] and builds elements for
+ * the [newUnit].
+ */
+class IncrementalCompilationUnitElementBuilder {
+ final Source unitSource;
+ final Source librarySource;
+ final CompilationUnit oldUnit;
+ final CompilationUnitElementImpl unitElement;
+ final CompilationUnit newUnit;
+ final ElementHolder holder = new ElementHolder();
+
+ /**
+ * The change between element models of [oldUnit] and [newUnit].
+ */
+ final CompilationUnitElementDelta unitDelta =
+ new CompilationUnitElementDelta();
+
+ factory IncrementalCompilationUnitElementBuilder(
+ CompilationUnit oldUnit, CompilationUnit newUnit) {
+ CompilationUnitElementImpl unitElement = oldUnit.element;
+ return new IncrementalCompilationUnitElementBuilder._(unitElement.source,
+ unitElement.librarySource, oldUnit, newUnit, unitElement);
+ }
+
+ IncrementalCompilationUnitElementBuilder._(this.unitSource,
+ this.librarySource, this.oldUnit, this.newUnit, this.unitElement);
+
+ /**
+ * Updates [oldUnit] to have the same directives and declarations, in the
+ * same order as in [newUnit]. Existing resolution is kept where possible.
+ *
+ * Updates [unitElement] by adding/removing elements as needed.
+ *
+ * Fills [unitDelta] with added/remove elements.
+ */
+ void build() {
+ new CompilationUnitBuilder().buildCompilationUnit(
+ unitSource, newUnit, librarySource);
+ _processDirectives();
+ _processUnitMembers();
+ newUnit.element = unitElement;
+ _replaceUnitContents(oldUnit, newUnit);
+ }
+
+ void _addElementToHolder(Element element) {
+ if (element is PropertyAccessorElement) {
+ holder.addAccessor(element);
+ } else if (element is ClassElement) {
+ if (element.isEnum) {
+ holder.addEnum(element);
+ } else {
+ holder.addType(element);
+ }
+ } else if (element is FunctionElement) {
+ holder.addFunction(element);
+ } else if (element is FunctionTypeAliasElement) {
+ holder.addTypeAlias(element);
+ } else if (element is TopLevelVariableElement) {
+ holder.addTopLevelVariable(element);
+ }
+ }
+
+ void _processDirectives() {
+ Map<String, Directive> oldDirectiveMap = new HashMap<String, Directive>();
+ for (Directive oldDirective in oldUnit.directives) {
+ String code = TokenUtils.getFullCode(oldDirective);
+ oldDirectiveMap[code] = oldDirective;
+ }
+ // Replace new nodes with the identical old nodes.
+ Set<Directive> removedDirectives = oldUnit.directives.toSet();
+ for (Directive newDirective in newUnit.directives) {
+ String code = TokenUtils.getFullCode(newDirective);
+ // Prepare an old directive.
+ Directive oldDirective = oldDirectiveMap[code];
+ if (oldDirective == null) {
+ unitDelta.hasDirectiveChange = true;
+ continue;
+ }
+ // URI's must be resolved to the same sources.
+ if (newDirective is UriBasedDirective &&
+ oldDirective is UriBasedDirective) {
+ if (oldDirective.source != newDirective.source) {
+ continue;
+ }
+ }
+ // Do replacement.
+ _replaceNode(newDirective, oldDirective);
+ removedDirectives.remove(oldDirective);
+ }
+ // If there are any directives left, then these directives were removed.
+ if (removedDirectives.isNotEmpty) {
+ unitDelta.hasDirectiveChange = true;
+ }
+ }
+
+ void _processUnitMembers() {
+ Map<String, CompilationUnitMember> oldNodeMap =
+ new HashMap<String, CompilationUnitMember>();
+ for (CompilationUnitMember oldNode in oldUnit.declarations) {
+ String code = TokenUtils.getFullCode(oldNode);
+ oldNodeMap[code] = oldNode;
+ }
+ // Prepare all old top-level elements.
+ Set<Element> removedElements = new Set<Element>();
+ removedElements.addAll(unitElement.accessors);
+ removedElements.addAll(unitElement.enums);
+ removedElements.addAll(unitElement.functions);
+ removedElements.addAll(unitElement.functionTypeAliases);
+ removedElements.addAll(unitElement.types);
+ removedElements.addAll(unitElement.topLevelVariables);
+ // Replace new nodes with the identical old nodes.
+ for (CompilationUnitMember newNode in newUnit.declarations) {
+ String code = TokenUtils.getFullCode(newNode);
+ // Prepare an old node.
+ CompilationUnitMember oldNode = oldNodeMap[code];
+ if (oldNode == null) {
+ List<Element> elements = _getElements(newNode);
+ elements.forEach(_addElementToHolder);
+ elements.forEach(unitDelta.addedDeclarations.add);
+ continue;
+ }
+ // Do replacement.
+ _replaceNode(newNode, oldNode);
+ List<Element> elements = _getElements(oldNode);
+ elements.forEach(_addElementToHolder);
+ elements.forEach(removedElements.remove);
+ }
+ unitDelta.removedDeclarations.addAll(removedElements);
+ // Update CompilationUnitElement.
+ unitElement.accessors = holder.accessors;
+ unitElement.enums = holder.enums;
+ unitElement.functions = holder.functions;
+ unitElement.typeAliases = holder.typeAliases;
+ unitElement.types = holder.types;
+ unitElement.topLevelVariables = holder.topLevelVariables;
+ holder.validate();
+ }
+
+ /**
+ * Replaces [newNode] with [oldNode], updates tokens and elements.
+ * The nodes must have the same tokens, but offsets may be different.
+ */
+ void _replaceNode(AstNode newNode, AstNode oldNode) {
+ // Replace node.
+ NodeReplacer.replace(newNode, oldNode);
+ // Replace tokens.
+ {
+ Token oldBeginToken = TokenUtils.getBeginTokenNotComment(newNode);
+ Token newBeginToken = TokenUtils.getBeginTokenNotComment(oldNode);
+ oldBeginToken.previous.setNext(newBeginToken);
+ oldNode.endToken.setNext(newNode.endToken.next);
+ }
+ // Change tokens offsets.
+ Map<int, int> offsetMap = new HashMap<int, int>();
+ TokenUtils.copyTokenOffsets(offsetMap, oldNode.beginToken,
+ newNode.beginToken, oldNode.endToken, newNode.endToken, true);
+ // Change elements offsets.
+ {
+ var visitor = new _UpdateElementOffsetsVisitor(offsetMap);
+ List<Element> elements = _getElements(oldNode);
+ for (Element element in elements) {
+ element.accept(visitor);
+ }
+ }
+ }
+
+ /**
+ * Returns [Element]s that are declared directly by the given [node].
+ * This does not include any child elements - parameters, local variables.
+ *
+ * Usually just one [Element] is returned, but [VariableDeclarationList]
+ * nodes may declare more than one.
+ */
+ static List<Element> _getElements(AstNode node) {
+ List<Element> elements = <Element>[];
+ if (node is TopLevelVariableDeclaration) {
+ VariableDeclarationList variableList = node.variables;
+ if (variableList != null) {
+ for (VariableDeclaration variable in variableList.variables) {
+ TopLevelVariableElement element = variable.element;
+ elements.add(element);
+ if (element.getter != null) {
+ elements.add(element.getter);
+ }
+ if (element.setter != null) {
+ elements.add(element.setter);
+ }
+ }
+ }
+ } else if (node is PartDirective || node is PartOfDirective) {
+ } else if (node is Directive && node.element != null) {
+ elements.add(node.element);
+ } else if (node is Declaration && node.element != null) {
+ Element element = node.element;
+ elements.add(element);
+ if (element is PropertyAccessorElement) {
+ elements.add(element.variable);
+ }
+ }
+ return elements;
+ }
+
+ /**
+ * Replaces contents of the [to] unit with the contenxts of the [from] unit.
+ */
+ static void _replaceUnitContents(CompilationUnit to, CompilationUnit from) {
+ to.directives.clear();
+ to.declarations.clear();
+ to.beginToken = from.beginToken;
+ to.scriptTag = from.scriptTag;
+ to.directives.addAll(from.directives);
+ to.declarations.addAll(from.declarations);
+ to.element = to.element;
+ to.lineInfo = from.lineInfo;
+ }
+}
+
+/**
+ * Utilities for [Token] manipulations.
+ */
+class TokenUtils {
+ static const String _SEPARATOR = "\uFFFF";
+
+ /**
+ * Copy offsets from [newToken]s to [oldToken]s.
+ */
+ static void copyTokenOffsets(Map<int, int> offsetMap, Token oldToken,
+ Token newToken, Token oldEndToken, Token newEndToken,
+ [bool goUpComment = false]) {
+ if (oldToken is CommentToken && newToken is CommentToken) {
+ if (goUpComment) {
+ copyTokenOffsets(offsetMap, (oldToken as CommentToken).parent,
+ (newToken as CommentToken).parent, oldEndToken, newEndToken);
+ }
+ while (oldToken != null) {
+ offsetMap[oldToken.offset] = newToken.offset;
+ oldToken.offset = newToken.offset;
+ oldToken = oldToken.next;
+ newToken = newToken.next;
+ }
+ assert(oldToken == null);
+ assert(newToken == null);
+ return;
+ }
+ while (true) {
+ if (oldToken.precedingComments != null) {
+ assert(newToken.precedingComments != null);
+ copyTokenOffsets(offsetMap, oldToken.precedingComments,
+ newToken.precedingComments, oldEndToken, newEndToken);
+ }
+ offsetMap[oldToken.offset] = newToken.offset;
+ oldToken.offset = newToken.offset;
+ if (oldToken == oldEndToken) {
+ assert(newToken == newEndToken);
+ break;
+ }
+ oldToken = oldToken.next;
+ newToken = newToken.next;
+ }
+ }
+
+ static Token getBeginTokenNotComment(AstNode node) {
+ Token oldBeginToken = node.beginToken;
+ if (oldBeginToken is CommentToken) {
+ oldBeginToken = (oldBeginToken as CommentToken).parent;
+ }
+ return oldBeginToken;
+ }
+
+ /**
+ * Return the token string of all the [node] tokens.
+ */
+ static String getFullCode(AstNode node) {
+ List<Token> tokens = getTokens(node);
+ return joinTokens(tokens);
+ }
+
+ /**
+ * Returns all tokends (including comments) of the given [node].
+ */
+ static List<Token> getTokens(AstNode node) {
+ List<Token> tokens = <Token>[];
+ Token token = getBeginTokenNotComment(node);
+ Token endToken = node.endToken;
+ while (true) {
+ // append comment tokens
+ for (Token commentToken = token.precedingComments;
+ commentToken != null;
+ commentToken = commentToken.next) {
+ tokens.add(commentToken);
+ }
+ // append token
+ tokens.add(token);
+ // next token
+ if (token == endToken) {
+ break;
+ }
+ token = token.next;
+ }
+ return tokens;
+ }
+
+ static String joinTokens(List<Token> tokens) {
+ return tokens.map((token) => token.lexeme).join(_SEPARATOR);
+ }
+}
+
+/**
+ * Updates name offsets of [Element]s according to the [map].
+ */
+class _UpdateElementOffsetsVisitor extends GeneralizingElementVisitor {
+ final Map<int, int> map;
+
+ _UpdateElementOffsetsVisitor(this.map);
+
+ void visitElement(Element element) {
+ if (element is CompilationUnitElement) {
+ return;
+ }
+ if (element.isSynthetic) {
+ return;
+ }
+ int oldOffset = element.nameOffset;
+ int newOffset = map[oldOffset];
+ assert(newOffset != null);
+ (element as ElementImpl).nameOffset = newOffset;
+ if (element is! LibraryElement) {
+ super.visitElement(element);
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698