blob: 4ed0bd67b73e07afdbb0a5c0d22ab8bc8a154632 [file] [log] [blame]
// Copyright (c) 2016, 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.
library fasta.formal_parameter_builder;
import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart'
show FormalParameterKind;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
import 'package:front_end/src/fasta/source/constructor_declaration.dart';
import 'package:kernel/ast.dart'
show DartType, DynamicType, Expression, NullLiteral, VariableDeclaration;
import 'package:kernel/class_hierarchy.dart';
import '../constant_context.dart' show ConstantContext;
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/body_builder_context.dart';
import '../kernel/internal_ast.dart' show VariableDeclarationImpl;
import '../modifier.dart';
import '../scope.dart' show Scope;
import '../source/source_factory_builder.dart';
import '../source/source_field_builder.dart';
import '../source/source_library_builder.dart';
import '../util/helpers.dart' show DelayedActionPerformer;
import 'builder.dart';
import 'constructor_builder.dart';
import 'declaration_builders.dart';
import 'library_builder.dart';
import 'modifier_builder.dart';
import 'omitted_type_builder.dart';
import 'type_builder.dart';
import 'variable_builder.dart';
abstract class ParameterBuilder {
TypeBuilder get type;
/// The kind of this parameter, i.e. if it's required, positional optional,
/// or named optional.
FormalParameterKind get kind;
bool get isPositional;
bool get isRequiredPositional;
bool get isNamed;
bool get isRequiredNamed;
String? get name;
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration);
}
/// A builder for a formal parameter, i.e. a parameter on a method or
/// constructor.
class FormalParameterBuilder extends ModifierBuilderImpl
implements VariableBuilder, ParameterBuilder, InferredTypeListener {
static const String noNameSentinel = 'no name sentinel';
@override
final int modifiers;
@override
TypeBuilder type;
@override
final String name;
@override
final Uri fileUri;
@override
final FormalParameterKind kind;
/// The variable declaration created for this formal parameter.
@override
VariableDeclaration? variable;
/// The first token of the default value, if any.
///
/// This is stored until outlines have been built through
/// [buildOutlineExpressions].
Token? initializerToken;
bool initializerWasInferred = false;
/// True if the initializer was declared by the programmer.
final bool hasImmediatelyDeclaredInitializer;
/// True if the initializer was declared by the programmer, either directly
/// or inferred from a super parameter.
bool hasDeclaredInitializer;
final bool isExtensionThis;
FormalParameterBuilder(this.kind, this.modifiers, this.type, this.name,
LibraryBuilder? compilationUnit, int charOffset,
{required Uri fileUri,
this.isExtensionThis = false,
required this.hasImmediatelyDeclaredInitializer})
: this.fileUri = fileUri,
this.hasDeclaredInitializer = hasImmediatelyDeclaredInitializer,
super(compilationUnit, charOffset) {
type.registerInferredTypeListener(this);
}
@override
String get debugName => "FormalParameterBuilder";
@override
bool get isRequiredPositional => kind.isRequiredPositional;
// TODO(johnniwinther): This was previously named `isOptional` so we might
// have some uses that intended to use the now existing `isOptional` method.
bool get isOptionalPositional => !isRequiredPositional;
@override
bool get isRequiredNamed => kind.isRequiredNamed;
@override
bool get isPositional => kind.isPositional;
@override
bool get isNamed => kind.isNamed;
bool get isOptional => kind.isOptional;
@override
bool get isLocal => true;
bool get isInitializingFormal => (modifiers & initializingFormalMask) != 0;
bool get isSuperInitializingFormal =>
(modifiers & superInitializingFormalMask) != 0;
bool get isCovariantByDeclaration => (modifiers & covariantMask) != 0;
// An initializing formal parameter might be final without its
// VariableDeclaration being final. See
// [ProcedureBuilder.computeFormalParameterInitializerScope]..
@override
bool get isAssignable =>
variable!.isAssignable &&
!isInitializingFormal &&
!isSuperInitializingFormal;
@override
String get fullNameForErrors => name;
VariableDeclaration build(SourceLibraryBuilder library) {
if (variable == null) {
bool isTypeOmitted = type is OmittedTypeBuilder;
DartType? builtType = type.build(library, TypeUse.parameterType);
variable = new VariableDeclarationImpl(
name == noNameSentinel ? null : name,
// `null` is used in [VariableDeclarationImpl] to signal an omitted
// type.
type: isTypeOmitted ? null : builtType,
isFinal: isFinal,
isConst: false,
isInitializingFormal: isInitializingFormal,
isCovariantByDeclaration: isCovariantByDeclaration,
isRequired: isRequiredNamed,
hasDeclaredInitializer: hasDeclaredInitializer,
isLowered: isExtensionThis,
isSynthesized: name == noNameSentinel)
..fileOffset = charOffset;
}
return variable!;
}
@override
void onInferredType(DartType type) {
if (variable != null) {
variable!.type = type;
}
}
@override
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration) {
return new FunctionTypeParameterBuilder(
kind, type.clone(newTypes, contextLibrary, contextDeclaration), name);
}
FormalParameterBuilder forPrimaryConstructor(
SourceLibraryBuilder sourceLibraryBuilder) {
return new FormalParameterBuilder(kind, modifiers | initializingFormalMask,
sourceLibraryBuilder.addInferableType(), name, null, charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer)
..parent = parent
..variable = variable;
}
FormalParameterBuilder forFormalParameterInitializerScope() {
if (isInitializingFormal) {
return new FormalParameterBuilder(
kind,
modifiers | finalMask | initializingFormalMask,
type,
name,
null,
charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer)
..parent = parent
..variable = variable;
} else if (isSuperInitializingFormal) {
return new FormalParameterBuilder(
kind,
modifiers | finalMask | superInitializingFormalMask,
type,
name,
null,
charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis,
hasImmediatelyDeclaredInitializer: hasImmediatelyDeclaredInitializer)
..parent = parent
..variable = variable;
} else {
return this;
}
}
void finalizeInitializingFormal(
DeclarationBuilder declarationBuilder,
ConstructorDeclaration constructorDeclaration,
ClassHierarchyBase hierarchy) {
Builder? fieldBuilder = declarationBuilder.lookupLocalMember(name);
if (fieldBuilder is SourceFieldBuilder) {
DartType fieldType = fieldBuilder.inferType(hierarchy);
fieldType = constructorDeclaration.substituteFieldType(fieldType);
type.registerInferredType(fieldType);
} else {
type.registerInferredType(const DynamicType());
}
}
bool get needsDefaultValuesBuiltAsOutlineExpressions {
// For modular compilation we need to include default values for optional
// and named parameters in several cases:
// * for const constructors to enable constant evaluation,
// * for instance methods because these might be needed to generated
// noSuchMethod forwarders, and
// * for generative constructors to support forwarding constructors
// in mixin applications.
if (parent is ConstructorBuilder) {
return true;
} else if (parent is SourceFactoryBuilder) {
return parent!.isFactory && parent!.isConst;
} else {
return parent!.isClassInstanceMember;
}
}
/// Builds the default value from this [initializerToken] if this is a
/// formal parameter on a const constructor or instance method.
void buildOutlineExpressions(SourceLibraryBuilder libraryBuilder,
List<DelayedActionPerformer> delayedActionPerformers) {
if (needsDefaultValuesBuiltAsOutlineExpressions) {
if (initializerToken != null) {
final DeclarationBuilder declarationBuilder =
parent!.parent as DeclarationBuilder;
Scope scope = declarationBuilder.scope;
BodyBuilderContext bodyBuilderContext =
new ParameterBodyBuilderContext(this);
BodyBuilder bodyBuilder = libraryBuilder.loader
.createBodyBuilderForOutlineExpression(
libraryBuilder, bodyBuilderContext, scope, fileUri);
bodyBuilder.constantContext = ConstantContext.required;
assert(!initializerWasInferred);
Expression initializer =
bodyBuilder.parseFieldInitializer(initializerToken!);
initializer = bodyBuilder.typeInferrer.inferParameterInitializer(
bodyBuilder, initializer, variable!.type, hasDeclaredInitializer);
variable!.initializer = initializer..parent = variable;
initializerWasInferred = true;
bodyBuilder.performBacklogComputations(
delayedActionPerformers: delayedActionPerformers,
allowFurtherDelays: false);
} else if (kind.isOptional) {
// As done by BodyBuilder.endFormalParameter.
variable!.initializer = new NullLiteral()..parent = variable;
}
}
initializerToken = null;
}
}
class FunctionTypeParameterBuilder implements ParameterBuilder {
@override
final FormalParameterKind kind;
@override
final TypeBuilder type;
@override
final String? name;
FunctionTypeParameterBuilder(this.kind, this.type, this.name);
@override
ParameterBuilder clone(
List<NamedTypeBuilder> newTypes,
SourceLibraryBuilder contextLibrary,
TypeParameterScopeBuilder contextDeclaration) {
return new FunctionTypeParameterBuilder(
kind, type.clone(newTypes, contextLibrary, contextDeclaration), name);
}
@override
bool get isNamed => kind.isNamed;
@override
bool get isRequiredNamed => kind.isRequiredNamed;
@override
bool get isPositional => kind.isPositional;
@override
bool get isRequiredPositional => kind.isRequiredPositional;
}