blob: 5c6e15998b90f17decae06f0511c8753b8b3106b [file] [log] [blame]
// Copyright (c) 2014, 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 type_mask2_test;
import 'dart:async';
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
import 'type_test_helper.dart';
import 'package:compiler/src/elements/elements.dart' show ClassElement;
import 'package:compiler/src/types/types.dart';
import 'package:compiler/src/world.dart' show ClosedWorld;
isCheckedMode() {
try {
dynamic i = 1;
// ignore: UNUSED_LOCAL_VARIABLE
String s = i;
return false;
} catch (e) {
return true;
}
}
void main() {
asyncTest(() async {
await testUnionTypeMaskFlatten();
await testStringSubtypes();
});
}
checkMasks(ClosedWorld closedWorld, List<ClassElement> allClasses,
List<FlatTypeMask> masks,
{FlatTypeMask result,
List<FlatTypeMask> disjointMasks,
FlatTypeMask flattened,
List<ClassElement> containedClasses}) {
List<FlatTypeMask> disjoint = <FlatTypeMask>[];
UnionTypeMask.unionOfHelper(masks, disjoint, closedWorld);
Expect.listEquals(disjointMasks, disjoint,
'Unexpected disjoint masks: $disjoint, expected $disjointMasks.');
if (flattened == null) {
// We only do the invalid call to flatten in checked mode, as flatten's
// behaviour in unchecked mode is not defined and thus cannot be
// reliably tested.
if (isCheckedMode()) {
Expect.throws(
() => UnionTypeMask.flatten(disjoint, closedWorld),
(e) => e is AssertionError,
'Expect assertion failure on flattening of $disjoint.');
}
} else {
TypeMask flattenResult = UnionTypeMask.flatten(disjoint, closedWorld);
Expect.equals(
flattened,
flattenResult,
'Unexpected flattening of $disjoint: '
'$flattenResult, expected $flattened.');
}
dynamic union = UnionTypeMask.unionOf(masks, closedWorld);
if (result == null) {
Expect.isTrue(union is UnionTypeMask,
'Expected union of $masks to be a union-type: $union.');
Expect.listEquals(
disjointMasks,
union.disjointMasks,
'Unexpected union masks: '
'${union.disjointMasks}, expected $disjointMasks.');
} else {
Expect.equals(
result, union, 'Unexpected union of $masks: $union, expected $result.');
}
if (containedClasses != null) {
for (ClassElement cls in allClasses) {
if (containedClasses.contains(cls)) {
Expect.isTrue(union.contains(cls, closedWorld),
'Expected $union to contain $cls.');
} else {
Expect.isFalse(union.contains(cls, closedWorld),
'$union not expected to contain $cls.');
}
}
}
return union;
}
Future testUnionTypeMaskFlatten() async {
TypeEnvironment env = await TypeEnvironment.create(r"""
class A {}
class B {}
class C extends A {}
class D implements A {}
class E extends B implements A {}
""", mainSource: r"""
main() {
new A();
new B();
new C();
new D();
new E();
}
""", compileMode: CompileMode.memory);
ClosedWorld closedWorld = env.closedWorld;
ClassElement Object_ = env.getElement("Object");
ClassElement A = env.getElement("A");
ClassElement B = env.getElement("B");
ClassElement C = env.getElement("C");
ClassElement D = env.getElement("D");
ClassElement E = env.getElement("E");
List<ClassElement> allClasses = <ClassElement>[Object_, A, B, C, D, E];
check(List<FlatTypeMask> masks,
{FlatTypeMask result,
List<FlatTypeMask> disjointMasks,
FlatTypeMask flattened,
List<ClassElement> containedClasses}) {
return checkMasks(closedWorld, allClasses, masks,
result: result,
disjointMasks: disjointMasks,
flattened: flattened,
containedClasses: containedClasses);
}
TypeMask empty = const TypeMask.nonNullEmpty();
TypeMask subclassObject = new TypeMask.nonNullSubclass(Object_, closedWorld);
TypeMask exactA = new TypeMask.nonNullExact(A, closedWorld);
TypeMask subclassA = new TypeMask.nonNullSubclass(A, closedWorld);
TypeMask subtypeA = new TypeMask.nonNullSubtype(A, closedWorld);
TypeMask exactB = new TypeMask.nonNullExact(B, closedWorld);
TypeMask subclassB = new TypeMask.nonNullSubclass(B, closedWorld);
TypeMask exactC = new TypeMask.nonNullExact(C, closedWorld);
TypeMask exactD = new TypeMask.nonNullExact(D, closedWorld);
TypeMask exactE = new TypeMask.nonNullExact(E, closedWorld);
check([], result: empty, disjointMasks: [], containedClasses: []);
check([exactA],
result: exactA, disjointMasks: [exactA], containedClasses: [A]);
check([exactA, exactA],
result: exactA, disjointMasks: [exactA], containedClasses: [A]);
check([exactA, exactB],
disjointMasks: [exactA, exactB],
flattened: subclassObject,
containedClasses: [A, B]);
check([subclassObject],
result: subclassObject,
disjointMasks: [subclassObject],
containedClasses: [Object_, A, B, C, D, E]);
check([subclassObject, exactA],
disjointMasks: [subclassObject],
result: subclassObject,
containedClasses: [Object_, A, B, C, D, E]);
check([exactA, exactC],
disjointMasks: [subclassA], result: subclassA, containedClasses: [A, C]);
check([exactA, exactB, exactC],
disjointMasks: [subclassA, exactB],
flattened: subclassObject,
containedClasses: [A, B, C]);
check([exactA, exactD],
disjointMasks: [subtypeA],
result: subtypeA,
containedClasses: [A, C, D, E]);
check([exactA, exactB, exactD],
disjointMasks: [subtypeA, exactB],
flattened: subclassObject,
containedClasses: [A, B, C, D, E]);
check([exactA, exactE],
disjointMasks: [subtypeA],
result: subtypeA,
containedClasses: [A, C, D, E]);
check([exactA, exactB, exactE],
disjointMasks: [subtypeA, exactB],
flattened: subclassObject,
containedClasses: [A, B, C, D, E]);
check([exactB, exactE, exactA],
disjointMasks: [subclassB, exactA],
flattened: subclassObject,
containedClasses: [A, B, E]);
check([exactE, exactA, exactB],
disjointMasks: [subtypeA, exactB],
flattened: subclassObject,
containedClasses: [A, B, C, D, E]);
check([exactE, exactB, exactA],
disjointMasks: [subclassB, exactA],
flattened: subclassObject,
containedClasses: [A, B, E]);
}
Future testStringSubtypes() async {
TypeEnvironment env = await TypeEnvironment.create('',
mainSource: r"""
main() {
'' is String;
}
""",
compileMode: CompileMode.memory);
ClosedWorld closedWorld = env.closedWorld;
ClassElement Object_ = env.getElement("Object");
ClassElement String_ = env.getElement("String");
ClassElement JSString = closedWorld.commonElements.jsStringClass;
Expect.isFalse(closedWorld.isDirectlyInstantiated(Object_));
Expect.isTrue(closedWorld.isIndirectlyInstantiated(Object_));
Expect.isTrue(closedWorld.isInstantiated(Object_));
Expect.isFalse(closedWorld.isDirectlyInstantiated(String_));
Expect.isFalse(closedWorld.isIndirectlyInstantiated(String_));
Expect.isFalse(closedWorld.isInstantiated(String_));
Expect.isTrue(closedWorld.isDirectlyInstantiated(JSString));
Expect.isFalse(closedWorld.isIndirectlyInstantiated(JSString));
Expect.isTrue(closedWorld.isInstantiated(JSString));
TypeMask subtypeString = new TypeMask.nonNullSubtype(String_, closedWorld);
TypeMask exactJSString = new TypeMask.nonNullExact(JSString, closedWorld);
TypeMask subtypeJSString = new TypeMask.nonNullSubtype(JSString, closedWorld);
TypeMask subclassJSString =
new TypeMask.nonNullSubclass(JSString, closedWorld);
Expect.equals(exactJSString, subtypeString);
Expect.equals(exactJSString, subtypeJSString);
Expect.equals(exactJSString, subclassJSString);
}