blob: 5f00d035a09e107b2bf24e18a5c4f41b6f7e814a [file] [log] [blame]
// Copyright (c) 2019, 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/scanner/token.dart' show Token;
import 'package:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
import '../base/constant_context.dart' show ConstantContext;
import '../base/local_scope.dart';
import '../base/messages.dart'
show
LocatedMessage,
Message,
messageMoreThanOneSuperInitializer,
messageRedirectingConstructorWithAnotherInitializer,
messageRedirectingConstructorWithMultipleRedirectInitializers,
messageRedirectingConstructorWithSuperInitializer,
messageSuperInitializerNotLast,
noLength;
import '../base/modifiers.dart';
import '../base/scope.dart';
import '../builder/builder.dart';
import '../builder/constructor_builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/member_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/omitted_type_builder.dart';
import '../builder/type_builder.dart';
import '../dill/dill_member_builder.dart';
import '../kernel/body_builder.dart' show BodyBuilder;
import '../kernel/body_builder_context.dart';
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/expression_generator_helper.dart';
import '../kernel/hierarchy/class_member.dart' show ClassMember;
import '../kernel/internal_ast.dart';
import '../kernel/kernel_helper.dart'
show
DelayedDefaultValueCloner,
TypeDependency,
finishConstructorAugmentation,
finishProcedureAugmentation;
import '../type_inference/inference_results.dart';
import '../type_inference/type_schema.dart';
import 'constructor_declaration.dart';
import 'name_scheme.dart';
import 'source_class_builder.dart';
import 'source_enum_builder.dart';
import 'source_extension_type_declaration_builder.dart';
import 'source_field_builder.dart';
import 'source_function_builder.dart';
import 'source_library_builder.dart' show SourceLibraryBuilder;
import 'source_loader.dart'
show CompilationPhaseForProblemReporting, SourceLoader;
import 'source_member_builder.dart';
abstract class SourceConstructorBuilder
implements ConstructorBuilder, SourceMemberBuilder {
@override
DeclarationBuilder get declarationBuilder;
/// Infers the types of any untyped initializing formals.
void inferFormalTypes(ClassHierarchyBase hierarchy);
void addSuperParameterDefaultValueCloners(
List<DelayedDefaultValueCloner> delayedDefaultValueCloners);
/// Returns `true` if this constructor is an redirecting generative
/// constructor.
///
/// It is considered redirecting if it has at least one redirecting
/// initializer.
bool get isRedirecting;
}
abstract class AbstractSourceConstructorBuilder
extends SourceFunctionBuilderImpl
implements SourceConstructorBuilder, Inferable, ConstructorDeclaration {
@override
final SourceLibraryBuilder libraryBuilder;
@override
final DeclarationBuilder declarationBuilder;
@override
final OmittedTypeBuilder returnType;
final int charOpenParenOffset;
bool _hasFormalsInferred = false;
Token? beginInitializers;
AbstractSourceConstructorBuilder(
List<MetadataBuilder>? metadata,
Modifiers modifiers,
this.returnType,
String name,
List<NominalVariableBuilder>? typeVariables,
List<FormalParameterBuilder>? formals,
this.libraryBuilder,
this.declarationBuilder,
Uri fileUri,
int charOffset,
this.charOpenParenOffset,
String? nativeMethodName,
this.beginInitializers)
: super(metadata, modifiers, name, typeVariables, formals,
declarationBuilder, fileUri, charOffset, nativeMethodName) {
if (formals != null) {
for (FormalParameterBuilder formal in formals) {
if (formal.isInitializingFormal || formal.isSuperInitializingFormal) {
formal.type.registerInferable(this);
}
}
}
}
@override
bool get isConstructor => true;
@override
ProcedureKind? get kind => null;
@override
Statement? get body {
if (bodyInternal == null && !isExternal) {
bodyInternal = new EmptyStatement();
}
return bodyInternal;
}
@override
AsyncMarker get asyncModifier => AsyncMarker.Sync;
@override
void inferTypes(ClassHierarchyBase hierarchy) {
inferFormalTypes(hierarchy);
}
@override
void inferFormalTypes(ClassHierarchyBase hierarchy) {
if (_hasFormalsInferred) return;
if (formals != null) {
libraryBuilder.loader.withUriForCrashReporting(fileUri, charOffset, () {
for (FormalParameterBuilder formal in formals!) {
if (formal.type is InferableTypeBuilder) {
if (formal.isInitializingFormal) {
formal.finalizeInitializingFormal(
declarationBuilder, this, hierarchy);
}
}
}
_inferSuperInitializingFormals(hierarchy);
});
}
_hasFormalsInferred = true;
}
// Coverage-ignore(suite): Not run.
void _inferSuperInitializingFormals(ClassHierarchyBase hierarchy) {}
void _buildFormals(Member member) {
if (formals != null) {
bool needsInference = false;
for (FormalParameterBuilder formal in formals!) {
if (formal.type is InferableTypeBuilder &&
(formal.isInitializingFormal || formal.isSuperInitializingFormal)) {
formal.variable!.type = const UnknownType();
needsInference = true;
} else if (!formal.hasDeclaredInitializer &&
formal.isSuperInitializingFormal) {
needsInference = true;
}
}
if (needsInference) {
libraryBuilder.loader.registerConstructorToBeInferred(member, this);
}
}
}
List<Initializer> get initializers;
void _injectInvalidInitializer(Message message, int charOffset, int length,
ExpressionGeneratorHelper helper) {
Initializer lastInitializer = initializers.removeLast();
assert(lastInitializer == superInitializer ||
lastInitializer == redirectingInitializer);
Initializer error = helper.buildInvalidInitializer(
helper.buildProblem(message, charOffset, length));
initializers.add(error..parent = member);
initializers.add(lastInitializer);
}
SuperInitializer? superInitializer;
RedirectingInitializer? redirectingInitializer;
@override
void addInitializer(Initializer initializer, ExpressionGeneratorHelper helper,
{required InitializerInferenceResult? inferenceResult}) {
if (initializer is SuperInitializer) {
if (superInitializer != null) {
_injectInvalidInitializer(messageMoreThanOneSuperInitializer,
initializer.fileOffset, "super".length, helper);
} else if (redirectingInitializer != null) {
_injectInvalidInitializer(
messageRedirectingConstructorWithSuperInitializer,
initializer.fileOffset,
"super".length,
helper);
} else {
inferenceResult?.applyResult(initializers, member);
superInitializer = initializer;
LocatedMessage? message = helper.checkArgumentsForFunction(
initializer.target.function,
initializer.arguments,
initializer.arguments.fileOffset, <TypeParameter>[]);
if (message != null) {
initializers.add(helper.buildInvalidInitializer(
helper.buildUnresolvedError(
helper.constructorNameForDiagnostics(
initializer.target.name.text),
initializer.fileOffset,
arguments: initializer.arguments,
isSuper: true,
message: message,
kind: UnresolvedKind.Constructor))
..parent = member);
} else {
initializers.add(initializer..parent = member);
}
}
} else if (initializer is RedirectingInitializer) {
if (superInitializer != null) {
// Point to the existing super initializer.
_injectInvalidInitializer(
messageRedirectingConstructorWithSuperInitializer,
superInitializer!.fileOffset,
"super".length,
helper);
} else if (redirectingInitializer != null) {
_injectInvalidInitializer(
messageRedirectingConstructorWithMultipleRedirectInitializers,
initializer.fileOffset,
noLength,
helper);
} else if (initializers.isNotEmpty) {
// Error on all previous ones.
for (int i = 0; i < initializers.length; i++) {
Initializer initializer = initializers[i];
int length = noLength;
if (initializer is AssertInitializer) length = "assert".length;
Initializer error = helper.buildInvalidInitializer(
helper.buildProblem(
messageRedirectingConstructorWithAnotherInitializer,
initializer.fileOffset,
length));
error.parent = member;
initializers[i] = error;
}
inferenceResult?.applyResult(initializers, member);
initializers.add(initializer..parent = member);
redirectingInitializer = initializer;
} else {
inferenceResult?.applyResult(initializers, member);
redirectingInitializer = initializer;
LocatedMessage? message = helper.checkArgumentsForFunction(
initializer.target.function,
initializer.arguments,
initializer.arguments.fileOffset, const <TypeParameter>[]);
if (message != null) {
initializers.add(helper.buildInvalidInitializer(
helper.buildUnresolvedError(
helper.constructorNameForDiagnostics(
initializer.target.name.text),
initializer.fileOffset,
arguments: initializer.arguments,
isSuper: false,
message: message,
kind: UnresolvedKind.Constructor))
..parent = member);
} else {
initializers.add(initializer..parent = member);
}
}
} else if (redirectingInitializer != null) {
int length = noLength;
if (initializer is AssertInitializer) length = "assert".length;
_injectInvalidInitializer(
messageRedirectingConstructorWithAnotherInitializer,
initializer.fileOffset,
length,
helper);
} else if (superInitializer != null) {
_injectInvalidInitializer(messageSuperInitializerNotLast,
initializer.fileOffset, noLength, helper);
} else {
inferenceResult?.applyResult(initializers, member);
initializers.add(initializer..parent = member);
}
}
void _buildConstructorForOutline(
Token? beginInitializers, LookupScope declarationScope) {
if (beginInitializers != null) {
final LocalScope? formalParameterScope;
if (isConst) {
// We're going to fully build the constructor so we need scopes.
formalParameterScope = computeFormalParameterInitializerScope(
computeFormalParameterScope(
computeTypeParameterScope(declarationBuilder.scope)));
} else {
formalParameterScope = null;
}
BodyBuilder bodyBuilder = libraryBuilder.loader
.createBodyBuilderForOutlineExpression(
libraryBuilder,
createBodyBuilderContext(
inOutlineBuildingPhase: true,
inMetadata: false,
inConstFields: false),
declarationScope,
fileUri,
formalParameterScope: formalParameterScope);
if (isConst) {
bodyBuilder.constantContext = ConstantContext.required;
}
inferFormalTypes(bodyBuilder.hierarchy);
bodyBuilder.parseInitializers(beginInitializers,
doFinishConstructor: isConst);
bodyBuilder.performBacklogComputations();
}
}
Procedure? get _constructorTearOff;
@override
VariableDeclaration? getTearOffParameter(int index) {
Procedure? constructorTearOff = _constructorTearOff;
if (constructorTearOff != null) {
if (index < constructorTearOff.function.positionalParameters.length) {
return constructorTearOff.function.positionalParameters[index];
} else {
index -= constructorTearOff.function.positionalParameters.length;
if (index < constructorTearOff.function.namedParameters.length) {
return constructorTearOff.function.namedParameters[index];
}
}
}
return null;
}
@override
// Coverage-ignore(suite): Not run.
void checkVariance(
SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) {}
@override
void checkTypes(
SourceLibraryBuilder library, TypeEnvironment typeEnvironment) {
library.checkTypesInConstructorBuilder(this, formals, typeEnvironment);
}
@override
// Coverage-ignore(suite): Not run.
List<ClassMember> get localMembers =>
throw new UnsupportedError('${runtimeType}.localMembers');
@override
// Coverage-ignore(suite): Not run.
List<ClassMember> get localSetters =>
throw new UnsupportedError('${runtimeType}.localSetters');
}
class DeclaredSourceConstructorBuilder
extends AbstractSourceConstructorBuilder {
late final Constructor _constructor;
@override
late final Procedure? _constructorTearOff;
Set<SourceFieldBuilder>? _initializedFields;
DeclaredSourceConstructorBuilder? actualOrigin;
List<DeclaredSourceConstructorBuilder>? _augmentations;
bool _hasDefaultValueCloner = false;
@override
List<FormalParameterBuilder>? formals;
final MemberName _memberName;
@override
String get fullNameForErrors {
return "${declarationBuilder.name}"
"${name.isEmpty ? '' : '.$name'}";
}
DeclaredSourceConstructorBuilder(
List<MetadataBuilder>? metadata,
Modifiers modifiers,
OmittedTypeBuilder returnType,
String name,
List<NominalVariableBuilder>? typeVariables,
this.formals,
SourceLibraryBuilder libraryBuilder,
DeclarationBuilder declarationBuilder,
Uri fileUri,
int startCharOffset,
int charOffset,
int charOpenParenOffset,
int charEndOffset,
Reference? constructorReference,
Reference? tearOffReference,
NameScheme nameScheme,
{String? nativeMethodName,
required bool forAbstractClassOrEnumOrMixin,
required Token? beginInitializers,
bool isSynthetic = false})
: _hasSuperInitializingFormals =
formals?.any((formal) => formal.isSuperInitializingFormal) ?? false,
_memberName = nameScheme.getDeclaredName(name),
super(
metadata,
modifiers,
returnType,
name,
typeVariables,
formals,
libraryBuilder,
declarationBuilder,
fileUri,
charOffset,
charOpenParenOffset,
nativeMethodName,
beginInitializers) {
_constructor = new Constructor(new FunctionNode(null),
name: dummyName,
fileUri: fileUri,
reference: constructorReference,
isSynthetic: isSynthetic)
..startFileOffset = startCharOffset
..fileOffset = charOffset
..fileEndOffset = charEndOffset;
nameScheme
.getConstructorMemberName(name, isTearOff: false)
.attachMember(_constructor);
_constructorTearOff = createConstructorTearOffProcedure(
nameScheme.getConstructorMemberName(name, isTearOff: true),
libraryBuilder,
fileUri,
charOffset,
tearOffReference,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin);
}
@override
// Coverage-ignore(suite): Not run.
Name get memberName => _memberName.name;
@override
SourceClassBuilder get classBuilder =>
super.classBuilder as SourceClassBuilder;
@override
Member get readTarget =>
_constructorTearOff ??
// The case is need to ensure that the upper bound is [Member] and not
// [GenericFunction].
// ignore: unnecessary_cast
_constructor as Member;
@override
// Coverage-ignore(suite): Not run.
Member? get writeTarget => null;
@override
Member get invokeTarget => constructor;
@override
FunctionNode get function => _constructor.function;
@override
// Coverage-ignore(suite): Not run.
Iterable<Member> get exportedMembers => [constructor];
@override
List<Initializer> get initializers => _constructor.initializers;
@override
DeclaredSourceConstructorBuilder get origin => actualOrigin ?? this;
// Coverage-ignore(suite): Not run.
List<SourceConstructorBuilder>? get augmentationsForTesting => _augmentations;
@override
bool get isDeclarationInstanceMember => false;
@override
// Coverage-ignore(suite): Not run.
bool get isClassInstanceMember => false;
@override
bool get isEffectivelyExternal {
bool isExternal = this.isExternal;
if (isExternal) {
List<SourceConstructorBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceConstructorBuilder augmentation in augmentations) {
isExternal &= augmentation.isExternal;
}
}
}
return isExternal;
}
@override
bool get isRedirecting {
for (Initializer initializer in initializers) {
if (initializer is RedirectingInitializer) {
return true;
}
}
return false;
}
@override
bool get isEffectivelyRedirecting {
bool isRedirecting = this.isRedirecting;
if (!isRedirecting) {
List<SourceConstructorBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceConstructorBuilder augmentation in augmentations) {
isRedirecting |= augmentation.isRedirecting;
}
}
}
return isRedirecting;
}
@override
void buildOutlineNodes(BuildNodesCallback f) {
_build();
f(
member: _constructor,
tearOff: _constructorTearOff,
kind: BuiltMemberKind.Constructor);
}
bool _hasBeenBuilt = false;
void _build() {
if (!_hasBeenBuilt) {
buildFunction();
_constructor.function.fileOffset = charOpenParenOffset;
_constructor.function.fileEndOffset = _constructor.fileEndOffset;
_constructor.function.typeParameters = const <TypeParameter>[];
_constructor.isConst = isConst;
_constructor.isExternal = isExternal;
if (_constructorTearOff != null) {
DelayedDefaultValueCloner delayedDefaultValueCloners =
buildConstructorTearOffProcedure(
tearOff: _constructorTearOff,
declarationConstructor: constructor,
implementationConstructor: _constructor,
enclosingDeclarationTypeParameters:
classBuilder.cls.typeParameters,
libraryBuilder: libraryBuilder);
_delayedDefaultValueCloners.add(delayedDefaultValueCloners);
}
_hasBeenBuilt = true;
}
_buildFormals(_constructor);
}
@override
VariableDeclaration getFormalParameter(int index) {
if (parent is SourceEnumBuilder) {
return formals![index + 2].variable!;
} else {
return super.getFormalParameter(index);
}
}
ConstructorBuilder? _computeSuperTargetBuilder(
List<Initializer>? initializers) {
Member superTarget;
ClassBuilder superclassBuilder;
TypeBuilder? supertype = classBuilder.supertypeBuilder;
TypeDeclarationBuilder? supertypeDeclaration =
supertype?.computeUnaliasedDeclaration(isUsedAsClass: false);
if (supertypeDeclaration is ClassBuilder) {
superclassBuilder = supertypeDeclaration;
} else {
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"Unaliased 'declaration' isn't a ClassBuilder.",
expectedPhase: CompilationPhaseForProblemReporting.outline));
return null;
}
if (initializers != null &&
initializers.isNotEmpty &&
initializers.last is SuperInitializer) {
superTarget = (initializers.last as SuperInitializer).target;
} else {
MemberBuilder? memberBuilder = superclassBuilder.constructorScope
.lookup("", charOffset, libraryBuilder.fileUri);
if (memberBuilder is ConstructorBuilder) {
superTarget = memberBuilder.invokeTarget;
} else {
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"Can't find the implied unnamed constructor in the superclass.",
expectedPhase: CompilationPhaseForProblemReporting.bodyBuilding));
return null;
}
}
MemberBuilder? constructorBuilder =
superclassBuilder.findConstructorOrFactory(superTarget.name.text,
charOffset, libraryBuilder.fileUri, libraryBuilder);
if (constructorBuilder is ConstructorBuilder) {
return constructorBuilder;
} else {
// Coverage-ignore-block(suite): Not run.
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder._computeSuperTargetBuilder: "
"Can't find a constructor with name '${superTarget.name.text}' in "
"the superclass.",
expectedPhase: CompilationPhaseForProblemReporting.outline));
return null;
}
}
final bool _hasSuperInitializingFormals;
final List<DelayedDefaultValueCloner> _delayedDefaultValueCloners =
<DelayedDefaultValueCloner>[];
@override
void _inferSuperInitializingFormals(ClassHierarchyBase hierarchy) {
if (_hasSuperInitializingFormals) {
List<Initializer>? initializers;
if (beginInitializers != null) {
BodyBuilder bodyBuilder = libraryBuilder.loader
.createBodyBuilderForOutlineExpression(
libraryBuilder,
createBodyBuilderContext(
inOutlineBuildingPhase: false,
inMetadata: false,
inConstFields: false),
declarationBuilder.scope,
fileUri);
if (isConst) {
bodyBuilder.constantContext = ConstantContext.required;
}
initializers = bodyBuilder.parseInitializers(beginInitializers!,
doFinishConstructor: false);
}
finalizeSuperInitializingFormals(
hierarchy, _delayedDefaultValueCloners, initializers);
}
}
void finalizeSuperInitializingFormals(
ClassHierarchyBase hierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
List<Initializer>? initializers) {
if (formals == null) return;
if (!_hasSuperInitializingFormals) return;
void performRecoveryForErroneousCase() {
for (FormalParameterBuilder formal in formals!) {
if (formal.isSuperInitializingFormal) {
TypeBuilder type = formal.type;
if (type is InferableTypeBuilder) {
type.registerInferredType(const InvalidType());
}
}
}
}
ConstructorBuilder? superTargetBuilder =
_computeSuperTargetBuilder(initializers);
if (superTargetBuilder is SourceConstructorBuilder) {
superTargetBuilder.inferFormalTypes(hierarchy);
}
Member superTarget;
FunctionNode? superConstructorFunction;
if (superTargetBuilder != null) {
superTarget = superTargetBuilder.invokeTarget;
superConstructorFunction = superTargetBuilder.function;
} else {
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder.finalizeSuperInitializingFormals: "
"Can't compute super target.",
expectedPhase: CompilationPhaseForProblemReporting.bodyBuilding));
// Perform a simple recovery.
return performRecoveryForErroneousCase();
}
List<DartType?> positionalSuperFormalType = [];
List<bool> positionalSuperFormalHasInitializer = [];
Map<String, DartType?> namedSuperFormalType = {};
Map<String, bool> namedSuperFormalHasInitializer = {};
for (VariableDeclaration formal
in superConstructorFunction.positionalParameters) {
positionalSuperFormalType.add(formal.type);
positionalSuperFormalHasInitializer.add(formal.hasDeclaredInitializer);
}
for (VariableDeclaration formal
in superConstructorFunction.namedParameters) {
namedSuperFormalType[formal.name!] = formal.type;
namedSuperFormalHasInitializer[formal.name!] =
formal.hasDeclaredInitializer;
}
int superInitializingFormalIndex = -1;
List<int?>? positionalSuperParameters;
List<String>? namedSuperParameters;
Supertype? supertype = hierarchy.getClassAsInstanceOf(
classBuilder.cls, superTarget.enclosingClass!);
assert(supertype != null);
Map<TypeParameter, DartType> substitution =
new Map<TypeParameter, DartType>.fromIterables(
supertype!.classNode.typeParameters, supertype.typeArguments);
for (int formalIndex = 0; formalIndex < formals!.length; formalIndex++) {
FormalParameterBuilder formal = formals![formalIndex];
if (formal.isSuperInitializingFormal) {
superInitializingFormalIndex++;
bool hasImmediatelyDeclaredInitializer =
formal.hasImmediatelyDeclaredInitializer;
DartType? correspondingSuperFormalType;
if (formal.isPositional) {
assert(positionalSuperFormalHasInitializer.length ==
positionalSuperFormalType.length);
if (superInitializingFormalIndex <
positionalSuperFormalHasInitializer.length) {
if (formal.isOptional) {
formal.hasDeclaredInitializer =
hasImmediatelyDeclaredInitializer ||
positionalSuperFormalHasInitializer[
superInitializingFormalIndex];
}
correspondingSuperFormalType =
positionalSuperFormalType[superInitializingFormalIndex];
if (!hasImmediatelyDeclaredInitializer &&
!formal.isRequiredPositional) {
(positionalSuperParameters ??= <int?>[]).add(formalIndex);
} else {
(positionalSuperParameters ??= <int?>[]).add(null);
}
} else {
assert(libraryBuilder.loader.assertProblemReportedElsewhere(
"DeclaredSourceConstructorBuilder"
".finalizeSuperInitializingFormals: "
"Super initializer count is greater than the count of "
"positional formals in the super constructor.",
expectedPhase:
CompilationPhaseForProblemReporting.bodyBuilding));
}
} else {
if (namedSuperFormalHasInitializer[formal.name] != null) {
if (formal.isOptional) {
formal.hasDeclaredInitializer =
hasImmediatelyDeclaredInitializer ||
namedSuperFormalHasInitializer[formal.name]!;
}
correspondingSuperFormalType = namedSuperFormalType[formal.name];
if (!hasImmediatelyDeclaredInitializer && !formal.isRequiredNamed) {
(namedSuperParameters ??= <String>[]).add(formal.name);
}
} else {
// TODO(cstefantsova): Report an error.
}
}
if (formal.type is InferableTypeBuilder) {
DartType? type = correspondingSuperFormalType;
if (substitution.isNotEmpty && type != null) {
type = substitute(type, substitution);
}
formal.type.registerInferredType(type ?? const DynamicType());
}
formal.variable!.hasDeclaredInitializer = formal.hasDeclaredInitializer;
}
}
if (positionalSuperParameters != null || namedSuperParameters != null) {
if (!_hasDefaultValueCloner) {
// If this constructor formals are part of a cyclic dependency this
// might be called more than once.
delayedDefaultValueCloners.add(new DelayedDefaultValueCloner(
superTarget, constructor,
positionalSuperParameters:
positionalSuperParameters ?? const <int>[],
namedSuperParameters: namedSuperParameters ?? const <String>[],
isOutlineNode: true,
libraryBuilder: libraryBuilder));
if (_constructorTearOff != null) {
delayedDefaultValueCloners.add(new DelayedDefaultValueCloner(
superTarget, _constructorTearOff,
positionalSuperParameters:
positionalSuperParameters ?? const <int>[],
namedSuperParameters: namedSuperParameters ?? const <String>[],
isOutlineNode: true,
libraryBuilder: libraryBuilder));
}
_hasDefaultValueCloner = true;
}
}
}
bool _hasBuiltOutlines = false;
@override
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
if (_hasBuiltOutlines) return;
if (isConst && isAugmenting) {
origin.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
super.buildOutlineExpressions(classHierarchy, delayedDefaultValueCloners);
// For modular compilation purposes we need to include initializers
// for const constructors into the outline. We also need to parse
// initializers to infer types of the super-initializing parameters.
if (isConst || _hasSuperInitializingFormals) {
_buildConstructorForOutline(beginInitializers, classBuilder.scope);
}
addSuperParameterDefaultValueCloners(delayedDefaultValueCloners);
if (isConst && isAugmenting) {
_finishAugmentation();
}
beginInitializers = null;
_hasBuiltOutlines = true;
}
@override
void addSuperParameterDefaultValueCloners(
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
if (beginInitializers != null && constructor.initializers.isNotEmpty) {
// If the initializers aren't built yet, we can't compute the super
// target. The synthetic initializers should be excluded, since they can
// be built separately from formal field initializers.
bool allInitializersAreSynthetic = true;
for (Initializer initializer in constructor.initializers) {
if (!initializer.isSynthetic) {
allInitializersAreSynthetic = false;
break;
}
}
if (!allInitializersAreSynthetic) {
ConstructorBuilder? superTargetBuilder =
_computeSuperTargetBuilder(constructor.initializers);
if (superTargetBuilder is SourceConstructorBuilder) {
superTargetBuilder
.addSuperParameterDefaultValueCloners(delayedDefaultValueCloners);
}
}
}
delayedDefaultValueCloners.addAll(_delayedDefaultValueCloners);
_delayedDefaultValueCloners.clear();
}
@override
void buildFunction() {
// According to the specification §9.3 the return type of a constructor
// function is its enclosing class.
super.buildFunction();
Class enclosingClass = classBuilder.cls;
List<DartType> typeParameterTypes = <DartType>[];
for (int i = 0; i < enclosingClass.typeParameters.length; i++) {
TypeParameter typeParameter = enclosingClass.typeParameters[i];
typeParameterTypes.add(
new TypeParameterType.withDefaultNullabilityForLibrary(
typeParameter, libraryBuilder.library));
}
InterfaceType type = new InterfaceType(
enclosingClass, Nullability.nonNullable, typeParameterTypes);
returnType.registerInferredType(type);
}
Constructor get constructor =>
isAugmenting ? origin.constructor : _constructor;
@override
Member get member => constructor;
void _finishAugmentation() {
finishConstructorAugmentation(origin.constructor, _constructor);
if (_constructorTearOff != null) {
finishProcedureAugmentation(
origin._constructorTearOff!, _constructorTearOff);
}
}
@override
int buildBodyNodes(BuildNodesCallback f) {
if (!isAugmenting) return 0;
_finishAugmentation();
return 1;
}
@override
void becomeNative(SourceLoader loader) {
_constructor.isExternal = true;
super.becomeNative(loader);
}
@override
void applyAugmentation(Builder augmentation) {
if (augmentation is DeclaredSourceConstructorBuilder) {
if (checkAugmentation(augmentation)) {
augmentation.actualOrigin = this;
(_augmentations ??= []).add(augmentation);
}
} else {
// Coverage-ignore-block(suite): Not run.
reportAugmentationMismatch(augmentation);
}
}
@override
void prepareInitializers() {
// For const constructors we parse initializers already at the outlining
// stage, there is no easy way to make body building stage skip initializer
// parsing, so we simply clear parsed initializers and rebuild them
// again.
// For when doing an experimental incremental compilation they are also
// potentially done more than once (because it rebuilds the bodies of an old
// compile), and so we also clear them.
// Note: this method clears both initializers from the target Kernel node
// and internal state associated with parsing initializers.
_constructor.initializers = [];
redirectingInitializer = null;
superInitializer = null;
}
@override
void prependInitializer(Initializer initializer) {
initializer.parent = constructor;
constructor.initializers.insert(0, initializer);
}
@override
void registerInitializedField(SourceFieldBuilder fieldBuilder) {
if (isAugmenting) {
origin.registerInitializedField(fieldBuilder);
} else {
(_initializedFields ??= {}).add(fieldBuilder);
}
}
@override
Set<SourceFieldBuilder>? takeInitializedFields() {
Set<SourceFieldBuilder>? result = _initializedFields;
_initializedFields = null;
return result;
}
void ensureGrowableFormals() {
if (formals != null) {
formals = new List<FormalParameterBuilder>.of(formals!, growable: true);
} else {
formals = <FormalParameterBuilder>[];
}
}
@override
void checkTypes(
SourceLibraryBuilder library, TypeEnvironment typeEnvironment) {
super.checkTypes(library, typeEnvironment);
List<DeclaredSourceConstructorBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (DeclaredSourceConstructorBuilder augmentation in augmentations) {
augmentation.checkTypes(library, typeEnvironment);
}
}
}
@override
DartType substituteFieldType(DartType fieldType) {
// Nothing to do. Regular generative constructors don't have their own
// type variables.
return fieldType;
}
@override
BodyBuilderContext createBodyBuilderContext(
{required bool inOutlineBuildingPhase,
required bool inMetadata,
required bool inConstFields}) {
return new ConstructorBodyBuilderContext(this,
inOutlineBuildingPhase: inOutlineBuildingPhase,
inMetadata: inMetadata,
inConstFields: inConstFields);
}
// TODO(johnniwinther): Add annotations to tear-offs.
@override
Iterable<Annotatable> get annotatables => [constructor];
@override
bool get isAugmented {
if (isAugmenting) {
return origin._augmentations!.last != this;
} else {
return _augmentations != null;
}
}
}
class SyntheticSourceConstructorBuilder extends DillConstructorBuilder
with SourceMemberBuilderMixin
implements SourceConstructorBuilder {
/// The constructor from which this synthesized constructor is defined.
///
/// This defines the parameter structure and the default values of this
/// constructor.
///
/// The [_immediatelyDefiningConstructor] might itself a synthesized
/// constructor and [_effectivelyDefiningConstructor] can be used to find
/// the constructor that effectively defines this constructor.
MemberBuilder? _immediatelyDefiningConstructor;
DelayedDefaultValueCloner? _delayedDefaultValueCloner;
TypeDependency? _typeDependency;
SyntheticSourceConstructorBuilder(SourceClassBuilder parent,
Constructor constructor, Procedure? constructorTearOff,
{MemberBuilder? definingConstructor,
DelayedDefaultValueCloner? delayedDefaultValueCloner,
TypeDependency? typeDependency})
: _immediatelyDefiningConstructor = definingConstructor,
_delayedDefaultValueCloner = delayedDefaultValueCloner,
_typeDependency = typeDependency,
super(constructor, constructorTearOff, parent);
@override
// Coverage-ignore(suite): Not run.
SourceLibraryBuilder get libraryBuilder =>
super.libraryBuilder as SourceLibraryBuilder;
@override
// Coverage-ignore(suite): Not run.
DeclarationBuilder get declarationBuilder => classBuilder!;
@override
// Coverage-ignore(suite): Not run.
bool get isRedirecting {
for (Initializer initializer in constructor.initializers) {
if (initializer is RedirectingInitializer) {
return true;
}
}
return false;
}
@override
void inferFormalTypes(ClassHierarchyBase hierarchy) {
if (_immediatelyDefiningConstructor is SourceConstructorBuilder) {
(_immediatelyDefiningConstructor as SourceConstructorBuilder)
.inferFormalTypes(hierarchy);
}
if (_typeDependency != null) {
_typeDependency!.copyInferred();
_typeDependency = null;
}
}
@override
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
if (_immediatelyDefiningConstructor != null) {
// Ensure that default value expressions have been created for [_origin].
// If [_origin] is from a source library, we need to build the default
// values and initializers first.
MemberBuilder origin = _immediatelyDefiningConstructor!;
if (origin is SourceConstructorBuilder) {
origin.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
addSuperParameterDefaultValueCloners(delayedDefaultValueCloners);
_immediatelyDefiningConstructor = null;
}
}
@override
void addSuperParameterDefaultValueCloners(
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
MemberBuilder? origin = _immediatelyDefiningConstructor;
if (origin is SourceConstructorBuilder) {
origin.addSuperParameterDefaultValueCloners(delayedDefaultValueCloners);
}
if (_delayedDefaultValueCloner != null) {
// For constant constructors default values are computed and cloned part
// of the outline expression and we there set `isOutlineNode` to `true`
// below.
//
// For non-constant constructors default values are cloned as part of the
// full compilation using `KernelTarget._delayedDefaultValueCloners`.
delayedDefaultValueCloners
.add(_delayedDefaultValueCloner!..isOutlineNode = true);
_delayedDefaultValueCloner = null;
}
}
@override
// Coverage-ignore(suite): Not run.
void checkVariance(
SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) {}
@override
void checkTypes(
SourceLibraryBuilder library, TypeEnvironment typeEnvironment) {}
}
class SourceExtensionTypeConstructorBuilder
extends AbstractSourceConstructorBuilder {
late final Procedure _constructor;
@override
late final Procedure? _constructorTearOff;
Set<SourceFieldBuilder>? _initializedFields;
@override
List<Initializer> initializers = [];
final MemberName _memberName;
DelayedDefaultValueCloner? _delayedDefaultValueCloner;
SourceExtensionTypeConstructorBuilder(
List<MetadataBuilder>? metadata,
Modifiers modifiers,
OmittedTypeBuilder returnType,
String name,
List<NominalVariableBuilder>? typeVariables,
List<FormalParameterBuilder>? formals,
SourceLibraryBuilder libraryBuilder,
SourceExtensionTypeDeclarationBuilder declarationBuilder,
Uri fileUri,
int startCharOffset,
int charOffset,
int charOpenParenOffset,
int charEndOffset,
Reference? constructorReference,
Reference? tearOffReference,
NameScheme nameScheme,
{String? nativeMethodName,
required bool forAbstractClassOrEnumOrMixin,
required Token? beginInitializers})
: _memberName = nameScheme.getDeclaredName(name),
super(
metadata,
modifiers,
returnType,
name,
typeVariables,
formals,
libraryBuilder,
declarationBuilder,
fileUri,
charOffset,
charOpenParenOffset,
nativeMethodName,
beginInitializers) {
_constructor = new Procedure(
dummyName, ProcedureKind.Method, new FunctionNode(null),
fileUri: fileUri, reference: constructorReference)
..fileOffset = charOffset
..fileEndOffset = charEndOffset;
nameScheme
.getConstructorMemberName(name, isTearOff: false)
.attachMember(_constructor);
_constructorTearOff = createConstructorTearOffProcedure(
nameScheme.getConstructorMemberName(name, isTearOff: true),
libraryBuilder,
fileUri,
charOffset,
tearOffReference,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin,
forceCreateLowering: true)
?..isExtensionTypeMember = true;
}
@override
// Coverage-ignore(suite): Not run.
Name get memberName => _memberName.name;
SourceExtensionTypeDeclarationBuilder get extensionTypeDeclarationBuilder =>
parent as SourceExtensionTypeDeclarationBuilder;
@override
Member get member => _constructor;
@override
Member get readTarget =>
_constructorTearOff ?? // Coverage-ignore(suite): Not run.
_constructor;
@override
// Coverage-ignore(suite): Not run.
Member? get writeTarget => null;
@override
Member get invokeTarget => _constructor;
@override
FunctionNode get function => _constructor.function;
@override
// Coverage-ignore(suite): Not run.
Iterable<Member> get exportedMembers => [_constructor];
@override
// Coverage-ignore(suite): Not run.
void addSuperParameterDefaultValueCloners(
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {}
@override
void _inferSuperInitializingFormals(ClassHierarchyBase hierarchy) {
if (formals != null) {
for (FormalParameterBuilder formal in formals!) {
if (formal.isSuperInitializingFormal) {
TypeBuilder formalTypeBuilder = formal.type;
if (formalTypeBuilder is InferableTypeBuilder) {
formalTypeBuilder.registerType(const InvalidType());
}
}
}
}
}
@override
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
super.buildOutlineExpressions(classHierarchy, delayedDefaultValueCloners);
if (isConst) {
// For modular compilation purposes we need to include initializers
// for const constructors into the outline.
LookupScope typeParameterScope =
computeTypeParameterScope(extensionTypeDeclarationBuilder.scope);
_buildConstructorForOutline(beginInitializers, typeParameterScope);
_buildBody();
}
beginInitializers = null;
if (_delayedDefaultValueCloner != null) {
delayedDefaultValueCloners.add(_delayedDefaultValueCloner!);
}
}
bool _hasBuiltBody = false;
void _buildBody() {
if (_hasBuiltBody) {
return;
}
if (!isExternal) {
VariableDeclaration thisVariable = this.thisVariable!;
List<Statement> statements = [thisVariable];
ExtensionTypeInitializerToStatementConverter visitor =
new ExtensionTypeInitializerToStatementConverter(
statements, thisVariable);
for (Initializer initializer in initializers) {
initializer.accept(visitor);
}
if (body != null && body is! EmptyStatement) {
statements.add(body!);
}
statements.add(new ReturnStatement(new VariableGet(thisVariable)));
body = new Block(statements);
}
_hasBuiltBody = true;
}
@override
int buildBodyNodes(BuildNodesCallback f) {
_buildBody();
// TODO(johnniwinther): Support augmentation.
return 0;
}
@override
void buildOutlineNodes(BuildNodesCallback f) {
_build();
f(
member: _constructor,
tearOff: _constructorTearOff,
kind: BuiltMemberKind.ExtensionTypeConstructor);
}
bool _hasBeenBuilt = false;
@override
void buildFunction() {
// According to the specification §9.3 the return type of a constructor
// function is its enclosing class.
super.buildFunction();
ExtensionTypeDeclaration extensionTypeDeclaration =
extensionTypeDeclarationBuilder.extensionTypeDeclaration;
List<DartType> typeParameterTypes = <DartType>[];
for (int i = 0; i < function.typeParameters.length; i++) {
TypeParameter typeParameter = function.typeParameters[i];
typeParameterTypes.add(
new TypeParameterType.withDefaultNullabilityForLibrary(
typeParameter, libraryBuilder.library));
}
ExtensionType type = new ExtensionType(
extensionTypeDeclaration, Nullability.nonNullable, typeParameterTypes);
returnType.registerInferredType(type);
}
void _build() {
if (!_hasBeenBuilt) {
buildFunction();
_constructor.function.fileOffset = charOpenParenOffset;
_constructor.function.fileEndOffset = _constructor.fileEndOffset;
_constructor.isConst = isConst;
_constructor.isExternal = isExternal;
_constructor.isStatic = true;
_constructor.isExtensionTypeMember = true;
if (_constructorTearOff != null) {
_delayedDefaultValueCloner = buildConstructorTearOffProcedure(
tearOff: _constructorTearOff,
declarationConstructor: _constructor,
implementationConstructor: _constructor,
libraryBuilder: libraryBuilder);
}
_hasBeenBuilt = true;
}
_buildFormals(_constructor);
}
@override
void prepareInitializers() {
// For const constructors we parse initializers already at the outlining
// stage, there is no easy way to make body building stage skip initializer
// parsing, so we simply clear parsed initializers and rebuild them
// again.
// For when doing an experimental incremental compilation they are also
// potentially done more than once (because it rebuilds the bodies of an old
// compile), and so we also clear them.
// Note: this method clears both initializers from the target Kernel node
// and internal state associated with parsing initializers.
initializers = [];
redirectingInitializer = null;
superInitializer = null;
}
@override
void prependInitializer(Initializer initializer) {
initializers.insert(0, initializer);
}
@override
void registerInitializedField(SourceFieldBuilder fieldBuilder) {
(_initializedFields ??= {}).add(fieldBuilder);
}
@override
Set<SourceFieldBuilder>? takeInitializedFields() {
Set<SourceFieldBuilder>? result = _initializedFields;
_initializedFields = null;
return result;
}
@override
bool get isEffectivelyExternal => isExternal;
@override
bool get isRedirecting {
for (Initializer initializer in initializers) {
if (initializer is ExtensionTypeRedirectingInitializer) {
return true;
}
}
return false;
}
@override
bool get isEffectivelyRedirecting => isRedirecting;
Substitution? _substitutionCache;
Substitution get _substitution {
if (typeVariables != null) {
assert(extensionTypeDeclarationBuilder.typeParameters!.length ==
typeVariables?.length);
_substitutionCache = Substitution.fromPairs(
extensionTypeDeclarationBuilder
.extensionTypeDeclaration.typeParameters,
new List<DartType>.generate(
extensionTypeDeclarationBuilder.typeParameters!.length,
(int index) =>
new TypeParameterType.withDefaultNullabilityForLibrary(
function.typeParameters[index],
libraryBuilder.origin.library)));
} else {
_substitutionCache = Substitution.empty;
}
return _substitutionCache!;
}
@override
DartType substituteFieldType(DartType fieldType) {
return _substitution.substituteType(fieldType);
}
@override
BodyBuilderContext createBodyBuilderContext(
{required bool inOutlineBuildingPhase,
required bool inMetadata,
required bool inConstFields}) {
return new ExtensionTypeConstructorBodyBuilderContext(this,
inOutlineBuildingPhase: inOutlineBuildingPhase,
inMetadata: inMetadata,
inConstFields: inConstFields);
}
// TODO(johnniwinther): Add annotations to tear-offs.
@override
Iterable<Annotatable> get annotatables => [_constructor];
@override
bool get isAugmented => false;
}
class ExtensionTypeInitializerToStatementConverter
implements InitializerVisitor<void> {
VariableDeclaration thisVariable;
final List<Statement> statements;
ExtensionTypeInitializerToStatementConverter(
this.statements, this.thisVariable);
@override
void visitAuxiliaryInitializer(AuxiliaryInitializer node) {
if (node is ExtensionTypeRedirectingInitializer) {
statements.add(new ExpressionStatement(
new VariableSet(
thisVariable,
new StaticInvocation(node.target, node.arguments)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset)
..fileOffset = node.fileOffset);
return;
} else if (node is ExtensionTypeRepresentationFieldInitializer) {
thisVariable
..initializer = (node.value..parent = thisVariable)
..fileOffset = node.fileOffset;
return;
}
// Coverage-ignore-block(suite): Not run.
throw new UnsupportedError(
"Unexpected initializer $node (${node.runtimeType})");
}
@override
void visitAssertInitializer(AssertInitializer node) {
statements.add(node.statement);
}
@override
// Coverage-ignore(suite): Not run.
void visitFieldInitializer(FieldInitializer node) {
thisVariable
..initializer = (node.value..parent = thisVariable)
..fileOffset = node.fileOffset;
}
@override
// Coverage-ignore(suite): Not run.
void visitInvalidInitializer(InvalidInitializer node) {
statements.add(new ExpressionStatement(
new InvalidExpression(null)..fileOffset = node.fileOffset)
..fileOffset);
}
@override
void visitLocalInitializer(LocalInitializer node) {
statements.add(node.variable);
}
@override
void visitRedirectingInitializer(RedirectingInitializer node) {
throw new UnsupportedError(
"Unexpected initializer $node (${node.runtimeType})");
}
@override
// Coverage-ignore(suite): Not run.
void visitSuperInitializer(SuperInitializer node) {
// TODO(johnniwinther): Report error for this case.
}
}