| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library dart2js.kernel.equivalence; |
| 6 |
| 7 import 'package:compiler/src/common/backend_api.dart'; |
| 8 import 'package:compiler/src/common/resolution.dart'; |
| 9 import 'package:compiler/src/common/work.dart'; |
| 10 import 'package:compiler/src/constants/expressions.dart'; |
| 11 import 'package:compiler/src/constants/values.dart'; |
| 12 import 'package:compiler/src/compiler.dart'; |
| 13 import 'package:compiler/src/elements/elements.dart'; |
| 14 import 'package:compiler/src/elements/entities.dart'; |
| 15 import 'package:compiler/src/elements/resolution_types.dart'; |
| 16 import 'package:compiler/src/elements/types.dart'; |
| 17 import 'package:compiler/src/enqueue.dart'; |
| 18 import 'package:compiler/src/kernel/elements.dart'; |
| 19 import 'package:compiler/src/kernel/element_map_impl.dart'; |
| 20 import 'package:compiler/src/serialization/equivalence.dart'; |
| 21 import 'package:compiler/src/ssa/kernel_impact.dart'; |
| 22 import 'package:compiler/src/universe/world_impact.dart'; |
| 23 import 'package:compiler/src/util/util.dart'; |
| 24 |
| 25 class KernelEquivalence { |
| 26 final WorldDeconstructionForTesting testing; |
| 27 |
| 28 /// Set of mixin applications assumed to be equivalent. |
| 29 /// |
| 30 /// We need co-inductive reasoning because mixin applications are compared |
| 31 /// structurally and therefore, in the case of generic mixin applications, |
| 32 /// meet themselves through the equivalence check of their type variables. |
| 33 Set<Pair<ClassEntity, ClassEntity>> assumedMixinApplications = |
| 34 new Set<Pair<ClassEntity, ClassEntity>>(); |
| 35 |
| 36 KernelEquivalence(KernelToElementMapImpl builder) |
| 37 : testing = new WorldDeconstructionForTesting(builder); |
| 38 |
| 39 TestStrategy get defaultStrategy => new TestStrategy( |
| 40 elementEquivalence: entityEquivalence, |
| 41 typeEquivalence: typeEquivalence, |
| 42 constantEquivalence: constantEquivalence, |
| 43 constantValueEquivalence: constantValueEquivalence); |
| 44 |
| 45 bool entityEquivalence(Element a, Entity b, {TestStrategy strategy}) { |
| 46 if (identical(a, b)) return true; |
| 47 if (a == null || b == null) return false; |
| 48 strategy ??= defaultStrategy; |
| 49 switch (a.kind) { |
| 50 case ElementKind.GENERATIVE_CONSTRUCTOR: |
| 51 if (b is KGenerativeConstructor) { |
| 52 return strategy.test(a, b, 'name', a.name, b.name) && |
| 53 strategy.testElements( |
| 54 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass); |
| 55 } |
| 56 return false; |
| 57 case ElementKind.FACTORY_CONSTRUCTOR: |
| 58 if (b is KFactoryConstructor) { |
| 59 return strategy.test(a, b, 'name', a.name, b.name) && |
| 60 strategy.testElements( |
| 61 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass); |
| 62 } |
| 63 return false; |
| 64 case ElementKind.CLASS: |
| 65 if (b is KClass) { |
| 66 List<InterfaceType> aMixinTypes = []; |
| 67 List<InterfaceType> bMixinTypes = []; |
| 68 ClassElement aClass = a; |
| 69 if (aClass.isUnnamedMixinApplication) { |
| 70 if (!testing.isUnnamedMixinApplication(b)) { |
| 71 return false; |
| 72 } |
| 73 while (aClass.isMixinApplication) { |
| 74 MixinApplicationElement aMixinApplication = aClass; |
| 75 aMixinTypes.add(aMixinApplication.mixinType); |
| 76 aClass = aMixinApplication.superclass; |
| 77 } |
| 78 KClass bClass = b; |
| 79 while (bClass != null) { |
| 80 InterfaceType mixinType = testing.getMixinTypeForClass(bClass); |
| 81 if (mixinType == null) break; |
| 82 bMixinTypes.add(mixinType); |
| 83 bClass = testing.getSuperclassForClass(bClass); |
| 84 } |
| 85 if (aMixinTypes.isNotEmpty || aMixinTypes.isNotEmpty) { |
| 86 Pair<ClassEntity, ClassEntity> pair = |
| 87 new Pair<ClassEntity, ClassEntity>(aClass, bClass); |
| 88 if (assumedMixinApplications.contains(pair)) { |
| 89 return true; |
| 90 } else { |
| 91 assumedMixinApplications.add(pair); |
| 92 bool result = strategy.testTypeLists( |
| 93 a, b, 'mixinTypes', aMixinTypes, bMixinTypes); |
| 94 assumedMixinApplications.remove(pair); |
| 95 return result; |
| 96 } |
| 97 } |
| 98 } else { |
| 99 if (testing.isUnnamedMixinApplication(b)) { |
| 100 return false; |
| 101 } |
| 102 } |
| 103 return strategy.test(a, b, 'name', a.name, b.name) && |
| 104 strategy.testElements(a, b, 'library', a.library, b.library); |
| 105 } |
| 106 return false; |
| 107 case ElementKind.LIBRARY: |
| 108 if (b is KLibrary) { |
| 109 LibraryElement libraryA = a; |
| 110 return libraryA.canonicalUri == b.canonicalUri; |
| 111 } |
| 112 return false; |
| 113 case ElementKind.FUNCTION: |
| 114 if (b is KMethod) { |
| 115 return strategy.test(a, b, 'name', a.name, b.name) && |
| 116 strategy.testElements( |
| 117 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) && |
| 118 strategy.testElements(a, b, 'library', a.library, b.library); |
| 119 } else if (b is KLocalFunction) { |
| 120 LocalFunctionElement aLocalFunction = a; |
| 121 return strategy.test(a, b, 'name', a.name, b.name ?? '') && |
| 122 strategy.testElements(a, b, 'executableContext', |
| 123 aLocalFunction.executableContext, b.executableContext) && |
| 124 strategy.testElements(a, b, 'memberContext', |
| 125 aLocalFunction.memberContext, b.memberContext); |
| 126 } |
| 127 return false; |
| 128 case ElementKind.GETTER: |
| 129 if (b is KGetter) { |
| 130 return strategy.test(a, b, 'name', a.name, b.name) && |
| 131 strategy.testElements( |
| 132 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) && |
| 133 strategy.testElements(a, b, 'library', a.library, b.library); |
| 134 } |
| 135 return false; |
| 136 case ElementKind.SETTER: |
| 137 if (b is KSetter) { |
| 138 return strategy.test(a, b, 'name', a.name, b.name) && |
| 139 strategy.testElements( |
| 140 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) && |
| 141 strategy.testElements(a, b, 'library', a.library, b.library); |
| 142 } |
| 143 return false; |
| 144 case ElementKind.FIELD: |
| 145 if (b is KField) { |
| 146 return strategy.test(a, b, 'name', a.name, b.name) && |
| 147 strategy.testElements( |
| 148 a, b, 'enclosingClass', a.enclosingClass, b.enclosingClass) && |
| 149 strategy.testElements(a, b, 'library', a.library, b.library); |
| 150 } |
| 151 return false; |
| 152 case ElementKind.TYPE_VARIABLE: |
| 153 if (b is KTypeVariable) { |
| 154 TypeVariableElement aElement = a; |
| 155 return strategy.test(a, b, 'index', aElement.index, b.index) && |
| 156 strategy.testElements(a, b, 'typeDeclaration', |
| 157 aElement.typeDeclaration, b.typeDeclaration); |
| 158 } |
| 159 return false; |
| 160 default: |
| 161 throw new UnsupportedError('Unsupported equivalence: ' |
| 162 '$a (${a.runtimeType}) vs $b (${b.runtimeType})'); |
| 163 } |
| 164 } |
| 165 |
| 166 bool typeEquivalence(ResolutionDartType a, DartType b, |
| 167 {TestStrategy strategy}) { |
| 168 if (identical(a, b)) return true; |
| 169 if (a == null || b == null) return false; |
| 170 strategy ??= defaultStrategy; |
| 171 switch (a.kind) { |
| 172 case ResolutionTypeKind.DYNAMIC: |
| 173 return b is DynamicType; |
| 174 case ResolutionTypeKind.VOID: |
| 175 return b is VoidType; |
| 176 case ResolutionTypeKind.INTERFACE: |
| 177 if (b is InterfaceType) { |
| 178 ResolutionInterfaceType aType = a; |
| 179 return strategy.testElements(a, b, 'element', a.element, b.element) && |
| 180 strategy.testTypeLists( |
| 181 a, b, 'typeArguments', aType.typeArguments, b.typeArguments); |
| 182 } |
| 183 return false; |
| 184 case ResolutionTypeKind.TYPE_VARIABLE: |
| 185 if (b is TypeVariableType) { |
| 186 return strategy.testElements(a, b, 'element', a.element, b.element); |
| 187 } |
| 188 return false; |
| 189 case ResolutionTypeKind.FUNCTION: |
| 190 if (b is FunctionType) { |
| 191 ResolutionFunctionType aType = a; |
| 192 return strategy.testTypes( |
| 193 a, b, 'returnType', aType.returnType, b.returnType) && |
| 194 strategy.testTypeLists(a, b, 'parameterTypes', |
| 195 aType.parameterTypes, b.parameterTypes) && |
| 196 strategy.testTypeLists(a, b, 'optionalParameterTypes', |
| 197 aType.optionalParameterTypes, b.optionalParameterTypes) && |
| 198 strategy.testLists(a, b, 'namedParameters', aType.namedParameters, |
| 199 b.namedParameters) && |
| 200 strategy.testTypeLists(a, b, 'namedParameterTypes', |
| 201 aType.namedParameterTypes, b.namedParameterTypes); |
| 202 } |
| 203 return false; |
| 204 default: |
| 205 throw new UnsupportedError('Unsupported equivalence: ' |
| 206 '$a (${a.runtimeType}) vs $b (${b.runtimeType})'); |
| 207 } |
| 208 } |
| 209 |
| 210 bool constantEquivalence(ConstantExpression exp1, ConstantExpression exp2, |
| 211 {TestStrategy strategy}) { |
| 212 strategy ??= defaultStrategy; |
| 213 return areConstantsEquivalent(exp1, exp2, strategy: strategy); |
| 214 } |
| 215 |
| 216 bool constantValueEquivalence(ConstantValue value1, ConstantValue value2, |
| 217 {TestStrategy strategy}) { |
| 218 strategy ??= defaultStrategy; |
| 219 return areConstantValuesEquivalent(value1, value2, strategy: strategy); |
| 220 } |
| 221 } |
| 222 |
| 223 class KernelTestWorkItemBuilder implements WorkItemBuilder { |
| 224 final Compiler _compiler; |
| 225 |
| 226 KernelTestWorkItemBuilder(this._compiler); |
| 227 |
| 228 @override |
| 229 WorkItem createWorkItem(MemberEntity entity) { |
| 230 return new KernelTestWorkItem( |
| 231 _compiler, _compiler.backend.impactTransformer, entity); |
| 232 } |
| 233 } |
| 234 |
| 235 class KernelTestWorkItem implements ResolutionWorkItem { |
| 236 final Compiler _compiler; |
| 237 final ImpactTransformer _impactTransformer; |
| 238 final MemberElement element; |
| 239 |
| 240 KernelTestWorkItem(this._compiler, this._impactTransformer, this.element); |
| 241 |
| 242 @override |
| 243 WorldImpact run() { |
| 244 ResolutionImpact resolutionImpact = build(_compiler, element.resolvedAst); |
| 245 return _impactTransformer.transformResolutionImpact(resolutionImpact); |
| 246 } |
| 247 } |
| 248 |
| 249 /// Visitor the performers unaliasing of all typedefs nested within a |
| 250 /// [ResolutionDartType]. |
| 251 class Unaliaser |
| 252 extends BaseResolutionDartTypeVisitor<dynamic, ResolutionDartType> { |
| 253 const Unaliaser(); |
| 254 |
| 255 @override |
| 256 ResolutionDartType visit(ResolutionDartType type, [_]) => |
| 257 type.accept(this, null); |
| 258 |
| 259 @override |
| 260 ResolutionDartType visitType(ResolutionDartType type, _) => type; |
| 261 |
| 262 List<ResolutionDartType> visitList(List<ResolutionDartType> types) => |
| 263 types.map(visit).toList(); |
| 264 |
| 265 @override |
| 266 ResolutionDartType visitInterfaceType(ResolutionInterfaceType type, _) { |
| 267 return type.createInstantiation(visitList(type.typeArguments)); |
| 268 } |
| 269 |
| 270 @override |
| 271 ResolutionDartType visitTypedefType(ResolutionTypedefType type, _) { |
| 272 return visit(type.unaliased); |
| 273 } |
| 274 |
| 275 @override |
| 276 ResolutionDartType visitFunctionType(ResolutionFunctionType type, _) { |
| 277 return new ResolutionFunctionType.synthesized( |
| 278 visit(type.returnType), |
| 279 visitList(type.parameterTypes), |
| 280 visitList(type.optionalParameterTypes), |
| 281 type.namedParameters, |
| 282 visitList(type.namedParameterTypes)); |
| 283 } |
| 284 } |
| 285 |
| 286 /// Perform unaliasing of all typedefs nested within a [ResolutionDartType]. |
| 287 ResolutionDartType unalias(ResolutionDartType type) { |
| 288 return const Unaliaser().visit(type); |
| 289 } |
| 290 |
| 291 bool elementFilter(Entity element) { |
| 292 if (element is ConstructorElement && element.isRedirectingFactory) { |
| 293 // Redirecting factory constructors are skipped in kernel. |
| 294 return false; |
| 295 } |
| 296 if (element is ClassElement) { |
| 297 for (ConstructorElement constructor in element.constructors) { |
| 298 if (!constructor.isRedirectingFactory) { |
| 299 return true; |
| 300 } |
| 301 } |
| 302 // The class cannot itself be instantiated. |
| 303 return false; |
| 304 } |
| 305 return true; |
| 306 } |
| OLD | NEW |