blob: 7bb0462c47a8e18a003ea1164b03625f7980bfc4 [file] [log] [blame]
// 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.
import 'package:kernel/kernel.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/core_types.dart';
import 'package:test/test.dart';
import 'class_hierarchy_basic.dart';
import 'dart:io';
import 'dart:math';
import 'self_check_util.dart';
main(List<String> args) {
runSelfCheck(args, (String filename) {
testClassHierarchyOnComponent(loadComponentFromBinary(filename));
});
}
void testClassHierarchyOnComponent(Component component, {bool verbose: false}) {
BasicClassHierarchy basic = new BasicClassHierarchy(component);
CoreTypes coreTypes = new CoreTypes(component);
ClosedWorldClassHierarchy classHierarchy =
new ClassHierarchy(component, coreTypes);
int total = classHierarchy.numberOfClasses;
int progress = 0;
for (var class1 in classHierarchy.classes) {
for (var class2 in classHierarchy.classes) {
bool isSubclass = classHierarchy.isSubclassOf(class1, class2);
bool isSubtype = classHierarchy.isSubtypeOf(class1, class2);
var asInstance = classHierarchy.getClassAsInstanceOf(class1, class2);
if (isSubclass != basic.isSubclassOf(class1, class2)) {
fail('isSubclassOf(${class1.name}, ${class2.name}) returned '
'$isSubclass but should be ${!isSubclass}');
}
if (isSubtype != basic.isSubtypeOf(class1, class2)) {
fail('isSubtypeOf(${class1.name}, ${class2.name}) returned '
'$isSubtype but should be ${!isSubtype}');
}
if (asInstance != basic.getClassAsInstanceOf(class1, class2)) {
fail('asInstanceOf(${class1.name}, ${class2.name}) returned '
'$asInstance but should be '
'${basic.getClassAsInstanceOf(class1, class2)}');
}
}
++progress;
if (verbose) {
stdout.write('\rSubclass queries ${100 * progress ~/ total}%');
}
}
Set<Name> names = new Set<Name>();
for (var classNode in classHierarchy.classes) {
for (var member in classNode.members) {
names.add(member.name);
}
}
List<Name> nameList = names.toList();
progress = 0;
for (var classNode in classHierarchy.classes) {
Iterable<Name> candidateNames = <Iterable<Name>>[
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);
if (actualGetter != expectedGetter) {
fail('lookupGetter($classNode, $name) returned '
'$actualGetter but should be $expectedGetter');
}
if (actualSetter != expectedSetter) {
fail('lookupSetter($classNode, $name) returned '
'$actualSetter but should be $expectedSetter');
}
}
++progress;
if (verbose) {
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}%');
}
}
for (var classNode in classHierarchy.classes) {
String getHash(member, superMember, setter) {
String eq = setter ? '=' : '';
return '$member$eq overrides $superMember$eq';
}
Set<String> expectedOverrides = new Set<String>();
basic.forEachOverridePair(classNode, (member, superMember, setter) {
expectedOverrides.add(getHash(member, superMember, setter));
});
Set<String> actualOverrides = new Set<String>();
classHierarchy.forEachOverridePair(classNode,
(member, superMember, setter) {
actualOverrides.add(getHash(member, superMember, setter));
});
for (var actual in actualOverrides) {
if (!expectedOverrides.contains(actual)) {
fail("forEachOverridePair($classNode) should not report that $actual");
}
}
for (var expected in expectedOverrides) {
if (!actualOverrides.contains(expected)) {
fail("forEachOverridePair($classNode) did not report that $expected");
}
}
}
if (verbose) {
print('\rProgress 100%. Done.');
}
}
var random = new Random(12345);
List<T> pickRandom<T>(List<T> items, int n) {
var result = <T>[];
for (int i = 0; i < n; ++i) {
result.add(items[random.nextInt(items.length)]);
}
return result;
}