blob: 53f2c0ed48d956f795cce211b327e18032c27fdf [file] [log] [blame] [edit]
// Copyright (c) 2015, 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 world_test;
import 'package:compiler/src/elements/names.dart';
import 'package:expect/async_helper.dart';
import 'package:expect/expect.dart';
import 'package:compiler/src/common/elements.dart';
import 'package:compiler/src/common/names.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/js_model/js_world.dart' show JClosedWorld;
import 'package:compiler/src/universe/class_hierarchy.dart';
import 'package:compiler/src/universe/class_set.dart';
import '../helpers/type_test_helper.dart';
void main() {
runTests() async {
await testClassSets();
await testProperties();
await testNativeClasses();
await testCommonSubclasses();
await testLiveMembers();
}
asyncTest(() async {
print('--test from kernel------------------------------------------------');
await runTests();
});
}
testClassSets() async {
var env = await TypeEnvironment.create(r"""
import 'dart:html' as html;
mixin class A implements X {}
mixin class B {}
class C_Super extends A {}
class C extends C_Super {}
class D implements A {}
class E extends B implements A {}
class F extends Object with A implements B {}
class G extends Object with B, A {}
class X {}
main() {
A();
B();
C();
D();
E();
F();
G();
html.window;
html.Worker('');
}
""", testBackendWorld: true);
JClosedWorld closedWorld = env.jClosedWorld;
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
final Object_ = env.getElement("Object") as ClassEntity;
final A = env.getElement("A") as ClassEntity;
final B = env.getElement("B") as ClassEntity;
final C = env.getElement("C") as ClassEntity;
final D = env.getElement("D") as ClassEntity;
final E = env.getElement("E") as ClassEntity;
final F = env.getElement("F") as ClassEntity;
final G = env.getElement("G") as ClassEntity;
final X = env.getElement("X") as ClassEntity;
void checkClasses(
String property,
ClassEntity cls,
Iterable<ClassEntity> foundClasses,
List<ClassEntity> expectedClasses, {
bool exact = true,
}) {
for (ClassEntity expectedClass in expectedClasses) {
Expect.isTrue(
foundClasses.contains(expectedClass),
"Expect $expectedClass in '$property' on $cls. "
"Found:\n ${foundClasses.join('\n ')}\n"
"${closedWorld.classHierarchy.dump(cls)}",
);
}
if (exact) {
Expect.equals(
expectedClasses.length,
foundClasses.length,
"Unexpected classes "
"${foundClasses.where((c) => !expectedClasses.contains(c))} "
"in '$property' on $cls.\n"
"${closedWorld.classHierarchy.dump(cls)}",
);
}
}
void check(
String property,
ClassEntity cls,
Iterable<ClassEntity> foundClasses,
List<ClassEntity> expectedClasses, {
bool exact = true,
void forEach(ClassEntity cls, ForEachFunction f)?,
int getCount(ClassEntity cls)?,
}) {
checkClasses(property, cls, foundClasses, expectedClasses, exact: exact);
if (forEach != null) {
List<ClassEntity> visited = <ClassEntity>[];
forEach(cls, (ClassEntity c) {
visited.add(c);
return IterationStep.continue_;
});
checkClasses(
'forEach($property)',
cls,
visited,
expectedClasses,
exact: exact,
);
}
if (getCount != null && exact) {
int count = getCount(cls);
Expect.equals(
expectedClasses.length,
count,
"Unexpected class count in '$property' on $cls.\n"
"${closedWorld.classHierarchy.dump(cls)}",
);
}
}
void testSubclasses(
ClassEntity cls,
List<ClassEntity> expectedClasses, {
bool exact = true,
}) {
check(
'subclassesOf',
cls,
closedWorld.classHierarchy.subclassesOf(cls),
expectedClasses,
exact: exact,
);
}
void testStrictSubclasses(
ClassEntity cls,
List<ClassEntity> expectedClasses, {
bool exact = true,
}) {
check(
'strictSubclassesOf',
cls,
closedWorld.classHierarchy.strictSubclassesOf(cls),
expectedClasses,
exact: exact,
forEach: closedWorld.classHierarchy.forEachStrictSubclassOf,
getCount: closedWorld.classHierarchy.strictSubclassCount,
);
}
void testStrictSubtypes(
ClassEntity cls,
List<ClassEntity> expectedClasses, {
bool exact = true,
}) {
check(
'strictSubtypesOf',
cls,
closedWorld.classHierarchy.strictSubtypesOf(cls),
expectedClasses,
exact: exact,
forEach: closedWorld.classHierarchy.forEachStrictSubtypeOf,
getCount: closedWorld.classHierarchy.strictSubtypeCount,
);
}
void testMixinUses(
ClassEntity cls,
List<ClassEntity> expectedClasses, {
bool exact = true,
}) {
check(
'mixinUsesOf',
cls,
closedWorld.mixinUsesOf(cls),
expectedClasses,
exact: exact,
);
}
testSubclasses(Object_, [A, B, C, D, E, F, G], exact: false);
testSubclasses(A, [A, C]);
testSubclasses(B, [B, E]);
testSubclasses(C, [C]);
testSubclasses(D, [D]);
testSubclasses(E, [E]);
testSubclasses(F, [F]);
testSubclasses(G, [G]);
testSubclasses(X, []);
testStrictSubclasses(Object_, [A, B, C, D, E, F, G], exact: false);
testStrictSubclasses(A, [C]);
testStrictSubclasses(B, [E]);
testStrictSubclasses(C, []);
testStrictSubclasses(D, []);
testStrictSubclasses(E, []);
testStrictSubclasses(F, []);
testStrictSubclasses(G, []);
testStrictSubclasses(X, []);
testStrictSubtypes(Object_, [A, B, C, D, E, F, G], exact: false);
testStrictSubtypes(A, [C, D, E, F, G]);
testStrictSubtypes(B, [E, F, G]);
testStrictSubtypes(C, []);
testStrictSubtypes(D, []);
testStrictSubtypes(E, []);
testStrictSubtypes(F, []);
testStrictSubtypes(G, []);
testStrictSubtypes(X, [A, C, D, E, F, G]);
testMixinUses(Object_, []);
testMixinUses(A, [
elementEnvironment.getSuperClass(F)!,
elementEnvironment.getSuperClass(G)!,
]);
testMixinUses(B, [
elementEnvironment.getSuperClass(elementEnvironment.getSuperClass(G)!)!,
]);
testMixinUses(C, []);
testMixinUses(D, []);
testMixinUses(E, []);
testMixinUses(F, []);
testMixinUses(G, []);
testMixinUses(X, []);
}
testProperties() async {
var env = await TypeEnvironment.create(r"""
mixin class A {}
class A1 extends A {}
class A2 implements A {}
class A3 extends Object with A {}
mixin class B {}
class B1 extends B {}
class B2 implements B {}
class B3 extends Object with B {}
mixin class C {}
class C1 extends C {}
class C2 implements C {}
class C3 extends Object with C {}
mixin class D {}
class D1 extends D {}
class D2 implements D {}
class D3 extends Object with D {}
mixin class E {}
class E1 extends E {}
class E2 implements E {}
class E3 extends Object with E {}
mixin class F {}
class F1 extends F {}
class F2 implements F {}
class F3 extends Object with F {}
mixin class G {}
class G1 extends G {}
class G2 extends G1 {}
class G3 extends G2 implements G {}
class G4 extends G2 with G {}
mixin class H {}
class H1 extends H {}
class H2 extends H1 {}
class H3 extends H2 implements H {}
class H4 extends H2 with H {}
main() {
B();
C1();
D2();
E3();
F1();
F2();
G2();
G3();
H4();
}
""", testBackendWorld: true);
JClosedWorld closedWorld = env.jClosedWorld;
check(String name, {bool? hasStrictSubtype, bool? hasOnlySubclasses}) {
final cls = env.getElement(name) as ClassEntity;
Expect.equals(
hasStrictSubtype,
closedWorld.classHierarchy.hasAnyStrictSubtype(cls),
"Unexpected hasAnyStrictSubtype property on $cls.",
);
Expect.equals(
hasOnlySubclasses,
closedWorld.classHierarchy.hasOnlySubclasses(cls),
"Unexpected hasOnlySubclasses property on $cls.",
);
}
check("Object", hasStrictSubtype: true, hasOnlySubclasses: true);
// No instantiated Ax classes.
check("A", hasStrictSubtype: false, hasOnlySubclasses: true);
check("A1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("A2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("A3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class B instantiated
check("B", hasStrictSubtype: false, hasOnlySubclasses: true);
check("B1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("B2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("B3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class C1 extends C instantiated
check("C", hasStrictSubtype: true, hasOnlySubclasses: true);
check("C1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("C2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("C3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class D2 implements D instantiated
check("D", hasStrictSubtype: true, hasOnlySubclasses: false);
check("D1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("D2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("D3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class E2 extends Object with E instantiated
check("E", hasStrictSubtype: true, hasOnlySubclasses: false);
check("E1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("E2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("E3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class F1 extends F instantiated
// class F2 implements F instantiated
check("F", hasStrictSubtype: true, hasOnlySubclasses: false);
check("F1", hasStrictSubtype: false, hasOnlySubclasses: true);
check("F2", hasStrictSubtype: false, hasOnlySubclasses: true);
check("F3", hasStrictSubtype: false, hasOnlySubclasses: true);
// class G2 extends G1 extends G instantiated
// class G3 extends G2 extends G1 extends G instantiated
check("G", hasStrictSubtype: true, hasOnlySubclasses: true);
check("G1", hasStrictSubtype: true, hasOnlySubclasses: true);
check("G2", hasStrictSubtype: true, hasOnlySubclasses: true);
check("G3", hasStrictSubtype: false, hasOnlySubclasses: true);
check("G4", hasStrictSubtype: false, hasOnlySubclasses: true);
// class H4 extends H2 with H extends H1 extends H instantiated
check("H", hasStrictSubtype: true, hasOnlySubclasses: true);
check("H1", hasStrictSubtype: true, hasOnlySubclasses: true);
check("H2", hasStrictSubtype: true, hasOnlySubclasses: true);
check("H3", hasStrictSubtype: false, hasOnlySubclasses: true);
check("H4", hasStrictSubtype: false, hasOnlySubclasses: true);
}
testNativeClasses() async {
var env = await TypeEnvironment.create(r"""
import 'dart:html' as html;
main() {
html.window; // Creates 'Window'.
html.Worker(''); // Creates 'Worker'.
html.CanvasElement() // Creates CanvasElement
..getContext(''); // Creates CanvasRenderingContext2D
}
""", testBackendWorld: true);
JClosedWorld closedWorld = env.jClosedWorld;
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
LibraryEntity dart_html = elementEnvironment.lookupLibrary(Uris.dartHtml)!;
ClassEntity clsEventTarget = elementEnvironment.lookupClass(
dart_html,
'EventTarget',
)!;
ClassEntity clsWindow = elementEnvironment.lookupClass(dart_html, 'Window')!;
ClassEntity clsAbstractWorker = elementEnvironment.lookupClass(
dart_html,
'AbstractWorker',
)!;
ClassEntity clsWorker = elementEnvironment.lookupClass(dart_html, 'Worker')!;
ClassEntity clsCanvasElement = elementEnvironment.lookupClass(
dart_html,
'CanvasElement',
)!;
ClassEntity clsCanvasRenderingContext = elementEnvironment.lookupClass(
dart_html,
'CanvasRenderingContext',
)!;
ClassEntity clsCanvasRenderingContext2D = elementEnvironment.lookupClass(
dart_html,
'CanvasRenderingContext2D',
)!;
List<ClassEntity> allClasses = [
clsEventTarget,
clsWindow,
clsAbstractWorker,
clsWorker,
clsCanvasElement,
clsCanvasRenderingContext,
clsCanvasRenderingContext2D,
];
check(
ClassEntity cls, {
required bool isDirectlyInstantiated,
required bool isAbstractlyInstantiated,
required bool isIndirectlyInstantiated,
required bool hasStrictSubtype,
required bool hasOnlySubclasses,
ClassEntity? lubOfInstantiatedSubclasses,
ClassEntity? lubOfInstantiatedSubtypes,
int? instantiatedSubclassCount,
int? instantiatedSubtypeCount,
List<ClassEntity> subclasses = const <ClassEntity>[],
List<ClassEntity> subtypes = const <ClassEntity>[],
}) {
ClassSet classSet = closedWorld.classHierarchy.getClassSet(cls);
ClassHierarchyNode node = classSet.node;
String dumpText = '\n${closedWorld.classHierarchy.dump(cls)}';
Expect.equals(
isDirectlyInstantiated,
closedWorld.classHierarchy.isDirectlyInstantiated(cls),
"Unexpected isDirectlyInstantiated property on $cls.$dumpText",
);
Expect.equals(
isAbstractlyInstantiated,
closedWorld.classHierarchy.isAbstractlyInstantiated(cls),
"Unexpected isAbstractlyInstantiated property on $cls.$dumpText",
);
Expect.equals(
isIndirectlyInstantiated,
closedWorld.classHierarchy.isIndirectlyInstantiated(cls),
"Unexpected isIndirectlyInstantiated property on $cls.$dumpText",
);
Expect.equals(
hasStrictSubtype,
closedWorld.classHierarchy.hasAnyStrictSubtype(cls),
"Unexpected hasAnyStrictSubtype property on $cls.$dumpText",
);
Expect.equals(
hasOnlySubclasses,
closedWorld.classHierarchy.hasOnlySubclasses(cls),
"Unexpected hasOnlySubclasses property on $cls.$dumpText",
);
Expect.equals(
lubOfInstantiatedSubclasses,
node.getLubOfInstantiatedSubclasses(),
"Unexpected getLubOfInstantiatedSubclasses() result on $cls.$dumpText",
);
Expect.equals(
lubOfInstantiatedSubtypes,
classSet.getLubOfInstantiatedSubtypes(),
"Unexpected getLubOfInstantiatedSubtypes() result on $cls.$dumpText",
);
if (instantiatedSubclassCount != null) {
Expect.equals(
instantiatedSubclassCount,
node.instantiatedSubclassCount,
"Unexpected instantiatedSubclassCount property on $cls.$dumpText",
);
}
if (instantiatedSubtypeCount != null) {
Expect.equals(
instantiatedSubtypeCount,
classSet.instantiatedSubtypeCount,
"Unexpected instantiatedSubtypeCount property on $cls.$dumpText",
);
}
for (ClassEntity other in allClasses) {
if (other == cls) continue;
if (!closedWorld.classHierarchy.isExplicitlyInstantiated(other)) continue;
Expect.equals(
subclasses.contains(other),
closedWorld.classHierarchy.isSubclassOf(other, cls),
"Unexpected subclass relation between $other and $cls.",
);
Expect.equals(
subtypes.contains(other),
closedWorld.classHierarchy.isSubtypeOf(other, cls),
"Unexpected subtype relation between $other and $cls.",
);
}
Set<ClassEntity> strictSubclasses = Set<ClassEntity>();
closedWorld.classHierarchy.forEachStrictSubclassOf(cls, (
ClassEntity other,
) {
if (allClasses.contains(other)) {
strictSubclasses.add(other);
}
return IterationStep.continue_;
});
Expect.setEquals(
subclasses,
strictSubclasses,
"Unexpected strict subclasses of $cls: ${strictSubclasses}.",
);
Set<ClassEntity> strictSubtypes = Set<ClassEntity>();
closedWorld.classHierarchy.forEachStrictSubtypeOf(cls, (ClassEntity other) {
if (allClasses.contains(other)) {
strictSubtypes.add(other);
}
return IterationStep.continue_;
});
Expect.setEquals(
subtypes,
strictSubtypes,
"Unexpected strict subtypes of $cls: $strictSubtypes.",
);
}
// Extended by Window.
check(
clsEventTarget,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: false,
isIndirectlyInstantiated: true,
hasStrictSubtype: true,
hasOnlySubclasses: true,
lubOfInstantiatedSubclasses: clsEventTarget,
lubOfInstantiatedSubtypes: clsEventTarget,
// May vary with implementation, do no test.
instantiatedSubclassCount: null,
instantiatedSubtypeCount: null,
subclasses: [clsWindow, clsCanvasElement, clsWorker],
subtypes: [clsWindow, clsCanvasElement, clsWorker],
);
// Created by 'html.window'.
check(
clsWindow,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: true,
isIndirectlyInstantiated: false,
hasStrictSubtype: false,
hasOnlySubclasses: true,
lubOfInstantiatedSubclasses: clsWindow,
lubOfInstantiatedSubtypes: clsWindow,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 0,
);
// Implemented by 'Worker'.
check(
clsAbstractWorker,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: false,
isIndirectlyInstantiated: false,
hasStrictSubtype: true,
hasOnlySubclasses: false,
lubOfInstantiatedSubclasses: null,
lubOfInstantiatedSubtypes: clsWorker,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 1,
subtypes: [clsWorker],
);
// Created by 'new html.Worker'.
check(
clsWorker,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: true,
isIndirectlyInstantiated: false,
hasStrictSubtype: false,
hasOnlySubclasses: true,
lubOfInstantiatedSubclasses: clsWorker,
lubOfInstantiatedSubtypes: clsWorker,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 0,
);
// Created by 'new html.CanvasElement'.
check(
clsCanvasElement,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: true,
isIndirectlyInstantiated: false,
hasStrictSubtype: false,
hasOnlySubclasses: true,
lubOfInstantiatedSubclasses: clsCanvasElement,
lubOfInstantiatedSubtypes: clsCanvasElement,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 0,
);
// Implemented by CanvasRenderingContext2D and RenderingContext.
check(
clsCanvasRenderingContext,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: false,
isIndirectlyInstantiated: false,
hasStrictSubtype: true,
hasOnlySubclasses: false,
lubOfInstantiatedSubclasses: null,
lubOfInstantiatedSubtypes: clsCanvasRenderingContext,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 2,
subtypes: [clsCanvasRenderingContext2D],
);
// Created by 'html.CanvasElement.getContext'.
check(
clsCanvasRenderingContext2D,
isDirectlyInstantiated: false,
isAbstractlyInstantiated: true,
isIndirectlyInstantiated: false,
hasStrictSubtype: false,
hasOnlySubclasses: true,
lubOfInstantiatedSubclasses: clsCanvasRenderingContext2D,
lubOfInstantiatedSubtypes: clsCanvasRenderingContext2D,
instantiatedSubclassCount: 0,
instantiatedSubtypeCount: 0,
);
}
testCommonSubclasses() async {
var env = await TypeEnvironment.create(r"""
class A {}
class B {}
class C extends A {}
class D implements A {}
class E extends B {}
class F implements C, E {}
class G extends C implements E {}
class H implements C {}
class I extends D implements E {}
class J extends E implements D {}
main() {
A();
B();
C();
D();
E();
F();
G();
H();
I();
J();
}
""", testBackendWorld: true);
JClosedWorld closedWorld = env.jClosedWorld;
final A = env.getElement("A") as ClassEntity;
final B = env.getElement("B") as ClassEntity;
final C = env.getElement("C") as ClassEntity;
final F = env.getElement("F") as ClassEntity;
final G = env.getElement("G") as ClassEntity;
final I = env.getElement("I") as ClassEntity;
final J = env.getElement("J") as ClassEntity;
ClassQuery? toClassQuery(
SubclassResult result,
ClassEntity cls1,
ClassQuery query1,
ClassEntity cls2,
ClassQuery query2,
) {
switch (result) {
case SimpleSubclassResult.empty:
return null;
case SimpleSubclassResult.exact1:
return ClassQuery.exact;
case SimpleSubclassResult.exact2:
return ClassQuery.exact;
case SimpleSubclassResult.subclass1:
return ClassQuery.subclass;
case SimpleSubclassResult.subclass2:
return ClassQuery.subclass;
case SimpleSubclassResult.subtype1:
return ClassQuery.subtype;
case SimpleSubclassResult.subtype2:
return ClassQuery.subtype;
case SetSubclassResult():
return null;
}
}
ClassEntity? toClassEntity(
SubclassResult result,
ClassEntity cls1,
ClassQuery query1,
ClassEntity cls2,
ClassQuery query2,
) {
switch (result) {
case SimpleSubclassResult.empty:
return null;
case SimpleSubclassResult.exact1:
return cls1;
case SimpleSubclassResult.exact2:
return cls2;
case SimpleSubclassResult.subclass1:
return cls1;
case SimpleSubclassResult.subclass2:
return cls2;
case SimpleSubclassResult.subtype1:
return cls1;
case SimpleSubclassResult.subtype2:
return cls2;
case SetSubclassResult():
return null;
}
}
void check(
ClassEntity cls1,
ClassQuery query1,
ClassEntity cls2,
ClassQuery query2,
SubclassResult expectedResult,
) {
SubclassResult result1 = closedWorld.classHierarchy.commonSubclasses(
cls1,
query1,
cls2,
query2,
);
SubclassResult result2 = closedWorld.classHierarchy.commonSubclasses(
cls2,
query2,
cls1,
query1,
);
Expect.equals(
toClassQuery(result1, cls1, query1, cls2, query2),
toClassQuery(result2, cls2, query2, cls1, query1),
"Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
"\n a vs b: $result1\n b vs a: $result2",
);
Expect.equals(
toClassEntity(result1, cls1, query1, cls2, query2),
toClassEntity(result2, cls2, query2, cls1, query1),
"Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
"\n a vs b: $result1\n b vs a: $result2",
);
switch (expectedResult) {
case SimpleSubclassResult():
Expect.equals(
expectedResult,
result1,
"Unexpected results for ($cls1,$query1) vs ($cls2,$query2):"
"\n expected: $expectedResult\n actual: $result1",
);
case SetSubclassResult():
Expect.type<SetSubclassResult>(result1);
Expect.type<SetSubclassResult>(result2);
result1 as SetSubclassResult;
result2 as SetSubclassResult;
Expect.setEquals(
result1.classes,
result2.classes,
"Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
"\n a vs b: $result1\n b vs a: $result2",
);
Expect.setEquals(
expectedResult.classes,
result1.classes,
"Unexpected results for ($cls1,$query1) vs ($cls2,$query2):"
"\n expected: $expectedResult\n actual: $result1",
);
}
}
check(A, ClassQuery.exact, A, ClassQuery.exact, SimpleSubclassResult.exact1);
check(
A,
ClassQuery.exact,
A,
ClassQuery.subclass,
SimpleSubclassResult.exact1,
);
check(
A,
ClassQuery.exact,
A,
ClassQuery.subtype,
SimpleSubclassResult.exact1,
);
check(
A,
ClassQuery.subclass,
A,
ClassQuery.subclass,
SimpleSubclassResult.subclass1,
);
check(
A,
ClassQuery.subclass,
A,
ClassQuery.subtype,
SimpleSubclassResult.subclass1,
);
check(
A,
ClassQuery.subtype,
A,
ClassQuery.subtype,
SimpleSubclassResult.subtype1,
);
check(A, ClassQuery.exact, B, ClassQuery.exact, SimpleSubclassResult.empty);
check(
A,
ClassQuery.exact,
B,
ClassQuery.subclass,
SimpleSubclassResult.empty,
);
check(
A,
ClassQuery.subclass,
B,
ClassQuery.exact,
SimpleSubclassResult.empty,
);
check(A, ClassQuery.exact, B, ClassQuery.subtype, SimpleSubclassResult.empty);
check(A, ClassQuery.subtype, B, ClassQuery.exact, SimpleSubclassResult.empty);
check(
A,
ClassQuery.subclass,
B,
ClassQuery.subclass,
SimpleSubclassResult.empty,
);
check(A, ClassQuery.subclass, B, ClassQuery.subtype, SetSubclassResult([G]));
check(A, ClassQuery.subtype, B, ClassQuery.subclass, SetSubclassResult([J]));
check(
A,
ClassQuery.subtype,
B,
ClassQuery.subtype,
SetSubclassResult([F, G, I, J]),
);
check(A, ClassQuery.exact, C, ClassQuery.exact, SimpleSubclassResult.empty);
check(
A,
ClassQuery.exact,
C,
ClassQuery.subclass,
SimpleSubclassResult.empty,
);
check(
A,
ClassQuery.subclass,
C,
ClassQuery.exact,
SimpleSubclassResult.exact2,
);
check(A, ClassQuery.exact, C, ClassQuery.subtype, SimpleSubclassResult.empty);
check(
A,
ClassQuery.subtype,
C,
ClassQuery.exact,
SimpleSubclassResult.exact2,
);
check(
A,
ClassQuery.subclass,
C,
ClassQuery.subclass,
SimpleSubclassResult.subclass2,
);
check(A, ClassQuery.subclass, C, ClassQuery.subtype, SetSubclassResult([C]));
check(
A,
ClassQuery.subtype,
C,
ClassQuery.subclass,
SimpleSubclassResult.subclass2,
);
check(
A,
ClassQuery.subtype,
C,
ClassQuery.subtype,
SimpleSubclassResult.subtype2,
);
check(B, ClassQuery.exact, C, ClassQuery.exact, SimpleSubclassResult.empty);
check(
B,
ClassQuery.exact,
C,
ClassQuery.subclass,
SimpleSubclassResult.empty,
);
check(
B,
ClassQuery.subclass,
C,
ClassQuery.exact,
SimpleSubclassResult.empty,
);
check(B, ClassQuery.exact, C, ClassQuery.subtype, SimpleSubclassResult.empty);
check(B, ClassQuery.subtype, C, ClassQuery.exact, SimpleSubclassResult.empty);
check(
B,
ClassQuery.subclass,
C,
ClassQuery.subclass,
SimpleSubclassResult.empty,
);
check(B, ClassQuery.subclass, C, ClassQuery.subtype, SetSubclassResult([]));
check(B, ClassQuery.subtype, C, ClassQuery.subclass, SetSubclassResult([G]));
check(
B,
ClassQuery.subtype,
C,
ClassQuery.subtype,
SetSubclassResult([F, G]),
);
}
testLiveMembers() async {
final env = await TypeEnvironment.create(r"""
class A { int a() => 1; }
mixin B { int b(); }
class C with B { int b() => 2; }
mixin D { int d(); }
mixin E on D { int e() => d(); }
class F implements D { int d() => 3; }
class G extends F with E {}
abstract class H { int h(); }
class I implements H { int h() => 4; }
abstract class J { int j(); }
abstract class K { int k(); }
class L extends K { int k() => 5; }
abstract class M { int m(); }
class N extends M { int m() => 6; }
main() {
A().a();
C().b();
G().e();
I().h();
L().k();
N();
}
""", testBackendWorld: true);
JClosedWorld closedWorld = env.jClosedWorld;
void check(
String clsName,
String memberName, {
bool expectExists = true,
bool expectAbstract = false,
bool expectConcrete = false,
}) {
final cls = env.getClass(clsName);
final member = closedWorld.elementEnvironment.lookupLocalClassMember(
cls,
Name(memberName, Uri()),
);
if (expectExists) {
Expect.isNotNull(
member,
"Expected $clsName to contain member $memberName.",
);
} else {
Expect.isNull(
member,
"Expected $clsName not to contain member $memberName.",
);
return;
}
Expect.isTrue(
expectAbstract ^ expectConcrete,
"Can only expect to be in one of liveAbstractInstanceMembers and "
"liveInstanceMembers.",
);
if (expectAbstract) {
Expect.isTrue(
closedWorld.liveAbstractInstanceMembers.contains(member),
"Expected $member to exist in liveAbstractInstanceMembers.",
);
Expect.isFalse(
closedWorld.liveInstanceMembers.contains(member),
"Expected $member to not exist in liveInstanceMembers.",
);
} else {
Expect.isTrue(
closedWorld.liveInstanceMembers.contains(member),
"Expected $member to exist in liveInstanceMembers.",
);
Expect.isFalse(
closedWorld.liveAbstractInstanceMembers.contains(member),
"Expected $member to not exist in liveAbstractInstanceMembers.",
);
}
}
check('A', 'a', expectConcrete: true);
check('B', 'b', expectAbstract: true);
check('C', 'b', expectConcrete: true);
check('D', 'd', expectAbstract: true);
check('E', 'e', expectConcrete: true);
check('F', 'd', expectConcrete: true);
check('H', 'h', expectAbstract: true);
check('I', 'h', expectConcrete: true);
check('J', 'j', expectExists: false);
check('K', 'k', expectAbstract: true);
check('L', 'k', expectConcrete: true);
check('M', 'm', expectExists: false);
check('N', 'm', expectExists: false);
}