From 8d693e032d5992dfcda044ceb86b522122a7e1eb Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Wed, 26 Mar 2025 01:41:15 -0700 Subject: [PATCH] [cfe] Add MethodDeclaration In preparation for merging SourceMethodBuilder and SyntheticMethodBuilder Change-Id: I1de377596d710070e4516b09aacb85f5cbab49b4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/417800 Commit-Queue: Johnni Winther Reviewed-by: Jens Johansen --- pkg/front_end/lib/src/fragment/fragment.dart | 3 +- pkg/front_end/lib/src/fragment/method.dart | 1185 +---------------- .../fragment/method/body_builder_context.dart | 102 ++ .../lib/src/fragment/method/declaration.dart | 256 ++++ .../lib/src/fragment/method/encoding.dart | 1019 ++++++++++++++ .../src/source/source_builder_factory.dart | 2 +- .../lib/src/source/source_method_builder.dart | 32 +- .../source/type_parameter_scope_builder.dart | 34 +- 8 files changed, 1438 insertions(+), 1195 deletions(-) create mode 100644 pkg/front_end/lib/src/fragment/method/body_builder_context.dart create mode 100644 pkg/front_end/lib/src/fragment/method/declaration.dart create mode 100644 pkg/front_end/lib/src/fragment/method/encoding.dart diff --git a/pkg/front_end/lib/src/fragment/fragment.dart b/pkg/front_end/lib/src/fragment/fragment.dart index d7bbc79b5ac..c0c7f58c402 100644 --- a/pkg/front_end/lib/src/fragment/fragment.dart +++ b/pkg/front_end/lib/src/fragment/fragment.dart @@ -49,9 +49,7 @@ import '../source/source_enum_builder.dart'; import '../source/source_extension_builder.dart'; import '../source/source_extension_type_declaration_builder.dart'; import '../source/source_factory_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_method_builder.dart'; import '../source/source_property_builder.dart'; @@ -66,6 +64,7 @@ import 'constructor/declaration.dart'; import 'factory/declaration.dart'; import 'getter/declaration.dart'; import 'getter/encoding.dart'; +import 'method/declaration.dart'; import 'setter/declaration.dart'; import 'setter/encoding.dart'; diff --git a/pkg/front_end/lib/src/fragment/method.dart b/pkg/front_end/lib/src/fragment/method.dart index e7313896ec7..da004452d8e 100644 --- a/pkg/front_end/lib/src/fragment/method.dart +++ b/pkg/front_end/lib/src/fragment/method.dart @@ -57,7 +57,7 @@ class MethodFragment implements Fragment, FunctionFragment { SourceMethodBuilder? _builder; - late final _MethodEncoding _encoding; + MethodDeclaration? _declaration; MethodFragment({ required this.name, @@ -88,19 +88,21 @@ class MethodFragment implements Fragment, FunctionFragment { return _builder!; } - void setBuilder( - ProblemReporting problemReporting, - SourceMethodBuilder value, - MethodEncodingStrategy encodingStrategy, - List unboundNominalParameters) { + void set builder(SourceMethodBuilder value) { assert(_builder == null, "Builder has already been computed for $this."); _builder = value; - _encoding = encodingStrategy.createMethodEncoding( - value, this, unboundNominalParameters); - typeParameterNameSpace.addTypeParameters( - problemReporting, _encoding.clonedAndDeclaredTypeParameters, - ownerName: name, allowNameConflict: true); - returnType.registerInferredTypeListener(_encoding); + } + + MethodDeclaration get declaration { + assert( + _declaration != null, "Declaration has not been computed for $this."); + return _declaration!; + } + + void set declaration(MethodDeclaration value) { + assert(_declaration == null, + "Declaration has already been computed for $this."); + _declaration = value; } @override @@ -108,83 +110,6 @@ class MethodFragment implements Fragment, FunctionFragment { return new _MethodBodyBuildingContext(this); } - void buildOutlineNode(SourceLibraryBuilder libraryBuilder, - NameScheme nameScheme, BuildNodesCallback f, - {required Reference reference, - required Reference? tearOffReference, - required List? classTypeParameters}) { - _encoding.buildOutlineNode(libraryBuilder, nameScheme, f, - reference: reference, - tearOffReference: tearOffReference, - isAbstractOrExternal: modifiers.isAbstract || modifiers.isExternal, - classTypeParameters: classTypeParameters); - } - - void buildOutlineExpressions( - ClassHierarchy classHierarchy, - SourceLibraryBuilder libraryBuilder, - DeclarationBuilder? declarationBuilder, - Annotatable annotatable, - {required bool isClassInstanceMember, - required bool createFileUriExpression}) { - _encoding.buildOutlineExpressions(classHierarchy, libraryBuilder, - declarationBuilder, createBodyBuilderContext(), annotatable, - isClassInstanceMember: isClassInstanceMember, - createFileUriExpression: createFileUriExpression); - } - - BodyBuilderContext createBodyBuilderContext() { - return new _MethodFragmentBodyBuilderContext( - this, builder.libraryBuilder, builder.declarationBuilder, - isDeclarationInstanceMember: builder.isDeclarationInstanceMember); - } - - void becomeNative(SourceLoader loader) { - _encoding.becomeNative(loader); - } - - int computeDefaultTypes(ComputeDefaultTypeContext context) { - return _encoding.computeDefaultTypes(context); - } - - void ensureTypes( - ClassMembersBuilder membersBuilder, - SourceClassBuilder enclosingClassBuilder, - Set? overrideDependencies) { - if (overrideDependencies != null) { - membersBuilder.inferMethodType(enclosingClassBuilder, _encoding.function, - returnType, declaredFormals, overrideDependencies, - name: name, - fileUri: fileUri, - nameOffset: nameOffset, - nameLength: name.length); - } - _encoding.ensureTypes( - enclosingClassBuilder.libraryBuilder, membersBuilder.hierarchyBuilder); - } - - void checkTypes( - SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { - _encoding.checkTypes(libraryBuilder, typeEnvironment); - } - - void checkVariance( - SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { - _encoding.checkVariance(sourceClassBuilder, typeEnvironment); - } - - Procedure? get readTarget => _encoding.readTarget; - - Procedure get invokeTarget => _encoding.invokeTarget; - - // Coverage-ignore(suite): Not run. - List? get typeParametersForTesting => - _encoding.clonedAndDeclaredTypeParameters; - - // Coverage-ignore(suite): Not run. - List? get formalsForTesting => - _encoding.formalsForTesting; - @override String toString() => '$runtimeType($name,$fileUri,$nameOffset)'; } @@ -206,7 +131,7 @@ class _MethodBodyBuildingContext implements FunctionBodyBuildingContext { @override LocalScope computeFormalParameterScope(LookupScope typeParameterScope) { - return _fragment._encoding.createFormalParameterScope(typeParameterScope); + return _fragment.declaration.createFormalParameterScope(typeParameterScope); } @override @@ -216,7 +141,7 @@ class _MethodBodyBuildingContext implements FunctionBodyBuildingContext { @override BodyBuilderContext createBodyBuilderContext() { - return _fragment.createBodyBuilderContext(); + return _fragment.declaration.createBodyBuilderContext(_fragment.builder); } @override @@ -228,1082 +153,8 @@ class _MethodBodyBuildingContext implements FunctionBodyBuildingContext { @override List? get thisTypeParameters => - _fragment._encoding.thisTypeParameters; + _fragment.declaration.thisTypeParameters; @override - VariableDeclaration? get thisVariable => _fragment._encoding.thisVariable; -} - -sealed class MethodEncodingStrategy { - factory MethodEncodingStrategy(DeclarationBuilder? declarationBuilder, - {required bool isInstanceMember}) { - switch (declarationBuilder) { - case ExtensionBuilder(): - if (isInstanceMember) { - return const _ExtensionInstanceMethodStrategy(); - } else { - return const _ExtensionStaticMethodStrategy(); - } - case ExtensionTypeDeclarationBuilder(): - if (isInstanceMember) { - return const _ExtensionTypeInstanceMethodStrategy(); - } else { - return const _ExtensionTypeStaticMethodStrategy(); - } - case null: - case ClassBuilder(): - return const _RegularMethodStrategy(); - } - } - - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters); -} - -class _RegularMethodStrategy implements MethodEncodingStrategy { - const _RegularMethodStrategy(); - - @override - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters) { - return fragment.isOperator - ? new _RegularOperatorEncoding(fragment) - : new _RegularMethodEncoding(fragment); - } -} - -class _ExtensionInstanceMethodStrategy implements MethodEncodingStrategy { - const _ExtensionInstanceMethodStrategy(); - - @override - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters) { - ExtensionBuilder declarationBuilder = - builder.declarationBuilder as ExtensionBuilder; - SynthesizedExtensionSignature signature = new SynthesizedExtensionSignature( - declarationBuilder: declarationBuilder, - extensionTypeParameterFragments: - fragment.enclosingDeclaration!.typeParameters, - unboundNominalParameters: unboundNominalParameters, - onTypeBuilder: declarationBuilder.onType, - fileUri: fragment.fileUri, - fileOffset: fragment.nameOffset); - return fragment.isOperator - ? new _ExtensionInstanceOperatorEncoding(fragment, - signature.clonedDeclarationTypeParameters, signature.thisFormal) - : new _ExtensionInstanceMethodEncoding(fragment, - signature.clonedDeclarationTypeParameters, signature.thisFormal); - } -} - -class _ExtensionStaticMethodStrategy implements MethodEncodingStrategy { - const _ExtensionStaticMethodStrategy(); - - @override - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters) { - return new _ExtensionStaticMethodEncoding(fragment); - } -} - -class _ExtensionTypeInstanceMethodStrategy implements MethodEncodingStrategy { - const _ExtensionTypeInstanceMethodStrategy(); - - @override - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters) { - ExtensionTypeDeclarationBuilder declarationBuilder = - builder.declarationBuilder as ExtensionTypeDeclarationBuilder; - SynthesizedExtensionTypeSignature signature = - new SynthesizedExtensionTypeSignature( - extensionTypeDeclarationBuilder: declarationBuilder, - extensionTypeTypeParameters: - fragment.enclosingDeclaration!.typeParameters, - unboundNominalParameters: unboundNominalParameters, - fileUri: fragment.fileUri, - fileOffset: fragment.nameOffset); - return fragment.isOperator - ? new _ExtensionTypeInstanceOperatorEncoding(fragment, - signature.clonedDeclarationTypeParameters, signature.thisFormal) - : new _ExtensionTypeInstanceMethodEncoding(fragment, - signature.clonedDeclarationTypeParameters, signature.thisFormal); - } -} - -class _ExtensionTypeStaticMethodStrategy implements MethodEncodingStrategy { - const _ExtensionTypeStaticMethodStrategy(); - - @override - _MethodEncoding createMethodEncoding( - SourceMethodBuilder builder, - MethodFragment fragment, - List unboundNominalParameters) { - return new _ExtensionTypeStaticMethodEncoding(fragment); - } -} - -sealed class _MethodEncoding implements InferredTypeListener { - VariableDeclaration? get thisVariable; - List? get thisTypeParameters; - FunctionNode get function; - Procedure? get readTarget; - Procedure get invokeTarget; - - void buildOutlineNode(SourceLibraryBuilder libraryBuilder, - NameScheme nameScheme, BuildNodesCallback f, - {required Reference reference, - required Reference? tearOffReference, - required bool isAbstractOrExternal, - required List? classTypeParameters}); - - void buildOutlineExpressions( - ClassHierarchy classHierarchy, - SourceLibraryBuilder libraryBuilder, - DeclarationBuilder? declarationBuilder, - BodyBuilderContext bodyBuilderContext, - Annotatable annotatable, - {required bool isClassInstanceMember, - required bool createFileUriExpression}); - - LocalScope createFormalParameterScope(LookupScope typeParameterScope); - - int computeDefaultTypes(ComputeDefaultTypeContext context); - - void ensureTypes( - SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy); - - void becomeNative(SourceLoader loader); - - List? get formals; - - VariableDeclaration getFormalParameter(int index); - - VariableDeclaration? getTearOffParameter(int index); - - void checkTypes( - SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment); - - void checkVariance( - SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment); - - List? get clonedAndDeclaredTypeParameters; - - List? get formalsForTesting; -} - -mixin _DirectMethodEncodingMixin implements _MethodEncoding { - MethodFragment get _fragment; - - Procedure? _procedure; - - @override - VariableDeclaration? get thisVariable => null; - - @override - List? get thisTypeParameters => null; - - BuiltMemberKind get _builtMemberKind; - - bool get _isExtensionMember; - - bool get _isExtensionTypeMember; - - ProcedureKind get _procedureKind; - - @override - void buildOutlineNode(SourceLibraryBuilder libraryBuilder, - NameScheme nameScheme, BuildNodesCallback f, - {required Reference reference, - required Reference? tearOffReference, - required bool isAbstractOrExternal, - List? classTypeParameters}) { - FunctionNode function = new FunctionNode( - isAbstractOrExternal ? null : new EmptyStatement(), - asyncMarker: _fragment.asyncModifier) - ..fileOffset = _fragment.formalsOffset - ..fileEndOffset = _fragment.endOffset; - buildTypeParametersAndFormals(libraryBuilder, function, - _fragment.declaredTypeParameters?.builders, _fragment.declaredFormals, - classTypeParameters: classTypeParameters, supportsTypeParameters: true); - if (_fragment.returnType is! InferableTypeBuilder) { - function.returnType = - _fragment.returnType.build(libraryBuilder, TypeUse.returnType); - } - - MemberName memberName = - nameScheme.getProcedureMemberName(_procedureKind, _fragment.name); - Procedure procedure = _procedure = new Procedure( - memberName.name, _procedureKind, function, - reference: reference, fileUri: _fragment.fileUri) - ..fileStartOffset = _fragment.startOffset - ..fileOffset = _fragment.nameOffset - ..fileEndOffset = _fragment.endOffset - ..isAbstract = _fragment.modifiers.isAbstract - ..isExternal = _fragment.modifiers.isExternal - ..isConst = _fragment.modifiers.isConst - ..isStatic = _fragment.modifiers.isStatic - ..isExtensionMember = _isExtensionMember - ..isExtensionTypeMember = _isExtensionTypeMember; - memberName.attachMember(procedure); - - f(kind: _builtMemberKind, member: procedure); - } - - @override - void buildOutlineExpressions( - ClassHierarchy classHierarchy, - SourceLibraryBuilder libraryBuilder, - DeclarationBuilder? declarationBuilder, - BodyBuilderContext bodyBuilderContext, - Annotatable annotatable, - {required bool isClassInstanceMember, - required bool createFileUriExpression}) { - buildMetadataForOutlineExpressions(libraryBuilder, _fragment.enclosingScope, - bodyBuilderContext, annotatable, _fragment.metadata, - fileUri: _fragment.fileUri, - createFileUriExpression: createFileUriExpression); - buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, - bodyBuilderContext, _fragment.declaredTypeParameters?.builders); - buildFormalsForOutlineExpressions( - libraryBuilder, declarationBuilder, _fragment.declaredFormals, - scope: _fragment.typeParameterScope, - isClassInstanceMember: isClassInstanceMember); - } - - @override - FunctionNode get function => _procedure!.function; - - @override - Procedure get invokeTarget { - assert(_procedure != null, "No procedure computed for $_fragment yet."); - return _procedure!; - } - - @override - LocalScope createFormalParameterScope(LookupScope parent) { - List? formals = _fragment.declaredFormals; - if (formals == null) { - return new FormalParameterScope(parent: parent); - } - Map local = {}; - for (FormalParameterBuilder formal in formals) { - if (formal.isWildcard) { - continue; - } - local[formal.name] = formal; - } - return new FormalParameterScope(local: local, parent: parent); - } - - @override - int computeDefaultTypes(ComputeDefaultTypeContext context) { - bool hasErrors = context.reportSimplicityIssuesForTypeParameters( - _fragment.declaredTypeParameters?.builders); - context.reportGenericFunctionTypesForFormals(_fragment.declaredFormals); - if (_fragment.returnType is! OmittedTypeBuilder) { - hasErrors |= - context.reportInboundReferenceIssuesForType(_fragment.returnType); - context.recursivelyReportGenericFunctionTypesAsBoundsForType( - _fragment.returnType); - } - return context.computeDefaultTypesForVariables( - _fragment.declaredTypeParameters?.builders, - inErrorRecovery: hasErrors); - } - - @override - void ensureTypes( - SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy) { - _fragment.returnType - .build(libraryBuilder, TypeUse.returnType, hierarchy: hierarchy); - List? declaredFormals = _fragment.declaredFormals; - if (declaredFormals != null) { - for (FormalParameterBuilder formal in declaredFormals) { - formal.type - .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); - } - } - } - - @override - void onInferredType(DartType type) { - function.returnType = type; - } - - @override - void becomeNative(SourceLoader loader) { - loader.addNativeAnnotation(_procedure!, _fragment.nativeMethodName!); - } - - @override - List? get formals => _fragment.declaredFormals; - - @override - VariableDeclaration getFormalParameter(int index) => - _fragment.declaredFormals![index].variable!; - - @override - VariableDeclaration? getTearOffParameter(int index) => null; - - @override - void checkTypes( - SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { - List? typeParameters = - _fragment.declaredTypeParameters?.builders; - if (typeParameters != null && typeParameters.isNotEmpty) { - libraryBuilder.checkTypeParameterDependencies(typeParameters); - } - libraryBuilder.checkInitializersInFormals( - _fragment.declaredFormals, typeEnvironment, - isAbstract: _fragment.modifiers.isAbstract, - isExternal: _fragment.modifiers.isExternal); - } - - @override - void checkVariance( - SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { - sourceClassBuilder.checkVarianceInTypeParameters( - typeEnvironment, _fragment.declaredTypeParameters?.builders); - sourceClassBuilder.checkVarianceInFormals( - typeEnvironment, _fragment.declaredFormals); - sourceClassBuilder.checkVarianceInReturnType( - typeEnvironment, function.returnType, - fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); - } - - @override - List? get clonedAndDeclaredTypeParameters => - _fragment.declaredTypeParameters?.builders; - - @override - // Coverage-ignore(suite): Not run. - List? get formalsForTesting => - _fragment.declaredFormals; -} - -class _RegularOperatorEncoding extends _MethodEncoding - with _DirectMethodEncodingMixin { - @override - final MethodFragment _fragment; - - _RegularOperatorEncoding(this._fragment) : assert(_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.Method; - - @override - ProcedureKind get _procedureKind => ProcedureKind.Operator; - - @override - bool get _isExtensionMember => false; - - @override - bool get _isExtensionTypeMember => false; - - @override - Procedure? get readTarget => null; -} - -class _RegularMethodEncoding extends _MethodEncoding - with _DirectMethodEncodingMixin { - @override - final MethodFragment _fragment; - - _RegularMethodEncoding(this._fragment) : assert(!_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.Method; - - @override - ProcedureKind get _procedureKind => ProcedureKind.Method; - - @override - bool get _isExtensionMember => false; - - @override - bool get _isExtensionTypeMember => false; - - @override - Procedure? get readTarget => invokeTarget; -} - -class _ExtensionStaticMethodEncoding extends _MethodEncoding - with _DirectMethodEncodingMixin { - @override - final MethodFragment _fragment; - - _ExtensionStaticMethodEncoding(this._fragment) - : assert(!_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionMethod; - - @override - ProcedureKind get _procedureKind => ProcedureKind.Method; - - @override - bool get _isExtensionMember => true; - - @override - bool get _isExtensionTypeMember => false; - - @override - Procedure? get readTarget => invokeTarget; -} - -class _ExtensionTypeStaticMethodEncoding extends _MethodEncoding - with _DirectMethodEncodingMixin { - @override - final MethodFragment _fragment; - - _ExtensionTypeStaticMethodEncoding(this._fragment) - : assert(!_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeMethod; - - @override - ProcedureKind get _procedureKind => ProcedureKind.Method; - - @override - bool get _isExtensionMember => false; - - @override - bool get _isExtensionTypeMember => true; - - @override - Procedure? get readTarget => invokeTarget; -} - -mixin _ExtensionInstanceMethodEncodingMixin implements _MethodEncoding { - MethodFragment get _fragment; - - Procedure? _procedure; - Procedure? _extensionTearOff; - - List? get _clonedDeclarationTypeParameters; - - FormalParameterBuilder get _thisFormal; - - @override - late final List? thisTypeParameters = - _clonedDeclarationTypeParameters != null - ? function.typeParameters - // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both - // required and unnecessary. - // ignore: unnecessary_non_null_assertion - .sublist(0, _clonedDeclarationTypeParameters!.length) - : null; - - @override - VariableDeclaration? get thisVariable => _thisFormal.variable!; - - BuiltMemberKind get _builtMemberKind; - - bool get _isExtensionMember; - - bool get _isExtensionTypeMember; - - bool get _isOperator; - - @override - FunctionNode get function => _procedure!.function; - - @override - Procedure get invokeTarget => _procedure!; - - @override - Procedure? get readTarget => _extensionTearOff; - - /// If this is an extension instance method then - /// [_extensionTearOffParameterMap] holds a map from the parameters of - /// the methods to the parameter of the closure returned in the tear-off. - /// - /// This map is used to set the default values on the closure parameters when - /// these have been built. - Map? _extensionTearOffParameterMap; - - /// Creates a top level function that creates a tear off of an extension - /// instance method. - /// - /// For this declaration - /// - /// extension E on A { - /// X method(S s, Y y) {} - /// } - /// - /// we create the top level function - /// - /// X E|method(A #this, S s, Y y) {} - /// - /// and the tear off function - /// - /// X Function(S, Y) E|get#method(A #this) { - /// return (S s, Y y) => E|method(#this, s, y); - /// } - /// - Procedure _buildExtensionTearOff( - Procedure procedure, NameScheme nameScheme, Reference? tearOffReference) { - _extensionTearOffParameterMap = {}; - - int fileStartOffset = _fragment.startOffset; - int fileOffset = _fragment.nameOffset; - int fileEndOffset = _fragment.endOffset; - - int extensionTypeParameterCount = - _clonedDeclarationTypeParameters?.length ?? 0; - - List typeParameters = []; - - Map substitutionMap = {}; - List typeArguments = []; - for (TypeParameter typeParameter in procedure.function.typeParameters) { - TypeParameter newTypeParameter = new TypeParameter(typeParameter.name); - typeParameters.add(newTypeParameter); - typeArguments.add(substitutionMap[typeParameter] = new TypeParameterType( - newTypeParameter, typeParameter.computeNullabilityFromBound())); - } - - List tearOffTypeParameters = []; - List closureTypeParameters = []; - Substitution substitution = Substitution.fromMap(substitutionMap); - for (int index = 0; index < typeParameters.length; index++) { - TypeParameter newTypeParameter = typeParameters[index]; - newTypeParameter.bound = substitution - .substituteType(procedure.function.typeParameters[index].bound); - newTypeParameter.defaultType = - procedure.function.typeParameters[index].defaultType; - if (index < extensionTypeParameterCount) { - tearOffTypeParameters.add(newTypeParameter); - } else { - closureTypeParameters.add(newTypeParameter); - } - } - - VariableDeclaration copyParameter( - VariableDeclaration parameter, DartType type) { - VariableDeclaration newParameter = new VariableDeclaration(parameter.name, - type: type, - isFinal: parameter.isFinal, - isLowered: parameter.isLowered, - isRequired: parameter.isRequired) - ..fileOffset = parameter.fileOffset; - _extensionTearOffParameterMap![parameter] = newParameter; - return newParameter; - } - - VariableDeclaration extensionThis = copyParameter( - procedure.function.positionalParameters.first, - substitution.substituteType( - procedure.function.positionalParameters.first.type)); - - DartType closureReturnType = - substitution.substituteType(procedure.function.returnType); - List closurePositionalParameters = []; - List closurePositionalArguments = []; - - for (int position = 0; - position < procedure.function.positionalParameters.length; - position++) { - VariableDeclaration parameter = - procedure.function.positionalParameters[position]; - if (position == 0) { - /// Pass `this` as a captured variable. - closurePositionalArguments - .add(new VariableGet(extensionThis)..fileOffset = fileOffset); - } else { - DartType type = substitution.substituteType(parameter.type); - VariableDeclaration newParameter = copyParameter(parameter, type); - closurePositionalParameters.add(newParameter); - closurePositionalArguments - .add(new VariableGet(newParameter)..fileOffset = fileOffset); - } - } - List closureNamedParameters = []; - List closureNamedArguments = []; - for (VariableDeclaration parameter in procedure.function.namedParameters) { - DartType type = substitution.substituteType(parameter.type); - VariableDeclaration newParameter = copyParameter(parameter, type); - closureNamedParameters.add(newParameter); - closureNamedArguments.add(new NamedExpression(parameter.name!, - new VariableGet(newParameter)..fileOffset = fileOffset)); - } - - Statement closureBody = new ReturnStatement( - new StaticInvocation( - procedure, - new Arguments(closurePositionalArguments, - types: typeArguments, named: closureNamedArguments)) - // We need to use the fileStartOffset on the StaticInvocation to - // avoid a possible "fake coverage miss" on the name of the - // extension method. - ..fileOffset = fileStartOffset) - ..fileOffset = fileOffset; - - FunctionExpression closure = new FunctionExpression( - new FunctionNode(closureBody, - typeParameters: closureTypeParameters, - positionalParameters: closurePositionalParameters, - namedParameters: closureNamedParameters, - requiredParameterCount: - procedure.function.requiredParameterCount - 1, - returnType: closureReturnType) - ..fileOffset = fileOffset - ..fileEndOffset = fileEndOffset) - // We need to use the fileStartOffset on the FunctionExpression to - // avoid a possible "fake coverage miss" on the name of the - // extension method. - ..fileOffset = fileStartOffset; - - FunctionNode function = new FunctionNode( - new ReturnStatement(closure)..fileOffset = fileOffset, - typeParameters: tearOffTypeParameters, - positionalParameters: [extensionThis], - requiredParameterCount: 1, - returnType: - closure.function.computeFunctionType(Nullability.nonNullable)) - ..fileOffset = fileOffset - ..fileEndOffset = fileEndOffset; - - MemberName tearOffName = - nameScheme.getProcedureMemberName(ProcedureKind.Getter, _fragment.name); - Procedure tearOff = new Procedure( - tearOffName.name, ProcedureKind.Method, function, - isStatic: true, - isExtensionMember: _isExtensionMember, - isExtensionTypeMember: _isExtensionTypeMember, - reference: tearOffReference, - fileUri: _fragment.fileUri) - ..fileUri = _fragment.fileUri - ..fileOffset = fileOffset - ..fileStartOffset = _fragment.startOffset - ..fileEndOffset = fileEndOffset; - tearOffName.attachMember(tearOff); - return tearOff; - } - - @override - void buildOutlineNode(SourceLibraryBuilder libraryBuilder, - NameScheme nameScheme, BuildNodesCallback f, - {required Reference reference, - required Reference? tearOffReference, - required bool isAbstractOrExternal, - required List? classTypeParameters}) { - List? typeParameters; - if (_clonedDeclarationTypeParameters != null) { - typeParameters = []; - // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both required - // and unnecessary. - // ignore: unnecessary_non_null_assertion - for (NominalParameterBuilder t in _clonedDeclarationTypeParameters!) { - typeParameters.add(t.parameter); - } - } - FunctionNode function = new FunctionNode( - isAbstractOrExternal ? null : new EmptyStatement(), - typeParameters: typeParameters, - positionalParameters: [_thisFormal.build(libraryBuilder)], - asyncMarker: _fragment.asyncModifier) - ..fileOffset = _fragment.formalsOffset - ..fileEndOffset = _fragment.endOffset; - buildTypeParametersAndFormals(libraryBuilder, function, - _fragment.declaredTypeParameters?.builders, _fragment.declaredFormals, - classTypeParameters: classTypeParameters, supportsTypeParameters: true); - if (_fragment.returnType is! InferableTypeBuilder) { - function.returnType = - _fragment.returnType.build(libraryBuilder, TypeUse.returnType); - } - - MemberName memberName = - nameScheme.getProcedureMemberName(ProcedureKind.Method, _fragment.name); - Procedure procedure = _procedure = new Procedure( - memberName.name, ProcedureKind.Method, function, - reference: reference, fileUri: _fragment.fileUri) - ..fileStartOffset = _fragment.startOffset - ..fileOffset = _fragment.nameOffset - ..fileEndOffset = _fragment.endOffset - ..isAbstract = _fragment.modifiers.isAbstract - ..isExternal = _fragment.modifiers.isExternal - ..isConst = _fragment.modifiers.isConst - ..isStatic = true - ..isExtensionMember = _isExtensionMember - ..isExtensionTypeMember = _isExtensionTypeMember; - memberName.attachMember(procedure); - - if (!_isOperator) { - _extensionTearOff = - _buildExtensionTearOff(procedure, nameScheme, tearOffReference); - } - - f(kind: _builtMemberKind, member: procedure, tearOff: _extensionTearOff); - } - - @override - void buildOutlineExpressions( - ClassHierarchy classHierarchy, - SourceLibraryBuilder libraryBuilder, - DeclarationBuilder? declarationBuilder, - BodyBuilderContext bodyBuilderContext, - Annotatable annotatable, - {required bool isClassInstanceMember, - required bool createFileUriExpression}) { - buildMetadataForOutlineExpressions(libraryBuilder, _fragment.enclosingScope, - bodyBuilderContext, annotatable, _fragment.metadata, - fileUri: _fragment.fileUri, - createFileUriExpression: createFileUriExpression); - - buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, - bodyBuilderContext, _fragment.declaredTypeParameters?.builders); - buildFormalsForOutlineExpressions( - libraryBuilder, declarationBuilder, _fragment.declaredFormals, - scope: _fragment.typeParameterScope, - isClassInstanceMember: isClassInstanceMember); - - buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, - bodyBuilderContext, _clonedDeclarationTypeParameters); - buildFormalForOutlineExpressions( - libraryBuilder, declarationBuilder, _thisFormal, - scope: _fragment.typeParameterScope, - isClassInstanceMember: isClassInstanceMember); - } - - @override - // Coverage-ignore(suite): Not run. - void becomeNative(SourceLoader loader) { - loader.addNativeAnnotation(_procedure!, _fragment.nativeMethodName!); - } - - @override - void checkTypes( - SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { - List? typeParameters = - _fragment.declaredTypeParameters?.builders; - if (typeParameters != null && typeParameters.isNotEmpty) { - libraryBuilder.checkTypeParameterDependencies(typeParameters); - } - libraryBuilder.checkInitializersInFormals( - _fragment.declaredFormals, typeEnvironment, - isAbstract: _fragment.modifiers.isAbstract, - isExternal: _fragment.modifiers.isExternal); - } - - @override - // Coverage-ignore(suite): Not run. - void checkVariance( - SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { - sourceClassBuilder.checkVarianceInTypeParameters( - typeEnvironment, _fragment.declaredTypeParameters?.builders); - sourceClassBuilder.checkVarianceInFormals( - typeEnvironment, _fragment.declaredFormals); - sourceClassBuilder.checkVarianceInReturnType( - typeEnvironment, function.returnType, - fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); - } - - @override - LocalScope createFormalParameterScope(LookupScope parent) { - Map local = {}; - - assert(!_thisFormal.isWildcard); - local[_thisFormal.name] = _thisFormal; - - List? formals = _fragment.declaredFormals; - if (formals != null) { - for (FormalParameterBuilder formal in formals) { - if (formal.isWildcard) { - continue; - } - local[formal.name] = formal; - } - } - return new FormalParameterScope(local: local, parent: parent); - } - - @override - int computeDefaultTypes(ComputeDefaultTypeContext context) { - bool hasErrors = context.reportSimplicityIssuesForTypeParameters( - _fragment.declaredTypeParameters?.builders); - context.reportGenericFunctionTypesForFormals(_fragment.declaredFormals); - if (_fragment.returnType is! OmittedTypeBuilder) { - hasErrors |= - context.reportInboundReferenceIssuesForType(_fragment.returnType); - context.recursivelyReportGenericFunctionTypesAsBoundsForType( - _fragment.returnType); - } - if (_clonedDeclarationTypeParameters != null && - _fragment.declaredTypeParameters != null) { - // We need to compute all default types together since they might be - // interdependent. - return context.computeDefaultTypesForVariables([ - // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both - // required and unnecessary. - // ignore: unnecessary_non_null_assertion - ..._clonedDeclarationTypeParameters!, - ..._fragment.declaredTypeParameters!.builders - ], inErrorRecovery: hasErrors); - } else if (_clonedDeclarationTypeParameters != null) { - return context.computeDefaultTypesForVariables( - _clonedDeclarationTypeParameters, - inErrorRecovery: hasErrors); - } else { - return context.computeDefaultTypesForVariables( - _fragment.declaredTypeParameters?.builders, - inErrorRecovery: hasErrors); - } - } - - @override - // Coverage-ignore(suite): Not run. - void ensureTypes( - SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy) { - _fragment.returnType - .build(libraryBuilder, TypeUse.fieldType, hierarchy: hierarchy); - _thisFormal.type - .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); - List? declaredFormals = _fragment.declaredFormals; - if (declaredFormals != null) { - for (FormalParameterBuilder formal in declaredFormals) { - formal.type - .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); - } - } - } - - @override - void onInferredType(DartType type) { - function.returnType = type; - } - - @override - List? get formals => - [_thisFormal, ...?_fragment.declaredFormals]; - - @override - VariableDeclaration getFormalParameter(int index) => - _fragment.declaredFormals![index].variable!; - - @override - VariableDeclaration? getTearOffParameter(int index) { - return _extensionTearOffParameterMap?[getFormalParameter(index)]; - } - - @override - List? get clonedAndDeclaredTypeParameters => - _clonedDeclarationTypeParameters != null || - _fragment.declaredTypeParameters != null - ? [ - ...?_clonedDeclarationTypeParameters, - ...?_fragment.declaredTypeParameters?.builders - ] - : null; - - @override - // Coverage-ignore(suite): Not run. - List? get formalsForTesting => - [_thisFormal, ...?_fragment.declaredFormals]; -} - -class _ExtensionInstanceOperatorEncoding extends _MethodEncoding - with _ExtensionInstanceMethodEncodingMixin { - @override - final MethodFragment _fragment; - - @override - final List? _clonedDeclarationTypeParameters; - - @override - final FormalParameterBuilder _thisFormal; - - _ExtensionInstanceOperatorEncoding( - this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) - : assert(_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionOperator; - - @override - bool get _isExtensionMember => true; - - @override - bool get _isExtensionTypeMember => false; - - @override - bool get _isOperator => true; -} - -class _ExtensionInstanceMethodEncoding extends _MethodEncoding - with _ExtensionInstanceMethodEncodingMixin { - @override - final MethodFragment _fragment; - - @override - final List? _clonedDeclarationTypeParameters; - - @override - final FormalParameterBuilder _thisFormal; - - _ExtensionInstanceMethodEncoding( - this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) - : assert(!_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionMethod; - - @override - bool get _isExtensionMember => true; - - @override - bool get _isExtensionTypeMember => false; - - @override - bool get _isOperator => false; -} - -class _ExtensionTypeInstanceOperatorEncoding extends _MethodEncoding - with _ExtensionInstanceMethodEncodingMixin { - @override - final MethodFragment _fragment; - - @override - final List? _clonedDeclarationTypeParameters; - - @override - final FormalParameterBuilder _thisFormal; - - _ExtensionTypeInstanceOperatorEncoding( - this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) - : assert(_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeOperator; - - @override - bool get _isExtensionMember => false; - - @override - bool get _isExtensionTypeMember => true; - - @override - bool get _isOperator => true; -} - -class _ExtensionTypeInstanceMethodEncoding extends _MethodEncoding - with _ExtensionInstanceMethodEncodingMixin { - @override - final MethodFragment _fragment; - - @override - final List? _clonedDeclarationTypeParameters; - - @override - final FormalParameterBuilder _thisFormal; - - _ExtensionTypeInstanceMethodEncoding( - this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) - : assert(!_fragment.isOperator); - - @override - BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeMethod; - - @override - bool get _isExtensionMember => false; - - @override - bool get _isExtensionTypeMember => true; - - @override - bool get _isOperator => false; -} - -class _MethodFragmentBodyBuilderContext extends BodyBuilderContext { - final MethodFragment _fragment; - - _MethodFragmentBodyBuilderContext( - this._fragment, - SourceLibraryBuilder libraryBuilder, - DeclarationBuilder? declarationBuilder, - {required bool isDeclarationInstanceMember}) - : super(libraryBuilder, declarationBuilder, - isDeclarationInstanceMember: isDeclarationInstanceMember); - - @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 - FunctionNode get function => _fragment._encoding.function; - - @override - void setAsyncModifier(AsyncMarker asyncModifier) { - assert( - asyncModifier == _fragment.asyncModifier, - "Unexpected change in async modifier on $_fragment from " - "${_fragment.asyncModifier} to $asyncModifier."); - } - - @override - bool get isExternalFunction => _fragment.modifiers.isExternal; - - @override - int get memberNameOffset => _fragment.nameOffset; - - @override - int get memberNameLength => _fragment.name.length; - - @override - DartType get returnTypeContext { - final bool isReturnTypeUndeclared = - _fragment.returnType is OmittedTypeBuilder && - function.returnType is DynamicType; - return isReturnTypeUndeclared ? const UnknownType() : function.returnType; - } - - @override - TypeBuilder get returnType => _fragment.returnType; - - @override - void registerFunctionBody(Statement body) { - function.body = body..parent = function; - } - - @override - void registerSuperCall() { - // TODO(johnniwinther): This should be set on the member built from this - // fragment and copied to the origin if necessary. - _fragment.builder.invokeTarget.transformerFlags |= - TransformerFlag.superCalls; - } - - @override - List? get formals => _fragment._encoding.formals; - - @override - VariableDeclaration getFormalParameter(int index) => - _fragment._encoding.getFormalParameter(index); - - @override - VariableDeclaration? getTearOffParameter(int index) => - _fragment._encoding.getTearOffParameter(index); - - @override - // Coverage-ignore(suite): Not run. - AugmentSuperTarget? get augmentSuperTarget { - if (_fragment.builder.isAugmentation) { - return _fragment.builder.augmentSuperTarget; - } - return null; - } + VariableDeclaration? get thisVariable => _fragment.declaration.thisVariable; } diff --git a/pkg/front_end/lib/src/fragment/method/body_builder_context.dart b/pkg/front_end/lib/src/fragment/method/body_builder_context.dart new file mode 100644 index 00000000000..40b4b826a57 --- /dev/null +++ b/pkg/front_end/lib/src/fragment/method/body_builder_context.dart @@ -0,0 +1,102 @@ +// 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:kernel/ast.dart'; +import 'package:kernel/transformations/flags.dart'; + +import '../../base/local_scope.dart'; +import '../../builder/declaration_builders.dart'; +import '../../builder/formal_parameter_builder.dart'; +import '../../builder/type_builder.dart'; +import '../../kernel/body_builder_context.dart'; +import '../../source/source_library_builder.dart'; +import '../../source/source_member_builder.dart'; +import '../../type_inference/type_schema.dart'; +import '../fragment.dart'; +import 'declaration.dart'; + +class MethodFragmentBodyBuilderContext extends BodyBuilderContext { + final MethodFragment _fragment; + final MethodDeclaration _declaration; + + MethodFragmentBodyBuilderContext( + this._fragment, + this._declaration, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + {required bool isDeclarationInstanceMember}) + : super(libraryBuilder, declarationBuilder, + isDeclarationInstanceMember: isDeclarationInstanceMember); + + @override + // Coverage-ignore(suite): Not run. + AugmentSuperTarget? get augmentSuperTarget { + if (_fragment.builder.isAugmentation) { + return _fragment.builder.augmentSuperTarget; + } + return null; + } + + @override + List? get formals => _declaration.formals; + + @override + FunctionNode get function => _declaration.function; + + @override + bool get isExternalFunction => _fragment.modifiers.isExternal; + + @override + int get memberNameLength => _fragment.name.length; + + @override + int get memberNameOffset => _fragment.nameOffset; + + @override + TypeBuilder get returnType => _fragment.returnType; + + @override + DartType get returnTypeContext { + final bool isReturnTypeUndeclared = + _fragment.returnType is OmittedTypeBuilder && + function.returnType is DynamicType; + return isReturnTypeUndeclared ? const UnknownType() : function.returnType; + } + + @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 + VariableDeclaration getFormalParameter(int index) => + _declaration.getFormalParameter(index); + + @override + VariableDeclaration? getTearOffParameter(int index) => + _declaration.getTearOffParameter(index); + + @override + void registerFunctionBody(Statement body) { + function.body = body..parent = function; + } + + @override + void registerSuperCall() { + // TODO(johnniwinther): This should be set on the member built from this + // fragment and copied to the origin if necessary. + _fragment.builder.invokeTarget.transformerFlags |= + TransformerFlag.superCalls; + } + + @override + void setAsyncModifier(AsyncMarker asyncModifier) { + assert( + asyncModifier == _fragment.asyncModifier, + "Unexpected change in async modifier on $_fragment from " + "${_fragment.asyncModifier} to $asyncModifier."); + } +} diff --git a/pkg/front_end/lib/src/fragment/method/declaration.dart b/pkg/front_end/lib/src/fragment/method/declaration.dart new file mode 100644 index 00000000000..3279ea7a666 --- /dev/null +++ b/pkg/front_end/lib/src/fragment/method/declaration.dart @@ -0,0 +1,256 @@ +// 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:kernel/ast.dart'; +import 'package:kernel/class_hierarchy.dart'; +import 'package:kernel/type_environment.dart'; + +import '../../base/local_scope.dart'; +import '../../base/messages.dart'; +import '../../base/scope.dart'; +import '../../builder/declaration_builders.dart'; +import '../../builder/formal_parameter_builder.dart'; +import '../../builder/metadata_builder.dart'; +import '../../builder/type_builder.dart'; +import '../../kernel/body_builder_context.dart'; +import '../../kernel/hierarchy/class_member.dart'; +import '../../kernel/hierarchy/members_builder.dart'; +import '../../kernel/type_algorithms.dart'; +import '../../source/name_scheme.dart'; +import '../../source/source_class_builder.dart'; +import '../../source/source_library_builder.dart'; +import '../../source/source_loader.dart'; +import '../../source/source_member_builder.dart'; +import '../../source/source_method_builder.dart'; +import '../fragment.dart'; +import 'body_builder_context.dart'; +import 'encoding.dart'; + +abstract class MethodDeclaration { + Uri get fileUri; + + List? get formals; + + List? get formalsForTesting; + + FunctionNode get function; + + Procedure get invokeTarget; + + bool get isOperator; + + List? get metadata; + + Procedure? get readTarget; + + TypeBuilder get returnType; + + List? get thisTypeParameters; + + VariableDeclaration? get thisVariable; + + List? get typeParametersForTesting; + + void becomeNative(SourceLoader loader); + + void buildOutlineExpressions( + ClassHierarchy classHierarchy, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + SourceMethodBuilder methodBuilder, + Annotatable annotatable, + {required bool isClassInstanceMember, + required bool createFileUriExpression}); + + void buildOutlineNode(SourceLibraryBuilder libraryBuilder, + NameScheme nameScheme, BuildNodesCallback f, + {required Reference reference, + required Reference? tearOffReference, + required List? classTypeParameters}); + + void checkTypes( + SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment); + + void checkVariance( + SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment); + + int computeDefaultTypes(ComputeDefaultTypeContext context); + + BodyBuilderContext createBodyBuilderContext(SourceMethodBuilder builder); + + void createEncoding( + ProblemReporting problemReporting, + SourceMethodBuilder builder, + MethodEncodingStrategy encodingStrategy, + List unboundNominalParameters); + + LocalScope createFormalParameterScope(LookupScope typeParameterScope); + + void ensureTypes( + ClassMembersBuilder membersBuilder, + SourceClassBuilder enclosingClassBuilder, + Set? overrideDependencies); + + VariableDeclaration getFormalParameter(int index); + + VariableDeclaration? getTearOffParameter(int index); +} + +class MethodDeclarationImpl implements MethodDeclaration { + final MethodFragment _fragment; + late final MethodEncoding _encoding; + + MethodDeclarationImpl(this._fragment) { + _fragment.declaration = this; + } + + @override + Uri get fileUri => _fragment.fileUri; + + @override + // TODO: implement formals + List? get formals => _encoding.formals; + + // Coverage-ignore(suite): Not run. + @override + List? get formalsForTesting => + _encoding.formalsForTesting; + + @override + FunctionNode get function => _encoding.function; + + @override + Procedure get invokeTarget => _encoding.invokeTarget; + + @override + bool get isOperator => _fragment.isOperator; + + @override + List? get metadata => _fragment.metadata; + + @override + Procedure? get readTarget => _encoding.readTarget; + + @override + TypeBuilder get returnType => _fragment.returnType; + + @override + List? get thisTypeParameters => _encoding.thisTypeParameters; + + @override + VariableDeclaration? get thisVariable => _encoding.thisVariable; + + // Coverage-ignore(suite): Not run. + @override + List? get typeParametersForTesting => + _encoding.clonedAndDeclaredTypeParameters; + + @override + void becomeNative(SourceLoader loader) { + _encoding.becomeNative(loader); + } + + @override + void buildOutlineExpressions( + ClassHierarchy classHierarchy, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + SourceMethodBuilder methodBuilder, + Annotatable annotatable, + {required bool isClassInstanceMember, + required bool createFileUriExpression}) { + _encoding.buildOutlineExpressions( + classHierarchy, + libraryBuilder, + declarationBuilder, + createBodyBuilderContext(methodBuilder), + annotatable, + isClassInstanceMember: isClassInstanceMember, + createFileUriExpression: createFileUriExpression); + } + + @override + void buildOutlineNode(SourceLibraryBuilder libraryBuilder, + NameScheme nameScheme, BuildNodesCallback f, + {required Reference reference, + required Reference? tearOffReference, + required List? classTypeParameters}) { + _encoding.buildOutlineNode(libraryBuilder, nameScheme, f, + reference: reference, + tearOffReference: tearOffReference, + isAbstractOrExternal: + _fragment.modifiers.isAbstract || _fragment.modifiers.isExternal, + classTypeParameters: classTypeParameters); + } + + @override + void checkTypes( + SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { + _encoding.checkTypes(libraryBuilder, typeEnvironment); + } + + @override + void checkVariance( + SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { + _encoding.checkVariance(sourceClassBuilder, typeEnvironment); + } + + @override + int computeDefaultTypes(ComputeDefaultTypeContext context) { + return _encoding.computeDefaultTypes(context); + } + + @override + BodyBuilderContext createBodyBuilderContext(SourceMethodBuilder builder) { + return new MethodFragmentBodyBuilderContext( + _fragment, this, builder.libraryBuilder, builder.declarationBuilder, + isDeclarationInstanceMember: builder.isDeclarationInstanceMember); + } + + @override + void createEncoding( + ProblemReporting problemReporting, + SourceMethodBuilder builder, + MethodEncodingStrategy encodingStrategy, + List unboundNominalParameters) { + _encoding = encodingStrategy.createMethodEncoding( + builder, _fragment, unboundNominalParameters); + _fragment.typeParameterNameSpace.addTypeParameters( + problemReporting, _encoding.clonedAndDeclaredTypeParameters, + ownerName: _fragment.name, allowNameConflict: true); + returnType.registerInferredTypeListener(_encoding); + } + + @override + LocalScope createFormalParameterScope(LookupScope typeParameterScope) { + return _encoding.createFormalParameterScope(typeParameterScope); + } + + @override + void ensureTypes( + ClassMembersBuilder membersBuilder, + SourceClassBuilder enclosingClassBuilder, + Set? overrideDependencies) { + if (overrideDependencies != null) { + membersBuilder.inferMethodType(enclosingClassBuilder, _encoding.function, + returnType, _fragment.declaredFormals, overrideDependencies, + name: _fragment.name, + fileUri: fileUri, + nameOffset: _fragment.nameOffset, + nameLength: _fragment.name.length); + } + _encoding.ensureTypes( + enclosingClassBuilder.libraryBuilder, membersBuilder.hierarchyBuilder); + } + + @override + VariableDeclaration getFormalParameter(int index) { + return _encoding.getFormalParameter(index); + } + + @override + VariableDeclaration? getTearOffParameter(int index) { + return _encoding.getTearOffParameter(index); + } +} diff --git a/pkg/front_end/lib/src/fragment/method/encoding.dart b/pkg/front_end/lib/src/fragment/method/encoding.dart new file mode 100644 index 00000000000..c12e56e618b --- /dev/null +++ b/pkg/front_end/lib/src/fragment/method/encoding.dart @@ -0,0 +1,1019 @@ +// 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:kernel/ast.dart'; +import 'package:kernel/class_hierarchy.dart'; +import 'package:kernel/type_algebra.dart'; +import 'package:kernel/type_environment.dart'; + +import '../../base/local_scope.dart'; +import '../../base/scope.dart'; +import '../../builder/builder.dart'; +import '../../builder/declaration_builders.dart'; +import '../../builder/formal_parameter_builder.dart'; +import '../../builder/omitted_type_builder.dart'; +import '../../builder/type_builder.dart'; +import '../../kernel/body_builder_context.dart'; +import '../../kernel/type_algorithms.dart'; +import '../../source/builder_factory.dart'; +import '../../source/name_scheme.dart'; +import '../../source/source_class_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_method_builder.dart'; +import '../../source/source_type_parameter_builder.dart'; +import '../fragment.dart'; + +sealed class MethodEncoding implements InferredTypeListener { + List? get clonedAndDeclaredTypeParameters; + List? get formals; + List? get formalsForTesting; + FunctionNode get function; + Procedure get invokeTarget; + + Procedure? get readTarget; + + List? get thisTypeParameters; + + VariableDeclaration? get thisVariable; + + void becomeNative(SourceLoader loader); + + void buildOutlineExpressions( + ClassHierarchy classHierarchy, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + BodyBuilderContext bodyBuilderContext, + Annotatable annotatable, + {required bool isClassInstanceMember, + required bool createFileUriExpression}); + + void buildOutlineNode(SourceLibraryBuilder libraryBuilder, + NameScheme nameScheme, BuildNodesCallback f, + {required Reference reference, + required Reference? tearOffReference, + required bool isAbstractOrExternal, + required List? classTypeParameters}); + + void checkTypes( + SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment); + + void checkVariance( + SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment); + + int computeDefaultTypes(ComputeDefaultTypeContext context); + + LocalScope createFormalParameterScope(LookupScope typeParameterScope); + + void ensureTypes( + SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy); + + VariableDeclaration getFormalParameter(int index); + + VariableDeclaration? getTearOffParameter(int index); +} + +sealed class MethodEncodingStrategy { + factory MethodEncodingStrategy(DeclarationBuilder? declarationBuilder, + {required bool isInstanceMember}) { + switch (declarationBuilder) { + case ExtensionBuilder(): + if (isInstanceMember) { + return const _ExtensionInstanceMethodStrategy(); + } else { + return const _ExtensionStaticMethodStrategy(); + } + case ExtensionTypeDeclarationBuilder(): + if (isInstanceMember) { + return const _ExtensionTypeInstanceMethodStrategy(); + } else { + return const _ExtensionTypeStaticMethodStrategy(); + } + case null: + case ClassBuilder(): + return const _RegularMethodStrategy(); + } + } + + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters); +} + +mixin _DirectMethodEncodingMixin implements MethodEncoding { + Procedure? _procedure; + + @override + List? get clonedAndDeclaredTypeParameters => + _fragment.declaredTypeParameters?.builders; + + @override + List? get formals => _fragment.declaredFormals; + + @override + // Coverage-ignore(suite): Not run. + List? get formalsForTesting => + _fragment.declaredFormals; + + @override + FunctionNode get function => _procedure!.function; + + @override + Procedure get invokeTarget { + assert(_procedure != null, "No procedure computed for $_fragment yet."); + return _procedure!; + } + + @override + List? get thisTypeParameters => null; + + @override + VariableDeclaration? get thisVariable => null; + + BuiltMemberKind get _builtMemberKind; + + MethodFragment get _fragment; + + bool get _isExtensionMember; + + bool get _isExtensionTypeMember; + + ProcedureKind get _procedureKind; + + @override + void becomeNative(SourceLoader loader) { + loader.addNativeAnnotation(_procedure!, _fragment.nativeMethodName!); + } + + @override + void buildOutlineExpressions( + ClassHierarchy classHierarchy, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + BodyBuilderContext bodyBuilderContext, + Annotatable annotatable, + {required bool isClassInstanceMember, + required bool createFileUriExpression}) { + buildMetadataForOutlineExpressions(libraryBuilder, _fragment.enclosingScope, + bodyBuilderContext, annotatable, _fragment.metadata, + fileUri: _fragment.fileUri, + createFileUriExpression: createFileUriExpression); + buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, + bodyBuilderContext, _fragment.declaredTypeParameters?.builders); + buildFormalsForOutlineExpressions( + libraryBuilder, declarationBuilder, _fragment.declaredFormals, + scope: _fragment.typeParameterScope, + isClassInstanceMember: isClassInstanceMember); + } + + @override + void buildOutlineNode(SourceLibraryBuilder libraryBuilder, + NameScheme nameScheme, BuildNodesCallback f, + {required Reference reference, + required Reference? tearOffReference, + required bool isAbstractOrExternal, + List? classTypeParameters}) { + FunctionNode function = new FunctionNode( + isAbstractOrExternal ? null : new EmptyStatement(), + asyncMarker: _fragment.asyncModifier) + ..fileOffset = _fragment.formalsOffset + ..fileEndOffset = _fragment.endOffset; + buildTypeParametersAndFormals(libraryBuilder, function, + _fragment.declaredTypeParameters?.builders, _fragment.declaredFormals, + classTypeParameters: classTypeParameters, supportsTypeParameters: true); + if (_fragment.returnType is! InferableTypeBuilder) { + function.returnType = + _fragment.returnType.build(libraryBuilder, TypeUse.returnType); + } + + MemberName memberName = + nameScheme.getProcedureMemberName(_procedureKind, _fragment.name); + Procedure procedure = _procedure = new Procedure( + memberName.name, _procedureKind, function, + reference: reference, fileUri: _fragment.fileUri) + ..fileStartOffset = _fragment.startOffset + ..fileOffset = _fragment.nameOffset + ..fileEndOffset = _fragment.endOffset + ..isAbstract = _fragment.modifiers.isAbstract + ..isExternal = _fragment.modifiers.isExternal + ..isConst = _fragment.modifiers.isConst + ..isStatic = _fragment.modifiers.isStatic + ..isExtensionMember = _isExtensionMember + ..isExtensionTypeMember = _isExtensionTypeMember; + memberName.attachMember(procedure); + + f(kind: _builtMemberKind, member: procedure); + } + + @override + void checkTypes( + SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { + List? typeParameters = + _fragment.declaredTypeParameters?.builders; + if (typeParameters != null && typeParameters.isNotEmpty) { + libraryBuilder.checkTypeParameterDependencies(typeParameters); + } + libraryBuilder.checkInitializersInFormals( + _fragment.declaredFormals, typeEnvironment, + isAbstract: _fragment.modifiers.isAbstract, + isExternal: _fragment.modifiers.isExternal); + } + + @override + void checkVariance( + SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { + sourceClassBuilder.checkVarianceInTypeParameters( + typeEnvironment, _fragment.declaredTypeParameters?.builders); + sourceClassBuilder.checkVarianceInFormals( + typeEnvironment, _fragment.declaredFormals); + sourceClassBuilder.checkVarianceInReturnType( + typeEnvironment, function.returnType, + fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); + } + + @override + int computeDefaultTypes(ComputeDefaultTypeContext context) { + bool hasErrors = context.reportSimplicityIssuesForTypeParameters( + _fragment.declaredTypeParameters?.builders); + context.reportGenericFunctionTypesForFormals(_fragment.declaredFormals); + if (_fragment.returnType is! OmittedTypeBuilder) { + hasErrors |= + context.reportInboundReferenceIssuesForType(_fragment.returnType); + context.recursivelyReportGenericFunctionTypesAsBoundsForType( + _fragment.returnType); + } + return context.computeDefaultTypesForVariables( + _fragment.declaredTypeParameters?.builders, + inErrorRecovery: hasErrors); + } + + @override + LocalScope createFormalParameterScope(LookupScope parent) { + List? formals = _fragment.declaredFormals; + if (formals == null) { + return new FormalParameterScope(parent: parent); + } + Map local = {}; + for (FormalParameterBuilder formal in formals) { + if (formal.isWildcard) { + continue; + } + local[formal.name] = formal; + } + return new FormalParameterScope(local: local, parent: parent); + } + + @override + void ensureTypes( + SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy) { + _fragment.returnType + .build(libraryBuilder, TypeUse.returnType, hierarchy: hierarchy); + List? declaredFormals = _fragment.declaredFormals; + if (declaredFormals != null) { + for (FormalParameterBuilder formal in declaredFormals) { + formal.type + .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); + } + } + } + + @override + VariableDeclaration getFormalParameter(int index) => + _fragment.declaredFormals![index].variable!; + + @override + VariableDeclaration? getTearOffParameter(int index) => null; + + @override + void onInferredType(DartType type) { + function.returnType = type; + } +} + +class _ExtensionInstanceMethodEncoding extends MethodEncoding + with _ExtensionInstanceMethodEncodingMixin { + @override + final MethodFragment _fragment; + + @override + final List? _clonedDeclarationTypeParameters; + + @override + final FormalParameterBuilder _thisFormal; + + _ExtensionInstanceMethodEncoding( + this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) + : assert(!_fragment.isOperator); + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionMethod; + + @override + bool get _isExtensionMember => true; + + @override + bool get _isExtensionTypeMember => false; + + @override + bool get _isOperator => false; +} + +mixin _ExtensionInstanceMethodEncodingMixin implements MethodEncoding { + Procedure? _procedure; + + Procedure? _extensionTearOff; + @override + late final List? thisTypeParameters = + _clonedDeclarationTypeParameters != null + ? function.typeParameters + // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both + // required and unnecessary. + // ignore: unnecessary_non_null_assertion + .sublist(0, _clonedDeclarationTypeParameters!.length) + : null; + + /// If this is an extension instance method then + /// [_extensionTearOffParameterMap] holds a map from the parameters of + /// the methods to the parameter of the closure returned in the tear-off. + /// + /// This map is used to set the default values on the closure parameters when + /// these have been built. + Map? _extensionTearOffParameterMap; + + @override + List? get clonedAndDeclaredTypeParameters => + _clonedDeclarationTypeParameters != null || + _fragment.declaredTypeParameters != null + ? [ + ...?_clonedDeclarationTypeParameters, + ...?_fragment.declaredTypeParameters?.builders + ] + : null; + + @override + List? get formals => + [_thisFormal, ...?_fragment.declaredFormals]; + + @override + // Coverage-ignore(suite): Not run. + List? get formalsForTesting => + [_thisFormal, ...?_fragment.declaredFormals]; + + @override + FunctionNode get function => _procedure!.function; + + @override + Procedure get invokeTarget => _procedure!; + + @override + Procedure? get readTarget => _extensionTearOff; + + @override + VariableDeclaration? get thisVariable => _thisFormal.variable!; + + BuiltMemberKind get _builtMemberKind; + + List? get _clonedDeclarationTypeParameters; + + MethodFragment get _fragment; + + bool get _isExtensionMember; + + bool get _isExtensionTypeMember; + + bool get _isOperator; + + FormalParameterBuilder get _thisFormal; + + @override + // Coverage-ignore(suite): Not run. + void becomeNative(SourceLoader loader) { + loader.addNativeAnnotation(_procedure!, _fragment.nativeMethodName!); + } + + @override + void buildOutlineExpressions( + ClassHierarchy classHierarchy, + SourceLibraryBuilder libraryBuilder, + DeclarationBuilder? declarationBuilder, + BodyBuilderContext bodyBuilderContext, + Annotatable annotatable, + {required bool isClassInstanceMember, + required bool createFileUriExpression}) { + buildMetadataForOutlineExpressions(libraryBuilder, _fragment.enclosingScope, + bodyBuilderContext, annotatable, _fragment.metadata, + fileUri: _fragment.fileUri, + createFileUriExpression: createFileUriExpression); + + buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, + bodyBuilderContext, _fragment.declaredTypeParameters?.builders); + buildFormalsForOutlineExpressions( + libraryBuilder, declarationBuilder, _fragment.declaredFormals, + scope: _fragment.typeParameterScope, + isClassInstanceMember: isClassInstanceMember); + + buildTypeParametersForOutlineExpressions(classHierarchy, libraryBuilder, + bodyBuilderContext, _clonedDeclarationTypeParameters); + buildFormalForOutlineExpressions( + libraryBuilder, declarationBuilder, _thisFormal, + scope: _fragment.typeParameterScope, + isClassInstanceMember: isClassInstanceMember); + } + + @override + void buildOutlineNode(SourceLibraryBuilder libraryBuilder, + NameScheme nameScheme, BuildNodesCallback f, + {required Reference reference, + required Reference? tearOffReference, + required bool isAbstractOrExternal, + required List? classTypeParameters}) { + List? typeParameters; + if (_clonedDeclarationTypeParameters != null) { + typeParameters = []; + // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both required + // and unnecessary. + // ignore: unnecessary_non_null_assertion + for (NominalParameterBuilder t in _clonedDeclarationTypeParameters!) { + typeParameters.add(t.parameter); + } + } + FunctionNode function = new FunctionNode( + isAbstractOrExternal ? null : new EmptyStatement(), + typeParameters: typeParameters, + positionalParameters: [_thisFormal.build(libraryBuilder)], + asyncMarker: _fragment.asyncModifier) + ..fileOffset = _fragment.formalsOffset + ..fileEndOffset = _fragment.endOffset; + buildTypeParametersAndFormals(libraryBuilder, function, + _fragment.declaredTypeParameters?.builders, _fragment.declaredFormals, + classTypeParameters: classTypeParameters, supportsTypeParameters: true); + if (_fragment.returnType is! InferableTypeBuilder) { + function.returnType = + _fragment.returnType.build(libraryBuilder, TypeUse.returnType); + } + + MemberName memberName = + nameScheme.getProcedureMemberName(ProcedureKind.Method, _fragment.name); + Procedure procedure = _procedure = new Procedure( + memberName.name, ProcedureKind.Method, function, + reference: reference, fileUri: _fragment.fileUri) + ..fileStartOffset = _fragment.startOffset + ..fileOffset = _fragment.nameOffset + ..fileEndOffset = _fragment.endOffset + ..isAbstract = _fragment.modifiers.isAbstract + ..isExternal = _fragment.modifiers.isExternal + ..isConst = _fragment.modifiers.isConst + ..isStatic = true + ..isExtensionMember = _isExtensionMember + ..isExtensionTypeMember = _isExtensionTypeMember; + memberName.attachMember(procedure); + + if (!_isOperator) { + _extensionTearOff = + _buildExtensionTearOff(procedure, nameScheme, tearOffReference); + } + + f(kind: _builtMemberKind, member: procedure, tearOff: _extensionTearOff); + } + + @override + void checkTypes( + SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment) { + List? typeParameters = + _fragment.declaredTypeParameters?.builders; + if (typeParameters != null && typeParameters.isNotEmpty) { + libraryBuilder.checkTypeParameterDependencies(typeParameters); + } + libraryBuilder.checkInitializersInFormals( + _fragment.declaredFormals, typeEnvironment, + isAbstract: _fragment.modifiers.isAbstract, + isExternal: _fragment.modifiers.isExternal); + } + + @override + // Coverage-ignore(suite): Not run. + void checkVariance( + SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { + sourceClassBuilder.checkVarianceInTypeParameters( + typeEnvironment, _fragment.declaredTypeParameters?.builders); + sourceClassBuilder.checkVarianceInFormals( + typeEnvironment, _fragment.declaredFormals); + sourceClassBuilder.checkVarianceInReturnType( + typeEnvironment, function.returnType, + fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); + } + + @override + int computeDefaultTypes(ComputeDefaultTypeContext context) { + bool hasErrors = context.reportSimplicityIssuesForTypeParameters( + _fragment.declaredTypeParameters?.builders); + context.reportGenericFunctionTypesForFormals(_fragment.declaredFormals); + if (_fragment.returnType is! OmittedTypeBuilder) { + hasErrors |= + context.reportInboundReferenceIssuesForType(_fragment.returnType); + context.recursivelyReportGenericFunctionTypesAsBoundsForType( + _fragment.returnType); + } + if (_clonedDeclarationTypeParameters != null && + _fragment.declaredTypeParameters != null) { + // We need to compute all default types together since they might be + // interdependent. + return context.computeDefaultTypesForVariables([ + // TODO(johnniwinther): Ambivalent analyzer. `!` seems to be both + // required and unnecessary. + // ignore: unnecessary_non_null_assertion + ..._clonedDeclarationTypeParameters!, + ..._fragment.declaredTypeParameters!.builders + ], inErrorRecovery: hasErrors); + } else if (_clonedDeclarationTypeParameters != null) { + return context.computeDefaultTypesForVariables( + _clonedDeclarationTypeParameters, + inErrorRecovery: hasErrors); + } else { + return context.computeDefaultTypesForVariables( + _fragment.declaredTypeParameters?.builders, + inErrorRecovery: hasErrors); + } + } + + @override + LocalScope createFormalParameterScope(LookupScope parent) { + Map local = {}; + + assert(!_thisFormal.isWildcard); + local[_thisFormal.name] = _thisFormal; + + List? formals = _fragment.declaredFormals; + if (formals != null) { + for (FormalParameterBuilder formal in formals) { + if (formal.isWildcard) { + continue; + } + local[formal.name] = formal; + } + } + return new FormalParameterScope(local: local, parent: parent); + } + + @override + // Coverage-ignore(suite): Not run. + void ensureTypes( + SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy) { + _fragment.returnType + .build(libraryBuilder, TypeUse.fieldType, hierarchy: hierarchy); + _thisFormal.type + .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); + List? declaredFormals = _fragment.declaredFormals; + if (declaredFormals != null) { + for (FormalParameterBuilder formal in declaredFormals) { + formal.type + .build(libraryBuilder, TypeUse.parameterType, hierarchy: hierarchy); + } + } + } + + @override + VariableDeclaration getFormalParameter(int index) => + _fragment.declaredFormals![index].variable!; + + @override + VariableDeclaration? getTearOffParameter(int index) { + return _extensionTearOffParameterMap?[getFormalParameter(index)]; + } + + @override + void onInferredType(DartType type) { + function.returnType = type; + } + + /// Creates a top level function that creates a tear off of an extension + /// instance method. + /// + /// For this declaration + /// + /// extension E on A { + /// X method(S s, Y y) {} + /// } + /// + /// we create the top level function + /// + /// X E|method(A #this, S s, Y y) {} + /// + /// and the tear off function + /// + /// X Function(S, Y) E|get#method(A #this) { + /// return (S s, Y y) => E|method(#this, s, y); + /// } + /// + Procedure _buildExtensionTearOff( + Procedure procedure, NameScheme nameScheme, Reference? tearOffReference) { + _extensionTearOffParameterMap = {}; + + int fileStartOffset = _fragment.startOffset; + int fileOffset = _fragment.nameOffset; + int fileEndOffset = _fragment.endOffset; + + int extensionTypeParameterCount = + _clonedDeclarationTypeParameters?.length ?? 0; + + List typeParameters = []; + + Map substitutionMap = {}; + List typeArguments = []; + for (TypeParameter typeParameter in procedure.function.typeParameters) { + TypeParameter newTypeParameter = new TypeParameter(typeParameter.name); + typeParameters.add(newTypeParameter); + typeArguments.add(substitutionMap[typeParameter] = new TypeParameterType( + newTypeParameter, typeParameter.computeNullabilityFromBound())); + } + + List tearOffTypeParameters = []; + List closureTypeParameters = []; + Substitution substitution = Substitution.fromMap(substitutionMap); + for (int index = 0; index < typeParameters.length; index++) { + TypeParameter newTypeParameter = typeParameters[index]; + newTypeParameter.bound = substitution + .substituteType(procedure.function.typeParameters[index].bound); + newTypeParameter.defaultType = + procedure.function.typeParameters[index].defaultType; + if (index < extensionTypeParameterCount) { + tearOffTypeParameters.add(newTypeParameter); + } else { + closureTypeParameters.add(newTypeParameter); + } + } + + VariableDeclaration copyParameter( + VariableDeclaration parameter, DartType type) { + VariableDeclaration newParameter = new VariableDeclaration(parameter.name, + type: type, + isFinal: parameter.isFinal, + isLowered: parameter.isLowered, + isRequired: parameter.isRequired) + ..fileOffset = parameter.fileOffset; + _extensionTearOffParameterMap![parameter] = newParameter; + return newParameter; + } + + VariableDeclaration extensionThis = copyParameter( + procedure.function.positionalParameters.first, + substitution.substituteType( + procedure.function.positionalParameters.first.type)); + + DartType closureReturnType = + substitution.substituteType(procedure.function.returnType); + List closurePositionalParameters = []; + List closurePositionalArguments = []; + + for (int position = 0; + position < procedure.function.positionalParameters.length; + position++) { + VariableDeclaration parameter = + procedure.function.positionalParameters[position]; + if (position == 0) { + /// Pass `this` as a captured variable. + closurePositionalArguments + .add(new VariableGet(extensionThis)..fileOffset = fileOffset); + } else { + DartType type = substitution.substituteType(parameter.type); + VariableDeclaration newParameter = copyParameter(parameter, type); + closurePositionalParameters.add(newParameter); + closurePositionalArguments + .add(new VariableGet(newParameter)..fileOffset = fileOffset); + } + } + List closureNamedParameters = []; + List closureNamedArguments = []; + for (VariableDeclaration parameter in procedure.function.namedParameters) { + DartType type = substitution.substituteType(parameter.type); + VariableDeclaration newParameter = copyParameter(parameter, type); + closureNamedParameters.add(newParameter); + closureNamedArguments.add(new NamedExpression(parameter.name!, + new VariableGet(newParameter)..fileOffset = fileOffset)); + } + + Statement closureBody = new ReturnStatement( + new StaticInvocation( + procedure, + new Arguments(closurePositionalArguments, + types: typeArguments, named: closureNamedArguments)) + // We need to use the fileStartOffset on the StaticInvocation to + // avoid a possible "fake coverage miss" on the name of the + // extension method. + ..fileOffset = fileStartOffset) + ..fileOffset = fileOffset; + + FunctionExpression closure = new FunctionExpression( + new FunctionNode(closureBody, + typeParameters: closureTypeParameters, + positionalParameters: closurePositionalParameters, + namedParameters: closureNamedParameters, + requiredParameterCount: + procedure.function.requiredParameterCount - 1, + returnType: closureReturnType) + ..fileOffset = fileOffset + ..fileEndOffset = fileEndOffset) + // We need to use the fileStartOffset on the FunctionExpression to + // avoid a possible "fake coverage miss" on the name of the + // extension method. + ..fileOffset = fileStartOffset; + + FunctionNode function = new FunctionNode( + new ReturnStatement(closure)..fileOffset = fileOffset, + typeParameters: tearOffTypeParameters, + positionalParameters: [extensionThis], + requiredParameterCount: 1, + returnType: + closure.function.computeFunctionType(Nullability.nonNullable)) + ..fileOffset = fileOffset + ..fileEndOffset = fileEndOffset; + + MemberName tearOffName = + nameScheme.getProcedureMemberName(ProcedureKind.Getter, _fragment.name); + Procedure tearOff = new Procedure( + tearOffName.name, ProcedureKind.Method, function, + isStatic: true, + isExtensionMember: _isExtensionMember, + isExtensionTypeMember: _isExtensionTypeMember, + reference: tearOffReference, + fileUri: _fragment.fileUri) + ..fileUri = _fragment.fileUri + ..fileOffset = fileOffset + ..fileStartOffset = _fragment.startOffset + ..fileEndOffset = fileEndOffset; + tearOffName.attachMember(tearOff); + return tearOff; + } +} + +class _ExtensionInstanceMethodStrategy implements MethodEncodingStrategy { + const _ExtensionInstanceMethodStrategy(); + + @override + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters) { + ExtensionBuilder declarationBuilder = + builder.declarationBuilder as ExtensionBuilder; + SynthesizedExtensionSignature signature = new SynthesizedExtensionSignature( + declarationBuilder: declarationBuilder, + extensionTypeParameterFragments: + fragment.enclosingDeclaration!.typeParameters, + unboundNominalParameters: unboundNominalParameters, + onTypeBuilder: declarationBuilder.onType, + fileUri: fragment.fileUri, + fileOffset: fragment.nameOffset); + return fragment.isOperator + ? new _ExtensionInstanceOperatorEncoding(fragment, + signature.clonedDeclarationTypeParameters, signature.thisFormal) + : new _ExtensionInstanceMethodEncoding(fragment, + signature.clonedDeclarationTypeParameters, signature.thisFormal); + } +} + +class _ExtensionInstanceOperatorEncoding extends MethodEncoding + with _ExtensionInstanceMethodEncodingMixin { + @override + final MethodFragment _fragment; + + @override + final List? _clonedDeclarationTypeParameters; + + @override + final FormalParameterBuilder _thisFormal; + + _ExtensionInstanceOperatorEncoding( + this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) + : assert(_fragment.isOperator); + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionOperator; + + @override + bool get _isExtensionMember => true; + + @override + bool get _isExtensionTypeMember => false; + + @override + bool get _isOperator => true; +} + +class _ExtensionStaticMethodEncoding extends MethodEncoding + with _DirectMethodEncodingMixin { + @override + final MethodFragment _fragment; + + _ExtensionStaticMethodEncoding(this._fragment) + : assert(!_fragment.isOperator); + + @override + Procedure? get readTarget => invokeTarget; + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionMethod; + + @override + bool get _isExtensionMember => true; + + @override + bool get _isExtensionTypeMember => false; + + @override + ProcedureKind get _procedureKind => ProcedureKind.Method; +} + +class _ExtensionStaticMethodStrategy implements MethodEncodingStrategy { + const _ExtensionStaticMethodStrategy(); + + @override + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters) { + return new _ExtensionStaticMethodEncoding(fragment); + } +} + +class _ExtensionTypeInstanceMethodEncoding extends MethodEncoding + with _ExtensionInstanceMethodEncodingMixin { + @override + final MethodFragment _fragment; + + @override + final List? _clonedDeclarationTypeParameters; + + @override + final FormalParameterBuilder _thisFormal; + + _ExtensionTypeInstanceMethodEncoding( + this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) + : assert(!_fragment.isOperator); + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeMethod; + + @override + bool get _isExtensionMember => false; + + @override + bool get _isExtensionTypeMember => true; + + @override + bool get _isOperator => false; +} + +class _ExtensionTypeInstanceMethodStrategy implements MethodEncodingStrategy { + const _ExtensionTypeInstanceMethodStrategy(); + + @override + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters) { + ExtensionTypeDeclarationBuilder declarationBuilder = + builder.declarationBuilder as ExtensionTypeDeclarationBuilder; + SynthesizedExtensionTypeSignature signature = + new SynthesizedExtensionTypeSignature( + extensionTypeDeclarationBuilder: declarationBuilder, + extensionTypeTypeParameters: + fragment.enclosingDeclaration!.typeParameters, + unboundNominalParameters: unboundNominalParameters, + fileUri: fragment.fileUri, + fileOffset: fragment.nameOffset); + return fragment.isOperator + ? new _ExtensionTypeInstanceOperatorEncoding(fragment, + signature.clonedDeclarationTypeParameters, signature.thisFormal) + : new _ExtensionTypeInstanceMethodEncoding(fragment, + signature.clonedDeclarationTypeParameters, signature.thisFormal); + } +} + +class _ExtensionTypeInstanceOperatorEncoding extends MethodEncoding + with _ExtensionInstanceMethodEncodingMixin { + @override + final MethodFragment _fragment; + + @override + final List? _clonedDeclarationTypeParameters; + + @override + final FormalParameterBuilder _thisFormal; + + _ExtensionTypeInstanceOperatorEncoding( + this._fragment, this._clonedDeclarationTypeParameters, this._thisFormal) + : assert(_fragment.isOperator); + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeOperator; + + @override + bool get _isExtensionMember => false; + + @override + bool get _isExtensionTypeMember => true; + + @override + bool get _isOperator => true; +} + +class _ExtensionTypeStaticMethodEncoding extends MethodEncoding + with _DirectMethodEncodingMixin { + @override + final MethodFragment _fragment; + + _ExtensionTypeStaticMethodEncoding(this._fragment) + : assert(!_fragment.isOperator); + + @override + Procedure? get readTarget => invokeTarget; + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.ExtensionTypeMethod; + + @override + bool get _isExtensionMember => false; + + @override + bool get _isExtensionTypeMember => true; + + @override + ProcedureKind get _procedureKind => ProcedureKind.Method; +} + +class _ExtensionTypeStaticMethodStrategy implements MethodEncodingStrategy { + const _ExtensionTypeStaticMethodStrategy(); + + @override + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters) { + return new _ExtensionTypeStaticMethodEncoding(fragment); + } +} + +class _RegularMethodEncoding extends MethodEncoding + with _DirectMethodEncodingMixin { + @override + final MethodFragment _fragment; + + _RegularMethodEncoding(this._fragment) : assert(!_fragment.isOperator); + + @override + Procedure? get readTarget => invokeTarget; + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.Method; + + @override + bool get _isExtensionMember => false; + + @override + bool get _isExtensionTypeMember => false; + + @override + ProcedureKind get _procedureKind => ProcedureKind.Method; +} + +class _RegularMethodStrategy implements MethodEncodingStrategy { + const _RegularMethodStrategy(); + + @override + MethodEncoding createMethodEncoding( + SourceMethodBuilder builder, + MethodFragment fragment, + List unboundNominalParameters) { + return fragment.isOperator + ? new _RegularOperatorEncoding(fragment) + : new _RegularMethodEncoding(fragment); + } +} + +class _RegularOperatorEncoding extends MethodEncoding + with _DirectMethodEncodingMixin { + @override + final MethodFragment _fragment; + + _RegularOperatorEncoding(this._fragment) : assert(_fragment.isOperator); + + @override + Procedure? get readTarget => null; + + @override + BuiltMemberKind get _builtMemberKind => BuiltMemberKind.Method; + + @override + bool get _isExtensionMember => false; + + @override + bool get _isExtensionTypeMember => false; + + @override + ProcedureKind get _procedureKind => ProcedureKind.Operator; +} diff --git a/pkg/front_end/lib/src/source/source_builder_factory.dart b/pkg/front_end/lib/src/source/source_builder_factory.dart index 2fd20199cf4..343e0c879c6 100644 --- a/pkg/front_end/lib/src/source/source_builder_factory.dart +++ b/pkg/front_end/lib/src/source/source_builder_factory.dart @@ -2122,7 +2122,7 @@ class BuilderFactoryImpl implements BuilderFactory, BuilderFactoryResult { fragment.declaration.becomeNative(loader); } for (MethodFragment fragment in _nativeMethodFragments) { - fragment.becomeNative(loader); + fragment.declaration.becomeNative(loader); } for (ConstructorFragment fragment in _nativeConstructorFragments) { fragment.builder.becomeNative(loader); diff --git a/pkg/front_end/lib/src/source/source_method_builder.dart b/pkg/front_end/lib/src/source/source_method_builder.dart index 986aea69cc2..0b7977e7e5d 100644 --- a/pkg/front_end/lib/src/source/source_method_builder.dart +++ b/pkg/front_end/lib/src/source/source_method_builder.dart @@ -14,7 +14,7 @@ import '../builder/formal_parameter_builder.dart'; import '../builder/metadata_builder.dart'; import '../builder/method_builder.dart'; import '../builder/type_builder.dart'; -import '../fragment/fragment.dart'; +import '../fragment/method/declaration.dart'; import '../kernel/hierarchy/class_member.dart'; import '../kernel/hierarchy/members_builder.dart'; import '../kernel/kernel_helper.dart'; @@ -52,8 +52,8 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl /// The declarations that introduces this method. Subsequent methods of the /// same name must be augmentations. - final MethodFragment _introductory; - final List _augmentations; + final MethodDeclaration _introductory; + final List _augmentations; final Modifiers _modifiers; @@ -74,8 +74,8 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl required this.isStatic, required Modifiers modifiers, required NameScheme nameScheme, - required MethodFragment introductory, - required List augmentations, + required MethodDeclaration introductory, + required List augmentations, required Reference? reference, required Reference? tearOffReference}) : _nameScheme = nameScheme, @@ -127,19 +127,19 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl @override void buildOutlineNodes(BuildNodesCallback f) { - List augmentedFragments = [ + List augmentedFragments = [ _introductory, ..._augmentations ]; // TODO(johnniwinther): Support augmenting a concrete method with an // abstract method. - MethodFragment lastFragment = augmentedFragments.removeLast(); + MethodDeclaration lastFragment = augmentedFragments.removeLast(); lastFragment.buildOutlineNode(libraryBuilder, _nameScheme, f, reference: _reference, tearOffReference: _tearOffReference, classTypeParameters: classBuilder?.cls.typeParameters); - for (MethodFragment augmented in augmentedFragments) { + for (MethodDeclaration augmented in augmentedFragments) { augmented.buildOutlineNode( libraryBuilder, _nameScheme, noAddBuildNodesCallback, reference: new Reference(), @@ -156,14 +156,14 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl void buildOutlineExpressions(ClassHierarchy classHierarchy, List delayedDefaultValueCloners) { if (!hasBuiltOutlineExpressions) { - _introductory.buildOutlineExpressions( - classHierarchy, libraryBuilder, declarationBuilder, _invokeTarget, + _introductory.buildOutlineExpressions(classHierarchy, libraryBuilder, + declarationBuilder, this, _invokeTarget, isClassInstanceMember: isClassInstanceMember, createFileUriExpression: _invokeTarget.fileUri != _introductory.fileUri); - for (MethodFragment augmentation in _augmentations) { - augmentation.buildOutlineExpressions( - classHierarchy, libraryBuilder, declarationBuilder, _invokeTarget, + for (MethodDeclaration augmentation in _augmentations) { + augmentation.buildOutlineExpressions(classHierarchy, libraryBuilder, + declarationBuilder, this, _invokeTarget, isClassInstanceMember: isClassInstanceMember, createFileUriExpression: _invokeTarget.fileUri != augmentation.fileUri); @@ -179,7 +179,7 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl // default values declared on the introductory method and omitted on the // augmenting method. _introductory.checkTypes(library, typeEnvironment); - for (MethodFragment augmentation in _augmentations) { + for (MethodDeclaration augmentation in _augmentations) { augmentation.checkTypes(library, typeEnvironment); } } @@ -189,7 +189,7 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { if (!isClassInstanceMember) return; _introductory.checkVariance(sourceClassBuilder, typeEnvironment); - for (MethodFragment augmentation in _augmentations) { + for (MethodDeclaration augmentation in _augmentations) { augmentation.checkVariance(sourceClassBuilder, typeEnvironment); } } @@ -241,7 +241,7 @@ class SourceMethodBuilder extends SourceMemberBuilderImpl int computeDefaultTypes(ComputeDefaultTypeContext context, {required bool inErrorRecovery}) { int count = _introductory.computeDefaultTypes(context); - for (MethodFragment augmentation in _augmentations) { + for (MethodDeclaration augmentation in _augmentations) { count += augmentation.computeDefaultTypes(context); } return count; diff --git a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart index f1f88a32f8e..978b9e77aa4 100644 --- a/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart +++ b/pkg/front_end/lib/src/source/type_parameter_scope_builder.dart @@ -25,6 +25,8 @@ import '../fragment/constructor/declaration.dart'; import '../fragment/factory/declaration.dart'; import '../fragment/fragment.dart'; import '../fragment/getter/declaration.dart'; +import '../fragment/method/declaration.dart'; +import '../fragment/method/encoding.dart'; import '../fragment/setter/declaration.dart'; import 'builder_factory.dart'; import 'name_scheme.dart'; @@ -1857,13 +1859,17 @@ void _computeBuildersFromFragments(String name, List fragments, } Modifiers modifiers = fragment.modifiers; - List augmentationFragments = []; + MethodDeclaration introductoryDeclaration = + new MethodDeclarationImpl(fragment); + + List augmentationDeclarations = []; if (augmentations != null) { for (Fragment augmentation in augmentations) { // Promote [augmentation] to [MethodFragment]. augmentation as MethodFragment; - augmentationFragments.add(augmentation); + augmentationDeclarations + .add(new MethodDeclarationImpl(augmentation)); createNominalParameterBuilders( augmentation.declaredTypeParameters, unboundNominalParameters); @@ -1874,7 +1880,6 @@ void _computeBuildersFromFragments(String name, List fragments, modifiers -= Modifiers.External; } } - augmentations = null; } SourceMethodBuilder methodBuilder = new SourceMethodBuilder( @@ -1885,17 +1890,28 @@ void _computeBuildersFromFragments(String name, List fragments, declarationBuilder: declarationBuilder, isStatic: modifiers.isStatic, modifiers: modifiers, - introductory: fragment, - augmentations: augmentationFragments, + introductory: introductoryDeclaration, + augmentations: augmentationDeclarations, nameScheme: nameScheme, reference: procedureReference, tearOffReference: tearOffReference); - fragment.setBuilder(problemReporting, methodBuilder, encodingStrategy, - unboundNominalParameters); - for (MethodFragment augmentation in augmentationFragments) { - augmentation.setBuilder(problemReporting, methodBuilder, + fragment.builder = methodBuilder; + if (augmentations != null) { + for (Fragment augmentation in augmentations) { + // Promote [augmentation] to [MethodFragment]. + augmentation as MethodFragment; + + augmentation.builder = methodBuilder; + } + augmentations = null; + } + introductoryDeclaration.createEncoding(problemReporting, methodBuilder, + encodingStrategy, unboundNominalParameters); + for (MethodDeclaration augmentation in augmentationDeclarations) { + augmentation.createEncoding(problemReporting, methodBuilder, encodingStrategy, unboundNominalParameters); } + builders.add(new _AddBuilder( fragment.name, methodBuilder, fragment.fileUri, fragment.nameOffset, inPatch: fragment.enclosingDeclaration?.isPatch ??