[kernel] Support interface member resolution in class hierarchy.

This is useful for handling of externals in tree shaking and type
propagation.

BUG=
R=ahe@google.com

Review URL: https://chromereviews.googleplex.com/435947013 .
This commit is contained in:
Asger Feldthaus
2016-05-27 11:25:00 +02:00
parent c5649381a8
commit b61129ee84
4 changed files with 215 additions and 36 deletions
+74 -16
View File
@@ -62,24 +62,23 @@ class ClassHierarchy {
return _infoFor[class_].directImplementers.isNotEmpty;
}
/// Returns the instantiation of [supertype] that is implemented by [type],
/// or `null` if [type] does not implement [supertype] at all.
InterfaceType getClassAsInstanceOf(Class type, Class supertype) {
if (identical(type, supertype)) return type.thisType;
_ClassInfo info = _infoFor[type];
_ClassInfo superInfo = _infoFor[supertype];
/// Returns the instantiation of [superclass] that is implemented by [class_],
/// or `null` if [class_] does not implement [superclass] at all.
InterfaceType getClassAsInstanceOf(Class class_, Class superclass) {
if (identical(class_, superclass)) return class_.thisType;
_ClassInfo info = _infoFor[class_];
_ClassInfo superInfo = _infoFor[superclass];
if (!info.isSubtypeOf(superInfo)) return null;
if (supertype.typeParameters.isEmpty) return supertype.rawType;
return info.genericSuperTypes[supertype];
if (superclass.typeParameters.isEmpty) return superclass.rawType;
return info.genericSuperTypes[superclass];
}
/// Returns the instantiation of [supertype] that is implemented by [type],
/// or `null` if [type] does not implement [supertype] at all.
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class supertype) {
InterfaceType castedType = getClassAsInstanceOf(type.classNode, supertype);
/// Returns the instantiation of [superclass] that is implemented by [type],
/// or `null` if [type] does not implement [superclass] at all.
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass) {
InterfaceType castedType = getClassAsInstanceOf(type.classNode, superclass);
if (castedType == null) return null;
return substitutePairwise(
castedType, type.classNode.typeParameters, type.typeArguments);
return substituteThisType(castedType, type);
}
/// Returns the instance member that would respond to a dynamic dispatch of
@@ -117,6 +116,28 @@ class ClassHierarchy {
return setters ? info.implementedSetters : info.implementedGettersAndCalls;
}
/// Returns the possibly abstract interface member of [class_] with the given
/// [name].
///
/// If [setters] is `false`, only fields, methods, and getters with that name
/// will be found. If [setters] is `true`, only non-final fields and setters
/// will be found.
///
/// If multiple members with that name are inherited and not overidden, the
/// member from the first declared supertype is returned.
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
List<Member> list = getInterfaceMembers(class_, setters: setter);
return _findMemberByName(list, name);
}
/// Returns the list of members denoting the interface for [class_], which
/// may include abstract members.
///
/// See [getInterfaceMember].
List<Member> getInterfaceMembers(Class class_, {bool setters: false}) {
return _buildInterfaceMembers(class_, _infoFor[class_], setters: setters);
}
ClassHierarchy._internal(Program program, int numberOfClasses)
: classes = new List<Class>(numberOfClasses) {
// Build the class ordering based on a topological sort.
@@ -224,13 +245,47 @@ class ClassHierarchy {
_inheritMembers(info.declaredSetters, inheritedSetters);
}
List<Member> _buildInterfaceMembers(Class classNode, _ClassInfo info,
{bool setters}) {
List<Member> members =
setters ? info.interfaceSetters : info.interfaceGettersAndCalls;
if (members != null) return members;
members = <Member>[];
for (Procedure member in classNode.mixin.procedures) {
if (member.isStatic) continue;
if (setters != member.isSetter) continue;
members.add(member);
}
for (Field member in classNode.mixin.fields) {
if (member.isStatic) continue;
if (setters && member.isFinal) continue;
members.add(member);
}
members.sort(_compareMembers);
void inheritFrom(InterfaceType type) {
if (type == null) return;
List<Member> inherited = _buildInterfaceMembers(type.classNode,
_infoFor[type.classNode], setters: setters);
members = _inheritMembers(members, inherited);
}
inheritFrom(classNode.supertype);
inheritFrom(classNode.mixedInType);
classNode.implementedTypes.forEach(inheritFrom);
if (setters) {
info.interfaceSetters = members;
} else {
info.interfaceGettersAndCalls = members;
}
return members;
}
/// Computes the list of implemented members, based on the declared instance
/// members and inherited instance members.
///
/// Both lists are sorted by name beforehand.
/// Both lists must be sorted by name beforehand.
List<Member> _inheritMembers(List<Member> declared, List<Member> inherited) {
List<Member> result = <Member>[]
..length = declared.length + inherited.length;
..length = declared.length + inherited.length;
// Since both lists are sorted, we can fuse them like in merge sort.
int storeIndex = 0;
int i = 0, j = 0;
@@ -607,5 +662,8 @@ class _ClassInfo {
/// (declared or inherited).
List<Member> implementedSetters;
List<Member> interfaceGettersAndCalls;
List<Member> interfaceSetters;
_ClassInfo(this.classNode);
}
@@ -19,6 +19,10 @@ class BasicClassHierarchy implements ClassHierarchy {
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, Member>> interfaceGettersAndCalls =
<Class, Map<Name, Member>>{};
final Map<Class, Map<Name, Member>> interfaceSetters =
<Class, Map<Name, Member>>{};
final List<Class> classes = <Class>[];
final Map<Class, int> classIndex = <Class, int>{};
@@ -28,6 +32,7 @@ class BasicClassHierarchy implements ClassHierarchy {
buildSuperTypeSets(classNode);
buildSuperTypeInstantiations(classNode);
buildDispatchTable(classNode);
buildInterfaceTable(classNode);
}
}
}
@@ -98,6 +103,38 @@ class BasicClassHierarchy implements ClassHierarchy {
}
}
void buildInterfaceTable(Class node) {
if (interfaceGettersAndCalls.containsKey(node)) return;
interfaceGettersAndCalls[node] = <Name, Member>{};
interfaceSetters[node] = <Name, Member>{};
void inheritFrom(InterfaceType type) {
if (type == null) return;
buildInterfaceTable(type.classNode);
interfaceGettersAndCalls[node]
.addAll(interfaceGettersAndCalls[type.classNode]);
interfaceSetters[node].addAll(interfaceSetters[type.classNode]);
}
node.implementedTypes.reversed.forEach(inheritFrom);
inheritFrom(node.mixedInType);
inheritFrom(node.supertype);
// 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] = procedure;
} else {
interfaceGettersAndCalls[node][procedure.name] = procedure;
}
}
for (Field field in node.mixin.fields) {
if (field.isStatic) continue;
interfaceGettersAndCalls[node][field.name] = field;
if (!field.isFinal) {
interfaceSetters[node][field.name] = field;
}
}
}
bool isSubclassOf(Class subtype, Class supertype) {
return superclasses[subtype].contains(supertype);
}
@@ -124,6 +161,18 @@ class BasicClassHierarchy implements ClassHierarchy {
: gettersAndCalls[class_].values;
}
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
return setter
? interfaceSetters[class_][name]
: interfaceGettersAndCalls[class_][name];
}
Iterable<Member> getInterfaceMembers(Class class_, {bool setters: false}) {
return setters
? interfaceSetters[class_].values
: interfaceGettersAndCalls[class_].values;
}
int getClassIndex(Class node) {
return classIndex[node];
}
+52 -11
View File
@@ -11,9 +11,10 @@ import 'dart:io';
ArgParser argParser = new ArgParser()
..addFlag('basic', help: 'Measure the basic implementation', negatable: false)
..addOption('cycle', abbr: 'c',
..addOption('cycle',
abbr: 'c',
help: 'Build N copies of the class hierarchy and cycle queries '
'between them',
'between them',
defaultsTo: '1');
String usage = '''
@@ -175,11 +176,38 @@ main(List<String> args) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
for (var _ in classHierarchy.getDispatchTargets(classNode)) {
}
for (var _ in classHierarchy.getDispatchTargets(classNode)) {}
}
int dispatchAllTargetsTime = watch.elapsedMicroseconds;
// Measure getInterfaceMember and getInterfaceMembers.
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
classHierarchy.getInterfaceMember(classNode, new Name('toString'));
}
int interfaceToStringTime = watch.elapsedMicroseconds;
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
classHierarchy.getInterfaceMember(classNode, new Name('getFloo'));
}
int interfaceGenericGetTime = watch.elapsedMicroseconds;
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
var classHierarchy = getClassHierarchy();
int classId = rnd.nextInt(classHierarchy.classes.length);
Class classNode = classHierarchy.classes[classId];
for (var _ in classHierarchy.getInterfaceMembers(classNode)) {}
}
int interfaceAllTargetsTime = watch.elapsedMicroseconds;
// Estimate overhead from test case generation.
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
@@ -192,6 +220,9 @@ main(List<String> args) {
dispatchToStringTime -= dispatchTargetNoise;
dispatchGenericGetTime -= dispatchTargetNoise;
dispatchAllTargetsTime -= dispatchTargetNoise;
interfaceToStringTime -= dispatchTargetNoise;
interfaceGenericGetTime -= dispatchTargetNoise;
interfaceAllTargetsTime -= dispatchTargetNoise;
String dispatchToStringPerSecond =
perSecond(dispatchToStringTime, numQueryTrials);
@@ -200,6 +231,13 @@ main(List<String> args) {
String dispatchAllTargetsPerSecond =
perSecond(dispatchAllTargetsTime, numQueryTrials);
String interfaceToStringPerSecond =
perSecond(interfaceToStringTime, numQueryTrials);
String interfaceGetPerSecond =
perSecond(interfaceGenericGetTime, numQueryTrials);
String interfaceAllTargetsPerSecond =
perSecond(interfaceAllTargetsTime, numQueryTrials);
var classHierarchy = getClassHierarchy();
List<int> depth = new List(classHierarchy.classes.length);
for (int i = 0; i < depth.length; ++i) {
@@ -229,13 +267,16 @@ main(List<String> args) {
classes: $numberOfClasses
build.cold: $coldBuildTime ms
build.hot: $hotBuildTime ms
query.isSubclassOf: $subclassPerSecond
query.isSubmixtureOf: $submixturePerSecond
query.isSubtypeOf: $subtypePerSecond
query.getClassAsInstanceOf: $asInstanceOfPerSecond
query.getDispatchTarget(toString): $dispatchToStringPerSecond
query.getDispatchTarget(getFloo): $dispatchGetPerSecond
query.getDispatchTargets.iterate: $dispatchAllTargetsPerSecond
query.isSubclassOf: $subclassPerSecond
query.isSubmixtureOf: $submixturePerSecond
query.isSubtypeOf: $subtypePerSecond
query.getClassAsInstanceOf: $asInstanceOfPerSecond
query.getDispatchTarget(toString): $dispatchToStringPerSecond
query.getDispatchTarget(getFloo): $dispatchGetPerSecond
query.getDispatchTargets.iterate: $dispatchAllTargetsPerSecond
query.getInterfaceMember(toString): $interfaceToStringPerSecond
query.getInterfaceMember(getFloo): $interfaceGetPerSecond
query.getInterfaceMembers.iterate: $interfaceAllTargetsPerSecond
isSubtypeOf.expense-histogram: $expenseHistogram
isSubtypeOf.compression-ratio: ${classHierarchy.getCompressionRatio()}
asInstanceOf.table-size: ${classHierarchy.getSuperTypeHashTableSize()}
+40 -9
View File
@@ -61,19 +61,20 @@ void testClassHierarchyOnProgram(Program program, {bool verbose: false}) {
List<Name> nameList = names.toList();
progress = 0;
for (var classNode in classHierarchy.classes) {
Iterable<Name> candidateNames =
[basic.gettersAndCalls[classNode].keys,
basic.setters[classNode].keys,
pickRandom(nameList, 100)].expand((x) => x);
Iterable<Name> candidateNames = [
basic.gettersAndCalls[classNode].keys,
basic.setters[classNode].keys,
pickRandom(nameList, 100)
].expand((x) => x);
for (Name name in candidateNames) {
Member expectedGetter =
basic.getDispatchTarget(classNode, name, setter: false);
Member expectedSetter =
basic.getDispatchTarget(classNode, name, setter: true);
Member actualGetter = classHierarchy
.getDispatchTarget(classNode, name, setter: false);
Member actualSetter = classHierarchy
.getDispatchTarget(classNode, name, setter: true);
Member actualGetter =
classHierarchy.getDispatchTarget(classNode, name, setter: false);
Member actualSetter =
classHierarchy.getDispatchTarget(classNode, name, setter: true);
if (actualGetter != expectedGetter) {
fail('lookupGetter($classNode, $name) returned '
'$actualGetter but should be $expectedGetter');
@@ -85,7 +86,37 @@ void testClassHierarchyOnProgram(Program program, {bool verbose: false}) {
}
++progress;
if (verbose) {
stdout.write('\rEnvironment queries ${100 * progress ~/ total}%');
stdout.write('\rDispatch queries ${100 * progress ~/ total}%');
}
}
progress = 0;
for (var classNode in classHierarchy.classes) {
Iterable<Name> candidateNames = [
basic.interfaceGettersAndCalls[classNode].keys,
basic.interfaceSetters[classNode].keys,
pickRandom(nameList, 100)
].expand((x) => x);
for (Name name in candidateNames) {
Member expectedGetter =
basic.getInterfaceMember(classNode, name, setter: false);
Member expectedSetter =
basic.getInterfaceMember(classNode, name, setter: true);
Member actualGetter =
classHierarchy.getInterfaceMember(classNode, name, setter: false);
Member actualSetter =
classHierarchy.getInterfaceMember(classNode, name, setter: true);
if (actualGetter != expectedGetter) {
fail('getInterfaceMember($classNode, $name) returned '
'$actualGetter but should be $expectedGetter');
}
if (actualSetter != expectedSetter) {
fail('getInterfaceMember($classNode, $name, setter: true) '
'returned $actualSetter but should be $expectedSetter');
}
}
++progress;
if (verbose) {
stdout.write('\rInterface queries ${100 * progress ~/ total}%');
}
}
if (verbose) {