Fine. Track combined signatures, to support top-merges.
Change-Id: Ia8aad87209f304f03a993db7883eb7c988c2fdf0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/424920
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
index 719e3f9..889bec1 100644
--- a/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
+++ b/pkg/analyzer/lib/src/dart/element/inheritance_manager3.dart
@@ -80,6 +80,11 @@
/// Cached instance interfaces for [InterfaceFragmentImpl].
final Map<InterfaceFragmentImpl, Interface> _interfaces = {};
+ /// Tracks signatures from superinterfaces that were combined.
+ /// It is used to track dependencies in manifests.
+ final Map<InterfaceFragmentImpl, Map<Name, List<ExecutableElement2OrMember>>>
+ _combinedSignatures = {};
+
/// The set of classes that are currently being processed, used to detect
/// self-referencing cycles.
final Set<InterfaceFragmentImpl> _processingClasses = {};
@@ -124,6 +129,9 @@
return null;
}
+ (_combinedSignatures[targetClass] ??= {})[name] =
+ candidates.map((e) => e.asElement2).toList();
+
if (doTopMerge) {
var typeSystem = targetClass.library.typeSystem;
return _topMerge(typeSystem, targetClass, validOverrides);
@@ -786,6 +794,7 @@
superImplemented: superImplemented,
superImplemented2: superImplemented2,
conflicts: conflicts.toFixedList(),
+ combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
);
}
@@ -985,6 +994,7 @@
superImplemented: const [],
superImplemented2: const [],
conflicts: conflicts.toFixedList(),
+ combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
);
}
@@ -1057,6 +1067,7 @@
superImplemented2: [superInterface2],
conflicts:
<Conflict>[...superConflicts, ...interfaceConflicts].toFixedList(),
+ combinedSignatures: _combinedSignatures.remove(fragment) ?? {},
);
}
@@ -1277,6 +1288,7 @@
superImplemented: const [{}],
superImplemented2: const [{}],
conflicts: const [],
+ combinedSignatures: const {},
);
/// The map of names to their signature in the interface.
@@ -1327,6 +1339,10 @@
/// members of the class.
final List<Conflict> conflicts;
+ /// Tracks signatures from superinterfaces that were combined.
+ /// It is used to track dependencies in manifests.
+ final Map<Name, List<ExecutableElement2OrMember>> combinedSignatures;
+
/// The map of names to the most specific signatures from the mixins,
/// superclasses, or interfaces.
Map<Name, ExecutableElementOrMember>? inheritedMap;
@@ -1344,6 +1360,7 @@
required this.superImplemented,
required this.superImplemented2,
required this.conflicts,
+ required this.combinedSignatures,
});
/// The map of declared names to their signatures.
diff --git a/pkg/analyzer/lib/src/fine/library_manifest.dart b/pkg/analyzer/lib/src/fine/library_manifest.dart
index 519f149..a91c166 100644
--- a/pkg/analyzer/lib/src/fine/library_manifest.dart
+++ b/pkg/analyzer/lib/src/fine/library_manifest.dart
@@ -137,7 +137,6 @@
typeParameters,
) {
classItem.declaredMembers.clear();
- classItem.interface.clear();
_addInterfaceElementExecutables(
encodingContext: encodingContext,
instanceElement: element,
@@ -374,7 +373,6 @@
typeParameters,
) {
mixinItem.declaredMembers.clear();
- mixinItem.interface.clear();
_addInterfaceElementExecutables(
encodingContext: encodingContext,
instanceElement: element,
@@ -575,6 +573,7 @@
// Must be created already.
var item = declaredItems[element] as InterfaceItem;
+ item.interface.beforeUpdating();
var interface = element.inheritanceManager.getInterface2(element);
for (var entry in interface.map2.entries) {
@@ -591,9 +590,34 @@
continue;
}
+ var combinedCandidates = interface.combinedSignatures[entry.key];
+ if (combinedCandidates != null) {
+ var candidateElements =
+ combinedCandidates
+ .map((candidate) => candidate.baseElement)
+ .toSet()
+ .toList();
+ if (candidateElements.length == 1) {
+ executable = candidateElements[0];
+ } else {
+ var candidateIds =
+ candidateElements.map((candidate) {
+ return _getInterfaceElementMemberId(candidate);
+ }).toList();
+ var idList = ManifestItemIdList(candidateIds);
+ var id = item.interface.combinedIdsTemp[idList];
+ id ??= ManifestItemId.generate();
+ item.interface.map[lookupName] = id;
+ item.interface.combinedIds[idList] = id;
+ continue;
+ }
+ }
+
var id = _getInterfaceElementMemberId(executable);
item.interface.map[lookupName] = id;
}
+
+ item.interface.afterUpdate();
}
void _fillInterfaceElementsInterface() {
diff --git a/pkg/analyzer/lib/src/fine/manifest_id.dart b/pkg/analyzer/lib/src/fine/manifest_id.dart
index d491fb9..30cca98 100644
--- a/pkg/analyzer/lib/src/fine/manifest_id.dart
+++ b/pkg/analyzer/lib/src/fine/manifest_id.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/src/summary2/data_reader.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
+import 'package:collection/collection.dart';
/// The globally unique identifier.
///
@@ -51,11 +52,46 @@
sink.writeUInt32(randomBits);
}
+ static List<ManifestItemId> readList(SummaryDataReader reader) {
+ return reader.readTypedList(() => ManifestItemId.read(reader));
+ }
+
static ManifestItemId? readOptional(SummaryDataReader reader) {
return reader.readOptionalObject(() => ManifestItemId.read(reader));
}
}
+class ManifestItemIdList {
+ final List<ManifestItemId> ids;
+
+ ManifestItemIdList(this.ids);
+
+ factory ManifestItemIdList.read(SummaryDataReader reader) {
+ return ManifestItemIdList(ManifestItemId.readList(reader));
+ }
+
+ @override
+ int get hashCode {
+ return const ListEquality<ManifestItemId>().hash(ids);
+ }
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) return true;
+ return other is ManifestItemIdList &&
+ const ListEquality<ManifestItemId>().equals(other.ids, ids);
+ }
+
+ @override
+ String toString() {
+ return '[${ids.join(', ')}]';
+ }
+
+ void write(BufferedSink sink) {
+ sink.writeList(ids, (id) => id.write(sink));
+ }
+}
+
extension ManifestItemIdExtension on ManifestItemId? {
void writeOptional(BufferedSink sink) {
sink.writeOptionalObject(this, (id) {
diff --git a/pkg/analyzer/lib/src/fine/manifest_item.dart b/pkg/analyzer/lib/src/fine/manifest_item.dart
index 7ea8a47..c5ca585 100644
--- a/pkg/analyzer/lib/src/fine/manifest_item.dart
+++ b/pkg/analyzer/lib/src/fine/manifest_item.dart
@@ -493,22 +493,48 @@
/// The map of names to their IDs in the interface.
final Map<LookupName, ManifestItemId> map;
- ManifestInterface({required this.map});
+ /// 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.map, required this.combinedIds});
factory ManifestInterface.empty() {
- return ManifestInterface(map: {});
+ return ManifestInterface(map: {}, combinedIds: {});
}
factory ManifestInterface.read(SummaryDataReader reader) {
- return ManifestInterface(map: LookupNameIdMapExtension.read(reader));
+ return ManifestInterface(
+ map: LookupNameIdMapExtension.read(reader),
+ combinedIds: reader.readMap(
+ readKey: () => ManifestItemIdList.read(reader),
+ readValue: () => ManifestItemId.read(reader),
+ ),
+ );
}
- void clear() {
+ void afterUpdate() {
+ combinedIdsTemp = {};
+ }
+
+ void beforeUpdating() {
map.clear();
+ combinedIdsTemp = combinedIds;
+ combinedIds = {};
}
void write(BufferedSink sink) {
map.write(sink);
+ sink.writeMap(
+ combinedIds,
+ writeKey: (key) => key.write(sink),
+ writeValue: (id) => id.write(sink),
+ );
}
}
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 4a226ec..ec203e3 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -25342,6 +25342,85 @@
);
}
+ test_manifest_class_getter_combinedSignatures_merged_addUnrelated() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ num get foo;
+}
+
+abstract class B {
+ int get foo;
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.getter: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.getter: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ interface
+ map
+ foo: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {
+ num get foo;
+}
+
+abstract class B {
+ int get foo;
+}
+
+abstract class C implements A, B {
+ void zzz();
+}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.getter: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.getter: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ declaredMembers
+ zzz.method: #M6
+ interface
+ map
+ foo: #M5
+ zzz: #M6
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ );
+ }
+
test_manifest_class_getter_metadata() async {
await _runLibraryManifestScenario(
initialCode: r'''
@@ -26417,6 +26496,362 @@
);
}
+ test_manifest_class_method_combinedSignatures_conflict_removeOne() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ int foo();
+}
+
+abstract class B {
+ double foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+''',
+ updatedCode: r'''
+abstract class A {
+ int foo();
+}
+
+abstract class B {}
+
+abstract class C implements A, B {}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ C: #M4
+ interface
+ map
+ foo: #M1
+''',
+ );
+ }
+
+ test_manifest_class_method_combinedSignatures_merged_addUnrelated() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ dynamic foo();
+}
+
+abstract class B {
+ void foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ interface
+ map
+ foo: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {
+ dynamic foo();
+}
+
+abstract class B {
+ void foo();
+ void zzz();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ zzz.method: #M6
+ interface
+ map
+ foo: #M3
+ zzz: #M6
+ C: #M4
+ interface
+ map
+ foo: #M5
+ zzz: #M6
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ );
+ }
+
+ test_manifest_class_method_combinedSignatures_merged_removeOne() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ dynamic foo();
+}
+
+abstract class B {
+ void foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ interface
+ map
+ foo: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {}
+
+abstract class B {
+ void foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ interface
+ map
+ foo: #M3
+''',
+ );
+ }
+
+ test_manifest_class_method_combinedSignatures_merged_sameBase() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A<T> {
+ T foo();
+}
+
+abstract class B implements A<dynamic> {}
+
+abstract class C implements A<void> {}
+
+abstract class D implements B, C {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ interface
+ map
+ foo: #M1
+ C: #M3
+ interface
+ map
+ foo: #M1
+ D: #M4
+ interface
+ map
+ foo: #M1
+''',
+ updatedCode: r'''
+abstract class A<T> {
+ T foo();
+}
+
+abstract class B implements A<dynamic> {}
+
+abstract class C implements A<void> {}
+
+abstract class D implements B, C {
+ void zzz() {}
+}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ interface
+ map
+ foo: #M1
+ C: #M3
+ interface
+ map
+ foo: #M1
+ D: #M4
+ declaredMembers
+ zzz.method: #M5
+ interface
+ map
+ foo: #M1
+ zzz: #M5
+''',
+ );
+ }
+
+ test_manifest_class_method_combinedSignatures_merged_updateOne() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ dynamic foo();
+}
+
+abstract class B {
+ void foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M3
+ interface
+ map
+ foo: #M3
+ C: #M4
+ interface
+ map
+ foo: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {
+ dynamic foo();
+}
+
+abstract class B {
+ int foo();
+}
+
+abstract class C implements A, B {}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.method: #M1
+ interface
+ map
+ foo: #M1
+ B: #M2
+ declaredMembers
+ foo.method: #M6
+ interface
+ map
+ foo: #M6
+ C: #M4
+ interface
+ map
+ foo: #M7
+ combinedIds
+ [#M1, #M6]: #M7
+''',
+ );
+ }
+
test_manifest_class_method_formalParameter_optionalNamed() async {
await _runLibraryManifestScenario(
initialCode: r'''
@@ -28150,6 +28585,85 @@
);
}
+ test_manifest_class_setter_combinedSignatures_merged_addUnrelated() async {
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ set foo(num _);
+}
+
+abstract class B {
+ set foo(int _);
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.setter: #M1
+ interface
+ map
+ foo=: #M1
+ B: #M2
+ declaredMembers
+ foo.setter: #M3
+ interface
+ map
+ foo=: #M3
+ C: #M4
+ interface
+ map
+ foo=: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {
+ set foo(num _);
+}
+
+abstract class B {
+ set foo(int _);
+}
+
+abstract class C implements A, B {
+ void zzz();
+}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ declaredMembers
+ foo.setter: #M1
+ interface
+ map
+ foo=: #M1
+ B: #M2
+ declaredMembers
+ foo.setter: #M3
+ interface
+ map
+ foo=: #M3
+ C: #M4
+ declaredMembers
+ zzz.method: #M6
+ interface
+ map
+ foo=: #M5
+ zzz: #M6
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ );
+ }
+
test_manifest_class_setter_metadata() async {
await _runLibraryManifestScenario(
initialCode: r'''
@@ -28630,6 +29144,101 @@
);
}
+ test_manifest_class_setter_topMerge() async {
+ configuration.withElementManifests = true;
+ await _runLibraryManifestScenario(
+ initialCode: r'''
+abstract class A {
+ set foo(List _);
+}
+
+abstract class B {
+ set foo(List<void> _);
+}
+
+abstract class C implements A, B {}
+''',
+ expectedInitialEvents: r'''
+[operation] linkLibraryCycle SDK
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ supertype: Object @ dart:core
+ declaredMembers
+ foo.setter: #M1
+ valueType: List @ dart:core
+ dynamic
+ interface
+ map
+ foo=: #M1
+ B: #M2
+ supertype: Object @ dart:core
+ declaredMembers
+ foo.setter: #M3
+ valueType: List @ dart:core
+ void
+ interface
+ map
+ foo=: #M3
+ C: #M4
+ supertype: Object @ dart:core
+ interfaces
+ A @ package:test/test.dart
+ B @ package:test/test.dart
+ interface
+ map
+ foo=: #M5
+ combinedIds
+ [#M1, #M3]: #M5
+''',
+ updatedCode: r'''
+abstract class A {
+ set foo(List _);
+}
+
+abstract class B {
+ set foo(List<int> _);
+}
+
+abstract class C implements A, B {}
+''',
+ expectedUpdatedEvents: r'''
+[operation] linkLibraryCycle
+ package:test/test.dart
+ manifest
+ A: #M0
+ supertype: Object @ dart:core
+ declaredMembers
+ foo.setter: #M1
+ valueType: List @ dart:core
+ dynamic
+ interface
+ map
+ foo=: #M1
+ B: #M2
+ supertype: Object @ dart:core
+ declaredMembers
+ foo.setter: #M6
+ valueType: List @ dart:core
+ int @ dart:core
+ interface
+ map
+ foo=: #M6
+ C: #M4
+ supertype: Object @ dart:core
+ interfaces
+ A @ package:test/test.dart
+ B @ package:test/test.dart
+ interface
+ map
+ foo=: #M7
+ combinedIds
+ [#M1, #M6]: #M7
+''',
+ );
+ }
+
test_manifest_class_setter_valueType() async {
configuration.withElementManifests = true;
await _runLibraryManifestScenario(
diff --git a/pkg/analyzer/test/src/dart/analysis/result_printer.dart b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
index c1153bb..22d6913 100644
--- a/pkg/analyzer/test/src/dart/analysis/result_printer.dart
+++ b/pkg/analyzer/test/src/dart/analysis/result_printer.dart
@@ -935,6 +935,20 @@
}
});
}
+
+ var combinedIds = item.interface.combinedIds;
+ if (combinedIds.isNotEmpty) {
+ sink.writelnWithIndent('combinedIds');
+ sink.withIndent(() {
+ for (var entry in combinedIds.entries) {
+ var idListStr = entry.key.ids
+ .map((id) => idProvider.manifestId(id))
+ .join(', ');
+ var idStr = idProvider.manifestId(entry.value);
+ sink.writelnWithIndent('[$idListStr]: $idStr');
+ }
+ });
+ }
});
}
}