| // Copyright (c) 2018, 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/clone.dart' show CloneVisitorNotMembers; |
| import 'package:kernel/core_types.dart' show CoreTypes; |
| import 'package:kernel/reference_from_index.dart'; |
| import 'package:kernel/src/nnbd_top_merge.dart'; |
| import 'package:kernel/src/norm.dart'; |
| import 'package:kernel/src/types.dart' show Types; |
| import 'package:kernel/type_algebra.dart'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../base/problems.dart' show unhandled; |
| import '../builder/declaration_builders.dart'; |
| import '../source/source_class_builder.dart'; |
| import 'hierarchy/class_member.dart'; |
| import 'hierarchy/hierarchy_builder.dart'; |
| import 'hierarchy/members_builder.dart'; |
| import 'member_covariance.dart'; |
| |
| /// Class used for computing and inspecting the combined member signature for |
| /// a set of overridden/inherited [ClassMember]s. |
| abstract class CombinedMemberSignatureBase { |
| /// The class members builder used for building this class. |
| final ClassMembersBuilder membersBuilder; |
| |
| /// The list of the members inherited into or overridden in |
| /// [extensionTypeDeclarationBuilder]. |
| final List<ClassMember> members; |
| |
| /// The target declaration for the combined member signature. |
| /// |
| /// The [_memberTypes] are computed in terms of each member is inherited into |
| /// [declarationBuilder]. |
| /// |
| /// [declarationBuilder] is also used for determining whether the combined |
| /// member signature should be computed using nnbd or legacy semantics. |
| DeclarationBuilder get declarationBuilder; |
| |
| /// If `true` the combined member signature is for the setter aspect of the |
| /// members. Otherwise it is for the getter/method aspect of the members. |
| final bool forSetter; |
| |
| /// The index within [members] for the member whose type is the most specific |
| /// among [members]. If `null`, the combined member signature is not defined |
| /// for [members] in [extensionTypeDeclarationBuilder]. |
| /// |
| /// For the legacy computation, the type of this member defines the combined |
| /// member signature. |
| /// |
| /// For the nnbd computation, this is one of the members whose type define |
| /// the combined member signature, and the indices of the remaining members |
| /// are stored in [_mutualSubtypes]. |
| int? _canonicalMemberIndex; |
| |
| /// For the nnbd computation, this maps each distinct but most specific member |
| /// type to the index of one of the [members] with that type. |
| /// |
| /// If there is only one most specific member type, this is `null`. |
| Map<DartType, int>? _mutualSubtypes; |
| |
| /// Cache for the types of [members] as inherited into |
| /// [extensionTypeDeclarationBuilder]. |
| List<DartType?>? _memberTypes; |
| |
| /// If `true` the combined member signature type has been computed. |
| /// |
| /// Note that the combined member signature type might be undefined in which |
| /// case [_combinedMemberSignatureType] is `null`. |
| bool _isCombinedMemberSignatureTypeComputed = false; |
| |
| /// Cache the computed combined member signature type. |
| /// |
| /// If the combined member signature type is undefined this is set to `null`. |
| DartType? _combinedMemberSignatureType; |
| |
| /// The indices for the members whose type needed legacy erasure. |
| /// |
| /// This is fully computed when [combinedMemberSignatureType] has been |
| /// computed. |
| Set<int>? _neededLegacyErasureIndices; |
| |
| bool _neededNnbdTopMerge = false; |
| |
| bool _needsCovarianceMerging = false; |
| |
| bool _isCombinedMemberSignatureCovarianceComputed = false; |
| |
| Covariance? _combinedMemberSignatureCovariance; |
| |
| /// Creates a [CombinedMemberSignatureBase] whose canonical member is already |
| /// defined. |
| CombinedMemberSignatureBase.internal( |
| this.membersBuilder, this._canonicalMemberIndex, this.members, |
| {required this.forSetter}); |
| |
| /// Creates a [CombinedMemberSignatureBase] for [members] inherited into |
| /// [extensionTypeDeclarationBuilder]. |
| /// |
| /// If [forSetter] is `true`, contravariance of the setter types is used to |
| /// compute the most specific member type. Otherwise covariance of the getter |
| /// types or function types is used. |
| CombinedMemberSignatureBase(this.membersBuilder, this.members, |
| {required this.forSetter}) { |
| int? bestSoFarIndex; |
| if (members.length == 1) { |
| bestSoFarIndex = 0; |
| } else { |
| DartType? bestTypeSoFar; |
| for (int candidateIndex = members.length - 1; |
| candidateIndex >= 0; |
| candidateIndex--) { |
| DartType candidateType = getMemberType(candidateIndex); |
| if (bestSoFarIndex == null) { |
| bestTypeSoFar = candidateType; |
| bestSoFarIndex = candidateIndex; |
| } else { |
| if (_isMoreSpecific(candidateType, bestTypeSoFar!, forSetter)) { |
| if (_isMoreSpecific(bestTypeSoFar, candidateType, forSetter)) { |
| if (_mutualSubtypes == null) { |
| _mutualSubtypes = { |
| bestTypeSoFar: bestSoFarIndex, |
| candidateType: candidateIndex |
| }; |
| } else { |
| _mutualSubtypes![candidateType] = candidateIndex; |
| } |
| } else { |
| _mutualSubtypes = null; |
| } |
| bestSoFarIndex = candidateIndex; |
| bestTypeSoFar = candidateType; |
| } |
| } |
| } |
| if (_mutualSubtypes?.length == 1) { |
| /// If all mutual subtypes have the same type, the type should not |
| /// be normalized. |
| _mutualSubtypes = null; |
| } |
| if (bestSoFarIndex != null) { |
| for (int candidateIndex = 0; |
| candidateIndex < members.length; |
| candidateIndex++) { |
| DartType candidateType = getMemberType(candidateIndex); |
| if (!_isMoreSpecific(bestTypeSoFar!, candidateType, forSetter)) { |
| int? favoredIndex = |
| getOverlookedOverrideProblemChoice(declarationBuilder); |
| bestSoFarIndex = favoredIndex; |
| _mutualSubtypes = null; |
| break; |
| } |
| } |
| } |
| } |
| |
| _canonicalMemberIndex = bestSoFarIndex; |
| } |
| |
| /// The member within [members] type is the most specific among [members]. |
| /// If `null`, the combined member signature is not defined for [members] in |
| /// [extensionTypeDeclarationBuilder]. |
| /// |
| /// For the legacy computation, the type of this member defines the combined |
| /// member signature. |
| /// |
| /// For the nnbd computation, this is one of the members whose type define |
| /// the combined member signature, and the indices of the all members whose |
| /// type define the combined member signature are in [mutualSubtypeIndices]. |
| ClassMember? get canonicalMember => |
| _canonicalMemberIndex != null ? members[_canonicalMemberIndex!] : null; |
| |
| /// The index within [members] for the member whose type is the most specific |
| /// among [members]. If `null`, the combined member signature is not defined |
| /// for [members] in [extensionTypeDeclarationBuilder]. |
| /// |
| /// For the legacy computation, the type of this member defines the combined |
| /// member signature. |
| /// |
| /// For the nnbd computation, this is one of the members whose type define |
| /// the combined member signature, and the indices of the all members whose |
| /// type define the combined member signature are in [mutualSubtypeIndices]. |
| int? get canonicalMemberIndex => _canonicalMemberIndex; |
| |
| ClassHierarchyBuilder get hierarchy => membersBuilder.hierarchyBuilder; |
| |
| CoreTypes get _coreTypes => hierarchy.coreTypes; |
| |
| Types get _types => hierarchy.types; |
| |
| Member _getMember(int index) { |
| ClassMember candidate = members[index]; |
| return candidate.getMember(membersBuilder); |
| } |
| |
| /// Returns `true` if legacy erasure was needed to compute the combined |
| /// member signature type. |
| /// |
| /// Legacy erasure is considered need of if the used of it resulted in a |
| /// different type. |
| bool get neededLegacyErasure { |
| _ensureCombinedMemberSignatureType(); |
| return _neededLegacyErasureIndices |
| // Coverage-ignore(suite): Not run. |
| ?.contains(canonicalMemberIndex) ?? |
| false; |
| } |
| |
| /// Returns `true` if nnbd top merge and normalization was needed to compute |
| /// the combined member signature type. |
| bool get neededNnbdTopMerge { |
| _ensureCombinedMemberSignatureType(); |
| return _neededNnbdTopMerge; |
| } |
| |
| /// Returns the this type of the [declarationBuilder]. |
| TypeDeclarationType get thisType; |
| |
| /// Returns `true` if the canonical member is declared in |
| /// [declarationBuilder]. |
| bool get isCanonicalMemberDeclared; |
| |
| /// Returns `true` if the canonical member is the 0th. |
| // TODO(johnniwinther): This is currently used under the assumption that the |
| // 0th member is either from the superclass or the one found if looked up |
| // the class hierarchy. This is a very brittle assumption. |
| bool get isCanonicalMemberFirst => _canonicalMemberIndex == 0; |
| |
| /// Returns type of the [index]th member in [members] as inherited in |
| /// [extensionTypeDeclarationBuilder]. |
| DartType getMemberType(int index) { |
| _memberTypes ??= new List<DartType?>.filled(members.length, null); |
| DartType? candidateType = _memberTypes![index]; |
| if (candidateType == null) { |
| Member target = _getMember(index); |
| candidateType = _computeMemberType(target); |
| _memberTypes![index] = candidateType; |
| } |
| return candidateType; |
| } |
| |
| DartType getMemberTypeForTarget(Member target) { |
| return _computeMemberType(target); |
| } |
| |
| void _ensureCombinedMemberSignatureType() { |
| if (!_isCombinedMemberSignatureTypeComputed) { |
| _isCombinedMemberSignatureTypeComputed = true; |
| if (_canonicalMemberIndex == null) { |
| return null; |
| } |
| DartType canonicalMemberType = |
| _combinedMemberSignatureType = getMemberType(_canonicalMemberIndex!); |
| if (_mutualSubtypes != null) { |
| _combinedMemberSignatureType = |
| norm(_coreTypes, _combinedMemberSignatureType!); |
| for (int index in _mutualSubtypes!.values) { |
| if (_canonicalMemberIndex != index) { |
| _combinedMemberSignatureType = nnbdTopMerge( |
| _coreTypes, |
| _combinedMemberSignatureType!, |
| norm(_coreTypes, getMemberType(index))); |
| assert( |
| _combinedMemberSignatureType != null, |
| "No combined member signature found for " |
| "${_mutualSubtypes!.values.map((int i) => getMemberType(i))} " |
| "for members ${members}"); |
| } |
| } |
| _neededNnbdTopMerge = |
| canonicalMemberType != _combinedMemberSignatureType; |
| } |
| } |
| } |
| |
| /// Returns the type of the combined member signature, if defined. |
| DartType? get combinedMemberSignatureType { |
| _ensureCombinedMemberSignatureType(); |
| return _combinedMemberSignatureType; |
| } |
| |
| Covariance _getMemberCovariance(int index) { |
| ClassMember candidate = members[index]; |
| return candidate.getCovariance(membersBuilder); |
| } |
| |
| void _ensureCombinedMemberSignatureCovariance() { |
| if (!_isCombinedMemberSignatureCovarianceComputed) { |
| _isCombinedMemberSignatureCovarianceComputed = true; |
| if (canonicalMemberIndex == null) { |
| return; |
| } |
| Covariance canonicalMemberCovariance = |
| _combinedMemberSignatureCovariance = |
| _getMemberCovariance(canonicalMemberIndex!); |
| if (members.length == 1) { |
| return; |
| } |
| for (int index = 0; index < members.length; index++) { |
| if (index != canonicalMemberIndex) { |
| _combinedMemberSignatureCovariance = |
| _combinedMemberSignatureCovariance! |
| .merge(_getMemberCovariance(index)); |
| } |
| } |
| _needsCovarianceMerging = |
| canonicalMemberCovariance != _combinedMemberSignatureCovariance; |
| } |
| } |
| |
| // Returns `true` if the covariance of [members] needs to be merged into |
| // the combined member signature. |
| bool get needsCovarianceMerging { |
| if (members.length != 1) { |
| _ensureCombinedMemberSignatureCovariance(); |
| } |
| return _needsCovarianceMerging; |
| } |
| |
| /// Returns [Covariance] for the combined member signature. |
| Covariance? get combinedMemberSignatureCovariance { |
| _ensureCombinedMemberSignatureCovariance(); |
| return _combinedMemberSignatureCovariance; |
| } |
| |
| /// Returns the type of the combined member signature, if defined, with |
| /// all method type parameters substituted with [typeParameters]. |
| /// |
| /// This is used for inferring types on a declared member from the type of the |
| /// combined member signature. |
| DartType? getCombinedSignatureTypeInContext( |
| List<TypeParameter> typeParameters) { |
| DartType? type = combinedMemberSignatureType; |
| if (type == null) { |
| return null; |
| } |
| int typeParameterCount = typeParameters.length; |
| if (type is FunctionType) { |
| List<StructuralParameter> signatureTypeParameters = type.typeParameters; |
| if (typeParameterCount != signatureTypeParameters.length) { |
| return null; |
| } |
| if (typeParameterCount == 0) { |
| return type; |
| } |
| List<DartType> types = |
| new List<DartType>.filled(typeParameterCount, dummyDartType); |
| for (int i = 0; i < typeParameterCount; i++) { |
| types[i] = |
| new TypeParameterType.forAlphaRenamingFromStructuralParameters( |
| signatureTypeParameters[i], typeParameters[i]); |
| } |
| FunctionTypeInstantiator instantiator = |
| new FunctionTypeInstantiator.fromIterables( |
| signatureTypeParameters, types); |
| for (int i = 0; i < typeParameterCount; i++) { |
| DartType typeParameterBound = typeParameters[i].bound; |
| DartType signatureTypeParameterBound = |
| instantiator.substitute(signatureTypeParameters[i].bound); |
| if (!_types |
| .performNullabilityAwareMutualSubtypesCheck( |
| typeParameterBound, signatureTypeParameterBound) |
| .isSubtypeWhenUsingNullabilities()) { |
| return null; |
| } |
| } |
| return instantiator.substitute(type.withoutTypeParameters); |
| } |
| // Coverage-ignore(suite): Not run. |
| else if (typeParameterCount != 0) { |
| return null; |
| } |
| return type; |
| } |
| |
| /// Create a member signature with the [combinedMemberSignatureType] using the |
| /// [canonicalMember] as member signature origin. |
| Procedure? createMemberFromSignature( |
| FileUriNode declarationNode, IndexedContainer? indexedContainer, |
| {bool copyLocation = true}) { |
| if (canonicalMemberIndex == null) { |
| return null; |
| } |
| Member member = _getMember(canonicalMemberIndex!); |
| Procedure combinedMemberSignature; |
| if (member is Procedure) { |
| switch (member.kind) { |
| case ProcedureKind.Getter: |
| combinedMemberSignature = _createGetterMemberSignature( |
| declarationNode, |
| indexedContainer, |
| member, |
| combinedMemberSignatureType!, |
| copyLocation: copyLocation); |
| break; |
| case ProcedureKind.Setter: |
| VariableDeclaration parameter = |
| member.function.positionalParameters.first; |
| combinedMemberSignature = _createSetterMemberSignature( |
| declarationNode, |
| indexedContainer, |
| member, |
| combinedMemberSignatureType!, |
| isCovariantByClass: parameter.isCovariantByClass, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration, |
| parameter: parameter, |
| copyLocation: copyLocation); |
| break; |
| case ProcedureKind.Method: |
| case ProcedureKind.Operator: |
| combinedMemberSignature = _createMethodSignature( |
| declarationNode, |
| indexedContainer, |
| member, |
| combinedMemberSignatureType as FunctionType, |
| copyLocation: copyLocation); |
| break; |
| // Coverage-ignore(suite): Not run. |
| case ProcedureKind.Factory: |
| throw new UnsupportedError( |
| 'Unexpected canonical member kind ${member.kind} for $member'); |
| } |
| } else if (member is Field) { |
| if (forSetter) { |
| combinedMemberSignature = _createSetterMemberSignature(declarationNode, |
| indexedContainer, member, combinedMemberSignatureType!, |
| isCovariantByClass: member.isCovariantByClass, |
| isCovariantByDeclaration: member.isCovariantByDeclaration, |
| copyLocation: copyLocation); |
| } else { |
| combinedMemberSignature = _createGetterMemberSignature(declarationNode, |
| indexedContainer, member, combinedMemberSignatureType!, |
| copyLocation: copyLocation); |
| } |
| } else { |
| throw new UnsupportedError( |
| 'Unexpected canonical member $member (${member.runtimeType})'); |
| } |
| combinedMemberSignatureCovariance!.applyCovariance(combinedMemberSignature); |
| return combinedMemberSignature; |
| } |
| |
| /// Creates a getter member signature for [member] with the given |
| /// [type]. |
| Procedure _createGetterMemberSignature(FileUriNode declarationNode, |
| IndexedContainer? indexedContainer, Member member, DartType type, |
| {required bool copyLocation}) { |
| Reference? reference = indexedContainer?.lookupGetterReference(member.name); |
| |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| // Coverage-ignore-block(suite): Not run. |
| fileUri = member.fileUri; |
| startFileOffset = |
| member is Procedure ? member.fileStartOffset : member.fileOffset; |
| fileOffset = member.fileOffset; |
| } else { |
| fileUri = declarationNode.fileUri; |
| fileOffset = startFileOffset = declarationNode.fileOffset; |
| } |
| return new Procedure( |
| member.name, |
| ProcedureKind.Getter, |
| new FunctionNode(null, returnType: type), |
| isAbstract: true, |
| fileUri: fileUri, |
| reference: reference, |
| isSynthetic: true, |
| stubKind: ProcedureStubKind.MemberSignature, |
| stubTarget: member.memberSignatureOrigin ?? member, |
| ) |
| ..fileStartOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..parent = declarationNode; |
| } |
| |
| /// Creates a setter member signature for [member] with the given |
| /// [type]. The flags of parameter is set according to |
| /// [isCovariantByDeclaration] and [isCovariantByClass] and the name of the |
| /// [parameter] is used, if provided. |
| Procedure _createSetterMemberSignature(FileUriNode declarationNode, |
| IndexedContainer? indexedContainer, Member member, DartType type, |
| {required bool isCovariantByDeclaration, |
| required bool isCovariantByClass, |
| VariableDeclaration? parameter, |
| required bool copyLocation}) { |
| Reference? reference = indexedContainer?.lookupSetterReference(member.name); |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| // Coverage-ignore-block(suite): Not run. |
| fileUri = member.fileUri; |
| startFileOffset = |
| member is Procedure ? member.fileStartOffset : member.fileOffset; |
| fileOffset = member.fileOffset; |
| } else { |
| fileUri = declarationNode.fileUri; |
| fileOffset = startFileOffset = declarationNode.fileOffset; |
| } |
| return new Procedure( |
| member.name, |
| ProcedureKind.Setter, |
| new FunctionNode(null, |
| returnType: const VoidType(), |
| positionalParameters: [ |
| new VariableDeclaration(parameter?.name ?? 'value', |
| type: type, isCovariantByDeclaration: isCovariantByDeclaration) |
| ..isCovariantByClass = isCovariantByClass |
| ..fileOffset = copyLocation |
| ? |
| // Coverage-ignore(suite): Not run. |
| parameter?.fileOffset ?? fileOffset |
| : fileOffset |
| ]), |
| isAbstract: true, |
| fileUri: fileUri, |
| reference: reference, |
| isSynthetic: true, |
| stubKind: ProcedureStubKind.MemberSignature, |
| stubTarget: member.memberSignatureOrigin ?? member, |
| ) |
| ..fileStartOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..parent = declarationNode; |
| } |
| |
| Procedure _createMethodSignature( |
| FileUriNode declarationNode, |
| IndexedContainer? indexedContainer, |
| Procedure procedure, |
| FunctionType functionType, |
| {required bool copyLocation}) { |
| Reference? reference = |
| indexedContainer?.lookupGetterReference(procedure.name); |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| // Coverage-ignore-block(suite): Not run. |
| fileUri = procedure.fileUri; |
| startFileOffset = procedure.fileStartOffset; |
| fileOffset = procedure.fileOffset; |
| } else { |
| fileUri = declarationNode.fileUri; |
| fileOffset = startFileOffset = declarationNode.fileOffset; |
| } |
| FunctionNode function = procedure.function; |
| List<VariableDeclaration> positionalParameters = []; |
| FreshTypeParametersFromStructuralParameters freshTypeParameters = |
| getFreshTypeParametersFromStructuralParameters( |
| functionType.typeParameters); |
| CloneVisitorNotMembers cloner = new CloneVisitorNotMembers(); |
| for (int i = 0; i < function.positionalParameters.length; i++) { |
| VariableDeclaration parameter = function.positionalParameters[i]; |
| DartType parameterType = |
| freshTypeParameters.substitute(functionType.positionalParameters[i]); |
| positionalParameters.add(new VariableDeclaration(parameter.name, |
| type: parameterType, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration, |
| initializer: cloner.cloneOptional(parameter.initializer)) |
| ..hasDeclaredInitializer = parameter.hasDeclaredInitializer |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation |
| ? |
| // Coverage-ignore(suite): Not run. |
| parameter.fileOffset |
| : fileOffset); |
| } |
| List<VariableDeclaration> namedParameters = []; |
| int namedParameterCount = function.namedParameters.length; |
| if (namedParameterCount == 1) { |
| NamedType namedType = functionType.namedParameters.first; |
| VariableDeclaration parameter = function.namedParameters.first; |
| namedParameters.add(new VariableDeclaration(parameter.name, |
| type: freshTypeParameters.substitute(namedType.type), |
| isRequired: namedType.isRequired, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration, |
| initializer: cloner.cloneOptional(parameter.initializer)) |
| ..hasDeclaredInitializer = parameter.hasDeclaredInitializer |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation |
| ? |
| // Coverage-ignore(suite): Not run. |
| parameter.fileOffset |
| : fileOffset); |
| } else if (namedParameterCount > 1) { |
| Map<String, NamedType> namedTypes = {}; |
| for (NamedType namedType in functionType.namedParameters) { |
| namedTypes[namedType.name] = namedType; |
| } |
| for (int i = 0; i < namedParameterCount; i++) { |
| VariableDeclaration parameter = function.namedParameters[i]; |
| NamedType namedParameterType = namedTypes[parameter.name]!; |
| namedParameters.add(new VariableDeclaration(parameter.name, |
| type: freshTypeParameters.substitute(namedParameterType.type), |
| isRequired: namedParameterType.isRequired, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration, |
| initializer: cloner.cloneOptional(parameter.initializer)) |
| ..hasDeclaredInitializer = parameter.hasDeclaredInitializer |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation |
| ? |
| // Coverage-ignore(suite): Not run. |
| parameter.fileOffset |
| : fileOffset); |
| } |
| } |
| return new Procedure( |
| procedure.name, |
| procedure.kind, |
| new FunctionNode(null, |
| typeParameters: freshTypeParameters.freshTypeParameters, |
| returnType: freshTypeParameters.substitute(functionType.returnType), |
| positionalParameters: positionalParameters, |
| namedParameters: namedParameters, |
| requiredParameterCount: function.requiredParameterCount), |
| isAbstract: true, |
| fileUri: fileUri, |
| reference: reference, |
| isSynthetic: true, |
| stubKind: ProcedureStubKind.MemberSignature, |
| stubTarget: procedure.memberSignatureOrigin ?? procedure, |
| ) |
| ..fileStartOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..parent = declarationNode; |
| } |
| |
| DartType _computeMemberType(Member member) { |
| DartType type; |
| if (member is Procedure) { |
| if (member.isGetter) { |
| type = member.getterType; |
| } else if (member.isSetter) { |
| type = member.setterType; |
| } else { |
| // TODO(johnniwinther): Why do we need the specific nullability here? |
| type = member.getterType.withDeclaredNullability( |
| declarationBuilder.libraryBuilder.library.nonNullable); |
| } |
| } else if (member is Field) { |
| type = member.type; |
| } else { |
| unhandled("${member.runtimeType}", "$member", |
| declarationBuilder.charOffset, declarationBuilder.fileUri); |
| } |
| if (member.enclosingTypeDeclaration!.typeParameters.isEmpty) { |
| return type; |
| } |
| TypeDeclarationType instance = hierarchy.getTypeAsInstanceOf( |
| thisType, member.enclosingTypeDeclaration!)!; |
| return Substitution.fromTypeDeclarationType(instance).substituteType(type); |
| } |
| |
| bool _isMoreSpecific(DartType a, DartType b, bool forSetter) { |
| if (forSetter) { |
| return _types.isSubtypeOf(b, a, SubtypeCheckMode.withNullabilities); |
| } else { |
| return _types.isSubtypeOf(a, b, SubtypeCheckMode.withNullabilities); |
| } |
| } |
| } |
| |
| class CombinedClassMemberSignature extends CombinedMemberSignatureBase { |
| final ClassBuilder classBuilder; |
| |
| /// Cache for the this type of [classBuilder]. |
| InterfaceType? _thisType; |
| |
| /// Creates a [CombinedClassMemberSignature] whose canonical member is already |
| /// defined. |
| CombinedClassMemberSignature.internal(ClassMembersBuilder membersBuilder, |
| this.classBuilder, int? canonicalMemberIndex, List<ClassMember> members, |
| {required bool forSetter}) |
| : super.internal(membersBuilder, canonicalMemberIndex, members, |
| forSetter: forSetter); |
| |
| /// Creates a [CombinedClassMemberSignature] for [members] inherited into |
| /// [classBuilder]. |
| /// |
| /// If [forSetter] is `true`, contravariance of the setter types is used to |
| /// compute the most specific member type. Otherwise covariance of the getter |
| /// types or function types is used. |
| CombinedClassMemberSignature(ClassMembersBuilder membersBuilder, |
| this.classBuilder, List<ClassMember> members, |
| {required bool forSetter}) |
| : super(membersBuilder, members, forSetter: forSetter); |
| |
| @override |
| DeclarationBuilder get declarationBuilder => classBuilder; |
| |
| /// The this type of [classBuilder]. |
| @override |
| InterfaceType get thisType { |
| return _thisType ??= |
| _coreTypes.thisInterfaceType(classBuilder.cls, Nullability.nonNullable); |
| } |
| |
| /// Returns `true` if the canonical member is declared in |
| /// [declarationBuilder]. |
| @override |
| bool get isCanonicalMemberDeclared { |
| return _canonicalMemberIndex != null && |
| _getMember(_canonicalMemberIndex!).enclosingClass == classBuilder.cls; |
| } |
| } |
| |
| class CombinedExtensionTypeMemberSignature extends CombinedMemberSignatureBase { |
| final ExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder; |
| |
| /// Cache for the this type of [extensionTypeDeclarationBuilder]. |
| ExtensionType? _thisType; |
| |
| /// Creates a [CombinedClassMemberSignature] for [members] inherited into |
| /// [extensionTypeDeclarationBuilder]. |
| /// |
| /// If [forSetter] is `true`, contravariance of the setter types is used to |
| /// compute the most specific member type. Otherwise covariance of the getter |
| /// types or function types is used. |
| CombinedExtensionTypeMemberSignature(ClassMembersBuilder membersBuilder, |
| this.extensionTypeDeclarationBuilder, List<ClassMember> members, |
| {required bool forSetter}) |
| : super(membersBuilder, members, forSetter: forSetter); |
| |
| @override |
| DeclarationBuilder get declarationBuilder => extensionTypeDeclarationBuilder; |
| |
| /// The this type of [extensionTypeDeclarationBuilder]. |
| @override |
| ExtensionType get thisType { |
| return _thisType ??= _coreTypes.thisExtensionType( |
| extensionTypeDeclarationBuilder.extensionTypeDeclaration, |
| Nullability.nonNullable); |
| } |
| |
| @override |
| bool get isCanonicalMemberDeclared => false; |
| } |