blob: d728aec1fc102fbe516b3f8aec19e52f0869765b [file] [log] [blame]
// 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/expect.dart';
import 'package:async_helper/async_helper.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;
class A implements X {}
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"""
class A {}
class A1 extends A {}
class A2 implements A {}
class A3 extends Object with A {}
class B {}
class B1 extends B {}
class B2 implements B {}
class B3 extends Object with B {}
class C {}
class C1 extends C {}
class C2 implements C {}
class C3 extends Object with C {}
class D {}
class D1 extends D {}
class D2 implements D {}
class D3 extends Object with D {}
class E {}
class E1 extends E {}
class E2 implements E {}
class E3 extends Object with E {}
class F {}
class F1 extends F {}
class F2 implements F {}
class F3 extends Object with F {}
class G {}
class G1 extends G {}
class G2 extends G1 {}
class G3 extends G2 implements G {}
class G4 extends G2 with G {}
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.dart_html)!;
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(SubclassResultKind kind, ClassEntity cls1,
ClassQuery query1, ClassEntity cls2, ClassQuery query2) {
switch (kind) {
case SubclassResultKind.EMPTY:
return null;
case SubclassResultKind.EXACT1:
return ClassQuery.EXACT;
case SubclassResultKind.EXACT2:
return ClassQuery.EXACT;
case SubclassResultKind.SUBCLASS1:
return ClassQuery.SUBCLASS;
case SubclassResultKind.SUBCLASS2:
return ClassQuery.SUBCLASS;
case SubclassResultKind.SUBTYPE1:
return ClassQuery.SUBTYPE;
case SubclassResultKind.SUBTYPE2:
return ClassQuery.SUBTYPE;
case SubclassResultKind.SET:
default:
return null;
}
}
ClassEntity? toClassEntity(SubclassResultKind kind, ClassEntity cls1,
ClassQuery query1, ClassEntity cls2, ClassQuery query2) {
switch (kind) {
case SubclassResultKind.EMPTY:
return null;
case SubclassResultKind.EXACT1:
return cls1;
case SubclassResultKind.EXACT2:
return cls2;
case SubclassResultKind.SUBCLASS1:
return cls1;
case SubclassResultKind.SUBCLASS2:
return cls2;
case SubclassResultKind.SUBTYPE1:
return cls1;
case SubclassResultKind.SUBTYPE2:
return cls2;
case SubclassResultKind.SET:
default:
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.kind, cls1, query1, cls2, query2),
toClassQuery(result2.kind, 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.kind, cls1, query1, cls2, query2),
toClassEntity(result2.kind, cls2, query2, cls1, query1),
"Asymmetric results for ($cls1,$query1) vs ($cls2,$query2):"
"\n a vs b: $result1\n b vs a: $result2");
Expect.equals(
expectedResult.kind,
result1.kind,
"Unexpected results for ($cls1,$query1) vs ($cls2,$query2):"
"\n expected: $expectedResult\n actual: $result1");
if (expectedResult.kind == SubclassResultKind.SET) {
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, SubclassResult.EXACT1);
check(A, ClassQuery.EXACT, A, ClassQuery.SUBCLASS, SubclassResult.EXACT1);
check(A, ClassQuery.EXACT, A, ClassQuery.SUBTYPE, SubclassResult.EXACT1);
check(
A, ClassQuery.SUBCLASS, A, ClassQuery.SUBCLASS, SubclassResult.SUBCLASS1);
check(
A, ClassQuery.SUBCLASS, A, ClassQuery.SUBTYPE, SubclassResult.SUBCLASS1);
check(A, ClassQuery.SUBTYPE, A, ClassQuery.SUBTYPE, SubclassResult.SUBTYPE1);
check(A, ClassQuery.EXACT, B, ClassQuery.EXACT, SubclassResult.EMPTY);
check(A, ClassQuery.EXACT, B, ClassQuery.SUBCLASS, SubclassResult.EMPTY);
check(A, ClassQuery.SUBCLASS, B, ClassQuery.EXACT, SubclassResult.EMPTY);
check(A, ClassQuery.EXACT, B, ClassQuery.SUBTYPE, SubclassResult.EMPTY);
check(A, ClassQuery.SUBTYPE, B, ClassQuery.EXACT, SubclassResult.EMPTY);
check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBCLASS, SubclassResult.EMPTY);
check(A, ClassQuery.SUBCLASS, B, ClassQuery.SUBTYPE, SubclassResult([G]));
check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBCLASS, SubclassResult([J]));
check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE,
SubclassResult([F, G, I, J]));
check(A, ClassQuery.EXACT, C, ClassQuery.EXACT, SubclassResult.EMPTY);
check(A, ClassQuery.EXACT, C, ClassQuery.SUBCLASS, SubclassResult.EMPTY);
check(A, ClassQuery.SUBCLASS, C, ClassQuery.EXACT, SubclassResult.EXACT2);
check(A, ClassQuery.EXACT, C, ClassQuery.SUBTYPE, SubclassResult.EMPTY);
check(A, ClassQuery.SUBTYPE, C, ClassQuery.EXACT, SubclassResult.EXACT2);
check(
A, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS, SubclassResult.SUBCLASS2);
check(A, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE, SubclassResult([C]));
check(
A, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS, SubclassResult.SUBCLASS2);
check(A, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE, SubclassResult.SUBTYPE2);
check(B, ClassQuery.EXACT, C, ClassQuery.EXACT, SubclassResult.EMPTY);
check(B, ClassQuery.EXACT, C, ClassQuery.SUBCLASS, SubclassResult.EMPTY);
check(B, ClassQuery.SUBCLASS, C, ClassQuery.EXACT, SubclassResult.EMPTY);
check(B, ClassQuery.EXACT, C, ClassQuery.SUBTYPE, SubclassResult.EMPTY);
check(B, ClassQuery.SUBTYPE, C, ClassQuery.EXACT, SubclassResult.EMPTY);
check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBCLASS, SubclassResult.EMPTY);
check(B, ClassQuery.SUBCLASS, C, ClassQuery.SUBTYPE, SubclassResult([]));
check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS, SubclassResult([G]));
check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE, SubclassResult([F, G]));
}
testLiveMembers() async {
final env = await TypeEnvironment.create(r"""
class A { int a() => 1; }
abstract class 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);
}