blob: e6fdc26c04813fae76c8f6688a9120c4c8df4c2b [file]
// 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:front_end/src/api_unstable/vm.dart'
show
messageWeakReferenceNotStatic,
messageWeakReferenceNotOneArgument,
messageWeakReferenceReturnTypeNotNullable,
messageWeakReferenceMismatchReturnAndArgumentTypes,
messageWeakReferenceTargetNotStaticTearoff,
messageWeakReferenceTargetHasParameters;
import 'package:kernel/ast.dart';
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
import 'package:vm/transformations/pragma.dart'
show ParsedWeakTearoffReference, PragmaAnnotationParser;
/// Recognizes and validates static weak references.
class StaticWeakReferences {
final PragmaAnnotationParser _annotationParser;
StaticWeakReferences(this._annotationParser);
bool isWeakReference(StaticInvocation node) =>
_isAnnotatedWithWeakReferencePragma(node.target);
bool isWeakReferenceDeclaration(Member node) =>
_isAnnotatedWithWeakReferencePragma(node);
bool _isAnnotatedWithWeakReferencePragma(Member m) {
for (final annotation in m.annotations) {
if (_annotationParser.parsePragma(annotation)
is ParsedWeakTearoffReference) {
return true;
}
}
return false;
}
void validateWeakReference(
StaticInvocation node, DiagnosticReporter diagnosticReporter) {
assert(isWeakReference(node));
final arguments = node.arguments;
if (arguments.positional.length != 1 || arguments.named.isNotEmpty) {
diagnosticReporter.report(messageWeakReferenceNotOneArgument,
node.fileOffset, 1, node.location?.file);
return;
}
final arg = arguments.positional.single;
if (arg is ConstantExpression) {
final constant = arg.constant;
if (constant is StaticTearOffConstant) {
final target = constant.target;
if (target.isStatic) {
final function = target.function;
if (function.positionalParameters.isNotEmpty ||
function.namedParameters.isNotEmpty ||
function.typeParameters.isNotEmpty) {
diagnosticReporter.report(messageWeakReferenceTargetHasParameters,
node.fileOffset, 1, node.location?.file);
}
return;
}
}
}
diagnosticReporter.report(messageWeakReferenceTargetNotStaticTearoff,
node.fileOffset, 1, node.location?.file);
}
void validateWeakReferenceDeclaration(
Member node, DiagnosticReporter diagnosticReporter) {
assert(isWeakReferenceDeclaration(node));
if (node is! Procedure ||
!node.isStatic ||
node.kind != ProcedureKind.Method) {
diagnosticReporter.report(messageWeakReferenceNotStatic, node.fileOffset,
1, node.location?.file);
return;
}
final function = node.function;
if (function.positionalParameters.length != 1 ||
function.requiredParameterCount != 1 ||
function.namedParameters.isNotEmpty) {
diagnosticReporter.report(messageWeakReferenceNotOneArgument,
node.fileOffset, 1, node.location?.file);
return;
}
final returnType = function.returnType;
if (returnType.nullability != Nullability.nullable) {
diagnosticReporter.report(messageWeakReferenceReturnTypeNotNullable,
node.fileOffset, 1, node.location?.file);
}
if (returnType != function.positionalParameters.single.type) {
diagnosticReporter.report(
messageWeakReferenceMismatchReturnAndArgumentTypes,
node.fileOffset,
1,
node.location?.file);
}
}
// Returns argument expression of the weak reference.
// Assumes weak reference is valid.
Expression getWeakReferenceArgument(StaticInvocation node) {
assert(isWeakReference(node));
return node.arguments.positional.single;
}
// Returns target method of the weak reference.
// Assumes weak reference is valid.
Procedure getWeakReferenceTarget(StaticInvocation node) {
final arg = getWeakReferenceArgument(node);
return ((arg as ConstantExpression).constant as StaticTearOffConstant)
.target;
}
}