| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library services.index; | |
| 6 | |
| 7 import 'dart:async'; | 5 import 'dart:async'; |
| 8 | 6 |
| 9 import 'package:analysis_server/src/provisional/index/index_core.dart'; | 7 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analysis_server/src/services/index/indexable_element.dart'; | |
| 11 import 'package:analyzer/dart/element/element.dart'; | 8 import 'package:analyzer/dart/element/element.dart'; |
| 12 import 'package:analyzer/src/generated/engine.dart'; | 9 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
| 13 | 10 import 'package:analyzer/src/generated/source.dart'; |
| 14 /** | 11 import 'package:analyzer/src/summary/format.dart'; |
| 15 * A filter for [Element] names. | 12 import 'package:analyzer/src/summary/idl.dart'; |
| 16 */ | 13 import 'package:analyzer/src/summary/index_unit.dart'; |
| 17 typedef bool ElementNameFilter(String name); | 14 import 'package:collection/collection.dart'; |
| 18 | 15 |
| 19 /** | 16 /** |
| 20 * The interface [Index] defines the behavior of objects that maintain an index | 17 * Return a new [Index] instance that keeps information in memory. |
| 21 * storing relations between indexable objects. | 18 */ |
| 19 Index createMemoryIndex() { |
| 20 return new Index._(); |
| 21 } |
| 22 |
| 23 /** |
| 24 * Return the index of the first occurrence of the [value] in the [sortedList], |
| 25 * or `-1` if the [value] is not in the list. |
| 26 */ |
| 27 int _findFirstOccurrence(List<int> sortedList, int value) { |
| 28 // Find an occurrence. |
| 29 int i = binarySearch(sortedList, value); |
| 30 if (i == -1) { |
| 31 return -1; |
| 32 } |
| 33 // Find the first occurrence. |
| 34 while (i > 0 && sortedList[i - 1] == value) { |
| 35 i--; |
| 36 } |
| 37 return i; |
| 38 } |
| 39 |
| 40 /** |
| 41 * Interface for storing and requesting relations. |
| 42 */ |
| 43 class Index { |
| 44 final Map<AnalysisContext, _ContextIndex> _contextIndexMap = |
| 45 <AnalysisContext, _ContextIndex>{}; |
| 46 |
| 47 Index._(); |
| 48 |
| 49 /** |
| 50 * Complete with a list of locations where elements of the given [kind] with |
| 51 * names satisfying the given [regExp] are defined. |
| 52 */ |
| 53 Future<List<Location>> getDefinedNames(RegExp regExp, IndexNameKind kind) { |
| 54 return _mergeLocations((_ContextIndex index) { |
| 55 return index.getDefinedNames(regExp, kind); |
| 56 }); |
| 57 } |
| 58 |
| 59 /** |
| 60 * Complete with a list of locations where the given [element] has relation |
| 61 * of the given [kind]. |
| 62 */ |
| 63 Future<List<Location>> getRelations(Element element, IndexRelationKind kind) { |
| 64 return _mergeLocations((_ContextIndex index) { |
| 65 return index.getRelations(element, kind); |
| 66 }); |
| 67 } |
| 68 |
| 69 /** |
| 70 * Complete with a list of locations where a class members with the given |
| 71 * [name] is referenced with a qualifier, but is not resolved. |
| 72 */ |
| 73 Future<List<Location>> getUnresolvedMemberReferences(String name) { |
| 74 return _mergeLocations((_ContextIndex index) { |
| 75 return index.getUnresolvedMemberReferences(name); |
| 76 }); |
| 77 } |
| 78 |
| 79 /** |
| 80 * Index the given fully resolved [unit]. |
| 81 */ |
| 82 void indexUnit(CompilationUnit unit) { |
| 83 if (unit == null || unit.element == null) { |
| 84 return; |
| 85 } |
| 86 AnalysisContext context = unit.element.context; |
| 87 _getContextIndex(context).indexUnit(unit); |
| 88 } |
| 89 |
| 90 /** |
| 91 * Remove all index information for the given [context]. |
| 92 */ |
| 93 void removeContext(AnalysisContext context) { |
| 94 _contextIndexMap.remove(context); |
| 95 } |
| 96 |
| 97 /** |
| 98 * Remove index information about the unit in the given [context]. |
| 99 */ |
| 100 void removeUnit( |
| 101 AnalysisContext context, Source librarySource, Source unitSource) { |
| 102 _contextIndexMap[context]?.removeUnit(librarySource, unitSource); |
| 103 } |
| 104 |
| 105 /** |
| 106 * Notify the index that the client is going to stop using it. |
| 107 */ |
| 108 void stop() {} |
| 109 |
| 110 /** |
| 111 * Return the [_ContextIndex] instance for the given [context]. |
| 112 */ |
| 113 _ContextIndex _getContextIndex(AnalysisContext context) { |
| 114 return _contextIndexMap.putIfAbsent(context, () { |
| 115 return new _ContextIndex(context); |
| 116 }); |
| 117 } |
| 118 |
| 119 /** |
| 120 * Complete with a list of all results returned by the [callback] for every |
| 121 * context specific index. |
| 122 */ |
| 123 Future<List<Location>> _mergeLocations( |
| 124 Future<List<Location>> callback(_ContextIndex index)) async { |
| 125 List<Location> locations = <Location>[]; |
| 126 for (_ContextIndex index in _contextIndexMap.values) { |
| 127 List<Location> contextLocations = await callback(index); |
| 128 locations.addAll(contextLocations); |
| 129 } |
| 130 return locations; |
| 131 } |
| 132 } |
| 133 |
| 134 /** |
| 135 * Information about location of a single relation in the index. |
| 22 * | 136 * |
| 23 * Any modification operations are executed before any read operation. | 137 * The location is expressed as a library specific unit containing the index |
| 24 * There is no guarantee about the order in which the [Future]s for read | 138 * relation, offset within this [Source] and length. |
| 25 * operations will complete. | 139 * |
| 26 */ | 140 * Clients may not extend, implement or mix-in this class. |
| 27 abstract class Index implements IndexStore { | 141 */ |
| 28 /** | 142 class Location { |
| 29 * Set the index contributors used by this index to the given list of | 143 /** |
| 30 * [contributors]. | 144 * The [AnalysisContext] containing this location. |
| 31 */ | 145 */ |
| 32 void set contributors(List<IndexContributor> contributors); | 146 final AnalysisContext context; |
| 33 | 147 |
| 34 /** | 148 /** |
| 35 * Answers index statistics. | 149 * The URI of the source of the library containing this location. |
| 36 */ | 150 */ |
| 37 String get statistics; | 151 final String libraryUri; |
| 38 | 152 |
| 39 /** | 153 /** |
| 40 * Returns top-level [Element]s whose names satisfy to [nameFilter]. | 154 * The URI of the source of the unit containing this location. |
| 41 */ | 155 */ |
| 42 List<Element> getTopLevelDeclarations(ElementNameFilter nameFilter); | 156 final String unitUri; |
| 43 | 157 |
| 44 /** | 158 /** |
| 45 * Processes the given [object] in order to record the relationships. | 159 * The kind of usage at this location. |
| 46 * | 160 */ |
| 47 * [context] - the [AnalysisContext] in which the [object] being indexed. | 161 final IndexRelationKind kind; |
| 48 * [object] - the object being indexed. | 162 |
| 49 */ | 163 /** |
| 50 void index(AnalysisContext context, Object object); | 164 * The offset of this location within the [unitUri]. |
| 51 | 165 */ |
| 52 /** | 166 final int offset; |
| 53 * Starts the index. | 167 |
| 54 * Should be called before any other method. | 168 /** |
| 55 */ | 169 * The length of this location. |
| 56 void run(); | 170 */ |
| 57 | 171 final int length; |
| 58 /** | 172 |
| 59 * Stops the index. | 173 /** |
| 60 * After calling this method operations may not be executed. | 174 * Is `true` if this location is qualified. |
| 61 */ | 175 */ |
| 62 void stop(); | 176 final bool isQualified; |
| 63 } | 177 |
| 64 | 178 /** |
| 65 /** | 179 * Is `true` if this location is resolved. |
| 66 * An [Element] which is used to index references to the name without specifying | 180 */ |
| 67 * a concrete kind of this name - field, method or something else. | 181 final bool isResolved; |
| 68 */ | 182 |
| 69 class IndexableName implements IndexableObject { | 183 Location(this.context, this.libraryUri, this.unitUri, this.kind, this.offset, |
| 70 /** | 184 this.length, this.isQualified, this.isResolved); |
| 71 * The name to be indexed. | |
| 72 */ | |
| 73 final String name; | |
| 74 | |
| 75 /** | |
| 76 * Initialize a newly created indexable name to represent the given [name]. | |
| 77 */ | |
| 78 IndexableName(this.name); | |
| 79 | 185 |
| 80 @override | 186 @override |
| 81 String get filePath => null; | 187 String toString() => 'Location{librarySourceUri: $libraryUri, ' |
| 82 | 188 'unitSourceUri: $unitUri, offset: $offset, length: $length, ' |
| 83 @override | 189 'isQualified: $isQualified}, isResolved: $isResolved}'; |
| 84 IndexableNameKind get kind => IndexableNameKind.INSTANCE; | 190 } |
| 85 | 191 |
| 86 @override | 192 /** |
| 87 int get offset { | 193 * Opaque identifier of a [PackageIndex]. |
| 194 */ |
| 195 abstract class PackageIndexId {} |
| 196 |
| 197 /** |
| 198 * Storage of [PackageIndex] objects. |
| 199 */ |
| 200 abstract class PackageIndexStore { |
| 201 /** |
| 202 * Complete with identifiers of all [PackageIndex] objects. |
| 203 */ |
| 204 Future<Iterable<PackageIndexId>> getIds(); |
| 205 |
| 206 /** |
| 207 * Complete with the [PackageIndex] with the given [id]. |
| 208 */ |
| 209 Future<PackageIndex> getIndex(PackageIndexId id); |
| 210 |
| 211 /** |
| 212 * Put the given [indexBuilder] into the store. |
| 213 */ |
| 214 void putIndex(String unitLibraryUri, String unitUnitUri, |
| 215 PackageIndexBuilder indexBuilder); |
| 216 } |
| 217 |
| 218 /** |
| 219 * The [AnalysisContext] specific index. |
| 220 */ |
| 221 class _ContextIndex { |
| 222 final AnalysisContext context; |
| 223 final Map<String, PackageIndex> indexMap = <String, PackageIndex>{}; |
| 224 |
| 225 _ContextIndex(this.context); |
| 226 |
| 227 /** |
| 228 * Complete with a list of locations where elements of the given [kind] with |
| 229 * names satisfying the given [regExp] are defined. |
| 230 */ |
| 231 Future<List<Location>> getDefinedNames( |
| 232 RegExp regExp, IndexNameKind kind) async { |
| 233 return _mergeLocations((_PackageIndexRequester requester) { |
| 234 return requester.getDefinedNames(context, regExp, kind); |
| 235 }); |
| 236 } |
| 237 |
| 238 /** |
| 239 * Complete with a list of locations where the given [element] has relation |
| 240 * of the given [kind]. |
| 241 */ |
| 242 Future<List<Location>> getRelations(Element element, IndexRelationKind kind) { |
| 243 return _mergeLocations((_PackageIndexRequester requester) { |
| 244 return requester.getRelations(context, element, kind); |
| 245 }); |
| 246 } |
| 247 |
| 248 /** |
| 249 * Complete with a list of locations where a class members with the given |
| 250 * [name] is referenced with a qualifier, but is not resolved. |
| 251 */ |
| 252 Future<List<Location>> getUnresolvedMemberReferences(String name) async { |
| 253 return _mergeLocations((_PackageIndexRequester requester) { |
| 254 return requester.getUnresolvedMemberReferences(context, name); |
| 255 }); |
| 256 } |
| 257 |
| 258 /** |
| 259 * Index the given fully resolved [unit]. |
| 260 */ |
| 261 void indexUnit(CompilationUnit unit) { |
| 262 // Index the unit. |
| 263 PackageIndexAssembler assembler = new PackageIndexAssembler(); |
| 264 assembler.index(unit); |
| 265 PackageIndexBuilder indexBuilder = assembler.assemble(); |
| 266 // Put the index into the map. |
| 267 List<int> indexBytes = indexBuilder.toBuffer(); |
| 268 PackageIndex index = new PackageIndex.fromBuffer(indexBytes); |
| 269 String key = _getUnitKeyForElement(unit.element); |
| 270 indexMap[key] = index; |
| 271 } |
| 272 |
| 273 /** |
| 274 * Remove index information about the unit. |
| 275 */ |
| 276 void removeUnit(Source librarySource, Source unitSource) { |
| 277 String key = _getUnitKeyForSource(librarySource, unitSource); |
| 278 indexMap.remove(key); |
| 279 } |
| 280 |
| 281 String _getUnitKeyForElement(CompilationUnitElement unitElement) { |
| 282 Source librarySource = unitElement.library.source; |
| 283 Source unitSource = unitElement.source; |
| 284 return _getUnitKeyForSource(librarySource, unitSource); |
| 285 } |
| 286 |
| 287 String _getUnitKeyForSource(Source librarySource, Source unitSource) { |
| 288 String unitLibraryUri = librarySource.uri.toString(); |
| 289 String unitUnitUri = unitSource.uri.toString(); |
| 290 return '$unitLibraryUri;$unitUnitUri'; |
| 291 } |
| 292 |
| 293 Future<List<Location>> _mergeLocations( |
| 294 List<Location> callback(_PackageIndexRequester requester)) async { |
| 295 List<Location> locations = <Location>[]; |
| 296 for (PackageIndex index in indexMap.values) { |
| 297 _PackageIndexRequester requester = new _PackageIndexRequester(index); |
| 298 List<Location> indexLocations = callback(requester); |
| 299 locations.addAll(indexLocations); |
| 300 } |
| 301 return locations; |
| 302 } |
| 303 } |
| 304 |
| 305 /** |
| 306 * Helper for requesting information from a single [PackageIndex]. |
| 307 */ |
| 308 class _PackageIndexRequester { |
| 309 final PackageIndex index; |
| 310 |
| 311 _PackageIndexRequester(this.index); |
| 312 |
| 313 /** |
| 314 * Return the [element]'s identifier in the [index] or `-1` if the |
| 315 * [element] is not referenced in the [index]. |
| 316 */ |
| 317 int findElementId(Element element) { |
| 318 // Find the id of the element's unit. |
| 319 int unitId = getUnitId(element); |
| 320 if (unitId == -1) { |
| 321 return -1; |
| 322 } |
| 323 // Prepare information about the element. |
| 324 ElementInfo info = PackageIndexAssembler.newElementInfo(unitId, element); |
| 325 // Find the first occurrence of an element with the same offset. |
| 326 int elementId = _findFirstOccurrence(index.elementOffsets, info.offset); |
| 327 if (elementId == -1) { |
| 328 return -1; |
| 329 } |
| 330 // Try to find the element id using offset, unit and kind. |
| 331 for (; |
| 332 elementId < index.elementOffsets.length && |
| 333 index.elementOffsets[elementId] == info.offset; |
| 334 elementId++) { |
| 335 if (index.elementUnits[elementId] == unitId && |
| 336 index.elementKinds[elementId] == info.kind) { |
| 337 return elementId; |
| 338 } |
| 339 } |
| 88 return -1; | 340 return -1; |
| 89 } | 341 } |
| 90 | 342 |
| 91 @override | 343 /** |
| 92 bool operator ==(Object object) => | 344 * Complete with a list of locations where elements of the given [kind] with |
| 93 object is IndexableName && object.name == name; | 345 * names satisfying the given [regExp] are defined. |
| 94 | 346 */ |
| 95 @override | 347 List<Location> getDefinedNames( |
| 96 String toString() => name; | 348 AnalysisContext context, RegExp regExp, IndexNameKind kind) { |
| 97 } | 349 List<Location> locations = <Location>[]; |
| 98 | 350 for (UnitIndex unitIndex in index.units) { |
| 99 /** | 351 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); |
| 100 * The kind of an indexable name. | 352 List<Location> unitLocations = |
| 101 */ | 353 requester.getDefinedNames(context, regExp, kind); |
| 102 class IndexableNameKind implements IndexableObjectKind<IndexableName> { | 354 locations.addAll(unitLocations); |
| 103 /** | 355 } |
| 104 * The unique instance of this class. | 356 return locations; |
| 105 */ | 357 } |
| 106 static final IndexableNameKind INSTANCE = | 358 |
| 107 new IndexableNameKind._(IndexableObjectKind.nextIndex); | 359 /** |
| 108 | 360 * Complete with a list of locations where the given [element] has relation |
| 109 /** | 361 * of the given [kind]. |
| 110 * The index uniquely identifying this kind. | 362 */ |
| 111 */ | 363 List<Location> getRelations( |
| 112 final int index; | 364 AnalysisContext context, Element element, IndexRelationKind kind) { |
| 113 | 365 int elementId = findElementId(element); |
| 114 /** | 366 if (elementId == -1) { |
| 115 * Initialize a newly created kind to have the given [index]. | 367 return const <Location>[]; |
| 116 */ | 368 } |
| 117 IndexableNameKind._(this.index) { | 369 List<Location> locations = <Location>[]; |
| 118 IndexableObjectKind.register(this); | 370 for (UnitIndex unitIndex in index.units) { |
| 119 } | 371 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); |
| 120 | 372 List<Location> unitLocations = |
| 121 @override | 373 requester.getRelations(context, elementId, kind); |
| 122 IndexableName decode(AnalysisContext context, String filePath, int offset) { | 374 locations.addAll(unitLocations); |
| 123 throw new UnsupportedError( | 375 } |
| 124 'Indexable names cannot be decoded through their kind'); | 376 return locations; |
| 125 } | 377 } |
| 126 | 378 |
| 127 @override | 379 /** |
| 128 int encodeHash(StringToInt stringToInt, IndexableName indexable) { | 380 * Return the identifier of [str] in the [index] or `-1` if [str] is not used |
| 129 String name = indexable.name; | 381 * in the [index]. |
| 130 return stringToInt(name); | 382 */ |
| 131 } | 383 int getStringId(String str) { |
| 132 } | 384 return binarySearch(index.strings, str); |
| 133 | 385 } |
| 134 /** | 386 |
| 135 * Constants used when populating and accessing the index. | 387 /** |
| 136 */ | 388 * Return the identifier of the [CompilationUnitElement] containing the |
| 137 class IndexConstants { | 389 * [element] in the [index] or `-1` if not found. |
| 138 /** | 390 */ |
| 139 * Left: the Universe or a Library. | 391 int getUnitId(Element element) { |
| 140 * Defines an Element. | 392 CompilationUnitElement unitElement = |
| 141 * Right: an Element declaration. | 393 PackageIndexAssembler.getUnitElement(element); |
| 142 */ | 394 int libraryUriId = getUriId(unitElement.library.source.uri); |
| 143 static final RelationshipImpl DEFINES = | 395 if (libraryUriId == -1) { |
| 144 RelationshipImpl.getRelationship("defines"); | 396 return -1; |
| 145 | 397 } |
| 146 /** | 398 int unitUriId = getUriId(unitElement.source.uri); |
| 147 * Left: class. | 399 if (unitUriId == -1) { |
| 148 * Has ancestor (extended or implemented, directly or indirectly). | 400 return -1; |
| 149 * Right: other class declaration. | 401 } |
| 150 */ | 402 for (int i = 0; i < index.unitLibraryUris.length; i++) { |
| 151 static final RelationshipImpl HAS_ANCESTOR = | 403 if (index.unitLibraryUris[i] == libraryUriId && |
| 152 RelationshipImpl.getRelationship("has-ancestor"); | 404 index.unitUnitUris[i] == unitUriId) { |
| 153 | 405 return i; |
| 154 /** | 406 } |
| 155 * Left: class. | 407 } |
| 156 * Is extended by. | 408 return -1; |
| 157 * Right: other class declaration. | 409 } |
| 158 */ | 410 |
| 159 static final RelationshipImpl IS_EXTENDED_BY = | 411 /** |
| 160 RelationshipImpl.getRelationship("is-extended-by"); | 412 * Return the URI of the library source of the library specific [unit]. |
| 161 | 413 */ |
| 162 /** | 414 String getUnitLibraryUri(int unit) { |
| 163 * Left: class. | 415 int id = index.unitLibraryUris[unit]; |
| 164 * Is implemented by. | 416 return index.strings[id]; |
| 165 * Right: other class declaration. | 417 } |
| 166 */ | 418 |
| 167 static final RelationshipImpl IS_IMPLEMENTED_BY = | 419 /** |
| 168 RelationshipImpl.getRelationship("is-implemented-by"); | 420 * Return the URI of the unit source of the library specific [unit]. |
| 169 | 421 */ |
| 170 /** | 422 String getUnitUnitUri(int unit) { |
| 171 * Left: class. | 423 int id = index.unitUnitUris[unit]; |
| 172 * Is mixed into. | 424 return index.strings[id]; |
| 173 * Right: other class declaration. | 425 } |
| 174 */ | 426 |
| 175 static final RelationshipImpl IS_MIXED_IN_BY = | 427 /** |
| 176 RelationshipImpl.getRelationship("is-mixed-in-by"); | 428 * Complete with a list of locations where a class members with the given |
| 177 | 429 * [name] is referenced with a qualifier, but is not resolved. |
| 178 /** | 430 */ |
| 179 * Left: local variable, parameter. | 431 List<Location> getUnresolvedMemberReferences( |
| 180 * Is read at. | 432 AnalysisContext context, String name) { |
| 181 * Right: location. | 433 List<Location> locations = <Location>[]; |
| 182 */ | 434 for (UnitIndex unitIndex in index.units) { |
| 183 static final RelationshipImpl IS_READ_BY = | 435 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); |
| 184 RelationshipImpl.getRelationship("is-read-by"); | 436 List<Location> unitLocations = |
| 185 | 437 requester.getUnresolvedMemberReferences(context, name); |
| 186 /** | 438 locations.addAll(unitLocations); |
| 187 * Left: local variable, parameter. | 439 } |
| 188 * Is both read and written at. | 440 return locations; |
| 189 * Right: location. | 441 } |
| 190 */ | 442 |
| 191 static final RelationshipImpl IS_READ_WRITTEN_BY = | 443 /** |
| 192 RelationshipImpl.getRelationship("is-read-written-by"); | 444 * Return the identifier of the [uri] in the [index] or `-1` if the [uri] is |
| 193 | 445 * not used in the [index]. |
| 194 /** | 446 */ |
| 195 * Left: local variable, parameter. | 447 int getUriId(Uri uri) { |
| 196 * Is written at. | 448 String str = uri.toString(); |
| 197 * Right: location. | 449 return getStringId(str); |
| 198 */ | 450 } |
| 199 static final RelationshipImpl IS_WRITTEN_BY = | 451 } |
| 200 RelationshipImpl.getRelationship("is-written-by"); | 452 |
| 201 | 453 /** |
| 202 /** | 454 * Helper for requesting information from a single [UnitIndex]. |
| 203 * Left: function, method, variable, getter. | 455 */ |
| 204 * Is invoked at. | 456 class _UnitIndexRequester { |
| 205 * Right: location. | 457 final _PackageIndexRequester packageRequester; |
| 206 */ | 458 final UnitIndex unitIndex; |
| 207 static final RelationshipImpl IS_INVOKED_BY = | 459 |
| 208 RelationshipImpl.getRelationship("is-invoked-by"); | 460 _UnitIndexRequester(this.packageRequester, this.unitIndex); |
| 209 | 461 |
| 210 /** | 462 /** |
| 211 * Left: function, function type, class, field, method. | 463 * Complete with a list of locations where elements of the given [kind] with |
| 212 * Is referenced (and not invoked, read/written) at. | 464 * names satisfying the given [regExp] are defined. |
| 213 * Right: location. | 465 */ |
| 214 */ | 466 List<Location> getDefinedNames( |
| 215 static final RelationshipImpl IS_REFERENCED_BY = | 467 AnalysisContext context, RegExp regExp, IndexNameKind kind) { |
| 216 RelationshipImpl.getRelationship("is-referenced-by"); | 468 List<Location> locations = <Location>[]; |
| 217 | 469 String unitLibraryUri = null; |
| 218 /** | 470 String unitUnitUri = null; |
| 219 * Left: name element. | 471 for (int i = 0; i < unitIndex.definedNames.length; i++) { |
| 220 * Is defined by. | 472 if (unitIndex.definedNameKinds[i] == kind) { |
| 221 * Right: concrete element declaration. | 473 int nameIndex = unitIndex.definedNames[i]; |
| 222 */ | 474 String name = packageRequester.index.strings[nameIndex]; |
| 223 static final RelationshipImpl NAME_IS_DEFINED_BY = | 475 if (regExp.matchAsPrefix(name) != null) { |
| 224 RelationshipImpl.getRelationship("name-is-defined-by"); | 476 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); |
| 225 | 477 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); |
| 226 IndexConstants._(); | 478 locations.add(new Location(context, unitLibraryUri, unitUnitUri, null, |
| 227 } | 479 unitIndex.definedNameOffsets[i], name.length, false, true)); |
| 228 | 480 } |
| 229 /** | 481 } |
| 230 * Instances of the class [LocationImpl] represent a location related to an | 482 } |
| 231 * element. | 483 return locations; |
| 232 * | 484 } |
| 233 * The location is expressed as an offset and length, but the offset is relative | 485 |
| 234 * to the resource containing the element rather than the start of the element | 486 /** |
| 235 * within that resource. | 487 * Return a list of locations where an element with the given [elementId] has |
| 236 */ | 488 * relation of the given [kind]. |
| 237 class LocationImpl implements Location { | 489 */ |
| 238 static const int _FLAG_QUALIFIED = 1 << 0; | 490 List<Location> getRelations( |
| 239 static const int _FLAG_RESOLVED = 1 << 1; | 491 AnalysisContext context, int elementId, IndexRelationKind kind) { |
| 240 | 492 // Find the first usage of the element. |
| 241 /** | 493 int i = _findFirstOccurrence(unitIndex.usedElements, elementId); |
| 242 * An empty array of locations. | 494 if (i == -1) { |
| 243 */ | 495 return const <Location>[]; |
| 244 static const List<LocationImpl> EMPTY_LIST = const <LocationImpl>[]; | 496 } |
| 245 | 497 // Create locations for every usage of the element. |
| 246 /** | 498 List<Location> locations = <Location>[]; |
| 247 * The indexable object containing this location. | 499 String unitLibraryUri = null; |
| 248 */ | 500 String unitUnitUri = null; |
| 249 final IndexableObject indexable; | 501 for (; |
| 250 | 502 i < unitIndex.usedElements.length && |
| 251 /** | 503 unitIndex.usedElements[i] == elementId; |
| 252 * The offset of this location within the resource containing the element. | 504 i++) { |
| 253 */ | 505 if (unitIndex.usedElementKinds[i] == kind) { |
| 254 final int offset; | 506 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); |
| 255 | 507 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); |
| 256 /** | 508 locations.add(new Location( |
| 257 * The length of this location. | 509 context, |
| 258 */ | 510 unitLibraryUri, |
| 259 final int length; | 511 unitUnitUri, |
| 260 | 512 kind, |
| 261 /** | 513 unitIndex.usedElementOffsets[i], |
| 262 * The flags of this location. | 514 unitIndex.usedElementLengths[i], |
| 263 */ | 515 unitIndex.usedElementIsQualifiedFlags[i], |
| 264 int _flags; | 516 true)); |
| 265 | 517 } |
| 266 /** | 518 } |
| 267 * Initializes a newly created location to be relative to the given | 519 return locations; |
| 268 * [indexable] object at the given [offset] with the given [length]. | 520 } |
| 269 */ | 521 |
| 270 LocationImpl(this.indexable, this.offset, this.length, | 522 /** |
| 271 {bool isQualified: false, bool isResolved: true}) { | 523 * Complete with a list of locations where a class members with the given |
| 272 if (indexable == null) { | 524 * [name] is referenced with a qualifier, but is not resolved. |
| 273 throw new ArgumentError("indexable object cannot be null"); | 525 */ |
| 274 } | 526 List<Location> getUnresolvedMemberReferences( |
| 275 _flags = 0; | 527 AnalysisContext context, String name) { |
| 276 if (isQualified) { | 528 // Find the name ID in the package index. |
| 277 _flags |= _FLAG_QUALIFIED; | 529 int nameId = packageRequester.getStringId(name); |
| 278 } | 530 if (nameId == -1) { |
| 279 if (isResolved) { | 531 return const <Location>[]; |
| 280 _flags |= _FLAG_RESOLVED; | 532 } |
| 281 } | 533 // Find the first usage of the name. |
| 282 } | 534 int i = _findFirstOccurrence(unitIndex.usedNames, nameId); |
| 283 | 535 if (i == -1) { |
| 284 /** | 536 return const <Location>[]; |
| 285 * The element containing this location. | 537 } |
| 286 */ | 538 // Create locations for every usage of the name. |
| 287 @deprecated | 539 List<Location> locations = <Location>[]; |
| 288 Element get element { | 540 String unitLibraryUri = null; |
| 289 if (indexable is IndexableElement) { | 541 String unitUnitUri = null; |
| 290 return (indexable as IndexableElement).element; | 542 for (; |
| 291 } | 543 i < unitIndex.usedNames.length && unitIndex.usedNames[i] == nameId; |
| 292 return null; | 544 i++) { |
| 293 } | 545 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); |
| 294 | 546 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); |
| 295 @override | 547 locations.add(new Location( |
| 296 bool get isQualified => (_flags & _FLAG_QUALIFIED) != 0; | 548 context, |
| 297 | 549 unitLibraryUri, |
| 298 @override | 550 unitUnitUri, |
| 299 bool get isResolved => (_flags & _FLAG_RESOLVED) != 0; | 551 unitIndex.usedNameKinds[i], |
| 300 | 552 unitIndex.usedNameOffsets[i], |
| 301 @override | 553 name.length, |
| 302 String toString() { | 554 unitIndex.usedNameIsQualifiedFlags[i], |
| 303 String flagsStr = ''; | 555 false)); |
| 304 if (isQualified) { | 556 } |
| 305 flagsStr += ' qualified'; | 557 return locations; |
| 306 } | 558 } |
| 307 if (isResolved) { | 559 } |
| 308 flagsStr += ' resolved'; | |
| 309 } | |
| 310 return '[$offset - ${offset + length}) $flagsStr in $indexable'; | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 /** | |
| 315 * A [LocationImpl] with attached data. | |
| 316 */ | |
| 317 class LocationWithData<D> extends LocationImpl { | |
| 318 final D data; | |
| 319 | |
| 320 LocationWithData(LocationImpl location, this.data) | |
| 321 : super(location.indexable, location.offset, location.length); | |
| 322 } | |
| 323 | |
| 324 /** | |
| 325 * Relationship between an element and a location. Relationships are identified | |
| 326 * by a globally unique identifier. | |
| 327 */ | |
| 328 class RelationshipImpl implements Relationship { | |
| 329 /** | |
| 330 * A table mapping relationship identifiers to relationships. | |
| 331 */ | |
| 332 static Map<String, RelationshipImpl> _RELATIONSHIP_MAP = {}; | |
| 333 | |
| 334 /** | |
| 335 * The next artificial hash code. | |
| 336 */ | |
| 337 static int _NEXT_HASH_CODE = 0; | |
| 338 | |
| 339 /** | |
| 340 * The artificial hash code for this object. | |
| 341 */ | |
| 342 final int _hashCode = _NEXT_HASH_CODE++; | |
| 343 | |
| 344 /** | |
| 345 * The unique identifier for this relationship. | |
| 346 */ | |
| 347 final String identifier; | |
| 348 | |
| 349 /** | |
| 350 * Initialize a newly created relationship with the given unique identifier. | |
| 351 */ | |
| 352 RelationshipImpl(this.identifier); | |
| 353 | |
| 354 @override | |
| 355 int get hashCode => _hashCode; | |
| 356 | |
| 357 @override | |
| 358 String toString() => identifier; | |
| 359 | |
| 360 /** | |
| 361 * Returns the relationship with the given unique [identifier]. | |
| 362 */ | |
| 363 static RelationshipImpl getRelationship(String identifier) { | |
| 364 RelationshipImpl relationship = _RELATIONSHIP_MAP[identifier]; | |
| 365 if (relationship == null) { | |
| 366 relationship = new RelationshipImpl(identifier); | |
| 367 _RELATIONSHIP_MAP[identifier] = relationship; | |
| 368 } | |
| 369 return relationship; | |
| 370 } | |
| 371 } | |
| OLD | NEW |