| // 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'; |
| |
| class ClassItem extends InterfaceItem { |
| ClassItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required super.typeParameters, |
| required super.supertype, |
| required super.interfaces, |
| required super.mixins, |
| required super.members, |
| }); |
| |
| factory ClassItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required ClassElementImpl2 element, |
| }) { |
| return context.withTypeParameters( |
| element.typeParameters2, |
| (typeParameters) { |
| return ClassItem( |
| libraryUri: element.library2.uri, |
| name: name, |
| id: id, |
| typeParameters: typeParameters, |
| supertype: element.supertype?.encode(context), |
| mixins: element.mixins.encode(context), |
| interfaces: element.interfaces.encode(context), |
| members: {}, |
| ); |
| }, |
| ); |
| } |
| |
| factory ClassItem.read(SummaryDataReader reader) { |
| return ClassItem( |
| libraryUri: reader.readUri(), |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| typeParameters: ManifestTypeParameter.readList(reader), |
| members: InstanceItem._readMembers(reader), |
| supertype: ManifestType.readOptional(reader), |
| mixins: ManifestType.readList(reader), |
| interfaces: ManifestType.readList(reader), |
| ); |
| } |
| |
| MatchContext? match(ClassElementImpl2 element) { |
| return super._matchInterfaceElement(element); |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind.class_); |
| super.write(sink); |
| } |
| } |
| |
| class ExportItem extends TopLevelItem { |
| ExportItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| }); |
| |
| factory ExportItem.read(SummaryDataReader reader) { |
| return ExportItem( |
| libraryUri: reader.readUri(), |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| ); |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind.export_); |
| super.write(sink); |
| } |
| } |
| |
| /// The item for [InstanceElementImpl2]. |
| sealed class InstanceItem extends TopLevelItem { |
| final List<ManifestTypeParameter> typeParameters; |
| final Map<LookupName, InstanceItemMemberItem> members; |
| |
| InstanceItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required this.typeParameters, |
| required this.members, |
| }); |
| |
| @override |
| void write(BufferedSink sink) { |
| super.write(sink); |
| typeParameters.writeList(sink); |
| sink.writeMap( |
| members, |
| writeKey: (name) => name.write(sink), |
| writeValue: (member) => member.write(sink), |
| ); |
| } |
| |
| static Map<LookupName, InstanceItemMemberItem> _readMembers( |
| SummaryDataReader reader, |
| ) { |
| return reader.readMap( |
| readKey: () => LookupName.read(reader), |
| readValue: () => InstanceItemMemberItem.read(reader), |
| ); |
| } |
| } |
| |
| class InstanceItemGetterItem extends InstanceItemMemberItem { |
| final ManifestType returnType; |
| |
| InstanceItemGetterItem({ |
| required super.name, |
| required super.id, |
| required super.isStatic, |
| required this.returnType, |
| }); |
| |
| factory InstanceItemGetterItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required GetterElement2OrMember element, |
| }) { |
| return InstanceItemGetterItem( |
| name: name, |
| id: id, |
| isStatic: element.isStatic, |
| returnType: element.returnType.encode(context), |
| ); |
| } |
| |
| factory InstanceItemGetterItem.read(SummaryDataReader reader) { |
| return InstanceItemGetterItem( |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| isStatic: reader.readBool(), |
| returnType: ManifestType.read(reader), |
| ); |
| } |
| |
| MatchContext? match( |
| MatchContext instanceContext, |
| GetterElement2OrMember element, |
| ) { |
| var context = MatchContext(parent: instanceContext); |
| if (element.isStatic != isStatic) { |
| return null; |
| } |
| if (!returnType.match(context, element.returnType)) { |
| return null; |
| } |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind2.instanceGetter); |
| super.write(sink); |
| returnType.write(sink); |
| } |
| } |
| |
| sealed class InstanceItemMemberItem extends ManifestItem { |
| final bool isStatic; |
| |
| InstanceItemMemberItem({ |
| required super.name, |
| required super.id, |
| required this.isStatic, |
| }); |
| |
| factory InstanceItemMemberItem.read(SummaryDataReader reader) { |
| var kind = reader.readEnum(_ManifestItemKind2.values); |
| switch (kind) { |
| case _ManifestItemKind2.instanceGetter: |
| return InstanceItemGetterItem.read(reader); |
| case _ManifestItemKind2.instanceMethod: |
| return InstanceItemMethodItem.read(reader); |
| case _ManifestItemKind2.interfaceConstructor: |
| return InterfaceItemConstructorItem.read(reader); |
| } |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| super.write(sink); |
| sink.writeBool(isStatic); |
| } |
| } |
| |
| class InstanceItemMethodItem extends InstanceItemMemberItem { |
| final ManifestFunctionType functionType; |
| |
| InstanceItemMethodItem({ |
| required super.name, |
| required super.id, |
| required super.isStatic, |
| required this.functionType, |
| }); |
| |
| factory InstanceItemMethodItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required MethodElement2OrMember element, |
| }) { |
| return InstanceItemMethodItem( |
| name: name, |
| id: id, |
| isStatic: element.isStatic, |
| functionType: element.type.encode(context), |
| ); |
| } |
| |
| factory InstanceItemMethodItem.read(SummaryDataReader reader) { |
| return InstanceItemMethodItem( |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| isStatic: reader.readBool(), |
| functionType: ManifestFunctionType.read(reader), |
| ); |
| } |
| |
| MatchContext? match( |
| MatchContext instanceContext, |
| MethodElement2OrMember element, |
| ) { |
| var context = MatchContext(parent: instanceContext); |
| if (element.isStatic != isStatic) { |
| return null; |
| } |
| if (!functionType.match(context, element.type)) { |
| return null; |
| } |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind2.instanceMethod); |
| super.write(sink); |
| functionType.writeNoTag(sink); |
| } |
| } |
| |
| /// The item for [InterfaceElementImpl2]. |
| sealed class InterfaceItem extends InstanceItem { |
| final ManifestType? supertype; |
| final List<ManifestType> interfaces; |
| final List<ManifestType> mixins; |
| |
| InterfaceItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required super.typeParameters, |
| required super.members, |
| required this.supertype, |
| required this.interfaces, |
| required this.mixins, |
| }); |
| |
| @override |
| void write(BufferedSink sink) { |
| super.write(sink); |
| supertype.writeOptional(sink); |
| mixins.writeList(sink); |
| interfaces.writeList(sink); |
| } |
| |
| MatchContext? _matchInterfaceElement(InterfaceElementImpl2 element) { |
| var context = MatchContext(parent: null); |
| context.addTypeParameters(element.typeParameters2); |
| if (supertype.match(context, element.supertype) && |
| interfaces.match(context, element.interfaces) && |
| mixins.match(context, element.mixins)) { |
| return context; |
| } |
| return null; |
| } |
| } |
| |
| class InterfaceItemConstructorItem extends InstanceItemMemberItem { |
| final bool isConst; |
| final bool isFactory; |
| final ManifestFunctionType functionType; |
| |
| InterfaceItemConstructorItem({ |
| required super.name, |
| required super.id, |
| required super.isStatic, |
| required this.isConst, |
| required this.isFactory, |
| required this.functionType, |
| }); |
| |
| factory InterfaceItemConstructorItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required ConstructorElementImpl2 element, |
| }) { |
| // TODO(scheglov): initializers |
| return InterfaceItemConstructorItem( |
| name: name, |
| id: id, |
| isStatic: false, |
| isConst: element.isConst, |
| isFactory: element.isFactory, |
| functionType: element.type.encode(context), |
| ); |
| } |
| |
| factory InterfaceItemConstructorItem.read(SummaryDataReader reader) { |
| return InterfaceItemConstructorItem( |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| isStatic: reader.readBool(), |
| isConst: reader.readBool(), |
| isFactory: reader.readBool(), |
| functionType: ManifestFunctionType.read(reader), |
| ); |
| } |
| |
| MatchContext? match( |
| MatchContext instanceContext, |
| ConstructorElementImpl2 element, |
| ) { |
| var context = MatchContext(parent: instanceContext); |
| if (isConst != element.isConst) { |
| return null; |
| } |
| if (isFactory != element.isFactory) { |
| return null; |
| } |
| if (!functionType.match(context, element.type)) { |
| return null; |
| } |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind2.interfaceConstructor); |
| super.write(sink); |
| sink.writeBool(isConst); |
| sink.writeBool(isFactory); |
| functionType.writeNoTag(sink); |
| } |
| } |
| |
| 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) { |
| var annotationAst = annotation.annotationAst; |
| if (!ast.match(context, annotationAst)) { |
| return false; |
| } |
| return true; |
| } |
| |
| 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 { |
| /// The name of the item, mostly for debugging. |
| final LookupName name; |
| |
| /// The unique identifier of this item. |
| final ManifestItemId id; |
| |
| ManifestItem({ |
| required this.name, |
| required this.id, |
| }); |
| |
| void write(BufferedSink sink) { |
| name.write(sink); |
| id.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 TopLevelFunctionItem extends TopLevelItem { |
| final ManifestFunctionType functionType; |
| |
| TopLevelFunctionItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required this.functionType, |
| }); |
| |
| factory TopLevelFunctionItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required TopLevelFunctionElementImpl element, |
| }) { |
| return TopLevelFunctionItem( |
| libraryUri: element.library2.uri, |
| name: name, |
| id: id, |
| functionType: element.type.encode(context), |
| ); |
| } |
| |
| factory TopLevelFunctionItem.read(SummaryDataReader reader) { |
| return TopLevelFunctionItem( |
| libraryUri: reader.readUri(), |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| functionType: ManifestFunctionType.read(reader), |
| ); |
| } |
| |
| MatchContext? match( |
| TopLevelFunctionElementImpl element, |
| ) { |
| var context = MatchContext(parent: null); |
| if (!functionType.match(context, element.type)) { |
| return null; |
| } |
| |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind.topLevelFunction); |
| super.write(sink); |
| functionType.writeNoTag(sink); |
| } |
| } |
| |
| class TopLevelGetterItem extends TopLevelItem { |
| final ManifestMetadata metadata; |
| final ManifestType returnType; |
| final ManifestNode? constInitializer; |
| |
| TopLevelGetterItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required this.metadata, |
| required this.returnType, |
| required this.constInitializer, |
| }); |
| |
| factory TopLevelGetterItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required GetterElementImpl element, |
| }) { |
| return TopLevelGetterItem( |
| libraryUri: element.library2.uri, |
| name: name, |
| id: id, |
| metadata: ManifestMetadata.encode(context, element.metadata2), |
| returnType: element.returnType.encode(context), |
| constInitializer: element.constInitializer?.encode(context), |
| ); |
| } |
| |
| factory TopLevelGetterItem.read(SummaryDataReader reader) { |
| return TopLevelGetterItem( |
| libraryUri: reader.readUri(), |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| metadata: ManifestMetadata.read(reader), |
| returnType: ManifestType.read(reader), |
| constInitializer: ManifestNode.readOptional(reader), |
| ); |
| } |
| |
| MatchContext? match(GetterElementImpl element) { |
| var context = MatchContext(parent: null); |
| |
| if (!metadata.match(context, element.metadata2)) { |
| return null; |
| } |
| |
| if (!returnType.match(context, element.returnType)) { |
| return null; |
| } |
| |
| if (!constInitializer.match(context, element.constInitializer)) { |
| return null; |
| } |
| |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind.topLevelGetter); |
| super.write(sink); |
| metadata.write(sink); |
| returnType.write(sink); |
| constInitializer.writeOptional(sink); |
| } |
| } |
| |
| sealed class TopLevelItem extends ManifestItem { |
| /// The URI of the declaring library, mostly for debugging. |
| final Uri libraryUri; |
| |
| TopLevelItem({ |
| required this.libraryUri, |
| required super.name, |
| required super.id, |
| }); |
| |
| factory TopLevelItem.read(SummaryDataReader reader) { |
| var kind = reader.readEnum(_ManifestItemKind.values); |
| switch (kind) { |
| case _ManifestItemKind.class_: |
| return ClassItem.read(reader); |
| case _ManifestItemKind.export_: |
| return ExportItem.read(reader); |
| case _ManifestItemKind.topLevelFunction: |
| return TopLevelFunctionItem.read(reader); |
| case _ManifestItemKind.topLevelGetter: |
| return TopLevelGetterItem.read(reader); |
| case _ManifestItemKind.topLevelSetter: |
| return TopLevelSetterItem.read(reader); |
| } |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeUri(libraryUri); |
| super.write(sink); |
| } |
| } |
| |
| class TopLevelSetterItem extends TopLevelItem { |
| final ManifestMetadata metadata; |
| final ManifestType valueType; |
| |
| TopLevelSetterItem({ |
| required super.libraryUri, |
| required super.name, |
| required super.id, |
| required this.metadata, |
| required this.valueType, |
| }); |
| |
| factory TopLevelSetterItem.fromElement({ |
| required LookupName name, |
| required ManifestItemId id, |
| required EncodeContext context, |
| required SetterElementImpl element, |
| }) { |
| return TopLevelSetterItem( |
| libraryUri: element.library2.uri, |
| name: name, |
| id: id, |
| metadata: ManifestMetadata.encode(context, element.metadata2), |
| valueType: element.formalParameters[0].type.encode(context), |
| ); |
| } |
| |
| factory TopLevelSetterItem.read(SummaryDataReader reader) { |
| return TopLevelSetterItem( |
| libraryUri: reader.readUri(), |
| name: LookupName.read(reader), |
| id: ManifestItemId.read(reader), |
| metadata: ManifestMetadata.read(reader), |
| valueType: ManifestType.read(reader), |
| ); |
| } |
| |
| MatchContext? match(SetterElementImpl element) { |
| var context = MatchContext(parent: null); |
| |
| if (!metadata.match(context, element.metadata2)) { |
| return null; |
| } |
| |
| if (!valueType.match(context, element.formalParameters[0].type)) { |
| return null; |
| } |
| |
| return context; |
| } |
| |
| @override |
| void write(BufferedSink sink) { |
| sink.writeEnum(_ManifestItemKind.topLevelSetter); |
| super.write(sink); |
| metadata.write(sink); |
| valueType.write(sink); |
| } |
| } |
| |
| enum _ManifestItemKind { |
| class_, |
| export_, |
| topLevelFunction, |
| topLevelGetter, |
| topLevelSetter, |
| } |
| |
| enum _ManifestItemKind2 { |
| instanceGetter, |
| instanceMethod, |
| interfaceConstructor, |
| } |
| |
| extension _AstNodeExtension on AstNode { |
| ManifestNode encode(EncodeContext context) { |
| return ManifestNode.encode(context, this); |
| } |
| } |
| |
| extension _GetterElementImplExtension on GetterElementImpl { |
| Expression? get constInitializer { |
| Expression? constInitializer; |
| if (isSynthetic) { |
| var variable = variable3!; |
| if (variable.isConst) { |
| constInitializer = variable.constantInitializer2?.expression; |
| } |
| } |
| |
| // TODO(scheglov): support all expressions |
| switch (constInitializer) { |
| case BinaryExpression(): |
| case IntegerLiteral(): |
| case SimpleIdentifier(): |
| break; |
| default: |
| constInitializer = null; |
| } |
| return constInitializer; |
| } |
| } |