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

Side by Side Diff: pkg/analyzer/lib/src/dart/resolver/inheritance_manager.dart

Issue 1903663003: Move InheritanceManager to its own file. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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>, &hellip;, 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698