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

Unified Diff: pkg/analyzer/lib/src/summary/index_unit.dart

Issue 1735243003: Initial package indexing implementation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 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: pkg/analyzer/lib/src/summary/index_unit.dart
diff --git a/pkg/analyzer/lib/src/summary/index_unit.dart b/pkg/analyzer/lib/src/summary/index_unit.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2e842b6de50bb5dd37a90101d783988c3f779de3
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary/index_unit.dart
@@ -0,0 +1,622 @@
+// Copyright (c) 2016, 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.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/ast/visitor.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:analyzer/src/summary/format.dart';
+import 'package:analyzer/src/summary/idl.dart';
+
+/**
+ * Object that gathers information about the whole package index and then uses
+ * it to assemble a new [PackageIndexBuilder].
Paul Berry 2016/02/25 23:05:13 It would be helpful to have a comment here explain
scheglov 2016/02/26 04:54:03 Done.
+ */
+class PackageIndexAssembler {
+ final Map<Element, _ElementInfo> _elementMap = <Element, _ElementInfo>{};
+
+ final Map<CompilationUnitElement, int> _elementUnitMap =
Paul Berry 2016/02/25 23:05:12 Please add doc comments for all these fields. I'v
scheglov 2016/02/26 04:54:02 Done.
+ <CompilationUnitElement, int>{};
+ final List<int> _elementLibraryUris = <int>[];
+ final List<int> _elementUnitUris = <int>[];
+
+ final Map<String, int> _uriMap = <String, int>{};
+ final List<String> _uris = <String>[];
+
+ final List<_UnitIndexAssembler> _units = <_UnitIndexAssembler>[];
+
+ PackageIndexAssembler();
+
+ /**
+ * Assemble a new [PackageIndexBuilder] using the gathered information.
Paul Berry 2016/02/25 23:05:12 s/using the gathered information/using the informa
scheglov 2016/02/26 04:54:02 Done.
+ */
+ PackageIndexBuilder assemble() {
+ List<_ElementInfo> elements = _elementMap.values.toList();
Paul Berry 2016/02/25 23:05:12 Suggestion: rename this to "elementInfos" to avoid
scheglov 2016/02/26 04:54:02 Done.
+ elements.sort((a, b) {
+ return a.offset - b.offset;
+ });
+ for (int i = 0; i < elements.length; i++) {
+ elements[i].id = i;
+ }
+ return new PackageIndexBuilder(
+ elementLibraryUris: _elementLibraryUris,
+ elementUnitUris: _elementUnitUris,
+ elementUnits: elements.map((e) => e.unitId).toList(),
+ elementOffsets: elements.map((e) => e.offset).toList(),
+ uris: _uris,
+ units: _units.map((unit) => unit.assemble()).toList());
+ }
+
+ /**
+ * Index the given fully resolved [unit].
+ */
+ void index(CompilationUnit unit) {
+ CompilationUnitElement unitElement = unit.element;
+ _UnitIndexAssembler assembler = new _UnitIndexAssembler(this, unitElement);
+ _units.add(assembler);
+ unit.accept(new _IndexContributor(assembler));
+ }
+
+ _ElementInfo _getElementInfo(Element element) {
+ return _elementMap.putIfAbsent(element, () {
+ CompilationUnitElement unitElement = getUnitElement(element);
+ int unitId = _getUnitElementId(unitElement);
+ return new _ElementInfo(element, unitId);
+ });
+ }
+
+ int _getUnitElementId(CompilationUnitElement element) {
Paul Berry 2016/02/25 23:05:12 Documentation on these private methods would be he
scheglov 2016/02/26 04:54:03 Done.
+ return _elementUnitMap.putIfAbsent(element, () {
+ assert(_elementLibraryUris.length == _elementUnitUris.length);
+ int id = _elementUnitUris.length;
+ _elementLibraryUris.add(_getUriId(element.library.source.uri));
+ _elementUnitUris.add(_getUriId(element.source.uri));
+ return id;
+ });
+ }
+
+ int _getUriId(Uri uri) {
+ String str = uri.toString();
+ return _uriMap.putIfAbsent(str, () {
+ int id = _uris.length;
+ _uris.add(str);
+ return id;
+ });
+ }
+
+ /**
+ * Return the [CompilationUnitElement] that should be used for [element].
+ */
+ static CompilationUnitElement getUnitElement(Element element) {
Paul Berry 2016/02/25 23:05:13 It looks like this is only used by other code in t
scheglov 2016/02/26 04:54:03 It is used in tests. And it also will be used in i
+ CompilationUnitElement unitElement;
+ for (Element e = element; e != null; e = e.enclosingElement) {
+ if (e is CompilationUnitElement) {
+ unitElement = e;
+ break;
+ }
+ if (e is LibraryElement) {
+ unitElement = e.definingCompilationUnit;
+ break;
+ }
+ }
+ assert(unitElement != null);
+ return unitElement;
+ }
+}
+
+/**
+ * Information about an element referenced in index.
+ */
+class _ElementInfo {
+ final Element element;
Paul Berry 2016/02/25 23:05:13 It looks like this is only used by the [offset] ge
scheglov 2016/02/26 04:54:03 Done.
+
+ /**
+ * The identifier of the [CompilationUnitElement] containing [element].
+ */
+ final int unitId;
+
+ /**
+ * The unique id of the [element] in the [PackageIndex]. It is set after
+ * after indexing of the whole package is done and we are assembling the
+ * full package index.
+ */
+ int id;
+
+ _ElementInfo(this.element, this.unitId);
+
+ int get offset => element.nameOffset;
+}
+
+/**
+ * Visits a resolved AST and adds relationships into [InternalIndexStore].
+ */
+class _IndexContributor extends GeneralizingAstVisitor {
+ final _UnitIndexAssembler assembler;
+
+ _IndexContributor(this.assembler);
+
+ /**
+ * Record that [element] has a relation of the given [kind] at the location
+ * of the given [node].
+ */
+ void recordRelation(Element element, IndexRelationKind kind, AstNode node) {
+ if (element != null && node != null) {
+ recordRelationOffset(element, kind, node.offset, node.length);
+ }
+ }
+
+ /**
+ * Record that [element] has a relation of the given [kind] at the given
+ * [offset] and [length].
+ */
+ void recordRelationOffset(
+ Element element, IndexRelationKind kind, int offset, int length) {
+ // Ignore elements that can't be referenced outside of the unit.
+ if (element == null ||
+ element is LocalVariableElement ||
+ element is ParameterElement &&
+ element.parameterKind != ParameterKind.NAMED ||
+ element is FunctionElement &&
+ element.enclosingElement is ExecutableElement) {
+ return;
+ }
+ // Add the relation.
+ assembler.addRelation(element, kind, offset, length);
+ }
+
+ /**
+ * Record that [element] has a relation of the given [kind] at the location
+ * of the given [token].
+ */
+ void recordRelationToken(
+ Element element, IndexRelationKind kind, Token token) {
+ if (element != null && token != null) {
+ recordRelationOffset(element, kind, token.offset, token.length);
+ }
+ }
+
+ @override
+ visitAssignmentExpression(AssignmentExpression node) {
+ _recordOperatorReference(node.operator, node.bestElement);
+ super.visitAssignmentExpression(node);
+ }
+
+ @override
+ visitBinaryExpression(BinaryExpression node) {
+ _recordOperatorReference(node.operator, node.bestElement);
+ super.visitBinaryExpression(node);
+ }
+
+ @override
+ visitClassDeclaration(ClassDeclaration node) {
+ ClassElement element = node.element;
+ _recordTopLevelElementDefinition(element);
+ {
+ ExtendsClause extendsClause = node.extendsClause;
+ if (extendsClause != null) {
+ TypeName superclassNode = extendsClause.superclass;
+ _recordSuperType(superclassNode, IndexRelationKind.IS_EXTENDED_BY);
+ } else {
+ InterfaceType superType = element.supertype;
+ if (superType != null) {
+ ClassElement objectElement = superType.element;
+ recordRelationOffset(objectElement, IndexRelationKind.IS_EXTENDED_BY,
+ node.name.offset, 0);
+ }
+ }
+ }
+ {
+ WithClause withClause = node.withClause;
+ if (withClause != null) {
+ for (TypeName mixinNode in withClause.mixinTypes) {
+ _recordSuperType(mixinNode, IndexRelationKind.IS_MIXED_IN_BY);
+ }
+ }
+ }
+ {
+ ImplementsClause implementsClause = node.implementsClause;
+ if (implementsClause != null) {
+ for (TypeName interfaceNode in implementsClause.interfaces) {
+ _recordSuperType(interfaceNode, IndexRelationKind.IS_IMPLEMENTED_BY);
+ }
+ }
+ }
+ super.visitClassDeclaration(node);
+ }
+
+ @override
+ visitClassTypeAlias(ClassTypeAlias node) {
+ ClassElement element = node.element;
Paul Berry 2016/02/25 23:05:12 There's a lot of common code between this method a
scheglov 2016/02/26 04:54:02 Done.
+ _recordTopLevelElementDefinition(element);
+ {
+ TypeName superclassNode = node.superclass;
+ if (superclassNode != null) {
+ _recordSuperType(superclassNode, IndexRelationKind.IS_EXTENDED_BY);
+ }
+ }
+ {
+ WithClause withClause = node.withClause;
+ if (withClause != null) {
+ for (TypeName mixinNode in withClause.mixinTypes) {
+ _recordSuperType(mixinNode, IndexRelationKind.IS_MIXED_IN_BY);
+ }
+ }
+ }
+ {
+ ImplementsClause implementsClause = node.implementsClause;
+ if (implementsClause != null) {
+ for (TypeName interfaceNode in implementsClause.interfaces) {
+ _recordSuperType(interfaceNode, IndexRelationKind.IS_IMPLEMENTED_BY);
+ }
+ }
+ }
+ super.visitClassTypeAlias(node);
+ }
+
+ @override
+ visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+ SimpleIdentifier fieldName = node.fieldName;
+ Expression expression = node.expression;
+ // field reference is write here
Paul Berry 2016/02/25 23:05:12 I don't understand this comment. Do you mean "rig
scheglov 2016/02/26 04:54:03 Initially the code was using IndexRelationKind.IS_
+ if (fieldName != null) {
+ Element element = fieldName.staticElement;
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, fieldName);
+ }
+ // index expression
+ if (expression != null) {
+ expression.accept(this);
+ }
+ }
+
+ @override
+ visitConstructorName(ConstructorName node) {
+ ConstructorElement element = node.staticElement;
+ // in 'class B = A;' actually A constructors are invoked
+ if (element != null &&
+ element.isSynthetic &&
+ element.redirectedConstructor != null) {
+ element = element.redirectedConstructor;
Paul Berry 2016/02/25 23:05:13 What if there are multiple levels of indirection?
scheglov 2016/02/26 04:54:03 I will add TODO and test/fix it in the next CL.
+ }
+ // record relation
+ if (node.name != null) {
+ int offset = node.period.offset;
+ int length = node.name.end - offset;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
+ } else {
+ int offset = node.type.end;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
+ }
+ super.visitConstructorName(node);
+ }
+
+ @override
+ visitEnumDeclaration(EnumDeclaration node) {
+ ClassElement element = node.element;
+ _recordTopLevelElementDefinition(element);
+ super.visitEnumDeclaration(node);
+ }
+
+ @override
+ visitExportDirective(ExportDirective node) {
+ ExportElement element = node.element;
+ if (element != null) {
+ LibraryElement expLibrary = element.exportedLibrary;
+ _recordLibraryReference(node, expLibrary);
+ }
+ _recordUriFileReference(node);
+ super.visitExportDirective(node);
+ }
+
+ @override
+ visitFunctionDeclaration(FunctionDeclaration node) {
+ Element element = node.element;
+ _recordTopLevelElementDefinition(element);
+ super.visitFunctionDeclaration(node);
+ }
+
+ @override
+ visitFunctionTypeAlias(FunctionTypeAlias node) {
+ Element element = node.element;
+ _recordTopLevelElementDefinition(element);
+ super.visitFunctionTypeAlias(node);
+ }
+
+ @override
+ visitImportDirective(ImportDirective node) {
+ ImportElement element = node.element;
+ if (element != null) {
+ LibraryElement impLibrary = element.importedLibrary;
+ _recordLibraryReference(node, impLibrary);
+ }
+ _recordUriFileReference(node);
+ super.visitImportDirective(node);
+ }
+
+ @override
+ visitIndexExpression(IndexExpression node) {
+ MethodElement element = node.bestElement;
+ if (element is MethodElement) {
+ Token operator = node.leftBracket;
+ recordRelationToken(element, IndexRelationKind.IS_INVOKED_BY, operator);
+ }
+ super.visitIndexExpression(node);
+ }
+
+ @override
+ visitMethodInvocation(MethodInvocation node) {
+ SimpleIdentifier name = node.methodName;
+ // TODO(scheglov) do we need this?
+// LocationImpl location = _createLocationForNode(name);
+// // name invocation
+// recordRelationshipIndexable(
+// new IndexableName(name.name), IndexConstants.IS_INVOKED_BY, location);
+ // element invocation
+ Element element = name.bestElement;
+ if (element is MethodElement ||
+ element is PropertyAccessorElement ||
+ element is FunctionElement ||
+ element is VariableElement) {
+ recordRelation(element, IndexRelationKind.IS_INVOKED_BY, node);
+ } else if (element is ClassElement) {
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, node);
+ }
+ node.target?.accept(this);
+ node.argumentList?.accept(this);
+ }
+
+ @override
+ visitPartDirective(PartDirective node) {
+ recordRelation(node.element, IndexRelationKind.IS_REFERENCED_BY, node);
+ _recordUriFileReference(node);
+ super.visitPartDirective(node);
+ }
+
+ @override
+ visitPartOfDirective(PartOfDirective node) {
+ recordRelation(node.element, IndexRelationKind.IS_REFERENCED_BY, node);
+ }
+
+ @override
+ visitPostfixExpression(PostfixExpression node) {
+ _recordOperatorReference(node.operator, node.bestElement);
+ super.visitPostfixExpression(node);
+ }
+
+ @override
+ visitPrefixExpression(PrefixExpression node) {
+ _recordOperatorReference(node.operator, node.bestElement);
+ super.visitPrefixExpression(node);
+ }
+
+ @override
+ visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
+ ConstructorElement element = node.staticElement;
+ if (node.constructorName != null) {
+ int offset = node.period.offset;
+ int length = node.constructorName.end - offset;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
+ } else {
+ int offset = node.thisKeyword.end;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
+ }
+ super.visitRedirectingConstructorInvocation(node);
+ }
+
+ @override
+ visitSimpleIdentifier(SimpleIdentifier node) {
+ // TODO(scheglov) do we need this?
+// IndexableName indexableName = new IndexableName(node.name);
+// LocationImpl location = _createLocationForNode(node);
+// if (location == null) {
+// return;
+// }
+ // name in declaration
+ if (node.inDeclarationContext()) {
+ // TODO(scheglov) do we need this?
+// recordRelationshipIndexable(
+// indexableName, IndexConstants.NAME_IS_DEFINED_BY, location);
+ return;
+ }
+ // name in an extends/with/implements clause
+ if (_isInExtendsWithImplementsClause(node)) {
+ return;
+ }
+ Element element = node.bestElement;
+ // this.field parameter
+ if (element is FieldFormalParameterElement) {
+ recordRelation(element.field, IndexRelationKind.IS_REFERENCED_BY, node);
+ return;
+ }
+ // record specific relations
+ if (element is ClassElement ||
+ element is FunctionElement ||
+ element is FunctionTypeAliasElement ||
+ element is LabelElement ||
+ element is MethodElement ||
+ element is PropertyAccessorElement ||
+ element is PropertyInducingElement ||
+ element is TypeParameterElement) {
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, node);
+ } else if (element is PrefixElement) {
Paul Berry 2016/02/25 23:05:13 This clause can be combined with the "if" clause a
scheglov 2016/02/26 04:54:03 Done.
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, node);
+ }
+ super.visitSimpleIdentifier(node);
+ }
+
+ @override
+ visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+ ConstructorElement element = node.staticElement;
+ if (node.constructorName != null) {
+ int offset = node.period.offset;
+ int length = node.constructorName.end - offset;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, length);
+ } else {
+ int offset = node.superKeyword.end;
+ recordRelationOffset(
+ element, IndexRelationKind.IS_REFERENCED_BY, offset, 0);
+ }
+ super.visitSuperConstructorInvocation(node);
+ }
+
+ @override
+ visitVariableDeclaration(VariableDeclaration node) {
+ VariableElement element = node.element;
+ _recordTopLevelElementDefinition(element);
+ // TODO(scheglov) do we need this?
+// // record declaration
+// {
+// SimpleIdentifier name = node.name;
+// LocationImpl location = _createLocationForNode(name);
+// location = _getLocationWithExpressionType(location, node.initializer);
+// recordRelationshipElement(
+// element, IndexConstants.NAME_IS_DEFINED_BY, location);
+// }
+ super.visitVariableDeclaration(node);
+ }
+
+ /**
+ * Records reference to defining [CompilationUnitElement] of the given
+ * [LibraryElement].
+ */
+ void _recordLibraryReference(UriBasedDirective node, LibraryElement library) {
Paul Berry 2016/02/25 23:05:13 All methods in this class are inherently private s
scheglov 2016/02/26 04:54:03 Done.
+ recordRelation(library, IndexRelationKind.IS_REFERENCED_BY, node?.uri);
+ }
+
+ /**
+ * Record reference to the given operator [Element] and name.
+ */
+ void _recordOperatorReference(Token operator, Element element) {
+ recordRelationToken(element, IndexRelationKind.IS_INVOKED_BY, operator);
+ // TODO(scheglov) do we need this?
+// // prepare location
+// LocationImpl location = _createLocationForToken(operator, element != null);
+// // record name reference
+// {
+// String name = operator.lexeme;
+// if (name == "++") {
+// name = "+";
+// }
+// if (name == "--") {
+// name = "-";
+// }
+// if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") {
+// name = name.substring(0, name.length - 1);
+// }
+// IndexableName indexableName = new IndexableName(name);
+// recordRelationshipIndexable(
+// indexableName, IndexConstants.IS_INVOKED_BY, location);
+// }
+// // record element reference
+// if (element != null) {
+// recordRelationshipElement(
+// element, IndexConstants.IS_INVOKED_BY, location);
+// }
+ }
+
+ /**
+ * Records a relation between [superNode] and its [Element].
+ */
+ void _recordSuperType(TypeName superNode, IndexRelationKind kind) {
+ if (superNode != null) {
+ Identifier superName = superNode.name;
+ if (superName != null) {
+ Element superElement = superName.staticElement;
+ recordRelation(superElement, kind, superName);
+ }
+ }
+ }
+
+ /**
+ * Record the top-level [element] definition.
+ */
+ void _recordTopLevelElementDefinition(Element element) {
+ // TODO(scheglov) do we need this?
+// if (element?.enclosingElement is CompilationUnitElement) {
+// IndexableElement indexable = new IndexableElement(element);
+// int offset = element.nameOffset;
+// int length = element.nameLength;
+// LocationImpl location = new LocationImpl(indexable, offset, length);
+// recordRelationshipElement(
+// _libraryElement, IndexConstants.DEFINES, location);
+// _store.recordTopLevelDeclaration(element);
+// }
+ }
+
+ void _recordUriFileReference(UriBasedDirective directive) {
+ Element element = directive.element;
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, directive.uri);
+ }
+
+ static bool _isInExtendsWithImplementsClause(SimpleIdentifier node) {
+ TypeName typeName;
+ AstNode parent = node?.parent;
+ AstNode parent2 = parent?.parent;
+ if (parent is TypeName && parent.name == node) {
+ typeName = parent;
+ } else if (parent is PrefixedIdentifier &&
+ parent.identifier == node &&
+ parent2 is TypeName &&
+ parent2.name == node) {
+ typeName = parent2;
+ } else {
+ return false;
+ }
+ AstNode clause = typeName.parent;
+ return clause is ExtendsClause ||
+ clause is WithClause ||
+ clause is ImplementsClause;
+ }
+}
+
+/**
+ * Information about a single relation.
Paul Berry 2016/02/25 23:05:12 Add a comment explaining that [_RelationInfo] alwa
scheglov 2016/02/26 04:54:03 Done.
+ */
+class _RelationInfo {
+ final _ElementInfo element;
Paul Berry 2016/02/25 23:05:13 Rename to "elementInfo". Otherwise it's easy to b
scheglov 2016/02/26 04:54:03 Done.
+ final IndexRelationKind kind;
+ final int offset;
+ final int length;
+
+ _RelationInfo(this.element, this.kind, this.offset, this.length);
+}
+
+/**
+ * Assembler of a single [CompilationUnit] index.
+ */
Paul Berry 2016/02/25 23:05:13 As with [PackageIndexAssembler], it would be helpf
scheglov 2016/02/26 04:54:03 Done.
+class _UnitIndexAssembler {
+ final PackageIndexAssembler pkg;
+ final CompilationUnitElement unitElement;
+ final List<_RelationInfo> relations = <_RelationInfo>[];
+
+ _UnitIndexAssembler(this.pkg, this.unitElement);
+
+ void addRelation(
+ Element element, IndexRelationKind kind, int offset, int length) {
+ _ElementInfo elementInfo = pkg._getElementInfo(element);
+ relations.add(new _RelationInfo(elementInfo, kind, offset, length));
+ }
+
+ /**
+ * Assemble a new [UnitIndexBuilder] using the gathered information.
Paul Berry 2016/02/25 23:05:12 s/using the gathered information/using the informa
scheglov 2016/02/26 04:54:03 Done.
+ */
+ UnitIndexBuilder assemble() {
+ relations.sort((a, b) {
+ return a.element.id - b.element.id;
+ });
+ return new UnitIndexBuilder(
+ elements: relations.map((r) => r.element.id).toList(),
+ kinds: relations.map((r) => r.kind).toList(),
+ locationOffsets: relations.map((r) => r.offset).toList(),
+ locationLengths: relations.map((r) => r.length).toList(),
+ libraryUri: pkg._getUriId(unitElement.library.source.uri),
+ unitUri: pkg._getUriId(unitElement.source.uri));
+ }
+}
« no previous file with comments | « no previous file | pkg/analyzer/test/src/abstract_single_unit.dart » ('j') | pkg/analyzer/test/src/summary/index_unit_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698