blob: b40523a113b539b808466c4a152c900844353e7c [file] [log] [blame]
// Copyright (c) 2021, 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 fasta.class_hierarchy_builder;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart' show ClassHierarchyBase;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/src/types.dart' show Types;
import 'package:kernel/type_algebra.dart' show Substitution, uniteNullabilities;
import '../../builder/class_builder.dart';
import '../../builder/type_builder.dart';
import '../../loader.dart' show Loader;
import '../../source/source_loader.dart' show SourceLoader;
import 'hierarchy_node.dart';
class ClassHierarchyBuilder implements ClassHierarchyBase {
final Map<Class, ClassHierarchyNode> nodes = <Class, ClassHierarchyNode>{};
final Map<ClassBuilder, Map<Class, Substitution>> substitutions =
<ClassBuilder, Map<Class, Substitution>>{};
final ClassBuilder objectClassBuilder;
final Loader loader;
final Class objectClass;
final Class futureClass;
final Class functionClass;
@override
final CoreTypes coreTypes;
late Types types;
ClassHierarchyBuilder(this.objectClassBuilder, this.loader, this.coreTypes)
: objectClass = objectClassBuilder.cls,
futureClass = coreTypes.futureClass,
functionClass = coreTypes.functionClass {
types = new Types(this);
}
void clear() {
nodes.clear();
substitutions.clear();
}
ClassHierarchyNode getNodeFromClassBuilder(ClassBuilder classBuilder) {
return nodes[classBuilder.cls] ??= new ClassHierarchyNodeBuilder(
this, classBuilder, substitutions[classBuilder] ??= {})
.build();
}
ClassHierarchyNode? getNodeFromTypeBuilder(TypeBuilder type) {
ClassBuilder? cls = getClass(type);
return cls == null ? null : getNodeFromClassBuilder(cls);
}
ClassHierarchyNode getNodeFromClass(Class cls) {
return nodes[cls] ??
getNodeFromClassBuilder(loader.computeClassBuilderFromTargetClass(cls));
}
Supertype? asSupertypeOf(InterfaceType subtype, Class supertype) {
if (subtype.classNode == supertype) {
return new Supertype(supertype, subtype.typeArguments);
}
ClassHierarchyNode clsNode = getNodeFromClass(subtype.classNode);
ClassHierarchyNode supertypeNode = getNodeFromClass(supertype);
List<Supertype> superclasses = clsNode.superclasses;
int depth = supertypeNode.depth;
if (depth < superclasses.length) {
Supertype superclass = superclasses[depth];
if (superclass.classNode == supertype) {
return Substitution.fromInterfaceType(subtype)
.substituteSupertype(superclass);
}
}
List<Supertype> superinterfaces = clsNode.interfaces;
for (int i = 0; i < superinterfaces.length; i++) {
Supertype superinterface = superinterfaces[i];
if (superinterface.classNode == supertype) {
return Substitution.fromInterfaceType(subtype)
.substituteSupertype(superinterface);
}
}
return null;
}
@override
InterfaceType getTypeAsInstanceOf(
InterfaceType type, Class superclass, Library clientLibrary) {
if (type.classNode == superclass) return type;
return asSupertypeOf(type, superclass)!
.asInterfaceType
.withDeclaredNullability(type.nullability);
}
@override
List<DartType>? getTypeArgumentsAsInstanceOf(
InterfaceType type, Class superclass) {
if (type.classNode == superclass) return type.typeArguments;
return asSupertypeOf(type, superclass)?.typeArguments;
}
@override
InterfaceType getLegacyLeastUpperBound(
InterfaceType type1, InterfaceType type2, Library clientLibrary) {
if (type1 == type2) return type1;
// LLUB(Null, List<dynamic>*) works differently for opt-in and opt-out
// libraries. In opt-out libraries the legacy behavior is preserved, so
// LLUB(Null, List<dynamic>*) = List<dynamic>*. In opt-out libraries the
// rules imply that LLUB(Null, List<dynamic>*) = List<dynamic>?.
if (!clientLibrary.isNonNullableByDefault) {
if (type1 is NullType) {
return type2;
}
if (type2 is NullType) {
return type1;
}
}
ClassHierarchyNode node1 = getNodeFromClass(type1.classNode);
ClassHierarchyNode node2 = getNodeFromClass(type2.classNode);
Set<ClassHierarchyNode> nodes1 = node1.computeAllSuperNodes(this).toSet();
List<ClassHierarchyNode> nodes2 = node2.computeAllSuperNodes(this);
List<ClassHierarchyNode> common = <ClassHierarchyNode>[];
for (int i = 0; i < nodes2.length; i++) {
ClassHierarchyNode node = nodes2[i];
// ignore: unnecessary_null_comparison
if (node == null) continue;
if (node.classBuilder.cls.isAnonymousMixin) {
// Never find unnamed mixin application in least upper bound.
continue;
}
if (nodes1.contains(node)) {
DartType candidate1 =
getTypeAsInstanceOf(type1, node.classBuilder.cls, clientLibrary);
DartType candidate2 =
getTypeAsInstanceOf(type2, node.classBuilder.cls, clientLibrary);
if (candidate1 == candidate2) {
common.add(node);
}
}
}
if (common.length == 1) {
return coreTypes.objectRawType(
uniteNullabilities(type1.nullability, type2.nullability));
}
common.sort(ClassHierarchyNode.compareMaxInheritancePath);
for (int i = 0; i < common.length - 1; i++) {
ClassHierarchyNode node = common[i];
if (node.maxInheritancePath != common[i + 1].maxInheritancePath) {
return getTypeAsInstanceOf(type1, node.classBuilder.cls, clientLibrary)
.withDeclaredNullability(
uniteNullabilities(type1.nullability, type2.nullability));
} else {
do {
i++;
} while (node.maxInheritancePath == common[i + 1].maxInheritancePath);
}
}
return coreTypes.objectRawType(
uniteNullabilities(type1.nullability, type2.nullability));
}
static ClassHierarchyBuilder build(ClassBuilder objectClass,
List<ClassBuilder> classes, SourceLoader loader, CoreTypes coreTypes) {
ClassHierarchyBuilder hierarchy =
new ClassHierarchyBuilder(objectClass, loader, coreTypes);
for (int i = 0; i < classes.length; i++) {
ClassBuilder classBuilder = classes[i];
if (!classBuilder.isPatch) {
hierarchy.nodes[classBuilder.cls] = new ClassHierarchyNodeBuilder(
hierarchy,
classBuilder,
hierarchy.substitutions[classBuilder] ??= {})
.build();
} else {
// TODO(ahe): Merge the injected members of patch into the hierarchy
// node of `cls.origin`.
}
}
return hierarchy;
}
}