blob: a75c8c4726190eba0ff47d7f7304570393824340 [file] [log] [blame]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/parser/formal_parameter_kind.dart';
import 'package:kernel/ast.dart';
import 'package:kernel/type_algebra.dart';
import '../../api_prototype/lowering_predicates.dart';
import '../../base/modifiers.dart';
import '../../builder/declaration_builders.dart';
import '../../builder/formal_parameter_builder.dart';
import '../../builder/named_type_builder.dart';
import '../../builder/omitted_type_builder.dart';
import '../../builder/type_builder.dart';
import '../../kernel/body_builder_context.dart';
import '../../kernel/constructor_tearoff_lowering.dart';
import '../../kernel/internal_ast.dart';
import '../../kernel/kernel_helper.dart';
import '../../source/name_scheme.dart';
import '../../source/source_class_builder.dart';
import '../../source/source_constructor_builder.dart';
import '../../source/source_extension_builder.dart';
import '../../source/source_extension_type_declaration_builder.dart';
import '../../source/source_function_builder.dart';
import '../../source/source_library_builder.dart';
import '../../source/source_loader.dart';
import '../../source/source_member_builder.dart';
import '../../source/source_type_parameter_builder.dart';
import '../../source/type_parameter_factory.dart';
import '../../type_inference/type_schema.dart';
import '../fragment.dart';
import 'body_builder_context.dart';
import 'declaration.dart';
abstract class ConstructorEncoding {
FunctionNode get function;
List<Initializer> get initializers;
void prepareInitializers();
void prependInitializer(Initializer initializer);
VariableDeclaration getFormalParameter(int index);
VariableDeclaration? getTearOffParameter(int index);
VariableDeclaration? get thisVariable;
List<TypeParameter>? get thisTypeParameters;
/// Mark the constructor as erroneous.
///
/// This is used during the compilation phase to set the appropriate flag on
/// the input AST node. The flag helps the verifier to skip apriori erroneous
/// members and to avoid reporting cascading errors.
void markAsErroneous();
void buildOutlineNodes(
BuildNodesCallback f, {
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required covariant DeclarationBuilder declarationBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int startOffset,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool forAbstractClassOrEnumOrMixin,
required bool isConst,
required bool isSynthetic,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
});
void buildBody();
BodyBuilderContext createBodyBuilderContext(
SourceConstructorBuilder constructorBuilder,
ConstructorFragmentDeclaration constructorDeclaration);
void registerFunctionBody(Statement value);
void registerNoBodyConstructor();
void addSuperParameterDefaultValueCloners(
{required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
required Member superTarget,
required List<int?>? positionalSuperParameters,
required List<String>? namedSuperParameters,
required SourceLibraryBuilder libraryBuilder});
void becomeNative(SourceLoader loader, String nativeMethodName);
Substitution computeFieldTypeSubstitution(
covariant DeclarationBuilder declarationBuilder,
List<SourceNominalParameterBuilder>? typeParameters);
bool get isRedirecting;
}
class RegularConstructorEncoding implements ConstructorEncoding {
late final Constructor _constructor;
late final Procedure? _constructorTearOff;
final bool _isExternal;
final bool _isEnumConstructor;
Statement? bodyInternal;
RegularConstructorEncoding(
{required bool isExternal, required bool isEnumConstructor})
: _isExternal = isExternal,
_isEnumConstructor = isEnumConstructor;
@override
void registerFunctionBody(Statement value) {
function.body = value..parent = function;
}
@override
void registerNoBodyConstructor() {
if (!_isExternal) {
registerFunctionBody(new EmptyStatement());
}
}
@override
FunctionNode get function => _constructor.function;
// Coverage-ignore(suite): Not run.
Member get constructor => _constructor;
// Coverage-ignore(suite): Not run.
Procedure? get constructorTearOff => _constructorTearOff;
@override
List<Initializer> get initializers => _constructor.initializers;
@override
bool get isRedirecting {
for (Initializer initializer in initializers) {
if (initializer is RedirectingInitializer) {
return true;
}
}
return false;
}
@override
VariableDeclaration? get thisVariable => null;
@override
List<TypeParameter>? get thisTypeParameters => null;
@override
void buildOutlineNodes(
BuildNodesCallback f, {
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required SourceClassBuilder declarationBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int startOffset,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool isSynthetic,
required bool forAbstractClassOrEnumOrMixin,
required bool isConst,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
}) {
_build(
constructorBuilder: constructorBuilder,
libraryBuilder: libraryBuilder,
classBuilder: declarationBuilder,
name: name,
nameScheme: nameScheme,
constructorReferences: constructorReferences,
fileUri: fileUri,
startOffset: startOffset,
fileOffset: fileOffset,
formalsOffset: formalsOffset,
endOffset: endOffset,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin,
isSynthetic: isSynthetic,
isConst: isConst,
returnType: returnType,
typeParameters: typeParameters,
formals: formals,
delayedDefaultValueCloners: delayedDefaultValueCloners);
f(
member: _constructor,
tearOff: _constructorTearOff,
kind: BuiltMemberKind.Constructor);
}
bool _hasBeenBuilt = false;
void _build({
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required SourceClassBuilder classBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int startOffset,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool forAbstractClassOrEnumOrMixin,
required bool isSynthetic,
required bool isConst,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
}) {
if (!_hasBeenBuilt) {
_constructor = new Constructor(
new FunctionNode(_isExternal ? null : new EmptyStatement()),
name: dummyName,
fileUri: fileUri,
reference: constructorReferences?.constructorReference,
isSynthetic: isSynthetic)
..startFileOffset = startOffset
..fileOffset = fileOffset
..fileEndOffset = endOffset;
nameScheme
.getConstructorMemberName(name, isTearOff: false)
.attachMember(_constructor);
_constructorTearOff = createConstructorTearOffProcedure(
nameScheme.getConstructorMemberName(name, isTearOff: true),
libraryBuilder,
fileUri,
fileOffset,
constructorReferences?.tearOffReference,
forAbstractClassOrEnumOrMixin:
forAbstractClassOrEnumOrMixin || _isEnumConstructor);
// According to the specification §9.3 the return type of a constructor
// function is its enclosing class.
function.asyncMarker = AsyncMarker.Sync;
buildTypeParametersAndFormals(
libraryBuilder, function, typeParameters, formals,
classTypeParameters: null, supportsTypeParameters: false);
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.withDefaultNullability(typeParameter));
}
InterfaceType type = new InterfaceType(
enclosingClass, Nullability.nonNullable, typeParameterTypes);
returnType.registerInferredType(type);
_constructor.function.fileOffset = formalsOffset;
_constructor.function.fileEndOffset = _constructor.fileEndOffset;
_constructor.function.typeParameters = const <TypeParameter>[];
_constructor.isConst = isConst;
_constructor.isExternal = _isExternal;
if (_constructorTearOff != null) {
DelayedDefaultValueCloner delayedDefaultValueCloner =
buildConstructorTearOffProcedure(
tearOff: _constructorTearOff,
declarationConstructor: _constructor,
implementationConstructor: _constructor,
enclosingDeclarationTypeParameters:
classBuilder.cls.typeParameters,
libraryBuilder: libraryBuilder);
delayedDefaultValueCloners.add(delayedDefaultValueCloner);
}
_hasBeenBuilt = true;
}
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(
new InferableConstructor(_constructor, constructorBuilder));
}
}
}
@override
void buildBody() {}
@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 = [];
// TODO(johnniwinther): Can these be moved here from the
// [SourceConstructorBuilder]?
//redirectingInitializer = null;
//superInitializer = null;
}
@override
void prependInitializer(Initializer initializer) {
initializer.parent = _constructor;
_constructor.initializers.insert(0, initializer);
}
@override
void becomeNative(SourceLoader loader, String nativeMethodName) {
_constructor.isExternal = true;
loader.addNativeAnnotation(_constructor, nativeMethodName);
}
@override
VariableDeclaration getFormalParameter(int index) {
if (_isEnumConstructor) {
// Skip synthetic parameters for index and name.
index += 2;
}
if (index < function.positionalParameters.length) {
return function.positionalParameters[index];
} else {
index -= function.positionalParameters.length;
assert(index < function.namedParameters.length);
return function.namedParameters[index];
}
}
@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;
}
bool _hasAddedDefaultValueCloners = false;
@override
void addSuperParameterDefaultValueCloners(
{required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
required Member superTarget,
required List<int?>? positionalSuperParameters,
required List<String>? namedSuperParameters,
required SourceLibraryBuilder libraryBuilder}) {
if (!_hasAddedDefaultValueCloners) {
// 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));
}
_hasAddedDefaultValueCloners = true;
}
}
@override
BodyBuilderContext createBodyBuilderContext(
SourceConstructorBuilder constructorBuilder,
ConstructorFragmentDeclaration constructorDeclaration) {
return new ConstructorBodyBuilderContext(
constructorBuilder, constructorDeclaration, _constructor);
}
@override
void markAsErroneous() {
_constructor.isErroneous = true;
}
@override
Substitution computeFieldTypeSubstitution(
covariant DeclarationBuilder declarationBuilder,
List<SourceNominalParameterBuilder>? typeParameters) {
// Nothing to substitute. Regular generative constructors don't have their
// own type parameters.
return Substitution.empty;
}
}
mixin _ExtensionTypeConstructorEncodingMixin<T extends DeclarationBuilder>
implements ConstructorEncoding {
late final Procedure _constructor;
late final Procedure? _constructorTearOff;
bool get _isExternal;
bool get _isExtensionMember;
bool get _isExtensionTypeMember;
Statement? bodyInternal;
/// If this procedure is an extension instance member or extension type
/// instance member, [_thisVariable] holds the synthetically added `this`
/// parameter.
VariableDeclaration? _thisVariable;
/// If this procedure is an extension instance member or extension type
/// instance member, [_thisTypeParameters] holds the type parameters copied
/// from the extension/extension type declaration.
List<TypeParameter>? _thisTypeParameters;
List<Initializer> _initializers = [];
@override
List<Initializer> get initializers => _initializers;
@override
void registerFunctionBody(Statement value) {
function.body = value..parent = function;
}
@override
void registerNoBodyConstructor() {
if (!_hasBuiltBody && !_isExternal) {
registerFunctionBody(new EmptyStatement());
}
}
@override
FunctionNode get function => _constructor.function;
bool _hasBeenBuilt = false;
DartType _computeThisType(T declarationBuilder, List<DartType> typeArguments);
void _build({
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required T declarationBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool forAbstractClassOrEnumOrMixin,
required bool isConst,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
}) {
if (!_hasBeenBuilt) {
_constructor = new Procedure(dummyName, ProcedureKind.Method,
new FunctionNode(_isExternal ? null : new EmptyStatement()),
fileUri: fileUri,
reference: constructorReferences?.constructorReference)
..fileOffset = fileOffset
..fileEndOffset = endOffset;
nameScheme
.getConstructorMemberName(name, isTearOff: false)
.attachMember(_constructor);
_constructorTearOff = createConstructorTearOffProcedure(
nameScheme.getConstructorMemberName(name, isTearOff: true),
libraryBuilder,
fileUri,
fileOffset,
constructorReferences?.tearOffReference,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin,
forceCreateLowering: true)
?..isExtensionMember = _isExtensionMember
..isExtensionTypeMember = _isExtensionTypeMember;
// According to the specification §9.3 the return type of a constructor
// function is its enclosing class.
function.asyncMarker = AsyncMarker.Sync;
buildTypeParametersAndFormals(
libraryBuilder, function, typeParameters, formals,
classTypeParameters: null, supportsTypeParameters: true);
if (declarationBuilder.typeParameters != null) {
int count = declarationBuilder.typeParameters!.length;
_thisTypeParameters = new List<TypeParameter>.generate(
count, (int index) => function.typeParameters[index],
growable: false);
}
List<DartType> typeArguments;
if (_thisTypeParameters != null) {
typeArguments = [
for (TypeParameter parameter in _thisTypeParameters!)
new TypeParameterType.withDefaultNullability(parameter)
];
} else {
typeArguments = [];
}
_thisVariable = new VariableDeclarationImpl(syntheticThisName,
isFinal: true,
type: _computeThisType(declarationBuilder, typeArguments))
..fileOffset = fileOffset
..isLowered = true;
List<DartType> typeParameterTypes = <DartType>[];
for (int i = 0; i < function.typeParameters.length; i++) {
TypeParameter typeParameter = function.typeParameters[i];
typeParameterTypes
.add(new TypeParameterType.withDefaultNullability(typeParameter));
}
returnType.registerInferredType(
_computeThisType(declarationBuilder, typeParameterTypes));
_constructor.function.fileOffset = formalsOffset;
_constructor.function.fileEndOffset = _constructor.fileEndOffset;
_constructor.isConst = isConst;
_constructor.isExternal = _isExternal;
_constructor.isStatic = true;
_constructor.isExtensionMember = _isExtensionMember;
_constructor.isExtensionTypeMember = _isExtensionTypeMember;
if (_constructorTearOff != null) {
delayedDefaultValueCloners.add(buildConstructorTearOffProcedure(
tearOff: _constructorTearOff,
declarationConstructor: _constructor,
implementationConstructor: _constructor,
libraryBuilder: libraryBuilder));
}
_hasBeenBuilt = true;
}
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(
new InferableConstructor(_constructor, constructorBuilder));
}
}
}
@override
VariableDeclaration? get thisVariable {
assert(_thisVariable != null,
"ProcedureBuilder.thisVariable has not been set.");
return _thisVariable;
}
@override
List<TypeParameter>? get thisTypeParameters {
// Use [_thisVariable] as marker for whether this type parameters have
// been computed.
assert(_thisVariable != null,
"ProcedureBuilder.thisTypeParameters has not been set.");
return _thisTypeParameters;
}
@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 = [];
// TODO(johnniwinther): Can these be moved here from the
// [SourceConstructorBuilder]?
//redirectingInitializer = null;
//superInitializer = null;
}
@override
void prependInitializer(Initializer initializer) {
_initializers.insert(0, initializer);
}
@override
VariableDeclaration getFormalParameter(int index) {
if (index < function.positionalParameters.length) {
return function.positionalParameters[index];
} else {
index -= function.positionalParameters.length;
assert(index < function.namedParameters.length);
return function.namedParameters[index];
}
}
@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;
}
bool _hasBuiltBody = false;
@override
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 (function.body != null && function.body is! EmptyStatement) {
statements.add(function.body!);
}
statements.add(new ReturnStatement(new VariableGet(thisVariable)));
registerFunctionBody(new Block(statements));
}
_hasBuiltBody = true;
}
@override
BodyBuilderContext createBodyBuilderContext(
SourceConstructorBuilder constructorBuilder,
ConstructorFragmentDeclaration constructorDeclaration) {
return new ConstructorBodyBuilderContext(
constructorBuilder, constructorDeclaration, _constructor);
}
@override
void markAsErroneous() {
_constructor.isErroneous = true;
}
@override
void addSuperParameterDefaultValueCloners(
{required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
required Member superTarget,
required List<int?>? positionalSuperParameters,
required List<String>? namedSuperParameters,
required SourceLibraryBuilder libraryBuilder}) {
throw new UnsupportedError(
'$runtimeType.addSuperParameterDefaultValueCloners');
}
@override
void becomeNative(SourceLoader loader, String nativeMethodName) {
throw new UnsupportedError('$runtimeType.becomeNative');
}
}
class _ExtensionTypeInitializerToStatementConverter
implements InitializerVisitor<void> {
VariableDeclaration thisVariable;
final List<Statement> statements;
_ExtensionTypeInitializerToStatementConverter(
this.statements, this.thisVariable);
@override
void visitAssertInitializer(AssertInitializer node) {
statements.add(node.statement);
}
@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
// 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.
}
}
class ExtensionTypeConstructorEncoding
with
_ExtensionTypeConstructorEncodingMixin<
SourceExtensionTypeDeclarationBuilder>
implements
ConstructorEncoding {
@override
final bool _isExternal;
ExtensionTypeConstructorEncoding({required bool isExternal})
: _isExternal = isExternal;
@override
DartType _computeThisType(
SourceExtensionTypeDeclarationBuilder declarationBuilder,
List<DartType> typeArguments) {
ExtensionTypeDeclaration extensionTypeDeclaration =
declarationBuilder.extensionTypeDeclaration;
return new ExtensionType(
extensionTypeDeclaration, Nullability.nonNullable, typeArguments);
}
@override
void buildOutlineNodes(
BuildNodesCallback f, {
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required SourceExtensionTypeDeclarationBuilder declarationBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int startOffset,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool forAbstractClassOrEnumOrMixin,
required bool isConst,
required bool isSynthetic,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
}) {
_build(
constructorBuilder: constructorBuilder,
libraryBuilder: libraryBuilder,
declarationBuilder: declarationBuilder,
name: name,
nameScheme: nameScheme,
constructorReferences: constructorReferences,
fileUri: fileUri,
fileOffset: fileOffset,
formalsOffset: formalsOffset,
endOffset: endOffset,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin,
isConst: isConst,
returnType: returnType,
typeParameters: typeParameters,
formals: formals,
delayedDefaultValueCloners: delayedDefaultValueCloners);
f(
member: _constructor,
tearOff: _constructorTearOff,
kind: BuiltMemberKind.ExtensionTypeConstructor);
}
@override
bool get _isExtensionMember => false;
@override
bool get _isExtensionTypeMember => true;
@override
Substitution computeFieldTypeSubstitution(
DeclarationBuilder declarationBuilder,
List<SourceNominalParameterBuilder>? typeParameters) {
if (typeParameters != null) {
assert(
declarationBuilder.typeParameters!.length == typeParameters.length);
return Substitution.fromPairs(
(declarationBuilder as SourceExtensionTypeDeclarationBuilder)
.extensionTypeDeclaration
.typeParameters,
new List<DartType>.generate(
declarationBuilder.typeParameters!.length,
(int index) => new TypeParameterType.withDefaultNullability(
function.typeParameters[index])));
} else {
return Substitution.empty;
}
}
@override
bool get isRedirecting {
for (Initializer initializer in initializers) {
if (initializer is ExtensionTypeRedirectingInitializer) {
return true;
}
}
return false;
}
}
class ExtensionConstructorEncoding
with _ExtensionTypeConstructorEncodingMixin<SourceExtensionBuilder>
implements ConstructorEncoding {
@override
final bool _isExternal;
ExtensionConstructorEncoding({required bool isExternal})
: _isExternal = isExternal;
@override
void buildOutlineNodes(
BuildNodesCallback f, {
required SourceConstructorBuilder constructorBuilder,
required SourceLibraryBuilder libraryBuilder,
required SourceExtensionBuilder declarationBuilder,
required String name,
required NameScheme nameScheme,
required ConstructorReferences? constructorReferences,
required Uri fileUri,
required int startOffset,
required int fileOffset,
required int formalsOffset,
required int endOffset,
required bool forAbstractClassOrEnumOrMixin,
required bool isConst,
required bool isSynthetic,
required TypeBuilder returnType,
required List<SourceNominalParameterBuilder>? typeParameters,
required List<FormalParameterBuilder>? formals,
required List<DelayedDefaultValueCloner> delayedDefaultValueCloners,
}) {
_build(
constructorBuilder: constructorBuilder,
libraryBuilder: libraryBuilder,
declarationBuilder: declarationBuilder,
name: name,
nameScheme: nameScheme,
constructorReferences: constructorReferences,
fileUri: fileUri,
fileOffset: fileOffset,
formalsOffset: formalsOffset,
endOffset: endOffset,
forAbstractClassOrEnumOrMixin: forAbstractClassOrEnumOrMixin,
isConst: isConst,
returnType: returnType,
typeParameters: typeParameters,
formals: formals,
delayedDefaultValueCloners: delayedDefaultValueCloners);
// Extension constructors are erroneous and are therefore not added to the
// AST.
}
@override
bool get _isExtensionMember => true;
@override
bool get _isExtensionTypeMember => false;
@override
DartType _computeThisType(
SourceExtensionBuilder declarationBuilder, List<DartType> typeArguments) {
Extension extension = declarationBuilder.extension;
return Substitution.fromPairs(extension.typeParameters, typeArguments)
.substituteType(extension.onType);
}
@override
// Coverage-ignore(suite): Not run.
Substitution computeFieldTypeSubstitution(
SourceExtensionBuilder declarationBuilder,
List<SourceNominalParameterBuilder>? typeParameters) {
if (typeParameters != null) {
assert(
declarationBuilder.typeParameters!.length == typeParameters.length);
return Substitution.fromPairs(
declarationBuilder.extension.typeParameters,
new List<DartType>.generate(
declarationBuilder.typeParameters!.length,
(int index) => new TypeParameterType.withDefaultNullability(
function.typeParameters[index])));
} else {
return Substitution.empty;
}
}
@override
// Coverage-ignore(suite): Not run.
bool get isRedirecting {
// TODO(johnniwinther): Update this if redirecting extension constructors
// are supported.
return false;
}
}
abstract class ConstructorEncodingStrategy {
factory ConstructorEncodingStrategy(DeclarationBuilder declarationBuilder) {
switch (declarationBuilder) {
case ClassBuilder():
if (declarationBuilder.isEnum) {
return const EnumConstructorEncodingStrategy();
} else {
return const RegularConstructorEncodingStrategy();
}
case ExtensionBuilder():
return const ExtensionConstructorEncodingStrategy();
case ExtensionTypeDeclarationBuilder():
return const ExtensionTypeConstructorEncodingStrategy();
}
}
List<FormalParameterBuilder>? createFormals({
required SourceLoader loader,
required List<FormalParameterBuilder>? formals,
required Uri fileUri,
required int fileOffset,
});
List<SourceNominalParameterBuilder>? createTypeParameters({
required DeclarationBuilder declarationBuilder,
required List<TypeParameterFragment>? declarationTypeParameterFragments,
required List<SourceNominalParameterBuilder>? typeParameters,
required TypeParameterFactory typeParameterFactory,
});
ConstructorEncoding createEncoding({required bool isExternal});
}
class RegularConstructorEncodingStrategy
implements ConstructorEncodingStrategy {
const RegularConstructorEncodingStrategy();
@override
ConstructorEncoding createEncoding({required bool isExternal}) {
return new RegularConstructorEncoding(
isExternal: isExternal, isEnumConstructor: false);
}
@override
List<FormalParameterBuilder>? createFormals(
{required SourceLoader loader,
required List<FormalParameterBuilder>? formals,
required Uri fileUri,
required int fileOffset}) {
return formals;
}
@override
List<SourceNominalParameterBuilder>? createTypeParameters(
{required DeclarationBuilder declarationBuilder,
required List<TypeParameterFragment>? declarationTypeParameterFragments,
required List<SourceNominalParameterBuilder>? typeParameters,
required TypeParameterFactory typeParameterFactory}) {
return typeParameters;
}
}
class EnumConstructorEncodingStrategy implements ConstructorEncodingStrategy {
const EnumConstructorEncodingStrategy();
@override
ConstructorEncoding createEncoding({required bool isExternal}) {
return new RegularConstructorEncoding(
isExternal: isExternal, isEnumConstructor: true);
}
@override
List<FormalParameterBuilder>? createFormals({
required SourceLoader loader,
required List<FormalParameterBuilder>? formals,
required Uri fileUri,
required int fileOffset,
}) {
return [
new FormalParameterBuilder(FormalParameterKind.requiredPositional,
Modifiers.empty, loader.target.intType, "#index", fileOffset,
fileUri: fileUri, hasImmediatelyDeclaredInitializer: false),
new FormalParameterBuilder(FormalParameterKind.requiredPositional,
Modifiers.empty, loader.target.stringType, "#name", fileOffset,
fileUri: fileUri, hasImmediatelyDeclaredInitializer: false),
...?formals
];
}
@override
List<SourceNominalParameterBuilder>? createTypeParameters(
{required DeclarationBuilder declarationBuilder,
required List<TypeParameterFragment>? declarationTypeParameterFragments,
required List<SourceNominalParameterBuilder>? typeParameters,
required TypeParameterFactory typeParameterFactory}) {
return typeParameters;
}
}
class ExtensionConstructorEncodingStrategy
implements ConstructorEncodingStrategy {
const ExtensionConstructorEncodingStrategy();
@override
ConstructorEncoding createEncoding({required bool isExternal}) {
return new ExtensionConstructorEncoding(isExternal: isExternal);
}
@override
List<FormalParameterBuilder>? createFormals(
{required SourceLoader loader,
required List<FormalParameterBuilder>? formals,
required Uri fileUri,
required int fileOffset}) {
return formals;
}
@override
List<SourceNominalParameterBuilder>? createTypeParameters(
{required DeclarationBuilder declarationBuilder,
required List<TypeParameterFragment>? declarationTypeParameterFragments,
required List<SourceNominalParameterBuilder>? typeParameters,
required TypeParameterFactory typeParameterFactory}) {
NominalParameterCopy? nominalVariableCopy =
typeParameterFactory.copyTypeParameters(
oldParameterBuilders: declarationBuilder.typeParameters,
oldParameterFragments: declarationTypeParameterFragments,
kind: TypeParameterKind.extensionSynthesized,
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Allowed);
if (nominalVariableCopy != null) {
if (typeParameters != null) {
// Coverage-ignore-block(suite): Not run.
typeParameters = nominalVariableCopy.newParameterBuilders
..addAll(typeParameters);
} else {
typeParameters = nominalVariableCopy.newParameterBuilders;
}
}
return typeParameters;
}
}
class ExtensionTypeConstructorEncodingStrategy
implements ConstructorEncodingStrategy {
const ExtensionTypeConstructorEncodingStrategy();
@override
List<FormalParameterBuilder>? createFormals(
{required SourceLoader loader,
required List<FormalParameterBuilder>? formals,
required Uri fileUri,
required int fileOffset}) {
return formals;
}
@override
List<SourceNominalParameterBuilder>? createTypeParameters({
required DeclarationBuilder declarationBuilder,
required List<TypeParameterFragment>? declarationTypeParameterFragments,
required List<SourceNominalParameterBuilder>? typeParameters,
required TypeParameterFactory typeParameterFactory,
}) {
NominalParameterCopy? nominalVariableCopy =
typeParameterFactory.copyTypeParameters(
oldParameterBuilders: declarationBuilder.typeParameters,
oldParameterFragments: declarationTypeParameterFragments,
kind: TypeParameterKind.extensionSynthesized,
instanceTypeParameterAccess:
InstanceTypeParameterAccessState.Allowed);
if (nominalVariableCopy != null) {
if (typeParameters != null) {
// Coverage-ignore-block(suite): Not run.
typeParameters = nominalVariableCopy.newParameterBuilders
..addAll(typeParameters);
} else {
typeParameters = nominalVariableCopy.newParameterBuilders;
}
}
return typeParameters;
}
@override
ConstructorEncoding createEncoding({required bool isExternal}) {
return new ExtensionTypeConstructorEncoding(isExternal: isExternal);
}
}