| // 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. | |
| import 'package:front_end/src/codes/diagnostic.dart' as diag; | |
| import 'package:kernel/ast.dart'; | |
| import 'package:kernel/src/nnbd_top_merge.dart'; | |
| import 'package:kernel/src/norm.dart'; | |
| import 'package:kernel/type_algebra.dart'; | |
| import '../../builder/builder.dart'; | |
| import '../../builder/declaration_builders.dart'; | |
| import '../../builder/library_builder.dart'; | |
| import '../../builder/type_builder.dart'; | |
| import '../../codes/cfe_codes.dart'; | |
| import '../../source/source_class_builder.dart'; | |
| import '../../source/source_library_builder.dart'; | |
| import '../../testing/id_testing_utils.dart' show typeToText; | |
| import '../../type_inference/type_schema.dart' show UnknownType; | |
| import 'hierarchy_builder.dart'; | |
| import 'mixin_inferrer.dart'; | |
| abstract class HierarchyNodeBuilder { | |
| final ClassHierarchyBuilder _hierarchy; | |
| new(this._hierarchy); | |
| LibraryBuilder get _libraryBuilder; | |
| String get _name; | |
| int get _fileOffset; | |
| Uri get _fileUri; | |
| Supertype _resolveSupertypeConflict(Supertype type, Supertype superclass) { | |
| Supertype? merge = nnbdTopMergeSupertype( | |
| _hierarchy.coreTypes, | |
| normSupertype(_hierarchy.coreTypes, superclass), | |
| normSupertype(_hierarchy.coreTypes, type), | |
| ); | |
| if (merge != null) { | |
| return merge; | |
| } | |
| LibraryBuilder libraryBuilder = _libraryBuilder; | |
| if (libraryBuilder is SourceLibraryBuilder) { | |
| libraryBuilder.addProblem( | |
| diag.ambiguousSupertypes.withArguments( | |
| name: _name, | |
| type1: superclass.asInterfaceType, | |
| type2: type.asInterfaceType, | |
| ), | |
| _fileOffset, | |
| noLength, | |
| _fileUri, | |
| ); | |
| } | |
| return superclass; | |
| } | |
| List<Supertype> _substSupertypes( | |
| TypeDeclarationType supertype, | |
| List<Supertype> supertypes, | |
| ) { | |
| List<TypeParameter> typeParameters = | |
| supertype.typeDeclaration.typeParameters; | |
| if (typeParameters.isEmpty) { | |
| return supertypes; | |
| } | |
| Map<TypeParameter, DartType> map = <TypeParameter, DartType>{}; | |
| List<DartType> arguments = supertype.typeArguments; | |
| for (int i = 0; i < typeParameters.length; i++) { | |
| map[typeParameters[i]] = arguments[i]; | |
| } | |
| Substitution substitution = Substitution.fromMap(map); | |
| List<Supertype>? result; | |
| for (int i = 0; i < supertypes.length; i++) { | |
| Supertype supertype = supertypes[i]; | |
| Supertype substituted = substitution.substituteSupertype(supertype); | |
| if (supertype != substituted) { | |
| result ??= supertypes.toList(); | |
| result[i] = substituted; | |
| } | |
| } | |
| return result ?? supertypes; | |
| } | |
| /// The class Function from dart:core is supposed to be ignored when used as | |
| /// an interface. | |
| List<TypeBuilder>? _ignoreFunction(List<TypeBuilder>? interfaces) { | |
| if (interfaces == null) return null; | |
| for (int i = 0; i < interfaces!.length; i++) { | |
| ClassBuilder? classBuilder = getClass(interfaces[i]); | |
| if (classBuilder != null && | |
| classBuilder.cls == _hierarchy.functionClass) { | |
| if (interfaces.length == 1) { | |
| return null; | |
| } else { | |
| interfaces = interfaces.toList(); | |
| interfaces.removeAt(i); | |
| return _ignoreFunction(interfaces); | |
| } | |
| } | |
| } | |
| return interfaces; | |
| } | |
| } | |
| class ClassHierarchyNodeBuilder extends HierarchyNodeBuilder { | |
| final ClassBuilder _classBuilder; | |
| new(super.hierarchy, this._classBuilder); | |
| ClassBuilder get _objectClass => _hierarchy.objectClassBuilder; | |
| @override | |
| LibraryBuilder get _libraryBuilder => _classBuilder.libraryBuilder; | |
| @override | |
| String get _name => _classBuilder.name; | |
| @override | |
| int get _fileOffset => _classBuilder.fileOffset; | |
| @override | |
| Uri get _fileUri => _classBuilder.fileUri; | |
| ClassHierarchyNode build() { | |
| ClassHierarchyNode? supernode; | |
| if (_objectClass != _classBuilder) { | |
| ClassBuilder? superClassBuilder = getClass( | |
| _classBuilder.supertypeBuilder!, | |
| ); | |
| supernode = _hierarchy.getNodeFromClassBuilder( | |
| superClassBuilder ?? _objectClass, | |
| ); | |
| } | |
| List<Supertype> superclasses; | |
| List<Supertype> interfacesList; | |
| int maxInheritancePath; | |
| ClassHierarchyNode? mixedInNode; | |
| List<ClassHierarchyNode>? interfaceNodes; | |
| if (_classBuilder.isMixinApplication) { | |
| mixedInNode = _inferMixinApplication(); | |
| } | |
| if (supernode == null) { | |
| // This should be Object. | |
| superclasses = new List<Supertype>.filled(0, dummySupertype); | |
| interfacesList = new List<Supertype>.filled(0, dummySupertype); | |
| maxInheritancePath = 0; | |
| } else { | |
| maxInheritancePath = supernode.maxInheritancePath + 1; | |
| superclasses = new List<Supertype>.filled( | |
| supernode.superclasses.length + 1, | |
| dummySupertype, | |
| ); | |
| Supertype? supertype = _classBuilder.supertypeBuilder!.buildSupertype( | |
| _classBuilder.libraryBuilder, | |
| TypeUse.classExtendsType, | |
| ); | |
| if (supertype == null) { | |
| // If the superclass is not an interface type we use Object instead. | |
| // A similar normalization is performed on [supernode] above. | |
| supertype = new Supertype( | |
| _hierarchy.coreTypes.objectClass, | |
| DartTypeList.empty, | |
| ); | |
| } | |
| InterfaceType superTypeAsInterfaceType = supertype.asInterfaceType; | |
| superclasses.setRange( | |
| 0, | |
| superclasses.length - 1, | |
| _substSupertypes(superTypeAsInterfaceType, supernode.superclasses), | |
| ); | |
| superclasses[superclasses.length - 1] = supertype; | |
| List<TypeBuilder>? directInterfaceBuilders = _ignoreFunction( | |
| _classBuilder.interfaceBuilders, | |
| ); | |
| if (_classBuilder.isMixinApplication) { | |
| if (directInterfaceBuilders == null) { | |
| directInterfaceBuilders = <TypeBuilder>[ | |
| _classBuilder.mixedInTypeBuilder!, | |
| ]; | |
| } else { | |
| directInterfaceBuilders = <TypeBuilder>[ | |
| _classBuilder.mixedInTypeBuilder!, | |
| ]..addAll(directInterfaceBuilders); | |
| } | |
| } | |
| List<Supertype> superclassInterfaces = supernode.interfaces; | |
| if (superclassInterfaces.isNotEmpty) { | |
| superclassInterfaces = _substSupertypes( | |
| superTypeAsInterfaceType, | |
| superclassInterfaces, | |
| ); | |
| } | |
| if (directInterfaceBuilders != null) { | |
| Map<Class, Supertype> interfaces = {}; | |
| if (superclassInterfaces.isNotEmpty) { | |
| for (int i = 0; i < superclassInterfaces.length; i++) { | |
| _addInterface(interfaces, superclasses, superclassInterfaces[i]); | |
| } | |
| } | |
| for (int i = 0; i < directInterfaceBuilders.length; i++) { | |
| Supertype? directInterface = directInterfaceBuilders[i] | |
| .buildSupertype( | |
| _classBuilder.libraryBuilder, | |
| _classBuilder.isMixinApplication | |
| ? TypeUse.classWithType | |
| : TypeUse.classImplementsType, | |
| ); | |
| if (directInterface != null) { | |
| _addInterface(interfaces, superclasses, directInterface); | |
| ClassHierarchyNode interfaceNode = _hierarchy.getNodeFromClass( | |
| directInterface.classNode, | |
| ); | |
| (interfaceNodes ??= []).add(interfaceNode); | |
| if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) { | |
| maxInheritancePath = interfaceNode.maxInheritancePath + 1; | |
| } | |
| InterfaceType directInterfaceAsInterfaceType = | |
| directInterface.asInterfaceType; | |
| List<Supertype> types = _substSupertypes( | |
| directInterfaceAsInterfaceType, | |
| interfaceNode.superclasses, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addInterface(interfaces, superclasses, types[i]); | |
| } | |
| if (interfaceNode.interfaces.isNotEmpty) { | |
| List<Supertype> types = _substSupertypes( | |
| directInterfaceAsInterfaceType, | |
| interfaceNode.interfaces, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addInterface(interfaces, superclasses, types[i]); | |
| } | |
| } | |
| } | |
| } | |
| interfacesList = interfaces.values.toList(); | |
| } else { | |
| interfacesList = superclassInterfaces; | |
| } | |
| } | |
| return new ClassHierarchyNode( | |
| _classBuilder, | |
| supernode, | |
| mixedInNode, | |
| interfaceNodes, | |
| superclasses, | |
| interfacesList, | |
| maxInheritancePath, | |
| ); | |
| } | |
| void _addInterface( | |
| Map<Class, Supertype> interfaces, | |
| List<Supertype> superclasses, | |
| Supertype type, | |
| ) { | |
| ClassHierarchyNode node = _hierarchy.getNodeFromClass(type.classNode); | |
| int depth = node.depth; | |
| int myDepth = superclasses.length; | |
| Supertype? superclass = depth < myDepth ? superclasses[depth] : null; | |
| if (superclass != null && superclass.classNode == type.classNode) { | |
| // This is a potential conflict. | |
| superclasses[depth] = _resolveSupertypeConflict(type, superclass); | |
| return; | |
| } else { | |
| Supertype? interface = interfaces[type.classNode]; | |
| if (interface != null) { | |
| // This is a potential conflict. | |
| interfaces[type.classNode] = _resolveSupertypeConflict(type, interface); | |
| return; | |
| } | |
| } | |
| interfaces[type.classNode] = type; | |
| } | |
| ClassHierarchyNode? _inferMixinApplication() { | |
| Class cls = _classBuilder.cls; | |
| Supertype? mixedInSupertype = cls.mixedInType; | |
| if (mixedInSupertype == null) return null; | |
| ClassHierarchyNode? mixinNode = _hierarchy.getNodeFromClass( | |
| mixedInSupertype.classNode, | |
| ); | |
| List<DartType> typeArguments = mixedInSupertype.typeArguments; | |
| if (typeArguments.isEmpty || typeArguments.first is! UnknownType) { | |
| return mixinNode; | |
| } | |
| ClassBuilder classBuilder = _classBuilder; | |
| if (classBuilder is SourceClassBuilder) { | |
| new BuilderMixinInferrer( | |
| classBuilder, | |
| _hierarchy, | |
| mixedInSupertype.classNode.typeParameters, | |
| ).infer(cls); | |
| classBuilder.setInferredMixedInTypeArguments( | |
| new List<TypeBuilder>.generate( | |
| typeArguments.length, | |
| (int i) => _hierarchy.loader.computeTypeBuilder(typeArguments[i]), | |
| growable: false, | |
| ), | |
| ); | |
| } | |
| return mixinNode; | |
| } | |
| } | |
| class ClassHierarchyNode( | |
| /// The class corresponding to this hierarchy node. | |
| final ClassBuilder classBuilder, | |
| /// The [ClassHierarchyNode] for the direct super class of [classBuilder], or | |
| /// `null` if this is `Object`. | |
| final ClassHierarchyNode? directSuperClassNode, | |
| /// The [ClassHierarchyNode] for the mixed in class, if [classBuilder] is a | |
| /// mixin application, or `null` otherwise; | |
| final ClassHierarchyNode? mixedInNode, | |
| /// The [ClassHierarchyNode]s for the direct super interfaces of | |
| /// [classBuilder]. | |
| final List<ClassHierarchyNode>? directInterfaceNodes, | |
| /// All superclasses of [classBuilder] excluding itself. The classes are | |
| /// sorted by depth from the root (Object) in ascending order. | |
| final List<Supertype> superclasses, | |
| /// The list of all classes implemented by [classBuilder] and its supertypes | |
| /// excluding any classes from [superclasses]. | |
| final List<Supertype> interfaces, | |
| /// The longest inheritance path from [classBuilder] to `Object`. | |
| final int maxInheritancePath, | |
| ) { | |
| int get depth => superclasses.length; | |
| /// Returns `true` if [classBuilder] is a mixin application. | |
| /// | |
| /// If `true`, [mixedInNode] is non-null. | |
| bool get isMixinApplication => mixedInNode != null; | |
| // Coverage-ignore(suite): Not run. | |
| /// Returns a list of all supertypes of [classBuilder], including this node. | |
| List<ClassHierarchyNode> computeAllSuperNodes( | |
| ClassHierarchyBuilder hierarchy, | |
| ) { | |
| List<ClassHierarchyNode> result = []; | |
| for (int i = 0; i < superclasses.length; i++) { | |
| Supertype type = superclasses[i]; | |
| result.add(hierarchy.getNodeFromClass(type.classNode)); | |
| } | |
| for (int i = 0; i < interfaces.length; i++) { | |
| Supertype type = interfaces[i]; | |
| result.add(hierarchy.getNodeFromClass(type.classNode)); | |
| } | |
| result.add(this); | |
| return result; | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = new StringBuffer(); | |
| sb | |
| ..write(classBuilder.fullNameForErrors) | |
| ..writeln(":"); | |
| if (maxInheritancePath != this.depth) { | |
| sb | |
| ..write(" Longest path to Object: ") | |
| ..writeln(maxInheritancePath); | |
| } | |
| sb..writeln(" superclasses:"); | |
| int depth = 0; | |
| for (Supertype superclass in superclasses) { | |
| sb.write(" " * (depth + 2)); | |
| if (depth != 0) sb.write("-> "); | |
| sb.write(typeToText(superclass.asInterfaceType)); | |
| sb.writeln(); | |
| depth++; | |
| } | |
| if (interfaces.isNotEmpty) { | |
| sb.write(" interfaces:"); | |
| bool first = true; | |
| for (Supertype i in interfaces) { | |
| if (!first) sb.write(","); | |
| sb.write(" "); | |
| sb.write(typeToText(i.asInterfaceType)); | |
| first = false; | |
| } | |
| sb.writeln(); | |
| } | |
| return "$sb"; | |
| } | |
| // Coverage-ignore(suite): Not run. | |
| static int compareMaxInheritancePath( | |
| ClassHierarchyNode a, | |
| ClassHierarchyNode b, | |
| ) { | |
| return b.maxInheritancePath.compareTo(a.maxInheritancePath); | |
| } | |
| } | |
| class ExtensionTypeHierarchyNodeBuilder extends HierarchyNodeBuilder { | |
| final ExtensionTypeDeclarationBuilder _extensionTypeBuilder; | |
| new(super._hierarchy, this._extensionTypeBuilder); | |
| @override | |
| LibraryBuilder get _libraryBuilder => _extensionTypeBuilder.libraryBuilder; | |
| @override | |
| String get _name => _extensionTypeBuilder.name; | |
| @override | |
| int get _fileOffset => _extensionTypeBuilder.fileOffset; | |
| @override | |
| Uri get _fileUri => _extensionTypeBuilder.fileUri; | |
| ExtensionTypeHierarchyNode build() { | |
| Map<Class, Supertype> superclasses = {}; | |
| Map<ExtensionTypeDeclaration, ExtensionType> superExtensionTypes = {}; | |
| List<ClassHierarchyNode>? superclassNodes; | |
| List<ExtensionTypeHierarchyNode>? superExtensionTypeNodes; | |
| int maxInheritancePath = 1; | |
| List<TypeBuilder>? directInterfaceBuilders = _ignoreFunction( | |
| _extensionTypeBuilder.interfaceBuilders, | |
| ); | |
| if (directInterfaceBuilders != null) { | |
| for (int i = 0; i < directInterfaceBuilders.length; i++) { | |
| DartType directInterface = directInterfaceBuilders[i].build( | |
| _extensionTypeBuilder.libraryBuilder, | |
| TypeUse.extensionTypeImplementsType, | |
| ); | |
| if (directInterface is InterfaceType) { | |
| Supertype supertype = new Supertype.byReference( | |
| directInterface.classReference, | |
| directInterface.typeArguments, | |
| ); | |
| _addSuperClass(superclasses, supertype); | |
| ClassHierarchyNode interfaceNode = _hierarchy.getNodeFromClass( | |
| directInterface.classNode, | |
| ); | |
| (superclassNodes ??= []).add(interfaceNode); | |
| if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) { | |
| maxInheritancePath = interfaceNode.maxInheritancePath + 1; | |
| } | |
| InterfaceType superTypeAsInterfaceType = supertype.asInterfaceType; | |
| List<Supertype> types = _substSupertypes( | |
| superTypeAsInterfaceType, | |
| interfaceNode.superclasses, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addSuperClass(superclasses, types[i]); | |
| } | |
| if (interfaceNode.interfaces.isNotEmpty) { | |
| List<Supertype> types = _substSupertypes( | |
| superTypeAsInterfaceType, | |
| interfaceNode.interfaces, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addSuperClass(superclasses, types[i]); | |
| } | |
| } | |
| } else if (directInterface is ExtensionType) { | |
| _addSuperExtensionType(superExtensionTypes, directInterface); | |
| ExtensionTypeHierarchyNode interfaceNode = _hierarchy | |
| .getNodeFromExtensionType( | |
| directInterface.extensionTypeDeclaration, | |
| ); | |
| (superExtensionTypeNodes ??= []).add(interfaceNode); | |
| if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) { | |
| maxInheritancePath = interfaceNode.maxInheritancePath + 1; | |
| } | |
| List<ExtensionType> types = _substSuperExtensionTypes( | |
| directInterface, | |
| interfaceNode.superExtensionTypes, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addSuperExtensionType(superExtensionTypes, types[i]); | |
| } | |
| if (interfaceNode.superclasses.isNotEmpty) { | |
| List<Supertype> types = _substSupertypes( | |
| directInterface, | |
| interfaceNode.superclasses, | |
| ); | |
| for (int i = 0; i < types.length; i++) { | |
| _addSuperClass(superclasses, types[i]); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return new ExtensionTypeHierarchyNode( | |
| _extensionTypeBuilder, | |
| superclasses.values.toList(), | |
| superExtensionTypes.values.toList(), | |
| superclassNodes, | |
| superExtensionTypeNodes, | |
| maxInheritancePath, | |
| ); | |
| } | |
| void _addSuperClass(Map<Class, Supertype> superClasses, Supertype type) { | |
| Supertype? interface = superClasses[type.classNode]; | |
| if (interface != null) { | |
| // This is a potential conflict. | |
| superClasses[type.classNode] = _resolveSupertypeConflict(type, interface); | |
| return; | |
| } | |
| superClasses[type.classNode] = type; | |
| } | |
| ExtensionType _resolveSuperExtensionTypeConflict( | |
| ExtensionType type, | |
| ExtensionType superclass, | |
| ) { | |
| DartType? merge = nnbdTopMerge( | |
| _hierarchy.coreTypes, | |
| norm(_hierarchy.coreTypes, superclass), | |
| norm(_hierarchy.coreTypes, type), | |
| ); | |
| if (merge != null) { | |
| return merge as ExtensionType; | |
| } | |
| LibraryBuilder libraryBuilder = _libraryBuilder; | |
| if (libraryBuilder is SourceLibraryBuilder) { | |
| libraryBuilder.addProblem( | |
| diag.ambiguousSupertypes.withArguments( | |
| name: _name, | |
| type1: superclass, | |
| type2: type, | |
| ), | |
| _fileOffset, | |
| noLength, | |
| _fileUri, | |
| ); | |
| } | |
| return superclass; | |
| } | |
| void _addSuperExtensionType( | |
| Map<ExtensionTypeDeclaration, ExtensionType> interfaces, | |
| ExtensionType type, | |
| ) { | |
| ExtensionType? interface = interfaces[type.extensionTypeDeclaration]; | |
| if (interface != null) { | |
| // This is a potential conflict. | |
| interfaces[type.extensionTypeDeclaration] = | |
| _resolveSuperExtensionTypeConflict(type, interface); | |
| return; | |
| } | |
| interfaces[type.extensionTypeDeclaration] = type; | |
| } | |
| List<ExtensionType> _substSuperExtensionTypes( | |
| ExtensionType superExtensionType, | |
| List<ExtensionType> superExtensionTypes, | |
| ) { | |
| List<TypeParameter> typeParameters = | |
| superExtensionType.extensionTypeDeclaration.typeParameters; | |
| if (typeParameters.isEmpty) { | |
| return superExtensionTypes; | |
| } | |
| Map<TypeParameter, DartType> map = <TypeParameter, DartType>{}; | |
| List<DartType> arguments = superExtensionType.typeArguments; | |
| for (int i = 0; i < typeParameters.length; i++) { | |
| map[typeParameters[i]] = arguments[i]; | |
| } | |
| Substitution substitution = Substitution.fromMap(map); | |
| List<ExtensionType>? result; | |
| for (int i = 0; i < superExtensionTypes.length; i++) { | |
| ExtensionType supertype = superExtensionTypes[i]; | |
| ExtensionType substituted = | |
| substitution.substituteType(supertype) as ExtensionType; | |
| if (supertype != substituted) { | |
| result ??= superExtensionTypes.toList(); | |
| result[i] = substituted; | |
| } | |
| } | |
| return result ?? superExtensionTypes; | |
| } | |
| } | |
| class ExtensionTypeHierarchyNode( | |
| /// The extension type corresponding to this hierarchy node. | |
| final ExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder, | |
| /// The list of all classes implemented by [extensionTypeDeclarationBuilder] | |
| /// and its superclasses. | |
| final List<Supertype> superclasses, | |
| /// The list of all extension types implemented by | |
| /// [extensionTypeDeclarationBuilder] and its super extension types. | |
| final List<ExtensionType> superExtensionTypes, | |
| /// The [ClassHierarchyNode]s for the direct superclasses of | |
| /// [extensionTypeDeclarationBuilder]. | |
| final List<ClassHierarchyNode>? directSuperclassNodes, | |
| /// The [ExtensionTypeHierarchyNode]s for the direct super extension types of | |
| /// [extensionTypeDeclarationBuilder]. | |
| final List<ExtensionTypeHierarchyNode>? directSuperExtensionTypeNodes, | |
| /// The longest inheritance path from [extensionTypeDeclarationBuilder] to | |
| /// `Object`. | |
| final int maxInheritancePath, | |
| ); | |
| ClassBuilder? getClass(TypeBuilder type) { | |
| Builder? declaration = type.computeUnaliasedDeclaration(isUsedAsClass: false); | |
| return declaration is ClassBuilder ? declaration : null; | |
| } |