blob: 84f174f2c88dd7a32dc9ef3c3564c77dd6207f6d [file]
// 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:collection/collection.dart';
import 'package:meta/meta.dart';
class ClassItem extends InterfaceItem<ClassElementImpl> {
final bool isAbstract;
final bool isBase;
final bool isFinal;
final bool isInterface;
final bool isMixinApplication;
final bool isMixinClass;
final bool isSealed;
ClassItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredMethods,
required super.declaredConstructors,
required super.inheritedConstructors,
required super.hasNonFinalField,
required super.supertype,
required super.mixins,
required super.interfaces,
required super.interface,
required this.isAbstract,
required this.isBase,
required this.isFinal,
required this.isInterface,
required this.isMixinApplication,
required this.isMixinClass,
required this.isSealed,
});
factory ClassItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ClassElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return ClassItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
isSimplyBounded: element.isSimplyBounded,
declaredConflicts: {},
declaredFields: {},
declaredGetters: {},
declaredSetters: {},
declaredMethods: {},
declaredConstructors: {},
inheritedConstructors: {},
hasNonFinalField: element.hasNonFinalField,
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
interface: ManifestInterface.empty(),
isAbstract: element.isAbstract,
isBase: element.isBase,
isFinal: element.isFinal,
isInterface: element.isInterface,
isMixinApplication: element.isMixinApplication,
isMixinClass: element.isMixinClass,
isSealed: element.isSealed,
);
});
}
factory ClassItem.read(SummaryDataReader reader) {
return ClassItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
isSimplyBounded: reader.readBool(),
declaredConflicts: reader.readLookupNameToIdMap(),
declaredFields: FieldItem.readMap(reader),
declaredGetters: GetterItem.readMap(reader),
declaredSetters: SetterItem.readMap(reader),
declaredMethods: MethodItem.readMap(reader),
declaredConstructors: ConstructorItem.readMap(reader),
inheritedConstructors: reader.readLookupNameToIdMap(),
hasNonFinalField: reader.readBool(),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
interface: ManifestInterface.read(reader),
isAbstract: reader.readBool(),
isBase: reader.readBool(),
isFinal: reader.readBool(),
isInterface: reader.readBool(),
isMixinApplication: reader.readBool(),
isMixinClass: reader.readBool(),
isSealed: reader.readBool(),
);
}
@override
bool match(MatchContext context, ClassElementImpl element) {
return super.match(context, element) &&
isAbstract == element.isAbstract &&
isBase == element.isBase &&
isFinal == element.isFinal &&
isInterface == element.isInterface &&
isMixinApplication == element.isMixinApplication &&
isMixinClass == element.isMixinClass &&
isSealed == element.isSealed;
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isAbstract);
sink.writeBool(isBase);
sink.writeBool(isFinal);
sink.writeBool(isInterface);
sink.writeBool(isMixinApplication);
sink.writeBool(isMixinClass);
sink.writeBool(isSealed);
}
}
class ConstructorItem extends ExecutableItem<ConstructorElementImpl> {
final bool isConst;
final bool isFactory;
final List<ManifestNode> constantInitializers;
ConstructorItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.hasEnclosingTypeParameterReference,
required super.hasImplicitReturnType,
required super.invokesSuperSelf,
required super.isAbstract,
required super.isExtensionTypeMember,
required super.isExternal,
required super.isSimplyBounded,
required super.isStatic,
required super.functionType,
required this.isConst,
required this.isFactory,
required this.constantInitializers,
});
factory ConstructorItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ConstructorElementImpl element,
}) {
return context.withFormalParameters(element.formalParameters, () {
return ConstructorItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
hasEnclosingTypeParameterReference:
element.hasEnclosingTypeParameterReference,
hasImplicitReturnType: element.hasImplicitReturnType,
invokesSuperSelf: element.invokesSuperSelf,
isAbstract: element.isAbstract,
isExtensionTypeMember: element.isExtensionTypeMember,
isExternal: element.isExternal,
isSimplyBounded: element.isSimplyBounded,
isStatic: false,
functionType: element.type.encode(context),
isConst: element.isConst,
isFactory: element.isFactory,
constantInitializers: element.constantInitializers
.map((initializer) => ManifestNode.encode(context, initializer))
.toFixedList(),
);
});
}
factory ConstructorItem.read(SummaryDataReader reader) {
return ConstructorItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
hasEnclosingTypeParameterReference: reader.readBool(),
hasImplicitReturnType: reader.readBool(),
invokesSuperSelf: reader.readBool(),
isAbstract: reader.readBool(),
isExtensionTypeMember: reader.readBool(),
isExternal: reader.readBool(),
isSimplyBounded: reader.readBool(),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
isConst: reader.readBool(),
isFactory: reader.readBool(),
constantInitializers: ManifestNode.readList(reader),
);
}
@override
bool match(MatchContext context, ConstructorElementImpl element) {
return context.withFormalParameters(element.formalParameters, () {
return super.match(context, element) &&
isConst == element.isConst &&
isFactory == element.isFactory &&
constantInitializers.match(context, element.constantInitializers);
});
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isConst);
sink.writeBool(isFactory);
constantInitializers.writeList(sink);
}
static Map<LookupName, ConstructorItem> readMap(SummaryDataReader reader) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => ConstructorItem.read(reader),
);
}
}
class EnumItem extends InterfaceItem<EnumElementImpl> {
EnumItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredMethods,
required super.declaredConstructors,
required super.inheritedConstructors,
required super.hasNonFinalField,
required super.interface,
required super.supertype,
required super.mixins,
required super.interfaces,
});
factory EnumItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required EnumElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return EnumItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
isSimplyBounded: element.isSimplyBounded,
declaredConflicts: {},
declaredFields: {},
declaredGetters: {},
declaredSetters: {},
declaredMethods: {},
declaredConstructors: {},
inheritedConstructors: {},
hasNonFinalField: element.hasNonFinalField,
interface: ManifestInterface.empty(),
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
);
});
}
factory EnumItem.read(SummaryDataReader reader) {
return EnumItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
isSimplyBounded: reader.readBool(),
declaredConflicts: reader.readLookupNameToIdMap(),
declaredFields: FieldItem.readMap(reader),
declaredGetters: GetterItem.readMap(reader),
declaredSetters: SetterItem.readMap(reader),
declaredMethods: MethodItem.readMap(reader),
declaredConstructors: ConstructorItem.readMap(reader),
inheritedConstructors: reader.readLookupNameToIdMap(),
hasNonFinalField: reader.readBool(),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
interface: ManifestInterface.read(reader),
);
}
}
sealed class ExecutableItem<E extends ExecutableElementImpl>
extends ManifestItem<E> {
final bool hasEnclosingTypeParameterReference;
final bool hasImplicitReturnType;
final bool invokesSuperSelf;
final bool isAbstract;
final bool isExtensionTypeMember;
final bool isExternal;
final bool isSimplyBounded;
final bool isStatic;
final ManifestFunctionType functionType;
ExecutableItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required this.hasEnclosingTypeParameterReference,
required this.hasImplicitReturnType,
required this.invokesSuperSelf,
required this.isAbstract,
required this.isExtensionTypeMember,
required this.isExternal,
required this.isSimplyBounded,
required this.isStatic,
required this.functionType,
});
@override
bool match(MatchContext context, E element) {
return super.match(context, element) &&
hasEnclosingTypeParameterReference ==
element.hasEnclosingTypeParameterReference &&
hasImplicitReturnType == element.hasImplicitReturnType &&
invokesSuperSelf == element.invokesSuperSelf &&
isAbstract == element.isAbstract &&
isExtensionTypeMember == element.isExtensionTypeMember &&
isExternal == element.isExternal &&
isSimplyBounded == element.isSimplyBounded &&
isStatic == element.isStatic &&
functionType.match(context, element.type);
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(hasEnclosingTypeParameterReference);
sink.writeBool(hasImplicitReturnType);
sink.writeBool(invokesSuperSelf);
sink.writeBool(isAbstract);
sink.writeBool(isExtensionTypeMember);
sink.writeBool(isExternal);
sink.writeBool(isSimplyBounded);
sink.writeBool(isStatic);
functionType.writeNoTag(sink);
}
}
/// The item for [ExtensionElementImpl].
class ExtensionItem<E extends ExtensionElementImpl> extends InstanceItem<E> {
final ManifestType extendedType;
ExtensionItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredMethods,
required super.declaredConstructors,
required super.inheritedConstructors,
required this.extendedType,
});
factory ExtensionItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ExtensionElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return ExtensionItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
isSimplyBounded: element.isSimplyBounded,
declaredConflicts: {},
declaredFields: {},
declaredGetters: {},
declaredSetters: {},
declaredMethods: {},
declaredConstructors: {},
inheritedConstructors: {},
extendedType: element.extendedType.encode(context),
);
});
}
factory ExtensionItem.read(SummaryDataReader reader) {
return ExtensionItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
isSimplyBounded: reader.readBool(),
declaredConflicts: reader.readLookupNameToIdMap(),
declaredFields: FieldItem.readMap(reader),
declaredGetters: GetterItem.readMap(reader),
declaredSetters: SetterItem.readMap(reader),
declaredMethods: MethodItem.readMap(reader),
declaredConstructors: ConstructorItem.readMap(reader),
inheritedConstructors: reader.readLookupNameToIdMap(),
extendedType: ManifestType.read(reader),
);
}
@override
bool match(MatchContext context, E element) {
return super.match(context, element) &&
extendedType.match(context, element.extendedType);
}
@override
void write(BufferedSink sink) {
super.write(sink);
extendedType.write(sink);
}
}
class ExtensionTypeItem extends InterfaceItem<ExtensionTypeElementImpl> {
final bool hasImplementsSelfReference;
final bool hasRepresentationSelfReference;
final ManifestType representationType;
final ManifestType typeErasure;
ExtensionTypeItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredMethods,
required super.declaredConstructors,
required super.inheritedConstructors,
required super.hasNonFinalField,
required super.interface,
required super.supertype,
required super.mixins,
required super.interfaces,
required this.hasImplementsSelfReference,
required this.hasRepresentationSelfReference,
required this.representationType,
required this.typeErasure,
});
factory ExtensionTypeItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required ExtensionTypeElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return ExtensionTypeItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
isSimplyBounded: element.isSimplyBounded,
declaredConflicts: {},
declaredFields: {},
declaredGetters: {},
declaredSetters: {},
declaredMethods: {},
declaredConstructors: {},
inheritedConstructors: {},
hasNonFinalField: element.hasNonFinalField,
interface: ManifestInterface.empty(),
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
hasImplementsSelfReference: element.hasImplementsSelfReference,
hasRepresentationSelfReference: element.hasRepresentationSelfReference,
representationType: element.representation.type.encode(context),
typeErasure: element.typeErasure.encode(context),
);
});
}
factory ExtensionTypeItem.read(SummaryDataReader reader) {
return ExtensionTypeItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
isSimplyBounded: reader.readBool(),
declaredConflicts: reader.readLookupNameToIdMap(),
declaredFields: FieldItem.readMap(reader),
declaredGetters: GetterItem.readMap(reader),
declaredSetters: SetterItem.readMap(reader),
declaredMethods: MethodItem.readMap(reader),
declaredConstructors: ConstructorItem.readMap(reader),
inheritedConstructors: reader.readLookupNameToIdMap(),
hasNonFinalField: reader.readBool(),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
interface: ManifestInterface.read(reader),
hasImplementsSelfReference: reader.readBool(),
hasRepresentationSelfReference: reader.readBool(),
representationType: ManifestType.read(reader),
typeErasure: ManifestType.read(reader),
);
}
@override
bool match(MatchContext context, ExtensionTypeElementImpl element) {
return super.match(context, element) &&
hasImplementsSelfReference == element.hasImplementsSelfReference &&
hasRepresentationSelfReference ==
element.hasRepresentationSelfReference &&
representationType.match(context, element.representation.type) &&
typeErasure.match(context, element.typeErasure);
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(hasImplementsSelfReference);
sink.writeBool(hasRepresentationSelfReference);
representationType.write(sink);
typeErasure.write(sink);
}
}
class FieldItem extends VariableItem<FieldElementImpl> {
final bool isStatic;
FieldItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.isConst,
required super.isFinal,
required super.isLate,
required super.type,
required super.constInitializer,
required this.isStatic,
});
factory FieldItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required FieldElementImpl element,
}) {
return FieldItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
isConst: element.isConst,
isFinal: element.isFinal,
isLate: element.isLate,
type: element.type.encode(context),
constInitializer: element.constantInitializer?.encode(context),
isStatic: element.isStatic,
);
}
factory FieldItem.read(SummaryDataReader reader) {
return FieldItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
isConst: reader.readBool(),
isFinal: reader.readBool(),
isLate: reader.readBool(),
type: ManifestType.read(reader),
constInitializer: ManifestNode.readOptional(reader),
isStatic: reader.readBool(),
);
}
@override
bool match(MatchContext context, FieldElementImpl element) {
return super.match(context, element) && isStatic == element.isStatic;
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isStatic);
}
static Map<LookupName, FieldItem> readMap(SummaryDataReader reader) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => FieldItem.read(reader),
);
}
}
class GetterItem extends ExecutableItem<GetterElementImpl> {
GetterItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.hasEnclosingTypeParameterReference,
required super.hasImplicitReturnType,
required super.invokesSuperSelf,
required super.isAbstract,
required super.isExtensionTypeMember,
required super.isExternal,
required super.isSimplyBounded,
required super.isStatic,
required super.functionType,
});
factory GetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required GetterElementImpl element,
}) {
return GetterItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
hasEnclosingTypeParameterReference:
element.hasEnclosingTypeParameterReference,
hasImplicitReturnType: element.hasImplicitReturnType,
invokesSuperSelf: element.invokesSuperSelf,
isAbstract: element.isAbstract,
isExtensionTypeMember: element.isExtensionTypeMember,
isExternal: element.isExternal,
isSimplyBounded: element.isSimplyBounded,
isStatic: element.isStatic,
functionType: element.type.encode(context),
);
}
factory GetterItem.read(SummaryDataReader reader) {
return GetterItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
hasEnclosingTypeParameterReference: reader.readBool(),
hasImplicitReturnType: reader.readBool(),
invokesSuperSelf: reader.readBool(),
isAbstract: reader.readBool(),
isExtensionTypeMember: reader.readBool(),
isExternal: reader.readBool(),
isSimplyBounded: reader.readBool(),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
);
}
static Map<LookupName, GetterItem> readMap(SummaryDataReader reader) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => GetterItem.read(reader),
);
}
}
/// The item for [InstanceElementImpl].
sealed class InstanceItem<E extends InstanceElementImpl>
extends ManifestItem<E> {
final List<ManifestTypeParameter> typeParameters;
final bool isSimplyBounded;
/// The names of duplicate or otherwise conflicting members.
/// Such names will not be added to `declaredXyz` maps.
Map<LookupName, ManifestItemId> declaredConflicts;
Map<LookupName, FieldItem> declaredFields;
Map<LookupName, GetterItem> declaredGetters;
Map<LookupName, SetterItem> declaredSetters;
Map<LookupName, MethodItem> declaredMethods;
Map<LookupName, ConstructorItem> declaredConstructors;
Map<LookupName, ManifestItemId> inheritedConstructors;
InstanceItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required this.typeParameters,
required this.isSimplyBounded,
required this.declaredConflicts,
required this.declaredFields,
required this.declaredGetters,
required this.declaredSetters,
required this.declaredMethods,
required this.declaredConstructors,
required this.inheritedConstructors,
});
void addDeclaredConstructor(LookupName lookupName, ConstructorItem item) {
if (declaredConflicts.containsKey(lookupName)) {
return;
}
var hasConflict = () {
// Constructors conflict with constructors.
if (declaredConstructors.containsKey(lookupName)) {
return true;
}
// Constructors conflict with static properties and methods.
return declaredGetters[lookupName]?.isStatic ??
declaredSetters[lookupName]?.isStatic ??
declaredMethods[lookupName]?.isStatic ??
false;
}();
if (hasConflict) {
_makeNameConflict(lookupName);
return;
}
declaredConstructors[lookupName] = item;
}
void addDeclaredGetter(LookupName lookupName, GetterItem item) {
if (declaredConflicts.containsKey(lookupName)) {
return;
}
var hasConflict = () {
// Getters conflict with methods and getters.
if (declaredGetters.containsKey(lookupName) ||
declaredMethods.containsKey(lookupName)) {
return true;
}
// Static getters conflict with constructors.
if (item.isStatic && declaredConstructors.containsKey(lookupName)) {
return true;
}
// Instance / static getters conflict with static / instance setter.
var lookupNameSetter = '${lookupName.asString}='.asLookupName;
if (declaredSetters[lookupNameSetter] case var setter?) {
if (setter.isStatic != item.isStatic) {
return true;
}
}
return false;
}();
if (hasConflict) {
_makeNameConflict(lookupName);
return;
}
declaredGetters[lookupName] = item;
}
void addDeclaredMethod(LookupName lookupName, MethodItem item) {
if (declaredConflicts.containsKey(lookupName)) {
return;
}
var hasConflict = () {
// Methods conflict with methods and properties.
if (declaredGetters.containsKey(lookupName) ||
declaredSetters.containsKey(lookupName.methodToSetter) ||
declaredMethods.containsKey(lookupName)) {
return true;
}
// Static methods conflict with constructors.
if (item.isStatic && declaredConstructors.containsKey(lookupName)) {
return true;
}
return false;
}();
if (hasConflict) {
_makeNameConflict(lookupName);
return;
}
declaredMethods[lookupName] = item;
}
void addDeclaredSetter(LookupName lookupName, SetterItem item) {
if (declaredConflicts.containsKey(lookupName)) {
return;
}
var hasConflict = () {
var lookupNameGetter = lookupName.setterToGetter;
// Setters conflict with setters and methods.
if (declaredSetters.containsKey(lookupName) ||
declaredMethods.containsKey(lookupNameGetter)) {
return true;
}
// Static setters conflict with constructors.
if (item.isStatic && declaredConstructors.containsKey(lookupNameGetter)) {
return true;
}
// Instance / static setters conflict with static / instance getter.
if (declaredGetters[lookupNameGetter] case var getter?) {
if (getter.isStatic != item.isStatic) {
return true;
}
}
return false;
}();
if (hasConflict) {
_makeNameConflict(lookupName);
return;
}
declaredSetters[lookupName] = item;
}
void addInheritedConstructor(LookupName lookupName, ManifestItemId id) {
// Inherited constructors exist only for class type aliases.
// So, not conflicts checking it required.
inheritedConstructors[lookupName] = id;
}
void beforeUpdatingMembers() {
declaredConflicts = {};
declaredFields = {};
declaredGetters = {};
declaredSetters = {};
declaredMethods = {};
declaredConstructors = {};
inheritedConstructors = {};
}
ManifestItemId? getConstructorId(LookupName name) {
return declaredConstructors[name]?.id ??
inheritedConstructors[name] ??
declaredConflicts[name];
}
ManifestItemId? getDeclaredFieldId(LookupName name) {
return declaredFields[name]?.id ?? declaredConflicts[name];
}
ManifestItemId? getDeclaredGetterId(LookupName name) {
return declaredGetters[name]?.id ?? declaredConflicts[name];
}
ManifestItemId? getDeclaredMethodId(LookupName name) {
return declaredMethods[name]?.id ?? declaredConflicts[name];
}
ManifestItemId? getDeclaredSetterId(LookupName name) {
return declaredSetters[name]?.id ?? declaredConflicts[name];
}
@override
bool match(MatchContext context, E element) {
context.addTypeParameters(element.typeParameters);
return super.match(context, element) &&
typeParameters.match(context, element.typeParameters) &&
isSimplyBounded == element.isSimplyBounded;
}
@override
void write(BufferedSink sink) {
super.write(sink);
typeParameters.write(sink);
sink.writeBool(isSimplyBounded);
declaredConflicts.write(sink);
declaredFields.write(sink);
declaredGetters.write(sink);
declaredSetters.write(sink);
declaredMethods.write(sink);
declaredConstructors.write(sink);
inheritedConstructors.write(sink);
}
void _makeNameConflict(LookupName lookupName2) {
var id = ManifestItemId.generate();
for (var lookupName in lookupName2.relatedNames) {
declaredConflicts[lookupName] = id;
declaredFields.remove(lookupName);
declaredGetters.remove(lookupName);
declaredSetters.remove(lookupName);
declaredMethods.remove(lookupName);
declaredConstructors.remove(lookupName);
inheritedConstructors.remove(lookupName);
}
}
}
/// The item for [InterfaceElementImpl].
sealed class InterfaceItem<E extends InterfaceElementImpl>
extends InstanceItem<E> {
bool hasNonFinalField;
final ManifestType? supertype;
final List<ManifestType> interfaces;
final List<ManifestType> mixins;
final ManifestInterface interface;
InterfaceItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredMethods,
required super.declaredConstructors,
required super.inheritedConstructors,
required this.hasNonFinalField,
required this.supertype,
required this.mixins,
required this.interfaces,
required this.interface,
});
ManifestItemId? getImplementedMethodId(LookupName name) {
return interface.implemented[name];
}
ManifestItemId? getInterfaceMethodId(LookupName name) {
return interface.map[name];
}
ManifestItemId? getSuperImplementedMethodId(int index, LookupName name) {
if (index < interface.superImplemented.length) {
return interface.superImplemented[index][name];
} else {
return null;
}
}
/// Intentionally omits [hasNonFinalField], which is tracked as a separate
/// requirement.
@override
bool match(MatchContext context, E element) {
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);
sink.writeBool(hasNonFinalField);
supertype.writeOptional(sink);
mixins.writeList(sink);
interfaces.writeList(sink);
interface.write(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) {
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),
);
}
}
/// Manifest version of `Interface` computed by `InheritanceManager`.
///
/// We store only IDs of the interface 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.
class ManifestInterface {
/// The ID of the interface, stays the same if all information in the
/// interface is the same.
ManifestItemId id;
/// The map of names to their IDs in the interface.
Map<LookupName, ManifestItemId> map;
Map<LookupName, ManifestItemId> implemented;
List<Map<LookupName, ManifestItemId>> superImplemented;
Map<LookupName, ManifestItemId> inherited;
/// We move [map] into here during building the manifest, so that we can
/// compare after building, and decide if [id] should be updated.
Map<LookupName, ManifestItemId> mapPrevious = {};
Map<LookupName, ManifestItemId> implementedPrevious = {};
List<Map<LookupName, ManifestItemId>> superImplementedPrevious = [];
Map<LookupName, ManifestItemId> inheritedPrevious = {};
/// Key: IDs of method declarations.
/// Value: ID assigned last time.
/// When the same signatures merge, the result is the same.
Map<ManifestItemIdList, ManifestItemId> combinedIds = {};
/// We move [combinedIds] into here during building the manifest, so that
/// we can fill [combinedIds] with new entries.
Map<ManifestItemIdList, ManifestItemId> combinedIdsTemp = {};
ManifestInterface({
required this.id,
required this.map,
required this.implemented,
required this.superImplemented,
required this.inherited,
required this.combinedIds,
});
factory ManifestInterface.empty() {
return ManifestInterface(
id: ManifestItemId.generate(),
map: {},
implemented: {},
superImplemented: [],
inherited: {},
combinedIds: {},
);
}
factory ManifestInterface.read(SummaryDataReader reader) {
return ManifestInterface(
id: ManifestItemId.read(reader),
map: reader.readLookupNameToIdMap(),
implemented: reader.readLookupNameToIdMap(),
superImplemented: reader.readTypedList(() {
return reader.readLookupNameToIdMap();
}),
inherited: reader.readLookupNameToIdMap(),
combinedIds: reader.readMap(
readKey: () => ManifestItemIdList.read(reader),
readValue: () => ManifestItemId.read(reader),
),
);
}
void afterUpdate() {
const mapEquality = MapEquality<LookupName, ManifestItemId>();
const listEquality = ListEquality<Map<LookupName, ManifestItemId>>(
MapEquality<LookupName, ManifestItemId>(),
);
if (!mapEquality.equals(map, mapPrevious) ||
!mapEquality.equals(implemented, implementedPrevious) ||
!listEquality.equals(superImplemented, superImplementedPrevious) ||
!mapEquality.equals(inherited, inheritedPrevious)) {
id = ManifestItemId.generate();
}
mapPrevious = {};
implementedPrevious = {};
superImplementedPrevious = [];
inheritedPrevious = {};
combinedIdsTemp = {};
}
void beforeUpdating() {
mapPrevious = map;
map = {};
implementedPrevious = implemented;
implemented = {};
superImplementedPrevious = superImplemented;
superImplemented = [];
inheritedPrevious = inherited;
inherited = {};
combinedIdsTemp = combinedIds;
combinedIds = {};
}
void write(BufferedSink sink) {
id.write(sink);
map.write(sink);
implemented.write(sink);
sink.writeList(superImplemented, (map) => map.write(sink));
inherited.write(sink);
sink.writeMap(
combinedIds,
writeKey: (key) => key.write(sink),
writeValue: (id) => id.write(sink),
);
}
}
sealed class ManifestItem<E extends ElementImpl> {
/// The unique identifier of this item.
final ManifestItemId id;
final bool isSynthetic;
final ManifestMetadata metadata;
ManifestItem({
required this.id,
required this.isSynthetic,
required this.metadata,
});
@mustCallSuper
bool match(MatchContext context, E element) {
return isSynthetic == element.isSynthetic &&
metadata.match(context, element.effectiveMetadata);
}
@mustCallSuper
void write(BufferedSink sink) {
id.write(sink);
sink.writeBool(isSynthetic);
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 MethodItem extends ExecutableItem<MethodElementImpl> {
MethodItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.hasEnclosingTypeParameterReference,
required super.hasImplicitReturnType,
required super.invokesSuperSelf,
required super.isAbstract,
required super.isExtensionTypeMember,
required super.isExternal,
required super.isSimplyBounded,
required super.isStatic,
required super.functionType,
});
factory MethodItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required MethodElementImpl element,
}) {
return MethodItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
hasEnclosingTypeParameterReference:
element.hasEnclosingTypeParameterReference,
hasImplicitReturnType: element.hasImplicitReturnType,
invokesSuperSelf: element.invokesSuperSelf,
isAbstract: element.isAbstract,
isExtensionTypeMember: element.isExtensionTypeMember,
isExternal: element.isExternal,
isSimplyBounded: element.isSimplyBounded,
isStatic: element.isStatic,
functionType: element.type.encode(context),
);
}
factory MethodItem.read(SummaryDataReader reader) {
return MethodItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
hasEnclosingTypeParameterReference: reader.readBool(),
hasImplicitReturnType: reader.readBool(),
invokesSuperSelf: reader.readBool(),
isAbstract: reader.readBool(),
isExtensionTypeMember: reader.readBool(),
isExternal: reader.readBool(),
isSimplyBounded: reader.readBool(),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
);
}
static Map<LookupName, MethodItem> readMap(SummaryDataReader reader) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => MethodItem.read(reader),
);
}
}
class MixinItem extends InterfaceItem<MixinElementImpl> {
final bool isBase;
final List<ManifestType> superclassConstraints;
final List<LookupName> superInvokedNames;
MixinItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.typeParameters,
required super.isSimplyBounded,
required super.supertype,
required super.interfaces,
required super.mixins,
required super.declaredConflicts,
required super.declaredFields,
required super.declaredMethods,
required super.declaredGetters,
required super.declaredSetters,
required super.declaredConstructors,
required super.inheritedConstructors,
required super.hasNonFinalField,
required super.interface,
required this.isBase,
required this.superclassConstraints,
required this.superInvokedNames,
}) : assert(supertype == null),
assert(mixins.isEmpty),
assert(superclassConstraints.isNotEmpty);
factory MixinItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required MixinElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return MixinItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
isSimplyBounded: element.isSimplyBounded,
declaredConflicts: {},
declaredFields: {},
declaredGetters: {},
declaredSetters: {},
declaredMethods: {},
declaredConstructors: {},
inheritedConstructors: {},
hasNonFinalField: element.hasNonFinalField,
interface: ManifestInterface.empty(),
supertype: element.supertype?.encode(context),
mixins: element.mixins.encode(context),
interfaces: element.interfaces.encode(context),
isBase: element.isBase,
superclassConstraints: element.superclassConstraints.encode(context),
superInvokedNames: element.superInvokedNames
.map((name) => name.asLookupName)
.toFixedList(),
);
});
}
factory MixinItem.read(SummaryDataReader reader) {
return MixinItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
isSimplyBounded: reader.readBool(),
declaredConflicts: reader.readLookupNameToIdMap(),
declaredFields: FieldItem.readMap(reader),
declaredGetters: GetterItem.readMap(reader),
declaredSetters: SetterItem.readMap(reader),
declaredMethods: MethodItem.readMap(reader),
declaredConstructors: ConstructorItem.readMap(reader),
inheritedConstructors: reader.readLookupNameToIdMap(),
hasNonFinalField: reader.readBool(),
supertype: ManifestType.readOptional(reader),
mixins: ManifestType.readList(reader),
interfaces: ManifestType.readList(reader),
interface: ManifestInterface.read(reader),
isBase: reader.readBool(),
superclassConstraints: ManifestType.readList(reader),
superInvokedNames: reader.readLookupNameList(),
);
}
@override
bool match(MatchContext context, MixinElementImpl element) {
return super.match(context, element) &&
isBase == element.isBase &&
superclassConstraints.match(context, element.superclassConstraints) &&
const IterableEquality<String>().equals(
superInvokedNames.map((lookupName) => lookupName.asString),
element.superInvokedNames,
);
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isBase);
superclassConstraints.writeList(sink);
superInvokedNames.write(sink);
}
}
class SetterItem extends ExecutableItem<SetterElementImpl> {
SetterItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.hasEnclosingTypeParameterReference,
required super.hasImplicitReturnType,
required super.invokesSuperSelf,
required super.isAbstract,
required super.isExtensionTypeMember,
required super.isExternal,
required super.isSimplyBounded,
required super.isStatic,
required super.functionType,
});
factory SetterItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required SetterElementImpl element,
}) {
return SetterItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(
context,
element.thisOrVariableMetadata,
),
hasEnclosingTypeParameterReference:
element.hasEnclosingTypeParameterReference,
hasImplicitReturnType: element.hasImplicitReturnType,
invokesSuperSelf: element.invokesSuperSelf,
isAbstract: element.isAbstract,
isExtensionTypeMember: element.isExtensionTypeMember,
isExternal: element.isExternal,
isSimplyBounded: element.isSimplyBounded,
isStatic: element.isStatic,
functionType: element.type.encode(context),
);
}
factory SetterItem.read(SummaryDataReader reader) {
return SetterItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
hasEnclosingTypeParameterReference: reader.readBool(),
hasImplicitReturnType: reader.readBool(),
invokesSuperSelf: reader.readBool(),
isAbstract: reader.readBool(),
isExtensionTypeMember: reader.readBool(),
isExternal: reader.readBool(),
isSimplyBounded: reader.readBool(),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
);
}
static Map<LookupName, SetterItem> readMap(SummaryDataReader reader) {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => SetterItem.read(reader),
);
}
}
class TopLevelFunctionItem extends ExecutableItem<TopLevelFunctionElementImpl> {
TopLevelFunctionItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.hasEnclosingTypeParameterReference,
required super.hasImplicitReturnType,
required super.invokesSuperSelf,
required super.isAbstract,
required super.isExtensionTypeMember,
required super.isExternal,
required super.isSimplyBounded,
required super.isStatic,
required super.functionType,
});
factory TopLevelFunctionItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required TopLevelFunctionElementImpl element,
}) {
return TopLevelFunctionItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
hasEnclosingTypeParameterReference:
element.hasEnclosingTypeParameterReference,
hasImplicitReturnType: element.hasImplicitReturnType,
invokesSuperSelf: element.invokesSuperSelf,
isAbstract: element.isAbstract,
isExtensionTypeMember: element.isExtensionTypeMember,
isExternal: element.isExternal,
isSimplyBounded: element.isSimplyBounded,
isStatic: element.isStatic,
functionType: element.type.encode(context),
);
}
factory TopLevelFunctionItem.read(SummaryDataReader reader) {
return TopLevelFunctionItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
hasEnclosingTypeParameterReference: reader.readBool(),
hasImplicitReturnType: reader.readBool(),
invokesSuperSelf: reader.readBool(),
isAbstract: reader.readBool(),
isExtensionTypeMember: reader.readBool(),
isExternal: reader.readBool(),
isSimplyBounded: reader.readBool(),
isStatic: reader.readBool(),
functionType: ManifestFunctionType.read(reader),
);
}
}
class TopLevelVariableItem extends VariableItem<TopLevelVariableElementImpl> {
TopLevelVariableItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required super.isConst,
required super.isFinal,
required super.isLate,
required super.type,
required super.constInitializer,
});
factory TopLevelVariableItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required TopLevelVariableElementImpl element,
}) {
return TopLevelVariableItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
isConst: element.isConst,
isFinal: element.isFinal,
isLate: element.isLate,
type: element.type.encode(context),
constInitializer: element.constantInitializer?.encode(context),
);
}
factory TopLevelVariableItem.read(SummaryDataReader reader) {
return TopLevelVariableItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
isConst: reader.readBool(),
isFinal: reader.readBool(),
isLate: reader.readBool(),
type: ManifestType.read(reader),
constInitializer: ManifestNode.readOptional(reader),
);
}
}
class TypeAliasItem extends ManifestItem<TypeAliasElementImpl> {
final List<ManifestTypeParameter> typeParameters;
final ManifestType aliasedType;
TypeAliasItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required this.typeParameters,
required this.aliasedType,
});
factory TypeAliasItem.fromElement({
required ManifestItemId id,
required EncodeContext context,
required TypeAliasElementImpl element,
}) {
return context.withTypeParameters(element.typeParameters, (typeParameters) {
return TypeAliasItem(
id: id,
isSynthetic: element.isSynthetic,
metadata: ManifestMetadata.encode(context, element.metadata),
typeParameters: typeParameters,
aliasedType: element.aliasedType.encode(context),
);
});
}
factory TypeAliasItem.read(SummaryDataReader reader) {
return TypeAliasItem(
id: ManifestItemId.read(reader),
isSynthetic: reader.readBool(),
metadata: ManifestMetadata.read(reader),
typeParameters: ManifestTypeParameter.readList(reader),
aliasedType: ManifestType.read(reader),
);
}
@override
bool match(MatchContext context, TypeAliasElementImpl element) {
context.addTypeParameters(element.typeParameters);
return super.match(context, element) &&
typeParameters.match(context, element.typeParameters) &&
aliasedType.match(context, element.aliasedType);
}
@override
void write(BufferedSink sink) {
super.write(sink);
typeParameters.write(sink);
aliasedType.write(sink);
}
}
sealed class VariableItem<E extends VariableElementImpl>
extends ManifestItem<E> {
final bool isConst;
final bool isFinal;
final bool isLate;
final ManifestType type;
final ManifestNode? constInitializer;
VariableItem({
required super.id,
required super.isSynthetic,
required super.metadata,
required this.isConst,
required this.isFinal,
required this.isLate,
required this.type,
required this.constInitializer,
});
@override
bool match(MatchContext context, E element) {
return super.match(context, element) &&
isConst == element.isConst &&
isFinal == element.isFinal &&
isLate == element.isLate &&
type.match(context, element.type) &&
constInitializer.match(context, element.constantInitializer);
}
@override
void write(BufferedSink sink) {
super.write(sink);
sink.writeBool(isConst);
sink.writeBool(isFinal);
sink.writeBool(isLate);
type.write(sink);
constInitializer.writeOptional(sink);
}
}
extension LookupNameToIdMapExtension on Map<LookupName, ManifestItemId> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension LookupNameToItemMapExtension on Map<LookupName, ManifestItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension SummaryDataReaderExtension on SummaryDataReader {
Map<LookupName, V> readLookupNameMap<V>({required V Function() readValue}) {
return readMap(
readKey: () => LookupName.read(this),
readValue: () => readValue(),
);
}
Map<LookupName, ManifestItemId> readLookupNameToIdMap() {
return readLookupNameMap(readValue: () => ManifestItemId.read(this));
}
}
extension _AnnotatableElementExtension on ElementImpl {
MetadataImpl get effectiveMetadata {
if (this case PropertyAccessorElementImpl accessor) {
return accessor.thisOrVariableMetadata;
}
return metadata;
}
}
extension _AstNodeExtension on AstNode {
ManifestNode encode(EncodeContext context) {
return ManifestNode.encode(context, this);
}
}
extension _LookupNameToConstructorItemMapExtension
on Map<LookupName, ConstructorItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension _LookupNameToFieldItemMapExtension on Map<LookupName, FieldItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension _LookupNameToGetterItemMapExtension on Map<LookupName, GetterItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension _LookupNameToMethodItemMapExtension on Map<LookupName, MethodItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension _LookupNameToSetterItemMapExtension on Map<LookupName, SetterItem> {
void write(BufferedSink sink) {
sink.writeMap(
this,
writeKey: (name) => name.write(sink),
writeValue: (items) => items.write(sink),
);
}
}
extension _PropertyAccessExtension on PropertyAccessorElementImpl {
MetadataImpl get thisOrVariableMetadata {
if (isSynthetic) {
return variable.metadata;
} else {
return metadata;
}
}
}