b0bfbadbc3
When a class inherits two abstract members with the same name from different supertypes, both members are now present the list returned by ClassHierarchy.getInterfaceMembers. Previously, only one of the member would be present. This ensures that all override pairs can be detected, which in turn is necessary for inserting covariance checks in strong mode. Another change in this CL is that interface members are built eagerly instead of on-demand. This makes the ClassHierarchy more reliable when used for transformation, and easier to benchmark. BUG= R=kmillikin@google.com Review URL: https://chromereviews.googleplex.com/520907013 .
252 lines
9.2 KiB
Dart
252 lines
9.2 KiB
Dart
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
library kernel.class_hierarchy_basic;
|
|
|
|
import 'package:kernel/class_hierarchy.dart';
|
|
import 'package:kernel/type_algebra.dart';
|
|
import 'package:kernel/ast.dart';
|
|
|
|
/// A simple implementation of the class hierarchy interface using
|
|
/// hash tables for everything.
|
|
class BasicClassHierarchy implements ClassHierarchy {
|
|
final Map<Class, Set<Class>> superclasses = <Class, Set<Class>>{};
|
|
final Map<Class, Set<Class>> superMixtures = <Class, Set<Class>>{};
|
|
final Map<Class, Set<Class>> supertypes = <Class, Set<Class>>{};
|
|
final Map<Class, Map<Class, InterfaceType>> supertypeInstantiations =
|
|
<Class, Map<Class, InterfaceType>>{};
|
|
final Map<Class, Map<Name, Member>> gettersAndCalls =
|
|
<Class, Map<Name, Member>>{};
|
|
final Map<Class, Map<Name, Member>> setters = <Class, Map<Name, Member>>{};
|
|
final Map<Class, Map<Name, List<Member>>> interfaceGettersAndCalls =
|
|
<Class, Map<Name, List<Member>>>{};
|
|
final Map<Class, Map<Name, List<Member>>> interfaceSetters =
|
|
<Class, Map<Name, List<Member>>>{};
|
|
final List<Class> classes = <Class>[];
|
|
final Map<Class, int> classIndex = <Class, int>{};
|
|
|
|
BasicClassHierarchy(Program program) {
|
|
for (var library in program.libraries) {
|
|
for (var classNode in library.classes) {
|
|
buildSuperTypeSets(classNode);
|
|
buildSuperTypeInstantiations(classNode);
|
|
buildDispatchTable(classNode);
|
|
buildInterfaceTable(classNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
void forEachOverridePair(
|
|
Class class_, callback(Member member, Member superMember, bool setter)) {
|
|
void report(Member member, Member superMember, bool setter) {
|
|
if (!identical(member, superMember)) {
|
|
callback(member, superMember, setter);
|
|
}
|
|
}
|
|
// Report declared members overriding inheritable members.
|
|
for (var member in class_.mixin.members) {
|
|
for (var supertype in class_.supers) {
|
|
if (member.hasGetter) {
|
|
for (var superMember
|
|
in getInterfaceMembersByName(supertype.classNode, member.name)) {
|
|
report(member, superMember, false);
|
|
}
|
|
}
|
|
if (member.hasSetter) {
|
|
for (var superMember in getInterfaceMembersByName(
|
|
supertype.classNode, member.name,
|
|
setter: true)) {
|
|
report(member, superMember, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Report inherited non-abstract members overriding inheritable or
|
|
// declared members.
|
|
for (var setter in [true, false]) {
|
|
for (var member in getDispatchTargets(class_, setters: setter)) {
|
|
// Report overriding inheritable members.
|
|
for (var supertype in class_.supers) {
|
|
for (var superMember in getInterfaceMembersByName(
|
|
supertype.classNode, member.name,
|
|
setter: setter)) {
|
|
report(member, superMember, setter);
|
|
}
|
|
}
|
|
// Report overriding declared abstract members.
|
|
if (!class_.isAbstract && member.enclosingClass != class_.mixin) {
|
|
for (var declaredMember in getInterfaceMembersByName(
|
|
class_, member.name,
|
|
setter: setter)) {
|
|
report(member, declaredMember, setter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void buildSuperTypeSets(Class node) {
|
|
if (superclasses.containsKey(node)) return;
|
|
superclasses[node] = new Set<Class>()..add(node);
|
|
superMixtures[node] = new Set<Class>()..add(node);
|
|
supertypes[node] = new Set<Class>()..add(node);
|
|
if (node.supertype != null) {
|
|
buildSuperTypeSets(node.supertype.classNode);
|
|
superclasses[node].addAll(superclasses[node.supertype.classNode]);
|
|
superMixtures[node].addAll(superMixtures[node.supertype.classNode]);
|
|
supertypes[node].addAll(supertypes[node.supertype.classNode]);
|
|
}
|
|
if (node.mixedInType != null) {
|
|
buildSuperTypeSets(node.mixedInType.classNode);
|
|
superMixtures[node].addAll(superMixtures[node.mixedInType.classNode]);
|
|
supertypes[node].addAll(supertypes[node.mixedInType.classNode]);
|
|
}
|
|
for (var supertype in node.implementedTypes) {
|
|
buildSuperTypeSets(supertype.classNode);
|
|
supertypes[node].addAll(supertypes[supertype.classNode]);
|
|
}
|
|
classes.add(node);
|
|
classIndex[node] = classes.length - 1;
|
|
}
|
|
|
|
void buildSuperTypeInstantiations(Class node) {
|
|
if (supertypeInstantiations.containsKey(node)) return;
|
|
supertypeInstantiations[node] = <Class, InterfaceType>{node: node.thisType};
|
|
for (var supertype in node.supers) {
|
|
var superclass = supertype.classNode;
|
|
buildSuperTypeInstantiations(superclass);
|
|
var substitution = new Map<TypeParameter, DartType>.fromIterables(
|
|
superclass.typeParameters, supertype.typeArguments);
|
|
supertypeInstantiations[superclass].forEach((key, type) {
|
|
supertypeInstantiations[node][key] = substitute(type, substitution);
|
|
});
|
|
}
|
|
}
|
|
|
|
void buildDispatchTable(Class node) {
|
|
if (gettersAndCalls.containsKey(node)) return;
|
|
gettersAndCalls[node] = <Name, Member>{};
|
|
setters[node] = <Name, Member>{};
|
|
if (node.supertype != null) {
|
|
buildDispatchTable(node.supertype.classNode);
|
|
gettersAndCalls[node].addAll(gettersAndCalls[node.supertype.classNode]);
|
|
setters[node].addAll(setters[node.supertype.classNode]);
|
|
}
|
|
// Overwrite map entries with declared members.
|
|
Class mixin = node.mixedInType?.classNode ?? node;
|
|
for (Procedure procedure in mixin.procedures) {
|
|
if (procedure.isStatic || procedure.isAbstract) continue;
|
|
if (procedure.kind == ProcedureKind.Setter) {
|
|
setters[node][procedure.name] = procedure;
|
|
} else {
|
|
gettersAndCalls[node][procedure.name] = procedure;
|
|
}
|
|
}
|
|
for (Field field in mixin.fields) {
|
|
if (field.isStatic) continue;
|
|
gettersAndCalls[node][field.name] = field;
|
|
if (!field.isFinal) {
|
|
setters[node][field.name] = field;
|
|
}
|
|
}
|
|
}
|
|
|
|
void mergeMaps(
|
|
Map<Name, List<Member>> source, Map<Name, List<Member>> destination) {
|
|
for (var name in source.keys) {
|
|
destination.putIfAbsent(name, () => <Member>[]).addAll(source[name]);
|
|
}
|
|
}
|
|
|
|
void buildInterfaceTable(Class node) {
|
|
if (interfaceGettersAndCalls.containsKey(node)) return;
|
|
interfaceGettersAndCalls[node] = <Name, List<Member>>{};
|
|
interfaceSetters[node] = <Name, List<Member>>{};
|
|
void inheritFrom(InterfaceType type) {
|
|
if (type == null) return;
|
|
buildInterfaceTable(type.classNode);
|
|
mergeMaps(interfaceGettersAndCalls[type.classNode],
|
|
interfaceGettersAndCalls[node]);
|
|
mergeMaps(interfaceSetters[type.classNode], interfaceSetters[node]);
|
|
}
|
|
inheritFrom(node.supertype);
|
|
inheritFrom(node.mixedInType);
|
|
node.implementedTypes.forEach(inheritFrom);
|
|
// Overwrite map entries with declared members.
|
|
for (Procedure procedure in node.mixin.procedures) {
|
|
if (procedure.isStatic) continue;
|
|
if (procedure.kind == ProcedureKind.Setter) {
|
|
interfaceSetters[node][procedure.name] = <Member>[procedure];
|
|
} else {
|
|
interfaceGettersAndCalls[node][procedure.name] = <Member>[procedure];
|
|
}
|
|
}
|
|
for (Field field in node.mixin.fields) {
|
|
if (field.isStatic) continue;
|
|
interfaceGettersAndCalls[node][field.name] = <Member>[field];
|
|
if (!field.isFinal) {
|
|
interfaceSetters[node][field.name] = <Member>[field];
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isSubclassOf(Class subtype, Class supertype) {
|
|
return superclasses[subtype].contains(supertype);
|
|
}
|
|
|
|
bool isSubmixtureOf(Class subtype, Class supertype) {
|
|
return superMixtures[subtype].contains(supertype);
|
|
}
|
|
|
|
bool isSubtypeOf(Class subtype, Class supertype) {
|
|
return supertypes[subtype].contains(supertype);
|
|
}
|
|
|
|
InterfaceType getClassAsInstanceOf(Class type, Class supertype) {
|
|
return supertypeInstantiations[type][supertype];
|
|
}
|
|
|
|
Member getDispatchTarget(Class class_, Name name, {bool setter: false}) {
|
|
return setter ? setters[class_][name] : gettersAndCalls[class_][name];
|
|
}
|
|
|
|
Iterable<Member> getDispatchTargets(Class class_, {bool setters: false}) {
|
|
return setters
|
|
? this.setters[class_].values
|
|
: gettersAndCalls[class_].values;
|
|
}
|
|
|
|
Member tryFirst(List<Member> members) {
|
|
return (members == null || members.isEmpty) ? null : members[0];
|
|
}
|
|
|
|
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
|
|
return tryFirst(getInterfaceMembersByName(class_, name, setter: setter));
|
|
}
|
|
|
|
Iterable<Member> getInterfaceMembersByName(Class class_, Name name,
|
|
{bool setter: false}) {
|
|
var iterable = setter
|
|
? interfaceSetters[class_][name]
|
|
: interfaceGettersAndCalls[class_][name];
|
|
return iterable == null ? const <Member>[] : iterable;
|
|
}
|
|
|
|
Iterable<Member> getInterfaceMembers(Class class_, {bool setters: false}) {
|
|
return setters
|
|
? interfaceSetters[class_].values.expand((x) => x)
|
|
: interfaceGettersAndCalls[class_].values.expand((x) => x);
|
|
}
|
|
|
|
int getClassIndex(Class node) {
|
|
return classIndex[node];
|
|
}
|
|
|
|
List<int> getExpenseHistogram() => <int>[];
|
|
double getCompressionRatio() => 0.0;
|
|
int getSuperTypeHashTableSize() => 0;
|
|
|
|
noSuchMethod(inv) => super.noSuchMethod(inv);
|
|
}
|