| 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 |