blob: 17b35726b969a75a7a56e891f4e77af728cfc688 [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.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/fine/lookup_name.dart';
import 'package:analyzer/src/fine/manifest_ast.dart';
import 'package:analyzer/src/fine/manifest_context.dart';
import 'package:analyzer/src/fine/manifest_id.dart';
import 'package:analyzer/src/fine/manifest_type.dart';
import 'package:analyzer/src/summary2/data_reader.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:meta/meta.dart';
class ClassItem extends InterfaceItem<ClassElementImpl2> {
ClassItem({
required super.id,
required super.metadata,
required super.typeParameters,
required super.supertype,
required super.mixins,
required super.interfaces,
required super.declaredMembers,
required super.inheritedMembers,
});
factory ClassItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ClassElementImpl2 element,
}) {
return context.withTypeParameters(element.typeParameters2, (
typeParameters,
) {
return ClassItem(
id: id,
metadata: ManifestMetadata.encode(context, element.metadata2),
typeParameters: typeParameters,
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
declaredMembers: {},
inheritedMembers: {},
);
});
}
factory ClassItem.read(SummaryDataReader reader) {
return ClassItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
declaredMembers: InstanceItem._readDeclaredMembers(reader),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
inheritedMembers: InterfaceItem._readInheritedMembers(reader),
);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ManifestItemKind.class_);
super.write(sink);
}
}
/// The item for [InstanceElementImpl2].
sealed class InstanceItem<E extends InstanceElementImpl2>
extends TopLevelItem<E> {
final List<ManifestTypeParameter> typeParameters;
final Map<LookupName, InstanceItemMemberItem> declaredMembers;
InstanceItem({
required super.id,
required super.metadata,
required this.typeParameters,
required this.declaredMembers,
});
ManifestItemId? getMemberId(LookupName name) {
return declaredMembers[name]?.id;
}
/// If there is already a declared member with [name], replace it with a
/// fresh instance of [InstanceItemDuplicateItem] and return `true`.
/// Otherwise return `false`.
bool replaceDuplicate(LookupName name) {
if (declaredMembers.containsKey(name)) {
declaredMembers[name] = InstanceItemDuplicateItem();
return true;
}
return false;
}
@override
void write(BufferedSink sink) {
super.write(sink);
typeParameters.writeList(sink);
sink.writeMap(
declaredMembers,
writeKey: (name) => name.write(sink),
writeValue: (member) => member.writeWithKind(sink),
);
}
static Map<LookupName, InstanceItemMemberItem> _readDeclaredMembers(
SummaryDataReader reader,
) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => InstanceItemMemberItem.read(reader),
);
}
}
/// Placeholder for a name that has duplicate declared member.
/// It is always given a new ID.
class InstanceItemDuplicateItem
extends InstanceItemMemberItem<ExecutableElementImpl2> {
factory InstanceItemDuplicateItem() {
return InstanceItemDuplicateItem._(
id: ManifestItemId.generate(),
metadata: ManifestMetadata(annotations: []),
isStatic: false,
);
}
factory InstanceItemDuplicateItem.read(SummaryDataReader reader) {
return InstanceItemDuplicateItem._(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
isStatic: reader.readBool(),
);
}
InstanceItemDuplicateItem._({
required super.id,
required super.metadata,
required super.isStatic,
});
@override
bool match(MatchContext context, ExecutableElementImpl2 element) {
super.match(context, element); // we ignore it
// Duplicate items don't have any specific information, they are just
// a flag, that previously the same name was declared twice. So, there is
// no way to meaningfully match them. We always return `false`, and
// build a new item (and ID) for this name.
//
// The reason is that we don't want to prefer first or last duplicated
// element, we just wait until there is no error. And once it is fixed,
// we will build the actual item, with a new ID, and recompute all uses
// once again.
return false;
}
@override
void writeKind(BufferedSink sink) {
sink.writeEnum(_InstanceItemMemberItemKind.duplicate);
}
}
class InstanceItemGetterItem extends InstanceItemMemberItem<GetterElementImpl> {
final ManifestType returnType;
final ManifestNode? constInitializer;
InstanceItemGetterItem({
required super.id,
required super.metadata,
required super.isStatic,
required this.returnType,
required this.constInitializer,
});
factory InstanceItemGetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required GetterElementImpl element,
}) {
return InstanceItemGetterItem(
id: id,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
isStatic: element.isStatic,
returnType: element.returnType.encode(context),
constInitializer: element.constInitializer?.encode(context),
);
}
factory InstanceItemGetterItem.read(SummaryDataReader reader) {
return InstanceItemGetterItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
isStatic: reader.readBool(),
returnType: ManifestType.read(reader),
constInitializer: ManifestNode.readOptional(reader),
);
}
@override
bool match(MatchContext context, GetterElementImpl element) {
return super.match(context, element) &&
returnType.match(context, element.returnType) &&
constInitializer.match(context, element.constInitializer);
}
@override
void write(BufferedSink sink) {
super.write(sink);
returnType.write(sink);
constInitializer.writeOptional(sink);
}
@override
void writeKind(BufferedSink sink) {
sink.writeEnum(_InstanceItemMemberItemKind.getter);
}
}
sealed class InstanceItemMemberItem<E extends ExecutableElementImpl2>
extends ManifestItem<E> {
final bool isStatic;
InstanceItemMemberItem({
required super.id,
required super.metadata,
required this.isStatic,
});
@override
bool match(MatchContext context, E element) {
return super.match(context, element) && element.isStatic == isStatic;
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isStatic);
}
void writeKind(BufferedSink sink);
void writeWithKind(BufferedSink sink) {
writeKind(sink);
write(sink);
}
static InstanceItemMemberItem<ExecutableElementImpl2> read(
SummaryDataReader reader,
) {
var kind = reader.readEnum(_InstanceItemMemberItemKind.values);
switch (kind) {
case _InstanceItemMemberItemKind.duplicate:
return InstanceItemDuplicateItem.read(reader);
case _InstanceItemMemberItemKind.getter:
return InstanceItemGetterItem.read(reader);
case _InstanceItemMemberItemKind.method:
return InstanceItemMethodItem.read(reader);
case _InstanceItemMemberItemKind.setter:
return InstanceItemSetterItem.read(reader);
case _InstanceItemMemberItemKind.constructor:
return InterfaceItemConstructorItem.read(reader);
}
}
}
class InstanceItemMethodItem
extends InstanceItemMemberItem<MethodElementImpl2> {
final ManifestFunctionType functionType;
InstanceItemMethodItem({
required super.id,
required super.metadata,
required super.isStatic,
required this.functionType,
});
factory InstanceItemMethodItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required MethodElementImpl2 element,
}) {
return InstanceItemMethodItem(
id: id,
metadata: ManifestMetadata.encode(context, element.metadata2),
isStatic: element.isStatic,
functionType: element.type.encode(context),
);
}
factory InstanceItemMethodItem.read(SummaryDataReader reader) {
return InstanceItemMethodItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
);
}
@override
bool match(MatchContext context, MethodElementImpl2 element) {
return super.match(context, element) &&
functionType.match(context, element.type);
}
@override
void write(BufferedSink sink) {
super.write(sink);
functionType.writeNoTag(sink);
}
@override
void writeKind(BufferedSink sink) {
sink.writeEnum(_InstanceItemMemberItemKind.method);
}
}
class InstanceItemSetterItem extends InstanceItemMemberItem<SetterElementImpl> {
final ManifestType valueType;
InstanceItemSetterItem({
required super.id,
required super.metadata,
required super.isStatic,
required this.valueType,
});
factory InstanceItemSetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required SetterElementImpl element,
}) {
return InstanceItemSetterItem(
id: id,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
isStatic: element.isStatic,
valueType: element.formalParameters[0].type.encode(context),
);
}
factory InstanceItemSetterItem.read(SummaryDataReader reader) {
return InstanceItemSetterItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
isStatic: reader.readBool(),
valueType: ManifestType.read(reader),
);
}
@override
bool match(MatchContext context, SetterElementImpl element) {
return super.match(context, element) &&
valueType.match(context, element.formalParameters[0].type);
}
@override
void write(BufferedSink sink) {
super.write(sink);
valueType.write(sink);
}
@override
void writeKind(BufferedSink sink) {
sink.writeEnum(_InstanceItemMemberItemKind.setter);
}
}
/// The item for [InterfaceElementImpl2].
sealed class InterfaceItem<E extends InterfaceElementImpl2>
extends InstanceItem<E> {
final ManifestType? supertype;
final List<ManifestType> interfaces;
final List<ManifestType> mixins;
/// We store only IDs of the inherited members, but not type substitutions,
/// because in order to invoke any of these members, you need an instance
/// of the class for this [InterfaceItem]. And any code that can give such
/// instance will reference the class name, directly as a type annotation, or
/// indirectly by invoking a function that references the class as a return
/// type. So, any such code depends on the header of the class, so includes
/// the type arguments for the class that declares the inherited member.
final Map<LookupName, ManifestItemId> inheritedMembers;
InterfaceItem({
required super.id,
required super.metadata,
required super.typeParameters,
required super.declaredMembers,
required this.supertype,
required this.mixins,
required this.interfaces,
required this.inheritedMembers,
});
@override
ManifestItemId? getMemberId(LookupName name) {
return declaredMembers[name]?.id ?? inheritedMembers[name];
}
@override
bool match(MatchContext context, E element) {
context.addTypeParameters(element.typeParameters2);
return super.match(context, element) &&
supertype.match(context, element.supertype) &&
interfaces.match(context, element.interfaces) &&
mixins.match(context, element.mixins);
}
@override
void write(BufferedSink sink) {
super.write(sink);
supertype.writeOptional(sink);
mixins.writeList(sink);
interfaces.writeList(sink);
sink.writeMap(
inheritedMembers,
writeKey: (name) => name.write(sink),
writeValue: (member) => member.write(sink),
);
}
static Map<LookupName, ManifestItemId> _readInheritedMembers(
SummaryDataReader reader,
) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => ManifestItemId.read(reader),
);
}
}
class InterfaceItemConstructorItem
extends InstanceItemMemberItem<ConstructorElementImpl2> {
final bool isConst;
final bool isFactory;
final ManifestFunctionType functionType;
final List<ManifestNode> constantInitializers;
InterfaceItemConstructorItem({
required super.id,
required super.metadata,
required super.isStatic,
required this.isConst,
required this.isFactory,
required this.functionType,
required this.constantInitializers,
});
factory InterfaceItemConstructorItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ConstructorElementImpl2 element,
}) {
return context.withFormalParameters(element.formalParameters, () {
return InterfaceItemConstructorItem(
id: id,
metadata: ManifestMetadata.encode(context, element.metadata2),
isStatic: false,
isConst: element.isConst,
isFactory: element.isFactory,
functionType: element.type.encode(context),
constantInitializers:
element.constantInitializers
.map((initializer) => ManifestNode.encode(context, initializer))
.toFixedList(),
);
});
}
factory InterfaceItemConstructorItem.read(SummaryDataReader reader) {
return InterfaceItemConstructorItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
isStatic: reader.readBool(),
isConst: reader.readBool(),
isFactory: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
constantInitializers: ManifestNode.readList(reader),
);
}
@override
bool match(MatchContext context, ConstructorElementImpl2 element) {
return context.withFormalParameters(element.formalParameters, () {
return super.match(context, element) &&
isConst == element.isConst &&
isFactory == element.isFactory &&
functionType.match(context, element.type) &&
constantInitializers.match(context, element.constantInitializers);
});
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isConst);
sink.writeBool(isFactory);
functionType.writeNoTag(sink);
constantInitializers.writeList(sink);
}
@override
void writeKind(BufferedSink sink) {
sink.writeEnum(_InstanceItemMemberItemKind.constructor);
}
}
class ManifestAnnotation {
final ManifestNode ast;
ManifestAnnotation({required this.ast});
factory ManifestAnnotation.read(SummaryDataReader reader) {
return ManifestAnnotation(ast: ManifestNode.read(reader));
}
bool match(MatchContext context, ElementAnnotationImpl annotation) {
return ast.match(context, annotation.annotationAst);
}
void write(BufferedSink sink) {
ast.write(sink);
}
static ManifestAnnotation encode(
EncodeContext context,
ElementAnnotationImpl annotation,
) {
return ManifestAnnotation(
ast: ManifestNode.encode(context, annotation.annotationAst),
);
}
}
sealed class ManifestItem<E extends AnnotatableElementImpl> {
/// The unique identifier of this item.
final ManifestItemId id;
final ManifestMetadata metadata;
ManifestItem({required this.id, required this.metadata});
@mustCallSuper
bool match(MatchContext context, E element) {
return metadata.match(context, element.effectiveMetadata);
}
@mustCallSuper
void write(BufferedSink sink) {
id.write(sink);
metadata.write(sink);
}
}
class ManifestMetadata {
final List<ManifestAnnotation> annotations;
ManifestMetadata({required this.annotations});
factory ManifestMetadata.encode(
EncodeContext context,
MetadataImpl metadata,
) {
return ManifestMetadata(
annotations:
metadata.annotations.map((annotation) {
return ManifestAnnotation.encode(context, annotation);
}).toFixedList(),
);
}
factory ManifestMetadata.read(SummaryDataReader reader) {
return ManifestMetadata(
annotations: reader.readTypedList(() {
return ManifestAnnotation.read(reader);
}),
);
}
bool match(MatchContext context, MetadataImpl metadata) {
var metadataAnnotations = metadata.annotations;
if (annotations.length != metadataAnnotations.length) {
return false;
}
for (var i = 0; i < metadataAnnotations.length; i++) {
if (!annotations[i].match(context, metadataAnnotations[i])) {
return false;
}
}
return true;
}
void write(BufferedSink sink) {
sink.writeList(annotations, (x) => x.write(sink));
}
}
class MixinItem extends InterfaceItem<MixinElementImpl2> {
final List<ManifestType> superclassConstraints;
MixinItem({
required super.id,
required super.metadata,
required super.typeParameters,
required super.supertype,
required super.interfaces,
required super.mixins,
required super.declaredMembers,
required super.inheritedMembers,
required this.superclassConstraints,
}) : assert(supertype == null),
assert(mixins.isEmpty),
assert(superclassConstraints.isNotEmpty);
factory MixinItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required MixinElementImpl2 element,
}) {
return context.withTypeParameters(element.typeParameters2, (
typeParameters,
) {
return MixinItem(
id: id,
metadata: ManifestMetadata.encode(context, element.metadata2),
typeParameters: typeParameters,
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
declaredMembers: {},
inheritedMembers: {},
superclassConstraints: element.superclassConstraints.encode(context),
);
});
}
factory MixinItem.read(SummaryDataReader reader) {
return MixinItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
declaredMembers: InstanceItem._readDeclaredMembers(reader),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
inheritedMembers: InterfaceItem._readInheritedMembers(reader),
superclassConstraints: ManifestType.readList(reader),
);
}
@override
bool match(MatchContext context, MixinElementImpl2 element) {
return super.match(context, element) &&
superclassConstraints.match(context, element.superclassConstraints);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ManifestItemKind.mixin_);
super.write(sink);
superclassConstraints.writeList(sink);
}
}
class TopLevelFunctionItem extends TopLevelItem<TopLevelFunctionElementImpl> {
final ManifestFunctionType functionType;
TopLevelFunctionItem({
required super.id,
required super.metadata,
required this.functionType,
});
factory TopLevelFunctionItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required TopLevelFunctionElementImpl element,
}) {
return TopLevelFunctionItem(
id: id,
metadata: ManifestMetadata.encode(context, element.metadata2),
functionType: element.type.encode(context),
);
}
factory TopLevelFunctionItem.read(SummaryDataReader reader) {
return TopLevelFunctionItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
functionType: ManifestFunctionType.read(reader),
);
}
@override
bool match(MatchContext context, TopLevelFunctionElementImpl element) {
return super.match(context, element) &&
functionType.match(context, element.type);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ManifestItemKind.topLevelFunction);
super.write(sink);
functionType.writeNoTag(sink);
}
}
class TopLevelGetterItem extends TopLevelItem<GetterElementImpl> {
final ManifestType returnType;
final ManifestNode? constInitializer;
TopLevelGetterItem({
required super.id,
required super.metadata,
required this.returnType,
required this.constInitializer,
});
factory TopLevelGetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required GetterElementImpl element,
}) {
return TopLevelGetterItem(
id: id,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
returnType: element.returnType.encode(context),
constInitializer: element.constInitializer?.encode(context),
);
}
factory TopLevelGetterItem.read(SummaryDataReader reader) {
return TopLevelGetterItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
returnType: ManifestType.read(reader),
constInitializer: ManifestNode.readOptional(reader),
);
}
@override
bool match(MatchContext context, GetterElementImpl element) {
return super.match(context, element) &&
returnType.match(context, element.returnType) &&
constInitializer.match(context, element.constInitializer);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ManifestItemKind.topLevelGetter);
super.write(sink);
returnType.write(sink);
constInitializer.writeOptional(sink);
}
}
sealed class TopLevelItem<E extends AnnotatableElementImpl>
extends ManifestItem<E> {
TopLevelItem({required super.id, required super.metadata});
static TopLevelItem<AnnotatableElementImpl> read(SummaryDataReader reader) {
var kind = reader.readEnum(_ManifestItemKind.values);
switch (kind) {
case _ManifestItemKind.class_:
return ClassItem.read(reader);
case _ManifestItemKind.mixin_:
return MixinItem.read(reader);
case _ManifestItemKind.topLevelFunction:
return TopLevelFunctionItem.read(reader);
case _ManifestItemKind.topLevelGetter:
return TopLevelGetterItem.read(reader);
case _ManifestItemKind.topLevelSetter:
return TopLevelSetterItem.read(reader);
}
}
}
class TopLevelSetterItem extends TopLevelItem<SetterElementImpl> {
final ManifestType valueType;
TopLevelSetterItem({
required super.id,
required super.metadata,
required this.valueType,
});
factory TopLevelSetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required SetterElementImpl element,
}) {
return TopLevelSetterItem(
id: id,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
valueType: element.formalParameters[0].type.encode(context),
);
}
factory TopLevelSetterItem.read(SummaryDataReader reader) {
return TopLevelSetterItem(
id: ManifestItemId.read(reader),
metadata: ManifestMetadata.read(reader),
valueType: ManifestType.read(reader),
);
}
@override
bool match(MatchContext context, SetterElementImpl element) {
return super.match(context, element) &&
valueType.match(context, element.formalParameters[0].type);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ManifestItemKind.topLevelSetter);
super.write(sink);
valueType.write(sink);
}
}
enum _InstanceItemMemberItemKind {
duplicate,
constructor,
method,
getter,
setter,
}
enum _ManifestItemKind {
class_,
mixin_,
topLevelFunction,
topLevelGetter,
topLevelSetter,
}
extension _AnnotatableElementExtension on AnnotatableElementImpl {
MetadataImpl get effectiveMetadata {
if (this case PropertyAccessorElementImpl2 accessor) {
return accessor.thisOrVariableMetadata;
}
return metadata2;
}
}
extension _AstNodeExtension on AstNode {
ManifestNode encode(EncodeContext context) {
return ManifestNode.encode(context, this);
}
}
extension _GetterElementImplExtension on GetterElementImpl {
Expression? get constInitializer {
if (isSynthetic) {
var variable = variable3!;
if (variable.isConst) {
return variable.constantInitializer2?.expression;
}
}
return null;
}
}
extension _PropertyAccessExtension on PropertyAccessorElementImpl2 {
MetadataImpl get thisOrVariableMetadata {
if (isSynthetic) {
return variable3!.metadata2;
} else {
return metadata2;
}
}
}