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