Index: pkg/compiler/lib/src/resolution/class_hierarchy.dart |
diff --git a/pkg/compiler/lib/src/resolution/class_hierarchy.dart b/pkg/compiler/lib/src/resolution/class_hierarchy.dart |
index 3b867eabc10860eb90ec417a956bb0004213d16e..0d9b50cfa0dac064c6ac80c4bb704f652d22ce50 100644 |
--- a/pkg/compiler/lib/src/resolution/class_hierarchy.dart |
+++ b/pkg/compiler/lib/src/resolution/class_hierarchy.dart |
@@ -13,6 +13,7 @@ import '../elements/modelx.dart' |
show |
BaseClassElementX, |
ErroneousElementX, |
+ LibraryElementX, |
MixinApplicationElementX, |
SynthesizedConstructorElementX, |
TypeVariableElementX, |
@@ -30,6 +31,10 @@ import 'registry.dart' show ResolutionRegistry; |
import 'resolution_common.dart' show CommonResolverVisitor, MappingVisitor; |
import 'scope.dart' show Scope, TypeDeclarationScope; |
+/// If `true` compatible mixin applications are shared within a library. This |
+/// matches the mixins generated by fasta. |
+bool useOptimizedMixins = false; |
+ |
class TypeDefinitionVisitor extends MappingVisitor<ResolutionDartType> { |
Scope scope; |
final TypeDeclarationElement enclosingElement; |
@@ -145,15 +150,11 @@ class ClassResolverVisitor extends TypeDefinitionVisitor { |
if (element.supertype == null && node.superclass != null) { |
MixinApplication superMixin = node.superclass.asMixinApplication(); |
if (superMixin != null) { |
- ResolutionDartType supertype = |
- resolveSupertype(element, superMixin.superclass); |
- Link<Node> link = superMixin.mixins.nodes; |
- while (!link.isEmpty) { |
- supertype = |
- applyMixin(supertype, checkMixinType(link.head), link.head); |
- link = link.tail; |
+ if (useOptimizedMixins) { |
+ element.supertype = createMixinsOptimized(element, superMixin); |
+ } else { |
+ element.supertype = createMixins(element, superMixin); |
} |
- element.supertype = supertype; |
} else { |
element.supertype = resolveSupertype(element, node.superclass); |
} |
@@ -285,14 +286,225 @@ class ClassResolverVisitor extends TypeDefinitionVisitor { |
// Generate anonymous mixin application elements for the |
// intermediate mixin applications (excluding the last). |
+ if (useOptimizedMixins) { |
+ createMixinsOptimized(element, node, isNamed: true); |
+ } else { |
+ createMixins(element, node, isNamed: true); |
+ } |
+ return element.computeType(resolution); |
+ } |
+ |
+ /// Create the mixin applications for [superMixin]. |
+ /// |
+ /// This algorithm is ported from |
+ /// `package:front_end/src/fasta/kernel/kernel_library_builder.dart` and |
+ /// added create allow for equivalence testing between the AST and kernel |
+ /// based compilations in face of shared mixins. It will be removed when we |
+ /// no longer need equivalence testing. |
+ ResolutionDartType createMixinsOptimized( |
+ BaseClassElementX element, MixinApplication superMixin, |
+ {bool isNamed: false}) { |
+ LibraryElementX library = element.library; |
+ Map<String, MixinApplicationElementX> mixinApplicationClasses = |
+ library.mixinApplicationCache; |
+ |
+ String name = element.isNamedMixinApplication ? element.name : null; |
+ ResolutionDartType supertype = |
+ resolveSupertype(element, superMixin.superclass); |
+ Link<Node> link = superMixin.mixins.nodes; |
+ List<ResolutionDartType> mixins = <ResolutionDartType>[]; |
+ List<Node> mixinNodes = <Node>[]; |
+ while (!link.isEmpty) { |
+ mixins.add(checkMixinType(link.head)); |
+ mixinNodes.add(link.head); |
+ link = link.tail; |
+ } |
+ |
+ List<List<String>> signatureParts = <List<String>>[]; |
+ Map<String, ResolutionDartType> freeTypes = <String, ResolutionDartType>{}; |
+ |
+ { |
+ Map<String, String> unresolved = <String, String>{}; |
+ int unresolvedCount = 0; |
+ |
+ /// Compute a signature of the type arguments used by the supertype and |
+ /// mixins. These types are free variables. At this point we can't |
+ /// trust that the number of type arguments match the type parameters, |
+ /// so we also need to be able to detect missing type arguments. To do |
+ /// so, we separate each list of type arguments by `^` and type |
+ /// arguments by `&`. For example, the mixin `C<S> with M<T, U>` would |
+ /// look like this: |
+ /// |
+ /// ^#U0^#U1&#U2 |
+ /// |
+ /// Where `#U0`, `#U1`, and `#U2` are the free variables arising from |
+ /// `S`, `T`, and `U` respectively. |
+ /// |
+ /// As we can resolve any type parameters used at this point, those are |
+ /// named `#T0` and so forth. This reduces the number of free variables |
+ /// which is crucial for memory usage and the Dart VM's bootstrap |
+ /// sequence. |
+ /// |
+ /// For example, consider this use of mixin applications: |
+ /// |
+ /// class _InternalLinkedHashMap<K, V> extends _HashVMBase |
+ /// with |
+ /// MapMixin<K, V>, |
+ /// _LinkedHashMapMixin<K, V>, |
+ /// _HashBase, |
+ /// _OperatorEqualsAndHashCode {} |
+ /// |
+ /// In this case, only two variables are free, and we produce this |
+ /// signature: `^^#T0&#T1^#T0&#T1^^`. Assume another class uses the |
+ /// sames mixins but with missing type arguments for `MapMixin`, its |
+ /// signature would be: `^^^#T0&#T1^^`. |
+ /// |
+ /// Note that we do not need to compute a signature for a named mixin |
+ /// application with only one mixin as we don't have to invent a name |
+ /// for any classes in this situation. |
+ void analyzeArguments(ResolutionDartType type, {bool isLast}) { |
+ if (isNamed && isLast) { |
+ // The last mixin of a named mixin application doesn't contribute |
+ // to free variables. |
+ return; |
+ } |
+ if (type is GenericType) { |
+ List<String> part = <String>[]; |
+ for (int i = 0; i < type.typeArguments.length; i++) { |
+ var argument = type.typeArguments[i]; |
+ String name; |
+ if (argument is ResolutionTypeVariableType) { |
+ int index = element.typeVariables.indexOf(argument) ?? -1; |
+ if (index != -1) { |
+ name = "#T${index}"; |
+ } |
+ } else if (argument is GenericType && argument.isRaw) { |
+ name = unresolved[argument.name] ??= "#U${unresolvedCount++}"; |
+ } |
+ name ??= "#U${unresolvedCount++}"; |
+ freeTypes[name] = argument; |
+ part.add(name); |
+ } |
+ signatureParts.add(part); |
+ } |
+ } |
+ |
+ analyzeArguments(supertype, isLast: false); |
+ for (int i = 0; i < mixins.length; i++) { |
+ analyzeArguments(mixins[i], isLast: i == mixins.length - 1); |
+ } |
+ } |
+ |
+ List<List<String>> currentSignatureParts = <List<String>>[]; |
+ String computeSignature(int index) { |
+ if (freeTypes.isEmpty) return ""; |
+ currentSignatureParts.add(signatureParts[index]); |
+ if (currentSignatureParts.any((l) => l.isNotEmpty)) { |
+ return "^${currentSignatureParts.map((l) => l.join('&')).join('^')}"; |
+ } else { |
+ return ""; |
+ } |
+ } |
+ |
+ Map<String, ResolutionTypeVariableType> computeTypeVariables( |
+ ClassElement cls, Node node) { |
+ Map<String, ResolutionTypeVariableType> variables = |
+ <String, ResolutionTypeVariableType>{}; |
+ int index = 0; |
+ for (List<String> strings in currentSignatureParts) { |
+ for (String name in strings) { |
+ variables[name] ??= new ResolutionTypeVariableType( |
+ new TypeVariableElementX(name, cls, index++, node)); |
+ } |
+ } |
+ return variables; |
+ } |
+ |
+ computeSignature(0); // This combines the supertype with the first mixin. |
+ |
+ for (int i = 0; i < mixins.length; i++) { |
+ int signatureIndex = i + 1; |
+ Set<String> supertypeArguments = new Set<String>(); |
+ for (List<String> part in currentSignatureParts) { |
+ supertypeArguments.addAll(part); |
+ } |
+ Node node = mixinNodes[i]; |
+ ResolutionDartType mixin = mixins[i]; |
+ |
+ bool lastAndNamed = i == mixins.length - 1 && isNamed; |
+ |
+ ResolutionInterfaceType createMixinApplication() { |
+ Map<String, ResolutionDartType> variables; |
+ MixinApplicationElementX mixinElement; |
+ ResolutionInterfaceType mixinType; |
+ if (lastAndNamed) { |
+ mixinElement = element; |
+ variables = freeTypes; |
+ } else { |
+ String signature = computeSignature(signatureIndex); |
+ name = supertype.name; |
+ int index = name.indexOf("^"); |
+ if (index != -1) { |
+ name = name.substring(0, index); |
+ } |
+ name = "$name&${mixin.name}$signature"; |
+ mixinElement = mixinApplicationClasses[name]; |
+ if (mixinElement != null) return mixinElement.thisType; |
+ |
+ mixinElement = new UnnamedMixinApplicationElementX( |
+ name, element, resolution.idGenerator.getNextFreeId(), node); |
+ variables = computeTypeVariables(mixinElement, node); |
+ mixinElement.setThisAndRawTypes(variables.values.toList()); |
+ mixinApplicationClasses[name] = mixinElement; |
+ } |
+ |
+ if (supertypeArguments.isNotEmpty) { |
+ List<ResolutionDartType> supertypeTypeArguments = |
+ <ResolutionDartType>[]; |
+ for (String part in supertypeArguments) { |
+ supertypeTypeArguments.add(variables[part]); |
+ } |
+ supertype = new ResolutionInterfaceType( |
+ supertype.element, supertypeTypeArguments); |
+ } |
+ |
+ if (lastAndNamed) { |
+ mixinType = mixin; |
+ } else { |
+ List<ResolutionDartType> mixinTypeArguments = <ResolutionDartType>[]; |
+ for (String part in signatureParts[signatureIndex]) { |
+ mixinTypeArguments.add(variables[part]); |
+ } |
+ mixinType = |
+ new ResolutionInterfaceType(mixin.element, mixinTypeArguments); |
+ } |
+ |
+ doApplyMixinTo(mixinElement, supertype, mixinType); |
+ mixinElement.resolutionState = STATE_DONE; |
+ mixinElement.supertypeLoadState = STATE_DONE; |
+ return mixinElement.thisType; |
+ } |
+ |
+ supertype = createMixinApplication(); |
+ } |
+ |
+ return new ResolutionInterfaceType( |
+ supertype.element, freeTypes.values.toList()); |
+ } |
+ |
+ ResolutionDartType createMixins(ClassElement element, MixinApplication node, |
+ {bool isNamed: false}) { |
ResolutionDartType supertype = resolveSupertype(element, node.superclass); |
Link<Node> link = node.mixins.nodes; |
- while (!link.tail.isEmpty) { |
+ while (!link.isEmpty) { |
+ if (isNamed && link.tail.isEmpty) { |
+ doApplyMixinTo(element, supertype, checkMixinType(link.head)); |
+ return supertype; |
+ } |
supertype = applyMixin(supertype, checkMixinType(link.head), link.head); |
link = link.tail; |
} |
- doApplyMixinTo(element, supertype, checkMixinType(link.head)); |
- return element.computeType(resolution); |
+ return supertype; |
} |
ResolutionDartType applyMixin( |