| // Copyright (c) 2024, 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/type_environment.dart'; |
| |
| import '../base/modifiers.dart'; |
| import '../base/name_space.dart'; |
| import '../builder/builder.dart'; |
| import '../builder/declaration_builders.dart'; |
| import '../builder/formal_parameter_builder.dart'; |
| import '../builder/metadata_builder.dart'; |
| import '../builder/method_builder.dart'; |
| import '../builder/type_builder.dart'; |
| import '../fragment/fragment.dart'; |
| import '../kernel/augmentation_lowering.dart'; |
| import '../kernel/hierarchy/class_member.dart'; |
| import '../kernel/hierarchy/members_builder.dart'; |
| import '../kernel/kernel_helper.dart'; |
| import '../kernel/member_covariance.dart'; |
| import '../kernel/type_algorithms.dart'; |
| import 'name_scheme.dart'; |
| import 'source_class_builder.dart'; |
| import 'source_function_builder.dart'; |
| import 'source_library_builder.dart'; |
| import 'source_member_builder.dart'; |
| |
| class SourceMethodBuilder extends SourceMemberBuilderImpl |
| implements MethodBuilder { |
| @override |
| final Uri fileUri; |
| |
| @override |
| final int fileOffset; |
| |
| @override |
| final String name; |
| |
| @override |
| final SourceLibraryBuilder libraryBuilder; |
| |
| @override |
| final DeclarationBuilder? declarationBuilder; |
| |
| @override |
| final bool isStatic; |
| |
| final NameScheme _nameScheme; |
| |
| @override |
| final bool isOperator; |
| |
| /// The declarations that introduces this method. Subsequent methods of the |
| /// same name must be augmentations. |
| // TODO(johnniwinther): Add [_augmentations] field. |
| final MethodFragment _introductory; |
| |
| Modifiers _modifiers; |
| |
| final Reference _reference; |
| final Reference? _tearOffReference; |
| |
| final MemberName _memberName; |
| |
| // TODO(johnniwinther): Implement augmentation using fragments. |
| |
| /// The builder for the original declaration. |
| SourceMethodBuilder? _origin; |
| |
| /// If this builder is a patch or an augmentation, this is the builder for |
| /// the immediately augmented procedure. |
| SourceMethodBuilder? _augmentedBuilder; |
| |
| Procedure? _augmentedMethod; |
| |
| int _augmentationIndex = 0; |
| |
| List<SourceMethodBuilder>? _augmentations; |
| |
| SourceMethodBuilder( |
| {required this.fileUri, |
| required this.fileOffset, |
| required this.name, |
| required this.libraryBuilder, |
| required this.declarationBuilder, |
| required this.isStatic, |
| required NameScheme nameScheme, |
| required MethodFragment fragment, |
| required Reference? reference, |
| required Reference? tearOffReference}) |
| : _nameScheme = nameScheme, |
| _introductory = fragment, |
| _modifiers = fragment.modifiers, |
| isOperator = fragment.isOperator, |
| _reference = reference ?? new Reference(), |
| _tearOffReference = tearOffReference, |
| _memberName = nameScheme.getDeclaredName(name); |
| |
| @override |
| Builder get parent => declarationBuilder ?? libraryBuilder; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isAugmentation => _modifiers.isAugment; |
| |
| @override |
| bool get isExternal => _modifiers.isExternal; |
| |
| @override |
| bool get isAbstract => _modifiers.isAbstract; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isConst => _modifiers.isConst; |
| |
| @override |
| bool get isAugment => _modifiers.isAugment; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isFinal => false; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isSynthesized => false; |
| |
| @override |
| bool get isEnumElement => false; |
| |
| // TODO(johnniwinther): What is this supposed to return? |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterable<Annotatable> get annotatables => [ |
| if (readTarget != null && invokeTarget != readTarget) |
| readTarget as Annotatable, |
| invokeTarget as Annotatable, |
| ]; |
| |
| // Coverage-ignore(suite): Not run. |
| // TODO(johnniwinther): Remove this. This is only needed for detecting patches |
| // and macro annotations and we should use the fragment directly once |
| // augmentations are fragments. |
| List<MetadataBuilder>? get metadata => _introductory.metadata; |
| |
| @override |
| void addAugmentation(Builder augmentation) { |
| _addAugmentation(augmentation); |
| } |
| |
| void _addAugmentation(Builder augmentation) { |
| if (augmentation is SourceMethodBuilder) { |
| if (checkAugmentation( |
| augmentationLibraryBuilder: augmentation.libraryBuilder, |
| origin: this, |
| augmentation: augmentation)) { |
| augmentation._origin = this; |
| |
| SourceMethodBuilder augmentedBuilder = |
| _augmentations == null ? this : _augmentations!.last; |
| augmentation._augmentedBuilder = augmentedBuilder; |
| augmentation._augmentationIndex = |
| augmentedBuilder._augmentationIndex + 1; |
| (_augmentations ??= []).add(augmentation); |
| } |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| reportAugmentationMismatch( |
| originLibraryBuilder: libraryBuilder, |
| origin: this, |
| augmentation: augmentation); |
| } |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void applyAugmentation(Builder augmentation) { |
| _addAugmentation(augmentation); |
| } |
| |
| @override |
| SourceMethodBuilder get origin => _origin ?? this; |
| |
| bool get isAugmented { |
| if (isAugmenting) { |
| return origin._augmentations!.last != this; |
| } else { |
| return _augmentations != null; |
| } |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| List<SourceMethodBuilder>? get augmentationsForTesting => _augmentations; |
| |
| Map<SourceMethodBuilder, AugmentSuperTarget?> _augmentedMethods = {}; |
| |
| // Coverage-ignore(suite): Not run. |
| AugmentSuperTarget? _createAugmentSuperTarget( |
| SourceMethodBuilder? targetBuilder) { |
| if (targetBuilder == null) return null; |
| |
| Procedure declaredMethod = targetBuilder._introductory.invokeTarget; |
| if (declaredMethod.isAbstract || declaredMethod.isExternal) { |
| return targetBuilder._augmentedBuilder != null |
| ? _getAugmentSuperTarget(targetBuilder._augmentedBuilder!) |
| : null; |
| } |
| |
| Procedure augmentedMethod = targetBuilder._augmentedMethod = new Procedure( |
| augmentedName(declaredMethod.name.text, libraryBuilder.library, |
| targetBuilder._augmentationIndex), |
| declaredMethod.kind, |
| declaredMethod.function, |
| fileUri: declaredMethod.fileUri) |
| ..flags = declaredMethod.flags |
| ..isStatic = declaredMethod.isStatic |
| ..parent = declaredMethod.parent |
| ..isInternalImplementation = true; |
| |
| return new AugmentSuperTarget( |
| declaration: targetBuilder, |
| readTarget: augmentedMethod, |
| invokeTarget: augmentedMethod, |
| writeTarget: null); |
| } |
| |
| // Coverage-ignore(suite): Not run. |
| AugmentSuperTarget? _getAugmentSuperTarget(SourceMethodBuilder augmentation) { |
| return _augmentedMethods[augmentation] ??= |
| _createAugmentSuperTarget(augmentation._augmentedBuilder); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| AugmentSuperTarget? get augmentSuperTarget => |
| origin._getAugmentSuperTarget(this); |
| |
| @override |
| int buildBodyNodes(BuildNodesCallback f) { |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| void addAugmentedMethod(SourceMethodBuilder builder) { |
| Procedure? augmentedMethod = builder._augmentedMethod; |
| if (augmentedMethod != null) { |
| // Coverage-ignore-block(suite): Not run. |
| augmentedMethod |
| ..fileOffset = builder._introductory.invokeTarget.fileOffset |
| ..fileEndOffset = builder._introductory.invokeTarget.fileEndOffset |
| ..fileStartOffset = |
| builder._introductory.invokeTarget.fileStartOffset |
| ..signatureType = builder._introductory.invokeTarget.signatureType |
| ..flags = builder._introductory.invokeTarget.flags; |
| f(member: augmentedMethod, kind: BuiltMemberKind.Method); |
| } |
| } |
| |
| addAugmentedMethod(this); |
| for (SourceMethodBuilder augmentation in augmentations) { |
| addAugmentedMethod(augmentation); |
| } |
| finishProcedureAugmentation(_introductory.invokeTarget, |
| augmentations.last._introductory.invokeTarget); |
| |
| return augmentations.length; |
| } |
| return 0; |
| } |
| |
| @override |
| void buildOutlineNodes(BuildNodesCallback f) { |
| _introductory.buildOutlineNode(libraryBuilder, _nameScheme, f, |
| reference: _reference, |
| tearOffReference: _tearOffReference, |
| classTypeParameters: classBuilder?.cls.typeParameters); |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| for (SourceMethodBuilder augmentation in augmentations) { |
| augmentation.buildOutlineNodes(( |
| {required Member member, |
| Member? tearOff, |
| required BuiltMemberKind kind}) { |
| // Don't add augmentations. |
| }); |
| } |
| } |
| } |
| |
| bool hasBuiltOutlineExpressions = false; |
| |
| @override |
| void buildOutlineExpressions(ClassHierarchy classHierarchy, |
| List<DelayedDefaultValueCloner> delayedDefaultValueCloners) { |
| if (!hasBuiltOutlineExpressions) { |
| _introductory.buildOutlineExpressions(classHierarchy, libraryBuilder, |
| declarationBuilder, invokeTarget as Annotatable, |
| isClassInstanceMember: isClassInstanceMember, |
| createFileUriExpression: isAugmented); |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| for (SourceMethodBuilder augmentation in augmentations) { |
| augmentation.buildOutlineExpressions( |
| classHierarchy, delayedDefaultValueCloners); |
| } |
| } |
| hasBuiltOutlineExpressions = true; |
| } |
| } |
| |
| @override |
| void checkTypes(SourceLibraryBuilder library, NameSpace nameSpace, |
| TypeEnvironment typeEnvironment) { |
| _introductory.checkTypes(library, typeEnvironment, |
| isExternal: isExternal, isAbstract: isAbstract); |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| for (SourceMethodBuilder augmentation in augmentations) { |
| augmentation.checkTypes(libraryBuilder, nameSpace, typeEnvironment); |
| } |
| } |
| } |
| |
| @override |
| void checkVariance( |
| SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { |
| if (!isClassInstanceMember) return; |
| _introductory.checkVariance(sourceClassBuilder, typeEnvironment); |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| for (SourceMethodBuilder augmentation in augmentations) { |
| augmentation.checkVariance(sourceClassBuilder, typeEnvironment); |
| } |
| } |
| } |
| |
| @override |
| Iterable<Reference> get exportedMemberReferences => [ |
| _reference, |
| ]; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool get isAssignable => |
| throw new UnsupportedError('$runtimeType.isAssignable'); |
| |
| List<ClassMember>? _localMembers; |
| |
| @override |
| List<ClassMember> get localMembers => |
| _localMembers ??= [new _MethodClassMember(this, _introductory)]; |
| |
| @override |
| List<ClassMember> get localSetters => const []; |
| |
| @override |
| Name get memberName => _memberName.name; |
| |
| @override |
| Member? get readTarget => isAugmenting |
| ? |
| // Coverage-ignore(suite): Not run. |
| _origin!.readTarget |
| : _introductory.readTarget; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference? get readTargetReference => isAugmenting |
| ? _origin!.readTargetReference |
| : (_tearOffReference ?? _reference); |
| |
| @override |
| Member get invokeTarget => |
| isAugmenting ? _origin!.invokeTarget : _introductory.invokeTarget; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference get invokeTargetReference => |
| isAugmenting ? _origin!.invokeTargetReference : _reference; |
| |
| @override |
| Member? get writeTarget => null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Reference? get writeTargetReference => null; |
| |
| @override |
| int computeDefaultTypes(ComputeDefaultTypeContext context, |
| {required bool inErrorRecovery}) { |
| int count = _introductory.computeDefaultTypes(context); |
| if (declarationBuilder == null) { |
| List<SourceMethodBuilder>? augmentations = _augmentations; |
| if (augmentations != null) { |
| for (SourceMethodBuilder augmentation in augmentations) { |
| count += augmentation.computeDefaultTypes(context, |
| inErrorRecovery: inErrorRecovery); |
| } |
| } |
| } |
| return count; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| Iterable<MetadataBuilder>? get metadataForTesting => _introductory.metadata; |
| |
| // Coverage-ignore(suite): Not run. |
| // TODO(johnniwinther): Remove these (or reinterpret them). These are used |
| // for testing and macros by rely on old assumptions of the builder model. |
| List<NominalParameterBuilder>? get typeParametersForTesting => |
| _introductory.typeParametersForTesting; |
| |
| // Coverage-ignore(suite): Not run. |
| List<FormalParameterBuilder>? get formalsForTesting => |
| _introductory.formalsForTesting; |
| |
| // Coverage-ignore(suite): Not run. |
| TypeBuilder? get returnTypeForTesting => _introductory.returnType; |
| |
| @override |
| bool get isAugmenting => this != origin; |
| |
| @override |
| bool get isProperty => false; |
| |
| @override |
| bool get isRegularMethod => !isOperator; |
| |
| bool _typeEnsured = false; |
| Set<ClassMember>? _overrideDependencies; |
| |
| void _registerOverrideDependency(Set<ClassMember> overriddenMembers) { |
| assert( |
| overriddenMembers.every((overriddenMember) => |
| overriddenMember.declarationBuilder != classBuilder), |
| "Unexpected override dependencies for $this: $overriddenMembers"); |
| _overrideDependencies ??= {}; |
| _overrideDependencies!.addAll(overriddenMembers); |
| } |
| |
| void _ensureTypes(ClassMembersBuilder membersBuilder) { |
| if (_typeEnsured) return; |
| _introductory.ensureTypes(membersBuilder, |
| declarationBuilder as SourceClassBuilder, _overrideDependencies); |
| _overrideDependencies = null; |
| _typeEnsured = true; |
| } |
| } |
| |
| class _MethodClassMember implements ClassMember { |
| final SourceMethodBuilder _builder; |
| final MethodFragment _fragment; |
| late final Covariance _covariance = |
| new Covariance.fromMethod(_builder.invokeTarget as Procedure); |
| |
| _MethodClassMember(this._builder, this._fragment); |
| |
| @override |
| int get charOffset => _fragment.nameOffset; |
| |
| @override |
| DeclarationBuilder get declarationBuilder => _builder.declarationBuilder!; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<ClassMember> get declarations => |
| throw new UnsupportedError('$runtimeType.declarations'); |
| |
| @override |
| Uri get fileUri => _fragment.fileUri; |
| |
| @override |
| bool get forSetter => false; |
| |
| @override |
| String get fullName { |
| String className = declarationBuilder.fullNameForErrors; |
| return "${className}.${fullNameForErrors}"; |
| } |
| |
| @override |
| String get fullNameForErrors => _builder.fullNameForErrors; |
| |
| @override |
| Covariance getCovariance(ClassMembersBuilder membersBuilder) => _covariance; |
| |
| @override |
| Member getMember(ClassMembersBuilder membersBuilder) { |
| return _builder.invokeTarget; |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| MemberResult getMemberResult(ClassMembersBuilder membersBuilder) { |
| if (isStatic) { |
| return new StaticMemberResult(getMember(membersBuilder), memberKind, |
| isDeclaredAsField: false, |
| fullName: '${declarationBuilder.name}.${_builder.memberName.text}'); |
| } else if (_builder.isExtensionTypeMember) { |
| ExtensionTypeDeclaration extensionTypeDeclaration = |
| (declarationBuilder as ExtensionTypeDeclarationBuilder) |
| .extensionTypeDeclaration; |
| Member member = getTearOff(membersBuilder) ?? getMember(membersBuilder); |
| return new ExtensionTypeMemberResult( |
| extensionTypeDeclaration, member, memberKind, name, |
| isDeclaredAsField: false); |
| } else { |
| return new TypeDeclarationInstanceMemberResult( |
| getMember(membersBuilder), memberKind, |
| isDeclaredAsField: false); |
| } |
| } |
| |
| @override |
| Member? getTearOff(ClassMembersBuilder membersBuilder) { |
| if (_builder.readTarget != _builder.invokeTarget) { |
| return _builder.readTarget; |
| } |
| return null; |
| } |
| |
| @override |
| bool get hasDeclarations => false; |
| |
| @override |
| void inferType(ClassMembersBuilder membersBuilder) { |
| _builder._ensureTypes(membersBuilder); |
| } |
| |
| @override |
| ClassMember get interfaceMember => this; |
| |
| @override |
| // TODO(johnniwinther): This should not be determined by the builder. A |
| // property can have a non-abstract getter and an abstract setter or the |
| // reverse. With augmentations, abstract introductory declarations might even |
| // be implemented by augmentations. |
| bool get isAbstract => _fragment.modifiers.isAbstract; |
| |
| @override |
| bool get isDuplicate => _builder.isDuplicate; |
| |
| @override |
| bool get isExtensionTypeMember => _builder.isExtensionTypeMember; |
| |
| @override |
| bool get isField => false; |
| |
| @override |
| bool get isGetter => false; |
| |
| @override |
| bool get isInternalImplementation => false; |
| |
| @override |
| bool get isNoSuchMethodForwarder => false; |
| |
| @override |
| bool isObjectMember(ClassBuilder objectClass) { |
| return declarationBuilder == objectClass; |
| } |
| |
| @override |
| bool get isProperty => false; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| bool isSameDeclaration(ClassMember other) { |
| return other is _MethodClassMember && _builder == other._builder; |
| } |
| |
| @override |
| bool get isSetter => false; |
| |
| @override |
| bool get isSourceDeclaration => true; |
| |
| @override |
| bool get isStatic => _fragment.modifiers.isStatic; |
| |
| @override |
| bool get isSynthesized => false; |
| |
| @override |
| ClassMemberKind get memberKind => ClassMemberKind.Method; |
| |
| @override |
| Name get name => _builder.memberName; |
| |
| @override |
| void registerOverrideDependency(Set<ClassMember> overriddenMembers) { |
| _builder._registerOverrideDependency(overriddenMembers); |
| } |
| |
| @override |
| String toString() => '$runtimeType($fullName,forSetter=${forSetter})'; |
| } |