OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'dart:async'; | |
6 | |
7 import 'package:analyzer/dart/ast/ast.dart'; | |
8 import 'package:analyzer/dart/element/element.dart'; | |
9 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | |
10 import 'package:analyzer/src/generated/source.dart'; | |
11 import 'package:analyzer/src/summary/format.dart'; | |
12 import 'package:analyzer/src/summary/idl.dart'; | |
13 import 'package:analyzer/src/summary/index_unit.dart'; | |
14 import 'package:collection/collection.dart'; | |
15 | |
16 /** | |
17 * Return a new [Index2] instance that keeps information in memory. | |
18 */ | |
19 Index2 createMemoryIndex2() { | |
20 return new Index2._(); | |
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 Index2 { | |
44 final Map<AnalysisContext, _ContextIndex> _contextIndexMap = | |
45 <AnalysisContext, _ContextIndex>{}; | |
46 | |
47 Index2._(); | |
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 * Return the [_ContextIndex] instance for the given [context]. | |
107 */ | |
108 _ContextIndex _getContextIndex(AnalysisContext context) { | |
109 return _contextIndexMap.putIfAbsent(context, () { | |
110 return new _ContextIndex(context); | |
111 }); | |
112 } | |
113 | |
114 /** | |
115 * Complete with a list of all results returned by the [callback] for every | |
116 * context specific index. | |
117 */ | |
118 Future<List<Location>> _mergeLocations( | |
119 Future<List<Location>> callback(_ContextIndex index)) async { | |
120 List<Location> locations = <Location>[]; | |
121 for (_ContextIndex index in _contextIndexMap.values) { | |
122 List<Location> contextLocations = await callback(index); | |
123 locations.addAll(contextLocations); | |
124 } | |
125 return locations; | |
126 } | |
127 } | |
128 | |
129 /** | |
130 * Information about location of a single relation in the index. | |
131 * | |
132 * The location is expressed as a library specific unit containing the index | |
133 * relation, offset within this [Source] and length. | |
134 * | |
135 * Clients may not extend, implement or mix-in this class. | |
136 */ | |
137 class Location { | |
138 /** | |
139 * The [AnalysisContext] containing this location. | |
140 */ | |
141 final AnalysisContext context; | |
142 | |
143 /** | |
144 * The URI of the source of the library containing this location. | |
145 */ | |
146 final String libraryUri; | |
147 | |
148 /** | |
149 * The URI of the source of the unit containing this location. | |
150 */ | |
151 final String unitUri; | |
152 | |
153 /** | |
154 * The kind of usage at this location. | |
155 */ | |
156 final IndexRelationKind kind; | |
157 | |
158 /** | |
159 * The offset of this location within the [unitUri]. | |
160 */ | |
161 final int offset; | |
162 | |
163 /** | |
164 * The length of this location. | |
165 */ | |
166 final int length; | |
167 | |
168 /** | |
169 * Is `true` if this location is qualified. | |
170 */ | |
171 final bool isQualified; | |
172 | |
173 /** | |
174 * Is `true` if this location is resolved. | |
175 */ | |
176 final bool isResolved; | |
177 | |
178 Location(this.context, this.libraryUri, this.unitUri, this.kind, this.offset, | |
179 this.length, this.isQualified, this.isResolved); | |
180 | |
181 @override | |
182 String toString() => 'Location{librarySourceUri: $libraryUri, ' | |
183 'unitSourceUri: $unitUri, offset: $offset, length: $length, ' | |
184 'isQualified: $isQualified}, isResolved: $isResolved}'; | |
185 } | |
186 | |
187 /** | |
188 * Opaque identifier of a [PackageIndex]. | |
189 */ | |
190 abstract class PackageIndexId {} | |
191 | |
192 /** | |
193 * Storage of [PackageIndex] objects. | |
194 */ | |
195 abstract class PackageIndexStore { | |
196 /** | |
197 * Complete with identifiers of all [PackageIndex] objects. | |
198 */ | |
199 Future<Iterable<PackageIndexId>> getIds(); | |
200 | |
201 /** | |
202 * Complete with the [PackageIndex] with the given [id]. | |
203 */ | |
204 Future<PackageIndex> getIndex(PackageIndexId id); | |
205 | |
206 /** | |
207 * Put the given [indexBuilder] into the store. | |
208 */ | |
209 void putIndex(String unitLibraryUri, String unitUnitUri, | |
210 PackageIndexBuilder indexBuilder); | |
211 } | |
212 | |
213 /** | |
214 * The [AnalysisContext] specific index. | |
215 */ | |
216 class _ContextIndex { | |
217 final AnalysisContext context; | |
218 final Map<String, PackageIndex> indexMap = <String, PackageIndex>{}; | |
219 | |
220 _ContextIndex(this.context); | |
221 | |
222 /** | |
223 * Complete with a list of locations where elements of the given [kind] with | |
224 * names satisfying the given [regExp] are defined. | |
225 */ | |
226 Future<List<Location>> getDefinedNames( | |
227 RegExp regExp, IndexNameKind kind) async { | |
228 return _mergeLocations((_PackageIndexRequester requester) { | |
229 return requester.getDefinedNames(context, regExp, kind); | |
230 }); | |
231 } | |
232 | |
233 /** | |
234 * Complete with a list of locations where the given [element] has relation | |
235 * of the given [kind]. | |
236 */ | |
237 Future<List<Location>> getRelations(Element element, IndexRelationKind kind) { | |
238 return _mergeLocations((_PackageIndexRequester requester) { | |
239 return requester.getRelations(context, element, kind); | |
240 }); | |
241 } | |
242 | |
243 /** | |
244 * Complete with a list of locations where a class members with the given | |
245 * [name] is referenced with a qualifier, but is not resolved. | |
246 */ | |
247 Future<List<Location>> getUnresolvedMemberReferences(String name) async { | |
248 return _mergeLocations((_PackageIndexRequester requester) { | |
249 return requester.getUnresolvedMemberReferences(context, name); | |
250 }); | |
251 } | |
252 | |
253 /** | |
254 * Index the given fully resolved [unit]. | |
255 */ | |
256 void indexUnit(CompilationUnit unit) { | |
257 // Index the unit. | |
258 PackageIndexAssembler assembler = new PackageIndexAssembler(); | |
259 assembler.index(unit); | |
260 PackageIndexBuilder indexBuilder = assembler.assemble(); | |
261 // Put the index into the map. | |
262 List<int> indexBytes = indexBuilder.toBuffer(); | |
263 PackageIndex index = new PackageIndex.fromBuffer(indexBytes); | |
264 String key = _getUnitKeyForElement(unit.element); | |
265 indexMap[key] = index; | |
266 } | |
267 | |
268 /** | |
269 * Remove index information about the unit. | |
270 */ | |
271 void removeUnit(Source librarySource, Source unitSource) { | |
272 String key = _getUnitKeyForSource(librarySource, unitSource); | |
273 indexMap.remove(key); | |
274 } | |
275 | |
276 String _getUnitKeyForElement(CompilationUnitElement unitElement) { | |
277 Source librarySource = unitElement.library.source; | |
278 Source unitSource = unitElement.source; | |
279 return _getUnitKeyForSource(librarySource, unitSource); | |
280 } | |
281 | |
282 String _getUnitKeyForSource(Source librarySource, Source unitSource) { | |
283 String unitLibraryUri = librarySource.uri.toString(); | |
284 String unitUnitUri = unitSource.uri.toString(); | |
285 return '$unitLibraryUri;$unitUnitUri'; | |
286 } | |
287 | |
288 Future<List<Location>> _mergeLocations( | |
289 List<Location> callback(_PackageIndexRequester requester)) async { | |
290 List<Location> locations = <Location>[]; | |
291 for (PackageIndex index in indexMap.values) { | |
292 _PackageIndexRequester requester = new _PackageIndexRequester(index); | |
293 List<Location> indexLocations = callback(requester); | |
294 locations.addAll(indexLocations); | |
295 } | |
296 return locations; | |
297 } | |
298 } | |
299 | |
300 /** | |
301 * Helper for requesting information from a single [PackageIndex]. | |
302 */ | |
303 class _PackageIndexRequester { | |
304 final PackageIndex index; | |
305 | |
306 _PackageIndexRequester(this.index); | |
307 | |
308 /** | |
309 * Return the [element]'s identifier in the [index] or `-1` if the | |
310 * [element] is not referenced in the [index]. | |
311 */ | |
312 int findElementId(Element element) { | |
313 // Find the id of the element's unit. | |
314 int unitId = getUnitId(element); | |
315 if (unitId == -1) { | |
316 return -1; | |
317 } | |
318 // Prepare information about the element. | |
319 ElementInfo info = PackageIndexAssembler.newElementInfo(unitId, element); | |
320 // Find the first occurrence of an element with the same offset. | |
321 int elementId = _findFirstOccurrence(index.elementOffsets, info.offset); | |
322 if (elementId == -1) { | |
323 return -1; | |
324 } | |
325 // Try to find the element id using offset, unit and kind. | |
326 for (; | |
327 elementId < index.elementOffsets.length && | |
328 index.elementOffsets[elementId] == info.offset; | |
329 elementId++) { | |
330 if (index.elementUnits[elementId] == unitId && | |
331 index.elementKinds[elementId] == info.kind) { | |
332 return elementId; | |
333 } | |
334 } | |
335 return -1; | |
336 } | |
337 | |
338 /** | |
339 * Complete with a list of locations where elements of the given [kind] with | |
340 * names satisfying the given [regExp] are defined. | |
341 */ | |
342 List<Location> getDefinedNames( | |
343 AnalysisContext context, RegExp regExp, IndexNameKind kind) { | |
344 List<Location> locations = <Location>[]; | |
345 for (UnitIndex unitIndex in index.units) { | |
346 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); | |
347 List<Location> unitLocations = | |
348 requester.getDefinedNames(context, regExp, kind); | |
349 locations.addAll(unitLocations); | |
350 } | |
351 return locations; | |
352 } | |
353 | |
354 /** | |
355 * Complete with a list of locations where the given [element] has relation | |
356 * of the given [kind]. | |
357 */ | |
358 List<Location> getRelations( | |
359 AnalysisContext context, Element element, IndexRelationKind kind) { | |
360 int elementId = findElementId(element); | |
361 if (elementId == -1) { | |
362 return const <Location>[]; | |
363 } | |
364 List<Location> locations = <Location>[]; | |
365 for (UnitIndex unitIndex in index.units) { | |
366 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); | |
367 List<Location> unitLocations = | |
368 requester.getRelations(context, elementId, kind); | |
369 locations.addAll(unitLocations); | |
370 } | |
371 return locations; | |
372 } | |
373 | |
374 /** | |
375 * Return the identifier of [str] in the [index] or `-1` if [str] is not used | |
376 * in the [index]. | |
377 */ | |
378 int getStringId(String str) { | |
379 return binarySearch(index.strings, str); | |
380 } | |
381 | |
382 /** | |
383 * Return the identifier of the [CompilationUnitElement] containing the | |
384 * [element] in the [index] or `-1` if not found. | |
385 */ | |
386 int getUnitId(Element element) { | |
387 CompilationUnitElement unitElement = | |
388 PackageIndexAssembler.getUnitElement(element); | |
389 int libraryUriId = getUriId(unitElement.library.source.uri); | |
390 if (libraryUriId == -1) { | |
391 return -1; | |
392 } | |
393 int unitUriId = getUriId(unitElement.source.uri); | |
394 if (unitUriId == -1) { | |
395 return -1; | |
396 } | |
397 for (int i = 0; i < index.unitLibraryUris.length; i++) { | |
398 if (index.unitLibraryUris[i] == libraryUriId && | |
399 index.unitUnitUris[i] == unitUriId) { | |
400 return i; | |
401 } | |
402 } | |
403 return -1; | |
404 } | |
405 | |
406 /** | |
407 * Return the URI of the library source of the library specific [unit]. | |
408 */ | |
409 String getUnitLibraryUri(int unit) { | |
410 int id = index.unitLibraryUris[unit]; | |
411 return index.strings[id]; | |
412 } | |
413 | |
414 /** | |
415 * Return the URI of the unit source of the library specific [unit]. | |
416 */ | |
417 String getUnitUnitUri(int unit) { | |
418 int id = index.unitUnitUris[unit]; | |
419 return index.strings[id]; | |
420 } | |
421 | |
422 /** | |
423 * Complete with a list of locations where a class members with the given | |
424 * [name] is referenced with a qualifier, but is not resolved. | |
425 */ | |
426 List<Location> getUnresolvedMemberReferences( | |
427 AnalysisContext context, String name) { | |
428 List<Location> locations = <Location>[]; | |
429 for (UnitIndex unitIndex in index.units) { | |
430 _UnitIndexRequester requester = new _UnitIndexRequester(this, unitIndex); | |
431 List<Location> unitLocations = | |
432 requester.getUnresolvedMemberReferences(context, name); | |
433 locations.addAll(unitLocations); | |
434 } | |
435 return locations; | |
436 } | |
437 | |
438 /** | |
439 * Return the identifier of the [uri] in the [index] or `-1` if the [uri] is | |
440 * not used in the [index]. | |
441 */ | |
442 int getUriId(Uri uri) { | |
443 String str = uri.toString(); | |
444 return getStringId(str); | |
445 } | |
446 } | |
447 | |
448 /** | |
449 * Helper for requesting information from a single [UnitIndex]. | |
450 */ | |
451 class _UnitIndexRequester { | |
452 final _PackageIndexRequester packageRequester; | |
453 final UnitIndex unitIndex; | |
454 | |
455 _UnitIndexRequester(this.packageRequester, this.unitIndex); | |
456 | |
457 /** | |
458 * Complete with a list of locations where elements of the given [kind] with | |
459 * names satisfying the given [regExp] are defined. | |
460 */ | |
461 List<Location> getDefinedNames( | |
462 AnalysisContext context, RegExp regExp, IndexNameKind kind) { | |
463 List<Location> locations = <Location>[]; | |
464 String unitLibraryUri = null; | |
465 String unitUnitUri = null; | |
466 for (int i = 0; i < unitIndex.definedNames.length; i++) { | |
467 if (unitIndex.definedNameKinds[i] == kind) { | |
468 int nameIndex = unitIndex.definedNames[i]; | |
469 String name = packageRequester.index.strings[nameIndex]; | |
470 if (regExp.matchAsPrefix(name) != null) { | |
471 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); | |
472 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); | |
473 locations.add(new Location(context, unitLibraryUri, unitUnitUri, null, | |
474 unitIndex.definedNameOffsets[i], name.length, false, true)); | |
475 } | |
476 } | |
477 } | |
478 return locations; | |
479 } | |
480 | |
481 /** | |
482 * Return a list of locations where an element with the given [elementId] has | |
483 * relation of the given [kind]. | |
484 */ | |
485 List<Location> getRelations( | |
486 AnalysisContext context, int elementId, IndexRelationKind kind) { | |
487 // Find the first usage of the element. | |
488 int i = _findFirstOccurrence(unitIndex.usedElements, elementId); | |
489 if (i == -1) { | |
490 return const <Location>[]; | |
491 } | |
492 // Create locations for every usage of the element. | |
493 List<Location> locations = <Location>[]; | |
494 String unitLibraryUri = null; | |
495 String unitUnitUri = null; | |
496 for (; | |
497 i < unitIndex.usedElements.length && | |
498 unitIndex.usedElements[i] == elementId; | |
499 i++) { | |
500 if (unitIndex.usedElementKinds[i] == kind) { | |
501 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); | |
502 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); | |
503 locations.add(new Location( | |
504 context, | |
505 unitLibraryUri, | |
506 unitUnitUri, | |
507 kind, | |
508 unitIndex.usedElementOffsets[i], | |
509 unitIndex.usedElementLengths[i], | |
510 unitIndex.usedElementIsQualifiedFlags[i], | |
511 true)); | |
512 } | |
513 } | |
514 return locations; | |
515 } | |
516 | |
517 /** | |
518 * Complete with a list of locations where a class members with the given | |
519 * [name] is referenced with a qualifier, but is not resolved. | |
520 */ | |
521 List<Location> getUnresolvedMemberReferences( | |
522 AnalysisContext context, String name) { | |
523 // Find the name ID in the package index. | |
524 int nameId = packageRequester.getStringId(name); | |
525 if (nameId == -1) { | |
526 return const <Location>[]; | |
527 } | |
528 // Find the first usage of the name. | |
529 int i = _findFirstOccurrence(unitIndex.usedNames, nameId); | |
530 if (i == -1) { | |
531 return const <Location>[]; | |
532 } | |
533 // Create locations for every usage of the name. | |
534 List<Location> locations = <Location>[]; | |
535 String unitLibraryUri = null; | |
536 String unitUnitUri = null; | |
537 for (; | |
538 i < unitIndex.usedNames.length && unitIndex.usedNames[i] == nameId; | |
539 i++) { | |
540 unitLibraryUri ??= packageRequester.getUnitLibraryUri(unitIndex.unit); | |
541 unitUnitUri ??= packageRequester.getUnitUnitUri(unitIndex.unit); | |
542 locations.add(new Location( | |
543 context, | |
544 unitLibraryUri, | |
545 unitUnitUri, | |
546 unitIndex.usedNameKinds[i], | |
547 unitIndex.usedNameOffsets[i], | |
548 name.length, | |
549 unitIndex.usedNameIsQualifiedFlags[i], | |
550 false)); | |
551 } | |
552 return locations; | |
553 } | |
554 } | |
OLD | NEW |