blob: f6a52b5a6ccbc4d6f6efb6980bbdcfc2776c471f [file] [log] [blame]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of 'body_builder.dart';
// TODO(ahe): Remove this and ensure all nodes have a location.
const int noLocation = TreeNode.noOffset;
enum JumpTargetKind {
Break,
Continue,
Goto, // Continue label in switch.
}
class Operator {
final Token token;
String get name => token.stringValue!;
final int charOffset;
Operator(this.token, this.charOffset);
@override
String toString() => "operator($name)";
}
class JumpTarget {
final List<Statement> users = <Statement>[];
final JumpTargetKind kind;
final int functionNestingLevel;
final Uri fileUri;
final int charOffset;
JumpTarget(
this.kind,
this.functionNestingLevel,
this.fileUri,
this.charOffset,
);
bool get isBreakTarget => kind == JumpTargetKind.Break;
bool get isContinueTarget => kind == JumpTargetKind.Continue;
bool get isGotoTarget => kind == JumpTargetKind.Goto;
bool get hasUsers => users.isNotEmpty;
void addBreak(Statement statement) {
assert(isBreakTarget);
users.add(statement);
}
void addContinue(Statement statement) {
assert(isContinueTarget);
users.add(statement);
}
void addGoto(Statement statement) {
assert(isGotoTarget);
users.add(statement);
}
void resolveBreaks(
Forest forest,
LabeledStatement target,
Statement targetStatement,
) {
assert(isBreakTarget);
for (Statement user in users) {
BreakStatementImpl breakStatement = user as BreakStatementImpl;
breakStatement.target = target;
breakStatement.targetStatement = targetStatement;
}
users.clear();
}
List<BreakStatementImpl>? resolveContinues(
Forest forest,
LabeledStatement target,
) {
assert(isContinueTarget);
List<BreakStatementImpl> statements = <BreakStatementImpl>[];
for (Statement user in users) {
BreakStatementImpl breakStatement = user as BreakStatementImpl;
breakStatement.target = target;
statements.add(breakStatement);
}
users.clear();
return statements;
}
void resolveGotos(Forest forest, SwitchCase target) {
assert(isGotoTarget);
for (Statement user in users) {
ContinueSwitchStatement continueSwitchStatement =
user as ContinueSwitchStatement;
continueSwitchStatement.target = target;
}
users.clear();
}
}
class LabelTarget implements JumpTarget {
final JumpTarget breakTarget;
final JumpTarget continueTarget;
@override
final int functionNestingLevel;
@override
final Uri fileUri;
@override
final int charOffset;
LabelTarget(this.functionNestingLevel, this.fileUri, this.charOffset)
: breakTarget = new JumpTarget(
JumpTargetKind.Break,
functionNestingLevel,
fileUri,
charOffset,
),
continueTarget = new JumpTarget(
JumpTargetKind.Continue,
functionNestingLevel,
fileUri,
charOffset,
);
@override
// Coverage-ignore(suite): Not run.
bool get hasUsers => breakTarget.hasUsers || continueTarget.hasUsers;
@override
// Coverage-ignore(suite): Not run.
List<Statement> get users => unsupported("users", charOffset, fileUri);
@override
// Coverage-ignore(suite): Not run.
JumpTargetKind get kind => unsupported("kind", charOffset, fileUri);
@override
bool get isBreakTarget => true;
@override
bool get isContinueTarget => true;
@override
bool get isGotoTarget => false;
@override
void addBreak(Statement statement) {
breakTarget.addBreak(statement);
}
@override
void addContinue(Statement statement) {
continueTarget.addContinue(statement);
}
@override
// Coverage-ignore(suite): Not run.
void addGoto(Statement statement) {
unsupported("addGoto", charOffset, fileUri);
}
@override
// Coverage-ignore(suite): Not run.
void resolveBreaks(
Forest forest,
LabeledStatement target,
Statement targetStatement,
) {
breakTarget.resolveBreaks(forest, target, targetStatement);
}
@override
// Coverage-ignore(suite): Not run.
List<BreakStatementImpl>? resolveContinues(
Forest forest,
LabeledStatement target,
) {
return continueTarget.resolveContinues(forest, target);
}
@override
// Coverage-ignore(suite): Not run.
void resolveGotos(Forest forest, SwitchCase target) {
unsupported("resolveGotos", charOffset, fileUri);
}
}
class FunctionTypeParameters {
final List<ParameterBuilder>? parameters;
final int charOffset;
final int length;
final Uri uri;
FunctionTypeParameters(
this.parameters,
this.charOffset,
this.length,
this.uri,
) {
if (parameters?.isEmpty ?? false) {
throw "Empty parameters should be null";
}
}
TypeBuilder toFunctionType(
TypeBuilder returnType,
NullabilityBuilder nullabilityBuilder, {
List<StructuralParameterBuilder>? structuralVariableBuilders,
required bool hasFunctionFormalParameterSyntax,
}) {
return new FunctionTypeBuilderImpl(
returnType,
structuralVariableBuilders,
parameters,
nullabilityBuilder,
uri,
charOffset,
hasFunctionFormalParameterSyntax: hasFunctionFormalParameterSyntax,
);
}
@override
String toString() {
return "FormalParameters($parameters, $charOffset, $uri)";
}
}
class FormalParameters {
final List<FormalParameterBuilder>? parameters;
final int charOffset;
final int length;
final Uri uri;
FormalParameters(this.parameters, this.charOffset, this.length, this.uri) {
if (parameters?.isEmpty ?? false) {
throw "Empty parameters should be null";
}
}
FunctionNode buildFunctionNode(
SourceLibraryBuilder library,
TypeBuilder? returnTypeBuilder,
List<NominalParameterBuilder>? typeParameterBuilders,
AsyncMarker asyncModifier,
Statement body,
int fileEndOffset,
) {
DartType returnType =
returnTypeBuilder?.build(library, TypeUse.returnType) ??
const DynamicType();
int requiredParameterCount = 0;
List<VariableDeclaration> positionalParameters = <VariableDeclaration>[];
List<VariableDeclaration> namedParameters = <VariableDeclaration>[];
if (parameters != null) {
for (FormalParameterBuilder formal in parameters!) {
VariableDeclaration parameter = formal.build(library);
if (formal.isPositional) {
positionalParameters.add(parameter);
if (formal.isRequiredPositional) requiredParameterCount++;
} else if (formal.isNamed) {
namedParameters.add(parameter);
}
}
namedParameters.sort((VariableDeclaration a, VariableDeclaration b) {
return a.name!.compareTo(b.name!);
});
}
List<TypeParameter>? typeParameters;
if (typeParameterBuilders != null) {
typeParameters = <TypeParameter>[];
for (NominalParameterBuilder t in typeParameterBuilders) {
typeParameters.add(t.parameter);
// Build the bound to detect cycles in typedefs.
t.bound?.build(library, TypeUse.typeParameterBound);
}
}
return new FunctionNode(
body,
typeParameters: typeParameters,
positionalParameters: positionalParameters,
namedParameters: namedParameters,
requiredParameterCount: requiredParameterCount,
returnType: returnType,
asyncMarker: asyncModifier,
)
..fileOffset = charOffset
..fileEndOffset = fileEndOffset;
}
LocalScope computeFormalParameterScope(
LocalScope parent,
ExpressionGeneratorHelper helper, {
bool wildcardVariablesEnabled = false,
}) {
if (parameters == null) return parent;
assert(parameters!.isNotEmpty);
Map<String, VariableBuilder> local = {};
for (FormalParameterBuilder parameter in parameters!) {
// Avoid having wildcard parameters in scope.
if (wildcardVariablesEnabled && parameter.isWildcard) continue;
Builder? existing = local[parameter.name];
if (existing != null) {
helper.reportDuplicatedDeclaration(
existing,
parameter.name,
parameter.fileOffset,
);
} else {
local[parameter.name] = parameter;
}
}
return parent.createNestedFixedScope(
kind: LocalScopeKind.formals,
local: local,
);
}
@override
String toString() {
return "FormalParameters($parameters, $charOffset, $uri)";
}
}
/// Returns a block like this:
///
/// {
/// statement;
/// body;
/// }
///
/// If [body] is a [Block], it's returned with [statement] prepended to it.
Block combineStatements(Statement statement, Statement body) {
if (body is Block) {
if (statement is Block) {
body.statements.insertAll(0, statement.statements);
setParents(statement.statements, body);
} else {
body.statements.insert(0, statement);
statement.parent = body;
}
return body;
} else {
return new Block(<Statement>[
if (statement is Block) ...statement.statements else statement,
body,
])..fileOffset = statement.fileOffset;
}
}
/// DartDocTest(
/// debugName("myClassName", "myName"),
/// "myClassName.myName"
/// )
/// DartDocTest(
/// debugName("myClassName", ""),
/// "myClassName"
/// )
/// DartDocTest(
/// debugName("", ""),
/// ""
/// )
String debugName(String className, String name) {
return name.isEmpty ? className : "$className.$name";
}
/// A data holder used to hold the information about a label that is pushed on
/// the stack.
class Label {
String name;
int charOffset;
Label(this.name, this.charOffset);
@override
String toString() => "label($name)";
}
class ForInElements {
VariableDeclaration? explicitVariableDeclaration;
VariableDeclaration? syntheticVariableDeclaration;
Expression? syntheticAssignment;
Expression? expressionProblem;
Statement? expressionEffects;
VariableDeclaration get variable =>
(explicitVariableDeclaration ?? syntheticVariableDeclaration)!;
}
class Condition {
final Expression expression;
final PatternGuard? patternGuard;
Condition(this.expression, [this.patternGuard]);
@override
String toString() =>
'Condition($expression'
'${patternGuard != null ? ',$patternGuard' : ''})';
}
final ExpressionOrPatternGuardCase dummyExpressionOrPatternGuardCase =
new ExpressionOrPatternGuardCase.expression(
TreeNode.noOffset,
dummyExpression,
);
class ExpressionOrPatternGuardCase {
final int caseOffset;
final Expression? expression;
final PatternGuard? patternGuard;
ExpressionOrPatternGuardCase.expression(
this.caseOffset,
Expression this.expression,
) : patternGuard = null;
ExpressionOrPatternGuardCase.patternGuard(
this.caseOffset,
PatternGuard this.patternGuard,
) : expression = null;
}
extension on MemberKind {
bool get isFunctionType {
switch (this) {
case MemberKind.FunctionTypeAlias:
case MemberKind.FunctionTypedParameter:
case MemberKind.GeneralizedFunctionType:
return true;
case MemberKind.Catch:
case MemberKind.Factory:
case MemberKind.Local:
case MemberKind.NonStaticMethod:
case MemberKind.StaticMethod:
case MemberKind.TopLevelMethod:
case MemberKind.ExtensionNonStaticMethod:
case MemberKind.ExtensionStaticMethod:
case MemberKind.ExtensionTypeNonStaticMethod:
case MemberKind.ExtensionTypeStaticMethod:
case MemberKind.NonStaticField:
case MemberKind.StaticField:
case MemberKind.TopLevelField:
case MemberKind.PrimaryConstructor:
return false;
}
}
}
/// Annotations that needs to be inferred about the body has been inferred.
class PendingAnnotations {
final List<SingleTargetAnnotations>? singleTargetAnnotations;
final List<MultiTargetAnnotations>? multiTargetAnnotations;
PendingAnnotations(this.singleTargetAnnotations, this.multiTargetAnnotations);
}
/// A single target holding annotations to be inferred.
class SingleTargetAnnotations {
final Annotatable target;
final List<int>? indicesOfAnnotationsToBeInferred;
SingleTargetAnnotations(this.target, [this.indicesOfAnnotationsToBeInferred]);
}
/// A multiple targets holding annotations to be inferred.
///
/// The annotations are on the first target and needs to be cloned to the
/// subsequent targets after inference.
class MultiTargetAnnotations {
final List<Annotatable> targets;
MultiTargetAnnotations(this.targets);
}
class BuildInitializersResult {
final List<Initializer>? initializers;
final bool needsImplicitSuperInitializer;
final PendingAnnotations? annotations;
BuildInitializersResult(
this.initializers,
this.needsImplicitSuperInitializer,
this.annotations,
);
}
class BuildParameterInitializerResult {
final Expression initializer;
final PendingAnnotations? annotations;
BuildParameterInitializerResult(this.initializer, this.annotations);
}
class BuildRedirectingFactoryMethodResult {
final PendingAnnotations? annotations;
BuildRedirectingFactoryMethodResult(this.annotations);
}
class BuildFieldsResult {
final Map<Identifier, Expression?> fieldInitializers;
final PendingAnnotations? annotations;
BuildFieldsResult(this.fieldInitializers, this.annotations);
}
class BuildPrimaryConstructorResult {
final FormalParameters? formals;
final PendingAnnotations? annotations;
BuildPrimaryConstructorResult(this.formals, this.annotations);
}
class BuildFunctionBodyResult {
final FormalParameters? formals;
final AsyncMarker asyncModifier;
final Statement? body;
final List<Initializer>? initializers;
final bool needsImplicitSuperInitializer;
final PendingAnnotations? annotations;
BuildFunctionBodyResult({
required this.formals,
required this.asyncModifier,
required this.body,
required this.initializers,
required this.needsImplicitSuperInitializer,
required this.annotations,
});
}
class BuildMetadataListResult {
final List<Expression> expressions;
final PendingAnnotations? annotations;
BuildMetadataListResult(this.expressions, this.annotations);
}
class BuildFieldInitializerResult {
final Expression initializer;
final PendingAnnotations? annotations;
BuildFieldInitializerResult(this.initializer, this.annotations);
}
class BuildEnumConstantResult {
final ArgumentsImpl arguments;
final PendingAnnotations? annotations;
BuildEnumConstantResult(this.arguments, this.annotations);
}
// Coverage-ignore(suite): Not run.
class BuildSingleExpressionResult {
final Expression expression;
final PendingAnnotations? annotations;
BuildSingleExpressionResult(this.expression, this.annotations);
}