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 |