Chromium Code Reviews| Index: pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
| diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
| index f18303f2d1dea629e4c5c4a164dd696c56f35cb3..5df1a4a8351331f0953a5889bd86a9626efe115a 100644 |
| --- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
| +++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart |
| @@ -12,7 +12,8 @@ import '../errors.dart' show internalError; |
| import '../loader.dart' show Loader; |
| -import '../modifier.dart' show abstractMask, staticMask; |
| +import '../modifier.dart' |
| + show abstractMask, namedMixinApplicationMask, staticMask; |
| import '../source/source_library_builder.dart' |
| show DeclarationBuilder, SourceLibraryBuilder; |
| @@ -39,7 +40,6 @@ import 'kernel_builder.dart' |
| KernelFunctionTypeBuilder, |
| KernelInvalidTypeBuilder, |
| KernelMixinApplicationBuilder, |
| - KernelNamedMixinApplicationBuilder, |
| KernelNamedTypeBuilder, |
| KernelProcedureBuilder, |
| KernelTypeBuilder, |
| @@ -47,7 +47,7 @@ import 'kernel_builder.dart' |
| LibraryBuilder, |
| MemberBuilder, |
| MetadataBuilder, |
| - NamedMixinApplicationBuilder, |
| + NamedTypeBuilder, |
| PrefixBuilder, |
| ProcedureBuilder, |
| Scope, |
| @@ -103,7 +103,7 @@ class KernelLibraryBuilder |
| List<KernelTypeBuilder> interfaces, |
| int charOffset) { |
| // Nested declaration began in `OutlineBuilder.beginClassDeclaration`. |
| - var declaration = endNestedDeclaration(); |
| + var declaration = endNestedDeclaration()..resolveTypes(typeVariables, this); |
| assert(declaration.parent == libraryDeclaration); |
| Map<String, MemberBuilder> members = declaration.members; |
| Map<String, MemberBuilder> constructors = declaration.constructors; |
| @@ -122,7 +122,8 @@ class KernelLibraryBuilder |
| modifiers, |
| className, |
| typeVariables, |
| - applyMixins(supertype), |
| + applyMixins(supertype, |
| + subclassName: className, typeVariables: typeVariables), |
| interfaces, |
| classScope, |
| constructorScope, |
| @@ -133,6 +134,13 @@ class KernelLibraryBuilder |
| Map<String, TypeVariableBuilder> typeVariablesByName = |
| checkTypeVariables(typeVariables, cls); |
| void setParent(String name, MemberBuilder member) { |
| + while (member != null) { |
| + member.parent = cls; |
| + member = member.next; |
| + } |
| + } |
| + |
| + void setParentAndCheckConflicts(String name, MemberBuilder member) { |
| if (typeVariablesByName != null) { |
| TypeVariableBuilder tv = typeVariablesByName[name]; |
| if (tv != null) { |
| @@ -141,16 +149,14 @@ class KernelLibraryBuilder |
| cls.addCompileTimeError(tv.charOffset, "This is the type variable."); |
| } |
| } |
| - while (member != null) { |
| - member.parent = cls; |
| - member = member.next; |
| - } |
| + setParent(name, member); |
| } |
| - members.forEach(setParent); |
| - constructors.forEach(setParent); |
| + members.forEach(setParentAndCheckConflicts); |
| + constructors.forEach(setParentAndCheckConflicts); |
| + // Formally, a setter has the name `id=`, so it can never conflict with a |
| + // type variable. |
| setters.forEach(setParent); |
| - declaration.resolveTypes(typeVariables, this); |
| addBuilder(className, cls, charOffset); |
| } |
| @@ -168,66 +174,266 @@ class KernelLibraryBuilder |
| existing.charOffset, "The other type variable named '${tv.name}'."); |
| } else { |
| typeVariablesByName[tv.name] = tv; |
| - if (tv.name == owner.name) { |
| - addCompileTimeError( |
| - tv.charOffset, |
| - "A type variable can't have the same name as its enclosing " |
| - "declaration."); |
| + if (owner is ClassBuilder) { |
|
Johnni Winther
2017/05/08 10:05:34
Why is the only for classes? Aren't `typedef F<F>(
ahe
2017/05/08 11:13:15
Not according to our tests, and it also seems like
|
| + if (tv.name == owner.name) { |
| + addCompileTimeError( |
| + tv.charOffset, |
| + "A type variable can't have the same name as its enclosing " |
| + "declaration."); |
| + } |
| } |
| } |
| } |
| return typeVariablesByName; |
| } |
| - KernelNamedTypeBuilder removeTypeVariables(KernelNamedTypeBuilder type) { |
| - return type.arguments == null |
| - ? type |
| - : addNamedType(type.name, null, type.charOffset); |
| - } |
| - |
| KernelTypeBuilder applyMixin( |
| - KernelTypeBuilder supertype, KernelTypeBuilder mixin, String name) { |
| - supertype = removeTypeVariables(supertype); |
| - mixin = removeTypeVariables(mixin); |
| + KernelTypeBuilder supertype, KernelTypeBuilder mixin, String signature, |
| + {List<MetadataBuilder> metadata, |
| + String name, |
| + List<TypeVariableBuilder> typeVariables, |
| + int modifiers: abstractMask, |
| + List<KernelTypeBuilder> interfaces, |
| + int charOffset: -1}) { |
| var constructors = <String, MemberBuilder>{}; |
| - if (name != null) { |
| - constructors[""] = new KernelConstructorBuilder( |
| - null, 0, null, "", null, null, this, mixin.charOffset, -1, -1, null); |
| - } |
| - if (name == null) { |
| - name = "${supertype.name}&${mixin.name}"; |
| + bool isNamed = name != null; |
| + SourceClassBuilder builder; |
| + if (isNamed) { |
| + modifiers |= namedMixinApplicationMask; |
| + } else { |
| + name = supertype.name; |
| + int index = name.indexOf("^"); |
| + if (index != -1) { |
| + name = name.substring(0, index); |
| + } |
| + name = "$name&${mixin.name}$signature"; |
| + builder = mixinApplicationClasses[name]; |
| } |
| - var builder = mixinApplicationClasses.putIfAbsent(name, () { |
| - var builder = new SourceClassBuilder( |
| - null, // metadata |
| - 0, // modifiers |
| + if (builder == null) { |
| + builder = new SourceClassBuilder( |
| + metadata, |
| + modifiers, |
| name, |
| - null, // typeVariables |
| + typeVariables, |
| supertype, |
| - null, // interfaces |
| + interfaces, |
| new Scope(<String, MemberBuilder>{}, <String, MemberBuilder>{}, |
| - scope.withTypeVariables(null /* typeVariables */), |
| + scope.withTypeVariables(typeVariables), |
| isModifiable: false), |
| new Scope(constructors, null, null, isModifiable: false), |
| this, |
| <ConstructorReferenceBuilder>[], |
| - -1, // charOffset |
| + charOffset, |
| null, |
| mixin); |
| - addBuilder(name, builder, -1 /* charOffset */); |
| - return builder; |
| - }); |
| - return new KernelNamedTypeBuilder(name, null, -1 /* charOffset */, fileUri) |
| - ..bind(builder); |
| + addBuilder(name, builder, charOffset); |
| + if (!isNamed) { |
| + mixinApplicationClasses[name] = builder; |
| + } |
| + } |
| + return addNamedType(name, <KernelTypeBuilder>[], charOffset) |
| + ..bind(isNamed ? builder : null); |
| } |
| - KernelTypeBuilder applyMixins(KernelTypeBuilder type, [String name]) { |
| + KernelTypeBuilder applyMixins(KernelTypeBuilder type, |
| + {List<MetadataBuilder> metadata, |
| + String name, |
| + String subclassName, |
| + List<TypeVariableBuilder> typeVariables, |
| + int modifiers: abstractMask, |
| + List<KernelTypeBuilder> interfaces, |
| + int charOffset: -1}) { |
| if (type is KernelMixinApplicationBuilder) { |
| + subclassName ??= name; |
| + List<List<String>> signatureParts = <List<String>>[]; |
| + Map<String, String> unresolved = <String, String>{}; |
| + Map<String, String> unresolvedReversed = <String, String>{}; |
| + int unresolvedCount = 0; |
| + Map<String, TypeBuilder> freeTypes = <String, TypeBuilder>{}; |
| + |
| + if (name == null || type.mixins.length != 1) { |
| + TypeBuilder last = type.mixins.last; |
| + |
| + /// 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(TypeBuilder type) { |
| + if (name != null && type == last) { |
| + // The last mixin of a named mixin application doesn't contribute |
| + // to free variables. |
| + return; |
| + } |
| + if (type is NamedTypeBuilder) { |
| + List<String> part = <String>[]; |
| + for (int i = 0; i < (type.arguments?.length ?? 0); i++) { |
| + var argument = type.arguments[i]; |
| + String name; |
| + if (argument is NamedTypeBuilder) { |
| + if (argument.builder != null) { |
| + int index = typeVariables?.indexOf(argument.builder) ?? -1; |
| + if (index != -1) { |
| + name = "#T${index}"; |
| + } |
| + } else if (argument.arguments == null) { |
| + name = unresolved[argument.name] ??= "#U${unresolvedCount++}"; |
| + } |
| + } |
| + name ??= "#U${unresolvedCount++}"; |
| + unresolvedReversed[name] = argument.name; |
| + freeTypes[name] = argument; |
| + part.add(name); |
| + type.arguments[i] = |
| + new KernelNamedTypeBuilder(name, null, -1, fileUri); |
| + } |
| + signatureParts.add(part); |
| + } |
| + } |
| + |
| + analyzeArguments(type.supertype); |
| + type.mixins.forEach(analyzeArguments); |
| + } |
| KernelTypeBuilder supertype = type.supertype; |
| + List<List<String>> currentSignatureParts = <List<String>>[]; |
| + int currentSignatureCount = 0; |
| + String computeSignature() { |
| + if (freeTypes.isEmpty) return ""; |
| + currentSignatureParts.add(signatureParts[currentSignatureCount++]); |
| + if (currentSignatureParts.any((l) => l.isNotEmpty)) { |
| + return "^${currentSignatureParts.map((l) => l.join('&')).join('^')}"; |
| + } else { |
| + return ""; |
| + } |
| + } |
| + |
| + Map<String, TypeVariableBuilder> computeTypeVariables() { |
| + Map<String, TypeVariableBuilder> variables = |
| + <String, TypeVariableBuilder>{}; |
| + for (List<String> strings in currentSignatureParts) { |
| + for (String name in strings) { |
| + variables[name] ??= addTypeVariable(name, null, -1); |
| + } |
| + } |
| + return variables; |
| + } |
| + |
| + checkArguments(t) { |
| + for (var argument in t.arguments ?? const []) { |
| + if (argument.builder == null && argument.name.startsWith("#")) { |
| + throw "No builder on ${argument.name}"; |
| + } |
| + } |
| + } |
| + |
| + computeSignature(); // This combines the supertype with the first mixin. |
| for (int i = 0; i < type.mixins.length - 1; i++) { |
| - supertype = applyMixin(supertype, type.mixins[i], null); |
| + Set<String> supertypeArguments = new Set<String>(); |
| + for (var part in currentSignatureParts) { |
| + supertypeArguments.addAll(part); |
| + } |
| + String signature = computeSignature(); |
| + var variables = computeTypeVariables(); |
| + if (supertypeArguments.isNotEmpty) { |
| + supertype = addNamedType( |
| + supertype.name, |
| + supertypeArguments.map((n) { |
| + var t = addNamedType(n, null, -1)..bind(variables[n]); |
|
Johnni Winther
2017/05/08 10:05:34
var t = ...
return t;
->
return ...
ahe
2017/05/08 11:13:15
Done.
|
| + return t; |
| + }).toList(), |
| + -1); |
| + } |
| + KernelNamedTypeBuilder mixin = type.mixins[i]; |
| + for (var type in mixin.arguments ?? const []) { |
| + type.bind(variables[type.name]); |
| + } |
| + checkArguments(supertype); |
| + checkArguments(mixin); |
| + supertype = applyMixin(supertype, mixin, signature, |
| + typeVariables: |
| + new List<TypeVariableBuilder>.from(variables.values)); |
| + } |
| + KernelNamedTypeBuilder mixin = type.mixins.last; |
| + |
| + Set<String> supertypeArguments = new Set<String>(); |
| + for (var part in currentSignatureParts) { |
| + supertypeArguments.addAll(part); |
| + } |
| + String signature = name == null ? computeSignature() : ""; |
| + var variables; |
| + if (name == null) { |
| + variables = computeTypeVariables(); |
| + typeVariables = new List<TypeVariableBuilder>.from(variables.values); |
| + if (supertypeArguments.isNotEmpty) { |
| + supertype = addNamedType( |
| + supertype.name, |
| + supertypeArguments.map((n) { |
| + var t = addNamedType(n, null, -1)..bind(variables[n]); |
| + return t; |
|
Johnni Winther
2017/05/08 10:05:34
Ditto
ahe
2017/05/08 11:13:15
Done.
|
| + }).toList(), |
| + -1); |
| + } |
| + } else { |
| + if (supertypeArguments.isNotEmpty) { |
| + supertype = addNamedType(supertype.name, |
| + supertypeArguments.map((n) => freeTypes[n]).toList(), -1); |
| + } |
| } |
| - return applyMixin(supertype, type.mixins.last, name); |
| + |
| + if (name == null) { |
| + for (var type in mixin.arguments ?? const []) { |
| + type.bind(variables[type.name]); |
| + } |
| + } |
| + checkArguments(supertype); |
| + checkArguments(mixin); |
| + |
| + KernelNamedTypeBuilder t = applyMixin(supertype, mixin, signature, |
| + metadata: metadata, |
| + name: name, |
| + typeVariables: typeVariables, |
| + modifiers: modifiers, |
| + interfaces: interfaces, |
| + charOffset: charOffset); |
| + if (name == null) { |
| + var builder = t.builder; |
| + t = addNamedType( |
| + t.name, freeTypes.keys.map((k) => freeTypes[k]).toList(), -1); |
| + if (builder != null) { |
| + t.bind(builder); |
| + } |
| + } |
| + return t; |
| } else { |
| return type; |
| } |
| @@ -242,10 +448,15 @@ class KernelLibraryBuilder |
| List<KernelTypeBuilder> interfaces, |
| int charOffset) { |
| // Nested declaration began in `OutlineBuilder.beginNamedMixinApplication`. |
| - var decl = endNestedDeclaration(); |
| - KernelTypeBuilder supertype = applyMixins(mixinApplication, name); |
| + endNestedDeclaration().resolveTypes(typeVariables, this); |
| + KernelNamedTypeBuilder supertype = applyMixins(mixinApplication, |
| + metadata: metadata, |
| + name: name, |
| + typeVariables: typeVariables, |
| + modifiers: modifiers, |
| + interfaces: interfaces, |
| + charOffset: charOffset); |
| checkTypeVariables(typeVariables, supertype.builder); |
| - decl.resolveTypes(typeVariables, this); |
| } |
| void addField(List<MetadataBuilder> metadata, int modifiers, |
| @@ -634,9 +845,16 @@ class KernelLibraryBuilder |
| @override |
| void includePart(covariant KernelLibraryBuilder part) { |
| + part.mixinApplicationClasses |
| + .forEach((String name, SourceClassBuilder builder) { |
| + SourceClassBuilder existing = |
| + mixinApplicationClasses.putIfAbsent(name, () => builder); |
| + if (existing != builder) { |
| + part.scope.local.remove(name); |
| + } |
| + }); |
| super.includePart(part); |
| nativeMethods.addAll(part.nativeMethods); |
| boundlessTypeVariables.addAll(part.boundlessTypeVariables); |
| - assert(mixinApplicationClasses.isEmpty); |
| } |
| } |