blob: 1a644c9bf177e3989146a26b7e77089dbe2e73fd [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:kernel/ast.dart';
import 'package:kernel/class_hierarchy.dart';
import 'package:kernel/transformations/flags.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart';
import '../base/identifiers.dart';
import '../base/local_scope.dart';
import '../base/modifiers.dart';
import '../base/name_space.dart';
import '../base/problems.dart' show unexpected, unhandled;
import '../base/scope.dart';
import '../builder/builder.dart';
import '../builder/constructor_reference_builder.dart';
import '../builder/declaration_builders.dart';
import '../builder/formal_parameter_builder.dart';
import '../builder/function_builder.dart';
import '../builder/member_builder.dart';
import '../builder/metadata_builder.dart';
import '../builder/omitted_type_builder.dart';
import '../builder/type_builder.dart';
import '../codes/cfe_codes.dart';
import '../dill/dill_extension_type_member_builder.dart';
import '../dill/dill_member_builder.dart';
import '../fragment/fragment.dart';
import '../kernel/body_builder_context.dart';
import '../kernel/constructor_tearoff_lowering.dart';
import '../kernel/hierarchy/class_member.dart';
import '../kernel/kernel_helper.dart';
import '../kernel/type_algorithms.dart';
import '../type_inference/inference_helper.dart';
import '../type_inference/type_inferrer.dart';
import '../type_inference/type_schema.dart';
import 'name_scheme.dart';
import 'redirecting_factory_body.dart';
import 'source_class_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';
class SourceFactoryBuilder extends SourceMemberBuilderImpl
implements SourceFunctionBuilder {
final Modifiers modifiers;
@override
final String name;
@override
final SourceLibraryBuilder libraryBuilder;
@override
final DeclarationBuilder declarationBuilder;
@override
final bool isExtensionInstanceMember = false;
SourceFactoryBuilder? _actualOrigin;
List<SourceFactoryBuilder>? _augmentations;
final MemberName _memberName;
@override
final Uri fileUri;
@override
final int fileOffset;
final FactoryFragment _introductory;
final _FactoryEncoding _encoding;
SourceFactoryBuilder(
{required this.modifiers,
required TypeBuilder returnType,
required this.name,
required List<NominalParameterBuilder>? typeParameters,
required this.libraryBuilder,
required this.declarationBuilder,
required this.fileUri,
required this.fileOffset,
required Reference? procedureReference,
required Reference? tearOffReference,
required NameScheme nameScheme,
required FactoryFragment fragment})
: _memberName = nameScheme.getDeclaredName(name),
_introductory = fragment,
_encoding = new _FactoryEncoding(fragment,
name: name,
libraryBuilder: libraryBuilder,
typeParameters: typeParameters,
returnType: returnType,
nameScheme: nameScheme,
procedureReference: procedureReference,
tearOffReference: tearOffReference);
@override
// Coverage-ignore(suite): Not run.
List<MetadataBuilder>? get metadata => _introductory.metadata;
@override
// Coverage-ignore(suite): Not run.
List<NominalParameterBuilder>? get typeParameters => _encoding.typeParameters;
@override
TypeBuilder get returnType => _encoding.returnType;
@override
// Coverage-ignore(suite): Not run.
List<FormalParameterBuilder>? get formals => _introductory.formals;
@override
// Coverage-ignore(suite): Not run.
Iterable<MetadataBuilder>? get metadataForTesting => metadata;
ConstructorReferenceBuilder? get redirectionTarget =>
_introductory.redirectionTarget;
@override
// Coverage-ignore(suite): Not run.
bool get isAugmentation => modifiers.isAugment;
@override
bool get isExternal => modifiers.isExternal;
@override
// Coverage-ignore(suite): Not run.
bool get isAbstract => modifiers.isAbstract;
@override
bool get isConst => modifiers.isConst;
@override
bool get isStatic => modifiers.isStatic;
@override
bool get isAugment => modifiers.isAugment;
@override
bool get isConstructor => false;
@override
// Coverage-ignore(suite): Not run.
bool get isAssignable => false;
@override
FormalParameterBuilder? getFormal(Identifier identifier) =>
_encoding.getFormal(identifier);
void setBody(Statement value) {
_encoding.setBody(value);
}
@override
// Coverage-ignore(suite): Not run.
bool get isNative => _encoding.isNative;
@override
VariableDeclaration getFormalParameter(int index) =>
_introductory.formals![index].variable!;
@override
VariableDeclaration? get thisVariable => null;
@override
List<TypeParameter>? get thisTypeParameters => null;
@override
void becomeNative(SourceLoader loader) {
_encoding.becomeNative(loader);
}
@override
Builder get parent => declarationBuilder;
@override
// Coverage-ignore(suite): Not run.
Name get memberName => _memberName.name;
// Coverage-ignore(suite): Not run.
List<SourceFactoryBuilder>? get augmentationsForTesting => _augmentations;
void _setAsyncModifier(AsyncMarker newModifier) {
_encoding.asyncModifier = newModifier;
}
@override
SourceFactoryBuilder get origin => _actualOrigin ?? this;
@override
// Coverage-ignore(suite): Not run.
bool get isRegularMethod => false;
@override
bool get isGetter => false;
@override
bool get isSetter => false;
@override
// Coverage-ignore(suite): Not run.
bool get isOperator => false;
@override
bool get isFactory => true;
@override
// Coverage-ignore(suite): Not run.
bool get isProperty => false;
@override
// Coverage-ignore(suite): Not run.
bool get isFinal => false;
@override
// Coverage-ignore(suite): Not run.
bool get isSynthesized => false;
@override
// Coverage-ignore(suite): Not run.
bool get isEnumElement => false;
Procedure get _procedure =>
isAugmenting ? origin._procedure : _encoding.procedure;
Procedure? get _tearOff => isAugmenting ? origin._tearOff : _encoding.tearOff;
@override
FunctionNode get function => _encoding.function;
@override
Member get readTarget => _tearOff ?? _procedure;
@override
// Coverage-ignore(suite): Not run.
Reference get readTargetReference => readTarget.reference;
@override
// Coverage-ignore(suite): Not run.
Member? get writeTarget => null;
@override
// Coverage-ignore(suite): Not run.
Reference? get writeTargetReference => null;
@override
Member? get invokeTarget => _procedure;
@override
// Coverage-ignore(suite): Not run.
Reference? get invokeTargetReference => _procedure.reference;
@override
// Coverage-ignore(suite): Not run.
Iterable<Reference> get exportedMemberReferences => [_procedure.reference];
@override
VariableDeclaration? getTearOffParameter(int index) =>
_encoding.getTearOffParameter(index);
@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');
@override
void addAugmentation(Builder augmentation) {
_addAugmentation(augmentation);
}
void _addAugmentation(Builder augmentation) {
if (augmentation is SourceFactoryBuilder) {
if (checkAugmentation(
augmentationLibraryBuilder: augmentation.libraryBuilder,
origin: this,
augmentation: augmentation)) {
augmentation._actualOrigin = this;
(_augmentations ??= []).add(augmentation);
}
} else {
// Coverage-ignore-block(suite): Not run.
reportAugmentationMismatch(
originLibraryBuilder: libraryBuilder,
origin: this,
augmentation: augmentation);
}
}
@override
// Coverage-ignore(suite): Not run.
void applyAugmentation(Builder augmentation) {
_addAugmentation(augmentation);
}
@override
int buildBodyNodes(BuildNodesCallback f) {
int count = 0;
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
count += augmentation.buildBodyNodes(f);
}
}
if (isAugmenting) {
_finishAugmentation();
}
return count;
}
@override
int computeDefaultTypes(ComputeDefaultTypeContext context,
{required bool inErrorRecovery}) {
int count = _encoding.computeDefaultTypes(context,
inErrorRecovery: inErrorRecovery);
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
count += augmentation.computeDefaultTypes(context,
inErrorRecovery: inErrorRecovery);
}
}
return count;
}
@override
// Coverage-ignore(suite): Not run.
void checkVariance(
SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) {}
@override
void checkTypes(SourceLibraryBuilder library, NameSpace nameSpace,
TypeEnvironment typeEnvironment) {
_encoding.checkTypes(library, nameSpace, typeEnvironment);
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
augmentation.checkTypes(library, nameSpace, typeEnvironment);
}
}
}
bool _hasBeenCheckedAsRedirectingFactory = false;
/// Checks the redirecting factories of this factory builder and its
/// augmentations.
void checkRedirectingFactories(TypeEnvironment typeEnvironment) {
if (_hasBeenCheckedAsRedirectingFactory) return;
_hasBeenCheckedAsRedirectingFactory = true;
if (_introductory.redirectionTarget != null) {
_encoding.checkRedirectingFactory(typeEnvironment);
}
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
if (augmentation.redirectionTarget != null) {
augmentation.checkRedirectingFactories(typeEnvironment);
}
}
}
}
@override
// Coverage-ignore(suite): Not run.
String get fullNameForErrors {
return "${declarationBuilder.name}"
"${name.isEmpty ? '' : '.$name'}";
}
// TODO(johnniwinther): Add annotations to tear-offs.
@override
Iterable<Annotatable> get annotatables => [_procedure];
/// Returns `true` if this member is augmented, either by being the origin
/// of a augmented member or by not being the last among augmentations.
bool get isAugmented {
if (isAugmenting) {
return origin._augmentations!.last != this;
} else {
return _augmentations != null;
}
}
// Coverage-ignore(suite): Not run.
List<DartType>? get _redirectionTypeArguments =>
_encoding.redirectionTypeArguments;
// Coverage-ignore(suite): Not run.
void set _redirectionTypeArguments(List<DartType>? value) {
_encoding.redirectionTypeArguments = value;
}
@override
void buildOutlineNodes(BuildNodesCallback f) {
_encoding.buildOutlineNodes(f);
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
augmentation.buildOutlineNodes((
{required Member member,
Member? tearOff,
required BuiltMemberKind kind}) {
// Don't add augmentations.
});
}
}
}
bool _hasBuiltOutlineExpressions = false;
@override
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
if (_hasBuiltOutlineExpressions) return;
_hasBuiltOutlineExpressions = true;
if (_introductory.redirectionTarget != null && isConst && isAugmenting) {
origin.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
_encoding.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
if (isConst && isAugmenting) {
_finishAugmentation();
}
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
augmentation.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
}
}
void _finishAugmentation() {
finishProcedureAugmentation(_procedure, _encoding.procedure);
if (_encoding.tearOff != null) {
finishProcedureAugmentation(_tearOff!, _encoding.tearOff!);
}
if (_introductory.redirectionTarget != null) {
if (origin.redirectionTarget != null) {
// Coverage-ignore-block(suite): Not run.
origin._redirectionTypeArguments = _redirectionTypeArguments;
}
}
}
BodyBuilderContext createBodyBuilderContext() {
return new FactoryBodyBuilderContext(this, _procedure);
}
void resolveRedirectingFactory() {
_encoding.resolveRedirectingFactory();
List<SourceFactoryBuilder>? augmentations = _augmentations;
if (augmentations != null) {
for (SourceFactoryBuilder augmentation in augmentations) {
augmentation.resolveRedirectingFactory();
}
}
}
void _setRedirectingFactoryBody(Member target, List<DartType> typeArguments) {
_encoding.setRedirectingFactoryBody(target, typeArguments);
}
}
class FactoryBodyBuilderContext extends BodyBuilderContext {
final SourceFactoryBuilder _member;
final Member _builtMember;
FactoryBodyBuilderContext(this._member, this._builtMember)
: super(_member.libraryBuilder, _member.declarationBuilder,
isDeclarationInstanceMember: _member.isDeclarationInstanceMember);
@override
VariableDeclaration getFormalParameter(int index) {
return _member.getFormalParameter(index);
}
@override
VariableDeclaration? getTearOffParameter(int index) {
return _member.getTearOffParameter(index);
}
@override
TypeBuilder get returnType => _member.returnType;
@override
// Coverage-ignore(suite): Not run.
List<FormalParameterBuilder>? get formals => _member.formals;
@override
LocalScope computeFormalParameterInitializerScope(LocalScope parent) {
/// Initializer formals or super parameters cannot occur in getters so
/// we don't need to create a new scope.
return parent;
}
@override
FormalParameterBuilder? getFormalParameterByName(Identifier name) {
return _member.getFormal(name);
}
@override
int get memberNameLength => _member.name.length;
@override
FunctionNode get function {
return _member.function;
}
@override
// Coverage-ignore(suite): Not run.
bool get isFactory {
return _member.isFactory;
}
@override
// Coverage-ignore(suite): Not run.
bool get isNativeMethod {
return _member.isNative;
}
@override
bool get isExternalFunction {
return _member.isExternal;
}
@override
bool get isSetter {
return _member.isSetter;
}
@override
// Coverage-ignore(suite): Not run.
AugmentSuperTarget? get augmentSuperTarget {
if (_member.isAugmentation) {
return _member.augmentSuperTarget;
}
return null;
}
@override
int get memberNameOffset => _member.fileOffset;
@override
// Coverage-ignore(suite): Not run.
void registerSuperCall() {
_builtMember.transformerFlags |= TransformerFlag.superCalls;
}
@override
void registerFunctionBody(Statement body) {
_member.setBody(body);
}
@override
void setAsyncModifier(AsyncMarker asyncModifier) {
_member._setAsyncModifier(asyncModifier);
}
@override
bool get isRedirectingFactory => _member.redirectionTarget != null;
@override
DartType get returnTypeContext {
return _member.function.returnType;
}
@override
String get redirectingFactoryTargetName {
return _member.redirectionTarget!.fullNameForErrors;
}
}
class _FactoryEncoding implements InferredTypeListener {
late final Procedure _procedureInternal;
late final Procedure? _factoryTearOff;
final FactoryFragment _fragment;
AsyncMarker _asyncModifier;
final List<NominalParameterBuilder>? typeParameters;
final TypeBuilder returnType;
DelayedDefaultValueCloner? _delayedDefaultValueCloner;
List<DartType>? _redirectionTypeArguments;
FreshTypeParameters? _tearOffTypeParameters;
_FactoryEncoding(this._fragment,
{required String name,
required SourceLibraryBuilder libraryBuilder,
required this.typeParameters,
required this.returnType,
required NameScheme nameScheme,
required Reference? procedureReference,
required Reference? tearOffReference})
: _asyncModifier = _fragment.redirectionTarget != null
? AsyncMarker.Sync
: _fragment.asyncModifier {
_procedureInternal = new Procedure(
dummyName,
nameScheme.isExtensionTypeMember
? ProcedureKind.Method
: ProcedureKind.Factory,
new FunctionNode(null)
..asyncMarker = _asyncModifier
..dartAsyncMarker = _asyncModifier,
fileUri: _fragment.fileUri,
reference: procedureReference)
..fileStartOffset = _fragment.startOffset
..fileOffset = _fragment.fullNameOffset
..fileEndOffset = _fragment.endOffset
..isExtensionTypeMember = nameScheme.isExtensionTypeMember;
nameScheme
.getConstructorMemberName(name, isTearOff: false)
.attachMember(_procedureInternal);
_factoryTearOff = createFactoryTearOffProcedure(
nameScheme.getConstructorMemberName(name, isTearOff: true),
libraryBuilder,
_fragment.fileUri,
_fragment.fullNameOffset,
tearOffReference,
forceCreateLowering: nameScheme.isExtensionTypeMember)
?..isExtensionTypeMember = nameScheme.isExtensionTypeMember;
returnType.registerInferredTypeListener(this);
}
Procedure get procedure => _procedureInternal;
Procedure? get tearOff => _factoryTearOff;
@override
// Coverage-ignore(suite): Not run.
void onInferredType(DartType type) {
_procedureInternal.function.returnType = type;
}
void set asyncModifier(AsyncMarker newModifier) {
_asyncModifier = newModifier;
_procedureInternal.function.asyncMarker = _asyncModifier;
_procedureInternal.function.dartAsyncMarker = _asyncModifier;
}
List<DartType>? get redirectionTypeArguments {
assert(_fragment.redirectionTarget != null);
return _redirectionTypeArguments;
}
void set redirectionTypeArguments(List<DartType>? value) {
assert(_fragment.redirectionTarget != null);
_redirectionTypeArguments = value;
}
void buildOutlineNodes(BuildNodesCallback f) {
_procedureInternal.function.asyncMarker = _asyncModifier;
if (_fragment.redirectionTarget == null &&
!_fragment.modifiers.isAbstract &&
!_fragment.modifiers.isExternal) {
_procedureInternal.function.body = new EmptyStatement()
..parent = _procedureInternal.function;
}
buildTypeParametersAndFormals(_fragment.builder.libraryBuilder,
_procedureInternal.function, typeParameters, _fragment.formals,
classTypeParameters: null, supportsTypeParameters: true);
if (returnType is! InferableTypeBuilder) {
_procedureInternal.function.returnType = returnType.build(
_fragment.builder.libraryBuilder, TypeUse.returnType);
}
_procedureInternal.function.fileOffset = _fragment.formalsOffset;
_procedureInternal.function.fileEndOffset =
_procedureInternal.fileEndOffset;
_procedureInternal.isAbstract = _fragment.modifiers.isAbstract;
_procedureInternal.isExternal = _fragment.modifiers.isExternal;
_procedureInternal.isConst = _fragment.modifiers.isConst;
_procedureInternal.isStatic = _fragment.modifiers.isStatic;
if (_fragment.redirectionTarget != null) {
if (_fragment.redirectionTarget!.typeArguments != null) {
redirectionTypeArguments = new List<DartType>.generate(
_fragment.redirectionTarget!.typeArguments!.length,
(int i) => _fragment.redirectionTarget!.typeArguments![i].build(
_fragment.builder.libraryBuilder,
TypeUse.redirectionTypeArgument),
growable: false);
}
if (_factoryTearOff != null) {
_tearOffTypeParameters =
buildRedirectingFactoryTearOffProcedureParameters(
tearOff: _factoryTearOff,
implementationConstructor: _procedureInternal,
libraryBuilder: _fragment.builder.libraryBuilder);
}
} else {
if (_factoryTearOff != null) {
_delayedDefaultValueCloner = buildConstructorTearOffProcedure(
tearOff: _factoryTearOff,
declarationConstructor: _fragment.builder._procedure,
implementationConstructor: _procedureInternal,
libraryBuilder: _fragment.builder.libraryBuilder);
}
}
f(
member: _procedureInternal,
tearOff: _factoryTearOff,
kind: _fragment.builder.isExtensionTypeMember
? (_fragment.redirectionTarget != null
? BuiltMemberKind.ExtensionTypeRedirectingFactory
: BuiltMemberKind.ExtensionTypeFactory)
: (_fragment.redirectionTarget != null
? BuiltMemberKind.RedirectingFactory
: BuiltMemberKind.Factory));
}
void buildOutlineExpressions(ClassHierarchy classHierarchy,
List<DelayedDefaultValueCloner> delayedDefaultValueCloners) {
_fragment.formals?.infer(classHierarchy);
if (_delayedDefaultValueCloner != null) {
delayedDefaultValueCloners.add(_delayedDefaultValueCloner!);
}
for (Annotatable annotatable in _fragment.builder.annotatables) {
MetadataBuilder.buildAnnotations(
annotatable,
_fragment.metadata,
_fragment.builder.createBodyBuilderContext(),
_fragment.builder.libraryBuilder,
_fragment.fileUri,
_fragment.enclosingScope,
createFileUriExpression: _fragment.builder.isAugmented);
}
if (typeParameters != null) {
for (int i = 0; i < typeParameters!.length; i++) {
typeParameters![i].buildOutlineExpressions(
_fragment.builder.libraryBuilder,
_fragment.builder.createBodyBuilderContext(),
classHierarchy,
_fragment.typeParameterScope);
}
}
if (_fragment.formals != null) {
// For const constructors we need to include default parameter values
// into the outline. For all other formals we need to call
// buildOutlineExpressions to clear initializerToken to prevent
// consuming too much memory.
for (FormalParameterBuilder formal in _fragment.formals!) {
formal.buildOutlineExpressions(_fragment.builder.libraryBuilder,
_fragment.builder.declarationBuilder,
scope: _fragment.typeParameterScope,
buildDefaultValue: FormalParameterBuilder
.needsDefaultValuesBuiltAsOutlineExpressions(
_fragment.builder));
}
}
if (_fragment.redirectionTarget == null) {
return;
}
RedirectingFactoryTarget? redirectingFactoryTarget =
_procedureInternal.function.redirectingFactoryTarget;
if (redirectingFactoryTarget == null) {
// The error is reported elsewhere.
return;
}
List<DartType>? typeArguments = redirectingFactoryTarget.typeArguments;
Member? target = redirectingFactoryTarget.target;
if (typeArguments != null && typeArguments.any((t) => t is UnknownType)) {
TypeInferrer inferrer = _fragment
.builder.libraryBuilder.loader.typeInferenceEngine
.createLocalTypeInferrer(
_fragment.fileUri,
_fragment.builder.declarationBuilder.thisType,
_fragment.builder.libraryBuilder,
_fragment.typeParameterScope,
null);
InferenceHelper helper = _fragment.builder.libraryBuilder.loader
.createBodyBuilderForOutlineExpression(
_fragment.builder.libraryBuilder,
_fragment.builder.createBodyBuilderContext(),
_fragment.enclosingScope,
_fragment.fileUri);
Builder? targetBuilder = _fragment.redirectionTarget!.target;
if (targetBuilder is SourceMemberBuilder) {
// Ensure that target has been built.
targetBuilder.buildOutlineExpressions(
classHierarchy, delayedDefaultValueCloners);
}
if (targetBuilder is FunctionBuilder) {
target = targetBuilder.invokeTarget!;
}
// Coverage-ignore(suite): Not run.
else if (targetBuilder is DillMemberBuilder) {
target = targetBuilder.invokeTarget!;
} else {
unhandled("${targetBuilder.runtimeType}", "buildOutlineExpressions",
_fragment.fullNameOffset, _fragment.fileUri);
}
typeArguments = inferrer.inferRedirectingFactoryTypeArguments(
helper,
_procedureInternal.function.returnType,
_fragment.builder._procedure.function,
_fragment.fullNameOffset,
target,
target.function!.computeFunctionType(Nullability.nonNullable));
if (typeArguments == null) {
assert(_fragment.builder.libraryBuilder.loader
.assertProblemReportedElsewhere(
"RedirectingFactoryTarget.buildOutlineExpressions",
expectedPhase: CompilationPhaseForProblemReporting.outline));
// Use 'dynamic' for recovery.
typeArguments = new List<DartType>.filled(
_fragment.builder.declarationBuilder.typeParametersCount,
const DynamicType(),
growable: true);
}
_procedureInternal.function.body = createRedirectingFactoryBody(
target, typeArguments, _procedureInternal.function);
_procedureInternal.function.body!.parent = _procedureInternal.function;
_procedureInternal.function.redirectingFactoryTarget =
new RedirectingFactoryTarget(target, typeArguments);
}
Set<Procedure> seenTargets = {};
while (target is Procedure && target.isRedirectingFactory) {
if (!seenTargets.add(target)) {
// Cyclic dependency.
target = null;
break;
}
RedirectingFactoryTarget redirectingFactoryTarget =
target.function.redirectingFactoryTarget!;
if (typeArguments != null) {
Substitution substitution = Substitution.fromPairs(
target.function.typeParameters, typeArguments);
typeArguments = redirectingFactoryTarget.typeArguments
?.map(substitution.substituteType)
.toList();
} else {
// Coverage-ignore-block(suite): Not run.
typeArguments = redirectingFactoryTarget.typeArguments;
}
target = redirectingFactoryTarget.target;
}
if (target is Constructor ||
target is Procedure &&
(target.isFactory || target.isExtensionTypeMember)) {
// Coverage-ignore(suite): Not run.
typeArguments ??= [];
if (_factoryTearOff != null) {
delayedDefaultValueCloners.add(buildRedirectingFactoryTearOffBody(
_factoryTearOff,
target!,
typeArguments,
_tearOffTypeParameters!,
_fragment.builder.libraryBuilder));
}
delayedDefaultValueCloners.add(new DelayedDefaultValueCloner(
target!, _fragment.builder._procedure,
libraryBuilder: _fragment.builder.libraryBuilder,
identicalSignatures: false));
}
}
void resolveRedirectingFactory() {
ConstructorReferenceBuilder? redirectionTarget =
_fragment.redirectionTarget;
if (redirectionTarget != null) {
// Compute the immediate redirection target, not the effective.
List<TypeBuilder>? typeArguments = redirectionTarget.typeArguments;
Builder? target = redirectionTarget.target;
if (typeArguments != null && target is MemberBuilder) {
TypeName redirectionTargetName = redirectionTarget.typeName;
if (redirectionTargetName.qualifier == null) {
// Do nothing. This is the case of an identifier followed by
// type arguments, such as the following:
// B<T>
// B<T>.named
} else {
if (target.name.isEmpty) {
// Do nothing. This is the case of a qualified
// non-constructor prefix (for example, with a library
// qualifier) followed by type arguments, such as the
// following:
// lib.B<T>
} else if (target.name != redirectionTargetName.name) {
// Do nothing. This is the case of a qualified
// non-constructor prefix followed by type arguments followed
// by a constructor name, such as the following:
// lib.B<T>.named
} else {
// TODO(cstefantsova,johnniwinther): Handle this in case in
// ConstructorReferenceBuilder.resolveIn and unify with other
// cases of handling of type arguments after constructor
// names.
_fragment.builder.libraryBuilder.addProblem(
messageConstructorWithTypeArguments,
redirectionTargetName.nameOffset,
redirectionTargetName.nameLength,
_fragment.fileUri);
}
}
}
Builder? targetBuilder = redirectionTarget.target;
Member? targetNode;
if (targetBuilder is FunctionBuilder) {
targetNode = targetBuilder.invokeTarget!;
} else if (targetBuilder is DillMemberBuilder) {
targetNode = targetBuilder.invokeTarget!;
} else if (targetBuilder is AmbiguousBuilder) {
_addProblemForRedirectingFactory(
templateDuplicatedDeclarationUse
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
} else {
_addProblemForRedirectingFactory(
templateRedirectionTargetNotFound
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
}
if (targetNode != null &&
targetNode is Constructor &&
targetNode.enclosingClass.isAbstract) {
_addProblemForRedirectingFactory(
templateAbstractRedirectedClassInstantiation
.withArguments(redirectionTarget.fullNameForErrors),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
targetNode = null;
}
if (targetNode != null &&
targetNode is Constructor &&
targetNode.enclosingClass.isEnum) {
_addProblemForRedirectingFactory(
messageEnumFactoryRedirectsToConstructor,
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
targetNode = null;
}
if (targetNode != null) {
List<DartType>? typeArguments = redirectionTypeArguments;
if (typeArguments == null) {
int typeArgumentCount;
if (targetBuilder!.isExtensionTypeMember) {
ExtensionTypeDeclarationBuilder extensionTypeDeclarationBuilder =
targetBuilder.parent as ExtensionTypeDeclarationBuilder;
typeArgumentCount =
extensionTypeDeclarationBuilder.typeParametersCount;
} else {
typeArgumentCount =
targetNode.enclosingClass!.typeParameters.length;
}
typeArguments =
new List<DartType>.filled(typeArgumentCount, const UnknownType());
}
setRedirectingFactoryBody(targetNode, typeArguments);
}
}
}
void setRedirectingFactoryBody(Member target, List<DartType> typeArguments) {
if (_procedureInternal.function.body != null) {
unexpected("null", "${_procedureInternal.function.body.runtimeType}",
_fragment.fullNameOffset, _fragment.fileUri);
}
// Ensure that constant factories only have constant targets/bodies.
if (_fragment.modifiers.isConst && !target.isConst) {
// Coverage-ignore-block(suite): Not run.
_fragment.builder.libraryBuilder.addProblem(
messageConstFactoryRedirectionToNonConst,
_fragment.fullNameOffset,
noLength,
_fragment.fileUri);
}
_procedureInternal.function.body = createRedirectingFactoryBody(
target, typeArguments, _procedureInternal.function)
..parent = _procedureInternal.function;
_procedureInternal.function.redirectingFactoryTarget =
new RedirectingFactoryTarget(target, typeArguments);
if (_fragment.builder.isAugmenting) {
if (_procedureInternal.function.typeParameters.isNotEmpty) {
Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
for (int i = 0;
i < _procedureInternal.function.typeParameters.length;
i++) {
substitution[_procedureInternal.function.typeParameters[i]] =
new TypeParameterType.withDefaultNullability(
_fragment.builder.origin.function.typeParameters[i]);
}
typeArguments = new List<DartType>.generate(typeArguments.length,
(int i) => substitute(typeArguments[i], substitution),
growable: false);
}
_fragment.builder.origin
._setRedirectingFactoryBody(target, typeArguments);
}
}
void _addProblemForRedirectingFactory(
Message message, int charOffset, int length, Uri fileUri) {
_fragment.builder.libraryBuilder
.addProblem(message, charOffset, length, fileUri);
String text = _fragment.builder.libraryBuilder.loader.target.context
.format(
message.withLocation(fileUri, charOffset, length), Severity.error)
.plain;
_setRedirectingFactoryError(text);
}
void _setRedirectingFactoryError(String message) {
assert(_fragment.redirectionTarget != null);
setBody(createRedirectingFactoryErrorBody(message));
_fragment.builder._procedure.function.redirectingFactoryTarget =
new RedirectingFactoryTarget.error(message);
if (_factoryTearOff != null) {
_factoryTearOff.function.body = createRedirectingFactoryErrorBody(message)
..parent = _factoryTearOff.function;
}
}
/// Checks this factory builder if it is for a redirecting factory.
void checkRedirectingFactory(TypeEnvironment typeEnvironment) {
assert(_fragment.redirectionTarget != null);
// Check that factory declaration is not cyclic.
if (_isCyclicRedirectingFactory(_fragment.builder)) {
_addProblemForRedirectingFactory(
templateCyclicRedirectingFactoryConstructors
.withArguments("${_fragment.builder.declarationBuilder.name}"
"${_fragment.name == '' ? '' : '.${_fragment.name}'}"),
_fragment.fullNameOffset,
noLength,
_fragment.fileUri);
return;
}
// The factory type cannot contain any type parameters other than those of
// its enclosing class, because constructors cannot specify type parameters
// of their own.
FunctionType factoryType = _procedureInternal.function
.computeThisFunctionType(Nullability.nonNullable);
if (_fragment.builder.isAugmenting) {
// The redirection target type uses the origin type parameters so we must
// substitute augmentation type parameters before checking subtyping.
if (_procedureInternal.function.typeParameters.isNotEmpty) {
Map<TypeParameter, DartType> substitution = <TypeParameter, DartType>{};
for (int i = 0;
i < _procedureInternal.function.typeParameters.length;
i++) {
substitution[_procedureInternal.function.typeParameters[i]] =
new TypeParameterType.withDefaultNullability(
_fragment.builder.origin.function.typeParameters[i]);
}
factoryType = substitute(factoryType, substitution) as FunctionType;
}
}
FunctionType? redirecteeType = _computeRedirecteeType(typeEnvironment);
Map<TypeParameter, DartType> substitutionMap = {};
for (int i = 0; i < factoryType.typeParameters.length; i++) {
TypeParameter functionTypeParameter =
_fragment.builder.origin.function.typeParameters[i];
substitutionMap[functionTypeParameter] =
new StructuralParameterType.withDefaultNullability(
factoryType.typeParameters[i]);
}
redirecteeType = redirecteeType != null
? substitute(redirecteeType, substitutionMap) as FunctionType
: null;
// TODO(hillerstrom): It would be preferable to know whether a failure
// happened during [_computeRedirecteeType].
if (redirecteeType == null) {
return;
}
Builder? redirectionTargetBuilder = _fragment.redirectionTarget!.target;
if (redirectionTargetBuilder is SourceFactoryBuilder &&
redirectionTargetBuilder.redirectionTarget != null) {
redirectionTargetBuilder.checkRedirectingFactories(typeEnvironment);
String? errorMessage = redirectionTargetBuilder
.function.redirectingFactoryTarget?.errorMessage;
if (errorMessage != null) {
_setRedirectingFactoryError(errorMessage);
}
}
Builder? redirectionTargetParent =
_fragment.redirectionTarget!.target?.parent;
bool redirectingTargetParentIsEnum = redirectionTargetParent is ClassBuilder
? redirectionTargetParent.isEnum
: false;
if (!((_fragment.builder.classBuilder?.cls.isEnum ?? false) &&
(_fragment.redirectionTarget!.target?.isConstructor ?? false) &&
redirectingTargetParentIsEnum)) {
// Check whether [redirecteeType] <: [factoryType].
FunctionType factoryTypeWithoutTypeParameters =
factoryType.withoutTypeParameters;
if (!typeEnvironment.isSubtypeOf(
redirecteeType,
factoryTypeWithoutTypeParameters,
SubtypeCheckMode.withNullabilities)) {
_addProblemForRedirectingFactory(
templateIncompatibleRedirecteeFunctionType.withArguments(
redirecteeType, factoryTypeWithoutTypeParameters),
_fragment.redirectionTarget!.charOffset,
noLength,
_fragment.redirectionTarget!.fileUri);
}
} else {
// Redirection to generative enum constructors is forbidden.
assert(_fragment.builder.libraryBuilder.loader
.assertProblemReportedElsewhere(
"RedirectingFactoryBuilder._checkRedirectingFactory: "
"Redirection to generative enum constructor.",
expectedPhase: CompilationPhaseForProblemReporting.bodyBuilding));
}
}
// Computes the function type of a given redirection target. Returns [null] if
// the type of the target could not be computed.
FunctionType? _computeRedirecteeType(TypeEnvironment typeEnvironment) {
assert(_fragment.redirectionTarget != null);
ConstructorReferenceBuilder redirectionTarget =
_fragment.redirectionTarget!;
Builder? targetBuilder = redirectionTarget.target;
FunctionNode targetNode;
if (targetBuilder == null) return null;
if (targetBuilder is FunctionBuilder) {
targetNode = targetBuilder.function;
} else if (targetBuilder is DillExtensionTypeFactoryBuilder) {
targetNode = targetBuilder.member.function!;
} else if (targetBuilder is AmbiguousBuilder) {
// Multiple definitions with the same name: An error has already been
// issued.
// TODO(http://dartbug.com/35294): Unfortunate error; see also
// https://dart-review.googlesource.com/c/sdk/+/85390/.
return null;
} else {
unhandled("${targetBuilder.runtimeType}", "computeRedirecteeType",
_fragment.fullNameOffset, _fragment.fileUri);
}
List<DartType>? typeArguments = _getRedirectionTypeArguments();
FunctionType targetFunctionType =
targetNode.computeFunctionType(Nullability.nonNullable);
if (typeArguments != null &&
targetFunctionType.typeParameters.length != typeArguments.length) {
_addProblemForRedirectingFactory(
templateTypeArgumentMismatch
.withArguments(targetFunctionType.typeParameters.length),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
return null;
}
// Compute the substitution of the target class type parameters if
// [redirectionTarget] has any type arguments.
FunctionTypeInstantiator? instantiator;
bool hasProblem = false;
if (typeArguments != null && typeArguments.length > 0) {
instantiator = new FunctionTypeInstantiator.fromIterables(
targetFunctionType.typeParameters, typeArguments);
for (int i = 0; i < targetFunctionType.typeParameters.length; i++) {
StructuralParameter typeParameter =
targetFunctionType.typeParameters[i];
DartType typeParameterBound =
instantiator.substitute(typeParameter.bound);
DartType typeArgument = typeArguments[i];
// Check whether the [typeArgument] respects the bounds of
// [typeParameter].
if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
SubtypeCheckMode.ignoringNullabilities)) {
// Coverage-ignore-block(suite): Not run.
_addProblemForRedirectingFactory(
templateRedirectingFactoryIncompatibleTypeArgument.withArguments(
typeArgument, typeParameterBound),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
hasProblem = true;
} else {
if (!typeEnvironment.isSubtypeOf(typeArgument, typeParameterBound,
SubtypeCheckMode.withNullabilities)) {
_addProblemForRedirectingFactory(
templateRedirectingFactoryIncompatibleTypeArgument
.withArguments(typeArgument, typeParameterBound),
redirectionTarget.charOffset,
noLength,
redirectionTarget.fileUri);
hasProblem = true;
}
}
}
} else if (typeArguments == null &&
targetFunctionType.typeParameters.length > 0) {
// TODO(hillerstrom): In this case, we need to perform type inference on
// the redirectee to obtain actual type arguments which would allow the
// following program to type check:
//
// class A<T> {
// factory A() = B;
// }
// class B<T> implements A<T> {
// B();
// }
//
return null;
}
// Substitute if necessary.
targetFunctionType = instantiator == null
? targetFunctionType
: (instantiator.substitute(targetFunctionType.withoutTypeParameters)
as FunctionType);
return hasProblem ? null : targetFunctionType;
}
static bool _isCyclicRedirectingFactory(SourceFactoryBuilder factory) {
assert(factory.redirectionTarget != null);
// We use the [tortoise and hare algorithm]
// (https://en.wikipedia.org/wiki/Cycle_detection#Tortoise_and_hare) to
// handle cycles.
Builder? tortoise = factory;
Builder? hare = factory.redirectionTarget!.target;
if (hare == factory) {
return true;
}
while (tortoise != hare) {
// Hare moves 2 steps forward.
if (hare is! SourceFactoryBuilder || hare.redirectionTarget == null) {
return false;
}
hare = hare.redirectionTarget!.target;
if (hare == factory) {
return true;
}
if (hare is! SourceFactoryBuilder || hare.redirectionTarget == null) {
return false;
}
hare = hare.redirectionTarget!.target;
if (hare == factory) {
return true;
}
// Tortoise moves one step forward. No need to test type of tortoise
// as it follows hare which already checked types.
tortoise = (tortoise as SourceFactoryBuilder).redirectionTarget!.target;
}
// Cycle found, but original factory doesn't belong to a cycle.
return false;
}
List<DartType>? _getRedirectionTypeArguments() {
assert(_fragment.redirectionTarget != null);
return _fragment
.builder._procedure.function.redirectingFactoryTarget!.typeArguments;
}
void setBody(Statement value) {
_procedureInternal.function.body = value
..parent = _procedureInternal.function;
}
void becomeNative(SourceLoader loader) {
for (Annotatable annotatable in _fragment.builder.annotatables) {
loader.addNativeAnnotation(annotatable, _fragment.nativeMethodName!);
}
_procedureInternal.isExternal = true;
}
// Coverage-ignore(suite): Not run.
bool get isNative => _fragment.nativeMethodName != null;
FunctionNode get function => _procedureInternal.function;
FormalParameterBuilder? getFormal(Identifier identifier) {
if (_fragment.formals != null) {
for (FormalParameterBuilder formal in _fragment.formals!) {
if (formal.isWildcard &&
identifier.name == '_' &&
formal.fileOffset == identifier.nameOffset) {
return formal;
}
if (formal.name == identifier.name &&
formal.fileOffset == identifier.nameOffset) {
return formal;
}
}
// Coverage-ignore(suite): Not run.
// If we have any formals we should find the one we're looking for.
assert(false, "$identifier not found in ${_fragment.formals}");
}
return null;
}
VariableDeclaration? getTearOffParameter(int index) {
if (_factoryTearOff != null) {
if (index < _factoryTearOff.function.positionalParameters.length) {
return _factoryTearOff.function.positionalParameters[index];
} else {
index -= _factoryTearOff.function.positionalParameters.length;
if (index < _factoryTearOff.function.namedParameters.length) {
return _factoryTearOff.function.namedParameters[index];
}
}
}
return null;
}
int computeDefaultTypes(ComputeDefaultTypeContext context,
{required bool inErrorRecovery}) {
int count = context.computeDefaultTypesForVariables(typeParameters,
// Type parameters are inherited from the enclosing declaration, so if
// it has issues, so do the constructors.
inErrorRecovery: inErrorRecovery);
context.reportGenericFunctionTypesForFormals(_fragment.formals);
return count;
}
void checkTypes(SourceLibraryBuilder library, NameSpace nameSpace,
TypeEnvironment typeEnvironment) {
if (_fragment.redirectionTarget != null) {
// Default values are not required on redirecting factory constructors so
// we don't call [checkInitializersInFormals].
} else {
library.checkInitializersInFormals(_fragment.formals, typeEnvironment,
isAbstract: _fragment.modifiers.isAbstract,
isExternal: _fragment.modifiers.isExternal);
}
}
}