23cde7f4b8
TEST=reformatting only Change-Id: I310e2568661e919a2c8354e01e19d18f7109f54c Cq-Include-Trybots: luci.dart.try:pkg-linux-debug-try,pkg-linux-release-try,pkg-mac-release-arm64-try,pkg-mac-release-try,pkg-win-release-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399261 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
154 lines
5.0 KiB
Dart
154 lines
5.0 KiB
Dart
// 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.
|
|
|
|
// This imports 'codes/cfe_codes.dart' instead of 'api_prototype/codes.dart' to
|
|
// avoid cyclic dependency between `package:vm/modular` and `package:front_end`.
|
|
import 'package:front_end/src/codes/cfe_codes.dart'
|
|
show
|
|
messageFfiDeeplyImmutableClassesMustBeFinalOrSealed,
|
|
messageFfiDeeplyImmutableFieldsModifiers,
|
|
messageFfiDeeplyImmutableFieldsMustBeDeeplyImmutable,
|
|
messageFfiDeeplyImmutableSubtypesMustBeDeeplyImmutable,
|
|
messageFfiDeeplyImmutableSupertypeMustBeDeeplyImmutable;
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
|
|
|
|
void validateLibraries(
|
|
List<Library> libraries,
|
|
CoreTypes coreTypes,
|
|
DiagnosticReporter diagnosticReporter,
|
|
) {
|
|
final validator = DeeplyImmutableValidator(coreTypes, diagnosticReporter);
|
|
for (final library in libraries) {
|
|
validator.visitLibrary(library);
|
|
}
|
|
}
|
|
|
|
/// Implements the `vm:deeply-immutable` semantics.
|
|
class DeeplyImmutableValidator {
|
|
static const vmDeeplyImmutable = "vm:deeply-immutable";
|
|
|
|
final CoreTypes coreTypes;
|
|
final DiagnosticReporter diagnosticReporter;
|
|
final Class pragmaClass;
|
|
final Field pragmaName;
|
|
|
|
DeeplyImmutableValidator(this.coreTypes, this.diagnosticReporter)
|
|
: pragmaClass = coreTypes.pragmaClass,
|
|
pragmaName = coreTypes.pragmaName;
|
|
|
|
void visitLibrary(Library library) {
|
|
for (final cls in library.classes) {
|
|
visitClass(cls);
|
|
}
|
|
}
|
|
|
|
void visitClass(Class node) {
|
|
_validateDeeplyImmutable(node);
|
|
}
|
|
|
|
void _validateDeeplyImmutable(Class node) {
|
|
if (!_isDeeplyImmutableClass(node)) {
|
|
// If class is not marked deeply immutable, check that none of the super
|
|
// types is marked deeply immutable.
|
|
final classes = [
|
|
if (node.superclass != null) node.superclass!,
|
|
for (final superType in node.implementedTypes) superType.classNode,
|
|
if (node.mixedInClass != null) node.mixedInClass!,
|
|
];
|
|
for (final superClass in classes) {
|
|
if (_isDeeplyImmutableClass(superClass)) {
|
|
diagnosticReporter.report(
|
|
messageFfiDeeplyImmutableSubtypesMustBeDeeplyImmutable,
|
|
node.fileOffset,
|
|
node.name.length,
|
|
node.location!.file,
|
|
);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
final superClass = node.superclass;
|
|
if (superClass != null && superClass != coreTypes.objectClass) {
|
|
if (!_isDeeplyImmutableClass(superClass)) {
|
|
diagnosticReporter.report(
|
|
messageFfiDeeplyImmutableSupertypeMustBeDeeplyImmutable,
|
|
node.fileOffset,
|
|
node.name.length,
|
|
node.location!.file,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Don't allow implementing, extending or mixing in deeply immutable classes
|
|
// in other libraries. Adding a `vm:deeply-immutable` pragma to a class that
|
|
// might be implemented, extended or mixed in would break subtypes that are
|
|
// not marked deeply immutable. (We could consider relaxing this and
|
|
// allowing breaking subtypes upon adding the pragma.)
|
|
if (!(node.isFinal || node.isSealed)) {
|
|
diagnosticReporter.report(
|
|
messageFfiDeeplyImmutableClassesMustBeFinalOrSealed,
|
|
node.fileOffset,
|
|
node.name.length,
|
|
node.location!.file,
|
|
);
|
|
}
|
|
|
|
// All instance fields should be non-late final and deeply immutable.
|
|
for (final field in node.fields) {
|
|
if (field.isStatic) {
|
|
// Static fields are not part of instances.
|
|
continue;
|
|
}
|
|
if (!_isDeeplyImmutableDartType(field.type)) {
|
|
diagnosticReporter.report(
|
|
messageFfiDeeplyImmutableFieldsMustBeDeeplyImmutable,
|
|
field.fileOffset,
|
|
field.name.text.length,
|
|
field.location!.file,
|
|
);
|
|
}
|
|
if (!field.isFinal || field.isLate) {
|
|
diagnosticReporter.report(
|
|
messageFfiDeeplyImmutableFieldsModifiers,
|
|
field.fileOffset,
|
|
field.name.text.length,
|
|
field.location!.file,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _isDeeplyImmutableDartType(DartType dartType) {
|
|
if (dartType is NullType) {
|
|
return true;
|
|
}
|
|
if (dartType is InterfaceType) {
|
|
final classNode = dartType.classNode;
|
|
return _isDeeplyImmutableClass(classNode);
|
|
}
|
|
if (dartType is TypeParameterType) {
|
|
return _isDeeplyImmutableDartType(dartType.bound);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool _isDeeplyImmutableClass(Class node) {
|
|
for (final annotation in node.annotations) {
|
|
if (annotation is ConstantExpression) {
|
|
final constant = annotation.constant;
|
|
if (constant is InstanceConstant &&
|
|
constant.classNode == pragmaClass &&
|
|
constant.fieldValues[pragmaName.fieldReference] ==
|
|
StringConstant(vmDeeplyImmutable)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|