blob: 5bd1325b9029406c255d7a093e11f28071f38a17 [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/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/fine/library_manifest.dart';
import 'package:analyzer/src/fine/lookup_name.dart';
import 'package:analyzer/src/fine/manifest_id.dart';
import 'package:analyzer/src/fine/manifest_item.dart';
import 'package:analyzer/src/fine/requirement_failure.dart';
import 'package:analyzer/src/summary2/data_reader.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:meta/meta.dart';
/// When [withFineDependencies], this variable might be set to accumulate
/// requirements for the analysis result being computed.
RequirementsManifest? globalResultRequirements;
/// Whether fine-grained dependencies feature is enabled.
///
/// This cannot be `const` because we change it in tests.
bool withFineDependencies = false;
/// Requirements for a single `export`.
@visibleForTesting
class ExportRequirement {
final Uri fragmentUri;
final Uri exportedUri;
final List<ExportRequirementCombinator> combinators;
final Map<LookupName, ManifestItemId> exportedIds;
ExportRequirement({
required this.fragmentUri,
required this.exportedUri,
required this.combinators,
required this.exportedIds,
});
factory ExportRequirement.read(SummaryDataReader reader) {
return ExportRequirement(
fragmentUri: reader.readUri(),
exportedUri: reader.readUri(),
combinators: reader.readTypedList(
() => ExportRequirementCombinator.read(reader),
),
exportedIds: reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => ManifestItemId.read(reader),
),
);
}
ExportFailure? isSatisfied({
required LinkedElementFactory elementFactory,
required Set<LookupName> declaredTopNames,
}) {
var libraryElement = elementFactory.libraryOfUri(exportedUri);
if (libraryElement == null) {
return ExportLibraryMissing(uri: exportedUri);
}
// SAFETY: every library has the manifest.
var libraryManifest = libraryElement.manifest!;
// Every now exported ID must be previously exported.
var actualCount = 0;
for (var topEntry in libraryManifest.exportedIds.entries) {
var lookupName = topEntry.key;
// If declared locally, export is no-op.
if (declaredTopNames.contains(lookupName)) {
continue;
}
if (!_passCombinators(lookupName)) {
continue;
}
actualCount++;
var actualId = topEntry.value;
var expectedId = exportedIds[lookupName];
if (actualId != expectedId) {
return ExportIdMismatch(
fragmentUri: fragmentUri,
exportedUri: exportedUri,
name: lookupName,
expectedId: expectedId,
actualId: actualId,
);
}
}
// Every now previously ID must be now exported.
if (exportedIds.length != actualCount) {
return ExportCountMismatch(
fragmentUri: fragmentUri,
exportedUri: exportedUri,
actualCount: actualCount,
requiredCount: exportedIds.length,
);
}
return null;
}
void write(BufferedSink sink) {
sink.writeUri(fragmentUri);
sink.writeUri(exportedUri);
sink.writeList(combinators, (combinator) => combinator.write(sink));
sink.writeMap(
exportedIds,
writeKey: (lookupName) => lookupName.write(sink),
writeValue: (id) => id.write(sink),
);
}
bool _passCombinators(LookupName lookupName) {
var baseName = lookupName.asBaseName;
for (var combinator in combinators) {
switch (combinator) {
case ExportRequirementHideCombinator():
if (combinator.hiddenBaseNames.contains(baseName)) {
return false;
}
case ExportRequirementShowCombinator():
if (!combinator.shownBaseNames.contains(baseName)) {
return false;
}
}
}
return true;
}
}
@visibleForTesting
sealed class ExportRequirementCombinator {
ExportRequirementCombinator();
factory ExportRequirementCombinator.read(SummaryDataReader reader) {
var kind = reader.readEnum(_ExportRequirementCombinatorKind.values);
switch (kind) {
case _ExportRequirementCombinatorKind.hide:
return ExportRequirementHideCombinator.read(reader);
case _ExportRequirementCombinatorKind.show:
return ExportRequirementShowCombinator.read(reader);
}
}
void write(BufferedSink sink);
}
@visibleForTesting
final class ExportRequirementHideCombinator
extends ExportRequirementCombinator {
final Set<BaseName> hiddenBaseNames;
ExportRequirementHideCombinator({required this.hiddenBaseNames});
factory ExportRequirementHideCombinator.read(SummaryDataReader reader) {
return ExportRequirementHideCombinator(
hiddenBaseNames: reader.readBaseNameSet(),
);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ExportRequirementCombinatorKind.hide);
sink.writeBaseNameIterable(hiddenBaseNames);
}
}
@visibleForTesting
final class ExportRequirementShowCombinator
extends ExportRequirementCombinator {
final Set<BaseName> shownBaseNames;
ExportRequirementShowCombinator({required this.shownBaseNames});
factory ExportRequirementShowCombinator.read(SummaryDataReader reader) {
return ExportRequirementShowCombinator(
shownBaseNames: reader.readBaseNameSet(),
);
}
@override
void write(BufferedSink sink) {
sink.writeEnum(_ExportRequirementCombinatorKind.show);
sink.writeBaseNameIterable(shownBaseNames);
}
}
/// Requirements for [InstanceElementImpl2].
///
/// If [InterfaceElementImpl2], there are additional requirements in form
/// of [InterfaceItemRequirements].
class InstanceItemRequirements {
final Map<LookupName, ManifestItemId?> requestedFields;
final Map<LookupName, ManifestItemId?> requestedGetters;
final Map<LookupName, ManifestItemId?> requestedSetters;
final Map<LookupName, ManifestItemId?> requestedMethods;
ManifestItemIdList? allDeclaredFields;
ManifestItemIdList? allDeclaredGetters;
ManifestItemIdList? allDeclaredSetters;
ManifestItemIdList? allDeclaredMethods;
InstanceItemRequirements({
required this.requestedFields,
required this.requestedGetters,
required this.requestedSetters,
required this.requestedMethods,
required this.allDeclaredFields,
required this.allDeclaredGetters,
required this.allDeclaredSetters,
required this.allDeclaredMethods,
});
factory InstanceItemRequirements.empty() {
return InstanceItemRequirements(
requestedFields: {},
requestedGetters: {},
requestedSetters: {},
requestedMethods: {},
allDeclaredFields: null,
allDeclaredGetters: null,
allDeclaredSetters: null,
allDeclaredMethods: null,
);
}
factory InstanceItemRequirements.read(SummaryDataReader reader) {
return InstanceItemRequirements(
requestedFields: reader.readNameToIdMap(),
requestedGetters: reader.readNameToIdMap(),
requestedSetters: reader.readNameToIdMap(),
requestedMethods: reader.readNameToIdMap(),
allDeclaredFields: ManifestItemIdList.readOptional(reader),
allDeclaredGetters: ManifestItemIdList.readOptional(reader),
allDeclaredSetters: ManifestItemIdList.readOptional(reader),
allDeclaredMethods: ManifestItemIdList.readOptional(reader),
);
}
void write(BufferedSink sink) {
sink.writeNameToIdMap(requestedFields);
sink.writeNameToIdMap(requestedGetters);
sink.writeNameToIdMap(requestedSetters);
sink.writeNameToIdMap(requestedMethods);
allDeclaredFields.writeOptional(sink);
allDeclaredGetters.writeOptional(sink);
allDeclaredSetters.writeOptional(sink);
allDeclaredMethods.writeOptional(sink);
}
}
/// Requirements for [InterfaceElementImpl2], in addition to those that
/// we already record as [InstanceItemRequirements].
///
/// Includes all requirements from class-like items: classes, enums,
/// extension types, mixins.
class InterfaceItemRequirements {
/// If the element was asked for its full interface.
ManifestItemId? interfaceId;
final Map<LookupName, ManifestItemId?> constructors;
/// These are "methods" in wide meaning: methods, getters, setters.
final Map<LookupName, ManifestItemId?> methods;
InterfaceItemRequirements({
required this.interfaceId,
required this.constructors,
required this.methods,
});
factory InterfaceItemRequirements.empty() {
return InterfaceItemRequirements(
interfaceId: null,
constructors: {},
methods: {},
);
}
factory InterfaceItemRequirements.read(SummaryDataReader reader) {
return InterfaceItemRequirements(
interfaceId: ManifestItemId.readOptional(reader),
constructors: reader.readNameToIdMap(),
methods: reader.readNameToIdMap(),
);
}
void write(BufferedSink sink) {
interfaceId.writeOptional(sink);
sink.writeNameToIdMap(constructors);
sink.writeNameToIdMap(methods);
}
}
/// Requirements for all `export`s of a library.
@visibleForTesting
class LibraryExportRequirements {
final Uri libraryUri;
final Set<LookupName> declaredTopNames;
final List<ExportRequirement> exports;
LibraryExportRequirements({
required this.libraryUri,
required this.declaredTopNames,
required this.exports,
});
factory LibraryExportRequirements.read(SummaryDataReader reader) {
return LibraryExportRequirements(
libraryUri: reader.readUri(),
declaredTopNames: reader.readLookupNameSet(),
exports: reader.readTypedList(() => ExportRequirement.read(reader)),
);
}
ExportFailure? isSatisfied({required LinkedElementFactory elementFactory}) {
for (var export in exports) {
var failure = export.isSatisfied(
elementFactory: elementFactory,
declaredTopNames: declaredTopNames,
);
if (failure != null) {
return failure;
}
}
return null;
}
void write(BufferedSink sink) {
sink.writeUri(libraryUri);
declaredTopNames.write(sink);
sink.writeList(exports, (export) => export.write(sink));
}
}
class RequirementsManifest {
/// LibraryUri => TopName => ID
final Map<Uri, Map<LookupName, ManifestItemId?>> topLevels = {};
/// LibraryUri => TopName => InstanceItemRequirements
final Map<Uri, Map<LookupName, InstanceItemRequirements>> instances = {};
/// LibraryUri => TopName => InterfaceItemRequirements
final Map<Uri, Map<LookupName, InterfaceItemRequirements>> interfaces = {};
final List<LibraryExportRequirements> exportRequirements = [];
int _recordingLockLevel = 0;
RequirementsManifest();
factory RequirementsManifest.read(SummaryDataReader reader) {
var result = RequirementsManifest();
result.topLevels.addAll(
reader.readMap(
readKey: () => reader.readUri(),
readValue: () => reader.readNameToIdMap(),
),
);
result.instances.addAll(
reader.readMap(
readKey: () => reader.readUri(),
readValue: () {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => InstanceItemRequirements.read(reader),
);
},
),
);
result.interfaces.addAll(
reader.readMap(
readKey: () => reader.readUri(),
readValue: () {
return reader.readMap(
readKey: () => LookupName.read(reader),
readValue: () => InterfaceItemRequirements.read(reader),
);
},
),
);
result.exportRequirements.addAll(
reader.readTypedList(() => LibraryExportRequirements.read(reader)),
);
return result;
}
/// Adds requirements to exports from libraries.
///
/// We have already computed manifests for each library.
void addExports({
required LinkedElementFactory elementFactory,
required Set<Uri> libraryUriSet,
}) {
for (var libraryUri in libraryUriSet) {
var libraryElement = elementFactory.libraryOfUri2(libraryUri);
_addExports(libraryElement);
}
}
/// Returns the first unsatisfied requirement, or `null` if all requirements
/// are satisfied.
RequirementFailure? isSatisfied({
required LinkedElementFactory elementFactory,
required Map<Uri, LibraryManifest> libraryManifests,
}) {
for (var libraryEntry in topLevels.entries) {
var libraryUri = libraryEntry.key;
var libraryElement = elementFactory.libraryOfUri(libraryUri);
var libraryManifest = libraryElement?.manifest;
if (libraryManifest == null) {
return LibraryMissing(uri: libraryUri);
}
for (var topLevelEntry in libraryEntry.value.entries) {
var name = topLevelEntry.key;
var actualId = libraryManifest.getExportedId(name);
if (topLevelEntry.value != actualId) {
return TopLevelIdMismatch(
libraryUri: libraryUri,
name: name,
expectedId: topLevelEntry.value,
actualId: actualId,
);
}
}
}
for (var libraryEntry in instances.entries) {
var libraryUri = libraryEntry.key;
var libraryElement = elementFactory.libraryOfUri(libraryUri);
var libraryManifest = libraryElement?.manifest;
if (libraryManifest == null) {
return LibraryMissing(uri: libraryUri);
}
for (var instanceEntry in libraryEntry.value.entries) {
var instanceName = instanceEntry.key;
var requirements = instanceEntry.value;
var instanceItem =
libraryManifest.declaredClasses[instanceName] ??
libraryManifest.declaredEnums[instanceName] ??
libraryManifest.declaredExtensions[instanceName] ??
libraryManifest.declaredExtensionTypes[instanceName] ??
libraryManifest.declaredMixins[instanceName];
if (instanceItem is! InstanceItem) {
return TopLevelNotInterface(
libraryUri: libraryUri,
name: instanceName,
);
}
for (var fieldEntry in requirements.requestedFields.entries) {
var name = fieldEntry.key;
var expectedId = fieldEntry.value;
var currentId = instanceItem.getDeclaredFieldId(name);
if (expectedId != currentId) {
return InstanceFieldIdMismatch(
libraryUri: libraryUri,
interfaceName: instanceName,
fieldName: name,
expectedId: expectedId,
actualId: currentId,
);
}
}
for (var getterEntry in requirements.requestedGetters.entries) {
var name = getterEntry.key;
var expectedId = getterEntry.value;
var currentId = instanceItem.getDeclaredGetterId(name);
if (expectedId != currentId) {
return InstanceMethodIdMismatch(
libraryUri: libraryUri,
interfaceName: instanceName,
methodName: name,
expectedId: expectedId,
actualId: currentId,
);
}
}
for (var setterEntry in requirements.requestedSetters.entries) {
var name = setterEntry.key;
var expectedId = setterEntry.value;
var currentId = instanceItem.getDeclaredSetterId(name);
if (expectedId != currentId) {
return InstanceMethodIdMismatch(
libraryUri: libraryUri,
interfaceName: instanceName,
methodName: name,
expectedId: expectedId,
actualId: currentId,
);
}
}
for (var methodEntry in requirements.requestedMethods.entries) {
var name = methodEntry.key;
var expectedId = methodEntry.value;
var currentId = instanceItem.getDeclaredMethodId(name);
if (expectedId != currentId) {
return InstanceMethodIdMismatch(
libraryUri: libraryUri,
interfaceName: instanceName,
methodName: name,
expectedId: expectedId,
actualId: currentId,
);
}
}
if (requirements.allDeclaredFields case var required?) {
var actualItems = instanceItem.declaredFields.values;
var actualIds = actualItems.map((item) => item.id);
if (!required.equalToIterable(actualIds)) {
return InstanceChildrenIdsMismatch(
libraryUri: libraryUri,
instanceName: instanceName,
childrenPropertyName: 'fields',
expectedIds: required,
actualIds: ManifestItemIdList(actualIds.toList()),
);
}
}
if (requirements.allDeclaredGetters case var required?) {
var actualItems = instanceItem.declaredGetters.values;
var actualIds = actualItems.map((item) => item.id);
if (!required.equalToIterable(actualIds)) {
return InstanceChildrenIdsMismatch(
libraryUri: libraryUri,
instanceName: instanceName,
childrenPropertyName: 'getters',
expectedIds: required,
actualIds: ManifestItemIdList(actualIds.toList()),
);
}
}
if (requirements.allDeclaredSetters case var required?) {
var actualItems = instanceItem.declaredSetters.values;
var actualIds = actualItems.map((item) => item.id);
if (!required.equalToIterable(actualIds)) {
return InstanceChildrenIdsMismatch(
libraryUri: libraryUri,
instanceName: instanceName,
childrenPropertyName: 'setters',
expectedIds: required,
actualIds: ManifestItemIdList(actualIds.toList()),
);
}
}
if (requirements.allDeclaredMethods case var required?) {
var actualItems = instanceItem.declaredMethods.values;
var actualIds = actualItems.map((item) => item.id);
if (!required.equalToIterable(actualIds)) {
return InstanceChildrenIdsMismatch(
libraryUri: libraryUri,
instanceName: instanceName,
childrenPropertyName: 'methods',
expectedIds: required,
actualIds: ManifestItemIdList(actualIds.toList()),
);
}
}
}
}
for (var libraryEntry in interfaces.entries) {
var libraryUri = libraryEntry.key;
var libraryElement = elementFactory.libraryOfUri(libraryUri);
var libraryManifest = libraryElement?.manifest;
if (libraryManifest == null) {
return LibraryMissing(uri: libraryUri);
}
for (var interfaceEntry in libraryEntry.value.entries) {
var interfaceName = interfaceEntry.key;
var interfaceItem =
libraryManifest.declaredClasses[interfaceName] ??
libraryManifest.declaredEnums[interfaceName] ??
libraryManifest.declaredExtensionTypes[interfaceName] ??
libraryManifest.declaredMixins[interfaceName];
if (interfaceItem is! InterfaceItem) {
return TopLevelNotInterface(
libraryUri: libraryUri,
name: interfaceName,
);
}
var interfaceRequirements = interfaceEntry.value;
if (interfaceRequirements.interfaceId case var expectedId?) {
var actualId = interfaceItem.interface.id;
if (expectedId != actualId) {
return InterfaceIdMismatch(
libraryUri: libraryUri,
interfaceName: interfaceName,
expectedId: expectedId,
actualId: actualId,
);
}
}
var constructors = interfaceRequirements.constructors;
for (var constructorEntry in constructors.entries) {
var constructorName = constructorEntry.key;
var constructorId = interfaceItem.getConstructorId(constructorName);
var expectedId = constructorEntry.value;
if (expectedId != constructorId) {
return InterfaceConstructorIdMismatch(
libraryUri: libraryUri,
interfaceName: interfaceName,
constructorName: constructorName,
expectedId: expectedId,
actualId: constructorId,
);
}
}
var methods = interfaceRequirements.methods;
for (var methodEntry in methods.entries) {
var methodName = methodEntry.key;
var methodId = interfaceItem.getInterfaceMethodId(methodName);
var expectedId = methodEntry.value;
if (expectedId != methodId) {
return InstanceMethodIdMismatch(
libraryUri: libraryUri,
interfaceName: interfaceName,
methodName: methodName,
expectedId: expectedId,
actualId: methodId,
);
}
}
}
}
for (var exportRequirement in exportRequirements) {
var failure = exportRequirement.isSatisfied(
elementFactory: elementFactory,
);
if (failure != null) {
return failure;
}
}
return null;
}
void record_classElement_allSubtypes({required ClassElementImpl2 element}) {
// TODO(scheglov): implement.
}
void record_classElement_hasNonFinalField({
required ClassElementImpl2 element,
}) {
// TODO(scheglov): implement.
}
void record_classElement_isEnumLike({required ClassElementImpl2 element}) {
// TODO(scheglov): implement.
}
void record_disable(Object target, String method) {
// TODO(scheglov): implement.
}
/// Record that [id] was looked up in the import prefix scope that
/// imports [importedLibraries].
void record_importPrefixScope_lookup({
required List<LibraryElementImpl> importedLibraries,
required String id,
}) {
var lookupName = id.asLookupName;
for (var importedLibrary in importedLibraries) {
if (importedLibrary.manifest case var manifest?) {
var uri = importedLibrary.uri;
var nameToId = topLevels[uri] ??= {};
nameToId[lookupName] = manifest.getExportedId(lookupName);
}
}
}
void record_instanceElement_fields({required InstanceElementImpl2 element}) {
if (_recordingLockLevel != 0) {
return;
}
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
requirements.allDeclaredFields ??= ManifestItemIdList(
item.declaredFields.values.map((item) => item.id).toList(),
);
}
void record_instanceElement_getField({
required InstanceElementImpl2 element,
required String name,
}) {
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var fieldName = name.asLookupName;
var fieldId = item.getDeclaredFieldId(fieldName);
requirements.requestedFields[fieldName] = fieldId;
}
void record_instanceElement_getGetter({
required InstanceElementImpl2 element,
required String name,
}) {
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var methodName = name.asLookupName;
var methodId = item.getDeclaredGetterId(methodName);
requirements.requestedGetters[methodName] = methodId;
}
void record_instanceElement_getMethod({
required InstanceElementImpl2 element,
required String name,
}) {
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var methodName = name.asLookupName;
var methodId = item.getDeclaredMethodId(methodName);
requirements.requestedMethods[methodName] = methodId;
}
void record_instanceElement_getSetter({
required InstanceElementImpl2 element,
required String name,
}) {
assert(!name.endsWith('='));
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var methodName = '$name='.asLookupName;
var methodId = item.getDeclaredSetterId(methodName);
requirements.requestedSetters[methodName] = methodId;
}
void record_instanceElement_getters({required InstanceElementImpl2 element}) {
if (_recordingLockLevel != 0) {
return;
}
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
requirements.allDeclaredGetters ??= ManifestItemIdList(
item.declaredGetters.values.map((item) => item.id).toList(),
);
}
void record_instanceElement_methods({required InstanceElementImpl2 element}) {
if (_recordingLockLevel != 0) {
return;
}
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
requirements.allDeclaredMethods ??= ManifestItemIdList(
item.declaredMethods.values.map((item) => item.id).toList(),
);
}
void record_instanceElement_setters({required InstanceElementImpl2 element}) {
if (_recordingLockLevel != 0) {
return;
}
var itemRequirements = _getInstanceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
requirements.allDeclaredSetters ??= ManifestItemIdList(
item.declaredSetters.values.map((item) => item.id).toList(),
);
}
void record_interface_all({required InterfaceElementImpl2 element}) {
var itemRequirements = _getInterfaceItem(element);
if (itemRequirements == null) {
return;
}
var interface = itemRequirements.item.interface;
itemRequirements.requirements.interfaceId = interface.id;
}
/// Record that a member with [nameObj] was requested from the interface
/// of [element]. The [methodElement] is used for consistency checking.
void record_interface_getMember({
required InterfaceElementImpl2 element,
required Name nameObj,
required ExecutableElement? methodElement,
}) {
// Skip private names, cannot be used outside this library.
if (!nameObj.isPublic) {
return;
}
var itemRequirements = _getInterfaceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var methodName = nameObj.name.asLookupName;
var methodId = item.getInterfaceMethodId(methodName);
requirements.methods[methodName] = methodId;
// Check for consistency between the actual interface and manifest.
if (methodElement != null) {
if (methodId == null) {
var qName = _qualifiedMethodName(element, methodName);
throw StateError('Expected ID for $qName');
}
} else {
if (methodId != null) {
var qName = _qualifiedMethodName(element, methodName);
throw StateError('Expected no ID for $qName');
}
}
}
void record_interfaceElement_getNamedConstructor({
required InterfaceElementImpl2 element,
required String name,
}) {
var itemRequirements = _getInterfaceItem(element);
if (itemRequirements == null) {
return;
}
var item = itemRequirements.item;
var requirements = itemRequirements.requirements;
var constructorName = name.asLookupName;
var constructorId = item.getConstructorId(constructorName);
requirements.constructors[constructorName] = constructorId;
}
void record_propertyAccessorElement_variable({
required PropertyAccessorElementImpl2 element,
required String? name,
}) {
if (name == null) {
return;
}
switch (element.enclosingElement) {
case InstanceElementImpl2 instanceElement:
record_instanceElement_getField(element: instanceElement, name: name);
default:
// TODO(scheglov): support for top-level variables
}
}
/// This method is invoked after linking of a library cycle, to exclude
/// requirements to the libraries of this same library cycle. We already
/// link these libraries together, so only requirements to the previous
/// libraries are interesting.
void removeReqForLibs(Set<Uri> bundleLibraryUriList) {
var uriSet = bundleLibraryUriList.toSet();
for (var exportRequirement in exportRequirements) {
exportRequirement.exports.removeWhere((export) {
return uriSet.contains(export.exportedUri);
});
}
for (var libUri in bundleLibraryUriList) {
topLevels.remove(libUri);
instances.remove(libUri);
interfaces.remove(libUri);
}
}
void write(BufferedSink sink) {
sink.writeMap(
topLevels,
writeKey: (uri) => sink.writeUri(uri),
writeValue: (map) => sink.writeNameToIdMap(map),
);
sink.writeMap(
instances,
writeKey: (uri) => sink.writeUri(uri),
writeValue: (nameToInstanceMap) {
sink.writeMap(
nameToInstanceMap,
writeKey: (name) => name.write(sink),
writeValue: (instance) => instance.write(sink),
);
},
);
sink.writeMap(
interfaces,
writeKey: (uri) => sink.writeUri(uri),
writeValue: (nameToInterfaceMap) {
sink.writeMap(
nameToInterfaceMap,
writeKey: (name) => name.write(sink),
writeValue: (interface) => interface.write(sink),
);
},
);
sink.writeList(
exportRequirements,
(requirement) => requirement.write(sink),
);
}
void _addExports(LibraryElementImpl libraryElement) {
var declaredTopNames =
libraryElement.children2
.map((element) => element.lookupName)
.nonNulls
.map((nameStr) => nameStr.asLookupName)
.toSet();
var fragments = <ExportRequirement>[];
for (var fragment in libraryElement.fragments) {
for (var export in fragment.libraryExports) {
var exportedLibrary = export.exportedLibrary2;
// If no library, then there is nothing to re-export.
if (exportedLibrary == null) {
continue;
}
var combinators =
export.combinators.map((combinator) {
switch (combinator) {
case HideElementCombinator():
return ExportRequirementHideCombinator(
hiddenBaseNames: combinator.hiddenNames.toBaseNameSet(),
);
case ShowElementCombinator():
return ExportRequirementShowCombinator(
shownBaseNames: combinator.shownNames.toBaseNameSet(),
);
}
}).toList();
// SAFETY: every library has the manifest.
var manifest = exportedLibrary.manifest!;
var exportedIds = <LookupName, ManifestItemId>{};
var exportMap = NamespaceBuilder().createExportNamespaceForDirective2(
export,
);
for (var entry in exportMap.definedNames2.entries) {
var lookupName = entry.key.asLookupName;
if (declaredTopNames.contains(lookupName)) {
continue;
}
// TODO(scheglov): must always be not null.
var id = manifest.getExportedId(lookupName);
if (id != null) {
exportedIds[lookupName] = id;
}
}
fragments.add(
ExportRequirement(
fragmentUri: fragment.source.uri,
exportedUri: exportedLibrary.uri,
combinators: combinators,
exportedIds: exportedIds,
),
);
}
}
if (fragments.isNotEmpty) {
exportRequirements.add(
LibraryExportRequirements(
libraryUri: libraryElement.uri,
declaredTopNames: declaredTopNames,
exports: fragments,
),
);
}
}
_InstanceItemWithRequirements? _getInstanceItem(
InstanceElementImpl2 element,
) {
var libraryElement = element.library2;
var manifest = libraryElement.manifest;
// If we are linking the library, its manifest is not set yet.
// But then we also don't care about this dependency.
if (manifest == null) {
return null;
}
var instanceName = element.lookupName?.asLookupName;
if (instanceName == null) {
return null;
}
var instancesMap = instances[libraryElement.uri] ??= {};
var instanceItem =
manifest.declaredClasses[instanceName] ??
manifest.declaredEnums[instanceName] ??
manifest.declaredExtensions[instanceName] ??
manifest.declaredExtensionTypes[instanceName] ??
manifest.declaredMixins[instanceName];
// SAFETY: every instance element must be in the manifest.
instanceItem as InstanceItem;
var requirements =
instancesMap[instanceName] ??= InstanceItemRequirements.empty();
return _InstanceItemWithRequirements(
item: instanceItem,
requirements: requirements,
);
}
_InterfaceItemWithRequirements? _getInterfaceItem(
InterfaceElementImpl2 element,
) {
var libraryElement = element.library2;
var manifest = libraryElement.manifest;
// If we are linking the library, its manifest is not set yet.
// But then we also don't care about this dependency.
if (manifest == null) {
return null;
}
var interfaceName = element.lookupName?.asLookupName;
if (interfaceName == null) {
return null;
}
var interfacesMap = interfaces[libraryElement.uri] ??= {};
var interfaceItem =
manifest.declaredClasses[interfaceName] ??
manifest.declaredEnums[interfaceName] ??
manifest.declaredExtensionTypes[interfaceName] ??
manifest.declaredMixins[interfaceName];
// SAFETY: every interface element must be in the manifest.
interfaceItem as InterfaceItem;
var requirements =
interfacesMap[interfaceName] ??= InterfaceItemRequirements.empty();
return _InterfaceItemWithRequirements(
item: interfaceItem,
requirements: requirements,
);
}
String _qualifiedMethodName(
InterfaceElementImpl2 element,
LookupName methodName,
) {
return '${element.library2.uri} '
'${element.displayName}.'
'${methodName.asString}';
}
}
enum _ExportRequirementCombinatorKind { hide, show }
class _InstanceItemWithRequirements {
final InstanceItem item;
final InstanceItemRequirements requirements;
_InstanceItemWithRequirements({
required this.item,
required this.requirements,
});
}
class _InterfaceItemWithRequirements {
final InterfaceItem item;
final InterfaceItemRequirements requirements;
_InterfaceItemWithRequirements({
required this.item,
required this.requirements,
});
}
extension RequirementsManifestExtension on RequirementsManifest? {
T withoutRecording<T>({
required String reason,
required T Function() operation,
}) {
var self = this;
if (self == null) {
return operation();
} else {
self._recordingLockLevel++;
try {
return operation();
} finally {
self._recordingLockLevel--;
}
}
}
}
extension _BufferedSinkExtension on BufferedSink {
void writeNameToIdMap(Map<LookupName, ManifestItemId?> map) {
writeMap(
map,
writeKey: (name) => name.write(this),
writeValue: (id) => id.writeOptional(this),
);
}
}
extension _SummaryDataReaderExtension on SummaryDataReader {
Map<LookupName, ManifestItemId?> readNameToIdMap() {
return readMap(
readKey: () => LookupName.read(this),
readValue: () => ManifestItemId.readOptional(this),
);
}
}