20faaa5d94
Change-Id: Ib18f554c1076f27a3f7c0df5a36410da41a1f467 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148784 Reviewed-by: Paul Berry <paulberry@google.com> Reviewed-by: Sigmund Cherem <sigmund@google.com>
695 lines
25 KiB
Dart
695 lines
25 KiB
Dart
// 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.
|
|
|
|
// @dart = 2.7
|
|
|
|
library world_test;
|
|
|
|
import 'package:expect/expect.dart';
|
|
import 'package:async_helper/async_helper.dart';
|
|
import 'package:compiler/src/common/names.dart';
|
|
import 'package:compiler/src/common_elements.dart';
|
|
import 'package:compiler/src/elements/entities.dart';
|
|
import 'package:compiler/src/universe/class_hierarchy.dart';
|
|
import 'package:compiler/src/universe/class_set.dart';
|
|
import 'package:compiler/src/world.dart' show JClosedWorld;
|
|
import '../helpers/type_test_helper.dart';
|
|
|
|
void main() {
|
|
runTests() async {
|
|
await testClassSets();
|
|
await testProperties();
|
|
await testNativeClasses();
|
|
await testCommonSubclasses();
|
|
}
|
|
|
|
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() {
|
|
new A();
|
|
new B();
|
|
new C();
|
|
new D();
|
|
new E();
|
|
new F();
|
|
new G();
|
|
html.window;
|
|
new html.Worker('');
|
|
}
|
|
""", testBackendWorld: true);
|
|
JClosedWorld closedWorld = env.jClosedWorld;
|
|
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
|
|
|
|
ClassEntity Object_ = env.getElement("Object");
|
|
ClassEntity A = env.getElement("A");
|
|
ClassEntity B = env.getElement("B");
|
|
ClassEntity C = env.getElement("C");
|
|
ClassEntity D = env.getElement("D");
|
|
ClassEntity E = env.getElement("E");
|
|
ClassEntity F = env.getElement("F");
|
|
ClassEntity G = env.getElement("G");
|
|
ClassEntity X = env.getElement("X");
|
|
|
|
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() {
|
|
new B();
|
|
new C1();
|
|
new D2();
|
|
new E3();
|
|
new F1();
|
|
new F2();
|
|
new G2();
|
|
new G3();
|
|
new H4();
|
|
}
|
|
""", testBackendWorld: true);
|
|
JClosedWorld closedWorld = env.jClosedWorld;
|
|
|
|
check(String name, {bool hasStrictSubtype, bool hasOnlySubclasses}) {
|
|
ClassEntity cls = env.getElement(name);
|
|
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'.
|
|
new html.Worker(''); // Creates 'Worker'.
|
|
new 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,
|
|
{bool isDirectlyInstantiated,
|
|
bool isAbstractlyInstantiated,
|
|
bool isIndirectlyInstantiated,
|
|
bool hasStrictSubtype,
|
|
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 = new 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 = new 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() {
|
|
new A();
|
|
new B();
|
|
new C();
|
|
new D();
|
|
new E();
|
|
new F();
|
|
new G();
|
|
new H();
|
|
new I();
|
|
new J();
|
|
}
|
|
""", testBackendWorld: true);
|
|
JClosedWorld closedWorld = env.jClosedWorld;
|
|
|
|
ClassEntity A = env.getElement("A");
|
|
ClassEntity B = env.getElement("B");
|
|
ClassEntity C = env.getElement("C");
|
|
ClassEntity F = env.getElement("F");
|
|
ClassEntity G = env.getElement("G");
|
|
ClassEntity I = env.getElement("I");
|
|
ClassEntity J = env.getElement("J");
|
|
|
|
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, new SubclassResult([G]));
|
|
check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBCLASS, new SubclassResult([J]));
|
|
check(A, ClassQuery.SUBTYPE, B, ClassQuery.SUBTYPE,
|
|
new 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, new 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, new SubclassResult([]));
|
|
check(B, ClassQuery.SUBTYPE, C, ClassQuery.SUBCLASS, new SubclassResult([G]));
|
|
check(
|
|
B, ClassQuery.SUBTYPE, C, ClassQuery.SUBTYPE, new SubclassResult([F, G]));
|
|
}
|