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

Side by Side Diff: pkg/analysis_server/lib/src/index/split_store.dart

Issue 348773003: Port SplitIndexStore to Dart. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, 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 library index.split.store;
6
7 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:io';
10 import 'dart:typed_data';
11
12 import 'package:analyzer/src/generated/element.dart';
13 import 'package:analyzer/src/generated/engine.dart';
14 import 'package:analyzer/src/generated/index.dart';
15 import 'package:analyzer/src/generated/source.dart';
16
17
18 /**
19 * A helper that encodes/decodes [AnalysisContext]s from/to integers.
20 */
21 class ContextCodec {
22 /**
23 * A table mapping contexts to their unique indices.
24 */
25 Map<AnalysisContext, int> _contextToIndex = new HashMap<AnalysisContext, int>(
Paul Berry 2014/06/20 15:46:01 The Java version of this class uses weak reference
Brian Wilkerson 2014/06/20 15:55:30 I thought we had already added an API to notify th
scheglov 2014/06/20 16:16:56 You're right. I plan to do this in the next CL.
26 );
27
28 /**
29 * A table mapping indices to the corresponding contexts.
30 */
31 Map<int, AnalysisContext> _indexToContext = new HashMap<int, AnalysisContext>(
32 );
33
34 /**
35 * Returns the [AnalysisContext] that corresponds to the given index.
36 */
37 AnalysisContext decode(int index) => _indexToContext[index];
38
39 /**
40 * Returns an unique index for the given [AnalysisContext].
41 */
42 int encode(AnalysisContext context) {
43 int index = _contextToIndex[context];
44 if (index == null) {
45 index = _indexToContext.length;
46 _contextToIndex[context] = index;
47 _indexToContext[index] = context;
48 }
49 return index;
50 }
51 }
52
53
54 /**
55 * A helper that encodes/decodes [Element]s to/from integers.
56 */
57 class ElementCodec {
58 /**
59 * A list that works as a mapping of integers to element encodings (in form of integer arrays).
60 */
61 List<List<int>> _indexToPath = [];
62
63 /**
64 * A table mapping element locations (in form of integer arrays) into a single integer.
65 */
66 IntArrayToIntMap _pathToIndex = new IntArrayToIntMap(10000, 0.75);
67
68 final StringCodec _stringCodec;
69
70 ElementCodec(this._stringCodec);
71
72 /**
73 * Returns an [Element] that corresponds to the given location.
74 *
75 * @param context the [AnalysisContext] to find [Element] in
76 * @param index an integer corresponding to the [Element]
77 * @return the [Element] or `null`
78 */
79 Element decode(AnalysisContext context, int index) {
80 List<int> path = _indexToPath[index];
81 List<String> components = _getLocationComponents(path);
82 ElementLocation location = new ElementLocationImpl.con3(components);
83 return context.getElement(location);
84 }
85
86 /**
87 * Returns a unique integer that corresponds to the given [Element].
88 */
89 int encode(Element element) {
90 List<int> path = _getLocationPath(element);
91 int index = _pathToIndex.get(path, -1);
92 if (index == -1) {
93 index = _indexToPath.length;
94 _pathToIndex.put(path, index);
95 _indexToPath.add(path);
96 }
97 return index;
98 }
99
100 List<String> _getLocationComponents(List<int> path) {
101 int length = path.length;
102 List<String> components = new List<String>();
103 for (int i = 0; i < length; i++) {
104 int componentId = path[i];
105 String component = _stringCodec.decode(componentId);
106 if (i < length - 1 && path[i + 1] < 0) {
107 component += "@${(-path[i + 1])}";
108 i++;
109 }
110 components.add(component);
111 }
112 return components;
113 }
114
115 List<int> _getLocationPath(Element element) {
116 List<String> components = element.location.components;
117 int length = components.length;
118 if (_hasLocalOffset(components)) {
119 List<int> path = new List<int>();
120 for (String component in components) {
121 int atOffset = component.indexOf('@');
122 if (atOffset == -1) {
123 path.add(_stringCodec.encode(component));
124 } else {
125 String preAtString = component.substring(0, atOffset);
126 String atString = component.substring(atOffset + 1);
127 path.add(_stringCodec.encode(preAtString));
128 path.add(-1 * int.parse(atString));
129 }
130 }
131 return path;
132 } else {
133 List<int> path = new List<int>.filled(length, 0);
134 for (int i = 0; i < length; i++) {
135 String component = components[i];
136 path[i] = _stringCodec.encode(component);
137 }
138 return path;
139 }
140 }
141
142 bool _hasLocalOffset(List<String> components) {
143 for (String component in components) {
144 if (component.indexOf('@') != -1) {
145 return true;
146 }
147 }
148 return false;
149 }
150 }
151
152
153 /**
154 * A manager for files content.
155 */
156 abstract class FileManager {
157 /**
158 * Removes all files.
159 */
160 void clear();
161
162 /**
163 * Deletes the file with the given name.
164 */
165 void delete(String name);
166
167 /**
168 * Read the entire file contents as a list of bytes.
169 */
170 Future<List<int>> read(String name);
171
172 /**
173 * Write a list of bytes to a file.
174 */
175 Future write(String name, Uint8List bytes);
176 }
177
178
179 /**
180 * A [FileManager] based [NodeManager].
181 */
182 class FileNodeManager implements NodeManager {
183 static int _VERSION = 1;
184
185 final ContextCodec contextCodec;
186
187 final ElementCodec elementCodec;
188
189 final StringCodec stringCodec;
190
191 final FileManager _fileManager;
192
193 int _locationCount = 0;
194
195 final Logger _logger;
196
197 Map<String, int> _nodeLocationCounts = {};
198
199 final RelationshipCodec _relationshipCodec;
200
201 FileNodeManager(this._fileManager, this._logger, this.stringCodec,
202 this.contextCodec, this.elementCodec, this._relationshipCodec);
203
204 @override
205 int get locationCount => _locationCount;
206
207 @override
208 void clear() {
209 _fileManager.clear();
210 }
211
212 @override
213 Future<IndexNode> getNode(String name) {
214 return _fileManager.read(name).then((List<int> bytes) {
215 if (bytes == null) {
216 return null;
217 }
218 _DataInputStream stream = new _DataInputStream(bytes);
219 return _readNode(stream);
220 }).catchError((e) {
Paul Berry 2014/06/20 15:46:01 Should probably log the stack trace as well.
scheglov 2014/06/20 16:16:56 OK, I will use CaughtException.
221 _logger.logError2("Exception during reading index file ${name}", e);
222 });
223 }
224
225 @override
226 IndexNode newNode(AnalysisContext context) => new IndexNode(context,
227 elementCodec, _relationshipCodec);
228
229 @override
230 Future putNode(String name, IndexNode node) {
231 // update location count
232 {
233 _locationCount -= _getLocationCount(name);
234 int nodeLocationCount = node.locationCount;
235 _nodeLocationCounts[name] = nodeLocationCount;
236 _locationCount += nodeLocationCount;
237 }
238 // write the node
239 return new Future.microtask(() {
240 _DataOutputStream stream = new _DataOutputStream();
241 _writeNode(node, stream);
242 var bytes = stream.getBytes();
243 return _fileManager.write(name, bytes);
244 }).catchError((e) {
Paul Berry 2014/06/20 15:46:01 Log the stack trace here too.
scheglov 2014/06/20 16:16:56 OK, I will use CaughtException.
245 _logger.logError2("Exception during writing index file ${name}", e);
246 });
247 }
248
249 @override
250 void removeNode(String name) {
251 // update location count
252 _locationCount -= _getLocationCount(name);
253 _nodeLocationCounts.remove(name);
254 // remove node
255 _fileManager.delete(name);
256 }
257
258 int _getLocationCount(String name) {
259 int locationCount = _nodeLocationCounts[name];
260 return locationCount != null ? locationCount : 0;
261 }
262
263 RelationKeyData _readElementRelationKey(_DataInputStream stream) {
264 int elementId = stream.readInt();
265 int relationshipId = stream.readInt();
266 return new RelationKeyData.forData(elementId, relationshipId);
267 }
268
269 LocationData _readLocationData(_DataInputStream stream) {
270 int elementId = stream.readInt();
271 int offset = stream.readInt();
272 int length = stream.readInt();
273 return new LocationData.forData(elementId, offset, length);
274 }
275
276 IndexNode _readNode(_DataInputStream stream) {
277 // check version
278 {
279 int version = stream.readInt();
280 if (version != _VERSION) {
281 throw new StateError(
282 "Version ${_VERSION} expected, but ${version} found.");
283 }
284 }
285 // context
286 int contextId = stream.readInt();
287 AnalysisContext context = contextCodec.decode(contextId);
288 if (context == null) {
289 return null;
290 }
291 // relations
292 Map<RelationKeyData, List<LocationData>> relations = {};
293 int numRelations = stream.readInt();
294 for (int i = 0; i < numRelations; i++) {
295 RelationKeyData key = _readElementRelationKey(stream);
296 int numLocations = stream.readInt();
297 List<LocationData> locations = new List<LocationData>();
298 for (int j = 0; j < numLocations; j++) {
299 locations.add(_readLocationData(stream));
300 }
301 relations[key] = locations;
302 }
303 // create IndexNode
304 IndexNode node = new IndexNode(context, elementCodec, _relationshipCodec);
305 node.relations = relations;
306 return node;
307 }
308
309 void _writeElementRelationKey(_DataOutputStream stream, RelationKeyData key) {
310 stream.writeInt(key.elementId);
311 stream.writeInt(key.relationshipId);
312 }
313
314 void _writeNode(IndexNode node, _DataOutputStream stream) {
315 // version
316 stream.writeInt(_VERSION);
317 // context
318 {
319 AnalysisContext context = node.context;
320 int contextId = contextCodec.encode(context);
321 stream.writeInt(contextId);
322 }
323 // relations
324 Map<RelationKeyData, List<LocationData>> relations = node.relations;
325 stream.writeInt(relations.length);
326 relations.forEach((key, locations) {
327 _writeElementRelationKey(stream, key);
328 stream.writeInt(locations.length);
329 for (LocationData location in locations) {
330 stream.writeInt(location.elementId);
331 stream.writeInt(location.offset);
332 stream.writeInt(location.length);
333 }
334 });
335 }
336 }
337
338
339 /**
340 * A single index file in-memory presentation.
341 */
342 class IndexNode {
343 final AnalysisContext context;
344
345 final ElementCodec _elementCodec;
346
347 Map<RelationKeyData, List<LocationData>> _relations = {};
Paul Berry 2014/06/20 15:46:01 Should this be a hashmap?
scheglov 2014/06/20 16:16:56 Done.
348
349 final RelationshipCodec _relationshipCodec;
350
351 IndexNode(this.context, this._elementCodec, this._relationshipCodec);
352
353 /**
354 * Returns number of locations in this node.
355 */
356 int get locationCount {
357 int locationCount = 0;
358 for (List<LocationData> locations in _relations.values) {
359 locationCount += locations.length;
360 }
361 return locationCount;
362 }
363
364 /**
365 * Returns the recorded relations.
366 */
367 Map<RelationKeyData, List<LocationData>> get relations => _relations;
368
369 /**
370 * Sets relations data. This method is used during loading data from a storage .
371 */
372 void set relations(Map<RelationKeyData, List<LocationData>> relations) {
373 this._relations.clear();
374 this._relations.addAll(relations);
375 }
376
377 /**
378 * Return the locations of the elements that have the given relationship with the given element.
379 *
380 * @param element the the element that has the relationship with the locations to be returned
381 * @param relationship the [Relationship] between the given element and the lo cations to be
382 * returned
383 */
384 List<Location> getRelationships(Element element, Relationship relationship) {
385 // prepare key
386 RelationKeyData key = new RelationKeyData.forObject(_elementCodec,
387 _relationshipCodec, element, relationship);
388 // find LocationData(s)
389 List<LocationData> locationDatas = _relations[key];
390 if (locationDatas == null) {
391 return Location.EMPTY_ARRAY;
392 }
393 // convert to Location(s)
394 List<Location> locations = [];
395 for (LocationData locationData in locationDatas) {
396 Location location = locationData.getLocation(context, _elementCodec);
397 if (location != null) {
398 locations.add(location);
399 }
400 }
401 return new List.from(locations);
Paul Berry 2014/06/20 15:46:01 "return locations;" should be sufficient. There's
scheglov 2014/06/20 16:16:56 Done.
402 }
403
404 /**
405 * Records that the given element and location have the given relationship.
406 *
407 * @param element the element that is related to the location
408 * @param relationship the [Relationship] between the element and the location
409 * @param location the [Location] where relationship happens
410 */
411 void recordRelationship(Element element, Relationship relationship,
412 Location location) {
413 RelationKeyData key = new RelationKeyData.forObject(_elementCodec,
414 _relationshipCodec, element, relationship);
415 // prepare LocationData(s)
416 List<LocationData> locationDatas = _relations[key];
417 if (locationDatas == null) {
418 locationDatas = [];
419 _relations[key] = locationDatas;
420 }
421 // add new LocationData
422 locationDatas.add(new LocationData.forObject(_elementCodec, location));
423 }
424 }
425
426
427 class IntArrayToIntMap {
428 // TODO(scheglov) consider using Int32List
429 final Map<List<int>, int> map = new HashMap<List<int>, int>(equals:
430 _intArrayEquals, hashCode: _intArrayHashCode);
431
432 IntArrayToIntMap(int initialCapacity, double loadFactor);
433
434 int get(List<int> key, int defaultValue) {
435 int value = map[key];
436 if (value == null) {
437 return defaultValue;
438 }
439 return value;
440 }
441
442 void put(List<int> key, int value) {
443 map[key] = value;
444 }
445
446 static bool _intArrayEquals(List<int> a, List<int> b) {
447 int length = a.length;
448 if (length != b.length) {
449 return false;
450 }
451 for (int i = 0; i < length; i++) {
452 if (a[i] != b[i]) {
453 return false;
454 }
455 }
456 return true;
457 }
458
459 static int _intArrayHashCode(List<int> key) {
460 return key.fold(0, (int result, int item) {
461 return 31 * result + item;
462 });
463 }
464 }
465
466
467 class IntToIntSetMap {
468 // TODO(scheglov) consider using Int32List
469 final Map<int, List<int>> _map = new HashMap<int, List<int>>();
Paul Berry 2014/06/20 15:46:01 Would it be better to use Map<int, HashSet<int>>?
scheglov 2014/06/20 16:16:56 It is intentional. At least in Java maps and sets
470 int _size = 0;
471
472 IntToIntSetMap(int initialCapacity, double loadFactor);
473
474 int get length => _size;
475
476 void add(int key, int value) {
477 List<int> values = _map[key];
478 if (values == null) {
479 values = new List<int>();
480 _map[key] = values;
481 }
482 if (values.indexOf(value) == -1) {
483 values.add(value);
484 _size++;
485 }
486 }
487
488 void clear() {
489 _map.clear();
490 _size = 0;
491 }
492
493 List<int> get(int key) {
494 List<int> values = _map[key];
495 if (values == null) {
496 values = <int>[];
497 }
498 return values;
499 }
500 }
501
502
503 /**
504 * A container with information about a [Location].
505 */
506 class LocationData {
507 final int elementId;
508 final int length;
509 final int offset;
510
511 LocationData.forData(this.elementId, this.offset, this.length);
512
513 LocationData.forObject(ElementCodec elementCodec, Location location)
514 : elementId = elementCodec.encode(location.element),
515 offset = location.offset,
516 length = location.length;
517
518 @override
519 int get hashCode {
520 return 31 * (31 * elementId + offset) + length;
521 }
522
523 @override
524 bool operator ==(Object obj) {
525 if (obj is! LocationData) {
526 return false;
527 }
528 LocationData other = obj;
529 return other.elementId == elementId && other.offset == offset &&
530 other.length == length;
531 }
532
533 /**
534 * Returns a {@link Location} that is represented by this {@link LocationData} .
535 */
536 Location getLocation(AnalysisContext context, ElementCodec elementCodec) {
537 Element element = elementCodec.decode(context, elementId);
538 if (element == null) {
539 return null;
540 }
541 return new Location(element, offset, length);
542 }
543 }
544
545
546 /**
547 * A manager for [IndexNode]s.
548 */
549 abstract class NodeManager {
550 /**
551 * The shared {@link ContextCodec} instance.
552 */
553 ContextCodec get contextCodec;
554
555 /**
556 * The shared {@link ElementCodec} instance.
557 */
558 ElementCodec get elementCodec;
559
560 /**
561 * A number of locations in all nodes.
562 */
563 int get locationCount;
564
565 /**
566 * The shared {@link StringCodec} instance.
567 */
568 StringCodec get stringCodec;
569
570 /**
571 * Removes all nodes.
572 */
573 void clear();
574
575 /**
576 * Returns the {@link IndexNode} with the given name, {@code null} if not foun d.
577 */
578 Future<IndexNode> getNode(String name);
579
580 /**
581 * Returns a new {@link IndexNode}.
582 */
583 IndexNode newNode(AnalysisContext context);
584
585 /**
586 * Associates the given {@link IndexNode} with the given name.
587 */
588 void putNode(String name, IndexNode node);
589
590 /**
591 * Removes the {@link IndexNode} with the given name.
592 */
593 void removeNode(String name);
594 }
595
596
597 /**
598 * An [Element] to [Location] relation key.
599 */
600 class RelationKeyData {
601 final int elementId;
602 final int relationshipId;
603
604 RelationKeyData.forData(this.elementId, this.relationshipId);
605
606 RelationKeyData.forObject(ElementCodec elementCodec,
607 RelationshipCodec relationshipCodec, Element element, Relationship relatio nship)
608 : elementId = elementCodec.encode(element),
609 relationshipId = relationshipCodec.encode(relationship);
610
611 @override
612 int get hashCode {
613 return 31 * elementId + relationshipId;
614 }
615
616 @override
617 bool operator ==(Object obj) {
618 if (obj is! RelationKeyData) {
619 return false;
620 }
621 RelationKeyData other = obj;
622 return other.elementId == elementId && other.relationshipId ==
623 relationshipId;
624 }
625 }
626
627
628 /**
629 * A helper that encodes/decodes [Relationship]s to/from integers.
630 */
631 class RelationshipCodec {
632 final StringCodec _stringCodec;
633
634 RelationshipCodec(this._stringCodec);
635
636 Relationship decode(int idIndex) {
637 String id = _stringCodec.decode(idIndex);
638 return Relationship.getRelationship(id);
639 }
640
641 int encode(Relationship relationship) {
642 String id = relationship.identifier;
643 return _stringCodec.encode(id);
644 }
645 }
646
647
648 /**
649 * An [IndexStore] which keeps index information in separate nodes for each unit .
650 */
651 class SplitIndexStore implements IndexStore {
652 /**
653 * The [ContextCodec] to encode/decode [AnalysisContext]s.
654 */
655 ContextCodec _contextCodec;
656
657 /**
658 * Information about "universe" elements. We need to keep them together to avo id loading of all
659 * index nodes.
660 *
661 * Order of keys: contextId, nodeId, Relationship.
662 */
663 Map<int, Map<int, Map<Relationship, List<LocationData>>>>
664 _contextNodeRelations = {};
Paul Berry 2014/06/20 15:46:01 1. Would a HashMap be better here? 2. Initializin
scheglov 2014/06/20 16:16:56 Done.
665
666 /**
667 * The mapping of library [Source] to the [Source]s of part units.
668 */
669 Map<AnalysisContext, Map<Source, Set<Source>>> _contextToLibraryToUnits = {};
670
671 /**
672 * The mapping of unit [Source] to the [Source]s of libraries it is used in.
673 */
674 Map<AnalysisContext, Map<Source, Set<Source>>> _contextToUnitToLibraries = {};
675
676 int _currentContextId = 0;
677
678 IndexNode _currentNode;
679
680 String _currentNodeName;
681
682 int _currentNodeNameId = 0;
683
684 /**
685 * The [ElementCodec] to encode/decode [Element]s.
686 */
687 ElementCodec _elementCodec;
688
689 /**
690 * A table mapping element names to the node names that may have relations wit h elements with
691 * these names.
692 */
693 IntToIntSetMap _nameToNodeNames = new IntToIntSetMap(10000, 0.75);
694
695 /**
696 * The [NodeManager] to get/put [IndexNode]s.
697 */
698 final NodeManager _nodeManager;
699
700 /**
701 * The set of known [Source]s.
702 */
703 Set<Source> _sources = new Set();
Paul Berry 2014/06/20 15:46:01 Would HashSet be better?
scheglov 2014/06/20 16:16:56 Done.
704
705 /**
706 * The [StringCodec] to encode/decode [String]s.
707 */
708 StringCodec _stringCodec;
709
710 SplitIndexStore(this._nodeManager) {
711 this._contextCodec = _nodeManager.contextCodec;
712 this._elementCodec = _nodeManager.elementCodec;
713 this._stringCodec = _nodeManager.stringCodec;
714 }
715
716 @override
717 String get statistics =>
718 "[${_nodeManager.locationCount} locations, ${_sources.length} sources, ${_ nameToNodeNames.length} names]";
719
720 @override
721 bool aboutToIndexDart(AnalysisContext context,
722 CompilationUnitElement unitElement) {
723 context = _unwrapContext(context);
724 // may be already disposed in other thread
725 if (context.isDisposed) {
726 return false;
727 }
728 // validate unit
729 if (unitElement == null) {
730 return false;
731 }
732 LibraryElement libraryElement = unitElement.library;
733 if (libraryElement == null) {
734 return false;
735 }
736 CompilationUnitElement definingUnitElement =
737 libraryElement.definingCompilationUnit;
738 if (definingUnitElement == null) {
739 return false;
740 }
741 // prepare sources
742 Source library = definingUnitElement.source;
743 Source unit = unitElement.source;
744 // special handling for the defining library unit
745 if (unit == library) {
746 // prepare new parts
747 Set<Source> newParts = new Set();
748 for (CompilationUnitElement part in libraryElement.parts) {
749 newParts.add(part.source);
750 }
751 // prepare old parts
752 Map<Source, Set<Source>> libraryToUnits =
753 _contextToLibraryToUnits[context];
754 if (libraryToUnits == null) {
755 libraryToUnits = {};
756 _contextToLibraryToUnits[context] = libraryToUnits;
757 }
758 Set<Source> oldParts = libraryToUnits[library];
759 // check if some parts are not in the library now
760 if (oldParts != null) {
761 Set<Source> noParts = oldParts.difference(newParts);
762 for (Source noPart in noParts) {
763 _removeLocations(context, library, noPart);
764 }
765 }
766 // remember new parts
767 libraryToUnits[library] = newParts;
768 }
769 // remember library/unit relations
770 _recordUnitInLibrary(context, library, unit);
771 _recordLibraryWithUnit(context, library, unit);
772 _sources.add(library);
773 _sources.add(unit);
774 // prepare node
775 String libraryName = library.fullName;
776 String unitName = unit.fullName;
777 int libraryNameIndex = _stringCodec.encode(libraryName);
778 int unitNameIndex = _stringCodec.encode(unitName);
779 _currentNodeName = "${libraryNameIndex}_${unitNameIndex}.index";
780 _currentNodeNameId = _stringCodec.encode(_currentNodeName);
781 _currentNode = _nodeManager.newNode(context);
782 _currentContextId = _contextCodec.encode(context);
783 // remove Universe information for the current node
784 for (Map<int, dynamic> nodeRelations in _contextNodeRelations.values) {
785 nodeRelations.remove(_currentNodeNameId);
786 }
787 // done
788 return true;
789 }
790
791 @override
792 bool aboutToIndexHtml(AnalysisContext context, HtmlElement htmlElement) {
793 context = _unwrapContext(context);
794 // may be already disposed in other thread
795 if (context.isDisposed) {
796 return false;
797 }
798 // remove locations
799 Source source = htmlElement.source;
800 _removeLocations(context, null, source);
801 // remember library/unit relations
802 _recordUnitInLibrary(context, null, source);
803 // prepare node
804 String sourceName = source.fullName;
805 int sourceNameIndex = _stringCodec.encode(sourceName);
806 _currentNodeName = "${sourceNameIndex}.index";
807 _currentNodeNameId = _stringCodec.encode(_currentNodeName);
808 _currentNode = _nodeManager.newNode(context);
809 return true;
810 }
811
812 @override
813 void clear() {
814 _nodeManager.clear();
815 _nameToNodeNames.clear();
816 }
817
818 @override
819 void doneIndex() {
820 if (_currentNode != null) {
821 _nodeManager.putNode(_currentNodeName, _currentNode);
822 _currentNodeName = null;
823 _currentNodeNameId = -1;
824 _currentNode = null;
825 _currentContextId = -1;
826 }
827 }
828
829 @override
830 List<Location> getRelationships(Element element, Relationship relationship) {
831 // TODO(scheglov) make IndexStore interface async
832 return <Location>[];
833 }
834
835 Future<List<Location>> getRelationshipsAsync(Element element,
836 Relationship relationship) {
837 // special support for UniverseElement
838 if (identical(element, UniverseElement.INSTANCE)) {
839 List<Location> locations = _getRelationshipsUniverse(relationship);
840 return new Future.value(locations);
841 }
842 // prepare node names
843 String name = _getElementName(element);
844 int nameId = _stringCodec.encode(name);
845 List<int> nodeNameIds = _nameToNodeNames.get(nameId);
846 // prepare Future(s) for reading each IndexNode
847 List<Future<List<Location>>> nodeFutures = <Future<List<Location>>>[];
848 for (int nodeNameId in nodeNameIds) {
849 String nodeName = _stringCodec.decode(nodeNameId);
850 Future<IndexNode> nodeFuture = _nodeManager.getNode(nodeName);
851 Future<List<Location>> locationsFuture = nodeFuture.then((node) {
852 if (node == null) {
853 // TODO(scheglov) remove node
854 return Location.EMPTY_ARRAY;
855 }
856 return node.getRelationships(element, relationship);
857 });
858 nodeFutures.add(locationsFuture);
859 }
860 // return Future that merges separate IndexNode Location(s)
861 return Future.wait(nodeFutures).then((List<List<Location>> locationsList) {
862 List<Location> allLocations = <Location>[];
863 for (List<Location> locations in locationsList) {
864 allLocations.addAll(locations);
865 }
866 return allLocations;
867 });
868 }
869
870 @override
871 void recordRelationship(Element element, Relationship relationship,
872 Location location) {
873 if (element == null || location == null) {
874 return;
875 }
876 // special support for UniverseElement
877 if (identical(element, UniverseElement.INSTANCE)) {
878 _recordRelationshipUniverse(relationship, location);
879 return;
880 }
881 // other elements
882 _recordNodeNameForElement(element);
883 _currentNode.recordRelationship(element, relationship, location);
884 }
885
886 @override
887 void removeContext(AnalysisContext context) {
888 context = _unwrapContext(context);
889 if (context == null) {
890 return;
891 }
892 // remove sources
893 removeSources(context, null);
894 // remove context information
895 _contextToLibraryToUnits.remove(context);
896 _contextToUnitToLibraries.remove(context);
897 _contextNodeRelations.remove(_contextCodec.encode(context));
898 }
899
900 @override
901 void removeSource(AnalysisContext context, Source source) {
902 context = _unwrapContext(context);
903 if (context == null) {
904 return;
905 }
906 // remove nodes for unit/library pairs
907 Map<Source, Set<Source>> unitToLibraries =
908 _contextToUnitToLibraries[context];
909 if (unitToLibraries != null) {
910 Set<Source> libraries = unitToLibraries.remove(source);
911 if (libraries != null) {
912 for (Source library in libraries) {
913 _removeLocations(context, library, source);
914 }
915 }
916 }
917 // remove nodes for library/unit pairs
918 Map<Source, Set<Source>> libraryToUnits = _contextToLibraryToUnits[context];
919 if (libraryToUnits != null) {
920 Set<Source> units = libraryToUnits.remove(source);
921 if (units != null) {
922 for (Source unit in units) {
923 _removeLocations(context, source, unit);
924 }
925 }
926 }
927 }
928
929 @override
930 void removeSources(AnalysisContext context, SourceContainer container) {
931 context = _unwrapContext(context);
932 if (context == null) {
933 return;
934 }
935 // remove nodes for unit/library pairs
936 Map<Source, Set<Source>> unitToLibraries =
937 _contextToUnitToLibraries[context];
938 if (unitToLibraries != null) {
939 List<Source> units = new List<Source>.from(unitToLibraries.keys);
940 for (Source source in units) {
941 if (container == null || container.contains(source)) {
942 removeSource(context, source);
943 }
944 }
945 }
946 // remove nodes for library/unit pairs
947 Map<Source, Set<Source>> libraryToUnits = _contextToLibraryToUnits[context];
948 if (libraryToUnits != null) {
949 List<Source> libraries = new List<Source>.from(libraryToUnits.keys);
950 for (Source source in libraries) {
951 if (container == null || container.contains(source)) {
952 removeSource(context, source);
953 }
954 }
955 }
956 }
957
958 String _getElementName(Element element) => element.name;
959
960 List<Location> _getRelationshipsUniverse(Relationship relationship) {
961 List<Location> locations = [];
962 _contextNodeRelations.forEach((contextId, contextRelations) {
963 AnalysisContext context = _contextCodec.decode(contextId);
964 if (context != null) {
965 for (Map<Relationship, List<LocationData>> nodeRelations in
966 contextRelations.values) {
967 List<LocationData> nodeLocations = nodeRelations[relationship];
968 if (nodeLocations != null) {
969 for (LocationData locationData in nodeLocations) {
970 Location location = locationData.getLocation(context,
971 _elementCodec);
972 if (location != null) {
973 locations.add(location);
974 }
975 }
976 }
977 }
978 }
979 });
980 return new List.from(locations);
Paul Berry 2014/06/20 15:46:01 Should be just "return locations;"
scheglov 2014/06/20 16:16:56 Done.
981 }
982
983 void _recordLibraryWithUnit(AnalysisContext context, Source library,
984 Source unit) {
985 Map<Source, Set<Source>> libraryToUnits = _contextToLibraryToUnits[context];
986 if (libraryToUnits == null) {
987 libraryToUnits = {};
988 _contextToLibraryToUnits[context] = libraryToUnits;
989 }
990 Set<Source> units = libraryToUnits[library];
991 if (units == null) {
992 units = new Set();
993 libraryToUnits[library] = units;
994 }
995 units.add(unit);
996 }
997
998 void _recordNodeNameForElement(Element element) {
999 String name = _getElementName(element);
1000 int nameId = _stringCodec.encode(name);
1001 _nameToNodeNames.add(nameId, _currentNodeNameId);
1002 }
1003
1004 void _recordRelationshipUniverse(Relationship relationship,
1005 Location location) {
1006 // in current context
1007 Map<int, Map<Relationship, List<LocationData>>> nodeRelations =
1008 _contextNodeRelations[_currentContextId];
1009 if (nodeRelations == null) {
1010 nodeRelations = {};
1011 _contextNodeRelations[_currentContextId] = nodeRelations;
1012 }
1013 // in current node
1014 Map<Relationship, List<LocationData>> relations =
1015 nodeRelations[_currentNodeNameId];
1016 if (relations == null) {
1017 relations = {};
1018 nodeRelations[_currentNodeNameId] = relations;
1019 }
1020 // for the given relationship
1021 List<LocationData> locations = relations[relationship];
1022 if (locations == null) {
1023 locations = [];
1024 relations[relationship] = locations;
1025 }
1026 // record LocationData
1027 locations.add(new LocationData.forObject(_elementCodec, location));
1028 }
1029
1030 void _recordUnitInLibrary(AnalysisContext context, Source library,
1031 Source unit) {
1032 Map<Source, Set<Source>> unitToLibraries =
1033 _contextToUnitToLibraries[context];
1034 if (unitToLibraries == null) {
1035 unitToLibraries = {};
1036 _contextToUnitToLibraries[context] = unitToLibraries;
1037 }
1038 Set<Source> libraries = unitToLibraries[unit];
1039 if (libraries == null) {
1040 libraries = new Set();
1041 unitToLibraries[unit] = libraries;
1042 }
1043 libraries.add(library);
1044 }
1045
1046 /**
1047 * Removes locations recorded in the given library/unit pair.
1048 */
1049 void _removeLocations(AnalysisContext context, Source library, Source unit) {
1050 // remove node
1051 String libraryName = library != null ? library.fullName : null;
1052 String unitName = unit.fullName;
1053 int libraryNameIndex = _stringCodec.encode(libraryName);
1054 int unitNameIndex = _stringCodec.encode(unitName);
1055 String nodeName = "${libraryNameIndex}_${unitNameIndex}.index";
1056 _nodeManager.removeNode(nodeName);
1057 // remove source
1058 _sources.remove(library);
1059 _sources.remove(unit);
1060 }
1061
1062 /**
1063 * When logging is on, [AnalysisEngine] actually creates
1064 * [InstrumentedAnalysisContextImpl], which wraps [AnalysisContextImpl] used t o create
1065 * actual [Element]s. So, in index we have to unwrap [InstrumentedAnalysisCont extImpl]
1066 * when perform any operation.
1067 */
1068 AnalysisContext _unwrapContext(AnalysisContext context) {
1069 if (context is InstrumentedAnalysisContextImpl) {
1070 context = (context as InstrumentedAnalysisContextImpl).basis;
1071 }
1072 return context;
1073 }
1074 }
1075
1076
1077 /**
1078 * A helper that encodes/decodes [String]s from/to integers.
1079 */
1080 class StringCodec {
1081 /**
1082 * A table mapping names to their unique indices.
1083 */
1084 final Map<String, int> nameToIndex = {};
1085
1086 /**
1087 * A table mapping indices to the corresponding strings.
1088 */
1089 List<String> _indexToName = [];
1090
1091 /**
1092 * Returns the [String] that corresponds to the given index.
1093 */
1094 String decode(int index) => _indexToName[index];
1095
1096 /**
1097 * Returns an unique index for the given [String].
1098 */
1099 int encode(String name) {
1100 int index = nameToIndex[name];
1101 if (index == null) {
1102 index = _indexToName.length;
1103 nameToIndex[name] = index;
1104 _indexToName.add(name);
1105 }
1106 return index;
1107 }
1108 }
1109
1110
1111 class _DataInputStream {
1112 ByteData _byteData;
1113 int _byteOffset = 0;
1114
1115 _DataInputStream(List<int> bytes) {
1116 ByteBuffer buffer = new Uint8List.fromList(bytes).buffer;
1117 _byteData = new ByteData.view(buffer);
1118 }
1119
1120 int readInt() {
1121 int result = _byteData.getInt32(_byteOffset);
1122 _byteOffset += 4;
1123 return result;
1124 }
1125 }
1126
1127
1128 class _DataOutputStream {
1129 BytesBuilder _buffer = new BytesBuilder();
1130
1131 Uint8List getBytes() {
1132 return new Uint8List.fromList(_buffer.takeBytes());
1133 }
1134
1135 void writeInt(int value) {
1136 _buffer.addByte((value & 0xFF000000) >> 24);
1137 _buffer.addByte((value & 0x00FF0000) >> 16);
1138 _buffer.addByte((value & 0x0000FF00) >> 8);
1139 _buffer.addByte(value & 0xFF);
1140 }
1141 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/generated/service_interfaces.dart ('k') | pkg/analysis_server/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698