Fine. Move addExports() invocation, split _LibraryMatch.
Change-Id: I834b8e922afd493de6a4425363ca61dfac2c3d4e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/413300
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index cf358cf..53b935e 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -221,7 +221,7 @@
var newLibraryManifests = <Uri, LibraryManifest>{};
performance.run('computeManifests', (performance) {
- newLibraryManifests = BundleManifest(
+ newLibraryManifests = LibraryManifestBuilder(
elementFactory: elementFactory,
inputLibraries: cycle.libraries,
inputLibraryManifests: inputLibraryManifests,
@@ -232,6 +232,10 @@
elementFactory.libraryManifests.addAll(newLibraryManifests);
});
+ requirementsManifest.addExports(
+ elementFactory: elementFactory,
+ libraryUriSet: cycle.libraryUris,
+ );
requirementsManifest.removeReqForLibs(cycle.libraryUris);
bundleEntry = LinkedBundleEntry(
diff --git a/pkg/analyzer/lib/src/fine/library_manifest.dart b/pkg/analyzer/lib/src/fine/library_manifest.dart
index ba5d68b..53f293b 100644
--- a/pkg/analyzer/lib/src/fine/library_manifest.dart
+++ b/pkg/analyzer/lib/src/fine/library_manifest.dart
@@ -15,13 +15,55 @@
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/util/performance/operation_performance.dart';
-class BundleManifest {
+/// The manifest of a single library.
+class LibraryManifest {
+ /// The URI of the library, mostly for debugging.
+ final Uri uri;
+
+ /// The manifests of the top-level items.
+ final Map<LookupName, TopLevelItem> items;
+
+ LibraryManifest({
+ required this.uri,
+ required this.items,
+ });
+
+ factory LibraryManifest.read(SummaryDataReader reader) {
+ return LibraryManifest(
+ uri: reader.readUri(),
+ items: reader.readMap(
+ readKey: () => LookupName.read(reader),
+ readValue: () => TopLevelItem.read(reader),
+ ),
+ );
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeUri(uri);
+ sink.writeMap(
+ items,
+ writeKey: (lookupName) => lookupName.write(sink),
+ writeValue: (item) => item.write(sink),
+ );
+ }
+}
+
+class LibraryManifestBuilder {
final LinkedElementFactory elementFactory;
final List<LibraryFileKind> inputLibraries;
+
+ /// The previous manifests for libraries.
+ ///
+ /// For correctness it does not matter what is in these manifests, they
+ /// can be absent at all for any library (and they are for new libraries).
+ /// But it does matter for performance, because we will give a new ID for any
+ /// element that is not in the manifest, or have different "meaning". This
+ /// will cause cascading changes to items that referenced these elements,
+ /// new IDs for them, etc.
final Map<Uri, LibraryManifest> inputLibraryManifests;
final BundleRequirementsManifest requirementsManifest;
- BundleManifest({
+ LibraryManifestBuilder({
required this.elementFactory,
required this.inputLibraries,
required this.inputLibraryManifests,
@@ -49,13 +91,14 @@
for (var libraryElement in libraryElements) {
var libraryUri = libraryElement.uri;
var manifest = _getInputManifest(libraryUri);
- manifest.compareStructures(
+ _LibraryMatch(
+ manifest: manifest,
+ library: libraryElement,
itemMap: itemMap,
+ structureMismatched: affectedElements,
refElementsMap: refElementsMap,
refExternalIds: refExternalIds,
- structureMismatched: affectedElements,
- library: libraryElement,
- );
+ ).compareStructures();
}
performance
@@ -200,7 +243,7 @@
result[libraryUri] = newManifest;
}
- // Add re-exported elements, and corresponding requirements.
+ // Add re-exported elements.
for (var libraryElement in libraryElements) {
var libraryUri = libraryElement.uri;
var manifest = result[libraryUri]!;
@@ -229,8 +272,6 @@
id: id,
);
}
-
- requirementsManifest.addExportRequirements(libraryElement);
}
return result;
@@ -242,164 +283,171 @@
}
}
-/// The manifest of a single library.
-class LibraryManifest {
- /// The URI of the library, mostly for debugging.
- final Uri uri;
+/// Compares structures of [library] children against [manifest].
+class _LibraryMatch {
+ final LibraryElementImpl library;
- /// The manifests of the top-level items.
- final Map<LookupName, TopLevelItem> items;
+ /// A previous manifest for the [library].
+ ///
+ /// Strictly speaking, it does not have to be the latest manifest, it could
+ /// be empty at all (and is empty when this is a new library). It is used
+ /// to give the same identifiers to the elements with the same meaning.
+ final LibraryManifest manifest;
- LibraryManifest({
- required this.uri,
- required this.items,
+ /// Elements that have structure matching the corresponding items from
+ /// [manifest].
+ final Map<Element2, ManifestItem> itemMap;
+
+ /// Elements with mismatched structure.
+ /// These elements will get new identifiers.
+ final Set<Element2> structureMismatched;
+
+ /// Key: an element of [library].
+ /// Value: the elements that the key references.
+ ///
+ /// This includes references to elements of this bundle, and of external
+ /// bundles. This information allows propagating invalidation from affected
+ /// elements to their dependents.
+ // TODO(scheglov): hm... maybe store it? And reverse it.
+ final Map<Element2, List<Element2>> refElementsMap;
+
+ /// Key: an element from an external bundle.
+ /// Value: the identifier at the time when [manifest] was built.
+ ///
+ /// If [LibraryManifestBuilder] later finds that some of these elements now
+ /// have different identifiers, it propagates invalidation using
+ /// [refElementsMap].
+ final Map<Element2, ManifestItemId> refExternalIds;
+
+ _LibraryMatch({
+ required this.manifest,
+ required this.library,
+ required this.itemMap,
+ required this.refElementsMap,
+ required this.refExternalIds,
+ required this.structureMismatched,
});
- factory LibraryManifest.read(SummaryDataReader reader) {
- return LibraryManifest(
- uri: reader.readUri(),
- items: reader.readMap(
- readKey: () => LookupName.read(reader),
- readValue: () => TopLevelItem.read(reader),
- ),
- );
- }
-
- /// Compares structures of [library] children against the [items] of this
- /// manifest.
- ///
- /// Records mismatched elements into [structureMismatched].
- ///
- /// Records dependencies into [refElementsMap]. This includes
- /// references to elements of this bundle, and of dependencies.
- ///
- /// Records the required identifiers (stored in this manifest) of elements
- /// that are not from this bundle.
- void compareStructures({
- required Map<Element2, ManifestItem> itemMap,
- required Map<Element2, List<Element2>> refElementsMap,
- required Map<Element2, ManifestItemId> refExternalIds,
- required Set<Element2> structureMismatched,
- required LibraryElementImpl library,
- }) {
- bool handleInterfaceExecutable(
- MatchContext interfaceMatchContext,
- Map<LookupName, InstanceMemberItem> members,
- Name nameObj,
- ExecutableElement2 executable,
- ) {
- // Skip private names, cannot be used outside this library.
- if (!nameObj.isPublic) {
- return true;
- }
-
- var item2 = members[nameObj.name.asLookupName];
-
- switch (executable) {
- case GetterElement2OrMember():
- if (item2 is! InstanceGetterItem) {
- return false;
- }
-
- var matchContext = item2.match(interfaceMatchContext, executable);
- if (matchContext == null) {
- return false;
- }
-
- itemMap[executable] = item2;
- refElementsMap[executable] = matchContext.elementList;
- refExternalIds.addAll(matchContext.externalIds);
- return true;
- case MethodElement2OrMember():
- if (item2 is! InstanceMethodItem) {
- return false;
- }
-
- var matchContext = item2.match(interfaceMatchContext, executable);
- if (matchContext == null) {
- item2.match(interfaceMatchContext, executable);
- return false;
- }
-
- itemMap[executable] = item2;
- refElementsMap[executable] = matchContext.elementList;
- refExternalIds.addAll(matchContext.externalIds);
- return true;
- case SetterElement2OrMember():
- // TODO(scheglov): implement
- return true;
- }
-
- // TODO(scheglov): fix it
- throw UnimplementedError('(${executable.runtimeType}) $executable');
- }
-
- bool handleClassElement(LookupName? name, ClassElementImpl2 element) {
- var item = items[name];
- if (item is! ClassItem) {
- return false;
- }
-
- var matchContext = item.match(element);
- if (matchContext == null) {
- return false;
- }
-
- itemMap[element] = item;
- refElementsMap[element] = matchContext.elementList;
- refExternalIds.addAll(matchContext.externalIds);
-
- var map2 = element.inheritanceManager.getInterface2(element).map2;
- for (var entry in map2.entries) {
- var nameObj = entry.key;
- var executable = entry.value;
- if (!handleInterfaceExecutable(
- matchContext, item.members, nameObj, executable)) {
- structureMismatched.add(executable);
- }
- }
-
- return true;
- }
-
- bool handleTopGetterElement(LookupName? name, GetterElementImpl element) {
- var item = items[name];
- if (item is! TopLevelGetterItem) {
- return false;
- }
-
- var matchContext = item.match(element);
- if (matchContext == null) {
- return false;
- }
-
- itemMap[element] = item;
- refElementsMap[element] = matchContext.elementList;
- refExternalIds.addAll(matchContext.externalIds);
- return true;
- }
-
+ void compareStructures() {
for (var element in library.children2) {
var name = element.lookupName?.asLookupName;
switch (element) {
case ClassElementImpl2():
- if (!handleClassElement(name, element)) {
+ if (!_matchClass(name: name, element: element)) {
structureMismatched.add(element);
}
case GetterElementImpl():
- if (!handleTopGetterElement(name, element)) {
+ if (!_matchTopGetter(name: name, element: element)) {
structureMismatched.add(element);
}
}
}
}
- void write(BufferedSink sink) {
- sink.writeUri(uri);
- sink.writeMap(
- items,
- writeKey: (lookupName) => lookupName.write(sink),
- writeValue: (item) => item.write(sink),
- );
+ bool _matchClass({
+ required LookupName? name,
+ required ClassElementImpl2 element,
+ }) {
+ var item = manifest.items[name];
+ if (item is! ClassItem) {
+ return false;
+ }
+
+ var matchContext = item.match(element);
+ if (matchContext == null) {
+ return false;
+ }
+
+ itemMap[element] = item;
+ refElementsMap[element] = matchContext.elementList;
+ refExternalIds.addAll(matchContext.externalIds);
+
+ var map2 = element.inheritanceManager.getInterface2(element).map2;
+ for (var entry in map2.entries) {
+ var nameObj = entry.key;
+ var executable = entry.value;
+ if (!_matchInterfaceExecutable(
+ interfaceMatchContext: matchContext,
+ members: item.members,
+ nameObj: nameObj,
+ executable: executable,
+ )) {
+ structureMismatched.add(executable);
+ }
+ }
+
+ return true;
+ }
+
+ bool _matchInterfaceExecutable({
+ required MatchContext interfaceMatchContext,
+ required Map<LookupName, InstanceMemberItem> members,
+ required Name nameObj,
+ required ExecutableElement2 executable,
+ }) {
+ // Skip private names, cannot be used outside this library.
+ if (!nameObj.isPublic) {
+ return true;
+ }
+
+ var item = members[nameObj.name.asLookupName];
+
+ switch (executable) {
+ case GetterElement2OrMember():
+ if (item is! InstanceGetterItem) {
+ return false;
+ }
+
+ var matchContext = item.match(interfaceMatchContext, executable);
+ if (matchContext == null) {
+ return false;
+ }
+
+ itemMap[executable] = item;
+ refElementsMap[executable] = matchContext.elementList;
+ refExternalIds.addAll(matchContext.externalIds);
+ return true;
+ case MethodElement2OrMember():
+ if (item is! InstanceMethodItem) {
+ return false;
+ }
+
+ var matchContext = item.match(interfaceMatchContext, executable);
+ if (matchContext == null) {
+ item.match(interfaceMatchContext, executable);
+ return false;
+ }
+
+ itemMap[executable] = item;
+ refElementsMap[executable] = matchContext.elementList;
+ refExternalIds.addAll(matchContext.externalIds);
+ return true;
+ case SetterElement2OrMember():
+ // TODO(scheglov): implement
+ return true;
+ }
+
+ // TODO(scheglov): fix it
+ throw UnimplementedError('(${executable.runtimeType}) $executable');
+ }
+
+ bool _matchTopGetter({
+ required LookupName? name,
+ required GetterElementImpl element,
+ }) {
+ var item = manifest.items[name];
+ if (item is! TopLevelGetterItem) {
+ return false;
+ }
+
+ var matchContext = item.match(element);
+ if (matchContext == null) {
+ return false;
+ }
+
+ itemMap[element] = item;
+ refElementsMap[element] = matchContext.elementList;
+ refExternalIds.addAll(matchContext.externalIds);
+ return true;
}
}
diff --git a/pkg/analyzer/lib/src/fine/requirements.dart b/pkg/analyzer/lib/src/fine/requirements.dart
index 34415cb..6bed89e 100644
--- a/pkg/analyzer/lib/src/fine/requirements.dart
+++ b/pkg/analyzer/lib/src/fine/requirements.dart
@@ -80,51 +80,16 @@
return result;
}
- void addExportRequirements(LibraryElementImpl libraryElement) {
- for (var fragment in libraryElement.fragments) {
- for (var export in fragment.libraryExports) {
- var exportedLibrary = export.exportedLibrary;
- // TODO(scheglov): record this
- 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();
-
- if (exportedLibrary.manifest case var manifest?) {
- var exportedIds = <LookupName, ManifestItemId>{};
- var exportMap =
- NamespaceBuilder().createExportNamespaceForDirective2(export);
- for (var entry in exportMap.definedNames2.entries) {
- var lookupName = entry.key.asLookupName;
- // TODO(scheglov): must always be not null.
- var item = manifest.items[lookupName];
- if (item != null) {
- exportedIds[lookupName] = item.id;
- }
- }
-
- exportRequirements.add(
- _ExportRequirement(
- fragmentUri: fragment.source.uri,
- exportedUri: exportedLibrary.uri,
- combinators: combinators,
- exportedIds: exportedIds,
- ),
- );
- }
- }
+ /// 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);
}
}
@@ -346,6 +311,55 @@
(requirement) => requirement.write(sink),
);
}
+
+ void _addExports(LibraryElementImpl libraryElement) {
+ for (var fragment in libraryElement.fragments) {
+ for (var export in fragment.libraryExports) {
+ var exportedLibrary = export.exportedLibrary;
+ // TODO(scheglov): record this
+ 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;
+ // TODO(scheglov): must always be not null.
+ var item = manifest.items[lookupName];
+ if (item != null) {
+ exportedIds[lookupName] = item.id;
+ }
+ }
+
+ exportRequirements.add(
+ _ExportRequirement(
+ fragmentUri: fragment.source.uri,
+ exportedUri: exportedLibrary.uri,
+ combinators: combinators,
+ exportedIds: exportedIds,
+ ),
+ );
+ }
+ }
+ }
}
@visibleForTesting