blob: 25bd0fff03c0bfb1b1775e3c3bb48f97d5dc6a6c [file] [log] [blame]
// Copyright (c) 2023, 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.
/// Handling of static weak references.
import 'package:kernel/ast.dart';
import 'package:kernel/core_types.dart' show CoreTypes;
import '../codes/cfe_codes.dart'
show
codeWeakReferenceNotStatic,
codeWeakReferenceNotOneArgument,
codeWeakReferenceReturnTypeNotNullable,
codeWeakReferenceMismatchReturnAndArgumentTypes,
codeWeakReferenceTargetNotStaticTearoff,
codeWeakReferenceTargetHasParameters;
import 'constant_evaluator.dart' show ErrorReporter;
/// Recognizes and validates static weak references.
/// Intrinsic method for static weak references can be
/// declared using `@pragma('weak-tearoff-reference')`.
class StaticWeakReferences {
static const String weakTearoffReferencePragma = 'weak-tearoff-reference';
// Coverage-ignore(suite): Not run.
static bool isWeakReference(StaticInvocation node) =>
node.target.hasWeakTearoffReferencePragma;
static bool isAnnotatedWithWeakReferencePragma(
Annotatable node,
CoreTypes coreTypes,
) {
List<Expression> annotations = node.annotations;
for (int i = 0; i < annotations.length; i++) {
Expression annotation = annotations[i];
if (annotation is ConstantExpression) {
Constant constant = annotation.constant;
if (constant is InstanceConstant) {
if (constant.classNode == coreTypes.pragmaClass) {
Constant? name =
constant.fieldValues[coreTypes.pragmaName.fieldReference];
if (name is StringConstant) {
if (name.value == weakTearoffReferencePragma) {
return true;
}
}
}
}
}
}
return false;
}
static void validateWeakReferenceUse(
StaticInvocation node,
ErrorReporter errorReporter,
) {
final Arguments arguments = node.arguments;
if (arguments.positional.length != 1 || arguments.named.isNotEmpty) {
// Coverage-ignore-block(suite): Not run.
errorReporter.report(
codeWeakReferenceNotOneArgument.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
return;
}
final Expression arg = arguments.positional.single;
if (arg is ConstantExpression) {
final Constant constant = arg.constant;
if (constant is StaticTearOffConstant) {
final Procedure target = constant.target;
if (target.isStatic) {
final FunctionNode function = target.function;
if (function.positionalParameters.isNotEmpty ||
// Coverage-ignore(suite): Not run.
function.namedParameters.isNotEmpty ||
// Coverage-ignore(suite): Not run.
function.typeParameters.isNotEmpty) {
errorReporter.report(
codeWeakReferenceTargetHasParameters.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
}
return;
}
}
}
errorReporter.report(
codeWeakReferenceTargetNotStaticTearoff.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
}
static void validateWeakReferenceDeclaration(
Annotatable node,
ErrorReporter errorReporter,
) {
if (node is! Procedure ||
!node.isStatic ||
node.kind != ProcedureKind.Method) {
errorReporter.report(
codeWeakReferenceNotStatic.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
return;
}
final FunctionNode function = node.function;
if (function.positionalParameters.length != 1 ||
function.requiredParameterCount != 1 ||
function.namedParameters.isNotEmpty) {
errorReporter.report(
codeWeakReferenceNotOneArgument.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
return;
}
final DartType returnType = function.returnType;
if (returnType.nullability != Nullability.nullable) {
errorReporter.report(
codeWeakReferenceReturnTypeNotNullable.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
}
if (returnType != function.positionalParameters.single.type) {
errorReporter.report(
codeWeakReferenceMismatchReturnAndArgumentTypes.withLocation(
node.location!.file,
node.fileOffset,
1,
),
);
}
node.hasWeakTearoffReferencePragma = true;
}
// Coverage-ignore(suite): Not run.
// Returns argument expression of the weak reference.
// Assumes weak reference is valid.
static Expression getWeakReferenceArgument(StaticInvocation node) {
assert(isWeakReference(node));
return node.arguments.positional.single;
}
// Coverage-ignore(suite): Not run.
// Returns target method of the weak reference.
// Assumes weak reference is valid.
static Procedure getWeakReferenceTarget(StaticInvocation node) {
final Expression arg = getWeakReferenceArgument(node);
return ((arg as ConstantExpression).constant as StaticTearOffConstant)
.target;
}
}