blob: 9be7acd6afeb2e6d01aba57d8cc9888fc9193445 [file] [log] [blame]
// Copyright (c) 2018, 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 'dart:core' hide Type;
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart';
import 'package:kernel/target/targets.dart' show Target, TargetFlags;
import 'package:kernel/testing/mock_sdk_component.dart';
import 'package:test/test.dart';
import 'package:vm/modular/target/vm.dart';
import 'package:vm/transformations/type_flow/types.dart';
class TestTypeHierarchy extends TypeHierarchy {
final Map<Class, TFClass> classes;
final Map<Class, Type> specializations;
int classIdCounter = 0;
TestTypeHierarchy(
CoreTypes coreTypes, Target target, this.classes, this.specializations)
: super(coreTypes, target, /*soundNullSafety=*/ true);
@override
Type specializeTypeCone(TFClass base, {bool allowWideCone = false}) {
return specializations[base.classNode]!;
}
@override
bool hasAllocatedSubtypes(TFClass cls) =>
specializeTypeCone(cls) is! EmptyType;
@override
TFClass getTFClass(Class c) =>
classes[c] ??= TFClass(++classIdCounter, c, {}, null);
@override
List<DartType> flattenedTypeArgumentsFor(Class klass) =>
throw "flattenedTypeArgumentsFor is not supported in the types test.";
@override
int genericInterfaceOffsetFor(Class klass, Class iface) =>
throw "genericInterfaceOffsetFor is not supported in the types test.";
@override
List<Type> flattenedTypeArgumentsForNonGeneric(Class klass) =>
throw "flattenedTypeArgumentsFor is not supported in the types test.";
}
class TestingTarget extends VmTarget {
TestingTarget() : super(TargetFlags());
// Mock SDK doesn't have _Closure class, use Function.
@override
Class concreteClosureClass(CoreTypes coreTypes) => coreTypes.functionClass;
}
main() {
final Component component = createMockSdkComponent();
final CoreTypes coreTypes = CoreTypes(component);
final Target target = TestingTarget();
test('types-builder', () {
final Class c1 = new Class(name: 'C1', fileUri: dummyUri);
final Class c2 = new Class(
name: 'C2',
typeParameters: [new TypeParameter('E')],
fileUri: dummyUri);
final TypesBuilder tb = new TestTypeHierarchy(coreTypes, target, {}, {});
final tfc1 = tb.getTFClass(c1);
final tfc2 = tb.getTFClass(c2);
final tfFunction = tb.getTFClass(coreTypes.functionClass);
final InterfaceType t1 = new InterfaceType(c1, Nullability.legacy);
final InterfaceType t2Raw = new InterfaceType(c2, Nullability.legacy);
final InterfaceType t2Generic =
new InterfaceType(c2, Nullability.legacy, [t1]);
final DartType t3 = const NullType();
final FunctionType f1 =
new FunctionType([t1], const VoidType(), Nullability.legacy);
expect(tb.fromStaticType(const NeverType.nonNullable(), false),
equals(emptyType));
expect(
tb.fromStaticType(const DynamicType(), true), equals(nullableAnyType));
expect(tb.fromStaticType(const VoidType(), true), equals(nullableAnyType));
expect(tb.fromStaticType(t1, false), equals(tfc1.coneType));
expect(tb.fromStaticType(t2Raw, false), equals(tfc2.coneType));
expect(tb.fromStaticType(t2Generic, false), equals(tfc2.coneType));
expect(tb.fromStaticType(t3, false), equals(emptyType));
expect(tb.fromStaticType(f1, false), equals(tfFunction.coneType));
expect(tb.fromStaticType(t1, true), equals(tfc1.coneType.nullable()));
expect(tb.fromStaticType(t2Raw, true), equals(tfc2.coneType.nullable()));
expect(
tb.fromStaticType(t2Generic, true), equals(tfc2.coneType.nullable()));
expect(tb.fromStaticType(t3, true), equals(nullableEmptyType));
expect(tb.fromStaticType(f1, true), equals(tfFunction.coneType.nullable()));
expect(nullableAnyType, equals(nullableAnyType));
});
test('union-intersection', () {
// T1 <: T3, T2 <: T3
final c1 = Class(name: 'T1', fileUri: dummyUri)..parent = dummyLibrary;
final c2 = Class(name: 'T2', fileUri: dummyUri)..parent = dummyLibrary;
final c3 = Class(name: 'T3', fileUri: dummyUri)..parent = dummyLibrary;
final c4 = Class(name: 'T4', fileUri: dummyUri)..parent = dummyLibrary;
final tfc3 = TFClass(3, c3, {}, null);
final tfc1 = TFClass(1, c1, {tfc3}, null);
final tfc2 = TFClass(2, c2, {tfc3}, null);
final tfc4 = TFClass(4, c4, {}, null);
final empty = emptyType;
final any = anyInstanceType;
final concreteT1 = tfc1.concreteType;
final concreteT2 = tfc2.concreteType;
final concreteT3 = tfc3.concreteType;
final concreteT4 = tfc4.concreteType;
final coneT1 = tfc1.coneType;
final coneT2 = tfc2.coneType;
final coneT3 = tfc3.coneType;
final coneT4 = tfc4.coneType;
final setT12 = SetType([concreteT1, concreteT2]);
final setT14 = SetType([concreteT1, concreteT4]);
final setT23 = SetType([concreteT2, concreteT3]);
final setT34 = SetType([concreteT3, concreteT4]);
final setT123 = SetType([concreteT1, concreteT2, concreteT3]);
final setT124 = SetType([concreteT1, concreteT2, concreteT4]);
final setT1234 = SetType([concreteT1, concreteT2, concreteT3, concreteT4]);
final nullableEmpty = empty.nullable();
final nullableAny = any.nullable();
final nullableConcreteT1 = concreteT1.nullable();
final nullableConcreteT2 = concreteT2.nullable();
final nullableConcreteT3 = concreteT3.nullable();
final nullableConeT1 = coneT1.nullable();
final nullableConeT3 = coneT3.nullable();
final nullableConeT4 = coneT4.nullable();
final nullableSetT12 = setT12.nullable();
final nullableSetT14 = setT14.nullable();
final nullableSetT23 = setT23.nullable();
final nullableSetT34 = setT34.nullable();
final nullableSetT123 = setT123.nullable();
final nullableSetT124 = setT124.nullable();
final nullableSetT1234 = setT1234.nullable();
// [A, B, union, intersection]
final testCases = [
// empty
[empty, empty, empty, empty],
[empty, any, any, empty],
[empty, concreteT1, concreteT1, empty],
[empty, coneT1, coneT1, empty],
[empty, setT12, setT12, empty],
[empty, nullableEmpty, nullableEmpty, empty],
[empty, nullableAny, nullableAny, empty],
[empty, nullableConcreteT1, nullableConcreteT1, empty],
[empty, nullableConeT1, nullableConeT1, empty],
[empty, nullableSetT12, nullableSetT12, empty],
// any
[any, any, any, any],
[any, concreteT1, any, concreteT1],
[any, coneT1, any, coneT1],
[any, setT12, any, setT12],
[any, nullableEmpty, nullableAny, empty],
[any, nullableAny, nullableAny, any],
[any, nullableConcreteT1, nullableAny, concreteT1],
[any, nullableConeT1, nullableAny, coneT1],
[any, nullableSetT12, nullableAny, setT12],
// nullableEmpty
[nullableEmpty, concreteT1, nullableConcreteT1, empty],
[nullableEmpty, coneT1, nullableConeT1, empty],
[nullableEmpty, setT12, nullableSetT12, empty],
[nullableEmpty, nullableEmpty, nullableEmpty, nullableEmpty],
[nullableEmpty, nullableAny, nullableAny, nullableEmpty],
[nullableEmpty, nullableConcreteT1, nullableConcreteT1, nullableEmpty],
[nullableEmpty, nullableConeT1, nullableConeT1, nullableEmpty],
[nullableEmpty, nullableSetT12, nullableSetT12, nullableEmpty],
// nullableAny
[nullableAny, concreteT1, nullableAny, concreteT1],
[nullableAny, coneT1, nullableAny, coneT1],
[nullableAny, setT12, nullableAny, setT12],
[nullableAny, nullableAny, nullableAny, nullableAny],
[nullableAny, nullableConcreteT1, nullableAny, nullableConcreteT1],
[nullableAny, nullableConeT1, nullableAny, nullableConeT1],
[nullableAny, nullableSetT12, nullableAny, nullableSetT12],
// concrete
[concreteT1, concreteT1, concreteT1, concreteT1],
[concreteT1, concreteT2, setT12, empty],
[concreteT1, coneT1, coneT1, concreteT1],
[concreteT1, coneT2, setT12, empty],
[concreteT1, coneT3, coneT3, concreteT1],
[concreteT1, coneT4, setT14, empty],
[concreteT1, setT12, setT12, concreteT1],
[concreteT1, setT23, setT123, empty],
[concreteT1, nullableConcreteT1, nullableConcreteT1, concreteT1],
[concreteT1, nullableConcreteT2, nullableSetT12, empty],
[concreteT1, nullableConeT1, nullableConeT1, concreteT1],
[concreteT1, nullableConeT3, nullableConeT3, concreteT1],
[concreteT1, nullableConeT4, nullableSetT14, empty],
[concreteT1, nullableSetT12, nullableSetT12, concreteT1],
[concreteT1, nullableSetT23, nullableSetT123, empty],
// cone
[coneT1, coneT1, coneT1, coneT1],
[coneT1, coneT2, setT12, empty],
[coneT1, coneT3, coneT3, coneT1],
[coneT3, coneT4, setT1234, empty],
[coneT1, setT12, setT12, concreteT1],
[coneT1, setT23, setT123, empty],
[coneT3, setT12, setT123, setT12],
[coneT3, setT1234, setT1234, setT123],
[coneT1, nullableConcreteT1, nullableConeT1, concreteT1],
[coneT1, nullableConcreteT2, nullableSetT12, empty],
[coneT3, nullableConcreteT2, nullableConeT3, concreteT2],
[coneT1, nullableConeT1, nullableConeT1, coneT1],
[coneT1, nullableConeT3, nullableConeT3, coneT1],
[coneT1, nullableConeT4, nullableSetT14, empty],
[coneT1, nullableSetT12, nullableSetT12, concreteT1],
[coneT3, nullableSetT23, nullableSetT123, setT23],
// set
[setT12, setT12, setT12, setT12],
[setT12, setT123, setT123, setT12],
[setT12, setT23, setT123, concreteT2],
[setT12, setT34, setT1234, empty],
[setT12, nullableConcreteT1, nullableSetT12, concreteT1],
[setT12, nullableConcreteT3, nullableSetT123, empty],
[setT12, nullableConeT1, nullableSetT12, concreteT1],
[setT12, nullableConeT3, nullableSetT123, setT12],
[setT12, nullableConeT4, nullableSetT124, empty],
[setT12, nullableSetT12, nullableSetT12, setT12],
[setT12, nullableSetT123, nullableSetT123, setT12],
[setT12, nullableSetT23, nullableSetT123, concreteT2],
[setT12, nullableSetT34, nullableSetT1234, empty],
// nullableConcrete
[
nullableConcreteT1,
nullableConcreteT1,
nullableConcreteT1,
nullableConcreteT1
],
[nullableConcreteT1, nullableConcreteT2, nullableSetT12, nullableEmpty],
[nullableConcreteT1, nullableConeT1, nullableConeT1, nullableConcreteT1],
[nullableConcreteT1, nullableConeT3, nullableConeT3, nullableConcreteT1],
[nullableConcreteT1, nullableConeT4, nullableSetT14, nullableEmpty],
[nullableConcreteT1, nullableSetT12, nullableSetT12, nullableConcreteT1],
[nullableConcreteT1, nullableSetT23, nullableSetT123, nullableEmpty],
// nullableCone
[nullableConeT1, nullableConeT1, nullableConeT1, nullableConeT1],
[nullableConeT1, nullableConeT3, nullableConeT3, nullableConeT1],
[nullableConeT1, nullableConeT4, nullableSetT14, nullableEmpty],
[nullableConeT1, nullableSetT12, nullableSetT12, nullableConcreteT1],
[nullableConeT1, nullableSetT23, nullableSetT123, nullableEmpty],
[nullableConeT3, nullableSetT14, nullableSetT1234, nullableConcreteT1],
// nullableSet
[nullableSetT12, nullableSetT12, nullableSetT12, nullableSetT12],
[nullableSetT12, nullableSetT23, nullableSetT123, nullableConcreteT2],
[nullableSetT12, nullableSetT34, nullableSetT1234, nullableEmpty],
];
final hierarchy = new TestTypeHierarchy(
coreTypes,
target,
// classes
{
c1: tfc1,
c2: tfc2,
c3: tfc3,
c4: tfc4,
},
// specializations
{
c1: concreteT1,
c2: concreteT2,
c3: setT123,
c4: concreteT4
});
for (List testCase in testCases) {
Type a = testCase[0] as Type;
Type b = testCase[1] as Type;
Type union = testCase[2] as Type;
Type intersection = testCase[3] as Type;
expect(a.union(b, hierarchy), equals(union),
reason: "Test case: UNION($a, $b) = $union");
expect(b.union(a, hierarchy), equals(union),
reason: "Test case: UNION($b, $a) = $union");
expect(a.intersection(b, hierarchy), equals(intersection),
reason: "Test case: INTERSECTION($a, $b) = $intersection");
expect(b.intersection(a, hierarchy), equals(intersection),
reason: "Test case: INTERSECTION($b, $a) = $intersection");
}
});
test('hashcode-equals', () {
final c1 = Class(name: 'C1', fileUri: dummyUri)..parent = dummyLibrary;
final c2 = Class(name: 'C2', fileUri: dummyUri)..parent = dummyLibrary;
final c3 = Class(name: 'C3', fileUri: dummyUri)..parent = dummyLibrary;
final tfc1 = TFClass(1, c1, {}, null);
final tfc2 = TFClass(2, c2, {}, null);
final tfc3 = TFClass(3, c3, {}, null);
final t1a = InterfaceType(c1, Nullability.legacy);
final t1b = InterfaceType(c1, Nullability.legacy);
final t2 = InterfaceType(c2, Nullability.legacy);
void eq(dynamic a, dynamic b) {
expect(a == b, isTrue, reason: "Test case: $a == $b");
expect(a.hashCode == b.hashCode, isTrue,
reason: "Test case: ${a}.hashCode == ${b}.hashCode");
}
void ne(dynamic a, dynamic b) {
expect(a == b, isFalse, reason: "Test case: $a != $b");
// Hash codes can be the same, but it is unlikely.
expect(a.hashCode == b.hashCode, isFalse,
reason: "Test case: ${a}.hashCode != ${b}.hashCode");
}
eq(t1a, t1b);
ne(t1a, t2);
eq(emptyType, emptyType);
ne(emptyType, anyInstanceType);
ne(emptyType, tfc1.concreteType);
ne(emptyType, tfc1.coneType);
ne(emptyType, SetType([tfc1.concreteType, tfc2.concreteType]));
ne(emptyType, nullableEmptyType);
eq(anyInstanceType, anyInstanceType);
ne(anyInstanceType, tfc1.concreteType);
ne(anyInstanceType, tfc1.coneType);
ne(anyInstanceType, SetType([tfc1.concreteType, tfc2.concreteType]));
ne(anyInstanceType, nullableEmptyType);
eq(tfc1.concreteType, tfc1.concreteType);
ne(tfc1.concreteType, tfc2.concreteType);
ne(tfc1.concreteType, tfc1.coneType);
ne(tfc1.concreteType, tfc2.coneType);
ne(tfc1.concreteType, SetType([tfc1.concreteType, tfc2.concreteType]));
ne(tfc1.concreteType, tfc1.concreteType.nullable());
eq(tfc1.coneType, tfc1.coneType);
ne(tfc1.coneType, tfc2.coneType);
ne(tfc1.coneType, SetType([tfc1.concreteType, tfc2.concreteType]));
ne(tfc1.coneType, tfc1.coneType.nullable());
eq(SetType([tfc1.concreteType, tfc2.concreteType]),
SetType([tfc1.concreteType, tfc2.concreteType]));
eq(SetType([tfc1.concreteType, tfc2.concreteType, tfc3.concreteType]),
SetType([tfc1.concreteType, tfc2.concreteType, tfc3.concreteType]));
ne(SetType([tfc1.concreteType, tfc2.concreteType]),
SetType([tfc1.concreteType, tfc2.concreteType, tfc3.concreteType]));
ne(SetType([tfc1.concreteType, tfc2.concreteType]),
SetType([tfc1.concreteType, tfc3.concreteType]));
ne(SetType([tfc1.concreteType, tfc2.concreteType]),
SetType([tfc1.concreteType, tfc2.concreteType]).nullable());
});
}