| // Copyright (c) 2024, 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. |
| |
| part of 'fragment.dart'; |
| |
| class MethodFragment implements Fragment, FunctionFragment { |
| @override |
| final String name; |
| |
| final Uri fileUri; |
| final int startOffset; |
| final int nameOffset; |
| final int formalsOffset; |
| final int endOffset; |
| final bool isTopLevel; |
| final List<MetadataBuilder>? metadata; |
| final Modifiers modifiers; |
| |
| /// The declared return type of this method. |
| /// |
| /// If the return type was omitted, this is an [InferableTypeBuilder]. |
| final TypeBuilder returnType; |
| |
| /// The name space for the type parameters available on this method. |
| /// |
| /// Initially this contains only the [declaredTypeParameters], but for |
| /// extension and extension type instance method this will include type |
| /// parameters cloned from the extension or extension type, respectively. |
| final NominalParameterNameSpace typeParameterNameSpace; |
| |
| /// The declared type parameters on this method. |
| final List<NominalParameterBuilder>? declaredTypeParameters; |
| |
| /// The scope that introduces type parameters on this method. |
| /// |
| /// This is based on [typeParameterNameSpace] and initially this contains only |
| /// the [declaredTypeParameters], but for extension and extension type |
| /// instance methods this will include type parameters cloned from the |
| /// extension or extension type, respectively. |
| final LookupScope typeParameterScope; |
| |
| /// The declared formals on this method. |
| final List<FormalParameterBuilder>? declaredFormals; |
| |
| final bool isOperator; |
| final AsyncMarker asyncModifier; |
| final String? nativeMethodName; |
| |
| SourceMethodBuilder? _builder; |
| |
| late final _MethodEncoding _encoding; |
| |
| MethodFragment( |
| {required this.name, |
| required this.fileUri, |
| required this.startOffset, |
| required this.nameOffset, |
| required this.formalsOffset, |
| required this.endOffset, |
| required this.isTopLevel, |
| required this.metadata, |
| required this.modifiers, |
| required this.returnType, |
| required this.declaredTypeParameters, |
| required this.typeParameterNameSpace, |
| required this.typeParameterScope, |
| required this.declaredFormals, |
| required this.isOperator, |
| required this.asyncModifier, |
| required this.nativeMethodName}); |
| |
| @override |
| SourceMethodBuilder get builder { |
| assert(_builder != null, "Builder has not been computed for $this."); |
| return _builder!; |
| } |
| |
| void setBuilder( |
| ProblemReporting problemReporting, |
| SourceMethodBuilder value, |
| MethodEncodingStrategy encodingStrategy, |
| List<NominalParameterBuilder> unboundNominalParameters) { |
| 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); |
| } |
| |
| @override |
| FunctionBodyBuildingContext createFunctionBodyBuildingContext() { |
| return new _MethodBodyBuildingContext(this); |
| } |
| |
| void buildOutlineNode(SourceLibraryBuilder libraryBuilder, |
| NameScheme nameScheme, BuildNodesCallback f, |
| {required Reference reference, |
| required Reference? tearOffReference, |
| required List<TypeParameter>? 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, |
| LookupScope parentScope, |
| Annotatable annotatable, |
| {required bool isClassInstanceMember, |
| required bool createFileUriExpression}) { |
| _encoding.buildOutlineExpressions( |
| classHierarchy, |
| libraryBuilder, |
| declarationBuilder, |
| parentScope, |
| 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<ClassMember>? 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, |
| {required bool isAbstract, required bool isExternal}) { |
| _encoding.checkTypes(libraryBuilder, typeEnvironment, |
| isAbstract: isAbstract, isExternal: isExternal); |
| } |
| |
| 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<NominalParameterBuilder>? get typeParametersForTesting => |
| _encoding.clonedAndDeclaredTypeParameters; |
| |
| // Coverage-ignore(suite): Not run. |
| List<FormalParameterBuilder>? get formalsForTesting => |
| _encoding.formalsForTesting; |
| |
| @override |
| String toString() => '$runtimeType($name,$fileUri,$nameOffset)'; |
| } |
| |
| class _MethodBodyBuildingContext implements FunctionBodyBuildingContext { |
| MethodFragment _fragment; |
| |
| _MethodBodyBuildingContext(this._fragment); |
| |
| @override |
| MemberKind get memberKind => _fragment.isTopLevel |
| ? MemberKind.TopLevelMethod |
| : (_fragment.modifiers.isStatic |
| ? MemberKind.StaticMethod |
| : MemberKind.NonStaticMethod); |
| |
| @override |
| bool get shouldBuild => true; |
| |
| @override |
| LocalScope computeFormalParameterScope(LookupScope typeParameterScope) { |
| return _fragment._encoding.createFormalParameterScope(typeParameterScope); |
| } |
| |
| @override |
| LookupScope get typeParameterScope { |
| return _fragment.typeParameterScope; |
| } |
| |
| @override |
| BodyBuilderContext createBodyBuilderContext() { |
| return _fragment.createBodyBuilderContext(); |
| } |
| |
| @override |
| InferenceDataForTesting? get inferenceDataForTesting => _fragment |
| .builder |
| .dataForTesting |
| // Coverage-ignore(suite): Not run. |
| ?.inferenceData; |
| |
| @override |
| List<TypeParameter>? get thisTypeParameters => |
| _fragment._encoding.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<NominalParameterBuilder> unboundNominalParameters); |
| } |
| |
| class _RegularMethodStrategy implements MethodEncodingStrategy { |
| const _RegularMethodStrategy(); |
| |
| @override |
| _MethodEncoding createMethodEncoding( |
| SourceMethodBuilder builder, |
| MethodFragment fragment, |
| List<NominalParameterBuilder> unboundNominalParameters) { |
| return fragment.isOperator |
| ? new _RegularOperatorEncoding(fragment) |
| : new _RegularMethodEncoding(fragment); |
| } |
| } |
| |
| class _ExtensionInstanceMethodStrategy implements MethodEncodingStrategy { |
| const _ExtensionInstanceMethodStrategy(); |
| |
| @override |
| _MethodEncoding createMethodEncoding( |
| SourceMethodBuilder builder, |
| MethodFragment fragment, |
| List<NominalParameterBuilder> unboundNominalParameters) { |
| ExtensionBuilder declarationBuilder = |
| builder.declarationBuilder as ExtensionBuilder; |
| SynthesizedExtensionSignature signature = new SynthesizedExtensionSignature( |
| declarationBuilder, unboundNominalParameters, |
| 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<NominalParameterBuilder> unboundNominalParameters) { |
| return new _ExtensionStaticMethodEncoding(fragment); |
| } |
| } |
| |
| class _ExtensionTypeInstanceMethodStrategy implements MethodEncodingStrategy { |
| const _ExtensionTypeInstanceMethodStrategy(); |
| |
| @override |
| _MethodEncoding createMethodEncoding( |
| SourceMethodBuilder builder, |
| MethodFragment fragment, |
| List<NominalParameterBuilder> unboundNominalParameters) { |
| ExtensionTypeDeclarationBuilder declarationBuilder = |
| builder.declarationBuilder as ExtensionTypeDeclarationBuilder; |
| SynthesizedExtensionTypeSignature signature = |
| new SynthesizedExtensionTypeSignature( |
| declarationBuilder, 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<NominalParameterBuilder> unboundNominalParameters) { |
| return new _ExtensionTypeStaticMethodEncoding(fragment); |
| } |
| } |
| |
| sealed class _MethodEncoding implements InferredTypeListener { |
| VariableDeclaration? get thisVariable; |
| List<TypeParameter>? 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<TypeParameter>? classTypeParameters}); |
| |
| void buildOutlineExpressions( |
| ClassHierarchy classHierarchy, |
| SourceLibraryBuilder libraryBuilder, |
| DeclarationBuilder? declarationBuilder, |
| LookupScope parentScope, |
| 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<FormalParameterBuilder>? get formals; |
| |
| VariableDeclaration getFormalParameter(int index); |
| |
| VariableDeclaration? getTearOffParameter(int index); |
| |
| void checkTypes( |
| SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment, |
| {required bool isAbstract, required bool isExternal}); |
| |
| void checkVariance( |
| SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment); |
| |
| List<NominalParameterBuilder>? get clonedAndDeclaredTypeParameters; |
| |
| List<FormalParameterBuilder>? get formalsForTesting; |
| } |
| |
| mixin _DirectMethodEncodingMixin implements _MethodEncoding { |
| MethodFragment get _fragment; |
| |
| Procedure? _procedure; |
| |
| @override |
| VariableDeclaration? get thisVariable => null; |
| |
| @override |
| List<TypeParameter>? 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<TypeParameter>? classTypeParameters}) { |
| FunctionNode function = new FunctionNode( |
| isAbstractOrExternal ? null : new EmptyStatement(), |
| asyncMarker: _fragment.asyncModifier) |
| ..fileOffset = _fragment.formalsOffset |
| ..fileEndOffset = _fragment.endOffset; |
| buildTypeParametersAndFormals(libraryBuilder, function, |
| _fragment.declaredTypeParameters, _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, |
| LookupScope parentScope, |
| BodyBuilderContext bodyBuilderContext, |
| Annotatable annotatable, |
| {required bool isClassInstanceMember, |
| required bool createFileUriExpression}) { |
| _buildMetadataForOutlineExpressions(libraryBuilder, parentScope, |
| bodyBuilderContext, annotatable, _fragment.metadata, |
| fileUri: _fragment.fileUri, |
| createFileUriExpression: createFileUriExpression); |
| _buildTypeParametersForOutlineExpressions( |
| classHierarchy, |
| libraryBuilder, |
| bodyBuilderContext, |
| _fragment.typeParameterScope, |
| _fragment.declaredTypeParameters); |
| _buildFormalsForOutlineExpressions( |
| libraryBuilder, declarationBuilder, _fragment.declaredFormals, |
| 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<FormalParameterBuilder>? formals = _fragment.declaredFormals; |
| if (formals == null) { |
| return new FormalParameterScope(parent: parent); |
| } |
| Map<String, Builder> local = <String, Builder>{}; |
| 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); |
| context.reportGenericFunctionTypesForFormals(_fragment.declaredFormals); |
| if (_fragment.returnType is! OmittedTypeBuilder) { |
| hasErrors |= |
| context.reportInboundReferenceIssuesForType(_fragment.returnType); |
| context.recursivelyReportGenericFunctionTypesAsBoundsForType( |
| _fragment.returnType); |
| } |
| return context.computeDefaultTypesForVariables( |
| _fragment.declaredTypeParameters, |
| inErrorRecovery: hasErrors); |
| } |
| |
| @override |
| void ensureTypes( |
| SourceLibraryBuilder libraryBuilder, ClassHierarchyBase hierarchy) { |
| _fragment.returnType |
| .build(libraryBuilder, TypeUse.returnType, hierarchy: hierarchy); |
| List<FormalParameterBuilder>? 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<FormalParameterBuilder>? 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, |
| {required bool isAbstract, required bool isExternal}) { |
| List<TypeParameterBuilder>? typeParameters = |
| _fragment.declaredTypeParameters; |
| if (typeParameters != null && typeParameters.isNotEmpty) { |
| libraryBuilder.checkTypeParameterDependencies(typeParameters); |
| } |
| libraryBuilder.checkInitializersInFormals( |
| _fragment.declaredFormals, typeEnvironment, |
| isAbstract: isAbstract, isExternal: isExternal); |
| } |
| |
| @override |
| void checkVariance( |
| SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { |
| sourceClassBuilder.checkVarianceInTypeParameters( |
| typeEnvironment, _fragment.declaredTypeParameters); |
| sourceClassBuilder.checkVarianceInFormals( |
| typeEnvironment, _fragment.declaredFormals); |
| sourceClassBuilder.checkVarianceInReturnType( |
| typeEnvironment, function.returnType, |
| fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); |
| } |
| |
| @override |
| List<NominalParameterBuilder>? get clonedAndDeclaredTypeParameters => |
| _fragment.declaredTypeParameters; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<FormalParameterBuilder>? 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 |
| // Coverage-ignore(suite): Not run. |
| 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<NominalParameterBuilder>? get _clonedDeclarationTypeParameters; |
| |
| FormalParameterBuilder get _thisFormal; |
| |
| @override |
| late final List<TypeParameter>? 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<VariableDeclaration, VariableDeclaration>? _extensionTearOffParameterMap; |
| |
| /// Creates a top level function that creates a tear off of an extension |
| /// instance method. |
| /// |
| /// For this declaration |
| /// |
| /// extension E<T> on A<T> { |
| /// X method<S>(S s, Y y) {} |
| /// } |
| /// |
| /// we create the top level function |
| /// |
| /// X E|method<T, S>(A<T> #this, S s, Y y) {} |
| /// |
| /// and the tear off function |
| /// |
| /// X Function<S>(S, Y) E|get#method<T>(A<T> #this) { |
| /// return (S s, Y y) => E|method<T, S>(#this, s, y); |
| /// } |
| /// |
| Procedure _buildExtensionTearOff( |
| Procedure procedure, NameScheme nameScheme, Reference? tearOffReference) { |
| _extensionTearOffParameterMap = {}; |
| |
| int fileOffset = _fragment.nameOffset; |
| int fileEndOffset = _fragment.endOffset; |
| |
| int extensionTypeParameterCount = |
| _clonedDeclarationTypeParameters?.length ?? 0; |
| |
| List<TypeParameter> typeParameters = <TypeParameter>[]; |
| |
| Map<TypeParameter, DartType> substitutionMap = {}; |
| List<DartType> typeArguments = <DartType>[]; |
| 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<TypeParameter> tearOffTypeParameters = <TypeParameter>[]; |
| List<TypeParameter> closureTypeParameters = <TypeParameter>[]; |
| 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<VariableDeclaration> closurePositionalParameters = []; |
| List<Expression> 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<VariableDeclaration> closureNamedParameters = []; |
| List<NamedExpression> 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)) |
| ..fileOffset = fileOffset) |
| ..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) |
| ..fileOffset = fileOffset; |
| |
| 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<TypeParameter>? classTypeParameters}) { |
| List<TypeParameter>? 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, _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, |
| LookupScope parentScope, |
| BodyBuilderContext bodyBuilderContext, |
| Annotatable annotatable, |
| {required bool isClassInstanceMember, |
| required bool createFileUriExpression}) { |
| _buildMetadataForOutlineExpressions(libraryBuilder, parentScope, |
| bodyBuilderContext, annotatable, _fragment.metadata, |
| fileUri: _fragment.fileUri, |
| createFileUriExpression: createFileUriExpression); |
| |
| _buildTypeParametersForOutlineExpressions( |
| classHierarchy, |
| libraryBuilder, |
| bodyBuilderContext, |
| _fragment.typeParameterScope, |
| _fragment.declaredTypeParameters); |
| _buildFormalsForOutlineExpressions( |
| libraryBuilder, declarationBuilder, _fragment.declaredFormals, |
| isClassInstanceMember: isClassInstanceMember); |
| |
| _buildTypeParametersForOutlineExpressions( |
| classHierarchy, |
| libraryBuilder, |
| bodyBuilderContext, |
| _fragment.typeParameterScope, |
| _clonedDeclarationTypeParameters); |
| _buildFormalForOutlineExpressions( |
| libraryBuilder, declarationBuilder, _thisFormal, |
| isClassInstanceMember: isClassInstanceMember); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void becomeNative(SourceLoader loader) { |
| loader.addNativeAnnotation(_procedure!, _fragment.nativeMethodName!); |
| } |
| |
| @override |
| void checkTypes( |
| SourceLibraryBuilder libraryBuilder, TypeEnvironment typeEnvironment, |
| {required bool isAbstract, required bool isExternal}) { |
| List<TypeParameterBuilder>? typeParameters = |
| _fragment.declaredTypeParameters; |
| if (typeParameters != null && typeParameters.isNotEmpty) { |
| libraryBuilder.checkTypeParameterDependencies(typeParameters); |
| } |
| libraryBuilder.checkInitializersInFormals( |
| _fragment.declaredFormals, typeEnvironment, |
| isAbstract: isAbstract, isExternal: isExternal); |
| } |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| void checkVariance( |
| SourceClassBuilder sourceClassBuilder, TypeEnvironment typeEnvironment) { |
| sourceClassBuilder.checkVarianceInTypeParameters( |
| typeEnvironment, _fragment.declaredTypeParameters); |
| sourceClassBuilder.checkVarianceInFormals( |
| typeEnvironment, _fragment.declaredFormals); |
| sourceClassBuilder.checkVarianceInReturnType( |
| typeEnvironment, function.returnType, |
| fileOffset: _fragment.nameOffset, fileUri: _fragment.fileUri); |
| } |
| |
| @override |
| LocalScope createFormalParameterScope(LookupScope parent) { |
| Map<String, Builder> local = <String, Builder>{}; |
| |
| assert(!_thisFormal.isWildcard); |
| local[_thisFormal.name] = _thisFormal; |
| |
| List<FormalParameterBuilder>? 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); |
| 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! |
| ], inErrorRecovery: hasErrors); |
| } else if (_clonedDeclarationTypeParameters != null) { |
| return context.computeDefaultTypesForVariables( |
| _clonedDeclarationTypeParameters, |
| inErrorRecovery: hasErrors); |
| } else { |
| return context.computeDefaultTypesForVariables( |
| _fragment.declaredTypeParameters, |
| 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<FormalParameterBuilder>? 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<FormalParameterBuilder>? 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<NominalParameterBuilder>? get clonedAndDeclaredTypeParameters => |
| _clonedDeclarationTypeParameters != null || |
| _fragment.declaredTypeParameters != null |
| ? [ |
| ...?_clonedDeclarationTypeParameters, |
| ...?_fragment.declaredTypeParameters |
| ] |
| : null; |
| |
| @override |
| // Coverage-ignore(suite): Not run. |
| List<FormalParameterBuilder>? get formalsForTesting => |
| [_thisFormal, ...?_fragment.declaredFormals]; |
| } |
| |
| class _ExtensionInstanceOperatorEncoding extends _MethodEncoding |
| with _ExtensionInstanceMethodEncodingMixin { |
| @override |
| final MethodFragment _fragment; |
| |
| @override |
| final List<NominalParameterBuilder>? _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<NominalParameterBuilder>? _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<NominalParameterBuilder>? _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<NominalParameterBuilder>? _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 setBody(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<FormalParameterBuilder>? get formals => _fragment._encoding.formals; |
| |
| @override |
| VariableDeclaration getFormalParameter(int index) => |
| _fragment._encoding.getFormalParameter(index); |
| |
| @override |
| VariableDeclaration? getTearOffParameter(int index) => |
| _fragment._encoding.getTearOffParameter(index); |
| |
| @override |
| AugmentSuperTarget? get augmentSuperTarget { |
| if (_fragment.builder.isAugmentation) { |
| return _fragment.builder.augmentSuperTarget; |
| } |
| return null; |
| } |
| } |