| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:collection'; |
| 6 |
| 7 import 'package:analyzer/dart/ast/ast.dart'; |
| 8 import 'package:analyzer/dart/ast/token.dart'; |
| 9 import 'package:analyzer/dart/element/element.dart'; |
| 10 import 'package:analyzer/dart/element/type.dart'; |
| 11 import 'package:analyzer/error/error.dart'; |
| 12 import 'package:analyzer/exception/exception.dart'; |
| 13 import 'package:analyzer/src/dart/ast/token.dart'; |
| 14 import 'package:analyzer/src/dart/element/element.dart'; |
| 15 import 'package:analyzer/src/dart/element/member.dart'; |
| 16 import 'package:analyzer/src/dart/element/type.dart'; |
| 17 import 'package:analyzer/src/error/codes.dart'; |
| 18 import 'package:analyzer/src/generated/type_system.dart'; |
| 19 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 20 |
| 21 /** |
| 22 * Instances of the class `InheritanceManager` manage the knowledge of where cla
ss members |
| 23 * (methods, getters & setters) are inherited from. |
| 24 */ |
| 25 class InheritanceManager { |
| 26 /** |
| 27 * The [LibraryElement] that is managed by this manager. |
| 28 */ |
| 29 LibraryElement _library; |
| 30 |
| 31 /** |
| 32 * A flag indicating whether abstract methods should be included when looking |
| 33 * up the superclass chain. |
| 34 */ |
| 35 bool _includeAbstractFromSuperclasses; |
| 36 |
| 37 /** |
| 38 * This is a mapping between each [ClassElement] and a map between the [String
] member |
| 39 * names and the associated [ExecutableElement] in the mixin and superclass ch
ain. |
| 40 */ |
| 41 Map<ClassElement, Map<String, ExecutableElement>> _classLookup; |
| 42 |
| 43 /** |
| 44 * This is a mapping between each [ClassElement] and a map between the [String
] member |
| 45 * names and the associated [ExecutableElement] in the interface set. |
| 46 */ |
| 47 Map<ClassElement, Map<String, ExecutableElement>> _interfaceLookup; |
| 48 |
| 49 /** |
| 50 * A map between each visited [ClassElement] and the set of [AnalysisError]s f
ound on |
| 51 * the class element. |
| 52 */ |
| 53 Map<ClassElement, Set<AnalysisError>> _errorsInClassElement = |
| 54 new HashMap<ClassElement, Set<AnalysisError>>(); |
| 55 |
| 56 /** |
| 57 * Initialize a newly created inheritance manager. |
| 58 * |
| 59 * @param library the library element context that the inheritance mappings ar
e being generated |
| 60 */ |
| 61 InheritanceManager(LibraryElement library, |
| 62 {bool includeAbstractFromSuperclasses: false}) { |
| 63 this._library = library; |
| 64 _includeAbstractFromSuperclasses = includeAbstractFromSuperclasses; |
| 65 _classLookup = new HashMap<ClassElement, Map<String, ExecutableElement>>(); |
| 66 _interfaceLookup = |
| 67 new HashMap<ClassElement, Map<String, ExecutableElement>>(); |
| 68 } |
| 69 |
| 70 /** |
| 71 * Set the new library element context. |
| 72 * |
| 73 * @param library the new library element |
| 74 */ |
| 75 void set libraryElement(LibraryElement library) { |
| 76 this._library = library; |
| 77 } |
| 78 |
| 79 /** |
| 80 * Return the set of [AnalysisError]s found on the passed [ClassElement], or |
| 81 * `null` if there are none. |
| 82 * |
| 83 * @param classElt the class element to query |
| 84 * @return the set of [AnalysisError]s found on the passed [ClassElement], or |
| 85 * `null` if there are none |
| 86 */ |
| 87 Set<AnalysisError> getErrors(ClassElement classElt) => |
| 88 _errorsInClassElement[classElt]; |
| 89 |
| 90 /** |
| 91 * Get and return a mapping between the set of all string names of the members
inherited from the |
| 92 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE
lement]. |
| 93 * |
| 94 * @param classElt the class element to query |
| 95 * @return a mapping between the set of all members inherited from the passed
[ClassElement] |
| 96 * superclass hierarchy, and the associated [ExecutableElement] |
| 97 */ |
| 98 @deprecated |
| 99 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) => |
| 100 new MemberMap.fromMap( |
| 101 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>())); |
| 102 |
| 103 /** |
| 104 * Get and return a mapping between the set of all string names of the members
inherited from the |
| 105 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl
ement]. |
| 106 * |
| 107 * @param classElt the class element to query |
| 108 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 109 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement]. |
| 110 */ |
| 111 @deprecated |
| 112 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => |
| 113 new MemberMap.fromMap( |
| 114 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>())); |
| 115 |
| 116 /** |
| 117 * Return a table mapping the string names of the members inherited from the |
| 118 * passed [ClassElement]'s superclass hierarchy, and the associated executable |
| 119 * element. |
| 120 */ |
| 121 Map<String, ExecutableElement> getMembersInheritedFromClasses( |
| 122 ClassElement classElt) => |
| 123 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()); |
| 124 |
| 125 /** |
| 126 * Return a table mapping the string names of the members inherited from the |
| 127 * passed [ClassElement]'s interface hierarchy, and the associated executable |
| 128 * element. |
| 129 */ |
| 130 Map<String, ExecutableElement> getMembersInheritedFromInterfaces( |
| 131 ClassElement classElt) => |
| 132 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()); |
| 133 |
| 134 /** |
| 135 * Given some [ClassElement] and some member name, this returns the |
| 136 * [ExecutableElement] that the class inherits from the mixins, |
| 137 * superclasses or interfaces, that has the member name, if no member is inher
ited `null` is |
| 138 * returned. |
| 139 * |
| 140 * @param classElt the class element to query |
| 141 * @param memberName the name of the executable element to find and return |
| 142 * @return the inherited executable element with the member name, or `null` if
no such |
| 143 * member exists |
| 144 */ |
| 145 ExecutableElement lookupInheritance( |
| 146 ClassElement classElt, String memberName) { |
| 147 if (memberName == null || memberName.isEmpty) { |
| 148 return null; |
| 149 } |
| 150 ExecutableElement executable = _computeClassChainLookupMap( |
| 151 classElt, new HashSet<ClassElement>())[memberName]; |
| 152 if (executable == null) { |
| 153 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>())[ |
| 154 memberName]; |
| 155 } |
| 156 return executable; |
| 157 } |
| 158 |
| 159 /** |
| 160 * Given some [ClassElement] and some member name, this returns the |
| 161 * [ExecutableElement] that the class either declares itself, or |
| 162 * inherits, that has the member name, if no member is inherited `null` is ret
urned. |
| 163 * |
| 164 * @param classElt the class element to query |
| 165 * @param memberName the name of the executable element to find and return |
| 166 * @return the inherited executable element with the member name, or `null` if
no such |
| 167 * member exists |
| 168 */ |
| 169 ExecutableElement lookupMember(ClassElement classElt, String memberName) { |
| 170 ExecutableElement element = _lookupMemberInClass(classElt, memberName); |
| 171 if (element != null) { |
| 172 return element; |
| 173 } |
| 174 return lookupInheritance(classElt, memberName); |
| 175 } |
| 176 |
| 177 /** |
| 178 * Determine the set of methods which is overridden by the given class member.
If no member is |
| 179 * inherited, an empty list is returned. If one of the inherited members is a |
| 180 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu
ent inherited |
| 181 * elements. |
| 182 * |
| 183 * @param classElt the class to query |
| 184 * @param memberName the name of the class member to query |
| 185 * @return a list of overridden methods |
| 186 */ |
| 187 List<ExecutableElement> lookupOverrides( |
| 188 ClassElement classElt, String memberName) { |
| 189 List<ExecutableElement> result = new List<ExecutableElement>(); |
| 190 if (memberName == null || memberName.isEmpty) { |
| 191 return result; |
| 192 } |
| 193 List<Map<String, ExecutableElement>> interfaceMaps = |
| 194 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>()); |
| 195 if (interfaceMaps != null) { |
| 196 for (Map<String, ExecutableElement> interfaceMap in interfaceMaps) { |
| 197 ExecutableElement overriddenElement = interfaceMap[memberName]; |
| 198 if (overriddenElement != null) { |
| 199 if (overriddenElement is MultiplyInheritedExecutableElement) { |
| 200 for (ExecutableElement element |
| 201 in overriddenElement.inheritedElements) { |
| 202 result.add(element); |
| 203 } |
| 204 } else { |
| 205 result.add(overriddenElement); |
| 206 } |
| 207 } |
| 208 } |
| 209 } |
| 210 return result; |
| 211 } |
| 212 |
| 213 /** |
| 214 * This method takes some inherited [FunctionType], and resolves all the param
eterized types |
| 215 * in the function type, dependent on the class in which it is being overridde
n. |
| 216 * |
| 217 * @param baseFunctionType the function type that is being overridden |
| 218 * @param memberName the name of the member, this is used to lookup the inheri
tance path of the |
| 219 * override |
| 220 * @param definingType the type that is overriding the member |
| 221 * @return the passed function type with any parameterized types substituted |
| 222 */ |
| 223 // TODO(jmesserly): investigate why this is needed in ErrorVerifier's override |
| 224 // checking. There seems to be some rare cases where we get partially |
| 225 // substituted type arguments, and the function types don't compare equally. |
| 226 FunctionType substituteTypeArgumentsInMemberFromInheritance( |
| 227 FunctionType baseFunctionType, |
| 228 String memberName, |
| 229 InterfaceType definingType) { |
| 230 // if the baseFunctionType is null, or does not have any parameters, |
| 231 // return it. |
| 232 if (baseFunctionType == null || |
| 233 baseFunctionType.typeArguments.length == 0) { |
| 234 return baseFunctionType; |
| 235 } |
| 236 // First, generate the path from the defining type to the overridden member |
| 237 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); |
| 238 _computeInheritancePath(inheritancePath, definingType, memberName); |
| 239 if (inheritancePath == null || inheritancePath.isEmpty) { |
| 240 // TODO(jwren) log analysis engine error |
| 241 return baseFunctionType; |
| 242 } |
| 243 FunctionType functionTypeToReturn = baseFunctionType; |
| 244 // loop backward through the list substituting as we go: |
| 245 while (!inheritancePath.isEmpty) { |
| 246 InterfaceType lastType = inheritancePath.removeLast(); |
| 247 List<DartType> parameterTypes = lastType.element.type.typeArguments; |
| 248 List<DartType> argumentTypes = lastType.typeArguments; |
| 249 functionTypeToReturn = |
| 250 functionTypeToReturn.substitute2(argumentTypes, parameterTypes); |
| 251 } |
| 252 return functionTypeToReturn; |
| 253 } |
| 254 |
| 255 /** |
| 256 * Compute and return a mapping between the set of all string names of the mem
bers inherited from |
| 257 * the passed [ClassElement] superclass hierarchy, and the associated |
| 258 * [ExecutableElement]. |
| 259 * |
| 260 * @param classElt the class element to query |
| 261 * @param visitedClasses a set of visited classes passed back into this method
when it calls |
| 262 * itself recursively |
| 263 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 264 * [ClassElement] superclass hierarchy, and the associated [Executable
Element] |
| 265 */ |
| 266 Map<String, ExecutableElement> _computeClassChainLookupMap( |
| 267 ClassElement classElt, Set<ClassElement> visitedClasses) { |
| 268 Map<String, ExecutableElement> resultMap = _classLookup[classElt]; |
| 269 if (resultMap != null) { |
| 270 return resultMap; |
| 271 } else { |
| 272 resultMap = new Map<String, ExecutableElement>(); |
| 273 } |
| 274 InterfaceType supertype = classElt.supertype; |
| 275 if (supertype == null) { |
| 276 // classElt is Object |
| 277 _classLookup[classElt] = resultMap; |
| 278 return resultMap; |
| 279 } |
| 280 ClassElement superclassElt = supertype.element; |
| 281 if (superclassElt != null) { |
| 282 if (!visitedClasses.contains(superclassElt)) { |
| 283 visitedClasses.add(superclassElt); |
| 284 try { |
| 285 resultMap = new Map<String, ExecutableElement>.from( |
| 286 _computeClassChainLookupMap(superclassElt, visitedClasses)); |
| 287 // |
| 288 // Substitute the super types down the hierarchy. |
| 289 // |
| 290 _substituteTypeParametersDownHierarchy(supertype, resultMap); |
| 291 // |
| 292 // Include the members from the superclass in the resultMap. |
| 293 // |
| 294 _recordMapWithClassMembers( |
| 295 resultMap, supertype, _includeAbstractFromSuperclasses); |
| 296 } finally { |
| 297 visitedClasses.remove(superclassElt); |
| 298 } |
| 299 } else { |
| 300 // This case happens only when the superclass was previously visited and |
| 301 // not in the lookup, meaning this is meant to shorten the compute for |
| 302 // recursive cases. |
| 303 _classLookup[superclassElt] = resultMap; |
| 304 return resultMap; |
| 305 } |
| 306 } |
| 307 // |
| 308 // Include the members from the mixins in the resultMap. If there are |
| 309 // multiple mixins, visit them in the order listed so that methods in later |
| 310 // mixins will overwrite identically-named methods in earlier mixins. |
| 311 // |
| 312 List<InterfaceType> mixins = classElt.mixins; |
| 313 for (InterfaceType mixin in mixins) { |
| 314 ClassElement mixinElement = mixin.element; |
| 315 if (mixinElement != null) { |
| 316 if (!visitedClasses.contains(mixinElement)) { |
| 317 visitedClasses.add(mixinElement); |
| 318 try { |
| 319 Map<String, ExecutableElement> map = |
| 320 new Map<String, ExecutableElement>(); |
| 321 // |
| 322 // Include the members from the mixin in the resultMap. |
| 323 // |
| 324 _recordMapWithClassMembers( |
| 325 map, mixin, _includeAbstractFromSuperclasses); |
| 326 // |
| 327 // Add the members from map into result map. |
| 328 // |
| 329 for (String memberName in map.keys) { |
| 330 ExecutableElement value = map[memberName]; |
| 331 ClassElement definingClass = value |
| 332 .getAncestor((Element element) => element is ClassElement); |
| 333 if (!definingClass.type.isObject) { |
| 334 ExecutableElement existingValue = resultMap[memberName]; |
| 335 if (existingValue == null || |
| 336 (existingValue != null && !_isAbstract(value))) { |
| 337 resultMap[memberName] = value; |
| 338 } |
| 339 } |
| 340 } |
| 341 } finally { |
| 342 visitedClasses.remove(mixinElement); |
| 343 } |
| 344 } else { |
| 345 // This case happens only when the superclass was previously visited |
| 346 // and not in the lookup, meaning this is meant to shorten the compute |
| 347 // for recursive cases. |
| 348 _classLookup[mixinElement] = resultMap; |
| 349 return resultMap; |
| 350 } |
| 351 } |
| 352 } |
| 353 _classLookup[classElt] = resultMap; |
| 354 return resultMap; |
| 355 } |
| 356 |
| 357 /** |
| 358 * Compute and return the inheritance path given the context of a type and a m
ember that is |
| 359 * overridden in the inheritance path (for which the type is in the path). |
| 360 * |
| 361 * @param chain the inheritance path that is built up as this method calls its
elf recursively, |
| 362 * when this method is called an empty [LinkedList] should be provide
d |
| 363 * @param currentType the current type in the inheritance path |
| 364 * @param memberName the name of the member that is being looked up the inheri
tance path |
| 365 */ |
| 366 void _computeInheritancePath(Queue<InterfaceType> chain, |
| 367 InterfaceType currentType, String memberName) { |
| 368 // TODO (jwren) create a public version of this method which doesn't require |
| 369 // the initial chain to be provided, then provided tests for this |
| 370 // functionality in InheritanceManagerTest |
| 371 chain.add(currentType); |
| 372 ClassElement classElt = currentType.element; |
| 373 InterfaceType supertype = classElt.supertype; |
| 374 // Base case- reached Object |
| 375 if (supertype == null) { |
| 376 // Looked up the chain all the way to Object, return null. |
| 377 // This should never happen. |
| 378 return; |
| 379 } |
| 380 // If we are done, return the chain |
| 381 // We are not done if this is the first recursive call on this method. |
| 382 if (chain.length != 1) { |
| 383 // We are done however if the member is in this classElt |
| 384 if (_lookupMemberInClass(classElt, memberName) != null) { |
| 385 return; |
| 386 } |
| 387 } |
| 388 // Mixins- note that mixins call lookupMemberInClass, not lookupMember |
| 389 List<InterfaceType> mixins = classElt.mixins; |
| 390 for (int i = mixins.length - 1; i >= 0; i--) { |
| 391 ClassElement mixinElement = mixins[i].element; |
| 392 if (mixinElement != null) { |
| 393 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName); |
| 394 if (elt != null) { |
| 395 // this is equivalent (but faster than) calling this method |
| 396 // recursively |
| 397 // (return computeInheritancePath(chain, mixins[i], memberName);) |
| 398 chain.add(mixins[i]); |
| 399 return; |
| 400 } |
| 401 } |
| 402 } |
| 403 // Superclass |
| 404 ClassElement superclassElt = supertype.element; |
| 405 if (lookupMember(superclassElt, memberName) != null) { |
| 406 _computeInheritancePath(chain, supertype, memberName); |
| 407 return; |
| 408 } |
| 409 // Interfaces |
| 410 List<InterfaceType> interfaces = classElt.interfaces; |
| 411 for (InterfaceType interfaceType in interfaces) { |
| 412 ClassElement interfaceElement = interfaceType.element; |
| 413 if (interfaceElement != null && |
| 414 lookupMember(interfaceElement, memberName) != null) { |
| 415 _computeInheritancePath(chain, interfaceType, memberName); |
| 416 return; |
| 417 } |
| 418 } |
| 419 } |
| 420 |
| 421 /** |
| 422 * Compute and return a mapping between the set of all string names of the mem
bers inherited from |
| 423 * the passed [ClassElement] interface hierarchy, and the associated |
| 424 * [ExecutableElement]. |
| 425 * |
| 426 * @param classElt the class element to query |
| 427 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls |
| 428 * itself recursively |
| 429 * @return a mapping between the set of all string names of the members inheri
ted from the passed |
| 430 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement] |
| 431 */ |
| 432 Map<String, ExecutableElement> _computeInterfaceLookupMap( |
| 433 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { |
| 434 Map<String, ExecutableElement> resultMap = _interfaceLookup[classElt]; |
| 435 if (resultMap != null) { |
| 436 return resultMap; |
| 437 } |
| 438 List<Map<String, ExecutableElement>> lookupMaps = |
| 439 _gatherInterfaceLookupMaps(classElt, visitedInterfaces); |
| 440 if (lookupMaps == null) { |
| 441 resultMap = new Map<String, ExecutableElement>(); |
| 442 } else { |
| 443 HashMap<String, List<ExecutableElement>> unionMap = |
| 444 _unionInterfaceLookupMaps(lookupMaps); |
| 445 resultMap = _resolveInheritanceLookup(classElt, unionMap); |
| 446 } |
| 447 _interfaceLookup[classElt] = resultMap; |
| 448 return resultMap; |
| 449 } |
| 450 |
| 451 /** |
| 452 * Collect a list of interface lookup maps whose elements correspond to all of
the classes |
| 453 * directly above [classElt] in the class hierarchy (the direct superclass if
any, all |
| 454 * mixins, and all direct superinterfaces). Each item in the list is the inter
face lookup map |
| 455 * returned by [computeInterfaceLookupMap] for the corresponding super, except
with type |
| 456 * parameters appropriately substituted. |
| 457 * |
| 458 * @param classElt the class element to query |
| 459 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls |
| 460 * itself recursively |
| 461 * @return `null` if there was a problem (such as a loop in the class hierarch
y) or if there |
| 462 * are no classes above this one in the class hierarchy. Otherwise, a
list of interface |
| 463 * lookup maps. |
| 464 */ |
| 465 List<Map<String, ExecutableElement>> _gatherInterfaceLookupMaps( |
| 466 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { |
| 467 InterfaceType supertype = classElt.supertype; |
| 468 ClassElement superclassElement = supertype?.element; |
| 469 List<InterfaceType> mixins = classElt.mixins; |
| 470 List<InterfaceType> interfaces = classElt.interfaces; |
| 471 // Recursively collect the list of mappings from all of the interface types |
| 472 List<Map<String, ExecutableElement>> lookupMaps = |
| 473 new List<Map<String, ExecutableElement>>(); |
| 474 // |
| 475 // Superclass element |
| 476 // |
| 477 if (superclassElement != null) { |
| 478 if (!visitedInterfaces.contains(superclassElement)) { |
| 479 try { |
| 480 visitedInterfaces.add(superclassElement); |
| 481 // |
| 482 // Recursively compute the map for the super type. |
| 483 // |
| 484 Map<String, ExecutableElement> map = |
| 485 _computeInterfaceLookupMap(superclassElement, visitedInterfaces); |
| 486 map = new Map<String, ExecutableElement>.from(map); |
| 487 // |
| 488 // Substitute the super type down the hierarchy. |
| 489 // |
| 490 _substituteTypeParametersDownHierarchy(supertype, map); |
| 491 // |
| 492 // Add any members from the super type into the map as well. |
| 493 // |
| 494 _recordMapWithClassMembers(map, supertype, true); |
| 495 lookupMaps.add(map); |
| 496 } finally { |
| 497 visitedInterfaces.remove(superclassElement); |
| 498 } |
| 499 } else { |
| 500 return null; |
| 501 } |
| 502 } |
| 503 // |
| 504 // Mixin elements |
| 505 // |
| 506 for (int i = mixins.length - 1; i >= 0; i--) { |
| 507 InterfaceType mixinType = mixins[i]; |
| 508 ClassElement mixinElement = mixinType.element; |
| 509 if (mixinElement != null) { |
| 510 if (!visitedInterfaces.contains(mixinElement)) { |
| 511 try { |
| 512 visitedInterfaces.add(mixinElement); |
| 513 // |
| 514 // Recursively compute the map for the mixin. |
| 515 // |
| 516 Map<String, ExecutableElement> map = |
| 517 _computeInterfaceLookupMap(mixinElement, visitedInterfaces); |
| 518 map = new Map<String, ExecutableElement>.from(map); |
| 519 // |
| 520 // Substitute the mixin type down the hierarchy. |
| 521 // |
| 522 _substituteTypeParametersDownHierarchy(mixinType, map); |
| 523 // |
| 524 // Add any members from the mixin type into the map as well. |
| 525 // |
| 526 _recordMapWithClassMembers(map, mixinType, true); |
| 527 lookupMaps.add(map); |
| 528 } finally { |
| 529 visitedInterfaces.remove(mixinElement); |
| 530 } |
| 531 } else { |
| 532 return null; |
| 533 } |
| 534 } |
| 535 } |
| 536 // |
| 537 // Interface elements |
| 538 // |
| 539 int interfaceLength = interfaces.length; |
| 540 for (int i = 0; i < interfaceLength; i++) { |
| 541 InterfaceType interfaceType = interfaces[i]; |
| 542 ClassElement interfaceElement = interfaceType.element; |
| 543 if (interfaceElement != null) { |
| 544 if (!visitedInterfaces.contains(interfaceElement)) { |
| 545 try { |
| 546 visitedInterfaces.add(interfaceElement); |
| 547 // |
| 548 // Recursively compute the map for the interfaces. |
| 549 // |
| 550 Map<String, ExecutableElement> map = |
| 551 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces); |
| 552 map = new Map<String, ExecutableElement>.from(map); |
| 553 // |
| 554 // Substitute the supertypes down the hierarchy |
| 555 // |
| 556 _substituteTypeParametersDownHierarchy(interfaceType, map); |
| 557 // |
| 558 // And add any members from the interface into the map as well. |
| 559 // |
| 560 _recordMapWithClassMembers(map, interfaceType, true); |
| 561 lookupMaps.add(map); |
| 562 } finally { |
| 563 visitedInterfaces.remove(interfaceElement); |
| 564 } |
| 565 } else { |
| 566 return null; |
| 567 } |
| 568 } |
| 569 } |
| 570 if (lookupMaps.length == 0) { |
| 571 return null; |
| 572 } |
| 573 return lookupMaps; |
| 574 } |
| 575 |
| 576 /** |
| 577 * Given some [classElement], this method finds and returns the executable |
| 578 * element with the given [memberName] in the class element. Static members, |
| 579 * members in super types and members not accessible from the current library |
| 580 * are not considered. |
| 581 */ |
| 582 ExecutableElement _lookupMemberInClass( |
| 583 ClassElement classElement, String memberName) { |
| 584 List<MethodElement> methods = classElement.methods; |
| 585 int methodLength = methods.length; |
| 586 for (int i = 0; i < methodLength; i++) { |
| 587 MethodElement method = methods[i]; |
| 588 if (memberName == method.name && |
| 589 method.isAccessibleIn(_library) && |
| 590 !method.isStatic) { |
| 591 return method; |
| 592 } |
| 593 } |
| 594 List<PropertyAccessorElement> accessors = classElement.accessors; |
| 595 int accessorLength = accessors.length; |
| 596 for (int i = 0; i < accessorLength; i++) { |
| 597 PropertyAccessorElement accessor = accessors[i]; |
| 598 if (memberName == accessor.name && |
| 599 accessor.isAccessibleIn(_library) && |
| 600 !accessor.isStatic) { |
| 601 return accessor; |
| 602 } |
| 603 } |
| 604 return null; |
| 605 } |
| 606 |
| 607 /** |
| 608 * Record the passed map with the set of all members (methods, getters and set
ters) in the type |
| 609 * into the passed map. |
| 610 * |
| 611 * @param map some non-`null` map to put the methods and accessors from the pa
ssed |
| 612 * [ClassElement] into |
| 613 * @param type the type that will be recorded into the passed map |
| 614 * @param doIncludeAbstract `true` if abstract members will be put into the ma
p |
| 615 */ |
| 616 void _recordMapWithClassMembers(Map<String, ExecutableElement> map, |
| 617 InterfaceType type, bool doIncludeAbstract) { |
| 618 Set<InterfaceType> seenTypes = new HashSet<InterfaceType>(); |
| 619 while (type.element.isMixinApplication) { |
| 620 List<InterfaceType> mixins = type.mixins; |
| 621 if (!seenTypes.add(type) || mixins.isEmpty) { |
| 622 // In the case of a circularity in the type hierarchy, just don't add |
| 623 // any members to the map. |
| 624 return; |
| 625 } |
| 626 type = mixins.last; |
| 627 } |
| 628 List<MethodElement> methods = type.methods; |
| 629 for (MethodElement method in methods) { |
| 630 if (method.isAccessibleIn(_library) && |
| 631 !method.isStatic && |
| 632 (doIncludeAbstract || !method.isAbstract)) { |
| 633 map[method.name] = method; |
| 634 } |
| 635 } |
| 636 List<PropertyAccessorElement> accessors = type.accessors; |
| 637 for (PropertyAccessorElement accessor in accessors) { |
| 638 if (accessor.isAccessibleIn(_library) && |
| 639 !accessor.isStatic && |
| 640 (doIncludeAbstract || !accessor.isAbstract)) { |
| 641 map[accessor.name] = accessor; |
| 642 } |
| 643 } |
| 644 } |
| 645 |
| 646 /** |
| 647 * This method is used to report errors on when they are found computing inher
itance information. |
| 648 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes
e generated |
| 649 * error codes are reported back into the analysis engine. |
| 650 * |
| 651 * @param classElt the location of the source for which the exception occurred |
| 652 * @param offset the offset of the location of the error |
| 653 * @param length the length of the location of the error |
| 654 * @param errorCode the error code to be associated with this error |
| 655 * @param arguments the arguments used to build the error message |
| 656 */ |
| 657 void _reportError(ClassElement classElt, int offset, int length, |
| 658 ErrorCode errorCode, List<Object> arguments) { |
| 659 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt]; |
| 660 if (errorSet == null) { |
| 661 errorSet = new HashSet<AnalysisError>(); |
| 662 _errorsInClassElement[classElt] = errorSet; |
| 663 } |
| 664 errorSet.add(new AnalysisError( |
| 665 classElt.source, offset, length, errorCode, arguments)); |
| 666 } |
| 667 |
| 668 /** |
| 669 * Given the set of methods defined by classes above [classElt] in the class h
ierarchy, |
| 670 * apply the appropriate inheritance rules to determine those methods inherite
d by or overridden |
| 671 * by [classElt]. Also report static warnings |
| 672 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and |
| 673 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap
propriate. |
| 674 * |
| 675 * @param classElt the class element to query. |
| 676 * @param unionMap a mapping from method name to the set of unique (in terms o
f signature) methods |
| 677 * defined in superclasses of [classElt]. |
| 678 * @return the inheritance lookup map for [classElt]. |
| 679 */ |
| 680 Map<String, ExecutableElement> _resolveInheritanceLookup( |
| 681 ClassElement classElt, Map<String, List<ExecutableElement>> unionMap) { |
| 682 Map<String, ExecutableElement> resultMap = |
| 683 new Map<String, ExecutableElement>(); |
| 684 unionMap.forEach((String key, List<ExecutableElement> list) { |
| 685 int numOfEltsWithMatchingNames = list.length; |
| 686 if (numOfEltsWithMatchingNames == 1) { |
| 687 // |
| 688 // Example: class A inherits only 1 method named 'm'. |
| 689 // Since it is the only such method, it is inherited. |
| 690 // Another example: class A inherits 2 methods named 'm' from 2 |
| 691 // different interfaces, but they both have the same signature, so it is |
| 692 // the method inherited. |
| 693 // |
| 694 resultMap[key] = list[0]; |
| 695 } else { |
| 696 // |
| 697 // Then numOfEltsWithMatchingNames > 1, check for the warning cases. |
| 698 // |
| 699 bool allMethods = true; |
| 700 bool allSetters = true; |
| 701 bool allGetters = true; |
| 702 for (ExecutableElement executableElement in list) { |
| 703 if (executableElement is PropertyAccessorElement) { |
| 704 allMethods = false; |
| 705 if (executableElement.isSetter) { |
| 706 allGetters = false; |
| 707 } else { |
| 708 allSetters = false; |
| 709 } |
| 710 } else { |
| 711 allGetters = false; |
| 712 allSetters = false; |
| 713 } |
| 714 } |
| 715 // |
| 716 // If there isn't a mixture of methods with getters, then continue, |
| 717 // otherwise create a warning. |
| 718 // |
| 719 if (allMethods || allGetters || allSetters) { |
| 720 // |
| 721 // Compute the element whose type is the subtype of all of the other |
| 722 // types. |
| 723 // |
| 724 List<ExecutableElement> elements = new List.from(list); |
| 725 List<FunctionType> executableElementTypes = |
| 726 new List<FunctionType>(numOfEltsWithMatchingNames); |
| 727 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
| 728 executableElementTypes[i] = elements[i].type; |
| 729 } |
| 730 List<int> subtypesOfAllOtherTypesIndexes = new List<int>(); |
| 731 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { |
| 732 FunctionType subtype = executableElementTypes[i]; |
| 733 if (subtype == null) { |
| 734 continue; |
| 735 } |
| 736 bool subtypeOfAllTypes = true; |
| 737 TypeSystem typeSystem = _library.context.typeSystem; |
| 738 for (int j = 0; |
| 739 j < numOfEltsWithMatchingNames && subtypeOfAllTypes; |
| 740 j++) { |
| 741 if (i != j) { |
| 742 if (!typeSystem.isSubtypeOf( |
| 743 subtype, executableElementTypes[j])) { |
| 744 subtypeOfAllTypes = false; |
| 745 break; |
| 746 } |
| 747 } |
| 748 } |
| 749 if (subtypeOfAllTypes) { |
| 750 subtypesOfAllOtherTypesIndexes.add(i); |
| 751 } |
| 752 } |
| 753 // |
| 754 // The following is split into three cases determined by the number of |
| 755 // elements in subtypesOfAllOtherTypes |
| 756 // |
| 757 if (subtypesOfAllOtherTypesIndexes.length == 1) { |
| 758 // |
| 759 // Example: class A inherited only 2 method named 'm'. |
| 760 // One has the function type '() -> dynamic' and one has the |
| 761 // function type '([int]) -> dynamic'. Since the second method is a |
| 762 // subtype of all the others, it is the inherited method. |
| 763 // Tests: InheritanceManagerTest. |
| 764 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_* |
| 765 // |
| 766 resultMap[key] = elements[subtypesOfAllOtherTypesIndexes[0]]; |
| 767 } else { |
| 768 if (subtypesOfAllOtherTypesIndexes.isEmpty) { |
| 769 // |
| 770 // Determine if the current class has a method or accessor with |
| 771 // the member name, if it does then then this class does not |
| 772 // "inherit" from any of the supertypes. See issue 16134. |
| 773 // |
| 774 bool classHasMember = false; |
| 775 if (allMethods) { |
| 776 classHasMember = classElt.getMethod(key) != null; |
| 777 } else { |
| 778 List<PropertyAccessorElement> accessors = classElt.accessors; |
| 779 for (int i = 0; i < accessors.length; i++) { |
| 780 if (accessors[i].name == key) { |
| 781 classHasMember = true; |
| 782 } |
| 783 } |
| 784 } |
| 785 // |
| 786 // Example: class A inherited only 2 method named 'm'. |
| 787 // One has the function type '() -> int' and one has the function |
| 788 // type '() -> String'. Since neither is a subtype of the other, |
| 789 // we create a warning, and have this class inherit nothing. |
| 790 // |
| 791 if (!classHasMember) { |
| 792 String firstTwoFuntionTypesStr = |
| 793 "${executableElementTypes[0]}, ${executableElementTypes[1]}"
; |
| 794 _reportError( |
| 795 classElt, |
| 796 classElt.nameOffset, |
| 797 classElt.nameLength, |
| 798 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, |
| 799 [key, firstTwoFuntionTypesStr]); |
| 800 } |
| 801 } else { |
| 802 // |
| 803 // Example: class A inherits 2 methods named 'm'. |
| 804 // One has the function type '(int) -> dynamic' and one has the |
| 805 // function type '(num) -> dynamic'. Since they are both a subtype |
| 806 // of the other, a synthetic function '(dynamic) -> dynamic' is |
| 807 // inherited. |
| 808 // Tests: test_getMapOfMembersInheritedFromInterfaces_ |
| 809 // union_multipleSubtypes_* |
| 810 // |
| 811 List<ExecutableElement> elementArrayToMerge = |
| 812 new List<ExecutableElement>( |
| 813 subtypesOfAllOtherTypesIndexes.length); |
| 814 for (int i = 0; i < elementArrayToMerge.length; i++) { |
| 815 elementArrayToMerge[i] = |
| 816 elements[subtypesOfAllOtherTypesIndexes[i]]; |
| 817 } |
| 818 ExecutableElement mergedExecutableElement = |
| 819 _computeMergedExecutableElement(elementArrayToMerge); |
| 820 resultMap[key] = mergedExecutableElement; |
| 821 } |
| 822 } |
| 823 } else { |
| 824 _reportError( |
| 825 classElt, |
| 826 classElt.nameOffset, |
| 827 classElt.nameLength, |
| 828 StaticWarningCode |
| 829 .INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD, |
| 830 [key]); |
| 831 } |
| 832 } |
| 833 }); |
| 834 return resultMap; |
| 835 } |
| 836 |
| 837 /** |
| 838 * Loop through all of the members in the given [map], performing type |
| 839 * parameter substitutions using a passed [supertype]. |
| 840 */ |
| 841 void _substituteTypeParametersDownHierarchy( |
| 842 InterfaceType superType, Map<String, ExecutableElement> map) { |
| 843 for (String memberName in map.keys) { |
| 844 ExecutableElement executableElement = map[memberName]; |
| 845 if (executableElement is MethodMember) { |
| 846 map[memberName] = MethodMember.from(executableElement, superType); |
| 847 } else if (executableElement is PropertyAccessorMember) { |
| 848 map[memberName] = |
| 849 PropertyAccessorMember.from(executableElement, superType); |
| 850 } |
| 851 } |
| 852 } |
| 853 |
| 854 /** |
| 855 * Union all of the [lookupMaps] together into a single map, grouping the Exec
utableElements |
| 856 * into a list where none of the elements are equal where equality is determin
ed by having equal |
| 857 * function types. (We also take note too of the kind of the element: ()->int
and () -> int may |
| 858 * not be equal if one is a getter and the other is a method.) |
| 859 * |
| 860 * @param lookupMaps the maps to be unioned together. |
| 861 * @return the resulting union map. |
| 862 */ |
| 863 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps( |
| 864 List<Map<String, ExecutableElement>> lookupMaps) { |
| 865 HashMap<String, List<ExecutableElement>> unionMap = |
| 866 new HashMap<String, List<ExecutableElement>>(); |
| 867 for (Map<String, ExecutableElement> lookupMap in lookupMaps) { |
| 868 for (String memberName in lookupMap.keys) { |
| 869 // Get the list value out of the unionMap |
| 870 List<ExecutableElement> list = unionMap.putIfAbsent( |
| 871 memberName, () => new List<ExecutableElement>()); |
| 872 // Fetch the entry out of this lookupMap |
| 873 ExecutableElement newExecutableElementEntry = lookupMap[memberName]; |
| 874 if (list.isEmpty) { |
| 875 // If the list is empty, just the new value |
| 876 list.add(newExecutableElementEntry); |
| 877 } else { |
| 878 // Otherwise, only add the newExecutableElementEntry if it isn't |
| 879 // already in the list, this covers situation where a class inherits |
| 880 // two methods (or two getters) that are identical. |
| 881 bool alreadyInList = false; |
| 882 bool isMethod1 = newExecutableElementEntry is MethodElement; |
| 883 for (ExecutableElement executableElementInList in list) { |
| 884 bool isMethod2 = executableElementInList is MethodElement; |
| 885 if (isMethod1 == isMethod2 && |
| 886 executableElementInList.type == |
| 887 newExecutableElementEntry.type) { |
| 888 alreadyInList = true; |
| 889 break; |
| 890 } |
| 891 } |
| 892 if (!alreadyInList) { |
| 893 list.add(newExecutableElementEntry); |
| 894 } |
| 895 } |
| 896 } |
| 897 } |
| 898 return unionMap; |
| 899 } |
| 900 |
| 901 /** |
| 902 * Given some array of [ExecutableElement]s, this method creates a synthetic e
lement as |
| 903 * described in 8.1.1: |
| 904 * |
| 905 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa
rameters of a |
| 906 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t
he number of |
| 907 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot
e the set of all |
| 908 * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then
let |
| 909 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i> |
| 910 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
i <= k.</i> |
| 911 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter
s of type |
| 912 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name
d parameters |
| 913 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>. |
| 914 * |
| 915 */ |
| 916 static ExecutableElement _computeMergedExecutableElement( |
| 917 List<ExecutableElement> elementArrayToMerge) { |
| 918 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]); |
| 919 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]); |
| 920 Set<String> namedParametersList = new HashSet<String>(); |
| 921 for (int i = 1; i < elementArrayToMerge.length; i++) { |
| 922 ExecutableElement element = elementArrayToMerge[i]; |
| 923 int numOfPositionalParams = _getNumOfPositionalParameters(element); |
| 924 if (h < numOfPositionalParams) { |
| 925 h = numOfPositionalParams; |
| 926 } |
| 927 int numOfRequiredParams = _getNumOfRequiredParameters(element); |
| 928 if (r > numOfRequiredParams) { |
| 929 r = numOfRequiredParams; |
| 930 } |
| 931 namedParametersList.addAll(_getNamedParameterNames(element)); |
| 932 } |
| 933 return _createSyntheticExecutableElement( |
| 934 elementArrayToMerge, |
| 935 elementArrayToMerge[0].displayName, |
| 936 r, |
| 937 h - r, |
| 938 new List.from(namedParametersList)); |
| 939 } |
| 940 |
| 941 /** |
| 942 * Used by [computeMergedExecutableElement] to actually create the |
| 943 * synthetic element. |
| 944 * |
| 945 * @param elementArrayToMerge the array used to create the synthetic element |
| 946 * @param name the name of the method, getter or setter |
| 947 * @param numOfRequiredParameters the number of required parameters |
| 948 * @param numOfPositionalParameters the number of positional parameters |
| 949 * @param namedParameters the list of [String]s that are the named parameters |
| 950 * @return the created synthetic element |
| 951 */ |
| 952 static ExecutableElement _createSyntheticExecutableElement( |
| 953 List<ExecutableElement> elementArrayToMerge, |
| 954 String name, |
| 955 int numOfRequiredParameters, |
| 956 int numOfPositionalParameters, |
| 957 List<String> namedParameters) { |
| 958 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; |
| 959 SimpleIdentifier nameIdentifier = |
| 960 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, 0)); |
| 961 ExecutableElementImpl executable; |
| 962 ExecutableElement elementToMerge = elementArrayToMerge[0]; |
| 963 if (elementToMerge is MethodElement) { |
| 964 MultiplyInheritedMethodElementImpl unionedMethod = |
| 965 new MultiplyInheritedMethodElementImpl(nameIdentifier); |
| 966 unionedMethod.inheritedElements = elementArrayToMerge; |
| 967 executable = unionedMethod; |
| 968 } else if (elementToMerge is PropertyAccessorElement) { |
| 969 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = |
| 970 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier); |
| 971 unionedPropertyAccessor.getter = elementToMerge.isGetter; |
| 972 unionedPropertyAccessor.setter = elementToMerge.isSetter; |
| 973 unionedPropertyAccessor.inheritedElements = elementArrayToMerge; |
| 974 executable = unionedPropertyAccessor; |
| 975 } else { |
| 976 throw new AnalysisException( |
| 977 'Invalid class of element in merge: ${elementToMerge.runtimeType}'); |
| 978 } |
| 979 int numOfParameters = numOfRequiredParameters + |
| 980 numOfPositionalParameters + |
| 981 namedParameters.length; |
| 982 List<ParameterElement> parameters = |
| 983 new List<ParameterElement>(numOfParameters); |
| 984 int i = 0; |
| 985 for (int j = 0; j < numOfRequiredParameters; j++, i++) { |
| 986 ParameterElementImpl parameter = new ParameterElementImpl("", 0); |
| 987 parameter.type = dynamicType; |
| 988 parameter.parameterKind = ParameterKind.REQUIRED; |
| 989 parameters[i] = parameter; |
| 990 } |
| 991 for (int k = 0; k < numOfPositionalParameters; k++, i++) { |
| 992 ParameterElementImpl parameter = new ParameterElementImpl("", 0); |
| 993 parameter.type = dynamicType; |
| 994 parameter.parameterKind = ParameterKind.POSITIONAL; |
| 995 parameters[i] = parameter; |
| 996 } |
| 997 for (int m = 0; m < namedParameters.length; m++, i++) { |
| 998 ParameterElementImpl parameter = |
| 999 new ParameterElementImpl(namedParameters[m], 0); |
| 1000 parameter.type = dynamicType; |
| 1001 parameter.parameterKind = ParameterKind.NAMED; |
| 1002 parameters[i] = parameter; |
| 1003 } |
| 1004 executable.returnType = dynamicType; |
| 1005 executable.parameters = parameters; |
| 1006 FunctionTypeImpl methodType = new FunctionTypeImpl(executable); |
| 1007 executable.type = methodType; |
| 1008 return executable; |
| 1009 } |
| 1010 |
| 1011 /** |
| 1012 * Given some [ExecutableElement], return the list of named parameters. |
| 1013 */ |
| 1014 static List<String> _getNamedParameterNames( |
| 1015 ExecutableElement executableElement) { |
| 1016 List<String> namedParameterNames = new List<String>(); |
| 1017 List<ParameterElement> parameters = executableElement.parameters; |
| 1018 for (int i = 0; i < parameters.length; i++) { |
| 1019 ParameterElement parameterElement = parameters[i]; |
| 1020 if (parameterElement.parameterKind == ParameterKind.NAMED) { |
| 1021 namedParameterNames.add(parameterElement.name); |
| 1022 } |
| 1023 } |
| 1024 return namedParameterNames; |
| 1025 } |
| 1026 |
| 1027 /** |
| 1028 * Given some [ExecutableElement] return the number of parameters of the speci
fied kind. |
| 1029 */ |
| 1030 static int _getNumOfParameters( |
| 1031 ExecutableElement executableElement, ParameterKind parameterKind) { |
| 1032 int parameterCount = 0; |
| 1033 List<ParameterElement> parameters = executableElement.parameters; |
| 1034 for (int i = 0; i < parameters.length; i++) { |
| 1035 ParameterElement parameterElement = parameters[i]; |
| 1036 if (parameterElement.parameterKind == parameterKind) { |
| 1037 parameterCount++; |
| 1038 } |
| 1039 } |
| 1040 return parameterCount; |
| 1041 } |
| 1042 |
| 1043 /** |
| 1044 * Given some [ExecutableElement] return the number of positional parameters. |
| 1045 * |
| 1046 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI
TIONAL]. |
| 1047 */ |
| 1048 static int _getNumOfPositionalParameters( |
| 1049 ExecutableElement executableElement) => |
| 1050 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) + |
| 1051 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL); |
| 1052 |
| 1053 /** |
| 1054 * Given some [ExecutableElement] return the number of required parameters. |
| 1055 */ |
| 1056 static int _getNumOfRequiredParameters(ExecutableElement executableElement) => |
| 1057 _getNumOfParameters(executableElement, ParameterKind.REQUIRED); |
| 1058 |
| 1059 /** |
| 1060 * Given some [ExecutableElement] returns `true` if it is an abstract member o
f a |
| 1061 * class. |
| 1062 * |
| 1063 * @param executableElement some [ExecutableElement] to evaluate |
| 1064 * @return `true` if the given element is an abstract member of a class |
| 1065 */ |
| 1066 static bool _isAbstract(ExecutableElement executableElement) { |
| 1067 if (executableElement is MethodElement) { |
| 1068 return executableElement.isAbstract; |
| 1069 } else if (executableElement is PropertyAccessorElement) { |
| 1070 return executableElement.isAbstract; |
| 1071 } |
| 1072 return false; |
| 1073 } |
| 1074 } |
| 1075 |
| 1076 /** |
| 1077 * This class is used to replace uses of `HashMap<String, ExecutableElement>` |
| 1078 * which are not as performant as this class. |
| 1079 */ |
| 1080 @deprecated |
| 1081 class MemberMap { |
| 1082 /** |
| 1083 * The current size of this map. |
| 1084 */ |
| 1085 int _size = 0; |
| 1086 |
| 1087 /** |
| 1088 * The array of keys. |
| 1089 */ |
| 1090 List<String> _keys; |
| 1091 |
| 1092 /** |
| 1093 * The array of ExecutableElement values. |
| 1094 */ |
| 1095 List<ExecutableElement> _values; |
| 1096 |
| 1097 /** |
| 1098 * Initialize a newly created member map to have the given [initialCapacity]. |
| 1099 * The map will grow if needed. |
| 1100 */ |
| 1101 MemberMap([int initialCapacity = 10]) { |
| 1102 _initArrays(initialCapacity); |
| 1103 } |
| 1104 |
| 1105 /** |
| 1106 * Initialize a newly created member map to contain the same members as the |
| 1107 * given [memberMap]. |
| 1108 */ |
| 1109 MemberMap.from(MemberMap memberMap) { |
| 1110 _initArrays(memberMap._size + 5); |
| 1111 for (int i = 0; i < memberMap._size; i++) { |
| 1112 _keys[i] = memberMap._keys[i]; |
| 1113 _values[i] = memberMap._values[i]; |
| 1114 } |
| 1115 _size = memberMap._size; |
| 1116 } |
| 1117 |
| 1118 /** |
| 1119 * Initialize a newly created member map to contain the same members as the |
| 1120 * given [map]. |
| 1121 */ |
| 1122 MemberMap.fromMap(Map<String, ExecutableElement> map) { |
| 1123 _size = map.length; |
| 1124 _initArrays(_size + 5); |
| 1125 int index = 0; |
| 1126 map.forEach((String memberName, ExecutableElement element) { |
| 1127 _keys[index] = memberName; |
| 1128 _values[index] = element; |
| 1129 index++; |
| 1130 }); |
| 1131 } |
| 1132 |
| 1133 /** |
| 1134 * The size of the map. |
| 1135 * |
| 1136 * @return the size of the map. |
| 1137 */ |
| 1138 int get size => _size; |
| 1139 |
| 1140 /** |
| 1141 * Given some key, return the ExecutableElement value from the map, if the key
does not exist in |
| 1142 * the map, `null` is returned. |
| 1143 * |
| 1144 * @param key some key to look up in the map |
| 1145 * @return the associated ExecutableElement value from the map, if the key doe
s not exist in the |
| 1146 * map, `null` is returned |
| 1147 */ |
| 1148 ExecutableElement get(String key) { |
| 1149 for (int i = 0; i < _size; i++) { |
| 1150 if (_keys[i] != null && _keys[i] == key) { |
| 1151 return _values[i]; |
| 1152 } |
| 1153 } |
| 1154 return null; |
| 1155 } |
| 1156 |
| 1157 /** |
| 1158 * Get and return the key at the specified location. If the key/value pair has
been removed from |
| 1159 * the set, then `null` is returned. |
| 1160 * |
| 1161 * @param i some non-zero value less than size |
| 1162 * @return the key at the passed index |
| 1163 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than |
| 1164 * zero or greater than or equal to the capacity of the arrays |
| 1165 */ |
| 1166 String getKey(int i) => _keys[i]; |
| 1167 |
| 1168 /** |
| 1169 * Get and return the ExecutableElement at the specified location. If the key/
value pair has been |
| 1170 * removed from the set, then then `null` is returned. |
| 1171 * |
| 1172 * @param i some non-zero value less than size |
| 1173 * @return the key at the passed index |
| 1174 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than |
| 1175 * zero or greater than or equal to the capacity of the arrays |
| 1176 */ |
| 1177 ExecutableElement getValue(int i) => _values[i]; |
| 1178 |
| 1179 /** |
| 1180 * Given some key/value pair, store the pair in the map. If the key exists alr
eady, then the new |
| 1181 * value overrides the old value. |
| 1182 * |
| 1183 * @param key the key to store in the map |
| 1184 * @param value the ExecutableElement value to store in the map |
| 1185 */ |
| 1186 void put(String key, ExecutableElement value) { |
| 1187 // If we already have a value with this key, override the value |
| 1188 for (int i = 0; i < _size; i++) { |
| 1189 if (_keys[i] != null && _keys[i] == key) { |
| 1190 _values[i] = value; |
| 1191 return; |
| 1192 } |
| 1193 } |
| 1194 // If needed, double the size of our arrays and copy values over in both |
| 1195 // arrays |
| 1196 if (_size == _keys.length) { |
| 1197 int newArrayLength = _size * 2; |
| 1198 List<String> keys_new_array = new List<String>(newArrayLength); |
| 1199 List<ExecutableElement> values_new_array = |
| 1200 new List<ExecutableElement>(newArrayLength); |
| 1201 for (int i = 0; i < _size; i++) { |
| 1202 keys_new_array[i] = _keys[i]; |
| 1203 } |
| 1204 for (int i = 0; i < _size; i++) { |
| 1205 values_new_array[i] = _values[i]; |
| 1206 } |
| 1207 _keys = keys_new_array; |
| 1208 _values = values_new_array; |
| 1209 } |
| 1210 // Put new value at end of array |
| 1211 _keys[_size] = key; |
| 1212 _values[_size] = value; |
| 1213 _size++; |
| 1214 } |
| 1215 |
| 1216 /** |
| 1217 * Given some [String] key, this method replaces the associated key and value
pair with |
| 1218 * `null`. The size is not decremented with this call, instead it is expected
that the users |
| 1219 * check for `null`. |
| 1220 * |
| 1221 * @param key the key of the key/value pair to remove from the map |
| 1222 */ |
| 1223 void remove(String key) { |
| 1224 for (int i = 0; i < _size; i++) { |
| 1225 if (_keys[i] == key) { |
| 1226 _keys[i] = null; |
| 1227 _values[i] = null; |
| 1228 return; |
| 1229 } |
| 1230 } |
| 1231 } |
| 1232 |
| 1233 /** |
| 1234 * Sets the ExecutableElement at the specified location. |
| 1235 * |
| 1236 * @param i some non-zero value less than size |
| 1237 * @param value the ExecutableElement value to store in the map |
| 1238 */ |
| 1239 void setValue(int i, ExecutableElement value) { |
| 1240 _values[i] = value; |
| 1241 } |
| 1242 |
| 1243 /** |
| 1244 * Initializes [keys] and [values]. |
| 1245 */ |
| 1246 void _initArrays(int initialCapacity) { |
| 1247 _keys = new List<String>(initialCapacity); |
| 1248 _values = new List<ExecutableElement>(initialCapacity); |
| 1249 } |
| 1250 } |
| OLD | NEW |