| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library analyzer.src.generated.resolver; | 5 library analyzer.src.generated.resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| 11 import 'package:analyzer/dart/ast/visitor.dart'; | 11 import 'package:analyzer/dart/ast/visitor.dart'; |
| 12 import 'package:analyzer/dart/element/element.dart'; | 12 import 'package:analyzer/dart/element/element.dart'; |
| 13 import 'package:analyzer/dart/element/type.dart'; | 13 import 'package:analyzer/dart/element/type.dart'; |
| 14 import 'package:analyzer/dart/element/visitor.dart'; | 14 import 'package:analyzer/dart/element/visitor.dart'; |
| 15 import 'package:analyzer/src/dart/ast/ast.dart'; | 15 import 'package:analyzer/src/dart/ast/ast.dart'; |
| 16 import 'package:analyzer/src/dart/ast/token.dart'; | 16 import 'package:analyzer/src/dart/ast/token.dart'; |
| 17 import 'package:analyzer/src/dart/ast/utilities.dart'; | 17 import 'package:analyzer/src/dart/ast/utilities.dart'; |
| 18 import 'package:analyzer/src/dart/element/element.dart'; | 18 import 'package:analyzer/src/dart/element/element.dart'; |
| 19 import 'package:analyzer/src/dart/element/member.dart'; | |
| 20 import 'package:analyzer/src/dart/element/type.dart'; | 19 import 'package:analyzer/src/dart/element/type.dart'; |
| 21 import 'package:analyzer/src/dart/element/utilities.dart'; | 20 import 'package:analyzer/src/dart/element/utilities.dart'; |
| 21 import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'; |
| 22 import 'package:analyzer/src/dart/resolver/scope.dart'; | 22 import 'package:analyzer/src/dart/resolver/scope.dart'; |
| 23 import 'package:analyzer/src/generated/constant.dart'; | 23 import 'package:analyzer/src/generated/constant.dart'; |
| 24 import 'package:analyzer/src/generated/element_resolver.dart'; | 24 import 'package:analyzer/src/generated/element_resolver.dart'; |
| 25 import 'package:analyzer/src/generated/engine.dart'; | 25 import 'package:analyzer/src/generated/engine.dart'; |
| 26 import 'package:analyzer/src/generated/error.dart'; | 26 import 'package:analyzer/src/generated/error.dart'; |
| 27 import 'package:analyzer/src/generated/error_verifier.dart'; | 27 import 'package:analyzer/src/generated/error_verifier.dart'; |
| 28 import 'package:analyzer/src/generated/java_core.dart'; | 28 import 'package:analyzer/src/generated/java_core.dart'; |
| 29 import 'package:analyzer/src/generated/java_engine.dart'; | 29 import 'package:analyzer/src/generated/java_engine.dart'; |
| 30 import 'package:analyzer/src/generated/source.dart'; | 30 import 'package:analyzer/src/generated/source.dart'; |
| 31 import 'package:analyzer/src/generated/static_type_analyzer.dart'; | 31 import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
| 32 import 'package:analyzer/src/generated/type_system.dart'; | 32 import 'package:analyzer/src/generated/type_system.dart'; |
| 33 import 'package:analyzer/src/generated/utilities_dart.dart'; | 33 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 34 import 'package:analyzer/src/task/strong/info.dart' | 34 import 'package:analyzer/src/task/strong/info.dart' |
| 35 show InferredType, StaticInfo; | 35 show InferredType, StaticInfo; |
| 36 | 36 |
| 37 export 'package:analyzer/src/dart/resolver/inheritance_manager.dart'; |
| 37 export 'package:analyzer/src/dart/resolver/scope.dart'; | 38 export 'package:analyzer/src/dart/resolver/scope.dart'; |
| 38 export 'package:analyzer/src/generated/type_system.dart'; | 39 export 'package:analyzer/src/generated/type_system.dart'; |
| 39 | 40 |
| 40 /** | 41 /** |
| 41 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for | 42 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for |
| 42 * violations of Dart best practices. | 43 * violations of Dart best practices. |
| 43 */ | 44 */ |
| 44 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { | 45 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| 45 // static String _HASHCODE_GETTER_NAME = "hashCode"; | 46 // static String _HASHCODE_GETTER_NAME = "hashCode"; |
| 46 | 47 |
| (...skipping 4782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4829 /** | 4830 /** |
| 4830 * Attach contextual type information [type] to [node] for use during | 4831 * Attach contextual type information [type] to [node] for use during |
| 4831 * inference. | 4832 * inference. |
| 4832 */ | 4833 */ |
| 4833 static void setTypeFromNode(AstNode innerNode, AstNode outerNode) { | 4834 static void setTypeFromNode(AstNode innerNode, AstNode outerNode) { |
| 4834 setType(innerNode, getType(outerNode)); | 4835 setType(innerNode, getType(outerNode)); |
| 4835 } | 4836 } |
| 4836 } | 4837 } |
| 4837 | 4838 |
| 4838 /** | 4839 /** |
| 4839 * Instances of the class `InheritanceManager` manage the knowledge of where cla
ss members | |
| 4840 * (methods, getters & setters) are inherited from. | |
| 4841 */ | |
| 4842 class InheritanceManager { | |
| 4843 /** | |
| 4844 * The [LibraryElement] that is managed by this manager. | |
| 4845 */ | |
| 4846 LibraryElement _library; | |
| 4847 | |
| 4848 /** | |
| 4849 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
| 4850 * names and the associated [ExecutableElement] in the mixin and superclass ch
ain. | |
| 4851 */ | |
| 4852 HashMap<ClassElement, MemberMap> _classLookup; | |
| 4853 | |
| 4854 /** | |
| 4855 * This is a mapping between each [ClassElement] and a map between the [String
] member | |
| 4856 * names and the associated [ExecutableElement] in the interface set. | |
| 4857 */ | |
| 4858 HashMap<ClassElement, MemberMap> _interfaceLookup; | |
| 4859 | |
| 4860 /** | |
| 4861 * A map between each visited [ClassElement] and the set of [AnalysisError]s f
ound on | |
| 4862 * the class element. | |
| 4863 */ | |
| 4864 HashMap<ClassElement, HashSet<AnalysisError>> _errorsInClassElement = | |
| 4865 new HashMap<ClassElement, HashSet<AnalysisError>>(); | |
| 4866 | |
| 4867 /** | |
| 4868 * Initialize a newly created inheritance manager. | |
| 4869 * | |
| 4870 * @param library the library element context that the inheritance mappings ar
e being generated | |
| 4871 */ | |
| 4872 InheritanceManager(LibraryElement library) { | |
| 4873 this._library = library; | |
| 4874 _classLookup = new HashMap<ClassElement, MemberMap>(); | |
| 4875 _interfaceLookup = new HashMap<ClassElement, MemberMap>(); | |
| 4876 } | |
| 4877 | |
| 4878 /** | |
| 4879 * Set the new library element context. | |
| 4880 * | |
| 4881 * @param library the new library element | |
| 4882 */ | |
| 4883 void set libraryElement(LibraryElement library) { | |
| 4884 this._library = library; | |
| 4885 } | |
| 4886 | |
| 4887 /** | |
| 4888 * Return the set of [AnalysisError]s found on the passed [ClassElement], or | |
| 4889 * `null` if there are none. | |
| 4890 * | |
| 4891 * @param classElt the class element to query | |
| 4892 * @return the set of [AnalysisError]s found on the passed [ClassElement], or | |
| 4893 * `null` if there are none | |
| 4894 */ | |
| 4895 HashSet<AnalysisError> getErrors(ClassElement classElt) => | |
| 4896 _errorsInClassElement[classElt]; | |
| 4897 | |
| 4898 /** | |
| 4899 * Get and return a mapping between the set of all string names of the members
inherited from the | |
| 4900 * passed [ClassElement] superclass hierarchy, and the associated [ExecutableE
lement]. | |
| 4901 * | |
| 4902 * @param classElt the class element to query | |
| 4903 * @return a mapping between the set of all members inherited from the passed
[ClassElement] | |
| 4904 * superclass hierarchy, and the associated [ExecutableElement] | |
| 4905 */ | |
| 4906 MemberMap getMapOfMembersInheritedFromClasses(ClassElement classElt) => | |
| 4907 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()); | |
| 4908 | |
| 4909 /** | |
| 4910 * Get and return a mapping between the set of all string names of the members
inherited from the | |
| 4911 * passed [ClassElement] interface hierarchy, and the associated [ExecutableEl
ement]. | |
| 4912 * | |
| 4913 * @param classElt the class element to query | |
| 4914 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 4915 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement]. | |
| 4916 */ | |
| 4917 MemberMap getMapOfMembersInheritedFromInterfaces(ClassElement classElt) => | |
| 4918 _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()); | |
| 4919 | |
| 4920 /** | |
| 4921 * Given some [ClassElement] and some member name, this returns the | |
| 4922 * [ExecutableElement] that the class inherits from the mixins, | |
| 4923 * superclasses or interfaces, that has the member name, if no member is inher
ited `null` is | |
| 4924 * returned. | |
| 4925 * | |
| 4926 * @param classElt the class element to query | |
| 4927 * @param memberName the name of the executable element to find and return | |
| 4928 * @return the inherited executable element with the member name, or `null` if
no such | |
| 4929 * member exists | |
| 4930 */ | |
| 4931 ExecutableElement lookupInheritance( | |
| 4932 ClassElement classElt, String memberName) { | |
| 4933 if (memberName == null || memberName.isEmpty) { | |
| 4934 return null; | |
| 4935 } | |
| 4936 ExecutableElement executable = | |
| 4937 _computeClassChainLookupMap(classElt, new HashSet<ClassElement>()) | |
| 4938 .get(memberName); | |
| 4939 if (executable == null) { | |
| 4940 return _computeInterfaceLookupMap(classElt, new HashSet<ClassElement>()) | |
| 4941 .get(memberName); | |
| 4942 } | |
| 4943 return executable; | |
| 4944 } | |
| 4945 | |
| 4946 /** | |
| 4947 * Given some [ClassElement] and some member name, this returns the | |
| 4948 * [ExecutableElement] that the class either declares itself, or | |
| 4949 * inherits, that has the member name, if no member is inherited `null` is ret
urned. | |
| 4950 * | |
| 4951 * @param classElt the class element to query | |
| 4952 * @param memberName the name of the executable element to find and return | |
| 4953 * @return the inherited executable element with the member name, or `null` if
no such | |
| 4954 * member exists | |
| 4955 */ | |
| 4956 ExecutableElement lookupMember(ClassElement classElt, String memberName) { | |
| 4957 ExecutableElement element = _lookupMemberInClass(classElt, memberName); | |
| 4958 if (element != null) { | |
| 4959 return element; | |
| 4960 } | |
| 4961 return lookupInheritance(classElt, memberName); | |
| 4962 } | |
| 4963 | |
| 4964 /** | |
| 4965 * Determine the set of methods which is overridden by the given class member.
If no member is | |
| 4966 * inherited, an empty list is returned. If one of the inherited members is a | |
| 4967 * [MultiplyInheritedExecutableElement], then it is expanded into its constitu
ent inherited | |
| 4968 * elements. | |
| 4969 * | |
| 4970 * @param classElt the class to query | |
| 4971 * @param memberName the name of the class member to query | |
| 4972 * @return a list of overridden methods | |
| 4973 */ | |
| 4974 List<ExecutableElement> lookupOverrides( | |
| 4975 ClassElement classElt, String memberName) { | |
| 4976 List<ExecutableElement> result = new List<ExecutableElement>(); | |
| 4977 if (memberName == null || memberName.isEmpty) { | |
| 4978 return result; | |
| 4979 } | |
| 4980 List<MemberMap> interfaceMaps = | |
| 4981 _gatherInterfaceLookupMaps(classElt, new HashSet<ClassElement>()); | |
| 4982 if (interfaceMaps != null) { | |
| 4983 for (MemberMap interfaceMap in interfaceMaps) { | |
| 4984 ExecutableElement overriddenElement = interfaceMap.get(memberName); | |
| 4985 if (overriddenElement != null) { | |
| 4986 if (overriddenElement is MultiplyInheritedExecutableElement) { | |
| 4987 MultiplyInheritedExecutableElement multiplyInheritedElement = | |
| 4988 overriddenElement; | |
| 4989 for (ExecutableElement element | |
| 4990 in multiplyInheritedElement.inheritedElements) { | |
| 4991 result.add(element); | |
| 4992 } | |
| 4993 } else { | |
| 4994 result.add(overriddenElement); | |
| 4995 } | |
| 4996 } | |
| 4997 } | |
| 4998 } | |
| 4999 return result; | |
| 5000 } | |
| 5001 | |
| 5002 /** | |
| 5003 * This method takes some inherited [FunctionType], and resolves all the param
eterized types | |
| 5004 * in the function type, dependent on the class in which it is being overridde
n. | |
| 5005 * | |
| 5006 * @param baseFunctionType the function type that is being overridden | |
| 5007 * @param memberName the name of the member, this is used to lookup the inheri
tance path of the | |
| 5008 * override | |
| 5009 * @param definingType the type that is overriding the member | |
| 5010 * @return the passed function type with any parameterized types substituted | |
| 5011 */ | |
| 5012 // TODO(jmesserly): investigate why this is needed in ErrorVerifier's override | |
| 5013 // checking. There seems to be some rare cases where we get partially | |
| 5014 // substituted type arguments, and the function types don't compare equally. | |
| 5015 FunctionType substituteTypeArgumentsInMemberFromInheritance( | |
| 5016 FunctionType baseFunctionType, | |
| 5017 String memberName, | |
| 5018 InterfaceType definingType) { | |
| 5019 // if the baseFunctionType is null, or does not have any parameters, | |
| 5020 // return it. | |
| 5021 if (baseFunctionType == null || | |
| 5022 baseFunctionType.typeArguments.length == 0) { | |
| 5023 return baseFunctionType; | |
| 5024 } | |
| 5025 // First, generate the path from the defining type to the overridden member | |
| 5026 Queue<InterfaceType> inheritancePath = new Queue<InterfaceType>(); | |
| 5027 _computeInheritancePath(inheritancePath, definingType, memberName); | |
| 5028 if (inheritancePath == null || inheritancePath.isEmpty) { | |
| 5029 // TODO(jwren) log analysis engine error | |
| 5030 return baseFunctionType; | |
| 5031 } | |
| 5032 FunctionType functionTypeToReturn = baseFunctionType; | |
| 5033 // loop backward through the list substituting as we go: | |
| 5034 while (!inheritancePath.isEmpty) { | |
| 5035 InterfaceType lastType = inheritancePath.removeLast(); | |
| 5036 List<DartType> parameterTypes = lastType.element.type.typeArguments; | |
| 5037 List<DartType> argumentTypes = lastType.typeArguments; | |
| 5038 functionTypeToReturn = | |
| 5039 functionTypeToReturn.substitute2(argumentTypes, parameterTypes); | |
| 5040 } | |
| 5041 return functionTypeToReturn; | |
| 5042 } | |
| 5043 | |
| 5044 /** | |
| 5045 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
| 5046 * the passed [ClassElement] superclass hierarchy, and the associated | |
| 5047 * [ExecutableElement]. | |
| 5048 * | |
| 5049 * @param classElt the class element to query | |
| 5050 * @param visitedClasses a set of visited classes passed back into this method
when it calls | |
| 5051 * itself recursively | |
| 5052 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 5053 * [ClassElement] superclass hierarchy, and the associated [Executable
Element] | |
| 5054 */ | |
| 5055 MemberMap _computeClassChainLookupMap( | |
| 5056 ClassElement classElt, HashSet<ClassElement> visitedClasses) { | |
| 5057 MemberMap resultMap = _classLookup[classElt]; | |
| 5058 if (resultMap != null) { | |
| 5059 return resultMap; | |
| 5060 } else { | |
| 5061 resultMap = new MemberMap(); | |
| 5062 } | |
| 5063 ClassElement superclassElt = null; | |
| 5064 InterfaceType supertype = classElt.supertype; | |
| 5065 if (supertype != null) { | |
| 5066 superclassElt = supertype.element; | |
| 5067 } else { | |
| 5068 // classElt is Object | |
| 5069 _classLookup[classElt] = resultMap; | |
| 5070 return resultMap; | |
| 5071 } | |
| 5072 if (superclassElt != null) { | |
| 5073 if (!visitedClasses.contains(superclassElt)) { | |
| 5074 visitedClasses.add(superclassElt); | |
| 5075 try { | |
| 5076 resultMap = new MemberMap.from( | |
| 5077 _computeClassChainLookupMap(superclassElt, visitedClasses)); | |
| 5078 // | |
| 5079 // Substitute the super types down the hierarchy. | |
| 5080 // | |
| 5081 _substituteTypeParametersDownHierarchy(supertype, resultMap); | |
| 5082 // | |
| 5083 // Include the members from the superclass in the resultMap. | |
| 5084 // | |
| 5085 _recordMapWithClassMembers(resultMap, supertype, false); | |
| 5086 } finally { | |
| 5087 visitedClasses.remove(superclassElt); | |
| 5088 } | |
| 5089 } else { | |
| 5090 // This case happens only when the superclass was previously visited and | |
| 5091 // not in the lookup, meaning this is meant to shorten the compute for | |
| 5092 // recursive cases. | |
| 5093 _classLookup[superclassElt] = resultMap; | |
| 5094 return resultMap; | |
| 5095 } | |
| 5096 } | |
| 5097 // | |
| 5098 // Include the members from the mixins in the resultMap. If there are | |
| 5099 // multiple mixins, visit them in the order listed so that methods in later | |
| 5100 // mixins will overwrite identically-named methods in earlier mixins. | |
| 5101 // | |
| 5102 List<InterfaceType> mixins = classElt.mixins; | |
| 5103 for (InterfaceType mixin in mixins) { | |
| 5104 ClassElement mixinElement = mixin.element; | |
| 5105 if (mixinElement != null) { | |
| 5106 if (!visitedClasses.contains(mixinElement)) { | |
| 5107 visitedClasses.add(mixinElement); | |
| 5108 try { | |
| 5109 MemberMap map = new MemberMap.from( | |
| 5110 _computeClassChainLookupMap(mixinElement, visitedClasses)); | |
| 5111 // | |
| 5112 // Substitute the super types down the hierarchy. | |
| 5113 // | |
| 5114 _substituteTypeParametersDownHierarchy(mixin, map); | |
| 5115 // | |
| 5116 // Include the members from the superclass in the resultMap. | |
| 5117 // | |
| 5118 _recordMapWithClassMembers(map, mixin, false); | |
| 5119 // | |
| 5120 // Add the members from map into result map. | |
| 5121 // | |
| 5122 for (int j = 0; j < map.size; j++) { | |
| 5123 String key = map.getKey(j); | |
| 5124 ExecutableElement value = map.getValue(j); | |
| 5125 if (key != null) { | |
| 5126 ClassElement definingClass = value | |
| 5127 .getAncestor((Element element) => element is ClassElement); | |
| 5128 if (!definingClass.type.isObject) { | |
| 5129 ExecutableElement existingValue = resultMap.get(key); | |
| 5130 if (existingValue == null || | |
| 5131 (existingValue != null && !_isAbstract(value))) { | |
| 5132 resultMap.put(key, value); | |
| 5133 } | |
| 5134 } | |
| 5135 } | |
| 5136 } | |
| 5137 } finally { | |
| 5138 visitedClasses.remove(mixinElement); | |
| 5139 } | |
| 5140 } else { | |
| 5141 // This case happens only when the superclass was previously visited | |
| 5142 // and not in the lookup, meaning this is meant to shorten the compute | |
| 5143 // for recursive cases. | |
| 5144 _classLookup[mixinElement] = resultMap; | |
| 5145 return resultMap; | |
| 5146 } | |
| 5147 } | |
| 5148 } | |
| 5149 _classLookup[classElt] = resultMap; | |
| 5150 return resultMap; | |
| 5151 } | |
| 5152 | |
| 5153 /** | |
| 5154 * Compute and return the inheritance path given the context of a type and a m
ember that is | |
| 5155 * overridden in the inheritance path (for which the type is in the path). | |
| 5156 * | |
| 5157 * @param chain the inheritance path that is built up as this method calls its
elf recursively, | |
| 5158 * when this method is called an empty [LinkedList] should be provide
d | |
| 5159 * @param currentType the current type in the inheritance path | |
| 5160 * @param memberName the name of the member that is being looked up the inheri
tance path | |
| 5161 */ | |
| 5162 void _computeInheritancePath(Queue<InterfaceType> chain, | |
| 5163 InterfaceType currentType, String memberName) { | |
| 5164 // TODO (jwren) create a public version of this method which doesn't require | |
| 5165 // the initial chain to be provided, then provided tests for this | |
| 5166 // functionality in InheritanceManagerTest | |
| 5167 chain.add(currentType); | |
| 5168 ClassElement classElt = currentType.element; | |
| 5169 InterfaceType supertype = classElt.supertype; | |
| 5170 // Base case- reached Object | |
| 5171 if (supertype == null) { | |
| 5172 // Looked up the chain all the way to Object, return null. | |
| 5173 // This should never happen. | |
| 5174 return; | |
| 5175 } | |
| 5176 // If we are done, return the chain | |
| 5177 // We are not done if this is the first recursive call on this method. | |
| 5178 if (chain.length != 1) { | |
| 5179 // We are done however if the member is in this classElt | |
| 5180 if (_lookupMemberInClass(classElt, memberName) != null) { | |
| 5181 return; | |
| 5182 } | |
| 5183 } | |
| 5184 // Mixins- note that mixins call lookupMemberInClass, not lookupMember | |
| 5185 List<InterfaceType> mixins = classElt.mixins; | |
| 5186 for (int i = mixins.length - 1; i >= 0; i--) { | |
| 5187 ClassElement mixinElement = mixins[i].element; | |
| 5188 if (mixinElement != null) { | |
| 5189 ExecutableElement elt = _lookupMemberInClass(mixinElement, memberName); | |
| 5190 if (elt != null) { | |
| 5191 // this is equivalent (but faster than) calling this method | |
| 5192 // recursively | |
| 5193 // (return computeInheritancePath(chain, mixins[i], memberName);) | |
| 5194 chain.add(mixins[i]); | |
| 5195 return; | |
| 5196 } | |
| 5197 } | |
| 5198 } | |
| 5199 // Superclass | |
| 5200 ClassElement superclassElt = supertype.element; | |
| 5201 if (lookupMember(superclassElt, memberName) != null) { | |
| 5202 _computeInheritancePath(chain, supertype, memberName); | |
| 5203 return; | |
| 5204 } | |
| 5205 // Interfaces | |
| 5206 List<InterfaceType> interfaces = classElt.interfaces; | |
| 5207 for (InterfaceType interfaceType in interfaces) { | |
| 5208 ClassElement interfaceElement = interfaceType.element; | |
| 5209 if (interfaceElement != null && | |
| 5210 lookupMember(interfaceElement, memberName) != null) { | |
| 5211 _computeInheritancePath(chain, interfaceType, memberName); | |
| 5212 return; | |
| 5213 } | |
| 5214 } | |
| 5215 } | |
| 5216 | |
| 5217 /** | |
| 5218 * Compute and return a mapping between the set of all string names of the mem
bers inherited from | |
| 5219 * the passed [ClassElement] interface hierarchy, and the associated | |
| 5220 * [ExecutableElement]. | |
| 5221 * | |
| 5222 * @param classElt the class element to query | |
| 5223 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
| 5224 * itself recursively | |
| 5225 * @return a mapping between the set of all string names of the members inheri
ted from the passed | |
| 5226 * [ClassElement] interface hierarchy, and the associated [ExecutableE
lement] | |
| 5227 */ | |
| 5228 MemberMap _computeInterfaceLookupMap( | |
| 5229 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
| 5230 MemberMap resultMap = _interfaceLookup[classElt]; | |
| 5231 if (resultMap != null) { | |
| 5232 return resultMap; | |
| 5233 } | |
| 5234 List<MemberMap> lookupMaps = | |
| 5235 _gatherInterfaceLookupMaps(classElt, visitedInterfaces); | |
| 5236 if (lookupMaps == null) { | |
| 5237 resultMap = new MemberMap(); | |
| 5238 } else { | |
| 5239 HashMap<String, List<ExecutableElement>> unionMap = | |
| 5240 _unionInterfaceLookupMaps(lookupMaps); | |
| 5241 resultMap = _resolveInheritanceLookup(classElt, unionMap); | |
| 5242 } | |
| 5243 _interfaceLookup[classElt] = resultMap; | |
| 5244 return resultMap; | |
| 5245 } | |
| 5246 | |
| 5247 /** | |
| 5248 * Collect a list of interface lookup maps whose elements correspond to all of
the classes | |
| 5249 * directly above [classElt] in the class hierarchy (the direct superclass if
any, all | |
| 5250 * mixins, and all direct superinterfaces). Each item in the list is the inter
face lookup map | |
| 5251 * returned by [computeInterfaceLookupMap] for the corresponding super, except
with type | |
| 5252 * parameters appropriately substituted. | |
| 5253 * | |
| 5254 * @param classElt the class element to query | |
| 5255 * @param visitedInterfaces a set of visited classes passed back into this met
hod when it calls | |
| 5256 * itself recursively | |
| 5257 * @return `null` if there was a problem (such as a loop in the class hierarch
y) or if there | |
| 5258 * are no classes above this one in the class hierarchy. Otherwise, a
list of interface | |
| 5259 * lookup maps. | |
| 5260 */ | |
| 5261 List<MemberMap> _gatherInterfaceLookupMaps( | |
| 5262 ClassElement classElt, HashSet<ClassElement> visitedInterfaces) { | |
| 5263 InterfaceType supertype = classElt.supertype; | |
| 5264 ClassElement superclassElement = | |
| 5265 supertype != null ? supertype.element : null; | |
| 5266 List<InterfaceType> mixins = classElt.mixins; | |
| 5267 List<InterfaceType> interfaces = classElt.interfaces; | |
| 5268 // Recursively collect the list of mappings from all of the interface types | |
| 5269 List<MemberMap> lookupMaps = new List<MemberMap>(); | |
| 5270 // | |
| 5271 // Superclass element | |
| 5272 // | |
| 5273 if (superclassElement != null) { | |
| 5274 if (!visitedInterfaces.contains(superclassElement)) { | |
| 5275 try { | |
| 5276 visitedInterfaces.add(superclassElement); | |
| 5277 // | |
| 5278 // Recursively compute the map for the super type. | |
| 5279 // | |
| 5280 MemberMap map = | |
| 5281 _computeInterfaceLookupMap(superclassElement, visitedInterfaces); | |
| 5282 map = new MemberMap.from(map); | |
| 5283 // | |
| 5284 // Substitute the super type down the hierarchy. | |
| 5285 // | |
| 5286 _substituteTypeParametersDownHierarchy(supertype, map); | |
| 5287 // | |
| 5288 // Add any members from the super type into the map as well. | |
| 5289 // | |
| 5290 _recordMapWithClassMembers(map, supertype, true); | |
| 5291 lookupMaps.add(map); | |
| 5292 } finally { | |
| 5293 visitedInterfaces.remove(superclassElement); | |
| 5294 } | |
| 5295 } else { | |
| 5296 return null; | |
| 5297 } | |
| 5298 } | |
| 5299 // | |
| 5300 // Mixin elements | |
| 5301 // | |
| 5302 for (int i = mixins.length - 1; i >= 0; i--) { | |
| 5303 InterfaceType mixinType = mixins[i]; | |
| 5304 ClassElement mixinElement = mixinType.element; | |
| 5305 if (mixinElement != null) { | |
| 5306 if (!visitedInterfaces.contains(mixinElement)) { | |
| 5307 try { | |
| 5308 visitedInterfaces.add(mixinElement); | |
| 5309 // | |
| 5310 // Recursively compute the map for the mixin. | |
| 5311 // | |
| 5312 MemberMap map = | |
| 5313 _computeInterfaceLookupMap(mixinElement, visitedInterfaces); | |
| 5314 map = new MemberMap.from(map); | |
| 5315 // | |
| 5316 // Substitute the mixin type down the hierarchy. | |
| 5317 // | |
| 5318 _substituteTypeParametersDownHierarchy(mixinType, map); | |
| 5319 // | |
| 5320 // Add any members from the mixin type into the map as well. | |
| 5321 // | |
| 5322 _recordMapWithClassMembers(map, mixinType, true); | |
| 5323 lookupMaps.add(map); | |
| 5324 } finally { | |
| 5325 visitedInterfaces.remove(mixinElement); | |
| 5326 } | |
| 5327 } else { | |
| 5328 return null; | |
| 5329 } | |
| 5330 } | |
| 5331 } | |
| 5332 // | |
| 5333 // Interface elements | |
| 5334 // | |
| 5335 for (InterfaceType interfaceType in interfaces) { | |
| 5336 ClassElement interfaceElement = interfaceType.element; | |
| 5337 if (interfaceElement != null) { | |
| 5338 if (!visitedInterfaces.contains(interfaceElement)) { | |
| 5339 try { | |
| 5340 visitedInterfaces.add(interfaceElement); | |
| 5341 // | |
| 5342 // Recursively compute the map for the interfaces. | |
| 5343 // | |
| 5344 MemberMap map = | |
| 5345 _computeInterfaceLookupMap(interfaceElement, visitedInterfaces); | |
| 5346 map = new MemberMap.from(map); | |
| 5347 // | |
| 5348 // Substitute the supertypes down the hierarchy | |
| 5349 // | |
| 5350 _substituteTypeParametersDownHierarchy(interfaceType, map); | |
| 5351 // | |
| 5352 // And add any members from the interface into the map as well. | |
| 5353 // | |
| 5354 _recordMapWithClassMembers(map, interfaceType, true); | |
| 5355 lookupMaps.add(map); | |
| 5356 } finally { | |
| 5357 visitedInterfaces.remove(interfaceElement); | |
| 5358 } | |
| 5359 } else { | |
| 5360 return null; | |
| 5361 } | |
| 5362 } | |
| 5363 } | |
| 5364 if (lookupMaps.length == 0) { | |
| 5365 return null; | |
| 5366 } | |
| 5367 return lookupMaps; | |
| 5368 } | |
| 5369 | |
| 5370 /** | |
| 5371 * Given some [ClassElement], this method finds and returns the [ExecutableEle
ment] of | |
| 5372 * the passed name in the class element. Static members, members in super type
s and members not | |
| 5373 * accessible from the current library are not considered. | |
| 5374 * | |
| 5375 * @param classElt the class element to query | |
| 5376 * @param memberName the name of the member to lookup in the class | |
| 5377 * @return the found [ExecutableElement], or `null` if no such member was foun
d | |
| 5378 */ | |
| 5379 ExecutableElement _lookupMemberInClass( | |
| 5380 ClassElement classElt, String memberName) { | |
| 5381 List<MethodElement> methods = classElt.methods; | |
| 5382 for (MethodElement method in methods) { | |
| 5383 if (memberName == method.name && | |
| 5384 method.isAccessibleIn(_library) && | |
| 5385 !method.isStatic) { | |
| 5386 return method; | |
| 5387 } | |
| 5388 } | |
| 5389 List<PropertyAccessorElement> accessors = classElt.accessors; | |
| 5390 for (PropertyAccessorElement accessor in accessors) { | |
| 5391 if (memberName == accessor.name && | |
| 5392 accessor.isAccessibleIn(_library) && | |
| 5393 !accessor.isStatic) { | |
| 5394 return accessor; | |
| 5395 } | |
| 5396 } | |
| 5397 return null; | |
| 5398 } | |
| 5399 | |
| 5400 /** | |
| 5401 * Record the passed map with the set of all members (methods, getters and set
ters) in the type | |
| 5402 * into the passed map. | |
| 5403 * | |
| 5404 * @param map some non-`null` map to put the methods and accessors from the pa
ssed | |
| 5405 * [ClassElement] into | |
| 5406 * @param type the type that will be recorded into the passed map | |
| 5407 * @param doIncludeAbstract `true` if abstract members will be put into the ma
p | |
| 5408 */ | |
| 5409 void _recordMapWithClassMembers( | |
| 5410 MemberMap map, InterfaceType type, bool doIncludeAbstract) { | |
| 5411 List<MethodElement> methods = type.methods; | |
| 5412 for (MethodElement method in methods) { | |
| 5413 if (method.isAccessibleIn(_library) && | |
| 5414 !method.isStatic && | |
| 5415 (doIncludeAbstract || !method.isAbstract)) { | |
| 5416 map.put(method.name, method); | |
| 5417 } | |
| 5418 } | |
| 5419 List<PropertyAccessorElement> accessors = type.accessors; | |
| 5420 for (PropertyAccessorElement accessor in accessors) { | |
| 5421 if (accessor.isAccessibleIn(_library) && | |
| 5422 !accessor.isStatic && | |
| 5423 (doIncludeAbstract || !accessor.isAbstract)) { | |
| 5424 map.put(accessor.name, accessor); | |
| 5425 } | |
| 5426 } | |
| 5427 } | |
| 5428 | |
| 5429 /** | |
| 5430 * This method is used to report errors on when they are found computing inher
itance information. | |
| 5431 * See [ErrorVerifier.checkForInconsistentMethodInheritance] to see where thes
e generated | |
| 5432 * error codes are reported back into the analysis engine. | |
| 5433 * | |
| 5434 * @param classElt the location of the source for which the exception occurred | |
| 5435 * @param offset the offset of the location of the error | |
| 5436 * @param length the length of the location of the error | |
| 5437 * @param errorCode the error code to be associated with this error | |
| 5438 * @param arguments the arguments used to build the error message | |
| 5439 */ | |
| 5440 void _reportError(ClassElement classElt, int offset, int length, | |
| 5441 ErrorCode errorCode, List<Object> arguments) { | |
| 5442 HashSet<AnalysisError> errorSet = _errorsInClassElement[classElt]; | |
| 5443 if (errorSet == null) { | |
| 5444 errorSet = new HashSet<AnalysisError>(); | |
| 5445 _errorsInClassElement[classElt] = errorSet; | |
| 5446 } | |
| 5447 errorSet.add(new AnalysisError( | |
| 5448 classElt.source, offset, length, errorCode, arguments)); | |
| 5449 } | |
| 5450 | |
| 5451 /** | |
| 5452 * Given the set of methods defined by classes above [classElt] in the class h
ierarchy, | |
| 5453 * apply the appropriate inheritance rules to determine those methods inherite
d by or overridden | |
| 5454 * by [classElt]. Also report static warnings | |
| 5455 * [StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE] and | |
| 5456 * [StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD] if ap
propriate. | |
| 5457 * | |
| 5458 * @param classElt the class element to query. | |
| 5459 * @param unionMap a mapping from method name to the set of unique (in terms o
f signature) methods | |
| 5460 * defined in superclasses of [classElt]. | |
| 5461 * @return the inheritance lookup map for [classElt]. | |
| 5462 */ | |
| 5463 MemberMap _resolveInheritanceLookup(ClassElement classElt, | |
| 5464 HashMap<String, List<ExecutableElement>> unionMap) { | |
| 5465 MemberMap resultMap = new MemberMap(); | |
| 5466 unionMap.forEach((String key, List<ExecutableElement> list) { | |
| 5467 int numOfEltsWithMatchingNames = list.length; | |
| 5468 if (numOfEltsWithMatchingNames == 1) { | |
| 5469 // | |
| 5470 // Example: class A inherits only 1 method named 'm'. | |
| 5471 // Since it is the only such method, it is inherited. | |
| 5472 // Another example: class A inherits 2 methods named 'm' from 2 | |
| 5473 // different interfaces, but they both have the same signature, so it is | |
| 5474 // the method inherited. | |
| 5475 // | |
| 5476 resultMap.put(key, list[0]); | |
| 5477 } else { | |
| 5478 // | |
| 5479 // Then numOfEltsWithMatchingNames > 1, check for the warning cases. | |
| 5480 // | |
| 5481 bool allMethods = true; | |
| 5482 bool allSetters = true; | |
| 5483 bool allGetters = true; | |
| 5484 for (ExecutableElement executableElement in list) { | |
| 5485 if (executableElement is PropertyAccessorElement) { | |
| 5486 allMethods = false; | |
| 5487 if (executableElement.isSetter) { | |
| 5488 allGetters = false; | |
| 5489 } else { | |
| 5490 allSetters = false; | |
| 5491 } | |
| 5492 } else { | |
| 5493 allGetters = false; | |
| 5494 allSetters = false; | |
| 5495 } | |
| 5496 } | |
| 5497 // | |
| 5498 // If there isn't a mixture of methods with getters, then continue, | |
| 5499 // otherwise create a warning. | |
| 5500 // | |
| 5501 if (allMethods || allGetters || allSetters) { | |
| 5502 // | |
| 5503 // Compute the element whose type is the subtype of all of the other | |
| 5504 // types. | |
| 5505 // | |
| 5506 List<ExecutableElement> elements = new List.from(list); | |
| 5507 List<FunctionType> executableElementTypes = | |
| 5508 new List<FunctionType>(numOfEltsWithMatchingNames); | |
| 5509 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
| 5510 executableElementTypes[i] = elements[i].type; | |
| 5511 } | |
| 5512 List<int> subtypesOfAllOtherTypesIndexes = new List<int>(); | |
| 5513 for (int i = 0; i < numOfEltsWithMatchingNames; i++) { | |
| 5514 FunctionType subtype = executableElementTypes[i]; | |
| 5515 if (subtype == null) { | |
| 5516 continue; | |
| 5517 } | |
| 5518 bool subtypeOfAllTypes = true; | |
| 5519 TypeSystem typeSystem = _library.context.typeSystem; | |
| 5520 for (int j = 0; | |
| 5521 j < numOfEltsWithMatchingNames && subtypeOfAllTypes; | |
| 5522 j++) { | |
| 5523 if (i != j) { | |
| 5524 if (!typeSystem.isSubtypeOf( | |
| 5525 subtype, executableElementTypes[j])) { | |
| 5526 subtypeOfAllTypes = false; | |
| 5527 break; | |
| 5528 } | |
| 5529 } | |
| 5530 } | |
| 5531 if (subtypeOfAllTypes) { | |
| 5532 subtypesOfAllOtherTypesIndexes.add(i); | |
| 5533 } | |
| 5534 } | |
| 5535 // | |
| 5536 // The following is split into three cases determined by the number of | |
| 5537 // elements in subtypesOfAllOtherTypes | |
| 5538 // | |
| 5539 if (subtypesOfAllOtherTypesIndexes.length == 1) { | |
| 5540 // | |
| 5541 // Example: class A inherited only 2 method named 'm'. | |
| 5542 // One has the function type '() -> dynamic' and one has the | |
| 5543 // function type '([int]) -> dynamic'. Since the second method is a | |
| 5544 // subtype of all the others, it is the inherited method. | |
| 5545 // Tests: InheritanceManagerTest. | |
| 5546 // test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_* | |
| 5547 // | |
| 5548 resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes[0]]); | |
| 5549 } else { | |
| 5550 if (subtypesOfAllOtherTypesIndexes.isEmpty) { | |
| 5551 // | |
| 5552 // Determine if the current class has a method or accessor with | |
| 5553 // the member name, if it does then then this class does not | |
| 5554 // "inherit" from any of the supertypes. See issue 16134. | |
| 5555 // | |
| 5556 bool classHasMember = false; | |
| 5557 if (allMethods) { | |
| 5558 classHasMember = classElt.getMethod(key) != null; | |
| 5559 } else { | |
| 5560 List<PropertyAccessorElement> accessors = classElt.accessors; | |
| 5561 for (int i = 0; i < accessors.length; i++) { | |
| 5562 if (accessors[i].name == key) { | |
| 5563 classHasMember = true; | |
| 5564 } | |
| 5565 } | |
| 5566 } | |
| 5567 // | |
| 5568 // Example: class A inherited only 2 method named 'm'. | |
| 5569 // One has the function type '() -> int' and one has the function | |
| 5570 // type '() -> String'. Since neither is a subtype of the other, | |
| 5571 // we create a warning, and have this class inherit nothing. | |
| 5572 // | |
| 5573 if (!classHasMember) { | |
| 5574 String firstTwoFuntionTypesStr = | |
| 5575 "${executableElementTypes[0]}, ${executableElementTypes[1]}"
; | |
| 5576 _reportError( | |
| 5577 classElt, | |
| 5578 classElt.nameOffset, | |
| 5579 classElt.nameLength, | |
| 5580 StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE, | |
| 5581 [key, firstTwoFuntionTypesStr]); | |
| 5582 } | |
| 5583 } else { | |
| 5584 // | |
| 5585 // Example: class A inherits 2 methods named 'm'. | |
| 5586 // One has the function type '(int) -> dynamic' and one has the | |
| 5587 // function type '(num) -> dynamic'. Since they are both a subtype | |
| 5588 // of the other, a synthetic function '(dynamic) -> dynamic' is | |
| 5589 // inherited. | |
| 5590 // Tests: test_getMapOfMembersInheritedFromInterfaces_ | |
| 5591 // union_multipleSubtypes_* | |
| 5592 // | |
| 5593 List<ExecutableElement> elementArrayToMerge = | |
| 5594 new List<ExecutableElement>( | |
| 5595 subtypesOfAllOtherTypesIndexes.length); | |
| 5596 for (int i = 0; i < elementArrayToMerge.length; i++) { | |
| 5597 elementArrayToMerge[i] = | |
| 5598 elements[subtypesOfAllOtherTypesIndexes[i]]; | |
| 5599 } | |
| 5600 ExecutableElement mergedExecutableElement = | |
| 5601 _computeMergedExecutableElement(elementArrayToMerge); | |
| 5602 resultMap.put(key, mergedExecutableElement); | |
| 5603 } | |
| 5604 } | |
| 5605 } else { | |
| 5606 _reportError( | |
| 5607 classElt, | |
| 5608 classElt.nameOffset, | |
| 5609 classElt.nameLength, | |
| 5610 StaticWarningCode | |
| 5611 .INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD, | |
| 5612 [key]); | |
| 5613 } | |
| 5614 } | |
| 5615 }); | |
| 5616 return resultMap; | |
| 5617 } | |
| 5618 | |
| 5619 /** | |
| 5620 * Loop through all of the members in some [MemberMap], performing type parame
ter | |
| 5621 * substitutions using a passed supertype. | |
| 5622 * | |
| 5623 * @param superType the supertype to substitute into the members of the [Membe
rMap] | |
| 5624 * @param map the MemberMap to perform the substitutions on | |
| 5625 */ | |
| 5626 void _substituteTypeParametersDownHierarchy( | |
| 5627 InterfaceType superType, MemberMap map) { | |
| 5628 for (int i = 0; i < map.size; i++) { | |
| 5629 ExecutableElement executableElement = map.getValue(i); | |
| 5630 if (executableElement is MethodMember) { | |
| 5631 executableElement = | |
| 5632 MethodMember.from(executableElement as MethodMember, superType); | |
| 5633 map.setValue(i, executableElement); | |
| 5634 } else if (executableElement is PropertyAccessorMember) { | |
| 5635 executableElement = PropertyAccessorMember.from( | |
| 5636 executableElement as PropertyAccessorMember, superType); | |
| 5637 map.setValue(i, executableElement); | |
| 5638 } | |
| 5639 } | |
| 5640 } | |
| 5641 | |
| 5642 /** | |
| 5643 * Union all of the [lookupMaps] together into a single map, grouping the Exec
utableElements | |
| 5644 * into a list where none of the elements are equal where equality is determin
ed by having equal | |
| 5645 * function types. (We also take note too of the kind of the element: ()->int
and () -> int may | |
| 5646 * not be equal if one is a getter and the other is a method.) | |
| 5647 * | |
| 5648 * @param lookupMaps the maps to be unioned together. | |
| 5649 * @return the resulting union map. | |
| 5650 */ | |
| 5651 HashMap<String, List<ExecutableElement>> _unionInterfaceLookupMaps( | |
| 5652 List<MemberMap> lookupMaps) { | |
| 5653 HashMap<String, List<ExecutableElement>> unionMap = | |
| 5654 new HashMap<String, List<ExecutableElement>>(); | |
| 5655 for (MemberMap lookupMap in lookupMaps) { | |
| 5656 int lookupMapSize = lookupMap.size; | |
| 5657 for (int i = 0; i < lookupMapSize; i++) { | |
| 5658 // Get the string key, if null, break. | |
| 5659 String key = lookupMap.getKey(i); | |
| 5660 if (key == null) { | |
| 5661 break; | |
| 5662 } | |
| 5663 // Get the list value out of the unionMap | |
| 5664 List<ExecutableElement> list = unionMap[key]; | |
| 5665 // If we haven't created such a map for this key yet, do create it and | |
| 5666 // put the list entry into the unionMap. | |
| 5667 if (list == null) { | |
| 5668 list = new List<ExecutableElement>(); | |
| 5669 unionMap[key] = list; | |
| 5670 } | |
| 5671 // Fetch the entry out of this lookupMap | |
| 5672 ExecutableElement newExecutableElementEntry = lookupMap.getValue(i); | |
| 5673 if (list.isEmpty) { | |
| 5674 // If the list is empty, just the new value | |
| 5675 list.add(newExecutableElementEntry); | |
| 5676 } else { | |
| 5677 // Otherwise, only add the newExecutableElementEntry if it isn't | |
| 5678 // already in the list, this covers situation where a class inherits | |
| 5679 // two methods (or two getters) that are identical. | |
| 5680 bool alreadyInList = false; | |
| 5681 bool isMethod1 = newExecutableElementEntry is MethodElement; | |
| 5682 for (ExecutableElement executableElementInList in list) { | |
| 5683 bool isMethod2 = executableElementInList is MethodElement; | |
| 5684 if (isMethod1 == isMethod2 && | |
| 5685 executableElementInList.type == | |
| 5686 newExecutableElementEntry.type) { | |
| 5687 alreadyInList = true; | |
| 5688 break; | |
| 5689 } | |
| 5690 } | |
| 5691 if (!alreadyInList) { | |
| 5692 list.add(newExecutableElementEntry); | |
| 5693 } | |
| 5694 } | |
| 5695 } | |
| 5696 } | |
| 5697 return unionMap; | |
| 5698 } | |
| 5699 | |
| 5700 /** | |
| 5701 * Given some array of [ExecutableElement]s, this method creates a synthetic e
lement as | |
| 5702 * described in 8.1.1: | |
| 5703 * | |
| 5704 * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional pa
rameters of a | |
| 5705 * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote t
he number of | |
| 5706 * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denot
e the set of all | |
| 5707 * named parameters of the <i>m<sub>1</sub>, …, m<sub>k</sub></i>. Then
let | |
| 5708 * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i> | |
| 5709 * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <=
i <= k.</i> | |
| 5710 * Then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameter
s of type | |
| 5711 * <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, name
d parameters | |
| 5712 * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>. | |
| 5713 * | |
| 5714 */ | |
| 5715 static ExecutableElement _computeMergedExecutableElement( | |
| 5716 List<ExecutableElement> elementArrayToMerge) { | |
| 5717 int h = _getNumOfPositionalParameters(elementArrayToMerge[0]); | |
| 5718 int r = _getNumOfRequiredParameters(elementArrayToMerge[0]); | |
| 5719 Set<String> namedParametersList = new HashSet<String>(); | |
| 5720 for (int i = 1; i < elementArrayToMerge.length; i++) { | |
| 5721 ExecutableElement element = elementArrayToMerge[i]; | |
| 5722 int numOfPositionalParams = _getNumOfPositionalParameters(element); | |
| 5723 if (h < numOfPositionalParams) { | |
| 5724 h = numOfPositionalParams; | |
| 5725 } | |
| 5726 int numOfRequiredParams = _getNumOfRequiredParameters(element); | |
| 5727 if (r > numOfRequiredParams) { | |
| 5728 r = numOfRequiredParams; | |
| 5729 } | |
| 5730 namedParametersList.addAll(_getNamedParameterNames(element)); | |
| 5731 } | |
| 5732 return _createSyntheticExecutableElement( | |
| 5733 elementArrayToMerge, | |
| 5734 elementArrayToMerge[0].displayName, | |
| 5735 r, | |
| 5736 h - r, | |
| 5737 new List.from(namedParametersList)); | |
| 5738 } | |
| 5739 | |
| 5740 /** | |
| 5741 * Used by [computeMergedExecutableElement] to actually create the | |
| 5742 * synthetic element. | |
| 5743 * | |
| 5744 * @param elementArrayToMerge the array used to create the synthetic element | |
| 5745 * @param name the name of the method, getter or setter | |
| 5746 * @param numOfRequiredParameters the number of required parameters | |
| 5747 * @param numOfPositionalParameters the number of positional parameters | |
| 5748 * @param namedParameters the list of [String]s that are the named parameters | |
| 5749 * @return the created synthetic element | |
| 5750 */ | |
| 5751 static ExecutableElement _createSyntheticExecutableElement( | |
| 5752 List<ExecutableElement> elementArrayToMerge, | |
| 5753 String name, | |
| 5754 int numOfRequiredParameters, | |
| 5755 int numOfPositionalParameters, | |
| 5756 List<String> namedParameters) { | |
| 5757 DynamicTypeImpl dynamicType = DynamicTypeImpl.instance; | |
| 5758 SimpleIdentifier nameIdentifier = | |
| 5759 new SimpleIdentifier(new StringToken(TokenType.IDENTIFIER, name, 0)); | |
| 5760 ExecutableElementImpl executable; | |
| 5761 if (elementArrayToMerge[0] is MethodElement) { | |
| 5762 MultiplyInheritedMethodElementImpl unionedMethod = | |
| 5763 new MultiplyInheritedMethodElementImpl(nameIdentifier); | |
| 5764 unionedMethod.inheritedElements = elementArrayToMerge; | |
| 5765 executable = unionedMethod; | |
| 5766 } else { | |
| 5767 MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = | |
| 5768 new MultiplyInheritedPropertyAccessorElementImpl(nameIdentifier); | |
| 5769 unionedPropertyAccessor.getter = | |
| 5770 (elementArrayToMerge[0] as PropertyAccessorElement).isGetter; | |
| 5771 unionedPropertyAccessor.setter = | |
| 5772 (elementArrayToMerge[0] as PropertyAccessorElement).isSetter; | |
| 5773 unionedPropertyAccessor.inheritedElements = elementArrayToMerge; | |
| 5774 executable = unionedPropertyAccessor; | |
| 5775 } | |
| 5776 int numOfParameters = numOfRequiredParameters + | |
| 5777 numOfPositionalParameters + | |
| 5778 namedParameters.length; | |
| 5779 List<ParameterElement> parameters = | |
| 5780 new List<ParameterElement>(numOfParameters); | |
| 5781 int i = 0; | |
| 5782 for (int j = 0; j < numOfRequiredParameters; j++, i++) { | |
| 5783 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
| 5784 parameter.type = dynamicType; | |
| 5785 parameter.parameterKind = ParameterKind.REQUIRED; | |
| 5786 parameters[i] = parameter; | |
| 5787 } | |
| 5788 for (int k = 0; k < numOfPositionalParameters; k++, i++) { | |
| 5789 ParameterElementImpl parameter = new ParameterElementImpl("", 0); | |
| 5790 parameter.type = dynamicType; | |
| 5791 parameter.parameterKind = ParameterKind.POSITIONAL; | |
| 5792 parameters[i] = parameter; | |
| 5793 } | |
| 5794 for (int m = 0; m < namedParameters.length; m++, i++) { | |
| 5795 ParameterElementImpl parameter = | |
| 5796 new ParameterElementImpl(namedParameters[m], 0); | |
| 5797 parameter.type = dynamicType; | |
| 5798 parameter.parameterKind = ParameterKind.NAMED; | |
| 5799 parameters[i] = parameter; | |
| 5800 } | |
| 5801 executable.returnType = dynamicType; | |
| 5802 executable.parameters = parameters; | |
| 5803 FunctionTypeImpl methodType = new FunctionTypeImpl(executable); | |
| 5804 executable.type = methodType; | |
| 5805 return executable; | |
| 5806 } | |
| 5807 | |
| 5808 /** | |
| 5809 * Given some [ExecutableElement], return the list of named parameters. | |
| 5810 */ | |
| 5811 static List<String> _getNamedParameterNames( | |
| 5812 ExecutableElement executableElement) { | |
| 5813 List<String> namedParameterNames = new List<String>(); | |
| 5814 List<ParameterElement> parameters = executableElement.parameters; | |
| 5815 for (int i = 0; i < parameters.length; i++) { | |
| 5816 ParameterElement parameterElement = parameters[i]; | |
| 5817 if (parameterElement.parameterKind == ParameterKind.NAMED) { | |
| 5818 namedParameterNames.add(parameterElement.name); | |
| 5819 } | |
| 5820 } | |
| 5821 return namedParameterNames; | |
| 5822 } | |
| 5823 | |
| 5824 /** | |
| 5825 * Given some [ExecutableElement] return the number of parameters of the speci
fied kind. | |
| 5826 */ | |
| 5827 static int _getNumOfParameters( | |
| 5828 ExecutableElement executableElement, ParameterKind parameterKind) { | |
| 5829 int parameterCount = 0; | |
| 5830 List<ParameterElement> parameters = executableElement.parameters; | |
| 5831 for (int i = 0; i < parameters.length; i++) { | |
| 5832 ParameterElement parameterElement = parameters[i]; | |
| 5833 if (parameterElement.parameterKind == parameterKind) { | |
| 5834 parameterCount++; | |
| 5835 } | |
| 5836 } | |
| 5837 return parameterCount; | |
| 5838 } | |
| 5839 | |
| 5840 /** | |
| 5841 * Given some [ExecutableElement] return the number of positional parameters. | |
| 5842 * | |
| 5843 * Note: by positional we mean [ParameterKind.REQUIRED] or [ParameterKind.POSI
TIONAL]. | |
| 5844 */ | |
| 5845 static int _getNumOfPositionalParameters( | |
| 5846 ExecutableElement executableElement) => | |
| 5847 _getNumOfParameters(executableElement, ParameterKind.REQUIRED) + | |
| 5848 _getNumOfParameters(executableElement, ParameterKind.POSITIONAL); | |
| 5849 | |
| 5850 /** | |
| 5851 * Given some [ExecutableElement] return the number of required parameters. | |
| 5852 */ | |
| 5853 static int _getNumOfRequiredParameters(ExecutableElement executableElement) => | |
| 5854 _getNumOfParameters(executableElement, ParameterKind.REQUIRED); | |
| 5855 | |
| 5856 /** | |
| 5857 * Given some [ExecutableElement] returns `true` if it is an abstract member o
f a | |
| 5858 * class. | |
| 5859 * | |
| 5860 * @param executableElement some [ExecutableElement] to evaluate | |
| 5861 * @return `true` if the given element is an abstract member of a class | |
| 5862 */ | |
| 5863 static bool _isAbstract(ExecutableElement executableElement) { | |
| 5864 if (executableElement is MethodElement) { | |
| 5865 return executableElement.isAbstract; | |
| 5866 } else if (executableElement is PropertyAccessorElement) { | |
| 5867 return executableElement.isAbstract; | |
| 5868 } | |
| 5869 return false; | |
| 5870 } | |
| 5871 } | |
| 5872 | |
| 5873 /** | |
| 5874 * This enum holds one of four states of a field initialization state through a
constructor | 4840 * This enum holds one of four states of a field initialization state through a
constructor |
| 5875 * signature, not initialized, initialized in the field declaration, initialized
in the field | 4841 * signature, not initialized, initialized in the field declaration, initialized
in the field |
| 5876 * formal, and finally, initialized in the initializers list. | 4842 * formal, and finally, initialized in the initializers list. |
| 5877 */ | 4843 */ |
| 5878 class INIT_STATE extends Enum<INIT_STATE> { | 4844 class INIT_STATE extends Enum<INIT_STATE> { |
| 5879 static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0); | 4845 static const INIT_STATE NOT_INIT = const INIT_STATE('NOT_INIT', 0); |
| 5880 | 4846 |
| 5881 static const INIT_STATE INIT_IN_DECLARATION = | 4847 static const INIT_STATE INIT_IN_DECLARATION = |
| 5882 const INIT_STATE('INIT_IN_DECLARATION', 1); | 4848 const INIT_STATE('INIT_IN_DECLARATION', 1); |
| 5883 | 4849 |
| 5884 static const INIT_STATE INIT_IN_FIELD_FORMAL = | 4850 static const INIT_STATE INIT_IN_FIELD_FORMAL = |
| 5885 const INIT_STATE('INIT_IN_FIELD_FORMAL', 2); | 4851 const INIT_STATE('INIT_IN_FIELD_FORMAL', 2); |
| 5886 | 4852 |
| 5887 static const INIT_STATE INIT_IN_INITIALIZERS = | 4853 static const INIT_STATE INIT_IN_INITIALIZERS = |
| 5888 const INIT_STATE('INIT_IN_INITIALIZERS', 3); | 4854 const INIT_STATE('INIT_IN_INITIALIZERS', 3); |
| 5889 | 4855 |
| 5890 static const List<INIT_STATE> values = const [ | 4856 static const List<INIT_STATE> values = const [ |
| 5891 NOT_INIT, | 4857 NOT_INIT, |
| 5892 INIT_IN_DECLARATION, | 4858 INIT_IN_DECLARATION, |
| 5893 INIT_IN_FIELD_FORMAL, | 4859 INIT_IN_FIELD_FORMAL, |
| 5894 INIT_IN_INITIALIZERS | 4860 INIT_IN_INITIALIZERS |
| 5895 ]; | 4861 ]; |
| 5896 | 4862 |
| 5897 const INIT_STATE(String name, int ordinal) : super(name, ordinal); | 4863 const INIT_STATE(String name, int ordinal) : super(name, ordinal); |
| 5898 } | 4864 } |
| 5899 | 4865 |
| 5900 /** | 4866 /** |
| 5901 * This class is used to replace uses of `HashMap<String, ExecutableElement>` | |
| 5902 * which are not as performant as this class. | |
| 5903 */ | |
| 5904 class MemberMap { | |
| 5905 /** | |
| 5906 * The current size of this map. | |
| 5907 */ | |
| 5908 int _size = 0; | |
| 5909 | |
| 5910 /** | |
| 5911 * The array of keys. | |
| 5912 */ | |
| 5913 List<String> _keys; | |
| 5914 | |
| 5915 /** | |
| 5916 * The array of ExecutableElement values. | |
| 5917 */ | |
| 5918 List<ExecutableElement> _values; | |
| 5919 | |
| 5920 /** | |
| 5921 * Initialize a newly created member map to have the given [initialCapacity]. | |
| 5922 * The map will grow if needed. | |
| 5923 */ | |
| 5924 MemberMap([int initialCapacity = 10]) { | |
| 5925 _initArrays(initialCapacity); | |
| 5926 } | |
| 5927 | |
| 5928 /** | |
| 5929 * Initialize a newly created member map to contain the same members as the | |
| 5930 * given [memberMap]. | |
| 5931 */ | |
| 5932 MemberMap.from(MemberMap memberMap) { | |
| 5933 _initArrays(memberMap._size + 5); | |
| 5934 for (int i = 0; i < memberMap._size; i++) { | |
| 5935 _keys[i] = memberMap._keys[i]; | |
| 5936 _values[i] = memberMap._values[i]; | |
| 5937 } | |
| 5938 _size = memberMap._size; | |
| 5939 } | |
| 5940 | |
| 5941 /** | |
| 5942 * The size of the map. | |
| 5943 * | |
| 5944 * @return the size of the map. | |
| 5945 */ | |
| 5946 int get size => _size; | |
| 5947 | |
| 5948 /** | |
| 5949 * Given some key, return the ExecutableElement value from the map, if the key
does not exist in | |
| 5950 * the map, `null` is returned. | |
| 5951 * | |
| 5952 * @param key some key to look up in the map | |
| 5953 * @return the associated ExecutableElement value from the map, if the key doe
s not exist in the | |
| 5954 * map, `null` is returned | |
| 5955 */ | |
| 5956 ExecutableElement get(String key) { | |
| 5957 for (int i = 0; i < _size; i++) { | |
| 5958 if (_keys[i] != null && _keys[i] == key) { | |
| 5959 return _values[i]; | |
| 5960 } | |
| 5961 } | |
| 5962 return null; | |
| 5963 } | |
| 5964 | |
| 5965 /** | |
| 5966 * Get and return the key at the specified location. If the key/value pair has
been removed from | |
| 5967 * the set, then `null` is returned. | |
| 5968 * | |
| 5969 * @param i some non-zero value less than size | |
| 5970 * @return the key at the passed index | |
| 5971 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
| 5972 * zero or greater than or equal to the capacity of the arrays | |
| 5973 */ | |
| 5974 String getKey(int i) => _keys[i]; | |
| 5975 | |
| 5976 /** | |
| 5977 * Get and return the ExecutableElement at the specified location. If the key/
value pair has been | |
| 5978 * removed from the set, then then `null` is returned. | |
| 5979 * | |
| 5980 * @param i some non-zero value less than size | |
| 5981 * @return the key at the passed index | |
| 5982 * @throw ArrayIndexOutOfBoundsException this exception is thrown if the passe
d index is less than | |
| 5983 * zero or greater than or equal to the capacity of the arrays | |
| 5984 */ | |
| 5985 ExecutableElement getValue(int i) => _values[i]; | |
| 5986 | |
| 5987 /** | |
| 5988 * Given some key/value pair, store the pair in the map. If the key exists alr
eady, then the new | |
| 5989 * value overrides the old value. | |
| 5990 * | |
| 5991 * @param key the key to store in the map | |
| 5992 * @param value the ExecutableElement value to store in the map | |
| 5993 */ | |
| 5994 void put(String key, ExecutableElement value) { | |
| 5995 // If we already have a value with this key, override the value | |
| 5996 for (int i = 0; i < _size; i++) { | |
| 5997 if (_keys[i] != null && _keys[i] == key) { | |
| 5998 _values[i] = value; | |
| 5999 return; | |
| 6000 } | |
| 6001 } | |
| 6002 // If needed, double the size of our arrays and copy values over in both | |
| 6003 // arrays | |
| 6004 if (_size == _keys.length) { | |
| 6005 int newArrayLength = _size * 2; | |
| 6006 List<String> keys_new_array = new List<String>(newArrayLength); | |
| 6007 List<ExecutableElement> values_new_array = | |
| 6008 new List<ExecutableElement>(newArrayLength); | |
| 6009 for (int i = 0; i < _size; i++) { | |
| 6010 keys_new_array[i] = _keys[i]; | |
| 6011 } | |
| 6012 for (int i = 0; i < _size; i++) { | |
| 6013 values_new_array[i] = _values[i]; | |
| 6014 } | |
| 6015 _keys = keys_new_array; | |
| 6016 _values = values_new_array; | |
| 6017 } | |
| 6018 // Put new value at end of array | |
| 6019 _keys[_size] = key; | |
| 6020 _values[_size] = value; | |
| 6021 _size++; | |
| 6022 } | |
| 6023 | |
| 6024 /** | |
| 6025 * Given some [String] key, this method replaces the associated key and value
pair with | |
| 6026 * `null`. The size is not decremented with this call, instead it is expected
that the users | |
| 6027 * check for `null`. | |
| 6028 * | |
| 6029 * @param key the key of the key/value pair to remove from the map | |
| 6030 */ | |
| 6031 void remove(String key) { | |
| 6032 for (int i = 0; i < _size; i++) { | |
| 6033 if (_keys[i] == key) { | |
| 6034 _keys[i] = null; | |
| 6035 _values[i] = null; | |
| 6036 return; | |
| 6037 } | |
| 6038 } | |
| 6039 } | |
| 6040 | |
| 6041 /** | |
| 6042 * Sets the ExecutableElement at the specified location. | |
| 6043 * | |
| 6044 * @param i some non-zero value less than size | |
| 6045 * @param value the ExecutableElement value to store in the map | |
| 6046 */ | |
| 6047 void setValue(int i, ExecutableElement value) { | |
| 6048 _values[i] = value; | |
| 6049 } | |
| 6050 | |
| 6051 /** | |
| 6052 * Initializes [keys] and [values]. | |
| 6053 */ | |
| 6054 void _initArrays(int initialCapacity) { | |
| 6055 _keys = new List<String>(initialCapacity); | |
| 6056 _values = new List<ExecutableElement>(initialCapacity); | |
| 6057 } | |
| 6058 } | |
| 6059 | |
| 6060 /** | |
| 6061 * Instances of the class `OverrideVerifier` visit all of the declarations in a
compilation | 4867 * Instances of the class `OverrideVerifier` visit all of the declarations in a
compilation |
| 6062 * unit to verify that if they have an override annotation it is being used corr
ectly. | 4868 * unit to verify that if they have an override annotation it is being used corr
ectly. |
| 6063 */ | 4869 */ |
| 6064 class OverrideVerifier extends RecursiveAstVisitor<Object> { | 4870 class OverrideVerifier extends RecursiveAstVisitor<Object> { |
| 6065 /** | 4871 /** |
| 6066 * The error reporter used to report errors. | 4872 * The error reporter used to report errors. |
| 6067 */ | 4873 */ |
| 6068 final ErrorReporter _errorReporter; | 4874 final ErrorReporter _errorReporter; |
| 6069 | 4875 |
| 6070 /** | 4876 /** |
| (...skipping 5884 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 11955 nonFields.add(node); | 10761 nonFields.add(node); |
| 11956 return null; | 10762 return null; |
| 11957 } | 10763 } |
| 11958 | 10764 |
| 11959 @override | 10765 @override |
| 11960 Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this); | 10766 Object visitNode(AstNode node) => node.accept(TypeResolverVisitor_this); |
| 11961 | 10767 |
| 11962 @override | 10768 @override |
| 11963 Object visitWithClause(WithClause node) => null; | 10769 Object visitWithClause(WithClause node) => null; |
| 11964 } | 10770 } |
| OLD | NEW |