| // 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 '../../ast.dart'; | |
| // ------------------------------------------------------------------------ | |
| // FUNCTIONS | |
| // ------------------------------------------------------------------------ | |
| /// A function declares parameters and has a body. | |
| /// | |
| /// This may occur in a procedure, constructor, function expression, or local | |
| /// function declaration. | |
| class FunctionNode extends TreeNode implements ScopeProvider, ContextConsumer { | |
| /// End offset in the source file it comes from. Valid values are from 0 and | |
| /// up, or -1 ([TreeNode.noOffset]) if the file end offset is not available | |
| /// (this is the default if none is specifically set). | |
| int fileEndOffset = TreeNode.noOffset; | |
| @override | |
| List<int>? get fileOffsetsIfMultiple => [fileOffset, fileEndOffset]; | |
| /// Kernel async marker for the function. | |
| /// | |
| /// See also [dartAsyncMarker]. | |
| AsyncMarker asyncMarker; | |
| /// Dart async marker for the function. | |
| /// | |
| /// See also [asyncMarker]. | |
| /// | |
| /// A Kernel function can represent a Dart function with a different async | |
| /// marker. | |
| /// | |
| /// For example, when async/await is translated away, | |
| /// a Dart async function might be represented by a Kernel sync function. | |
| AsyncMarker dartAsyncMarker; | |
| List<TypeParameter> typeParameters; | |
| int requiredParameterCount; | |
| List<PositionalParameter> positionalParameters; | |
| List<NamedParameter> namedParameters; | |
| ThisVariable? thisVariable; | |
| DartType returnType; // Not null. | |
| Statement? _body; | |
| @override | |
| Scope? scope; | |
| @override | |
| List<VariableContext>? capturedContexts; | |
| /// The emitted value of non-sync functions | |
| /// | |
| /// For `async` functions [emittedValueType] is the future value type, that | |
| /// is, the returned element type. For instance | |
| /// | |
| /// Future<Foo> method1() async => new Foo(); | |
| /// FutureOr<Foo> method2() async => new Foo(); | |
| /// | |
| /// here the return types are `Future<Foo>` and `FutureOr<Foo>` for `method1` | |
| /// and `method2`, respectively, but the future value type is in both cases | |
| /// `Foo`. | |
| /// | |
| /// For pre-nnbd libraries, this is set to `flatten(T)` of the return type | |
| /// `T`, which can be seen as the pre-nnbd equivalent of the future value | |
| /// type. | |
| /// | |
| /// For `sync*` functions [emittedValueType] is the type of the element of the | |
| /// iterable returned by the function. | |
| /// | |
| /// For `async*` functions [emittedValueType] is the type of the element of | |
| /// the stream returned by the function. | |
| /// | |
| /// For sync functions (those not marked with one of `async`, `sync*`, or | |
| /// `async*`) the value of [emittedValueType] is null. | |
| DartType? emittedValueType; | |
| /// If the function is a redirecting factory constructor, this holds | |
| /// the target and type arguments of the redirection. | |
| RedirectingFactoryTarget? redirectingFactoryTarget; | |
| void Function()? lazyBuilder; | |
| void _buildLazy() { | |
| void Function()? lazyBuilderLocal = lazyBuilder; | |
| if (lazyBuilderLocal != null) { | |
| lazyBuilder = null; | |
| lazyBuilderLocal(); | |
| } | |
| } | |
| Statement? get body { | |
| _buildLazy(); | |
| return _body; | |
| } | |
| void set body(Statement? body) { | |
| _buildLazy(); | |
| _body = body; | |
| } | |
| new( | |
| this._body, { | |
| List<TypeParameter>? typeParameters, | |
| List<PositionalParameter>? positionalParameters, | |
| List<NamedParameter>? namedParameters, | |
| int? requiredParameterCount, | |
| this.returnType = const DynamicType(), | |
| this.asyncMarker = AsyncMarker.Sync, | |
| AsyncMarker? dartAsyncMarker, | |
| this.emittedValueType, | |
| this.thisVariable, | |
| }) : this.positionalParameters = positionalParameters ?? [], | |
| this.requiredParameterCount = | |
| requiredParameterCount ?? positionalParameters?.length ?? 0, | |
| this.namedParameters = namedParameters ?? [], | |
| this.typeParameters = typeParameters ?? <TypeParameter>[], | |
| this.dartAsyncMarker = dartAsyncMarker ?? asyncMarker { | |
| setParents(this.typeParameters, this); | |
| setParents(this.positionalParameters, this); | |
| setParents(this.namedParameters, this); | |
| thisVariable?.parent = this; | |
| _body?.parent = this; | |
| } | |
| static DartType _getTypeOfVariable(Variable node) => node.type; | |
| static NamedType _getNamedTypeOfVariable( | |
| NamedParameter node, [ | |
| Substitution? substitution, | |
| ]) { | |
| return new NamedType( | |
| node.parameterName, | |
| substitution != null ? substitution.substituteType(node.type) : node.type, | |
| isRequired: node.isRequired, | |
| ); | |
| } | |
| /// Returns the function type of the node reusing its type parameters. | |
| /// | |
| /// This getter works similarly to [functionType], but reuses type parameters | |
| /// of the function node (or the class enclosing it -- see the comment on | |
| /// [functionType] about constructors of generic classes) in the result. It | |
| /// is useful in some contexts, especially when reasoning about the function | |
| /// type of the enclosing generic function and in combination with | |
| /// [FunctionType.withoutTypeParameters]. | |
| FunctionType computeThisFunctionType(Nullability nullability) { | |
| TreeNode? parent = this.parent; | |
| List<TypeParameter> typeParametersToCopy = parent is Constructor | |
| ? parent.enclosingClass.typeParameters | |
| : typeParameters; | |
| // TODO(johnniwinther,cstefantsova): Cache the function type here and use | |
| // [DartType.withDeclaredNullability] to handle the variants. | |
| return computeFunctionTypeFromData( | |
| returnType: returnType, | |
| typeParameters: typeParametersToCopy, | |
| positionalParameters: positionalParameters, | |
| namedParameters: namedParameters, | |
| nullability: nullability, | |
| requiredParameterCount: requiredParameterCount, | |
| ); | |
| } | |
| /// Returns the function type of the function node. | |
| /// | |
| /// If the function node describes a generic function, the resulting function | |
| /// type will be generic. If the function node describes a constructor of a | |
| /// generic class, the resulting function type will be generic with its type | |
| /// parameters constructed after those of the class. In both cases, if the | |
| /// resulting function type is generic, a fresh set of type parameters is used | |
| /// in it. | |
| // TODO(johnniwinther,cstefantsova): Merge it with [computeThisFunctionType]. | |
| FunctionType computeFunctionType(Nullability nullability) { | |
| return computeThisFunctionType(nullability); | |
| } | |
| static FunctionType computeFunctionTypeFromData({ | |
| required DartType returnType, | |
| required List<TypeParameter> typeParameters, | |
| required List<PositionalParameter> positionalParameters, | |
| required List<NamedParameter> namedParameters, | |
| required Nullability nullability, | |
| required int requiredParameterCount, | |
| }) { | |
| StructuralParameterList structuralParameters; | |
| DartType functionReturnType; | |
| DartTypeList positionalParameterTypes; | |
| NamedDartTypeList namedParameterTypes; | |
| if (typeParameters.isEmpty) { | |
| structuralParameters = StructuralParameterList.empty; | |
| functionReturnType = returnType; | |
| positionalParameterTypes = DartTypeList.generate( | |
| positionalParameters.length, | |
| (index) => _getTypeOfVariable(positionalParameters[index]), | |
| ); | |
| if (namedParameters.isEmpty) { | |
| namedParameterTypes = NamedDartTypeList.empty; | |
| } else { | |
| namedParameterTypes = NamedDartTypeList.generate( | |
| namedParameters.length, | |
| (index) => _getNamedTypeOfVariable(namedParameters[index]), | |
| ); | |
| namedParameterTypes.sort(); | |
| } | |
| } else { | |
| // We need create a copy of the list of type parameters, otherwise | |
| // transformations like erasure don't work. | |
| FreshStructuralParametersFromTypeParameters freshStructuralParameters = | |
| getFreshStructuralParametersFromTypeParameters(typeParameters); | |
| structuralParameters = freshStructuralParameters.freshTypeParameters; | |
| Substitution substitution = freshStructuralParameters.substitution; | |
| functionReturnType = substitution.substituteType(returnType); | |
| positionalParameterTypes = DartTypeList.generate( | |
| positionalParameters.length, | |
| (index) => substitution.substituteType( | |
| _getTypeOfVariable(positionalParameters[index]), | |
| ), | |
| ); | |
| if (namedParameters.isEmpty) { | |
| namedParameterTypes = NamedDartTypeList.empty; | |
| } else { | |
| namedParameterTypes = NamedDartTypeList.generate( | |
| namedParameters.length, | |
| (index) => | |
| _getNamedTypeOfVariable(namedParameters[index], substitution), | |
| ); | |
| namedParameterTypes.sort(); | |
| } | |
| } | |
| return new FunctionType( | |
| positionalParameterTypes, | |
| functionReturnType, | |
| nullability, | |
| namedParameters: namedParameterTypes, | |
| typeParameters: structuralParameters, | |
| requiredParameterCount: requiredParameterCount, | |
| ); | |
| } | |
| @override | |
| R accept<R>(TreeVisitor<R> v) => v.visitFunctionNode(this); | |
| @override | |
| R accept1<R, A>(TreeVisitor1<R, A> v, A arg) => | |
| v.visitFunctionNode(this, arg); | |
| @override | |
| void visitChildren(Visitor v) { | |
| visitList(typeParameters, v); | |
| visitList(positionalParameters, v); | |
| visitList(namedParameters, v); | |
| returnType.accept(v); | |
| // TODO(cstefantsova): Uncomment the following. | |
| // thisVariable?.accept(v); | |
| emittedValueType?.accept(v); | |
| redirectingFactoryTarget?.target?.acceptReference(v); | |
| if (redirectingFactoryTarget?.typeArguments != null) { | |
| visitList(redirectingFactoryTarget!.typeArguments!, v); | |
| } | |
| body?.accept(v); | |
| } | |
| @override | |
| void transformChildren(Transformer v) { | |
| v.transformList(typeParameters, this); | |
| v.transformList(positionalParameters, this); | |
| v.transformList(namedParameters, this); | |
| returnType = v.visitDartType(returnType); | |
| // TODO(cstefantsova): Uncomment the following. | |
| // if (thisVariable != null) { | |
| // thisVariable = v.transform(thisVariable!)..parent = this; | |
| // } | |
| if (emittedValueType != null) { | |
| emittedValueType = v.visitDartType(emittedValueType!); | |
| } | |
| if (redirectingFactoryTarget?.typeArguments != null) { | |
| redirectingFactoryTarget!.typeArguments = v.transformDartTypeList( | |
| redirectingFactoryTarget!.typeArguments!, | |
| ); | |
| } | |
| if (body != null) { | |
| body = v.transform(body!); | |
| body?.parent = this; | |
| } | |
| } | |
| @override | |
| void transformOrRemoveChildren(RemovingTransformer v) { | |
| v.transformTypeParameterList(typeParameters, this); | |
| v.transformVariableList(positionalParameters, this); | |
| v.transformVariableList(namedParameters, this); | |
| returnType = v.visitDartType(returnType, cannotRemoveSentinel); | |
| if (thisVariable != null) { | |
| thisVariable = v.transformOrRemove(thisVariable!, dummyThisVariable) | |
| ?..parent = this; | |
| } | |
| if (emittedValueType != null) { | |
| emittedValueType = v.visitDartType( | |
| emittedValueType!, | |
| cannotRemoveSentinel, | |
| ); | |
| } | |
| if (redirectingFactoryTarget?.typeArguments != null) { | |
| redirectingFactoryTarget!.typeArguments = v.transformDartTypeList( | |
| redirectingFactoryTarget!.typeArguments!, | |
| ); | |
| } | |
| if (body != null) { | |
| body = v.transformOrRemoveStatement(body!); | |
| body?.parent = this; | |
| } | |
| } | |
| @override | |
| String toString() { | |
| return "FunctionNode(${toStringInternal()})"; | |
| } | |
| @override | |
| void toTextInternal(AstPrinter printer) { | |
| // TODO(johnniwinther): Implement this. | |
| } | |
| } | |
| enum AsyncMarker { | |
| // Do not change the order of these, the frontends depend on it. | |
| Sync, | |
| SyncStar, | |
| Async, | |
| AsyncStar, | |
| } |