| // 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/class_hierarchy.dart' show ClassHierarchyBase; |
| |
| import 'package:kernel/core_types.dart' show CoreTypes; |
| |
| import 'package:kernel/type_algebra.dart' show Substitution; |
| import 'package:kernel/type_environment.dart'; |
| |
| import 'package:kernel/src/legacy_erasure.dart'; |
| import 'package:kernel/src/nnbd_top_merge.dart'; |
| import 'package:kernel/src/norm.dart'; |
| import 'package:kernel/src/types.dart' show Types; |
| |
| import '../problems.dart' show unhandled; |
| |
| 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 members. |
| abstract class CombinedMemberSignatureBase<T> { |
| ClassHierarchyBase get hierarchy; |
| |
| Name get name; |
| |
| /// The target class for the combined member signature. |
| /// |
| /// The [_memberTypes] are computed in terms of each member is inherited into |
| /// [classBuilder]. |
| /// |
| /// [classBuilder] is also used for determining whether the combined member |
| /// signature should be computed using nnbd or legacy semantics. |
| final SourceClassBuilder classBuilder; |
| |
| /// The list of members from which the combined member signature is computed. |
| List<T> get members; |
| |
| /// 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 [classBuilder]. |
| /// |
| /// 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 [classBuilder]. |
| List<DartType?>? _memberTypes; |
| |
| List<Covariance?>? _memberCovariances; |
| |
| /// Cache for the this type of [classBuilder]. |
| InterfaceType? _thisType; |
| |
| /// 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 _containsNnbdTypes = false; |
| |
| bool _needsCovarianceMerging = false; |
| |
| bool _isCombinedMemberSignatureCovarianceComputed = false; |
| |
| Covariance? _combinedMemberSignatureCovariance; |
| |
| /// Creates a [CombinedClassMemberSignature] whose canonical member is already |
| /// defined. |
| CombinedMemberSignatureBase.internal( |
| this.classBuilder, this._canonicalMemberIndex, this.forSetter) |
| // ignore: unnecessary_null_comparison |
| : assert(forSetter != null); |
| |
| /// 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. |
| CombinedMemberSignatureBase(this.classBuilder, {required this.forSetter}) { |
| // ignore: unnecessary_null_comparison |
| assert(forSetter != null); |
| int? bestSoFarIndex; |
| if (members.length == 1) { |
| bestSoFarIndex = 0; |
| } else { |
| bool isNonNullableByDefault = |
| classBuilder.libraryBuilder.isNonNullableByDefault; |
| |
| 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 (isNonNullableByDefault && |
| _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(classBuilder); |
| 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 |
| /// [classBuilder]. |
| /// |
| /// 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]. |
| T? 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 [classBuilder]. |
| /// |
| /// 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; |
| |
| /// For the nnbd computation, the indices of the [members] with most specific |
| /// member type. |
| /// |
| /// If there is only one most specific member type, this is `null`. |
| Set<int>? get mutualSubtypeIndices => _mutualSubtypes?.values.toSet(); |
| |
| Member _getMember(int index); |
| |
| CoreTypes get _coreTypes => hierarchy.coreTypes; |
| |
| Types get _types; |
| |
| /// 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?.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 `true` if the type of combined member signature has nnbd types. |
| /// |
| /// If the combined member signature for an opt-in class is computed from |
| /// identical legacy types, that is, without the need for nnbd top merge, then |
| /// the type will be copied over directly and a member created from the |
| /// combined member signature will therefore be a legacy member, even though |
| /// it is declared in an opt in class. |
| /// |
| /// To avoid reporting errors as if the member was an opt-in member, it is |
| /// marked as nullable-by-default. |
| /// |
| /// For instance |
| /// |
| /// // opt out: |
| /// mixin Mixin { |
| /// void method({int named}) {} |
| /// } |
| /// // opt in: |
| /// class Super { |
| /// void method({required covariant int named}) {} |
| /// } |
| /// class Class extends Super with Mixin { |
| /// // A forwarding stop for Mixin.method will be inserted here: |
| /// // void method({covariant int named}) -> Mixin.method |
| /// } |
| /// class SubClass extends Class { |
| /// // This is a valid override since `Class.method` should should |
| /// // not be considered as _not_ having a required named parameter - |
| /// // it is legacy and doesn't know about required named parameters. |
| /// void method({required int named}) {} |
| /// } |
| /// |
| bool get containsNnbdTypes { |
| _ensureCombinedMemberSignatureType(); |
| return _containsNnbdTypes; |
| } |
| |
| /// The this type of [classBuilder]. |
| InterfaceType get thisType { |
| return _thisType ??= _coreTypes.thisInterfaceType( |
| classBuilder.cls, classBuilder.libraryBuilder.nonNullable); |
| } |
| |
| /// Returns `true` if the canonical member is declared in [classBuilder]. |
| bool get isCanonicalMemberDeclared { |
| return _canonicalMemberIndex != null && |
| _getMember(_canonicalMemberIndex!).enclosingClass == classBuilder.cls; |
| } |
| |
| /// 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 |
| /// [classBuilder]. |
| DartType getMemberType(int index) { |
| _memberTypes ??= new List<DartType?>.filled(members.length, null); |
| DartType? candidateType = _memberTypes![index]; |
| if (candidateType == null) { |
| Member target = _getMember(index); |
| // ignore: unnecessary_null_comparison |
| assert(target != null, |
| "No member computed for index ${index} in ${members}"); |
| candidateType = _computeMemberType(thisType, target); |
| if (!classBuilder.libraryBuilder.isNonNullableByDefault) { |
| DartType? legacyErasure; |
| if (target == hierarchy.coreTypes.objectEquals) { |
| // In legacy code we special case `Object.==` to infer `dynamic` |
| // instead `Object!`. |
| legacyErasure = new FunctionType([const DynamicType()], |
| hierarchy.coreTypes.boolLegacyRawType, Nullability.legacy); |
| } else { |
| legacyErasure = rawLegacyErasure(candidateType); |
| } |
| if (legacyErasure != null) { |
| _neededLegacyErasureIndices ??= {}; |
| _neededLegacyErasureIndices!.add(index); |
| candidateType = legacyErasure; |
| } |
| } |
| _memberTypes![index] = candidateType; |
| } |
| return candidateType; |
| } |
| |
| DartType getMemberTypeForTarget(Member target) { |
| DartType candidateType = _computeMemberType(thisType, target); |
| if (!classBuilder.libraryBuilder.isNonNullableByDefault) { |
| DartType? legacyErasure; |
| if (target == hierarchy.coreTypes.objectEquals) { |
| // In legacy code we special case `Object.==` to infer `dynamic` |
| // instead `Object!`. |
| legacyErasure = new FunctionType([const DynamicType()], |
| hierarchy.coreTypes.boolLegacyRawType, Nullability.legacy); |
| } else { |
| legacyErasure = rawLegacyErasure(candidateType); |
| } |
| if (legacyErasure != null) { |
| candidateType = legacyErasure; |
| } |
| } |
| return candidateType; |
| } |
| |
| void _ensureCombinedMemberSignatureType() { |
| if (!_isCombinedMemberSignatureTypeComputed) { |
| _isCombinedMemberSignatureTypeComputed = true; |
| if (_canonicalMemberIndex == null) { |
| return null; |
| } |
| if (classBuilder.libraryBuilder.isNonNullableByDefault) { |
| DartType canonicalMemberType = _combinedMemberSignatureType = |
| getMemberType(_canonicalMemberIndex!); |
| _containsNnbdTypes = |
| _getMember(_canonicalMemberIndex!).isNonNullableByDefault; |
| 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; |
| _containsNnbdTypes = _neededNnbdTopMerge; |
| } |
| } else { |
| _combinedMemberSignatureType = getMemberType(_canonicalMemberIndex!); |
| } |
| } |
| } |
| |
| /// Returns the type of the combined member signature, if defined. |
| DartType? get combinedMemberSignatureType { |
| _ensureCombinedMemberSignatureType(); |
| return _combinedMemberSignatureType; |
| } |
| |
| Covariance _getMemberCovariance(int index); |
| |
| 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<TypeParameter> 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.forAlphaRenaming( |
| signatureTypeParameters[i], typeParameters[i]); |
| } |
| Substitution substitution = |
| Substitution.fromPairs(signatureTypeParameters, types); |
| for (int i = 0; i < typeParameterCount; i++) { |
| DartType typeParameterBound = typeParameters[i].bound; |
| DartType signatureTypeParameterBound = |
| substitution.substituteType(signatureTypeParameters[i].bound); |
| if (!_types |
| .performNullabilityAwareMutualSubtypesCheck( |
| typeParameterBound, signatureTypeParameterBound) |
| .isSubtypeWhenUsingNullabilities()) { |
| return null; |
| } |
| } |
| return substitution.substituteType(type.withoutTypeParameters); |
| } else if (typeParameterCount != 0) { |
| return null; |
| } |
| return type; |
| } |
| |
| /// Create a member signature with the [combinedMemberSignatureType] using the |
| /// [canonicalMember] as member signature origin. |
| Procedure? createMemberFromSignature({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( |
| member, combinedMemberSignatureType!, |
| copyLocation: copyLocation); |
| break; |
| case ProcedureKind.Setter: |
| VariableDeclaration parameter = |
| member.function.positionalParameters.first; |
| combinedMemberSignature = _createSetterMemberSignature( |
| member, combinedMemberSignatureType!, |
| isCovariantByClass: parameter.isCovariantByClass, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration, |
| parameter: parameter, |
| copyLocation: copyLocation); |
| break; |
| case ProcedureKind.Method: |
| case ProcedureKind.Operator: |
| combinedMemberSignature = _createMethodSignature( |
| member, combinedMemberSignatureType as FunctionType, |
| copyLocation: copyLocation); |
| break; |
| case ProcedureKind.Factory: |
| default: |
| throw new UnsupportedError( |
| 'Unexpected canonical member kind ${member.kind} for $member'); |
| } |
| } else if (member is Field) { |
| if (forSetter) { |
| combinedMemberSignature = _createSetterMemberSignature( |
| member, combinedMemberSignatureType!, |
| isCovariantByClass: member.isCovariantByClass, |
| isCovariantByDeclaration: member.isCovariantByDeclaration, |
| copyLocation: copyLocation); |
| } else { |
| combinedMemberSignature = _createGetterMemberSignature( |
| 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(Member member, DartType type, |
| {required bool copyLocation}) { |
| // ignore: unnecessary_null_comparison |
| assert(copyLocation != null); |
| Class enclosingClass = classBuilder.cls; |
| Reference? reference = |
| classBuilder.referencesFromIndexed?.lookupGetterReference(member.name); |
| |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| fileUri = member.fileUri; |
| startFileOffset = |
| member is Procedure ? member.startFileOffset : member.fileOffset; |
| fileOffset = member.fileOffset; |
| } else { |
| fileUri = enclosingClass.fileUri; |
| fileOffset = startFileOffset = enclosingClass.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, |
| ) |
| ..startFileOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..isNonNullableByDefault = containsNnbdTypes |
| ..parent = enclosingClass; |
| } |
| |
| /// 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(Member member, DartType type, |
| {required bool isCovariantByDeclaration, |
| required bool isCovariantByClass, |
| VariableDeclaration? parameter, |
| required bool copyLocation}) { |
| // ignore: unnecessary_null_comparison |
| assert(isCovariantByDeclaration != null); |
| // ignore: unnecessary_null_comparison |
| assert(isCovariantByClass != null); |
| // ignore: unnecessary_null_comparison |
| assert(copyLocation != null); |
| Class enclosingClass = classBuilder.cls; |
| Reference? reference = |
| classBuilder.referencesFromIndexed?.lookupSetterReference(member.name); |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| fileUri = member.fileUri; |
| startFileOffset = |
| member is Procedure ? member.startFileOffset : member.fileOffset; |
| fileOffset = member.fileOffset; |
| } else { |
| fileUri = enclosingClass.fileUri; |
| fileOffset = startFileOffset = enclosingClass.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 |
| ? parameter?.fileOffset ?? fileOffset |
| : fileOffset |
| ]), |
| isAbstract: true, |
| fileUri: fileUri, |
| reference: reference, |
| isSynthetic: true, |
| stubKind: ProcedureStubKind.MemberSignature, |
| stubTarget: member.memberSignatureOrigin ?? member, |
| ) |
| ..startFileOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..isNonNullableByDefault = containsNnbdTypes |
| ..parent = enclosingClass; |
| } |
| |
| Procedure _createMethodSignature( |
| Procedure procedure, FunctionType functionType, |
| {required bool copyLocation}) { |
| // ignore: unnecessary_null_comparison |
| assert(copyLocation != null); |
| Class enclosingClass = classBuilder.cls; |
| Reference? reference = classBuilder.referencesFromIndexed |
| ?.lookupGetterReference(procedure.name); |
| Uri fileUri; |
| int startFileOffset; |
| int fileOffset; |
| if (copyLocation) { |
| fileUri = procedure.fileUri; |
| startFileOffset = procedure.startFileOffset; |
| fileOffset = procedure.fileOffset; |
| } else { |
| fileUri = enclosingClass.fileUri; |
| fileOffset = startFileOffset = enclosingClass.fileOffset; |
| } |
| FunctionNode function = procedure.function; |
| List<VariableDeclaration> positionalParameters = []; |
| for (int i = 0; i < function.positionalParameters.length; i++) { |
| VariableDeclaration parameter = function.positionalParameters[i]; |
| DartType parameterType = functionType.positionalParameters[i]; |
| positionalParameters.add(new VariableDeclaration(parameter.name, |
| type: parameterType, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration) |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation ? 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: namedType.type, |
| isRequired: namedType.isRequired, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration) |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation ? 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: namedParameterType.type, |
| isRequired: namedParameterType.isRequired, |
| isCovariantByDeclaration: parameter.isCovariantByDeclaration) |
| ..isCovariantByClass = parameter.isCovariantByClass |
| ..fileOffset = copyLocation ? parameter.fileOffset : fileOffset); |
| } |
| } |
| return new Procedure( |
| procedure.name, |
| procedure.kind, |
| new FunctionNode(null, |
| typeParameters: functionType.typeParameters, |
| returnType: functionType.returnType, |
| positionalParameters: positionalParameters, |
| namedParameters: namedParameters, |
| requiredParameterCount: function.requiredParameterCount), |
| isAbstract: true, |
| fileUri: fileUri, |
| reference: reference, |
| isSynthetic: true, |
| stubKind: ProcedureStubKind.MemberSignature, |
| stubTarget: procedure.memberSignatureOrigin ?? procedure, |
| ) |
| ..startFileOffset = startFileOffset |
| ..fileOffset = fileOffset |
| ..isNonNullableByDefault = containsNnbdTypes |
| ..parent = enclosingClass; |
| } |
| |
| DartType _computeMemberType(InterfaceType thisType, 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( |
| classBuilder.cls.enclosingLibrary.nonNullable); |
| } |
| } else if (member is Field) { |
| type = member.type; |
| } else { |
| unhandled("${member.runtimeType}", "$member", classBuilder.charOffset, |
| classBuilder.fileUri); |
| } |
| if (member.enclosingClass!.typeParameters.isEmpty) { |
| return type; |
| } |
| InterfaceType? instance = hierarchy.getTypeAsInstanceOf( |
| thisType, member.enclosingClass!, classBuilder.libraryBuilder.library); |
| assert( |
| instance != null, |
| "No instance of $thisType as ${member.enclosingClass} found for " |
| "$member."); |
| return Substitution.fromInterfaceType(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 used for computing and inspecting the combined member signature for |
| /// a set of overridden/inherited [ClassMember]s. |
| class CombinedClassMemberSignature |
| extends CombinedMemberSignatureBase<ClassMember> { |
| /// The class members builder used for building this class. |
| final ClassMembersBuilder membersBuilder; |
| |
| @override |
| ClassHierarchyBuilder get hierarchy => membersBuilder.hierarchyBuilder; |
| |
| /// The list of the members inherited into or overridden in [classBuilder]. |
| @override |
| final List<ClassMember> members; |
| |
| /// Creates a [CombinedClassMemberSignature] whose canonical member is already |
| /// defined. |
| CombinedClassMemberSignature.internal(this.membersBuilder, |
| SourceClassBuilder classBuilder, int canonicalMemberIndex, this.members, |
| {required bool forSetter}) |
| : super.internal(classBuilder, canonicalMemberIndex, 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( |
| this.membersBuilder, SourceClassBuilder classBuilder, this.members, |
| {required bool forSetter}) |
| : super(classBuilder, forSetter: forSetter); |
| |
| @override |
| Name get name => members.first.name; |
| |
| @override |
| Types get _types => hierarchy.types; |
| |
| @override |
| Member _getMember(int index) { |
| ClassMember candidate = members[index]; |
| Member target = candidate.getMember(membersBuilder); |
| // ignore: unnecessary_null_comparison |
| assert(target != null, |
| "No member computed for ${candidate} (${candidate.runtimeType})"); |
| return target; |
| } |
| |
| @override |
| Covariance _getMemberCovariance(int index) { |
| ClassMember candidate = members[index]; |
| Covariance covariance = candidate.getCovariance(membersBuilder); |
| // ignore: unnecessary_null_comparison |
| assert(covariance != null, |
| "No covariance computed for ${candidate} (${candidate.runtimeType})"); |
| return covariance; |
| } |
| } |
| |
| /// Class used for computing and inspecting the combined member signature for |
| /// a set of overridden/inherited [Member]s. |
| class CombinedMemberSignatureBuilder |
| extends CombinedMemberSignatureBase<Member> { |
| @override |
| final ClassHierarchyBase hierarchy; |
| |
| @override |
| final Types _types; |
| |
| @override |
| final List<Member> members; |
| |
| CombinedMemberSignatureBuilder( |
| this.hierarchy, SourceClassBuilder classBuilder, this.members, |
| {required bool forSetter}) |
| : _types = new Types(hierarchy), |
| super(classBuilder, forSetter: forSetter); |
| |
| @override |
| Name get name => members.first.name; |
| |
| @override |
| Member _getMember(int index) => members[index]; |
| |
| @override |
| Covariance _getMemberCovariance(int index) { |
| _memberCovariances ??= new List<Covariance?>.filled(members.length, null); |
| Covariance? covariance = _memberCovariances![index]; |
| if (covariance == null) { |
| _memberCovariances![index] = covariance = |
| new Covariance.fromMember(members[index], forSetter: forSetter); |
| } |
| return covariance; |
| } |
| } |