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

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: Fixes for review comments. 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
« no previous file with comments | « no previous file | pkg/analyzer/test/src/abstract_single_unit.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..446094508c4dd5b1bfb1ca7a8248a0f3d4caa980
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary/index_unit.dart
@@ -0,0 +1,649 @@
+// 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]. Call [index] on each compilation
+ * unit to be indexed, then call [assemble] to retrieve the complete index for
+ * the package.
+ */
+class PackageIndexAssembler {
+ /**
+ * Map associating referenced elements with their [_ElementInfo]s.
+ */
+ final Map<Element, _ElementInfo> _elementMap = <Element, _ElementInfo>{};
+
+ /**
+ * Map associating [CompilationUnitElement]s with their identifiers, which
+ * are indices into [_elementLibraryUris] and [_elementUnitUris].
+ */
+ final Map<CompilationUnitElement, int> _elementUnitMap =
+ <CompilationUnitElement, int>{};
+
+ /**
+ * Each item of this list corresponds to the library URI of a unique
+ * [CompilationUnitElement]. It is an index into [_uris].
+ */
+ final List<int> _elementLibraryUris = <int>[];
+
+ /**
+ * Each item of this list corresponds to the unit URI of a unique
+ * [CompilationUnitElement]. It is an index into [_uris].
+ */
+ final List<int> _elementUnitUris = <int>[];
+
+ /**
+ * Map associating URIs with their identifiers, which are indices
+ * into [_uris].
+ */
+ final Map<String, int> _uriMap = <String, int>{};
+
+ /**
+ * List of unique URIs used in this index.
+ */
+ final List<String> _uris = <String>[];
+
+ /**
+ * List of information about each unit indexed in this index.
+ */
+ final List<_UnitIndexAssembler> _units = <_UnitIndexAssembler>[];
+
+ /**
+ * Assemble a new [PackageIndexBuilder] using the information gathered by
+ * [index].
+ */
+ PackageIndexBuilder assemble() {
+ List<_ElementInfo> elementInfoList = _elementMap.values.toList();
+ elementInfoList.sort((a, b) {
+ return a.offset - b.offset;
+ });
+ for (int i = 0; i < elementInfoList.length; i++) {
+ elementInfoList[i].id = i;
+ }
+ return new PackageIndexBuilder(
+ elementLibraryUris: _elementLibraryUris,
+ elementUnitUris: _elementUnitUris,
+ elementUnits: elementInfoList.map((e) => e.unitId).toList(),
+ elementOffsets: elementInfoList.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));
+ }
+
+ /**
+ * Return the unique [_ElementInfo] corresponding the [element]. The field
+ * [_ElementInfo.id] is filled by [assemble] during final sorting.
+ */
+ _ElementInfo _getElementInfo(Element element) {
+ return _elementMap.putIfAbsent(element, () {
+ CompilationUnitElement unitElement = getUnitElement(element);
+ int unitId = _getUnitElementId(unitElement);
+ return new _ElementInfo(unitId, element.nameOffset);
+ });
+ }
+
+ /**
+ * Add information about [unitElement] to [_elementUnitUris] and
+ * [_elementLibraryUris] if necessary, and return the location in those
+ * arrays representing [unitElement].
+ */
+ int _getUnitElementId(CompilationUnitElement unitElement) {
+ return _elementUnitMap.putIfAbsent(unitElement, () {
+ assert(_elementLibraryUris.length == _elementUnitUris.length);
+ int id = _elementUnitUris.length;
+ _elementLibraryUris.add(_getUriId(unitElement.library.source.uri));
+ _elementUnitUris.add(_getUriId(unitElement.source.uri));
+ return id;
+ });
+ }
+
+ /**
+ * Add information about [uri] to [_uris] if necessary, and return the
+ * location in this array representing [uri].
+ */
+ 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].
+ * Throw [StateError] if the [element] is not linked into a unit.
+ */
+ static CompilationUnitElement getUnitElement(Element element) {
+ for (Element e = element; e != null; e = e.enclosingElement) {
+ if (e is CompilationUnitElement) {
+ return e;
+ }
+ if (e is LibraryElement) {
+ return e.definingCompilationUnit;
+ }
+ }
+ throw new StateError(element.toString());
+ }
+}
+
+/**
+ * Information about an element referenced in index.
+ */
+class _ElementInfo {
+ /**
+ * The identifier of the [CompilationUnitElement] containing this element.
+ */
+ final int unitId;
+
+ /**
+ * The name offset of the element.
+ */
+ final int offset;
+
+ /**
+ * The unique id of the element. It is set after indexing of the whole
+ * package is done and we are assembling the full package index.
+ */
+ int id;
+
+ _ElementInfo(this.unitId, this.offset);
+}
+
+/**
+ * Visits a resolved AST and adds relationships into [_UnitIndexAssembler].
+ */
+class _IndexContributor extends GeneralizingAstVisitor {
+ final _UnitIndexAssembler assembler;
+
+ _IndexContributor(this.assembler);
+
+ /**
+ * Record information about a [ClassDeclaration] or [ClassTypeAlias] with
+ * the given [nameNode]. Nodes [superNode], [withClause] and
+ * [implementsClause] can be `null`.
+ */
+ void recordClassClauses(SimpleIdentifier nameNode, TypeName superNode,
+ WithClause withClause, ImplementsClause implementsClause) {
+ if (superNode != null) {
+ recordSuperType(superNode, IndexRelationKind.IS_EXTENDED_BY);
+ } else {
+ ClassElement element = nameNode.staticElement;
+ InterfaceType superType = element.supertype;
+ if (superType != null) {
+ ClassElement objectElement = superType.element;
+ recordRelationOffset(objectElement, IndexRelationKind.IS_EXTENDED_BY,
+ nameNode.offset, 0);
+ }
+ }
+ if (withClause != null) {
+ for (TypeName mixinNode in withClause.mixinTypes) {
+ recordSuperType(mixinNode, IndexRelationKind.IS_MIXED_IN_BY);
+ }
+ }
+ if (implementsClause != null) {
+ for (TypeName interfaceNode in implementsClause.interfaces) {
+ recordSuperType(interfaceNode, IndexRelationKind.IS_IMPLEMENTED_BY);
+ }
+ }
+ }
+
+ /**
+ * Records reference to defining [CompilationUnitElement] of the given
+ * [LibraryElement].
+ */
+ void recordLibraryReference(UriBasedDirective node, LibraryElement library) {
+ 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);
+// }
+ }
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ @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);
+ recordClassClauses(node.name, node.extendsClause?.superclass,
+ node.withClause, node.implementsClause);
+ super.visitClassDeclaration(node);
+ }
+
+ @override
+ visitClassTypeAlias(ClassTypeAlias node) {
+ ClassElement element = node.element;
+ recordTopLevelElementDefinition(element);
+ recordClassClauses(
+ node.name, node.superclass, node.withClause, node.implementsClause);
+ super.visitClassTypeAlias(node);
+ }
+
+ @override
+ visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+ SimpleIdentifier fieldName = node.fieldName;
+ if (fieldName != null) {
+ Element element = fieldName.staticElement;
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, fieldName);
+ }
+ node.expression?.accept(this);
+ }
+
+ @override
+ visitConstructorName(ConstructorName node) {
+ ConstructorElement element = node.staticElement;
+ // in 'class B = A;' actually A constructors are invoked
+ // TODO(scheglov) add support for multiple levels of redirection
+ // TODO(scheglov) test for a loop of redirection
+ if (element != null &&
+ element.isSynthetic &&
+ element.redirectedConstructor != null) {
+ element = element.redirectedConstructor;
+ }
+ // 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
+ // TODO(scheglov) consider removing the conditions
+ if (element is ClassElement ||
+ element is FunctionElement ||
+ element is FunctionTypeAliasElement ||
+ element is LabelElement ||
+ element is MethodElement ||
+ element is PrefixElement ||
+ element is PropertyAccessorElement ||
+ element is PropertyInducingElement ||
+ element is TypeParameterElement) {
+ recordRelation(element, IndexRelationKind.IS_REFERENCED_BY, 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);
+ }
+
+ 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. Any [_RelationInfo] is always part
+ * of a [_UnitIndexAssembler], so [offset] and [length] should be understood
+ * within the context of the compilation unit pointed to by the
+ * [_UnitIndexAssembler].
+ */
+class _RelationInfo {
+ final _ElementInfo elementInfo;
+ final IndexRelationKind kind;
+ final int offset;
+ final int length;
+
+ _RelationInfo(this.elementInfo, this.kind, this.offset, this.length);
+}
+
+/**
+ * Assembler of a single [CompilationUnit] index. The intended usage sequence:
+ *
+ * - Call [addRelation] for each relation found in the compilation unit.
+ * - Assign ids to all the [_ElementInfo] objects reachable from [relations].
+ * - Call [assemble] to produce the final unit index.
+ */
+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) {
+ try {
+ _ElementInfo elementInfo = pkg._getElementInfo(element);
+ relations.add(new _RelationInfo(elementInfo, kind, offset, length));
+ } on StateError {}
+ }
+
+ /**
+ * Assemble a new [UnitIndexBuilder] using the information gathered
+ * by [addRelation]
+ */
+ UnitIndexBuilder assemble() {
+ relations.sort((a, b) {
+ return a.elementInfo.id - b.elementInfo.id;
+ });
+ return new UnitIndexBuilder(
+ elements: relations.map((r) => r.elementInfo.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') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698