| // Copyright (c) 2014, 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 services.index; |
| |
| import 'dart:async'; |
| |
| import 'package:analysis_server/analysis/index_core.dart'; |
| import 'package:analysis_server/src/services/index/indexable_element.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| |
| /** |
| * A filter for [Element] names. |
| */ |
| typedef bool ElementNameFilter(String name); |
| |
| /** |
| * The interface [Index] defines the behavior of objects that maintain an index |
| * storing relations between indexable objects. |
| * |
| * Any modification operations are executed before any read operation. |
| * There is no guarantee about the order in which the [Future]s for read |
| * operations will complete. |
| */ |
| abstract class Index implements IndexStore { |
| /** |
| * Set the index contributors used by this index to the given list of |
| * [contributors]. |
| */ |
| void set contributors(List<IndexContributor> contributors); |
| |
| /** |
| * Answers index statistics. |
| */ |
| String get statistics; |
| |
| /** |
| * Returns top-level [Element]s whose names satisfy to [nameFilter]. |
| */ |
| List<Element> getTopLevelDeclarations(ElementNameFilter nameFilter); |
| |
| /** |
| * Processes the given [object] in order to record the relationships. |
| * |
| * [context] - the [AnalysisContext] in which the [object] being indexed. |
| * [object] - the object being indexed. |
| */ |
| void index(AnalysisContext context, Object object); |
| |
| /** |
| * Starts the index. |
| * Should be called before any other method. |
| */ |
| void run(); |
| |
| /** |
| * Stops the index. |
| * After calling this method operations may not be executed. |
| */ |
| void stop(); |
| } |
| |
| /** |
| * An [Element] which is used to index references to the name without specifying |
| * a concrete kind of this name - field, method or something else. |
| */ |
| class IndexableName implements IndexableObject { |
| /** |
| * The name to be indexed. |
| */ |
| final String name; |
| |
| /** |
| * Initialize a newly created indexable name to represent the given [name]. |
| */ |
| IndexableName(this.name); |
| |
| @override |
| IndexableObjectKind get kind => IndexableNameKind.INSTANCE; |
| |
| @override |
| int get length => 0; |
| |
| @override |
| int get offset { |
| return -1; |
| } |
| |
| @override |
| Source get source => null; |
| |
| @override |
| bool operator ==(Object object) => |
| object is IndexableName && object.name == name; |
| |
| @override |
| String toString() => name; |
| } |
| |
| /** |
| * The kind of an indexable name. |
| */ |
| class IndexableNameKind implements IndexableObjectKind { |
| /** |
| * The unique instance of this class. |
| */ |
| static final IndexableNameKind INSTANCE = |
| new IndexableNameKind._(IndexableObjectKind.nextIndex); |
| |
| /** |
| * The index uniquely identifying this kind. |
| */ |
| final int index; |
| |
| /** |
| * Initialize a newly created kind to have the given [index]. |
| */ |
| IndexableNameKind._(this.index) { |
| IndexableObjectKind.register(this); |
| } |
| |
| @override |
| IndexableObject decode(AnalysisContext context, String filePath, int offset) { |
| throw new UnsupportedError( |
| 'Indexable names cannot be decoded through their kind'); |
| } |
| } |
| |
| /** |
| * Constants used when populating and accessing the index. |
| */ |
| class IndexConstants { |
| /** |
| * Left: the Universe or a Library. |
| * Defines an Element. |
| * Right: an Element declaration. |
| */ |
| static final RelationshipImpl DEFINES = |
| RelationshipImpl.getRelationship("defines"); |
| |
| /** |
| * Left: class. |
| * Has ancestor (extended or implemented, directly or indirectly). |
| * Right: other class declaration. |
| */ |
| static final RelationshipImpl HAS_ANCESTOR = |
| RelationshipImpl.getRelationship("has-ancestor"); |
| |
| /** |
| * Left: class. |
| * Is extended by. |
| * Right: other class declaration. |
| */ |
| static final RelationshipImpl IS_EXTENDED_BY = |
| RelationshipImpl.getRelationship("is-extended-by"); |
| |
| /** |
| * Left: class. |
| * Is implemented by. |
| * Right: other class declaration. |
| */ |
| static final RelationshipImpl IS_IMPLEMENTED_BY = |
| RelationshipImpl.getRelationship("is-implemented-by"); |
| |
| /** |
| * Left: class. |
| * Is mixed into. |
| * Right: other class declaration. |
| */ |
| static final RelationshipImpl IS_MIXED_IN_BY = |
| RelationshipImpl.getRelationship("is-mixed-in-by"); |
| |
| /** |
| * Left: local variable, parameter. |
| * Is read at. |
| * Right: location. |
| */ |
| static final RelationshipImpl IS_READ_BY = |
| RelationshipImpl.getRelationship("is-read-by"); |
| |
| /** |
| * Left: local variable, parameter. |
| * Is both read and written at. |
| * Right: location. |
| */ |
| static final RelationshipImpl IS_READ_WRITTEN_BY = |
| RelationshipImpl.getRelationship("is-read-written-by"); |
| |
| /** |
| * Left: local variable, parameter. |
| * Is written at. |
| * Right: location. |
| */ |
| static final RelationshipImpl IS_WRITTEN_BY = |
| RelationshipImpl.getRelationship("is-written-by"); |
| |
| /** |
| * Left: function, method, variable, getter. |
| * Is invoked at. |
| * Right: location. |
| */ |
| static final RelationshipImpl IS_INVOKED_BY = |
| RelationshipImpl.getRelationship("is-invoked-by"); |
| |
| /** |
| * Left: function, function type, class, field, method. |
| * Is referenced (and not invoked, read/written) at. |
| * Right: location. |
| */ |
| static final RelationshipImpl IS_REFERENCED_BY = |
| RelationshipImpl.getRelationship("is-referenced-by"); |
| |
| /** |
| * Left: name element. |
| * Is defined by. |
| * Right: concrete element declaration. |
| */ |
| static final RelationshipImpl NAME_IS_DEFINED_BY = |
| RelationshipImpl.getRelationship("name-is-defined-by"); |
| |
| IndexConstants._(); |
| } |
| |
| /** |
| * Instances of the class [LocationImpl] represent a location related to an |
| * element. |
| * |
| * The location is expressed as an offset and length, but the offset is relative |
| * to the resource containing the element rather than the start of the element |
| * within that resource. |
| */ |
| class LocationImpl implements Location { |
| static const int _FLAG_QUALIFIED = 1 << 0; |
| static const int _FLAG_RESOLVED = 1 << 1; |
| |
| /** |
| * An empty array of locations. |
| */ |
| static const List<LocationImpl> EMPTY_LIST = const <LocationImpl>[]; |
| |
| /** |
| * The indexable object containing this location. |
| */ |
| final IndexableObject indexable; |
| |
| /** |
| * The offset of this location within the resource containing the element. |
| */ |
| final int offset; |
| |
| /** |
| * The length of this location. |
| */ |
| final int length; |
| |
| /** |
| * The flags of this location. |
| */ |
| int _flags; |
| |
| /** |
| * Initializes a newly created location to be relative to the given |
| * [indexable] object at the given [offset] with the given [length]. |
| */ |
| LocationImpl(this.indexable, this.offset, this.length, |
| {bool isQualified: false, bool isResolved: true}) { |
| if (indexable == null) { |
| throw new ArgumentError("indexable object cannot be null"); |
| } |
| _flags = 0; |
| if (isQualified) { |
| _flags |= _FLAG_QUALIFIED; |
| } |
| if (isResolved) { |
| _flags |= _FLAG_RESOLVED; |
| } |
| } |
| |
| /** |
| * The element containing this location. |
| */ |
| @deprecated |
| Element get element { |
| if (indexable is IndexableElement) { |
| return (indexable as IndexableElement).element; |
| } |
| return null; |
| } |
| |
| @override |
| bool get isQualified => (_flags & _FLAG_QUALIFIED) != 0; |
| |
| @override |
| bool get isResolved => (_flags & _FLAG_RESOLVED) != 0; |
| |
| @override |
| String toString() { |
| String flagsStr = ''; |
| if (isQualified) { |
| flagsStr += ' qualified'; |
| } |
| if (isResolved) { |
| flagsStr += ' resolved'; |
| } |
| return '[${offset} - ${(offset + length)}) $flagsStr in ${indexable}'; |
| } |
| } |
| |
| /** |
| * A [LocationImpl] with attached data. |
| */ |
| class LocationWithData<D> extends LocationImpl { |
| final D data; |
| |
| LocationWithData(LocationImpl location, this.data) |
| : super(location.indexable, location.offset, location.length); |
| } |
| |
| /** |
| * Relationship between an element and a location. Relationships are identified |
| * by a globally unique identifier. |
| */ |
| class RelationshipImpl implements Relationship { |
| /** |
| * A table mapping relationship identifiers to relationships. |
| */ |
| static Map<String, RelationshipImpl> _RELATIONSHIP_MAP = {}; |
| |
| /** |
| * The next artificial hash code. |
| */ |
| static int _NEXT_HASH_CODE = 0; |
| |
| /** |
| * The artifitial hash code for this object. |
| */ |
| final int _hashCode = _NEXT_HASH_CODE++; |
| |
| /** |
| * The unique identifier for this relationship. |
| */ |
| final String identifier; |
| |
| /** |
| * Initialize a newly created relationship with the given unique identifier. |
| */ |
| RelationshipImpl(this.identifier); |
| |
| @override |
| int get hashCode => _hashCode; |
| |
| @override |
| String toString() => identifier; |
| |
| /** |
| * Returns the relationship with the given unique [identifier]. |
| */ |
| static RelationshipImpl getRelationship(String identifier) { |
| RelationshipImpl relationship = _RELATIONSHIP_MAP[identifier]; |
| if (relationship == null) { |
| relationship = new RelationshipImpl(identifier); |
| _RELATIONSHIP_MAP[identifier] = relationship; |
| } |
| return relationship; |
| } |
| } |