OLD | NEW |
| (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 // This code was auto-generated, is not intended to be edited, and is subject to | |
6 // significant change. Please see the README file for more information. | |
7 | |
8 library engine.index; | |
9 | |
10 import 'dart:collection' show Queue; | |
11 import 'java_core.dart'; | |
12 import 'java_engine.dart'; | |
13 import 'source.dart'; | |
14 import 'scanner.dart' show Token; | |
15 import 'ast.dart'; | |
16 import 'element.dart'; | |
17 import 'resolver.dart' show Namespace, NamespaceBuilder; | |
18 import 'engine.dart'; | |
19 import 'html.dart' as ht; | |
20 | |
21 /** | |
22 * Visits resolved [CompilationUnit] and adds Angular specific relationships int
o | |
23 * [IndexStore]. | |
24 */ | |
25 class AngularDartIndexContributor extends GeneralizingAstVisitor<Object> { | |
26 final IndexStore _store; | |
27 | |
28 AngularDartIndexContributor(this._store); | |
29 | |
30 @override | |
31 Object visitClassDeclaration(ClassDeclaration node) { | |
32 ClassElement classElement = node.element; | |
33 if (classElement != null) { | |
34 List<ToolkitObjectElement> toolkitObjects = classElement.toolkitObjects; | |
35 for (ToolkitObjectElement object in toolkitObjects) { | |
36 if (object is AngularComponentElement) { | |
37 _indexComponent(object); | |
38 } | |
39 if (object is AngularDecoratorElement) { | |
40 AngularDecoratorElement directive = object; | |
41 _indexDirective(directive); | |
42 } | |
43 } | |
44 } | |
45 // stop visiting | |
46 return null; | |
47 } | |
48 | |
49 @override | |
50 Object visitCompilationUnitMember(CompilationUnitMember node) => null; | |
51 | |
52 void _indexComponent(AngularComponentElement component) { | |
53 _indexProperties(component.properties); | |
54 } | |
55 | |
56 void _indexDirective(AngularDecoratorElement directive) { | |
57 _indexProperties(directive.properties); | |
58 } | |
59 | |
60 /** | |
61 * Index [FieldElement] references from [AngularPropertyElement]s. | |
62 */ | |
63 void _indexProperties(List<AngularPropertyElement> properties) { | |
64 for (AngularPropertyElement property in properties) { | |
65 FieldElement field = property.field; | |
66 if (field != null) { | |
67 int offset = property.fieldNameOffset; | |
68 if (offset == -1) { | |
69 continue; | |
70 } | |
71 int length = field.name.length; | |
72 Location location = new Location(property, offset, length); | |
73 // getter reference | |
74 if (property.propertyKind.callsGetter()) { | |
75 PropertyAccessorElement getter = field.getter; | |
76 if (getter != null) { | |
77 _store.recordRelationship(getter, IndexConstants.IS_REFERENCED_BY_QU
ALIFIED, location); | |
78 } | |
79 } | |
80 // setter reference | |
81 if (property.propertyKind.callsSetter()) { | |
82 PropertyAccessorElement setter = field.setter; | |
83 if (setter != null) { | |
84 _store.recordRelationship(setter, IndexConstants.IS_REFERENCED_BY_QU
ALIFIED, location); | |
85 } | |
86 } | |
87 } | |
88 } | |
89 } | |
90 } | |
91 | |
92 /** | |
93 * Visits resolved [HtmlUnit] and adds relationships into [IndexStore]. | |
94 */ | |
95 class AngularHtmlIndexContributor extends ExpressionVisitor { | |
96 /** | |
97 * The [IndexStore] to record relations into. | |
98 */ | |
99 final IndexStore _store; | |
100 | |
101 /** | |
102 * The index contributor used to index Dart [Expression]s. | |
103 */ | |
104 IndexContributor _indexContributor; | |
105 | |
106 HtmlElement _htmlUnitElement; | |
107 | |
108 /** | |
109 * Initialize a newly created Angular HTML index contributor. | |
110 * | |
111 * @param store the [IndexStore] to record relations into. | |
112 */ | |
113 AngularHtmlIndexContributor(this._store) { | |
114 _indexContributor = new IndexContributor_AngularHtmlIndexContributor(_store,
this); | |
115 } | |
116 | |
117 @override | |
118 void visitExpression(Expression expression) { | |
119 // Formatter | |
120 if (expression is SimpleIdentifier) { | |
121 SimpleIdentifier identifier = expression; | |
122 Element element = identifier.bestElement; | |
123 if (element is AngularElement) { | |
124 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, _cr
eateLocationForIdentifier(identifier)); | |
125 return; | |
126 } | |
127 } | |
128 // index as a normal Dart expression | |
129 expression.accept(_indexContributor); | |
130 } | |
131 | |
132 @override | |
133 Object visitHtmlUnit(ht.HtmlUnit node) { | |
134 _htmlUnitElement = node.element; | |
135 CompilationUnitElement dartUnitElement = _htmlUnitElement.angularCompilation
Unit; | |
136 _indexContributor.enterScope(dartUnitElement); | |
137 return super.visitHtmlUnit(node); | |
138 } | |
139 | |
140 @override | |
141 Object visitXmlAttributeNode(ht.XmlAttributeNode node) { | |
142 Element element = node.element; | |
143 if (element != null) { | |
144 ht.Token nameToken = node.nameToken; | |
145 Location location = _createLocationForToken(nameToken); | |
146 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, locat
ion); | |
147 } | |
148 return super.visitXmlAttributeNode(node); | |
149 } | |
150 | |
151 @override | |
152 Object visitXmlTagNode(ht.XmlTagNode node) { | |
153 Element element = node.element; | |
154 if (element != null) { | |
155 // tag | |
156 { | |
157 ht.Token tagToken = node.tagToken; | |
158 Location location = _createLocationForToken(tagToken); | |
159 _store.recordRelationship(element, IndexConstants.ANGULAR_REFERENCE, loc
ation); | |
160 } | |
161 // maybe add closing tag range | |
162 ht.Token closingTag = node.closingTag; | |
163 if (closingTag != null) { | |
164 Location location = _createLocationForToken(closingTag); | |
165 _store.recordRelationship(element, IndexConstants.ANGULAR_CLOSING_TAG_RE
FERENCE, location); | |
166 } | |
167 } | |
168 return super.visitXmlTagNode(node); | |
169 } | |
170 | |
171 Location _createLocationForIdentifier(SimpleIdentifier identifier) => new Loca
tion(_htmlUnitElement, identifier.offset, identifier.length); | |
172 | |
173 Location _createLocationForToken(ht.Token token) => new Location(_htmlUnitElem
ent, token.offset, token.length); | |
174 } | |
175 | |
176 /** | |
177 * Instances of the [ClearOperation] implement an operation that removes all of
the | |
178 * information from the index. | |
179 */ | |
180 class ClearOperation implements IndexOperation { | |
181 /** | |
182 * The index store against which this operation is being run. | |
183 */ | |
184 final IndexStore _indexStore; | |
185 | |
186 ClearOperation(this._indexStore); | |
187 | |
188 @override | |
189 bool get isQuery => false; | |
190 | |
191 @override | |
192 void performOperation() { | |
193 _indexStore.clear(); | |
194 } | |
195 | |
196 @override | |
197 bool removeWhenSourceRemoved(Source source) => false; | |
198 | |
199 @override | |
200 String toString() => "ClearOperation()"; | |
201 } | |
202 | |
203 /** | |
204 * Recursively visits [HtmlUnit] and every embedded [Expression]. | |
205 */ | |
206 abstract class ExpressionVisitor extends ht.RecursiveXmlVisitor<Object> { | |
207 /** | |
208 * Visits the given [Expression]s embedded into tag or attribute. | |
209 * | |
210 * @param expression the [Expression] to visit, not `null` | |
211 */ | |
212 void visitExpression(Expression expression); | |
213 | |
214 @override | |
215 Object visitXmlAttributeNode(ht.XmlAttributeNode node) { | |
216 _visitExpressions(node.expressions); | |
217 return super.visitXmlAttributeNode(node); | |
218 } | |
219 | |
220 @override | |
221 Object visitXmlTagNode(ht.XmlTagNode node) { | |
222 _visitExpressions(node.expressions); | |
223 return super.visitXmlTagNode(node); | |
224 } | |
225 | |
226 /** | |
227 * Visits [Expression]s of the given [XmlExpression]s. | |
228 */ | |
229 void _visitExpressions(List<ht.XmlExpression> expressions) { | |
230 for (ht.XmlExpression xmlExpression in expressions) { | |
231 if (xmlExpression is AngularXmlExpression) { | |
232 AngularXmlExpression angularXmlExpression = xmlExpression; | |
233 List<Expression> dartExpressions = angularXmlExpression.expression.expre
ssions; | |
234 for (Expression dartExpression in dartExpressions) { | |
235 visitExpression(dartExpression); | |
236 } | |
237 } | |
238 if (xmlExpression is ht.RawXmlExpression) { | |
239 ht.RawXmlExpression rawXmlExpression = xmlExpression; | |
240 visitExpression(rawXmlExpression.expression); | |
241 } | |
242 } | |
243 } | |
244 } | |
245 | |
246 /** | |
247 * Instances of the [GetRelationshipsOperation] implement an operation used to a
ccess the | |
248 * locations that have a specified relationship with a specified element. | |
249 */ | |
250 class GetRelationshipsOperation implements IndexOperation { | |
251 final IndexStore _indexStore; | |
252 | |
253 final Element element; | |
254 | |
255 final Relationship relationship; | |
256 | |
257 final RelationshipCallback callback; | |
258 | |
259 /** | |
260 * Initialize a newly created operation that will access the locations that ha
ve a specified | |
261 * relationship with a specified element. | |
262 */ | |
263 GetRelationshipsOperation(this._indexStore, this.element, this.relationship, t
his.callback); | |
264 | |
265 @override | |
266 bool get isQuery => true; | |
267 | |
268 @override | |
269 void performOperation() { | |
270 List<Location> locations; | |
271 locations = _indexStore.getRelationships(element, relationship); | |
272 callback.hasRelationships(element, relationship, locations); | |
273 } | |
274 | |
275 @override | |
276 bool removeWhenSourceRemoved(Source source) => false; | |
277 | |
278 @override | |
279 String toString() => "GetRelationships(${element}, ${relationship})"; | |
280 } | |
281 | |
282 /** | |
283 * The interface [Index] defines the behavior of objects that maintain an index
storing | |
284 * [Relationship] between [Element]. All of the operations | |
285 * defined on the index are asynchronous, and results, when there are any, are p
rovided through a | |
286 * callback. | |
287 * | |
288 * Despite being asynchronous, the results of the operations are guaranteed to b
e consistent with | |
289 * the expectation that operations are performed in the order in which they are
requested. | |
290 * Modification operations are executed before any read operation. There is no g
uarantee about the | |
291 * order in which the callbacks for read operations will be invoked. | |
292 */ | |
293 abstract class Index { | |
294 /** | |
295 * Asynchronously remove from the index all of the information. | |
296 */ | |
297 void clear(); | |
298 | |
299 /** | |
300 * Asynchronously invoke the given callback with an array containing all of th
e locations of the | |
301 * elements that have the given relationship with the given element. For examp
le, if the element | |
302 * represents a method and the relationship is the is-referenced-by relationsh
ip, then the | |
303 * locations that will be passed into the callback will be all of the places w
here the method is | |
304 * invoked. | |
305 * | |
306 * @param element the element that has the relationship with the locations to
be returned | |
307 * @param relationship the relationship between the given element and the loca
tions to be returned | |
308 * @param callback the callback that will be invoked when the locations are fo
und | |
309 */ | |
310 void getRelationships(Element element, Relationship relationship, Relationship
Callback callback); | |
311 | |
312 /** | |
313 * Answer index statistics. | |
314 */ | |
315 String get statistics; | |
316 | |
317 /** | |
318 * Asynchronously process the given [HtmlUnit] in order to record the relation
ships. | |
319 * | |
320 * @param context the [AnalysisContext] in which [HtmlUnit] was resolved | |
321 * @param unit the [HtmlUnit] being indexed | |
322 */ | |
323 void indexHtmlUnit(AnalysisContext context, ht.HtmlUnit unit); | |
324 | |
325 /** | |
326 * Asynchronously process the given [CompilationUnit] in order to record the r
elationships. | |
327 * | |
328 * @param context the [AnalysisContext] in which [CompilationUnit] was resolve
d | |
329 * @param unit the [CompilationUnit] being indexed | |
330 */ | |
331 void indexUnit(AnalysisContext context, CompilationUnit unit); | |
332 | |
333 /** | |
334 * Asynchronously remove from the index all of the information associated with
the given context. | |
335 * | |
336 * This method should be invoked when a context is disposed. | |
337 * | |
338 * @param context the [AnalysisContext] to remove | |
339 */ | |
340 void removeContext(AnalysisContext context); | |
341 | |
342 /** | |
343 * Asynchronously remove from the index all of the information associated with
elements or | |
344 * locations in the given source. This includes relationships between an eleme
nt in the given | |
345 * source and any other locations, relationships between any other elements an
d a location within | |
346 * the given source. | |
347 * | |
348 * This method should be invoked when a source is no longer part of the code b
ase. | |
349 * | |
350 * @param context the [AnalysisContext] in which [Source] being removed | |
351 * @param source the [Source] being removed | |
352 */ | |
353 void removeSource(AnalysisContext context, Source source); | |
354 | |
355 /** | |
356 * Asynchronously remove from the index all of the information associated with
elements or | |
357 * locations in the given sources. This includes relationships between an elem
ent in the given | |
358 * sources and any other locations, relationships between any other elements a
nd a location within | |
359 * the given sources. | |
360 * | |
361 * This method should be invoked when multiple sources are no longer part of t
he code base. | |
362 * | |
363 * @param the [AnalysisContext] in which [Source]s being removed | |
364 * @param container the [SourceContainer] holding the sources being removed | |
365 */ | |
366 void removeSources(AnalysisContext context, SourceContainer container); | |
367 | |
368 /** | |
369 * Should be called in separate [Thread] to process request in this [Index]. D
oes not | |
370 * return until the [stop] method is called. | |
371 */ | |
372 void run(); | |
373 | |
374 /** | |
375 * Should be called to stop process running [run], so stop processing requests
. | |
376 */ | |
377 void stop(); | |
378 } | |
379 | |
380 /** | |
381 * Constants used when populating and accessing the index. | |
382 */ | |
383 abstract class IndexConstants { | |
384 /** | |
385 * An element used to represent the universe. | |
386 */ | |
387 static final Element UNIVERSE = UniverseElement.INSTANCE; | |
388 | |
389 /** | |
390 * The relationship used to indicate that a container (the left-operand) conta
ins the definition | |
391 * of a class at a specific location (the right operand). | |
392 */ | |
393 static final Relationship DEFINES_CLASS = Relationship.getRelationship("define
s-class"); | |
394 | |
395 /** | |
396 * The relationship used to indicate that a container (the left-operand) conta
ins the definition | |
397 * of a function at a specific location (the right operand). | |
398 */ | |
399 static final Relationship DEFINES_FUNCTION = Relationship.getRelationship("def
ines-function"); | |
400 | |
401 /** | |
402 * The relationship used to indicate that a container (the left-operand) conta
ins the definition | |
403 * of a class type alias at a specific location (the right operand). | |
404 */ | |
405 static final Relationship DEFINES_CLASS_ALIAS = Relationship.getRelationship("
defines-class-alias"); | |
406 | |
407 /** | |
408 * The relationship used to indicate that a container (the left-operand) conta
ins the definition | |
409 * of a function type at a specific location (the right operand). | |
410 */ | |
411 static final Relationship DEFINES_FUNCTION_TYPE = Relationship.getRelationship
("defines-function-type"); | |
412 | |
413 /** | |
414 * The relationship used to indicate that a container (the left-operand) conta
ins the definition | |
415 * of a method at a specific location (the right operand). | |
416 */ | |
417 static final Relationship DEFINES_VARIABLE = Relationship.getRelationship("def
ines-variable"); | |
418 | |
419 /** | |
420 * The relationship used to indicate that a name (the left-operand) is defined
at a specific | |
421 * location (the right operand). | |
422 */ | |
423 static final Relationship IS_DEFINED_BY = Relationship.getRelationship("is-def
ined-by"); | |
424 | |
425 /** | |
426 * The relationship used to indicate that a type (the left-operand) is extende
d by a type at a | |
427 * specific location (the right operand). | |
428 */ | |
429 static final Relationship IS_EXTENDED_BY = Relationship.getRelationship("is-ex
tended-by"); | |
430 | |
431 /** | |
432 * The relationship used to indicate that a type (the left-operand) is impleme
nted by a type at a | |
433 * specific location (the right operand). | |
434 */ | |
435 static final Relationship IS_IMPLEMENTED_BY = Relationship.getRelationship("is
-implemented-by"); | |
436 | |
437 /** | |
438 * The relationship used to indicate that a type (the left-operand) is mixed i
nto a type at a | |
439 * specific location (the right operand). | |
440 */ | |
441 static final Relationship IS_MIXED_IN_BY = Relationship.getRelationship("is-mi
xed-in-by"); | |
442 | |
443 /** | |
444 * The relationship used to indicate that a parameter or variable (the left-op
erand) is read at a | |
445 * specific location (the right operand). | |
446 */ | |
447 static final Relationship IS_READ_BY = Relationship.getRelationship("is-read-b
y"); | |
448 | |
449 /** | |
450 * The relationship used to indicate that a parameter or variable (the left-op
erand) is both read | |
451 * and modified at a specific location (the right operand). | |
452 */ | |
453 static final Relationship IS_READ_WRITTEN_BY = Relationship.getRelationship("i
s-read-written-by"); | |
454 | |
455 /** | |
456 * The relationship used to indicate that a parameter or variable (the left-op
erand) is modified | |
457 * (assigned to) at a specific location (the right operand). | |
458 */ | |
459 static final Relationship IS_WRITTEN_BY = Relationship.getRelationship("is-wri
tten-by"); | |
460 | |
461 /** | |
462 * The relationship used to indicate that an element (the left-operand) is ref
erenced at a | |
463 * specific location (the right operand). This is used for everything except r
ead/write operations | |
464 * for fields, parameters, and variables. Those use either [IS_REFERENCED_BY_Q
UALIFIED], | |
465 * [IS_REFERENCED_BY_UNQUALIFIED], [IS_READ_BY], [IS_WRITTEN_BY] or | |
466 * [IS_READ_WRITTEN_BY], as appropriate. | |
467 */ | |
468 static final Relationship IS_REFERENCED_BY = Relationship.getRelationship("is-
referenced-by"); | |
469 | |
470 /** | |
471 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is | |
472 * referenced at a specific location (the right operand). This is used for qua
lified resolved | |
473 * references to methods and fields. | |
474 */ | |
475 static final Relationship IS_REFERENCED_BY_QUALIFIED_RESOLVED = Relationship.g
etRelationship("is-referenced-by-qualified-resolved"); | |
476 | |
477 /** | |
478 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is | |
479 * referenced at a specific location (the right operand). This is used for qua
lified unresolved | |
480 * references to methods and fields. | |
481 */ | |
482 static final Relationship IS_REFERENCED_BY_QUALIFIED_UNRESOLVED = Relationship
.getRelationship("is-referenced-by-qualified-unresolved"); | |
483 | |
484 /** | |
485 * The relationship used to indicate that an element (the left-operand) is ref
erenced at a | |
486 * specific location (the right operand). This is used for field accessors and
methods. | |
487 */ | |
488 static final Relationship IS_REFERENCED_BY_QUALIFIED = Relationship.getRelatio
nship("is-referenced-by-qualified"); | |
489 | |
490 /** | |
491 * The relationship used to indicate that an element (the left-operand) is ref
erenced at a | |
492 * specific location (the right operand). This is used for field accessors and
methods. | |
493 */ | |
494 static final Relationship IS_REFERENCED_BY_UNQUALIFIED = Relationship.getRelat
ionship("is-referenced-by-unqualified"); | |
495 | |
496 /** | |
497 * The relationship used to indicate that an element (the left-operand) is inv
oked at a specific | |
498 * location (the right operand). This is used for functions. | |
499 */ | |
500 static final Relationship IS_INVOKED_BY = Relationship.getRelationship("is-inv
oked-by"); | |
501 | |
502 /** | |
503 * The relationship used to indicate that an element (the left-operand) is inv
oked at a specific | |
504 * location (the right operand). This is used for methods. | |
505 */ | |
506 static final Relationship IS_INVOKED_BY_QUALIFIED = Relationship.getRelationsh
ip("is-invoked-by-qualified"); | |
507 | |
508 /** | |
509 * The relationship used to indicate that an element (the left-operand) is inv
oked at a specific | |
510 * location (the right operand). This is used for methods. | |
511 */ | |
512 static final Relationship IS_INVOKED_BY_UNQUALIFIED = Relationship.getRelation
ship("is-invoked-by-unqualified"); | |
513 | |
514 /** | |
515 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is invoked | |
516 * at a specific location (the right operand). This is used for resolved invoc
ations. | |
517 */ | |
518 static final Relationship NAME_IS_INVOKED_BY_RESOLVED = Relationship.getRelati
onship("name-is-invoked-by-resolved"); | |
519 | |
520 /** | |
521 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is read at | |
522 * a specific location (the right operand). | |
523 */ | |
524 static final Relationship NAME_IS_READ_BY_RESOLVED = Relationship.getRelations
hip("name-is-read-by-resolved"); | |
525 | |
526 /** | |
527 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is both | |
528 * read and written at a specific location (the right operand). | |
529 */ | |
530 static final Relationship NAME_IS_READ_WRITTEN_BY_RESOLVED = Relationship.getR
elationship("name-is-read-written-by-resolved"); | |
531 | |
532 /** | |
533 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is written | |
534 * at a specific location (the right operand). | |
535 */ | |
536 static final Relationship NAME_IS_WRITTEN_BY_RESOLVED = Relationship.getRelati
onship("name-is-written-by-resolved"); | |
537 | |
538 /** | |
539 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is invoked | |
540 * at a specific location (the right operand). This is used for unresolved inv
ocations. | |
541 */ | |
542 static final Relationship NAME_IS_INVOKED_BY_UNRESOLVED = Relationship.getRela
tionship("name-is-invoked-by-unresolved"); | |
543 | |
544 /** | |
545 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is read at | |
546 * a specific location (the right operand). | |
547 */ | |
548 static final Relationship NAME_IS_READ_BY_UNRESOLVED = Relationship.getRelatio
nship("name-is-read-by-unresolved"); | |
549 | |
550 /** | |
551 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is both | |
552 * read and written at a specific location (the right operand). | |
553 */ | |
554 static final Relationship NAME_IS_READ_WRITTEN_BY_UNRESOLVED = Relationship.ge
tRelationship("name-is-read-written-by-unresolved"); | |
555 | |
556 /** | |
557 * The relationship used to indicate that an [NameElementImpl] (the left-opera
nd) is written | |
558 * at a specific location (the right operand). | |
559 */ | |
560 static final Relationship NAME_IS_WRITTEN_BY_UNRESOLVED = Relationship.getRela
tionship("name-is-written-by-unresolved"); | |
561 | |
562 /** | |
563 * Reference to some [AngularElement]. | |
564 */ | |
565 static final Relationship ANGULAR_REFERENCE = Relationship.getRelationship("an
gular-reference"); | |
566 | |
567 /** | |
568 * Reference to some closing tag of an XML element. | |
569 */ | |
570 static final Relationship ANGULAR_CLOSING_TAG_REFERENCE = Relationship.getRela
tionship("angular-closing-tag-reference"); | |
571 } | |
572 | |
573 /** | |
574 * Visits resolved AST and adds relationships into [IndexStore]. | |
575 */ | |
576 class IndexContributor extends GeneralizingAstVisitor<Object> { | |
577 /** | |
578 * @return the [Location] representing location of the [Element]. | |
579 */ | |
580 static Location createLocation(Element element) { | |
581 if (element != null) { | |
582 int offset = element.nameOffset; | |
583 int length = element.displayName.length; | |
584 return new Location(element, offset, length); | |
585 } | |
586 return null; | |
587 } | |
588 | |
589 /** | |
590 * @return the [ImportElement] that is referenced by this node with [PrefixEle
ment], | |
591 * may be `null`. | |
592 */ | |
593 static ImportElement getImportElement(SimpleIdentifier prefixNode) { | |
594 IndexContributor_ImportElementInfo info = getImportElementInfo(prefixNode); | |
595 return info != null ? info._element : null; | |
596 } | |
597 | |
598 /** | |
599 * @return the [ImportElementInfo] with [ImportElement] that is referenced by
this | |
600 * node with [PrefixElement], may be `null`. | |
601 */ | |
602 static IndexContributor_ImportElementInfo getImportElementInfo(SimpleIdentifie
r prefixNode) { | |
603 IndexContributor_ImportElementInfo info = new IndexContributor_ImportElement
Info(); | |
604 // prepare environment | |
605 AstNode parent = prefixNode.parent; | |
606 CompilationUnit unit = prefixNode.getAncestor((node) => node is CompilationU
nit); | |
607 LibraryElement libraryElement = unit.element.library; | |
608 // prepare used element | |
609 Element usedElement = null; | |
610 if (parent is PrefixedIdentifier) { | |
611 PrefixedIdentifier prefixed = parent; | |
612 if (identical(prefixed.prefix, prefixNode)) { | |
613 usedElement = prefixed.staticElement; | |
614 info._periodEnd = prefixed.period.end; | |
615 } | |
616 } | |
617 if (parent is MethodInvocation) { | |
618 MethodInvocation invocation = parent; | |
619 if (identical(invocation.target, prefixNode)) { | |
620 usedElement = invocation.methodName.staticElement; | |
621 info._periodEnd = invocation.period.end; | |
622 } | |
623 } | |
624 // we need used Element | |
625 if (usedElement == null) { | |
626 return null; | |
627 } | |
628 // find ImportElement | |
629 String prefix = prefixNode.name; | |
630 Map<ImportElement, Set<Element>> importElementsMap = {}; | |
631 info._element = _internalGetImportElement(libraryElement, prefix, usedElemen
t, importElementsMap); | |
632 if (info._element == null) { | |
633 return null; | |
634 } | |
635 return info; | |
636 } | |
637 | |
638 /** | |
639 * If the given expression has resolved type, returns the new location with th
is type. | |
640 * | |
641 * @param location the base location | |
642 * @param expression the expression assigned at the given location | |
643 */ | |
644 static Location _getLocationWithExpressionType(Location location, Expression e
xpression) { | |
645 if (expression != null) { | |
646 return new LocationWithData<DartType>.con1(location, expression.bestType); | |
647 } | |
648 return location; | |
649 } | |
650 | |
651 /** | |
652 * If the given node is the part of the [ConstructorFieldInitializer], returns
location with | |
653 * type of the initializer expression. | |
654 */ | |
655 static Location _getLocationWithInitializerType(SimpleIdentifier node, Locatio
n location) { | |
656 if (node.parent is ConstructorFieldInitializer) { | |
657 ConstructorFieldInitializer initializer = node.parent as ConstructorFieldI
nitializer; | |
658 if (identical(initializer.fieldName, node)) { | |
659 location = _getLocationWithExpressionType(location, initializer.expressi
on); | |
660 } | |
661 } | |
662 return location; | |
663 } | |
664 | |
665 /** | |
666 * If the given identifier has a synthetic [PropertyAccessorElement], i.e. acc
essor for | |
667 * normal field, and it is LHS of assignment, then include [Type] of the assig
ned value into | |
668 * the [Location]. | |
669 * | |
670 * @param identifier the identifier to record location | |
671 * @param element the element of the identifier | |
672 * @param location the raw location | |
673 * @return the [Location] with the type of the assigned value | |
674 */ | |
675 static Location _getLocationWithTypeAssignedToField(SimpleIdentifier identifie
r, Element element, Location location) { | |
676 // we need accessor | |
677 if (element is! PropertyAccessorElement) { | |
678 return location; | |
679 } | |
680 PropertyAccessorElement accessor = element as PropertyAccessorElement; | |
681 // should be setter | |
682 if (!accessor.isSetter) { | |
683 return location; | |
684 } | |
685 // accessor should be synthetic, i.e. field normal | |
686 if (!accessor.isSynthetic) { | |
687 return location; | |
688 } | |
689 // should be LHS of assignment | |
690 AstNode parent; | |
691 { | |
692 AstNode node = identifier; | |
693 parent = node.parent; | |
694 // new T().field = x; | |
695 if (parent is PropertyAccess) { | |
696 PropertyAccess propertyAccess = parent as PropertyAccess; | |
697 if (identical(propertyAccess.propertyName, node)) { | |
698 node = propertyAccess; | |
699 parent = propertyAccess.parent; | |
700 } | |
701 } | |
702 // obj.field = x; | |
703 if (parent is PrefixedIdentifier) { | |
704 PrefixedIdentifier prefixedIdentifier = parent as PrefixedIdentifier; | |
705 if (identical(prefixedIdentifier.identifier, node)) { | |
706 node = prefixedIdentifier; | |
707 parent = prefixedIdentifier.parent; | |
708 } | |
709 } | |
710 } | |
711 // OK, remember the type | |
712 if (parent is AssignmentExpression) { | |
713 AssignmentExpression assignment = parent as AssignmentExpression; | |
714 Expression rhs = assignment.rightHandSide; | |
715 location = _getLocationWithExpressionType(location, rhs); | |
716 } | |
717 // done | |
718 return location; | |
719 } | |
720 | |
721 /** | |
722 * @return the [ImportElement] that declares given [PrefixElement] and imports
library | |
723 * with given "usedElement". | |
724 */ | |
725 static ImportElement _internalGetImportElement(LibraryElement libraryElement,
String prefix, Element usedElement, Map<ImportElement, Set<Element>> importEleme
ntsMap) { | |
726 // validate Element | |
727 if (usedElement == null) { | |
728 return null; | |
729 } | |
730 if (usedElement.enclosingElement is! CompilationUnitElement) { | |
731 return null; | |
732 } | |
733 LibraryElement usedLibrary = usedElement.library; | |
734 // find ImportElement that imports used library with used prefix | |
735 List<ImportElement> candidates = null; | |
736 for (ImportElement importElement in libraryElement.imports) { | |
737 // required library | |
738 if (importElement.importedLibrary != usedLibrary) { | |
739 continue; | |
740 } | |
741 // required prefix | |
742 PrefixElement prefixElement = importElement.prefix; | |
743 if (prefix == null) { | |
744 if (prefixElement != null) { | |
745 continue; | |
746 } | |
747 } else { | |
748 if (prefixElement == null) { | |
749 continue; | |
750 } | |
751 if (prefix != prefixElement.name) { | |
752 continue; | |
753 } | |
754 } | |
755 // no combinators => only possible candidate | |
756 if (importElement.combinators.length == 0) { | |
757 return importElement; | |
758 } | |
759 // OK, we have candidate | |
760 if (candidates == null) { | |
761 candidates = []; | |
762 } | |
763 candidates.add(importElement); | |
764 } | |
765 // no candidates, probably element is defined in this library | |
766 if (candidates == null) { | |
767 return null; | |
768 } | |
769 // one candidate | |
770 if (candidates.length == 1) { | |
771 return candidates[0]; | |
772 } | |
773 // ensure that each ImportElement has set of elements | |
774 for (ImportElement importElement in candidates) { | |
775 if (importElementsMap.containsKey(importElement)) { | |
776 continue; | |
777 } | |
778 Namespace namespace = new NamespaceBuilder().createImportNamespaceForDirec
tive(importElement); | |
779 Set<Element> elements = new Set(); | |
780 importElementsMap[importElement] = elements; | |
781 } | |
782 // use import namespace to choose correct one | |
783 for (MapEntry<ImportElement, Set<Element>> entry in getMapEntrySet(importEle
mentsMap)) { | |
784 if (entry.getValue().contains(usedElement)) { | |
785 return entry.getKey(); | |
786 } | |
787 } | |
788 // not found | |
789 return null; | |
790 } | |
791 | |
792 /** | |
793 * @return `true` if given "node" is part of an import [Combinator]. | |
794 */ | |
795 static bool _isIdentifierInImportCombinator(SimpleIdentifier node) { | |
796 AstNode parent = node.parent; | |
797 return parent is Combinator; | |
798 } | |
799 | |
800 /** | |
801 * @return `true` if given "node" is part of [PrefixedIdentifier] "prefix.node
". | |
802 */ | |
803 static bool _isIdentifierInPrefixedIdentifier(SimpleIdentifier node) { | |
804 AstNode parent = node.parent; | |
805 return parent is PrefixedIdentifier && identical(parent.identifier, node); | |
806 } | |
807 | |
808 final IndexStore _store; | |
809 | |
810 LibraryElement _libraryElement; | |
811 | |
812 Map<ImportElement, Set<Element>> _importElementsMap = {}; | |
813 | |
814 /** | |
815 * A stack whose top element (the element with the largest index) is an elemen
t representing the | |
816 * inner-most enclosing scope. | |
817 */ | |
818 Queue<Element> _elementStack = new Queue(); | |
819 | |
820 IndexContributor(this._store); | |
821 | |
822 /** | |
823 * Enter a new scope represented by the given [Element]. | |
824 */ | |
825 void enterScope(Element element) { | |
826 _elementStack.addFirst(element); | |
827 } | |
828 | |
829 /** | |
830 * @return the inner-most enclosing [Element], may be `null`. | |
831 */ | |
832 Element peekElement() { | |
833 for (Element element in _elementStack) { | |
834 if (element != null) { | |
835 return element; | |
836 } | |
837 } | |
838 return null; | |
839 } | |
840 | |
841 @override | |
842 Object visitAssignmentExpression(AssignmentExpression node) { | |
843 _recordOperatorReference(node.operator, node.bestElement); | |
844 return super.visitAssignmentExpression(node); | |
845 } | |
846 | |
847 @override | |
848 Object visitBinaryExpression(BinaryExpression node) { | |
849 _recordOperatorReference(node.operator, node.bestElement); | |
850 return super.visitBinaryExpression(node); | |
851 } | |
852 | |
853 @override | |
854 Object visitClassDeclaration(ClassDeclaration node) { | |
855 ClassElement element = node.element; | |
856 enterScope(element); | |
857 try { | |
858 _recordElementDefinition(element, IndexConstants.DEFINES_CLASS); | |
859 { | |
860 ExtendsClause extendsClause = node.extendsClause; | |
861 if (extendsClause != null) { | |
862 TypeName superclassNode = extendsClause.superclass; | |
863 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
864 } else { | |
865 InterfaceType superType = element.supertype; | |
866 if (superType != null) { | |
867 ClassElement objectElement = superType.element; | |
868 recordRelationship(objectElement, IndexConstants.IS_EXTENDED_BY, _cr
eateLocationFromOffset(node.name.offset, 0)); | |
869 } | |
870 } | |
871 } | |
872 { | |
873 WithClause withClause = node.withClause; | |
874 if (withClause != null) { | |
875 for (TypeName mixinNode in withClause.mixinTypes) { | |
876 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
877 } | |
878 } | |
879 } | |
880 { | |
881 ImplementsClause implementsClause = node.implementsClause; | |
882 if (implementsClause != null) { | |
883 for (TypeName interfaceNode in implementsClause.interfaces) { | |
884 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
885 } | |
886 } | |
887 } | |
888 return super.visitClassDeclaration(node); | |
889 } finally { | |
890 _exitScope(); | |
891 } | |
892 } | |
893 | |
894 @override | |
895 Object visitClassTypeAlias(ClassTypeAlias node) { | |
896 ClassElement element = node.element; | |
897 enterScope(element); | |
898 try { | |
899 _recordElementDefinition(element, IndexConstants.DEFINES_CLASS_ALIAS); | |
900 { | |
901 TypeName superclassNode = node.superclass; | |
902 if (superclassNode != null) { | |
903 _recordSuperType(superclassNode, IndexConstants.IS_EXTENDED_BY); | |
904 } | |
905 } | |
906 { | |
907 WithClause withClause = node.withClause; | |
908 if (withClause != null) { | |
909 for (TypeName mixinNode in withClause.mixinTypes) { | |
910 _recordSuperType(mixinNode, IndexConstants.IS_MIXED_IN_BY); | |
911 } | |
912 } | |
913 } | |
914 { | |
915 ImplementsClause implementsClause = node.implementsClause; | |
916 if (implementsClause != null) { | |
917 for (TypeName interfaceNode in implementsClause.interfaces) { | |
918 _recordSuperType(interfaceNode, IndexConstants.IS_IMPLEMENTED_BY); | |
919 } | |
920 } | |
921 } | |
922 return super.visitClassTypeAlias(node); | |
923 } finally { | |
924 _exitScope(); | |
925 } | |
926 } | |
927 | |
928 @override | |
929 Object visitCompilationUnit(CompilationUnit node) { | |
930 CompilationUnitElement unitElement = node.element; | |
931 if (unitElement != null) { | |
932 _elementStack.add(unitElement); | |
933 _libraryElement = unitElement.enclosingElement; | |
934 if (_libraryElement != null) { | |
935 return super.visitCompilationUnit(node); | |
936 } | |
937 } | |
938 return null; | |
939 } | |
940 | |
941 @override | |
942 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
943 ConstructorElement element = node.element; | |
944 // define | |
945 { | |
946 Location location; | |
947 if (node.name != null) { | |
948 int start = node.period.offset; | |
949 int end = node.name.end; | |
950 location = _createLocationFromOffset(start, end - start); | |
951 } else { | |
952 int start = node.returnType.end; | |
953 location = _createLocationFromOffset(start, 0); | |
954 } | |
955 recordRelationship(element, IndexConstants.IS_DEFINED_BY, location); | |
956 } | |
957 // visit children | |
958 enterScope(element); | |
959 try { | |
960 return super.visitConstructorDeclaration(node); | |
961 } finally { | |
962 _exitScope(); | |
963 } | |
964 } | |
965 | |
966 @override | |
967 Object visitConstructorName(ConstructorName node) { | |
968 ConstructorElement element = node.staticElement; | |
969 // in 'class B = A;' actually A constructors are invoked | |
970 if (element != null && element.isSynthetic && element.redirectedConstructor
!= null) { | |
971 element = element.redirectedConstructor; | |
972 } | |
973 // prepare location | |
974 Location location; | |
975 if (node.name != null) { | |
976 int start = node.period.offset; | |
977 int end = node.name.end; | |
978 location = _createLocationFromOffset(start, end - start); | |
979 } else { | |
980 int start = node.type.end; | |
981 location = _createLocationFromOffset(start, 0); | |
982 } | |
983 // record relationship | |
984 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
985 return super.visitConstructorName(node); | |
986 } | |
987 | |
988 @override | |
989 Object visitExportDirective(ExportDirective node) { | |
990 ExportElement element = node.element; | |
991 if (element != null) { | |
992 LibraryElement expLibrary = element.exportedLibrary; | |
993 _recordLibraryReference(node, expLibrary); | |
994 } | |
995 return super.visitExportDirective(node); | |
996 } | |
997 | |
998 @override | |
999 Object visitFormalParameter(FormalParameter node) { | |
1000 ParameterElement element = node.element; | |
1001 enterScope(element); | |
1002 try { | |
1003 return super.visitFormalParameter(node); | |
1004 } finally { | |
1005 _exitScope(); | |
1006 } | |
1007 } | |
1008 | |
1009 @override | |
1010 Object visitFunctionDeclaration(FunctionDeclaration node) { | |
1011 Element element = node.element; | |
1012 _recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION); | |
1013 enterScope(element); | |
1014 try { | |
1015 return super.visitFunctionDeclaration(node); | |
1016 } finally { | |
1017 _exitScope(); | |
1018 } | |
1019 } | |
1020 | |
1021 @override | |
1022 Object visitFunctionTypeAlias(FunctionTypeAlias node) { | |
1023 Element element = node.element; | |
1024 _recordElementDefinition(element, IndexConstants.DEFINES_FUNCTION_TYPE); | |
1025 return super.visitFunctionTypeAlias(node); | |
1026 } | |
1027 | |
1028 @override | |
1029 Object visitImportDirective(ImportDirective node) { | |
1030 ImportElement element = node.element; | |
1031 if (element != null) { | |
1032 LibraryElement impLibrary = element.importedLibrary; | |
1033 _recordLibraryReference(node, impLibrary); | |
1034 } | |
1035 return super.visitImportDirective(node); | |
1036 } | |
1037 | |
1038 @override | |
1039 Object visitIndexExpression(IndexExpression node) { | |
1040 MethodElement element = node.bestElement; | |
1041 if (element is MethodElement) { | |
1042 Token operator = node.leftBracket; | |
1043 Location location = _createLocationFromToken(operator); | |
1044 recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED, locati
on); | |
1045 } | |
1046 return super.visitIndexExpression(node); | |
1047 } | |
1048 | |
1049 @override | |
1050 Object visitMethodDeclaration(MethodDeclaration node) { | |
1051 ExecutableElement element = node.element; | |
1052 enterScope(element); | |
1053 try { | |
1054 return super.visitMethodDeclaration(node); | |
1055 } finally { | |
1056 _exitScope(); | |
1057 } | |
1058 } | |
1059 | |
1060 @override | |
1061 Object visitMethodInvocation(MethodInvocation node) { | |
1062 SimpleIdentifier name = node.methodName; | |
1063 Element element = name.bestElement; | |
1064 if (element is MethodElement || element is PropertyAccessorElement) { | |
1065 Location location = _createLocationFromNode(name); | |
1066 Relationship relationship; | |
1067 if (node.target != null) { | |
1068 relationship = IndexConstants.IS_INVOKED_BY_QUALIFIED; | |
1069 } else { | |
1070 relationship = IndexConstants.IS_INVOKED_BY_UNQUALIFIED; | |
1071 } | |
1072 recordRelationship(element, relationship, location); | |
1073 } | |
1074 if (element is FunctionElement || element is VariableElement) { | |
1075 Location location = _createLocationFromNode(name); | |
1076 recordRelationship(element, IndexConstants.IS_INVOKED_BY, location); | |
1077 } | |
1078 // name invocation | |
1079 { | |
1080 Element nameElement = new NameElementImpl(name.name); | |
1081 Location location = _createLocationFromNode(name); | |
1082 Relationship kind = element != null ? IndexConstants.NAME_IS_INVOKED_BY_RE
SOLVED : IndexConstants.NAME_IS_INVOKED_BY_UNRESOLVED; | |
1083 _store.recordRelationship(nameElement, kind, location); | |
1084 } | |
1085 _recordImportElementReferenceWithoutPrefix(name); | |
1086 return super.visitMethodInvocation(node); | |
1087 } | |
1088 | |
1089 @override | |
1090 Object visitPartDirective(PartDirective node) { | |
1091 Element element = node.element; | |
1092 Location location = _createLocationFromNode(node.uri); | |
1093 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
1094 return super.visitPartDirective(node); | |
1095 } | |
1096 | |
1097 @override | |
1098 Object visitPartOfDirective(PartOfDirective node) { | |
1099 Location location = _createLocationFromNode(node.libraryName); | |
1100 recordRelationship(node.element, IndexConstants.IS_REFERENCED_BY, location); | |
1101 return null; | |
1102 } | |
1103 | |
1104 @override | |
1105 Object visitPostfixExpression(PostfixExpression node) { | |
1106 _recordOperatorReference(node.operator, node.bestElement); | |
1107 return super.visitPostfixExpression(node); | |
1108 } | |
1109 | |
1110 @override | |
1111 Object visitPrefixExpression(PrefixExpression node) { | |
1112 _recordOperatorReference(node.operator, node.bestElement); | |
1113 return super.visitPrefixExpression(node); | |
1114 } | |
1115 | |
1116 @override | |
1117 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
1118 Element nameElement = new NameElementImpl(node.name); | |
1119 Location location = _createLocationFromNode(node); | |
1120 // name in declaration | |
1121 if (node.inDeclarationContext()) { | |
1122 recordRelationship(nameElement, IndexConstants.IS_DEFINED_BY, location); | |
1123 return null; | |
1124 } | |
1125 // prepare information | |
1126 Element element = node.bestElement; | |
1127 // qualified name reference | |
1128 _recordQualifiedMemberReference(node, element, nameElement, location); | |
1129 // stop if already handled | |
1130 if (_isAlreadyHandledName(node)) { | |
1131 return null; | |
1132 } | |
1133 // record name read/write | |
1134 { | |
1135 bool inGetterContext = node.inGetterContext(); | |
1136 bool inSetterContext = node.inSetterContext(); | |
1137 if (inGetterContext && inSetterContext) { | |
1138 Relationship kind = element != null ? IndexConstants.NAME_IS_READ_WRITTE
N_BY_RESOLVED : IndexConstants.NAME_IS_READ_WRITTEN_BY_UNRESOLVED; | |
1139 _store.recordRelationship(nameElement, kind, location); | |
1140 } else if (inGetterContext) { | |
1141 Relationship kind = element != null ? IndexConstants.NAME_IS_READ_BY_RES
OLVED : IndexConstants.NAME_IS_READ_BY_UNRESOLVED; | |
1142 _store.recordRelationship(nameElement, kind, location); | |
1143 } else if (inSetterContext) { | |
1144 Relationship kind = element != null ? IndexConstants.NAME_IS_WRITTEN_BY_
RESOLVED : IndexConstants.NAME_IS_WRITTEN_BY_UNRESOLVED; | |
1145 _store.recordRelationship(nameElement, kind, location); | |
1146 } | |
1147 } | |
1148 // record specific relations | |
1149 if (element is ClassElement || element is FunctionElement || element is Func
tionTypeAliasElement || element is LabelElement || element is TypeParameterEleme
nt) { | |
1150 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
1151 } else if (element is FieldElement) { | |
1152 location = _getLocationWithInitializerType(node, location); | |
1153 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
1154 } else if (element is FieldFormalParameterElement) { | |
1155 FieldFormalParameterElement fieldParameter = element; | |
1156 FieldElement field = fieldParameter.field; | |
1157 recordRelationship(field, IndexConstants.IS_REFERENCED_BY_QUALIFIED, locat
ion); | |
1158 } else if (element is PrefixElement) { | |
1159 _recordImportElementReferenceWithPrefix(node); | |
1160 } else if (element is PropertyAccessorElement || element is MethodElement) { | |
1161 location = _getLocationWithTypeAssignedToField(node, element, location); | |
1162 if (node.isQualified) { | |
1163 recordRelationship(element, IndexConstants.IS_REFERENCED_BY_QUALIFIED, l
ocation); | |
1164 } else { | |
1165 recordRelationship(element, IndexConstants.IS_REFERENCED_BY_UNQUALIFIED,
location); | |
1166 } | |
1167 } else if (element is ParameterElement || element is LocalVariableElement) { | |
1168 bool inGetterContext = node.inGetterContext(); | |
1169 bool inSetterContext = node.inSetterContext(); | |
1170 if (inGetterContext && inSetterContext) { | |
1171 recordRelationship(element, IndexConstants.IS_READ_WRITTEN_BY, location)
; | |
1172 } else if (inGetterContext) { | |
1173 recordRelationship(element, IndexConstants.IS_READ_BY, location); | |
1174 } else if (inSetterContext) { | |
1175 recordRelationship(element, IndexConstants.IS_WRITTEN_BY, location); | |
1176 } else { | |
1177 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
1178 } | |
1179 } | |
1180 _recordImportElementReferenceWithoutPrefix(node); | |
1181 return super.visitSimpleIdentifier(node); | |
1182 } | |
1183 | |
1184 @override | |
1185 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
1186 ConstructorElement element = node.staticElement; | |
1187 Location location; | |
1188 if (node.constructorName != null) { | |
1189 int start = node.period.offset; | |
1190 int end = node.constructorName.end; | |
1191 location = _createLocationFromOffset(start, end - start); | |
1192 } else { | |
1193 int start = node.keyword.end; | |
1194 location = _createLocationFromOffset(start, 0); | |
1195 } | |
1196 recordRelationship(element, IndexConstants.IS_REFERENCED_BY, location); | |
1197 return super.visitSuperConstructorInvocation(node); | |
1198 } | |
1199 | |
1200 @override | |
1201 Object visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | |
1202 VariableDeclarationList variables = node.variables; | |
1203 for (VariableDeclaration variableDeclaration in variables.variables) { | |
1204 Element element = variableDeclaration.element; | |
1205 _recordElementDefinition(element, IndexConstants.DEFINES_VARIABLE); | |
1206 } | |
1207 return super.visitTopLevelVariableDeclaration(node); | |
1208 } | |
1209 | |
1210 @override | |
1211 Object visitTypeParameter(TypeParameter node) { | |
1212 TypeParameterElement element = node.element; | |
1213 enterScope(element); | |
1214 try { | |
1215 return super.visitTypeParameter(node); | |
1216 } finally { | |
1217 _exitScope(); | |
1218 } | |
1219 } | |
1220 | |
1221 @override | |
1222 Object visitVariableDeclaration(VariableDeclaration node) { | |
1223 VariableElement element = node.element; | |
1224 // record declaration | |
1225 { | |
1226 SimpleIdentifier name = node.name; | |
1227 Location location = _createLocationFromNode(name); | |
1228 location = _getLocationWithExpressionType(location, node.initializer); | |
1229 recordRelationship(element, IndexConstants.IS_DEFINED_BY, location); | |
1230 } | |
1231 // visit | |
1232 enterScope(element); | |
1233 try { | |
1234 return super.visitVariableDeclaration(node); | |
1235 } finally { | |
1236 _exitScope(); | |
1237 } | |
1238 } | |
1239 | |
1240 @override | |
1241 Object visitVariableDeclarationList(VariableDeclarationList node) { | |
1242 NodeList<VariableDeclaration> variables = node.variables; | |
1243 if (variables != null) { | |
1244 // use first VariableDeclaration as Element for Location(s) in type | |
1245 { | |
1246 TypeName type = node.type; | |
1247 if (type != null) { | |
1248 for (VariableDeclaration variableDeclaration in variables) { | |
1249 enterScope(variableDeclaration.element); | |
1250 try { | |
1251 type.accept(this); | |
1252 } finally { | |
1253 _exitScope(); | |
1254 } | |
1255 // only one iteration | |
1256 break; | |
1257 } | |
1258 } | |
1259 } | |
1260 // visit variables | |
1261 variables.accept(this); | |
1262 } | |
1263 return null; | |
1264 } | |
1265 | |
1266 /** | |
1267 * Record the given relationship between the given [Element] and [Location]. | |
1268 */ | |
1269 void recordRelationship(Element element, Relationship relationship, Location l
ocation) { | |
1270 if (element != null && location != null) { | |
1271 _store.recordRelationship(element, relationship, location); | |
1272 } | |
1273 } | |
1274 | |
1275 /** | |
1276 * @return the [Location] representing location of the [AstNode]. | |
1277 */ | |
1278 Location _createLocationFromNode(AstNode node) => _createLocationFromOffset(no
de.offset, node.length); | |
1279 | |
1280 /** | |
1281 * @param offset the offset of the location within [Source] | |
1282 * @param length the length of the location | |
1283 * @return the [Location] representing the given offset and length within the
inner-most | |
1284 * [Element]. | |
1285 */ | |
1286 Location _createLocationFromOffset(int offset, int length) { | |
1287 Element element = peekElement(); | |
1288 return new Location(element, offset, length); | |
1289 } | |
1290 | |
1291 /** | |
1292 * @return the [Location] representing location of the [Token]. | |
1293 */ | |
1294 Location _createLocationFromToken(Token token) => _createLocationFromOffset(to
ken.offset, token.length); | |
1295 | |
1296 /** | |
1297 * Exit the current scope. | |
1298 */ | |
1299 void _exitScope() { | |
1300 _elementStack.removeFirst(); | |
1301 } | |
1302 | |
1303 /** | |
1304 * @return `true` if given node already indexed as more interesting reference,
so it should | |
1305 * not be indexed again. | |
1306 */ | |
1307 bool _isAlreadyHandledName(SimpleIdentifier node) { | |
1308 AstNode parent = node.parent; | |
1309 if (parent is MethodInvocation) { | |
1310 return identical(parent.methodName, node); | |
1311 } | |
1312 return false; | |
1313 } | |
1314 | |
1315 /** | |
1316 * Records the [Element] definition in the library and universe. | |
1317 */ | |
1318 void _recordElementDefinition(Element element, Relationship relationship) { | |
1319 Location location = createLocation(element); | |
1320 recordRelationship(_libraryElement, relationship, location); | |
1321 recordRelationship(IndexConstants.UNIVERSE, relationship, location); | |
1322 } | |
1323 | |
1324 /** | |
1325 * Records [ImportElement] reference if given [SimpleIdentifier] references so
me | |
1326 * top-level element and not qualified with import prefix. | |
1327 */ | |
1328 void _recordImportElementReferenceWithoutPrefix(SimpleIdentifier node) { | |
1329 if (_isIdentifierInImportCombinator(node)) { | |
1330 return; | |
1331 } | |
1332 if (_isIdentifierInPrefixedIdentifier(node)) { | |
1333 return; | |
1334 } | |
1335 Element element = node.staticElement; | |
1336 ImportElement importElement = _internalGetImportElement(_libraryElement, nul
l, element, _importElementsMap); | |
1337 if (importElement != null) { | |
1338 Location location = _createLocationFromOffset(node.offset, 0); | |
1339 recordRelationship(importElement, IndexConstants.IS_REFERENCED_BY, locatio
n); | |
1340 } | |
1341 } | |
1342 | |
1343 /** | |
1344 * Records [ImportElement] that declares given prefix and imports library with
element used | |
1345 * with given prefix node. | |
1346 */ | |
1347 void _recordImportElementReferenceWithPrefix(SimpleIdentifier prefixNode) { | |
1348 IndexContributor_ImportElementInfo info = getImportElementInfo(prefixNode); | |
1349 if (info != null) { | |
1350 int offset = prefixNode.offset; | |
1351 int length = info._periodEnd - offset; | |
1352 Location location = _createLocationFromOffset(offset, length); | |
1353 recordRelationship(info._element, IndexConstants.IS_REFERENCED_BY, locatio
n); | |
1354 } | |
1355 } | |
1356 | |
1357 /** | |
1358 * Records reference to defining [CompilationUnitElement] of the given | |
1359 * [LibraryElement]. | |
1360 */ | |
1361 void _recordLibraryReference(UriBasedDirective node, LibraryElement library) { | |
1362 if (library != null) { | |
1363 Location location = _createLocationFromNode(node.uri); | |
1364 recordRelationship(library.definingCompilationUnit, IndexConstants.IS_REFE
RENCED_BY, location); | |
1365 } | |
1366 } | |
1367 | |
1368 /** | |
1369 * Record reference to the given operator [Element] and name. | |
1370 */ | |
1371 void _recordOperatorReference(Token operator, Element element) { | |
1372 // prepare location | |
1373 Location location = _createLocationFromToken(operator); | |
1374 // record name reference | |
1375 { | |
1376 String name = operator.lexeme; | |
1377 if (name == "++") { | |
1378 name = "+"; | |
1379 } | |
1380 if (name == "--") { | |
1381 name = "-"; | |
1382 } | |
1383 if (StringUtilities.endsWithChar(name, 0x3D) && name != "==") { | |
1384 name = name.substring(0, name.length - 1); | |
1385 } | |
1386 Element nameElement = new NameElementImpl(name); | |
1387 Relationship relationship = element != null ? IndexConstants.IS_REFERENCED
_BY_QUALIFIED_RESOLVED : IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED; | |
1388 recordRelationship(nameElement, relationship, location); | |
1389 } | |
1390 // record element reference | |
1391 if (element != null) { | |
1392 recordRelationship(element, IndexConstants.IS_INVOKED_BY_QUALIFIED, locati
on); | |
1393 } | |
1394 } | |
1395 | |
1396 /** | |
1397 * Records reference if the given [SimpleIdentifier] looks like a qualified pr
operty access | |
1398 * or method invocation. | |
1399 */ | |
1400 void _recordQualifiedMemberReference(SimpleIdentifier node, Element element, E
lement nameElement, Location location) { | |
1401 if (node.isQualified) { | |
1402 Relationship relationship = element != null ? IndexConstants.IS_REFERENCED
_BY_QUALIFIED_RESOLVED : IndexConstants.IS_REFERENCED_BY_QUALIFIED_UNRESOLVED; | |
1403 recordRelationship(nameElement, relationship, location); | |
1404 } | |
1405 } | |
1406 | |
1407 /** | |
1408 * Records extends/implements relationships between given [ClassElement] and [
Type] of | |
1409 * "superNode". | |
1410 */ | |
1411 void _recordSuperType(TypeName superNode, Relationship relationship) { | |
1412 if (superNode != null) { | |
1413 Identifier superName = superNode.name; | |
1414 if (superName != null) { | |
1415 Element superElement = superName.staticElement; | |
1416 recordRelationship(superElement, relationship, _createLocationFromNode(s
uperNode)); | |
1417 } | |
1418 } | |
1419 } | |
1420 } | |
1421 | |
1422 class IndexContributor_AngularHtmlIndexContributor extends IndexContributor { | |
1423 final AngularHtmlIndexContributor AngularHtmlIndexContributor_this; | |
1424 | |
1425 IndexContributor_AngularHtmlIndexContributor(IndexStore arg0, this.AngularHtml
IndexContributor_this) : super(arg0); | |
1426 | |
1427 @override | |
1428 Element peekElement() => AngularHtmlIndexContributor_this._htmlUnitElement; | |
1429 | |
1430 @override | |
1431 void recordRelationship(Element element, Relationship relationship, Location l
ocation) { | |
1432 AngularElement angularElement = AngularHtmlUnitResolver.getAngularElement(el
ement); | |
1433 if (angularElement != null) { | |
1434 element = angularElement; | |
1435 relationship = IndexConstants.ANGULAR_REFERENCE; | |
1436 } | |
1437 super.recordRelationship(element, relationship, location); | |
1438 } | |
1439 } | |
1440 | |
1441 /** | |
1442 * Information about [ImportElement] and place where it is referenced using | |
1443 * [PrefixElement]. | |
1444 */ | |
1445 class IndexContributor_ImportElementInfo { | |
1446 ImportElement _element; | |
1447 | |
1448 int _periodEnd = 0; | |
1449 } | |
1450 | |
1451 /** | |
1452 * Instances of the [IndexHtmlUnitOperation] implement an operation that adds da
ta to the | |
1453 * index based on the resolved [HtmlUnit]. | |
1454 */ | |
1455 class IndexHtmlUnitOperation implements IndexOperation { | |
1456 /** | |
1457 * The index store against which this operation is being run. | |
1458 */ | |
1459 final IndexStore _indexStore; | |
1460 | |
1461 /** | |
1462 * The context in which [HtmlUnit] was resolved. | |
1463 */ | |
1464 final AnalysisContext _context; | |
1465 | |
1466 /** | |
1467 * The [HtmlUnit] being indexed. | |
1468 */ | |
1469 final ht.HtmlUnit unit; | |
1470 | |
1471 /** | |
1472 * The element of the [HtmlUnit] being indexed. | |
1473 */ | |
1474 HtmlElement _htmlElement; | |
1475 | |
1476 /** | |
1477 * The source being indexed. | |
1478 */ | |
1479 Source _source; | |
1480 | |
1481 /** | |
1482 * Initialize a newly created operation that will index the specified [HtmlUni
t]. | |
1483 * | |
1484 * @param indexStore the index store against which this operation is being run | |
1485 * @param context the context in which [HtmlUnit] was resolved | |
1486 * @param unit the fully resolved [HtmlUnit] | |
1487 */ | |
1488 IndexHtmlUnitOperation(this._indexStore, this._context, this.unit) { | |
1489 this._htmlElement = unit.element; | |
1490 this._source = _htmlElement.source; | |
1491 } | |
1492 | |
1493 /** | |
1494 * @return the [Source] to be indexed. | |
1495 */ | |
1496 Source get source => _source; | |
1497 | |
1498 @override | |
1499 bool get isQuery => false; | |
1500 | |
1501 @override | |
1502 void performOperation() { | |
1503 try { | |
1504 bool mayIndex = _indexStore.aboutToIndexHtml(_context, _htmlElement); | |
1505 if (!mayIndex) { | |
1506 return; | |
1507 } | |
1508 AngularHtmlIndexContributor contributor = new AngularHtmlIndexContributor(
_indexStore); | |
1509 unit.accept(contributor); | |
1510 _indexStore.doneIndex(); | |
1511 } catch (exception) { | |
1512 AnalysisEngine.instance.logger.logError2("Could not index ${unit.element.l
ocation}", exception); | |
1513 } | |
1514 } | |
1515 | |
1516 @override | |
1517 bool removeWhenSourceRemoved(Source source) => this._source == source; | |
1518 | |
1519 @override | |
1520 String toString() => "IndexHtmlUnitOperation(${_source.fullName})"; | |
1521 } | |
1522 | |
1523 /** | |
1524 * The interface [IndexOperation] defines the behavior of objects used to perfor
m operations | |
1525 * on an index. | |
1526 */ | |
1527 abstract class IndexOperation { | |
1528 /** | |
1529 * Return `true` if this operation returns information from the index. | |
1530 * | |
1531 * @return `true` if this operation returns information from the index | |
1532 */ | |
1533 bool get isQuery; | |
1534 | |
1535 /** | |
1536 * Perform the operation implemented by this operation. | |
1537 */ | |
1538 void performOperation(); | |
1539 | |
1540 /** | |
1541 * Return `true` if this operation should be removed from the operation queue
when the | |
1542 * given resource has been removed. | |
1543 * | |
1544 * @param source the [Source] that has been removed | |
1545 * @return `true` if this operation should be removed from the operation queue
as a | |
1546 * result of removing the resource | |
1547 */ | |
1548 bool removeWhenSourceRemoved(Source source); | |
1549 } | |
1550 | |
1551 /** | |
1552 * Container of information computed by the index - relationships between elemen
ts. | |
1553 */ | |
1554 abstract class IndexStore { | |
1555 /** | |
1556 * Notifies the index store that we are going to index the unit with the given
element. | |
1557 * | |
1558 * If the unit is a part of a library, then all its locations are removed. If
it is a defining | |
1559 * compilation unit of a library, then index store also checks if some previou
sly indexed parts of | |
1560 * the library are not parts of the library anymore, and clears their informat
ion. | |
1561 * | |
1562 * @param the [AnalysisContext] in which unit being indexed | |
1563 * @param unitElement the element of the unit being indexed | |
1564 * @return `true` the given [AnalysisContext] is active, or `false` if it was | |
1565 * removed before, so no any unit may be indexed with it | |
1566 */ | |
1567 bool aboutToIndexDart(AnalysisContext context, CompilationUnitElement unitElem
ent); | |
1568 | |
1569 /** | |
1570 * Notifies the index store that we are going to index the given [HtmlElement]
. | |
1571 * | |
1572 * @param the [AnalysisContext] in which unit being indexed | |
1573 * @param htmlElement the [HtmlElement] being indexed | |
1574 * @return `true` the given [AnalysisContext] is active, or `false` if it was | |
1575 * removed before, so no any unit may be indexed with it | |
1576 */ | |
1577 bool aboutToIndexHtml(AnalysisContext context, HtmlElement htmlElement); | |
1578 | |
1579 /** | |
1580 * Removes all of the information. | |
1581 */ | |
1582 void clear(); | |
1583 | |
1584 /** | |
1585 * Notifies that index store that the current Dart or HTML unit indexing is do
ne. | |
1586 * | |
1587 * If this method is not invoked after corresponding "aboutToIndex*" invocatio
n, all recorded | |
1588 * information may be lost. | |
1589 */ | |
1590 void doneIndex(); | |
1591 | |
1592 /** | |
1593 * Return the locations of the elements that have the given relationship with
the given element. | |
1594 * For example, if the element represents a method and the relationship is the
is-referenced-by | |
1595 * relationship, then the returned locations will be all of the places where t
he method is | |
1596 * invoked. | |
1597 * | |
1598 * @param element the the element that has the relationship with the locations
to be returned | |
1599 * @param relationship the [Relationship] between the given element and the lo
cations to be | |
1600 * returned | |
1601 * @return the locations that have the given relationship with the given eleme
nt | |
1602 */ | |
1603 List<Location> getRelationships(Element element, Relationship relationship); | |
1604 | |
1605 /** | |
1606 * Answer index statistics. | |
1607 */ | |
1608 String get statistics; | |
1609 | |
1610 /** | |
1611 * Record that the given element and location have the given relationship. For
example, if the | |
1612 * relationship is the is-referenced-by relationship, then the element would b
e the element being | |
1613 * referenced and the location would be the point at which it is referenced. E
ach element can have | |
1614 * the same relationship with multiple locations. In other words, if the follo
wing code were | |
1615 * executed | |
1616 * | |
1617 * <pre> | |
1618 * recordRelationship(element, isReferencedBy, location1); | |
1619 * recordRelationship(element, isReferencedBy, location2); | |
1620 * </pre> | |
1621 * | |
1622 * then both relationships would be maintained in the index and the result of
executing | |
1623 * | |
1624 * <pre> | |
1625 * getRelationship(element, isReferencedBy); | |
1626 * </pre> | |
1627 * | |
1628 * would be an array containing both <code>location1</code> and <code>location
2</code>. | |
1629 * | |
1630 * @param element the element that is related to the location | |
1631 * @param relationship the [Relationship] between the element and the location | |
1632 * @param location the [Location] where relationship happens | |
1633 */ | |
1634 void recordRelationship(Element element, Relationship relationship, Location l
ocation); | |
1635 | |
1636 /** | |
1637 * Remove from the index all of the information associated with [AnalysisConte
xt]. | |
1638 * | |
1639 * This method should be invoked when a context is disposed. | |
1640 * | |
1641 * @param the [AnalysisContext] being removed | |
1642 */ | |
1643 void removeContext(AnalysisContext context); | |
1644 | |
1645 /** | |
1646 * Remove from the index all of the information associated with elements or lo
cations in the given | |
1647 * source. This includes relationships between an element in the given source
and any other | |
1648 * locations, relationships between any other elements and a location within t
he given source. | |
1649 * | |
1650 * This method should be invoked when a source is no longer part of the code b
ase. | |
1651 * | |
1652 * @param the [AnalysisContext] in which [Source] being removed | |
1653 * @param source the source being removed | |
1654 */ | |
1655 void removeSource(AnalysisContext context, Source source); | |
1656 | |
1657 /** | |
1658 * Remove from the index all of the information associated with elements or lo
cations in the given | |
1659 * sources. This includes relationships between an element in the given source
s and any other | |
1660 * locations, relationships between any other elements and a location within t
he given sources. | |
1661 * | |
1662 * This method should be invoked when multiple sources are no longer part of t
he code base. | |
1663 * | |
1664 * @param the [AnalysisContext] in which [Source]s being removed | |
1665 * @param container the [SourceContainer] holding the sources being removed | |
1666 */ | |
1667 void removeSources(AnalysisContext context, SourceContainer container); | |
1668 } | |
1669 | |
1670 /** | |
1671 * Instances of the [IndexUnitOperation] implement an operation that adds data t
o the index | |
1672 * based on the resolved [CompilationUnit]. | |
1673 */ | |
1674 class IndexUnitOperation implements IndexOperation { | |
1675 /** | |
1676 * The index store against which this operation is being run. | |
1677 */ | |
1678 final IndexStore _indexStore; | |
1679 | |
1680 /** | |
1681 * The context in which compilation unit was resolved. | |
1682 */ | |
1683 final AnalysisContext _context; | |
1684 | |
1685 /** | |
1686 * The compilation unit being indexed. | |
1687 */ | |
1688 final CompilationUnit unit; | |
1689 | |
1690 /** | |
1691 * The element of the compilation unit being indexed. | |
1692 */ | |
1693 CompilationUnitElement _unitElement; | |
1694 | |
1695 /** | |
1696 * The source being indexed. | |
1697 */ | |
1698 Source _source; | |
1699 | |
1700 /** | |
1701 * Initialize a newly created operation that will index the specified unit. | |
1702 * | |
1703 * @param indexStore the index store against which this operation is being run | |
1704 * @param context the context in which compilation unit was resolved | |
1705 * @param unit the fully resolved AST structure | |
1706 */ | |
1707 IndexUnitOperation(this._indexStore, this._context, this.unit) { | |
1708 this._unitElement = unit.element; | |
1709 this._source = _unitElement.source; | |
1710 } | |
1711 | |
1712 /** | |
1713 * @return the [Source] to be indexed. | |
1714 */ | |
1715 Source get source => _source; | |
1716 | |
1717 @override | |
1718 bool get isQuery => false; | |
1719 | |
1720 @override | |
1721 void performOperation() { | |
1722 try { | |
1723 bool mayIndex = _indexStore.aboutToIndexDart(_context, _unitElement); | |
1724 if (!mayIndex) { | |
1725 return; | |
1726 } | |
1727 unit.accept(new IndexContributor(_indexStore)); | |
1728 unit.accept(new AngularDartIndexContributor(_indexStore)); | |
1729 _indexStore.doneIndex(); | |
1730 } catch (exception) { | |
1731 AnalysisEngine.instance.logger.logError2("Could not index ${unit.element.l
ocation}", exception); | |
1732 } | |
1733 } | |
1734 | |
1735 @override | |
1736 bool removeWhenSourceRemoved(Source source) => this._source == source; | |
1737 | |
1738 @override | |
1739 String toString() => "IndexUnitOperation(${_source.fullName})"; | |
1740 } | |
1741 | |
1742 /** | |
1743 * Instances of the class <code>Location</code> represent a location related to
an element. The | |
1744 * location is expressed as an offset and length, but the offset is relative to
the resource | |
1745 * containing the element rather than the start of the element within that resou
rce. | |
1746 */ | |
1747 class Location { | |
1748 /** | |
1749 * An empty array of locations. | |
1750 */ | |
1751 static List<Location> EMPTY_ARRAY = new List<Location>(0); | |
1752 | |
1753 /** | |
1754 * The element containing this location. | |
1755 */ | |
1756 final Element element; | |
1757 | |
1758 /** | |
1759 * The offset of this location within the resource containing the element. | |
1760 */ | |
1761 final int offset; | |
1762 | |
1763 /** | |
1764 * The length of this location. | |
1765 */ | |
1766 final int length; | |
1767 | |
1768 /** | |
1769 * Internal field used to hold a key that is referenced at this location. | |
1770 */ | |
1771 Object internalKey; | |
1772 | |
1773 /** | |
1774 * Initialize a newly create location to be relative to the given element at t
he given offset with | |
1775 * the given length. | |
1776 * | |
1777 * @param element the [Element] containing this location | |
1778 * @param offset the offset of this location within the resource containing th
e element | |
1779 * @param length the length of this location | |
1780 */ | |
1781 Location(this.element, this.offset, this.length) { | |
1782 if (element == null) { | |
1783 throw new IllegalArgumentException("element location cannot be null"); | |
1784 } | |
1785 } | |
1786 | |
1787 /** | |
1788 * Returns a clone of this [Location]. | |
1789 */ | |
1790 Location newClone() => new Location(element, offset, length); | |
1791 | |
1792 @override | |
1793 String toString() => "[${offset} - ${(offset + length)}) in ${element}"; | |
1794 } | |
1795 | |
1796 /** | |
1797 * [Location] with attached data. | |
1798 */ | |
1799 class LocationWithData<D> extends Location { | |
1800 final D data; | |
1801 | |
1802 LocationWithData.con1(Location location, this.data) : super(location.element,
location.offset, location.length); | |
1803 | |
1804 LocationWithData.con2(Element element, int offset, int length, this.data) : su
per(element, offset, length); | |
1805 | |
1806 @override | |
1807 Location newClone() => new LocationWithData<D>.con2(element, offset, length, d
ata); | |
1808 } | |
1809 | |
1810 /** | |
1811 * [IndexStore] which keeps all information in memory, but can write it to strea
m and read | |
1812 * later. | |
1813 */ | |
1814 abstract class MemoryIndexStore implements IndexStore { | |
1815 } | |
1816 | |
1817 /** | |
1818 * [IndexStore] which keeps full index in memory. | |
1819 */ | |
1820 class MemoryIndexStoreImpl implements MemoryIndexStore { | |
1821 /** | |
1822 * When logging is on, [AnalysisEngine] actually creates | |
1823 * [InstrumentedAnalysisContextImpl], which wraps [AnalysisContextImpl] used t
o create | |
1824 * actual [Element]s. So, in index we have to unwrap [InstrumentedAnalysisCont
extImpl] | |
1825 * when perform any operation. | |
1826 */ | |
1827 static AnalysisContext unwrapContext(AnalysisContext context) { | |
1828 if (context is InstrumentedAnalysisContextImpl) { | |
1829 context = (context as InstrumentedAnalysisContextImpl).basis; | |
1830 } | |
1831 return context; | |
1832 } | |
1833 | |
1834 /** | |
1835 * @return the [Source] of the enclosing [LibraryElement], may be `null`. | |
1836 */ | |
1837 static Source _getLibrarySourceOrNull(Element element) { | |
1838 LibraryElement library = element.library; | |
1839 if (library == null) { | |
1840 return null; | |
1841 } | |
1842 if (library.isAngularHtml) { | |
1843 return null; | |
1844 } | |
1845 return library.source; | |
1846 } | |
1847 | |
1848 /** | |
1849 * This map is used to canonicalize equal keys. | |
1850 */ | |
1851 Map<MemoryIndexStoreImpl_ElementRelationKey, MemoryIndexStoreImpl_ElementRelat
ionKey> _canonicalKeys = {}; | |
1852 | |
1853 /** | |
1854 * The mapping of [ElementRelationKey] to the [Location]s, one-to-many. | |
1855 */ | |
1856 Map<MemoryIndexStoreImpl_ElementRelationKey, Set<Location>> _keyToLocations =
{}; | |
1857 | |
1858 /** | |
1859 * The mapping of [Source] to the [ElementRelationKey]s. It is used in | |
1860 * [removeSource] to identify keys to remove from | |
1861 * [keyToLocations]. | |
1862 */ | |
1863 Map<AnalysisContext, Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImp
l_ElementRelationKey>>> _contextToSourceToKeys = {}; | |
1864 | |
1865 /** | |
1866 * The mapping of [Source] to the [Location]s existing in it. It is used in | |
1867 * [clearSource0] to identify locations to remove from | |
1868 * [keyToLocations]. | |
1869 */ | |
1870 Map<AnalysisContext, Map<MemoryIndexStoreImpl_Source2, List<Location>>> _conte
xtToSourceToLocations = {}; | |
1871 | |
1872 /** | |
1873 * The mapping of library [Source] to the [Source]s of part units. | |
1874 */ | |
1875 Map<AnalysisContext, Map<Source, Set<Source>>> _contextToLibraryToUnits = {}; | |
1876 | |
1877 /** | |
1878 * The mapping of unit [Source] to the [Source]s of libraries it is used in. | |
1879 */ | |
1880 Map<AnalysisContext, Map<Source, Set<Source>>> _contextToUnitToLibraries = {}; | |
1881 | |
1882 int _sourceCount = 0; | |
1883 | |
1884 int _keyCount = 0; | |
1885 | |
1886 int _locationCount = 0; | |
1887 | |
1888 @override | |
1889 bool aboutToIndexDart(AnalysisContext context, CompilationUnitElement unitElem
ent) { | |
1890 context = unwrapContext(context); | |
1891 // may be already disposed in other thread | |
1892 if (context.isDisposed) { | |
1893 return false; | |
1894 } | |
1895 // validate unit | |
1896 if (unitElement == null) { | |
1897 return false; | |
1898 } | |
1899 LibraryElement libraryElement = unitElement.library; | |
1900 if (libraryElement == null) { | |
1901 return false; | |
1902 } | |
1903 CompilationUnitElement definingUnitElement = libraryElement.definingCompilat
ionUnit; | |
1904 if (definingUnitElement == null) { | |
1905 return false; | |
1906 } | |
1907 // prepare sources | |
1908 Source library = definingUnitElement.source; | |
1909 Source unit = unitElement.source; | |
1910 // special handling for the defining library unit | |
1911 if (unit == library) { | |
1912 // prepare new parts | |
1913 Set<Source> newParts = new Set(); | |
1914 for (CompilationUnitElement part in libraryElement.parts) { | |
1915 newParts.add(part.source); | |
1916 } | |
1917 // prepare old parts | |
1918 Map<Source, Set<Source>> libraryToUnits = _contextToLibraryToUnits[context
]; | |
1919 if (libraryToUnits == null) { | |
1920 libraryToUnits = {}; | |
1921 _contextToLibraryToUnits[context] = libraryToUnits; | |
1922 } | |
1923 Set<Source> oldParts = libraryToUnits[library]; | |
1924 // check if some parts are not in the library now | |
1925 if (oldParts != null) { | |
1926 Set<Source> noParts = oldParts.difference(newParts); | |
1927 for (Source noPart in noParts) { | |
1928 _removeLocations(context, library, noPart); | |
1929 } | |
1930 } | |
1931 // remember new parts | |
1932 libraryToUnits[library] = newParts; | |
1933 } | |
1934 // remember libraries in which unit is used | |
1935 _recordUnitInLibrary(context, library, unit); | |
1936 // remove locations | |
1937 _removeLocations(context, library, unit); | |
1938 // remove keys | |
1939 { | |
1940 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelation
Key>> sourceToKeys = _contextToSourceToKeys[context]; | |
1941 if (sourceToKeys != null) { | |
1942 MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(
library, unit); | |
1943 bool hadSource = sourceToKeys.remove(source2) != null; | |
1944 if (hadSource) { | |
1945 _sourceCount--; | |
1946 } | |
1947 } | |
1948 } | |
1949 // OK, we can index | |
1950 return true; | |
1951 } | |
1952 | |
1953 @override | |
1954 bool aboutToIndexHtml(AnalysisContext context, HtmlElement htmlElement) { | |
1955 context = unwrapContext(context); | |
1956 // may be already disposed in other thread | |
1957 if (context.isDisposed) { | |
1958 return false; | |
1959 } | |
1960 // remove locations | |
1961 Source source = htmlElement.source; | |
1962 _removeLocations(context, null, source); | |
1963 // remove keys | |
1964 { | |
1965 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelation
Key>> sourceToKeys = _contextToSourceToKeys[context]; | |
1966 if (sourceToKeys != null) { | |
1967 MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(
null, source); | |
1968 bool hadSource = sourceToKeys.remove(source2) != null; | |
1969 if (hadSource) { | |
1970 _sourceCount--; | |
1971 } | |
1972 } | |
1973 } | |
1974 // remember libraries in which unit is used | |
1975 _recordUnitInLibrary(context, null, source); | |
1976 // OK, we can index | |
1977 return true; | |
1978 } | |
1979 | |
1980 @override | |
1981 void clear() { | |
1982 _canonicalKeys.clear(); | |
1983 _keyToLocations.clear(); | |
1984 _contextToSourceToKeys.clear(); | |
1985 _contextToSourceToLocations.clear(); | |
1986 _contextToLibraryToUnits.clear(); | |
1987 _contextToUnitToLibraries.clear(); | |
1988 } | |
1989 | |
1990 @override | |
1991 void doneIndex() { | |
1992 } | |
1993 | |
1994 @override | |
1995 List<Location> getRelationships(Element element, Relationship relationship) { | |
1996 MemoryIndexStoreImpl_ElementRelationKey key = new MemoryIndexStoreImpl_Eleme
ntRelationKey(element, relationship); | |
1997 Set<Location> locations = _keyToLocations[key]; | |
1998 if (locations != null) { | |
1999 return new List.from(locations); | |
2000 } | |
2001 return Location.EMPTY_ARRAY; | |
2002 } | |
2003 | |
2004 @override | |
2005 String get statistics => "${_locationCount} relationships in ${_keyCount} keys
in ${_sourceCount} sources"; | |
2006 | |
2007 int internalGetKeyCount() => _keyToLocations.length; | |
2008 | |
2009 int internalGetLocationCount() { | |
2010 int count = 0; | |
2011 for (Set<Location> locations in _keyToLocations.values) { | |
2012 count += locations.length; | |
2013 } | |
2014 return count; | |
2015 } | |
2016 | |
2017 int internalGetLocationCountForContext(AnalysisContext context) { | |
2018 context = unwrapContext(context); | |
2019 int count = 0; | |
2020 for (Set<Location> locations in _keyToLocations.values) { | |
2021 for (Location location in locations) { | |
2022 if (identical(location.element.context, context)) { | |
2023 count++; | |
2024 } | |
2025 } | |
2026 } | |
2027 return count; | |
2028 } | |
2029 | |
2030 int internalGetSourceKeyCount(AnalysisContext context) { | |
2031 int count = 0; | |
2032 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKe
y>> sourceToKeys = _contextToSourceToKeys[context]; | |
2033 if (sourceToKeys != null) { | |
2034 for (Set<MemoryIndexStoreImpl_ElementRelationKey> keys in sourceToKeys.val
ues) { | |
2035 count += keys.length; | |
2036 } | |
2037 } | |
2038 return count; | |
2039 } | |
2040 | |
2041 @override | |
2042 void recordRelationship(Element element, Relationship relationship, Location l
ocation) { | |
2043 if (element == null || location == null) { | |
2044 return; | |
2045 } | |
2046 location = location.newClone(); | |
2047 // at the index level we don't care about Member(s) | |
2048 if (element is Member) { | |
2049 element = (element as Member).baseElement; | |
2050 } | |
2051 // System.out.println(element + " " + relationship + " " + location); | |
2052 // prepare information | |
2053 AnalysisContext elementContext = element.context; | |
2054 AnalysisContext locationContext = location.element.context; | |
2055 Source elementSource = element.source; | |
2056 Source locationSource = location.element.source; | |
2057 Source elementLibrarySource = _getLibrarySourceOrNull(element); | |
2058 Source locationLibrarySource = _getLibrarySourceOrNull(location.element); | |
2059 // sanity check | |
2060 if (locationContext == null) { | |
2061 return; | |
2062 } | |
2063 if (locationSource == null) { | |
2064 return; | |
2065 } | |
2066 if (elementContext == null && element is! NameElementImpl && element is! Uni
verseElementImpl) { | |
2067 return; | |
2068 } | |
2069 if (elementSource == null && element is! NameElementImpl && element is! Univ
erseElementImpl) { | |
2070 return; | |
2071 } | |
2072 // may be already disposed in other thread | |
2073 if (elementContext != null && elementContext.isDisposed) { | |
2074 return; | |
2075 } | |
2076 if (locationContext.isDisposed) { | |
2077 return; | |
2078 } | |
2079 // record: key -> location(s) | |
2080 MemoryIndexStoreImpl_ElementRelationKey key = _getCanonicalKey(element, rela
tionship); | |
2081 { | |
2082 Set<Location> locations = _keyToLocations.remove(key); | |
2083 if (locations == null) { | |
2084 locations = _createLocationIdentitySet(); | |
2085 } else { | |
2086 _keyCount--; | |
2087 } | |
2088 _keyToLocations[key] = locations; | |
2089 _keyCount++; | |
2090 locations.add(location); | |
2091 _locationCount++; | |
2092 } | |
2093 // record: location -> key | |
2094 location.internalKey = key; | |
2095 // prepare source pairs | |
2096 MemoryIndexStoreImpl_Source2 elementSource2 = new MemoryIndexStoreImpl_Sourc
e2(elementLibrarySource, elementSource); | |
2097 MemoryIndexStoreImpl_Source2 locationSource2 = new MemoryIndexStoreImpl_Sour
ce2(locationLibrarySource, locationSource); | |
2098 // record: element source -> keys | |
2099 { | |
2100 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelation
Key>> sourceToKeys = _contextToSourceToKeys[elementContext]; | |
2101 if (sourceToKeys == null) { | |
2102 sourceToKeys = {}; | |
2103 _contextToSourceToKeys[elementContext] = sourceToKeys; | |
2104 } | |
2105 Set<MemoryIndexStoreImpl_ElementRelationKey> keys = sourceToKeys[elementSo
urce2]; | |
2106 if (keys == null) { | |
2107 keys = new Set(); | |
2108 sourceToKeys[elementSource2] = keys; | |
2109 _sourceCount++; | |
2110 } | |
2111 keys.remove(key); | |
2112 keys.add(key); | |
2113 } | |
2114 // record: location source -> locations | |
2115 { | |
2116 Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _con
textToSourceToLocations[locationContext]; | |
2117 if (sourceToLocations == null) { | |
2118 sourceToLocations = {}; | |
2119 _contextToSourceToLocations[locationContext] = sourceToLocations; | |
2120 } | |
2121 List<Location> locations = sourceToLocations[locationSource2]; | |
2122 if (locations == null) { | |
2123 locations = []; | |
2124 sourceToLocations[locationSource2] = locations; | |
2125 } | |
2126 locations.add(location); | |
2127 } | |
2128 } | |
2129 | |
2130 @override | |
2131 void removeContext(AnalysisContext context) { | |
2132 context = unwrapContext(context); | |
2133 if (context == null) { | |
2134 return; | |
2135 } | |
2136 // remove sources | |
2137 removeSources(context, null); | |
2138 // remove context | |
2139 _contextToSourceToKeys.remove(context); | |
2140 _contextToSourceToLocations.remove(context); | |
2141 _contextToLibraryToUnits.remove(context); | |
2142 _contextToUnitToLibraries.remove(context); | |
2143 } | |
2144 | |
2145 @override | |
2146 void removeSource(AnalysisContext context, Source unit) { | |
2147 context = unwrapContext(context); | |
2148 if (context == null) { | |
2149 return; | |
2150 } | |
2151 // remove locations defined in source | |
2152 Map<Source, Set<Source>> unitToLibraries = _contextToUnitToLibraries[context
]; | |
2153 if (unitToLibraries != null) { | |
2154 Set<Source> libraries = unitToLibraries.remove(unit); | |
2155 if (libraries != null) { | |
2156 for (Source library in libraries) { | |
2157 MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source
2(library, unit); | |
2158 // remove locations defined in source | |
2159 _removeLocations(context, library, unit); | |
2160 // remove keys for elements defined in source | |
2161 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRela
tionKey>> sourceToKeys = _contextToSourceToKeys[context]; | |
2162 if (sourceToKeys != null) { | |
2163 Set<MemoryIndexStoreImpl_ElementRelationKey> keys = sourceToKeys.rem
ove(source2); | |
2164 if (keys != null) { | |
2165 for (MemoryIndexStoreImpl_ElementRelationKey key in keys) { | |
2166 _canonicalKeys.remove(key); | |
2167 Set<Location> locations = _keyToLocations.remove(key); | |
2168 if (locations != null) { | |
2169 _keyCount--; | |
2170 _locationCount -= locations.length; | |
2171 } | |
2172 } | |
2173 _sourceCount--; | |
2174 } | |
2175 } | |
2176 } | |
2177 } | |
2178 } | |
2179 } | |
2180 | |
2181 @override | |
2182 void removeSources(AnalysisContext context, SourceContainer container) { | |
2183 context = unwrapContext(context); | |
2184 if (context == null) { | |
2185 return; | |
2186 } | |
2187 // remove sources #1 | |
2188 Map<MemoryIndexStoreImpl_Source2, Set<MemoryIndexStoreImpl_ElementRelationKe
y>> sourceToKeys = _contextToSourceToKeys[context]; | |
2189 if (sourceToKeys != null) { | |
2190 List<MemoryIndexStoreImpl_Source2> sources = []; | |
2191 for (MemoryIndexStoreImpl_Source2 source2 in sources) { | |
2192 Source source = source2._unitSource; | |
2193 if (container == null || container.contains(source)) { | |
2194 removeSource(context, source); | |
2195 } | |
2196 } | |
2197 } | |
2198 // remove sources #2 | |
2199 Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _conte
xtToSourceToLocations[context]; | |
2200 if (sourceToLocations != null) { | |
2201 List<MemoryIndexStoreImpl_Source2> sources = []; | |
2202 for (MemoryIndexStoreImpl_Source2 source2 in sources) { | |
2203 Source source = source2._unitSource; | |
2204 if (container == null || container.contains(source)) { | |
2205 removeSource(context, source); | |
2206 } | |
2207 } | |
2208 } | |
2209 } | |
2210 | |
2211 /** | |
2212 * Creates new [Set] that uses object identity instead of equals. | |
2213 */ | |
2214 Set<Location> _createLocationIdentitySet() => new Set<Location>.identity(); | |
2215 | |
2216 /** | |
2217 * @return the canonical [ElementRelationKey] for given [Element] and | |
2218 * [Relationship], i.e. unique instance for this combination. | |
2219 */ | |
2220 MemoryIndexStoreImpl_ElementRelationKey _getCanonicalKey(Element element, Rela
tionship relationship) { | |
2221 MemoryIndexStoreImpl_ElementRelationKey key = new MemoryIndexStoreImpl_Eleme
ntRelationKey(element, relationship); | |
2222 MemoryIndexStoreImpl_ElementRelationKey canonicalKey = _canonicalKeys[key]; | |
2223 if (canonicalKey == null) { | |
2224 canonicalKey = key; | |
2225 _canonicalKeys[key] = canonicalKey; | |
2226 } | |
2227 return canonicalKey; | |
2228 } | |
2229 | |
2230 void _recordUnitInLibrary(AnalysisContext context, Source library, Source unit
) { | |
2231 Map<Source, Set<Source>> unitToLibraries = _contextToUnitToLibraries[context
]; | |
2232 if (unitToLibraries == null) { | |
2233 unitToLibraries = {}; | |
2234 _contextToUnitToLibraries[context] = unitToLibraries; | |
2235 } | |
2236 Set<Source> libraries = unitToLibraries[unit]; | |
2237 if (libraries == null) { | |
2238 libraries = new Set(); | |
2239 unitToLibraries[unit] = libraries; | |
2240 } | |
2241 libraries.add(library); | |
2242 } | |
2243 | |
2244 /** | |
2245 * Removes locations recorded in the given library/unit pair. | |
2246 */ | |
2247 void _removeLocations(AnalysisContext context, Source library, Source unit) { | |
2248 MemoryIndexStoreImpl_Source2 source2 = new MemoryIndexStoreImpl_Source2(libr
ary, unit); | |
2249 Map<MemoryIndexStoreImpl_Source2, List<Location>> sourceToLocations = _conte
xtToSourceToLocations[context]; | |
2250 if (sourceToLocations != null) { | |
2251 List<Location> sourceLocations = sourceToLocations.remove(source2); | |
2252 if (sourceLocations != null) { | |
2253 for (Location location in sourceLocations) { | |
2254 MemoryIndexStoreImpl_ElementRelationKey key = location.internalKey as
MemoryIndexStoreImpl_ElementRelationKey; | |
2255 Set<Location> relLocations = _keyToLocations[key]; | |
2256 if (relLocations != null) { | |
2257 relLocations.remove(location); | |
2258 _locationCount--; | |
2259 // no locations with this key | |
2260 if (relLocations.isEmpty) { | |
2261 _canonicalKeys.remove(key); | |
2262 _keyToLocations.remove(key); | |
2263 _keyCount--; | |
2264 } | |
2265 } | |
2266 } | |
2267 } | |
2268 } | |
2269 } | |
2270 } | |
2271 | |
2272 class MemoryIndexStoreImpl_ElementRelationKey { | |
2273 final Element _element; | |
2274 | |
2275 final Relationship _relationship; | |
2276 | |
2277 MemoryIndexStoreImpl_ElementRelationKey(this._element, this._relationship); | |
2278 | |
2279 @override | |
2280 bool operator ==(Object obj) { | |
2281 MemoryIndexStoreImpl_ElementRelationKey other = obj as MemoryIndexStoreImpl_
ElementRelationKey; | |
2282 Element otherElement = other._element; | |
2283 return identical(other._relationship, _relationship) && otherElement.nameOff
set == _element.nameOffset && otherElement.kind == _element.kind && otherElement
.displayName == _element.displayName && otherElement.source == _element.source; | |
2284 } | |
2285 | |
2286 @override | |
2287 int get hashCode => JavaArrays.makeHashCode([ | |
2288 _element.source, | |
2289 _element.nameOffset, | |
2290 _element.kind, | |
2291 _element.displayName, | |
2292 _relationship]); | |
2293 | |
2294 @override | |
2295 String toString() => "${_element} ${_relationship}"; | |
2296 } | |
2297 | |
2298 class MemoryIndexStoreImpl_Source2 { | |
2299 final Source _librarySource; | |
2300 | |
2301 final Source _unitSource; | |
2302 | |
2303 MemoryIndexStoreImpl_Source2(this._librarySource, this._unitSource); | |
2304 | |
2305 @override | |
2306 bool operator ==(Object obj) { | |
2307 if (identical(obj, this)) { | |
2308 return true; | |
2309 } | |
2310 if (obj is! MemoryIndexStoreImpl_Source2) { | |
2311 return false; | |
2312 } | |
2313 MemoryIndexStoreImpl_Source2 other = obj as MemoryIndexStoreImpl_Source2; | |
2314 return other._librarySource == _librarySource && other._unitSource == _unitS
ource; | |
2315 } | |
2316 | |
2317 @override | |
2318 int get hashCode => JavaArrays.makeHashCode([_librarySource, _unitSource]); | |
2319 | |
2320 @override | |
2321 String toString() => "${_librarySource} ${_unitSource}"; | |
2322 } | |
2323 | |
2324 /** | |
2325 * Special [Element] which is used to index references to the name without speci
fying concrete | |
2326 * kind of this name - field, method or something else. | |
2327 */ | |
2328 class NameElementImpl extends ElementImpl { | |
2329 NameElementImpl(String name) : super("name:${name}", -1); | |
2330 | |
2331 @override | |
2332 accept(ElementVisitor visitor) => null; | |
2333 | |
2334 @override | |
2335 ElementKind get kind => ElementKind.NAME; | |
2336 } | |
2337 | |
2338 /** | |
2339 * The enumeration <code>ProcessorState</code> represents the possible states of
an operation | |
2340 * processor. | |
2341 */ | |
2342 class ProcessorState extends Enum<ProcessorState> { | |
2343 /** | |
2344 * The processor is ready to be run (has not been run before). | |
2345 */ | |
2346 static const ProcessorState READY = const ProcessorState('READY', 0); | |
2347 | |
2348 /** | |
2349 * The processor is currently performing operations. | |
2350 */ | |
2351 static const ProcessorState RUNNING = const ProcessorState('RUNNING', 1); | |
2352 | |
2353 /** | |
2354 * The processor is currently performing operations but has been asked to stop
. | |
2355 */ | |
2356 static const ProcessorState STOP_REQESTED = const ProcessorState('STOP_REQESTE
D', 2); | |
2357 | |
2358 /** | |
2359 * The processor has stopped performing operations and cannot be used again. | |
2360 */ | |
2361 static const ProcessorState STOPPED = const ProcessorState('STOPPED', 3); | |
2362 | |
2363 static const List<ProcessorState> values = const [READY, RUNNING, STOP_REQESTE
D, STOPPED]; | |
2364 | |
2365 const ProcessorState(String name, int ordinal) : super(name, ordinal); | |
2366 } | |
2367 | |
2368 /** | |
2369 * Relationship between an element and a location. Relationships are identified
by a globally unique | |
2370 * identifier. | |
2371 */ | |
2372 class Relationship { | |
2373 /** | |
2374 * The unique identifier for this relationship. | |
2375 */ | |
2376 final String _uniqueId; | |
2377 | |
2378 /** | |
2379 * A table mapping relationship identifiers to relationships. | |
2380 */ | |
2381 static Map<String, Relationship> _RelationshipMap = {}; | |
2382 | |
2383 /** | |
2384 * Return the relationship with the given unique identifier. | |
2385 * | |
2386 * @param uniqueId the unique identifier for the relationship | |
2387 * @return the relationship with the given unique identifier | |
2388 */ | |
2389 static Relationship getRelationship(String uniqueId) { | |
2390 Relationship relationship = _RelationshipMap[uniqueId]; | |
2391 if (relationship == null) { | |
2392 relationship = new Relationship(uniqueId); | |
2393 _RelationshipMap[uniqueId] = relationship; | |
2394 } | |
2395 return relationship; | |
2396 } | |
2397 | |
2398 /** | |
2399 * @return all registered [Relationship]s. | |
2400 */ | |
2401 static Iterable<Relationship> values() => _RelationshipMap.values; | |
2402 | |
2403 /** | |
2404 * Initialize a newly created relationship to have the given unique identifier
. | |
2405 * | |
2406 * @param uniqueId the unique identifier for this relationship | |
2407 */ | |
2408 Relationship(this._uniqueId); | |
2409 | |
2410 /** | |
2411 * Return the unique identifier for this relationship. | |
2412 * | |
2413 * @return the unique identifier for this relationship | |
2414 */ | |
2415 String get identifier => _uniqueId; | |
2416 | |
2417 @override | |
2418 String toString() => _uniqueId; | |
2419 } | |
2420 | |
2421 /** | |
2422 * The interface <code>RelationshipCallback</code> defines the behavior of objec
ts that are invoked | |
2423 * with the results of a query about a given relationship. | |
2424 */ | |
2425 abstract class RelationshipCallback { | |
2426 /** | |
2427 * This method is invoked when the locations that have a specified relationshi
p with a specified | |
2428 * element are available. For example, if the element is a field and the relat
ionship is the | |
2429 * is-referenced-by relationship, then this method will be invoked with each l
ocation at which the | |
2430 * field is referenced. | |
2431 * | |
2432 * @param element the [Element] that has the relationship with the locations | |
2433 * @param relationship the relationship between the given element and the loca
tions | |
2434 * @param locations the locations that were found | |
2435 */ | |
2436 void hasRelationships(Element element, Relationship relationship, List<Locatio
n> locations); | |
2437 } | |
2438 | |
2439 /** | |
2440 * Instances of the [RemoveContextOperation] implement an operation that removes
from the | |
2441 * index any data based on the specified [AnalysisContext]. | |
2442 */ | |
2443 class RemoveContextOperation implements IndexOperation { | |
2444 /** | |
2445 * The index store against which this operation is being run. | |
2446 */ | |
2447 final IndexStore _indexStore; | |
2448 | |
2449 /** | |
2450 * The context being removed. | |
2451 */ | |
2452 final AnalysisContext context; | |
2453 | |
2454 /** | |
2455 * Initialize a newly created operation that will remove the specified resourc
e. | |
2456 * | |
2457 * @param indexStore the index store against which this operation is being run | |
2458 * @param context the [AnalysisContext] to remove | |
2459 */ | |
2460 RemoveContextOperation(this._indexStore, this.context); | |
2461 | |
2462 @override | |
2463 bool get isQuery => false; | |
2464 | |
2465 @override | |
2466 void performOperation() { | |
2467 _indexStore.removeContext(context); | |
2468 } | |
2469 | |
2470 @override | |
2471 bool removeWhenSourceRemoved(Source source) => false; | |
2472 | |
2473 @override | |
2474 String toString() => "RemoveContext(${context})"; | |
2475 } | |
2476 | |
2477 /** | |
2478 * Instances of the [RemoveSourceOperation] implement an operation that removes
from the index | |
2479 * any data based on the content of a specified source. | |
2480 */ | |
2481 class RemoveSourceOperation implements IndexOperation { | |
2482 /** | |
2483 * The index store against which this operation is being run. | |
2484 */ | |
2485 final IndexStore _indexStore; | |
2486 | |
2487 /** | |
2488 * The context in which source being removed. | |
2489 */ | |
2490 final AnalysisContext _context; | |
2491 | |
2492 /** | |
2493 * The source being removed. | |
2494 */ | |
2495 final Source source; | |
2496 | |
2497 /** | |
2498 * Initialize a newly created operation that will remove the specified resourc
e. | |
2499 * | |
2500 * @param indexStore the index store against which this operation is being run | |
2501 * @param context the [AnalysisContext] to remove source in | |
2502 * @param source the [Source] to remove from index | |
2503 */ | |
2504 RemoveSourceOperation(this._indexStore, this._context, this.source); | |
2505 | |
2506 @override | |
2507 bool get isQuery => false; | |
2508 | |
2509 @override | |
2510 void performOperation() { | |
2511 _indexStore.removeSource(_context, source); | |
2512 } | |
2513 | |
2514 @override | |
2515 bool removeWhenSourceRemoved(Source source) => false; | |
2516 | |
2517 @override | |
2518 String toString() => "RemoveSource(${source.fullName})"; | |
2519 } | |
2520 | |
2521 /** | |
2522 * Instances of the [RemoveSourcesOperation] implement an operation that removes
from the | |
2523 * index any data based on the content of source belonging to a [SourceContainer
]. | |
2524 */ | |
2525 class RemoveSourcesOperation implements IndexOperation { | |
2526 /** | |
2527 * The index store against which this operation is being run. | |
2528 */ | |
2529 final IndexStore _indexStore; | |
2530 | |
2531 /** | |
2532 * The context to remove container. | |
2533 */ | |
2534 final AnalysisContext _context; | |
2535 | |
2536 /** | |
2537 * The source container to remove. | |
2538 */ | |
2539 final SourceContainer container; | |
2540 | |
2541 /** | |
2542 * Initialize a newly created operation that will remove the specified resourc
e. | |
2543 * | |
2544 * @param indexStore the index store against which this operation is being run | |
2545 * @param context the [AnalysisContext] to remove container in | |
2546 * @param container the [SourceContainer] to remove from index | |
2547 */ | |
2548 RemoveSourcesOperation(this._indexStore, this._context, this.container); | |
2549 | |
2550 @override | |
2551 bool get isQuery => false; | |
2552 | |
2553 @override | |
2554 void performOperation() { | |
2555 _indexStore.removeSources(_context, container); | |
2556 } | |
2557 | |
2558 @override | |
2559 bool removeWhenSourceRemoved(Source source) => false; | |
2560 | |
2561 @override | |
2562 String toString() => "RemoveSources(${container})"; | |
2563 } | |
2564 | |
2565 /** | |
2566 * The interface `UniverseElement` defines element to use when we want to reques
t "defines" | |
2567 * relations without specifying exact library. | |
2568 */ | |
2569 abstract class UniverseElement implements Element { | |
2570 static final UniverseElement INSTANCE = UniverseElementImpl.INSTANCE; | |
2571 } | |
2572 | |
2573 /** | |
2574 * Implementation of [UniverseElement]. | |
2575 */ | |
2576 class UniverseElementImpl extends ElementImpl implements UniverseElement { | |
2577 static UniverseElementImpl INSTANCE = new UniverseElementImpl(); | |
2578 | |
2579 UniverseElementImpl() : super("--universe--", -1); | |
2580 | |
2581 @override | |
2582 accept(ElementVisitor visitor) => null; | |
2583 | |
2584 @override | |
2585 ElementKind get kind => ElementKind.UNIVERSE; | |
2586 } | |
OLD | NEW |