blob: 710a98c9287d0e48645124155cacdb7f596766c9 [file] [log] [blame] [edit]
// 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 {
/// 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<VariableDeclaration> positionalParameters;
List<VariableDeclaration> namedParameters;
DartType returnType; // Not null.
Statement? _body;
/// 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;
}
FunctionNode(this._body,
{List<TypeParameter>? typeParameters,
List<VariableDeclaration>? positionalParameters,
List<VariableDeclaration>? namedParameters,
int? requiredParameterCount,
this.returnType = const DynamicType(),
this.asyncMarker = AsyncMarker.Sync,
AsyncMarker? dartAsyncMarker,
this.emittedValueType})
: this.positionalParameters =
positionalParameters ?? <VariableDeclaration>[],
this.requiredParameterCount =
requiredParameterCount ?? positionalParameters?.length ?? 0,
this.namedParameters = namedParameters ?? <VariableDeclaration>[],
this.typeParameters = typeParameters ?? <TypeParameter>[],
this.dartAsyncMarker = dartAsyncMarker ?? asyncMarker {
setParents(this.typeParameters, this);
setParents(this.positionalParameters, this);
setParents(this.namedParameters, this);
_body?.parent = this;
}
static DartType _getTypeOfVariable(VariableDeclaration node) => node.type;
static NamedType _getNamedTypeOfVariable(VariableDeclaration node,
[Substitution? substitution]) {
return new NamedType(
node.name!,
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,
{bool reuseTypeParameters = false}) {
TreeNode? parent = this.parent;
List<StructuralParameter> structuralParameters;
List<TypeParameter> typeParametersToCopy = parent is Constructor
? parent.enclosingClass.typeParameters
: typeParameters;
DartType returnType;
List<DartType> positionalParameters;
List<NamedType> namedParameters;
if (typeParametersToCopy.isEmpty || reuseTypeParameters) {
structuralParameters = const <StructuralParameter>[];
returnType = this.returnType;
List<VariableDeclaration> thisPositionals = this.positionalParameters;
positionalParameters = List.generate(thisPositionals.length,
(index) => _getTypeOfVariable(thisPositionals[index]),
growable: false);
List<VariableDeclaration> thisNamed = this.namedParameters;
if (thisNamed.isEmpty) {
namedParameters = const <NamedType>[];
} else {
namedParameters = List.generate(thisNamed.length,
(index) => _getNamedTypeOfVariable(thisNamed[index]),
growable: false);
namedParameters.sort();
}
} else {
// We need create a copy of the list of type parameters, otherwise
// transformations like erasure don't work.
FreshStructuralParametersFromTypeParameters freshStructuralParameters =
getFreshStructuralParametersFromTypeParameters(typeParametersToCopy);
structuralParameters = freshStructuralParameters.freshTypeParameters;
Substitution substitution = freshStructuralParameters.substitution;
returnType = substitution.substituteType(this.returnType);
List<VariableDeclaration> thisPositionals = this.positionalParameters;
positionalParameters = List.generate(
thisPositionals.length,
(index) => substitution
.substituteType(_getTypeOfVariable(thisPositionals[index])),
growable: false);
List<VariableDeclaration> thisNamed = this.namedParameters;
if (thisNamed.isEmpty) {
namedParameters = const <NamedType>[];
} else {
namedParameters = List.generate(thisNamed.length,
(index) => _getNamedTypeOfVariable(thisNamed[index], substitution),
growable: false);
namedParameters.sort();
}
}
// TODO(johnniwinther,cstefantsova): Cache the function type here and use
// [DartType.withDeclaredNullability] to handle the variants.
return new FunctionType(positionalParameters, returnType, nullability,
namedParameters: namedParameters,
typeParameters: structuralParameters,
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);
}
@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);
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);
if (emittedValueType != null) {
emittedValueType = v.visitDartType(emittedValueType!);
}
if (redirectingFactoryTarget?.typeArguments != null) {
v.transformDartTypeList(redirectingFactoryTarget!.typeArguments!);
}
if (body != null) {
body = v.transform(body!);
body?.parent = this;
}
}
@override
void transformOrRemoveChildren(RemovingTransformer v) {
v.transformTypeParameterList(typeParameters, this);
v.transformVariableDeclarationList(positionalParameters, this);
v.transformVariableDeclarationList(namedParameters, this);
returnType = v.visitDartType(returnType, cannotRemoveSentinel);
if (emittedValueType != null) {
emittedValueType =
v.visitDartType(emittedValueType!, cannotRemoveSentinel);
}
if (redirectingFactoryTarget?.typeArguments != null) {
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,
}