mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
Implement Dart 1.0 LUB algorithm (for interface types) in kernel.
Note: I intend to implement the full LUB algorithm (including strong mode behaviors) in front_end, however this piece of the algorithm makes sense to be in kernel so that it can take advantage of _ClassInfo. R=ahe@google.com, johnniwinther@google.com Review-Url: https://codereview.chromium.org/2848083002 .
This commit is contained in:
parent
c305d183c3
commit
29570728a3
5 changed files with 586 additions and 5 deletions
|
@ -6,6 +6,7 @@ library kernel.class_hierarchy;
|
|||
import 'ast.dart';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'src/heap.dart';
|
||||
import 'type_algebra.dart';
|
||||
|
||||
/// Data structure for answering various subclassing queries.
|
||||
|
@ -62,6 +63,155 @@ class ClassHierarchy {
|
|||
return _infoFor[class_].directImplementers.isNotEmpty;
|
||||
}
|
||||
|
||||
/// Returns the number of steps in the longest inheritance path from [class_]
|
||||
/// to [rootClass].
|
||||
int getClassDepth(Class class_) => _infoFor[class_].depth;
|
||||
|
||||
/// Returns a list of classes appropriate for use in calculating a least upper
|
||||
/// bound.
|
||||
///
|
||||
/// The returned list is a list of all classes that [class_] is a subtype of
|
||||
/// (including itself), sorted first by depth (deepest first) and then by
|
||||
/// class index.
|
||||
List<Class> getRankedSuperclasses(Class class_) {
|
||||
return _getRankedSuperclassInfos(_infoFor[class_])
|
||||
.map((info) => info.classNode)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<_ClassInfo> _getRankedSuperclassInfos(_ClassInfo info) {
|
||||
if (info.leastUpperBoundInfos != null) return info.leastUpperBoundInfos;
|
||||
var heap = new _LubHeap()..add(info);
|
||||
var chain = <_ClassInfo>[];
|
||||
info.leastUpperBoundInfos = chain;
|
||||
_ClassInfo lastInfo = null;
|
||||
while (heap.isNotEmpty) {
|
||||
var nextInfo = heap.remove();
|
||||
if (identical(nextInfo, lastInfo)) continue;
|
||||
chain.add(nextInfo);
|
||||
lastInfo = nextInfo;
|
||||
var classNode = nextInfo.classNode;
|
||||
void addToHeap(Supertype supertype) {
|
||||
heap.add(_infoFor[supertype.classNode]);
|
||||
}
|
||||
|
||||
if (classNode.supertype != null) addToHeap(classNode.supertype);
|
||||
if (classNode.mixedInType != null) addToHeap(classNode.mixedInType);
|
||||
classNode.implementedTypes.forEach(addToHeap);
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
/// Returns the least upper bound of two interface types, as defined by Dart
|
||||
/// 1.0.
|
||||
///
|
||||
/// Given two interfaces I and J, let S_I be the set of superinterfaces of I,
|
||||
/// let S_J be the set of superinterfaces of J, and let
|
||||
/// S = (I union S_I) intersect (J union S_J). Furthermore, we define
|
||||
/// S_n = {T | T in S and depth(T) = n} for any finite n where depth(T) is
|
||||
/// the number of steps in the longest inheritance path from T to Object. Let
|
||||
/// q be the largest number such that S_q has cardinality one. The least
|
||||
/// upper bound of I and J is the sole element of S_q.
|
||||
///
|
||||
/// This is called the "classic" least upper bound to distinguish it from the
|
||||
/// strong mode least upper bound, which has special behaviors in the case
|
||||
/// where one type is a subtype of the other, or where both types are based on
|
||||
/// the same class.
|
||||
InterfaceType getClassicLeastUpperBound(
|
||||
InterfaceType type1, InterfaceType type2) {
|
||||
// The algorithm is: first we compute a list of superclasses for both types,
|
||||
// ordered from greatest to least depth, and ordered by topological sort
|
||||
// index within each depth. Due to the sort order, we can find the
|
||||
// intersection of these lists by a simple walk.
|
||||
//
|
||||
// Then, for each class in the intersection, determine the exact type that
|
||||
// is implemented by type1 and type2. If the types match, that type is a
|
||||
// candidate (it's a member of S_n). As soon as we find a candidate which
|
||||
// is unique for its depth, we return it.
|
||||
//
|
||||
// As an optimization, if the class for I is a subtype of the class for J,
|
||||
// then we know that the list of superclasses of J is a subset of the list
|
||||
// of superclasses for I; therefore it is sufficient to compute just the
|
||||
// list of superclasses for J. To avoid complicating the code below (which
|
||||
// intersects the two lists), we set both lists equal to the list of
|
||||
// superclasses for J. And vice versa with the role of I and J swapped.
|
||||
|
||||
// Compute the list of superclasses for both types, with the above
|
||||
// optimization.
|
||||
_ClassInfo info1 = _infoFor[type1.classNode];
|
||||
_ClassInfo info2 = _infoFor[type2.classNode];
|
||||
List<_ClassInfo> classes1;
|
||||
List<_ClassInfo> classes2;
|
||||
if (identical(info1, info2) || info1.isSubtypeOf(info2)) {
|
||||
classes1 = classes2 = _getRankedSuperclassInfos(info2);
|
||||
} else if (info2.isSubtypeOf(info1)) {
|
||||
classes1 = classes2 = _getRankedSuperclassInfos(info1);
|
||||
} else {
|
||||
classes1 = _getRankedSuperclassInfos(info1);
|
||||
classes2 = _getRankedSuperclassInfos(info2);
|
||||
}
|
||||
|
||||
// Walk the lists finding their intersection, looking for a depth that has a
|
||||
// single candidate.
|
||||
int i1 = 0;
|
||||
int i2 = 0;
|
||||
InterfaceType candidate = null;
|
||||
int currentDepth = -1;
|
||||
int numCandidatesAtThisDepth = 0;
|
||||
while (true) {
|
||||
_ClassInfo next = classes1[i1];
|
||||
_ClassInfo next2 = classes2[i2];
|
||||
if (!identical(next, next2)) {
|
||||
if (_LubHeap.sortsBeforeStatic(next, next2)) {
|
||||
++i1;
|
||||
} else {
|
||||
++i2;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
++i2;
|
||||
++i1;
|
||||
if (next.depth != currentDepth) {
|
||||
if (numCandidatesAtThisDepth == 1) return candidate;
|
||||
currentDepth = next.depth;
|
||||
numCandidatesAtThisDepth = 0;
|
||||
candidate = null;
|
||||
} else if (numCandidatesAtThisDepth > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For each class in the intersection, find the exact type that is
|
||||
// implemented by type1 and type2. If they match, it's a candidate.
|
||||
//
|
||||
// Two additional optimizations:
|
||||
//
|
||||
// - If this class lacks type parameters, we know there is a match without
|
||||
// needing to substitute.
|
||||
//
|
||||
// - If the depth is 0, we have reached Object, so we can return it
|
||||
// immediately. Since all interface types are subtypes of Object, this
|
||||
// ensures the loop terminates.
|
||||
if (next.classNode.typeParameters.isEmpty) {
|
||||
candidate = next.classNode.rawType;
|
||||
if (currentDepth == 0) return candidate;
|
||||
++numCandidatesAtThisDepth;
|
||||
} else {
|
||||
var superType1 = identical(info1, next)
|
||||
? type1
|
||||
: Substitution.fromInterfaceType(type1).substituteType(
|
||||
info1.genericSuperTypes[next.classNode].asInterfaceType);
|
||||
var superType2 = identical(info2, next)
|
||||
? type2
|
||||
: Substitution.fromInterfaceType(type2).substituteType(
|
||||
info2.genericSuperTypes[next.classNode].asInterfaceType);
|
||||
if (superType1 == superType2) {
|
||||
candidate = superType1;
|
||||
++numCandidatesAtThisDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the instantiation of [superclass] that is implemented by [class_],
|
||||
/// or `null` if [class_] does not implement [superclass] at all.
|
||||
Supertype getClassAsInstanceOf(Class class_, Class superclass) {
|
||||
|
@ -286,27 +436,33 @@ class ClassHierarchy {
|
|||
|
||||
/// Upwards traversal of the class hierarchy that orders classes so super
|
||||
/// types before their subtypes.
|
||||
///
|
||||
/// Returns the depth of the visited class (the number of steps in the longest
|
||||
/// inheritance path to the root class).
|
||||
int _topSortIndex = 0;
|
||||
void _topologicalSortVisit(Class classNode) {
|
||||
int _topologicalSortVisit(Class classNode) {
|
||||
var info = _infoFor[classNode];
|
||||
if (info != null) {
|
||||
if (info.isBeingVisited) {
|
||||
throw 'Cyclic inheritance involving ${info.classNode.name}';
|
||||
}
|
||||
return; // Already built.
|
||||
return info.depth; // Already built.
|
||||
}
|
||||
int superDepth = -1;
|
||||
_infoFor[classNode] = info = new _ClassInfo(classNode);
|
||||
info.isBeingVisited = true;
|
||||
if (classNode.supertype != null) {
|
||||
_topologicalSortVisit(classNode.supertype.classNode);
|
||||
superDepth =
|
||||
max(superDepth, _topologicalSortVisit(classNode.supertype.classNode));
|
||||
_recordSuperTypes(info, classNode.supertype);
|
||||
}
|
||||
if (classNode.mixedInType != null) {
|
||||
_topologicalSortVisit(classNode.mixedInType.classNode);
|
||||
superDepth = max(
|
||||
superDepth, _topologicalSortVisit(classNode.mixedInType.classNode));
|
||||
_recordSuperTypes(info, classNode.mixedInType);
|
||||
}
|
||||
for (var supertype in classNode.implementedTypes) {
|
||||
_topologicalSortVisit(supertype.classNode);
|
||||
superDepth = max(superDepth, _topologicalSortVisit(supertype.classNode));
|
||||
_recordSuperTypes(info, supertype);
|
||||
}
|
||||
_buildDeclaredMembers(classNode, info);
|
||||
|
@ -315,6 +471,7 @@ class ClassHierarchy {
|
|||
info.topologicalIndex = id;
|
||||
classes[id] = info.classNode;
|
||||
info.isBeingVisited = false;
|
||||
return info.depth = superDepth + 1;
|
||||
}
|
||||
|
||||
void _buildDeclaredMembers(Class classNode, _ClassInfo info) {
|
||||
|
@ -791,6 +948,7 @@ class _ClassInfo {
|
|||
int topologicalIndex = 0;
|
||||
int topDownIndex = -1;
|
||||
bool isBeingVisited = false;
|
||||
int depth = 0;
|
||||
|
||||
// Super types must always occur before subtypes in these lists.
|
||||
// For example:
|
||||
|
@ -810,6 +968,8 @@ class _ClassInfo {
|
|||
Uint32List submixtureIntervalList;
|
||||
Uint32List subtypeIntervalList;
|
||||
|
||||
List<_ClassInfo> leastUpperBoundInfos;
|
||||
|
||||
bool isSubclassOf(_ClassInfo other) {
|
||||
return _intervalListContains(other.subclassIntervalList, topDownIndex);
|
||||
}
|
||||
|
@ -900,3 +1060,19 @@ class ClassSet {
|
|||
return new ClassSet(_hierarchy, builder.buildIntervalList());
|
||||
}
|
||||
}
|
||||
|
||||
/// Heap for use in computing least upper bounds.
|
||||
///
|
||||
/// The heap is sorted such that classes that are deepest in the hierarchy
|
||||
/// are removed first; in the case of ties, classes with lower topological sort
|
||||
/// index are removed first.
|
||||
class _LubHeap extends Heap<_ClassInfo> {
|
||||
@override
|
||||
bool sortsBefore(_ClassInfo a, _ClassInfo b) => sortsBeforeStatic(a, b);
|
||||
|
||||
static bool sortsBeforeStatic(_ClassInfo a, _ClassInfo b) {
|
||||
if (a.depth > b.depth) return true;
|
||||
if (a.depth < b.depth) return false;
|
||||
return a.topologicalIndex < b.topologicalIndex;
|
||||
}
|
||||
}
|
||||
|
|
62
pkg/kernel/lib/src/heap.dart
Normal file
62
pkg/kernel/lib/src/heap.dart
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Copyright (c) 2017, 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.
|
||||
|
||||
/// Basic implementation of a heap, with O(log n) insertion and removal.
|
||||
abstract class Heap<T> {
|
||||
final _items = <T>[];
|
||||
|
||||
bool get isEmpty => _items.isEmpty;
|
||||
|
||||
bool get isNotEmpty => _items.isNotEmpty;
|
||||
|
||||
void add(T item) {
|
||||
int index = _items.length;
|
||||
_items.length += 1;
|
||||
while (index > 0) {
|
||||
T parent = _items[_parentIndex(index)];
|
||||
if (sortsBefore(parent, item)) break;
|
||||
_items[index] = parent;
|
||||
index = _parentIndex(index);
|
||||
}
|
||||
_items[index] = item;
|
||||
}
|
||||
|
||||
T remove() {
|
||||
T removed = _items[0];
|
||||
T orphan = _items.removeLast();
|
||||
if (_items.isNotEmpty) _reInsert(orphan);
|
||||
return removed;
|
||||
}
|
||||
|
||||
/// Client should use a derived class to specify the sort order.
|
||||
bool sortsBefore(T a, T b);
|
||||
|
||||
int _firstChildIndex(int index) {
|
||||
return (index << 1) + 1;
|
||||
}
|
||||
|
||||
int _parentIndex(int index) {
|
||||
return (index - 1) >> 1;
|
||||
}
|
||||
|
||||
void _reInsert(T item) {
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int childIndex = _firstChildIndex(index);
|
||||
if (childIndex >= _items.length) break;
|
||||
T child = _items[childIndex];
|
||||
if (childIndex + 1 < _items.length) {
|
||||
T nextChild = _items[childIndex + 1];
|
||||
if (sortsBefore(nextChild, child)) {
|
||||
child = nextChild;
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
if (sortsBefore(item, child)) break;
|
||||
_items[index] = _items[childIndex];
|
||||
index = childIndex;
|
||||
}
|
||||
_items[index] = item;
|
||||
}
|
||||
}
|
66
pkg/kernel/lib/testing/mock_sdk_program.dart
Normal file
66
pkg/kernel/lib/testing/mock_sdk_program.dart
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) 2017, 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 'package:kernel/ast.dart';
|
||||
|
||||
/// Returns a [Program] object containing empty definitions of core SDK classes.
|
||||
Program createMockSdkProgram() {
|
||||
var coreLib = new Library(Uri.parse('dart:core'));
|
||||
var asyncLib = new Library(Uri.parse('dart:async'));
|
||||
var internalLib = new Library(Uri.parse('dart:_internal'));
|
||||
|
||||
Class addClass(Library lib, Class c) {
|
||||
lib.addClass(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
var objectClass = addClass(coreLib, new Class(name: 'Object'));
|
||||
var objectType = objectClass.rawType;
|
||||
|
||||
TypeParameter typeParam(String name, [DartType bound]) {
|
||||
return new TypeParameter(name, bound ?? objectType);
|
||||
}
|
||||
|
||||
Class class_(String name,
|
||||
{Supertype supertype,
|
||||
List<TypeParameter> typeParameters,
|
||||
List<Supertype> implementedTypes}) {
|
||||
return new Class(
|
||||
name: name,
|
||||
supertype: supertype ?? objectClass.asThisSupertype,
|
||||
typeParameters: typeParameters,
|
||||
implementedTypes: implementedTypes);
|
||||
}
|
||||
|
||||
addClass(coreLib, class_('Null'));
|
||||
addClass(coreLib, class_('bool'));
|
||||
var num = addClass(coreLib, class_('num'));
|
||||
addClass(coreLib, class_('String'));
|
||||
var iterable =
|
||||
addClass(coreLib, class_('Iterable', typeParameters: [typeParam('T')]));
|
||||
{
|
||||
var T = typeParam('T');
|
||||
addClass(
|
||||
coreLib,
|
||||
class_('List', typeParameters: [
|
||||
T
|
||||
], implementedTypes: [
|
||||
new Supertype(iterable, [new TypeParameterType(T)])
|
||||
]));
|
||||
}
|
||||
addClass(
|
||||
coreLib, class_('Map', typeParameters: [typeParam('K'), typeParam('V')]));
|
||||
addClass(coreLib, class_('int', supertype: num.asThisSupertype));
|
||||
addClass(coreLib, class_('double', supertype: num.asThisSupertype));
|
||||
addClass(coreLib, class_('Iterator', typeParameters: [typeParam('T')]));
|
||||
addClass(coreLib, class_('Symbol'));
|
||||
addClass(coreLib, class_('Type'));
|
||||
addClass(coreLib, class_('Function'));
|
||||
addClass(coreLib, class_('Invocation'));
|
||||
addClass(asyncLib, class_('Future', typeParameters: [typeParam('T')]));
|
||||
addClass(asyncLib, class_('Stream', typeParameters: [typeParam('T')]));
|
||||
addClass(internalLib, class_('Symbol'));
|
||||
|
||||
return new Program([coreLib, asyncLib, internalLib]);
|
||||
}
|
38
pkg/kernel/test/heap_test.dart
Normal file
38
pkg/kernel/test/heap_test.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2017, 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 'package:kernel/src/heap.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
check_sort(Iterable<int> initialOrder) {
|
||||
var values = initialOrder.toList();
|
||||
var heap = new _intHeap();
|
||||
values.forEach(heap.add);
|
||||
values.sort();
|
||||
var result = <int>[];
|
||||
while (heap.isNotEmpty) {
|
||||
expect(heap.isEmpty, isFalse);
|
||||
result.add(heap.remove());
|
||||
}
|
||||
expect(heap.isEmpty, isTrue);
|
||||
expect(result, values);
|
||||
}
|
||||
|
||||
test('sort', () {
|
||||
check_sort(<int>[3, 1, 4, 1, 5, 9, 2, 6]);
|
||||
});
|
||||
|
||||
test('sort_already_sorted', () {
|
||||
check_sort(<int>[1, 1, 2, 3, 4, 5, 6, 9]);
|
||||
});
|
||||
|
||||
test('sort_reverse_sorted', () {
|
||||
check_sort(<int>[9, 6, 5, 4, 3, 2, 1, 1]);
|
||||
});
|
||||
}
|
||||
|
||||
class _intHeap extends Heap<int> {
|
||||
bool sortsBefore(int a, int b) => a < b;
|
||||
}
|
239
pkg/kernel/test/lub_test.dart
Normal file
239
pkg/kernel/test/lub_test.dart
Normal file
|
@ -0,0 +1,239 @@
|
|||
// Copyright (c) 2017, 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 'package:kernel/ast.dart';
|
||||
import 'package:kernel/class_hierarchy.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
import 'package:kernel/testing/mock_sdk_program.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
Library makeTestLibrary(Program program) {
|
||||
var library = new Library(Uri.parse('org-dartlang:///test.dart'))
|
||||
..parent = program;
|
||||
program.libraries.add(library);
|
||||
return library;
|
||||
}
|
||||
|
||||
test('depth', () {
|
||||
var program = createMockSdkProgram();
|
||||
var coreTypes = new CoreTypes(program);
|
||||
var defaultSuper = coreTypes.objectClass.asThisSupertype;
|
||||
var library = makeTestLibrary(program);
|
||||
|
||||
Class addClass(Class c) {
|
||||
library.addClass(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
var base = addClass(new Class(name: 'base', supertype: defaultSuper));
|
||||
var extends_ =
|
||||
addClass(new Class(name: 'extends_', supertype: base.asThisSupertype));
|
||||
var with_ = addClass(new Class(
|
||||
name: 'with_',
|
||||
supertype: defaultSuper,
|
||||
mixedInType: base.asThisSupertype));
|
||||
var implements_ = addClass(new Class(
|
||||
name: 'implements_',
|
||||
supertype: defaultSuper,
|
||||
implementedTypes: [base.asThisSupertype]));
|
||||
var hierarchy = new ClassHierarchy(program);
|
||||
|
||||
expect(hierarchy.getClassDepth(coreTypes.objectClass), 0);
|
||||
expect(hierarchy.getClassDepth(base), 1);
|
||||
expect(hierarchy.getClassDepth(extends_), 2);
|
||||
expect(hierarchy.getClassDepth(with_), 2);
|
||||
expect(hierarchy.getClassDepth(implements_), 2);
|
||||
});
|
||||
|
||||
test('ranked_superclasses', () {
|
||||
var program = createMockSdkProgram();
|
||||
var coreTypes = new CoreTypes(program);
|
||||
var defaultSuper = coreTypes.objectClass.asThisSupertype;
|
||||
var library = makeTestLibrary(program);
|
||||
|
||||
Class addClass(String name, List<Class> implements_) {
|
||||
var c = new Class(
|
||||
name: name,
|
||||
supertype: defaultSuper,
|
||||
implementedTypes: implements_.map((c) => c.asThisSupertype).toList());
|
||||
library.addClass(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Create the class hierarchy:
|
||||
//
|
||||
// Object
|
||||
// |
|
||||
// A
|
||||
// / \
|
||||
// B C
|
||||
// | |
|
||||
// | D
|
||||
// \ /
|
||||
// E
|
||||
var a = addClass('A', []);
|
||||
var b = addClass('B', [a]);
|
||||
var c = addClass('C', [a]);
|
||||
var d = addClass('D', [c]);
|
||||
var e = addClass('E', [b, d]);
|
||||
var hierarchy = new ClassHierarchy(program);
|
||||
|
||||
expect(hierarchy.getRankedSuperclasses(a), [a, coreTypes.objectClass]);
|
||||
expect(hierarchy.getRankedSuperclasses(b), [b, a, coreTypes.objectClass]);
|
||||
expect(hierarchy.getRankedSuperclasses(c), [c, a, coreTypes.objectClass]);
|
||||
expect(
|
||||
hierarchy.getRankedSuperclasses(d), [d, c, a, coreTypes.objectClass]);
|
||||
if (hierarchy.getClassIndex(b) < hierarchy.getClassIndex(c)) {
|
||||
expect(hierarchy.getRankedSuperclasses(e),
|
||||
[e, d, b, c, a, coreTypes.objectClass]);
|
||||
} else {
|
||||
expect(hierarchy.getRankedSuperclasses(e),
|
||||
[e, d, c, b, a, coreTypes.objectClass]);
|
||||
}
|
||||
});
|
||||
|
||||
test('least_upper_bound_non_generic', () {
|
||||
var program = createMockSdkProgram();
|
||||
var coreTypes = new CoreTypes(program);
|
||||
var defaultSuper = coreTypes.objectClass.asThisSupertype;
|
||||
var library = makeTestLibrary(program);
|
||||
|
||||
Class addClass(String name, List<Class> implements_) {
|
||||
var c = new Class(
|
||||
name: name,
|
||||
supertype: defaultSuper,
|
||||
implementedTypes: implements_.map((c) => c.asThisSupertype).toList());
|
||||
library.addClass(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Create the class hierarchy:
|
||||
//
|
||||
// Object
|
||||
// / \
|
||||
// A B
|
||||
// /|\
|
||||
// C D E
|
||||
// |X|/
|
||||
// FG HI
|
||||
//
|
||||
// (F and G both implement (C, D); H and I both implement (C, D, E).
|
||||
var a = addClass('A', []);
|
||||
var b = addClass('B', []);
|
||||
var c = addClass('C', [a]);
|
||||
var d = addClass('D', [a]);
|
||||
var e = addClass('E', [a]);
|
||||
var f = addClass('F', [c, d]);
|
||||
var g = addClass('G', [c, d]);
|
||||
var h = addClass('H', [c, d, e]);
|
||||
var i = addClass('I', [c, d, e]);
|
||||
var hierarchy = new ClassHierarchy(program);
|
||||
|
||||
expect(hierarchy.getClassicLeastUpperBound(a.rawType, b.rawType),
|
||||
coreTypes.objectClass.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(
|
||||
a.rawType, coreTypes.objectClass.rawType),
|
||||
coreTypes.objectClass.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(
|
||||
coreTypes.objectClass.rawType, b.rawType),
|
||||
coreTypes.objectClass.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(c.rawType, d.rawType), a.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(c.rawType, a.rawType), a.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(a.rawType, d.rawType), a.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(f.rawType, g.rawType), a.rawType);
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(h.rawType, i.rawType), a.rawType);
|
||||
});
|
||||
|
||||
test('least_upper_bound_non_generic', () {
|
||||
var program = createMockSdkProgram();
|
||||
var coreTypes = new CoreTypes(program);
|
||||
var defaultSuper = coreTypes.objectClass.asThisSupertype;
|
||||
var library = makeTestLibrary(program);
|
||||
var int = coreTypes.intClass.rawType;
|
||||
var double = coreTypes.doubleClass.rawType;
|
||||
var bool = coreTypes.boolClass.rawType;
|
||||
|
||||
Class addClass(String name, List<String> typeParameterNames,
|
||||
List<Supertype> implements_(List<DartType> typeParameterTypes)) {
|
||||
var typeParameters = typeParameterNames
|
||||
.map((name) => new TypeParameter(name, coreTypes.objectClass.rawType))
|
||||
.toList();
|
||||
var typeParameterTypes = typeParameters
|
||||
.map((parameter) => new TypeParameterType(parameter))
|
||||
.toList();
|
||||
var c = new Class(
|
||||
name: name,
|
||||
typeParameters: typeParameters,
|
||||
supertype: defaultSuper,
|
||||
implementedTypes: implements_(typeParameterTypes));
|
||||
library.addClass(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Create the class hierarchy:
|
||||
//
|
||||
// Object
|
||||
// |
|
||||
// A
|
||||
// / \
|
||||
// B<T> C<U>
|
||||
// \ /
|
||||
// D<T,U>
|
||||
// / \
|
||||
// E F
|
||||
//
|
||||
// Where E implements D<int, double> and F implements D<int, bool>.
|
||||
var a = addClass('A', [], (_) => []);
|
||||
var b = addClass('B', ['T'], (_) => [a.asThisSupertype]);
|
||||
var c = addClass('C', ['U'], (_) => [a.asThisSupertype]);
|
||||
var d = addClass('D', ['T', 'U'], (typeParameterTypes) {
|
||||
var t = typeParameterTypes[0];
|
||||
var u = typeParameterTypes[1];
|
||||
return [
|
||||
new Supertype(b, [t]),
|
||||
new Supertype(c, [u])
|
||||
];
|
||||
});
|
||||
var e = addClass(
|
||||
'E',
|
||||
[],
|
||||
(_) => [
|
||||
new Supertype(d, [int, double])
|
||||
]);
|
||||
var f = addClass(
|
||||
'F',
|
||||
[],
|
||||
(_) => [
|
||||
new Supertype(d, [int, bool])
|
||||
]);
|
||||
var hierarchy = new ClassHierarchy(program);
|
||||
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
||||
new InterfaceType(d, [int, double])),
|
||||
new InterfaceType(d, [int, double]));
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
||||
new InterfaceType(d, [int, bool])),
|
||||
new InterfaceType(b, [int]));
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
||||
new InterfaceType(d, [bool, double])),
|
||||
new InterfaceType(c, [double]));
|
||||
expect(
|
||||
hierarchy.getClassicLeastUpperBound(new InterfaceType(d, [int, double]),
|
||||
new InterfaceType(d, [bool, int])),
|
||||
a.rawType);
|
||||
expect(hierarchy.getClassicLeastUpperBound(e.rawType, f.rawType),
|
||||
new InterfaceType(b, [int]));
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue