| // Copyright (c) 2025, 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:_fe_analyzer_shared/src/metadata/expressions.dart' as shared; |
| import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token; |
| import 'package:front_end/src/codes/diagnostic.dart' as diag; |
| import 'package:kernel/ast.dart'; |
| import 'package:kernel/class_hierarchy.dart'; |
| import 'package:kernel/core_types.dart'; |
| import 'package:kernel/type_environment.dart'; |
| |
| import '../../api_prototype/experimental_flags.dart'; |
| import '../../base/messages.dart'; |
| import '../../base/problems.dart'; |
| import '../../base/scope.dart'; |
| import '../../base/uri_offset.dart'; |
| import '../../builder/declaration_builders.dart'; |
| import '../../builder/metadata_builder.dart'; |
| import '../../builder/omitted_type_builder.dart'; |
| import '../../builder/property_builder.dart'; |
| import '../../builder/type_builder.dart'; |
| import '../../kernel/body_builder_context.dart'; |
| import '../../kernel/hierarchy/class_member.dart'; |
| import '../../kernel/hierarchy/members_builder.dart'; |
| import '../../kernel/implicit_field_type.dart'; |
| import '../../kernel/late_lowering.dart' as late_lowering; |
| import '../../kernel/macro/metadata.dart'; |
| import '../../kernel/type_algorithms.dart'; |
| import '../../source/check_helper.dart'; |
| import '../../source/name_scheme.dart'; |
| import '../../source/source_class_builder.dart'; |
| import '../../source/source_library_builder.dart'; |
| import '../../source/source_member_builder.dart'; |
| import '../../source/source_property_builder.dart'; |
| import '../../source/type_parameter_factory.dart'; |
| import '../../type_inference/context_allocation_strategy.dart'; |
| import '../../type_inference/type_inference_engine.dart'; |
| import '../../type_inference/type_inferrer.dart'; |
| import '../../util/helpers.dart'; |
| import '../fragment.dart'; |
| import '../getter/declaration.dart'; |
| import '../setter/declaration.dart'; |
| |
| /// Common interface for fragments that can declare a field. |
| abstract class FieldDeclaration { |
| UriOffsetLength? get uriOffset; |
| |
| FieldQuality get fieldQuality; |
| |
| /// The metadata declared on this fragment. |
| List<MetadataBuilder>? get metadata; |
| |
| /// Builds the core AST structures for this field declaration as needed for |
| /// the outline. |
| void buildFieldOutlineNode( |
| SourceLibraryBuilder libraryBuilder, |
| NameScheme nameScheme, |
| BuildNodesCallback f, |
| PropertyReferences references, { |
| required List<TypeParameter>? classTypeParameters, |
| }); |
| |
| void buildFieldOutlineExpressions({ |
| required ClassHierarchy classHierarchy, |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required List<Annotatable> annotatables, |
| required Uri annotatablesFileUri, |
| required bool forConstantConstructor, |
| }); |
| |
| int computeFieldDefaultTypes(ComputeDefaultTypeContext context); |
| |
| void createFieldEncoding(SourcePropertyBuilder builder); |
| |
| void checkFieldTypes( |
| ProblemReporting problemReporting, |
| TypeEnvironment typeEnvironment, |
| SourcePropertyBuilder? setterBuilder, |
| ); |
| |
| /// Checks the variance of type parameters [sourceClassBuilder] used in the |
| /// type of this field declaration. |
| void checkFieldVariance( |
| SourceClassBuilder sourceClassBuilder, |
| TypeEnvironment typeEnvironment, |
| ); |
| |
| /// Return `true` if the declaration introduces a setter. |
| bool get hasSetter; |
| |
| /// Return `true` if the declaration has an initializer. |
| bool get hasInitializer; |
| |
| /// Return `true` if the declaration is final. |
| bool get isFinal; |
| |
| /// Return `true` if the declaration is late. |
| bool get isLate; |
| |
| /// Returns `true` if the field of this property is not a valid declaration. |
| /// |
| /// For instance declaring an instance field in an extension or extension type |
| /// is not allowed and cannot be encoded coherently in the AST. |
| bool get isInvalidField; |
| |
| /// Returns `true` if this field is declared by an enum element. |
| bool get isEnumElement; |
| |
| /// Returns `true` if the declaration is const. |
| bool get isConst; |
| |
| /// The [DartType] of this field declaration. |
| abstract DartType fieldType; |
| |
| /// Creates the AST node for this field as the default initializer. |
| /// |
| /// This is only used for instance fields. |
| void buildImplicitDefaultValue(); |
| |
| /// Creates the [Initializer] for the implicit initialization of this field |
| /// in a constructor. |
| /// |
| /// This is only used for instance fields. |
| Initializer buildImplicitInitializer(); |
| |
| /// Builds the [Initializer]s for each field used to encode this field |
| /// using the [fileOffset] for the created nodes and [value] as the initial |
| /// field value. |
| /// |
| /// This is only used for instance fields. |
| List<Initializer> buildInitializer( |
| int fileOffset, |
| Expression value, { |
| required bool isSynthetic, |
| }); |
| |
| /// Creates an [Initializer] for initializing this field with its declared |
| /// initializer value and removes the initializer expression from the field |
| /// itself. |
| /// |
| /// This is used to support access of primary constructor parameters in the |
| /// field initializers. For instance |
| /// |
| /// class C(var int a, final int b, int c) { |
| /// int d = a + b + c; |
| /// } |
| /// |
| Initializer takePrimaryConstructorFieldInitializer(); |
| |
| /// Ensures that the type of this field declaration has been computed. |
| void ensureTypes( |
| ClassMembersBuilder membersBuilder, |
| Set<ClassMember>? getterOverrideDependencies, |
| Set<ClassMember>? setterOverrideDependencies, |
| ); |
| |
| /// Infers the type of this field declaration. |
| DartType inferType(ClassHierarchyBase hierarchy); |
| |
| shared.Expression? get initializerExpression; |
| } |
| |
| class RegularFieldDeclaration |
| with FieldDeclarationMixin, FieldFragmentDeclarationMixin |
| implements |
| FieldDeclaration, |
| FieldFragmentDeclaration, |
| GetterDeclaration, |
| SetterDeclaration, |
| Inferable, |
| InferredTypeListener { |
| final FieldFragment _fragment; |
| |
| late final FieldEncoding _encoding; |
| |
| shared.Expression? _initializerExpression; |
| |
| @override |
| bool hasBodyBeenBuilt = false; |
| |
| RegularFieldDeclaration(this._fragment) { |
| _fragment.declaration = this; |
| } |
| |
| @override |
| UriOffsetLength get uriOffset => _fragment.uriOffset; |
| |
| @override |
| SourcePropertyBuilder get builder => _fragment.builder; |
| |
| @override |
| FieldQuality get fieldQuality => _fragment.modifiers.isAbstract |
| ? FieldQuality.Abstract |
| : _fragment.modifiers.isExternal |
| ? FieldQuality.External |
| : FieldQuality.Concrete; |
| |
| @override |
| DartType get fieldType => _encoding.type; |
| |
| @override |
| Uri get fileUri => _fragment.fileUri; |
| |
| @override |
| GetterQuality get getterQuality => _fragment.modifiers.isAbstract |
| ? GetterQuality.ImplicitAbstract |
| : _fragment.modifiers.isExternal |
| ? GetterQuality.ImplicitExternal |
| : GetterQuality.Implicit; |
| |
| @override |
| bool get hasInitializer => _fragment.modifiers.hasInitializer; |
| |
| @override |
| bool get hasSetter => _fragment.hasSetter; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| shared.Expression? get initializerExpression => _initializerExpression; |
| |
| @override |
| bool get isConst => _fragment.modifiers.isConst; |
| |
| @override |
| bool get isEnumElement => false; |
| |
| @override |
| bool get isInvalidField => |
| builder.isExtensionTypeInstanceMember || |
| builder.isExtensionInstanceMember; |
| |
| @override |
| bool get isFinal => _fragment.modifiers.isFinal; |
| |
| @override |
| bool get isLate => _fragment.modifiers.isLate; |
| |
| @override |
| bool get isStatic => |
| _fragment.modifiers.isStatic || builder.declarationBuilder == null; |
| |
| @override |
| List<ClassMember> get localMembers => _encoding.localMembers; |
| |
| @override |
| List<ClassMember> get localSetters => _encoding.localSetters; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<MetadataBuilder>? get metadata => _fragment.metadata; |
| |
| @override |
| int get nameOffset => _fragment.nameOffset; |
| |
| @override |
| Member get readTarget => _encoding.readTarget; |
| |
| @override |
| SetterQuality get setterQuality => !hasSetter |
| ? SetterQuality.Absent |
| : _fragment.modifiers.isAbstract |
| ? SetterQuality.ImplicitAbstract |
| : _fragment.modifiers.isExternal |
| ? SetterQuality.ImplicitExternal |
| : SetterQuality.Implicit; |
| |
| @override |
| TypeBuilder get type => _fragment.type; |
| |
| @override |
| Member? get writeTarget => _encoding.writeTarget; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| DartType get fieldTypeInternal => _encoding.type; |
| |
| @override |
| void set fieldTypeInternal(DartType value) { |
| _encoding.type = value; |
| } |
| |
| @override |
| InferenceDefaultType get inferenceDefaultType => InferenceDefaultType.Dynamic; |
| |
| @override |
| void buildBody( |
| CoreTypes coreTypes, |
| Expression? initializer, { |
| required ScopeProviderInfo? scopeProviderInfo, |
| }) { |
| assert(!hasBodyBeenBuilt, "Body has already been built for $this."); |
| hasBodyBeenBuilt = true; |
| if (!_fragment.modifiers.hasInitializer && |
| initializer != null && |
| initializer is! NullLiteral && |
| // Coverage-ignore(suite): Not run. |
| !_fragment.modifiers.isConst && |
| // Coverage-ignore(suite): Not run. |
| !_fragment.modifiers.isFinal) { |
| internalProblem( |
| diag.internalProblemAlreadyInitialized, |
| nameOffset, |
| fileUri, |
| ); |
| } |
| _encoding.createBodies( |
| coreTypes, |
| initializer, |
| scopeProviderInfo: scopeProviderInfo, |
| ); |
| } |
| |
| @override |
| void buildImplicitDefaultValue() { |
| _encoding.buildImplicitDefaultValue(); |
| } |
| |
| @override |
| Initializer buildImplicitInitializer() { |
| return _encoding.buildImplicitInitializer(); |
| } |
| |
| @override |
| List<Initializer> buildInitializer( |
| int fileOffset, |
| Expression value, { |
| required bool isSynthetic, |
| }) { |
| return _encoding.createInitializer( |
| fileOffset, |
| value, |
| isSynthetic: isSynthetic, |
| ); |
| } |
| |
| @override |
| void buildFieldOutlineExpressions({ |
| required ClassHierarchy classHierarchy, |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required List<Annotatable> annotatables, |
| required Uri annotatablesFileUri, |
| required bool forConstantConstructor, |
| }) { |
| BodyBuilderContext bodyBuilderContext = createBodyBuilderContext(); |
| for (Annotatable annotatable in annotatables) { |
| buildMetadataForOutlineExpressions( |
| libraryBuilder: libraryBuilder, |
| extensionScope: _fragment.enclosingCompilationUnit.extensionScope, |
| scope: _fragment.enclosingScope, |
| bodyBuilderContext: bodyBuilderContext, |
| annotatable: annotatable, |
| annotatableFileUri: annotatablesFileUri, |
| metadata: _fragment.metadata, |
| annotationsFileUri: _fragment.fileUri, |
| ); |
| } |
| // For modular compilation we need to include initializers of all const |
| // fields and all non-static final fields in classes with const constructors |
| // into the outline. |
| Token? token = _fragment.takeInitializerTokenForOutline(); |
| if (!hasBodyBeenBuilt && token != null) { |
| if (_fragment.modifiers.isConst || forConstantConstructor) { |
| if (hasInitializerBeenComputed) { |
| buildBody( |
| classHierarchy.coreTypes, |
| cachedFieldInitializer, |
| scopeProviderInfo: null, |
| ); |
| } else { |
| var ( |
| _, |
| initializer, |
| scopeProviderInfo, |
| ) = _buildFieldInitializerFromToken( |
| classHierarchy: classHierarchy, |
| libraryBuilder: libraryBuilder, |
| bodyBuilderContext: bodyBuilderContext, |
| declaredFieldType: fieldType, |
| token: token, |
| inferenceDefaultType: inferenceDefaultType, |
| ); |
| buildBody( |
| classHierarchy.coreTypes, |
| initializer, |
| scopeProviderInfo: scopeProviderInfo, |
| ); |
| } |
| } |
| } |
| } |
| |
| (DartType, Expression, ScopeProviderInfo?) _buildFieldInitializerFromToken({ |
| required ClassHierarchyBase classHierarchy, |
| required SourceLibraryBuilder libraryBuilder, |
| required BodyBuilderContext bodyBuilderContext, |
| DartType? declaredFieldType, |
| required Token token, |
| required InferenceDefaultType inferenceDefaultType, |
| }) { |
| LookupScope scope = _fragment.enclosingScope; |
| InferredFieldInitializer inferredFieldInitializer = libraryBuilder.loader |
| .createResolver() |
| .buildFieldInitializer( |
| libraryBuilder: libraryBuilder, |
| bodyBuilderContext: bodyBuilderContext, |
| fileUri: fileUri, |
| extensionScope: _fragment.enclosingCompilationUnit.extensionScope, |
| scope: scope, |
| isLate: isLate, |
| declaredFieldType: declaredFieldType, |
| startToken: token, |
| inferenceDataForTesting: builder |
| .dataForTesting |
| // Coverage-ignore(suite): Not run. |
| ?.inferenceData, |
| inferenceDefaultType: inferenceDefaultType, |
| ); |
| if (computeSharedExpressionForTesting) { |
| // Coverage-ignore-block(suite): Not run. |
| _initializerExpression = parseFieldInitializer( |
| libraryBuilder.loader, |
| token, |
| libraryBuilder.importUri, |
| fileUri, |
| scope, |
| libraryBuilder.libraryFeatures, |
| ); |
| } |
| return ( |
| inferredFieldInitializer.expressionInferenceResult.inferredType, |
| inferredFieldInitializer.expressionInferenceResult.expression, |
| inferredFieldInitializer.scopeProviderInfo, |
| ); |
| } |
| |
| @override |
| void buildFieldOutlineNode( |
| SourceLibraryBuilder libraryBuilder, |
| NameScheme nameScheme, |
| BuildNodesCallback f, |
| PropertyReferences references, { |
| required List<TypeParameter>? classTypeParameters, |
| }) { |
| _encoding.buildOutlineNode( |
| libraryBuilder, |
| nameScheme, |
| references, |
| isAbstractOrExternal: |
| _fragment.modifiers.isAbstract || _fragment.modifiers.isExternal, |
| classTypeParameters: classTypeParameters, |
| ); |
| if (type is! InferableTypeBuilder) { |
| fieldType = type.build(libraryBuilder, TypeUse.fieldType); |
| } |
| _encoding.registerMembers(f); |
| } |
| |
| @override |
| void checkFieldTypes( |
| ProblemReporting problemReporting, |
| TypeEnvironment typeEnvironment, |
| SourcePropertyBuilder? setterBuilder, |
| ) { |
| problemReporting.checkTypesInField( |
| typeEnvironment: typeEnvironment, |
| isInstanceMember: builder.isDeclarationInstanceMember, |
| isLate: isLate, |
| isExternal: _fragment.modifiers.isExternal, |
| hasInitializer: hasInitializer, |
| fieldType: fieldType, |
| name: _fragment.name, |
| nameLength: _fragment.name.length, |
| nameOffset: nameOffset, |
| fileUri: fileUri, |
| ); |
| } |
| |
| @override |
| void checkFieldVariance( |
| SourceClassBuilder sourceClassBuilder, |
| TypeEnvironment typeEnvironment, |
| ) { |
| sourceClassBuilder.checkVarianceInField( |
| typeEnvironment, |
| fieldType: fieldType, |
| isInstanceMember: !isStatic, |
| hasSetter: hasSetter, |
| isCovariantByDeclaration: _fragment.modifiers.isCovariant, |
| fileUri: fileUri, |
| fileOffset: nameOffset, |
| ); |
| } |
| |
| @override |
| int computeFieldDefaultTypes(ComputeDefaultTypeContext context) { |
| if (type is! OmittedTypeBuilder) { |
| context.reportInboundReferenceIssuesForType(type); |
| context.recursivelyReportGenericFunctionTypesAsBoundsForType(type); |
| } |
| return 0; |
| } |
| |
| @override |
| BodyBuilderContext createBodyBuilderContext() { |
| return new FieldFragmentBodyBuilderContext( |
| builder, |
| this, |
| isLateField: _fragment.modifiers.isLate, |
| isAbstractField: _fragment.modifiers.isAbstract, |
| isExternalField: _fragment.modifiers.isExternal, |
| nameOffset: _fragment.nameOffset, |
| nameLength: _fragment.name.length, |
| isConst: _fragment.modifiers.isConst, |
| ); |
| } |
| |
| @override |
| void createFieldEncoding(SourcePropertyBuilder builder) { |
| _fragment.builder = builder; |
| |
| SourceLibraryBuilder libraryBuilder = builder.libraryBuilder; |
| |
| bool isAbstract = _fragment.modifiers.isAbstract; |
| bool isExternal = _fragment.modifiers.isExternal; |
| bool isInstanceMember = builder.isDeclarationInstanceMember; |
| bool isExtensionMember = builder.isExtensionMember; |
| bool isExtensionTypeMember = builder.isExtensionTypeMember; |
| |
| // If in mixed mode, late lowerings cannot use `null` as a sentinel on |
| // non-nullable fields since they can be assigned from legacy code. |
| late_lowering.IsSetStrategy isSetStrategy = late_lowering |
| .computeIsSetStrategy(libraryBuilder); |
| if (isAbstract || isExternal) { |
| _encoding = new AbstractOrExternalFieldEncoding( |
| _fragment, |
| isExtensionInstanceMember: isExtensionMember && isInstanceMember, |
| isExtensionTypeInstanceMember: |
| isExtensionTypeMember && isInstanceMember, |
| isAbstract: isAbstract, |
| isExternal: isExternal, |
| ); |
| } else if ((isExtensionMember || isExtensionTypeMember) && |
| isInstanceMember) { |
| // Field on a extension or extension type. Encode as abstract. |
| _encoding = new ExtensionInstanceFieldEncoding( |
| _fragment, |
| isExtensionInstanceMember: isExtensionMember, |
| ); |
| } else if (isLate && |
| libraryBuilder.loader.target.backendTarget.isLateFieldLoweringEnabled( |
| hasInitializer: hasInitializer, |
| isFinal: isFinal, |
| isStatic: !isInstanceMember, |
| )) { |
| if (hasInitializer) { |
| if (isFinal) { |
| _encoding = new LateFinalFieldWithInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } else { |
| _encoding = new LateFieldWithInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } |
| } else { |
| if (isFinal) { |
| _encoding = new LateFinalFieldWithoutInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } else { |
| _encoding = new LateFieldWithoutInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } |
| } |
| } else if (libraryBuilder |
| .loader |
| .target |
| .backendTarget |
| .useStaticFieldLowering && |
| !isInstanceMember && |
| !_fragment.modifiers.isConst && |
| hasInitializer) { |
| if (isFinal) { |
| _encoding = new LateFinalFieldWithInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } else { |
| _encoding = new LateFieldWithInitializerEncoding( |
| _fragment, |
| isSetStrategy: isSetStrategy, |
| ); |
| } |
| } else { |
| _encoding = new RegularFieldEncoding(_fragment, isEnumElement: false); |
| } |
| |
| type.registerInferredTypeListener(this); |
| Token? token = _fragment.takeInitializerTokenForTopLevelInference(); |
| if (type is InferableTypeBuilder) { |
| if (!_fragment.modifiers.hasInitializer && isStatic) { |
| // A static field without type and initializer will always be inferred |
| // to have type `dynamic`. |
| type.registerInferredType(const DynamicType()); |
| } else { |
| // A field with no type and initializer or an instance field without |
| // type and initializer need to have the type inferred. |
| _encoding.type = new InferredType( |
| libraryBuilder: libraryBuilder, |
| typeBuilder: type, |
| inferType: inferType, |
| computeType: _computeInferredType, |
| fileUri: fileUri, |
| name: _fragment.name, |
| nameOffset: nameOffset, |
| nameLength: _fragment.name.length, |
| token: token, |
| ); |
| type.registerInferable(this); |
| } |
| } |
| } |
| |
| @override |
| void ensureTypes( |
| ClassMembersBuilder membersBuilder, |
| Set<ClassMember>? getterOverrideDependencies, |
| Set<ClassMember>? setterOverrideDependencies, |
| ) { |
| if (getterOverrideDependencies != null || |
| setterOverrideDependencies != null) { |
| membersBuilder.inferFieldType( |
| builder.declarationBuilder as SourceClassBuilder, |
| type, |
| [...?getterOverrideDependencies, ...?setterOverrideDependencies], |
| name: _fragment.name, |
| fileUri: fileUri, |
| nameOffset: nameOffset, |
| nameLength: _fragment.name.length, |
| isAssignable: hasSetter, |
| isClosureContextLoweringEnabled: _fragment |
| .builder |
| .libraryBuilder |
| .loader |
| .target |
| .backendTarget |
| .flags |
| .isClosureContextLoweringEnabled, |
| ); |
| } else { |
| // Coverage-ignore-block(suite): Not run. |
| type.build( |
| builder.libraryBuilder, |
| TypeUse.fieldType, |
| hierarchy: membersBuilder.hierarchyBuilder, |
| ); |
| } |
| } |
| |
| @override |
| void registerSuperCall() { |
| _encoding.registerSuperCall(); |
| } |
| |
| (DartType, Expression?, ScopeProviderInfo?) _computeInferredType( |
| ClassHierarchyBase classHierarchy, |
| Token? token, |
| ) { |
| SourceLibraryBuilder libraryBuilder = builder.libraryBuilder; |
| if (token != null) { |
| return _buildFieldInitializerFromToken( |
| classHierarchy: classHierarchy, |
| libraryBuilder: libraryBuilder, |
| bodyBuilderContext: createBodyBuilderContext(), |
| token: token, |
| inferenceDefaultType: InferenceDefaultType.Dynamic, |
| ); |
| } else { |
| return (const DynamicType(), null, null); |
| } |
| } |
| |
| @override |
| void setCovariantByClassInternal() { |
| _encoding.setCovariantByClass(); |
| } |
| |
| @override |
| void buildGetterOutlineExpressions({ |
| required ClassHierarchy classHierarchy, |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required SourcePropertyBuilder propertyBuilder, |
| required Annotatable annotatable, |
| required Uri annotatableFileUri, |
| }) {} |
| |
| @override |
| void buildGetterOutlineNode({ |
| required SourceLibraryBuilder libraryBuilder, |
| required NameScheme nameScheme, |
| required BuildNodesCallback f, |
| required PropertyReferences? references, |
| required List<TypeParameter>? classTypeParameters, |
| }) {} |
| |
| @override |
| void buildSetterOutlineExpressions({ |
| required ClassHierarchy classHierarchy, |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required SourcePropertyBuilder propertyBuilder, |
| required Annotatable annotatable, |
| required Uri annotatableFileUri, |
| }) {} |
| |
| @override |
| void buildSetterOutlineNode({ |
| required SourceLibraryBuilder libraryBuilder, |
| required ProblemReporting problemReporting, |
| required NameScheme nameScheme, |
| required BuildNodesCallback f, |
| required PropertyReferences? references, |
| required List<TypeParameter>? classTypeParameters, |
| }) {} |
| |
| @override |
| void checkGetterTypes( |
| ProblemReporting problemReporting, |
| LibraryFeatures libraryFeatures, |
| TypeEnvironment typeEnvironment, |
| SourcePropertyBuilder? setterBuilder, |
| ) {} |
| |
| @override |
| void checkGetterVariance( |
| SourceClassBuilder sourceClassBuilder, |
| TypeEnvironment typeEnvironment, |
| ) {} |
| |
| @override |
| void checkSetterTypes( |
| ProblemReporting problemReporting, |
| TypeEnvironment typeEnvironment, |
| ) {} |
| |
| @override |
| void checkSetterVariance( |
| SourceClassBuilder sourceClassBuilder, |
| TypeEnvironment typeEnvironment, |
| ) {} |
| |
| @override |
| int computeGetterDefaultTypes(ComputeDefaultTypeContext context) { |
| return 0; |
| } |
| |
| @override |
| int computeSetterDefaultTypes(ComputeDefaultTypeContext context) { |
| return 0; |
| } |
| |
| @override |
| void createGetterEncoding( |
| ProblemReporting problemReporting, |
| SourcePropertyBuilder builder, |
| PropertyEncodingStrategy encodingStrategy, |
| TypeParameterFactory typeParameterFactory, |
| ) {} |
| |
| @override |
| void createSetterEncoding( |
| ProblemReporting problemReporting, |
| SourcePropertyBuilder builder, |
| PropertyEncodingStrategy encodingStrategy, |
| TypeParameterFactory typeParameterFactory, |
| ) {} |
| |
| @override |
| void ensureGetterTypes({ |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required ClassMembersBuilder membersBuilder, |
| required Set<ClassMember>? getterOverrideDependencies, |
| }) {} |
| |
| @override |
| void ensureSetterTypes({ |
| required SourceLibraryBuilder libraryBuilder, |
| required DeclarationBuilder? declarationBuilder, |
| required ClassMembersBuilder membersBuilder, |
| required Set<ClassMember>? setterOverrideDependencies, |
| }) {} |
| |
| @override |
| Iterable<Reference> getExportedGetterReferences( |
| PropertyReferences references, |
| ) { |
| return [references.getterReference]; |
| } |
| |
| @override |
| Iterable<Reference> getExportedSetterReferences( |
| PropertyReferences references, |
| ) { |
| return hasSetter ? [references.setterReference] : const []; |
| } |
| |
| @override |
| Initializer takePrimaryConstructorFieldInitializer() { |
| return _encoding.takePrimaryConstructorFieldInitializer(); |
| } |
| } |
| |
| mixin FieldDeclarationMixin |
| implements FieldDeclaration, Inferable, InferredTypeListener { |
| Uri get fileUri; |
| |
| int get nameOffset; |
| |
| SourcePropertyBuilder get builder; |
| |
| @override |
| bool get isConst; |
| |
| /// The [TypeBuilder] for the declared type of this field declaration. |
| TypeBuilder get type; |
| |
| void setCovariantByClassInternal(); |
| |
| abstract DartType fieldTypeInternal; |
| |
| @override |
| void onInferredType(DartType type) { |
| fieldType = type; |
| } |
| |
| @override |
| void inferTypes(ClassHierarchyBase hierarchy) { |
| inferType(hierarchy); |
| } |
| |
| @override |
| DartType inferType(ClassHierarchyBase hierarchy) { |
| if (fieldType is! InferredType) { |
| // We have already inferred a type. |
| return fieldType; |
| } |
| |
| return builder.libraryBuilder.loader.withUriForCrashReporting( |
| fileUri, |
| nameOffset, |
| () { |
| InferredType implicitFieldType = fieldType as InferredType; |
| var ( |
| DartType inferredType, |
| Expression? initializer, |
| ScopeProviderInfo? _, |
| ) = implicitFieldType.computeType( |
| hierarchy, |
| ); |
| if (fieldType is InferredType) { |
| // `fieldType` may have changed if a circularity was detected when |
| // [inferredType] was computed. |
| type.registerInferredType(inferredType); |
| |
| // TODO(johnniwinther): Isn't this handled in the [fieldType] setter? |
| IncludesTypeParametersNonCovariantly? needsCheckVisitor; |
| DeclarationBuilder? declarationBuilder = builder.declarationBuilder; |
| if (declarationBuilder is ClassBuilder) { |
| Class enclosingClass = declarationBuilder.cls; |
| if (enclosingClass.typeParameters.isNotEmpty) { |
| needsCheckVisitor = new IncludesTypeParametersNonCovariantly( |
| enclosingClass.typeParameters, |
| // We are checking the field type as if it is the type of the |
| // parameter of the implicit setter and this is a contravariant |
| // position. |
| initialVariance: Variance.contravariant, |
| ); |
| } |
| } |
| if (needsCheckVisitor != null) { |
| if (fieldType.accept(needsCheckVisitor)) { |
| setCovariantByClassInternal(); |
| } |
| } |
| cacheFieldInitializer(initializer); |
| } |
| return fieldType; |
| }, |
| ); |
| } |
| |
| /// Builds the body of this field using [initializer] as the initializer |
| /// expression. |
| void buildBody( |
| CoreTypes coreTypes, |
| Expression? initializer, { |
| required ScopeProviderInfo? scopeProviderInfo, |
| }); |
| |
| /// Caches the [initializer], computed for top level inference. |
| /// |
| /// The field initializer is included in the outline if the field is constant, |
| /// or an instance field in a class with a const constructor. Otherwise, the |
| /// field added to the body during full compilation. |
| void cacheFieldInitializer(Expression? initializer); |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| DartType get fieldType => fieldTypeInternal; |
| |
| @override |
| void set fieldType(DartType value) { |
| fieldTypeInternal = value; |
| DeclarationBuilder? declarationBuilder = builder.declarationBuilder; |
| // TODO(johnniwinther): Should this be `hasSetter`? |
| if (!isFinal && !isConst && declarationBuilder is ClassBuilder) { |
| Class enclosingClass = declarationBuilder.cls; |
| if (enclosingClass.typeParameters.isNotEmpty) { |
| IncludesTypeParametersNonCovariantly needsCheckVisitor = |
| new IncludesTypeParametersNonCovariantly( |
| enclosingClass.typeParameters, |
| // We are checking the field type as if it is the type of the |
| // parameter of the implicit setter and this is a contravariant |
| // position. |
| initialVariance: Variance.contravariant, |
| ); |
| if (value.accept(needsCheckVisitor)) { |
| setCovariantByClassInternal(); |
| } |
| } |
| } |
| } |
| } |
| |
| abstract class FieldFragmentDeclaration { |
| bool get isStatic; |
| |
| void buildFieldInitializer({ |
| required TypeInferrer typeInferrer, |
| required CoreTypes coreTypes, |
| required Uri fileUri, |
| Expression? initializer, |
| required ThisVariable? internalThisVariable, |
| }); |
| |
| BodyBuilderContext createBodyBuilderContext(); |
| |
| /// Registers that a `super` call has occurred in the initializer of this |
| /// field. |
| void registerSuperCall(); |
| } |
| |
| mixin FieldFragmentDeclarationMixin implements FieldFragmentDeclaration { |
| DartType get fieldType; |
| |
| /// Whether the body of this field has been built. |
| /// |
| /// Constant fields have their initializer built in the outline so we avoid |
| /// building them twice as part of the non-outline build. |
| bool get hasInitializerBeenComputed => _hasInitializerBeenComputed; |
| |
| /// Whether the body of this field has been built. |
| /// |
| /// Constant fields have their initializer built in the outline so we avoid |
| /// building them twice as part of the non-outline build. |
| bool get hasBodyBeenBuilt; |
| |
| bool _hasInitializerBeenComputed = false; |
| Expression? _fieldInitializerCache; |
| |
| /// Builds the body of this field using [initializer] as the initializer |
| /// expression. |
| void buildBody( |
| CoreTypes coreType, |
| Expression? initializer, { |
| required ScopeProviderInfo? scopeProviderInfo, |
| }); |
| |
| /// Caches the [initializer], computed for top level inference. |
| /// |
| /// The field initializer is included in the outline if the field is constant, |
| /// or an instance field in a class with a const constructor. Otherwise, the |
| /// field added to the body during full compilation. |
| void cacheFieldInitializer(Expression? initializer) { |
| _fieldInitializerCache = initializer; |
| _hasInitializerBeenComputed = true; |
| } |
| |
| Expression? get cachedFieldInitializer => _fieldInitializerCache; |
| |
| InferenceDefaultType get inferenceDefaultType; |
| |
| @override |
| void buildFieldInitializer({ |
| required TypeInferrer typeInferrer, |
| required CoreTypes coreTypes, |
| required Uri fileUri, |
| Expression? initializer, |
| required ThisVariable? internalThisVariable, |
| }) { |
| if (_fieldInitializerCache != null) { |
| if (!hasBodyBeenBuilt) { |
| buildBody(coreTypes, _fieldInitializerCache, scopeProviderInfo: null); |
| } |
| } else if (initializer != null) { |
| if (!hasBodyBeenBuilt) { |
| InferredFieldInitializer inferredFieldInitializer = typeInferrer |
| .inferFieldInitializer( |
| fileUri: fileUri, |
| declaredType: fieldType, |
| initializer: initializer, |
| inferenceDefaultType: inferenceDefaultType, |
| internalThisVariable: internalThisVariable, |
| ); |
| initializer = |
| inferredFieldInitializer.expressionInferenceResult.expression; |
| _hasInitializerBeenComputed = true; |
| buildBody( |
| coreTypes, |
| initializer, |
| scopeProviderInfo: inferredFieldInitializer.scopeProviderInfo, |
| ); |
| } |
| } else if (!hasBodyBeenBuilt) { |
| _hasInitializerBeenComputed = true; |
| buildBody(coreTypes, null, scopeProviderInfo: null); |
| } |
| } |
| } |