blob: 3b8fe9b226d9a86734437586d178c4f030b19794 [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.
// @dart = 2.9
library fasta.formal_parameter_builder;
import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart'
show
isMandatoryFormalParameterKind,
isOptionalNamedFormalParameterKind,
isOptionalPositionalFormalParameterKind;
import 'package:_fe_analyzer_shared/src/parser/parser.dart'
show FormalParameterKind;
import 'package:_fe_analyzer_shared/src/scanner/scanner.dart' show Token;
import 'package:front_end/src/fasta/builder/procedure_builder.dart';
import 'package:kernel/ast.dart'
show DartType, DynamicType, Expression, VariableDeclaration;
import 'package:kernel/src/legacy_erasure.dart';
import '../constant_context.dart' show ConstantContext;
import '../modifier.dart';
import '../scope.dart' show Scope;
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_loader.dart' show SourceLoader;
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/internal_ast.dart' show VariableDeclarationImpl;
import 'builder.dart';
import 'class_builder.dart';
import 'constructor_builder.dart';
import 'field_builder.dart';
import 'library_builder.dart';
import 'metadata_builder.dart';
import 'modifier_builder.dart';
import 'type_builder.dart';
import 'variable_builder.dart';
/// A builder for a formal parameter, i.e. a parameter on a method or
/// constructor.
class FormalParameterBuilder extends ModifierBuilderImpl
implements VariableBuilder {
/// List of metadata builders for the metadata declared on this parameter.
final List<MetadataBuilder> metadata;
final int modifiers;
final TypeBuilder type;
final String name;
/// The kind of this parameter, i.e. if it's required, positional optional,
/// or named optional.
FormalParameterKind kind = FormalParameterKind.mandatory;
/// The variable declaration created for this formal parameter.
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.
bool hasDeclaredInitializer = false;
final bool isExtensionThis;
FormalParameterBuilder(this.metadata, this.modifiers, this.type, this.name,
LibraryBuilder compilationUnit, int charOffset,
{Uri fileUri, this.isExtensionThis: false})
: super(compilationUnit, charOffset, fileUri);
String get debugName => "FormalParameterBuilder";
// TODO(johnniwinther): Cleanup `isRequired` semantics in face of required
// named parameters.
bool get isRequired => isMandatoryFormalParameterKind(kind);
// TODO(johnniwinther): Rename to `isRequired`.
bool get isNamedRequired => (modifiers & requiredMask) != 0;
bool get isPositional {
return isOptionalPositionalFormalParameterKind(kind) ||
isMandatoryFormalParameterKind(kind);
}
bool get isNamed => isOptionalNamedFormalParameterKind(kind);
bool get isOptional => !isRequired;
bool get isLocal => true;
bool get isInitializingFormal => (modifiers & initializingFormalMask) != 0;
bool get isCovariant => (modifiers & covariantMask) != 0;
// An initializing formal parameter might be final without its
// VariableDeclaration being final. See
// [ProcedureBuilder.computeFormalParameterInitializerScope]..
bool get isAssignable => variable.isAssignable && !isInitializingFormal;
@override
String get fullNameForErrors => name;
VariableDeclaration build(
SourceLibraryBuilder library, int functionNestingLevel,
[bool notInstanceContext]) {
if (variable == null) {
DartType builtType = type?.build(library, null, notInstanceContext);
if (!library.isNonNullableByDefault && builtType != null) {
builtType = legacyErasure(builtType);
}
variable = new VariableDeclarationImpl(name, functionNestingLevel,
type: builtType,
isFinal: isFinal,
isConst: isConst,
isFieldFormal: isInitializingFormal,
isCovariant: isCovariant,
isRequired: isNamedRequired,
hasDeclaredInitializer: hasDeclaredInitializer,
isLowered: isExtensionThis)
..fileOffset = charOffset;
}
return variable;
}
FormalParameterBuilder clone(List<TypeBuilder> newTypes) {
// TODO(dmitryas): It's not clear how [metadata] is used currently, and
// how it should be cloned. Consider cloning it instead of reusing it.
return new FormalParameterBuilder(
metadata, modifiers, type?.clone(newTypes), name, parent, charOffset,
fileUri: fileUri, isExtensionThis: isExtensionThis)
..kind = kind;
}
FormalParameterBuilder forFormalParameterInitializerScope() {
assert(variable != null);
return !isInitializingFormal
? this
: (new FormalParameterBuilder(
metadata,
modifiers | finalMask | initializingFormalMask,
type,
name,
null,
charOffset,
fileUri: fileUri,
isExtensionThis: isExtensionThis)
..parent = parent
..variable = variable);
}
void finalizeInitializingFormal(ClassBuilder classBuilder) {
assert(variable.type == null);
Builder fieldBuilder = classBuilder.lookupLocalMember(name);
if (fieldBuilder is FieldBuilder) {
variable.type = fieldBuilder.inferType();
} else {
variable.type = const DynamicType();
}
}
/// Builds the default value from this [initializerToken] if this is a
/// formal parameter on a const constructor or instance method.
void buildOutlineExpressions(LibraryBuilder library) {
if (initializerToken != null) {
// For modular compilation we need to include initializers for optional
// and named parameters of const constructors into the outline - to enable
// constant evaluation. Similarly we need to include initializers for
// optional and named parameters of instance methods because these might
// be needed to generated noSuchMethod forwarders.
bool isConstConstructorParameter = false;
if (parent is ConstructorBuilder) {
isConstConstructorParameter = parent.isConst;
} else if (parent is ProcedureBuilder) {
isConstConstructorParameter = parent.isFactory && parent.isConst;
}
if (isConstConstructorParameter || parent.isClassInstanceMember) {
final ClassBuilder classBuilder = parent.parent;
Scope scope = classBuilder.scope;
BodyBuilder bodyBuilder = library.loader
.createBodyBuilderForOutlineExpression(
library, classBuilder, this, 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;
if (library.loader is SourceLoader) {
SourceLoader loader = library.loader;
loader.transformPostInference(
variable,
bodyBuilder.transformSetLiterals,
bodyBuilder.transformCollections,
library.library);
}
initializerWasInferred = true;
bodyBuilder.resolveRedirectingFactoryTargets();
}
}
initializerToken = null;
}
}