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

Unified Diff: pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart

Issue 2862223002: Rewrite mixin application handling in Fasta. (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
}
}

Powered by Google App Engine
This is Rietveld 408576698